if, or, and, env
This commit is contained in:
parent
124537b781
commit
5de730af97
9 changed files with 250 additions and 35 deletions
|
@ -62,6 +62,24 @@ func (as *AstStringer) visitAssignment(a *Assign) any {
|
|||
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) {
|
||||
as.str.WriteString("(print ")
|
||||
p.val.accept(as)
|
||||
|
@ -92,3 +110,19 @@ func (as *AstStringer) visitBlockStmt(b *BlockStmt) {
|
|||
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
36
expr.go
|
@ -6,7 +6,9 @@ type ExprVisitor interface {
|
|||
visitLiteral(l *Literal) any
|
||||
visitGrouping(g *Grouping) any
|
||||
visitVariable(v *Variable) any
|
||||
visitLogicalOr(l *LogicalOr) any
|
||||
visitAssignment(a *Assign) any
|
||||
visitLogicalAnd(l *LogicalAnd) any
|
||||
}
|
||||
|
||||
type Expr interface {
|
||||
|
@ -42,12 +44,26 @@ type Assign struct {
|
|||
value Expr
|
||||
}
|
||||
|
||||
func (u *Unary) expr() {}
|
||||
func (b *Binary) expr() {}
|
||||
func (l *Literal) expr() {}
|
||||
func (g *Grouping) expr() {}
|
||||
func (v *Variable) expr() {}
|
||||
func (a *Assign) expr() {}
|
||||
type LogicalOr struct {
|
||||
left Expr
|
||||
or Token
|
||||
right 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 {
|
||||
return v.visitUnary(u)
|
||||
|
@ -72,3 +88,11 @@ func (va *Variable) accept(v ExprVisitor) any {
|
|||
func (a *Assign) accept(v ExprVisitor) any {
|
||||
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)
|
||||
}
|
||||
|
|
14
expr_rpn.go
14
expr_rpn.go
|
@ -54,3 +54,17 @@ func (as *ExprToRPN) visitAssignment(a *Assign) any {
|
|||
as.str.WriteString(fmt.Sprintf("%v %s =", a.value, a.variable.lexeme))
|
||||
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
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"slices"
|
||||
)
|
||||
|
||||
type Interpreter struct {
|
||||
|
@ -119,13 +120,11 @@ func (i *Interpreter) visitUnary(u *Unary) any {
|
|||
|
||||
func (i *Interpreter) visitVariable(v *Variable) any {
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -133,8 +132,6 @@ func (i *Interpreter) visitAssignment(a *Assign) any {
|
|||
|
||||
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)
|
||||
|
@ -144,6 +141,14 @@ func (i *Interpreter) visitAssignment(a *Assign) any {
|
|||
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) {
|
||||
fmt.Printf("%v\n", i.evaluate(p.val))
|
||||
}
|
||||
|
@ -175,6 +180,33 @@ func (i *Interpreter) visitBlockStmt(b *BlockStmt) {
|
|||
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) {
|
||||
i.errors = append(i.errors, 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 {
|
||||
|
||||
if a == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
ltype := reflect.TypeOf(a)
|
||||
rtype := reflect.TypeOf(b)
|
||||
|
||||
|
@ -205,6 +242,11 @@ func isFloats(a any, b any) bool {
|
|||
}
|
||||
|
||||
func isStrings(a any, b any) bool {
|
||||
|
||||
if a == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
ltype := reflect.TypeOf(a)
|
||||
rtype := reflect.TypeOf(b)
|
||||
|
||||
|
|
78
parser.go
78
parser.go
|
@ -66,7 +66,7 @@ func (p *Parser) varDecl() Stmt {
|
|||
return &VarStmt{name, initializer}
|
||||
}
|
||||
|
||||
// statement -> exprStmt | printStmt | block
|
||||
// statement -> exprStmt | printStmt | block | ifStmt | env
|
||||
func (p *Parser) statement() Stmt {
|
||||
if p.match(PRINT) {
|
||||
return p.printStmt()
|
||||
|
@ -76,6 +76,14 @@ func (p *Parser) statement() Stmt {
|
|||
return p.block()
|
||||
}
|
||||
|
||||
if p.match(IF) {
|
||||
return p.ifStmt()
|
||||
}
|
||||
|
||||
if p.match(ENV) {
|
||||
return p.envStmt()
|
||||
}
|
||||
|
||||
return p.exprStmt()
|
||||
}
|
||||
|
||||
|
@ -83,12 +91,22 @@ func (p *Parser) statement() Stmt {
|
|||
func (p *Parser) exprStmt() Stmt {
|
||||
expr := p.expression()
|
||||
p.consume(SEMICOLON, "Expect ';' after expression.")
|
||||
|
||||
if expr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &ExprStmt{expr}
|
||||
}
|
||||
|
||||
// printStmt -> "print" expression ";"
|
||||
func (p *Parser) printStmt() Stmt {
|
||||
expr := p.expression()
|
||||
|
||||
if expr == nil {
|
||||
p.panic(&ParseError{p.previous(), "Expect expression after 'print'"})
|
||||
}
|
||||
|
||||
p.consume(SEMICOLON, "Expect ';' after expression.")
|
||||
return &PrintStmt{expr}
|
||||
}
|
||||
|
@ -97,7 +115,7 @@ func (p *Parser) printStmt() Stmt {
|
|||
func (p *Parser) block() Stmt {
|
||||
|
||||
stmts := []Stmt{}
|
||||
for !p.check(RIGHT_BRACE) {
|
||||
for !p.check(RIGHT_BRACE) && !p.isAtEnd() {
|
||||
stmts = append(stmts, p.declaration())
|
||||
}
|
||||
|
||||
|
@ -106,14 +124,36 @@ func (p *Parser) block() Stmt {
|
|||
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
|
||||
func (p *Parser) expression() Expr {
|
||||
return p.assignment()
|
||||
}
|
||||
|
||||
// assignment -> IDENTIFIER "=" assignment | equality
|
||||
// assignment -> IDENTIFIER "=" assignment | logicalOr
|
||||
func (p *Parser) assignment() Expr {
|
||||
expr := p.equality()
|
||||
expr := p.logicalOr()
|
||||
|
||||
if p.match(EQUAL) {
|
||||
eq := p.previous()
|
||||
|
@ -129,6 +169,34 @@ func (p *Parser) assignment() 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 )*
|
||||
func (p *Parser) equality() Expr {
|
||||
expr := p.comparison()
|
||||
|
@ -289,7 +357,7 @@ func (p *Parser) synchronize() {
|
|||
}
|
||||
|
||||
switch p.peek().typ {
|
||||
case CLASS, FOR, FUN, IF, PRINT, RETURN, VAR, WHILE:
|
||||
case CLASS, FOR, FUN, IF, PRINT, RETURN, VAR, WHILE, ENV:
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ const (
|
|||
// keywords
|
||||
AND
|
||||
CLASS
|
||||
ENV
|
||||
ELSE
|
||||
FALSE
|
||||
FUN
|
||||
|
@ -79,6 +80,7 @@ var keywords = map[string]TokenType{
|
|||
"true": TRUE,
|
||||
"var": VAR,
|
||||
"while": WHILE,
|
||||
"env": ENV,
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
|
|
21
stmt.go
21
stmt.go
|
@ -1,10 +1,12 @@
|
|||
package main
|
||||
|
||||
type StmtVisitor interface {
|
||||
visitIfStmt(i *IfStmt)
|
||||
visitVarStmt(v *VarStmt)
|
||||
visitExprStmt(es *ExprStmt)
|
||||
visitPrintStmt(p *PrintStmt)
|
||||
visitBlockStmt(b *BlockStmt)
|
||||
visitEnvStmt(e *EnvStmt)
|
||||
}
|
||||
|
||||
type Stmt interface {
|
||||
|
@ -29,10 +31,21 @@ type BlockStmt struct {
|
|||
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 (es *ExprStmt) stmt() {}
|
||||
func (p *PrintStmt) stmt() {}
|
||||
func (b *BlockStmt) stmt() {}
|
||||
func (e *EnvStmt) stmt() {}
|
||||
|
||||
func (p *PrintStmt) accept(v StmtVisitor) {
|
||||
v.visitPrintStmt(p)
|
||||
|
@ -49,3 +62,11 @@ func (vs *VarStmt) accept(v StmtVisitor) {
|
|||
func (b *BlockStmt) accept(v StmtVisitor) {
|
||||
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
9
tests/env.lox
Normal file
|
@ -0,0 +1,9 @@
|
|||
var a = 1;
|
||||
{
|
||||
var b = 2;
|
||||
{
|
||||
var c =3;
|
||||
env;
|
||||
}
|
||||
env;
|
||||
}
|
|
@ -32,26 +32,27 @@ func _() {
|
|||
_ = x[NUMBER-21]
|
||||
_ = x[AND-22]
|
||||
_ = x[CLASS-23]
|
||||
_ = x[ELSE-24]
|
||||
_ = x[FALSE-25]
|
||||
_ = x[FUN-26]
|
||||
_ = x[FOR-27]
|
||||
_ = x[IF-28]
|
||||
_ = x[NIL-29]
|
||||
_ = x[OR-30]
|
||||
_ = x[PRINT-31]
|
||||
_ = x[RETURN-32]
|
||||
_ = x[SUPER-33]
|
||||
_ = x[THIS-34]
|
||||
_ = x[TRUE-35]
|
||||
_ = x[VAR-36]
|
||||
_ = x[WHILE-37]
|
||||
_ = x[EOF-38]
|
||||
_ = 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]
|
||||
}
|
||||
|
||||
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 {
|
||||
if i < 0 || i >= TokenType(len(_TokenType_index)-1) {
|
||||
|
|
Loading…
Reference in a new issue