|
1 | 1 | # Gengo (言語) |
2 | 2 |
|
3 | | -gengo is a small embeddable scripting language/runtime implemented in Zig. |
| 3 | +A small embeddable scripting language implemented in Zig. Single-pass Pratt compiler → bytecode VM. Runs natively or as a WASI module. |
| 4 | + |
| 5 | +**[Try it in the browser →](https://gengoscript.github.io/gengo/)** |
4 | 6 |
|
5 | 7 | Project status: early-stage and intentionally evolving. |
6 | 8 |
|
7 | | -## Language Example |
| 9 | +## Language at a Glance |
8 | 10 |
|
9 | 11 | ```gengo |
10 | 12 | std := import("std") |
11 | 13 |
|
12 | | -type User struct { |
13 | | - id int, |
14 | | - name string, |
15 | | - bio ?string |
| 14 | +type Shape interface { |
| 15 | + area() float |
| 16 | +} |
| 17 | +
|
| 18 | +type Rect struct { w float, h float } |
| 19 | +
|
| 20 | +func (r Rect) area() float { |
| 21 | + return r.w * r.h |
16 | 22 | } |
17 | 23 |
|
18 | | -func greet(u User) { |
19 | | - // Multiline escaped string: |
20 | | - // open with ", continue with " at same column, close on last line with ". |
21 | | - msg := "Hello, |
22 | | - "this is gengo |
23 | | - "User:" |
24 | | - std.io.println(msg + " " + u.name) |
| 24 | +func printArea(s Shape) { |
| 25 | + std.io.printf("area: %f\n", s.area()) |
25 | 26 | } |
26 | 27 |
|
27 | | -u := User{ id: 7, name: "user", bio: "こんにちは" } |
28 | | -greet(u) |
| 28 | +printArea(Rect{w: 4.0, h: 3.0}) |
29 | 29 | ``` |
30 | 30 |
|
31 | | -Raw multiline strings use `'` with the same continuation/termination shape and keep backslashes literally. |
| 31 | +```gengo |
| 32 | +std := import("std") |
32 | 33 |
|
33 | | -Additional current syntax/features: |
34 | | -- `const` immutable bindings; `:=` is first-class mutable declaration syntax |
35 | | -- explicit typed declarations: `x int = 10`, `const x int = 10` |
36 | | -- mandatory typed params: `func sum(...xs int) int { ... }` |
37 | | -- `std.io.printf("%s %d", "x", 1)` |
38 | | -- nominal/range types: |
39 | | - - `type Month int range 1..12` |
40 | | - - `type UserId string` |
41 | | -- enums with qualified members: |
42 | | - - `type Status enum { pending, approved, denied }` |
43 | | - - `Status.pending` |
| 34 | +// Named types with range constraints |
| 35 | +type Celsius float range -273.15..1000.0 |
44 | 36 |
|
45 | | -## Toolchain |
| 37 | +// Multi-return with trap binding — panics on error, caught by defer/recover |
| 38 | +func parseTemp(s string) { |
| 39 | + defer func() { |
| 40 | + err := std.core.recover() |
| 41 | + if err != null { std.io.println("bad input:", err) } |
| 42 | + }() |
46 | 43 |
|
47 | | -- Zig: `0.16.0` (matches CI) |
48 | | -- wasmtime: recent preview1-compatible release |
| 44 | + n, trap := std.conv.to_float(s) |
| 45 | + std.io.printf("%.1f °C\n", float(Celsius(n))) |
| 46 | +} |
| 47 | +
|
| 48 | +parseTemp("98.6") |
| 49 | +parseTemp("not a number") |
| 50 | +``` |
| 51 | + |
| 52 | +## Features |
| 53 | + |
| 54 | +**Types** |
| 55 | +- Structs with typed fields, nullable (`?T`), union (`A|B`), and method receivers |
| 56 | +- Interfaces with structural satisfaction checking |
| 57 | +- Enums with qualified member access (`Status.pending`) |
| 58 | +- Named scalar types: `type UserId string`, `type Month int range 1..12` |
| 59 | +- First-class `error` values; `any` as the empty interface |
| 60 | + |
| 61 | +**Functions** |
| 62 | +- Typed parameters (mandatory); `any` to opt out |
| 63 | +- Variadic: `func sum(...xs int) int` |
| 64 | +- Multi-return: `return value, err` |
| 65 | +- Named return variables: `func f() (result float, err ?error)` |
| 66 | +- Closures with upvalue capture |
| 67 | + |
| 68 | +**Error Handling** |
| 69 | +- `defer` — runs on function exit in LIFO order |
| 70 | +- `std.core.recover()` — intercepts panics from inside a defer |
| 71 | +- `assert condition` / `assert condition, "message"` — panic if false |
| 72 | +- `x, trap := f()` — panic if the bound slot is non-null, pass through if null |
| 73 | + |
| 74 | +**Control Flow** |
| 75 | +- `if / else if / else` with optional init statement |
| 76 | +- `for cond`, `for init; cond; post`, `for v in seq`, `for i, v in seq` |
| 77 | +- `switch expr { case v { } default { } }` |
| 78 | +- `break`, `continue`, `return` |
| 79 | + |
| 80 | +**Declarations** |
| 81 | +- `x := expr` — inferred mutable |
| 82 | +- `x Type = expr` — explicit typed mutable |
| 83 | +- `const x Type = expr` — immutable |
| 84 | +- `_` discard in destructure: `val, _ := f()` |
| 85 | + |
| 86 | +## Standard Library |
| 87 | + |
| 88 | +| Namespace | Functions | |
| 89 | +|---|---| |
| 90 | +| `std.io` | `println`, `printf` | |
| 91 | +| `std.core` | `len`, `bytelen`, `append`, `contains`, `remove`, `has`, `delete`, `keys`, `values`, `error`, `is_error`, `recover`, `gc`, `gc_live_objects`, `gc_stats` | |
| 92 | +| `std.string` | `split`, `join`, `trim`, `upper`, `lower`, `starts_with`, `ends_with`, `index_of` | |
| 93 | +| `std.math` | `abs`, `sqrt`, `floor`, `ceil`, `round`, `sin`, `cos`, `tan`, `log`, `log2`, `log10`, `pow`, `min`, `max`, `pi`, `e`, `inf` | |
| 94 | +| `std.conv` | `to_int`, `to_float`, `to_bool`, `to_string` | |
49 | 95 |
|
50 | 96 | ## Quick Start |
51 | 97 |
|
52 | | -Build the WASI runtime: |
| 98 | +**Native CLI** (recommended for development): |
| 99 | + |
| 100 | +```bash |
| 101 | +zig build -Dpreset=dev cli |
| 102 | +./zig-out/bin/gengo script.gengo |
| 103 | +``` |
| 104 | + |
| 105 | +**WASI runtime** (for browser/sandboxed embedding): |
53 | 106 |
|
54 | 107 | ```bash |
55 | 108 | zig build -Dpreset=dev wasi |
| 109 | +wasmtime --dir . ./zig-out/lib/gengo-test.wasm -- script.gengo |
56 | 110 | ``` |
57 | 111 |
|
58 | | -Run a script: |
| 112 | +**Conformance tests:** |
59 | 113 |
|
60 | 114 | ```bash |
61 | | -wasmtime --dir . ./gengo-test.wasm -- examples/simple_math.gengo |
| 115 | +zig build -Dpreset=dev test |
62 | 116 | ``` |
63 | 117 |
|
64 | | -Run conformance tests: |
| 118 | +**Benchmarks:** |
65 | 119 |
|
66 | 120 | ```bash |
67 | | -WASMTIME_BIN=/path/to/wasmtime zig build -Dpreset=dev test |
| 121 | +zig build -Dpreset=dev bench |
68 | 122 | ``` |
69 | 123 |
|
70 | | -## Repo Layout |
| 124 | +## Build Presets |
71 | 125 |
|
72 | | -- `lang/`: lexer, compiler, bytecode, VM logic |
73 | | -- `runtime/`: runtime services, host ABI, memory |
74 | | -- `examples/`: spec, fail-cases, benchmarks, parity cases |
75 | | -- `docs/`: language and runtime docs |
76 | | -- `tests/`: harness scripts used by `make test` / `make bench` |
| 126 | +| Preset | Use | |
| 127 | +|---|---| |
| 128 | +| `dev` | Default — generous limits, debug-friendly | |
| 129 | +| `tiny` | Minimal heap and stack for constrained embedding | |
| 130 | +| `stress` | Tight limits to catch edge cases in tests | |
77 | 131 |
|
78 | | -## Notes |
| 132 | +Pass with `-Dpreset=<name>` to any build step. |
79 | 133 |
|
80 | | -- File extension is `.gengo`. |
81 | | -- `import("std")` is the supported builtin namespace. |
82 | | -- Language behavior may change as the project evolves. |
83 | | -- Runtime defaults are small by design (see `docs/language.md` limits section). |
84 | | -- Managed heap allocations are class-based; a single managed block is currently capped at `32 KiB`. |
85 | | -- Embedding API guide: `docs/embedding.md` (`runtime/api.zig`). |
| 134 | +## Toolchain |
86 | 135 |
|
87 | | -## Browser Playground |
| 136 | +- Zig `0.16.0` |
| 137 | +- wasmtime (any recent preview1-compatible release) — only needed for WASM target |
88 | 138 |
|
89 | | -A GitHub Pages playground is included under `playground/`. |
| 139 | +## Repo Layout |
90 | 140 |
|
91 | | -- It runs the WASI `gengo-test.wasm` in-browser. |
92 | | -- It mounts your script as an in-memory file (`script.gengo`) and executes it. |
| 141 | +``` |
| 142 | +src/ |
| 143 | + lang/ lexer, compiler, bytecode, VM |
| 144 | + runtime/ heap, GC, host ABI, embedding API |
| 145 | +examples/ |
| 146 | + spec/ conformance pass-cases (+ .out expected output) |
| 147 | + spec/fail/ conformance fail-cases (+ .err expected error) |
| 148 | + bench/ benchmark scripts |
| 149 | +docs/ language reference, embedding guide, changelog |
| 150 | +playground/ browser playground (GitHub Pages) |
| 151 | +``` |
| 152 | + |
| 153 | +## Embedding |
| 154 | + |
| 155 | +Gengo is designed to be embedded in Zig hosts. See `docs/embedding.md` and `src/runtime/api.zig`. |
| 156 | + |
| 157 | +Runtime limits (heap, stack, call depth, etc.) are configured via preset files in `src/runtime/`. |
| 158 | + |
| 159 | +## Notes |
93 | 160 |
|
94 | | -After pushing to `main`, the Pages workflow publishes the site. |
| 161 | +- File extension: `.gengo` |
| 162 | +- Only `import("std")` is currently supported; file imports are planned |
| 163 | +- A single managed heap block is capped at 32 KiB regardless of total heap size |
| 164 | +- Language surface is still evolving; breaking changes are expected |
0 commit comments