scheme2js

Florian Loitsch

17 July 2011

Version 20110717

scheme2js - compiles Scheme programs to JavaScript

Table of Contents

Synopsis

scheme2js [-h|--help] [--version] [-v|--verbose] [--js-dot-notation] [--bigloo-modules] [--compress] [--infotron] [--encapsulate-modules] [--export-globals] [--allow-unresolved] [--js-this] [--js-return] [--statics-suffix suffix] [--constant-runtime] [--indent width] [-O level] [--tailrec] [--while] [--inlining] [--max-inline-size size] [--rec-inline-nb nb] [--var-elimination] [--propagation] [--constant-propagation] [--bigloo-runtime-eval] [--correct-modulo] [--optimize-calls] [--optimize-boolify] [--optimize-consts] [--trampoline] [--max-tail-depth depth] [--suspend-resume] [--call/cc] [-g] [-d stage] [-I dir] -o outfile infile

Description

scheme2js takes infile and writes to outfile. If in/outfile is '-' stdin/out is used. The input must be a Scheme file. scheme2js translates the program into JavaScript.

Options

All boolean options can be prefixed with 'no-', to deactivate them. For example '--no-js-dot-notation' would disable JavaScript's dot-notation.
Options are evaluated in order. If conflicting options are given, the last entry wins. This allows to use -O and then to adjust the values subsequently.

IO

-o outfile
Write output into outfile. If outfile equals "-" then the result will be printed to stdout.
-I dir
Add dir to Include-path. This path will be searched for imported modules.
infile
The Scheme Input-file.
--compress
Compress the generated output. Nearly all unnecessary spaces are removed.

Information

-h|--help
Prints a help-message.
--version
Prints the version of scheme2js.
-v|--verbose
Activate verbose output.

JavaScript Interface

