Files
gemigreerd-js-maker/scrbl/js-maker.scrbl
T
2026-06-08 13:21:57 +02:00

122 lines
3.7 KiB
Racket

#lang scribble/manual
@(require (for-label racket/base js-maker))
@title{js-maker}
@author+email["Hans Dijkema" "hans@dijkewijk.nl"]
@defmodule[js-maker]
@racketmodname[js-maker] provides a deliberately small macro for generating
JavaScript text from a compact Racket/Scheme-like syntax. The public API is
intentionally limited to @racket[js]. The lower-level dispatcher used by the
implementation is internal and is not exported.
@section{Public API}
@defform[(js form ...)]{
Generates a JavaScript program fragment as a string.
Each @racket[form] is translated and emitted as a JavaScript statement. The
macro is statement-oriented. When a value must leave a generated function, use
an explicit @racket[(return expr)] form.
@codeblock{
(js
(define (add1 x)
(return (+ x 1))))
}
}
There is no public @racket[js/expression] form in js-maker 3. Expression-style
checks can be written by generating a function and using @racket[(return ...)]
inside that function.
@section{Supported forms}
The compact implementation supports:
@itemlist[
@item{@racket[(define (name arg ...) body ...)]}
@item{@racket[(define name expr)]}
@item{@racket[(lambda (arg ...) body ...)] and @racket[(λ (arg ...) body ...)]}
@item{@racket[(if condition then else)]}
@item{@racket[(begin body ...)]}
@item{@racket[(return expr)]}
@item{@racket[(set! target expr)]}
@item{ordinary @racket[let], with parallel binding semantics}
@item{@racket[let*], with sequential binding semantics}
@item{named @racket[let], compiled as a @tt{while (true)} loop}
@item{@racket[quote] and @racket[eval] for simple datum insertion}
@item{ordinary calls and the common infix operators @racket[+], @racket[-], @racket[*], @racket[/], @racket[>], @racket[<], @racket[>=], @racket[<=], @racket[==], @racket[===], @racket[!=], @racket[!==]}
@item{@racket[and], @racket[or] and @racket[not]}
@item{@racket[(send obj method arg ...)]}
@item{@racket[(new Class arg ...)]}
@item{@racket[(js-dot obj field ...)] and @racket[(dot obj field ...)]}
@item{@racket[(js-ref obj key ...)], for JavaScript bracket/index access}
@item{@racket[(list ...)] and @racket[(cons a b)]}
]
@section{Indexed access}
@racket[js-ref] is a form understood by the @racket[js] macro. It is not
exported as a separate binding. It generates JavaScript bracket access, which
works for arrays and computed object properties.
@codeblock{
(js
(define (arrayAt xs i)
(return (js-ref xs i))))
}
@codeblock{
(js
(define (nameOf obj)
(return (js-ref obj "name"))))
}
The same form can be used as a @racket[set!] target:
@codeblock{
(js
(define (put xs i value)
(set! (js-ref xs i) value)
(return xs)))
}
@section{Ordinary let}
Ordinary @racket[let] keeps Racket's parallel binding semantics. Initializers
are evaluated before the bound names are introduced. The generated JavaScript
therefore uses temporary constants and an inner block, so JavaScript's temporal
dead zone does not accidentally shadow initializer references.
@codeblock{
(js
(define (ordinaryLet x)
(let ([x 1] [y x])
(return y))))
}
@section{Named let}
Named @racket[let] is compiled to a loop. A tail call to the loop name is
translated into parallel assignments to the loop variables followed by
@tt{continue}. A branch that exits the loop should use @racket[(return ...)].
@codeblock{
(js
(define (sumTo n)
(let loop ([i 0] [acc 0])
(if (> i n)
(return acc)
(loop (+ i 1) (+ acc i))))))
}
@section{Tests and demos}
The package contains maintained tests under @filepath{testing/} and small demos
under @filepath{demo/}. The old js-maker 2 tests for runtime shims and broader
language constructs have been removed or replaced where they did not apply to
the compact js-maker 3 surface language.