Warning
This is a work in progress! For updates on the ongoing development, follow me on Twitter or Bluesky, or watch the repository.
An experimental Go parser and WebAssembly compiler written in Rust. Goiaba translates Go source code into WebAssembly bytecode, enabling Go programs to run in web browsers and other WebAssembly environments.
Reasons why I am building it:
- Create a complete bindgen support for JavaScript (web/nodejs/bun/deno)
- Study of gollvm project
- It will be used by a code editor I am writing in Rust called Boo
- Well, mostly learn tbh
- Parse Go source code into an Abstract Syntax Tree (AST)
- Compile Go functions to WebAssembly modules
- Support for fundamental Go language features (functions, control flow, arithmetic)
- Export Go functions for use in JavaScript/WebAssembly environments
- Export Go functions for use in Rust through C ABI
- Export Go functions for use in Zig through C ABI
- Command-line interface for compilation
- Programmatic API for integration into Rust projects
cargo install goiabaAdd to your Cargo.toml:
[dependencies]
goiaba = "*"Basic compilation:
goiaba main.go -o main.wasmCompile with verbose output:
goiaba input.go --output output.wasm --verboseCompile multiple files:
goiaba main.go utils.go math.goBuild all Go files in a directory:
goiaba build .Generate a complete web project with HTML and JavaScript:
goiaba build . --web ./web-projectCreate a simple Go file with an exported function:
// add.go
package main
//export add
func add(x int, y int) int {
return x + y
}Compile it:
goiaba add.goThis generates add.wasm, that exports add function purely.
Compile multiple Go files together (like Go packages):
# Compile specific files
goiaba main.go utils.go math.go
# Or use the build command to compile all .go files in a directory
goiaba build .Example with multiple files:
// main.go
package main
//export calculate
func calculate(a int, b int) int {
return add(a, b) * multiply(a, b)
}// math.go
package main
func add(x int, y int) int {
return x + y
}
func multiply(x int, y int) int {
return x * y
}Goiaba supports structs, arrays, and slices:
package main
type Person struct {
name string
age int
}
//export createPerson
func createPerson(name string, age int) Person {
return Person{name: name, age: age}
}
//export getPersonName
func getPersonName(p Person) string {
return p.name
}
//export processArray
func processArray(arr []int) int {
sum := 0
for i := 0; i < len(arr); i++ {
sum += arr[i]
}
return sum
}All standard Go control flow constructs are supported:
package main
//export fibonacci
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
//export findMax
func findMax(arr []int) int {
if len(arr) == 0 {
return 0
}
max := arr[0]
for i := 1; i < len(arr); i++ {
if arr[i] > max {
max = arr[i]
}
}
return max
}
//export gradeToLetter
func gradeToLetter(score int) string {
switch {
case score >= 90:
return "A"
case score >= 80:
return "B"
case score >= 70:
return "C"
case score >= 60:
return "D"
default:
return "F"
}
}package main
import "strings"
//export greet
func greetName(name string) string {
message := strings.Join([]string{"Hello,", name}, " ")
return message
}
//export stringLength
func stringLength(s string) int {
return len(s)
}use goiaba::wasm::compiler::compile_str;
fn main() {
let go_source = r#"
package main
//export add
func add(x int, y int) int {
return x + y
}
"#;
let wasm_bytes = compile_str(go_source)
.expect("Failed to compile Go to WASM");
// Write to file or use with a WASM runtime
std::fs::write("output.wasm", wasm_bytes)
.expect("Failed to write WASM file");
}use goiaba::wasm::compiler::compile_str;
use wasmtime::{Engine, Instance, Module, Store};
fn main() {
let go_source = r#"
package main
//export add
func add(x int, y int) int {
return x + y
}
"#;
let wasm_bytes = compile_str(go_source)
.expect("Failed to compile Go to WASM");
// Create a WASM runtime
let engine = Engine::default();
let module = Module::from_binary(&engine, &wasm_bytes)
.expect("Failed to load WASM module");
let mut store = Store::new(&engine, ());
// Instantiate the module
let instance = Instance::new(&mut store, &module, &[])
.expect("Failed to instantiate module");
// Get the exported function
let add_func = instance
.get_typed_func::<(i32, i32), i32>(&mut store, "add")
.expect("Failed to get 'add' function");
// Call the function
let result = add_func
.call(&mut store, (5, 3))
.expect("Failed to call 'add' function");
assert_eq!(result, 8);
}use goiaba::parser::parse_str;
fn main() {
let source = r#"
package main
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
"#;
match parse_str(source) {
Ok((objects, file)) => {
println!("Successfully parsed Go source code");
// Access AST nodes through objects and file
}
Err(err) => {
eprintln!("Parse error: {}", err);
}
}
}To make Go functions callable from WebAssembly, use the //export directive:
//export function_name
func function_name(param1 int, param2 int) int {
return param1 + param2
}The exported name will be used in the WebAssembly module exports.
Goiaba supports importing functions from standard library packages. Currently supported packages include:
math: Mathematical functionsstrings: String manipulation functions
When you compile this code, Goiaba will generate WASM imports for the stdlib functions used. The resulting WASM module can be instantiated with appropriate import objects that provide the implementations of these functions.
math.Sqrt(float64) float64- Square root functionstrings.Len(string) int- String lengthstrings.Join([]string, string) string- Join strings with separator
Note: The current implementation provides basic support for these functions. Full Go standard library compatibility is planned for future versions.
- Go source code parsing to Abstract Syntax Tree (AST)
- Translation of Go constructs to WebAssembly representations
- WebAssembly bytecode generation
- Function definitions with parameter and return types
- Variable declarations and assignments
- Control flow statements (if/else, for loops)
- Exportable WASM functions
- Arithmetic operations (+, -, *, /, %)
- Comparison operations (<, >, <=, >=, ==, !=)
- Bitwise operations (&, |, ^, <<, >>)
- Logical operations (&&, ||, !)
- Increment/decrement operators (++, --)
- Recursive function calls
- Struct types with field access and assignment
- Command-line interface
- Multi-file compilation support
- Directory-based package building
- Unary operators (negation, logical NOT)
- Arrays and slices
- String literals and operations
- Switch statements
- Pointer dereferencing and operations
- Methods on types
- Interfaces
- Multiple return values
- Defer statements
- Panic and recover
- Package imports
- Standard library functions
- Floating-point operations
- Memory management optimizations
- Goroutines and channels
- Complete standard library support
- Source maps for debugging
- Optimization passes for generated WASM
- JavaScript bindings generation (wasm-bindgen)
- Rust code generation
- Zig code generation
- LLVM-IR target compilation
Goiaba consists of several key components:
- Parser: Lexical analysis and syntax parsing of Go source code
- AST: Internal representation of Go program structure
- Translator: Conversion from Go AST to WebAssembly IR
- Compiler: Generation of WebAssembly bytecode
- CLI: Command-line interface for user interaction
The generated WebAssembly code prioritizes correctness over optimization. Future versions will include:
- Dead code elimination
- Constant folding
- Register allocation improvements
- Memory access optimization
- Function inlining for small functions
Contributions are welcome. Please ensure linting and tests pass before submitting pull requests:
task lint
task testCurrent limitations of the compiler, yet to be added:
- No garbage collection (manual memory management)
- Limited standard library support
- No concurrency primitives (goroutines, channels)
- No optimizer passes
BSD-3-Clause
Copyright (c) 2024-present Raphael Amorim
This project builds upon concepts from the Go language specification and WebAssembly standards. Parser implementation is adapted from the Goscript project.