| /* $Id: grammar.lemon 21321 2007-04-03 19:08:00Z lego $ */ |
| |
| %include { |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include "dfilter-int.h" |
| #include "syntax-tree.h" |
| #include "sttype-range.h" |
| #include "sttype-test.h" |
| #include "sttype-function.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 {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($$);} |
| |
| /* 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("Unexpected end of filter string."); |
| return; |
| } |
| |
| switch(stnode_type_id(TOKEN)) { |
| case STTYPE_UNINITIALIZED: |
| dfilter_fail("Syntax error."); |
| break; |
| case STTYPE_TEST: |
| dfilter_fail("Syntax error, TEST."); |
| break; |
| case STTYPE_STRING: |
| dfilter_fail("The string \"%s\" was unexpected in this context.", |
| stnode_data(TOKEN)); |
| break; |
| case STTYPE_UNPARSED: |
| dfilter_fail("\"%s\" was unexpected in this context.", |
| stnode_data(TOKEN)); |
| break; |
| case STTYPE_INTEGER: |
| dfilter_fail("The integer %d was unexpected in this context.", |
| stnode_value(TOKEN)); |
| break; |
| case STTYPE_FIELD: |
| hfinfo = stnode_data(TOKEN); |
| dfilter_fail("Syntax error near \"%s\".", hfinfo->abbrev); |
| break; |
| case STTYPE_FUNCTION: |
| dfilter_fail("The function s was unexpected in this context."); |
| 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; |
| } |
| } |
| |
| /* 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) ::= UNPARSED(U). { E = U; } |
| entity(E) ::= range(R). { E = R; } |
| |
| |
| /* Ranges */ |
| range(R) ::= FIELD(F) LBRACKET drnode_list(L) RBRACKET. |
| { |
| R = stnode_new(STTYPE_RANGE, NULL); |
| sttype_range_set(R, F, 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); |
| } |
| |
| 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; } |
| |
| |
| /* 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; |
| } |
| |