From 38545bdcefde8c66ec7d58a1e29cae099d129dfd Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Sat, 7 Apr 2018 10:30:31 +0200 Subject: [PATCH] Better reporting of parse errors --- TODO | 1 - parse.cc | 80 +++++++++++++++++++++++++++----------------------------- 2 files changed, 38 insertions(+), 43 deletions(-) diff --git a/TODO b/TODO index 48bcd54..3228957 100644 --- a/TODO +++ b/TODO @@ -2,7 +2,6 @@ - What if an input page specifies /Rotate? - "-f" switch - Help -- More precise parsing errors | # Position bbox on a new paper | paper("a4") diff --git a/parse.cc b/parse.cc index 37ee377..aa840ba 100644 --- a/parse.cc +++ b/parse.cc @@ -35,20 +35,8 @@ static token_type buffered_token = TOK_NONE; static string token; static double token_num; -static void NONRET parse_error(const char *msg, ...); static void parse_commands(list &cmds); -static void parse_error(const char *msg, ...) -{ - va_list args; - va_start(args, msg); - fprintf(stderr, "Parse error: "); - vfprintf(stderr, msg, args); - fprintf(stderr, "\n"); - va_end(args); - exit(1); -} - static token_type get_next_token() { while (*in_pos == ' ' || *in_pos == '\t' || *in_pos == '\r' || *in_pos == '\n') @@ -68,7 +56,7 @@ static token_type get_next_token() size_t end_pos; token_num = stod(token, &end_pos); if (end_pos < token.length()) - parse_error("Invalid number %s", token.c_str()); + err("Invalid number %s", token.c_str()); return TOK_NUMBER; } @@ -90,12 +78,12 @@ static token_type get_next_token() while (*in_pos != '"') { if (!*in_pos) - parse_error("Unterminated string"); + err("Unterminated string"); if (*in_pos == '\\') { in_pos++; if (*in_pos != '"' && *in_pos != '\\') - parse_error("Unrecognized escape sequence \\%c", *in_pos); + err("Unrecognized escape sequence \\%c", *in_pos); } token += *in_pos++; } @@ -126,7 +114,7 @@ static token_type get_next_token() in_pos++; return TOK_DOTDOT; } - parse_error("Unrecognized character '%c'", c); + err("Unrecognized character '%c'", c); } } @@ -214,7 +202,7 @@ static double parse_dimen(const arg_def *adef) { token_type t = next_token(); if (t != TOK_NUMBER) - parse_error("Argument %s must be a dimension", adef->name); + err("Argument %s must be a dimension", adef->name); double tmp = token_num; t = next_token(); @@ -225,12 +213,12 @@ static double parse_dimen(const arg_def *adef) return_token(); return 0; } - parse_error("Argument %s must have a unit", adef->name); + err("Argument %s must have a unit", adef->name); } for (uint i=0; units[i].name; i++) if (token == units[i].name) return tmp * units[i].multiplier; - parse_error("Unknown unit %s", token.c_str()); + err("Unknown unit %s", token.c_str()); } static void parse_pipeline(cmd *c) @@ -241,7 +229,7 @@ static void parse_pipeline(cmd *c) while (peek_token() != TOK_CLOSE_BRACE) { if (pp->branches.size() && next_token() != TOK_COMMA) - parse_error("Comma expected between pipeline branches"); + err("Comma expected between pipeline branches"); pipeline_branch *pb = new pipeline_branch; pp->branches.push_back(pb); @@ -251,15 +239,15 @@ static void parse_pipeline(cmd *c) { t = next_token(); if (t == TOK_END) - parse_error("Missing close brace"); + err("Missing close brace"); if (t == TOK_CLOSE_BRACE || t == TOK_COLON || t == TOK_COMMA) break; pipeline_selector ps; if (t != TOK_NUMBER) - parse_error("Pipeline selectors must start with a number"); + err("Pipeline selectors must start with a number"); if (!token_is_int()) - parse_error("Pipeline selectors must be integers"); + err("Pipeline selectors must be integers"); ps.from = (int) token_num; ps.to = ps.from; @@ -268,9 +256,9 @@ static void parse_pipeline(cmd *c) next_token(); t = next_token(); if (t != TOK_NUMBER) - parse_error("Pipeline selectors must be numbers or ranges"); + err("Pipeline selectors must be numbers or ranges"); if (!token_is_int()) - parse_error("Pipeline selectors must be integers"); + err("Pipeline selectors must be integers"); ps.to = (int) token_num; } @@ -316,9 +304,9 @@ static void parse_args(cmd *c) while (adefs[argi].name && token != adefs[argi].name) argi++; if (!adefs[argi].name) - parse_error("Command %s has no argument %s", cdef->name, token.c_str()); + err("Command %s has no argument %s", cdef->name, token.c_str()); if (c->args.count(token)) - parse_error("Argument %s given multiple times", token.c_str()); + err("Argument %s given multiple times", token.c_str()); t = next_token(); if (t == TOK_EQUAL) has_value = true; @@ -327,14 +315,14 @@ static void parse_args(cmd *c) saw_named = true; } else if (saw_named) - parse_error("Positional arguments must precede named ones"); + err("Positional arguments must precede named ones"); else { return_token(); while (next_pos < num_args && !(adefs[next_pos].type & AT_POSITIONAL)) next_pos++; if (next_pos >= num_args) - parse_error("Too many positional arguments for command %s", cdef->name); + err("Too many positional arguments for command %s", cdef->name); argi = next_pos++; has_value = true; } @@ -349,19 +337,19 @@ static void parse_args(cmd *c) case AT_STRING: t = next_token(); if (t != TOK_STRING) - parse_error("Argument %s must be a string", adef->name); + err("Argument %s must be a string", adef->name); val = new arg_string(token); break; case AT_INT: t = next_token(); if (t != TOK_NUMBER || !token_is_int()) - parse_error("Argument %s must be an integer", adef->name); + err("Argument %s must be an integer", adef->name); val = new arg_int((int) token_num); break; case AT_DOUBLE: t = next_token(); if (t != TOK_NUMBER) - parse_error("Argument %s must be a number", adef->name); + err("Argument %s must be a number", adef->name); val = new arg_double(token_num); break; case AT_DIMEN: @@ -370,7 +358,7 @@ static void parse_args(cmd *c) case AT_SWITCH: t = next_token(); if (t != TOK_NUMBER || !token_is_int() || ((int) token_num != 0 && (int) token_num != 1)) - parse_error("Argument %s must be a switch", adef->name); + err("Argument %s must be a switch", adef->name); val = new arg_int((int) token_num); break; default: @@ -382,7 +370,7 @@ static void parse_args(cmd *c) if (type == AT_SWITCH) val = new arg_int(1); else - parse_error("Argument %s must have a value", adef->name); + err("Argument %s must have a value", adef->name); } c->args[adef->name] = val; @@ -391,12 +379,12 @@ static void parse_args(cmd *c) if (t == TOK_CLOSE_PAREN) break; if (t != TOK_COMMA) - parse_error("Comma expected after argument %s", adef->name); + err("Comma expected after argument %s", adef->name); } for (uint i=0; iargs.count(adefs[i].name)) - parse_error("Command %s is missing an argument %s", cdef->name, adefs[i].name); + err("Command %s is missing an argument %s", cdef->name, adefs[i].name); } static void debug_cmd(cmd *c, uint indent=0) @@ -435,7 +423,7 @@ static cmd *parse_cmd() while (cdef->name && token != cdef->name) cdef++; if (!cdef->name) - parse_error("Unknown command %s", token.c_str()); + err("Unknown command %s", token.c_str()); cmd *c = new cmd; c->def = cdef; @@ -446,11 +434,11 @@ static cmd *parse_cmd() if (peek_token() == TOK_OPEN_BRACE) { if (!cdef->has_pipeline) - parse_error("Command %s does not accept a pipeline", cdef->name); + err("Command %s does not accept a pipeline", cdef->name); parse_pipeline(c); } else if (cdef->has_pipeline) - parse_error("Command %s requires a pipeline", cdef->name); + err("Command %s requires a pipeline", cdef->name); return c; } @@ -495,9 +483,17 @@ void parse(const char *in, list &cmds) { debug("### Parsing commands"); in_pos = in; - parse_commands(cmds); - if (next_token() != TOK_END) - parse_error("Extra tokens after commands"); + + try + { + parse_commands(cmds); + if (next_token() != TOK_END) + err("Extra tokens after commands"); + } + catch (exception &e) + { + die("Parse error: %s (at position %d)", e.what(), (int)(in_pos - in)); + } if (debug_level > 1) debug_cmds(cmds); -- 2.39.2