Как определить, что результат выражения не используется в интерпретируемом языке программирования? - PullRequest
0 голосов
/ 10 мая 2018

Я работаю над простым процедурным интерпретируемым языком сценариев, написанным на Java с использованием ANTLR4. Просто хобби проект. Я написал несколько DSL с использованием ANTLR4, и лексер и парсер не представили реальных проблем. Я получил немного работы с языком, интерпретируя напрямую из дерева разбора, но эта стратегия, кроме медленной, начала разрушаться, когда я начал добавлять функции.

Итак, я создал виртуальную машину на основе стека, основанную на главе 10 «Шаблоны языковой реализации: создание собственных доменных и общих языков программирования». У меня есть ассемблер для виртуальной машины, который работает хорошо, и сейчас я пытаюсь заставить язык сценариев генерировать ассемблер через AST.

Что-то, чего я не могу понять, это как определить, когда выражение или результат функции не используется, чтобы я мог сгенерировать инструкцию POP, чтобы отбросить значение из верхней части стека операндов.

Я хочу, чтобы такие выражения, как операторы присваивания, были выражениями, чтобы я мог делать такие вещи, как:

x = y = 1;

В AST узел назначения обозначается символом (lvalue), а значение r получается из посещения дочерних элементов узла назначения. В конце посещения узла назначения значение r сохраняется в lvalue и перезагружается обратно в стек операндов, чтобы его можно было использовать в качестве результата выражения.

Это генерирует (для 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 голосов
/ 10 мая 2018
statement
    : ...
    | expression

Если вы пометите этот случай как # ExpressionStatement, вы можете генерировать всплывающую подсказку после каждого выражения выражения, переопределив exitExpressionStatement() в слушателе или visitExpressionStatement в посетителе.

0 голосов
/ 10 мая 2018

IMO, дело не в правильной грамматике, а в том, как вы обрабатываете дерево AST / разбора. Факт, если результат используется или нет, может быть определен путем проверки братьев и сестер (и родных братьев и сестер и т. Д.). Например, присваивание присваивается lvalue, оператору и rvalue, поэтому, когда вы определили значение rvalue, проверьте соседний узел предыдущего узла дерева, если это оператор. Аналогичным образом вы можете проверить, является ли родительский элемент выражением в скобках (для вызовов вложенных функций, группировки и т. Д.).

...