Robuuster returns in javascript.
This commit is contained in:
@@ -76,42 +76,46 @@ Use this prompt when asking ChatGPT to make a new Racket module or extend this
|
|||||||
one:
|
one:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Work on a Racket module/package in a version-locked buildmap.
|
Work on a Racket module/package in a versioned build directory.
|
||||||
|
|
||||||
Important:
|
Important:
|
||||||
- Always create a new subdirectory for the release, for example
|
- Always create a new subdirectory for the deliverable, for example
|
||||||
/mnt/data/<project>-build-NNN/<collection-name>.
|
/mnt/data/<project>-build-NNN/<collection-name>.
|
||||||
- Do not work directly in /mnt/data with individual files of the same name;
|
- Do not work directly in /mnt/data with loose files that have the same names;
|
||||||
avoid version confusion by copying/patching everything into that buildmap.
|
avoid version confusion by copying and patching everything inside that build
|
||||||
- Keep the package structure consistent:
|
directory.
|
||||||
|
- Keep the package structure stable:
|
||||||
- main.rkt for the public module;
|
- main.rkt for the public module;
|
||||||
- testing/ for test infrastructure and regression tests;
|
- testing/ for test infrastructure and regression tests;
|
||||||
- demo/ for demonstration files;
|
- demo/ for demonstration files;
|
||||||
- info.rkt for package metadata and test entry points.
|
- info.rkt for package metadata and test entry points.
|
||||||
- Adjust require paths to match that structure before testing.
|
- Adjust require paths to that structure before testing.
|
||||||
- Test with Racket itself, for example:
|
- Test with Racket itself, for example:
|
||||||
/tmp/racket/bin/raco make <collection>/main.rkt <collection>/testing/<tests>.rkt
|
/tmp/racket/bin/raco make <collection>/main.rkt <collection>/testing/<tests>.rkt
|
||||||
/tmp/racket/bin/racket <collection>/testing/<tests>.rkt
|
/tmp/racket/bin/racket <collection>/testing/<tests>.rkt
|
||||||
- If JavaScript is required, use a separate executor/test-framework module.
|
- If JavaScript execution is needed, use a separate executor/test-framework
|
||||||
Tests must not fail simply because node/deno/bun/qjs is missing; they must
|
module. Tests must not fail solely because node/deno/bun/qjs is missing; in
|
||||||
skip with clear warnings unless a REQUIRE environment variable has been set.
|
that case they should use an explicit non-failing JavaScript stub that reports
|
||||||
- Do not use shell-based internet access for dependencies. If packages are needed, retrieve
|
this to stdout, unless a REQUIRE environment variable is set.
|
||||||
them via the rktsndbx bootstrap/package-index flow.
|
- Do not use shell-based internet access for dependencies. If packages are
|
||||||
- After completion, provide a zip file containing exactly the tested buildmap.
|
needed, fetch them through the rktsndbx bootstrap/package-index flow.
|
||||||
- Briefly report which commands were executed, what the test results were,
|
- Deliver a zip file containing exactly the tested build directory.
|
||||||
and which zip file contains the tested result.
|
- Briefly report which commands were run, what the test results were, and which
|
||||||
|
zip contains the tested result.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Latest tested fix
|
## Latest tested fix
|
||||||
|
|
||||||
This build includes the `with-handlers` callee-position fix for inline lambda
|
This build includes the `with-handlers` callee-position fix for inline lambda
|
||||||
handlers, including rest-argument handlers such as `(lambda args ...)`. It also
|
handlers, including rest-argument handlers such as `(lambda args ...)`. It also
|
||||||
fixes top-level `js` statement-context handling for `with-handlers`, so a
|
fixes top-level `js` statement-context handling in general: `js` now emits
|
||||||
side-effecting catch handler does not prematurely return from the surrounding
|
program/statement text and does not invent an implicit top-level `return`. This
|
||||||
JavaScript wrapper. Use `js/expression` when the value of a `with-handlers`
|
keeps snippets valid when they are passed directly to WebView
|
||||||
form itself is needed. The build also adds a Racket-like division-by-zero
|
`runJavaScript`/`evaluateJavaScript`, where a top-level JavaScript `return` is
|
||||||
runtime check for `/`, so the generic `exn?` handler subset can catch
|
a syntax error. Use `js/expression` when a generated JavaScript value is needed.
|
||||||
`(/ 10 0)`.
|
The build 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
|
## JavaScript use case demos
|
||||||
|
|
||||||
|
|||||||
@@ -1439,32 +1439,13 @@ if (~a !== false) return ~a;" tmp (compile-expr arg) tmp tmp)))
|
|||||||
[(list f args ...) (compile-call f args)]
|
[(list f args ...) (compile-call f args)]
|
||||||
[_ (literal->js d)]))
|
[_ (literal->js d)]))
|
||||||
|
|
||||||
(define (top-tail-returnable? d)
|
|
||||||
(match d
|
|
||||||
[(list 'define _ ...) #f]
|
|
||||||
[(list 'define-values _ ...) #f]
|
|
||||||
[(list 'define-class _ ...) #f]
|
|
||||||
[(list 'set! _ ...) #f]
|
|
||||||
[(list 'vector-set! _ ...) #f]
|
|
||||||
[(list 'set-prop! _ ...) #f]
|
|
||||||
[(list 'delete-prop! _ ...) #f]
|
|
||||||
[(list 'while _ ...) #f]
|
|
||||||
[(list 'for _ ...) #f]
|
|
||||||
;; Top-level js is statement/program output. with-handlers may contain
|
|
||||||
;; a handler used only for side effects; compiling it as an implicit
|
|
||||||
;; tail return would emit return statements inside the generated
|
|
||||||
;; try/catch and prematurely leave the caller's wrapper. Use
|
|
||||||
;; js/expression when the value of with-handlers is needed.
|
|
||||||
[(list 'with-handlers _ ...) #f]
|
|
||||||
[_ #t]))
|
|
||||||
|
|
||||||
(define (compile-top forms)
|
(define (compile-top forms)
|
||||||
(cond
|
;; A js form produces JavaScript program/statement text. It must be valid
|
||||||
[(null? forms) ""]
|
;; when handed directly to a WebView run/evaluateJavaScript entry point, so
|
||||||
[(top-tail-returnable? (last forms))
|
;; it may not invent top-level return statements. Return insertion still
|
||||||
(compile-body forms #:return-last? #t)]
|
;; happens in expression contexts and function bodies, where JavaScript
|
||||||
[else
|
;; return is syntactically valid.
|
||||||
(compile-body forms #:return-last? #f)])))
|
(compile-body forms #:return-last? #f)))
|
||||||
|
|
||||||
(define-syntax (js stx)
|
(define-syntax (js stx)
|
||||||
(syntax-case stx ()
|
(syntax-case stx ()
|
||||||
|
|||||||
+13
-21
@@ -16,18 +16,10 @@
|
|||||||
(list (verbatim rkt) (verbatim js)))))
|
(list (verbatim rkt) (verbatim js)))))
|
||||||
|
|
||||||
@title{js-maker: a Syntax-Driven Racket-to-JavaScript Generator}
|
@title{js-maker: a Syntax-Driven Racket-to-JavaScript Generator}
|
||||||
@author+email["Hans Dijkema" "hans@dijkewijk.nl"]
|
@author+email["Hans Dijkema" ""]
|
||||||
|
|
||||||
@defmodule[js-maker]
|
@defmodule[js-maker]
|
||||||
|
|
||||||
This module has been largely coded as an evolution to the @tt{js-transformer} setup
|
|
||||||
from @tt{racket-webview} by supervising it's evolution using AI.
|
|
||||||
It is astonishing what AI can do these days. To get the AI agent this far
|
|
||||||
on racket and testing the output by itself, I had to get @tt{racket} installed
|
|
||||||
in the coding sandbox of the AI agent.
|
|
||||||
See @hyperlink["https://racket.discourse.group/t/a-small-experiment-bootstrapping-racket-into-a-chatgpt-sandbox-at-openai/4238"]{the discourse article}
|
|
||||||
about that.
|
|
||||||
|
|
||||||
@bold{js-maker} is a small, syntax-driven JavaScript generator for writing a
|
@bold{js-maker} is a small, syntax-driven JavaScript generator for writing a
|
||||||
practical JavaScript subset in Racket notation. It provides two macros,
|
practical JavaScript subset in Racket notation. It provides two macros,
|
||||||
@racket[js] and @racket[js/expression]. In the ordinary case the macros
|
@racket[js] and @racket[js/expression]. In the ordinary case the macros
|
||||||
@@ -70,13 +62,12 @@ function square(x) {
|
|||||||
Inside function bodies, the last expression is returned automatically unless it
|
Inside function bodies, the last expression is returned automatically unless it
|
||||||
is already a statement form such as @racket[return], @racket[define],
|
is already a statement form such as @racket[return], @racket[define],
|
||||||
@racket[set!], @racket[while], or @racket[for]. At the top level of a
|
@racket[set!], @racket[while], or @racket[for]. At the top level of a
|
||||||
@racket[js] form, js-maker also returns the value of a final value-producing
|
@racket[js] form, however, js-maker emits statement code and does not invent an
|
||||||
form, such as @racket[let], @racket[begin], or @racket[if]. This makes
|
implicit @tt{return}. This keeps the generated code valid when it is handed
|
||||||
@racket[js] output suitable as the body of a WebView @tt{runJavaScript}
|
directly to a WebView @tt{runJavaScript} or @tt{evaluateJavaScript} entry point,
|
||||||
wrapper function. A top-level @racket[with-handlers] is treated as a statement
|
where a top-level JavaScript @tt{return} would be an @tt{Illegal return}
|
||||||
so that a catch handler used for side effects does not prematurely return from
|
syntax error. Use @racket[js/expression] when a generated JavaScript value is
|
||||||
the surrounding wrapper. Use @racket[js/expression] when the value of a
|
needed.
|
||||||
@racket[with-handlers] form itself is needed.
|
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
@@ -95,7 +86,7 @@ emits JavaScript similar to:
|
|||||||
{
|
{
|
||||||
let el = document.getElementById("test");
|
let el = document.getElementById("test");
|
||||||
el.innerHTML = "<h1>Hi</h1>";
|
el.innerHTML = "<h1>Hi</h1>";
|
||||||
return true;
|
true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,14 +152,15 @@ source text.
|
|||||||
#<<RKT
|
#<<RKT
|
||||||
(let ([x 10]
|
(let ([x 10]
|
||||||
[y 20])
|
[y 20])
|
||||||
(js (let ([a (inject (* x y))])
|
(js/expression
|
||||||
(return (* a a)))))
|
(let ([a (inject (* x y))])
|
||||||
|
(* a a))))
|
||||||
RKT
|
RKT
|
||||||
#<<JS
|
#<<JS
|
||||||
{
|
(() => {
|
||||||
let a = 200;
|
let a = 200;
|
||||||
return (a * a);
|
return (a * a);
|
||||||
}
|
})()
|
||||||
JS
|
JS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -37,9 +37,9 @@
|
|||||||
(runtime-eval-macro-function 't "BEE"))
|
(runtime-eval-macro-function 't "BEE"))
|
||||||
(unless (and (regexp-match? #rx"getElementById\\(\"t\"\\)" runtime-eval-macro-program)
|
(unless (and (regexp-match? #rx"getElementById\\(\"t\"\\)" runtime-eval-macro-program)
|
||||||
(regexp-match? #rx"innerHTML = \"BEE\"" runtime-eval-macro-program)
|
(regexp-match? #rx"innerHTML = \"BEE\"" runtime-eval-macro-program)
|
||||||
(regexp-match? #rx"return true;" runtime-eval-macro-program))
|
(not (regexp-match? #rx"return true;" runtime-eval-macro-program)))
|
||||||
(error 'jsmaker-regression
|
(error 'jsmaker-regression
|
||||||
"runtime eval inside a user macro should keep use-site lexical bindings, got: ~a"
|
"runtime eval inside a user macro should keep use-site lexical bindings and should not emit an implicit top-level return, got: ~a"
|
||||||
runtime-eval-macro-program))
|
runtime-eval-macro-program))
|
||||||
|
|
||||||
(define-syntax with-id->el/inject
|
(define-syntax with-id->el/inject
|
||||||
@@ -58,9 +58,9 @@
|
|||||||
(runtime-inject-macro-function 't "BEE"))
|
(runtime-inject-macro-function 't "BEE"))
|
||||||
(unless (and (regexp-match? #rx"getElementById\\(\"t\"\\)" runtime-inject-macro-program)
|
(unless (and (regexp-match? #rx"getElementById\\(\"t\"\\)" runtime-inject-macro-program)
|
||||||
(regexp-match? #rx"innerHTML = \"BEE\"" runtime-inject-macro-program)
|
(regexp-match? #rx"innerHTML = \"BEE\"" runtime-inject-macro-program)
|
||||||
(regexp-match? #rx"return true;" runtime-inject-macro-program))
|
(not (regexp-match? #rx"return true;" runtime-inject-macro-program)))
|
||||||
(error 'jsmaker-regression
|
(error 'jsmaker-regression
|
||||||
"runtime inject inside a user macro should keep use-site lexical bindings, got: ~a"
|
"runtime inject inside a user macro should keep use-site lexical bindings and should not emit an implicit top-level return, got: ~a"
|
||||||
runtime-inject-macro-program))
|
runtime-inject-macro-program))
|
||||||
|
|
||||||
(define simple-let-star
|
(define simple-let-star
|
||||||
|
|||||||
Reference in New Issue
Block a user