我正在研究一种为堆栈机器生成“类似汇编器”指令的编译器。 (更确切地说,我以文本形式生成Java字节码指令,我可以将其嵌入到一个文件中,然后我可以使用Jasmin将其编译为java .class文件)。我完全是出于教育目的而做的事情。
假设以下java like表达式:
a = 1+2
ANTLR将生成一个看起来像这样的解析树:
a=
|
|
+
/ \
/ \
1 2
走树后命令给我“1,2,+,a =”我转换成:
lcd 1 // push integer 1 on top of stack
lcd 2 // push integer 2 on top of stack
iadd // remove the top two numbers from stack, perform addition with those numbers, and push the result on stack
istore 0 // pop top element from stack and store it in variable space number zero.
指令以空堆栈开始,最后堆栈再次为空。这是我的编译器目前可以做的。它通过遍历ANTLR为我发布的语法树生成这些指令。
现在问题:假设我有以下表达式:
a = b = 1+2
甚至
callToSomeFunctionWhichReturnsAValue();
在第一个表达式中,生成两个“istore”-instructions,两者都试图从堆栈中弹出一个值。第一个会成功,第二个会找到一个空堆栈,所有东西都会被淹没。
另一方面,第二个示例向永不弹出的堆栈添加值。如果我在一个循环中发生这种情况,我最终会得到一个堆栈溢出(我在这里不是在讨论Q& A网站)。
当然,解决方案是添加“dup”指令(复制堆栈顶部的项目)或“pop”指令(丢弃堆栈顶部的值)。但我怎么知道何时何地?
答案 0 :(得分:2)
如果不看一些你的语法,很难确切地知道要建议什么。我在这里做了一些关于你所写的可能不正确的假设。但是,我认为你所遇到的问题通常足以解决它,即使我没有严格遵守你的问题。如果不出意外,我希望所提供的示例能够满足您的需求。
算术运算符从左到右进行计算。您似乎遇到的问题是=
中的a = b = 1 + 2
运算符是从左到右进行计算的,因为这是节点在AST中出现的顺序,也就是您访问它们的顺序。
但=
操作数表达式从右到左进行计算。您可以通过更改节点顺序使树代表这一点。但我认为更有意义的是改变树步行器以适应所需的评估顺序,同时保留AST中输入的自然顺序。
例如,假设您的AST显示如下:
=
/ \
a =
/ \
b +
/ \
1 2
按正确的顺序评估运算符的步骤如下:
=
。它知道首先评估右侧,因此它会跳过处理a
。它继续到第二个孩子=
。=
。它评估第二个孩子(+
),暂时跳过b
。 +
。现在3
在堆栈中,它是堆栈中唯一的东西。=
逻辑在最后一个孩子(+
)评估后接管。调用dup
,istore
调用b
。 3
在堆栈中。=
逻辑在最后一个孩子(=
)评估后接管。调用dup
,istore
调用a
。 3
在堆栈中。pop
。堆栈现在是空的。注意:通过此过程,每个语句 - 无论是否执行分配 - 都应该以一个且只有一个值结束到堆栈。只有语句的结尾会弹出它。没有自然价值推送的表达式,例如对void foo()
的调用,仍然需要推送一些东西,即使它是null
,0
或某些保留的void
宾语。这是早期编译阶段的工作,以确保不将这些表达式分配给。
下面是一个简短的示例标记解析器和一个树解析器,以及一些测试输入和输出,演示了如何在ANTLR(v3)中完成此操作。为简单起见,我使用了伪指令,并没有尝试优化输出。
grammar Ordered;
options
{
output = AST;
}
tokens {
COMPUNIT;
STAT;
REFID;
}
compilationUnit : statement* EOF -> ^(COMPUNIT statement*);
statement: assign_expr SEMI -> ^(STAT assign_expr)
| expr SEMI -> ^(STAT expr)
;
assign_expr: ID EQ^ (assign_expr | expr); //call recursively to keep the tree simple.
expr : add_expr;
add_expr : primary_expr (PLUS^ primary_expr)*;
primary_expr
: NUM
| (ID -> REFID[$ID.text]) //An ID expr is a reference to the thing named in ID.
| LPAR! expr RPAR!
;
SEMI: ';';
EQ : '=';
LPAR : '(';
RPAR : ')';
PLUS : '+';
ID : ('a'..'z'|'A'..'Z')+;
NUM: ('0'..'9')+;
WS : (' '|'\t'|'\f'|'\r'|'\n')+ {skip();};
tree grammar OrderedTreeParser;
options {
output = AST;
tokenVocab = Ordered;
ASTLabelType = CommonTree;
filter = true;
}
@members {
private java.util.LinkedList<String> assigningIds = new java.util.LinkedList<String>();
}
topdown
: enter_assign
;
enter_assign
: ^(EQ ID {assigningIds.push($ID.getText());} .+) //Push our ID and handle assignment during bottomup.
;
bottomup
: NUM {System.out.println("lcd " + $NUM.getText());}
| EQ
{
System.out.println("dup");
System.out.println("istore " + assigningIds.pop());
}
| PLUS {System.out.println("iadd");}
| REFID {System.out.println("iload " + $REFID.getText());}
| STAT {System.out.println("pop");}
;
import java.io.IOException;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.CommonTreeNodeStream;
import org.antlr.runtime.tree.Tree;
public class OrderedTest {
public static void main(String[] args) throws Exception {
test("a = 1;");
test("a = 1 + 2;");
test("a = b = 1 + 2;");
test("a = b = 1 + c;");
test("x = y = z = 1 + 2;");
test("1 + 2;"); //no assignment
}
private static void test(String str) throws RecognitionException, Exception, IOException {
CharStream input = new ANTLRStringStream(str);
OrderedLexer lexer = new OrderedLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
OrderedParser parser = new OrderedParser(tokens);
OrderedParser.compilationUnit_return result = parser.compilationUnit();
if (lexer.getNumberOfSyntaxErrors() > 0 || parser.getNumberOfSyntaxErrors() > 0){
throw new Exception("Syntax Errors encountered!");
}
OrderedTreeParser tparser = new OrderedTreeParser(new CommonTreeNodeStream(result.getTree()));
tparser.downup(result.getTree());
System.out.println("---------");
}
}
<强>输入强>
a = 1;
<强>输出强>
lcd 1
dup
istore a
pop
<强>输入强>
a = 1 + 2;
<强>输出强>
lcd 1
lcd 2
iadd
dup
istore a
pop
<强>输入强>
a = b = 1 + 2;
<强>输出强>
lcd 1
lcd 2
iadd
dup
istore b
dup
istore a
pop
<强>输入强>
a = b = 1 + c;
<强>输出强>
lcd 1
iload c
iadd
dup
istore b
dup
istore a
pop
<强>输入强>
x = y = z = 1 + 2;
<强>输出强>
lcd 1
lcd 2
iadd
dup
istore z
dup
istore y
dup
istore x
pop
<强>输入强>
1 + 2;
<强>输出强>
lcd 1
lcd 2
iadd
pop