Skip to content

Commit

Permalink
Improve LoxFunction performances (#7)
Browse files Browse the repository at this point in the history
* `return` implementation without using `panic()`

* Fix lint error
  • Loading branch information
fpotier authored Aug 17, 2024
1 parent e5c77c5 commit 2856a23
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 28 deletions.
23 changes: 13 additions & 10 deletions go/pkg/runtime/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type Interpreter struct {
HadRuntimeError bool
ErrorFormatter loxerror.ErrorFormatter
OutputStream io.Writer
hasReturned bool
frameNumber int
globals *Environment
environment *Environment
locals map[ast.Expression]int
Expand All @@ -28,6 +30,8 @@ func NewInterpreter(outputStream io.Writer, errorFormatter loxerror.ErrorFormatt
HadRuntimeError: false,
ErrorFormatter: errorFormatter,
OutputStream: outputStream,
hasReturned: false,
frameNumber: 0,
globals: NewEnvironment(),
environment: nil,
locals: make(map[ast.Expression]int),
Expand Down Expand Up @@ -128,7 +132,9 @@ func (i *Interpreter) VisitCallExpression(callExpression *ast.CallExpression) {
if function.Arity() != len(arguments) {
panic(NewBadArity(callExpression.Position.Line, function.Name(), function.Arity(), len(arguments)))
}
i.frameNumber++
i.Value = function.Call(i, arguments)
i.frameNumber--
} else {
panic(NewNotCallable(callExpression.Position.Line))
}
Expand Down Expand Up @@ -271,12 +277,12 @@ func (i *Interpreter) VisitPrintStatement(printStatement *ast.PrintStatement) {
}

func (i *Interpreter) VisitReturnStatement(returnStatement *ast.ReturnStatement) {
var value ast.LoxValue = ast.NewNilValue()
i.hasReturned = true
if returnStatement.Value != nil {
value = i.evaluate(returnStatement.Value)
i.Value = i.evaluate(returnStatement.Value)
} else {
i.Value = ast.NewNilValue()
}

panic(value)
}

func (i *Interpreter) VisitVariableStatement(variableStatement *ast.VariableStatement) {
Expand All @@ -289,7 +295,7 @@ func (i *Interpreter) VisitVariableStatement(variableStatement *ast.VariableStat
}

func (i *Interpreter) VisitWhileStatement(whileStatement *ast.WhileStatement) {
for i.evaluate(whileStatement.Condition).IsTruthy() {
for i.evaluate(whileStatement.Condition).IsTruthy() && !(i.frameNumber > 0 && i.hasReturned) {
i.execute(whileStatement.Body)
}
}
Expand All @@ -298,12 +304,9 @@ func (i *Interpreter) executeBlock(statements []ast.Statement, subEnvironment *E
previousEnv := i.environment
i.environment = subEnvironment
defer func() { i.environment = previousEnv }()
// TODO error handling
for _, statement := range statements {
i.execute(statement)
for index := 0; index < len(statements) && !(i.frameNumber > 0 && i.hasReturned); index++ {
i.execute(statements[index])
}

i.environment = previousEnv
}

func (i *Interpreter) execute(statement ast.Statement) {
Expand Down
23 changes: 5 additions & 18 deletions go/pkg/runtime/loxfunction.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,36 +33,23 @@ func (f LoxFunction) Equals(v ast.LoxValue) bool {
}
return false
}

func (f LoxFunction) Call(i *Interpreter, arguments []ast.LoxValue) (returnValue ast.LoxValue) {
environment := NewSubEnvironment(f.Closure)
for i := range f.Declaration.Parameters {
environment.Define(f.Declaration.Parameters[i].Lexeme, arguments[i])
}

// If no return statement is executed 'nil' is returned
returnValue = ast.NewNilValue()

defer func() {
if r := recover(); r != nil {
if rt, ok := r.(ast.LoxValue); ok {
if f.isConstructor {
returnValue = f.Closure.GetAt(0, "this")
} else {
returnValue = rt
}
return
}
panic(r)
}
}()

hasReturned := i.hasReturned
i.hasReturned = false
i.executeBlock(f.Declaration.Body, environment)
i.hasReturned = hasReturned

if f.isConstructor {
return f.Closure.GetAt(0, "this")
}

return returnValue
return i.Value
}
func (f LoxFunction) Arity() int { return len(f.Declaration.Parameters) }

Expand Down

0 comments on commit 2856a23

Please sign in to comment.