interpreter
This commit is contained in:
parent
680df31650
commit
50ecb3dc2e
7 changed files with 302 additions and 115 deletions
127
ast.go
127
ast.go
|
@ -1,20 +1,20 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Visitor interface {
|
type Visitor interface {
|
||||||
visitBinary(b *Binary)
|
visitUnary(u *Unary) any
|
||||||
visitLiteral(l *Literal)
|
visitBinary(b *Binary) any
|
||||||
visitGrouping(g *Grouping)
|
visitLiteral(l *Literal) any
|
||||||
visitUnary(u *Unary)
|
visitGrouping(g *Grouping) any
|
||||||
}
|
}
|
||||||
|
|
||||||
type Expr interface {
|
type Expr interface {
|
||||||
expr()
|
expr()
|
||||||
accept(v Visitor)
|
accept(v Visitor) any
|
||||||
|
}
|
||||||
|
|
||||||
|
type Unary struct {
|
||||||
|
op Token
|
||||||
|
right Expr
|
||||||
}
|
}
|
||||||
|
|
||||||
type Binary struct {
|
type Binary struct {
|
||||||
|
@ -23,114 +23,31 @@ type Binary struct {
|
||||||
right Expr
|
right Expr
|
||||||
}
|
}
|
||||||
|
|
||||||
type Unary struct {
|
type Literal struct {
|
||||||
op Token
|
value any
|
||||||
right Expr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Grouping struct {
|
type Grouping struct {
|
||||||
expression Expr
|
expression Expr
|
||||||
}
|
}
|
||||||
|
|
||||||
type Literal struct {
|
|
||||||
value any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *Unary) expr() {}
|
func (u *Unary) expr() {}
|
||||||
func (g *Grouping) expr() {}
|
|
||||||
func (l *Literal) expr() {}
|
|
||||||
func (b *Binary) expr() {}
|
func (b *Binary) expr() {}
|
||||||
|
func (l *Literal) expr() {}
|
||||||
|
func (g *Grouping) expr() {}
|
||||||
|
|
||||||
func (u *Unary) accept(v Visitor) {
|
func (u *Unary) accept(v Visitor) any {
|
||||||
v.visitUnary(u)
|
return v.visitUnary(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Grouping) accept(v Visitor) {
|
func (b *Binary) accept(v Visitor) any {
|
||||||
v.visitGrouping(g)
|
return v.visitBinary(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Literal) accept(v Visitor) {
|
func (l *Literal) accept(v Visitor) any {
|
||||||
v.visitLiteral(l)
|
return v.visitLiteral(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Binary) accept(v Visitor) {
|
func (g *Grouping) accept(v Visitor) any {
|
||||||
v.visitBinary(b)
|
return v.visitGrouping(g)
|
||||||
}
|
|
||||||
|
|
||||||
type AstStringer struct {
|
|
||||||
str strings.Builder
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as AstStringer) String(expr Expr) string {
|
|
||||||
|
|
||||||
if expr == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
expr.accept(&as)
|
|
||||||
return as.str.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *AstStringer) visitBinary(b *Binary) {
|
|
||||||
as.str.WriteString("(")
|
|
||||||
as.str.WriteString(b.op.lexeme)
|
|
||||||
as.str.WriteString(" ")
|
|
||||||
b.left.accept(as)
|
|
||||||
as.str.WriteString(" ")
|
|
||||||
b.right.accept(as)
|
|
||||||
as.str.WriteString(")")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *AstStringer) visitLiteral(l *Literal) {
|
|
||||||
as.str.WriteString(fmt.Sprintf("%v", l.value))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *AstStringer) visitGrouping(g *Grouping) {
|
|
||||||
as.str.WriteString("(group ")
|
|
||||||
g.expression.accept(as)
|
|
||||||
as.str.WriteString(")")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *AstStringer) visitUnary(u *Unary) {
|
|
||||||
as.str.WriteString(fmt.Sprintf("(%s ", u.op.lexeme))
|
|
||||||
u.right.accept(as)
|
|
||||||
as.str.WriteString(")")
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
b.left.accept(as)
|
|
||||||
as.str.WriteString(" ")
|
|
||||||
b.right.accept(as)
|
|
||||||
as.str.WriteString(" ")
|
|
||||||
as.str.WriteString(b.op.lexeme)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *AstToRPN) visitLiteral(l *Literal) {
|
|
||||||
as.str.WriteString(fmt.Sprintf("%v", l.value))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *AstToRPN) visitGrouping(g *Grouping) {
|
|
||||||
g.expression.accept(as)
|
|
||||||
as.str.WriteString(" group")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *AstToRPN) visitUnary(u *Unary) {
|
|
||||||
u.right.accept(as)
|
|
||||||
as.str.WriteString(fmt.Sprintf(" %s", u.op.lexeme))
|
|
||||||
}
|
}
|
||||||
|
|
46
ast_rpn.go
Normal file
46
ast_rpn.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
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
|
||||||
|
}
|
51
ast_string.go
Normal file
51
ast_string.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AstStringer struct {
|
||||||
|
str strings.Builder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as AstStringer) String(expr Expr) string {
|
||||||
|
|
||||||
|
if expr == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
expr.accept(&as)
|
||||||
|
return as.str.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *AstStringer) visitBinary(b *Binary) any {
|
||||||
|
as.str.WriteString("(")
|
||||||
|
as.str.WriteString(b.op.lexeme)
|
||||||
|
as.str.WriteString(" ")
|
||||||
|
b.left.accept(as)
|
||||||
|
as.str.WriteString(" ")
|
||||||
|
b.right.accept(as)
|
||||||
|
as.str.WriteString(")")
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *AstStringer) visitLiteral(l *Literal) any {
|
||||||
|
as.str.WriteString(fmt.Sprintf("%v", l.value))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *AstStringer) visitGrouping(g *Grouping) any {
|
||||||
|
as.str.WriteString("(group ")
|
||||||
|
g.expression.accept(as)
|
||||||
|
as.str.WriteString(")")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *AstStringer) visitUnary(u *Unary) any {
|
||||||
|
as.str.WriteString(fmt.Sprintf("(%s ", u.op.lexeme))
|
||||||
|
u.right.accept(as)
|
||||||
|
as.str.WriteString(")")
|
||||||
|
return nil
|
||||||
|
}
|
6
error.go
6
error.go
|
@ -6,6 +6,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var hadError = false
|
var hadError = false
|
||||||
|
var hadRuntimeError = false
|
||||||
|
|
||||||
func printError(token Token, message string) {
|
func printError(token Token, message string) {
|
||||||
if token.typ == EOF {
|
if token.typ == EOF {
|
||||||
|
@ -19,3 +20,8 @@ func report(line int, where string, message string) {
|
||||||
log.Printf("[%d] Error %s: %s", line, where, message)
|
log.Printf("[%d] Error %s: %s", line, where, message)
|
||||||
hadError = true
|
hadError = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func reportRuntimeError(token Token, message string) {
|
||||||
|
log.Printf("[%d] Error: %s", token.line, message)
|
||||||
|
hadRuntimeError = true
|
||||||
|
}
|
||||||
|
|
28
glox.go
28
glox.go
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
@ -31,6 +32,7 @@ func runPrompt() {
|
||||||
}
|
}
|
||||||
run([]byte(scanner.Text()))
|
run([]byte(scanner.Text()))
|
||||||
hadError = false
|
hadError = false
|
||||||
|
hadRuntimeError = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,15 +42,39 @@ func runFile(path string) {
|
||||||
try(err)
|
try(err)
|
||||||
|
|
||||||
run(file)
|
run(file)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case hadError:
|
||||||
|
os.Exit(65)
|
||||||
|
case hadRuntimeError:
|
||||||
|
os.Exit(70)
|
||||||
|
default:
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(source []byte) {
|
func run(source []byte) {
|
||||||
tokens := newScanner(source).scan()
|
tokens := newScanner(source).scan()
|
||||||
|
|
||||||
|
if hadError {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ast := newParser(tokens).parse()
|
ast := newParser(tokens).parse()
|
||||||
|
|
||||||
|
if hadError {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
println(AstStringer{}.String(ast))
|
println(AstStringer{}.String(ast))
|
||||||
println(AstToRPN{}.String(ast))
|
|
||||||
|
res := newInterpreter().evaluate(ast)
|
||||||
|
|
||||||
|
if hadRuntimeError {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%v\n", res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func try(err error) {
|
func try(err error) {
|
||||||
|
|
148
interpreter.go
Normal file
148
interpreter.go
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Interpreter struct{}
|
||||||
|
|
||||||
|
type RuntimeError struct {
|
||||||
|
token Token
|
||||||
|
msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (re RuntimeError) Error() string {
|
||||||
|
return re.msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInterpreter() *Interpreter {
|
||||||
|
return &Interpreter{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interpreter) evaluate(e Expr) any {
|
||||||
|
defer i.recover()
|
||||||
|
return e.accept(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interpreter) recover() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
pe, ok := err.(RuntimeError)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reportRuntimeError(pe.token, pe.msg)
|
||||||
|
hadRuntimeError = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interpreter) visitBinary(b *Binary) any {
|
||||||
|
left := i.evaluate(b.left)
|
||||||
|
right := i.evaluate(b.right)
|
||||||
|
|
||||||
|
switch b.op.typ {
|
||||||
|
case MINUS:
|
||||||
|
checkIfFloats(b.op, left, right)
|
||||||
|
return left.(float64) - right.(float64)
|
||||||
|
case SLASH:
|
||||||
|
checkIfFloats(b.op, left, right)
|
||||||
|
return left.(float64) / right.(float64)
|
||||||
|
case STAR:
|
||||||
|
checkIfFloats(b.op, left, right)
|
||||||
|
return left.(float64) * right.(float64)
|
||||||
|
case GREATER:
|
||||||
|
checkIfFloats(b.op, left, right)
|
||||||
|
return left.(float64) > right.(float64)
|
||||||
|
case LESS:
|
||||||
|
checkIfFloats(b.op, left, right)
|
||||||
|
return left.(float64) < right.(float64)
|
||||||
|
case GREATER_EQUAL:
|
||||||
|
checkIfFloats(b.op, left, right)
|
||||||
|
return left.(float64) >= right.(float64)
|
||||||
|
case LESS_EQUAL:
|
||||||
|
checkIfFloats(b.op, left, right)
|
||||||
|
return left.(float64) <= right.(float64)
|
||||||
|
case BANG_EQUAL:
|
||||||
|
return !reflect.DeepEqual(left, right)
|
||||||
|
case EQUAL_EQUAL:
|
||||||
|
return reflect.DeepEqual(left, right)
|
||||||
|
case PLUS:
|
||||||
|
if isFloats(left, right) {
|
||||||
|
return left.(float64) + right.(float64)
|
||||||
|
}
|
||||||
|
|
||||||
|
if isStrings(left, right) {
|
||||||
|
return left.(string) + right.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(RuntimeError{b.op, fmt.Sprintf("Operands must be numbers or strings: %v %s %v", left, b.op.lexeme, right)})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interpreter) visitLiteral(l *Literal) any {
|
||||||
|
return l.value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interpreter) visitGrouping(g *Grouping) any {
|
||||||
|
return i.evaluate(g.expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interpreter) visitUnary(u *Unary) any {
|
||||||
|
val := i.evaluate(u.right)
|
||||||
|
|
||||||
|
switch u.op.typ {
|
||||||
|
case MINUS:
|
||||||
|
checkIfFloat(u.op, val)
|
||||||
|
return -val.(float64)
|
||||||
|
case BANG:
|
||||||
|
return !isTruthy(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkIfFloat(op Token, val any) {
|
||||||
|
if _, ok := val.(float64); ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(RuntimeError{op, "value must ne a number."})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkIfFloats(op Token, a any, b any) {
|
||||||
|
if isFloats(a, b) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(RuntimeError{op, fmt.Sprintf("Operands must be numbers: %v %s %v", a, op.lexeme, b)})
|
||||||
|
}
|
||||||
|
|
||||||
|
func isFloats(a any, b any) bool {
|
||||||
|
ltype := reflect.TypeOf(a)
|
||||||
|
rtype := reflect.TypeOf(b)
|
||||||
|
|
||||||
|
return ltype.Kind() == rtype.Kind() && ltype.Kind() == reflect.Float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func isStrings(a any, b any) bool {
|
||||||
|
ltype := reflect.TypeOf(a)
|
||||||
|
rtype := reflect.TypeOf(b)
|
||||||
|
|
||||||
|
return ltype.Kind() == rtype.Kind() && ltype.Kind() == reflect.String
|
||||||
|
}
|
||||||
|
|
||||||
|
func isTruthy(val any) bool {
|
||||||
|
if val == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if boolean, ok := val.(bool); ok {
|
||||||
|
return boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
11
parser.go
11
parser.go
|
@ -88,7 +88,7 @@ func (p *Parser) factor() Expr {
|
||||||
for p.match(SLASH, STAR) {
|
for p.match(SLASH, STAR) {
|
||||||
op := p.previous()
|
op := p.previous()
|
||||||
right := p.unary()
|
right := p.unary()
|
||||||
exp = &Unary{op, right}
|
exp = &Binary{exp, op, right}
|
||||||
}
|
}
|
||||||
|
|
||||||
return exp
|
return exp
|
||||||
|
@ -190,14 +190,7 @@ func (p *Parser) synchronize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch p.peek().typ {
|
switch p.peek().typ {
|
||||||
case CLASS:
|
case CLASS, FOR, FUN, IF, PRINT, RETURN, VAR, WHILE:
|
||||||
case FOR:
|
|
||||||
case FUN:
|
|
||||||
case IF:
|
|
||||||
case PRINT:
|
|
||||||
case RETURN:
|
|
||||||
case VAR:
|
|
||||||
case WHILE:
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue