[gambit-list] Compiling to Javascript

Marc Feeley feeley at iro.umontreal.ca
Sat Jul 13 09:41:21 EDT 2019


Forgot to answer about the list of inlined primitive procedures.  You can use the following script to get the list for a specific backend and with a specific set of declarations.


#!/usr/bin/env gsi-script

;; usage: gsc -i inlined.scm <backend> <declarations>
;;
;; for example,
;;
;;     gsc -i inlined.scm js "(not safe)"

(define (main . args)
  (c#target-select! (string->symbol (car args)) '() #f)
  (let* ((env
          (c#add-declarations (##sourcify-deep
                               (cons '##declare
                                     (with-input-from-string (cadr args)
                                       read-all))
                               (##make-source #f #f))
                              (c#make-global-environment)))
         (result
          '()))
    (for-each
     (lambda (x)
       (let* ((name (car x)) (proc (c#target.prim-info name)))
         (if (and proc ((c#proc-obj-inlinable? proc) env))
             (set! result (cons (symbol->string name) result)))))
     (table->list c#targ-prim-proc-table))
    (for-each
     println
     (c#sort-list result string<?))))


Here’s the current list of 377 inlined primitives for the JavaScript backend with the declarations (##declare (not safe)):

##bignum.adigit-<
##bignum.adigit-=
##bignum.adigit-add!
##bignum.adigit-bitwise-and!
##bignum.adigit-bitwise-ior!
##bignum.adigit-bitwise-not!
##bignum.adigit-bitwise-xor!
##bignum.adigit-cat!
##bignum.adigit-copy!
##bignum.adigit-dec!
##bignum.adigit-inc!
##bignum.adigit-length
##bignum.adigit-negative?
##bignum.adigit-ones?
##bignum.adigit-shrink!
##bignum.adigit-sub!
##bignum.adigit-zero?
##bignum.make
##bignum.mdigit-div!
##bignum.mdigit-length
##bignum.mdigit-mul!
##bignum.mdigit-quotient
##bignum.mdigit-ref
##bignum.mdigit-remainder
##bignum.mdigit-set!
##bignum.mdigit-test?
##bignum.negative?
##bignum?
##boolean?
##box
##box?
##caaaar
##caaadr
##caaar
##caadar
##caaddr
##caadr
##caar
##cadaar
##cadadr
##cadar
##caddar
##cadddr
##caddr
##cadr
##car
##cdaaar
##cdaadr
##cdaar
##cdadar
##cdaddr
##cdadr
##cdar
##cddaar
##cddadr
##cddar
##cdddar
##cddddr
##cdddr
##cddr
##cdr
##char->integer
##char<=?
##char<?
##char=?
##char>=?
##char>?
##char?
##check-heap-limit
##closure-code
##closure-length
##closure-ref
##closure-set!
##closure?
##cons
##continuation-denv
##continuation-denv-set!
##continuation-frame
##continuation-frame-set!
##continuation-fs
##continuation-link
##continuation-next
##continuation-ref
##continuation-ret
##continuation-set!
##continuation-slot-live?
##continuation?
##cpxnum-imag
##cpxnum-make
##cpxnum-real
##cpxnum?
##current-thread
##eof-object?
##eq?
##f32vector
##f32vector-length
##f32vector-ref
##f32vector-set!
##f32vector-shrink!
##f32vector?
##f64vector
##f64vector-length
##f64vector-ref
##f64vector-set!
##f64vector-shrink!
##f64vector?
##first-argument
##fixnum->bignum
##fixnum->flonum
##fixnum->flonum-exact?
##fixnum?
##fl*
##fl+
##fl-
##fl/
##fl<
##fl<=
##fl=
##fl>
##fl>=
##flabs
##flacos
##flacosh
##flasin
##flasinh
##flatan
##flatanh
##flceiling
##flcopysign
##flcos
##flcosh
##fleqv?
##fleven?
##flexp
##flexpm1
##flexpt
##flfinite?
##flfloor
##flilogb
##flinfinite?
##flinteger?
##fllog
##fllog1p
##flmax
##flmin
##flnan?
##flnegative?
##flodd?
##flonum->fixnum
##flonum?
##flpositive?
##flround
##flscalbn
##flsin
##flsinh
##flsqrt
##flsquare
##fltan
##fltanh
##fltruncate
##flzero?
##foreign-tags
##foreign?
##frame-fs
##frame-link
##frame-ref
##frame-ret
##frame-set!
##frame-slot-live?
##frame?
##fx*
##fx*?
##fx+
##fx+?
##fx-
##fx-?
##fx<
##fx<=
##fx=
##fx>
##fx>=
##fxabs
##fxabs?
##fxand
##fxarithmetic-shift
##fxarithmetic-shift-left
##fxarithmetic-shift-left?
##fxarithmetic-shift-right
##fxarithmetic-shift-right?
##fxarithmetic-shift?
##fxbit-count
##fxbit-set?
##fxeven?
##fxfirst-bit-set
##fxif
##fxior
##fxlength
##fxmax
##fxmin
##fxmodulo
##fxnegative?
##fxnot
##fxodd?
##fxpositive?
##fxquotient
##fxremainder
##fxsquare
##fxsquare?
##fxwrap*
##fxwrap+
##fxwrap-
##fxwrapabs
##fxwraparithmetic-shift
##fxwraparithmetic-shift-left
##fxwraparithmetic-shift-left?
##fxwraparithmetic-shift?
##fxwraplogical-shift-right
##fxwraplogical-shift-right?
##fxwrapquotient
##fxwrapsquare
##fxxor
##fxzero?
##global-var-primitive-ref
##global-var-primitive-set!
##global-var-ref
##global-var-set!
##global-var?
##identity
##integer->char
##keyword->string
##keyword-hash
##keyword-hash-set!
##keyword-interned?
##keyword-name
##keyword-name-set!
##keyword?
##list
##make-closure
##make-continuation
##make-delay-promise
##make-f32vector
##make-f64vector
##make-frame
##make-global-var
##make-s16vector
##make-s32vector
##make-s64vector
##make-s8vector
##make-string
##make-structure
##make-subprocedure
##make-u16vector
##make-u32vector
##make-u64vector
##make-u8vector
##make-uninterned-keyword
##make-uninterned-symbol
##make-values
##make-vector
##make-will
##mutable?
##not
##null?
##pair?
##procedure?
##promise-state
##promise-state-set!
##promise?
##ratnum-denominator
##ratnum-make
##ratnum-numerator
##ratnum?
##return-fs
##return?
##s16vector
##s16vector-length
##s16vector-ref
##s16vector-set!
##s16vector-shrink!
##s16vector?
##s32vector
##s32vector-length
##s32vector-ref
##s32vector-set!
##s32vector-shrink!
##s32vector?
##s64vector
##s64vector-length
##s64vector-ref
##s64vector-set!
##s64vector-shrink!
##s64vector?
##s8vector
##s8vector-length
##s8vector-ref
##s8vector-set!
##s8vector-shrink!
##s8vector?
##set-box!
##set-car!
##set-cdr!
##string
##string->keyword
##string->symbol
##string-length
##string-ref
##string-set!
##string-shrink!
##string?
##structure
##structure-direct-instance-of?
##structure-length
##structure-type
##structure-type-set!
##structure?
##subprocedure-id
##subprocedure-nb-closed
##subprocedure-nb-parameters
##subprocedure-parent
##subprocedure-parent-info
##subprocedure-parent-name
##subtype
##subtype-set!
##symbol->string
##symbol-hash
##symbol-hash-set!
##symbol-interned?
##symbol-name
##symbol-name-set!
##symbol?
##type
##type-cast
##u16vector
##u16vector-length
##u16vector-ref
##u16vector-set!
##u16vector-shrink!
##u16vector?
##u32vector
##u32vector-length
##u32vector-ref
##u32vector-set!
##u32vector-shrink!
##u32vector?
##u64vector
##u64vector-length
##u64vector-ref
##u64vector-set!
##u64vector-shrink!
##u64vector?
##u8vector
##u8vector-length
##u8vector-ref
##u8vector-set!
##u8vector-shrink!
##u8vector?
##unbound?
##unbox
##unchecked-structure-ref
##unchecked-structure-set!
##values
##values-length
##values-ref
##values-set!
##values?
##vector
##vector-length
##vector-ref
##vector-set!
##vector-shrink!
##vector?
##void
##will-action
##will-action-set!
##will-testator
##will-testator-set!
##will?

Marc



> On Jul 13, 2019, at 3:30 AM, hashavtech at outlook.com wrote:
> 
> Thank you, this helps a lot. Saving continuations in a js variable sound exactly like what I needed
> From: Marc Feeley <feeley at iro.umontreal.ca>
> Sent: Monday, July 8, 2019 4:47 PM
> To: hashavtech at outlook.com
> Cc: gambit-list at iro.umontreal.ca
> Subject: Re: [gambit-list] Compiling to Javascript
>  
> Why do you need GVM.init() ?  Currently, the Gambit VM can’t be initialized more than once so GVM.init() is a noop.
> 
> To export Scheme procedures as JavaScript global functions you can use the following js-set! procedure (which works because the g_scm2host runtime system function knows how to convert Scheme procedures to JavaScript functions):
> 
> 
> (define (js-set! global procedure)
>   (##inline-host-statement
>    "(function () {return this;}())[g_scm2host(@1@)] = g_scm2host(@2@);"
>    global
>    procedure)
>   (void))
> 
> ;; sample use:
> 
> (define (scheme_add a b)
>   (println "scheme_add")
>   (fx+ a b))
> 
> (js-set! "scheme_add" scheme_add)
> 
> 
> In the JS code you can then simply call scheme_add(10,20) to call the Scheme procedure with that name and get the value 30 back.  The JavaScript values 10 and 20 are converted to Scheme using g_host2scm and the resulting Scheme value is converted back to JavaScript using g_scm2host.
> 
> The js-set! procedure combined with continuations can be used to implement your pause-gvm procedure:
> 
> 
> (define (pause-gvm)
>   (##continuation-capture
>    (lambda (c)
>      (js-set! "GVM_resume"
>               (lambda () (##continuation-return-no-winding c #f)))
>      (##inline-host-statement "return null;")))) ;; cause trampoline to exit
> 
> ;; sample use:
> 
> (define (scheme_add2 a b)
>   (println "scheme_add2")
>   (pause-gvm)
>   (fx+ a b))
> 
> (js-set! "scheme_add2" scheme_add2)
> 
> 
> Then in your JavaScript code you can do:
> 
> scheme_add2(10,20);   // will return (with an unspecified result) when pause-gvm is called
> res = GVM_resume();   // resume execution after the pause-gvm, and returns 30
> 
> 
> This could form the basis for a Scheme-level thread system whose execution is event-driven.  Implementation left as an exercice…
> 
> Marc
> 
> 
> 
> > On Jul 8, 2019, at 7:34 AM, hashavtech at outlook.com wrote:
> > 
> > Thank you for the detailed example, it helped a lot. I've played with running Scheme in a browser and it's a lot of fun! (Although writing custom runtime library is not easy for a newcomer. For example is there a list of what is supported by the compiler out of the box - such as fx+ and friends? )
> > 
> > In any case this was using compiled Scheme as a main script, and I was thinking how to use Scheme with some JS framework that does not like to relinquish control. Ideally I think there'd be possible to do the following:
> > 
> >        • Initialize Gambit VM, something like   GVM.init();
> >        • Later call into some procedure passing parameters:  GM.call('do-scheme-stuff', [param1, param2]);
> >        • from scheme code to be able to pause the execution and to return control to the external javascript. Something like this:
> > (define (dostuff)
> > ... (define (add-to-menu option) (##inline-host-expression "addToGlobalFrameworkMenu(...)"))
> >   (add-to-menu "option1")
> >   (add-to-menu "option2")
> >   (##pause-vm)
> > 
> > Where ##pause-vm will stop executing scheme code. Later it'd be possible in JS to do GVM.resume("option2") and have (##pause-vm) return with the supplied value. Alternatively, we'd be able call GVM.dropStack() to trash the scheme stack and start anew with GVM.call('other-entry-point', ['more values']);
> > 
> > There does not seem to be anything in the manual regarding the various backends such as JS, so I'm not sure, but most of this probably is possible?
> > 
> > 
> > From: Marc Feeley <feeley at iro.umontreal.ca>
> > Sent: Friday, July 5, 2019 1:46 PM
> > To: hashavtech at outlook.com
> > Cc: gambit-list at iro.umontreal.ca
> > Subject: Re: [gambit-list] Compiling to Javascript
> >  
> > I’m currently working on implementing an intelligent linker that can remove the parts of the runtime library that are not needed by the application.  For the moment, to get a compact executable you need to write your own runtime library for the functions you need.  If you only need basic stuff, this may be a reasonable undertaking.
> > 
> > Below is a sample application split in 2 files: “lib.scm” is the runtime library and “app.scm” is the main program.  The library contains definitions of for-each, append, etc.  Note that some operations like fx+, fx-, fx<, cons, car, cdr, println don’t need to be defined because the Gambit compiler treats them as inlined primitives (they compile to the corresponding JavaScript code instead of being actual Scheme procedure calls).
> > 
> > Here is how this application is linked, and the result of executing the app:
> > 
> > % make
> > gsc -target js -c lib.scm
> > gsc -target js -c app.scm
> > gsc -target js -link -l lib app.js
> > gsc -target js -c lib.scm
> > cat app_.js lib.js app.js > linked_app.js
> > wc *.js
> >      339     870    7244 app.js
> >      651    1931   14992 app_.js
> >      335     853    7937 lib.js
> >     1325    3654   30173 linked_app.js
> >     2650    7308   60346 total
> > node linked_app.js
> > >>> lib.scm
> > >>> app.scm
> > 1
> > 1
> > 2
> > 3
> > 5
> > 8
> > 410
> > 9227465
> > 
> > The JavaScript code is 30K bytes, and half of that is in app_.js that is the runtime system (which won’t grow much if the application is bigger).
> > 
> > Aside from the intelligent linker, a more compact code could be obtained by minimizing the JavaScript code either with an external minimizer or by reducing the size of the identifiers that are emitted by the compiler and not pretty-printing the JavaScipt code which adds lots of spaces for indentation.
> > 
> > Marc
> > 
> > 
> > 
> > ------------------------------------------------------------
> > # makefile
> > 
> > #GSC=../gsc/gsc -:=.. -target js
> > GSC=gsc -target js
> > 
> > all: run
> > 
> > lib.js: lib.scm
> >         $(GSC) -c lib.scm
> > 
> > app.js: app.scm
> >         $(GSC) -c app.scm
> > 
> > app_.js: lib.js app.js
> >         $(GSC) -link -l lib app.js
> > 
> > linked_app.js: lib.js app.js app_.js
> >         $(GSC) -c lib.scm
> >         cat app_.js lib.js app.js > linked_app.js
> >         wc *.js
> > 
> > run: linked_app.js
> >         node linked_app.js
> > 
> > clean:
> >         rm -f lib.js app.js app_.js linked_app.js
> > ------------------------------------------------------------
> > ;; file: lib.scm
> > 
> > (declare (standard-bindings) (extended-bindings) (not safe) (not run-time-bindings))
> > 
> > (println ">>> lib.scm")
> > 
> > (define (for-each f lst)
> >   (if (pair? lst)
> >       (begin
> >         (f (car lst))
> >         (for-each f (cdr lst)))))
> > 
> > (define (append lst1 lst2)
> >   (if (pair? lst1)
> >       (cons (car lst1) (append (cdr lst1) lst2))
> >       lst2))
> > 
> > (define (current-milliseconds)
> >   (##inline-host-expression "Date.now()"))
> > 
> > (define (js-alert obj)
> >   (##inline-host-statement "alert(g_scm2js(@1@));" obj))
> > 
> > (app#) ;; execute app.scm module
> > ------------------------------------------------------------
> > ;; file: app.scm
> > 
> > (declare (standard-bindings) (extended-bindings) (not safe) (not run-time-bindings))
> > 
> > (println ">>> app.scm")
> > 
> > (define (fib n) (if (fx< n 2) n (fx+ (fib (fx- n 1)) (fib (fx- n 2)))))
> > 
> > (for-each
> >  (lambda (x)
> >    (println (fib x)))
> >  (append '(1 2 3)
> >          '(4 5 6)))
> > 
> > (let* ((start (current-milliseconds))
> >        (result (fib 35))
> >        (end (current-milliseconds)))
> >   (js-alert (fx- end start))
> >   (js-alert result))
> > ------------------------------------------------------------
> > 
> > 
> > > On Jun 29, 2019, at 10:59 AM, hashavtech at outlook.com wrote:
> > > 
> > > There are some messages in 2013 and 2016 that gave me a hint of what needs to be done in order to compile to js. But it seems that some things have changed (for example the gsc with -target js does not seem to produce a .js file, it gave me an .o1 file that contained Javascript). I was able to compile a working example once but the .js file contains the whole _gambit.js library and is 22 MB in size. That isn't really what I was hoping for. I've seen it mentioned that there was some work done on only including the necessary parts of the library, but I was unable to figure out how to do it (I'm new to Gambit although not to Lisp). 
> > > 
> > > I'd be glad for some help getting from a Scheme file to a working and reasonably-sized file. My goal is to include scheme-sourced parts in a web app, so there probably will be some interoperability issues, but at the moment I'm stuck at the compilation stage.
> > > 
> > > _______________________________________________
> > > Gambit-list mailing list
> > > Gambit-list at iro.umontreal.ca
> > > https://mailman.iro.umontreal.ca/cgi-bin/mailman/listinfo/gambit-list





More information about the Gambit-list mailing list