diff --git a/src/fart.propane b/src/fart.propane index f832e9b..5532859 100644 --- a/src/fart.propane +++ b/src/fart.propane @@ -1,5 +1,25 @@ tree; tree_prefix P; +ptype double; + +<
> + +<< +#include + +/* Store the numeric value of the matched text in *out. Returns false if the + * value cannot be represented. */ +static bool parse_number(uint8_t const * match_text, size_t match_length, double * out) +{ + const char * first = (const char *)match_text; + const char * last = first + match_length; + std::from_chars_result r = std::from_chars(first, last, *out); + return (r.ec == std::errc()) && (r.ptr == last); +} +>> drop /\s+/; @@ -14,8 +34,18 @@ token rcurly /\}/; token scene; token width; -token uint /\d+/; -token number /-?\d+(\.\d+)?/; +token uint /\d+/ << + if (!parse_number(match_text, match_length, &$$)) + { + $terminate(PARSER_TERMINATE_NUMBER_RANGE); + } +>> +token number /-?\d+(\.\d+)?/ << + if (!parse_number(match_text, match_length, &$$)) + { + $terminate(PARSER_TERMINATE_NUMBER_RANGE); + } +>> Start -> RenderParams Scene; diff --git a/src/fart/Engine.cpp b/src/fart/Engine.cpp index c69eca5..b55e6f4 100644 --- a/src/fart/Engine.cpp +++ b/src/fart/Engine.cpp @@ -62,5 +62,9 @@ int Engine::run(string_view input_file, opt output_file) return 1; } + /* CLI params take precedence over scene params; defaults fill the rest. */ + params.merge(scene->params); + params.apply_defaults(); + return 0; } diff --git a/src/fart/Parser.cpp b/src/fart/Parser.cpp index 33bd203..edd50d5 100644 --- a/src/fart/Parser.cpp +++ b/src/fart/Parser.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include Scene * Parser::load_scene(const string_view & input_file) @@ -49,6 +50,19 @@ Scene * Parser::load_scene(const string_view & input_file) break; } + case P_USER_TERMINATED: + if (p_user_terminate_code(context) == PARSER_TERMINATE_NUMBER_RANGE) + { + fprintf(stderr, "%.*s:%u:%u: error: number out of range\n", + fname_len, fname, pos.row, pos.col); + } + else + { + fprintf(stderr, "%.*s:%u:%u: internal error: unknown terminate code %zu\n", + fname_len, fname, pos.row, pos.col, p_user_terminate_code(context)); + } + break; + default: fprintf(stderr, "%.*s:%u:%u: internal error: unknown parser result %zu\n", fname_len, fname, pos.row, pos.col, result); @@ -61,29 +75,67 @@ Scene * Parser::load_scene(const string_view & input_file) return nullptr; } - Scene * scene = build_scene(p_result(context)); + Scene * scene = build_scene(input_file, p_result(context)); p_context_delete(context); return scene; } -Scene * Parser::build_scene(PStart start) +/* Convert a uint token value to a size_t, reporting an error if it does not + * fit. */ +static bool token_to_size(const string_view & input_file, PToken token, opt * out) +{ + /* SIZE_MAX + 1, computed so that it is exactly representable as a double. */ + const double size_limit = (double)(SIZE_MAX / 2u + 1u) * 2.0; + double value = token.pvalue(); + if (value >= size_limit) + { + p_position_t pos = token.position(); + fprintf(stderr, "%.*s:%u:%u: error: value out of range\n", + (int)input_file.size(), input_file.data(), pos.row, pos.col); + return false; + } + *out = (size_t)value; + return true; +} + +static Color vector_to_color(PVector vector) +{ + Color color{ + vector.pNumber2().pToken().pvalue(), + vector.pNumber4().pToken().pvalue(), + vector.pNumber6().pToken().pvalue(), + }; + if (PNumber a = vector.pNumber8(); p_node_valid(a)) + { + color.a = a.pToken().pvalue(); + } + return color; +} + +Scene * Parser::build_scene(const string_view & input_file, PStart start) { Scene * scene = new Scene(); for (PRenderParams rps = start.pRenderParams(); p_node_valid(rps); rps = rps.pRenderParams()) { PRenderParam rp = rps.pRenderParam(); + bool ok = true; if (PBackground background = rp.pBackground(); p_node_valid(background)) { - /* TODO: set scene->params.background from the Vector. */ + scene->params.background = vector_to_color(background.pVector()); } else if (PHeight height = rp.pHeight(); p_node_valid(height)) { - /* TODO: set scene->params.height from the uint token. */ + ok = token_to_size(input_file, height.pToken2(), &scene->params.height); } else if (PWidth width = rp.pWidth(); p_node_valid(width)) { - /* TODO: set scene->params.width from the uint token. */ + ok = token_to_size(input_file, width.pToken2(), &scene->params.width); + } + if (!ok) + { + delete scene; + return nullptr; } } diff --git a/src/fart/Parser.h b/src/fart/Parser.h index a9427fa..f3bb243 100644 --- a/src/fart/Parser.h +++ b/src/fart/Parser.h @@ -10,5 +10,5 @@ public: static Scene * load_scene(const string_view & input_file); private: - static Scene * build_scene(PStart start); + static Scene * build_scene(const string_view & input_file, PStart start); };