From 7e49d405f5521c4f501c6f6172c826ea13366211 Mon Sep 17 00:00:00 2001 From: Greg Date: Mon, 30 Sep 2024 21:46:31 +0300 Subject: [PATCH] partial ast --- .vscode/launch.json | 7 ++ .vscode/settings.json | 5 ++ README.md | 3 + glox.go | 164 ++++++++++++++++++++++++++++++------------ 4 files changed, 135 insertions(+), 44 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 README.md diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5c7247b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,7 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1b49d42 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cSpell.words": [ + "glox" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..027fc60 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# GLOX + +Lox interpreter written i Go. \ No newline at end of file diff --git a/glox.go b/glox.go index 0b796b2..82721aa 100644 --- a/glox.go +++ b/glox.go @@ -7,23 +7,72 @@ import ( "os" "regexp" "strconv" + "strings" "unicode" "unicode/utf8" ) func main() { - switch len(os.Args) { - case 1: - runPrompt() - case 2: - runFile(os.Args[1]) - default: - println("Usage: glox [file]") + expr := &Binary{ + left: &Literal{value: 1}, + op: Token{typ: PLUS, lexeme: "+", literal: "", line: 1}, + right: &Binary{ + left: &Literal{value: 3}, + op: Token{typ: PLUS, lexeme: "+", literal: "", line: 1}, + right: &Literal{value: 4}, + }, + } + + println((&AstStringer{}).String(expr)) + + // switch len(os.Args) { + // case 1: + // runPrompt() + // case 2: + // runFile(os.Args[1]) + // default: + // println("Usage: glox [file]") + // os.Exit(1) + // } +} + +func runPrompt() { + scanner := bufio.NewScanner(os.Stdin) + scanner.Split(bufio.ScanLines) + + for { + print("> ") + scanner.Scan() + line := scanner.Text() + if len(line) == 0 { + break + } + run([]byte(scanner.Text())) + hadError = false + } +} + +func runFile(path string) { + file, err := os.ReadFile(path) + + panic(err) + + run(file) + + if hadError { os.Exit(1) } } +func run(source []byte) { + tokens := newScanner(source).scan() + + for _, token := range tokens { + println(token.String()) + } +} + var hadError = false //go:generate go run golang.org/x/tools/cmd/stringer -type=TokenType @@ -98,6 +147,69 @@ var keywords = map[string]TokenType{ "while": WHILE, } +type Expr interface { + expr() + accept(v Visitor) +} + +type Unary struct { + op Token + right Expr +} + +type Grouping struct { + expr Expr +} + +type Literal struct { + value any +} + +func (l *Literal) expr() {} +func (l *Literal) accept(v Visitor) { + v.visitLiteral(l) +} + +type Binary struct { + left Expr + op Token + right Expr +} + +func (b *Binary) expr() {} +func (b *Binary) accept(v Visitor) { + v.visitBinary(b) +} + +type Visitor interface { + visitBinary(b *Binary) + visitLiteral(l *Literal) +} + +type AstStringer struct { + str strings.Builder +} + +func (as *AstStringer) String(expr Expr) string { + 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)) +} + type Token struct { typ TokenType lexeme string @@ -105,7 +217,7 @@ type Token struct { line int } -func (t *Token) string() string { +func (t *Token) String() string { return fmt.Sprintf("%s - %s - %v", t.typ, t.lexeme, t.literal) } @@ -322,39 +434,3 @@ func panic(err error) { log.Fatal(err) } } - -func runPrompt() { - scanner := bufio.NewScanner(os.Stdin) - scanner.Split(bufio.ScanLines) - - for { - print("> ") - scanner.Scan() - line := scanner.Text() - if len(line) == 0 { - break - } - run([]byte(scanner.Text())) - hadError = false - } -} - -func runFile(path string) { - file, err := os.ReadFile(path) - - panic(err) - - run(file) - - if hadError { - os.Exit(1) - } -} - -func run(source []byte) { - tokens := newScanner(source).scan() - - for _, token := range tokens { - println(token.string()) - } -}