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