--js-dot-notation
Allow JavaScript dot-notation in source-file. This allows access to object-properties like o.x.
--infotron
Activates support for Infotrons. See Section Infotron for more details.
--bigloo-modules
Uses Bigloo style module declarations (see the Modules section below).
--encapsulate-modules
Wrap the module (the compiled file) into an anonymous function. Global variables that are not exported (for instance top-level 'let'-variables) thus do not pollute the JavaScript top-level.
--export-globals
Export all global variables. Variables introduced by global 'let's are not affected by this flag. By default all global variables of files without 'module'-clause are exported. Files with 'module'-clause do only export variables declared in the module-clause.
--allow-unresolved
Variables that are unresolved are supposed to be JavaScript variables or exported variables from other modules. By default files without 'module'-clause allow unresolved. Files with a 'module'-clause yield error-messages on unknown variables.
--js-this
Allow the access of JavaScript's 'this' inside Scheme procedures.
--js-return
Introduces a special form: 'return', which has the same semantics as JavaScript's 'return'.
--constant-runtime
Assume runtime is constant. Disallows assignments to runtime-functions. When enabled the interface files runtime_interface.js and runtime_interface_callcc.js are not needed anymore.
--statics-suffix suffix
Sets the suffix for static variables. Static variables are global variables that are not exported. This avoids name-clashes with non-exported variables of different modules. If a module is encapsulated, then this flag has no effect. By default '_' followed by the file-name without extension is used (which is the same as the module's name).
--indent width
Sets the indentation width for the produced code.

Optimizations

--tailrec
Transform (obvious) tail-recursive loops into 'while'-loops.
--while
Searches for common loop-pattern. Improves the generated 'while'-loops.
--inlining
Inlines (small) functions, and functions, that are only used once (-> no code size increase).
--max-inline-size size
Only inline functions smaller than size. The calculated size is a rough estimate of the final code-size. Small functions have a size of about 30.
--rec-inline-nb nb
Inline at most nb nested functions. That is, if a function has been inlined, continue inlining inside the inlined function's body, but only nb times. A value of 1 forbits inlining inside the body of an inlined function. Functions that are only called at one location are exempted from this limitation.
--var-elimination
Reduce the number of variables, by substituting variables. '(let ((x expr)) (let ((y x)) ....))' becomes '(let ((y expr)) ...)'.
--propagation
Enables constant-propagation. Whenever possible var-references are propagated too: if variable 'x' has the same value as 'y' the references to 'x' are replaced by a reference to 'y'.
--constant-propagation
Propagates constant variables. Suppose 'x' is initialized with a constant value 'c'. Then all references to 'x' are replaced by 'c'
--bigloo-runtime-eval
Use a Bigloo 'eval' during compilation when a constant expressions (for instance '(+ 2 5)) is encountered. Only a limited 'safe' subset of runtime-functions are evaluated this way. However the result might not be the same as if evaluated at runtime. For example (/ 5 2) would yield 2 when evaluated by Bigloo, but yields 2.5 when evaluated at runtime.
--correct-modulo
The semantics of Scheme's and JavaScript's modulo differ. With this flag the more expensive R5RS modulo is simulated in JavaScript. Only of relevance when --optimize-calls is activated.
--optimize-calls
Peephole optimization of small runtime-functions. Runtime functions like '+', 'null?', etc. are directly inlined with their JavaScript equivalent and do not invoke any function-call.
--optimize-boolify
During boolification do not test against 'false' if the expression is known to be of type bool.
--optimize-consts
Store explicit constants (like lists and vectors) in global variables, so they are not recreated.
-O level
Sets the optimization level (default is -O 1).
Each optimization level enables/disables several flags at once:
-O0
--no-tailrec --no-inlining --no-inline-runtime --no-constant-runtime --no-propagation --no-constant-propagation --no-while --correct-modulo --no-optimize-calls --no-optimize-boolify --no-optimize-set! --max-tail-depth 40 --no-var-elimination --no-optimize-consts --no-bigloo-runtime-eval
-O1
--tailrec --inlining --max-rec-inline 3 --max-inline-size 30 --inline-runtime --constant-runtime --propagation --constant-propagation --while --no-correct-modulo --optimize-calls --optimize-boolify --optimize-set! --max-tail-depth 40 --var-elimination --optimize-consts --no-bigloo-runtime-eval
-O2
--tailrec --inlining --max-rec-inline 1 --max-inline-size 15 --no-inline-runtime --constant-runtime --propagation --constant-propagation --while --no-correct-modulo --optimize-calls --optimize-boolify --optimize-set! --max-tail-depth 40 --var-elimination --optimize-consts --bigloo-runtime-eval
-O3
--tailrec --inlining --max-rec-inline 4 --max-inline-size 45 --inline-runtime --constant-runtime --propagation --constant-propagation --while --no-correct-modulo --optimize-calls --optimize-boolify --optimize-set! --max-tail-depth 40 --var-elimination --optimize-consts
-Obench
--tailrec --inlining --max-rec-inline 4 --max-inline-size 45 --inline-runtime --constant-runtime --propagation --constant-propagation --while --no-correct-modulo --optimize-calls --optimize-boolify --optimize-set! --max-tail-depth 40 --var-elimination --optimize-consts --bigloo-runtime-eval

Trampolines and Call/cc

--trampoline
Enables trampolines. The given implementation does not provide naive trampolines, but a more efficient version that only returns trampolines after a constant number of tail-calls.
--max-tail-depth depth
Sets the maximum depth of consecutive tail-calls before a trampoline is returned. This option is only relevant when trampolines are enabled.
--suspend-resume
Enables 'suspend/resume', a weaker (but faster) version of 'call/cc'. A call to 'suspend' captures the current continuation. 'Suspend' does not return however, and the only way to continue the execution is to invoke the captured continuation. The captured continuation can only be invoked once. This form is useful, when the program needs to pause, and wait for an event.
--call/cc
Enables 'call/cc'.
--extern-invokes-call/cc
Assume imported variables (or unresolved variables) call 'call/cc' (even if they do not have a 'call/cc' entry in their 'export'-clause).

Debug

-g
Adds debugging information.
-d stage
Depending on 'stage' either print the expanded source, or a Scheme-version of the AST at the chosen compilation stage (into outfile). If you really need this, have a look at the source for valid 'stage's.

Files

share/runtime.js
The runtime stripped of 'call/cc'-related procedures.
share/runtime_interface.js
Compiled programs access the runtime through the variables declared in this file. Only needed, when '--constant-runtime' is not used.
share/runtime_callcc.js
The 'call/cc' part of the runtime. If a program is compiled without 'call/cc' or 'suspend/resume' support, then this file is not needed.
share/runtime_interface_callcc.js
Compiled programs access the 'call/cc' runtime through variables declared in this file. Only needed, when the program has been compiled with 'call/cc' or 'suspend/resume' support and '--constant-runtime' is not activated.

Modules

Bigger programs can be split into modules. In this case the first expression of the input-file must be a module-clause. Currently two module-clauses are supported. An old deprecated one, and a new one that has been modeled after Bigloo.
For the new one see Bigloo. Contrary to Bigloo the file-name and the module-name must be the same (thus avoiding the need for an '.afile'). This limitation might be changed in future versions. In addition to the Bigloo clauses a 'JS' and 'scheme2js-pragma' clause is supported. The 'JS' clause serves to import JavaScript variables. The 'scheme2js-pragma' to add additional optimization information for exported variables. It is an A-List with the variable-name used as key. Here an example with all recognized optimization-clauses:
Explanatory example:
(module my-module          ;; filename must be my-module.scm/sch
  (import some-other-module
          and-a-second-module)
  (include "some-file"
           "another-file")
  (export (macro macro1)  ;; a (define-macro (macro1 ..) must be in the source
          (macro macro2)) ;; same here
  (export (my-fun::bool arg ...) ;; a function returning a bool.
          my-var)                ;; export simply a variable
  (scheme2js-pragma       ;; optimization-information for exported variables.
    (my-fun (JS "myFun")          ;; use "myFun" as JS id for this variable.
            (call/cc? #t)           ;; this function may invoke call/cc,
            (call/cc-params (0 2)))) ;;  but only if arg 0 or 2 invoke call/cc.
  (JS "some_JS_var"))       ;; import 'some_JS_var' as 'some_JS_var'
  (JS scheme-var)           ;; import mangled form of 'scheme-var' as 'scheme-var'
  (JS (scm-var "jsVar")     ;; import 'jsVar' as 'scm-var'
      (scm-var2 jsVar2)))   ;; imort  'jsVar2' as 'scm-var2' (no mangling)
Here is an example for the old deprecated module-form:
(module my-module          ;; filename must be my-module.scm/sch
  (import some-other-module
          and-a-second-module)
  (include "some-file"
           "another-file")
  (export-macros           ;; export (and use in module) macros.
          (define-macro (macro-name ...) (...))) ;; same syntax as if in module-body.
  (export (my-fun?
            (JS js-id)        ;; the JS-id
            (type bool)    ;; declare (return-)type of variable/function.
            (constant? #t) ;; must not be changed from the outside
            (call/cc? #t)  ;; may invoke call/cc
            (call/cc-params (0 2))) ;; but only if arg 0 or 2 invoke call/cc.
          my-var           ;; just export the variable without any additional information.
          my-fun2)         ;; works for functions too.
  (JS                      ;; import from JavaScript (same syntax as 'export')
      (time-out-set! (JS setTimeout)) ;; import setTimeout as time-out-set!
      window))                        ;; import window as window
The module-name must be equal to the filename (minus path and extension).
When exporting variables, only the variable-name is needed. The 'JS' entry allows to export functions to JavaScript under a different name, and the remaining entries help scheme2js to optimize the program.
When module 'foo' is imported, then a file foo.scm or foo.sch is searched in the include-directories (given with -I ).
Modules without any top-level (but with a module-clause) can be used to declare JavaScript functions and make them accessible to Scheme modules.

Infotrons

Infotrons are modules for JDA (http://foundry.maya.com/jda/). When activated with '--infotron' scheme2js recognizes modules starting with an 'infotron' clause as infotrons and compiles them accordingly. Main changes to plain modules are:
- infotrons start with 'infotron' instead of 'module',
- they must not export any variables or macros
- they can declare 'uuid'-clauses (or the more convenient 'uuid-seed' string which is then used to construct a uuid),
- they can declare 'properties' that are accepted during the initial configuration.
- they can define the name for the initial configuration in the 'config-name' clause. By default 'config' is used. and
- they can declare the inputs ('iterms') and outputs ('oterms')
- top-level must have defines at top.
Example:
(infotron jsAlert
	  (uuid-seed "jsAlert - Florian Loitsch - Inria")
	  (iterms (trigger_in on_trigger 10))
	  (oterms close_event_out)
	  (properties message))

(define (on_trigger msg)
   (alert config.message)
   (close_event_out msg))

See Also

Hop (http://hop.inria.fr)
JDA (http://foundry.maya.com/jda/)
R5RS (http://www.schemers.org/Documents/Standards/R5RS/)

Bugs

Due to limitations in JavaScript, there are no integers (exact numbers).
At the moment scheme2js does not support hygienic macros.
The 'eval' function is still missing, too.

Author

Florian Loitsch
Email: florian.loitsch@sophia.inria.fr

Version

Version: 20110717