variables
This commit is contained in:
		
							parent
							
								
									50ecb3dc2e
								
							
						
					
					
						commit
						6fad12456c
					
				
					 14 changed files with 485 additions and 228 deletions
				
			
		
							
								
								
									
										10
									
								
								.vscode/launch.json
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.vscode/launch.json
									
										
									
									
										vendored
									
									
								
							|  | @ -3,5 +3,13 @@ | ||||||
|     // Hover to view descriptions of existing attributes. |     // Hover to view descriptions of existing attributes. | ||||||
|     // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 |     // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||||||
|     "version": "0.2.0", |     "version": "0.2.0", | ||||||
|     "configurations": [] |     "configurations": [ | ||||||
|  |         { | ||||||
|  |             "name": "Attach to Process", | ||||||
|  |             "type": "go", | ||||||
|  |             "request": "attach", | ||||||
|  |             "mode": "local", | ||||||
|  |             "processId": 0 | ||||||
|  |         } | ||||||
|  |     ] | ||||||
| } | } | ||||||
							
								
								
									
										4
									
								
								.vscode/settings.json
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.vscode/settings.json
									
										
									
									
										vendored
									
									
								
							|  | @ -1,5 +1,7 @@ | ||||||
| { | { | ||||||
|     "cSpell.words": [ |     "cSpell.words": [ | ||||||
|         "glox" |         "glox", | ||||||
|  |         "PAREN", | ||||||
|  |         "stmts" | ||||||
|     ] |     ] | ||||||
| } | } | ||||||
							
								
								
									
										53
									
								
								ast.go
									
										
									
									
									
								
							
							
						
						
									
										53
									
								
								ast.go
									
										
									
									
									
								
							|  | @ -1,53 +0,0 @@ | ||||||
| package main |  | ||||||
| 
 |  | ||||||
