Skip to content

Commit

Permalink
feat: add basic AST pretty printer implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Akash <[email protected]>
  • Loading branch information
SkySingh04 committed Jan 9, 2025
1 parent 1b84411 commit d717a4e
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 28 deletions.
3 changes: 2 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ operator → "==" | "!=" | "<" | "<=" | ">" | ">="
| "+" | "-" | "*" | "/" ;
```


We can run the script to autogenerate the ast's with `go run .\scripts\main.go` , which will generate the trees in `expr.go` package.
Similarly , we can run the pretty printer with ` go run .\printer\main.go` , to show a basic AST output of `(* (- 123) (group 45.67))`


#### Step 3 : Static Analysis
Expand Down
15 changes: 8 additions & 7 deletions expr/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ type Expr interface {
Accept(visitor ExprVisitor) interface{}
}

// ExprVisitor is the visitor interface for Expr types.
type ExprVisitor interface {
VisitBinaryExpr(*Binary) interface{}
VisitGroupingExpr(*Grouping) interface{}
VisitLiteralExpr(*Literal) interface{}
VisitUnaryExpr(*Unary) interface{}
}

// Binary represents the Binary expression type.
type Binary struct {
Left Expr
Expand Down Expand Up @@ -52,10 +60,3 @@ func (e *Unary) Accept(visitor ExprVisitor) interface{} {
return visitor.VisitUnaryExpr(e)
}

// ExprVisitor is the visitor interface for Expr types.
type ExprVisitor interface {
VisitBinaryExpr(*Binary) interface{}
VisitGroupingExpr(*Grouping) interface{}
VisitLiteralExpr(*Literal) interface{}
VisitUnaryExpr(*Unary) interface{}
}
86 changes: 86 additions & 0 deletions printer/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// package temporarily must be set to main to run this script

package printer

// package main

import (
"GoCrab/expr"
"GoCrab/lexer"
"fmt"
"strings"
)

func main() {

Check failure on line 14 in printer/main.go

View workflow job for this annotation

GitHub Actions / Linting

func `main` is unused (unused)
// Construct the AST manually.
expression := &expr.Binary{
Left: &expr.Unary{
Operator: lexer.Token{
Type: lexer.Minus,
Lexeme: "-",
Literal: nil,
Line: 1,
},
Right: &expr.Literal{Value: 123},
},
Operator: lexer.Token{
Type: lexer.Star,
Lexeme: "*",
Literal: nil,
Line: 1,
},
Right: &expr.Grouping{
Expression: &expr.Literal{Value: 45.67},
},
}

// Print the AST.
printer := AstPrinter{}
fmt.Println(printer.Print(expression))
}

// AstPrinter implements the ExprVisitor interface to print expressions.
type AstPrinter struct{}

// Print takes an Expr and returns its string representation.
func (p *AstPrinter) Print(expr expr.Expr) string {
return expr.Accept(p).(string)
}

// VisitBinaryExpr handles Binary expressions.
func (p *AstPrinter) VisitBinaryExpr(expr *expr.Binary) interface{} {
return p.parenthesize(expr.Operator.Lexeme, expr.Left, expr.Right)
}

// VisitGroupingExpr handles Grouping expressions.
func (p *AstPrinter) VisitGroupingExpr(expr *expr.Grouping) interface{} {
return p.parenthesize("group", expr.Expression)
}

// VisitLiteralExpr handles Literal expressions.
func (p *AstPrinter) VisitLiteralExpr(expr *expr.Literal) interface{} {
if expr.Value == nil {
return "nil"
}
return fmt.Sprintf("%v", expr.Value)
}

// VisitUnaryExpr handles Unary expressions.
func (p *AstPrinter) VisitUnaryExpr(expr *expr.Unary) interface{} {
return p.parenthesize(expr.Operator.Lexeme, expr.Right)
}

// parenthesize formats an expression in a Lisp-style string.
func (p *AstPrinter) parenthesize(name string, exprs ...expr.Expr) string {
var builder strings.Builder

builder.WriteString("(")
builder.WriteString(name)
for _, subExpr := range exprs {
builder.WriteString(" ")
builder.WriteString(subExpr.Accept(p).(string))
}
builder.WriteString(")")

return builder.String()
}
36 changes: 16 additions & 20 deletions scripts/main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// package temporarily must be set to main to run this script
// package scripts
package scripts

package main
// package main

import (
"fmt"
Expand All @@ -20,10 +20,6 @@ func main() {
}

func defineAst(outputDir string, baseName string, types []string) {

Check failure on line 22 in scripts/main.go

View workflow job for this annotation

GitHub Actions / Linting

func `defineAst` is unused (unused)
if err := os.MkdirAll(outputDir, 0755); err != nil {
fmt.Fprintf(os.Stderr, "Could not create directory: %v\n", err)
os.Exit(1)
}
path := fmt.Sprintf("%s/%s.go", outputDir, strings.ToLower(baseName))
file, err := os.Create(path)
if err != nil {
Expand All @@ -44,16 +40,29 @@ func defineAst(outputDir string, baseName string, types []string) {
file.WriteString(fmt.Sprintf("\tAccept(visitor %sVisitor) interface{}\n", baseName))
file.WriteString("}\n\n")

// Write the visitor interface.
defineVisitor(file, baseName, types)

// Write each subclass.
for _, typeDef := range types {
parts := strings.Split(typeDef, ":")
className := strings.TrimSpace(parts[0])
fields := strings.TrimSpace(parts[1])
defineType(file, baseName, className, fields)
}
}

func defineVisitor(file *os.File, baseName string, types []string) {

Check failure on line 55 in scripts/main.go

View workflow job for this annotation

GitHub Actions / Linting

func `defineVisitor` is unused (unused)
// Write the visitor interface.
defineVisitor(file, baseName, types)
file.WriteString(fmt.Sprintf("// %sVisitor is the visitor interface for %s types.\n", baseName, baseName))
file.WriteString(fmt.Sprintf("type %sVisitor interface {\n", baseName))

for _, typeDef := range types {
className := strings.TrimSpace(strings.Split(typeDef, ":")[0])
file.WriteString(fmt.Sprintf("\tVisit%s%s(*%s) interface{}\n", className, baseName, className))
}

file.WriteString("}\n\n")
}

func defineType(file *os.File, baseName, className, fieldList string) {

Check failure on line 68 in scripts/main.go

View workflow job for this annotation

GitHub Actions / Linting

func `defineType` is unused (unused)
Expand All @@ -77,16 +86,3 @@ func defineType(file *os.File, baseName, className, fieldList string) {
file.WriteString(fmt.Sprintf("\treturn visitor.Visit%s%s(e)\n", className, baseName))
file.WriteString("}\n\n")
}

func defineVisitor(file *os.File, baseName string, types []string) {
// Write the visitor interface.
file.WriteString(fmt.Sprintf("// %sVisitor is the visitor interface for %s types.\n", baseName, baseName))
file.WriteString(fmt.Sprintf("type %sVisitor interface {\n", baseName))

for _, typeDef := range types {
className := strings.TrimSpace(strings.Split(typeDef, ":")[0])
file.WriteString(fmt.Sprintf("\tVisit%s%s(*%s) interface{}\n", className, baseName, className))
}

file.WriteString("}\n")
}

0 comments on commit d717a4e

Please sign in to comment.