+/*
+ * PaperJam -- Command parser
+ *
+ * (c) 2018 Martin Mares <mj@ucw.cz>
+ */
+
#include <cassert>
#include <cstdarg>
#include <cstdlib>
while (*in_pos >= 'A' && *in_pos <= 'Z' ||
*in_pos >= 'a' && *in_pos <= 'z' ||
*in_pos >= '0' && *in_pos <= '9' ||
- *in_pos == '_')
+ *in_pos == '_' ||
+ *in_pos == '-')
token += *in_pos++;
return TOK_IDENT;
}
string dump() { return "<string> \"" + val + '"'; }
};
-static arg_val null_arg;
+arg_val null_arg;
/*** Parser ***/
t = next_token();
if (t != TOK_IDENT)
- parse_error("Parameter %s must have a unit", adef->name);
+ {
+ if (is_zero(tmp))
+ {
+ return_token();
+ return 0;
+ }
+ parse_error("Parameter %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;
while (adefs[num_args].name)
num_args++;
- c->args.resize(num_args, &null_arg);
-
token_type t = next_token();
if (t != TOK_OPEN_PAREN)
{
for (;;)
{
t = next_token();
+ if (t == TOK_CLOSE_PAREN)
+ break;
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.at(argi)->given())
+ if (c->args.count(token))
parse_error("Parameter %s given multiple times", token.c_str());
t = next_token();
- if (t != TOK_EQUAL)
- parse_error("Parameter name must be followed by '='");
+ if (t == TOK_EQUAL)
+ has_value = true;
+ else
+ return_token();
saw_named = true;
}
else if (saw_named)
if (next_pos >= num_args)
parse_error("Too many positional arguments for command %s", cdef->name);
argi = next_pos++;
+ has_value = true;
}
const arg_def *adef = &adefs[argi];
+ uint type = adef->type & AT_TYPE_MASK;
arg_val *val = NULL;
- switch (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);
- 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);
- 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);
- val = new arg_double(token_num);
- break;
- case AT_DIMEN:
- val = new arg_double(parse_dimen(adef));
- break;
- default:
- abort();
+ if (has_value)
+ {
+ switch (type)
+ {
+ case AT_STRING:
+ t = next_token();
+ if (t != TOK_STRING)
+ parse_error("Parameter %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("Parameter %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);
+ val = new arg_double(token_num);
+ break;
+ case AT_DIMEN:
+ val = new arg_double(parse_dimen(adef));
+ break;
+ 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);
+ val = new arg_int((int) token_num);
+ break;
+ default:
+ abort();
+ }
+ }
+ else
+ {
+ if (type == AT_SWITCH)
+ val = new arg_int(1);
+ else
+ parse_error("Parameter %s must have a value", adef->name);
}
- c->args.at(argi) = val;
+ c->args[adef->name] = val;
t = next_token();
if (t == TOK_CLOSE_PAREN)
}
for (uint i=0; i<num_args; i++)
- if ((adefs[i].type & AT_MANDATORY) && !c->args.at(i)->given())
+ 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);
}
static void debug_cmd(cmd *c, uint indent=0)
{
printf("%*sCommand %s\n", indent, "", c->def->name);
- for (size_t i=0; i < c->args.size(); i++)
+ for (uint i=0; c->def->arg_defs[i].name; i++)
{
const arg_def *adef = &c->def->arg_defs[i];
- string dump = c->args.at(i)->dump();
+ string dump = c->arg(adef->name)->dump();
printf("%*sArg #%d: %s = %s\n", indent+4, "", (int) i, adef->name, dump.c_str());
}
if (c->pipe)
{
for (auto c: cmds)
{
- c->exec = c->def->constructor(c);
+ try
+ {
+ c->exec = c->def->constructor(c);
+ }
+ catch (exception &e)
+ {
+ die("Error in %s: %s", c->def->name, e.what());
+ }
if (c->pipe)
{
for (auto pb: c->pipe->branches)
instantiate(cmds);
}
-void help()
+void parser_help()
{
for (int i=0; cmd_table[i].name; i++)
{
const arg_def *arg = def->arg_defs;
static const char * const type_names[] = {
- "string", "int", "double", "dimen"
+ "string", "int", "double", "dimen", "switch"
};
while (arg->name)
{