%include { #include "config.h" #include #include "dfilter-int.h" #include "syntax-tree.h" #include "sttype-range.h" #include "sttype-test.h" #include "sttype-function.h" #include "sttype-set.h" #include "drange.h" #include "grammar.h" #ifdef _WIN32 #pragma warning(disable:4671) #endif /* End of C code */ } /* 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; /* Mark unused, similar to Q_UNUSED */ stnode_free($$); } %type sentence {stnode_t*} %type expr {stnode_t*} %destructor expr {stnode_free($$);} %type entity {stnode_t*} %destructor entity {stnode_free($$);} %type relation_test {stnode_t*} %destructor relation_test {stnode_free($$);} %type logical_test {stnode_t*} %destructor logical_test {stnode_free($$);} %type rel_op2 {test_op_t} %type range {stnode_t*} %destructor range {stnode_free($$);} %type drnode {drange_node*} %destructor drnode {drange_node_free($$);} %type drnode_list {GSList*} %destructor drnode_list {drange_node_free_list($$);} %type funcparams {GSList*} %destructor funcparams {st_funcparams_free($$);} %type setnode_list {GSList*} %destructor setnode_list {set_nodelist_free($$);} /* This is called as soon as a syntax error happens. After that, any "error" symbols are shifted, if possible. */ %syntax_error { header_field_info *hfinfo; if (!TOKEN) { dfilter_fail(dfw, "Unexpected end of filter string."); dfw->syntax_error = TRUE; return; } switch(stnode_type_id(TOKEN)) { case STTYPE_UNINITIALIZED: dfilter_fail(dfw, "Syntax error."); break; case STTYPE_TEST: dfilter_fail(dfw, "Syntax error, TEST."); break; case STTYPE_STRING: dfilter_fail(dfw, "The string \"%s\" was unexpected in this context.", (char *)stnode_data(TOKEN)); break; case STTYPE_CHARCONST: dfilter_fail(dfw, "The character constant \"%s\" was unexpected in this context.", (char *)stnode_data(TOKEN)); break; case STTYPE_UNPARSED: dfilter_fail(dfw, "\"%s\" was unexpected in this context.", (char *)stnode_data(TOKEN)); break; case STTYPE_INTEGER: dfilter_fail(dfw, "The integer %d was unexpected in this context.", stnode_value(TOKEN)); break; case STTYPE_FIELD: hfinfo = (header_field_info *)stnode_data(TOKEN); dfilter_fail(dfw, "Syntax error near \"%s\".", hfinfo->abbrev); break; case STTYPE_FUNCTION: dfilter_fail(dfw, "The function s was unexpected in this context."); break; case STTYPE_SET: dfilter_fail(dfw, "Syntax error, SET."); break; /* These aren't handed to use as terminal tokens from the scanner, so was can assert that we'll never see them here. */ case STTYPE_NUM_TYPES: case STTYPE_RANGE: case STTYPE_FVALUE: g_assert_not_reached(); break; } dfw->syntax_error = TRUE; } /* 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->syntax_error = TRUE; } /* ----------------- The grammar -------------- */ /* Associativity */ %left TEST_AND. %left TEST_OR. %nonassoc TEST_EQ TEST_NE TEST_LT TEST_LE TEST_GT TEST_GE TEST_CONTAINS TEST_MATCHES TEST_BITWISE_AND. %right TEST_NOT. /* Top-level targets */ sentence ::= expr(X). { dfw->st_root = X; } sentence ::= . { dfw->st_root = NULL; } expr(X) ::= relation_test(R). { X = R; } expr(X) ::= logical_test(L). { X = L; } /* Logical tests */ logical_test(T) ::= expr(E) TEST_AND expr(F). { T = stnode_new(STTYPE_TEST, NULL); sttype_test_set2(T, TEST_OP_AND, E, F); } logical_test(T) ::= expr(E) TEST_OR expr(F). { T = stnode_new(STTYPE_TEST, NULL); sttype_test_set2(T, TEST_OP_OR, E, F); } logical_test(T) ::= TEST_NOT expr(E). { T = stnode_new(STTYPE_TEST, NULL); sttype_test_set1(T, TEST_OP_NOT, E); } logical_test(T) ::= entity(E). { T = stnode_new(STTYPE_TEST, NULL); sttype_test_set1(T, TEST_OP_EXISTS, E); } /* Entities, or things that can be compared/tested/checked */ entity(E) ::= FIELD(F). { E = F; } entity(E) ::= STRING(S). { E = S; } entity(E) ::= CHARCONST(C). { E = C; } entity(E) ::= UNPARSED(U). { E = U; } entity(E) ::= range(R). { E = R; } range_body(B) ::= FIELD(F). { B = F; } range_body(B) ::= STRING(S). { B = S; } range_body(B) ::= range(R). { B = R; } /* Ranges */ range(R) ::= range_body(B) LBRACKET drnode_list(L) RBRACKET. { R = stnode_new(STTYPE_RANGE, NULL); sttype_range_set(R, B, L); /* Delete the list, but not the drange_nodes that * the list contains. */ g_slist_free(L); } drnode_list(L) ::= drnode(D). { L = g_slist_append(NULL, D); } drnode_list(L) ::= drnode_list(P) COMMA drnode(D). { L = g_slist_append(P, D); } /* x:y is offset:length */ drnode(D) ::= INTEGER(X) COLON INTEGER(Y). { D = drange_node_new(); drange_node_set_start_offset(D, stnode_value(X)); drange_node_set_length(D, stnode_value(Y)); stnode_free(X); stnode_free(Y); } /* x-y == offset:offset */ drnode(D) ::= INTEGER(X) HYPHEN INTEGER(Y). { D = drange_node_new(); drange_node_set_start_offset(D, stnode_value(X)); drange_node_set_end_offset(D, stnode_value(Y)); stnode_free(X); stnode_free(Y); } /* :y == from start to offset */ drnode(D) ::= COLON INTEGER(Y). { D = drange_node_new(); drange_node_set_start_offset(D, 0); drange_node_set_length(D, stnode_value(Y)); stnode_free(Y); } /* x: from offset to end */ drnode(D) ::= INTEGER(X) COLON. { D = drange_node_new(); drange_node_set_start_offset(D, stnode_value(X)); drange_node_set_to_the_end(D); stnode_free(X); } /* x == x:1 */ drnode(D) ::= INTEGER(X). { D = drange_node_new(); drange_node_set_start_offset(D, stnode_value(X)); drange_node_set_length(D, 1); stnode_free(X); } /* Relational tests */ relation_test(T) ::= entity(E) rel_op2(O) entity(F). { T = stnode_new(STTYPE_TEST, NULL); sttype_test_set2(T, O, E, F); } /* 'a == b == c' or 'a < b <= c <= d < e' */ relation_test(T) ::= entity(E) rel_op2(O) relation_test(R). { stnode_t *L, *F; /* for now generate it like E O F TEST_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 { g_assert(F != NULL && stnode_type_id(F) == STTYPE_TEST); sttype_test_get(F, NULL, &F, NULL); } while (stnode_type_id(F) == STTYPE_TEST); L = stnode_new(STTYPE_TEST, NULL); sttype_test_set2(L, O, E, stnode_dup(F)); T = stnode_new(STTYPE_TEST, NULL); sttype_test_set2(T, TEST_OP_AND, L, R); } rel_op2(O) ::= TEST_EQ. { O = TEST_OP_EQ; } rel_op2(O) ::= TEST_NE. { O = TEST_OP_NE; } rel_op2(O) ::= TEST_GT. { O = TEST_OP_GT; } rel_op2(O) ::= TEST_GE. { O = TEST_OP_GE; } rel_op2(O) ::= TEST_LT. { O = TEST_OP_LT; } rel_op2(O) ::= TEST_LE. { O = TEST_OP_LE; } rel_op2(O) ::= TEST_BITWISE_AND. { O = TEST_OP_BITWISE_AND; } rel_op2(O) ::= TEST_CONTAINS. { O = TEST_OP_CONTAINS; } rel_op2(O) ::= TEST_MATCHES. { O = TEST_OP_MATCHES; } relation_test(T) ::= entity(E) TEST_IN LBRACE setnode_list(L) RBRACE. { stnode_t *S; T = stnode_new(STTYPE_TEST, NULL); S = stnode_new(STTYPE_SET, L); sttype_test_set2(T, TEST_OP_IN, E, S); } setnode_list(L) ::= entity(E). { L = g_slist_append(NULL, E); } setnode_list(L) ::= setnode_list(P) entity(E). { L = g_slist_append(P, E); } /* Functions */ /* A function can have one or more parameters */ entity(E) ::= FUNCTION(F) LPAREN funcparams(P) RPAREN. { E = F; sttype_function_set_params(E, P); } /* A function can have zero parameters. */ entity(E) ::= FUNCTION(F) LPAREN RPAREN. { E = F; } funcparams(P) ::= entity(E). { P = g_slist_append(NULL, E); } funcparams(P) ::= funcparams(L) COMMA entity(E). { P = g_slist_append(L, E); } /* Any expression inside parens is simply that expression */ expr(X) ::= LPAREN expr(Y) RPAREN. { X = Y; stnode_set_bracket(X, TRUE); }