Skip to content

Commit a0ff060

Browse files
committed
Add Go example
1 parent 637508c commit a0ff060

File tree

7 files changed

+159
-1
lines changed

7 files changed

+159
-1
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ To run the tests, you will need:
55
- The emcc compiler: <https://emscripten.org/docs/getting_started/downloads.html>
66
- An up-to-date Rust toolchain: <https://www.rust-lang.org/>
77
- A zig compiler, version `0.11`: <https://ziglang.org/learn/getting-started/#installing-zig>
8+
- A tinygo compiler, version `0.32`: <https://tinygo.org/getting-started/install/>
89

910
Then, you can run the tests with `cargo test`.
1011

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Note that plugins require typst version `0.8` or more.
66

77
## You want to write a plugin
88

9-
A plugin can be written in Rust, C, Zig, or any language than compiles to WebAssembly.
9+
A plugin can be written in Rust, C, Zig, Go or any language than compiles to WebAssembly.
1010

1111
Rust plugins can use this crate to automatically implement the protocol with a macro:
1212

@@ -37,6 +37,7 @@ See the example for your language:
3737
- [Rust](examples/hello_rust/)
3838
- [Zig](examples/hello_zig/)
3939
- [C](examples/hello_c/)
40+
- [Go](examples/hello_go/)
4041

4142
If you have all the required dependencies, you may build all examples by running `cargo test`.
4243

examples/hello_go/README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Go wasm plugin example
2+
3+
This is a bare-bone typst plugin, written in Go.
4+
5+
## Compile
6+
7+
To compile this example, you need the [TinyGo compiler]. Then, run the command
8+
9+
```sh
10+
GOOS="wasip1" GOARCH="wasm" tinygo build -o hello.wasm
11+
```
12+
13+
[TinyGo compiler]: https://tinygo.org/getting-started/install/
14+
15+
16+
This invocation of Tinygo builds with WASI[^1], so we need to stub WASI functions:
17+
18+
[^1]: I personally could not get a working binary with `GOOS="js"` (i.e. wasm).
19+
20+
```sh
21+
pushd ../../wasi-stub
22+
cargo run -- ../examples/hello_go/hello.wasm -o ../examples/hello_go/hello.wasm
23+
popd
24+
```
25+
26+
## Build with typst
27+
28+
Simply run `typst compile hello.typ`, and observe that it works !

examples/hello_go/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/astrale-sharp/wasm-minimal-protocol/tree/master/examples/hello_go
2+
3+
go 1.22.2

examples/hello_go/hello.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package main
2+
3+
import (
4+
"unsafe"
5+
)
6+
7+
// ===
8+
// functions for the protocol
9+
10+
//go:wasmimport typst_env wasm_minimal_protocol_write_args_to_buffer
11+
func write_args_to_buffer(ptr int32)
12+
13+
func WriteArgsToBuffer(argBuf []byte) {
14+
ptr := int32(uintptr(unsafe.Pointer(unsafe.SliceData(argBuf))))
15+
write_args_to_buffer(ptr)
16+
}
17+
18+
//go:wasmimport typst_env wasm_minimal_protocol_send_result_to_host
19+
func send_result_to_host(ptr, size int32)
20+
21+
func SendResultToHost(resBuf []byte) {
22+
size := int32(len(resBuf))
23+
ptr := int32(uintptr(unsafe.Pointer(unsafe.SliceData(resBuf))))
24+
send_result_to_host(ptr, size)
25+
}
26+
27+
// ===
28+
29+
// main is required for the `wasip1` target, even if it isn't used.
30+
func main() {}
31+
32+
//go:export hello
33+
func hello() int32 {
34+
const message = "Hello from wasm!!!"
35+
SendResultToHost([]byte(message))
36+
return 0
37+
}
38+
39+
//go:export double_it
40+
func doubleIt(arg1Len int32) int32 {
41+
buf := make([]byte, arg1Len*2)
42+
WriteArgsToBuffer(buf)
43+
44+
copy(buf[arg1Len:], buf[:arg1Len])
45+
SendResultToHost(buf)
46+
return 0
47+
}
48+
49+
//go:export concatenate
50+
func concatenate(arg1Len, arg2Len int32) int32 {
51+
buf := make([]byte, arg1Len+arg2Len+1)
52+
WriteArgsToBuffer(buf)
53+
54+
copy(buf[arg1Len+1:], buf[arg1Len:])
55+
buf[arg1Len] = '*'
56+
SendResultToHost(buf)
57+
return 0
58+
}
59+
60+
//go:export shuffle
61+
func shuffle(arg1Len, arg2Len, arg3Len int) int32 {
62+
argBuf := make([]byte, arg1Len+arg2Len+arg3Len)
63+
arg1 := argBuf[:arg1Len]
64+
arg2 := argBuf[arg1Len : arg1Len+arg2Len]
65+
arg3 := argBuf[arg1Len+arg2Len:]
66+
WriteArgsToBuffer(argBuf)
67+
68+
resBuf := make([]byte, 0, arg1Len+arg2Len+arg3Len+2)
69+
resBuf = append(resBuf, arg3...)
70+
resBuf = append(resBuf, '-')
71+
resBuf = append(resBuf, arg1...)
72+
resBuf = append(resBuf, '-')
73+
resBuf = append(resBuf, arg2...)
74+
SendResultToHost(resBuf)
75+
return 0
76+
}
77+
78+
//go:export returns_ok
79+
func returnsOk() int32 {
80+
const message = "This is an `Ok`"
81+
SendResultToHost([]byte(message))
82+
return 0
83+
}
84+
85+
//go:export returns_err
86+
func returnsErr() int32 {
87+
const message = "This is an `Err`"
88+
SendResultToHost([]byte(message))
89+
return 1
90+
}
91+
92+
//go:export will_panic
93+
func willPanic() int32 {
94+
panic("Panicking, this message will not be seen...")
95+
}

examples/hello_go/hello.typ

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#{
2+
let p = plugin("./hello.wasm")
3+
4+
assert.eq(str(p.hello()), "Hello from wasm!!!")
5+
assert.eq(str(p.double_it(bytes("abc"))), "abcabc")
6+
assert.eq(str(p.concatenate(bytes("hello"), bytes("world"))), "hello*world")
7+
assert.eq(str(p.shuffle(bytes("s1"), bytes("s2"), bytes("s3"))), "s3-s1-s2")
8+
assert.eq(str(p.returns_ok()), "This is an `Ok`")
9+
// p.will_panic() // Fails compilation
10+
// p.returns_err() // Fails compilation with an error message
11+
}

tests/test.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,22 @@ fn test_zig() {
144144
wasi_stub(dir_path.clone() + "hello-wasi.wasm");
145145
typst_compile(&dir_path);
146146
}
147+
148+
#[test]
149+
fn test_go() {
150+
let dir_path = "examples/hello_go/".to_string();
151+
let build_go_wasi = Command::new("tinygo")
152+
.arg("build")
153+
.arg("-o")
154+
.arg("hello.wasm")
155+
.env("GOOS", "wasip1")
156+
.env("GOARCH", "wasm")
157+
.current_dir(&dir_path)
158+
.status()
159+
.unwrap();
160+
if !build_go_wasi.success() {
161+
panic!("Compiling with tinygo failed");
162+
}
163+
wasi_stub(dir_path.clone() + "hello.wasm");
164+
typst_compile(&dir_path);
165+
}

0 commit comments

Comments
 (0)