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…
	
	Add table
		
		Reference in a new issue