blocks, env

This commit is contained in:
Greg 2024-10-05 23:27:00 +03:00
parent 6fad12456c
commit 124537b781
9 changed files with 148 additions and 47 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
glox

View file

@ -6,12 +6,13 @@ 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
View 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

Binary file not shown.

32
glox.go
View file

@ -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, interactive bool) []error { func (gl *Glox) run(source []byte) {
tokens, err := newScanner(source).scan() tokens, _ := newScanner(source).scan()
if err != nil { stmts, _ := newParser(tokens).parse()
return []error{err}
}
stmts, parseErrs := newParser(tokens).parse() fmt.Println(AstStringer{stmts: stmts})
if len(parseErrs) != 0 && !interactive { gl.Interpreter.interpret(stmts)
return parseErrs
}
println(AstStringer{}.String(stmts))
return gl.Interpreter.interpret(stmts)
} }

View file

@ -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,25 +118,30 @@ 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 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
} }
i.panic(&RuntimeError{v.name, fmt.Sprintf("Undefined variable '%s'.", v.name.lexeme)}) return val
return nil
} }
func (i *Interpreter) visitAssignment(a *Assign) any { func (i *Interpreter) visitAssignment(a *Assign) any {
if _, ok := i.env[a.variable.lexeme]; ok {
val := i.evaluate(a.value) if !i.env.exists(a.variable.lexeme) {
i.env[a.variable.lexeme] = val i.panic(&RuntimeError{a.variable, fmt.Sprintf("Can't assign: undefined variable '%s'.", a.variable.lexeme)})
return val
return nil
} }
i.panic(&RuntimeError{a.variable, fmt.Sprintf("Undefined variable '%s'.", a.variable.lexeme)}) val := i.evaluate(a.value)
return nil i.env.set(a.variable.lexeme, val)
return val
} }
func (i *Interpreter) visitPrintStmt(p *PrintStmt) { func (i *Interpreter) visitPrintStmt(p *PrintStmt) {
@ -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) {

View file

@ -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
View file

@ -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
View 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;