[英]Java - How to handle parse tree recursion for leaf nodes?
首先,不好意思稱呼。 我真的不知道該怎么稱呼這個問題。
我正在嘗試為我的Turtle圖形程序編寫一個解析器(如果您不知道它是什么,您基本上會創建一種小型編程語言,該語言由用於指導“ Turtle”運動的命令組成。例如,輸入“ FORW 5. LEFT 45.”將使烏龜向前移動5步,然后向左旋轉45度。)
我的BNF語法如下:
<EXPR>::= <CMD><EXPR> | <EOF>
<CMD>::=
<FORW><INT><PERIOD>
| <BACK><INT><PERIOD>
| <LEFT><INT><PERIOD>
| <RIGHT><INT><PERIOD>
| <DOWN><PERIOD>
| <UP><PERIOD>
| <COLOR><HEX><PERIOD>
| <REP><INT><CMD>
| <REP><INT><QUOTE><EXPR><QUOTE>
(除<EXPR>
和<CMD>
之外,所有<-.->
都是終端[令牌]。)
REP命令將一組命令(引號內的命令)重復X次。 例如: REP 3 "FORW 5. LEFT 45."
重復命令FORW5。向左移45。3次。
這是我在解析器中實現時遇到的命令(REP)(因為它的語法中包含非終結符)。
解析樹:
// Ett syntaxträd
abstract class ParseTree {
abstract public int evaluate();
}
// Ett syntaxträd som representerar ett tal
class ExprNode extends ParseTree {
ParseTree left, right;
public ExprNode(ParseTree left, ParseTree right) {
this.left = left;
this.right = right;
}
public int evaluate() {
return 0;
}
}
// Ett syntaxträd som representerar någon av de fyra binära operationerna
class CMDNode extends ParseTree {
TokenType cmd;
int num;
String hex;
ParseTree child;
public CMDNode(TokenType cmd) {
this.cmd = cmd;
}
public CMDNode(TokenType cmd, int num) {
this.cmd = cmd;
this.num = num;
}
public CMDNode(TokenType cmd, String hex) {
this.cmd = cmd;
this.hex = hex;
}
public CMDNode(TokenType cmd, ParseTree child) {
this.cmd = cmd;
this.child = child;
}
public int evaluate() {
return 0;
}
}
(對不起,以瑞典語為准)
分析器:
/**
* En rekursiv medåknings-parser för aritmetiska uttryck.
* Se README för mer info.
*
*
*/
public class Parser {
private Lexer lexer;
/** Variabler för att kunna ge pratig förklaring av vad som händer
* i parsningen. Om man inte har behov av denna feature kan koden
* som relaterar till dessa variabler tas bort.
*/
private boolean verbose;
private int depth;
/** Om verbose är satt till sann kommer Parsern att prata en massa
* medans den gör sitt jobb.
*/
public Parser(Lexer lexer, boolean verbose) {
this.lexer = lexer;
this.verbose = verbose;
}
private void talk(String s) {
if (verbose)
System.out.printf("%"+(3*depth+1)+"s%s\n", "", s);
}
public ParseTree parse() throws SyntaxError {
// Startsymbol är Expr
depth = 0;
talk("Start parse()");
++depth;
ParseTree result = Expr();
// Borde inte finnas något kvar av indata när vi parsat ett uttryck
if (lexer.nextToken().getType() != TokenType.EOF) {
throw new SyntaxError();
}
return result;
}
private ParseTree Expr() throws SyntaxError {
//talk("Enter Expr()");
//++depth;
ParseTree result = Cmd();
//talk("[Expr()] Read cmd done");
while (lexer.peekToken().getType() == TokenType.FORW ||
lexer.peekToken().getType() == TokenType.BACK||
lexer.peekToken().getType() == TokenType.LEFT||
lexer.peekToken().getType() == TokenType.RIGHT||
lexer.peekToken().getType() == TokenType.UP||
lexer.peekToken().getType() == TokenType.DOWN||
lexer.peekToken().getType() == TokenType.COLOR||
lexer.peekToken().getType() == TokenType.REP) {
ParseTree expression = Expr();
//talk("[Expr()] Read operator " + operator);
//talk("[Expr()] Read term done");
result = new ExprNode(result, expression);
}
//--depth;
//talk("Leave Expr()");
return result;
}
private ParseTree Cmd() throws SyntaxError {
Token t = lexer.nextToken();
if(t.getType() == TokenType.FORW || t.getType() == TokenType.BACK || t.getType() == TokenType.LEFT || t.getType() == TokenType.RIGHT) {
Token num = lexer.nextToken();
if(num.getType() != TokenType.DECIMAL) {
throw new SyntaxError();
}
if(lexer.nextToken().getType() != TokenType.PERIOD) {
throw new SyntaxError();
}
return new CMDNode(t.getType(), (Integer)num.getData());
}
else if(t.getType() == TokenType.UP || t.getType() == TokenType.DOWN) {
if(lexer.nextToken().getType() != TokenType.PERIOD) {
throw new SyntaxError();
}
return new CMDNode(t.getType());
}
else if(t.getType() == TokenType.COLOR) {
Token hex = lexer.nextToken();
if(hex.getType() != TokenType.HEX) {
throw new SyntaxError();
}
if(lexer.nextToken().getType() != TokenType.PERIOD) {
throw new SyntaxError();
}
return new CMDNode(t.getType(), (String)hex.getData());
}
else if(t.getType() == TokenType.REP) {
Token num = lexer.nextToken();
if(num.getType() != TokenType.DECIMAL) {
throw new SyntaxError();
}
if(lexer.peekToken().getType() == TokenType.QUOTE) {
Expr();
}
else {
Cmd();
}
}
else {
throw new SyntaxError();
}
return null;
}
}
我添加了所有代碼,但是當我嘗試處理REP令牌時,最重要的部分(我正在努力的部分)位於解析器代碼的底部。
else if(t.getType() == TokenType.REP) {
Token num = lexer.nextToken();
if(num.getType() != TokenType.DECIMAL) {
throw new SyntaxError();
}
if(lexer.peekToken().getType() == TokenType.QUOTE) {
Expr();
}
else {
Cmd();
}
根據語法,REP可以有兩個“結果”。 它只能重復一個命令X次或一組命令X次(我在QUOTE令牌的幫助下識別了一組命令)。 但是我不知道在第二個if
和else
語句中該做什么或該寫else
。 我目前僅對Expr()
函數或Cmd()
函數添加了遞歸調用,但是我認為這是錯誤的。 我認為這將代替創建連接到當前CMDNode的節點來創建第二個“解析樹”。 我不知道該如何解決。
很抱歉,冗長而and腳的解釋,但我希望您已經了解了問題所在。
只是作為旁注,我對樹結構沒有很早的經驗,所以這個問題對您來說似乎很愚蠢:)
謝謝!
這是未經測試的偽代碼,但足以幫助您繼續開發。
if(lexer.peekToken().getType() == TokenType.QUOTE) {
// Here we know there will be one or more children and that the sequence starts and ends with a "quote command"
List<ParseTree> children = new ArrayList<>();
ParseTree child = Cmd(); // The initial "quote command" - just ignore
while ((child = Cmd()) != TokenType.QUOTE) {
// Will stop looping when the second quote is found
children.add(child);
}
return new CMDNode(t.getType(), children); // Yes, you need to create a new constructor
}
else {
// Here we know there will only one child
ParseTree child = Cmd();
return new CMDNode(t.getType(), child);
}
您還需要添加一個新的“ quote命令”:
// Existing code
else if(t.getType() == TokenType.REP) {
...
}
// New code starts here
else if(t.getType() == TokenType.QUOTE) {
// This "command" is only used when parsing the input string.
return new CMDNode(t.getType());
}
// Existing code again
else {
throw new SyntaxError();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.