js-vm: Javascript virtual machine for Racket
js-vm provides tools to develop Racket programs that run in Javascript. It provides a Javascript runtime that interprets Racket bytecode, functions for building and testing packages of translated code, and libraries to use features of a web-browser’s environment.
This project is intimately related with Moby Scheme, as Moby Scheme uses js-vm as its underlying runtime.
1 Quick Start
#lang planet dyoo/js-vm:1:3 |
(printf "hello world\n") |
(check-expect (* 3 4 5) 60) |
(current-seconds) |
(image-url "http://racket-lang.org/logo.png") |
(check-expect (big-bang 0 |
(on-tick add1 1) |
(stop-when (lambda (x) (= x 10)))) |
10) |
"last line" |
This program is in a language that has been enriched with Javascript-specific functions. It can be partially evaluated in plain Racket, but evaluation will halt at the call to image-url because image-url constructs a image DOM element and needs to run in an Javascript context.
Once the program is saved, create a new file called "run.rkt" in the same working directory with the following contents:
#lang racket |
(require (planet dyoo/js-vm:1:3)) |
(run-in-browser "test.rkt") |
(read-line) |
When this program is executed, run-in-browser will take "test.rkt" and translate it to run on the browser; a temporary web-server will opened and your browser’s window will open with the running program.
Finally, you can create zip packages by using create-zip-package. For example, modify "run.rkt" to be:
#lang racket |
(require dyoo/js-vm:1:3) |
(create-zip-package "test.rkt" "test.zip") |
2 Examples
2.1 Falling ball
#lang planet dyoo/js-vm:1:3 |
; Simple falling ball example. A red ball falls down the screen |
; until hitting the bottom. |
(define-struct world (radius y)) |
; The dimensions of the screen: |
(define WIDTH 320) |
(define HEIGHT 480) |
; The radius of the red circle. |
(define RADIUS 15) |
; The world is the distance from the top of the screen. |
(define INITIAL-WORLD (make-world RADIUS 0)) |
; tick: world -> world |
; Moves the ball down. |
(define (tick w) |
(make-world RADIUS (+ (world-y w) 5))) |
; hits-floor?: world -> boolean |
; Returns true when the distance reaches the screen height. |
(define (hits-floor? w) |
(>= (world-y w) HEIGHT)) |
; We have some simple test cases. |
(check-expect (hits-floor? (make-world RADIUS 0)) false) |
(check-expect (hits-floor? (make-world RADIUS HEIGHT)) true) |
; render: world -> scene |
; Produces a scene with the circle at a height described by the world. |
(define (render w) |
(place-image (circle RADIUS "solid" "red") (/ WIDTH 2) (world-y w) |
(empty-scene WIDTH HEIGHT))) |
; Start up a big bang, 15 frames a second. |
(check-expect (big-bang INITIAL-WORLD |
(on-tick tick 1/15) |
(to-draw render) |
(stop-when hits-floor?)) |
(make-world 15 480)) |
3 Running js-vm
(require (planet dyoo/js-vm:1:3)) |
(run-in-browser input-file) → void |
input-file : path-string? |
At the moment, js-vm currently supports programs written in the (planet dyoo/js-vm:1:3/lang/wescheme) and (planet dyoo/js-vm:1:3/lang/base) languages; further development on js-vm will work toward supporting modules written in full Racket. require should work as long as the required modules, too, are in the supported languages.
| ||||||||||||||
input-file : path-string? | ||||||||||||||
output-zip-file : path-string? |
4 WeScheme
(require (planet dyoo/js-vm:1:3/lang/wescheme)) |
It provides the bindings from (planet dyoo/js-vm:1:3/lang/base), (planet dyoo/js-vm:1:3/lang/posn), (planet dyoo/js-vm:1:3/image/image), (planet dyoo/js-vm:1:3/jsworld/jsworld), and (planet dyoo/js-vm:1:3/check-expect/check-expect). It also adds open-image-url and js-big-bang as aliases for image-url and big-bang respectively.
5 Jsworld
jsworld provides a world programming library that allows simple animation and games, as well as reactive HTML graphical user interfaces.
(require (planet dyoo/js-vm:1:3/jsworld/jsworld)) |
(big-bang a-world handlers ...) → world |
a-world : world |
handlers : handler |
By default, the page that’s displayed contains a rendering of the world value. In the presence of an to-draw or to-draw-page handler, big-bang will show a customized view.
The majority of the handlers register different stimuli that can trigger changes to the world. One instance is on-tick, which registers a function to update the world on a clock tick.
When the big-bang computation terminates through a stop-when, the final world is returned as its value.
(to-draw hook) → handler |
hook : (world -> scene) |
#lang planet dyoo/js-vm:1:3 |
(define WIDTH 320) |
(define HEIGHT 480) |
(define RADIUS 15) |
(define INITIAL-WORLD 0) |
(define (tick w) |
(+ w 5)) |
(define (hits-floor? w) |
(>= w HEIGHT)) |
(check-expect (hits-floor? 0) false) |
(check-expect (hits-floor? HEIGHT) true) |
(define (render w) |
(place-image (circle RADIUS "solid" "red") (/ WIDTH 2) w |
(empty-scene WIDTH HEIGHT))) |
(big-bang INITIAL-WORLD |
(on-tick tick 1/15) |
(to-draw render) |
(stop-when hits-floor?)) |
(stop-when stop?) → handler? |
stop? : (world -> boolean) |
#lang planet dyoo/js-vm:1:3 |
(define (at-ten x) |
(>= x 10)) |
(big-bang 0 |
(on-tick add1 1) |
(stop-when at-ten)) |
(on-tick world-updater [delay]) → handler? |
world-updater : (world -> world) |
delay : number? = 1/20 |
(on-key world-updater) → handler? |
world-updater : (world key? -> world) |
(key=? key1 key2) → boolean? |
key1 : key? |
key2 : key? |
(to-draw-page to-dom to-css) → handler |
to-dom : (world -> (DOM-sexp)) |
to-css : (world -> (CSS-sexp)) |
5.1 Jsworld Types
A dom-sexp describes the structure of a web page:
dom-sexp | = | (list dom-element dom-sexp ...) |
a css-sexp describes the structure of a page’s styling:
css-sexp | = |
|
attrib | = | (list string string) |
Each of the dom-elements can take in an optional attribute list to assign to the new dom element; the common useful attribute is a key-value binding for an "id", which can be used to identify an element in the css-drawing function.
(define a-dom-sexp (list (js-div '(("id" "main-div"))) |
(list (js-text "Hello world")))) |
(define a-css-sexp (list (list "main-div" |
(list "background" "white") |
(list "font-size" "40px")))) |
5.2 HTML user interface constructors
Here are the dom-element constructors.
(js-div [attribs]) → dom-element? |
attribs : (listof attrib?) = '() |
(js-p [attribs]) → dom-element? |
attribs : (listof attrib?) = '() |
(js-button world-update-f [attribs]) → dom-element |
world-update-f : (world -> world) |
attribs : (listof attrib) = '() |
#lang planet dyoo/js-vm:1:3 |
(define (press w) |
(add1 w)) |
(define (draw w) |
(list (js-div) |
(list (js-button press) (list (js-text "Press me"))) |
(list (js-text (format "Button presses: ~a" w))))) |
(define (draw-css w) |
'()) |
(big-bang 0 |
(to-draw-page draw draw-css)) |
(js-text text) → dom-element |
text : string? |
(js-input type world-update-f [attribs]) → dom-element | ||||||||
type : string | ||||||||
| ||||||||
attribs : (listof attrib) = '() |
"text"
"password"
"checkbox"
#lang planet dyoo/js-vm:1:3 |
(define (refresh w form-val) |
form-val) |
(define input-node |
(js-input "text" refresh '(("id" "myname")))) |
(define (draw w) |
(list (js-div) |
(list (js-div) (list (js-text (format "I see: ~s~n" w)))) |
(list (js-div) (list input-node)))) |
(define (draw-css w) |
'()) |
(big-bang "" |
(to-draw-page draw draw-css)) |
#lang planet dyoo/js-vm:1:3 |
(define (make-ingredient-checkbox-sexp ingredient) |
(local [(define (on-check w v) |
(cond |
[v |
(cons ingredient w)] |
[else |
(remove ingredient w)]))] |
(list (js-div) |
(list (js-text ingredient)) |
(list (js-input "checkbox" |
on-check |
`(("value" ,ingredient))))))) |
(define c1 (make-ingredient-checkbox-sexp "mushrooms")) |
(define c2 (make-ingredient-checkbox-sexp "green peppers")) |
(define c3 (make-ingredient-checkbox-sexp "olives")) |
(define (draw w) |
(list (js-div) |
c1 |
c2 |
c3 |
(list (js-text (format "The world is: ~s" w))))) |
(define (draw-css w) |
'()) |
(big-bang '() |
(to-draw-page draw draw-css)) |
(js-img url [attribs]) → dom-element |
url : string |
attribs : (listof attrib) = '() |
(js-select options world-update-f [attribs]) → dom-element |
options : (listof string?) |
world-update-f : (world string -> world) |
attribs : (listof attrib) = '() |
#lang planet dyoo/js-vm:1:3 |
(define (select-house w an-option) |
an-option) |
(define a-select-element |
(js-select (list "" |
"Gryffindor" |
"Hufflepuff" |
"Ravenclaw" |
"Slytherin") |
select-house)) |
(define (draw w) |
(list (js-div) |
(list a-select-element) |
(list (js-text (format "House: ~a" w))))) |
(define (draw-css w) |
'()) |
(big-bang "" |
(to-draw-page draw draw-css)) |
6 Images
(require (planet dyoo/js-vm:1:3/image/image)) |
This module provides functions for creating images. The design of the library is meant to follow 2htdp/image.
(image? x) → boolean? |
x : any/c |
(image=? x y) → boolean? |
x : any/c |
y : any/c |
(circle radius style color) → image? |
radius : nonnegative-real? |
style : (one-of/c 'solid 'outline) |
color : color? |
(nw:rectangle width height style color) → image? |
width : number? |
height : number? |
style : (one-of/c 'solid 'outline) |
color : color? |
(rectangle width height style color) → image? |
width : number? |
height : number? |
style : (one-of/c 'solid 'outline) |
color : color? |
(ellipse) → image? |
(empty-scene width height) → image? |
width : number? |
height : number? |
(place-image x y an-image background) → image? |
x : number? |
y : number? |
an-image : image? |
background : image? |
(overlay/xy img1 x y img2) → image? |
img1 : image? |
x : real? |
y : real? |
img2 : image? |
(underlay/xy img1 x y img2) → image? |
img1 : image? |
x : real? |
y : real? |
img2 : image? |
(put-pinhole img x y) → image? |
img : image? |
x : real? |
y : real? |
(image-width an-image) → number? |
an-image : image? |
(image-height an-image) → number? |
an-image : image? |
Colors can be specified either by an RGB color structure, or by string name. Both are described now.
(make-color red green blue) → color |
red : number? |
green : number? |
blue : number? |
(color-red c) → number |
c : color? |
(color-green c) → number |
c : color? |
(color-blue c) → number |
c : color? |
6.1 Available colors
"orange"
"red"
"orangered"
"tomato"
"darkred"
"red"
"firebrick"
"crimson"
"deeppink"
"maroon"
"indian red"
"indianred"
"medium violet red"
"mediumvioletred"
"violet red"
"violetred"
"lightcoral"
"hotpink"
"palevioletred"
"lightpink"
"rosybrown"
"pink"
"orchid"
"lavenderblush"
"snow"
"chocolate"
"saddlebrown"
"brown"
"darkorange"
"coral"
"sienna"
"orange"
"salmon"
"peru"
"darkgoldenrod"
"goldenrod"
"sandybrown"
"lightsalmon"
"darksalmon"
"gold"
"yellow"
"olive"
"burlywood"
"tan"
"navajowhite"
"peachpuff"
"khaki"
"darkkhaki"
"moccasin"
"wheat"
"bisque"
"palegoldenrod"
"blanchedalmond"
"medium goldenrod"
"mediumgoldenrod"
"papayawhip"
"mistyrose"
"lemonchiffon"
"antiquewhite"
"cornsilk"
"lightgoldenrodyellow"
"oldlace"
"linen"
"lightyellow"
"seashell"
"beige"
"floralwhite"
"ivory"
"green"
"lawngreen"
"chartreuse"
"green yellow"
"greenyellow"
"yellow green"
"yellowgreen"
"medium forest green"
"olivedrab"
"mediumforestgreen"
"dark olive green"
"darkolivegreen"
"darkseagreen"
"lime"
"dark green"
"darkgreen"
"lime green"
"limegreen"
"forest green"
"forestgreen"
"spring green"
"springgreen"
"medium spring green"
"mediumspringgreen"
"sea green"
"seagreen"
"medium sea green"
"mediumseagreen"
"aquamarine"
"lightgreen"
"pale green"
"palegreen"
"medium aquamarine"
"mediumaquamarine"
"turquoise"
"lightseagreen"
"medium turquoise"
"mediumturquoise"
"honeydew"
"mintcream"
"royalblue"
"dodgerblue"
"deepskyblue"
"cornflowerblue"
"steel blue"
"steelblue"
"lightskyblue"
"dark turquoise"
"darkturquoise"
"cyan"
"aqua"
"darkcyan"
"teal"
"sky blue"
"skyblue"
"cadet blue"
"cadetblue"
"dark slate gray"
"darkslategray"
"lightslategray"
"slategray"
"light steel blue"
"lightsteelblue"
"light blue"
"lightblue"
"powderblue"
"paleturquoise"
"lightcyan"
"aliceblue"
"azure"
"medium blue"
"mediumblue"
"darkblue"
"midnight blue"
"midnightblue"
"navy"
"blue"
"indigo"
"blue violet"
"blueviolet"
"medium slate blue"
"mediumslateblue"
"slate blue"
"slateblue"
"purple"
"dark slate blue"
"darkslateblue"
"darkviolet"
"dark orchid"
"darkorchid"
"mediumpurple"
"cornflower blue"
"medium orchid"
"mediumorchid"
"magenta"
"fuchsia"
"darkmagenta"
"violet"
"plum"
"lavender"
"thistle"
"ghostwhite"
"white"
"whitesmoke"
"gainsboro"
"light gray"
"lightgray"
"silver"
"gray"
"dark gray"
"darkgray"
"dim gray"
"dimgray"
"black"
7 Implementing Javascript Modules
Warning: the material in this section is unstable and likely to change.
7.1 Module Implementation in Javascript
(require (planet dyoo/js-vm:1:3/lang/js-impl/js-impl)) |
// double.js |
EXPORTS['double'] = |
new types.PrimProc('double', 1, false, false, function(x) { |
return jsnums.multiply(x, 2)}); |
Any module implemented with (planet dyoo/js-vm:1:3/lang/js-impl/js-impl) will provide bindings that require a Javascript context.
7.2 Conditional Module Implmentation in javascript
(require (planet dyoo/js-vm:1:3/lang/js-impl/js-conditional)) |
Any module implemented with (planet dyoo/js-vm:1:3/lang/js-conditional/js-conditional) can run either in a Racket or Javascript context.
8 Foreign Function Interface
(require (planet dyoo/js-vm:1:3/ffi/ffi)) |
9 Base language
(require (planet dyoo/js-vm:1:3/lang/base)) |
define
define-struct
if
cond
else
case
quote
unquote
unquote-splicing
lambda
case-lambda
let
let*
letrec
letrec-values
local
quasiquote
begin
begin0
set!
and
or
when
unless
require
for-syntax
define-for-syntax
begin-for-syntax
prefix-in
only-in
provide
planet
all-defined-out
all-from-out
except-out
rename-out
define-syntax
let/cc
with-continuation-mark
true
false
pi
e
empty
eof
null
shared
with-handlers
write
display
newline
current-print
current-continuation-marks
continuation-mark-set->list
for-each
make-thread-cell
make-struct-type
make-struct-field-accessor
make-struct-field-mutator
struct-type?
struct-constructor-procedure?
struct-predicate-procedure?
struct-accessor-procedure?
struct-mutator-procedure?
procedure-arity
procedure-arity-includes?
make-arity-at-least
arity-at-least?
arity-at-least-value
apply
values
call-with-values
compose
current-inexact-milliseconds
current-seconds
not
void
random
sleep
identity
raise
error
make-exn
make-exn:fail
make-exn:fail:contract
make-exn:fail:contract:arity
make-exn:fail:contract:variable
make-exn:fail:contract:divide-by-zero
exn-message
exn-continuation-marks
exn?
exn:fail?
exn:fail:contract?
exn:fail:contract:arity?
exn:fail:contract:variable?
exn:fail:contract:divide-by-zero?
*
-
+
=
=~
/
sub1
add1
<
>
<=
>=
abs
quotient
remainder
modulo
max
min
gcd
lcm
floor
ceiling
round
numerator
denominator
expt
exp
log
sin
cos
tan
asin
acos
atan
sinh
cosh
sqr
sqrt
integer-sqrt
make-rectangular
make-polar
real-part
imag-part
angle
magnitude
conjugate
sgn
inexact->exact
exact->inexact
number->string
string->number
procedure?
pair?
cons?
empty?
null?
undefined?
immutable?
void?
symbol?
string?
char?
boolean?
vector?
struct?
eof-object?
bytes?
byte?
number?
complex?
real?
rational?
integer?
exact?
inexact?
odd?
even?
zero?
positive?
negative?
box?
hash?
eq?
eqv?
equal?
equal~?
false?
boolean=?
symbol=?
cons
car
cdr
caar
cadr
cdar
cddr
caaar
caadr
cadar
cdaar
cdadr
cddar
caddr
cdddr
cadddr
rest
first
second
third
fourth
fifth
sixth
seventh
eighth
length
list?
list
list*
list-ref
list-tail
append
reverse
map
andmap
ormap
memq
memv
member
memf
assq
assv
assoc
remove
filter
foldl
foldr
quicksort
sort
argmax
argmin
build-list
box
box-immutable
unbox
set-box!
make-hash
make-hasheq
hash-set!
hash-ref
hash-remove!
hash-map
hash-for-each
make-string
replicate
string
string-length
string-ref
string=?
string-ci=?
string<?
string>?
string<=?
string>=?
string-ci<?
string-ci>?
string-ci<=?
string-ci>=?
substring
string-append
string->list
list->string
string-copy
string->symbol
symbol->string
format
printf
string->int
int->string
explode
implode
string-alphabetic?
string-ith
string-lower-case?
string-numeric?
string-upper-case?
string-whitespace?
build-string
string->immutable-string
string-set!
string-fill!
make-bytes
bytes
bytes->immutable-bytes
bytes-length
bytes-ref
bytes-set!
subbytes
bytes-copy
bytes-fill!
bytes-append
bytes->list
list->bytes
bytes=?
bytes<?
bytes>?
make-vector
vector
vector-length
vector-ref
vector-set!
vector->list
list->vector
build-vector
char=?
char<?
char>?
char<=?
char>=?
char-ci=?
char-ci<?
char-ci>?
char-ci<=?
char-ci>=?
char-alphabetic?
char-numeric?
char-whitespace?
char-upper-case?
char-lower-case?
char->integer
integer->char
char-upcase
char-downcase
call-with-current-continuation
call/cc
call-with-continuation-prompt
abort-current-continuation
default-continuation-prompt-tag
make-continuation-prompt-tag
make-reader-graph
make-placeholder
placeholder-set!