static string token;
static double token_num;
-static void NONRET parse_error(const char *msg, ...);
static void parse_commands(list<cmd *> &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')
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;
}
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++;
}
in_pos++;
return TOK_DOTDOT;
}
- parse_error("Unrecognized character '%c'", c);
+ err("Unrecognized character '%c'", c);
}
}
{
token_type t = next_token();
if (t != TOK_NUMBER)
- parse_error("Parameter %s must be a dimension", adef->name);
+ err("Argument %s must be a dimension", adef->name);
double tmp = token_num;
t = next_token();
return_token();
return 0;
}
- parse_error("Parameter %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)
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);
{
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;
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;
}
t = next_token();
if (t == TOK_CLOSE_PAREN)
break;
+
+ while (next_pos < num_args && !(adefs[next_pos].type & AT_POSITIONAL))
+ next_pos++;
+
uint argi = 0;
bool has_value = false;
+
if (t == TOK_IDENT)
{
while (adefs[argi].name && token != adefs[argi].name)
argi++;
- if (!adefs[argi].name)
- parse_error("Command %s has no parameter %s", cdef->name, token.c_str());
- if (c->args.count(token))
- parse_error("Parameter %s given multiple times", token.c_str());
- t = next_token();
- if (t == TOK_EQUAL)
- has_value = true;
+ if (adefs[argi].name)
+ {
+ if (c->args.count(token))
+ err("Argument %s given multiple times", token.c_str());
+ t = next_token();
+ if (t == TOK_EQUAL)
+ has_value = true;
+ else
+ return_token();
+ saw_named = true;
+ }
+ else if (next_pos < num_args && (adefs[next_pos].type & AT_TYPE_MASK) == AT_STRING)
+ {
+ // Shortcut syntax: positional arguments of string type can be specified
+ // as bare identifiers if they do not collide with names or other arguments.
+ return_token();
+ argi = next_pos++;
+ has_value = true;
+ }
else
- return_token();
- saw_named = true;
+ err("Command %s has no argument %s", cdef->name, token.c_str());
}
else if (saw_named)
- parse_error("Positional parameters must precede named ones");
- else
+ err("Positional arguments must precede named ones");
+ else if (next_pos < num_args)
{
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);
argi = next_pos++;
has_value = true;
}
+ else
+ err("Too many positional arguments for command %s", cdef->name);
const arg_def *adef = &adefs[argi];
uint type = adef->type & AT_TYPE_MASK;
{
case AT_STRING:
t = next_token();
- if (t != TOK_STRING)
- parse_error("Parameter %s must be a string", adef->name);
+ if (t != TOK_STRING && t != TOK_IDENT)
+ err("Argument %s must be a string or identifier", adef->name);
val = new arg_string(token);
break;
case AT_INT:
t = next_token();
if (t != TOK_NUMBER || !token_is_int())
- parse_error("Parameter %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("Parameter %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:
case AT_SWITCH:
t = next_token();
if (t != TOK_NUMBER || !token_is_int() || ((int) token_num != 0 && (int) token_num != 1))
- parse_error("Parameter %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:
if (type == AT_SWITCH)
val = new arg_int(1);
else
- parse_error("Parameter %s must have a value", adef->name);
+ err("Argument %s must have a value", adef->name);
}
c->args[adef->name] = val;
if (t == TOK_CLOSE_PAREN)
break;
if (t != TOK_COMMA)
- parse_error("Comma expected after parameter %s", adef->name);
+ err("Comma expected after argument %s", adef->name);
}
for (uint i=0; i<num_args; i++)
if ((adefs[i].type & AT_MANDATORY) && !c->args.count(adefs[i].name))
- parse_error("Command %s is missing a parameter %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)
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;
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;
}
{
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);
for (int i=0; cmd_table[i].name; i++)
{
const cmd_def *def = &cmd_table[i];
- printf("%s\n", def->name);
+ printf("%s - %s\n", def->name, def->help);
const arg_def *arg = def->arg_defs;
static const char * const type_names[] = {
- "string", "int", "double", "dimen", "switch"
+ "string", "int", "real", "dimen", "switch"
};
while (arg->name)
{
- printf("\t%s (%s)%s%s\n",
+ char a[32];
+ snprintf(a, sizeof(a), "%s%s=%s<%s>",
+ (arg->type & AT_POSITIONAL) ? "[" : "",
arg->name,
- type_names[arg->type & AT_TYPE_MASK],
- (arg->type & AT_MANDATORY) ? " [mandatory]" : "",
- (arg->type & AT_POSITIONAL) ? " [positional]" : "");
+ (arg->type & AT_POSITIONAL) ? "]" : "",
+ type_names[arg->type & AT_TYPE_MASK]);
+ printf(" %-20s %s\n", a, arg->help);
arg++;
}
if (def->has_pipeline)
- printf("\t{ pipeline }\n");
+ printf(" { pipeline }\n");
}
}