我之前说过,我们将充实我们的空荡的ParseProgram方法。现在是 时候了。我们将解析return语句。第一步,就像之前的let语句一样 它们,是在ast包中定义我们可以表示的必要结构AST中的return语句。
下面是Monkey中的返回语句:
return 5;
return 10;
return add(15);
有了let语句的经验,我们很容易发现这些语句背后的结构:
return <expression>;
返回语句仅有关键字return和表达式组成。这使得ast.ReturnStatement的定义非常简单:
// ast/ast.go
type ReturnStatement struct {
Token token.Token // the 'return' token
ReturnValue Expression
}
func (rs *ReturnStatement) statementNode() {}
func (rs *ReturnStatement) TokenLiteral() string { return rs.Token.Literal }
这个节点没有你之前看过的任何东西:它有一个用于初始token的字段和一个包含要返回的表达式的ReturnValue字段。我们现在将再次跳过表达式的解析和分号,但稍后会回到这一点。statementNode和TokenLiteral方法用于完成Node和Statement接口,看起来与*ast.LetStatement上定义的方法相同。我们接下来编写的测试看起来也与let语句的测试非常相似:
// parser/parser_test.go
func TestReturnStatement(t *testing.T) {
input :=`
return 5;
return 10;
return 993322;
`
l := lexer.New(input)
p := new(l)
program := p.ParseProgram()
checkParserErrors(t,p)
if len(program.Statements) != 3 {
t.Fatalf("program.Statements does not contain 3 statements. got=%d",
len(program.Statements))
}
for _, stmt := range program.Statements {
returnStmt, ok := stmt.(*ast.ReturnStatement)
if !ok {
t.Errorf("stmt not *ast.returnStatement. got=%T", stmt)
continue
}
if returnStmt.TokenLiteral() != "return" {
t.Errorf("returnStmt.TokenLiteral not 'return', got %q",
returnStmt.TokenLiteral())
}
}
}
当然,一旦表达式解析到位,这些测试用例也必须扩展。但是没关系,测试不是一成不变的。 但事实上,他们失败了:
$ go test ./parser
--- FAIL: TestReturnStatements (0.00s)
parser_test.go:77: program.Statements does not contain 3 statements. got=0
FAIL
FAIL monkey/parser 0.007s
所以让我们改变我们的ParseProgram方法以将token.RETURN标记也考虑在内使它们通过:
// parser/parser.go
func (p *Parser) parseStatement() ast.Statement {
switch p.curToken.Type {
case token.LET:
return p.parseLetStatement()
case token.RETURN:
return p.parseReturnStatement()
default:
return nil
}
}
在向你展示之前,我可以对parseReturnStatement方法进行大量模糊处理。但是,好吧,我不会,因为它很小。没什么可模糊表达的。
// parser/parser.go
func (p *Parser) parseReturnStatement() *ast.ReturnStatement {
stmt := &ast.ReturnStatement{Token: p.curToken}
p.nextToken()
// TODO: We're skipping the expressions until we
// encounter a semicolon
for !p.curTokenIs(token.SEMICOLON) {
p.nextToken()
}
return stmt
}
我告诉过你:它很小。它仅仅做的事情就是构造了一个ast.ReturnStatement,使用当前token它作为token坐在上面。然后它通过调用nextToken()为接下来的表达式提供解析器,最后,有警察出来了。它跳过每个表达式,直到遇到分号。就是这样,我们的测试通过了:
$ go test ./parser
ok monkey/parser 0.009s
是时候再一次庆祝了!我们现在解析了Monkey语言中所有的语句。好吧:仅仅只有两个语句。let和return语句。语言的其余部分仅由表达式组成。这就是我们接下来要解析的内容。
< 2.4解析器起步:解析LET语句 | > 2.6解析表达式 |
---|