%include { #include "config.h" #include #include "dfilter-int.h" #include "syntax-tree.h" #include "sttype-field.h" #include "sttype-slice.h" #include "sttype-op.h" #include "sttype-function.h" #include "sttype-set.h" #include "drange.h" #include "grammar.h" #ifdef _WIN32 #pragma warning(disable:4671) #endif static stnode_t * new_function(dfwork_t *dfw, stnode_t *node); #define FAIL(dfw, node, ...) dfilter_fail(dfw, DF_ERROR_GENERIC, stnode_location(node), __VA_ARGS__) DIAG_OFF_LEMON() } /* end of %include */ %code { DIAG_ON_LEMON() } /* Parser Information */ %name Dfilter %token_prefix TOKEN_ %extra_argument {dfwork_t *dfw} /* Terminal and Non-Terminal types and destructors */ %token_type {stnode_t*} %token_destructor { (void)dfw; stnode_free($$); } %default_type {stnode_t*} %default_destructor {stnode_free($$);} %type range_node_list {GSList*} %destructor range_node_list {drange_node_free_list($$);} %type layer {GSList*} %destructor layer {drange_node_free_list($$);} %type function_params {GSList*} %destructor function_params {st_funcparams_free($$);} %type set_list {GSList*} %destructor set_list {set_nodelist_free($$);} %type set_element {GSList*} %destructor set_element {set_nodelist_free($$);} /* This is called as soon as a syntax error happens. After that, any "error" symbols are shifted, if possible. */ %syntax_error { if (!TOKEN) { dfilter_fail(dfw, DF_ERROR_GENERIC, NULL, "Unexpected end of filter expression."); return; } FAIL(dfw, TOKEN, "\"%s\" was unexpected in this context.", stnode_token(TOKEN)); } /* When a parse fails, mark an error. This occurs after the above syntax_error code and after the parser fails to use error recovery, shifting an "error" symbol and successfully shifting 3 more symbols. */ %parse_failure { dfw->parse_failure = TRUE; } /* ----------------- The grammar -------------- */ /* Associativity */ %left TEST_OR. %left TEST_AND. %right TEST_NOT. %nonassoc TEST_ALL_EQ TEST_ANY_EQ TEST_ALL_NE TEST_ANY_NE TEST_LT TEST_LE TEST_GT TEST_GE TEST_CONTAINS TEST_MATCHES. %left BITWISE_AND. %left PLUS MINUS. %left STAR RSLASH PERCENT. %nonassoc UNARY_PLUS UNARY_MINUS. /* Top-level targets */ sentence ::= expr(X). { dfw->st_root = X; } sentence ::= . { dfw->st_root = NULL; } expr(X) ::= relation(R). { X = R; } expr(X) ::= arithmetic_expr(E). { X = E; } /* Logical tests */ expr(X) ::= expr(Y) TEST_AND(T) expr(Z). { X = T; sttype_oper_set2(X, STNODE_OP_AND, Y, Z); } expr(X) ::= expr(Y) TEST_OR(T) expr(Z). { X = T; sttype_oper_set2(X, STNODE_OP_OR, Y, Z); } expr(X) ::= TEST_NOT(T) expr(Y). { X = T; sttype_oper_set1(X, STNODE_OP_NOT, Y); } /* Any expression inside parens is simply that expression */ expr(X) ::= LPAREN expr(Y) RPAREN. { X = Y; } /* Entities, or things that can be compared/tested/checked */ atom(A) ::= STRING(S). { A = S; } atom(A) ::= CHARCONST(N). { A = N; } atom(A) ::= LITERAL(S). { A = S; } layer(R) ::= HASH LBRACKET range_node_list(L) RBRACKET. { R = L; } layer(R) ::= HASH INTEGER(N). { char *err_msg = NULL; drange_node *range = drange_node_from_str(stnode_token(N), &err_msg); if (err_msg != NULL) { FAIL(dfw, N, "%s", err_msg); g_free(err_msg); } stnode_free(N); R = g_slist_append(NULL, range); } layered_field(R) ::= FIELD(F). { R = F; } layered_field(R) ::= FIELD(F) layer(L). { R = F; sttype_field_set_range(R, L); g_slist_free(L); } layered_field(R) ::= UNPARSED(U) layer(L). { header_field_info *hfinfo = dfilter_resolve_unparsed(dfw, stnode_token(U)); if (hfinfo == NULL) { FAIL(dfw, U, "%s is not a valid field", stnode_token(U)); } R = stnode_new(STTYPE_FIELD, hfinfo, NULL, stnode_location(U)); stnode_free(U); sttype_field_set_range(R, L); g_slist_free(L); } field(R) ::= layered_field(F). { R = F; } field(R) ::= ATSIGN layered_field(F). { R = F; sttype_field_set_raw(R, TRUE); } field(R) ::= ATSIGN UNPARSED(U). { const char *token = stnode_token(U); const stloc_t *loc = stnode_location(U); header_field_info *hfinfo = dfilter_resolve_unparsed(dfw, token); if (hfinfo == NULL) { FAIL(dfw, U, "%s is not a valid field", stnode_token(U)); } R = stnode_new(STTYPE_FIELD, hfinfo, g_strdup(token), loc); sttype_field_set_raw(R, TRUE); stnode_free(U); } reference(R) ::= DOLLAR LBRACE field(F) RBRACE. { /* convert field to reference */ R = stnode_new(STTYPE_REFERENCE, sttype_field_hfinfo(F), NULL, stnode_location(F)); sttype_field_set_drange(R, sttype_field_drange_steal(F)); sttype_field_set_raw(R, sttype_field_raw(F)); stnode_free(F); } reference(R) ::= DOLLAR LBRACE UNPARSED(U) RBRACE. { header_field_info *hfinfo = dfilter_resolve_unparsed(dfw, stnode_token(U)); if (hfinfo == NULL) { FAIL(dfw, U, "%s is not a valid field", stnode_token(U)); } R = stnode_new(STTYPE_REFERENCE, hfinfo, NULL, stnode_location(U)); stnode_free(U); } entity(E) ::= atom(A). { E = A; } entity(E) ::= slice(R). { E = R; } entity(E) ::= function(F). { E = F; } entity(E) ::= field(F). { E = F; } entity(E) ::= reference(R). { E = R; } entity(E) ::= UNPARSED(U). { const char *token = stnode_token(U); const stloc_t *loc = stnode_location(U); header_field_info *hfinfo = dfilter_resolve_unparsed(dfw, token); if (hfinfo != NULL) { E = stnode_new(STTYPE_FIELD, hfinfo, g_strdup(token), loc); } else { E = stnode_new(STTYPE_LITERAL, g_strdup(token), g_strdup(token), loc); } stnode_free(U); } arithmetic_expr(T) ::= entity(N). { T = N; } arithmetic_expr(T) ::= PLUS entity(N). [UNARY_PLUS] { T = N; } arithmetic_expr(T) ::= MINUS(M) entity(N). [UNARY_MINUS] { T = M; sttype_oper_set1(T, STNODE_OP_UNARY_MINUS, N); } arithmetic_expr(T) ::= arithmetic_expr(F) BITWISE_AND(O) arithmetic_expr(M). { T = O; sttype_oper_set2(T, STNODE_OP_BITWISE_AND, F, M); } arithmetic_expr(T) ::= arithmetic_expr(F) PLUS(O) arithmetic_expr(M). { T = O; sttype_oper_set2(T, STNODE_OP_ADD, F, M); } arithmetic_expr(T) ::= arithmetic_expr(F) MINUS(O) arithmetic_expr(M). { T = O; sttype_oper_set2(T, STNODE_OP_SUBTRACT, F, M); } arithmetic_expr(T) ::= arithmetic_expr(F) STAR(O) arithmetic_expr(M). { T = O; sttype_oper_set2(T, STNODE_OP_MULTIPLY, F, M); } arithmetic_expr(T) ::= arithmetic_expr(F) RSLASH(O) arithmetic_expr(M). { T = O; sttype_oper_set2(T, STNODE_OP_DIVIDE, F, M); } arithmetic_expr(T) ::= arithmetic_expr(F) PERCENT(O) arithmetic_expr(M). { T = O; sttype_oper_set2(T, STNODE_OP_MODULO, F, M); } arithmetic_expr(T) ::= LBRACE arithmetic_expr(F) RBRACE. { T = F; } /* Relational tests */ cmp_op(O) ::= TEST_ALL_EQ(L). { O = L; sttype_oper_set_op(O, STNODE_OP_ALL_EQ); } cmp_op(O) ::= TEST_ANY_EQ(L). { O = L; sttype_oper_set_op(O, STNODE_OP_ANY_EQ); } cmp_op(O) ::= TEST_ALL_NE(L). { O = L; sttype_oper_set_op(O, STNODE_OP_ALL_NE); } cmp_op(O) ::= TEST_ANY_NE(L). { O = L; sttype_oper_set_op(O, STNODE_OP_ANY_NE); } cmp_op(O) ::= TEST_GT(L). { O = L; sttype_oper_set_op(O, STNODE_OP_GT); } cmp_op(O) ::= TEST_GE(L). { O = L; sttype_oper_set_op(O, STNODE_OP_GE); } cmp_op(O) ::= TEST_LT(L). { O = L; sttype_oper_set_op(O, STNODE_OP_LT); } cmp_op(O) ::= TEST_LE(L). { O = L; sttype_oper_set_op(O, STNODE_OP_LE); } comparison_test(T) ::= arithmetic_expr(E) cmp_op(O) arithmetic_expr(F). { T = O; sttype_oper_set2_args(O, E, F); } /* 'a == b == c' or 'a < b <= c <= d < e' */ comparison_test(T) ::= arithmetic_expr(E) cmp_op(O) comparison_test(R). { stnode_t *L, *F; /* for now generate it like E O F STNODE_OP_AND F P G, later it could be optimized or semantically checked (to make a <= b >= c or a == b != c invalid)? */ F = R; do { ws_assert(F != NULL && stnode_type_id(F) == STTYPE_TEST); sttype_oper_get(F, NULL, &F, NULL); } while (stnode_type_id(F) == STTYPE_TEST); L = O; sttype_oper_set2_args(L, E, stnode_dup(F)); T = stnode_new(STTYPE_TEST, NULL, NULL, NULL); sttype_oper_set2(T, STNODE_OP_AND, L, R); } relation_test(T) ::= comparison_test(C). { T = C; } relation_test(T) ::= entity(E) TEST_CONTAINS(L) entity(F). { T = L; sttype_oper_set2(T, STNODE_OP_CONTAINS, E, F); } relation_test(T) ::= entity(E) TEST_MATCHES(L) entity(F). { T = L; sttype_oper_set2(T, STNODE_OP_MATCHES, E, F); } relation_test(T) ::= entity(E) TEST_IN(O) set(S). { T = O; sttype_oper_set2(T, STNODE_OP_IN, E, S); } relation_test(T) ::= entity(E) TEST_NOT(P) TEST_IN(O) set(S). { T = P; sttype_oper_set2(O, STNODE_OP_IN, E, S); sttype_oper_set1(T, STNODE_OP_NOT, O); } relation(R) ::= relation_test(T). { R = T; } relation(R) ::= ANY relation_test(T). { R = T; sttype_test_set_match(R, STNODE_MATCH_ANY); } relation(R) ::= ALL relation_test(T). { R = T; sttype_test_set_match(R, STNODE_MATCH_ALL); } set(S) ::= LBRACE set_list(L) RBRACE. { S = stnode_new(STTYPE_SET, L, NULL, NULL); } set_list(L) ::= set_element(N). { L = g_slist_concat(NULL, N); } set_list(L) ::= set_list(P) COMMA set_element(N). { L = g_slist_concat(P, N); } set_entity(N) ::= entity(E). { N = E; } set_entity(N) ::= MINUS(M) entity(E). { N = M; sttype_oper_set1(N, STNODE_OP_UNARY_MINUS, E); } set_entity(N) ::= PLUS entity(E). { N = E; } set_element(N) ::= set_entity(X). { N = g_slist_append(NULL, X); N = g_slist_append(N, NULL); } set_element(N) ::= set_entity(X) DOTDOT set_entity(Y). { N = g_slist_append(NULL, X); N = g_slist_append(N, Y); } /* Slices */ slice(R) ::= entity(E) LBRACKET range_node_list(L) RBRACKET. { R = stnode_new(STTYPE_SLICE, NULL, NULL, NULL); sttype_slice_set(R, E, L); /* Delete the list, but not the drange_nodes that * the list contains. */ g_slist_free(L); } range_node_list(L) ::= RANGE_NODE(N). { char *err_msg = NULL; drange_node *rn = drange_node_from_str(stnode_token(N), &err_msg); if (err_msg != NULL) { FAIL(dfw, N, "%s", err_msg); g_free(err_msg); } L = g_slist_append(NULL, rn); stnode_free(N); } range_node_list(L) ::= range_node_list(P) COMMA RANGE_NODE(N). { char *err_msg = NULL; drange_node *rn = drange_node_from_str(stnode_token(N), &err_msg); if (err_msg != NULL) { FAIL(dfw, N, "%s", err_msg); g_free(err_msg); } L = g_slist_append(P, rn); stnode_free(N); } /* Functions */ %code { static stnode_t * new_function(dfwork_t *dfw, stnode_t *node) { const char *name = stnode_token(node); df_func_def_t *def = df_func_lookup(name); if (!def) { FAIL(dfw, node, "Function '%s' does not exist", name); } stnode_replace(node, STTYPE_FUNCTION, def); return node; } } /* A function can have one or more parameters */ function(F) ::= UNPARSED(U) LPAREN function_params(P) RPAREN. { F = new_function(dfw, U); sttype_function_set_params(F, P); } /* A function can have zero parameters. */ function(F) ::= UNPARSED(U) LPAREN RPAREN. { F = new_function(dfw, U); } function_params(P) ::= arithmetic_expr(E). { P = g_slist_append(NULL, E); } function_params(P) ::= function_params(L) COMMA arithmetic_expr(E). { P = g_slist_append(L, E); }