gxjs-loader
v0.0.4
Published
(G)erbil E(x)tended (J)avaScript (S)cheme Loader
Downloads
1
Readme
GxJS Loader: Load Scheme files into JavaScript
GxJS Loader is used to take either source code written in Gerbil Gambit or Gambit Scheme scheme and transpile them to JavaScript.
Installation
Easy! I choose yarn
, but npm
also works.
yarn add gxjs-loader --dev
Usage
Gerbil
To start with here’s a Gerbil file (.ss
) that mixes JavaScript code with
Gerbil code.
(import :js)
(js#declaration "console.log('started GxJS loader!')")
(def property-name "This is valid")
(def GxJS-jso
{
thisIsTheAnswer: 42
'this "is how we make the moonshine"
property-name 'symbols-have-no-js-value!
})
(def (this-is-gxjs! (val 42))
(js#statement "console.log('This is GxJS!', (@1@), (@2@), (@3@))"
val
(js#foreign->js GxJS-jso)
(js#scm->js (string-append "string " "append"))))
(js#statement "console.log('finished GxJS loader');
module.exports = RTS.scm2host(@1@);" this-is-gxjs!)
Now a JavaScript file that requires what we export, calls what we export, and exports what we export.
const GxJS = require('gxjs-loader!./gxjs-loader-usage.ss')
GxJS('yay!');
module.exports = GxJS;
When run at the shell after compilation with node -e
that outputs:
started GxJS loader!
finished GxJS loader
This is GxJS! { codes: [ 121, 97, 121, 33 ] } {
thisIsTheAnswer: 42,
this: 'is how we hake the moonshine',
'This is valid': {
name: 'symbols-have-no-js-value!',
hash: 386716666,
interned: true
}
} string append
Note that the RTS took our 'yay!'
JavaScript string and turned it into a
scheme string when calling the function.
Gambit
Gerbil itself transpiles its Meta-Scheme dialect to Gambit scheme. If we
want to compile a Gambit scheme file without calling gxc
, the gerbil
compiler, we can do so.
(declare (extended-bindings))
(##inline-host-declaration "console.log('Started Gambit-only GxJS loader!')")
(define property-name "This is valid")
(define GxJS-vector
(##vector
property-name 42
'this "is how we hake the moonshine"))
(define (this-is-gambit-gxjs! #!optional (val 42))
(##inline-host-statement "console.log('This is GxJS!', (@1@), (@2@))"
val GxJS-vector))
(##inline-host-statement "console.log('finished Gambit-only GxJS loader');
module.exports = RTS.scm2host(@1@);" this-is-gambit-gxjs!)
Now a JavaScript file that requires what we export, calls what we export, and exports what we export.
const gambitGxJS = require('gxjs-loader!./gxjs-loader-usage.scm')
gambitGxJS('gambitYay!');
module.exports = gambitGxJS;
cd ../gxjs-loader/ && yarn run webpack && du -h dist/* && cd - ; echo "TESTING"; echo; cd ../gxjs-tests/; yarn run webpack ; node -e "require ('./dist/main.js')"
Options: Make the JS code differ
By default the loader just loads the scheme file, using Gerbil then Gambit to
compile it first, into the gxjs
runtime system.
It does so by adding const RTS = require('gxjs')
to the top, then wrapping the
output from the compiler in an ArrowFunctionExpression
that it calls.
If the extenstion is .scm
we do not call Gerbil at all, by default.
But that can be changed. Mostly for developing the runtimes things need changing around. Here are the options.
-link
: If this is passed and not false make a link file that has the RTS definition and do not require any RTS’s.return
: When this is passed a symbol name then the very end of the code has areturn
statement for that symbol. i.e.gxjs-loader?-linkreturn=RTS
adds that.call
: All the generated code is wrapped in anArrowFunctionExpression
, mostly to isolate it and deal with scope. If we want to call that function, which be default we do,call
contains the arguments we wish to call it with. Default:[]
.If
false
we do not call the generated function.exports
: Do we want to place amodule.exports =
before the function or function call expression? If this is passed and notfalse
, yes, yes we do. i.e.'gxjs-loader?-link&return=RTS&exports!./gxjs-link-loader-runtime.ss'
args
: This is an array of parameters to declare the ArrowFunction with. By default[]
of course.RTS
: If is is a string that becomes the argument that werequire
forconst RTS
to become the runtime system. If it’sfalse
there is no RTS required.i.e.
'gxjs-loader?args=["RTS"]&RTS=false&call=false&exports!./gxjs-link-loader-runtime.ss'
or'gxjs-loader?RTS=./gxjs-link-loader.js!./gxjs-link-use-runtime.ss'
-link
to create and embed a runtime system.
Most of the time there is a const RTS = require('gxjs')
inserted in the file.
But sometimes, like, so far, the one time I needed to create the runtime
contained in 'gxjs'
, we actually want the compiler to create one for us.
Let’s create the runtime we want, which is the smallest. Essentially all it does
is change the upstream (Gambit v4.9.3
) module initialization to one that
always runs.
(import :js)
(js#statement #<<EOF
RTS.module_register = function(module_descr) {
const r = this;
r.sp = -1;
r.stack[++this.sp] = void 0;
r.r0 = this.underflow;
r.nargs = 0;
r.trampoline(module_descr[4]);
};
EOF
)
And now the js
to use it.
const RTS = require('gxjs-loader?-link&return=RTS&exports!./gxjs-link-loader-runtime.ss');
const extraRunTime = require('gxjs-loader?args=["RTS"]&RTS=false&call=false&exports!./gxjs-link-loader-runtime.ss');
extraRunTime(RTS);
console.log('New RTS:', RTS.glo, extraRunTime)
module.exports = RTS;
Now we can use that RTS elsewhere
(import :js)
(js#declaration "console.log('Using another RTS,', RTS.glo)")
(js#statement "module.exports = 42")
const answer = require('gxjs-loader?RTS=./gxjs-link-loader.js!./gxjs-link-use-runtime.ss')
module.exports = answer;
Support
Go to https://github.com/drewc/gxjs/issues or contact the author.