Skip to content

feat: add WebAssembly binding for client-side analysis and execution - #228

Open
ascandone wants to merge 1 commit into
mainfrom
feat/wasm-binding
Open

ascandone wants to merge 1 commit into
mainfrom
feat/wasm-binding

Conversation

@ascandone

Copy link
Copy Markdown
Contributor

What

Adds a browser WebAssembly build of numscript, so a host can parse, run and
statically analyse scripts entirely client-side — no backend.

Changes

  • internal/wasmbinding — host-testable core (Check, Run,
    NeededVariables, Version) wrapping analysis.CheckSource and the
    interpreter over an in-memory StaticStore, emitting stable JSON envelopes.
    Diagnostics carry 0-based, LSP-aligned ranges/severities; balance amounts keep
    integer precision via big.Int; panics are recovered into error envelopes.
  • cmd/wasm — a thin syscall/js shim (js && wasm build tag) that
    registers globalThis.__numscript.{check,run,getNeededVariables,version} and
    keeps the runtime alive.
  • Justfile — just wasm builds dist/numscript.wasm + wasm_exec.js
    (standard Go, -ldflags="-s -w"; ~7.3 MB, ~1.7 MB gzip).

Why

Powers a fully client-side numscript playground (and any other editor):
real-time parse/type diagnostics with exact line spans, plus execution, without
a server round-trip.

Testing

  • go test ./internal/wasmbinding/... (incl. -race), go vet (host + GOOS=js GOARCH=wasm), gofmt.
  • Node smoke test of the built wasm: check (valid → none; invalid → error at the exact range), run (postings, 10^21 balance exact, missing-funds error), getNeededVariables.

https://claude.ai/code/session_016gu5cG7jeqGumbny3XkfDC

return n, true
}

f, _, err := big.ParseFloat(s, 10, 256, big.ToNearestEven)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [minor] Fixed-precision float fallback can silently change balance amounts

The fallback rounds the input to 256 bits before checking f.Int's accuracy. That accuracy only describes conversion of the already-rounded float, not preservation of the input. For example, a decimal balance consisting of 1 followed by a decimal point, 100 zeros and 1 rounds to exactly 1 and is accepted, contrary to the documented rejection of fractional amounts. Large scientific-notation integers beyond 256 significant bits can likewise be changed before execution.

Suggestion: Parse decimal/scientific notation exactly using an integer mantissa and decimal exponent (or an exact rational), and accept only values with no fractional remainder. Add tests for a fractional value smaller than the float precision and a scientific-notation integer exceeding 256 bits.


type neededVarsEnvelope struct {
Ok bool `json:"ok"`
Variables map[string]string `json:"variables,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [minor] Successful variable discovery omits the empty variables object

For a script with no required variables, NeededVariables initializes an empty map, but the variables,omitempty tag removes it from the response. The result is {"ok":true}, rather than the shim's documented successful {ok, variables} envelope. Hosts that enumerate the successful response's variables without a special missing-field fallback will fail for ordinary scripts with no inputs; the existing test only covers a nonempty map.

Suggestion: Keep variables present on successful responses, including an empty object, while retaining the error envelope shape as needed. Add a regression test asserting the raw JSON shape for a script with no required variables.

@NumaryBot NumaryBot added risk: medium NumaryBot classified this pull request as medium risk. bot-reviewed NumaryBot completed its review workflow for the current head. review-inconclusive labels Oct 29, 2026
Add a browser WebAssembly build of numscript so a host can parse, run and
statically analyse scripts entirely client-side, with no backend.

- internal/wasmbinding: host-testable core (Check/Run/NeededVariables/Version)
  that wraps analysis.CheckSource and the interpreter over an in-memory
  StaticStore, emitting stable JSON envelopes. Diagnostics carry 0-based,
  LSP-aligned ranges and severities; balance amounts keep integer precision via
  big.Int; panics are recovered into error envelopes.
- cmd/wasm: a syscall/js shim registering globalThis.__numscript.{check,run,
  getNeededVariables,version} (js && wasm build tag).
- Justfile: `just wasm` builds dist/numscript.wasm + wasm_exec.js.

Claude-Session: https://claude.ai/code/session_016gu5cG7jeqGumbny3XkfDC
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot-reviewed NumaryBot completed its review workflow for the current head. review-inconclusive risk: medium NumaryBot classified this pull request as medium risk.

Development

Successfully merging this pull request may close these issues.

2 participants