go-ruby-prism is a Go library that leverages the Ruby Prism parser compiled to WebAssembly, enabling Ruby code parsing and analysis without the need for CGO bindings. This solution provides native and efficient integration for Ruby code analysis in Go applications.
go get github.com/danielgatis/go-ruby-prism
git clone https://github.com/danielgatis/go-ruby-prism.git cd go-ruby-prism make all
package main import ( "context" "fmt" "log" parser "github.com/danielgatis/go-ruby-prism/parser" ) func main() { ctx := context.Background() // Create a new parser instance p, err := parser.NewParser(ctx) if err != nil { log.Fatal(err) } defer p.Close(ctx) // Ruby code to analyze source := "puts 'Hello, World!'" // Parse the code result, err := p.Parse(ctx, []byte(source)) if err != nil { log.Fatal(err) } fmt.Printf("AST Root: %T\n", result.Value) fmt.Printf("Errors: %d\n", len(result.Errors)) fmt.Printf("Warnings: %d\n", len(result.Warnings)) }1. Converting AST to JSON
package main import ( "context" "encoding/json" "fmt" "log" parser "github.com/danielgatis/go-ruby-prism/parser" ) func main() { ctx := context.Background() p, err := parser.NewParser(ctx) if err != nil { log.Fatal(err) } defer p.Close(ctx) source := ` class User attr_reader :name, :email def initialize(name, email) @name = name @email = email end def greet puts "Hello, #{@name}!" end end ` result, err := p.Parse(ctx, []byte(source)) if err != nil { log.Fatal(err) } // Convert to JSON jsonResult, err := json.MarshalIndent(result, "", " ") if err != nil { log.Fatal(err) } fmt.Println(string(jsonResult)) }2. Using the Visitor Pattern
package main import ( "context" "fmt" "log" "github.com/danielgatis/go-ruby-prism/parser" ) type CodeAnalyzer struct { parser.DefaultVisitor methodCount int classCount int } func (v *CodeAnalyzer) Visit(node parser.Node) { switch node.(type) { case *parser.DefNode: v.methodCount++ fmt.Printf("📍 Found method at line %d\n", node.StartLine()) case *parser.ClassNode: v.classCount++ fmt.Printf("📍 Found class at line %d\n", node.StartLine()) } v.DefaultVisitor.Visit(node) } func (v *CodeAnalyzer) Analyze(node parser.Node) { for _, child := range node.ChildNodes() { v.Visit(child) if len(child.ChildNodes()) > 0 { v.Analyze(child) } } } func main() { ctx := context.Background() p, err := parser.NewParser(ctx) if err != nil { log.Fatal(err) } defer p.Close(ctx) source := ` class Calculator def add(a, b) a + b end def multiply(a, b) a * b end end class MathUtils def self.pi 3.14159 end end ` result, err := p.Parse(ctx, []byte(source)) if err != nil { log.Fatal(err) } analyzer := &CodeAnalyzer{} analyzer.Analyze(result.Value) fmt.Printf("\n📊 Analysis complete:\n") fmt.Printf(" Classes found: %d\n", analyzer.classCount) fmt.Printf(" Methods found: %d\n", analyzer.methodCount) }3. Rails Application Analysis
The project includes a complete example that downloads and analyzes the entire Rails codebase:
go run example/parse_rails/main.go
This example demonstrates:
NewParser(ctx context.Context, options ...ParserOption) (*Parser, error)
Creates a new parser instance with the specified options.
Parse(ctx context.Context, source []byte) (*ParseResult, error)
Parses the provided Ruby code and returns the resulting AST.
Close(ctx context.Context) error
Releases WebAssembly runtime resources. Should always be called when the parser is no longer needed.
// Configure source file parser.WithFilePath("app/models/user.rb") // Configure starting line parser.WithLine(10) // Configure encoding parser.WithEncoding("UTF-8") // Enable frozen string literals parser.WithFrozenStringLiteral(true) // Configure Ruby version parser.WithVersion(parser.SyntaxVersionV3_4) // Configure as main script parser.WithMainScript(true) // Configure local variable scopes parser.WithScopes([][][]byte{{[]byte("local_var")}}) // Configure custom logger parser.WithLogger(customLogger)
The ParseResult
structure contains:
type ParseResult struct { Value Node // Root AST node Errors []Error // Parsing errors Warnings []Warning // Parser warnings Source []byte // Original source code }Supported Syntax Versions
const ( SyntaxVersionLatest SyntaxVersion = iota // Latest version SyntaxVersionV3_3 // Ruby 3.3 SyntaxVersionV3_4 // Ruby 3.4 )
type CustomLogger struct{} func (l *CustomLogger) Debug(format string, args ...interface{}) { log.Printf("[DEBUG] "+format, args...) } // Use custom logger p, err := parser.NewParser(ctx, parser.WithLogger(&CustomLogger{}))
p, err := parser.NewParser(ctx, parser.WithMainScript(true), parser.WithVersion(parser.SyntaxVersionV3_4), parser.WithFrozenStringLiteral(true), parser.WithEncoding("UTF-8"), )Parsing with File Context
p, err := parser.NewParser(ctx, parser.WithFilePath("app/controllers/users_controller.rb"), parser.WithLine(1), parser.WithMainScript(true), )
go-ruby-prism/
├── example/ # Usage examples
│ ├── json/ # JSON conversion
│ ├── parse_rails/ # Rails application analysis
│ └── visitor/ # Visitor pattern
├── parser/ # Main parser API
│ ├── parser.go # Main interface
│ ├── gen_nodes.go # Generated AST nodes
│ ├── gen_visitor.go # Generated visitor pattern
│ └── parsing_options.go # Configuration options
├── prism/ # Ruby Prism submodule
├── wasm/ # WebAssembly runtime
└── templates/ # Code generation templates
# Complete build (recommended for first time) make all # Generate Go code only make generate # Build WASM only make wasm_build # Format code make format # Clean temporary files make clean
git submodule update --init --recursive
go fmt
to all filestype ParserPool struct { parsers chan *parser.Parser ctx context.Context } func NewParserPool(ctx context.Context, size int) *ParserPool { pool := &ParserPool{ parsers: make(chan *parser.Parser, size), ctx: ctx, } for i := 0; i < size; i++ { p, _ := parser.NewParser(ctx) pool.parsers <- p } return pool } func (p *ParserPool) Parse(source []byte) (*parser.ParseResult, error) { parser := <-p.parsers defer func() { p.parsers <- parser }() return parser.Parse(p.ctx, source) }
All examples are available in the /example
directory:
example/json/
): Converts AST to JSON formatexample/visitor/
): Demonstrates custom AST traversalexample/parse_rails/
): Complete Rails application analysisTo run any example:
go run example/[example-name]/main.go
# Set up development environment git clone https://github.com/danielgatis/go-ruby-prism.git cd go-ruby-prism make all # Run tests go test ./... # Check linting golangci-lint run
Copyright (c) 2024-present Daniel Gatis
Licensed under MIT License
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4