package interpreter import "golox/ast" // callable is an interface for callables like functions and methods. type callable interface { arity() int call(i *Interpreter, arguments []any) any } // nativeCallable is a struct that implements the callable interface. type nativeCallable struct { arityFn func() int callFn func(interpreter *Interpreter, args []any) any } // newNativeCallable creates a new nativeCallable. func newNativeCallable(arityFn func() int, callFn func(interpreter *Interpreter, args []any) any) *nativeCallable { return &nativeCallable{ arityFn: arityFn, callFn: callFn, } } // arity returns the number of arguments the callable expects. func (n *nativeCallable) arity() int { return n.arityFn() } // call calls the callable with the given arguments. func (n *nativeCallable) call(i *Interpreter, arguments []any) any { return n.callFn(i, arguments) } // String returns a string representation of the callable. func (n *nativeCallable) String() string { return "" } // function is a struct that implements the callable interface. type function struct { declaration *ast.FunctionStmt } // newFunction creates a new function. func newFunction(declaration *ast.FunctionStmt) *function { return &function{ declaration: declaration, } } // arity returns the number of arguments the function expects. func (f *function) arity() int { return len(f.declaration.Params) } // call calls the function with the given arguments. func (f *function) call(i *Interpreter, arguments []any) (result any) { env := newEnvironment(i.globals) for i, param := range f.declaration.Params { env.define(param.Lexeme, arguments[i]) } defer func() { if r := recover(); r != nil { if e, ok := r.(ReturnValue); ok { result = e.Value } else { panic(r) } } }() i.executeBlock(f.declaration.Body, env) return nil } // String returns a string representation of the function. func (f *function) String() string { return "" }