functions
This commit is contained in:
parent
e8aeb6d5c5
commit
a6e0673c3b
9 changed files with 136 additions and 5 deletions
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
|
"arity",
|
||||||
"glox",
|
"glox",
|
||||||
"PAREN",
|
"PAREN",
|
||||||
"stmts"
|
"stmts"
|
||||||
|
|
|
@ -71,6 +71,17 @@ func (as *AstStringer) visitLogical(l *Logical) any {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (as *AstStringer) visitCall(c *Call) any {
|
||||||
|
as.str.WriteString("(call ")
|
||||||
|
c.callee.accept(as)
|
||||||
|
for _, arg := range c.arguments {
|
||||||
|
arg.accept(as)
|
||||||
|
}
|
||||||
|
as.str.WriteString(")")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (as *AstStringer) visitPrintStmt(p *PrintStmt) {
|
func (as *AstStringer) visitPrintStmt(p *PrintStmt) {
|
||||||
as.str.WriteString("(print ")
|
as.str.WriteString("(print ")
|
||||||
p.val.accept(as)
|
p.val.accept(as)
|
||||||
|
|
6
callable.go
Normal file
6
callable.go
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
type Callable struct {
|
||||||
|
arity int
|
||||||
|
call func(*Interpreter, ...any) any
|
||||||
|
}
|
12
expr.go
12
expr.go
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
type ExprVisitor interface {
|
type ExprVisitor interface {
|
||||||
|
visitCall(c *Call) any
|
||||||
visitUnary(u *Unary) any
|
visitUnary(u *Unary) any
|
||||||
visitBinary(b *Binary) any
|
visitBinary(b *Binary) any
|
||||||
visitLiteral(l *Literal) any
|
visitLiteral(l *Literal) any
|
||||||
|
@ -49,6 +50,13 @@ type Logical struct {
|
||||||
right Expr
|
right Expr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Call struct {
|
||||||
|
callee Expr
|
||||||
|
paren Token
|
||||||
|
arguments []Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Call) expr() {}
|
||||||
func (u *Unary) expr() {}
|
func (u *Unary) expr() {}
|
||||||
func (a *Assign) expr() {}
|
func (a *Assign) expr() {}
|
||||||
func (b *Binary) expr() {}
|
func (b *Binary) expr() {}
|
||||||
|
@ -84,3 +92,7 @@ func (a *Assign) accept(v ExprVisitor) any {
|
||||||
func (l *Logical) accept(v ExprVisitor) any {
|
func (l *Logical) accept(v ExprVisitor) any {
|
||||||
return v.visitLogical(l)
|
return v.visitLogical(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Call) accept(v ExprVisitor) any {
|
||||||
|
return v.visitCall(c)
|
||||||
|
}
|
||||||
|
|
|
@ -61,3 +61,12 @@ func (as *ExprToRPN) visitLogical(lo *Logical) any {
|
||||||
as.str.WriteString(" or")
|
as.str.WriteString(" or")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (as *ExprToRPN) visitCall(c *Call) any {
|
||||||
|
for _, arg := range c.arguments {
|
||||||
|
arg.accept(as)
|
||||||
|
}
|
||||||
|
c.callee.accept(as)
|
||||||
|
as.str.WriteString(" call")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
13
globals.go
Normal file
13
globals.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
func defineGlobals(env *Environment) {
|
||||||
|
|
||||||
|
env.set("clock", &Callable{
|
||||||
|
arity: 0,
|
||||||
|
call: func(i *Interpreter, arg ...any) any {
|
||||||
|
return time.Now().Unix()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
|
@ -8,9 +8,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Interpreter struct {
|
type Interpreter struct {
|
||||||
env *Environment
|
env *Environment
|
||||||
errors []error
|
globals *Environment
|
||||||
brk bool
|
errors []error
|
||||||
|
brk bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuntimeError struct {
|
type RuntimeError struct {
|
||||||
|
@ -23,7 +24,17 @@ func (re *RuntimeError) Error() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newInterpreter() *Interpreter {
|
func newInterpreter() *Interpreter {
|
||||||
return &Interpreter{env: newEnvironment(nil), errors: []error{}, brk: false}
|
|
||||||
|
globals := newEnvironment(nil)
|
||||||
|
|
||||||
|
defineGlobals(globals)
|
||||||
|
|
||||||
|
return &Interpreter{
|
||||||
|
env: globals,
|
||||||
|
globals: globals,
|
||||||
|
errors: []error{},
|
||||||
|
brk: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Interpreter) interpret(stmts []Stmt) []error {
|
func (i *Interpreter) interpret(stmts []Stmt) []error {
|
||||||
|
@ -156,6 +167,36 @@ func (i *Interpreter) visitLogical(lo *Logical) any {
|
||||||
return i.evaluate(lo.right)
|
return i.evaluate(lo.right)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Interpreter) visitCall(c *Call) any {
|
||||||
|
|
||||||
|
callee := i.evaluate(c.callee)
|
||||||
|
|
||||||
|
args := []any{}
|
||||||
|
|
||||||
|
for _, arg := range c.arguments {
|
||||||
|
args = append(args, i.evaluate(arg))
|
||||||
|
}
|
||||||
|
|
||||||
|
callable, ok := callee.(*Callable)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
i.panic(&RuntimeError{c.paren, "Can only call function and classes."})
|
||||||
|
}
|
||||||
|
|
||||||
|
if callable.arity != len(args) {
|
||||||
|
i.panic(&RuntimeError{
|
||||||
|
c.paren,
|
||||||
|
fmt.Sprintf(
|
||||||
|
"Expected %d arguments but got %d",
|
||||||
|
callable.arity,
|
||||||
|
len(args),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return callable.call(i, args...)
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Interpreter) visitPrintStmt(p *PrintStmt) {
|
func (i *Interpreter) visitPrintStmt(p *PrintStmt) {
|
||||||
fmt.Printf("%v\n", i.evaluate(p.val))
|
fmt.Printf("%v\n", i.evaluate(p.val))
|
||||||
}
|
}
|
||||||
|
|
36
parser.go
36
parser.go
|
@ -346,7 +346,41 @@ func (p *Parser) unary() Expr {
|
||||||
return &Unary{op, right}
|
return &Unary{op, right}
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.primary()
|
return p.call()
|
||||||
|
}
|
||||||
|
|
||||||
|
// call -> primary ( "(" arguments? ")" )*
|
||||||
|
func (p *Parser) call() Expr {
|
||||||
|
expr := p.primary()
|
||||||
|
|
||||||
|
for {
|
||||||
|
if p.match(LEFT_PAREN) {
|
||||||
|
expr = p.arguments(expr)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
|
// arguments -> expression ( "," expression )*
|
||||||
|
func (p *Parser) arguments(callee Expr) Expr {
|
||||||
|
arguments := []Expr{}
|
||||||
|
|
||||||
|
if !p.check(RIGHT_PAREN) {
|
||||||
|
for {
|
||||||
|
arguments = append(arguments, p.expression())
|
||||||
|
|
||||||
|
if !p.match(COMMA) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
paren := p.consume(RIGHT_PAREN, "Expect ')' after arguments.")
|
||||||
|
|
||||||
|
return &Call{callee, paren, arguments}
|
||||||
}
|
}
|
||||||
|
|
||||||
// primary -> NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" | IDENTIFIER
|
// primary -> NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" | IDENTIFIER
|
||||||
|
|
4
tests/functions.lox
Normal file
4
tests/functions.lox
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
print "functions test";
|
||||||
|
|
||||||
|
print clock();
|
Loading…
Reference in a new issue