Skip to content

Commit c77cd41

Browse files
committed
Merge branch 'dev' into line-numbers
2 parents 2da9845 + 062397c commit c77cd41

File tree

253 files changed

+32339
-18381
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

253 files changed

+32339
-18381
lines changed

.github/workflows/deploy_branches.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ jobs:
5151
eval $(opam env)
5252
export OPAMYES=1
5353
opam clean --all-switches --download-cache --logs --repo-cache --unused-repositories
54+
- name: Setup zarith native BigInt runtime
55+
run: opam exec -- make setup-zarith
56+
working-directory: ./source
5457
- name: Build Release
5558
run: |
5659
export DUNE_CACHE=enabled

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,8 @@ hazel.opam.locked.old
5959

6060
# Code coverage
6161
_coverage/
62-
node_modules/**
62+
node_modules/**
63+
node_modules
64+
65+
# Claude Code
66+
.claude/

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2016-2024
3+
Copyright (c) 2016-2026
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

Makefile

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,23 @@ TEST_DIR="$(shell pwd)/_build/default/test"
22
HTML_DIR="$(shell pwd)/_build/default/src/web/www"
33
SERVER="http://0.0.0.0:8000/"
44

5-
.PHONY: all deps change-deps setup-instructor setup-student dev dev-helper dev-student fmt watch watch-release release release-student grade echo-html-dir serve serve2 repl test clean
5+
.PHONY: all deps change-deps setup-instructor setup-student dev dev-helper dev-student fmt watch watch-release release release-student grade echo-html-dir serve serve2 repl test clean setup-zarith
66

77
all: dev
88

9+
# Install native BigInt runtime for zarith_stubs_js to fix WebWorker postMessage serialization.
10+
# The vendored runtime.js uses native JS BigInt (from zarith_stubs_js v0.17.0) which survives
11+
# structured clone, unlike the BigInteger.js library used in older versions.
12+
setup-zarith:
13+
@echo "Installing native BigInt zarith runtime..."
14+
@cp vendor/zarith_native_bigint_runtime.js "$$(opam var lib)/zarith_stubs_js/runtime.js"
15+
916
deps:
1017
opam repo add archive git+https://github.com/ocaml/opam-repository-archive
1118
opam update
1219
opam install ./hazel.opam.locked --deps-only --with-test --with-doc
1320
npm install
21+
$(MAKE) setup-zarith
1422

1523
change-deps:
1624
opam update
@@ -25,7 +33,7 @@ setup-instructor:
2533
setup-student:
2634
cp src/web/exercises/settings/ExerciseSettings_student.re src/web/exercises/settings/ExerciseSettings.re
2735

28-
dev-helper:
36+
dev-helper: setup-zarith
2937
dune fmt --auto-promote || true
3038
dune build @ocaml-index @src/fmt --auto-promote src --profile dev
3139

@@ -36,16 +44,16 @@ dev-student: setup-student dev-helper
3644
fmt:
3745
dune fmt --auto-promote
3846

39-
watch: setup-instructor
47+
watch: setup-instructor setup-zarith
4048
dune build @ocaml-index @src/fmt --auto-promote src --profile dev --watch
4149

42-
watch-release: setup-instructor
50+
watch-release: setup-instructor setup-zarith
4351
dune build @src/fmt --auto-promote src --profile release --watch
4452

45-
release: setup-instructor
53+
release: setup-instructor setup-zarith
4654
dune build @src/fmt --auto-promote src --profile release
4755

48-
release-student: setup-student
56+
release-student: setup-student setup-zarith
4957
dune build @src/fmt --auto-promote src --profile dev # Uses dev profile for performance reasons. It may be worth it to retest since the ocaml upgrade
5058

5159
grade:
@@ -86,7 +94,7 @@ coverage:
8694
dune runtest --instrument-with bisect_ppx --force
8795
bisect-ppx-report summary
8896

89-
ci:
97+
ci: setup-zarith
9098
dune build --profile dev
9199
dune runtest --instrument-with bisect_ppx --force
92100

docs/bigint-webworker-fix.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# BigInt WebWorker Serialization Fix
2+
3+
This document explains the vendored zarith runtime and why it's needed.
4+
5+
**Vendored file:** `vendor/zarith_native_bigint_runtime.js`
6+
**Source:** `zarith_stubs_js` v0.17.0
7+
**Purpose:** Fixes BigInt serialization through WebWorker `postMessage`
8+
9+
### The Problem
10+
11+
Hazel uses a WebWorker for evaluation to avoid blocking the UI. Data (including
12+
the AST with BigInt values) is sent to/from the worker via `postMessage`, which
13+
uses the browser's [structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).
14+
15+
The `zarith_stubs_js` package (v0.16.x) that provides BigInt support for
16+
js_of_ocaml uses the [BigInteger.js](https://github.com/peterolson/BigInteger.js)
17+
library internally. BigInteger.js creates custom JavaScript objects with
18+
prototype methods like `.lt()`, `.add()`, etc. When these objects pass through
19+
structured clone, **the prototype chain is lost** - the data survives but the
20+
methods don't. This caused crashes when displaying evaluation results containing
21+
large integers.
22+
23+
### The Solution
24+
25+
Starting in v0.17.0, `zarith_stubs_js` switched to using **native JavaScript
26+
`BigInt`** instead of BigInteger.js. Native `BigInt` is a primitive type that
27+
fully survives structured clone.
28+
29+
However, we cannot simply upgrade to zarith_stubs_js v0.17.0 because it requires
30+
upgrading the Jane Street packages. At the time of this fix (January 2026),
31+
bonsai v0.17.0 on opam constrains `js_of_ocaml < 5.7.0`, and versions of
32+
`js_of_ocaml-compiler` before 5.7.0 require `ocaml < 5.2`. Note that bonsai's
33+
master branch on GitHub has updated to require `js_of_ocaml >= 6.0.0`, so a
34+
future bonsai release should resolve this constraint issue.
35+
36+
### Our Approach
37+
38+
We vendor the `runtime.js` from zarith_stubs_js v0.17.0 and copy it into the
39+
opam switch at build time, replacing the v0.16.x version. This gives us native
40+
BigInt support without upgrading the package.
41+
42+
The `Makefile` has a `setup-zarith` target that performs this copy:
43+
44+
```makefile
45+
setup-zarith:
46+
cp vendor/zarith_native_bigint_runtime.js "$$(opam var lib)/zarith_stubs_js/runtime.js"
47+
```
48+
49+
This target is automatically run before builds (`make dev`, `make release`, etc.)
50+
and in CI.
51+
52+
### Alternatives Considered
53+
54+
1. **Full package upgrade** - Blocked by OCaml version / js_of_ocaml constraints
55+
as described above.
56+
57+
2. **O(n) serialization shim** - Our previous approach: recursively walk the
58+
entire message payload before/after `postMessage`, converting BigInts to
59+
tagged strings (`{__hazel_bigint__: "123"}`) and back. This worked but added
60+
overhead proportional to message size (4 traversals per worker round-trip).
61+
62+
### Future Considerations
63+
64+
- **When a new bonsai release is published to opam** (with the js_of_ocaml >= 6.0.0
65+
constraint from master), we should be able to remove this vendored file and do
66+
a proper upgrade. At that point:
67+
1. Upgrade zarith_stubs_js to v0.17.0+ via opam
68+
2. Remove `vendor/zarith_native_bigint_runtime.js`
69+
3. Remove the `setup-zarith` target from the Makefile
70+
4. Remove the `setup-zarith` dependencies from other Makefile targets
71+
72+
- **If zarith_stubs_js changes its internal representation again**, we may need
73+
to update the vendored file. Check the [zarith_stubs_js releases](https://github.com/janestreet/zarith_stubs_js)
74+
for changes.
75+
76+
- **The vendored runtime.js API is stable** - it implements the same `ml_z_*`
77+
functions as the original, just with native BigInt instead of BigInteger.js.
78+
There should be no compatibility issues.
79+
80+
### Related Files
81+
82+
- `vendor/zarith_native_bigint_runtime.js` - The vendored runtime file
83+
- `src/web/util/WorkerClient.re` - Client-side worker communication
84+
- `src/web/util/WorkerServer.re` - Worker-side message handling
85+
- `Makefile` - `setup-zarith` target
86+
- `.github/workflows/deploy_branches.yml` - CI setup-zarith step

hazel

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ set -e
88
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
99

1010
# Path to the JS target relative to the script
11-
JS_TARGET="$SCRIPT_DIR/_build/default/src/hazelcli/cli.bc.js"
11+
JS_TARGET="$SCRIPT_DIR/_build/default/src/CLI/cli.bc.js"
1212

1313
# Run Dune in the script's directory
14-
(cd "$SCRIPT_DIR" && dune build ./src/hazelcli/cli.bc.js)
14+
(cd "$SCRIPT_DIR" && dune build ./src/CLI/cli.bc.js)
1515

1616
# Execute the compiled JavaScript with node
1717
node "$JS_TARGET" "$@"
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Precedence Migration Script
2+
3+
## Why This Exists
4+
5+
Hazel serializes syntax trees into `.ml` files (in `src/web/init/docs/` and `src/b2t2/slides/`). These serialized representations include numeric precedence values baked into nib shapes like `(Concave N)`.
6+
7+
This branch reorganizes precedence values with the following semantic changes:
8+
9+
1. **Move `semi` to be the tightest structural form** — sequences naturally extend into bodies
10+
2. **Consolidate `prod` with `comma`** — both now use the same precedence value
11+
3. **Make `comma` tighter than `let`** — tuples can appear as let bodies without parens
12+
4. **Make `comma` tighter than `rule_sep`** — tuples can appear as case scrutinees without parens
13+
14+
### Parsing examples
15+
16+
| Expression | Dev parsing | New parsing |
17+
|------------|-------------|-------------|
18+
| `fun x -> a; b` | `(fun x -> a); b` | `fun x -> (a; b)` ← changed |
19+
| `if c then a; b else d` | `(if c then a); b else d` | `if c then (a; b) else d` ← changed |
20+
| `let x = 1 in a; b` | `let x = 1 in (a; b)` | `let x = 1 in (a; b)` |
21+
| `fun x -> a, b` | `(fun x -> a), b` | `(fun x -> a), b` |
22+
| `let x = 1 in x, y` | `(let x = 1 in x), y` | `let x = 1 in (x, y)` ← changed |
23+
| `case a, b \| ...` | (comma outside case) | `case (a, b) \| ...` ← changed |
24+
25+
## Precedence Changes
26+
27+
### Old Values (dev baseline)
28+
29+
```
30+
concave_grout = 34
31+
if_ = 35
32+
fun_ = 36
33+
prod = 37
34+
semi = 38
35+
lab = 39
36+
let_ = 40
37+
rule_arr = 41
38+
rule_pre = 42
39+
rule_sep = 43
40+
case_ = 44
41+
comma = 47
42+
min = 48
43+
```
44+
45+
### New Values (this branch)
46+
47+
```
48+
concave_grout = 34 (unchanged)
49+
50+
// ===== SEMICOLON (tightest structural form) =====
51+
semi = 35 (moved up from 38)
52+
53+
// ===== STRUCTURAL FORMS =====
54+
if_ = 36 (shifted from 35)
55+
fun_ = 37 (shifted from 36)
56+
lab = 39 (unchanged)
57+
case_ = 42 (moved from 44)
58+
comma = 44 (moved from 47, prod consolidated here)
59+
let_ = 45 (moved from 40)
60+
rule_sep = 46 (moved from 43)
61+
62+
min = 48 (unchanged)
63+
```
64+
65+
### Migration Mapping
66+
67+
| Dev Value | New Value | Identifier |
68+
|-----------|-----------|------------|
69+
| 35 | 36 | if_ |
70+
| 36 | 37 | fun_ |
71+
| 37 | 44 | prod (→ comma) |
72+
| 38 | 35 | semi |
73+
| 40 | 45 | let_ |
74+
| 43 | 46 | rule_sep |
75+
| 44 | 42 | case_ |
76+
| 47 | 44 | comma |
77+
78+
**Note:** Values 39 (lab), 41 (rule_arr), 42 (rule_pre) are unchanged or unused in current slide files.
79+
80+
**Note:** There are cycles in the migration (35→36→37→44, 38→35, 44→42), so the script uses a two-phase approach with temporary values (100+) to avoid collisions.
81+
82+
## Usage
83+
84+
```bash
85+
# Preview changes without modifying the file
86+
./scripts/migrate_precedence.sh --dry-run path/to/file.ml
87+
88+
# Apply the migration
89+
./scripts/migrate_precedence.sh path/to/file.ml
90+
91+
# Migrate all slide files
92+
find src/web/init/docs src/b2t2/slides -name "*.ml" -exec ./scripts/migrate_precedence.sh {} \;
93+
```
94+
95+
The script will:
96+
- Skip files already migrated (no old dev values found)
97+
- Show a diff in dry-run mode
98+
- Report success after migration
99+
100+
## What It Changes
101+
102+
The script handles two pattern types in serialized files:
103+
104+
1. **Inline patterns**: `(Concave N)``(Concave M)`
105+
2. **Line-wrapped patterns**: When the number appears at the start of a line (after 9 spaces) due to OCaml string wrapping
106+
107+
## Platform Support
108+
109+
- **macOS**: Tested and working
110+
- **Linux**: Included but untested (uses different `sed -i` syntax)
111+
112+
## When to Use
113+
114+
Run this script if you:
115+
- Have slide files from the dev branch that need updating
116+
- Created new slide files based on dev templates
117+
- See test failures in `DocSlides.ReparseBackuptext` related to precedence mismatches
118+
119+
## Technical Notes
120+
121+
### Two-phase migration
122+
123+
Because some source and target values overlap (creating cycles like 35→36→37→44), the script uses temporary values (100+) in phase 1, then converts to final values in phase 2. This prevents sed from double-transforming values.
124+
125+
### Segment structure vs precedence
126+
127+
The segment structure (serialized in `.ml` files) does **not** do precedence parsing. It's a tree based on matching delimiters. Precedence numbers are metadata baked into tiles that only affect how the structure is later interpreted. This migration only updates those metadata numbers.

0 commit comments

Comments
 (0)