Jison:为AND和OR生成带有多个子节点的AST节点

时间:2015-07-10 13:53:01

标签: javascript sql parsing jison

我正在使用JavaScript生成一个简单的SQL to Mongo查询条件。 我正在使用Jison来解析SQL的where子句。

以下语法以二叉树的形式返回AST,其中OR和AND嵌套。 我想要的是获得一个AST,其中OR节点具有单个节点(平面树)中的所有项。

/* lexical grammar */
/* http://stackoverflow.com/questions/8467150/how-to-get-abstract-syntax-tree-ast-out-of-jison-parser */
%lex
%%

\s+                   /* skip whitespace */
[0-9]+("."[0-9]+)?\b                return 'NUMBER'
'AND'                 return 'AND'
'OR'                  return 'OR'
'NOT'                 return 'NOT'
'BETWEEN'             return 'BETWEEN'
L?\"(\\.|[^\\"])*\"                    return 'STRING_LITERAL'
'('                   return 'LPAREN'
')'                   return 'RPAREN'
'!='                  return 'NEQ'
'>='                  return 'GE'
'<='                  return 'LE'
'='                   return 'EQ'
'>'                   return 'GT'
'<'                   return 'LT'
'IN'                  return 'IN'
'NIN'                 return 'NIN'
'+'                   return 'PLUS'
'-'                   return 'MINUS'
','                   return 'COMMA'
[_a-zA-Z][_\.a-zA-Z0-9]{0,30}            return 'IDEN'
<<EOF>>               return 'EOF'
.                     return 'INVALID'

/lex

%left OR
%left AND
%right NOT
%left NEQ EQ
%left GT LE LT GE
$left PLUS MINUS

%start start
%% /* language grammar */

start
    :  search_condition EOF
        {return $1;}
    ;

search_condition
    : search_condition OR boolean_term
        {$$ = {
            'or': [ $1, $3 ]
            };
        }
    | boolean_term
    ;

boolean_term
    : boolean_factor
    | boolean_term AND boolean_factor
        {$$ = {
            'and': [ $1, $3 ]
            };
        }
    ;

boolean_factor
    : boolean_test
    ;

boolean_test
    : boolean_primary
    ;

boolean_primary
    : predicate
    | LPAREN search_condition RPAREN
        {$$ = $2}
    ;

predicate
    : comparison_predicate
    | in_predicate
    | nin_predicate
    | between_predicate
    ;

comparison_predicate
    : IDEN comp_op value_expression
        {$$ = {
            var: $1,
            op: $2,
            val: $3
            };
        }
    ;

value_expression
    : NUMBER
    | STRING_LITERAL
    ;

comp_op
    : EQ
    | NEQ
    | GT
    | GE
    | LT
    | LE
    ;

in_predicate
    : IDEN IN in_predicate_value
    {$$ = {
            in: $3
            };
        }
    ;

nin_predicate
    : IDEN NIN in_predicate_value
    {$$ = {
            nin: $3
            };
        }
    ;

in_predicate_value
    : LPAREN in_value_list RPAREN
    {$$ = [$2];}
    ;

in_value_list
    : in_value_list_element
        {$$ = []; $$.push($1); }
    | in_value_list COMMA in_value_list_element
        {$1.push($3); $$ = $1; }
    ;

in_value_list_element
    : value_expression
        {$$ = $1;}
    ;

between_predicate
    : IDEN BETWEEN value_expression AND value_expression
    {$$ = {
            between: {
                from: $3,
                to: $5
            }

            };
        }
    ;

当我解析以下内容时

var ast = parser.parse('a=1 OR b=2 OR c=3 OR d=4 ');

返回

{
  "or": [
    {
      "or": [
        {
          "or": [
            {
              "var": "a",
              "op": "=",
              "val": "1"
            },
            {
              "var": "b",
              "op": "=",
              "val": "2"
            }
          ]
        },
        {
          "var": "c",
          "op": "=",
          "val": "3"
        }
      ]
    },
    {
      "var": "d",
      "op": "=",
      "val": "4"
    }
  ]
}

但我希望它返回

{
  "or": [
    {
      "var": "a",
      "op": "=",
      "val": "1"
    },
    {
      "var": "b",
      "op": "=",
      "val": "2"
    },
    {
      "var": "c",
      "op": "=",
      "val": "3"
    },
    {
      "var": "d",
      "op": "=",
      "val": "4"
    }
  ]
}

使用Jison可以吗?如果是这样,需要进行哪些更改?

1 个答案:

答案 0 :(得分:1)

您只需要修复操作。

首先,更改search_condition规则中的操作,如下所示:

search_condition
    : search_condition OR boolean_term
        { $1['or'].push($3); $$ = $1; }
    | boolean_term
        { $$ = { 'or': [ $1 ] }; }
    ;

这确保search_condition始终生成or节点,即使该节点仅包含一个元素。由于基本生成创建(单数)or节点,递归生成可以自由地附加到它。

如果你想摆脱退化的or节点(在search_condition不包含OR运算符的情况下),你可以在包装器中这样做(或者直接在开始生产中):

start
    :  simplified_search_condition EOF
       { return $1; }
    ;

simplified_search_condition
    :  search_condition EOF
       { $$ = $1['or'].length == 1 ? $1['or'] : $1; }
    ;