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 {
|
||||
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
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.
32
glox.go
32
glox.go
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
22
parser.go
22
parser.go
|
@ -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
18
stmt.go
|
@ -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
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…
Reference in a new issue