我正在为着色引擎编写一个编译器,并且每个工作正常,直到我到达语句解析部分。
我使用了用类定义的抽象语法树来完成所有工作(简化类型检查和中间代码生成)..所以我有一个祖先类ASTNode
和所有降序类例如ASTFloat
,ASTExpression
,ASTIdentifier
等等。
在.y
文件中,我能够以通用方式构建AST:
nexp:
T_LPAR nexp T_RPAR { $$ = $2; }
| nexp OP_PLUS nexp { $$ = new ASTBExpression('+', (ASTExpression*)$1, (ASTExpression*)$3); }
| nexp OP_MINUS nexp { $$ = new ASTBExpression('-', (ASTExpression*)$1, (ASTExpression*)$3); }
| nexp OP_TIMES nexp { $$ = new ASTBExpression('*', (ASTExpression*)$1, (ASTExpression*)$3); }
它工作得很好但后来我尝试以这种方式生成范围的语句(例如 if 语句的主体):我使用了一个类ASTStatements
有一个ASTNode*
列表,必须由解析器填充每个语句。
所以这种方法与此类似:
statements:
statement { if ($$ == null) $$ = new ASTStatements(); ((ASTStatements*)$$)->addStatement($1); } statements { $$->generateASM(); }
;
问题是该项应该在每个语句块中初始化一次,但我不知道该怎么做。使用if ($$ == null)
是我尝试过的黑客攻击,但它不起作用,因为yylval
可以包含任何内容。
使用Bison处理此类情况的正常/最佳方式是什么?
答案 0 :(得分:1)
尝试增强语法,如下所示:
statements: statement { $$ = new ASTStatements();
((ASTStatements*)$$)->addStatement($1); }
| statements statement { ((ASTStatements*)$$)->addStatement($2); }
不确定这是否有帮助。
答案 1 :(得分:1)
有很多理由喜欢yacc的左递归规则,但有一点你可以在输入中尽早减少。
在任何情况下,当你这样做时,你可以使用这样的模式:
statements: { $$ = new ... }
| statements statement { /* now $1 and $2 do just what you want */ }
;
答案 2 :(得分:1)
我通过生成一个语句列表而不是一个退化树来解决这个问题。所以涉及的类对象是:
ASTStatements
{
ASTStatements *m_next;
ASTStatement *m_statement;
....
public:
ASTStatements(ASTStatement *statement) // used for last one
ASTStatements(ASTStatement *stat, ASTStatements *next) // used with a next one
}
以下列方式使用.y
中的规则:
statements: /* empty */ { $$ = null; }
| statements statement { if ($1 == null) $$ = new ASTStatements($2); else $$ = new ASTStatements($2, (ASTStatements*)$1); }
实际上这是左递归的,允许语句尽快减少而不会使堆栈混乱。对于我的语言中涉及的任何其他类型的“符号列表”,我采用相同的方法。