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 --verboseGenerate a complete nodejs/bun/deno library from Go source code:
goiaba main.go --libGenerate a complete web project with HTML and JavaScript:
goiaba main.go --web ./web-projectuse 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.
- 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
- 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 all tests pass before submitting pull requests:
cargo test
cargo clippy
cargo fmtRun the test suite:
make testCurrent limitations of the compiler, yet to be added:
- No garbage collection (manual memory management)
- Limited standard library support
- No concurrency primitives (goroutines, channels)
- Single file compilation only
- 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.