158 lines
6.8 KiB
Markdown
158 lines
6.8 KiB
Markdown
# js-maker
|
|
|
|
A syntax-driven Racket-to-JavaScript macro experiment.
|
|
|
|
## Layout
|
|
|
|
```text
|
|
jsmaker/
|
|
main.rkt public macro module
|
|
info.rkt package metadata and package test entry
|
|
private/
|
|
syntax-helpers.rkt compatibility/helper material
|
|
utils.rkt compatibility/helper material
|
|
scrbl/
|
|
jsmaker.scrbl Scribble documentation
|
|
testing/
|
|
jsmaker-executors.rkt JavaScript engine discovery/execution
|
|
jsmaker-test-framework.rkt JS regression framework
|
|
jsmaker-test-runner.rkt old-name compatibility wrapper
|
|
jsmaker-regression.rkt core expression tests
|
|
jsmaker-regexp-regression.rkt regexp tests
|
|
jsmaker-program-regression.rkt larger program tests
|
|
jsmaker-regressions.rkt aggregate test entry
|
|
demo/
|
|
show-jsmaker-output.rkt
|
|
show-optimized.rkt
|
|
```
|
|
|
|
## Notes on private helpers
|
|
|
|
Static analysis of this package layout shows that `main.rkt`, the test
|
|
infrastructure, the regression tests, demos and Scribble documentation do not
|
|
require `private/utils.rkt` or `private/syntax-helpers.rkt`. Those files are
|
|
retained as compatibility material from the source project and are omitted from
|
|
compilation and the package test entry point in `info.rkt`.
|
|
|
|
The current public module has no dependency on Gregor or the old helper
|
|
modules. Gregor-style date/time forms are translated syntactically by
|
|
`main.rkt` into a small JavaScript-side representation.
|
|
|
|
## Added language support
|
|
|
|
This package includes conservative support for:
|
|
|
|
- `(with-handlers ([exn? handler]) body ...)`, translated to JavaScript
|
|
`try`/`catch`. Only generic `exn?` predicates are accepted.
|
|
- Gregor-style local names such as `date`, `time`, `moment`, `parse-date`,
|
|
`parse-time`, `parse-moment`, `date->string`, `time->string`, `->year`,
|
|
`->month`, `->day`, `->hours`, `->minutes`, `->seconds`, `->js-date`, and
|
|
`js-date->datetime`. Import prefixes are deliberately not hardcoded; the
|
|
compiler matches on the local identifier name after any `prefix:` part.
|
|
|
|
## Run tests
|
|
|
|
From the directory above `jsmaker`:
|
|
|
|
```bash
|
|
raco make jsmaker/main.rkt jsmaker/testing/jsmaker-regressions.rkt \
|
|
jsmaker/scrbl/jsmaker.scrbl
|
|
racket jsmaker/testing/jsmaker-regressions.rkt
|
|
raco test -p jsmaker
|
|
```
|
|
|
|
The test framework looks for JavaScript engines such as `node`, `deno`,
|
|
`bun`, `qjs`, `d8`, `jsc`, `js`. Chromium is only used when explicitly selected
|
|
or when `JSMAKER_BROWSER_FALLBACK=1` is set.
|
|
|
|
When no JavaScript engine is available, the tests generate the JavaScript test
|
|
files and print warnings, but do not fail unless `JSMAKER_REQUIRE_ENGINE` or
|
|
`JSMAKER_REQUIRE_NODE` is set.
|
|
|
|
Useful environment variables:
|
|
|
|
```bash
|
|
JSMAKER_ENGINE=auto|node|deno|bun|qjs|d8|jsc|js|chromium
|
|
JSMAKER_ENGINE_PATH=/path/to/executable
|
|
JSMAKER_NODE=/path/to/node
|
|
JSMAKER_REQUIRE_ENGINE=1
|
|
JSMAKER_ENGINE_TIMEOUT_SECONDS=15
|
|
JSMAKER_BROWSER_FALLBACK=1
|
|
```
|
|
|
|
## Suggested start prompt for future work
|
|
|
|
Use this prompt when asking ChatGPT to make a new Racket module or extend this
|
|
one:
|
|
|
|
```text
|
|
Werk aan een Racket-module/package in een versievaste buildmap.
|
|
|
|
Belangrijk:
|
|
- Maak altijd een nieuwe submap voor de oplevering, bijvoorbeeld
|
|
/mnt/data/<project>-build-NNN/<collection-name>.
|
|
- Werk niet direct in /mnt/data met losse bestanden met dezelfde naam;
|
|
voorkom versieverwarring door alles in die buildmap te kopiëren/patchen.
|
|
- Houd de package-structuur stabiel:
|
|
- main.rkt voor de publieke module;
|
|
- private/ voor helpers en utils;
|
|
- testing/ voor test-infrastructuur en regressietests;
|
|
- demo/ voor demonstratiebestanden;
|
|
- info.rkt voor package metadata en test entry points.
|
|
- Pas require-paden aan op die structuur voordat je test.
|
|
- Test met Racket zelf, bijvoorbeeld:
|
|
/tmp/racket/bin/raco make <collection>/main.rkt <collection>/testing/<tests>.rkt
|
|
/tmp/racket/bin/racket <collection>/testing/<tests>.rkt
|
|
- Als JavaScript nodig is, gebruik een aparte executor/test-framework module.
|
|
Tests mogen niet falen alleen omdat node/deno/bun/qjs ontbreekt; ze moeten
|
|
dan skippen met duidelijke warnings, tenzij een REQUIRE-envvar is gezet.
|
|
- Gebruik geen shell-internet voor dependencies. Als packages nodig zijn, haal
|
|
ze via de rktsndbx bootstrap/package-index flow op.
|
|
- Lever na afloop een zip van exact de geteste buildmap op.
|
|
- Rapporteer kort welke commando's zijn uitgevoerd, wat de testresultaten waren,
|
|
en welke zip het geteste resultaat bevat.
|
|
```
|
|
|
|
|
|
## Latest tested fix
|
|
|
|
This build includes the `with-handlers` callee-position fix for inline lambda
|
|
handlers, including rest-argument handlers such as `(lambda args ...)`. It also
|
|
adds a Racket-like division-by-zero runtime check for `/`, so the generic
|
|
`exn?` handler subset can catch `(/ 10 0)`.
|
|
|
|
## JavaScript use case demos
|
|
|
|
The package includes a larger set of JavaScript use case snippets in
|
|
`demo/js-usecases.rkt`. They are written in the Racket surface syntax accepted
|
|
by `js` and compiled to JavaScript by the macro. The generated JavaScript is
|
|
also written to `demo/js-usecases.generated.js`.
|
|
|
|
The corresponding regression tests live in `testing/jsmaker-usecases.rkt` and
|
|
are included by `testing/jsmaker-regressions.rkt`. The test framework now awaits
|
|
Promise-valued tests, so asynchronous examples such as the Fetch API can be
|
|
checked with Node as well.
|
|
|
|
Covered use cases include random numbers, `Set`, JavaScript falsey values,
|
|
currying, object destructuring, `setInterval`/`clearInterval`, object property
|
|
get/set/delete, string concatenation order, `Object.freeze`/`Object.seal`,
|
|
switch/case, classes with constructor defaults, sorting objects, array deletion
|
|
techniques, Bubble Sort, recursive Binary Search, `Map` counting, DOM HTML
|
|
access, anagram checks, pair-sum checks, and Fetch API result/error handling.
|
|
|
|
## Use-case documentation
|
|
|
|
The file `scrbl/usecases.scrbl` documents the JavaScript use cases from
|
|
`demo/js-usecases.rkt`. Each use case is shown as Racket/js-maker source next
|
|
to representative generated JavaScript, followed by the behavior covered by the
|
|
regression test.
|
|
|
|
The use-case tests in `testing/jsmaker-usecases.rkt` intentionally use
|
|
`js/expression` for the test calls wherever possible. Raw JavaScript is kept
|
|
only for small test-harness preambles such as fake timers, fake DOM objects, and
|
|
fake fetch.
|
|
|
|
## Hash regression tests
|
|
|
|
This build adds `testing/jsmaker-hash-regression.rkt`, covering common hash operations such as `hash`, `make-hash`, `hash-ref`, `hash-set`, `hash-set!`, `hash-remove`, `hash-remove!`, `hash-update`, `hash-update!`, `hash-clear`, `hash-clear!`, `hash-copy`, `hash-keys`, `hash-values`, `hash->list`, `hash-map`, and `hash-for-each`. The current JavaScript backend represents hashes as plain JavaScript objects, so this is a practical string/symbol-key subset rather than a full Racket hash-table implementation for arbitrary keys.
|