if, or, and, env

This commit is contained in:
Greg 2024-10-06 16:30:57 +03:00
parent 124537b781
commit 5de730af97
9 changed files with 250 additions and 35 deletions

View file

@ -62,6 +62,24 @@ func (as *AstStringer) visitAssignment(a *Assign) any {
return nil return nil
} }
func (as *AstStringer) visitLogicalOr(l *LogicalOr) any {
as.str.WriteString("(or ")
l.left.accept(as)
as.str.WriteString(" ")
l.right.accept(as)
as.str.WriteString(")")
return nil
}
func (as *AstStringer) visitLogicalAnd(l *LogicalAnd) any {
as.str.WriteString("(and ")
l.left.accept(as)
as.str.WriteString(" ")
l.right.accept(as)
as.str.WriteString(")")
return nil
}
func (as *AstStringer) visitPrintStmt(p *PrintStmt) { func (as *AstStringer) visitPrintStmt(p *PrintStmt) {
as.str.WriteString("(print ") as.str.WriteString("(print ")
p.val.accept(as) p.val.accept(as)
@ -92,3 +110,19 @@ func (as *AstStringer) visitBlockStmt(b *BlockStmt) {
as.str.WriteString(")") as.str.WriteString(")")
} }
func (as *AstStringer) visitIfStmt(i *IfStmt) {
as.str.WriteString("(if ")
i.expr.accept(as)
as.str.WriteString(" ")
i.then.accept(as)
if i.or != nil {
as.str.WriteString(" ")
i.or.accept(as)
}
as.str.WriteString(")")
}
func (as *AstStringer) visitEnvStmt(e *EnvStmt) {
as.str.WriteString("(env)")
}

36
expr.go
View file

@ -6,7 +6,9 @@ type ExprVisitor interface {
visitLiteral(l *Literal) any visitLiteral(l *Literal) any
visitGrouping(g *Grouping) any visitGrouping(g *Grouping) any
visitVariable(v *Variable) any visitVariable(v *Variable) any
visitLogicalOr(l *LogicalOr) any
visitAssignment(a *Assign) any visitAssignment(a *Assign) any
visitLogicalAnd(l *LogicalAnd) any
} }
type Expr interface { type Expr interface {
@ -42,12 +44,26 @@ type Assign struct {
value Expr value Expr
} }
func (u *Unary) expr() {} type LogicalOr struct {
func (b *Binary) expr() {} left Expr
func (l *Literal) expr() {} or Token
func (g *Grouping) expr() {} right Expr
func (v *Variable) expr() {} }
func (a *Assign) expr() {}
type LogicalAnd struct {
left Expr
and Token
right Expr
}
func (u *Unary) expr() {}
func (a *Assign) expr() {}
func (b *Binary) expr() {}
func (l *Literal) expr() {}
func (g *Grouping) expr() {}
func (v *Variable) expr() {}
func (l *LogicalOr) expr() {}
func (l *LogicalAnd) expr() {}
func (u *Unary) accept(v ExprVisitor) any { func (u *Unary) accept(v ExprVisitor) any {
return v.visitUnary(u) return v.visitUnary(u)
@ -72,3 +88,11 @@ func (va *Variable) accept(v ExprVisitor) any {
func (a *Assign) accept(v ExprVisitor) any { func (a *Assign) accept(v ExprVisitor) any {
return v.visitAssignment(a) return v.visitAssignment(a)
} }
func (l *LogicalOr) accept(v ExprVisitor) any {
return v.visitLogicalOr(l)
}
func (l *LogicalAnd) accept(v ExprVisitor) any {
return v.visitLogicalAnd(l)
}

View file

@ -54,3 +54,17 @@ func (as *ExprToRPN) visitAssignment(a *Assign) any {
as.str.WriteString(fmt.Sprintf("%v %s =", a.value, a.variable.lexeme)) as.str.WriteString(fmt.Sprintf("%v %s =", a.value, a.variable.lexeme))
return nil return nil
} }
func (as *ExprToRPN) visitLogicalOr(lo *LogicalOr) any {
lo.left.accept(as)
lo.right.accept(as)
as.str.WriteString(" or")
return nil
}
func (as *ExprToRPN) visitLogicalAnd(la *LogicalAnd) any {
la.left.accept(as)
la.right.accept(as)
as.str.WriteString(" and")
return nil
}

View file

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"log" "log"
"reflect" "reflect"
"slices"
) )
type Interpreter struct { type Interpreter struct {
@ -119,13 +120,11 @@ func (i *Interpreter) visitUnary(u *Unary) any {
func (i *Interpreter) visitVariable(v *Variable) any { func (i *Interpreter) visitVariable(v *Variable) any {
val := i.env.get(v.name.lexeme) if !i.env.exists(v.name.lexeme) {
i.panic(&RuntimeError{v.name, fmt.Sprintf("Can't assign: undefined variable '%s'.", v.name.lexeme)})
if val == nil {
i.panic(&RuntimeError{v.name, fmt.Sprintf("Can't evaluate: Undefined variable '%s'.", v.name.lexeme)})
return nil
} }
val := i.env.get(v.name.lexeme)
return val return val
} }
@ -133,8 +132,6 @@ func (i *Interpreter) visitAssignment(a *Assign) any {
if !i.env.exists(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)}) i.panic(&RuntimeError{a.variable, fmt.Sprintf("Can't assign: undefined variable '%s'.", a.variable.lexeme)})
return nil
} }
val := i.evaluate(a.value) val := i.evaluate(a.value)
@ -144,6 +141,14 @@ func (i *Interpreter) visitAssignment(a *Assign) any {
return val return val
} }
func (i *Interpreter) visitLogicalOr(lo *LogicalOr) any {
return isTruthy(i.evaluate(lo.left)) || isTruthy(i.evaluate(lo.right))
}
func (i *Interpreter) visitLogicalAnd(la *LogicalAnd) any {
return isTruthy(i.evaluate(la.left)) && isTruthy(i.evaluate(la.right))
}
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))
} }
@ -175,6 +180,33 @@ func (i *Interpreter) visitBlockStmt(b *BlockStmt) {
i.env = parentEnv i.env = parentEnv
} }
func (i *Interpreter) visitIfStmt(iff *IfStmt) {
if isTruthy(i.evaluate(iff.expr)) {
iff.then.accept(i)
} else if iff.or != nil {
iff.or.accept(i)
}
}
func (i *Interpreter) visitEnvStmt(e *EnvStmt) {
walker := i.env
flatten := []*Environment{}
for walker != nil {
flatten = slices.Insert(flatten, 0, walker)
walker = walker.parent
}
for ident, e := range flatten {
fmt.Printf("%*s", ident, "")
fmt.Printf("%+v\n", *e)
}
}
func (i *Interpreter) panic(re *RuntimeError) { func (i *Interpreter) panic(re *RuntimeError) {
i.errors = append(i.errors, re) i.errors = append(i.errors, re)
log.Println(re) log.Println(re)
@ -198,6 +230,11 @@ func (i *Interpreter) checkIfFloats(op Token, a any, b any) {
} }
func isFloats(a any, b any) bool { func isFloats(a any, b any) bool {
if a == nil || b == nil {
return false
}
ltype := reflect.TypeOf(a) ltype := reflect.TypeOf(a)
rtype := reflect.TypeOf(b) rtype := reflect.TypeOf(b)
@ -205,6 +242,11 @@ func isFloats(a any, b any) bool {
} }
func isStrings(a any, b any) bool { func isStrings(a any, b any) bool {
if a == nil || b == nil {
return false
}
ltype := reflect.TypeOf(a) ltype := reflect.TypeOf(a)
rtype := reflect.TypeOf(b) rtype := reflect.TypeOf(b)

View file

@ -66,7 +66,7 @@ func (p *Parser) varDecl() Stmt {
return &VarStmt{name, initializer} return &VarStmt{name, initializer}
} }
// statement -> exprStmt | printStmt | block // statement -> exprStmt | printStmt | block | ifStmt | env
func (p *Parser) statement() Stmt { func (p *Parser) statement() Stmt {
if p.match(PRINT) { if p.match(PRINT) {
return p.printStmt() return p.printStmt()
@ -76,6 +76,14 @@ func (p *Parser) statement() Stmt {
return p.block() return p.block()
} }
if p.match(IF) {
return p.ifStmt()
}
if p.match(ENV) {
return p.envStmt()
}
return p.exprStmt() return p.exprStmt()
} }
@ -83,12 +91,22 @@ func (p *Parser) statement() Stmt {
func (p *Parser) exprStmt() Stmt { func (p *Parser) exprStmt() Stmt {
expr := p.expression() expr := p.expression()
p.consume(SEMICOLON, "Expect ';' after expression.") p.consume(SEMICOLON, "Expect ';' after expression.")
if expr == nil {
return nil
}
return &ExprStmt{expr} return &ExprStmt{expr}
} }
// printStmt -> "print" expression ";" // printStmt -> "print" expression ";"
func (p *Parser) printStmt() Stmt { func (p *Parser) printStmt() Stmt {
expr := p.expression() expr := p.expression()
if expr == nil {
p.panic(&ParseError{p.previous(), "Expect expression after 'print'"})
}
p.consume(SEMICOLON, "Expect ';' after expression.") p.consume(SEMICOLON, "Expect ';' after expression.")
return &PrintStmt{expr} return &PrintStmt{expr}
} }
@ -97,7 +115,7 @@ func (p *Parser) printStmt() Stmt {
func (p *Parser) block() Stmt { func (p *Parser) block() Stmt {
stmts := []Stmt{} stmts := []Stmt{}
for !p.check(RIGHT_BRACE) { for !p.check(RIGHT_BRACE) && !p.isAtEnd() {
stmts = append(stmts, p.declaration()) stmts = append(stmts, p.declaration())
} }
@ -106,14 +124,36 @@ func (p *Parser) block() Stmt {
return &BlockStmt{stmts} return &BlockStmt{stmts}
} }
// if -> "if" "(" expression ")" statement ("else" statement)?
func (p *Parser) ifStmt() Stmt {
name := p.previous()
p.consume(LEFT_PAREN, "Expect '(' after 'if'.")
expr := p.expression()
p.consume(RIGHT_PAREN, "Expect ')' after 'if' condition.")
then := p.statement()
var or Stmt = nil
if p.match(ELSE) {
or = p.statement()
}
return &IfStmt{name, expr, then, or}
}
// env -> "env" ";"
func (p *Parser) envStmt() Stmt {
p.consume(SEMICOLON, "Expect ';' after 'env'.")
return &EnvStmt{}
}
// expression -> assignment // expression -> assignment
func (p *Parser) expression() Expr { func (p *Parser) expression() Expr {
return p.assignment() return p.assignment()
} }
// assignment -> IDENTIFIER "=" assignment | equality // assignment -> IDENTIFIER "=" assignment | logicalOr
func (p *Parser) assignment() Expr { func (p *Parser) assignment() Expr {
expr := p.equality() expr := p.logicalOr()
if p.match(EQUAL) { if p.match(EQUAL) {
eq := p.previous() eq := p.previous()
@ -129,6 +169,34 @@ func (p *Parser) assignment() Expr {
return expr return expr
} }
// logicalOr -> logicalAnd ( "or" logicalAnd )*
func (p *Parser) logicalOr() Expr {
left := p.logicalAnd()
for p.match(OR) {
or := p.previous()
right := p.logicalAnd()
left = &LogicalOr{left, or, right}
}
return left
}
// logicalAnd -> equality ( "and" equality )*
func (p *Parser) logicalAnd() Expr {
left := p.equality()
for p.match(AND) {
or := p.previous()
right := p.equality()
left = &LogicalAnd{left, or, right}
}
return left
}
// equality -> comparison ( ( "==" | "!=" ) comparison )* // equality -> comparison ( ( "==" | "!=" ) comparison )*
func (p *Parser) equality() Expr { func (p *Parser) equality() Expr {
expr := p.comparison() expr := p.comparison()
@ -289,7 +357,7 @@ func (p *Parser) synchronize() {
} }
switch p.peek().typ { switch p.peek().typ {
case CLASS, FOR, FUN, IF, PRINT, RETURN, VAR, WHILE: case CLASS, FOR, FUN, IF, PRINT, RETURN, VAR, WHILE, ENV:
return return
} }

View file

@ -44,6 +44,7 @@ const (
// keywords // keywords
AND AND
CLASS CLASS
ENV
ELSE ELSE
FALSE FALSE
FUN FUN
@ -79,6 +80,7 @@ var keywords = map[string]TokenType{
"true": TRUE, "true": TRUE,
"var": VAR, "var": VAR,
"while": WHILE, "while": WHILE,
"env": ENV,
} }
type Token struct { type Token struct {

21
stmt.go
View file

@ -1,10 +1,12 @@
package main package main
type StmtVisitor interface { type StmtVisitor interface {
visitIfStmt(i *IfStmt)
visitVarStmt(v *VarStmt) visitVarStmt(v *VarStmt)
visitExprStmt(es *ExprStmt) visitExprStmt(es *ExprStmt)
visitPrintStmt(p *PrintStmt) visitPrintStmt(p *PrintStmt)
visitBlockStmt(b *BlockStmt) visitBlockStmt(b *BlockStmt)
visitEnvStmt(e *EnvStmt)
} }
type Stmt interface { type Stmt interface {
@ -29,10 +31,21 @@ type BlockStmt struct {
stmts []Stmt stmts []Stmt
} }
type EnvStmt struct{}
type IfStmt struct {
name Token
expr Expr
then Stmt
or Stmt
}
func (i *IfStmt) stmt() {}
func (vs *VarStmt) stmt() {} func (vs *VarStmt) stmt() {}
func (es *ExprStmt) stmt() {} func (es *ExprStmt) stmt() {}
func (p *PrintStmt) stmt() {} func (p *PrintStmt) stmt() {}
func (b *BlockStmt) stmt() {} func (b *BlockStmt) stmt() {}
func (e *EnvStmt) stmt() {}
func (p *PrintStmt) accept(v StmtVisitor) { func (p *PrintStmt) accept(v StmtVisitor) {
v.visitPrintStmt(p) v.visitPrintStmt(p)
@ -49,3 +62,11 @@ func (vs *VarStmt) accept(v StmtVisitor) {
func (b *BlockStmt) accept(v StmtVisitor) { func (b *BlockStmt) accept(v StmtVisitor) {
v.visitBlockStmt(b) v.visitBlockStmt(b)
} }
func (i *IfStmt) accept(v StmtVisitor) {
v.visitIfStmt(i)
}
func (e *EnvStmt) accept(v StmtVisitor) {
v.visitEnvStmt(e)
}

9
tests/env.lox Normal file
View file

@ -0,0 +1,9 @@
var a = 1;
{
var b = 2;
{
var c =3;
env;
}
env;
}

View file

@ -32,26 +32,27 @@ func _() {
_ = x[NUMBER-21] _ = x[NUMBER-21]
_ = x[AND-22] _ = x[AND-22]
_ = x[CLASS-23] _ = x[CLASS-23]
_ = x[ELSE-24] _ = x[ENV-24]
_ = x[FALSE-25] _ = x[ELSE-25]
_ = x[FUN-26] _ = x[FALSE-26]
_ = x[FOR-27] _ = x[FUN-27]
_ = x[IF-28] _ = x[FOR-28]
_ = x[NIL-29] _ = x[IF-29]
_ = x[OR-30] _ = x[NIL-30]
_ = x[PRINT-31] _ = x[OR-31]
_ = x[RETURN-32] _ = x[PRINT-32]
_ = x[SUPER-33] _ = x[RETURN-33]
_ = x[THIS-34] _ = x[SUPER-34]
_ = x[TRUE-35] _ = x[THIS-35]
_ = x[VAR-36] _ = x[TRUE-36]
_ = x[WHILE-37] _ = x[VAR-37]
_ = x[EOF-38] _ = x[WHILE-38]
_ = x[EOF-39]
} }
const _TokenType_name = "LEFT_PARENRIGHT_PARENLEFT_BRACERIGHT_BRACECOMMADOTMINUSPLUSSEMICOLONSLASHSTARBANGBANG_EQUALEQUALEQUAL_EQUALGREATERGREATER_EQUALLESSLESS_EQUALIDENTIFIERSTRINGNUMBERANDCLASSELSEFALSEFUNFORIFNILORPRINTRETURNSUPERTHISTRUEVARWHILEEOF" const _TokenType_name = "LEFT_PARENRIGHT_PARENLEFT_BRACERIGHT_BRACECOMMADOTMINUSPLUSSEMICOLONSLASHSTARBANGBANG_EQUALEQUALEQUAL_EQUALGREATERGREATER_EQUALLESSLESS_EQUALIDENTIFIERSTRINGNUMBERANDCLASSENVELSEFALSEFUNFORIFNILORPRINTRETURNSUPERTHISTRUEVARWHILEEOF"
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, 175, 180, 183, 186, 188, 191, 193, 198, 204, 209, 213, 217, 220, 225, 228} 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}
func (i TokenType) String() string { func (i TokenType) String() string {
if i < 0 || i >= TokenType(len(_TokenType_index)-1) { if i < 0 || i >= TokenType(len(_TokenType_index)-1) {