return语句支持
在这一篇文章中,我们要加入对于return语句的处理,为将来要说的函数提供返回值支持。
我们需要做如下的更改:
- 在词元(Token)源码
token.go中加入新的词元(Token)类型 - 在抽象语法树(AST)的源码
ast.go中加入return语句对应的抽象语法表示。 - 在语法解析器(Parser)的源码
parser.go中加入对return语句的语法解析。 - 在对象(Object)源码
object.go中加入新的对象类型(返回值对象) - 在解释器(Evaluator)的源码
eval.go中加入对return语句的解释。
词元(Token)更改
第一处改动
//token.go
const (
//...
//reserved keywords
TOKEN_RETURN //return
)
第6行,我们加入了一个新的词元(Token)类型。
第二处改动
//token.go
//词元类型的字符串表示
func (tt TokenType) String() string {
switch tt {
//...
case TOKEN_RETURN:
return "RETURN"
}
}
第三处改动
//token.go
//关键字map
var keywords = map[string]TokenType{
//...
"return": TOKEN_RETURN,
}
抽象语法树(AST)的更改
我们的return语句,需要什么信息呢?
- 词元信息(我们的老朋友了,调试、输出或者报错用)
- 返回值表达式
//ast.go
//return语句
type ReturnStatement struct {
Token token.Token // the 'return' token
ReturnValue Expression //返回的表达式。如果为nil,表明不返回任何值
}
//开始位置
func (rs *ReturnStatement) Pos() token.Position {
return rs.Token.Pos
}
//结束位置
func (rs *ReturnStatement) End() token.Position {
if rs.ReturnValue == nil { //如果没有返回值
length := utf8.RuneCountInString(rs.Token.Literal)
pos := rs.Token.Pos
return token.Position{Filename: pos.Filename, Line: pos.Line, Col: pos.Col + length}
}
return rs.ReturnValue.End()
}
//表面'return'是一个语句
func (rs *ReturnStatement) statementNode() {}
func (rs *ReturnStatement) TokenLiteral() string { return rs.Token.Literal }
//'return'语句的字符串表示
func (rs *ReturnStatement) String() string {
var out bytes.Buffer
out.WriteString(rs.TokenLiteral() + " ")
if rs.ReturnValue != nil {
out.WriteString(rs.ReturnValue.String())
}
out.WriteString("; ")
return out.String()
}
这都是读者比较熟悉的代码了,所以也没有太多需要解释的。
语法解析器(Parser)的更改
我们需要做两处更改:
- 在
parseStatement()函数的switch分支中加入对词元类型为TOKEN_RETURN的判断 - 增加解析
return语句的函数
//parser.go
func (p *Parser) parseStatement() ast.Statement {
switch p.curToken.Type {
//...
case token.TOKEN_RETURN:
return p.parseReturnStatement()
}
}
//解析'return'语句:
// 1. return <expression>;
// 2. return;
func (p *Parser) parseReturnStatement() *ast.ReturnStatement {
stmt := &ast.ReturnStatement{Token: p.curToken}
if p.peekTokenIs(token.TOKEN_SEMICOLON) { //e.g.{ return; }
p.nextToken()
return stmt
}
p.nextToken()
stmt.ReturnValue = p.parseExpressionStatement().Expression
return stmt
}
我们在ParseStatement()函数的switch分支中增加了对词元类型为TOKEN_RETURN的判断(第5-6行)。
同时增加了对return语句的解析(代码14-25行)。
对象(Object)的更改
由于我们新增了一个return类型,我们就需要在对象(Object)系统中加入对这个新的类型的支持。
- 新增一个对象类型(Object Type)
- 实现这个新增对象类型(即实现
object接口的方法)
新增一个对象类型(Object Type)
//object.go
const (
//...
RETURN_VALUE_OBJ = "RETURN_VALUE"
)
实现return对象类型(即实现object接口的方法)
//object.go
//'return'对象
type ReturnValue struct {
Value Object
}
func (rv *ReturnValue) Type() ObjectType { return RETURN_VALUE_OBJ }
func (rv *ReturnValue) Inspect() string { return rv.Value.Inspect() }
内容也比较简单,就不多解释了。
解释器(Evaluator)的更改
我们需要在解释器(Evaluator)的Eval函数的switch分支中加入对return类型的处理:
//eval.go
func Eval(node ast.Node, scope *Scope) (val Object) {
switch node := node.(type) {
//...
case *ast.ReturnStatement:
if node.ReturnValue == nil {
return &ReturnValue{Value: NIL} //如果没有返回值,我们默认返回NIL对象
}
val := Eval(node.ReturnValue, scope)
return &ReturnValue{Value: val} //返回'return'对象
}
return nil
}
我们还剩下一个工作,在解释程序(Program)节点的函数中(parseProgram),我们也需要对返回值进行处理:
//eval.go
//解释程序节点
func evalProgram(program *ast.Program, scope *Scope) (results Object) {
for _, stmt := range program.Statements {
results = Eval(stmt, scope)
if returnValue, ok := results.(*ReturnValue); ok {
return returnValue.Value //返回'return'对象中保存的返回值
}
}
if results == nil {
return NIL
}
return results
}
上面代码的第7-9行为新增代码,如果处理的语句的返回值为return对象的时候,就返回return对象中存储的值。
至此,对return语句的支持就完成了。后续的章节我们会扩展这个return语句,使其支持多个返回值。
下一节,我们将加入对块语句(block statement)的支持。