vanta-lang 0.28.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vanta_lang-0.28.0/CHANGELOG.md +786 -0
- vanta_lang-0.28.0/LICENSE +21 -0
- vanta_lang-0.28.0/MANIFEST.in +11 -0
- vanta_lang-0.28.0/PKG-INFO +306 -0
- vanta_lang-0.28.0/README.md +285 -0
- vanta_lang-0.28.0/ROADMAP.md +58 -0
- vanta_lang-0.28.0/compiler/__init__.py +2 -0
- vanta_lang-0.28.0/compiler/ast_nodes.py +241 -0
- vanta_lang-0.28.0/compiler/codegen_cpp.py +995 -0
- vanta_lang-0.28.0/compiler/driver.py +741 -0
- vanta_lang-0.28.0/compiler/formats.py +53 -0
- vanta_lang-0.28.0/compiler/lexer.py +184 -0
- vanta_lang-0.28.0/compiler/loader.py +98 -0
- vanta_lang-0.28.0/compiler/parser.py +532 -0
- vanta_lang-0.28.0/compiler/tokens.py +47 -0
- vanta_lang-0.28.0/compiler/type_checker.py +1446 -0
- vanta_lang-0.28.0/compiler/typesys.py +130 -0
- vanta_lang-0.28.0/docs/ARCHITECTURE.md +194 -0
- vanta_lang-0.28.0/docs/LANGUAGE_REFERENCE.md +1798 -0
- vanta_lang-0.28.0/docs/TUTORIAL.md +1161 -0
- vanta_lang-0.28.0/examples/assets/ball.png +0 -0
- vanta_lang-0.28.0/examples/assets/ball_spin.png +0 -0
- vanta_lang-0.28.0/examples/assets/blip.wav +0 -0
- vanta_lang-0.28.0/examples/assets/boom.wav +0 -0
- vanta_lang-0.28.0/examples/assets/brick.png +0 -0
- vanta_lang-0.28.0/examples/assets/brick.wav +0 -0
- vanta_lang-0.28.0/examples/assets/enemy.png +0 -0
- vanta_lang-0.28.0/examples/assets/gen_assets.py +221 -0
- vanta_lang-0.28.0/examples/assets/hit.wav +0 -0
- vanta_lang-0.28.0/examples/assets/laser.png +0 -0
- vanta_lang-0.28.0/examples/assets/laser.wav +0 -0
- vanta_lang-0.28.0/examples/assets/lose.wav +0 -0
- vanta_lang-0.28.0/examples/assets/paddle.bmp +0 -0
- vanta_lang-0.28.0/examples/assets/ship_anim.png +0 -0
- vanta_lang-0.28.0/examples/assets/tiles.png +0 -0
- vanta_lang-0.28.0/examples/gamekit/README.md +304 -0
- vanta_lang-0.28.0/examples/gamekit/anim.vnt +62 -0
- vanta_lang-0.28.0/examples/gamekit/audio.vnt +214 -0
- vanta_lang-0.28.0/examples/gamekit/collide.vnt +46 -0
- vanta_lang-0.28.0/examples/gamekit/fonts/LICENSE.md +13 -0
- vanta_lang-0.28.0/examples/gamekit/fonts/tom-thumb.ttf +0 -0
- vanta_lang-0.28.0/examples/gamekit/gamepad.vnt +115 -0
- vanta_lang-0.28.0/examples/gamekit/gfx.vnt +37 -0
- vanta_lang-0.28.0/examples/gamekit/image.vnt +98 -0
- vanta_lang-0.28.0/examples/gamekit/input.vnt +21 -0
- vanta_lang-0.28.0/examples/gamekit/rng.vnt +19 -0
- vanta_lang-0.28.0/examples/gamekit/sdl.vnt +98 -0
- vanta_lang-0.28.0/examples/gamekit/text.vnt +160 -0
- vanta_lang-0.28.0/pyproject.toml +36 -0
- vanta_lang-0.28.0/runtime/vanta_runtime.hpp +818 -0
- vanta_lang-0.28.0/setup.cfg +4 -0
- vanta_lang-0.28.0/setup.py +35 -0
- vanta_lang-0.28.0/tests/test_cli.py +98 -0
- vanta_lang-0.28.0/tests/test_codegen.py +890 -0
- vanta_lang-0.28.0/tests/test_integration.py +2399 -0
- vanta_lang-0.28.0/tests/test_lexer.py +97 -0
- vanta_lang-0.28.0/tests/test_loader.py +99 -0
- vanta_lang-0.28.0/tests/test_parser.py +272 -0
- vanta_lang-0.28.0/tests/test_type_checker.py +2176 -0
- vanta_lang-0.28.0/vanta_lang.egg-info/PKG-INFO +306 -0
- vanta_lang-0.28.0/vanta_lang.egg-info/SOURCES.txt +62 -0
- vanta_lang-0.28.0/vanta_lang.egg-info/dependency_links.txt +1 -0
- vanta_lang-0.28.0/vanta_lang.egg-info/entry_points.txt +2 -0
- vanta_lang-0.28.0/vanta_lang.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,786 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes to Vanta, newest first. Versions correspond to git tags.
|
|
4
|
+
|
|
5
|
+
## v0.28.0 — 2026-10-20
|
|
6
|
+
|
|
7
|
+
- Installable Vanta. `pip install vanta-lang` (or `pipx install vanta-lang`)
|
|
8
|
+
now installs the `vanta` command without a checkout. A `setup.py` build hook
|
|
9
|
+
copies `runtime/vanta_runtime.hpp` and the game kit into the wheel as
|
|
10
|
+
`compiler/runtime/` and `compiler/templates/`, and `MANIFEST.in` keeps the
|
|
11
|
+
same files in the sdist. The driver resolves either layout, so a checkout and
|
|
12
|
+
an installed wheel behave the same.
|
|
13
|
+
- `vanta doctor` reports the Python and C++ compiler versions, the resolved
|
|
14
|
+
runtime header and game kit paths, compiles a one-line runtime probe, and
|
|
15
|
+
makes a best-effort SDL2 / SDL2_ttf / SDL2_image check. Missing SDL only
|
|
16
|
+
warns: non-game programs need nothing beyond the C++ compiler.
|
|
17
|
+
- `vanta new` keeps working from an installed wheel because the starter points
|
|
18
|
+
at the game kit inside the installed compiler.
|
|
19
|
+
- Packaging checks. CI gains a `package` job that builds the wheel and sdist,
|
|
20
|
+
runs `twine check`, installs the wheel into a fresh venv, and exercises
|
|
21
|
+
`--version`, `doctor`, a hello program, `new`, `check`, and a headless game
|
|
22
|
+
run on Linux and Windows. `.github/workflows/release.yml` repeats the smoke
|
|
23
|
+
test on version tags, attaches the wheel and sdist to the GitHub release, and
|
|
24
|
+
publishes to PyPI through Trusted Publishing when the `PUBLISH_PYPI`
|
|
25
|
+
repository variable is set.
|
|
26
|
+
- Metadata is release-ready: SPDX `MIT`, license files, classifiers, project
|
|
27
|
+
URLs, and `setuptools>=77`. 588 tests total.
|
|
28
|
+
- Docs: README and the tutorial lead with `pipx install vanta-lang`,
|
|
29
|
+
`vanta doctor`, and the installed-command workflow; the reference gains an
|
|
30
|
+
installing/running section and the architecture notes describe the build
|
|
31
|
+
hook and layout resolution.
|
|
32
|
+
|
|
33
|
+
## v0.27.0 — 2026-10-20
|
|
34
|
+
|
|
35
|
+
- Breakout polish. `examples/breakout.vnt` now has three walls of 4, 5, and 6
|
|
36
|
+
rows (125 bricks total), with diagonal gaps in the later walls. Clearing a
|
|
37
|
+
wall serves the next one; clearing the third wins. The score carries across
|
|
38
|
+
walls, and the HUD shows the current level and best score.
|
|
39
|
+
- Pause. P or Space freezes the paddle, ball, collisions, and ball animation
|
|
40
|
+
while events and drawing keep running; a centered `PAUSED` label shows the
|
|
41
|
+
state. Esc and the close button still quit. `gamekit/sdl.vnt` gains the
|
|
42
|
+
`KEY_SPACE` and `KEY_P` symbols.
|
|
43
|
+
- Score persistence. The best score is read from `breakout.score` at startup
|
|
44
|
+
and only rewritten when the run beats it. The optional second program
|
|
45
|
+
argument changes the score path, so tests and headless runs stay out of the
|
|
46
|
+
working tree; `breakout.score` is git-ignored.
|
|
47
|
+
- Per-sound gain. `gamekit/audio.vnt` gains
|
|
48
|
+
`sound_load_gain(audio, path, gain)`: it loads a WAV like `sound_load` and
|
|
49
|
+
scales every mono S16 sample in place. Gain is clamped to 0..1, so it
|
|
50
|
+
attenuates without clipping; `sound_load` remains the full-volume shortcut.
|
|
51
|
+
- Floating-point determinism. The driver now passes `-ffp-contract=off` to
|
|
52
|
+
GCC/Clang (and `/fp:strict` to MSVC), so `f32` expressions are not
|
|
53
|
+
contracted into fused multiply-adds. The breakout autopilot therefore
|
|
54
|
+
reaches the same frame 5969 on x86-64 and Apple arm64; without the flag the
|
|
55
|
+
FMA-capable Mac took 7070 frames.
|
|
56
|
+
- The breakout headless probe now freezes the three-wall clear
|
|
57
|
+
(`cleared 125 lives 3 level 3 best 125 frames 5969`) and then loads a higher
|
|
58
|
+
score in a one-frame run to prove it is not overwritten. The sound probe
|
|
59
|
+
loads a stereo file at half gain and checks the folded samples scale from
|
|
60
|
+
6000 to 3000, then loads it at gain 2.0 and checks the clamp keeps the
|
|
61
|
+
original 6000 peak. 586 tests total.
|
|
62
|
+
- Docs: README, tutorial, reference, roadmap, and the kit comments describe
|
|
63
|
+
levels, pause, persistence, and gain. The frozen pong and strike runs are
|
|
64
|
+
unchanged; breakout's frozen run deliberately records the new mode.
|
|
65
|
+
|
|
66
|
+
## v0.26.0 — 2026-10-20
|
|
67
|
+
|
|
68
|
+
- `str` now owns its storage automatically. It is an immutable,
|
|
69
|
+
reference-counted value: literals and command-line arguments are zero-copy
|
|
70
|
+
static views, while `str_concat`, `to_str_i32`, `to_str_f32`, `file_read`,
|
|
71
|
+
and `StrBuilder.to_str()` return owned buffers. Copies share the buffer and
|
|
72
|
+
the last copy frees it.
|
|
73
|
+
- The fixed 64 KiB process-lifetime string pool is gone. Long-running string
|
|
74
|
+
loops no longer exhaust a global limit, `file_read` reads arbitrarily large
|
|
75
|
+
files (up to the `i32` length limit), and `str_pool_reset()` remains only as
|
|
76
|
+
a compatibility no-op that invalidates nothing.
|
|
77
|
+
- `file_read` and `file_write` stay byte-exact: embedded NUL bytes are kept in
|
|
78
|
+
the returned `str`, counted by `str_len`, and written by `file_write`. C
|
|
79
|
+
functions and `print_str` still see a `const char*` and stop at the first
|
|
80
|
+
NUL.
|
|
81
|
+
- The C boundary is unchanged for existing code: extern declarations keep
|
|
82
|
+
`str` as `const char*`, direct calls convert at the call site, and a C
|
|
83
|
+
function returning `str` has its result copied into an owned value. Small
|
|
84
|
+
generated adapters let extern functions with `str` signatures be used as
|
|
85
|
+
`func(str)` values and let Vanta functions/builtins with `str` signatures be
|
|
86
|
+
passed to C callbacks.
|
|
87
|
+
- `Arena` now constructs and destroys non-trivial element types, and
|
|
88
|
+
`arena.reset()` runs those destructors. `alloc::<str>`, arrays of `str`, and
|
|
89
|
+
structs containing `str` therefore work without leaking or double-freeing.
|
|
90
|
+
- 15 new tests (13 net, after replacing the two pool-exhaustion tests): large
|
|
91
|
+
file reads, pool-free allocation loops, string reset compatibility,
|
|
92
|
+
builder/arena lifetime, arena string destruction, owned `List<str>`, const
|
|
93
|
+
strings, embedded NUL round-trips, extern `str` arguments, extern `str`
|
|
94
|
+
function values, and C callbacks with `str` arguments and returns. 586
|
|
95
|
+
total.
|
|
96
|
+
- Documentation: the reference, tutorial, architecture notes, README, roadmap,
|
|
97
|
+
and file-I/O example all describe owned strings; every new snippet was
|
|
98
|
+
executed with its output frozen.
|
|
99
|
+
|
|
100
|
+
## v0.25.0 — 2026-10-20
|
|
101
|
+
|
|
102
|
+
- Checked pointer access: `p.at(i, size)` reads, writes, and compound-assigns
|
|
103
|
+
through a `ptr<T>` after checking `0 <= i < size`. The pointer has no length
|
|
104
|
+
of its own, so the caller supplies the accessible count. `ptr<void>` is
|
|
105
|
+
rejected, nested array elements work
|
|
106
|
+
(`rows.at(row, row_count).at(column)`), and the new `examples/ptr_at.vnt`
|
|
107
|
+
demonstrates the read/write paths.
|
|
108
|
+
- An out-of-bounds pointer index stops the program with
|
|
109
|
+
`vanta: pointer index I out of bounds (size N) at line L`. The raw `p[i]`,
|
|
110
|
+
`ptr_cast::<T>(p)`, and `ptr_offset(p, n)` escape hatches are unchanged, and
|
|
111
|
+
`size` remains a programmer promise — null is still `ptr_is_null`'s job.
|
|
112
|
+
- 10 new tests: pointer `at` reads/writes/compound assignment, nested array
|
|
113
|
+
access, `ptr<void>` rejection, size type checking, the `let`-pointer and
|
|
114
|
+
temporary-pointer write rules, generated-code shape, the new example, and
|
|
115
|
+
out-of-bounds read/write failures. 573 total.
|
|
116
|
+
|
|
117
|
+
## v0.24.1 — 2026-10-20
|
|
118
|
+
|
|
119
|
+
- `vanta game.vnt` is shorthand for `vanta run game.vnt`: a bare source path
|
|
120
|
+
now compiles and runs. Installing the checkout once with
|
|
121
|
+
`python -m pip install -e .` creates the `vanta` command, so games launch
|
|
122
|
+
without repeating `python main.py` or spelling out link flags.
|
|
123
|
+
- `vanta new game.vnt` writes a playable starter — a paddle, a falling block,
|
|
124
|
+
three lives, and a frame-limited mode for headless smoke tests. Its imports
|
|
125
|
+
point at the checkout's game kit relative to the new file.
|
|
126
|
+
- The driver links SDL2, SDL2_ttf, and SDL2_image automatically when it sees
|
|
127
|
+
their extern block names. Explicit `--link` values still work and are
|
|
128
|
+
deduplicated; custom C libraries still use `--include-dir`, `--lib-dir`,
|
|
129
|
+
and `--link`. On Windows the compiler's own `bin` directory is added to the
|
|
130
|
+
child `PATH`, so the SDL DLLs are found without a manual environment setup.
|
|
131
|
+
- `vanta build --bundle` (Windows) writes `build/<name>/<name>.exe`, walks the
|
|
132
|
+
executable's PE imports with `objdump`, and copies every non-system DLL it
|
|
133
|
+
loads — SDL2, SDL2_ttf, SDL2_image, and their codec dependencies — beside
|
|
134
|
+
the executable. It also copies `examples/assets` and the kit font, so the
|
|
135
|
+
bundled example games run from a folder with only Windows on `PATH`.
|
|
136
|
+
- Compile failures now print a platform-specific install hint for missing SDL
|
|
137
|
+
development libraries (MSYS2 package names on Windows, `brew` on macOS, and
|
|
138
|
+
`apt-get` on Linux), and MSVC users are told why the MinGW import libraries
|
|
139
|
+
cannot be used.
|
|
140
|
+
- 7 new tests: command normalization, auto-link detection and ordering, custom
|
|
141
|
+
externs staying unlinked, explicit-link deduplication, starter generation,
|
|
142
|
+
overwrite protection, and a Windows bundle that runs with a system-only
|
|
143
|
+
`PATH`. 563 total.
|
|
144
|
+
|
|
145
|
+
## v0.24.0 — 2026-10-20
|
|
146
|
+
|
|
147
|
+
- The fourth playable game. `examples/strike.vnt` is a one-screen wave
|
|
148
|
+
shooter: left/right arrows (or A/D, or a gamepad stick/D-pad) move the
|
|
149
|
+
ship, the cannon fires on a fixed cadence, and three waves of six saucers
|
|
150
|
+
descend, drift, and bounce off the walls. A bolt destroys a saucer; a
|
|
151
|
+
saucer that touches the ship or slips past the bottom costs one of three
|
|
152
|
+
lives. Clear all eighteen to win. The ship is an animated four-frame
|
|
153
|
+
strip, the saucers are one white texture tinted per wave, the HUD is
|
|
154
|
+
`text_draw`, and the shot/hit/boom sounds are WAV files. With no input the
|
|
155
|
+
autopilot lines up under the lowest saucer and clears the game without
|
|
156
|
+
losing a life at frame 1020 — the suite freezes those counts. The game
|
|
157
|
+
imports nine of the ten kit modules (all but `rng.vnt`) and links SDL2,
|
|
158
|
+
SDL2_ttf, and SDL2_image.
|
|
159
|
+
- `gamekit/collide.vnt`, the tenth module, is pure Vanta with no externs and
|
|
160
|
+
no SDL dependency: `collide_rects`, `collide_point`, `collide_circles`,
|
|
161
|
+
and `collide_circle_rect`. Rectangles are `(x, y, w, h)`; edges that only
|
|
162
|
+
touch are not overlaps, `collide_point` is half-open, and the circle tests
|
|
163
|
+
compare squared distances, so none of them calls `sqrt`. The pure module
|
|
164
|
+
gets its own headless test on every machine with a C++ compiler.
|
|
165
|
+
- New generated assets: `ship_anim.png` (four 24x24 frames), `enemy.png`,
|
|
166
|
+
`laser.png`, and `laser.wav` / `hit.wav` / `boom.wav`, all from
|
|
167
|
+
`examples/assets/gen_assets.py`.
|
|
168
|
+
- No compiler changes: the kit and the game are plain Vanta. The new tests
|
|
169
|
+
are the collision semantics and the frozen strike run, for 556 total.
|
|
170
|
+
- Documentation: the game-kit chapters in the tutorial and reference now
|
|
171
|
+
cover ten modules and collision, the README lists the fourth game and the
|
|
172
|
+
ten-module kit, and the game-kit README documents the new helpers. Every
|
|
173
|
+
new snippet was executed with its output frozen.
|
|
174
|
+
|
|
175
|
+
## v0.23.0 — 2026-10-20
|
|
176
|
+
|
|
177
|
+
- `extern` functions can return function values. A signature like `func
|
|
178
|
+
signal(sig: i32, handler: func(i32) -> void) -> func(i32) -> void` now
|
|
179
|
+
compiles: the generated `extern "C"` declaration folds the declarator into
|
|
180
|
+
the name (`void(*signal(int32_t, void(*)(int32_t)))(int32_t)`) instead of
|
|
181
|
+
emitting a bare function type where C needs a name. Returned values are
|
|
182
|
+
ordinary function values — call them, store them, or hand them back to C.
|
|
183
|
+
Pointer-to-function returns (`ptr<func(...)>`) and nested function-typed
|
|
184
|
+
returns (`func() -> func() -> i32`) spell correctly too; an array or list
|
|
185
|
+
of function values is still rejected as an extern return, since C cannot
|
|
186
|
+
return those by value. `examples/extern_return.vnt` with
|
|
187
|
+
`examples/extern_return_lib.c` is the buildable demo: C picks a function,
|
|
188
|
+
returns null for an unknown id, and the program calls, compares, and
|
|
189
|
+
null-tests the result.
|
|
190
|
+
- Function-value equality. `==` and `!=` compare function values by address;
|
|
191
|
+
values of the same type are accepted, different signatures are a compile
|
|
192
|
+
error, and ordering is rejected with a message pointing at `==`/`!=`. A
|
|
193
|
+
null built with `ptr_null::<func(...)>()` compares equal to any null of its
|
|
194
|
+
type, and `ptr_is_null` now accepts function values as well as pointers, so
|
|
195
|
+
the explicit null test needs no cast or sentinel. Struct equality follows:
|
|
196
|
+
a function-valued field no longer blocks comparing the whole struct, because
|
|
197
|
+
the field compares by address like any function value. Still no closures:
|
|
198
|
+
equality compares plain function pointers.
|
|
199
|
+
- Tests: 11 new cases cover the checker, generated declarations (including
|
|
200
|
+
nested and pointer-to-function returns), equality emission, struct equality
|
|
201
|
+
with a function field, and a real C fixture that hands function pointers
|
|
202
|
+
back across the boundary. 554 total.
|
|
203
|
+
- Docs: the reference's Function values section gains the equality rules and
|
|
204
|
+
a frozen snippet, its Extern section gains a runnable `signal` example, and
|
|
205
|
+
the tutorial, README, and version banners follow. Every new snippet is
|
|
206
|
+
executed with its output frozen; the frozen pong and breakout runs are
|
|
207
|
+
unchanged.
|
|
208
|
+
|
|
209
|
+
## v0.22.0 — 2026-10-19
|
|
210
|
+
|
|
211
|
+
- Sprite animation. `gamekit/anim.vnt` (a ninth module) is a frame clock over
|
|
212
|
+
`tex_draw_src` for a horizontal strip of equal frames. `anim_new(count, fw,
|
|
213
|
+
fh, fps)` describes the strip; `anim_tick(anim, dt)` advances the clock by
|
|
214
|
+
the time the game hands it and *returns* the updated clock — structs are
|
|
215
|
+
values, so `anim = anim_tick(anim, dt)` is the idiom — the frame wraps, and
|
|
216
|
+
`anim_draw` blits the current frame through `tex_draw_src`. Nothing reads a
|
|
217
|
+
wall clock, a dead texture draws nothing, and an empty strip (zero frames)
|
|
218
|
+
stays on frame 0. The assets script grows `ball_spin.png` (four 10x10
|
|
219
|
+
frames, a highlight orbiting the ball), and `examples/breakout.vnt` now
|
|
220
|
+
spins its ball from it with a fixed 1/60 s step. The animation is purely
|
|
221
|
+
visual: the physics is untouched, and the frozen headless run still prints
|
|
222
|
+
exactly `cleared 40 lives 3 frames 2064`.
|
|
223
|
+
- Text sprites. `gamekit/text.vnt` gains `TextSprite` — the render-once path
|
|
224
|
+
for text that redraws every frame. `text_sprite` renders one string into a
|
|
225
|
+
texture (same colours as `text_draw`, with `text_sprite_ok` as the test and
|
|
226
|
+
a dead sprite for a failed font or an empty string), `text_sprite_draw`
|
|
227
|
+
blits it with no TTF call at all, and `text_sprite_free` destroys it;
|
|
228
|
+
`text_draw` is unchanged. The alternative — a bounded cache keyed by
|
|
229
|
+
(font, size, string, colour) — was weighed and rejected: the kit has no
|
|
230
|
+
hidden state to put it in, pool strings can be invalidated under a cache,
|
|
231
|
+
and an explicit handle is the kit's shape with the same measured win.
|
|
232
|
+
- The measured before/after ships as `examples/text_bench.vnt`: one 24 pt
|
|
233
|
+
string, both paths, counts stable and milliseconds the machine's. On the
|
|
234
|
+
development machine (headless dummy driver, software renderer) 40000 draws
|
|
235
|
+
went from ~130 ms via `text_draw` to ~4 ms via one sprite — about 30x. A
|
|
236
|
+
smoke test asserts the two output lines' shape and never the numbers, the
|
|
237
|
+
rule being that timings are never frozen. 3 new tests (543 total).
|
|
238
|
+
- Docs: the tutorial gains an Animated sprites section and the render-once
|
|
239
|
+
paragraph in Text on screen; the reference's kit chapter lists nine
|
|
240
|
+
modules, and both READMEs follow. Every new snippet is executed with its
|
|
241
|
+
output frozen, and the frozen runs of pong and breakout are re-verified
|
|
242
|
+
unchanged.
|
|
243
|
+
|
|
244
|
+
## v0.21.0 — 2026-10-19
|
|
245
|
+
|
|
246
|
+
- Game controllers. `gamekit/gamepad.vnt` (an eighth module) wraps SDL2's
|
|
247
|
+
`SDL_GameController` API: `pad_open(index)` returns a `Pad`, with `pad_ok`
|
|
248
|
+
as the test, `pad_attached` for the unplug check, and `pad_button` /
|
|
249
|
+
`pad_axis` polling the current state. A missing controller — none plugged
|
|
250
|
+
in, or an index out of range — opens *dead*, the same shape as a dead
|
|
251
|
+
`Screen`, `Tex`, or `Sound`: every other call is a guarded no-op, so a
|
|
252
|
+
keyboard-only machine stays playable. `pad_open` starts SDL's refcounted
|
|
253
|
+
gamecontroller subsystem and `pad_close` stops it, so pads close before
|
|
254
|
+
`screen_close`'s `SDL_Quit`. Axes stay raw — sticks span -32768..32767
|
|
255
|
+
with 0 at rest, triggers 0..32767 — and the `PAD_BUTTON_*` / `PAD_AXIS_*`
|
|
256
|
+
constants name the standard controls, extended share/paddle/touchpad
|
|
257
|
+
buttons included. This is polling, not events: the event loop stays
|
|
258
|
+
keyboard-focused.
|
|
259
|
+
- `examples/sdl_gamepad.vnt` (raw C calls, SDL2 core only) opens a controller
|
|
260
|
+
by index and polls A, d-pad-left, the left stick, and the left trigger;
|
|
261
|
+
with no controller it reports the dead path and exits cleanly. The two new
|
|
262
|
+
headless tests open index 9999 — out of range on every machine — so their
|
|
263
|
+
frozen output is hardware-independent, and the existing games still build
|
|
264
|
+
without the module. 2 new tests (540 total).
|
|
265
|
+
- The tutorial gains a Gamepads section (dead-handle semantics, raw axis
|
|
266
|
+
ranges, polling vs events), the reference adds the module to the kit
|
|
267
|
+
chapter, and both READMEs count eight modules. The new snippets join the
|
|
268
|
+
frozen-output doc checks.
|
|
269
|
+
|
|
270
|
+
## v0.20.0 — 2026-10-19
|
|
271
|
+
|
|
272
|
+
- Sprites. `gamekit/image.vnt` (a seventh module) wraps SDL2_image:
|
|
273
|
+
`tex_load` decodes a PNG or BMP file into a `Tex` — a handle plus the
|
|
274
|
+
decoded size; a missing or unreadable file comes back dead, so `tex_ok`
|
|
275
|
+
is the test. `tex_draw` blits at natural size, `tex_draw_src` blits one
|
|
276
|
+
rectangle of the texture (sprite-sheet frames), and `tex_draw_ex` rotates
|
|
277
|
+
by a `f64` angle in degrees and/or flips with the new `SDL_FLIP_*`
|
|
278
|
+
constants, about the texture's own centre — and none of the three take a
|
|
279
|
+
scratch rect: SDL copies the rectangles during the call, so the helpers
|
|
280
|
+
build them on a tiny transient arena. `tex_tint` and `tex_alpha` set a
|
|
281
|
+
colour and an opacity multiplied into every later draw of that texture.
|
|
282
|
+
Importing the module means linking SDL2_image; `sdl.vnt` gains
|
|
283
|
+
`SDL_QueryTexture`, `SDL_RenderCopyEx`, the colour/alpha mods, and
|
|
284
|
+
`SDL_DestroyTexture` (moved there from `text.vnt`, where it would have
|
|
285
|
+
collided in the flat namespace).
|
|
286
|
+
- Sound files. `gamekit/audio.vnt` gains `sound_load` / `sound_ok` /
|
|
287
|
+
`sound_play` / `sound_free`: 16-bit PCM WAV at 44.1 kHz, mono or stereo,
|
|
288
|
+
decoded by `SDL_LoadWAV_RW` into a buffer SDL allocates. Stereo is folded
|
|
289
|
+
to mono in place at load, so the `Sound`'s byte count halves; any other
|
|
290
|
+
rate, sample format, or channel count loads as a dead sound, and playing
|
|
291
|
+
or freeing one is a no-op — games stay quiet on machines with no device.
|
|
292
|
+
The game holds the `Sound` across plays and hands the buffer back with
|
|
293
|
+
`sound_free`.
|
|
294
|
+
- `examples/sdl_texture.vnt` (raw C calls, SDL2_image): loads PNG and BMP
|
|
295
|
+
files, draws tinted, draws a sheet region flipped, rotates 90 degrees,
|
|
296
|
+
and checks every draw by reading pixels back from a forced software
|
|
297
|
+
renderer — exact on every machine, headless included.
|
|
298
|
+
`examples/sdl_sound.vnt` loads `examples/assets/blip.wav`, prints the
|
|
299
|
+
file's spec, queues it, and drains the device. The seven assets (three
|
|
300
|
+
PNGs, one BMP, three WAVs) are generated by the committed
|
|
301
|
+
`examples/assets/gen_assets.py` — nothing is vendored.
|
|
302
|
+
- `examples/breakout.vnt` is the third playable game and the worked example
|
|
303
|
+
for both additions: a four-row wall tinted over one white brick texture,
|
|
304
|
+
a ball and paddle loaded from files, and WAV hits. With no key held the
|
|
305
|
+
paddle auto-plays, so the headless run is one exact trajectory: it clears
|
|
306
|
+
all 40 bricks with all three lives intact at frame 2064, and the test suite
|
|
307
|
+
freezes `cleared 40 lives 3 frames 2064` (the frame cap is only a
|
|
308
|
+
backstop).
|
|
309
|
+
- CI installs SDL2_image on all three platforms, so the new tests run
|
|
310
|
+
everywhere; the sprite and sound-file snippets join the frozen-output doc
|
|
311
|
+
checks. 5 new tests (538 total).
|
|
312
|
+
|
|
313
|
+
## v0.19.0 — 2026-10-19
|
|
314
|
+
|
|
315
|
+
- SDL2 audio. `examples/sdl_audio.vnt` plays a synthesized tone through the
|
|
316
|
+
device's *queue* API — `SDL_OpenAudioDevice` with a hand-built
|
|
317
|
+
`SDL_AudioSpec` (its `callback` field a true C function-pointer type),
|
|
318
|
+
`SDL_QueueAudio`, `SDL_PauseAudioDevice`, `SDL_GetQueuedAudioSize` — and
|
|
319
|
+
`examples/sdl_audio_cb.vnt` puts a Vanta function in the spec's `callback`
|
|
320
|
+
field instead, so SDL runs it on its audio thread; callback state travels
|
|
321
|
+
through the `userdata` pointer, as with any C callback. Under SDL's dummy
|
|
322
|
+
driver both run headless in real time, which is how the tests cover them.
|
|
323
|
+
- `ptr_null::<T>()` builds the null pointer explicitly — the value C APIs ask
|
|
324
|
+
*for*, where `ptr_is_null` only tests one C hands back. `T` may be a data
|
|
325
|
+
type (`void` gives the anonymous `ptr<void>`) or a function type, which
|
|
326
|
+
makes it a null callback: the empty slot a queue-mode `SDL_AudioSpec`
|
|
327
|
+
carries (`SDL_OpenAudioDevice` also wants NULL, not `""`, for "the default
|
|
328
|
+
device"). Like the other pointer builtins it takes no arguments; there is
|
|
329
|
+
still no `nil` literal.
|
|
330
|
+
- The game kit gains `gamekit/audio.vnt` (six modules now): `audio_open` /
|
|
331
|
+
`audio_ok` / `audio_close`, `audio_play_tone(aud, freq, ms, buf, capacity)`
|
|
332
|
+
— fills a caller-owned `i16` buffer with a square wave, queues it, and
|
|
333
|
+
returns the samples queued (0 for a dead device, nonsense values, or an
|
|
334
|
+
SDL error; a tone longer than the buffer is cut short) — plus
|
|
335
|
+
`audio_pending` (the bytes still queued) and `audio_wait`. Failure is
|
|
336
|
+
quiet: with no working device every call does nothing, so games keep
|
|
337
|
+
running silently. The kit ships no sound files; tones are generated.
|
|
338
|
+
- `examples/pong.vnt` beeps: 880 Hz on a player paddle hit, 660 Hz on an AI
|
|
339
|
+
hit, and low/high tones on points. The beeps are fire-and-forget, so the
|
|
340
|
+
headless run is unchanged (`player 0 ai 5 frames 576`).
|
|
341
|
+
- The audio tests need no library beyond SDL2 core, so CI gains no packages;
|
|
342
|
+
they run under `SDL_AUDIODRIVER=dummy`, whose device paces consumption in
|
|
343
|
+
real time, so queueing and draining stay deterministic. Examples:
|
|
344
|
+
`sdl_audio.vnt`, `sdl_audio_cb.vnt`, `gamekit/audio.vnt`. 12 new tests
|
|
345
|
+
(533 total).
|
|
346
|
+
|
|
347
|
+
## v0.18.0 — 2026-10-19
|
|
348
|
+
|
|
349
|
+
- `ptr_offset(p, n)` moves a pointer `n` elements with its element type kept
|
|
350
|
+
(`p + n` in the generated C++). The offset must be an integer (`i32`;
|
|
351
|
+
narrow values promote) and may be negative; `ptr<void>` is rejected —
|
|
352
|
+
`cannot offset a ptr<void> (it has no element size); ptr_cast it to a
|
|
353
|
+
typed pointer first`. C out-parameters are the motivating case:
|
|
354
|
+
`TTF_SizeUTF8(font, text, &w, &h)` reaches its second slot with
|
|
355
|
+
`ptr_offset(scratch, 1)` instead of a second allocation.
|
|
356
|
+
- Text rendering, through SDL_ttf. `examples/sdl_text.vnt` draws with the
|
|
357
|
+
raw C API — `TTF_Init` / `TTF_OpenFont`, `TTF_SizeUTF8`,
|
|
358
|
+
`TTF_RenderUTF8_Blended` into a texture, `SDL_RenderCopy` — with
|
|
359
|
+
`SDL_Color` declared as a Vanta struct of four `u8` channels passed to C
|
|
360
|
+
by value (the layout was verified against a C mirror).
|
|
361
|
+
- The game kit gains `gamekit/text.vnt`: `font_open` / `font_ok` /
|
|
362
|
+
`font_close`, `text_measure` (writes width and height into a caller
|
|
363
|
+
scratch block), `text_draw(screen, font, x, y, text, scratch, r, g, b)`,
|
|
364
|
+
and `text_quit`. The scratch layout is 10 `i32` slots: `[0] w`, `[1] h`,
|
|
365
|
+
`[2..5]` source rect, `[6..9]` destination rect — one `arena.alloc`
|
|
366
|
+
covers measure and draw. A font that failed to open measures 0x0 and
|
|
367
|
+
draws nothing instead of crashing.
|
|
368
|
+
- `examples/pong.vnt` now draws its score as text (the pip rectangles are
|
|
369
|
+
gone) and links `SDL2_ttf`; the headless run is unchanged
|
|
370
|
+
(`player 0 ai 5 frames 576`).
|
|
371
|
+
- The kit ships a font: `examples/gamekit/fonts/tom-thumb.ttf`, a 3x5
|
|
372
|
+
pixel monospace face (CC0/CC-BY 3.0; credits in `fonts/LICENSE.md`), so
|
|
373
|
+
examples, docs, and CI depend on no system fonts.
|
|
374
|
+
- CI installs SDL_ttf everywhere (`libsdl2-ttf-dev`, `sdl2_ttf`,
|
|
375
|
+
`mingw-w64-x86_64-SDL2_ttf`); the SDL_ttf tests skip when it is missing,
|
|
376
|
+
like the SDL2 tests. Examples: `ptr_offset.vnt`, `sdl_text.vnt`,
|
|
377
|
+
`gamekit/text.vnt`. 14 new tests (521 total).
|
|
378
|
+
|
|
379
|
+
## v0.17.0 — 2026-10-18
|
|
380
|
+
|
|
381
|
+
- Narrow integers: `i8`, `u8`, `i16`, `u16`. They store their exact width —
|
|
382
|
+
a `u8` field is one byte (`uint8_t` in the generated C++) — and read as
|
|
383
|
+
`i32` in arithmetic, C's integer promotion. Assigning any integer into a
|
|
384
|
+
narrow slot keeps the low bits, but a *literal* that does not fit is a
|
|
385
|
+
compile-time error (`initializer: value 300 out of range for u8`), and the
|
|
386
|
+
same check covers literals beside a narrow value (`x == 300`). Narrow types
|
|
387
|
+
work everywhere a type is written: fields, arrays, `ptr<u16>`, `List<u8>`,
|
|
388
|
+
and `extern` signatures. Pushing to a narrow `List` element type now emits
|
|
389
|
+
an explicit template argument so wider values still convert.
|
|
390
|
+
- `ptr_cast::<T>(p)` reinterprets a pointer's element type
|
|
391
|
+
(`reinterpret_cast` semantics, unchecked): view the same memory as another
|
|
392
|
+
type, or give a `ptr<void>` from C a useful element type again. Any data
|
|
393
|
+
type is a legal target, `void` included (erasing the type); function-type
|
|
394
|
+
targets are rejected (`ptr_cast cannot target a function type`).
|
|
395
|
+
- `for x in xs` iterates a `List<T>` or an array element by element. The loop
|
|
396
|
+
variable is a read-only copy, one per iteration; `break`/`continue` mean the
|
|
397
|
+
same as in range loops; anything else is a compile error (`cannot iterate
|
|
398
|
+
over ptr<i32>; for-each supports arrays and Lists`). A list's length is
|
|
399
|
+
re-read every pass, so do not grow one mid-walk.
|
|
400
|
+
- Struct equality: `==`/`!=` on user structs compare field by field — numbers
|
|
401
|
+
by value, `str` by content, arrays element-wise, nested structs
|
|
402
|
+
recursively. A field with no equality (pointer, function value, `List`,
|
|
403
|
+
`Quat`/`Matrix4`) is a compile error naming the path (`cannot compare struct
|
|
404
|
+
'Outer' with '==': field 'inner.p' (ptr<i32>) has no equality`). Every
|
|
405
|
+
comparable struct gets non-member `operator==`/`!=` emitted — all
|
|
406
|
+
declarations before all definitions, so nesting order does not matter.
|
|
407
|
+
- Enum types may now appear in `extern` signatures (parameters, returns,
|
|
408
|
+
`ptr<Enum>`, `List<Enum>`); the generated C++ names the fixed-`int32_t`
|
|
409
|
+
enum and forward-declares it ahead of the `extern "C"` block, so the C side
|
|
410
|
+
declares `int32_t` or a matching C enum. The old "pass `enum_to_i32(...)`
|
|
411
|
+
instead" limitation is gone.
|
|
412
|
+
- Examples: `narrow_ints.vnt` plus a C fixture (exact-width parameters and a
|
|
413
|
+
C mirror struct reading a Vanta struct's bytes for the layout check),
|
|
414
|
+
`foreach.vnt`, `struct_eq.vnt`, `ptr_cast.vnt`, and `enums_extern.vnt` plus
|
|
415
|
+
a C fixture. 52 new tests (507 total).
|
|
416
|
+
|
|
417
|
+
## v0.16.0 — 2026-10-18
|
|
418
|
+
|
|
419
|
+
- C callbacks: an `extern` parameter typed `func(...)` is now a C function
|
|
420
|
+
pointer, so C libraries can call back into Vanta code. Pass any function
|
|
421
|
+
value — a Vanta function or another `extern`. The generated C++ spells the
|
|
422
|
+
callback inline where it appears (`int32_t(*v_f)(int32_t)` in a
|
|
423
|
+
declaration, `int32_t(**v_slot)(int32_t)` through a `ptr<func(...)>`), so
|
|
424
|
+
no aliases are needed before the namespace.
|
|
425
|
+
- An `extern` cannot return a function type yet: the error says so and
|
|
426
|
+
suggests wrapping the C function in a shim (`extern function 'get' cannot
|
|
427
|
+
return a function type yet; wrap it in a C shim that calls it`).
|
|
428
|
+
- Codegen fix: struct types in extern signatures are now namespace-qualified
|
|
429
|
+
and forward-declared ahead of the `extern "C"` block, so
|
|
430
|
+
`func f(p: ptr<State>)` in an `extern` compiles (previously the bare
|
|
431
|
+
`State` name was emitted before any declaration).
|
|
432
|
+
- `examples/callbacks.vnt` + `examples/callbacks_lib.c`: a callback C calls
|
|
433
|
+
twice (`f(f(x))`) and a visitor with a `ptr<i32>` user-data pointer.
|
|
434
|
+
A libc `atexit` test exercises callbacks on every platform. 8 new tests
|
|
435
|
+
(455 total).
|
|
436
|
+
|
|
437
|
+
## v0.15.0 — 2026-10-18
|
|
438
|
+
|
|
439
|
+
- `List<T>` can be edited in place: `xs.insert(i, v)` inserts at index `i`,
|
|
440
|
+
shifting the rest right (`i` may be `len(xs)`, the same as `push`), and
|
|
441
|
+
`xs.remove(i)` removes and returns the element at `i`, shifting the rest
|
|
442
|
+
left. Out-of-range indexes stop the program with
|
|
443
|
+
`vanta: insert index I out of bounds (size N) at line L` /
|
|
444
|
+
`vanta: remove index I out of bounds (size N) at line L`.
|
|
445
|
+
- `xs.sort(cmp)` sorts a list in place with a comparator — a function value
|
|
446
|
+
`func(T, T) -> i32` returning negative, zero, or positive for less-than,
|
|
447
|
+
equal, greater-than. It is a stable, `O(n log n)` merge sort: elements the
|
|
448
|
+
comparator calls equal keep their order.
|
|
449
|
+
- The comparator is fully checked: a missing, wrongly-typed, or wrong-arity
|
|
450
|
+
comparator is a compile error naming the expected signature, e.g.
|
|
451
|
+
`'sort' expects a comparator func(i32, i32) -> i32, got func(i32, i32) -> bool`.
|
|
452
|
+
- Codegen fix: function-pointer aliases may now mention structs by value —
|
|
453
|
+
struct forward declarations are emitted before the aliases — so comparators
|
|
454
|
+
over `List<Struct>` work.
|
|
455
|
+
- `examples/lists.vnt` now demonstrates insert, remove, and a comparator
|
|
456
|
+
sort (stable ties included); 17 new tests (447 total).
|
|
457
|
+
|
|
458
|
+
## v0.14.0 — 2026-10-18
|
|
459
|
+
|
|
460
|
+
- Added the game kit: `examples/gamekit/` packages the SDL2 plumbing as four
|
|
461
|
+
importable modules — `sdl.vnt` (`extern` declarations, `EV_*`/`KEY_*`
|
|
462
|
+
constants, the `Screen` lifecycle), `input.vnt` (event accessors),
|
|
463
|
+
`gfx.vnt` (clear, filled rect, outline, present), and `rng.vnt` (bounded
|
|
464
|
+
integers and floats, random signs).
|
|
465
|
+
- `examples/pong.vnt`, the second playable game, is built on the kit: W/S
|
|
466
|
+
or Up/Down against a tracking AI, wall bounces, contact-point steering,
|
|
467
|
+
first to 5 wins. `match` picks the serve direction.
|
|
468
|
+
- Pong runs headless like the other SDL examples: a frame-limit argument
|
|
469
|
+
plus `SDL_VIDEODRIVER=dummy`. The frozen headless run: the AI wins 5-0 in
|
|
470
|
+
frame 576. `gamekit/rng.vnt` has no SDL dependency, so it gets its own
|
|
471
|
+
test that runs on every machine with a C++ compiler.
|
|
472
|
+
- No compiler changes: the kit is plain Vanta, and the language needed
|
|
473
|
+
nothing new — struct fields typed `ptr<void>` (the `Screen` handle), a
|
|
474
|
+
failure value carried by C's own NULLs instead of a nil literal, and
|
|
475
|
+
ordinary imports all already worked.
|
|
476
|
+
- 2 new tests (430 total).
|
|
477
|
+
|
|
478
|
+
## v0.13.0 — 2026-10-18
|
|
479
|
+
|
|
480
|
+
- Added `match`: branch on an enum value with `Enum.Member:` arms (several
|
|
481
|
+
members per arm, commas between them) and an optional `else:` fallback.
|
|
482
|
+
Arms run top to bottom; the first match wins. `examples/match.vnt` walks
|
|
483
|
+
a little state machine:
|
|
484
|
+
|
|
485
|
+
```vanta
|
|
486
|
+
match g.state:
|
|
487
|
+
State.Menu:
|
|
488
|
+
print_str("fresh start")
|
|
489
|
+
State.Playing, State.Paused:
|
|
490
|
+
print_str("in game")
|
|
491
|
+
else:
|
|
492
|
+
print_str("somewhere else")
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
- The checker proves coverage: when every member has an arm, no `else` is
|
|
496
|
+
needed, and a missing member is a compile error that names the members
|
|
497
|
+
it needs — add a member to an enum later and every match that must
|
|
498
|
+
handle it says so. A member used by two arms, a member of a different
|
|
499
|
+
enum, and a misspelled member are errors too.
|
|
500
|
+
- `match` arms have their own scope, and `break`/`continue` inside an arm
|
|
501
|
+
still control the enclosing loop: `match` is not a loop, and codegen
|
|
502
|
+
keeps it that way (an if/else chain rather than a C++ `switch`). The
|
|
503
|
+
matched value is evaluated exactly once.
|
|
504
|
+
- `match` on numbers, strings, or structs is a compile error; it is a
|
|
505
|
+
statement, not an expression. `match` joins the reserved keywords.
|
|
506
|
+
- 25 new tests (428 total).
|
|
507
|
+
|
|
508
|
+
## v0.12.0 — 2026-10-18
|
|
509
|
+
|
|
510
|
+
- SDL2 support, shown end to end. `examples/sdl_window.vnt` opens a window
|
|
511
|
+
and clears it to a colour; `examples/sdl_game.vnt` is the first playable
|
|
512
|
+
Vanta game — move the paddle with Left/Right (or A/D) to catch falling
|
|
513
|
+
coins, Esc or the close button quits. Both are pure `extern`
|
|
514
|
+
declarations, so no SDL headers are involved at compile time:
|
|
515
|
+
|
|
516
|
+
```powershell
|
|
517
|
+
python main.py run examples/sdl_game.vnt --lib-dir C:/msys64/mingw64/lib --link SDL2
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
- Pointers widen to `ptr<void>` implicitly, matching C's conversion to
|
|
521
|
+
`void*`: any `ptr<T>` is accepted where `ptr<void>` is expected, which
|
|
522
|
+
is how opaque C handles (`SDL_Window`, `SDL_Renderer`, ...) are typed.
|
|
523
|
+
The narrowing direction stays an error, and a `ptr<void>` cannot be
|
|
524
|
+
indexed — it has no element type.
|
|
525
|
+
- New builtin `ptr_is_null(p) -> bool`: compares any pointer (including
|
|
526
|
+
`ptr<void>`) against null so failed calls can be handled. Pointer
|
|
527
|
+
bindings can be reassigned with `mut`, and user functions may take and
|
|
528
|
+
return pointers.
|
|
529
|
+
- Fixed: functions returning pointers never compiled — the implicit
|
|
530
|
+
default return was invalid C++ (`T*{}`); a fallthrough now yields
|
|
531
|
+
`nullptr`. Same bug class as the v0.11.0 `str` fix.
|
|
532
|
+
- Both SDL examples take an optional frame-limit argument and run headless
|
|
533
|
+
under `SDL_VIDEODRIVER=dummy`, so the test suite builds, links, and runs
|
|
534
|
+
them for real; CI installs SDL2 on Linux, macOS, and Windows. 20 new
|
|
535
|
+
tests (403 total).
|
|
536
|
+
|
|
537
|
+
## v0.11.0 — 2026-10-18
|
|
538
|
+
|
|
539
|
+
- Added enums: `enum Name:` declares a fixed set of named members, numbered
|
|
540
|
+
from 0 in declaration order and written `Name.Member` (e.g.
|
|
541
|
+
`State.Playing`). An enum is a first-class type — variables, struct
|
|
542
|
+
fields, function parameters and returns, arrays, `List<T>`, function
|
|
543
|
+
values, and imported modules all accept enum values unchanged.
|
|
544
|
+
- Values compare with `==` and `!=`. Ordering comparisons on enums, and
|
|
545
|
+
compares between different enums or against numbers, are compile errors.
|
|
546
|
+
`enum_to_i32(v)` returns a member's number for printing, arithmetic, or
|
|
547
|
+
passing to an `extern` function (extern signatures cannot take enum types
|
|
548
|
+
directly yet). Enum values cannot be constructed (`State(1)` is an error),
|
|
549
|
+
and enum types have no fields — `s.Menu` suggests `State.Menu`.
|
|
550
|
+
- Fixed: functions returning `str` never compiled — the implicit default
|
|
551
|
+
return that codegen appends was invalid C++ (`return const char*{};`).
|
|
552
|
+
A function that falls through now yields the empty string.
|
|
553
|
+
- Fixed: a type declared after a function of the same name (`func P()` then
|
|
554
|
+
`struct P:`) is now reported as a collision instead of silently shadowing
|
|
555
|
+
the function.
|
|
556
|
+
- New example `examples/enums.vnt`: a tiny state machine switching on a
|
|
557
|
+
`State` enum, plus `enum_to_i32` and iteration over an array of members;
|
|
558
|
+
plus 42 tests (383 total).
|
|
559
|
+
|
|
560
|
+
## v0.10.0 — 2026-10-18
|
|
561
|
+
|
|
562
|
+
- Added `StrBuilder`, an arena-backed string builder: `arena.str_builder()`
|
|
563
|
+
creates one growing buffer, `append_str` / `append_i32` / `append_u32` /
|
|
564
|
+
`append_f32` / `append_f64` / `append_bool` append (formatted exactly like
|
|
565
|
+
the matching `print_*`), `len(b)` reads the byte count, `clear()` resets it
|
|
566
|
+
for reuse, and `to_str()` returns the current contents as a `str` — copied
|
|
567
|
+
into a fresh arena block, so the result stays stable across later appends,
|
|
568
|
+
growth, and `clear()`. Growth doubles from 32 bytes, and the fixed string
|
|
569
|
+
pool is never touched.
|
|
570
|
+
- Like `List<T>`, a builder is a handle: copies alias the same buffer, appends
|
|
571
|
+
work through a plain `let`, and helpers can take a `StrBuilder` parameter
|
|
572
|
+
and append to the caller's builder. `arena.reset()` invalidates the builder
|
|
573
|
+
and the strings taken from it.
|
|
574
|
+
- New example `examples/str_builder.vnt`: building a line once, reusing the
|
|
575
|
+
buffer with `clear()`, and a helper that appends through its parameter;
|
|
576
|
+
plus 28 tests (341 total).
|
|
577
|
+
|
|
578
|
+
## v0.9.0 — 2026-10-18
|
|
579
|
+
|
|
580
|
+
- Added dynamic lists: `List<T>`, a growable array backed by an `Arena`.
|
|
581
|
+
`arena.list::<T>()` creates an empty list, `xs.push(v)` appends (growing by
|
|
582
|
+
doubling from a capacity of 4), `xs.pop()` removes the last element, and
|
|
583
|
+
`len(xs)` reads the size. Element access reuses the v0.7.0 checked
|
|
584
|
+
`xs.at(i)` — reads and writes, with out-of-bounds errors naming the list
|
|
585
|
+
size and source line — plus the unchecked `xs[i]` form.
|
|
586
|
+
- A list is a handle: copies alias the same storage, and passing one to a
|
|
587
|
+
function lets the function append to the caller's list. Since a list is an
|
|
588
|
+
indirection like `ptr<T>`, element writes and `push`/`pop` work through a
|
|
589
|
+
plain `let`; `mut` only controls rebinding. `List<void>` and `List<Arena>`
|
|
590
|
+
are compile errors, and `arena.reset()` invalidates the arena's lists.
|
|
591
|
+
- New example `examples/lists.vnt`: particles in a list, integrated through a
|
|
592
|
+
function call, with checked reads and writes and a pop; plus 39 tests
|
|
593
|
+
(313 total).
|
|
594
|
+
|
|
595
|
+
## v0.8.0 — 2026-10-18
|
|
596
|
+
|
|
597
|
+
- Added trigonometry: `sin`, `cos`, and `tan` for `f32` and `f64`, plus
|
|
598
|
+
`atan2_f32`/`atan2_f64` — the angle of the vector `(x, y)`, the usual
|
|
599
|
+
"heading toward a point" operation. Angles are radians, and the f32/f64
|
|
600
|
+
pairs follow the existing `sqrt_f32`/`sqrt_f64` naming. They are callable
|
|
601
|
+
like any builtin and usable as function values.
|
|
602
|
+
- Extended `examples/arrays_and_math.vnt` with a 45-degree unit vector and
|
|
603
|
+
its heading recovered through `atan2`, plus 7 tests (274 total).
|
|
604
|
+
|
|
605
|
+
## v0.7.0 — 2026-10-18
|
|
606
|
+
|
|
607
|
+
- Added bounds-checked array access: `xs.at(i)` checks the index against the
|
|
608
|
+
fixed size of `xs` before touching memory. Reads return the element, and
|
|
609
|
+
writes (`xs.at(i) = v`, including compound forms) obey the normal mutability
|
|
610
|
+
rules. An out-of-range or negative index prints
|
|
611
|
+
`vanta: index I out of bounds (size N) at line L` to stderr and exits 1.
|
|
612
|
+
Plain `xs[i]` stays unchecked.
|
|
613
|
+
- `at` composes with nested arrays — `g.at(y).at(x)` reads and
|
|
614
|
+
`g.at(y).at(x) = v` writes — and rejects `ptr<T>` receivers with "a pointer
|
|
615
|
+
has no length", since pointers have no size to check against.
|
|
616
|
+
- Codegen emits `vanta::at_checked` for reads and `vanta::at_checked_ref` (a
|
|
617
|
+
real reference, so writes land in the array) for writes, carrying the
|
|
618
|
+
access's source line into the failure message.
|
|
619
|
+
- Added `examples/bounds.vnt`, plus 29 tests (267 total).
|
|
620
|
+
|
|
621
|
+
## v0.6.0 — 2026-10-18
|
|
622
|
+
|
|
623
|
+
- Functions are first-class values. A `func(P1, P2) -> R` type names any
|
|
624
|
+
function with that signature (omit the arrow for `void`), and values can be
|
|
625
|
+
stored in variables, passed as arguments, returned, kept in struct fields and
|
|
626
|
+
arrays, or allocated from an arena. The same call syntax invokes them:
|
|
627
|
+
`f(x)`, `handlers[0](x)`, `op.run(x)`, `pick()(x)`.
|
|
628
|
+
- Values are plain function pointers — there are no closures, so nothing is
|
|
629
|
+
captured. Vanta functions (emitted with their `fn_` prefix), `extern`
|
|
630
|
+
functions, and builtins like `print_str` all work as values.
|
|
631
|
+
- Rejected on purpose: `len`/`print_fmt`/`write_fmt`/`assert` and type names as
|
|
632
|
+
values, `==`/`!=` on function values, function values in `const`
|
|
633
|
+
initializers, and function types in `extern` signatures (C callbacks come
|
|
634
|
+
later).
|
|
635
|
+
- Local bindings now shadow functions of the same name in call position, so
|
|
636
|
+
the canonical `func apply(f: func(i32) -> i32, ...)` works even when a
|
|
637
|
+
function named `f` exists.
|
|
638
|
+
- Codegen emits one `using FnPtrN = R(*)(...)` alias per distinct function
|
|
639
|
+
type, innermost first; type names matching `FnPtr<digits>` are reserved.
|
|
640
|
+
- Added `examples/dispatch.vnt`, plus 51 tests (238 total).
|
|
641
|
+
|
|
642
|
+
## v0.5.0 — 2026-10-18
|
|
643
|
+
|
|
644
|
+
- 2D arrays work end to end. Nested array types and literals (`[[i32, 8], 4]`)
|
|
645
|
+
already parsed, but writes through pointers and struct fields were rejected;
|
|
646
|
+
assignment targets are now validated by walking the whole storage chain, so
|
|
647
|
+
arena-backed grids (`arena.alloc::<[i32, 8]>(h)`) and `mut` struct grids are
|
|
648
|
+
writable at any depth while `let` bindings and parameters stay immutable.
|
|
649
|
+
- Array literals and call argument lists may span lines: the lexer ignores
|
|
650
|
+
newlines inside `[...]` and `(...)`.
|
|
651
|
+
- Added `examples/grid.vnt`: a stack checkerboard, a struct-held heightmap,
|
|
652
|
+
and an arena-backed dynamic grid, plus 11 tests (187 total).
|
|
653
|
+
|
|
654
|
+
## v0.4.0 — 2026-10-18
|
|
655
|
+
|
|
656
|
+
- Added `import "path.vnt"`. An import merges another file's declarations
|
|
657
|
+
into the program: paths resolve relative to the importing file, imports are
|
|
658
|
+
transitive, and each file is loaded once, so diamonds are fine. Import
|
|
659
|
+
cycles and missing files are compile errors. Declarations merge into the
|
|
660
|
+
existing flat namespace — imported functions, structs, constants, and
|
|
661
|
+
externs are used by their bare names. `import` is now a reserved word.
|
|
662
|
+
- Errors inside imported files now name the file that contains them: every
|
|
663
|
+
AST position carries its source file, and `CheckError` prefers it.
|
|
664
|
+
- Added `examples/imports_demo.vnt` with its module
|
|
665
|
+
`examples/imports_demo_lib.vnt`, plus 12 tests (176 total).
|
|
666
|
+
- New `compiler/loader.py` owns import expansion; `_frontend_only` now goes
|
|
667
|
+
through it.
|
|
668
|
+
|
|
669
|
+
## v0.3.1 — 2026-10-18
|
|
670
|
+
|
|
671
|
+
- Added `file_exists(path)`, `file_write(path, data)`, and `file_read(path)`.
|
|
672
|
+
`file_read` allocates from the string pool (valid until `str_pool_reset()`)
|
|
673
|
+
and returns `""` on any failure; a file too large for the pool fails loudly
|
|
674
|
+
with a specific message instead of truncating. `file_write` returns the byte
|
|
675
|
+
count or `-1` on failure.
|
|
676
|
+
- Added `examples/file_io.vnt`: reads a file named on the command line and
|
|
677
|
+
writes a copy to `<path>.copy`, reporting both byte counts.
|
|
678
|
+
|
|
679
|
+
## v0.3.0 — 2026-10-18
|
|
680
|
+
|
|
681
|
+
- Added `extern "lib" { ... }` declarations. Extern functions are declared
|
|
682
|
+
without bodies, type-checked like any other function, and called under their
|
|
683
|
+
original names; the generated C++ puts them in an `extern "C" {}` block
|
|
684
|
+
outside `namespace vanta_user`, so the linker sees unmangled symbols.
|
|
685
|
+
- Added `--include-dir`, `--lib-dir`, and `--link` (all repeatable) to `build`
|
|
686
|
+
and `run`: `-I`/`-L`/`-l` on GCC-style toolchains, `/I`/`/LIBPATH:`/`<lib>.lib`
|
|
687
|
+
on MSVC.
|
|
688
|
+
- Added `examples/extern_demo.vnt` with its C side in
|
|
689
|
+
`examples/extern_demo_lib.c`, plus an end-to-end integration test that
|
|
690
|
+
compiles the C library, links it, and checks the output.
|
|
691
|
+
- Repository history: the tags v0.1.1 through v0.2.5 were re-cut so that each
|
|
692
|
+
tagged commit reports its own version from `--version`; the stored version
|
|
693
|
+
string had lagged the tags before v0.2.2's sync and again until v0.2.6.
|
|
694
|
+
|
|
695
|
+
## v0.2.6 — 2026-10-18
|
|
696
|
+
|
|
697
|
+
- Added `print_fmt(fmt, args...)` and `write_fmt(fmt, args...)`. Placeholders are
|
|
698
|
+
`{}` and are resolved at compile time: the type checker counts them and checks
|
|
699
|
+
each argument's type, and the code generator turns each one into the matching
|
|
700
|
+
printf conversion. A literal/integer mismatched format is a compile error, not
|
|
701
|
+
a runtime surprise.
|
|
702
|
+
- Format literals support `{{` and `}}` for literal braces, and `%` is escaped
|
|
703
|
+
automatically so a stray percent cannot corrupt the output.
|
|
704
|
+
- Added a process-lifetime string pool with `str_concat`, `str_len`,
|
|
705
|
+
`to_str_i32`, `to_str_f32`, `to_str_bool`, and `str_pool_reset`. Exhaustion
|
|
706
|
+
fails loudly rather than corrupting memory.
|
|
707
|
+
- Added `str_to_i32` and `str_to_f32`, without which command-line arguments
|
|
708
|
+
could not be used as numbers.
|
|
709
|
+
- Added `examples/report.vnt`.
|
|
710
|
+
- New `compiler/formats.py` holds the format parser shared by the checker and
|
|
711
|
+
the code generator.
|
|
712
|
+
|
|
713
|
+
## v0.2.5 — 2026-10-18
|
|
714
|
+
|
|
715
|
+
- Added `arg_count()` and `arg_str(i)`; the generated `main` now receives
|
|
716
|
+
`argc`/`argv` and hands them to the runtime
|
|
717
|
+
- `python main.py run prog.vnt a b` now passes `a b` through to the program
|
|
718
|
+
|
|
719
|
+
## v0.2.4 — 2026-10-18
|
|
720
|
+
|
|
721
|
+
- Added `examples/life.vnt` — Conway's Game of Life on a 40x20 torus, drawn as a
|
|
722
|
+
character grid
|
|
723
|
+
|
|
724
|
+
## v0.2.3 — 2026-10-18
|
|
725
|
+
|
|
726
|
+
- Added `write_i32`, `write_u32`, `write_f32`, `write_f64`, `write_bool`, and
|
|
727
|
+
`write_str` — like their `print_*` counterparts but with no trailing newline
|
|
728
|
+
- Registered `print_newline()`, which had been sitting unreachable in the runtime
|
|
729
|
+
- Added `abs_i32`, `abs_f32`, `min_i32`, `max_i32`, `min_f32`, `max_f32`,
|
|
730
|
+
`floor_f32`, `ceil_f32`
|
|
731
|
+
|
|
732
|
+
## v0.2.2 — 2026-10-18
|
|
733
|
+
|
|
734
|
+
- Added `rand_f32()` — xorshift32, returns `f32` in `[0, 1)`
|
|
735
|
+
- Added `rand_seed(seed: i32)` to select a random stream
|
|
736
|
+
- Random state is fixed at startup, so programs are reproducible until seeded
|
|
737
|
+
- Added 7 tests (type checker, codegen, and an end-to-end determinism test)
|
|
738
|
+
- Fixed: `compiler.VERSION` still reported `0.1.0` and the generated C++ header
|
|
739
|
+
hardcoded `0.2.0`. Both now report `0.2.2`, and the header reads the shared
|
|
740
|
+
`VERSION` constant instead of a literal, so it cannot drift again.
|
|
741
|
+
|
|
742
|
+
## v0.2.1 — 2026-10-17
|
|
743
|
+
|
|
744
|
+
- Added `examples/boids.vnt` — 30 boids on a 100x100 torus, deterministic init
|
|
745
|
+
- Added an end-to-end integration test asserting the four quadrant counts sum
|
|
746
|
+
to 30, so the simulation cannot silently stop placing every boid
|
|
747
|
+
|
|
748
|
+
## v0.2.0 — 2026-10-17
|
|
749
|
+
|
|
750
|
+
- Added the `str` type, with escape sequences `\t`, `\n`, `\"`, `\\`
|
|
751
|
+
- Added `print_str`
|
|
752
|
+
- `==` and `!=` on `str` compare contents via `strcmp`, not pointers
|
|
753
|
+
- Added `Vec2.normalized` and `Quat.dot`
|
|
754
|
+
- Added `translation`, `scale`, and `from_quat` builtins returning `Matrix4`
|
|
755
|
+
|
|
756
|
+
## v0.1.3 — 2026-10-17
|
|
757
|
+
|
|
758
|
+
- Added fixed-size arrays: `[T, N]` types, array literals, indexing, and `len(xs)`
|
|
759
|
+
- Added Quat math: `Quat * Quat`, `Quat * Vec3`
|
|
760
|
+
- Added Matrix4 extras: `Matrix4 * Matrix4`, `Matrix4 * Vec3`
|
|
761
|
+
- Documented `examples/arrays_and_math.vnt`
|
|
762
|
+
|
|
763
|
+
## v0.1.2 — 2026-10-17
|
|
764
|
+
|
|
765
|
+
- Added `const` declarations (compile-time values, emitted as C++ `constexpr`)
|
|
766
|
+
- Added `assert(cond)`
|
|
767
|
+
- Added return-type inference when `-> T` is omitted
|
|
768
|
+
- Added 3-argument `range(start, end, step)`
|
|
769
|
+
- Added element-wise `Vec2` / `Vec3` equality and inequality
|
|
770
|
+
- Fixed mixed-literal return-type inference
|
|
771
|
+
|
|
772
|
+
## v0.1.1 — 2026-10-17
|
|
773
|
+
|
|
774
|
+
- Added compound assignment `+=`, `-=`, `*=`, `/=`
|
|
775
|
+
- Added `break` and `continue`, with loop-context checking
|
|
776
|
+
- Added `examples/loops.vnt`
|
|
777
|
+
|
|
778
|
+
## v0.1.0 — 2026-10-17
|
|
779
|
+
|
|
780
|
+
- Initial working compiler:
|
|
781
|
+
source → lex → parse → type-check → C++17 → native binary
|
|
782
|
+
- Types: `i32`, `u32`, `f32`, `f64`, `bool`, `void`, `ptr<T>`
|
|
783
|
+
- Structs, `component`, and `system` declarations
|
|
784
|
+
- Non-copyable stack-scoped `Arena` allocator
|
|
785
|
+
- Built-in `Vec2`, `Vec3`, `Matrix4`, `Quat`
|
|
786
|
+
- Examples: `examples/vector_math.vnt`, `examples/physics.vnt`
|