wip on classes
This commit is contained in:
parent
0d98ae8ab1
commit
4ea80598b3
10 changed files with 206 additions and 46 deletions
|
@ -10,6 +10,18 @@ type AstStringer struct {
|
||||||
stmts []Stmt
|
stmts []Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (as *AstStringer) visitGet(g *Get) any {
|
||||||
|
as.str.WriteString(fmt.Sprintf("(get %s)", g.name.lexeme))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *AstStringer) visitSet(s *Set) any {
|
||||||
|
as.str.WriteString(fmt.Sprintf("(set %s ", s.name.lexeme))
|
||||||
|
s.obj.accept(as)
|
||||||
|
as.str.WriteString(")")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (as AstStringer) String() string {
|
func (as AstStringer) String() string {
|
||||||
|
|
||||||
for _, stmt := range as.stmts {
|
for _, stmt := range as.stmts {
|
||||||
|
|
40
callable.go
40
callable.go
|
@ -4,43 +4,3 @@ type Callable interface {
|
||||||
arity() int
|
arity() int
|
||||||
call(i *Interpreter, args ...any) (ret any)
|
call(i *Interpreter, args ...any) (ret any)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Function struct {
|
|
||||||
name Token
|
|
||||||
args []Token
|
|
||||||
body []Stmt
|
|
||||||
closure *Environment
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Function) call(i *Interpreter, args ...any) (ret any) {
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
re, ok := err.(Return)
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = re.val
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
env := newEnvironment(f.closure)
|
|
||||||
|
|
||||||
for idx, arg := range f.args {
|
|
||||||
env.define(arg.lexeme, args[idx])
|
|
||||||
}
|
|
||||||
|
|
||||||
i.executeBlock(f.body, env)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *Function) arity() int {
|
|
||||||
return len(f.args)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newFunction(name Token, args []Token, body []Stmt, env *Environment) Callable {
|
|
||||||
return &Function{name, args, body, env}
|
|
||||||
}
|
|
||||||
|
|
28
class.go
28
class.go
|
@ -1,9 +1,37 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
type Class struct {
|
type Class struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ClassInstance struct {
|
||||||
|
klass *Class
|
||||||
|
props map[string]any
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClassInstance) String() string {
|
||||||
|
return fmt.Sprintf("instance of %s", c.klass.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClassInstance) get(name string) (any, bool) {
|
||||||
|
val, ok := c.props[name]
|
||||||
|
return val, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClassInstance) set(name string, val any) {
|
||||||
|
c.props[name] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Class) arity() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Class) call(i *Interpreter, args ...any) (ret any) {
|
||||||
|
return &ClassInstance{c, map[string]any{}}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Class) String() string {
|
func (c *Class) String() string {
|
||||||
return c.name
|
return c.name
|
||||||
}
|
}
|
||||||
|
|
23
expr.go
23
expr.go
|
@ -10,6 +10,8 @@ type ExprVisitor interface {
|
||||||
visitVariable(v *Variable) any
|
visitVariable(v *Variable) any
|
||||||
visitLogical(l *Logical) any
|
visitLogical(l *Logical) any
|
||||||
visitAssignment(a *Assign) any
|
visitAssignment(a *Assign) any
|
||||||
|
visitGet(g *Get) any
|
||||||
|
visitSet(s *Set) any
|
||||||
}
|
}
|
||||||
|
|
||||||
type Expr interface {
|
type Expr interface {
|
||||||
|
@ -63,6 +65,17 @@ type Lambda struct {
|
||||||
body []Stmt
|
body []Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Get struct {
|
||||||
|
name Token
|
||||||
|
obj Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
type Set struct {
|
||||||
|
name Token
|
||||||
|
obj Expr
|
||||||
|
value Expr
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Call) expr() {}
|
func (c *Call) expr() {}
|
||||||
func (u *Unary) expr() {}
|
func (u *Unary) expr() {}
|
||||||
func (a *Assign) expr() {}
|
func (a *Assign) expr() {}
|
||||||
|
@ -72,6 +85,8 @@ func (l *Literal) expr() {}
|
||||||
func (g *Grouping) expr() {}
|
func (g *Grouping) expr() {}
|
||||||
func (v *Variable) expr() {}
|
func (v *Variable) expr() {}
|
||||||
func (l *Logical) expr() {}
|
func (l *Logical) expr() {}
|
||||||
|
func (g *Get) expr() {}
|
||||||
|
func (s *Set) expr() {}
|
||||||
|
|
||||||
func (u *Unary) accept(v ExprVisitor) any {
|
func (u *Unary) accept(v ExprVisitor) any {
|
||||||
return v.visitUnary(u)
|
return v.visitUnary(u)
|
||||||
|
@ -108,3 +123,11 @@ func (c *Call) accept(v ExprVisitor) any {
|
||||||
func (l *Lambda) accept(v ExprVisitor) any {
|
func (l *Lambda) accept(v ExprVisitor) any {
|
||||||
return v.visitLambda(l)
|
return v.visitLambda(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *Get) accept(v ExprVisitor) any {
|
||||||
|
return v.visitGet(g)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Set) accept(v ExprVisitor) any {
|
||||||
|
return v.visitSet(s)
|
||||||
|
}
|
||||||
|
|
41
function.go
Normal file
41
function.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
type Function struct {
|
||||||
|
name Token
|
||||||
|
args []Token
|
||||||
|
body []Stmt
|
||||||
|
closure *Environment
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Function) call(i *Interpreter, args ...any) (ret any) {
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
re, ok := err.(Return)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = re.val
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
env := newEnvironment(f.closure)
|
||||||
|
|
||||||
|
for idx, arg := range f.args {
|
||||||
|
env.define(arg.lexeme, args[idx])
|
||||||
|
}
|
||||||
|
|
||||||
|
i.executeBlock(f.body, env)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Function) arity() int {
|
||||||
|
return len(f.args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFunction(name Token, args []Token, body []Stmt, env *Environment) Callable {
|
||||||
|
return &Function{name, args, body, env}
|
||||||
|
}
|
|
@ -201,6 +201,42 @@ func (i *Interpreter) visitCall(c *Call) any {
|
||||||
return callable.call(i, args...)
|
return callable.call(i, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Interpreter) visitGet(g *Get) any {
|
||||||
|
|
||||||
|
object := i.evaluate(g.obj)
|
||||||
|
|
||||||
|
instance, ok := object.(*ClassInstance)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
i.panic(&RuntimeError{g.name, "Only class instances can have properties"})
|
||||||
|
}
|
||||||
|
|
||||||
|
val, ok := instance.get(g.name.lexeme)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
i.panic(&RuntimeError{g.name, fmt.Sprintf("Undefined propery %s", g.name.lexeme)})
|
||||||
|
}
|
||||||
|
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interpreter) visitSet(s *Set) any {
|
||||||
|
|
||||||
|
object := i.evaluate(s.obj)
|
||||||
|
|
||||||
|
instance, ok := object.(*ClassInstance)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
i.panic(&RuntimeError{s.name, "Only class instances have fields."})
|
||||||
|
}
|
||||||
|
|
||||||
|
value := i.evaluate(s.value)
|
||||||
|
|
||||||
|
instance.set(s.name.lexeme, value)
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Interpreter) visitFunStmt(f *FunStmt) {
|
func (i *Interpreter) visitFunStmt(f *FunStmt) {
|
||||||
i.env.define(f.name.lexeme, newFunction(f.name, f.args, f.body, i.env))
|
i.env.define(f.name.lexeme, newFunction(f.name, f.args, f.body, i.env))
|
||||||
}
|
}
|
||||||
|
|
16
parser.go
16
parser.go
|
@ -76,7 +76,7 @@ func (p *Parser) varDecl() Stmt {
|
||||||
initializer = p.expression()
|
initializer = p.expression()
|
||||||
}
|
}
|
||||||
|
|
||||||
p.consume(SEMICOLON, "Expect ';' after expression.")
|
p.consume(SEMICOLON, "Expect ';' after expression in var declaration;")
|
||||||
|
|
||||||
return &VarStmt{name, initializer}
|
return &VarStmt{name, initializer}
|
||||||
}
|
}
|
||||||
|
@ -174,7 +174,7 @@ func (p *Parser) statement() Stmt {
|
||||||
// exprStmt -> expression ";"
|
// exprStmt -> expression ";"
|
||||||
func (p *Parser) exprStmt() Stmt {
|
func (p *Parser) exprStmt() Stmt {
|
||||||
expr := p.expression()
|
expr := p.expression()
|
||||||
p.consume(SEMICOLON, "Expect ';' after expression.")
|
p.consume(SEMICOLON, "Expect ';' after statement.")
|
||||||
|
|
||||||
if expr == nil {
|
if expr == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -191,7 +191,7 @@ func (p *Parser) printStmt() Stmt {
|
||||||
p.panic(&ParseError{p.previous(), "Expect expression after 'print'"})
|
p.panic(&ParseError{p.previous(), "Expect expression after 'print'"})
|
||||||
}
|
}
|
||||||
|
|
||||||
p.consume(SEMICOLON, "Expect ';' after expression.")
|
p.consume(SEMICOLON, "Expect ';' after print expression.")
|
||||||
return &PrintStmt{expr}
|
return &PrintStmt{expr}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,7 +316,7 @@ func (p *Parser) expression() Expr {
|
||||||
return p.assignment()
|
return p.assignment()
|
||||||
}
|
}
|
||||||
|
|
||||||
// assignment -> IDENTIFIER "=" assignment | or
|
// assignment -> (call ".")? IDENTIFIER "=" assignment | or
|
||||||
func (p *Parser) assignment() Expr {
|
func (p *Parser) assignment() Expr {
|
||||||
expr := p.or()
|
expr := p.or()
|
||||||
|
|
||||||
|
@ -326,11 +326,12 @@ func (p *Parser) assignment() Expr {
|
||||||
|
|
||||||
if variable, ok := expr.(*Variable); ok {
|
if variable, ok := expr.(*Variable); ok {
|
||||||
return &Assign{variable.name, val}
|
return &Assign{variable.name, val}
|
||||||
|
} else if get, ok := expr.(*Get); ok {
|
||||||
|
return &Set{get.name, get.obj, val}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.panic(&ParseError{eq, "Invalid assignment target."})
|
p.panic(&ParseError{eq, "Invalid assignment target."})
|
||||||
}
|
}
|
||||||
|
|
||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,13 +425,16 @@ func (p *Parser) unary() Expr {
|
||||||
return p.call()
|
return p.call()
|
||||||
}
|
}
|
||||||
|
|
||||||
// call -> primary ( "(" arguments? ")" )*
|
// call -> primary ( "(" arguments? ")" | "." IDENTIFIER )*
|
||||||
func (p *Parser) call() Expr {
|
func (p *Parser) call() Expr {
|
||||||
expr := p.primary()
|
expr := p.primary()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if p.match(LEFT_PAREN) {
|
if p.match(LEFT_PAREN) {
|
||||||
expr = p.arguments(expr)
|
expr = p.arguments(expr)
|
||||||
|
} else if p.match(DOT) {
|
||||||
|
name := p.consume(IDENTIFIER, "Expect property name after '.'")
|
||||||
|
expr = &Get{name, expr}
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
13
parser_test.go
Normal file
13
parser_test.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestSimpleParser(t *testing.T) {
|
||||||
|
s := newScanner([]byte("print 1;"))
|
||||||
|
tokens, _ := s.scan()
|
||||||
|
p, _ := newParser(tokens).parse()
|
||||||
|
|
||||||
|
if p == nil {
|
||||||
|
t.Fatal("cant parse")
|
||||||
|
}
|
||||||
|
}
|
11
resolver.go
11
resolver.go
|
@ -191,3 +191,14 @@ func (r *Resolver) visitClassStmt(c *ClassStmt) {
|
||||||
r.declare(c.name)
|
r.declare(c.name)
|
||||||
r.define(c.name)
|
r.define(c.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) visitGet(g *Get) any {
|
||||||
|
r.resolveExprs(g.obj)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resolver) visitSet(s *Set) any {
|
||||||
|
r.resolveExprs(s.value)
|
||||||
|
r.resolveExprs(s.obj)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
32
tests/class.lox
Normal file
32
tests/class.lox
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
class Car {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
print Car;
|
||||||
|
|
||||||
|
var opel = Car();
|
||||||
|
|
||||||
|
env;
|
||||||
|
|
||||||
|
print opel;
|
||||||
|
|
||||||
|
opel.name = "Opel";
|
||||||
|
|
||||||
|
class Engine {}
|
||||||
|
|
||||||
|
print Engine;
|
||||||
|
|
||||||
|
var eng = Engine();
|
||||||
|
|
||||||
|
eng.power = "200hp";
|
||||||
|
|
||||||
|
opel.engine = eng;
|
||||||
|
|
||||||
|
print opel.engine.power;
|
||||||
|
|
||||||
|
|
||||||
|
opel.run = fun () {
|
||||||
|
}
|
||||||
|
|
||||||
|
opel.run();
|
Loading…
Add table
Reference in a new issue