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

时间:2018-05-10 07:54:17

标签: antlr antlr4

我正在研究一种简单的过程解释脚本语言,使用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代码生成(在我的情况下是程序集生成)时,这样做是否更好?我只是看不出最好的地方。

2 个答案:

答案 0 :(得分:0)

IMO它不是正确语法的问题,而是你如何处理AST /解析树。如果使用或不使用结果的事实可以通过检查兄弟姐妹(和父母兄弟姐妹等)来确定。例如,赋值由左值,运算符和右值组成,因此当您确定了右值时,检查前一个树节点兄弟是否是运算符。类似地,您可以检查父项是否是括号表达式(对于嵌套函数调用,分组等)。

答案 1 :(得分:0)

statement
    : ...
    | expression

如果您使用# ExpressionStatement标记此案例,则可以在每个表达式语句后通过覆盖侦听器中的exitExpressionStatement()或访问者中的visitExpressionStatement来生成弹出窗口。