我正在研究一种简单的过程解释脚本语言,使用ANTLR4用Java编写。只是一个爱好项目。我用ANTLR4编写了一些DSL,而词法分析器和解析器没有提出任何实际问题。通过直接从解析树解释,我得到了相当多的语言,但是当我开始添加函数时,除了速度缓慢之外,该策略开始崩溃。
所以我基于“语言实现模式:创建自己的特定领域和通用编程语言”的第10章创建了一个基于堆栈的虚拟机。我有一个适用于虚拟机的汇编程序,我正在尝试通过AST生成脚本语言生成程序集。
我无法看到的是如何检测表达式或函数结果何时未使用,以便我可以生成POP指令以丢弃操作数堆栈顶部的值。
我希望赋值语句之类的东西是表达式,以便我可以执行以下操作:
x = y = 1;
在AST中,赋值节点用符号(左值)注释,而右值来自访问赋值节点的子节点。在赋值节点访问结束时,rvalue存储在左值中,并将其重新加载回操作数堆栈,以便它可以用作表达式结果。
这会生成(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 : [ \t\r\n] -> skip;
ANYCHAR : . ;
所以我的问题是检测未使用的表达式结果的常用位置在哪里,即当表达式用作普通语句时?这是我在解析期间应该检测到的东西,然后注释AST节点?或者,在访问AST代码生成(在我的情况下是程序集生成)时,这样做是否更好?我只是看不出最好的地方。
答案 0 :(得分:0)
IMO它不是正确语法的问题,而是你如何处理AST /解析树。如果使用或不使用结果的事实可以通过检查兄弟姐妹(和父母兄弟姐妹等)来确定。例如,赋值由左值,运算符和右值组成,因此当您确定了右值时,检查前一个树节点兄弟是否是运算符。类似地,您可以检查父项是否是括号表达式(对于嵌套函数调用,分组等)。
答案 1 :(得分:0)
statement
: ...
| expression
如果您使用# ExpressionStatement
标记此案例,则可以在每个表达式语句后通过覆盖侦听器中的exitExpressionStatement()
或访问者中的visitExpressionStatement
来生成弹出窗口。