如何检测解释型编程语言中未使用的表达式结果?



我正在研究一种简单的过程解释脚本语言,使用ANTLR4用Java编写。只是一个爱好项目。我已经使用ANTLR4编写了一些DSL,词法器和解析器没有出现真正的问题。通过直接从解析树进行解释,我得到了相当多的语言工作,但是当我开始添加函数时,这种策略除了速度慢之外,开始崩溃。

因此,我基于"语言实现模式:创建自己的特定于域的通用编程语言"的第 10 章创建了一个基于堆栈的虚拟机。我有一个运行良好的 VM 汇编程序,我现在正在尝试使脚本语言通过 AST 生成程序集。

我不太明白的是,如何检测表达式或函数结果何时未使用,以便我可以生成 POP 指令以丢弃操作数堆栈顶部的值。

我希望赋值语句之类的东西是表达式,这样我就可以做这样的事情:

x = y = 1;

在 AST 中,赋值节点用符号(左值(进行批注,右值来自访问赋值节点的子节点。在赋值节点访问结束时,右值将存储到左值中,并将其重新加载回操作数堆栈中,以便可以将其用作表达式结果。

这将生成(对于x = y = 1(:

CLOAD 1    ; Push constant value
GSTOR y    ; Store into global y and pop
GLOAD y    ; Push value of y
GSTOR x    ; Store into global x and pop
GLOAD x    ; Push value of x 

但它需要在最后使用 POP 指令来丢弃结果,否则操作数堆栈会随着这些未使用的结果而开始增长。我看不出最好的方法。

我想我的语法可能有缺陷,这阻止了我在这里看到解决方案。

grammar g;
// ----------------------------------------------------------------------------
// Parser
// ----------------------------------------------------------------------------
parse
: (functionDefinition | compoundStatement)*
;
functionDefinition
: FUNCTION ID parameterSpecification compoundStatement
;
parameterSpecification
: '(' (ID (',' ID)*)? ')'
;
compoundStatement
: '{' compoundStatement* '}'
| conditionalStatement
| iterationStatement
| statement ';'
;
statement
: declaration
| expression
| exitStatement
| printStatement
| returnStatement
;
declaration
: LET ID ASSIGN expression                                                  # ConstantDeclaration
| VAR ID ASSIGN expression                                                  # VariableDeclaration
;
conditionalStatement
: ifStatement
;
ifStatement
: IF expression compoundStatement (ELSE compoundStatement)?
;
exitStatement
: EXIT
;
iterationStatement
: WHILE expression compoundStatement                                        # WhileStatement
| DO compoundStatement WHILE expression                                     # DoStatement
| FOR ID IN expression TO expression (STEP expression)? compoundStatement   # ForStatement
;
printStatement
: PRINT '(' (expression (',' expression)*)? ')'                             # SimplePrintStatement
| PRINTF '(' STRING (',' expression)* ')'                                   # PrintFormatStatement
;
returnStatement
: RETURN expression?
;
expression
: expression '[' expression ']'                                             # Indexed
| ID DEFAULT expression                                                     # DefaultValue
| ID op=(INC | DEC)                                                         # Postfix
| op=(ADD | SUB | NOT) expression                                           # Unary
| op=(INC | DEC) ID                                                         # Prefix
| expression op=(MUL | DIV | MOD) expression                                # Multiplicative
| expression op=(ADD | SUB) expression                                      # Additive
| expression op=(GT | GE | LT | LE) expression                              # Relational
| expression op=(EQ | NE) expression                                        # Equality
| expression AND expression                                                 # LogicalAnd
| expression OR expression                                                  # LogicalOr
| expression IF expression ELSE expression                                  # Ternary
| ID '(' (expression (',' expression)*)? ')'                                # FunctionCall
| '(' expression ')'                                                        # Parenthesized
| '[' (expression (',' expression)* )? ']'                                  # LiteralArray
| ID                                                                        # Identifier
| NUMBER                                                                    # LiteralNumber
| STRING                                                                    # LiteralString
| BOOLEAN                                                                   # LiteralBoolean
| ID ASSIGN expression                                                      # SimpleAssignment
| ID op=(CADD | CSUB | CMUL | CDIV) expression                              # CompoundAssignment
| ID '[' expression ']' ASSIGN expression                                   # IndexedAssignment
;
// ----------------------------------------------------------------------------
// Lexer
// ----------------------------------------------------------------------------
fragment
IDCHR           : [A-Za-z_$];
fragment
DIGIT           : [0-9];
fragment
ESC             : '\' ["\];
COMMENT         : '#' .*? 'n' -> skip;
// ----------------------------------------------------------------------------
// Keywords
// ----------------------------------------------------------------------------
DO              : 'do';
ELSE            : 'else';
EXIT            : 'exit';
FOR             : 'for';
FUNCTION        : 'function';
IF              : 'if';
IN              : 'in';
LET             : 'let';
PRINT           : 'print';
PRINTF          : 'printf';
RETURN          : 'return';
STEP            : 'step';
TO              : 'to';
VAR             : 'var';
WHILE           : 'while';
// ----------------------------------------------------------------------------
// Operators
// ----------------------------------------------------------------------------
ADD             : '+';
DIV             : '/';
MOD             : '%';
MUL             : '*';
SUB             : '-';
DEC             : '--';
INC             : '++';
ASSIGN          : '=';
CADD            : '+=';
CDIV            : '/=';
CMUL            : '*=';
CSUB            : '-=';
GE              : '>=';
GT              : '>';
LE              : '<=';
LT              : '<';
AND             : '&&';
EQ              : '==';
NE              : '!=';
NOT             : '!';
OR              : '||';
DEFAULT         : '??';
// ----------------------------------------------------------------------------
// Literals and identifiers
// ----------------------------------------------------------------------------
BOOLEAN         : ('true'|'false');
NUMBER          : DIGIT+ ('.' DIGIT+)?;
STRING          : '"' (ESC | .)*? '"';
ID              : IDCHR (IDCHR | DIGIT)*;
WHITESPACE      : [ trn] -> skip;
ANYCHAR         : . ;

所以我的问题是检测未使用的表达式结果的通常位置在哪里,即当表达式用作纯语句时?我应该在解析过程中检测到它,然后注释 AST 节点吗?还是在访问 AST 进行代码生成(在我的情况下为程序集生成(时这样做更好?我只是看不出在哪里最好。

IMO 这不是正确的语法问题,而是如何处理 AST/parse 树的问题。是否使用结果的事实可以通过检查兄弟姐妹(和父母兄弟姐妹等(来确定。例如,由左值、运算符和右值进行赋值,因此当您确定右值时,请检查上一个树节点同级是否是运算符。同样,您可以检查父项是否是括号表达式(用于嵌套函数调用、分组等(。

statement
: ...
| expression

如果使用# ExpressionStatement标记此大小写,则可以通过覆盖侦听器中的exitExpressionStatement()或访问者中的visitExpressionStatement,在每个表达式语句之后生成弹出

相关内容

  • 没有找到相关文章

最新更新