[!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:
cargo install goiaba
Add to your Cargo.toml:
[dependencies]
goiaba = "*"
Basic compilation:
goiaba main.go -o main.wasm
Compile with verbose output:
goiaba input.go --output output.wasm --verbose
Compile multiple files:
goiaba main.go utils.go math.go
Build all Go files in a directory:
goiaba build .
Generate a complete web project with HTML and JavaScript:
goiaba build . --web ./web-project
Create 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.go
This 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 functionsWhen 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 separatorNote: The current implementation provides basic support for these functions. Full Go standard library compatibility is planned for future versions.
Goiaba consists of several key components:
The generated WebAssembly code prioritizes correctness over optimization. Future versions will include:
Contributions are welcome. Please ensure linting and tests pass before submitting pull requests:
task lint
task test
Current limitations of the compiler, yet to be added:
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.
Rust
96.4%
Go
3.6%
[!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:
cargo install goiaba
Add to your Cargo.toml:
[dependencies]
goiaba = "*"
Basic compilation:
goiaba main.go -o main.wasm
Compile with verbose output:
goiaba input.go --output output.wasm --verbose
Compile multiple files:
goiaba main.go utils.go math.go
Build all Go files in a directory:
goiaba build .
Generate a complete web project with HTML and JavaScript:
goiaba build . --web ./web-project
Create 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.go
This 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 functionsWhen 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 separatorNote: The current implementation provides basic support for these functions. Full Go standard library compatibility is planned for future versions.
Goiaba consists of several key components:
The generated WebAssembly code prioritizes correctness over optimization. Future versions will include:
Contributions are welcome. Please ensure linting and tests pass before submitting pull requests:
task lint
task test
Current limitations of the compiler, yet to be added:
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.
Rust
96.4%
Go
3.6%