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

Jison: produce AST node with multiple children for AND and OR

我正在 JavaScript 中进行简单的 SQL 到 Mongo 查询条件生成。 我正在使用 Jison 来解析 SQL 的 where 子句。

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

/* lexical grammar */
/*  */
%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 ;}
    ;

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

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

boolean_factor
    : boolean_test
    ;

boolean_test
    : boolean_primary
    ;

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

predicate
    : comparison_predicate
    | in_predicate
    | nin_predicate
    | between_predicate
    ;

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

value_expression
    : NUMBER
    | STRING_LITERAL
    ;

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

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

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

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

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

in_value_list_element
    : value_expression
        {$$ = ;}
    ;

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

            };
        }
    ;

当我解析以下内容时

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

它returns

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

但我想要它 return

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

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

你只需要修复这些动作。

首先,将search_condition规则中的动作修改如下:

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

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

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

start
    :  simplified_search_condition EOF
       { return ; }
    ;

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