blocks, env
This commit is contained in:
		
							parent
							
								
									6fad12456c
								
							
						
					
					
						commit
						124537b781
					
				
					 9 changed files with 148 additions and 47 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | glox | ||||||
|  | @ -7,11 +7,12 @@ import ( | ||||||
| 
 | 
 | ||||||
| type AstStringer struct { | type AstStringer struct { | ||||||
| 	str   strings.Builder | 	str   strings.Builder | ||||||
|  | 	stmts []Stmt | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (as AstStringer) String(stmts []Stmt) string { | func (as AstStringer) String() string { | ||||||
| 
 | 
 | ||||||
| 	for _, stmt := range stmts { | 	for _, stmt := range as.stmts { | ||||||
| 		stmt.accept(&as) | 		stmt.accept(&as) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -80,3 +81,14 @@ func (as *AstStringer) visitVarStmt(vs *VarStmt) { | ||||||
| 		as.str.WriteString(fmt.Sprintf("(var %v)", vs.name.literal)) | 		as.str.WriteString(fmt.Sprintf("(var %v)", vs.name.literal)) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (as *AstStringer) visitBlockStmt(b *BlockStmt) { | ||||||
|  | 	as.str.WriteString("(block ") | ||||||
|  | 
 | ||||||
|  | 	for _, stmt := range b.stmts { | ||||||
|  | 		stmt.accept(as) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	as.str.WriteString(")") | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										37
									
								
								env.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								env.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | type Environment struct { | ||||||
|  | 	values map[string]any | ||||||
|  | 	parent *Environment | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func newEnvironment(parent *Environment) *Environment { | ||||||
|  | 	return &Environment{values: map[string]any{}, parent: parent} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (env *Environment) get(key string) any { | ||||||
|  | 	if found, ok := env.values[key]; ok { | ||||||
|  | 		return found | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if env.parent != nil { | ||||||
|  | 		return env.parent.get(key) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (env *Environment) exists(key string) bool { | ||||||
|  | 	_, ok := env.values[key] | ||||||
|  | 
 | ||||||
|  | 	if !ok && env.parent != nil { | ||||||
|  | 		return env.parent.exists(key) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ok | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (env *Environment) set(key string, val any) { | ||||||
|  | 	env.values[key] = val | ||||||
|  | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								glox
									
										
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								glox
									
										
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										38
									
								
								glox.go
									
										
									
									
									
								
							
							
						
						
									
										38
									
								
								glox.go
									
										
									
									
									
								
							|  | @ -2,6 +2,7 @@ package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bufio" | 	"bufio" | ||||||
|  | 	"fmt" | ||||||
| 	"log" | 	"log" | ||||||
| 	"os" | 	"os" | ||||||
| ) | ) | ||||||
|  | @ -32,7 +33,7 @@ func (gl *Glox) runPrompt() { | ||||||
| 		if !scanner.Scan() { | 		if !scanner.Scan() { | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 		gl.run(scanner.Bytes(), true) | 		gl.run(scanner.Bytes()) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -43,32 +44,15 @@ func (gl *Glox) runFile(path string) { | ||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	runErrors := gl.run(file, false) | 	gl.run(file) | ||||||
| 
 |  | ||||||
| 	if len(runErrors) != 0 { |  | ||||||
| 		for _, e := range runErrors { |  | ||||||
| 			log.Print(e) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 		os.Exit(1) | func (gl *Glox) run(source []byte) { | ||||||
| 	} | 	tokens, _ := newScanner(source).scan() | ||||||
| 
 | 
 | ||||||
| } | 	stmts, _ := newParser(tokens).parse() | ||||||
| 
 | 
 | ||||||
| func (gl *Glox) run(source []byte, interactive bool) []error { | 	fmt.Println(AstStringer{stmts: stmts}) | ||||||
| 	tokens, err := newScanner(source).scan() | 
 | ||||||
| 
 | 	gl.Interpreter.interpret(stmts) | ||||||
| 	if err != nil { |  | ||||||
| 		return []error{err} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	stmts, parseErrs := newParser(tokens).parse() |  | ||||||
| 
 |  | ||||||
| 	if len(parseErrs) != 0 && !interactive { |  | ||||||
| 		return parseErrs |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	println(AstStringer{}.String(stmts)) |  | ||||||
| 
 |  | ||||||
| 	return gl.Interpreter.interpret(stmts) |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ import ( | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type Interpreter struct { | type Interpreter struct { | ||||||
| 	env    map[string]any | 	env    *Environment | ||||||
| 	errors []error | 	errors []error | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -21,7 +21,7 @@ func (re *RuntimeError) Error() string { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newInterpreter() *Interpreter { | func newInterpreter() *Interpreter { | ||||||
| 	return &Interpreter{env: make(map[string]any)} | 	return &Interpreter{env: newEnvironment(nil)} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (i *Interpreter) interpret(stmts []Stmt) []error { | func (i *Interpreter) interpret(stmts []Stmt) []error { | ||||||
|  | @ -118,27 +118,32 @@ func (i *Interpreter) visitUnary(u *Unary) any { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (i *Interpreter) visitVariable(v *Variable) any { | func (i *Interpreter) visitVariable(v *Variable) any { | ||||||
| 	if found, ok := i.env[v.name.lexeme]; ok { |  | ||||||
| 		return found |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	i.panic(&RuntimeError{v.name, fmt.Sprintf("Undefined variable '%s'.", v.name.lexeme)}) | 	val := i.env.get(v.name.lexeme) | ||||||
| 
 | 
 | ||||||
|  | 	if val == nil { | ||||||
|  | 		i.panic(&RuntimeError{v.name, fmt.Sprintf("Can't evaluate: Undefined variable '%s'.", v.name.lexeme)}) | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| func (i *Interpreter) visitAssignment(a *Assign) any { |  | ||||||
| 	if _, ok := i.env[a.variable.lexeme]; ok { |  | ||||||
| 		val := i.evaluate(a.value) |  | ||||||
| 		i.env[a.variable.lexeme] = val |  | ||||||
| 	return val | 	return val | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 	i.panic(&RuntimeError{a.variable, fmt.Sprintf("Undefined variable '%s'.", a.variable.lexeme)}) | func (i *Interpreter) visitAssignment(a *Assign) any { | ||||||
|  | 
 | ||||||
|  | 	if !i.env.exists(a.variable.lexeme) { | ||||||
|  | 		i.panic(&RuntimeError{a.variable, fmt.Sprintf("Can't assign: undefined variable '%s'.", a.variable.lexeme)}) | ||||||
| 
 | 
 | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	val := i.evaluate(a.value) | ||||||
|  | 
 | ||||||
|  | 	i.env.set(a.variable.lexeme, val) | ||||||
|  | 
 | ||||||
|  | 	return val | ||||||
|  | } | ||||||
|  | 
 | ||||||
| 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)) | ||||||
| } | } | ||||||
|  | @ -155,7 +160,19 @@ func (i *Interpreter) visitVarStmt(v *VarStmt) { | ||||||
| 		val = i.evaluate(v.initializer) | 		val = i.evaluate(v.initializer) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	i.env[v.name.lexeme] = val | 	i.env.set(v.name.lexeme, val) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (i *Interpreter) visitBlockStmt(b *BlockStmt) { | ||||||
|  | 
 | ||||||
|  | 	parentEnv := i.env | ||||||
|  | 	i.env = newEnvironment(parentEnv) | ||||||
|  | 
 | ||||||
|  | 	for _, stmt := range b.stmts { | ||||||
|  | 		stmt.accept(i) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	i.env = parentEnv | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (i *Interpreter) panic(re *RuntimeError) { | func (i *Interpreter) panic(re *RuntimeError) { | ||||||
|  |  | ||||||
							
								
								
									
										22
									
								
								parser.go
									
										
									
									
									
								
							
							
						
						
									
										22
									
								
								parser.go
									
										
									
									
									
								
							|  | @ -66,11 +66,16 @@ func (p *Parser) varDecl() Stmt { | ||||||
| 	return &VarStmt{name, initializer} | 	return &VarStmt{name, initializer} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // statement -> exprStmt | printStmt | // statement -> exprStmt | printStmt | block | ||||||
| func (p *Parser) statement() Stmt { | func (p *Parser) statement() Stmt { | ||||||
| 	if p.match(PRINT) { | 	if p.match(PRINT) { | ||||||
| 		return p.printStmt() | 		return p.printStmt() | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	if p.match(LEFT_BRACE) { | ||||||
|  | 		return p.block() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return p.exprStmt() | 	return p.exprStmt() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -88,6 +93,19 @@ func (p *Parser) printStmt() Stmt { | ||||||
| 	return &PrintStmt{expr} | 	return &PrintStmt{expr} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // block -> "{" statement* "}" | ||||||
|  | func (p *Parser) block() Stmt { | ||||||
|  | 
 | ||||||
|  | 	stmts := []Stmt{} | ||||||
|  | 	for !p.check(RIGHT_BRACE) { | ||||||
|  | 		stmts = append(stmts, p.declaration()) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	p.consume(RIGHT_BRACE, "Unclosed block: Expected '}'.") | ||||||
|  | 
 | ||||||
|  | 	return &BlockStmt{stmts} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // expression -> assignment | // expression -> assignment | ||||||
| func (p *Parser) expression() Expr { | func (p *Parser) expression() Expr { | ||||||
| 	return p.assignment() | 	return p.assignment() | ||||||
|  | @ -199,7 +217,7 @@ func (p *Parser) primary() Expr { | ||||||
| 		return &Grouping{expr} | 		return &Grouping{expr} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	p.panic(&ParseError{p.peek(), "Expect expression"}) | 	// p.panic(&ParseError{p.peek(), "Expect expression"}) | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										18
									
								
								stmt.go
									
										
									
									
									
								
							
							
						
						
									
										18
									
								
								stmt.go
									
										
									
									
									
								
							|  | @ -1,9 +1,10 @@ | ||||||
| package main | package main | ||||||
| 
 | 
 | ||||||
| type StmtVisitor interface { | type StmtVisitor interface { | ||||||
| 	visitPrintStmt(p *PrintStmt) |  | ||||||
| 	visitExprStmt(es *ExprStmt) |  | ||||||
| 	visitVarStmt(v *VarStmt) | 	visitVarStmt(v *VarStmt) | ||||||
|  | 	visitExprStmt(es *ExprStmt) | ||||||
|  | 	visitPrintStmt(p *PrintStmt) | ||||||
|  | 	visitBlockStmt(b *BlockStmt) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Stmt interface { | type Stmt interface { | ||||||
|  | @ -24,9 +25,14 @@ type VarStmt struct { | ||||||
| 	initializer Expr | 	initializer Expr | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *PrintStmt) stmt() {} | type BlockStmt struct { | ||||||
| func (es *ExprStmt) stmt() {} | 	stmts []Stmt | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (vs *VarStmt) stmt()  {} | func (vs *VarStmt) stmt()  {} | ||||||
|  | func (es *ExprStmt) stmt() {} | ||||||
|  | func (p *PrintStmt) stmt() {} | ||||||
|  | func (b *BlockStmt) stmt() {} | ||||||
| 
 | 
 | ||||||
| func (p *PrintStmt) accept(v StmtVisitor) { | func (p *PrintStmt) accept(v StmtVisitor) { | ||||||
| 	v.visitPrintStmt(p) | 	v.visitPrintStmt(p) | ||||||
|  | @ -39,3 +45,7 @@ func (se *ExprStmt) accept(v StmtVisitor) { | ||||||
| func (vs *VarStmt) accept(v StmtVisitor) { | func (vs *VarStmt) accept(v StmtVisitor) { | ||||||
| 	v.visitVarStmt(vs) | 	v.visitVarStmt(vs) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (b *BlockStmt) accept(v StmtVisitor) { | ||||||
|  | 	v.visitBlockStmt(b) | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										22
									
								
								tests/blocks.lox
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								tests/blocks.lox
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | ||||||
|  | var a = "global a"; | ||||||
|  | var b = "global b"; | ||||||
|  | var c = "global c"; | ||||||
|  | 
 | ||||||
|  | { | ||||||
|  |     var a = "outer a"; | ||||||
|  |     var b = "outer b"; | ||||||
|  |     { | ||||||
|  |         var a = "inner a"; | ||||||
|  |         print a; | ||||||
|  |         print b; | ||||||
|  |         print c; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     print a; | ||||||
|  |     print b; | ||||||
|  |     print c; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | print a; | ||||||
|  | print b; | ||||||
|  | print c; | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue