You are looking at historical revision 22868 of this page. It may differ significantly from its current revision.

spock

Introduction

SPOCK is a compiler and runtime system supporting most of R5RS Scheme. You can use it either by statically generating JavaScript files from Scheme source files or by translating s-expressions containing Scheme code on the fly. The compiler uses Henry Baker's Cheney-on-the-MTA compilation strategy.

This extension is still in a very early state and is likely to contain numerous bugs.

Programming interface

State management

Compilation of Scheme to JavaScript is controlled by a state object that specifies options relevant to the translation process together with a syntactic environment, i.e. the macro-definitions available to the code to be compiled. Depending on the use of the compiler, it must be possible to explicitly create a new state or re-use an existing one. For example, in a web-server, one usually needs a state local to the current request - syntactic definitions and compilation options should not necessarily be shared between multiple requests.

Compilation options

Compilation options are given as a list of symbols, where some symbols must be followed by an argument:

[DIR]
Option Argument Meaning
source show source forms
expand show forms after macro-expansion
canonicalized show forms after canonicalization
optimized show forms after optimization
cps show forms after CPS-conversion
strict enable strict mode
optimize enable optimizations
block enable block-compilation
library-path add DIR to library path or return library path
namespace VAR put globals into module
xref show cross-reference
runtime include runtime-system in generated code
library compile runtime library
seal wrap toplevel definitions into local scope
debug enable debug-mode
usage PROC invoke PROC on usage-errors
fail PROC invoke PROC on compiler-errors
import FILENAME expand FILENAME
environment STORE provide syntactic environment
debug-syntax show debug-output during expansion
verbose show diagnostic messages
prepare just prepare state without compiling
code EXP code to be compiled instead of file
output-file FILENAME specify output-file

Note that the argument to the library-path option may be omitted, which means it must be the last element in the option list.

make-spock-state
[procedure] (make-spock-state OPTION ...)

Creates a fresh compilation state, with settings given in OPTION .. and returns this state.

spock-state?
[procedure] (spock-state? X)

Returns #t if X is a compilaton state or #f otherwise.

current-spock-state
[parameter] current-spock-state

The currently active compilation state. Defaults to #f, which means a new state will be created automatically for the next compilation (via <script>) and will be re-used on subsequent compilations.

spock
[procedure] (spock FILENAME-OR-OPTION ...)

The main interface to the compiler. Any string-argument given will be taken as the name of a Scheme source file to be compiled. If the code option is given and followed by an expression, then that expression is compiled instead of reading source from files. The other options are the same as those that can be passed to make-spock-state.

spock-initialize
[procedure] (spock-initialize OPTIONS ...)

Creates a compilation state using the given options and sets the value of the parameter current-spock-state. Initialization of a compilation state needs a bit of internal setup, so it is advisable to call this procedure in time-critical applications that need to generate code quickly.

<script>
[syntax] (<script> FORM)

