You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bison语法解析:带分号表达式的子节点属性继承问题

分号敏感表达式解析问题解决方案

需求背景

开发自定义语言语法时,要求表达式末尾有无分号触发不同解析逻辑:

  • 无分号表达式(如1+2)使用OpAdd、OpMult等节点类型
  • 带分号表达式(如1+2;)使用OpAdd2、OpMult2等节点类型
  • 嵌套表达式(如1+2*3;)的所有子表达式需要继承根表达式的分号属性,即2*3应使用OpMult2而非默认的OpMult

现有语法规则(问题版本)

%%
program
    : stmt
    ;

stmt
    : assignment
    | expression
    | expression_with_semicolon
    | str 
    ;

assignment
    : KW_LET id OP_ASSIGN expression {
        $$ = Node::add<ast::OpAssignLiteral>($2, $4);
    }
    | KW_LET id OP_COLON type OP_ASSIGN expression {
        $$ = Node::add<ast::OpAssignTypeAndLiteral>($2, $4, $6);
    }
    | KW_LET id OP_COLON type {
        $$ = Node::add<ast::OpAssignType>($2, $4);
    }
    ;

id
    : IDENTIFIER { $$ = Node::add<ast::Identifier>(curtoken); }
    ;

expression
    : expression OP_PLUS expression { $$ = Node::add<ast::OpAdd>($1, $3); }
    | expression OP_MINUS expression { $$ = Node::add<ast::OpSub>($1, $3); }
    | term
    ;

expression_with_semicolon
    : expression OP_PLUS expression OP_SEMICOLON { $$ = Node::add<ast::OpAdd2>($1, $3); }
    | expression OP_MINUS expression OP_SEMICOLON { $$ = Node::add<ast::OpSub2>($1, $3); }
    | term_with_semicolon
    ;

term
    : term OP_MULT term { $$ = Node::add<ast::OpMult>($1, $3); }
    | term OP_DIVF term { $$ = Node::add<ast::OpDivF>($1, $3); }
    | factor
    ;

term_with_semicolon
    : term OP_MULT term OP_SEMICOLON { $$ = Node::add<ast::OpMult2>($1, $3); }
    | term OP_DIVF term OP_SEMICOLON { $$ = Node::add<ast::OpDivF2>($1, $3); }
    | factor OP_SEMICOLON { $$ = $1; }
    ;
    
factor
    : posneg
    | OP_LPAREN expression OP_RPAREN { $$ = $2; }
    ;

posneg
    : L_INTEGER { $$ = Node::add<ast::Integer>(curtoken); }
    | OP_PLUS factor { $$ = Node::add<ast::SignedNode>(OP_PLUS, $2); }
    | OP_MINUS factor { $$ = Node::add<ast::SignedNode>(OP_MINUS, $2); }
    ;

type
    : TYPE { $$ = Node::add<ast::Type>(curtoken); }
    ;

str
    : SSTRING OP_SEMICOLON { $$ = Node::add<ast::String>(curtoken); }
    ;
%%

当前问题

仅能处理无分号和非嵌套带分号表达式,嵌套场景失效:比如1+2*3;中的2*3会被解析为OpMult,而非期望的OpMult2。

尝试过的无效方案

  • 全局布尔标记:试图通过标记区分带分号上下文,但无法在解析规则中修改全局变量
  • 新增内部规则:添加expression_with_semicolon_inner等规则,但与现有expression规则产生冲突

可行解决方案

核心思路是通过带参数的语法规则传递分号上下文标记,让所有子表达式继承根表达式的分号属性。

修改后的语法规则

%%
program
    : stmt
    ;

stmt
    : assignment
    | expression(false)  // 无分号上下文的表达式
    | expression(true) OP_SEMICOLON  // 带分号上下文的表达式,最后匹配分号
    | str 
    ;

assignment
    : KW_LET id OP_ASSIGN expression(false) {
        $$ = Node::add<ast::OpAssignLiteral>($2, $4);
    }
    | KW_LET id OP_COLON type OP_ASSIGN expression(false) {
        $$ = Node::add<ast::OpAssignTypeAndLiteral>($2, $4, $6);
    }
    | KW_LET id OP_COLON type {
        $$ = Node::add<ast::OpAssignType>($2, $4);
    }
    ;

id
    : IDENTIFIER { $$ = Node::add<ast::Identifier>(curtoken); }
    ;

// 带上下文参数的表达式规则:$has_semicolon为布尔标记,标记当前是否处于带分号上下文
expression[has_semicolon]
    : expression[has_semicolon] OP_PLUS expression[has_semicolon] {
        // 根据上下文选择对应的节点类型
        $$ = $has_semicolon ? Node::add<ast::OpAdd2>($1, $3) : Node::add<ast::OpAdd>($1, $3);
    }
    | expression[has_semicolon] OP_MINUS expression[has_semicolon] {
        $$ = $has_semicolon ? Node::add<ast::OpSub2>($1, $3) : Node::add<ast::OpSub>($1, $3);
    }
    | term[has_semicolon]
    ;

term[has_semicolon]
    : term[has_semicolon] OP_MULT term[has_semicolon] {
        $$ = $has_semicolon ? Node::add<ast::OpMult2>($1, $3) : Node::add<ast::OpMult>($1, $3);
    }
    | term[has_semicolon] OP_DIVF term[has_semicolon] {
        $$ = $has_semicolon ? Node::add<ast::OpDivF2>($1, $3) : Node::add<ast::OpDivF>($1, $3);
    }
    | factor[has_semicolon]
    ;
    
factor[has_semicolon]
    : posneg
    | OP_LPAREN expression[has_semicolon] OP_RPAREN { $$ = $2; }
    ;

posneg
    : L_INTEGER { $$ = Node::add<ast::Integer>(curtoken); }
    | OP_PLUS factor[false] { $$ = Node::add<ast::SignedNode>(OP_PLUS, $2); }
    | OP_MINUS factor[false] { $$ = Node::add<ast::SignedNode>(OP_MINUS, $2); }
    ;

type
    : TYPE { $$ = Node::add<ast::Type>(curtoken); }
    ;

str
    : SSTRING OP_SEMICOLON { $$ = Node::add<ast::String>(curtoken); }
    ;
%%

方案说明

  1. 上下文参数传递:为expression、term、factor规则添加[has_semicolon]布尔参数,标记当前是否处于带分号的根表达式上下文
  2. 统一规则入口:在stmt中,带分号表达式通过expression(true) OP_SEMICOLON触发,无分号则用expression(false)
  3. 子表达式继承上下文:所有嵌套的子表达式会自动继承上层的has_semicolon标记,确保整个表达式树的节点类型统一
  4. 语义动作分支:每个运算符规则的语义动作根据has_semicolon的值,选择对应的节点类型(如OpAdd或OpAdd2)

该方案避免了重复定义规则,解决了上下文传递问题,能正确处理嵌套表达式的分号属性继承。

内容的提问来源于stack exchange,提问作者Jjcv Vih

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 11:27:07