oke.
This commit is contained in:
+91
-54
@@ -3,82 +3,119 @@
|
||||
@(require (for-label racket/base js-maker))
|
||||
|
||||
@title{js-maker}
|
||||
@author{Hans Dijkema}
|
||||
@author+email["Hans Dijkema" "hans@dijkewijk.nl"]
|
||||
|
||||
@defmodule[js-maker]
|
||||
|
||||
@emph{js-maker} is a deliberately small syntax-driven JavaScript string maker.
|
||||
It provides one public macro, @racket[js]. The helper machinery used to render
|
||||
subexpressions is private to the package.
|
||||
@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 ...)]{
|
||||
Produces a JavaScript string for the supplied forms. Each top-level form is
|
||||
rendered as a JavaScript statement. The macro is intended for small generated
|
||||
snippets, demos, and controlled code generation; it is not a complete Racket to
|
||||
JavaScript compiler.
|
||||
Generates a JavaScript program fragment as a string.
|
||||
|
||||
Examples:
|
||||
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.
|
||||
|
||||
@racketblock[
|
||||
(js (+ 1 2))
|
||||
(js (define answer 42))
|
||||
(js (define (square x)
|
||||
(return (* x x))))
|
||||
]
|
||||
@codeblock{
|
||||
(js
|
||||
(define (add1 x)
|
||||
(return (+ x 1))))
|
||||
}
|
||||
}
|
||||
|
||||
@section{Supported core forms}
|
||||
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.
|
||||
|
||||
The compact js-maker 3 implementation supports:
|
||||
@section{Supported forms}
|
||||
|
||||
The compact implementation supports:
|
||||
|
||||
@itemlist[
|
||||
@item{@racket[define] for values and functions.}
|
||||
@item{@racket[lambda] and @racket[lambda]-style generated JavaScript functions.}
|
||||
@item{@racket[if], @racket[begin], @racket[set!], and explicit @racket[return].}
|
||||
@item{Ordinary @racket[let] with parallel binding semantics.}
|
||||
@item{@racket[let*] with sequential binding semantics.}
|
||||
@item{Named @racket[let] in tail-recursive loop style.}
|
||||
@item{Common infix operators such as @racket[+], @racket[-], @racket[*],
|
||||
@racket[/], comparisons, @racket[and], @racket[or], and @racket[not].}
|
||||
@item{@racket[list], @racket[cons], @racket[send], @racket[js-dot], and
|
||||
@racket[new].}
|
||||
@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{Let semantics}
|
||||
@section{Indexed access}
|
||||
|
||||
Ordinary @racket[let] evaluates all right-hand sides before introducing the new
|
||||
bindings. js-maker preserves that behavior by emitting temporary JavaScript
|
||||
constants and then opening a nested block for the real @tt{let} bindings.
|
||||
This avoids JavaScript temporal-dead-zone shadowing when a bound name is also
|
||||
used by a right-hand side.
|
||||
@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.
|
||||
|
||||
@racketblock[
|
||||
(js (define (ordinary-let x)
|
||||
(let ([x 1] [y x])
|
||||
(return y))))
|
||||
]
|
||||
@codeblock{
|
||||
(js
|
||||
(define (arrayAt xs i)
|
||||
(return (js-ref xs i))))
|
||||
}
|
||||
|
||||
The generated JavaScript returns the original argument as the value of
|
||||
@tt{y}, matching Racket's ordinary @racket[let] semantics.
|
||||
@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 emitted as a @tt{while (true)} loop. A tail call to the
|
||||
loop name is translated to parallel update assignments followed by
|
||||
@tt{continue}. The intended style is statement-oriented and uses explicit
|
||||
@racket[return] for terminating branches.
|
||||
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 ...)].
|
||||
|
||||
@racketblock[
|
||||
(js (define (sum-to n)
|
||||
(let loop ([i 0] [acc 0])
|
||||
(if (> i n)
|
||||
(return acc)
|
||||
(loop (+ i 1) (+ acc i))))))
|
||||
]
|
||||
@codeblock{
|
||||
(js
|
||||
(define (sumTo n)
|
||||
(let loop ([i 0] [acc 0])
|
||||
(if (> i n)
|
||||
(return acc)
|
||||
(loop (+ i 1) (+ acc i))))))
|
||||
}
|
||||
|
||||
@section{Package layout}
|
||||
@section{Tests and demos}
|
||||
|
||||
The package includes small demos under @filepath{demo/} and a regression suite
|
||||
under @filepath{testing/}. The public API remains just @racket[js].
|
||||
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.
|
||||
|
||||
+57
-33
@@ -2,42 +2,66 @@
|
||||
|
||||
@(require (for-label racket/base js-maker))
|
||||
|
||||
@title{js-maker use cases}
|
||||
@title{js-maker Use Cases}
|
||||
@author+email["Hans Dijkema" "hans@dijkewijk.nl"]
|
||||
|
||||
@section{Generating a small function}
|
||||
@defmodule[js-maker/demo/js-usecases]
|
||||
|
||||
@racketblock[
|
||||
(js (define (square x)
|
||||
(return (* x x))))
|
||||
]
|
||||
The demos in @filepath{demo/js-usecases.rkt} use only the public
|
||||
@racket[js] macro and the compact js-maker 3 form set. They are intentionally
|
||||
small: their purpose is to show the supported surface language, not to recreate
|
||||
the larger runtime-helper branch.
|
||||
|
||||
This produces a JavaScript function declaration. Racket identifiers are mapped
|
||||
to JavaScript-friendly names by replacing unsupported characters with
|
||||
underscores.
|
||||
@section{Random number}
|
||||
|
||||
@section{Generating a loop}
|
||||
|
||||
@racketblock[
|
||||
(js (define (sum-to n)
|
||||
(let loop ([i 0] [acc 0])
|
||||
(if (> i n)
|
||||
(return acc)
|
||||
(loop (+ i 1) (+ acc i))))))
|
||||
]
|
||||
|
||||
The named @racket[let] form is useful for simple loops while keeping ordinary
|
||||
Racket binding semantics for the initial values and loop updates.
|
||||
|
||||
@section{Generating DOM-style calls}
|
||||
|
||||
@racketblock[
|
||||
@codeblock{
|
||||
(js
|
||||
(define title (send document getElementById "title"))
|
||||
(set! (js-dot title innerHTML) "Hello")
|
||||
(send title addEventListener "click"
|
||||
(lambda (evt) (return #t))))
|
||||
]
|
||||
(define (randomBetween1And5)
|
||||
(return (+ (send Math floor (* (send Math random) 5)) 1))))
|
||||
}
|
||||
|
||||
This is string generation only. The generated JavaScript must still be run in a
|
||||
JavaScript environment that provides the referenced objects, such as
|
||||
@tt{document}.
|
||||
@section{Unique values}
|
||||
|
||||
@codeblock{
|
||||
(js
|
||||
(define (uniqueValues xs)
|
||||
(return (send Array from (new Set xs)))))
|
||||
}
|
||||
|
||||
@section{Indexed access}
|
||||
|
||||
@codeblock{
|
||||
(js
|
||||
(define (arrayAt xs i)
|
||||
(return (js-ref xs i))))
|
||||
}
|
||||
|
||||
@section{Named let loop}
|
||||
|
||||
@codeblock{
|
||||
(js
|
||||
(define (sumTo n)
|
||||
(let loop ([i 0] [acc 0])
|
||||
(if (> i n)
|
||||
(return acc)
|
||||
(loop (+ i 1) (+ acc i))))))
|
||||
}
|
||||
|
||||
@section{Function value}
|
||||
|
||||
@codeblock{
|
||||
(js
|
||||
(define (makeAdder x)
|
||||
(return (lambda (y)
|
||||
(return (+ x y))))))
|
||||
}
|
||||
|
||||
@section{DOM setter}
|
||||
|
||||
@codeblock{
|
||||
(js
|
||||
(define (setHtml id html)
|
||||
(let ([el (send document getElementById id)])
|
||||
(set! (js-dot el innerHTML) html)
|
||||
(return (js-dot el innerHTML)))))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user