gxjs-loader

0.0.4 • Public • Published

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 a return statement for that symbol. i.e. gxjs-loader?-linkreturn=RTS adds that.

  • call: All the generated code is wrapped in an ArrowFunctionExpression, 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 a module.exports = before the function or function call expression? If this is passed and not false, 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 we require for const RTS to become the runtime system. If it’s false 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.

Readme

Keywords

none

Package Sidebar

Install

npm i gxjs-loader

Weekly Downloads

4

Version

0.0.4

License

MIT

Unpacked Size

105 kB

Total Files

7

Last publish

Collaborators

  • drewc