From 0d98ae8ab165490794a2beed51e94153a3f1458c Mon Sep 17 00:00:00 2001 From: Greg Date: Wed, 6 Nov 2024 00:09:50 +0200 Subject: [PATCH] class statement --- ast_string.go | 4 ++++ class.go | 9 +++++++++ interpreter.go | 6 ++++++ parser.go | 22 ++++++++++++++++++++-- resolver.go | 5 +++++ stmt.go | 11 +++++++++++ tests/functions.lox | 2 +- 7 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 class.go diff --git a/ast_string.go b/ast_string.go index 9d7c575..fdc1690 100644 --- a/ast_string.go +++ b/ast_string.go @@ -190,3 +190,7 @@ func (as *AstStringer) visitReturnStmt(r *ReturnStmt) { r.value.accept(as) as.str.WriteString(")") } + +func (as *AstStringer) visitClassStmt(c *ClassStmt) { + fmt.Printf("(class %s)", c.name.lexeme) +} diff --git a/class.go b/class.go new file mode 100644 index 0000000..1c66b77 --- /dev/null +++ b/class.go @@ -0,0 +1,9 @@ +package main + +type Class struct { + name string +} + +func (c *Class) String() string { + return c.name +} diff --git a/interpreter.go b/interpreter.go index 85868d7..3370235 100644 --- a/interpreter.go +++ b/interpreter.go @@ -205,6 +205,12 @@ func (i *Interpreter) visitFunStmt(f *FunStmt) { i.env.define(f.name.lexeme, newFunction(f.name, f.args, f.body, i.env)) } +func (i *Interpreter) visitClassStmt(c *ClassStmt) { + i.env.define(c.name.lexeme, nil) + class := &Class{c.name.lexeme} + i.env.assign(c.name, class) + +} func (i *Interpreter) visitLambda(l *Lambda) any { return newFunction(l.name, l.args, l.body, i.env) } diff --git a/parser.go b/parser.go index 5a093eb..8b6553d 100644 --- a/parser.go +++ b/parser.go @@ -48,7 +48,8 @@ func (p *Parser) parse() ([]Stmt, error) { return stmts, errors.Join(p.errors...) } -// declaration -> varDecl | funDecl | statement +// declaration -> +// varDecl | funDecl | classDecl | statement func (p *Parser) declaration() Stmt { defer p.synchronize() if p.match(VAR) { @@ -59,6 +60,10 @@ func (p *Parser) declaration() Stmt { return p.function("function") } + if p.match(CLASS) { + return p.classDecl() + } + return p.statement() } @@ -76,10 +81,23 @@ func (p *Parser) varDecl() Stmt { return &VarStmt{name, initializer} } +// classDecl -> "class" IDENTIFIER "{" function* "}" +func (p *Parser) classDecl() Stmt { + name := p.consume(IDENTIFIER, "Expect identifier for variable") + p.consume(LEFT_BRACE, "Expect '{' after class identifier") + + methods := []FunStmt{} + for !p.isAtEnd() && !p.check(RIGHT_BRACE) { + methods = append(methods, *p.function("method")) + } + p.consume(RIGHT_BRACE, "Expect '}' after class definition") + return &ClassStmt{name, methods} +} + // funDecl -> "fun" function // function -> IDENTIFIER "(" parameters? ")" blockStmt // parameters -> IDENTIFIER ( "," IDENTIFIER )* -func (p *Parser) function(kind string) Stmt { +func (p *Parser) function(kind string) *FunStmt { name := p.consume(IDENTIFIER, fmt.Sprintf("Expect %s name.", kind)) p.consume(LEFT_PAREN, fmt.Sprintf("Expect '(' after %s name.", kind)) diff --git a/resolver.go b/resolver.go index 3dc15c7..922f4c5 100644 --- a/resolver.go +++ b/resolver.go @@ -186,3 +186,8 @@ func (r *Resolver) visitUnary(u *Unary) any { r.resolveExprs(u.right) return nil } + +func (r *Resolver) visitClassStmt(c *ClassStmt) { + r.declare(c.name) + r.define(c.name) +} diff --git a/stmt.go b/stmt.go index 99bc9b9..db2fd67 100644 --- a/stmt.go +++ b/stmt.go @@ -11,6 +11,7 @@ type StmtVisitor interface { visitWhileStmt(w *WhileStmt) visitBreakStmt(b *BreakStmt) visitReturnStmt(r *ReturnStmt) + visitClassStmt(c *ClassStmt) } type Stmt interface { @@ -49,6 +50,11 @@ type WhileStmt struct { body Stmt } +type ClassStmt struct { + name Token + methods []FunStmt +} + type BreakStmt struct{} type FunStmt struct { @@ -71,6 +77,7 @@ func (b *BlockStmt) stmt() {} func (w *WhileStmt) stmt() {} func (b *BreakStmt) stmt() {} func (r *ReturnStmt) stmt() {} +func (c *ClassStmt) stmt() {} func (p *PrintStmt) accept(v StmtVisitor) { v.visitPrintStmt(p) @@ -111,3 +118,7 @@ func (f *FunStmt) accept(v StmtVisitor) { func (r *ReturnStmt) accept(v StmtVisitor) { v.visitReturnStmt(r) } + +func (c *ClassStmt) accept(v StmtVisitor) { + v.visitClassStmt(c) +} diff --git a/tests/functions.lox b/tests/functions.lox index 1568247..d1f268c 100644 --- a/tests/functions.lox +++ b/tests/functions.lox @@ -68,4 +68,4 @@ fun thrice(fn) { thrice(fun (a) { print a; }); -print fun () { return "hello, "; }() + "world"; \ No newline at end of file +print fun () { return "hello, "; }() + "world";