Compiles the Scheme (unquoted) expression FORM using the current compilation state and returns the generated JavaScript code as a string. FORM may contain sub-expressions of the form (<unscript> and <unsript-splicing>, respectively, which work similar to unquote and unquote-splicing in R5RS Scheme, but switch between code to be compiled and code or literal data to be computed in the host environment, e.g.

 (<script>
   (begin
     (define (square x) (* x x))
     (print (square '(<unscript> (read))))))

will return a <script> HTML element containing JavaScript code that defines a function and calls it with the argument read at execution of the <script> expression.

Note that the compiled form needs runtime support code to execute successfully. See <script-header> below.

<script-header>
[procedure] (<script-header> #!key minified debug path)

Emits HTML to load the runtime system. Keyword-arguments are available to specify whether a "minified" version of the runtime library should be loaded, and whether a debug version should be used. Minified means a compressed library that can be loaded faster by the client. The debug version performs additional error checks and keeps a trace of recently invoked functions.

Note that the JavaScript files containing the runtime library need to be available to be transmitted to the client. In the usual case of web-server code generating script content this means, one of the files

 spock-runtime.js
 spock-runtime-min.js
 spock-runtime-debug.js
 spock-runtime-debug-min.js

should be in a location that holds files to be retrieved by the client and it should match the keyword arguments given to <script-header>. The path keyword argument can be used to override the path prefix and defaults to an empty path.

The runtime library is installed in spock subdirectory of the extension repository and can be located in the path returned by entering

 chicken-spock -library-path

Special read syntax

To make embedding of Scheme code fragments easier, read syntax is available that simplifies the use of the <script>:

 #`EXPRESSION   --->   (<script> EXPRESSION)
 #^EXPRESSION   --->   (<unscript> EXPRESSION)
 #^@EXPRESSION  --->   (<unscript-splicing> EXPRESSION)

In compiled code, you must explicitly load the spock extension to make the read-syntax available by passing -X spock to the compiler and the spock module must be imported.

Using the static compiler

The compiler takes one or more Scheme files are translates them into a single JavaScript file, writing it by default to stdout. You can use the -o option to specify a file into which the code should be written instead. The compiler understands most of the options that can be given to make-spock-state, enter

 chicken-spock -help

for a list of command-line switches.

Supported language

Deviations from R5RS

4.1.1
Access to unbound variables will trigger whatever error-handling the underlying JavScript implementation provides.
6.1
eq? will return #f when given standard procedures, since these are mostly implemented by using so called "identifier syntax" - in other words
 (eq? car car)      ===>  #f
6.2.2
there is no concept of exactness in JavaScript - all numbers are internally represented as floating-point numbers and are thus inexact; "bignums", exact rational numbers and complex numbers are not supported.
6.2.5
the following standard procedures are not implemented: denominator, numerator, image-part, real-part, rationalize, make-polar and make-rectangular. inexact->exact merely rounds its argument, exact->inexact returns its argument unchanged.
6.3.5
non-integral string indices are not checked and return undefined result.
6.3.6
non-integral vector indices are not checked and return undefined result.
6.4
continuations captured over the program toplevel can not be re-entered, so
 (define k (call-with-current-continuation (lambda (k) k)))
 (display "once\n")
 (k #f)

will not print once a second time and will simply return at the next toplevel expression.

6.5
eval is not supported.
6.6.4
load can only load JavaScript code and always does so asynchronously; transcript-on and transcript-off are not supported.

Extensions to R5RS

4.1.1
SPOCK provides a special notation to simplify access to object properties, similar to the dot notation supported by several Java- and JavaScript-based Scheme implementations:
 .NAME            ==>   (%property-ref "NAME")
 .NAME1.NAME2     ==>   (%property-ref "NAME1.NAME2")
 NAME1.NAME2      ==>   (%host-ref "NAME1.NAME2")
 NAME.            ==>   (%host-ref "NAME")
 (set! NAME1.NAME2 X)   ==>   (%host-set! "NAME1.NAME2" X)
 (set! NAME1. X)        ==>   (%host-set! "NAME1" X)
 (set! (.NAME X) Y)     ==>   (%property-set! "NAME" X Y)
 (set! (.NAME1.NAME2 X) Y)     ==>   (%property-set! "NAME1.NAME2" X Y)
6.3.5
the second argument to substring is optional and defaults to the length of the argument string; string-fill! accepts an optional third and fourth argument specifying the start end end index of the region whould should be filled.
6.3.6
vector-fill! accepts an optional third and fourth argument specifying the start end end index of the region whould should be filled.
6.4
for-each and map accept a vector as their second argument, but only when called with two arguments; apply accepts a vector as last argument.
6.6.4
load takes an optional second argument, which should be a procedure of one argument; this procedure will be called with the filename argument when the file could be successfully loaded; the filename is actually an URL.

Non-standard syntax

 (begin1 X1 X2 ...)
 (bind STRING ...)
 (fluid-let ((ID1 X1) ...) BODY ...)
 (when X1 BODY ...)
 (unless X1 BODY ...)
 (cut ...)
 (cond-expand CLAUSE ...)
 (define-syntax-rule (NAME ARG ...) BODY)
 (new CLASS ARG ...)
 (define-library-section NAME CLAUSE ...)        used internally
 (define-native NAME ...)
 (define-native-method NAME ...)
 (define-entry-point NAME EXP)
 (define-entry-point (NAME . LLIST) BODY ...)

Non-standard library procedures

 (% SYMBOL VALUE ...)
 (bind-method JSFUNC OBJECT)
 (callback PROC)
 (callback-method PROC)
 (compl PROC)
 (const X)
 (current-error-port)
 (exit CODE)
 (file-exists? STRING)
 (id X)
 (jstring X)
 (milliseconds [THUNK])
 (native JSFUNC)
 (native-method JSFUNC)
 (o PROC ...)
 (print X ...)
 (resume STATE)
 (suspend PROC)
 (void ...)
 (void? X)
 (with-input-from-port PORT THUNK)
 (with-output-to-port PORT THUNK)
Internal non-standard special forms
 (%check (CLASS) X)
 (%check TYPE X)
 (%check X)
 (%code STRING-OR-SYMBOL ...)
 (%continue X ...)
 (%dispatch LAMDBA ... X)
 (%host-ref NAME ...)
 (%host-set! NAME X)
 (%inline (NUM-OR-OP ...) X ...)
 (%inline .NAME X ...)
 (%inline NAME X ...)
 (%loop LLIST X)
 (%native-lambda STRING ...)
 (%new CLASS X ...)
 (%property-ref NAME [X])
 (%property-set! NAME X Y)
 (%syntax-error MSG [X])
 (%void)

How Scheme types map to JavaScript types

Scheme JavaScript
number number
character [Object SPOCK.Char]
eof-object [Object SPOCK.EndOfFile] == SPOCK.EOF
string string or [Object SPOCK.String]
symbol [Object SPOCK.Symbol]
port [Object SPOCK.Port]
boolean boolean
pair [Object SPOCK.Pair]
vector Array
null null
void undefined

Scheme code can handle native JavaScript strings, which are immmutable. String literals will be directly represented as native JavaScript strings. Mutable strings (as returned by make-string, for example) will be instances of SPOCK.String and must be manually converted to JavaScript strings when passed to JavaScript code.

Notes

Examples

Using dynamic code generation

(use spock)

(display (<script-header> debug: #t))

(display #`(print #^(+ 3 4)))

Using the static compiler

<!-- drag.html - drag a box around the screen -->

<html>
  <head>
    <script src="spock-runtime-debug.js"></script>
    <script src="drag.js"></script>
    <style type="text/css">
      body {
        font-family: Arial, Helvetica;
        font-size: x-large;
      }

      #info {
        position: absolute;
        right-margin: auto;
        left: 0px;
        top-margin; auto;
        bottom: 0px;
      }

      #box {
        width: 200px;
        height: 200px;
        background-color: red;
        position: absolute;
        left: 50%;
        top: 50%;
      }
    </style>
  </head>
  <body>
    <div id="info">xxx</div>
    <div id="box">Push me.</div>
  </body>
</html>
;;;; drag.scm

(define-native-method document.getElementById)

(define (box) (document.getElementById document. "box"))
(define (info) (document.getElementById document. "info"))

(define (mouse-position event)
  (values 
   (- (+ (.clientX event) document.body.scrollLeft) document.body.clientLeft)
   (- (+ (.clientY event) document.body.scrollTop) document.body.clientTop)))

(define (mouse-move event)
  (call-with-values (cut mouse-position event)
    (lambda (x y)
      (move-element (box) x y)
      (show-position x y))))

(define (move-element elt x y)
  (set! (.style.left elt) x)
  (set! (.style.top elt) y))

(define (move-element-by elt x y)
  (call-with-values (cut element-position elt)
    (lambda (x1 y1)
      (move-element elt (+ x1 x) (+ y1 y)))))

(define (element-position elt)
  (values 
   (.offsetLeft elt)
   (.offsetTop elt)))

(define (show-position x y)
  (set! (.innerHTML (info))
    (jstring
     (string-append
      (number->string x) "/" (number->string y)))))

(set! document.onmousemove (callback mouse-move))

To compile this, enter:

 chicken-spock drag.scm -o drag.js

Now copy spock-runtime.js from the repository into the same directory holding drag.js and drag.html and open drag.html in your browser.

Authors

felix winkelmann

License

Copyright (c) 2011, Felix L. Winkelmann
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
conditions are met:
  Redistributions of source code must retain the above copyright notice, this list of conditions and the following
    disclaimer. 
  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
    disclaimer in the documentation and/or other materials provided with the distribution. 
  Neither the name of the author nor the names of its contributors may be used to endorse or promote
    products derived from this software without specific prior written permission. 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

The pattern matching library used by SPOCK is derived from code is written by Alex Shinn and placed in the Public Domain.

The pretty printer used internally is Copyright (c) 1991, Marc Feeley (feeley@iro.umontreal.ca), Distribution restrictions: none

The syntax-rules expander used is alexpander, Copyright 2002-2004 Al Petrofsky <alexpander@petrofsky.org>

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in
  the documentation and/or other materials provided with the
  distribution.
Neither the name of the author nor the names of its contributors
  may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Version History

0.1
initial release