Hi,
Sorry to disturb you again on this. I continued my experiments with “-target js”. I reimplemented quasiquote, unquote… and I stumbled on a strange bug that mystifies me: now “cons” function is not known. I have sucessfully compiled programs using cons with -target js many times but now it just won’t compile it correctly. Here is a simplified version of the code just to illustrate what I see:
; File: Minimal-Ex1.scm
(declare (standard-bindings) (extended-bindings) (fixnum) (not safe))
; include Marc's code (include "obj2str.scm")
;;; ;;;; debugging utility ;;;
(define (console.log x) ;; Note: the parameter x will be in variable Gambit_r1 (##inline-host-statement "console.log(Gambit_r1.toString());\n"))
;;; ;;;; list utilities I had to redefine ;;;
(define (myreverse l) (define (iter a b) (if (null? a) b (iter (cdr a) (cons (car a) b)))) (iter l (list)))
(define (iota n) (myreverse (riota n)))
(define (riota n) (if (= n 0) (cons 0 '()) (cons n (riota (- n 1)))))
(define (foldl func accum lst) (if (null? lst) accum (foldl func (func accum (car lst)) (cdr lst))))
(define (foldr func end lst) (if (null? lst) end (func (car lst) (foldr func end (cdr lst)))))
(define (appendo . lists) (foldl append2 '() lists))
(define (append2 l1 l2) (foldr cons l2 l1))
(console.log (object->string (cons 1 2))) ; OK (1 . 2) (console.log (object->string (iota 5))) ; OK (0 1 2 3 4 5) (console.log (object->string (append2 (list 1 2 3) (list 'a 'b 'c)))) ; FAILS [Error] ReferenceError: Can't find variable: Gambit_bb1_cons
—————————————————————————————————————- magnan$ gsc -c -target js Minimal-Ex1.scm magnan$ node Minimal-Ex1.js (1 . 2) (0 1 2 3 4 5)
/Users/magnan/CDSCode/CDSProducts/Athena-Embedded/src/Athena-Embedded/Minimal-Ex1.js:5551 Gambit_r1 = (Gambit_bb1_cons); ^ ReferenceError: Gambit_bb1_cons is not defined
You see that (cons 1 2) and (iota 5) compiles well but not (append2 … ). Can you explain this?
Thank you, Francois Magnan
On 2013-11-18, at 16:02, Francois Magnan magnan@categoricaldesign.com wrote:
Hi Marc,
I was finally able to make a simplified version my program work when compiled to javascript but I am missing some crucial elements to complete the experiment:
1 - Variable number of parameters for functions: (define (function . args) …) 2 - Quasiquote, unquote and unquote-splicing
Without 1) I cannot get functions like “append” so I have a hard time trying to solve 2). Can you give me hints for that or drop me an email when those features become available with the “-target js” option?
Thank you, François
On 2013-11-14, at 17:06, Marc Feeley feeley@iro.umontreal.ca wrote:
On Nov 14, 2013, at 4:23 PM, Francois Magnan magnan@categoricaldesign.com wrote:
By the way ##inline-host-code doesn’t seem to work anymore.
(define (console.log x) ;; Note: the parameter x will be in variable Gambit_r1 (##inline-host-code “console.log(Gambit_r1);\n”))
How can I inline javascript code now? Thank you, Francois
Recall that ##inline-host-code no longer exists... you have to use ##inline-host-statement and ##inline-host-expression depending on the nature of the code. For example, this works for me using node:
(declare (extended-bindings) (not safe))
(define (show arg) (##inline-host-statement "console.log(Gambit_r1.toString());"))
(show 'hello)
(show "world")
(show (##inline-host-expression "1+2+3"))
Marc
Afficher les réponses par date
Francois,
Two Q:s:
First, when you got a very basic sample app like a DOM-based hangman going, feel free to share code
Second, how is the type mapping - fixnums, flonums, bignums all wrapped to JS double, Scheme string and vector to JS string and vector?
Thanks, Mikael
2013/11/21 Francois Magnan magnan@categoricaldesign.com
Hi,
Sorry to disturb you again on this. I continued my experiments with “-target js”. I reimplemented quasiquote, unquote… and I stumbled on a strange bug that mystifies me: now “cons” function is not known. I have sucessfully compiled programs using cons with -target js many times but now it just won’t compile it correctly. Here is a simplified version of the code just to illustrate what I see:
; File: Minimal-Ex1.scm
(declare (standard-bindings) (extended-bindings) (fixnum) (not safe))
; include Marc's code (include "obj2str.scm")
;;; ;;;; debugging utility ;;;
(define (console.log x) ;; Note: the parameter x will be in variable Gambit_r1 (##inline-host-statement "console.log(Gambit_r1.toString());\n"))
;;; ;;;; list utilities I had to redefine ;;;
(define (myreverse l) (define (iter a b) (if (null? a) b (iter (cdr a) (cons (car a) b)))) (iter l (list)))
(define (iota n) (myreverse (riota n)))
(define (riota n) (if (= n 0) (cons 0 '()) (cons n (riota (- n 1)))))
(define (foldl func accum lst) (if (null? lst) accum (foldl func (func accum (car lst)) (cdr lst))))
(define (foldr func end lst) (if (null? lst) end (func (car lst) (foldr func end (cdr lst)))))
(define (appendo . lists) (foldl append2 '() lists))
(define (append2 l1 l2) (foldr cons l2 l1))
(console.log (object->string (cons 1 2))) ; OK (1 . 2) (console.log (object->string (iota 5))) ; OK (0 1 2 3 4 5) (console.log (object->string (append2 (list 1 2 3) (list 'a 'b 'c)))) ; FAILS [Error] ReferenceError: Can't find variable: Gambit_bb1_cons
—————————————————————————————————————- magnan$ gsc -c -target js Minimal-Ex1.scm magnan$ node Minimal-Ex1.js (1 . 2) (0 1 2 3 4 5)
/Users/magnan/CDSCode/CDSProducts/Athena-Embedded/src/Athena-Embedded/Minimal-Ex1.js:5551 Gambit_r1 = (Gambit_bb1_cons); ^ ReferenceError: Gambit_bb1_cons is not defined
You see that (cons 1 2) and (iota 5) compiles well but not (append2 … ). Can you explain this?
Thank you, Francois Magnan
On 2013-11-18, at 16:02, Francois Magnan magnan@categoricaldesign.com wrote:
Hi Marc,
I was finally able to make a simplified version my program work when compiled to javascript but I am missing some crucial elements to complete the experiment:
1 - Variable number of parameters for functions: (define (function . args) …) 2 - Quasiquote, unquote and unquote-splicing
Without 1) I cannot get functions like “append” so I have a hard time trying to solve 2). Can you give me hints for that or drop me an email when those features become available with the “-target js” option?
Thank you, François
On 2013-11-14, at 17:06, Marc Feeley feeley@iro.umontreal.ca wrote:
On Nov 14, 2013, at 4:23 PM, Francois Magnan magnan@categoricaldesign.com wrote:
By the way ##inline-host-code doesn’t seem to work anymore.
(define (console.log x) ;; Note: the parameter x will be in variable Gambit_r1 (##inline-host-code “console.log(Gambit_r1);\n”))
How can I inline javascript code now? Thank you, Francois
Recall that ##inline-host-code no longer exists... you have to use ##inline-host-statement and ##inline-host-expression depending on the nature of the code. For example, this works for me using node:
(declare (extended-bindings) (not safe))
(define (show arg) (##inline-host-statement "console.log(Gambit_r1.toString());"))
(show 'hello)
(show "world")
(show (##inline-host-expression "1+2+3"))
Marc
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
On Nov 22, 2013, at 1:40 AM, Mikael mikael.rcv@gmail.com wrote:
Francois,
Two Q:s:
First, when you got a very basic sample app like a DOM-based hangman going, feel free to share code
Second, how is the type mapping - fixnums, flonums, bignums all wrapped to JS double, Scheme string and vector to JS string and vector?
The data representation is customizable with two options for each type. There is a "natural" mapping (Scheme number -> JS number, Scheme vector -> JS array, etc) and one which uses classes.
Note that the natural mapping may violate some of the Scheme semantics. For example, JS numbers don't carry the concept of exactness, and JS strings are immutable whereas Scheme strings are mutable. On the other hand the natural mapping may be useful in cases where some details of the Scheme semantics are not important (the generated JS code is easier to read/understand, the generated JS code can be more easily interfaced with existing JS libraries, etc). A mapping which respects the Scheme semantics is:
- Fixnums are mapped to JS numbers - Booleans are mapped to JS booleans - Symbols are mapped to JS strings - Vectors are mapped to JS arrays - Procedures are mapped to JS functions - Strings, characters, flonums (and other numbers), and pairs are implemented with JS classes
Unfortunately, this mapping makes it expensive to implement some Gambit specific operations, such as ##subtype, ##subtype-set! and ##vector-ref. So it may be necessary to implement non immediate data (vectors, structures, etc) uniformly with classes when using the standard Gambit runtime system. I haven't explored this aspect much, but it may be that supporting all the Gambit primitives isn't practical. A solution may be to have two modes (Standard Scheme and Gambit Scheme) so the user can select the best mode for his needs.
Marc
(For general interest)
2013/11/22 Marc Feeley feeley@iro.umontreal.ca
On Nov 22, 2013, at 1:40 AM, Mikael mikael.rcv@gmail.com wrote:
Francois,
Two Q:s:
First, when you got a very basic sample app like a DOM-based hangman
going, feel free to share code
Second, how is the type mapping - fixnums, flonums, bignums all wrapped
to JS double, Scheme string and vector to JS string and vector?
(1)
The data representation is customizable with two options for each type. There is a "natural" mapping (Scheme number -> JS number, Scheme vector -> JS array, etc) and one which uses classes.
(2)
Note that the natural mapping may violate some of the Scheme semantics. For example, JS numbers don't carry the concept of exactness, and JS strings are immutable whereas Scheme strings are mutable. On the other hand the natural mapping may be useful in cases where some details of the Scheme semantics are not important (the generated JS code is easier to read/understand, the generated JS code can be more easily interfaced with existing JS libraries, etc). A mapping which respects the Scheme semantics is:
- Fixnums are mapped to JS numbers
- Booleans are mapped to JS booleans
- Symbols are mapped to JS strings
- Vectors are mapped to JS arrays
- Procedures are mapped to JS functions
- Strings, characters, flonums (and other numbers), and pairs are
implemented with JS classes
Wait, first(1) you suggest there's a customizable option, and then second(2) you outline a holistic approach for Scheme-JS type mapping.
How is it? And if anything is configurable, where's the switch?
Unfortunately, this mapping makes it expensive to implement some Gambit
specific operations, such as ##subtype, ##subtype-set! and
Didn't even hear about ##subtype* til now - Any use of JS turns out to be for some special purpose anyhow, so that there's some real corner case of Scheme execution in this environment that has a low performance is really completely cool.
##vector-ref.
Wait, why, can't this just be ordinarily expanded to JS' variable[index]?
So it may be necessary to implement non immediate data (vectors, structures, etc) uniformly with classes when using the standard Gambit runtime system.
I haven't explored this aspect much, but it may be that supporting all the Gambit primitives isn't practical.
(same note as above re special purpose here)
A solution may be to have two modes (Standard Scheme and Gambit Scheme) so the user can select the best mode for his needs.
What would the difference be approx here?
Marc
On Nov 22, 2013, at 4:52 PM, Mikael mikael.rcv@gmail.com wrote:
(For general interest)
2013/11/22 Marc Feeley feeley@iro.umontreal.ca
On Nov 22, 2013, at 1:40 AM, Mikael mikael.rcv@gmail.com wrote:
Francois,
Two Q:s:
First, when you got a very basic sample app like a DOM-based hangman going, feel free to share code
Second, how is the type mapping - fixnums, flonums, bignums all wrapped to JS double, Scheme string and vector to JS string and vector?
(1) The data representation is customizable with two options for each type. There is a "natural" mapping (Scheme number -> JS number, Scheme vector -> JS array, etc) and one which uses classes.
(2) Note that the natural mapping may violate some of the Scheme semantics. For example, JS numbers don't carry the concept of exactness, and JS strings are immutable whereas Scheme strings are mutable. On the other hand the natural mapping may be useful in cases where some details of the Scheme semantics are not important (the generated JS code is easier to read/understand, the generated JS code can be more easily interfaced with existing JS libraries, etc). A mapping which respects the Scheme semantics is:
- Fixnums are mapped to JS numbers
- Booleans are mapped to JS booleans
- Symbols are mapped to JS strings
- Vectors are mapped to JS arrays
- Procedures are mapped to JS functions
- Strings, characters, flonums (and other numbers), and pairs are implemented with JS classes
Wait, first(1) you suggest there's a customizable option, and then second(2) you outline a holistic approach for Scheme-JS type mapping.
How is it? And if anything is configurable, where's the switch?
The source code of the universal backend was written to support both data representations, selectable for each type. So it is "customizable" in the sense that you simply have to change a single definition in _t-univ.scm to select the representation for each Scheme type (see the top of the file, e.g. (define (univ-flonum-representation ctx) 'class) ). It would be possible to expose these settings to the user, although it is not clear what is the best way to do that. Perhaps that could be part of the target's name, e.g.
gsc -target js file.scm gsc -target js-foo file.scm gsc -target js-bar file.scm
where foo and bar would indicate a particular choice of data representations that make some compromises (e.g. incomplete support of the Gambit specific features, lesser performance for some of the Gambit specific features, etc).
Unfortunately, this mapping makes it expensive to implement some Gambit specific operations, such as ##subtype, ##subtype-set! and
Didn't even hear about ##subtype* til now - Any use of JS turns out to be for some special purpose anyhow, so that there's some real corner case of Scheme execution in this environment that has a low performance is really completely cool.
The problem is that ##subtype, etc are used in the implementation of the Gambit runtime library. For example, ##subtype is used in the implementation of the procedure equal?-hash and some type predicates. So even if ##subtype is not used directly by users, some user callable procedures in the Gambit runtime library do use ##subtype.
##vector-ref.
Wait, why, can't this just be ordinarily expanded to JS' variable[index]?
Because ##vector-ref is used to access slots of many Gambit objects, not just vectors. For example the slot at index 0 of a symbol is the symbol's name, and the slot at index 0 of a structure is the type descriptor:
(##vector-ref 'foo 0)
"foo"
(##vector-ref (current-input-port) 0)
#<type #4 device-port>
If a Scheme datatype is represented using a JS class (for example a Scheme symbol is represented with a JS class with a "name" property), then (##vector-ref sym 0) if it is translated to sym[0] will not retrieve the name of the symbol. It would be possible to avoid this problem by using JS arrays to represent all memory allocated Gambit objects, but this would not be a natural translation (harder to understand the generated code, harder to write external JS code accessing the data representation, etc). Another approach would be to put some type dispatch logic in ##vector-ref to do the right thing with symbols, structures, etc, but this would slow down ##vector-ref.
Of course another approach would be to refactor the Gambit runtime system to replace such "unclean" uses of ##vector-ref by calls to type specific primitives. It is not clear how much work this would be (primitives have to be added to the compiler and the runtime system has to be refactored).
So it may be necessary to implement non immediate data (vectors, structures, etc) uniformly with classes when using the standard Gambit runtime system.
I haven't explored this aspect much, but it may be that supporting all the Gambit primitives isn't practical.
(same note as above re special purpose here)
A solution may be to have two modes (Standard Scheme and Gambit Scheme) so the user can select the best mode for his needs.
What would the difference be approx here?
Hard to say. The Gambit runtime system has to be examined carefully to see what would break for a particular choice of representation. For example, in a mode where number exactness is not supported (allowing all Scheme numbers to be represented with the JS "number" type, except for complex numbers), there will be a major impact on the numerical functions in lib/_num.scm . Some (how many?) will have to be modified to be consistent with the fact that there is no exactness. Should the lack of exactness be handled the same as treating all numbers as inexact numbers? etc.
Marc
Hi Marc,
It seems I stumbled on a problem with the javascript backend. When I compile my program I get an error message from gsc:
*** ERROR IN c#univ-dump-procs -- Wrong number of arguments passed to procedure (#<procedure #2> '#(#(target js #<procedure #3> #<procedure #4> #<procedure #5 c#target.dump> 5 #<procedure #6 c#target.prim-info> #<procedure #7 ... '#<procedure #8> '(("Gambit_" "stack") "[" (("Gambit_" "sp") (-2)) "]") '(("Gambit_" "stack") "[" (("Gambit_" "sp") (-1)) "]") '(("Gambit_" "stack") "[" (("Gambit_" "sp") (-4)) "]") ‘("Gambit_" ("r" 1)))
If I comment out some function definitions in my code I don’t get the error. It looks to me like there is a limit to the number of procedures that can be handled in a given file. It seems to be related to the maximum number of arguments a function can take.
Is there a way to split the code into multiple files?
Thank you, Francois Magnan
On 2013-11-23, at 09:54, Marc Feeley feeley@iro.umontreal.ca wrote:
On Nov 22, 2013, at 4:52 PM, Mikael mikael.rcv@gmail.com wrote:
(For general interest)
2013/11/22 Marc Feeley feeley@iro.umontreal.ca
On Nov 22, 2013, at 1:40 AM, Mikael mikael.rcv@gmail.com wrote:
Francois,
Two Q:s:
First, when you got a very basic sample app like a DOM-based hangman going, feel free to share code
Second, how is the type mapping - fixnums, flonums, bignums all wrapped to JS double, Scheme string and vector to JS string and vector?
(1) The data representation is customizable with two options for each type. There is a "natural" mapping (Scheme number -> JS number, Scheme vector -> JS array, etc) and one which uses classes.
(2) Note that the natural mapping may violate some of the Scheme semantics. For example, JS numbers don't carry the concept of exactness, and JS strings are immutable whereas Scheme strings are mutable. On the other hand the natural mapping may be useful in cases where some details of the Scheme semantics are not important (the generated JS code is easier to read/understand, the generated JS code can be more easily interfaced with existing JS libraries, etc). A mapping which respects the Scheme semantics is:
- Fixnums are mapped to JS numbers
- Booleans are mapped to JS booleans
- Symbols are mapped to JS strings
- Vectors are mapped to JS arrays
- Procedures are mapped to JS functions
- Strings, characters, flonums (and other numbers), and pairs are implemented with JS classes
Wait, first(1) you suggest there's a customizable option, and then second(2) you outline a holistic approach for Scheme-JS type mapping.
How is it? And if anything is configurable, where's the switch?
The source code of the universal backend was written to support both data representations, selectable for each type. So it is "customizable" in the sense that you simply have to change a single definition in _t-univ.scm to select the representation for each Scheme type (see the top of the file, e.g. (define (univ-flonum-representation ctx) 'class) ). It would be possible to expose these settings to the user, although it is not clear what is the best way to do that. Perhaps that could be part of the target's name, e.g.
gsc -target js file.scm gsc -target js-foo file.scm gsc -target js-bar file.scm
where foo and bar would indicate a particular choice of data representations that make some compromises (e.g. incomplete support of the Gambit specific features, lesser performance for some of the Gambit specific features, etc).
Unfortunately, this mapping makes it expensive to implement some Gambit specific operations, such as ##subtype, ##subtype-set! and
Didn't even hear about ##subtype* til now - Any use of JS turns out to be for some special purpose anyhow, so that there's some real corner case of Scheme execution in this environment that has a low performance is really completely cool.
The problem is that ##subtype, etc are used in the implementation of the Gambit runtime library. For example, ##subtype is used in the implementation of the procedure equal?-hash and some type predicates. So even if ##subtype is not used directly by users, some user callable procedures in the Gambit runtime library do use ##subtype.
##vector-ref.
Wait, why, can't this just be ordinarily expanded to JS' variable[index]?
Because ##vector-ref is used to access slots of many Gambit objects, not just vectors. For example the slot at index 0 of a symbol is the symbol's name, and the slot at index 0 of a structure is the type descriptor:
(##vector-ref 'foo 0)
"foo"
(##vector-ref (current-input-port) 0)
#<type #4 device-port>
If a Scheme datatype is represented using a JS class (for example a Scheme symbol is represented with a JS class with a "name" property), then (##vector-ref sym 0) if it is translated to sym[0] will not retrieve the name of the symbol. It would be possible to avoid this problem by using JS arrays to represent all memory allocated Gambit objects, but this would not be a natural translation (harder to understand the generated code, harder to write external JS code accessing the data representation, etc). Another approach would be to put some type dispatch logic in ##vector-ref to do the right thing with symbols, structures, etc, but this would slow down ##vector-ref.
Of course another approach would be to refactor the Gambit runtime system to replace such "unclean" uses of ##vector-ref by calls to type specific primitives. It is not clear how much work this would be (primitives have to be added to the compiler and the runtime system has to be refactored).
So it may be necessary to implement non immediate data (vectors, structures, etc) uniformly with classes when using the standard Gambit runtime system.
I haven't explored this aspect much, but it may be that supporting all the Gambit primitives isn't practical.
(same note as above re special purpose here)
A solution may be to have two modes (Standard Scheme and Gambit Scheme) so the user can select the best mode for his needs.
What would the difference be approx here?
Hard to say. The Gambit runtime system has to be examined carefully to see what would break for a particular choice of representation. For example, in a mode where number exactness is not supported (allowing all Scheme numbers to be represented with the JS "number" type, except for complex numbers), there will be a major impact on the numerical functions in lib/_num.scm . Some (how many?) will have to be modified to be consistent with the fact that there is no exactness. Should the lack of exactness be handled the same as treating all numbers as inexact numbers? etc.
Marc
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
Hi,
My assumption was wrong… The following code is just a macro that generate 100000 functions and it compiles fine. At least you can have 100000 functions so it should be ok!!! The problem was that I was calling the |max| function. In the javascript backend this function only takes two args. In the scheme world it is an n-ary function.
Sorry for pointing out the wrong thing.
(declare (standard-bindings) (extended-bindings) (fixnum) (not safe))
(define-macro (generate-dummy-function-defs)
; reverse is not available (in target js) (define (myreverse l) (define (iter a b) (if (null? a) b (iter (cdr a) (cons (car a) b)))) (iter l (list)))
(define (iota n) (myreverse (riota n)))
(define (riota n) (if (= n 0) (cons 0 '()) (cons n (riota (- n 1)))))
`(begin ,@(map (lambda (i) `(define (,(string->symbol (string-append “fct” (number->string i)))) ,i)) (iota 100000))))
(generate-dummy-function-defs)
François
On Jan 24, 2014, at 10:52 AM, Francois Magnan magnan@categoricaldesign.com wrote:
Hi Marc,
It seems I stumbled on a problem with the javascript backend. When I compile my program I get an error message from gsc:
*** ERROR IN c#univ-dump-procs -- Wrong number of arguments passed to procedure (#<procedure #2> '#(#(target js #<procedure #3> #<procedure #4> #<procedure #5 c#target.dump> 5 #<procedure #6 c#target.prim-info> #<procedure #7 ... '#<procedure #8> '(("Gambit_" "stack") "[" (("Gambit_" "sp") (-2)) "]") '(("Gambit_" "stack") "[" (("Gambit_" "sp") (-1)) "]") '(("Gambit_" "stack") "[" (("Gambit_" "sp") (-4)) "]") ‘("Gambit_" ("r" 1)))
If I comment out some function definitions in my code I don’t get the error. It looks to me like there is a limit to the number of procedures that can be handled in a given file. It seems to be related to the maximum number of arguments a function can take.
Is there a way to split the code into multiple files?
Thank you, Francois Magnan
On 2013-11-23, at 09:54, Marc Feeley feeley@iro.umontreal.ca wrote:
On Nov 22, 2013, at 4:52 PM, Mikael mikael.rcv@gmail.com wrote:
(For general interest)
2013/11/22 Marc Feeley feeley@iro.umontreal.ca
On Nov 22, 2013, at 1:40 AM, Mikael mikael.rcv@gmail.com wrote:
Francois,
Two Q:s:
First, when you got a very basic sample app like a DOM-based hangman going, feel free to share code
Second, how is the type mapping - fixnums, flonums, bignums all wrapped to JS double, Scheme string and vector to JS string and vector?
(1) The data representation is customizable with two options for each type. There is a "natural" mapping (Scheme number -> JS number, Scheme vector -> JS array, etc) and one which uses classes.
(2) Note that the natural mapping may violate some of the Scheme semantics. For example, JS numbers don't carry the concept of exactness, and JS strings are immutable whereas Scheme strings are mutable. On the other hand the natural mapping may be useful in cases where some details of the Scheme semantics are not important (the generated JS code is easier to read/understand, the generated JS code can be more easily interfaced with existing JS libraries, etc). A mapping which respects the Scheme semantics is:
- Fixnums are mapped to JS numbers
- Booleans are mapped to JS booleans
- Symbols are mapped to JS strings
- Vectors are mapped to JS arrays
- Procedures are mapped to JS functions
- Strings, characters, flonums (and other numbers), and pairs are implemented with JS classes
Wait, first(1) you suggest there's a customizable option, and then second(2) you outline a holistic approach for Scheme-JS type mapping.
How is it? And if anything is configurable, where's the switch?
The source code of the universal backend was written to support both data representations, selectable for each type. So it is "customizable" in the sense that you simply have to change a single definition in _t-univ.scm to select the representation for each Scheme type (see the top of the file, e.g. (define (univ-flonum-representation ctx) 'class) ). It would be possible to expose these settings to the user, although it is not clear what is the best way to do that. Perhaps that could be part of the target's name, e.g.
gsc -target js file.scm gsc -target js-foo file.scm gsc -target js-bar file.scm
where foo and bar would indicate a particular choice of data representations that make some compromises (e.g. incomplete support of the Gambit specific features, lesser performance for some of the Gambit specific features, etc).
Unfortunately, this mapping makes it expensive to implement some Gambit specific operations, such as ##subtype, ##subtype-set! and
Didn't even hear about ##subtype* til now - Any use of JS turns out to be for some special purpose anyhow, so that there's some real corner case of Scheme execution in this environment that has a low performance is really completely cool.
The problem is that ##subtype, etc are used in the implementation of the Gambit runtime library. For example, ##subtype is used in the implementation of the procedure equal?-hash and some type predicates. So even if ##subtype is not used directly by users, some user callable procedures in the Gambit runtime library do use ##subtype.
##vector-ref.
Wait, why, can't this just be ordinarily expanded to JS' variable[index]?
Because ##vector-ref is used to access slots of many Gambit objects, not just vectors. For example the slot at index 0 of a symbol is the symbol's name, and the slot at index 0 of a structure is the type descriptor:
(##vector-ref 'foo 0)
"foo"
(##vector-ref (current-input-port) 0)
#<type #4 device-port>
If a Scheme datatype is represented using a JS class (for example a Scheme symbol is represented with a JS class with a "name" property), then (##vector-ref sym 0) if it is translated to sym[0] will not retrieve the name of the symbol. It would be possible to avoid this problem by using JS arrays to represent all memory allocated Gambit objects, but this would not be a natural translation (harder to understand the generated code, harder to write external JS code accessing the data representation, etc). Another approach would be to put some type dispatch logic in ##vector-ref to do the right thing with symbols, structures, etc, but this would slow down ##vector-ref.
Of course another approach would be to refactor the Gambit runtime system to replace such "unclean" uses of ##vector-ref by calls to type specific primitives. It is not clear how much work this would be (primitives have to be added to the compiler and the runtime system has to be refactored).
So it may be necessary to implement non immediate data (vectors, structures, etc) uniformly with classes when using the standard Gambit runtime system.
I haven't explored this aspect much, but it may be that supporting all the Gambit primitives isn't practical.
(same note as above re special purpose here)
A solution may be to have two modes (Standard Scheme and Gambit Scheme) so the user can select the best mode for his needs.
What would the difference be approx here?
Hard to say. The Gambit runtime system has to be examined carefully to see what would break for a particular choice of representation. For example, in a mode where number exactness is not supported (allowing all Scheme numbers to be represented with the JS "number" type, except for complex numbers), there will be a major impact on the numerical functions in lib/_num.scm . Some (how many?) will have to be modified to be consistent with the fact that there is no exactness. Should the lack of exactness be handled the same as treating all numbers as inexact numbers? etc.
Marc
Gambit-list mailing list Gambit-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/gambit-list
On Nov 21, 2013, at 11:01 AM, Francois Magnan magnan@categoricaldesign.com wrote:
Hi,
Sorry to disturb you again on this. I continued my experiments with “-target js”. I reimplemented quasiquote, unquote… and I stumbled on a strange bug that mystifies me: now “cons” function is not known. I have sucessfully compiled programs using cons with -target js many times but now it just won’t compile it correctly. Here is a simplified version of the code just to illustrate what I see:
If you want to pass the cons procedure to fold, you need the cons global variable to be bound to the cons procedure. This binding of the cons global variable to the cons procedure is the responsibility of the Gambit runtime system (which as you know is not complete at this point). So you have to explicitly do the binding with:
(define (cons x y) (##cons x y)) (define (car x) (##car x)) (define (cdr x) (##cdr x)) (define (set-car! x y) (##set-car! x y)) (define (set-cdr! x y) (##set-cdr! x y))
This works because the call to ##cons is inlined by the compiler.
Alternatively, you could do:
(fold (lambda (x y) (cons x y)) ...)
This works because the call to cons is transformed to a call to ##cons by the compiler.
Note that it is up to the runtime system to do error checking, so you may want to do:
(define (car x) (if (##pair? x) (##car x) (error "oops")))
Marc