variables

This commit is contained in:
Greg 2024-10-05 18:58:49 +03:00
parent 50ecb3dc2e
commit 6fad12456c
14 changed files with 485 additions and 228 deletions

10
.vscode/launch.json vendored
View file

@ -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
}
]
} }

View file

@ -1,5 +1,7 @@
{ {
"cSpell.words": [ "cSpell.words": [
"glox" "glox",
"PAREN",
"stmts"
] ]
} }

53
ast.go
View file

@ -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)
}

View file

@ -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
}

View file

@ -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))
}
}

View file

@ -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
View 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
View 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

Binary file not shown.

90
glox.go
View file

@ -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)
} }

View file

@ -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
View file

@ -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)
}
} }
return stmts, p.errors
} }
// expression -> equality // 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
} }

View file

@ -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
View 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)
}