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

@ -7,11 +7,12 @@ import (
type AstStringer struct {
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)
}
@ -80,3 +81,14 @@ func (as *AstStringer) visitVarStmt(vs *VarStmt) {
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 (
"bufio"
"fmt"
"log"
"os"
)
@ -32,7 +33,7 @@ func (gl *Glox) runPrompt() {
if !scanner.Scan() {
break
}
gl.run(scanner.Bytes(), true)
gl.run(scanner.Bytes())
}
}
@ -43,32 +44,15 @@ func (gl *Glox) runFile(path string) {
log.Fatal(err)
}
runErrors := gl.run(file, false)
if len(runErrors) != 0 {
for _, e := range runErrors {
log.Print(e)
}
os.Exit(1)
}
gl.run(file)
}
func (gl *Glox) run(source []byte, interactive bool) []error {
tokens, err := newScanner(source).scan()
func (gl *Glox) run(source []byte) {
tokens, _ := newScanner(source).scan()
if err != nil {
return []error{err}
}
stmts, _ := newParser(tokens).parse()
stmts, parseErrs := newParser(tokens).parse()
fmt.Println(AstStringer{stmts: stmts})
if len(parseErrs) != 0 && !interactive {
return parseErrs
}
println(AstStringer{}.String(stmts))
return gl.Interpreter.interpret(stmts)
gl.Interpreter.interpret(stmts)
}

View file

@ -7,7 +7,7 @@ import (
)
type Interpreter struct {
env map[string]any
env *Environment
errors []error
}
@ -21,7 +21,7 @@ func (re *RuntimeError) Error() string {
}
func newInterpreter() *Interpreter {
return &Interpreter{env: make(map[string]any)}
return &Interpreter{env: newEnvironment(nil)}
}
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 {
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 nil
return val
}
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
}
i.panic(&RuntimeError{a.variable, fmt.Sprintf("Undefined variable '%s'.", a.variable.lexeme)})
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
}
val := i.evaluate(a.value)
i.env.set(a.variable.lexeme, val)
return val
}
func (i *Interpreter) visitPrintStmt(p *PrintStmt) {
@ -155,7 +160,19 @@ func (i *Interpreter) visitVarStmt(v *VarStmt) {
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) {

View file

@ -66,11 +66,16 @@ func (p *Parser) varDecl() Stmt {
return &VarStmt{name, initializer}
}
// statement -> exprStmt | printStmt
// statement -> exprStmt | printStmt | block
func (p *Parser) statement() Stmt {
if p.match(PRINT) {
return p.printStmt()
}
if p.match(LEFT_BRACE) {
return p.block()
}
return p.exprStmt()
}
@ -88,6 +93,19 @@ func (p *Parser) printStmt() Stmt {
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
func (p *Parser) expression() Expr {
return p.assignment()
@ -199,7 +217,7 @@ func (p *Parser) primary() Expr {
return &Grouping{expr}
}
p.panic(&ParseError{p.peek(), "Expect expression"})
// p.panic(&ParseError{p.peek(), "Expect expression"})
return nil
}

18
stmt.go
View file

@ -1,9 +1,10 @@
package main
type StmtVisitor interface {
visitPrintStmt(p *PrintStmt)
visitExprStmt(es *ExprStmt)
visitVarStmt(v *VarStmt)
visitExprStmt(es *ExprStmt)
visitPrintStmt(p *PrintStmt)
visitBlockStmt(b *BlockStmt)
}
type Stmt interface {
@ -24,9 +25,14 @@ type VarStmt struct {
initializer Expr
}
func (p *PrintStmt) stmt() {}
func (es *ExprStmt) stmt() {}
type BlockStmt struct {
stmts []Stmt
}
func (vs *VarStmt) stmt() {}
func (es *ExprStmt) stmt() {}
func (p *PrintStmt) stmt() {}
func (b *BlockStmt) stmt() {}
func (p *PrintStmt) accept(v StmtVisitor) {
v.visitPrintStmt(p)
@ -39,3 +45,7 @@ func (se *ExprStmt) accept(v StmtVisitor) {
func (vs *VarStmt) accept(v StmtVisitor) {
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;