uint8_t cur_ltable; /* 0 <= cur_ltable < n_tables. */
};
-char *ovnacts_parse(struct lexer *, const struct ovnact_parse_params *,
- struct ofpbuf *ovnacts, struct expr **prereqsp)
- OVS_WARN_UNUSED_RESULT;
+bool ovnacts_parse(struct lexer *, const struct ovnact_parse_params *,
+ struct ofpbuf *ovnacts, struct expr **prereqsp);
char *ovnacts_parse_string(const char *s, const struct ovnact_parse_params *,
struct ofpbuf *ovnacts, struct expr **prereqsp)
OVS_WARN_UNUSED_RESULT;
int n_bits; /* Number of bits. */
};
-char *expr_field_parse(struct lexer *lexer, const struct shash *symtab,
- struct expr_field *field, struct expr **prereqsp)
- OVS_WARN_UNUSED_RESULT;
+bool expr_field_parse(struct lexer *, const struct shash *symtab,
+ struct expr_field *, struct expr **prereqsp);
void expr_field_format(const struct expr_field *, struct ds *);
struct expr_symbol *expr_symtab_add_field(struct shash *symtab,
void expr_format(const struct expr *, struct ds *);
void expr_print(const struct expr *);
struct expr *expr_parse(struct lexer *, const struct shash *symtab,
- const struct shash *macros,
- char **errorp);
+ const struct shash *macros);
struct expr *expr_parse_string(const char *, const struct shash *symtab,
const struct shash *macros,
char **errorp);
char *string;
};
-char *expr_constant_parse(struct lexer *, const struct expr_field *,
- union expr_constant *)
- OVS_WARN_UNUSED_RESULT;
+bool expr_constant_parse(struct lexer *, const struct expr_field *,
+ union expr_constant *);
void expr_constant_format(const union expr_constant *,
enum expr_constant_type, struct ds *);
void expr_constant_destroy(const union expr_constant *,
bool in_curlies; /* Whether the constants were in {}. */
};
-char *expr_constant_set_parse(struct lexer *, struct expr_constant_set *cs)
- OVS_WARN_UNUSED_RESULT;
+bool expr_constant_set_parse(struct lexer *, struct expr_constant_set *);
void expr_constant_set_format(const struct expr_constant_set *, struct ds *);
void expr_constant_set_destroy(struct expr_constant_set *cs);
const char *input; /* Remaining input (not owned by lexer). */
const char *start; /* Start of current token in 'input'. */
struct lex_token token; /* Current token (owned by lexer). */
+ char *error; /* Error message, if any (owned by lexer). */
};
void lexer_init(struct lexer *, const char *input);
enum lex_type lexer_get(struct lexer *);
enum lex_type lexer_lookahead(const struct lexer *);
bool lexer_match(struct lexer *, enum lex_type);
+bool lexer_force_match(struct lexer *, enum lex_type);
bool lexer_match_id(struct lexer *, const char *id);
bool lexer_is_int(const struct lexer *);
bool lexer_get_int(struct lexer *, int *value);
+bool lexer_force_int(struct lexer *, int *value);
+
+void lexer_force_end(struct lexer *);
+
+void lexer_error(struct lexer *, const char *message, ...)
+ OVS_PRINTF_FORMAT(2, 3);
+void lexer_syntax_error(struct lexer *, const char *message, ...)
+ OVS_PRINTF_FORMAT(2, 3);
+
+char *lexer_steal_error(struct lexer *);
#endif /* ovn/lex.h */
struct action_context {
const struct ovnact_parse_params *pp; /* Parameters. */
struct lexer *lexer; /* Lexer for pulling more tokens. */
- char *error; /* Error, if any, otherwise NULL. */
struct ofpbuf *ovnacts; /* Actions. */
struct expr *prereqs; /* Prerequisites to apply to match. */
};
static bool parse_action(struct action_context *);
static bool
-action_error_handle_common(struct action_context *ctx)
+action_parse_field(struct action_context *ctx,
+ int n_bits, bool rw, struct expr_field *f)
{
- if (ctx->error) {
- /* Already have an error, suppress this one since the cascade seems
- * unlikely to be useful. */
- return true;
- } else if (ctx->lexer->token.type == LEX_T_ERROR) {
- /* The lexer signaled an error. Nothing at the action level
- * accepts an error token, so we'll inevitably end up here with some
- * meaningless parse error. Report the lexical error instead. */
- ctx->error = xstrdup(ctx->lexer->token.s);
- return true;
- } else {
+ if (!expr_field_parse(ctx->lexer, ctx->pp->symtab, f, &ctx->prereqs)) {
return false;
}
-}
-
-static void OVS_PRINTF_FORMAT(2, 3)
-action_error(struct action_context *ctx, const char *message, ...)
-{
- if (action_error_handle_common(ctx)) {
- return;
- }
-
- va_list args;
- va_start(args, message);
- ctx->error = xvasprintf(message, args);
- va_end(args);
-}
-
-static void OVS_PRINTF_FORMAT(2, 3)
-action_syntax_error(struct action_context *ctx, const char *message, ...)
-{
- if (action_error_handle_common(ctx)) {
- return;
- }
-
- struct ds s;
-
- ds_init(&s);
- ds_put_cstr(&s, "Syntax error");
- if (ctx->lexer->token.type == LEX_T_END) {
- ds_put_cstr(&s, " at end of input");
- } else if (ctx->lexer->start) {
- ds_put_format(&s, " at `%.*s'",
- (int) (ctx->lexer->input - ctx->lexer->start),
- ctx->lexer->start);
- }
-
- if (message) {
- ds_put_char(&s, ' ');
-
- va_list args;
- va_start(args, message);
- ds_put_format_valist(&s, message, args);
- va_end(args);
- }
- ds_put_char(&s, '.');
-
- ctx->error = ds_steal_cstr(&s);
-}
-
-static bool
-action_force_match(struct action_context *ctx, enum lex_type t)
-{
- if (lexer_match(ctx->lexer, t)) {
- return true;
- } else {
- struct lex_token token = { .type = t };
- struct ds s = DS_EMPTY_INITIALIZER;
- lex_token_format(&token, &s);
-
- action_syntax_error(ctx, "expecting `%s'", ds_cstr(&s));
-
- ds_destroy(&s);
+ char *error = expr_type_check(f, n_bits, rw);
+ if (error) {
+ lexer_error(ctx->lexer, "%s", error);
+ free(error);
return false;
}
-}
-static bool
-action_parse_field(struct action_context *ctx,
- int n_bits, bool rw, struct expr_field *f)
-{
- char *error = expr_field_parse(ctx->lexer, ctx->pp->symtab, f,
- &ctx->prereqs);
- if (!error) {
- error = expr_type_check(f, n_bits, rw);
- if (!error) {
- return true;
- }
- }
-
- action_error(ctx, "%s", error);
- free(error);
- return false;
-}
-
-static bool
-action_get_int(struct action_context *ctx, int *value)
-{
- bool ok = lexer_get_int(ctx->lexer, value);
- if (!ok) {
- action_syntax_error(ctx, "expecting small integer");
- }
- return ok;
+ return true;
}
static bool
return true;
}
}
- action_syntax_error(ctx, "expecting port number");
+ lexer_syntax_error(ctx->lexer, "expecting port number");
return false;
}
parse_NEXT(struct action_context *ctx)
{
if (!ctx->pp->n_tables) {
- action_error(ctx, "\"next\" action not allowed here.");
+ lexer_error(ctx->lexer, "\"next\" action not allowed here.");
} else if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
int ltable;
- if (!action_get_int(ctx, <able)) {
- return;
- }
- if (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
- action_syntax_error(ctx, "expecting `)'");
+ if (!lexer_force_int(ctx->lexer, <able) ||
+ !lexer_force_match(ctx->lexer, LEX_T_RPAREN)) {
return;
}
if (ltable >= ctx->pp->n_tables) {
- action_error(ctx, "\"next\" argument must be in range 0 to %d.",
+ lexer_error(ctx->lexer,
+ "\"next\" argument must be in range 0 to %d.",
ctx->pp->n_tables - 1);
return;
}
if (ctx->pp->cur_ltable < ctx->pp->n_tables) {
ovnact_put_NEXT(ctx->ovnacts)->ltable = ctx->pp->cur_ltable + 1;
} else {
- action_error(ctx, "\"next\" action not allowed in last table.");
+ lexer_error(ctx->lexer,
+ "\"next\" action not allowed in last table.");
}
}
}
size_t ofs = ctx->ovnacts->size;
struct ovnact_load *load = ovnact_put_LOAD(ctx->ovnacts);
load->dst = *lhs;
+
char *error = expr_type_check(lhs, lhs->n_bits, true);
- if (!error) {
- error = expr_constant_parse(ctx->lexer, lhs, &load->imm);
- }
if (error) {
ctx->ovnacts->size = ofs;
- action_error(ctx, "%s", error);
+ lexer_error(ctx->lexer, "%s", error);
free(error);
+ return;
+ }
+ if (!expr_constant_parse(ctx->lexer, lhs, &load->imm)) {
+ ctx->ovnacts->size = ofs;
+ return;
}
}
const struct expr_field *lhs)
{
struct expr_field rhs;
- char *error = expr_field_parse(ctx->lexer, ctx->pp->symtab, &rhs,
- &ctx->prereqs);
- if (error) {
- action_error(ctx, "%s", error);
- free(error);
+ if (!expr_field_parse(ctx->lexer, ctx->pp->symtab, &rhs, &ctx->prereqs)) {
return;
}
const struct expr_symbol *rs = rhs.symbol;
if ((ls->width != 0) != (rs->width != 0)) {
if (exchange) {
- action_error(ctx,
- "Can't exchange %s field (%s) with %s field (%s).",
- ls->width ? "integer" : "string",
- ls->name,
- rs->width ? "integer" : "string",
- rs->name);
+ lexer_error(ctx->lexer,
+ "Can't exchange %s field (%s) with %s field (%s).",
+ ls->width ? "integer" : "string",
+ ls->name,
+ rs->width ? "integer" : "string",
+ rs->name);
} else {
- action_error(ctx,
- "Can't assign %s field (%s) to %s field (%s).",
- rs->width ? "integer" : "string",
- rs->name,
- ls->width ? "integer" : "string",
- ls->name);
+ lexer_error(ctx->lexer,
+ "Can't assign %s field (%s) to %s field (%s).",
+ rs->width ? "integer" : "string",
+ rs->name,
+ ls->width ? "integer" : "string",
+ ls->name);
}
return;
}
if (lhs->n_bits != rhs.n_bits) {
if (exchange) {
- action_error(ctx, "Can't exchange %d-bit field with %d-bit field.",
- lhs->n_bits, rhs.n_bits);
+ lexer_error(ctx->lexer,
+ "Can't exchange %d-bit field with %d-bit field.",
+ lhs->n_bits, rhs.n_bits);
} else {
- action_error(ctx,
- "Can't assign %d-bit value to %d-bit destination.",
- rhs.n_bits, lhs->n_bits);
+ lexer_error(ctx->lexer,
+ "Can't assign %d-bit value to %d-bit destination.",
+ rhs.n_bits, lhs->n_bits);
}
return;
} else if (!lhs->n_bits &&
ls->field->n_bits != rs->field->n_bits) {
- action_error(ctx, "String fields %s and %s are incompatible for "
- "%s.", ls->name, rs->name,
- exchange ? "exchange" : "assignment");
+ lexer_error(ctx->lexer, "String fields %s and %s are incompatible for "
+ "%s.", ls->name, rs->name,
+ exchange ? "exchange" : "assignment");
return;
}
- error = expr_type_check(lhs, lhs->n_bits, true);
+ char *error = expr_type_check(lhs, lhs->n_bits, true);
if (!error) {
error = expr_type_check(&rhs, rhs.n_bits, true);
}
if (error) {
- action_error(ctx, "%s", error);
+ lexer_error(ctx->lexer, "%s", error);
free(error);
return;
}
static void
parse_DEC_TTL(struct action_context *ctx)
{
- action_force_match(ctx, LEX_T_DECREMENT);
+ lexer_force_match(ctx->lexer, LEX_T_DECREMENT);
ovnact_put_DEC_TTL(ctx->ovnacts);
add_prerequisite(ctx, "ip");
}
parse_CT_NEXT(struct action_context *ctx)
{
if (ctx->pp->cur_ltable >= ctx->pp->n_tables) {
- action_error(ctx, "\"ct_next\" action not allowed in last table.");
+ lexer_error(ctx->lexer,
+ "\"ct_next\" action not allowed in last table.");
return;
}
struct ovnact_ct_commit *cc)
{
if (lexer_match_id(ctx->lexer, "ct_mark")) {
- if (!action_force_match(ctx, LEX_T_EQUALS)) {
+ if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
return;
}
if (ctx->lexer->token.type == LEX_T_INTEGER) {
cc->ct_mark = ntohll(ctx->lexer->token.value.integer);
cc->ct_mark_mask = ntohll(ctx->lexer->token.mask.integer);
} else {
- action_syntax_error(ctx, "expecting integer");
+ lexer_syntax_error(ctx->lexer, "expecting integer");
return;
}
lexer_get(ctx->lexer);
} else if (lexer_match_id(ctx->lexer, "ct_label")) {
- if (!action_force_match(ctx, LEX_T_EQUALS)) {
+ if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
return;
}
if (ctx->lexer->token.type == LEX_T_INTEGER) {
cc->ct_label = ctx->lexer->token.value.be128_int;
cc->ct_label_mask = ctx->lexer->token.mask.be128_int;
} else {
- action_syntax_error(ctx, "expecting integer");
+ lexer_syntax_error(ctx->lexer, "expecting integer");
return;
}
lexer_get(ctx->lexer);
} else {
- action_syntax_error(ctx, NULL);
+ lexer_syntax_error(ctx->lexer, NULL);
}
}
if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
parse_ct_commit_arg(ctx, ct_commit);
- if (ctx->error) {
+ if (ctx->lexer->error) {
return;
}
lexer_match(ctx->lexer, LEX_T_COMMA);
add_prerequisite(ctx, "ip");
if (ctx->pp->cur_ltable >= ctx->pp->n_tables) {
- action_error(ctx, "\"%s\" action not allowed in last table.", name);
+ lexer_error(ctx->lexer,
+ "\"%s\" action not allowed in last table.", name);
return;
}
cn->ltable = ctx->pp->cur_ltable + 1;
if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
if (ctx->lexer->token.type != LEX_T_INTEGER
|| ctx->lexer->token.format != LEX_F_IPV4) {
- action_syntax_error(ctx, "expecting IPv4 address");
+ lexer_syntax_error(ctx->lexer, "expecting IPv4 address");
return;
}
cn->ip = ctx->lexer->token.value.ipv4;
lexer_get(ctx->lexer);
- if (!action_force_match(ctx, LEX_T_RPAREN)) {
+ if (!lexer_force_match(ctx->lexer, LEX_T_RPAREN)) {
return;
}
}
parse_ct_lb_action(struct action_context *ctx)
{
if (ctx->pp->cur_ltable >= ctx->pp->n_tables) {
- action_error(ctx, "\"ct_lb\" action not allowed in last table.");
+ lexer_error(ctx->lexer, "\"ct_lb\" action not allowed in last table.");
return;
}
while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
if (ctx->lexer->token.type != LEX_T_INTEGER
|| mf_subvalue_width(&ctx->lexer->token.value) > 32) {
- action_syntax_error(ctx, "expecting IPv4 address");
+ lexer_syntax_error(ctx->lexer, "expecting IPv4 address");
return;
}
parse_nested_action(struct action_context *ctx, enum ovnact_type type,
const char *prereq)
{
- if (!action_force_match(ctx, LEX_T_LCURLY)) {
+ if (!lexer_force_match(ctx->lexer, LEX_T_LCURLY)) {
return;
}
struct action_context inner_ctx = {
.pp = ctx->pp,
.lexer = ctx->lexer,
- .error = NULL,
.ovnacts = &nested,
.prereqs = NULL
};
* actions. */
expr_destroy(inner_ctx.prereqs);
- if (inner_ctx.error) {
- ctx->error = inner_ctx.error;
+ if (inner_ctx.lexer->error) {
ovnacts_free(nested.data, nested.size);
ofpbuf_uninit(&nested);
return;
parse_get_mac_bind(struct action_context *ctx, int width,
struct ovnact_get_mac_bind *get_mac)
{
- action_force_match(ctx, LEX_T_LPAREN);
+ lexer_force_match(ctx->lexer, LEX_T_LPAREN);
action_parse_field(ctx, 0, false, &get_mac->port);
- action_force_match(ctx, LEX_T_COMMA);
+ lexer_force_match(ctx->lexer, LEX_T_COMMA);
action_parse_field(ctx, width, false, &get_mac->ip);
- action_force_match(ctx, LEX_T_RPAREN);
+ lexer_force_match(ctx->lexer, LEX_T_RPAREN);
}
static void
parse_put_mac_bind(struct action_context *ctx, int width,
struct ovnact_put_mac_bind *put_mac)
{
- action_force_match(ctx, LEX_T_LPAREN);
+ lexer_force_match(ctx->lexer, LEX_T_LPAREN);
action_parse_field(ctx, 0, false, &put_mac->port);
- action_force_match(ctx, LEX_T_COMMA);
+ lexer_force_match(ctx->lexer, LEX_T_COMMA);
action_parse_field(ctx, width, false, &put_mac->ip);
- action_force_match(ctx, LEX_T_COMMA);
+ lexer_force_match(ctx->lexer, LEX_T_COMMA);
action_parse_field(ctx, 48, false, &put_mac->mac);
- action_force_match(ctx, LEX_T_RPAREN);
+ lexer_force_match(ctx->lexer, LEX_T_RPAREN);
}
static void
bool v6)
{
if (ctx->lexer->token.type != LEX_T_ID) {
- action_syntax_error(ctx, NULL);
+ lexer_syntax_error(ctx->lexer, NULL);
return;
}
const struct hmap *map = v6 ? ctx->pp->dhcpv6_opts : ctx->pp->dhcp_opts;
o->option = dhcp_opts_find(map, ctx->lexer->token.s);
if (!o->option) {
- action_syntax_error(ctx, "expecting %s option name", name);
+ lexer_syntax_error(ctx->lexer, "expecting %s option name", name);
return;
}
lexer_get(ctx->lexer);
- if (!action_force_match(ctx, LEX_T_EQUALS)) {
+ if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
return;
}
- char *error = expr_constant_set_parse(ctx->lexer, &o->value);
- if (error) {
+ if (!expr_constant_set_parse(ctx->lexer, &o->value)) {
memset(&o->value, 0, sizeof o->value);
- action_error(ctx, "%s", error);
- free(error);
return;
}
if (!strcmp(o->option->type, "str")) {
if (o->value.type != EXPR_C_STRING) {
- action_error(ctx, "%s option %s requires string value.",
- name, o->option->name);
+ lexer_error(ctx->lexer, "%s option %s requires string value.",
+ name, o->option->name);
return;
}
} else {
if (o->value.type != EXPR_C_INTEGER) {
- action_error(ctx, "%s option %s requires numeric value.",
- name, o->option->name);
+ lexer_error(ctx->lexer, "%s option %s requires numeric value.",
+ name, o->option->name);
return;
}
}
/* Validate that the destination is a 1-bit, modifiable field. */
char *error = expr_type_check(dst, 1, true);
if (error) {
- action_error(ctx, "%s", error);
+ lexer_error(ctx->lexer, "%s", error);
free(error);
return;
}
struct ovnact_dhcp_option *o = &pdo->options[pdo->n_options++];
memset(o, 0, sizeof *o);
parse_dhcp_opt(ctx, o, pdo->ovnact.type == OVNACT_PUT_DHCPV6_OPTS);
- if (ctx->error) {
+ if (ctx->lexer->error) {
return;
}
if (pdo->ovnact.type == OVNACT_PUT_DHCPV4_OPTS
&& !find_offerip(pdo->options, pdo->n_options)) {
- action_error(ctx, "put_dhcp_opts requires offerip to be specified.");
+ lexer_error(ctx->lexer,
+ "put_dhcp_opts requires offerip to be specified.");
return;
}
}
static void
parse_set_action(struct action_context *ctx)
{
- struct expr *prereqs = NULL;
struct expr_field lhs;
- char *error;
+ if (!expr_field_parse(ctx->lexer, ctx->pp->symtab, &lhs, &ctx->prereqs)) {
+ return;
+ }
- error = expr_field_parse(ctx->lexer, ctx->pp->symtab, &lhs, &ctx->prereqs);
- if (!error) {
- if (lexer_match(ctx->lexer, LEX_T_EXCHANGE)) {
- parse_assignment_action(ctx, true, &lhs);
- } else if (lexer_match(ctx->lexer, LEX_T_EQUALS)) {
- if (ctx->lexer->token.type != LEX_T_ID) {
- parse_LOAD(ctx, &lhs);
- } else if (!strcmp(ctx->lexer->token.s, "put_dhcp_opts")
- && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
- parse_put_dhcp_opts(ctx, &lhs, ovnact_put_PUT_DHCPV4_OPTS(
- ctx->ovnacts));
- } else if (!strcmp(ctx->lexer->token.s, "put_dhcpv6_opts")
- && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
- parse_put_dhcp_opts(ctx, &lhs, ovnact_put_PUT_DHCPV6_OPTS(
- ctx->ovnacts));
- } else {
- parse_assignment_action(ctx, false, &lhs);
- }
+ if (lexer_match(ctx->lexer, LEX_T_EXCHANGE)) {
+ parse_assignment_action(ctx, true, &lhs);
+ } else if (lexer_match(ctx->lexer, LEX_T_EQUALS)) {
+ if (ctx->lexer->token.type != LEX_T_ID) {
+ parse_LOAD(ctx, &lhs);
+ } else if (!strcmp(ctx->lexer->token.s, "put_dhcp_opts")
+ && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
+ parse_put_dhcp_opts(ctx, &lhs, ovnact_put_PUT_DHCPV4_OPTS(
+ ctx->ovnacts));
+ } else if (!strcmp(ctx->lexer->token.s, "put_dhcpv6_opts")
+ && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
+ parse_put_dhcp_opts(ctx, &lhs, ovnact_put_PUT_DHCPV6_OPTS(
+ ctx->ovnacts));
} else {
- action_syntax_error(ctx, "expecting `=' or `<->'");
- }
- if (!error) {
- ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, prereqs);
+ parse_assignment_action(ctx, false, &lhs);
}
- }
-
- if (error) {
- expr_destroy(prereqs);
- action_error(ctx, "%s", error);
- free(error);
+ } else {
+ lexer_syntax_error(ctx->lexer, "expecting `=' or `<->'");
}
}
parse_action(struct action_context *ctx)
{
if (ctx->lexer->token.type != LEX_T_ID) {
- action_syntax_error(ctx, NULL);
+ lexer_syntax_error(ctx->lexer, NULL);
return false;
}
} else if (lexer_match_id(ctx->lexer, "put_nd")) {
parse_put_mac_bind(ctx, 128, ovnact_put_PUT_ND(ctx->ovnacts));
} else {
- action_syntax_error(ctx, "expecting action");
+ lexer_syntax_error(ctx->lexer, "expecting action");
}
- if (!lexer_match(ctx->lexer, LEX_T_SEMICOLON)) {
- action_syntax_error(ctx, "expecting ';'");
- }
- return !ctx->error;
+ lexer_force_match(ctx->lexer, LEX_T_SEMICOLON);
+ return !ctx->lexer->error;
}
static void
&& lexer_lookahead(ctx->lexer) == LEX_T_SEMICOLON) {
lexer_get(ctx->lexer); /* Skip "drop". */
lexer_get(ctx->lexer); /* Skip ";". */
- if (ctx->lexer->token.type != LEX_T_END) {
- action_syntax_error(ctx, "expecting end of input");
- }
+ lexer_force_end(ctx->lexer);
return;
}
* it sets '*prereqsp' to NULL. The caller owns '*prereqsp' and must
* eventually free it.
*
- * Returns NULL on success, otherwise a malloc()'d error message that the
- * caller must free. On failure, 'ovnacts' has the same contents and
- * '*prereqsp' is set to NULL, but some tokens may have been consumed from
- * 'lexer'.
+ * Returns true if successful, false if an error occurred. Upon return,
+ * returns true if and only if lexer->error is NULL.
*/
-char * OVS_WARN_UNUSED_RESULT
+bool
ovnacts_parse(struct lexer *lexer, const struct ovnact_parse_params *pp,
struct ofpbuf *ovnacts, struct expr **prereqsp)
{
struct action_context ctx = {
.pp = pp,
.lexer = lexer,
- .error = NULL,
.ovnacts = ovnacts,
.prereqs = NULL,
};
- parse_actions(&ctx);
+ if (!lexer->error) {
+ parse_actions(&ctx);
+ }
- if (!ctx.error) {
+ if (!lexer->error) {
*prereqsp = ctx.prereqs;
- return NULL;
+ return true;
} else {
ofpbuf_pull(ovnacts, ovnacts_start);
ovnacts_free(ovnacts->data, ovnacts->size);
ovnacts->size = ovnacts_start;
expr_destroy(ctx.prereqs);
*prereqsp = NULL;
- return ctx.error;
+ return false;
}
}
struct ofpbuf *ofpacts, struct expr **prereqsp)
{
struct lexer lexer;
- char *error;
lexer_init(&lexer, s);
lexer_get(&lexer);
- error = ovnacts_parse(&lexer, pp, ofpacts, prereqsp);
+ ovnacts_parse(&lexer, pp, ofpacts, prereqsp);
+ char *error = lexer_steal_error(&lexer);
lexer_destroy(&lexer);
return error;
struct lexer *lexer; /* Lexer for pulling more tokens. */
const struct shash *symtab; /* Symbol table. */
const struct shash *macros; /* Table of macros. */
- char *error; /* Error, if any, otherwise NULL. */
bool not; /* True inside odd number of NOT operators. */
};
static void expr_not(struct expr *);
static bool parse_field(struct expr_context *, struct expr_field *);
-static bool
-expr_error_handle_common(struct expr_context *ctx)
-{
- if (ctx->error) {
- /* Already have an error, suppress this one since the cascade seems
- * unlikely to be useful. */
- return true;
- } else if (ctx->lexer->token.type == LEX_T_ERROR) {
- /* The lexer signaled an error. Nothing at the expression level
- * accepts an error token, so we'll inevitably end up here with some
- * meaningless parse error. Report the lexical error instead. */
- ctx->error = xstrdup(ctx->lexer->token.s);
- return true;
- } else {
- return false;
- }
-}
-
-static void OVS_PRINTF_FORMAT(2, 3)
-expr_error(struct expr_context *ctx, const char *message, ...)
-{
- if (expr_error_handle_common(ctx)) {
- return;
- }
-
- va_list args;
- va_start(args, message);
- ctx->error = xvasprintf(message, args);
- va_end(args);
-}
-
-static void OVS_PRINTF_FORMAT(2, 3)
-expr_syntax_error(struct expr_context *ctx, const char *message, ...)
-{
- if (expr_error_handle_common(ctx)) {
- return;
- }
-
- struct ds s;
-
- ds_init(&s);
- ds_put_cstr(&s, "Syntax error ");
- if (ctx->lexer->token.type == LEX_T_END) {
- ds_put_cstr(&s, "at end of input ");
- } else if (ctx->lexer->start) {
- ds_put_format(&s, "at `%.*s' ",
- (int) (ctx->lexer->input - ctx->lexer->start),
- ctx->lexer->start);
- }
-
- va_list args;
- va_start(args, message);
- ds_put_format_valist(&s, message, args);
- va_end(args);
-
- ctx->error = ds_steal_cstr(&s);
-}
-
static struct expr *
make_cmp__(const struct expr_field *f, enum expr_relop r,
const union expr_constant *c)
struct expr_constant_set *cs)
{
if (cs->type != (f->symbol->width ? EXPR_C_INTEGER : EXPR_C_STRING)) {
- expr_error(ctx, "%s field %s is not compatible with %s constant.",
- f->symbol->width ? "Integer" : "String",
- f->symbol->name,
- cs->type == EXPR_C_INTEGER ? "integer" : "string");
+ lexer_error(ctx->lexer,
+ "%s field %s is not compatible with %s constant.",
+ f->symbol->width ? "Integer" : "String",
+ f->symbol->name,
+ cs->type == EXPR_C_INTEGER ? "integer" : "string");
return false;
}
for (size_t i = 0; i < cs->n_values; i++) {
int w = expr_constant_width(&cs->values[i]);
if (w > f->symbol->width) {
- expr_error(ctx, "%d-bit constant is not compatible with "
- "%d-bit field %s.",
- w, f->symbol->width, f->symbol->name);
+ lexer_error(ctx->lexer,
+ "%d-bit constant is not compatible with %d-bit "
+ "field %s.", w, f->symbol->width, f->symbol->name);
return false;
}
}
if (r != EXPR_R_EQ && r != EXPR_R_NE) {
if (cs->in_curlies) {
- expr_error(ctx, "Only == and != operators may be used "
- "with value sets.");
+ lexer_error(ctx->lexer, "Only == and != operators may be used "
+ "with value sets.");
goto exit;
}
if (f->symbol->level == EXPR_L_NOMINAL ||
f->symbol->level == EXPR_L_BOOLEAN) {
- expr_error(ctx, "Only == and != operators may be used "
- "with %s field %s.",
- expr_level_to_string(f->symbol->level),
- f->symbol->name);
+ lexer_error(ctx->lexer, "Only == and != operators may be used "
+ "with %s field %s.",
+ expr_level_to_string(f->symbol->level),
+ f->symbol->name);
goto exit;
}
if (cs->values[0].masked) {
- expr_error(ctx, "Only == and != operators may be used with "
- "masked constants. Consider using subfields instead "
- "(e.g. eth.src[0..15] > 0x1111 in place of "
- "eth.src > 00:00:00:00:11:11/00:00:00:00:ff:ff).");
+ lexer_error(ctx->lexer, "Only == and != operators may be used "
+ "with masked constants. Consider using subfields "
+ "instead (e.g. eth.src[0..15] > 0x1111 in place of "
+ "eth.src > 00:00:00:00:11:11/00:00:00:00:ff:ff).");
goto exit;
}
}
positive ^= ctx->not;
if (!positive) {
const char *name = f->symbol->name;
- expr_error(ctx, "Nominal predicate %s may only be tested "
- "positively, e.g. `%s' or `%s == 1' but not "
- "`!%s' or `%s == 0'.",
- name, name, name, name, name);
+ lexer_error(ctx->lexer,
+ "Nominal predicate %s may only be tested "
+ "positively, e.g. `%s' or `%s == 1' but not "
+ "`!%s' or `%s == 0'.",
+ name, name, name, name, name);
goto exit;
}
}
} else if (r != (ctx->not ? EXPR_R_NE : EXPR_R_EQ)) {
- expr_error(ctx, "Nominal field %s may only be tested for "
- "equality (taking enclosing `!' operators into "
- "account).", f->symbol->name);
+ lexer_error(ctx->lexer, "Nominal field %s may only be tested for "
+ "equality (taking enclosing `!' operators into "
+ "account).", f->symbol->name);
goto exit;
}
}
return e;
}
-static bool
-expr_get_int(struct expr_context *ctx, int *value)
-{
- bool ok = lexer_get_int(ctx->lexer, value);
- if (!ok) {
- expr_syntax_error(ctx, "expecting small integer.");
- }
- return ok;
-}
-
static bool
parse_field(struct expr_context *ctx, struct expr_field *f)
{
const struct expr_symbol *symbol;
if (ctx->lexer->token.type != LEX_T_ID) {
- expr_syntax_error(ctx, "expecting field name.");
+ lexer_syntax_error(ctx->lexer, "expecting field name");
return false;
}
symbol = shash_find_data(ctx->symtab, ctx->lexer->token.s);
if (!symbol) {
- expr_syntax_error(ctx, "expecting field name.");
+ lexer_syntax_error(ctx->lexer, "expecting field name");
return false;
}
lexer_get(ctx->lexer);
int low, high;
if (!symbol->width) {
- expr_error(ctx, "Cannot select subfield of string field %s.",
- symbol->name);
+ lexer_error(ctx->lexer,
+ "Cannot select subfield of string field %s.",
+ symbol->name);
return false;
}
- if (!expr_get_int(ctx, &low)) {
+ if (!lexer_force_int(ctx->lexer, &low)) {
return false;
}
if (lexer_match(ctx->lexer, LEX_T_ELLIPSIS)) {
- if (!expr_get_int(ctx, &high)) {
+ if (!lexer_force_int(ctx->lexer, &high)) {
return false;
}
} else {
high = low;
}
- if (!lexer_match(ctx->lexer, LEX_T_RSQUARE)) {
- expr_syntax_error(ctx, "expecting `]'.");
+ if (!lexer_force_match(ctx->lexer, LEX_T_RSQUARE)) {
return false;
}
if (low > high) {
- expr_error(ctx, "Invalid bit range %d to %d.", low, high);
+ lexer_error(ctx->lexer, "Invalid bit range %d to %d.", low, high);
return false;
} else if (high >= symbol->width) {
- expr_error(ctx, "Cannot select bits %d to %d of %d-bit field %s.",
- low, high, symbol->width, symbol->name);
+ lexer_error(ctx->lexer,
+ "Cannot select bits %d to %d of %d-bit field %s.",
+ low, high, symbol->width, symbol->name);
return false;
} else if (symbol->level == EXPR_L_NOMINAL
&& (low != 0 || high != symbol->width - 1)) {
- expr_error(ctx, "Cannot select subfield of nominal field %s.",
- symbol->name);
+ lexer_error(ctx->lexer,
+ "Cannot select subfield of nominal field %s.",
+ symbol->name);
return false;
}
lexer_get(ctx->lexer);
return true;
} else {
- expr_syntax_error(ctx, "expecting relational operator.");
+ lexer_syntax_error(ctx->lexer, "expecting relational operator");
return false;
}
}
cs->type = type;
return true;
} else {
- expr_syntax_error(ctx, "expecting %s.",
- cs->type == EXPR_C_INTEGER ? "integer" : "string");
+ lexer_syntax_error(ctx->lexer, "expecting %s",
+ cs->type == EXPR_C_INTEGER ? "integer" : "string");
return false;
}
}
? shash_find_data(ctx->macros, ctx->lexer->token.s)
: NULL);
if (!addr_set) {
- expr_syntax_error(ctx, "expecting address set name.");
+ lexer_syntax_error(ctx->lexer, "expecting address set name");
return false;
}
lexer_get(ctx->lexer);
return true;
} else {
- expr_syntax_error(ctx, "expecting constant.");
+ lexer_syntax_error(ctx->lexer, "expecting constant");
return false;
}
}
}
/* Parses from 'lexer' a single integer or string constant compatible with the
- * type of 'f' into 'c'. On success, returns NULL. On failure, returns an
- * xmalloc()'ed error message that the caller must free and 'c' is
+ * type of 'f' into 'c'.
+ *
+ * Returns true if successful, false if an error occurred. Upon return,
+ * returns true if and only if lexer->error is NULL. On failure, 'c' is
* indeterminate. */
-char * OVS_WARN_UNUSED_RESULT
+bool
expr_constant_parse(struct lexer *lexer, const struct expr_field *f,
union expr_constant *c)
{
+ if (lexer->error) {
+ return false;
+ }
+
struct expr_context ctx = { .lexer = lexer };
struct expr_constant_set cs;
}
expr_constant_set_destroy(&cs);
- return ctx.error;
+ return !lexer->error;
}
/* Appends to 's' a re-parseable representation of constant 'c' with the given
/* Parses from 'lexer' a single or {}-enclosed set of at least one integer or
* string constants into 'cs', which the caller need not have initialized.
- * Returns NULL on success, in which case the caller owns 'cs', otherwise a
- * xmalloc()'ed error message owned by the caller, in which case 'cs' is
+ *
+ * Returns true if successful, false if an error occurred. Upon return,
+ * returns true if and only if lexer->error is NULL. On failure, 'cs' is
* indeterminate. */
-char * OVS_WARN_UNUSED_RESULT
+bool
expr_constant_set_parse(struct lexer *lexer, struct expr_constant_set *cs)
{
- struct expr_context ctx = { .lexer = lexer };
- parse_constant_set(&ctx, cs);
- return ctx.error;
+ if (!lexer->error) {
+ struct expr_context ctx = { .lexer = lexer };
+ parse_constant_set(&ctx, cs);
+ }
+ return !lexer->error;
}
/* Appends to 's' a re-parseable representation of 'cs'. */
*atomic = false;
if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
struct expr *e = expr_parse__(ctx);
- if (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
+ if (!lexer_force_match(ctx->lexer, LEX_T_RPAREN)) {
expr_destroy(e);
- expr_syntax_error(ctx, "expecting `)'.");
return NULL;
}
*atomic = true;
if (!expr_relop_from_token(ctx->lexer->token.type, &r)) {
if (!f.n_bits || ctx->lexer->token.type == LEX_T_EQUALS) {
- expr_syntax_error(ctx, "expecting relational operator.");
+ lexer_syntax_error(ctx->lexer,
+ "expecting relational operator");
return NULL;
} else if (f.n_bits > 1 && !ctx->not) {
- expr_error(ctx, "Explicit `!= 0' is required for inequality "
+ lexer_error(ctx->lexer,
+ "Explicit `!= 0' is required for inequality "
"test of multibit field against 0.");
return NULL;
}
(r2 == EXPR_R_LT || r2 == EXPR_R_LE)) ||
((r1 == EXPR_R_GT || r1 == EXPR_R_GE) &&
(r2 == EXPR_R_GT || r2 == EXPR_R_GE)))) {
- expr_error(ctx, "Range expressions must have the form "
- "`x < field < y' or `x > field > y', with each "
- "`<' optionally replaced by `<=' or `>' by `>=').");
+ lexer_error(ctx->lexer, "Range expressions must have the "
+ "form `x < field < y' or `x > field > y', with "
+ "each `<' optionally replaced by `<=' or `>' by "
+ "`>=').");
expr_constant_set_destroy(&c1);
expr_constant_set_destroy(&c2);
return NULL;
struct expr *e1 = make_cmp(ctx, &f, expr_relop_turn(r1), &c1);
struct expr *e2 = make_cmp(ctx, &f, r2, &c2);
- if (ctx->error) {
+ if (ctx->lexer->error) {
expr_destroy(e1);
expr_destroy(e2);
return NULL;
if (expr) {
if (!atomic) {
- expr_error(ctx, "Missing parentheses around operand of !.");
+ lexer_error(ctx->lexer,
+ "Missing parentheses around operand of !.");
expr_destroy(expr);
return NULL;
}
if (ctx->lexer->token.type == LEX_T_LOG_AND
|| ctx->lexer->token.type == LEX_T_LOG_OR) {
expr_destroy(e);
- expr_error(ctx,
- "&& and || must be parenthesized when used together.");
+ lexer_error(ctx->lexer,
+ "&& and || must be parenthesized when used together.");
return NULL;
}
}
return e;
}
-/* Parses an expression using the symbols in 'symtab' from 'lexer'. If
- * successful, returns the new expression and sets '*errorp' to NULL. On
- * failure, returns NULL and sets '*errorp' to an explanatory error message.
- * The caller must eventually free the returned expression (with
- * expr_destroy()) or error (with free()). */
+/* Parses an expression from 'lexer' using the symbols in 'symtab' and macros
+ * in 'macros'. If successful, returns the new expression; on failure, returns
+ * NULL. Returns nonnull if and only if lexer->error is NULL. */
struct expr *
expr_parse(struct lexer *lexer, const struct shash *symtab,
- const struct shash *macros, char **errorp)
+ const struct shash *macros)
{
struct expr_context ctx = { .lexer = lexer,
.symtab = symtab,
.macros = macros };
- struct expr *e = expr_parse__(&ctx);
- *errorp = ctx.error;
- ovs_assert((ctx.error != NULL) != (e != NULL));
- return e;
+ return lexer->error ? NULL : expr_parse__(&ctx);
}
-/* Like expr_parse(), but the expression is taken from 's'. */
+/* Parses the expression in 's' using the symbols in 'symtab' and macros in
+ * 'macros'. If successful, returns the new expression and sets '*errorp' to
+ * NULL. On failure, returns NULL and sets '*errorp' to an explanatory error
+ * message. The caller must eventually free the returned expression (with
+ * expr_destroy()) or error (with free()). */
struct expr *
expr_parse_string(const char *s, const struct shash *symtab,
const struct shash *macros, char **errorp)
{
struct lexer lexer;
- struct expr *expr;
lexer_init(&lexer, s);
lexer_get(&lexer);
- expr = expr_parse(&lexer, symtab, macros, errorp);
- if (!*errorp && lexer.token.type != LEX_T_END) {
- *errorp = xstrdup("Extra tokens at end of input.");
+ struct expr *expr = expr_parse(&lexer, symtab, macros);
+ lexer_force_end(&lexer);
+ *errorp = lexer_steal_error(&lexer);
+ if (*errorp) {
expr_destroy(expr);
expr = NULL;
}
}
\f
/* Parses a field or subfield from 'lexer' into 'field', obtaining field names
- * from 'symtab'. Returns NULL if successful, otherwise an error message owned
- * by the caller. */
-char * OVS_WARN_UNUSED_RESULT
+ * from 'symtab'. Returns true if successful, false if an error occurred.
+ * Upon return, returns true if and only if lexer->error is NULL. */
+bool
expr_field_parse(struct lexer *lexer, const struct shash *symtab,
struct expr_field *field, struct expr **prereqsp)
{
struct expr_context ctx = { .lexer = lexer, .symtab = symtab };
if (parse_field(&ctx, field) && field->symbol->predicate) {
- ctx.error = xasprintf("Predicate symbol %s used where lvalue "
- "required.", field->symbol->name);
+ lexer_error(lexer, "Predicate symbol %s used where lvalue required.",
+ field->symbol->name);
}
- if (!ctx.error) {
+ if (!lexer->error) {
const struct expr_symbol *symbol = field->symbol;
while (symbol) {
if (symbol->prereqs) {
+ char *error;
struct ovs_list nesting = OVS_LIST_INITIALIZER(&nesting);
struct expr *e = parse_and_annotate(symbol->prereqs, symtab,
- &nesting, &ctx.error);
- if (ctx.error) {
+ &nesting, &error);
+ if (error) {
+ lexer_error(lexer, "%s", error);
+ free(error);
break;
}
*prereqsp = expr_combine(EXPR_T_AND, *prereqsp, e);
symbol = symbol->parent;
}
}
- if (ctx.error) {
- memset(field, 0, sizeof *field);
+ if (!lexer->error) {
+ return true;
}
- return ctx.error;
+ memset(field, 0, sizeof *field);
+ return false;
}
/* Appends to 's' a re-parseable representation of 'field'. */
lexer_get(&lexer);
struct expr_context ctx = { .lexer = &lexer, .symtab = symtab };
- bool ok = parse_field(&ctx, field);
- if (!ok) {
- *errorp = ctx.error;
- } else if (lexer.token.type != LEX_T_END) {
- *errorp = xstrdup("Extra tokens at end of input.");
- ok = false;
- }
-
+ parse_field(&ctx, field);
+ lexer_force_end(&lexer);
+ *errorp = lexer_steal_error(&lexer);
lexer_destroy(&lexer);
- return ok;
+ return !*errorp;
}
/* Adds 'name' as a subfield of a larger field in 'symtab'. Whenever
lexer->input = input;
lexer->start = NULL;
lex_token_init(&lexer->token);
+ lexer->error = NULL;
}
/* Frees storage associated with 'lexer'. */
lexer_destroy(struct lexer *lexer)
{
lex_token_destroy(&lexer->token);
+ free(lexer->error);
}
/* Obtains the next token from 'lexer' into 'lexer->token', and returns the
}
}
+bool
+lexer_force_match(struct lexer *lexer, enum lex_type t)
+{
+ if (lexer_match(lexer, t)) {
+ return true;
+ } else {
+ struct lex_token token = { .type = t };
+ struct ds s = DS_EMPTY_INITIALIZER;
+ lex_token_format(&token, &s);
+
+ lexer_syntax_error(lexer, "expecting `%s'", ds_cstr(&s));
+
+ ds_destroy(&s);
+
+ return false;
+ }
+}
+
/* If 'lexer''s current token is the identifier given in 'id', advances 'lexer'
* to the next token and returns true. Otherwise returns false. */
bool
return false;
}
}
+
+bool
+lexer_force_int(struct lexer *lexer, int *value)
+{
+ bool ok = lexer_get_int(lexer, value);
+ if (!ok) {
+ lexer_syntax_error(lexer, "expecting small integer");
+ }
+ return ok;
+}
+
+void
+lexer_force_end(struct lexer *lexer)
+{
+ if (lexer->token.type != LEX_T_END) {
+ lexer_syntax_error(lexer, "expecting end of input");
+ }
+}
+
+static bool
+lexer_error_handle_common(struct lexer *lexer)
+{
+ if (lexer->error) {
+ /* Already have an error, suppress this one since the cascade seems
+ * unlikely to be useful. */
+ return true;
+ } else if (lexer->token.type == LEX_T_ERROR) {
+ /* The lexer signaled an error. Nothing at a higher level accepts an
+ * error token, so we'll inevitably end up here with some meaningless
+ * parse error. Report the lexical error instead. */
+ lexer->error = xstrdup(lexer->token.s);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void OVS_PRINTF_FORMAT(2, 3)
+lexer_error(struct lexer *lexer, const char *message, ...)
+{
+ if (lexer_error_handle_common(lexer)) {
+ return;
+ }
+
+ va_list args;
+ va_start(args, message);
+ lexer->error = xvasprintf(message, args);
+ va_end(args);
+}
+
+void OVS_PRINTF_FORMAT(2, 3)
+lexer_syntax_error(struct lexer *lexer, const char *message, ...)
+{
+ if (lexer_error_handle_common(lexer)) {
+ return;
+ }
+
+ struct ds s;
+
+ ds_init(&s);
+ ds_put_cstr(&s, "Syntax error");
+ if (lexer->token.type == LEX_T_END) {
+ ds_put_cstr(&s, " at end of input");
+ } else if (lexer->start) {
+ ds_put_format(&s, " at `%.*s'",
+ (int) (lexer->input - lexer->start),
+ lexer->start);
+ }
+
+ if (message) {
+ ds_put_char(&s, ' ');
+
+ va_list args;
+ va_start(args, message);
+ ds_put_format_valist(&s, message, args);
+ va_end(args);
+ }
+ ds_put_char(&s, '.');
+
+ lexer->error = ds_steal_cstr(&s);
+}
+
+char *
+lexer_steal_error(struct lexer *lexer)
+{
+ char *error = lexer->error;
+ lexer->error = NULL;
+ return error;
+}
1 == eth.type == 2 => Range expressions must have the form `x < field < y' or `x > field > y', with each `<' optionally replaced by `<=' or `>' by `>=').
-eth.dst[40] x => Extra tokens at end of input.
+eth.dst[40] x => Syntax error at `x' expecting end of input.
ip4.src == {1.2.3.4, $set1, $unknownset} => Syntax error at `$unknownset' expecting address set name.
eth.src == {$set3, badmac, 00:00:00:00:00:01} => Syntax error at `badmac' expecting constant.
next; xyzzy;
Syntax error at `xyzzy' expecting action.
next
- Syntax error at end of input expecting ';'.
+ Syntax error at end of input expecting `;'.
]])
sed '/^[[ ]]/d' test-cases.txt > input.txt
cp test-cases.txt expout