| type Visitor interface { |  | ||||||
| 	visitUnary(u *Unary) any |  | ||||||
| 	visitBinary(b *Binary) any |  | ||||||
| 	visitLiteral(l *Literal) any |  | ||||||
| 	visitGrouping(g *Grouping) any |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type Expr interface { |  | ||||||
| 	expr() |  | ||||||
| 	accept(v Visitor) any |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type Unary struct { |  | ||||||
| 	op    Token |  | ||||||
| 	right Expr |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type Binary struct { |  | ||||||
| 	left  Expr |  | ||||||
| 	op    Token |  | ||||||
| 	right Expr |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type Literal struct { |  | ||||||
| 	value any |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| type Grouping struct { |  | ||||||
| 	expression Expr |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (u *Unary) expr()    {} |  | ||||||
| func (b *Binary) expr()   {} |  | ||||||
| func (l *Literal) expr()  {} |  | ||||||
| func (g *Grouping) expr() {} |  | ||||||
| 
 |  | ||||||
| func (u *Unary) accept(v Visitor) any { |  | ||||||
| 	return v.visitUnary(u) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (b *Binary) accept(v Visitor) any { |  | ||||||
| 	return v.visitBinary(b) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (l *Literal) accept(v Visitor) any { |  | ||||||
| 	return v.visitLiteral(l) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (g *Grouping) accept(v Visitor) any { |  | ||||||
| 	return v.visitGrouping(g) |  | ||||||
| } |  | ||||||
							
								
								
									
										46
									
								
								ast_rpn.go
									
										
									
									
									
								
							
							
						
						
									
										46
									
								
								ast_rpn.go
									
										
									
									
									
								
							|  | @ -1,46 +0,0 @@ | ||||||
| package main |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"strings" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| type AstToRPN struct { |  | ||||||
| 	str strings.Builder |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (as AstToRPN) String(expr Expr) string { |  | ||||||
| 
 |  | ||||||
| 	if expr == nil { |  | ||||||
| 		return "" |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	expr.accept(&as) |  | ||||||
| 	return as.str.String() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (as *AstToRPN) 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 *AstToRPN) visitLiteral(l *Literal) any { |  | ||||||
| 	as.str.WriteString(fmt.Sprintf("%v", l.value)) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (as *AstToRPN) visitGrouping(g *Grouping) any { |  | ||||||
| 	g.expression.accept(as) |  | ||||||
| 	as.str.WriteString(" group") |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (as *AstToRPN) visitUnary(u *Unary) any { |  | ||||||
| 	u.right.accept(as) |  | ||||||
| 	as.str.WriteString(fmt.Sprintf(" %s", u.op.lexeme)) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  | @ -9,13 +9,12 @@ type AstStringer struct { | ||||||
| 	str strings.Builder | 	str strings.Builder | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (as AstStringer) String(expr Expr) string { | func (as AstStringer) String(stmts []Stmt) string { | ||||||
| 
 | 
 | ||||||
| 	if expr == nil { | 	for _, stmt := range stmts { | ||||||
| 		return "" | 		stmt.accept(&as) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	expr.accept(&as) |  | ||||||
| 	return as.str.String() | 	return as.str.String() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -49,3 +48,35 @@ func (as *AstStringer) visitUnary(u *Unary) any { | ||||||
| 	as.str.WriteString(")") | 	as.str.WriteString(")") | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (as *AstStringer) visitVariable(va *Variable) any { | ||||||
|  | 	as.str.WriteString(va.name.lexeme) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (as *AstStringer) visitAssignment(a *Assign) any { | ||||||
|  | 	as.str.WriteString(fmt.Sprintf("(= %s ", a.variable.lexeme)) | ||||||
|  | 	a.value.accept(as) | ||||||
|  | 	as.str.WriteString(")") | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (as *AstStringer) visitPrintStmt(p *PrintStmt) { | ||||||
|  | 	as.str.WriteString("(print ") | ||||||
|  | 	p.val.accept(as) | ||||||
|  | 	as.str.WriteString(")") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (as *AstStringer) visitExprStmt(se *ExprStmt) { | ||||||
|  | 	se.expr.accept(as) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (as *AstStringer) visitVarStmt(vs *VarStmt) { | ||||||
|  | 	if vs.initializer != nil { | ||||||
|  | 		as.str.WriteString(fmt.Sprintf("(var %v ", vs.name.literal)) | ||||||
|  | 		vs.initializer.accept(as) | ||||||
|  | 		as.str.WriteString(")") | ||||||
|  | 	} else { | ||||||
|  | 		as.str.WriteString(fmt.Sprintf("(var %v)", vs.name.literal)) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										27
									
								
								error.go
									
										
									
									
									
								
							
							
						
						
									
										27
									
								
								error.go
									
										
									
									
									
								
							|  | @ -1,27 +0,0 @@ | ||||||
| package main |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"log" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| var hadError = false |  | ||||||
| var hadRuntimeError = false |  | ||||||
| 
 |  | ||||||
| func printError(token Token, message string) { |  | ||||||
| 	if token.typ == EOF { |  | ||||||
| 		report(token.line, " at and", message) |  | ||||||
| 	} else { |  | ||||||
| 		report(token.line, fmt.Sprintf(" at '%s'", token.lexeme), message) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func report(line int, where string, message string) { |  | ||||||
| 	log.Printf("[%d] Error %s: %s", line, where, message) |  | ||||||
| 	hadError = true |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func reportRuntimeError(token Token, message string) { |  | ||||||
| 	log.Printf("[%d] Error: %s", token.line, message) |  | ||||||
| 	hadRuntimeError = true |  | ||||||
| } |  | ||||||
							
								
								
									
										74
									
								
								expr.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								expr.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,74 @@ | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | type ExprVisitor interface { | ||||||
|  | 	visitUnary(u *Unary) any | ||||||
|  | 	visitBinary(b *Binary) any | ||||||
|  | 	visitLiteral(l *Literal) any | ||||||
|  | 	visitGrouping(g *Grouping) any | ||||||
|  | 	visitVariable(v *Variable) any | ||||||
|  | 	visitAssignment(a *Assign) any | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Expr interface { | ||||||
|  | 	expr() | ||||||
|  | 	accept(v ExprVisitor) any | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Unary struct { | ||||||
|  | 	op    Token | ||||||
|  | 	right Expr | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Binary struct { | ||||||
|  | 	left  Expr | ||||||
|  | 	op    Token | ||||||
|  | 	right Expr | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Literal struct { | ||||||
|  | 	value any | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Grouping struct { | ||||||
|  | 	expression Expr | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Variable struct { | ||||||
|  | 	name Token | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Assign struct { | ||||||
|  | 	variable Token | ||||||
|  | 	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()   {} | ||||||
|  | 
 | ||||||
|  | func (u *Unary) accept(v ExprVisitor) any { | ||||||
|  | 	return v.visitUnary(u) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (b *Binary) accept(v ExprVisitor) any { | ||||||
|  | 	return v.visitBinary(b) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (l *Literal) accept(v ExprVisitor) any { | ||||||
|  | 	return v.visitLiteral(l) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (g *Grouping) accept(v ExprVisitor) any { | ||||||
|  | 	return v.visitGrouping(g) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (va *Variable) accept(v ExprVisitor) any { | ||||||
|  | 	return v.visitVariable(va) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (a *Assign) accept(v ExprVisitor) any { | ||||||
|  | 	return v.visitAssignment(a) | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								expr_rpn.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								expr_rpn.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | ||||||
|  | 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 | ||||||
|  | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								glox
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								glox
									
										
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										90
									
								
								glox.go
									
										
									
									
									
								
							
							
						
						
									
										90
									
								
								glox.go
									
										
									
									
									
								
							|  | @ -2,83 +2,73 @@ package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bufio" | 	"bufio" | ||||||
| 	"fmt" |  | ||||||
| 	"log" | 	"log" | ||||||
| 	"os" | 	"os" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | type Glox struct { | ||||||
|  | 	Interpreter *Interpreter | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func main() { | func main() { | ||||||
|  | 	glox := &Glox{newInterpreter()} | ||||||
| 	switch len(os.Args) { | 	switch len(os.Args) { | ||||||
| 	case 1: | 	case 1: | ||||||
| 		runPrompt() | 		glox.runPrompt() | ||||||
| 	case 2: | 	case 2: | ||||||
| 		runFile(os.Args[1]) | 		glox.runFile(os.Args[1]) | ||||||
| 	default: | 	default: | ||||||
| 		println("Usage: glox [file]") | 		println("Usage: glox [file]") | ||||||
| 		os.Exit(1) | 		os.Exit(1) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func runPrompt() { | func (gl *Glox) runPrompt() { | ||||||
| 	scanner := bufio.NewScanner(os.Stdin) | 	scanner := bufio.NewScanner(os.Stdin) | ||||||
| 	scanner.Split(bufio.ScanLines) | 	scanner.Split(bufio.ScanLines) | ||||||
| 
 | 
 | ||||||
| 	for { | 	for { | ||||||
| 		print("> ") | 		print("> ") | ||||||
| 		scanner.Scan() | 		if !scanner.Scan() { | ||||||
| 		line := scanner.Text() |  | ||||||
| 		if len(line) == 0 { |  | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 		run([]byte(scanner.Text())) | 		gl.run(scanner.Bytes(), true) | ||||||
| 		hadError = false |  | ||||||
| 		hadRuntimeError = false |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func runFile(path string) { | func (gl *Glox) runFile(path string) { | ||||||
| 	file, err := os.ReadFile(path) | 	file, err := os.ReadFile(path) | ||||||
| 
 | 
 | ||||||
| 	try(err) |  | ||||||
| 
 |  | ||||||
| 	run(file) |  | ||||||
| 
 |  | ||||||
| 	switch { |  | ||||||
| 	case hadError: |  | ||||||
| 		os.Exit(65) |  | ||||||
| 	case hadRuntimeError: |  | ||||||
| 		os.Exit(70) |  | ||||||
| 	default: |  | ||||||
| 		os.Exit(0) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func run(source []byte) { |  | ||||||
| 	tokens := newScanner(source).scan() |  | ||||||
| 
 |  | ||||||
| 	if hadError { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	ast := newParser(tokens).parse() |  | ||||||
| 
 |  | ||||||
| 	if hadError { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	println(AstStringer{}.String(ast)) |  | ||||||
| 
 |  | ||||||
| 	res := newInterpreter().evaluate(ast) |  | ||||||
| 
 |  | ||||||
| 	if hadRuntimeError { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	fmt.Printf("%v\n", res) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func try(err error) { |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	runErrors := gl.run(file, false) | ||||||
|  | 
 | ||||||
|  | 	if len(runErrors) != 0 { | ||||||
|  | 		for _, e := range runErrors { | ||||||
|  | 			log.Print(e) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (gl *Glox) run(source []byte, interactive bool) []error { | ||||||
|  | 	tokens, err := newScanner(source).scan() | ||||||
|  | 
 | ||||||
|  | 	if err != nil { | ||||||
|  | 		return []error{err} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	stmts, parseErrs := newParser(tokens).parse() | ||||||
|  | 
 | ||||||
|  | 	if len(parseErrs) != 0 && !interactive { | ||||||
|  | 		return parseErrs | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	println(AstStringer{}.String(stmts)) | ||||||
|  | 
 | ||||||
|  | 	return gl.Interpreter.interpret(stmts) | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										105
									
								
								interpreter.go
									
										
									
									
									
								
							
							
						
						
									
										105
									
								
								interpreter.go
									
										
									
									
									
								
							|  | @ -2,67 +2,79 @@ package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"log" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type Interpreter struct{} | type Interpreter struct { | ||||||
|  | 	env    map[string]any | ||||||
|  | 	errors []error | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| type RuntimeError struct { | type RuntimeError struct { | ||||||
| 	token Token | 	token Token | ||||||
| 	msg   string | 	msg   string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (re RuntimeError) Error() string { | func (re *RuntimeError) Error() string { | ||||||
| 	return re.msg | 	return fmt.Sprintf("RuntimeError [%d][%s] Error: %s", re.token.line, re.token.typ, re.msg) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newInterpreter() *Interpreter { | func newInterpreter() *Interpreter { | ||||||
| 	return &Interpreter{} | 	return &Interpreter{env: make(map[string]any)} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (i *Interpreter) evaluate(e Expr) any { | func (i *Interpreter) interpret(stmts []Stmt) []error { | ||||||
| 	defer i.recover() | 	defer i.recover() | ||||||
| 	return e.accept(i) | 
 | ||||||
|  | 	i.errors = []error{} | ||||||
|  | 
 | ||||||
|  | 	for _, stmt := range stmts { | ||||||
|  | 		stmt.accept(i) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return i.errors | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (i *Interpreter) recover() { | func (i *Interpreter) recover() { | ||||||
| 	if err := recover(); err != nil { | 	if err := recover(); err != nil { | ||||||
| 		pe, ok := err.(RuntimeError) | 		_, ok := err.(*RuntimeError) | ||||||
| 
 | 
 | ||||||
| 		if !ok { | 		if !ok { | ||||||
| 			panic(err) | 			panic(err) | ||||||
| 		} | 		} | ||||||
| 
 |  | ||||||
| 		reportRuntimeError(pe.token, pe.msg) |  | ||||||
| 		hadRuntimeError = true |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (i *Interpreter) evaluate(e Expr) any { | ||||||
|  | 	return e.accept(i) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (i *Interpreter) visitBinary(b *Binary) any { | func (i *Interpreter) visitBinary(b *Binary) any { | ||||||
| 	left := i.evaluate(b.left) | 	left := i.evaluate(b.left) | ||||||
| 	right := i.evaluate(b.right) | 	right := i.evaluate(b.right) | ||||||
| 
 | 
 | ||||||
| 	switch b.op.typ { | 	switch b.op.typ { | ||||||
| 	case MINUS: | 	case MINUS: | ||||||
| 		checkIfFloats(b.op, left, right) | 		i.checkIfFloats(b.op, left, right) | ||||||
| 		return left.(float64) - right.(float64) | 		return left.(float64) - right.(float64) | ||||||
| 	case SLASH: | 	case SLASH: | ||||||
| 		checkIfFloats(b.op, left, right) | 		i.checkIfFloats(b.op, left, right) | ||||||
| 		return left.(float64) / right.(float64) | 		return left.(float64) / right.(float64) | ||||||
| 	case STAR: | 	case STAR: | ||||||
| 		checkIfFloats(b.op, left, right) | 		i.checkIfFloats(b.op, left, right) | ||||||
| 		return left.(float64) * right.(float64) | 		return left.(float64) * right.(float64) | ||||||
| 	case GREATER: | 	case GREATER: | ||||||
| 		checkIfFloats(b.op, left, right) | 		i.checkIfFloats(b.op, left, right) | ||||||
| 		return left.(float64) > right.(float64) | 		return left.(float64) > right.(float64) | ||||||
| 	case LESS: | 	case LESS: | ||||||
| 		checkIfFloats(b.op, left, right) | 		i.checkIfFloats(b.op, left, right) | ||||||
| 		return left.(float64) < right.(float64) | 		return left.(float64) < right.(float64) | ||||||
| 	case GREATER_EQUAL: | 	case GREATER_EQUAL: | ||||||
| 		checkIfFloats(b.op, left, right) | 		i.checkIfFloats(b.op, left, right) | ||||||
| 		return left.(float64) >= right.(float64) | 		return left.(float64) >= right.(float64) | ||||||
| 	case LESS_EQUAL: | 	case LESS_EQUAL: | ||||||
| 		checkIfFloats(b.op, left, right) | 		i.checkIfFloats(b.op, left, right) | ||||||
| 		return left.(float64) <= right.(float64) | 		return left.(float64) <= right.(float64) | ||||||
| 	case BANG_EQUAL: | 	case BANG_EQUAL: | ||||||
| 		return !reflect.DeepEqual(left, right) | 		return !reflect.DeepEqual(left, right) | ||||||
|  | @ -78,7 +90,7 @@ func (i *Interpreter) visitBinary(b *Binary) any { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	panic(RuntimeError{b.op, fmt.Sprintf("Operands must be numbers or strings: %v %s %v", left, b.op.lexeme, right)}) | 	i.panic(&RuntimeError{b.op, fmt.Sprintf("Operands must be numbers or strings: %v %s %v", left, b.op.lexeme, right)}) | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  | @ -96,7 +108,7 @@ func (i *Interpreter) visitUnary(u *Unary) any { | ||||||
| 
 | 
 | ||||||
| 	switch u.op.typ { | 	switch u.op.typ { | ||||||
| 	case MINUS: | 	case MINUS: | ||||||
| 		checkIfFloat(u.op, val) | 		i.checkIfFloat(u.op, val) | ||||||
| 		return -val.(float64) | 		return -val.(float64) | ||||||
| 	case BANG: | 	case BANG: | ||||||
| 		return !isTruthy(val) | 		return !isTruthy(val) | ||||||
|  | @ -105,20 +117,67 @@ func (i *Interpreter) visitUnary(u *Unary) any { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func checkIfFloat(op Token, val any) { | func (i *Interpreter) visitVariable(v *Variable) any { | ||||||
|  | 	if found, ok := i.env[v.name.lexeme]; ok { | ||||||
|  | 		return found | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	i.panic(&RuntimeError{v.name, fmt.Sprintf("Undefined variable '%s'.", v.name.lexeme)}) | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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)}) | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (i *Interpreter) visitPrintStmt(p *PrintStmt) { | ||||||
|  | 	fmt.Printf("%v\n", i.evaluate(p.val)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (i *Interpreter) visitExprStmt(se *ExprStmt) { | ||||||
|  | 	i.evaluate(se.expr) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (i *Interpreter) visitVarStmt(v *VarStmt) { | ||||||
|  | 
 | ||||||
|  | 	var val any = nil | ||||||
|  | 
 | ||||||
|  | 	if v.initializer != nil { | ||||||
|  | 		val = i.evaluate(v.initializer) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	i.env[v.name.lexeme] = val | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (i *Interpreter) panic(re *RuntimeError) { | ||||||
|  | 	i.errors = append(i.errors, re) | ||||||
|  | 	log.Println(re) | ||||||
|  | 	panic(re) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (i *Interpreter) checkIfFloat(op Token, val any) { | ||||||
| 	if _, ok := val.(float64); ok { | 	if _, ok := val.(float64); ok { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	panic(RuntimeError{op, "value must ne a number."}) | 	i.panic(&RuntimeError{op, "value must be a number."}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func checkIfFloats(op Token, a any, b any) { | func (i *Interpreter) checkIfFloats(op Token, a any, b any) { | ||||||
| 	if isFloats(a, b) { | 	if isFloats(a, b) { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	panic(RuntimeError{op, fmt.Sprintf("Operands must be numbers: %v %s %v", a, op.lexeme, b)}) | 	i.panic(&RuntimeError{op, fmt.Sprintf("Operands must be numbers: %v %s %v", a, op.lexeme, b)}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func isFloats(a any, b any) bool { | func isFloats(a any, b any) bool { | ||||||
|  |  | ||||||
							
								
								
									
										134
									
								
								parser.go
									
										
									
									
									
								
							
							
						
						
									
										134
									
								
								parser.go
									
										
									
									
									
								
							|  | @ -2,11 +2,13 @@ package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"log" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type Parser struct { | type Parser struct { | ||||||
| 	tokens  []Token | 	tokens  []Token | ||||||
| 	current int | 	current int | ||||||
|  | 	errors  []error | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type ParseError struct { | type ParseError struct { | ||||||
|  | @ -14,8 +16,8 @@ type ParseError struct { | ||||||
| 	message string | 	message string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (pe ParseError) Error() string { | func (pe *ParseError) Error() string { | ||||||
| 	return fmt.Sprintf("%s: %s", pe.token.lexeme, pe.message) | 	return fmt.Sprintf("ParseError [%d][%s]: %s", pe.token.line, pe.token.typ, pe.message) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newParser(tokens []Token) *Parser { | func newParser(tokens []Token) *Parser { | ||||||
|  | @ -25,21 +27,88 @@ func newParser(tokens []Token) *Parser { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *Parser) parse() Expr { | // program -> declaration* EOF | ||||||
|  | func (p *Parser) parse() ([]Stmt, []error) { | ||||||
| 	defer p.recover() | 	defer p.recover() | ||||||
| 	return p.expression() |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| func (p *Parser) recover() { | 	stmts := []Stmt{} | ||||||
| 	if err := recover(); err != nil { | 
 | ||||||
| 		pe := err.(ParseError) | 	for !p.isAtEnd() { | ||||||
| 		printError(pe.token, pe.message) | 
 | ||||||
|  | 		if stmt := p.declaration(); stmt != nil { | ||||||
|  | 			stmts = append(stmts, stmt) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| // expression -> equality | 	return stmts, p.errors | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // declaration -> varDecl | statement | ||||||
|  | func (p *Parser) declaration() Stmt { | ||||||
|  | 	defer p.synchronize() | ||||||
|  | 	if p.match(VAR) { | ||||||
|  | 		return p.varDecl() | ||||||
|  | 	} | ||||||
|  | 	return p.statement() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // varDecl -> "var" IDENTIFIER ("=" expression)? ";" | ||||||
|  | func (p *Parser) varDecl() Stmt { | ||||||
|  | 	name := p.consume(IDENTIFIER, "expect identifier for variable") | ||||||
|  | 
 | ||||||
|  | 	var initializer Expr = nil | ||||||
|  | 	if p.match(EQUAL) { | ||||||
|  | 		initializer = p.expression() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	p.consume(SEMICOLON, "Expect ';' after expression.") | ||||||
|  | 
 | ||||||
|  | 	return &VarStmt{name, initializer} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // statement -> exprStmt | printStmt | ||||||
|  | func (p *Parser) statement() Stmt { | ||||||
|  | 	if p.match(PRINT) { | ||||||
|  | 		return p.printStmt() | ||||||
|  | 	} | ||||||
|  | 	return p.exprStmt() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // exprStmt -> expression ";" | ||||||
|  | func (p *Parser) exprStmt() Stmt { | ||||||
|  | 	expr := p.expression() | ||||||
|  | 	p.consume(SEMICOLON, "Expect ';' after expression.") | ||||||
|  | 	return &ExprStmt{expr} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // printStmt -> "print" expression ";" | ||||||
|  | func (p *Parser) printStmt() Stmt { | ||||||
|  | 	expr := p.expression() | ||||||
|  | 	p.consume(SEMICOLON, "Expect ';' after expression.") | ||||||
|  | 	return &PrintStmt{expr} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // expression -> assignment | ||||||
| func (p *Parser) expression() Expr { | func (p *Parser) expression() Expr { | ||||||
| 	return p.equality() | 	return p.assignment() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // assignment -> IDENTIFIER "=" assignment | equality | ||||||
|  | func (p *Parser) assignment() Expr { | ||||||
|  | 	expr := p.equality() | ||||||
|  | 
 | ||||||
|  | 	if p.match(EQUAL) { | ||||||
|  | 		eq := p.previous() | ||||||
|  | 		val := p.assignment() | ||||||
|  | 
 | ||||||
|  | 		if variable, ok := expr.(*Variable); ok { | ||||||
|  | 			return &Assign{variable.name, val} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		p.panic(&ParseError{eq, "Invalid assignment target."}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return expr | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // equality -> comparison ( ( "==" | "!=" ) comparison )* | // equality -> comparison ( ( "==" | "!=" ) comparison )* | ||||||
|  | @ -105,7 +174,7 @@ func (p *Parser) unary() Expr { | ||||||
| 	return p.primary() | 	return p.primary() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // primary -> NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" | // primary -> NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" | IDENTIFIER | ||||||
| func (p *Parser) primary() Expr { | func (p *Parser) primary() Expr { | ||||||
| 	switch { | 	switch { | ||||||
| 	case p.match(FALSE): | 	case p.match(FALSE): | ||||||
|  | @ -120,13 +189,17 @@ func (p *Parser) primary() Expr { | ||||||
| 		return &Literal{p.previous().literal} | 		return &Literal{p.previous().literal} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if p.match(IDENTIFIER) { | ||||||
|  | 		return &Variable{p.previous()} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if p.match(LEFT_PAREN) { | 	if p.match(LEFT_PAREN) { | ||||||
| 		expr := p.expression() | 		expr := p.expression() | ||||||
| 		p.consume(RIGHT_PAREN, "Expect ')' after expression") | 		p.consume(RIGHT_PAREN, "Expect ')' after expression") | ||||||
| 		return &Grouping{expr} | 		return &Grouping{expr} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	panic(ParseError{p.peek(), "Expect expression"}) | 	p.panic(&ParseError{p.peek(), "Expect expression"}) | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  | @ -176,12 +249,20 @@ func (p *Parser) consume(typ TokenType, mes string) Token { | ||||||
| 		return p.advance() | 		return p.advance() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	panic(ParseError{p.peek(), mes}) | 	p.panic(&ParseError{p.peek(), mes}) | ||||||
| 
 | 
 | ||||||
| 	return Token{} | 	return Token{} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *Parser) synchronize() { | func (p *Parser) synchronize() { | ||||||
|  | 	err := recover() | ||||||
|  | 
 | ||||||
|  | 	pe := p.isParseError(err) | ||||||
|  | 
 | ||||||
|  | 	if pe == nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	p.advance() | 	p.advance() | ||||||
| 
 | 
 | ||||||
| 	for !p.isAtEnd() { | 	for !p.isAtEnd() { | ||||||
|  | @ -196,4 +277,29 @@ func (p *Parser) synchronize() { | ||||||
| 
 | 
 | ||||||
| 		p.advance() | 		p.advance() | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *Parser) recover() { | ||||||
|  | 	p.isParseError(recover()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *Parser) panic(pe *ParseError) { | ||||||
|  | 	p.errors = append(p.errors, pe) | ||||||
|  | 	log.Println(pe) | ||||||
|  | 	panic(pe) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *Parser) isParseError(err any) *ParseError { | ||||||
|  | 	if err == nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	pe, ok := err.(*ParseError) | ||||||
|  | 
 | ||||||
|  | 	if !ok { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return pe | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										34
									
								
								scanner.go
									
										
									
									
									
								
							
							
						
						
									
										34
									
								
								scanner.go
									
										
									
									
									
								
							|  | @ -2,6 +2,7 @@ package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"log" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"unicode" | 	"unicode" | ||||||
|  | @ -91,19 +92,29 @@ func (t *Token) String() string { | ||||||
| 	return fmt.Sprintf("%s - %s - %v", t.typ, t.lexeme, t.literal) | 	return fmt.Sprintf("%s - %s - %v", t.typ, t.lexeme, t.literal) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | type ScanError struct { | ||||||
|  | 	line    int | ||||||
|  | 	message string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (se *ScanError) Error() string { | ||||||
|  | 	return fmt.Sprintf("ScanError [%d] Error: %s", se.line, se.message) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type Scanner struct { | type Scanner struct { | ||||||
| 	source  []byte | 	source  []byte | ||||||
| 	tokens  []Token | 	tokens  []Token | ||||||
| 	start   int | 	start   int | ||||||
| 	current int | 	current int | ||||||
| 	line    int | 	line    int | ||||||
|  | 	err     error | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newScanner(source []byte) *Scanner { | func newScanner(source []byte) *Scanner { | ||||||
| 	return &Scanner{source: source, start: 0, current: 0, line: 1} | 	return &Scanner{source: source, start: 0, current: 0, line: 1, err: nil} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Scanner) scan() []Token { | func (s *Scanner) scan() ([]Token, error) { | ||||||
| 
 | 
 | ||||||
| 	for !s.isAtEnd() { | 	for !s.isAtEnd() { | ||||||
| 		s.start = s.current | 		s.start = s.current | ||||||
|  | @ -112,7 +123,7 @@ func (s *Scanner) scan() []Token { | ||||||
| 
 | 
 | ||||||
| 	s.tokens = append(s.tokens, Token{EOF, "EOF", struct{}{}, s.line}) | 	s.tokens = append(s.tokens, Token{EOF, "EOF", struct{}{}, s.line}) | ||||||
| 
 | 
 | ||||||
| 	return s.tokens | 	return s.tokens, s.err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *Scanner) scanToken() { | func (s *Scanner) scanToken() { | ||||||
|  | @ -191,7 +202,7 @@ func (s *Scanner) scanToken() { | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		report(s.line, "", fmt.Sprintf("Unexpected character %s", string(c))) | 		s.error(&ScanError{s.line, fmt.Sprintf("Unexpected character %s", string(c))}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -205,12 +216,12 @@ func (s *Scanner) identifier() { | ||||||
| 		s.advance() | 		s.advance() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	str := s.source[s.start:s.current] | 	str := string(s.source[s.start:s.current]) | ||||||
| 
 | 
 | ||||||
| 	if id, found := keywords[string(str)]; found { | 	if id, found := keywords[string(str)]; found { | ||||||
| 		s.addToken(id, struct{}{}) | 		s.addToken(id, str) | ||||||
| 	} else { | 	} else { | ||||||
| 		s.addToken(IDENTIFIER, struct{}{}) | 		s.addToken(IDENTIFIER, str) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | @ -224,7 +235,7 @@ func (s *Scanner) string() { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if s.isAtEnd() { | 	if s.isAtEnd() { | ||||||
| 		report(s.line, "", "Unterminated string") | 		s.error(&ScanError{s.line, "Unterminated string"}) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -249,7 +260,7 @@ func (s *Scanner) number() { | ||||||
| 	num, err := strconv.ParseFloat(string(s.source[s.start:s.current]), 64) | 	num, err := strconv.ParseFloat(string(s.source[s.start:s.current]), 64) | ||||||
| 
 | 
 | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		report(s.line, "", err.Error()) | 		s.error(&ScanError{s.line, err.Error()}) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	s.addToken(NUMBER, num) | 	s.addToken(NUMBER, num) | ||||||
|  | @ -298,3 +309,8 @@ func (s *Scanner) match(ch rune) bool { | ||||||
| func (s *Scanner) isAtEnd() bool { | func (s *Scanner) isAtEnd() bool { | ||||||
| 	return s.current >= len(s.source) | 	return s.current >= len(s.source) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (s *Scanner) error(err *ScanError) { | ||||||
|  | 	log.Print(err) | ||||||
|  | 	s.err = err | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										41
									
								
								stmt.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								stmt.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | type StmtVisitor interface { | ||||||
|  | 	visitPrintStmt(p *PrintStmt) | ||||||
|  | 	visitExprStmt(es *ExprStmt) | ||||||
|  | 	visitVarStmt(v *VarStmt) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Stmt interface { | ||||||
|  | 	stmt() | ||||||
|  | 	accept(v StmtVisitor) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type PrintStmt struct { | ||||||
|  | 	val Expr | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ExprStmt struct { | ||||||
|  | 	expr Expr | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type VarStmt struct { | ||||||
|  | 	name        Token | ||||||
|  | 	initializer Expr | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *PrintStmt) stmt() {} | ||||||
|  | func (es *ExprStmt) stmt() {} | ||||||
|  | func (vs *VarStmt) stmt()  {} | ||||||
|  | 
 | ||||||
|  | func (p *PrintStmt) accept(v StmtVisitor) { | ||||||
|  | 	v.visitPrintStmt(p) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (se *ExprStmt) accept(v StmtVisitor) { | ||||||
|  | 	v.visitExprStmt(se) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (vs *VarStmt) accept(v StmtVisitor) { | ||||||
|  | 	v.visitVarStmt(vs) | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue