Compare commits
10 commits
6bb2408269
...
0d98ae8ab1
Author | SHA1 | Date | |
---|---|---|---|
0d98ae8ab1 | |||
c1549bbc6d | |||
1af4030dc1 | |||
a24586e601 | |||
1117f2c104 | |||
1fdd522c8f | |||
ce0492c489 | |||
a6e0673c3b | |||
e8aeb6d5c5 | |||
69042ccf99 |
20 changed files with 1071 additions and 152 deletions
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"cSpell.words": [
|
||||
"arity",
|
||||
"glox",
|
||||
"PAREN",
|
||||
"stmts"
|
||||
|
|
|
@ -71,6 +71,43 @@ func (as *AstStringer) visitLogical(l *Logical) any {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (as *AstStringer) visitCall(c *Call) any {
|
||||
as.str.WriteString("(call ")
|
||||
c.callee.accept(as)
|
||||
if len(c.args) != 0 {
|
||||
as.str.WriteString(" ")
|
||||
}
|
||||
for i, arg := range c.args {
|
||||
arg.accept(as)
|
||||
if i < len(c.args)-1 {
|
||||
as.str.WriteString(" ")
|
||||
}
|
||||
}
|
||||
as.str.WriteString(")")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (as *AstStringer) visitLambda(l *Lambda) any {
|
||||
as.str.WriteString("(lambda ")
|
||||
if len(l.args) != 0 {
|
||||
as.str.WriteString("(")
|
||||
for i, arg := range l.args {
|
||||
as.str.WriteString(arg.lexeme)
|
||||
if i < len(l.args)-1 {
|
||||
as.str.WriteString(" ")
|
||||
}
|
||||
}
|
||||
as.str.WriteString(")")
|
||||
}
|
||||
for _, stmt := range l.body {
|
||||
stmt.accept(as)
|
||||
}
|
||||
as.str.WriteString(")")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (as *AstStringer) visitPrintStmt(p *PrintStmt) {
|
||||
as.str.WriteString("(print ")
|
||||
p.val.accept(as)
|
||||
|
@ -104,7 +141,7 @@ func (as *AstStringer) visitBlockStmt(b *BlockStmt) {
|
|||
|
||||
func (as *AstStringer) visitIfStmt(i *IfStmt) {
|
||||
as.str.WriteString("(if ")
|
||||
i.expr.accept(as)
|
||||
i.cond.accept(as)
|
||||
as.str.WriteString(" ")
|
||||
i.then.accept(as)
|
||||
if i.or != nil {
|
||||
|
@ -125,3 +162,35 @@ func (as *AstStringer) visitWhileStmt(w *WhileStmt) {
|
|||
w.body.accept(as)
|
||||
as.str.WriteString(")")
|
||||
}
|
||||
|
||||
func (as *AstStringer) visitBreakStmt(b *BreakStmt) {
|
||||
as.str.WriteString("(break)")
|
||||
}
|
||||
|
||||
func (as *AstStringer) visitFunStmt(f *FunStmt) {
|
||||
as.str.WriteString(fmt.Sprintf("(fun %s ", f.name.lexeme))
|
||||
if len(f.args) != 0 {
|
||||
as.str.WriteString("(")
|
||||
for i, arg := range f.args {
|
||||
as.str.WriteString(arg.lexeme)
|
||||
if i < len(f.args)-1 {
|
||||
as.str.WriteString(" ")
|
||||
}
|
||||
}
|
||||
as.str.WriteString(")")
|
||||
}
|
||||
for _, stmt := range f.body {
|
||||
stmt.accept(as)
|
||||
}
|
||||
as.str.WriteString(")")
|
||||
}
|
||||
|
||||
func (as *AstStringer) visitReturnStmt(r *ReturnStmt) {
|
||||
as.str.WriteString("(return ")
|
||||
r.value.accept(as)
|
||||
as.str.WriteString(")")
|
||||
}
|
||||
|
||||
func (as *AstStringer) visitClassStmt(c *ClassStmt) {
|
||||
fmt.Printf("(class %s)", c.name.lexeme)
|
||||
}
|
||||
|
|
46
callable.go
Normal file
46
callable.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
package main
|
||||
|
||||
type Callable interface {
|
||||
arity() int
|
||||
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}
|
||||
}
|
9
class.go
Normal file
9
class.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package main
|
||||
|
||||
type Class struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (c *Class) String() string {
|
||||
return c.name
|
||||
}
|
55
env.go
55
env.go
|
@ -1,12 +1,14 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Environment struct {
|
||||
values map[string]any
|
||||
parent *Environment
|
||||
enclosing *Environment
|
||||
}
|
||||
|
||||
func newEnvironment(parent *Environment) *Environment {
|
||||
return &Environment{values: map[string]any{}, parent: parent}
|
||||
func newEnvironment(enclosing *Environment) *Environment {
|
||||
return &Environment{map[string]any{}, enclosing}
|
||||
}
|
||||
|
||||
func (env *Environment) get(key string) any {
|
||||
|
@ -14,8 +16,8 @@ func (env *Environment) get(key string) any {
|
|||
return found
|
||||
}
|
||||
|
||||
if env.parent != nil {
|
||||
return env.parent.get(key)
|
||||
if env.enclosing != nil {
|
||||
return env.enclosing.get(key)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -23,20 +25,39 @@ func (env *Environment) get(key string) any {
|
|||
|
||||
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) {
|
||||
|
||||
if env.parent != nil && env.parent.exists(key) {
|
||||
env.parent.set(key, val)
|
||||
}
|
||||
|
||||
func (env *Environment) define(key string, val any) {
|
||||
env.values[key] = val
|
||||
}
|
||||
|
||||
func (env *Environment) assign(key Token, val any) *RuntimeError {
|
||||
if env.exists(key.lexeme) {
|
||||
env.values[key.lexeme] = val
|
||||
return nil
|
||||
}
|
||||
|
||||
if env.enclosing == nil {
|
||||
return &RuntimeError{key, fmt.Sprintf("Can't assign: undefined variable '%s'.", key.lexeme)}
|
||||
}
|
||||
|
||||
return env.enclosing.assign(key, val)
|
||||
}
|
||||
|
||||
func (env *Environment) getAt(distance int, key string) any {
|
||||
return env.ancestor(distance).get(key)
|
||||
}
|
||||
|
||||
func (env *Environment) assignAt(distance int, key Token, val any) {
|
||||
env.ancestor(distance).values[key.lexeme] = val
|
||||
}
|
||||
|
||||
func (env *Environment) ancestor(distance int) *Environment {
|
||||
parent := env
|
||||
for i := 0; i < distance; i++ {
|
||||
parent = parent.enclosing
|
||||
}
|
||||
|
||||
return parent
|
||||
}
|
||||
|
|
24
expr.go
24
expr.go
|
@ -1,7 +1,9 @@
|
|||
package main
|
||||
|
||||
type ExprVisitor interface {
|
||||
visitCall(c *Call) any
|
||||
visitUnary(u *Unary) any
|
||||
visitLambda(l *Lambda) any
|
||||
visitBinary(b *Binary) any
|
||||
visitLiteral(l *Literal) any
|
||||
visitGrouping(g *Grouping) any
|
||||
|
@ -49,9 +51,23 @@ type Logical struct {
|
|||
right Expr
|
||||
}
|
||||
|
||||
type Call struct {
|
||||
callee Expr
|
||||
paren Token
|
||||
args []Expr
|
||||
}
|
||||
|
||||
type Lambda struct {
|
||||
name Token
|
||||
args []Token
|
||||
body []Stmt
|
||||
}
|
||||
|
||||
func (c *Call) expr() {}
|
||||
func (u *Unary) expr() {}
|
||||
func (a *Assign) expr() {}
|
||||
func (b *Binary) expr() {}
|
||||
func (l *Lambda) expr() {}
|
||||
func (l *Literal) expr() {}
|
||||
func (g *Grouping) expr() {}
|
||||
func (v *Variable) expr() {}
|
||||
|
@ -84,3 +100,11 @@ func (a *Assign) accept(v ExprVisitor) any {
|
|||
func (l *Logical) accept(v ExprVisitor) any {
|
||||
return v.visitLogical(l)
|
||||
}
|
||||
|
||||
func (c *Call) accept(v ExprVisitor) any {
|
||||
return v.visitCall(c)
|
||||
}
|
||||
|
||||
func (l *Lambda) accept(v ExprVisitor) any {
|
||||
return v.visitLambda(l)
|
||||
}
|
||||
|
|
63
expr_rpn.go
63
expr_rpn.go
|
@ -1,63 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ExprToRPN struct {
|
||||
str strings.Builder
|
||||
}
|
||||
|
||||
func (as ExprToRPN) String(expr Expr) string {
|
||||
|
||||
if expr == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
expr.accept(&as)
|
||||
return as.str.String()
|
||||
}
|
||||
|
||||
func (as *ExprToRPN) visitBinary(b *Binary) any {
|
||||
b.left.accept(as)
|
||||
as.str.WriteString(" ")
|
||||
b.right.accept(as)
|
||||
as.str.WriteString(" ")
|
||||
as.str.WriteString(b.op.lexeme)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (as *ExprToRPN) visitLiteral(l *Literal) any {
|
||||
as.str.WriteString(fmt.Sprintf("%v", l.value))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (as *ExprToRPN) visitGrouping(g *Grouping) any {
|
||||
g.expression.accept(as)
|
||||
as.str.WriteString(" group")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (as *ExprToRPN) visitUnary(u *Unary) any {
|
||||
u.right.accept(as)
|
||||
as.str.WriteString(fmt.Sprintf(" %s", u.op.lexeme))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (as *ExprToRPN) visitVariable(va *Variable) any {
|
||||
as.str.WriteString(va.name.lexeme)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (as *ExprToRPN) visitAssignment(a *Assign) any {
|
||||
as.str.WriteString(fmt.Sprintf("%v %s =", a.value, a.variable.lexeme))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (as *ExprToRPN) visitLogical(lo *Logical) any {
|
||||
lo.left.accept(as)
|
||||
lo.right.accept(as)
|
||||
as.str.WriteString(" or")
|
||||
return nil
|
||||
}
|
17
globals.go
Normal file
17
globals.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package main
|
||||
|
||||
import "time"
|
||||
|
||||
type ClockFun struct{}
|
||||
|
||||
func (cf *ClockFun) call(i *Interpreter, args ...any) any {
|
||||
return time.Now().Unix()
|
||||
}
|
||||
|
||||
func (cf *ClockFun) arity() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func defineGlobals(env *Environment) {
|
||||
env.define("clock", &ClockFun{})
|
||||
}
|
39
glox.go
39
glox.go
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
@ -27,12 +28,22 @@ func (gl *Glox) runPrompt() {
|
|||
scanner := bufio.NewScanner(os.Stdin)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
doRun := func(line []byte) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
gl.run(line)
|
||||
}
|
||||
|
||||
for {
|
||||
print("> ")
|
||||
if !scanner.Scan() {
|
||||
break
|
||||
}
|
||||
gl.run(scanner.Bytes())
|
||||
doRun(scanner.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,11 +58,29 @@ func (gl *Glox) runFile(path string) {
|
|||
}
|
||||
|
||||
func (gl *Glox) run(source []byte) {
|
||||
tokens, _ := newScanner(source).scan()
|
||||
tokens, err := newScanner(source).scan()
|
||||
|
||||
stmts, _ := newParser(tokens).parse()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// fmt.Println(AstStringer{stmts: stmts})
|
||||
stmts, parseErrs := newParser(tokens).parse()
|
||||
|
||||
gl.Interpreter.interpret(stmts)
|
||||
if parseErrs != nil {
|
||||
panic(parseErrs)
|
||||
}
|
||||
|
||||
fmt.Println(AstStringer{stmts: stmts})
|
||||
|
||||
resolveErrs := newResolver(gl.Interpreter).resolveStmts(stmts...)
|
||||
|
||||
if resolveErrs != nil {
|
||||
panic(resolveErrs)
|
||||
}
|
||||
|
||||
interpreterErrs := gl.Interpreter.interpret(stmts)
|
||||
|
||||
if interpreterErrs != nil {
|
||||
panic(interpreterErrs)
|
||||
}
|
||||
}
|
||||
|
|
154
interpreter.go
154
interpreter.go
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
|
@ -9,7 +10,10 @@ import (
|
|||
|
||||
type Interpreter struct {
|
||||
env *Environment
|
||||
globals *Environment
|
||||
locals map[Expr]int
|
||||
errors []error
|
||||
brk bool
|
||||
}
|
||||
|
||||
type RuntimeError struct {
|
||||
|
@ -17,15 +21,30 @@ type RuntimeError struct {
|
|||
msg string
|
||||
}
|
||||
|
||||
type Return struct {
|
||||
val any
|
||||
}
|
||||
|
||||
func (re *RuntimeError) Error() string {
|
||||
return fmt.Sprintf("RuntimeError [%d][%s] Error: %s", re.token.line, re.token.typ, re.msg)
|
||||
}
|
||||
|
||||
func newInterpreter() *Interpreter {
|
||||
return &Interpreter{env: newEnvironment(nil)}
|
||||
|
||||
globals := newEnvironment(nil)
|
||||
|
||||
defineGlobals(globals)
|
||||
|
||||
return &Interpreter{
|
||||
env: globals,
|
||||
globals: globals,
|
||||
locals: map[Expr]int{},
|
||||
errors: []error{},
|
||||
brk: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Interpreter) interpret(stmts []Stmt) []error {
|
||||
func (i *Interpreter) interpret(stmts []Stmt) error {
|
||||
defer i.recover()
|
||||
|
||||
i.errors = []error{}
|
||||
|
@ -34,7 +53,7 @@ func (i *Interpreter) interpret(stmts []Stmt) []error {
|
|||
stmt.accept(i)
|
||||
}
|
||||
|
||||
return i.errors
|
||||
return errors.Join(i.errors...)
|
||||
}
|
||||
|
||||
func (i *Interpreter) recover() {
|
||||
|
@ -119,25 +138,22 @@ func (i *Interpreter) visitUnary(u *Unary) any {
|
|||
}
|
||||
|
||||
func (i *Interpreter) visitVariable(v *Variable) any {
|
||||
|
||||
if !i.env.exists(v.name.lexeme) {
|
||||
i.panic(&RuntimeError{v.name, fmt.Sprintf("Can't assign: undefined variable '%s'.", v.name.lexeme)})
|
||||
}
|
||||
|
||||
val := i.env.get(v.name.lexeme)
|
||||
return val
|
||||
return i.lookUpVariable(v.name, v)
|
||||
}
|
||||
|
||||
func (i *Interpreter) visitAssignment(a *Assign) any {
|
||||
val := i.evaluate(a.value)
|
||||
distance, isLocal := i.locals[a]
|
||||
|
||||
if !i.env.exists(a.variable.lexeme) {
|
||||
i.panic(&RuntimeError{a.variable, fmt.Sprintf("Can't assign: undefined variable '%s'.", a.variable.lexeme)})
|
||||
if isLocal {
|
||||
i.env.assignAt(distance, a.variable, val)
|
||||
return val
|
||||
}
|
||||
|
||||
val := i.evaluate(a.value)
|
||||
|
||||
i.env.set(a.variable.lexeme, val)
|
||||
|
||||
err := i.globals.assign(a.variable, val)
|
||||
if err != nil {
|
||||
i.panic(err)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
|
@ -155,6 +171,60 @@ func (i *Interpreter) visitLogical(lo *Logical) any {
|
|||
return i.evaluate(lo.right)
|
||||
}
|
||||
|
||||
func (i *Interpreter) visitCall(c *Call) any {
|
||||
|
||||
callee := i.evaluate(c.callee)
|
||||
|
||||
args := []any{}
|
||||
|
||||
for _, arg := range c.args {
|
||||
args = append(args, i.evaluate(arg))
|
||||
}
|
||||
|
||||
callable, ok := callee.(Callable)
|
||||
|
||||
if !ok {
|
||||
i.panic(&RuntimeError{c.paren, "Can only call function and classes."})
|
||||
}
|
||||
|
||||
if callable.arity() != len(args) {
|
||||
i.panic(&RuntimeError{
|
||||
c.paren,
|
||||
fmt.Sprintf(
|
||||
"Expected %d arguments but got %d",
|
||||
callable.arity(),
|
||||
len(args),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
return callable.call(i, args...)
|
||||
}
|
||||
|
||||
func (i *Interpreter) visitFunStmt(f *FunStmt) {
|
||||
i.env.define(f.name.lexeme, newFunction(f.name, f.args, f.body, i.env))
|
||||
}
|
||||
|
||||
func (i *Interpreter) visitClassStmt(c *ClassStmt) {
|
||||
i.env.define(c.name.lexeme, nil)
|
||||
class := &Class{c.name.lexeme}
|
||||
i.env.assign(c.name, class)
|
||||
|
||||
}
|
||||
func (i *Interpreter) visitLambda(l *Lambda) any {
|
||||
return newFunction(l.name, l.args, l.body, i.env)
|
||||
}
|
||||
|
||||
func (i *Interpreter) visitReturnStmt(r *ReturnStmt) {
|
||||
var value any
|
||||
|
||||
if r.value != nil {
|
||||
value = i.evaluate(r.value)
|
||||
}
|
||||
|
||||
panic(Return{value})
|
||||
}
|
||||
|
||||
func (i *Interpreter) visitPrintStmt(p *PrintStmt) {
|
||||
fmt.Printf("%v\n", i.evaluate(p.val))
|
||||
}
|
||||
|
@ -171,23 +241,41 @@ func (i *Interpreter) visitVarStmt(v *VarStmt) {
|
|||
val = i.evaluate(v.initializer)
|
||||
}
|
||||
|
||||
i.env.set(v.name.lexeme, val)
|
||||
i.env.define(v.name.lexeme, val)
|
||||
}
|
||||
|
||||
func (i *Interpreter) visitBlockStmt(b *BlockStmt) {
|
||||
i.executeBlock(b.stmts, newEnvironment(i.env))
|
||||
}
|
||||
|
||||
func (i *Interpreter) executeBlock(stmts []Stmt, current *Environment) {
|
||||
|
||||
parentEnv := i.env
|
||||
i.env = newEnvironment(parentEnv)
|
||||
i.env = current
|
||||
|
||||
// need to restore environment after
|
||||
// panic(Return) in visitReturnStmt
|
||||
defer func() {
|
||||
i.env = parentEnv
|
||||
}()
|
||||
|
||||
for _, stmt := range stmts {
|
||||
|
||||
if i.brk {
|
||||
break
|
||||
}
|
||||
|
||||
for _, stmt := range b.stmts {
|
||||
stmt.accept(i)
|
||||
}
|
||||
|
||||
i.env = parentEnv
|
||||
}
|
||||
|
||||
func (i *Interpreter) visitBreakStmt(b *BreakStmt) {
|
||||
i.brk = true
|
||||
}
|
||||
|
||||
func (i *Interpreter) visitIfStmt(iff *IfStmt) {
|
||||
if isTruthy(i.evaluate(iff.expr)) {
|
||||
if isTruthy(i.evaluate(iff.cond)) {
|
||||
iff.then.accept(i)
|
||||
|
||||
} else if iff.or != nil {
|
||||
|
@ -203,9 +291,11 @@ func (i *Interpreter) visitEnvStmt(e *EnvStmt) {
|
|||
|
||||
for walker != nil {
|
||||
flatten = slices.Insert(flatten, 0, walker)
|
||||
walker = walker.parent
|
||||
walker = walker.enclosing
|
||||
}
|
||||
|
||||
fmt.Printf("globals: %+v\n", *i.globals)
|
||||
|
||||
for ident, e := range flatten {
|
||||
fmt.Printf("%*s", ident, "")
|
||||
fmt.Printf("%+v\n", *e)
|
||||
|
@ -214,10 +304,30 @@ func (i *Interpreter) visitEnvStmt(e *EnvStmt) {
|
|||
|
||||
func (i *Interpreter) visitWhileStmt(w *WhileStmt) {
|
||||
for isTruthy(i.evaluate(w.cond)) {
|
||||
|
||||
if i.brk {
|
||||
i.brk = false
|
||||
break
|
||||
}
|
||||
|
||||
w.body.accept(i)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Interpreter) resolve(expr Expr, depth int) {
|
||||
i.locals[expr] = depth
|
||||
}
|
||||
|
||||
func (i *Interpreter) lookUpVariable(name Token, expr Expr) any {
|
||||
distance, isLocal := i.locals[expr]
|
||||
|
||||
if !isLocal {
|
||||
return i.globals.get(name.lexeme)
|
||||
}
|
||||
|
||||
return i.env.getAt(distance, name.lexeme)
|
||||
}
|
||||
|
||||
func (i *Interpreter) panic(re *RuntimeError) {
|
||||
i.errors = append(i.errors, re)
|
||||
log.Println(re)
|
||||
|
|
244
parser.go
244
parser.go
|
@ -1,8 +1,8 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Parser struct {
|
||||
|
@ -17,7 +17,12 @@ type ParseError struct {
|
|||
}
|
||||
|
||||
func (pe *ParseError) Error() string {
|
||||
return fmt.Sprintf("ParseError [%d][%s]: %s", pe.token.line, pe.token.typ, pe.message)
|
||||
return fmt.Sprintf(
|
||||
"ParseError [%d][%s]: %s",
|
||||
pe.token.line,
|
||||
pe.token.typ,
|
||||
pe.message,
|
||||
)
|
||||
}
|
||||
|
||||
func newParser(tokens []Token) *Parser {
|
||||
|
@ -28,7 +33,7 @@ func newParser(tokens []Token) *Parser {
|
|||
}
|
||||
|
||||
// program -> declaration* EOF
|
||||
func (p *Parser) parse() ([]Stmt, []error) {
|
||||
func (p *Parser) parse() ([]Stmt, error) {
|
||||
defer p.recover()
|
||||
|
||||
stmts := []Stmt{}
|
||||
|
@ -40,21 +45,31 @@ func (p *Parser) parse() ([]Stmt, []error) {
|
|||
}
|
||||
}
|
||||
|
||||
return stmts, p.errors
|
||||
return stmts, errors.Join(p.errors...)
|
||||
}
|
||||
|
||||
// declaration -> varDecl | statement
|
||||
// declaration ->
|
||||
// varDecl | funDecl | classDecl | statement
|
||||
func (p *Parser) declaration() Stmt {
|
||||
defer p.synchronize()
|
||||
if p.match(VAR) {
|
||||
return p.varDecl()
|
||||
}
|
||||
|
||||
if p.match(FUN) {
|
||||
return p.function("function")
|
||||
}
|
||||
|
||||
if p.match(CLASS) {
|
||||
return p.classDecl()
|
||||
}
|
||||
|
||||
return p.statement()
|
||||
}
|
||||
|
||||
// varDecl -> "var" IDENTIFIER ("=" expression)? ";"
|
||||
func (p *Parser) varDecl() Stmt {
|
||||
name := p.consume(IDENTIFIER, "expect identifier for variable")
|
||||
name := p.consume(IDENTIFIER, "Expect identifier for variable")
|
||||
|
||||
var initializer Expr = nil
|
||||
if p.match(EQUAL) {
|
||||
|
@ -66,12 +81,59 @@ func (p *Parser) varDecl() Stmt {
|
|||
return &VarStmt{name, initializer}
|
||||
}
|
||||
|
||||
// classDecl -> "class" IDENTIFIER "{" function* "}"
|
||||
func (p *Parser) classDecl() Stmt {
|
||||
name := p.consume(IDENTIFIER, "Expect identifier for variable")
|
||||
p.consume(LEFT_BRACE, "Expect '{' after class identifier")
|
||||
|
||||
methods := []FunStmt{}
|
||||
for !p.isAtEnd() && !p.check(RIGHT_BRACE) {
|
||||
methods = append(methods, *p.function("method"))
|
||||
}
|
||||
p.consume(RIGHT_BRACE, "Expect '}' after class definition")
|
||||
return &ClassStmt{name, methods}
|
||||
}
|
||||
|
||||
// funDecl -> "fun" function
|
||||
// function -> IDENTIFIER "(" parameters? ")" blockStmt
|
||||
// parameters -> IDENTIFIER ( "," IDENTIFIER )*
|
||||
func (p *Parser) function(kind string) *FunStmt {
|
||||
name := p.consume(IDENTIFIER, fmt.Sprintf("Expect %s name.", kind))
|
||||
|
||||
p.consume(LEFT_PAREN, fmt.Sprintf("Expect '(' after %s name.", kind))
|
||||
|
||||
args := []Token{}
|
||||
for !p.check(RIGHT_PAREN) {
|
||||
args = append(
|
||||
args,
|
||||
p.consume(
|
||||
IDENTIFIER,
|
||||
fmt.Sprintf("Expect %s argument.", kind),
|
||||
),
|
||||
)
|
||||
|
||||
if p.check(COMMA) {
|
||||
p.advance()
|
||||
}
|
||||
}
|
||||
|
||||
p.consume(RIGHT_PAREN, fmt.Sprintf("Expect ')' after %s name.", kind))
|
||||
p.consume(LEFT_BRACE, fmt.Sprintf("Expect '{' after %s arguments.", kind))
|
||||
|
||||
body := p.block()
|
||||
|
||||
return &FunStmt{name, args, body}
|
||||
}
|
||||
|
||||
// statement -> exprStmt
|
||||
//
|
||||
// | whileStmt
|
||||
// | forStmt
|
||||
// | printStmt
|
||||
// | blockStmt
|
||||
// | breakStmt
|
||||
// | ifStmt
|
||||
// | returnStmt
|
||||
// | env
|
||||
func (p *Parser) statement() Stmt {
|
||||
if p.match(PRINT) {
|
||||
|
@ -94,6 +156,18 @@ func (p *Parser) statement() Stmt {
|
|||
return p.whileStmt()
|
||||
}
|
||||
|
||||
if p.match(FOR) {
|
||||
return p.forStmt()
|
||||
}
|
||||
|
||||
if p.match(BREAK) {
|
||||
return p.breakStmt()
|
||||
}
|
||||
|
||||
if p.match(RETURN) {
|
||||
return p.returnStmt()
|
||||
}
|
||||
|
||||
return p.exprStmt()
|
||||
}
|
||||
|
||||
|
@ -121,8 +195,7 @@ func (p *Parser) printStmt() Stmt {
|
|||
return &PrintStmt{expr}
|
||||
}
|
||||
|
||||
// blockStmt -> "{" statement* "}"
|
||||
func (p *Parser) blockStmt() Stmt {
|
||||
func (p *Parser) block() []Stmt {
|
||||
|
||||
stmts := []Stmt{}
|
||||
for !p.check(RIGHT_BRACE) && !p.isAtEnd() {
|
||||
|
@ -131,7 +204,18 @@ func (p *Parser) blockStmt() Stmt {
|
|||
|
||||
p.consume(RIGHT_BRACE, "Unclosed block: Expected '}'.")
|
||||
|
||||
return &BlockStmt{stmts}
|
||||
return stmts
|
||||
}
|
||||
|
||||
// blockStmt -> "{" statement* "}"
|
||||
func (p *Parser) blockStmt() *BlockStmt {
|
||||
return &BlockStmt{p.block()}
|
||||
}
|
||||
|
||||
// breakStmt -> break ";"
|
||||
func (p *Parser) breakStmt() Stmt {
|
||||
p.consume(SEMICOLON, "Expect ';' after break.")
|
||||
return &BreakStmt{}
|
||||
}
|
||||
|
||||
// if -> "if" "(" expression ")" statement ("else" statement)?
|
||||
|
@ -160,12 +244,73 @@ func (p *Parser) whileStmt() Stmt {
|
|||
return &WhileStmt{cond, body}
|
||||
}
|
||||
|
||||
// for -> "for" ( "(" ( varDecl | exprStmt | ";" ) expression? ";" expression ")" )? statement
|
||||
func (p *Parser) forStmt() Stmt {
|
||||
|
||||
if p.check(LEFT_BRACE) {
|
||||
return &WhileStmt{&Literal{true}, p.statement()}
|
||||
}
|
||||
|
||||
p.consume(LEFT_PAREN, "Expect '(' after 'for'.")
|
||||
|
||||
var init Stmt
|
||||
|
||||
if p.match(SEMICOLON) {
|
||||
init = nil
|
||||
} else if p.match(VAR) {
|
||||
init = p.varDecl()
|
||||
} else {
|
||||
init = p.exprStmt()
|
||||
}
|
||||
|
||||
var cond Expr
|
||||
|
||||
if !p.check(SEMICOLON) {
|
||||
cond = p.expression()
|
||||
}
|
||||
|
||||
p.consume(SEMICOLON, "Expect ';' after for loop condition;")
|
||||
|
||||
var incr Expr
|
||||
|
||||
if !p.check(RIGHT_PAREN) {
|
||||
incr = p.expression()
|
||||
}
|
||||
|
||||
p.consume(RIGHT_PAREN, "Expect ')' after for clauses;")
|
||||
|
||||
var body = p.statement()
|
||||
|
||||
if incr != nil {
|
||||
body = &BlockStmt{[]Stmt{body, &ExprStmt{incr}}}
|
||||
}
|
||||
|
||||
if cond == nil {
|
||||
cond = &Literal{true}
|
||||
}
|
||||
|
||||
body = &WhileStmt{cond, body}
|
||||
|
||||
if init != nil {
|
||||
body = &BlockStmt{[]Stmt{init, body}}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
// env -> "env" ";"
|
||||
func (p *Parser) envStmt() Stmt {
|
||||
p.consume(SEMICOLON, "Expect ';' after 'env'.")
|
||||
return &EnvStmt{}
|
||||
}
|
||||
|
||||
// return -> "return" expression ";"
|
||||
func (p *Parser) returnStmt() Stmt {
|
||||
ret := p.expression()
|
||||
p.consume(SEMICOLON, "Expect ';' after return;")
|
||||
return &ReturnStmt{ret}
|
||||
}
|
||||
|
||||
// expression -> assignment
|
||||
func (p *Parser) expression() Expr {
|
||||
return p.assignment()
|
||||
|
@ -276,10 +421,52 @@ func (p *Parser) unary() Expr {
|
|||
return &Unary{op, right}
|
||||
}
|
||||
|
||||
return p.primary()
|
||||
return p.call()
|
||||
}
|
||||
|
||||
// primary -> NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" | IDENTIFIER
|
||||
// call -> primary ( "(" arguments? ")" )*
|
||||
func (p *Parser) call() Expr {
|
||||
expr := p.primary()
|
||||
|
||||
for {
|
||||
if p.match(LEFT_PAREN) {
|
||||
expr = p.arguments(expr)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return expr
|
||||
}
|
||||
|
||||
// arguments -> expression ( "," expression )*
|
||||
func (p *Parser) arguments(callee Expr) Expr {
|
||||
arguments := []Expr{}
|
||||
|
||||
if !p.check(RIGHT_PAREN) {
|
||||
for {
|
||||
arguments = append(arguments, p.expression())
|
||||
|
||||
if !p.match(COMMA) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paren := p.consume(RIGHT_PAREN, "Expect ')' after arguments.")
|
||||
|
||||
return &Call{callee, paren, arguments}
|
||||
}
|
||||
|
||||
// primary -> IDENTIFIER
|
||||
//
|
||||
// | NUMBER
|
||||
// | STRING
|
||||
// | "true"
|
||||
// | "false"
|
||||
// | "nil"
|
||||
// | "(" expression ")"
|
||||
// | lambda
|
||||
func (p *Parser) primary() Expr {
|
||||
switch {
|
||||
case p.match(FALSE):
|
||||
|
@ -290,6 +477,10 @@ func (p *Parser) primary() Expr {
|
|||
return &Literal{nil}
|
||||
}
|
||||
|
||||
if p.match(FUN) {
|
||||
return p.lambda()
|
||||
}
|
||||
|
||||
if p.match(NUMBER, STRING) {
|
||||
return &Literal{p.previous().literal}
|
||||
}
|
||||
|
@ -304,11 +495,39 @@ func (p *Parser) primary() Expr {
|
|||
return &Grouping{expr}
|
||||
}
|
||||
|
||||
// p.panic(&ParseError{p.peek(), "Expect expression"})
|
||||
p.panic(&ParseError{p.peek(), "Expect expression"})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Parser) lambda() Expr {
|
||||
name := p.previous()
|
||||
|
||||
p.consume(LEFT_PAREN, "Expect '(' before lambda arguments.")
|
||||
|
||||
args := []Token{}
|
||||
for !p.check(RIGHT_PAREN) {
|
||||
args = append(
|
||||
args,
|
||||
p.consume(
|
||||
IDENTIFIER,
|
||||
"Expect lambda argument.",
|
||||
),
|
||||
)
|
||||
|
||||
if p.check(COMMA) {
|
||||
p.advance()
|
||||
}
|
||||
}
|
||||
|
||||
p.consume(RIGHT_PAREN, "Expect ')' after lambda arguments.")
|
||||
p.consume(LEFT_BRACE, "Expect '{' before lambda body.")
|
||||
|
||||
body := p.block()
|
||||
|
||||
return &Lambda{name, args, body}
|
||||
}
|
||||
|
||||
func (p *Parser) previous() Token {
|
||||
return p.tokens[p.current-1]
|
||||
}
|
||||
|
@ -391,7 +610,6 @@ func (p *Parser) recover() {
|
|||
|
||||
func (p *Parser) panic(pe *ParseError) {
|
||||
p.errors = append(p.errors, pe)
|
||||
log.Println(pe)
|
||||
panic(pe)
|
||||
}
|
||||
|
||||
|
|
193
resolver.go
Normal file
193
resolver.go
Normal file
|
@ -0,0 +1,193 @@
|
|||
package main
|
||||
|
||||
type Scope map[string]bool
|
||||
|
||||
type Resolver struct {
|
||||
interpreter *Interpreter
|
||||
scopes Stack[Scope]
|
||||
}
|
||||
|
||||
type ResolveError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (r *ResolveError) Error() string {
|
||||
return r.msg
|
||||
}
|
||||
|
||||
func newResolver(i *Interpreter) *Resolver {
|
||||
return &Resolver{i, NewStack[Scope]()}
|
||||
}
|
||||
|
||||
func (r *Resolver) resolveStmts(stmts ...Stmt) error {
|
||||
for _, stmt := range stmts {
|
||||
stmt.accept(r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Resolver) resolveExprs(exprs ...Expr) error {
|
||||
for _, expr := range exprs {
|
||||
expr.accept(r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Resolver) beginScope() {
|
||||
r.scopes.Push(map[string]bool{})
|
||||
}
|
||||
|
||||
func (r *Resolver) endScope() {
|
||||
r.scopes.Pop()
|
||||
}
|
||||
|
||||
func (r *Resolver) declare(token Token) {
|
||||
if !r.scopes.Empty() {
|
||||
r.scopes.Peek()[token.lexeme] = false
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resolver) define(token Token) {
|
||||
if !r.scopes.Empty() {
|
||||
r.scopes.Peek()[token.lexeme] = true
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resolver) visitBlockStmt(b *BlockStmt) {
|
||||
r.beginScope()
|
||||
r.resolveStmts(b.stmts...)
|
||||
r.endScope()
|
||||
}
|
||||
|
||||
func (r *Resolver) visitVarStmt(v *VarStmt) {
|
||||
r.declare(v.name)
|
||||
if v.initializer != nil {
|
||||
r.resolveExprs(v.initializer)
|
||||
}
|
||||
r.define(v.name)
|
||||
}
|
||||
|
||||
func (r *Resolver) visitVariable(v *Variable) any {
|
||||
if !r.scopes.Empty() {
|
||||
defined, declared := r.scopes.Peek()[v.name.lexeme]
|
||||
|
||||
if declared && !defined {
|
||||
panic(&ResolveError{"Can't read local variable in its own initializer."})
|
||||
}
|
||||
}
|
||||
|
||||
r.resolveLocal(v, v.name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Resolver) visitAssignment(a *Assign) any {
|
||||
r.resolveExprs(a.value)
|
||||
r.resolveLocal(a, a.variable)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Resolver) resolveLocal(expr Expr, name Token) {
|
||||
for i := r.scopes.Size() - 1; i >= 0; i-- {
|
||||
if _, exists := r.scopes.At(i)[name.lexeme]; exists {
|
||||
r.interpreter.resolve(expr, r.scopes.Size()-1-i)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resolver) visitFunStmt(fun *FunStmt) {
|
||||
r.declare(fun.name)
|
||||
r.define(fun.name)
|
||||
r.resolveFun(fun)
|
||||
}
|
||||
|
||||
func (r *Resolver) resolveFun(fun *FunStmt) {
|
||||
r.beginScope()
|
||||
for _, arg := range fun.args {
|
||||
r.declare(arg)
|
||||
r.define(arg)
|
||||
}
|
||||
r.resolveStmts(fun.body...)
|
||||
r.endScope()
|
||||
}
|
||||
|
||||
func (r *Resolver) visitExprStmt(es *ExprStmt) {
|
||||
r.resolveExprs(es.expr)
|
||||
}
|
||||
|
||||
func (r *Resolver) visitBreakStmt(b *BreakStmt) {}
|
||||
func (r *Resolver) visitEnvStmt(b *EnvStmt) {}
|
||||
func (r *Resolver) visitIfStmt(ifs *IfStmt) {
|
||||
r.resolveExprs(ifs.cond)
|
||||
r.resolveStmts(ifs.then)
|
||||
if ifs.or != nil {
|
||||
r.resolveStmts(ifs.or)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resolver) visitPrintStmt(p *PrintStmt) {
|
||||
r.resolveExprs(p.val)
|
||||
}
|
||||
|
||||
func (r *Resolver) visitReturnStmt(ret *ReturnStmt) {
|
||||
if ret.value != nil {
|
||||
r.resolveExprs(ret.value)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resolver) visitWhileStmt(w *WhileStmt) {
|
||||
r.resolveExprs(w.cond)
|
||||
r.resolveStmts(w.body)
|
||||
}
|
||||
|
||||
func (r *Resolver) visitBinary(b *Binary) any {
|
||||
r.resolveExprs(b.left)
|
||||
r.resolveExprs(b.right)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Resolver) visitCall(c *Call) any {
|
||||
r.resolveExprs(c.callee)
|
||||
for _, arg := range c.args {
|
||||
r.resolveExprs(arg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Resolver) visitGrouping(g *Grouping) any {
|
||||
r.resolveExprs(g.expression)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Resolver) visitLambda(l *Lambda) any {
|
||||
r.beginScope()
|
||||
for _, arg := range l.args {
|
||||
r.declare(arg)
|
||||
r.define(arg)
|
||||
}
|
||||
r.resolveStmts(l.body...)
|
||||
r.endScope()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Resolver) visitLiteral(l *Literal) any {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Resolver) visitLogical(l *Logical) any {
|
||||
r.resolveExprs(l.left)
|
||||
r.resolveExprs(l.right)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Resolver) visitUnary(u *Unary) any {
|
||||
r.resolveExprs(u.right)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Resolver) visitClassStmt(c *ClassStmt) {
|
||||
r.declare(c.name)
|
||||
r.define(c.name)
|
||||
}
|
|
@ -43,6 +43,7 @@ const (
|
|||
|
||||
// keywords
|
||||
AND
|
||||
BREAK
|
||||
CLASS
|
||||
ENV
|
||||
ELSE
|
||||
|
@ -65,6 +66,7 @@ const (
|
|||
|
||||
var keywords = map[string]TokenType{
|
||||
"and": AND,
|
||||
"break": BREAK,
|
||||
"class": CLASS,
|
||||
"else": ELSE,
|
||||
"false": FALSE,
|
||||
|
|
47
stack.go
Normal file
47
stack.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
package main
|
||||
|
||||
type Stack[Item any] interface {
|
||||
Push(Item)
|
||||
Pop() Item
|
||||
Peek() Item
|
||||
At(int) Item
|
||||
Size() int
|
||||
Empty() bool
|
||||
}
|
||||
|
||||
type node[Item any] struct {
|
||||
item Item
|
||||
next *node[Item]
|
||||
}
|
||||
|
||||
type stack[OfType any] []OfType
|
||||
|
||||
func NewStack[OfType any]() Stack[OfType] {
|
||||
return &stack[OfType]{}
|
||||
}
|
||||
|
||||
func (s *stack[Item]) Push(item Item) {
|
||||
*s = append(*s, item)
|
||||
}
|
||||
|
||||
func (s *stack[Item]) Pop() Item {
|
||||
last := s.Peek()
|
||||
*s = (*s)[:len(*s)-1]
|
||||
return last
|
||||
}
|
||||
|
||||
func (s *stack[Item]) At(idx int) Item {
|
||||
return (*s)[idx]
|
||||
}
|
||||
|
||||
func (s *stack[Item]) Peek() Item {
|
||||
return (*s)[len(*s)-1]
|
||||
}
|
||||
|
||||
func (s *stack[_]) Size() int {
|
||||
return len(*s)
|
||||
}
|
||||
|
||||
func (s *stack[_]) Empty() bool {
|
||||
return s.Size() == 0
|
||||
}
|
45
stmt.go
45
stmt.go
|
@ -3,11 +3,15 @@ package main
|
|||
type StmtVisitor interface {
|
||||
visitIfStmt(i *IfStmt)
|
||||
visitVarStmt(v *VarStmt)
|
||||
visitEnvStmt(e *EnvStmt)
|
||||
visitFunStmt(f *FunStmt)
|
||||
visitExprStmt(es *ExprStmt)
|
||||
visitPrintStmt(p *PrintStmt)
|
||||
visitBlockStmt(b *BlockStmt)
|
||||
visitEnvStmt(e *EnvStmt)
|
||||
visitWhileStmt(w *WhileStmt)
|
||||
visitBreakStmt(b *BreakStmt)
|
||||
visitReturnStmt(r *ReturnStmt)
|
||||
visitClassStmt(c *ClassStmt)
|
||||
}
|
||||
|
||||
type Stmt interface {
|
||||
|
@ -36,7 +40,7 @@ type EnvStmt struct{}
|
|||
|
||||
type IfStmt struct {
|
||||
name Token
|
||||
expr Expr
|
||||
cond Expr
|
||||
then Stmt
|
||||
or Stmt
|
||||
}
|
||||
|
@ -46,13 +50,34 @@ type WhileStmt struct {
|
|||
body Stmt
|
||||
}
|
||||
|
||||
type ClassStmt struct {
|
||||
name Token
|
||||
methods []FunStmt
|
||||
}
|
||||
|
||||
type BreakStmt struct{}
|
||||
|
||||
type FunStmt struct {
|
||||
name Token
|
||||
args []Token
|
||||
body []Stmt
|
||||
}
|
||||
|
||||
type ReturnStmt struct {
|
||||
value Expr
|
||||
}
|
||||
|
||||
func (i *IfStmt) stmt() {}
|
||||
func (f *FunStmt) stmt() {}
|
||||
func (e *EnvStmt) stmt() {}
|
||||
func (vs *VarStmt) stmt() {}
|
||||
func (es *ExprStmt) stmt() {}
|
||||
func (p *PrintStmt) stmt() {}
|
||||
func (b *BlockStmt) stmt() {}
|
||||
func (w *WhileStmt) stmt() {}
|
||||
func (b *BreakStmt) stmt() {}
|
||||
func (r *ReturnStmt) stmt() {}
|
||||
func (c *ClassStmt) stmt() {}
|
||||
|
||||
func (p *PrintStmt) accept(v StmtVisitor) {
|
||||
v.visitPrintStmt(p)
|
||||
|
@ -81,3 +106,19 @@ func (e *EnvStmt) accept(v StmtVisitor) {
|
|||
func (w *WhileStmt) accept(v StmtVisitor) {
|
||||
v.visitWhileStmt(w)
|
||||
}
|
||||
|
||||
func (b *BreakStmt) accept(v StmtVisitor) {
|
||||
v.visitBreakStmt(b)
|
||||
}
|
||||
|
||||
func (f *FunStmt) accept(v StmtVisitor) {
|
||||
v.visitFunStmt(f)
|
||||
}
|
||||
|
||||
func (r *ReturnStmt) accept(v StmtVisitor) {
|
||||
v.visitReturnStmt(r)
|
||||
}
|
||||
|
||||
func (c *ClassStmt) accept(v StmtVisitor) {
|
||||
v.visitClassStmt(c)
|
||||
}
|
||||
|
|
36
tests/for.lox
Normal file
36
tests/for.lox
Normal file
|
@ -0,0 +1,36 @@
|
|||
|
||||
print "full form -------------------------";
|
||||
for (var i = 0; i < 100; i = i + 20) print i;
|
||||
|
||||
print "without init ----------------------";
|
||||
var n = 100;
|
||||
for (;n > 0; n = n - 20) print n;
|
||||
|
||||
print "only cond -----------------------";
|
||||
|
||||
var i = 1;
|
||||
for (;i < 100;) {
|
||||
print i;
|
||||
i = i + 10;
|
||||
}
|
||||
|
||||
print "inf ---------------------------";
|
||||
|
||||
var b = 0;
|
||||
for {
|
||||
print b;
|
||||
b = b + 5;
|
||||
if (b > 10) break;
|
||||
|
||||
print "after break";
|
||||
}
|
||||
|
||||
print "fibonachi ------------------------";
|
||||
var temp;
|
||||
var first = 0;
|
||||
|
||||
for (var second = 1; first < 10000; second = temp + second) {
|
||||
print first;
|
||||
temp = first;
|
||||
first = second;
|
||||
}
|
71
tests/functions.lox
Normal file
71
tests/functions.lox
Normal file
|
@ -0,0 +1,71 @@
|
|||
|
||||
print "native function";
|
||||
|
||||
print clock();
|
||||
|
||||
fun count(n) {
|
||||
print n;
|
||||
if (n > 1) count(n - 1);
|
||||
print n;
|
||||
}
|
||||
|
||||
count(10);
|
||||
|
||||
fun hi(name, surname) {
|
||||
print "hello, " + name + " " + surname + "!";
|
||||
}
|
||||
|
||||
hi("John", "Doe");
|
||||
|
||||
|
||||
fun re(turn) {
|
||||
print "before return";
|
||||
return turn;
|
||||
print "should not be printed";
|
||||
}
|
||||
|
||||
print re("turn");
|
||||
|
||||
|
||||
fun sum(start, end) {
|
||||
if (start == end) return start;
|
||||
return start + sum(start + 1, end);
|
||||
}
|
||||
|
||||
print sum(1, 3);
|
||||
|
||||
fun fib(n) {
|
||||
if (n <= 1) return n;
|
||||
|
||||
return fib(n - 2) + fib(n - 1);
|
||||
}
|
||||
|
||||
for (var i = 1; i <= 10; i = i + 1) {
|
||||
print fib(i);
|
||||
}
|
||||
|
||||
fun makeCounter() {
|
||||
var i = 0;
|
||||
|
||||
fun incr() {
|
||||
i = i + 1;
|
||||
print i;
|
||||
}
|
||||
|
||||
return incr;
|
||||
}
|
||||
|
||||
var counter = makeCounter();
|
||||
|
||||
counter();
|
||||
counter();
|
||||
|
||||
fun thrice(fn) {
|
||||
for (var i = 1; i <= 3; i = i + 1) {
|
||||
fn(i);
|
||||
}
|
||||
}
|
||||
|
||||
thrice(fun (a) { print a; });
|
||||
|
||||
print fun () { return "hello, "; }() + "world";
|
12
tests/scope_and_binding.lox
Normal file
12
tests/scope_and_binding.lox
Normal file
|
@ -0,0 +1,12 @@
|
|||
var a = "global";
|
||||
|
||||
{
|
||||
fun showA() {
|
||||
print a;
|
||||
}
|
||||
|
||||
showA();
|
||||
var a = "inner v2";
|
||||
|
||||
showA();
|
||||
}
|
35
tests/while.lox
Normal file
35
tests/while.lox
Normal file
|
@ -0,0 +1,35 @@
|
|||
var iterator = 100;
|
||||
|
||||
{
|
||||
while (iterator > 0) {
|
||||
{
|
||||
print iterator;
|
||||
iterator = iterator - 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
while (iterator < 100) {
|
||||
{
|
||||
{
|
||||
print iterator;
|
||||
iterator = iterator + 2;
|
||||
|
||||
if (iterator > 50) break;
|
||||
|
||||
print "shoud not be printed after 50";
|
||||
}
|
||||
|
||||
print "OUTER also shoud not be printed after 50";
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (iterator < 100) iterator = iterator + 2;
|
||||
|
||||
|
||||
|
||||
print iterator;
|
|
@ -31,28 +31,29 @@ func _() {
|
|||
_ = x[STRING-20]
|
||||
_ = x[NUMBER-21]
|
||||
_ = x[AND-22]
|
||||
_ = x[CLASS-23]
|
||||
_ = x[ENV-24]
|
||||
_ = x[ELSE-25]
|
||||
_ = x[FALSE-26]
|
||||
_ = x[FUN-27]
|
||||
_ = x[FOR-28]
|
||||
_ = x[IF-29]
|
||||
_ = x[NIL-30]
|
||||
_ = x[OR-31]
|
||||
_ = x[PRINT-32]
|
||||
_ = x[RETURN-33]
|
||||
_ = x[SUPER-34]
|
||||
_ = x[THIS-35]
|
||||
_ = x[TRUE-36]
|
||||
_ = x[VAR-37]
|
||||
_ = x[WHILE-38]
|
||||
_ = x[EOF-39]
|
||||
_ = x[BREAK-23]
|
||||
_ = x[CLASS-24]
|
||||
_ = x[ENV-25]
|
||||
_ = x[ELSE-26]
|
||||
_ = x[FALSE-27]
|
||||
_ = x[FUN-28]
|
||||
_ = x[FOR-29]
|
||||
_ = x[IF-30]
|
||||
_ = x[NIL-31]
|
||||
_ = x[OR-32]
|
||||
_ = x[PRINT-33]
|
||||
_ = x[RETURN-34]
|
||||
_ = x[SUPER-35]
|
||||
_ = x[THIS-36]
|
||||
_ = x[TRUE-37]
|
||||
_ = x[VAR-38]
|
||||
_ = x[WHILE-39]
|
||||
_ = x[EOF-40]
|
||||
}
|
||||
|
||||
const _TokenType_name = "LEFT_PARENRIGHT_PARENLEFT_BRACERIGHT_BRACECOMMADOTMINUSPLUSSEMICOLONSLASHSTARBANGBANG_EQUALEQUALEQUAL_EQUALGREATERGREATER_EQUALLESSLESS_EQUALIDENTIFIERSTRINGNUMBERANDCLASSENVELSEFALSEFUNFORIFNILORPRINTRETURNSUPERTHISTRUEVARWHILEEOF"
|
||||
const _TokenType_name = "LEFT_PARENRIGHT_PARENLEFT_BRACERIGHT_BRACECOMMADOTMINUSPLUSSEMICOLONSLASHSTARBANGBANG_EQUALEQUALEQUAL_EQUALGREATERGREATER_EQUALLESSLESS_EQUALIDENTIFIERSTRINGNUMBERANDBREAKCLASSENVELSEFALSEFUNFORIFNILORPRINTRETURNSUPERTHISTRUEVARWHILEEOF"
|
||||
|
||||
var _TokenType_index = [...]uint8{0, 10, 21, 31, 42, 47, 50, 55, 59, 68, 73, 77, 81, 91, 96, 107, 114, 127, 131, 141, 151, 157, 163, 166, 171, 174, 178, 183, 186, 189, 191, 194, 196, 201, 207, 212, 216, 220, 223, 228, 231}
|
||||
var _TokenType_index = [...]uint8{0, 10, 21, 31, 42, 47, 50, 55, 59, 68, 73, 77, 81, 91, 96, 107, 114, 127, 131, 141, 151, 157, 163, 166, 171, 176, 179, 183, 188, 191, 194, 196, 199, 201, 206, 212, 217, 221, 225, 228, 233, 236}
|
||||
|
||||
func (i TokenType) String() string {
|
||||
if i < 0 || i >= TokenType(len(_TokenType_index)-1) {
|
||||
|
|
Loading…
Reference in a new issue