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": [
|
||||
"arity",
|
||||
"glox",
|
||||
"PAREN",
|
||||
"stmts"
|
||||
|
|
|
@ -71,6 +71,17 @@ func (as *AstStringer) visitLogical(l *Logical) any {
|
|||
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) {
|
||||
as.str.WriteString("(print ")
|
||||
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
|
||||
|
||||
type ExprVisitor interface {
|
||||
visitCall(c *Call) any
|
||||
visitUnary(u *Unary) any
|
||||
visitBinary(b *Binary) any
|
||||
visitLiteral(l *Literal) any
|
||||
|
@ -49,6 +50,13 @@ type Logical struct {
|
|||
right Expr
|
||||
}
|
||||
|
||||
type Call struct {
|
||||
callee Expr
|
||||
paren Token
|
||||
arguments []Expr
|
||||
}
|
||||
|
||||
func (c *Call) expr() {}
|
||||
func (u *Unary) expr() {}
|
||||
func (a *Assign) expr() {}
|
||||
func (b *Binary) expr() {}
|
||||
|
@ -84,3 +92,7 @@ func (a *Assign) accept(v ExprVisitor) any {
|
|||
func (l *Logical) accept(v ExprVisitor) any {
|
||||
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")
|
||||
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()
|
||||
},
|
||||
})
|
||||
}
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
type Interpreter struct {
|
||||
env *Environment
|
||||
globals *Environment
|
||||
errors []error
|
||||
brk bool
|
||||
}
|
||||
|
@ -23,7 +24,17 @@ func (re *RuntimeError) Error() string {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
@ -156,6 +167,36 @@ func (i *Interpreter) visitLogical(lo *Logical) any {
|
|||
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) {
|
||||
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 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
|
||||
|
|
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