diff --git a/NOTES b/NOTES index b78af7f..6b0d027 100644 --- a/NOTES +++ b/NOTES @@ -683,4 +683,64 @@ circularity between helpers and types. The parameters I'm using to control bounce are too high for Firefox, -leading it to raise the dialog about an out of control jva process. Not good. \ No newline at end of file +leading it to raise the dialog about an out of control jva process. Not good. + +---------------------------------------------------------------------- + +Working out the type mappings between values in Racket and values in JavaScript + + + + Racket JavaScript Switched over? + + + number jsnums.SchemeNumber yes + immutable strings JavaScript string + mutable strings types.Str + + vector + + + regular-expressions + path + bytes + box + placeholder + character + symbol + keyword + + pair yes + empty yes + + eq-hashtable + equal-hashtable + + struct-type + struct + + path + + continuation-mark-set + + primitive-procedure + closure + case-lambda + + + undefined undefined + void plt.runtime.VOID + +---------------------------------------------------------------------- + +I should add the functions: + + get-viewport-width + get-viewport-height + +Other notes from Shriram: too slow, inconsistent positioning from jsworld. + + +---------------------------------------------------------------------- + +Added base as the default planet language. \ No newline at end of file diff --git a/README b/README index e24389e..eec16ac 100644 --- a/README +++ b/README @@ -3,6 +3,7 @@ Whalesong: a compiler from Racket to JavaScript. Danny Yoo (dyoo@cs.wpi.edu) + ====================================================================== @@ -16,9 +17,25 @@ amount of time. Example usage -[FIXME] +Create a simple, standalong executable of your program. At the +moment, the program must be written in the base language of whalesong. +(This restriction currently prevents arbitrary racket/base programs +from compiling, and we'll be working to remove this restriction.) + + $ cat hello.rkt + #lang planet dyoo/whalesong + (display "hello world") + (newline) + + $ ./whalesong.rkt build hello.rkt + + $ ls -l hello.xhtml + -rw-rw-r-- 1 dyoo nogroup 692213 Jun 7 18:00 hello.xhtml + + +[FIXME: add more examples] ====================================================================== diff --git a/compiler/compiler.rkt b/compiler/compiler.rkt index 867e639..d57af6e 100644 --- a/compiler/compiler.rkt +++ b/compiler/compiler.rkt @@ -321,7 +321,7 @@ ;; that has not yet been invoked. ;; fixme: This also needs to generate code for the requires and provides. (match mod - [(struct Module (name path prefix requires code)) + [(struct Module (name path prefix requires provides code)) (let*: ([after-module-body (make-label 'afterModuleBody)] [module-entry (make-label 'module-entry)] [names : (Listof (U False Symbol GlobalBucket ModuleVariable)) @@ -2156,6 +2156,7 @@ (Module-path exp) (Module-prefix exp) (Module-requires exp) + (Module-provides exp) (adjust-expression-depth (Module-code exp) n (add1 skip)))] [(Constant? exp) diff --git a/compiler/expression-structs.rkt b/compiler/expression-structs.rkt index 7407ade..66f2c13 100644 --- a/compiler/expression-structs.rkt +++ b/compiler/expression-structs.rkt @@ -37,10 +37,18 @@ [path : ModuleLocator] [prefix : Prefix] [requires : (Listof ModuleLocator)] + [provides : (Listof ModuleProvide)] [code : Expression]) #:transparent) +(define-struct: ModuleProvide ([internal-name : Symbol] + [external-name : Symbol] + [source : ModuleLocator]) + #:transparent) + + + (define-struct: Top ([prefix : Prefix] [code : Expression]) #:transparent) diff --git a/compiler/il-structs.rkt b/compiler/il-structs.rkt index e038cbb..515da30 100644 --- a/compiler/il-structs.rkt +++ b/compiler/il-structs.rkt @@ -334,10 +334,10 @@ #:transparent) ;; Check the closure procedure value in 'proc and make sure it can accept the -;; # of arguments (stored as a number in the val register.). -(define-struct: CheckClosureArity! ([arity : OpArg]) +;; # of arguments (stored as a number in the argcount register.). +(define-struct: CheckClosureArity! ([num-args : OpArg]) #:transparent) -(define-struct: CheckPrimitiveArity! ([arity : OpArg]) +(define-struct: CheckPrimitiveArity! ([num-args : OpArg]) #:transparent) diff --git a/examples/alert.rkt b/examples/alert.rkt new file mode 100644 index 0000000..367abd0 --- /dev/null +++ b/examples/alert.rkt @@ -0,0 +1,3 @@ +#lang planet dyoo/whalesong +(require (planet dyoo/whalesong/js)) +(alert "hello world") diff --git a/examples/dom-play.rkt b/examples/dom-play.rkt new file mode 100644 index 0000000..6e5c6b7 --- /dev/null +++ b/examples/dom-play.rkt @@ -0,0 +1,37 @@ +#lang planet dyoo/whalesong + +(require (planet dyoo/whalesong/js)) + + +;; insert-break: -> void +(define (insert-break) + (call ($ "
") "appendTo" body) + (void)) + + +(define (write-message msg) + (void (call (call (call ($ "") "text" msg) + "css" "white-space" "pre") + "appendTo" + body))) + + + +;; Set the background green. +(void (call body "css" "background-color" "lightgreen")) +(void (call ($ "

Hello World

") "appendTo" body)) +(write-message "Hello, this is a test!") +(insert-break) +(let loop ([i 0]) + (cond + [(= i 10) + (void)] + [else + (write-message "iteration ") (write-message i) + (insert-break) + (loop (add1 i))])) + +(write-message "viewport-width: ") (write-message (viewport-width)) +(insert-break) +(write-message "viewport-height: ") (write-message (viewport-height)) +(insert-break) \ No newline at end of file diff --git a/examples/hello.rkt b/examples/hello.rkt new file mode 100644 index 0000000..3f39caa --- /dev/null +++ b/examples/hello.rkt @@ -0,0 +1,4 @@ +#lang planet dyoo/whalesong + +(display "hello world") +(newline) \ No newline at end of file diff --git a/examples/simple-world-program.rkt b/examples/simple-world-program.rkt new file mode 100644 index 0000000..3405f84 --- /dev/null +++ b/examples/simple-world-program.rkt @@ -0,0 +1,9 @@ +#lang planet dyoo/whalesong +(require (planet dyoo/whalesong/world)) + +(display "hello again") +(newline) + +(is-color? "red") +(is-color? "blue") +(is-color? 42) \ No newline at end of file diff --git a/examples/window-size.rkt b/examples/window-size.rkt new file mode 100644 index 0000000..8475a84 --- /dev/null +++ b/examples/window-size.rkt @@ -0,0 +1,7 @@ +#lang planet dyoo/whalesong + +(when (in-javascript-context?) + (viewport-width)) + +(when (in-javascript-context?) + (viewport-height)) diff --git a/js-assembler/assemble-helpers.rkt b/js-assembler/assemble-helpers.rkt index 444078a..76911cd 100644 --- a/js-assembler/assemble-helpers.rkt +++ b/js-assembler/assemble-helpers.rkt @@ -101,13 +101,13 @@ (cond [(symbol? val) (format "~s" (symbol->string val))] [(pair? val) - (format "[~a, ~a]" + (format "RUNTIME.makePair(~a, ~a)" (loop (car val)) (loop (cdr val)))] [(boolean? val) (if val "true" "false")] [(void? val) - "null"] + "RUNTIME.VOID"] [(empty? val) (format "RUNTIME.NULL")] [(number? val) @@ -122,7 +122,7 @@ [(empty? vals) "RUNTIME.NULL"] [else - (format "[~a, ~a]" (first vals) (loop (rest vals)))]))) + (format "RUNTIME.makePair(~a, ~a)" (first vals) (loop (rest vals)))]))) diff --git a/js-assembler/assemble-open-coded.rkt b/js-assembler/assemble-open-coded.rkt index f7aeae1..d6b6123 100644 --- a/js-assembler/assemble-open-coded.rkt +++ b/js-assembler/assemble-open-coded.rkt @@ -68,26 +68,21 @@ [(>=) (assemble-boolean-chain "jsnums.greaterThanOrEqual" checked-operands)] - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - [(cons) - (format "[~a, ~a]" (first checked-operands) (second checked-operands))] + (format "RUNTIME.makePair(~a, ~a)" + (first checked-operands) + (second checked-operands))] [(car) - (format "(~a)[0]" (first checked-operands))] + (format "(~a).first" (first checked-operands))] [(cdr) - (format "(~a)[1]" (first checked-operands))] + (format "(~a).rest" (first checked-operands))] [(list) (let loop ([checked-operands checked-operands]) - (cond - [(empty? checked-operands) - "RUNTIME.NULL"] - [else - (format "[~a,~a]" (first checked-operands) (loop (rest checked-operands)))]))] + (assemble-listof-assembled-values checked-operands))] [(null?) (format "(~a === RUNTIME.NULL)" (first checked-operands))] @@ -151,17 +146,15 @@ (let: ([test-string : String (case domain [(number) - (format "(jsnums.isSchemeNumber(~a))" + (format "jsnums.isSchemeNumber(~a)" operand-string)] [(string) (format "(typeof(~a) === 'string')" operand-string)] [(list) - (format "(~a === [] || (typeof(~a) === 'object' && (~a).length === 2))" - operand-string operand-string operand-string)] + (format "RUNTIME.isList(~a)" operand-string)] [(pair) - (format "(typeof(~a) === 'object' && (~a).length === 2)" - operand-string operand-string)] + (format "RUNTIME.isPair(~a)" operand-string)] [(box) (format "(typeof(~a) === 'object' && (~a).length === 1)" operand-string operand-string)])]) diff --git a/js-assembler/assemble-perform-statement.rkt b/js-assembler/assemble-perform-statement.rkt index 5c76afd..01531c8 100644 --- a/js-assembler/assemble-perform-statement.rkt +++ b/js-assembler/assemble-perform-statement.rkt @@ -27,13 +27,14 @@ RUNTIME.raiseOperatorIsNotClosure(MACHINE, MACHINE.proc); } if (! RUNTIME.isArityMatching(MACHINE.proc.arity, ~a)) { - RUNTIME.raiseArityMismatchError(MACHINE.proc, + RUNTIME.raiseArityMismatchError(MACHINE, + MACHINE.proc, MACHINE.proc.arity, ~a); } EOF - (assemble-oparg (CheckClosureArity!-arity op)) - (assemble-oparg (CheckClosureArity!-arity op)))] + (assemble-oparg (CheckClosureArity!-num-args op)) + (assemble-oparg (CheckClosureArity!-num-args op)))] [(CheckPrimitiveArity!? op) @@ -42,13 +43,14 @@ EOF RUNTIME.raiseOperatorIsNotPrimitiveProcedure(MACHINE, MACHINE.proc); } if (! RUNTIME.isArityMatching(MACHINE.proc.arity, ~a)) { - RUNTIME.raiseArityMismatchError(MACHINE.proc, + RUNTIME.raiseArityMismatchError(MACHINE, + MACHINE.proc, MACHINE.proc.arity, ~a); } EOF - (assemble-oparg (CheckPrimitiveArity!-arity op)) - (assemble-oparg (CheckPrimitiveArity!-arity op)))] + (assemble-oparg (CheckPrimitiveArity!-num-args op)) + (assemble-oparg (CheckPrimitiveArity!-num-args op)))] [(ExtendEnvironment/Prefix!? op) diff --git a/js-assembler/assemble.rkt b/js-assembler/assemble.rkt index fdfde55..ffe7512 100644 --- a/js-assembler/assemble.rkt +++ b/js-assembler/assemble.rkt @@ -186,7 +186,7 @@ EOF "") (cond [(DebugPrint? stmt) - (format "MACHINE.params.currentOutputPort.write(MACHINE, ~a);" (assemble-oparg (DebugPrint-value stmt)))] + (format "MACHINE.params.currentOutputPort.writeDomNode(MACHINE, $('').text(~a));" (assemble-oparg (DebugPrint-value stmt)))] [(AssignImmediateStatement? stmt) (let: ([t : String (assemble-target (AssignImmediateStatement-target stmt))] [v : OpArg (AssignImmediateStatement-value stmt)]) diff --git a/js-assembler/package.rkt b/js-assembler/package.rkt index 07393a5..dd4d853 100644 --- a/js-assembler/package.rkt +++ b/js-assembler/package.rkt @@ -2,8 +2,14 @@ (require "assemble.rkt" "quote-cdata.rkt" - "../make.rkt" - "../make-structs.rkt" + "../make/make.rkt" + "../make/make-structs.rkt" + "../parameters.rkt" + "../compiler/expression-structs.rkt" + "../parser/path-rewriter.rkt" + "../parser/parse-bytecode.rkt" + racket/match + (prefix-in query: "../lang/js/query.rkt") (planet dyoo/closure-compile:1:1) (prefix-in runtime: "get-runtime.rkt") (prefix-in racket: racket/base)) @@ -21,52 +27,172 @@ write-runtime) + +;; notify: string (listof any)* -> void +;; Print out log message during the build process. +(define (notify msg . args) + (displayln (apply format msg args))) + + + + + +(define-struct js-impl (name ;; symbol + real-path ;; path + src ;; string + ) + #:transparent) + + ;; Packager: produce single .js files to be included to execute a ;; program. (define (package-anonymous source-code - #:should-follow? should-follow? + #:should-follow-children? should-follow? #:output-port op) (fprintf op "(function() {\n") (package source-code - #:should-follow? should-follow? + #:should-follow-children? should-follow? #:output-port op) (fprintf op " return invoke; })\n")) +;; source-is-javascript-module?: Source -> boolean +;; Returns true if the source looks like a Javascript-implemented module. +(define (source-is-javascript-module? src) + (cond + [(StatementsSource? src) + #f] + [(MainModuleSource? src) + (source-is-javascript-module? (MainModuleSource-source src))] + [(ModuleSource? src) + (query:has-javascript-implementation? `(file ,(path->string (ModuleSource-path src))))] + [(SexpSource? src) + #f] + [(UninterpretedSource? src) + #f])) + + +;; get-javascript-implementation: source -> UninterpretedSource +(define (get-javascript-implementation src) + + (define (get-provided-name-code bytecode) + (match bytecode + [(struct Top [_ (struct Module (name path prefix requires provides code))]) + (apply string-append + (map (lambda (p) + (format "modrec.namespace[~s] = exports[~s];\n" + (symbol->string (ModuleProvide-internal-name p)) + (symbol->string (ModuleProvide-external-name p)))) + provides))] + [else + ""])) + (cond + [(StatementsSource? src) + (error 'get-javascript-implementation src)] + [(MainModuleSource? src) + (get-javascript-implementation (MainModuleSource-source src))] + [(ModuleSource? src) + (let ([name (rewrite-path (ModuleSource-path src))] + [text (query:query `(file ,(path->string (ModuleSource-path src))))] + [bytecode (parse-bytecode (ModuleSource-path src))]) + (make-UninterpretedSource + (format " +MACHINE.modules[~s] = + new plt.runtime.ModuleRecord(~s, + function(MACHINE) { + if(--MACHINE.callsBeforeTrampoline<0) { throw arguments.callee; } + var modrec = MACHINE.modules[~s]; + var exports = {}; + modrec.isInvoked = true; + (function(MACHINE, RUNTIME, EXPORTS){~a})(MACHINE, plt.runtime, exports); + // FIXME: we need to inject the namespace with the values defined in exports. + ~a + return MACHINE.control.pop().label(MACHINE); + }); +" + (symbol->string name) + (symbol->string name) + (symbol->string name) + text + (get-provided-name-code bytecode))))] + [(SexpSource? src) + (error 'get-javascript-implementation)] + [(UninterpretedSource? src) + (error 'get-javascript-implementation)])) + + + + + + + ;; package: Source (path -> boolean) output-port -> void -;; Compile package for the given source program. should-follow? -;; indicates whether we should continue following module paths. +;; Compile package for the given source program. +;; +;; should-follow-children? indicates whether we should continue +;; following module paths of a source's dependencies. ;; ;; The generated output defines a function called 'invoke' with ;; four arguments (MACHINE, SUCCESS, FAIL, PARAMS). When called, it will ;; execute the code to either run standalone expressions or ;; load in modules. (define (package source-code - #:should-follow? should-follow? + #:should-follow-children? should-follow? #:output-port op) + + + ;; wrap-source: source -> source + ;; Translate all JavaScript-implemented sources into uninterpreted sources; + ;; we'll leave its interpretation to on-visit-src. + (define (wrap-source src) + (cond + [(source-is-javascript-module? src) + (get-javascript-implementation src)] + [else + src])) + + + (define (on-visit-src src ast stmts) + (cond + [(UninterpretedSource? src) + (fprintf op (UninterpretedSource-datum src))] + [else + (assemble/write-invoke stmts op) + (fprintf op "(MACHINE, function() { ")])) + + + (define (after-visit-src src ast stmts) + (cond + [(UninterpretedSource? src) + (void)] + [else + (fprintf op " }, FAIL, PARAMS);")])) + + + (define (on-last-src) + (fprintf op "SUCCESS();")) + + (define packaging-configuration (make-Configuration - + wrap-source + should-follow? ;; on - (lambda (src ast stmts) - (assemble/write-invoke stmts op) - (fprintf op "(MACHINE, function() { ")) + on-visit-src ;; after - (lambda (src ast stmts) - (fprintf op " }, FAIL, PARAMS);")) - + after-visit-src + ;; last - (lambda () - (fprintf op "SUCCESS();")))) + on-last-src)) (fprintf op "var invoke = (function(MACHINE, SUCCESS, FAIL, PARAMS) {") @@ -90,10 +216,15 @@ ;; write-runtime: output-port -> void (define (write-runtime op) + + (define (wrap-source src) src) (let ([packaging-configuration (make-Configuration - ;; should-follow? - (lambda (src p) #t) + + wrap-source + + ;; should-follow-children? + (lambda (src) #t) ;; on (lambda (src ast stmts) (assemble/write-invoke stmts op) @@ -114,12 +245,19 @@ (make (list only-bootstrapped-code) packaging-configuration) (fprintf op "})(plt.runtime.currentMachine,\nfunction(){ plt.runtime.setReadyTrue(); },\nfunction(){},\n{});\n"))) - + + +(define (compress x) + (if (current-compress-javascript?) + (closure-compile x) + x)) + + (define *the-runtime* (let ([buffer (open-output-string)]) (write-runtime buffer) - (closure-compile + (compress (get-output-string buffer)))) @@ -150,9 +288,9 @@ EOF (define (get-code source-code) (let ([buffer (open-output-string)]) (package source-code - #:should-follow? (lambda (src p) #t) + #:should-follow-children? (lambda (src) #t) #:output-port buffer) - (closure-compile + (compress (get-output-string buffer)))) @@ -161,14 +299,14 @@ EOF (define (get-standalone-code source-code) (let ([buffer (open-output-string)]) (write-standalone-code source-code buffer) - (closure-compile + (compress (get-output-string buffer)))) ;; write-standalone-code: source output-port -> void (define (write-standalone-code source-code op) (package-anonymous source-code - #:should-follow? (lambda (src p) #t) + #:should-follow-children? (lambda (src) #t) #:output-port op) (fprintf op "()(plt.runtime.currentMachine, function() {}, function() {}, {});\n")) @@ -193,6 +331,8 @@ var invokeMainModule = function() { if (console && console.log) { console.log(e.stack || e); } + MACHINE.params.currentErrorDisplayer( + MACHINE, $(plt.helpers.toDomNode(e.stack || e)).css('color', 'red')); })}, function() { // On module loading failure diff --git a/js-assembler/runtime-src/helpers.js b/js-assembler/runtime-src/helpers.js index 130b493..520659e 100644 --- a/js-assembler/runtime-src/helpers.js +++ b/js-assembler/runtime-src/helpers.js @@ -549,8 +549,8 @@ if (! this['plt']) { this['plt'] = {}; } var makeLowLevelEqHash = function() { - return new _Hashtable(function(x) { return getEqHashCode(x); }, - function(x, y) { return x === y; }); + return new Hashtable(function(x) { return getEqHashCode(x); }, + function(x, y) { return x === y; }); }; @@ -570,14 +570,15 @@ if (! this['plt']) { this['plt'] = {}; } if (! cache) { cache = makeLowLevelEqHash(); } - + if (x === null) { + return "null"; + } if (typeof(x) === 'object') { if (cache.containsKey(x)) { return "..."; } } - - if (x == undefined || x == null) { + if (x == undefined) { return "#"; } if (typeof(x) == 'string') { @@ -606,12 +607,14 @@ if (! this['plt']) { this['plt'] = {}; } if (! cache) { cache = makeLowLevelEqHash(); } + if (x === null) { + return "null"; + } if (typeof(x) === 'object') { if (cache.containsKey(x)) { return "..."; } } - if (x == undefined || x == null) { return "#"; } @@ -635,24 +638,74 @@ if (! this['plt']) { this['plt'] = {}; } }; - // toDomNode: scheme-value -> dom-node - var toDomNode = function(x, cache) { - if (! cache) { - cache = makeLowLevelEqHash(); + + + var ToDomNodeParameters = function(params) { + if (! params) { params = {}; } + this.cache = makeLowLevelEqHash(); + for (var k in params) { + if (params.hasOwnProperty(k)) { + this[k] = params[k]; + } } + this.objectCounter = 0; + }; + + // getMode: -> (U "print" "display" "write") + ToDomNodeParameters.prototype.getMode = function() { + if (this.mode) { + return this.mode; + } + return 'print'; + }; + + ToDomNodeParameters.prototype.containsKey = function(x) { + return this.cache.containsKey(x); + }; + + ToDomNodeParameters.prototype.get = function(x) { + return this.cache.get(x); + }; + + ToDomNodeParameters.prototype.remove = function(x) { + return this.cache.remove(x); + }; + + ToDomNodeParameters.prototype.put = function(x) { + this.objectCounter++; + return this.cache.put(x, this.objectCounter); + }; + + + // toDomNode: scheme-value -> dom-node + var toDomNode = function(x, params) { + if (params === 'write') { + params = new ToDomNodeParameters({'mode' : 'write'}); + } else if (params === 'print') { + params = new ToDomNodeParameters({'mode' : 'print'}); + } else if (params === 'display') { + params = new ToDomNodeParameters({'mode' : 'display'}); + } else { + params = params || new ToDomNodeParameters({'mode' : 'display'}); + } if (jsnums.isSchemeNumber(x)) { - return numberToDomNode(x); + return numberToDomNode(x, params); + } + + if (x === null) { + var node = document.createElement("span"); + node.appendChild(document.createTextNode("null")); + return node; } if (typeof(x) == 'object') { - if (cache.containsKey(x)) { + if (params.containsKey(x)) { var node = document.createElement("span"); - node.appendChild(document.createTextNode("...")); + node.appendChild(document.createTextNode("#" + params.get(x))); return node; } } - if (x == undefined || x == null) { var node = document.createElement("span"); node.appendChild(document.createTextNode("#")); @@ -661,11 +714,17 @@ if (! this['plt']) { this['plt'] = {}; } if (typeof(x) == 'string') { var wrapper = document.createElement("span"); - wrapper.style["white-space"] = "pre"; - var node = document.createTextNode(toWrittenString(x)); + wrapper.style["white-space"] = "pre"; + var node; + if (params.getMode() === 'write' || params.getMode() === 'print') { + node = document.createTextNode(toWrittenString(x)); + } else { + node = document.createTextNode(toDisplayedString(x)); + } wrapper.appendChild(node); return wrapper; } + if (typeof(x) != 'object' && typeof(x) != 'function') { var node = document.createElement("span"); node.appendChild(document.createTextNode(x.toString())); @@ -676,22 +735,23 @@ if (! this['plt']) { this['plt'] = {}; } if (x.nodeType) { returnVal = x; } else if (typeof(x.toDomNode) !== 'undefined') { - returnVal = x.toDomNode(cache); - } else if (typeof(x.toWrittenString) !== 'undefined') { - + returnVal = x.toDomNode(params); + } else if (params.getMode() === 'write' && + typeof(x.toWrittenString) !== 'undefined') { var node = document.createElement("span"); - node.appendChild(document.createTextNode(x.toWrittenString(cache))); + node.appendChild(document.createTextNode(x.toWrittenString(params))); returnVal = node; - } else if (typeof(x.toDisplayedString) !== 'undefined') { + } else if (params.getMode() === 'display' && + typeof(x.toDisplayedString) !== 'undefined') { var node = document.createElement("span"); - node.appendChild(document.createTextNode(x.toDisplayedString(cache))); + node.appendChild(document.createTextNode(x.toDisplayedString(params))); returnVal = node; } else { var node = document.createElement("span"); node.appendChild(document.createTextNode(x.toString())); returnVal = node; } - cache.remove(x); + params.remove(x); return returnVal; }; @@ -699,7 +759,7 @@ if (! this['plt']) { this['plt'] = {}; } // numberToDomNode: jsnum -> dom // Given a jsnum, produces a dom-node representation. - var numberToDomNode = function(n) { + var numberToDomNode = function(n, params) { var node; if (jsnums.isExact(n)) { if (jsnums.isInteger(n)) { @@ -726,16 +786,49 @@ if (! this['plt']) { this['plt'] = {}; } // rationalToDomNode: rational -> dom-node var rationalToDomNode = function(n) { - var node = document.createElement("span"); + var repeatingDecimalNode = document.createElement("span"); var chunks = jsnums.toRepeatingDecimal(jsnums.numerator(n), - jsnums.denominator(n)); - node.appendChild(document.createTextNode(chunks[0] + '.')) - node.appendChild(document.createTextNode(chunks[1])); - var overlineSpan = document.createElement("span"); - overlineSpan.style.textDecoration = 'overline'; - overlineSpan.appendChild(document.createTextNode(chunks[2])); - node.appendChild(overlineSpan); - return node; + jsnums.denominator(n), + {limit: 25}); + repeatingDecimalNode.appendChild(document.createTextNode(chunks[0] + '.')) + repeatingDecimalNode.appendChild(document.createTextNode(chunks[1])); + if (chunks[2] === '...') { + repeatingDecimalNode.appendChild( + document.createTextNode(chunks[2])); + } else if (chunks[2] !== '0') { + var overlineSpan = document.createElement("span"); + overlineSpan.style.textDecoration = 'overline'; + overlineSpan.appendChild(document.createTextNode(chunks[2])); + repeatingDecimalNode.appendChild(overlineSpan); + } + + + var fractionalNode = document.createElement("span"); + var numeratorNode = document.createElement("sup"); + numeratorNode.appendChild(document.createTextNode(String(jsnums.numerator(n)))); + var denominatorNode = document.createElement("sub"); + denominatorNode.appendChild(document.createTextNode(String(jsnums.denominator(n)))); + fractionalNode.appendChild(numeratorNode); + fractionalNode.appendChild(document.createTextNode("/")); + fractionalNode.appendChild(denominatorNode); + + + var numberNode = document.createElement("span"); + numberNode.appendChild(repeatingDecimalNode); + numberNode.appendChild(fractionalNode); + fractionalNode.style['display'] = 'none'; + + var showingRepeating = true; + + numberNode.onclick = function(e) { + showingRepeating = !showingRepeating; + repeatingDecimalNode.style['display'] = + (showingRepeating ? 'inline' : 'none') + fractionalNode.style['display'] = + (!showingRepeating ? 'inline' : 'none') + }; + numberNode.style['cursor'] = 'pointer'; + return numberNode; } @@ -743,6 +836,62 @@ if (! this['plt']) { this['plt'] = {}; } + var escapeString = function(s) { + return '"' + replaceUnprintableStringChars(s) + '"'; + }; + + var replaceUnprintableStringChars = function(s) { + var ret = []; + for (var i = 0; i < s.length; i++) { + var val = s.charCodeAt(i); + switch(val) { + case 7: ret.push('\\a'); break; + case 8: ret.push('\\b'); break; + case 9: ret.push('\\t'); break; + case 10: ret.push('\\n'); break; + case 11: ret.push('\\v'); break; + case 12: ret.push('\\f'); break; + case 13: ret.push('\\r'); break; + case 34: ret.push('\\"'); break; + case 92: ret.push('\\\\'); break; + default: if (val >= 32 && val <= 126) { + ret.push( s.charAt(i) ); + } + else { + var numStr = val.toString(16).toUpperCase(); + while (numStr.length < 4) { + numStr = '0' + numStr; + } + ret.push('\\u' + numStr); + } + break; + } + } + return ret.join(''); + }; + + + + + + + // clone: object -> object + // Copies an object. The new object should respond like the old + // object, including to things like instanceof + var clone = function(obj) { + var C = function() {} + C.prototype = obj; + var c = new C(); + for (property in obj) { + if (obj.hasOwnProperty(property)) { + c[property] = obj[property]; + } + } + return c; + }; + + + @@ -783,15 +932,16 @@ if (! this['plt']) { this['plt'] = {}; } helpers.makeLowLevelEqHash = makeLowLevelEqHash; helpers.heir = heir; - - - - + helpers.escapeString = escapeString; helpers.toWrittenString = toWrittenString; helpers.toDisplayedString = toDisplayedString; - helpers.toDomNode = toDomNode; + helpers.toDomNode = toDomNode; + helpers.ToDomNodeParameters = ToDomNodeParameters; + + helpers.clone = clone; + scope.link.announceReady('helpers'); })(this['plt']); diff --git a/js-assembler/runtime-src/primitives.js b/js-assembler/runtime-src/primitives.js index 208b799..207c522 100644 --- a/js-assembler/runtime-src/primitives.js +++ b/js-assembler/runtime-src/primitives.js @@ -195,7 +195,7 @@ That way, we can do a simple grep. var length = function(lst) { checkList(lst, 'length', 1, [lst]); var ret = 0; - for (; !lst.isEmpty(); lst = lst.rest()) { + for (; !lst.isEmpty(); lst = lst.rest) { ret = ret+1; } return ret; @@ -224,8 +224,8 @@ That way, we can do a simple grep. var fArgs = []; var argsRest = []; for (var i = 0; i < args.length; i++) { - fArgs.push(args[i].first()); - argsRest.push(args[i].rest()); + fArgs.push(args[i].first); + argsRest.push(args[i].rest); } fArgs.push(acc); return CALL(f, fArgs, @@ -246,20 +246,20 @@ That way, we can do a simple grep. } var compYes = new PrimProc('compYes', 1, false, false, - function(x) { return CALL(comp, [x, lst.first()], id); }); + function(x) { return CALL(comp, [x, lst.first], id); }); var compNo = new PrimProc('compNo', 1, false, false, - function(x) { return CALL(comp, [x, lst.first()], + function(x) { return CALL(comp, [x, lst.first], function(res) { return !res; }); }); return CALL(PRIMITIVES['filter'], - [compYes, lst.rest()], + [compYes, lst.rest], quicksortHelp(function(sorted1) { return CALL(PRIMITIVES['filter'], - [compNo, lst.rest()], + [compNo, lst.rest], quicksortHelp(function(sorted2) { return k( append([sorted1, - types.list([lst.first()]), + types.list([lst.first]), sorted2]) ); })); })); @@ -487,7 +487,7 @@ That way, we can do a simple grep. var isAssocList = function(x) { - return isPair(x) && isPair(x.rest()) && isEmpty(x.rest().rest()); + return isPair(x) && isPair(x.rest) && isEmpty(x.rest.rest); }; @@ -725,8 +725,8 @@ That way, we can do a simple grep. var argsFirst = []; var argsRest = []; for (var i = 0; i < args.length; i++) { - argsFirst.push(args[i].first()); - argsRest.push(args[i].rest()); + argsFirst.push(args[i].first); + argsRest.push(args[i].rest); } return CALL(f, argsFirst, @@ -1109,11 +1109,11 @@ That way, we can do a simple grep. return CALL(new PrimProc('', 1, false, false, function(args) { - return callWithValues(fList.first(), args); + return callWithValues(fList.first, args); }), [x], function(result) { - return composeHelp(result, fList.rest()); + return composeHelp(result, fList.rest); }); } return new PrimProc('', 0, true, false, @@ -2032,9 +2032,9 @@ That way, we can do a simple grep. var nextResult = parse(child); if (isString(nextResult) && !result.isEmpty() && - isString(result.first())) { - result = types.cons(result.first() + nextResult, - result.rest()); + isString(result.first)) { + result = types.cons(result.first + nextResult, + result.rest); } else { result = types.cons(nextResult, result); } @@ -2219,7 +2219,7 @@ That way, we can do a simple grep. false, false, function(lst) { check(lst, isPair, 'car', 'pair', 1); - return lst.first(); + return lst.first; }); PRIMITIVES['cdr'] = @@ -2228,7 +2228,7 @@ That way, we can do a simple grep. false, false, function (lst) { check(lst, isPair, 'cdr', 'pair', 1); - return lst.rest(); + return lst.rest; }); PRIMITIVES['caar'] = @@ -2236,9 +2236,9 @@ That way, we can do a simple grep. 1, false, false, function(lst) { - check(lst, function(x) { return (isPair(x) && isPair(x.first())); }, + check(lst, function(x) { return (isPair(x) && isPair(x.first)); }, 'caar', 'caarable value', 1); - return lst.first().first(); + return lst.first.first; }); PRIMITIVES['cadr'] = @@ -2246,9 +2246,9 @@ That way, we can do a simple grep. 1, false, false, function(lst) { - check(lst, function(x) { return isPair(x) && isPair(x.rest()); }, + check(lst, function(x) { return isPair(x) && isPair(x.rest); }, 'cadr', 'cadrable value', 1); - return lst.rest().first(); + return lst.rest.first; }); PRIMITIVES['cdar'] = @@ -2256,9 +2256,9 @@ That way, we can do a simple grep. 1, false, false, function(lst) { - check(lst, function(x) { return isPair(x) && isPair(x.first()); }, + check(lst, function(x) { return isPair(x) && isPair(x.first); }, 'cdar', 'cdarable value', 1); - return lst.first().rest(); + return lst.first.rest; }); PRIMITIVES['cddr'] = @@ -2266,9 +2266,9 @@ That way, we can do a simple grep. 1, false, false, function(lst) { - check(lst, function(x) { return isPair(x) && isPair(x.rest()); }, + check(lst, function(x) { return isPair(x) && isPair(x.rest); }, 'cddr', 'cddrable value', 1); - return lst.rest().rest(); + return lst.rest.rest; }); PRIMITIVES['caaar'] = @@ -2277,10 +2277,10 @@ That way, we can do a simple grep. false, false, function(lst) { check(lst, function(x) { return ( isPair(x) && - isPair(x.first()) && - isPair(x.first().first()) ); }, + isPair(x.first) && + isPair(x.first.first) ); }, 'caaar', 'caaarable value', 1); - return lst.first().first().first(); + return lst.first.first.first; }); PRIMITIVES['caadr'] = @@ -2289,10 +2289,10 @@ That way, we can do a simple grep. false, false, function(lst) { check(lst, function(x) { return ( isPair(x) && - isPair(x.rest()) && - isPair(x.rest().first()) ); }, + isPair(x.rest) && + isPair(x.rest.first) ); }, 'caadr', 'caadrable value', 1); - return lst.rest().first().first(); + return lst.rest.first.first; }); PRIMITIVES['cadar'] = @@ -2301,10 +2301,10 @@ That way, we can do a simple grep. false, false, function(lst) { check(lst, function(x) { return ( isPair(x) && - isPair(x.first()) && - isPair(x.first().rest()) ); }, + isPair(x.first) && + isPair(x.first.rest) ); }, 'cadar', 'cadarable value', 1); - return lst.first().rest().first(); + return lst.first.rest.first; }); PRIMITIVES['cdaar'] = @@ -2313,10 +2313,10 @@ That way, we can do a simple grep. false, false, function(lst) { check(lst, function(x) { return ( isPair(x) && - isPair(x.first()) && - isPair(x.first().first()) ); }, + isPair(x.first) && + isPair(x.first.first) ); }, 'cdaar', 'cdaarable value', 1); - return lst.first().first().rest(); + return lst.first.first.rest; }); PRIMITIVES['cdadr'] = @@ -2325,10 +2325,10 @@ That way, we can do a simple grep. false, false, function(lst) { check(lst, function(x) { return ( isPair(x) && - isPair(x.rest()) && - isPair(x.rest().first()) ); }, + isPair(x.rest) && + isPair(x.rest.first) ); }, 'cdadr', 'cdadrable value', 1); - return lst.rest().first().rest(); + return lst.rest.first.rest; }); PRIMITIVES['cddar'] = @@ -2337,10 +2337,10 @@ That way, we can do a simple grep. false, false, function(lst) { check(lst, function(x) { return ( isPair(x) && - isPair(x.first()) && - isPair(x.first().rest()) ); }, + isPair(x.first) && + isPair(x.first.rest) ); }, 'cddar', 'cddarable value', 1); - return lst.first().rest().rest(); + return lst.first.rest.rest; }); PRIMITIVES['caddr'] = @@ -2349,10 +2349,10 @@ That way, we can do a simple grep. false, false, function(lst) { check(lst, function(x) { return ( isPair(x) && - isPair(x.rest()) && - isPair(x.rest().rest()) ); }, + isPair(x.rest) && + isPair(x.rest.rest) ); }, 'caddr', 'caddrable value', 1); - return lst.rest().rest().first(); + return lst.rest.rest.first; }); PRIMITIVES['cdddr'] = @@ -2361,10 +2361,10 @@ That way, we can do a simple grep. false, false, function(lst) { check(lst, function(x) { return ( isPair(x) && - isPair(x.rest()) && - isPair(x.rest().rest()) ); }, + isPair(x.rest) && + isPair(x.rest.rest) ); }, 'cdddr', 'cdddrable value', 1); - return lst.rest().rest().rest(); + return lst.rest.rest.rest; }); PRIMITIVES['cadddr'] = @@ -2373,11 +2373,11 @@ That way, we can do a simple grep. false, false, function(lst) { check(lst, function(x) { return ( isPair(x) && - isPair(x.rest()) && - isPair(x.rest().rest()) && - isPair(x.rest().rest().rest()) ); }, + isPair(x.rest) && + isPair(x.rest.rest) && + isPair(x.rest.rest.rest) ); }, 'cadddr', 'cadddrable value', 1); - return lst.rest().rest().rest().first(); + return lst.rest.rest.rest.first; }); @@ -2388,7 +2388,7 @@ That way, we can do a simple grep. function(lst) { check(lst, function(x) { return isList(x) && !isEmpty(x); }, 'rest', 'non-empty list', 1); - return lst.rest(); + return lst.rest; }); PRIMITIVES['first'] = @@ -2398,7 +2398,7 @@ That way, we can do a simple grep. function(lst) { check(lst, function(x) { return isList(x) && !isEmpty(x); }, 'first', 'non-empty list', 1); - return lst.first(); + return lst.first; }); PRIMITIVES['second'] = @@ -2407,7 +2407,7 @@ That way, we can do a simple grep. false, false, function(lst) { checkListOfLength(lst, 2, 'second', 1); - return lst.rest().first(); + return lst.rest.first; }); PRIMITIVES['third'] = @@ -2416,7 +2416,7 @@ That way, we can do a simple grep. false, false, function(lst) { checkListOfLength(lst, 3, 'third', 1); - return lst.rest().rest().first(); + return lst.rest.rest.first; }); PRIMITIVES['fourth'] = @@ -2425,7 +2425,7 @@ That way, we can do a simple grep. false, false, function(lst) { checkListOfLength(lst, 4, 'fourth', 1); - return lst.rest().rest().rest().first(); + return lst.rest.rest.rest.first; }); PRIMITIVES['fifth'] = @@ -2434,7 +2434,7 @@ That way, we can do a simple grep. false, false, function(lst) { checkListOfLength(lst, 5, 'fifth', 1); - return lst.rest().rest().rest().rest().first(); + return lst.rest.rest.rest.rest.first; }); PRIMITIVES['sixth'] = @@ -2443,7 +2443,7 @@ That way, we can do a simple grep. false, false, function(lst) { checkListOfLength(lst, 6, 'sixth', 1); - return lst.rest().rest().rest().rest().rest().first(); + return lst.rest.rest.rest.rest.rest.first; }); PRIMITIVES['seventh'] = @@ -2453,7 +2453,7 @@ That way, we can do a simple grep. false, false, function(lst) { checkListOfLength(lst, 7, 'seventh', 1); - return lst.rest().rest().rest().rest().rest().rest().first(); + return lst.rest.rest.rest.rest.rest.rest.first; }); PRIMITIVES['eighth'] = @@ -2462,7 +2462,7 @@ That way, we can do a simple grep. false, false, function(lst) { checkListOfLength(lst, 8, 'eighth', 1); - return lst.rest().rest().rest().rest().rest().rest().rest().first(); + return lst.rest.rest.rest.rest.rest.rest.rest.first; }); @@ -2536,7 +2536,7 @@ That way, we can do a simple grep. helpers.toDisplayedString(origList)); raise( types.incompleteExn(types.exnFailContract, msg, []) ); } - lst = lst.rest(); + lst = lst.rest; } @@ -2548,7 +2548,7 @@ That way, we can do a simple grep. msg, []) ); } - return lst.first(); + return lst.first; }); PRIMITIVES['list-tail'] = @@ -2578,7 +2578,7 @@ That way, we can do a simple grep. helpers.toDisplayedString(origList)); raise( types.incompleteExn(types.exnFailContract, msg, []) ); } - lst = lst.rest(); + lst = lst.rest; } return lst; }); @@ -2622,8 +2622,8 @@ That way, we can do a simple grep. var argsFirst = []; var argsRest = []; for (var i = 0; i < args.length; i++) { - argsFirst.push(args[i].first()); - argsRest.push(args[i].rest()); + argsFirst.push(args[i].first); + argsRest.push(args[i].rest); } var result = CALL(f, argsFirst, function(result) { @@ -2658,8 +2658,8 @@ That way, we can do a simple grep. var argsFirst = []; var argsRest = []; for (var i = 0; i < args.length; i++) { - argsFirst.push(args[i].first()); - argsRest.push(args[i].rest()); + argsFirst.push(args[i].first); + argsRest.push(args[i].rest); } return CALL(f, argsFirst, @@ -2699,8 +2699,8 @@ That way, we can do a simple grep. var argsFirst = []; var argsRest = []; for (var i = 0; i < args.length; i++) { - argsFirst.push(args[i].first()); - argsRest.push(args[i].rest()); + argsFirst.push(args[i].first); + argsRest.push(args[i].rest); } return CALL(f, argsFirst, @@ -2734,10 +2734,10 @@ That way, we can do a simple grep. } while ( !lst.isEmpty() ) { - if ( isEq(item, lst.first()) ) { + if ( isEq(item, lst.first) ) { return lst; } - lst = lst.rest(); + lst = lst.rest; if (! isPair(lst) && lst !== types.EMPTY) { var msg = ('memq: not a proper list: ' + helpers.toDisplayedString(origList)); @@ -2764,10 +2764,10 @@ That way, we can do a simple grep. []) ); } while ( !lst.isEmpty() ) { - if ( isEqv(item, lst.first()) ) { + if ( isEqv(item, lst.first) ) { return lst; } - lst = lst.rest(); + lst = lst.rest; if (! isPair(lst) && lst !== types.EMPTY) { var msg = ('memv: not a proper list: ' + helpers.toDisplayedString(origList)); @@ -2795,10 +2795,10 @@ That way, we can do a simple grep. []) ); } while ( !lst.isEmpty() ) { - if ( isEqual(item, lst.first()) ) { + if ( isEqual(item, lst.first) ) { return lst; } - lst = lst.rest(); + lst = lst.rest; if (! isPair(lst) && lst !== types.EMPTY) { var msg = ('member: not a proper list: ' + @@ -2825,12 +2825,12 @@ That way, we can do a simple grep. return false; } - return CALL(f, [lst.first()], + return CALL(f, [lst.first], function(result) { if (result) { return lst; } - return memfHelp(lst.rest()); + return memfHelp(lst.rest); }); } return memfHelp(initList); @@ -2852,18 +2852,18 @@ That way, we can do a simple grep. []) ); } while ( !lst.isEmpty() ) { - if (! isPair(lst.first())) { + if (! isPair(lst.first)) { var msg = ('assq: non-pair found in list: ' + - helpers.toDisplayedString(lst.first()) +' in ' + + helpers.toDisplayedString(lst.first) +' in ' + helpers.toDisplayedString(origList)); raise( types.incompleteExn(types.exnFailContract, msg, []) ); } - if ( isEq(item, lst.first().first()) ) { - return lst.first(); + if ( isEq(item, lst.first.first) ) { + return lst.first; } - lst = lst.rest(); + lst = lst.rest; if (! isPair(lst) && lst !== types.EMPTY) { var msg = ('assq: not a proper list: ' + @@ -2892,18 +2892,18 @@ That way, we can do a simple grep. []) ); } while ( !lst.isEmpty() ) { - if (! isPair(lst.first())) { + if (! isPair(lst.first)) { var msg = ('assv: non-pair found in list: ' + - helpers.toDisplayedString(lst.first()) +' in ' + + helpers.toDisplayedString(lst.first) +' in ' + helpers.toDisplayedString(origList)); raise( types.incompleteExn(types.exnFailContract, msg, []) ); } - if ( isEqv(item, lst.first().first()) ) { - return lst.first(); + if ( isEqv(item, lst.first.first) ) { + return lst.first; } - lst = lst.rest(); + lst = lst.rest; if (! isPair(lst) && lst !== types.EMPTY) { var msg = ('assv: not a proper list: ' + helpers.toDisplayedString(origList)); @@ -2931,18 +2931,18 @@ That way, we can do a simple grep. []) ); } while ( !lst.isEmpty() ) { - if (! isPair(lst.first())) { + if (! isPair(lst.first)) { var msg = ('assoc: non-pair found in list: ' + - helpers.toDisplayedString(lst.first()) +' in ' + + helpers.toDisplayedString(lst.first) +' in ' + helpers.toDisplayedString(origList)); raise( types.incompleteExn(types.exnFailContract, msg, []) ); } - if ( isEqual(item, lst.first().first()) ) { - return lst.first(); + if ( isEqual(item, lst.first.first) ) { + return lst.first; } - lst = lst.rest(); + lst = lst.rest; if (! isPair(lst) && lst !== types.EMPTY) { var msg = ('assoc: not a proper list: ' + @@ -2965,11 +2965,11 @@ That way, we can do a simple grep. var originalLst = lst; var result = types.EMPTY; while ( !lst.isEmpty() ) { - if ( isEqual(item, lst.first()) ) { - return append([result.reverse(), lst.rest()]); + if ( isEqual(item, lst.first) ) { + return append([result.reverse(), lst.rest]); } else { - result = types.cons(lst.first(), result); - lst = lst.rest(); + result = types.cons(lst.first, result); + lst = lst.rest; } } return originalLst; @@ -2989,14 +2989,14 @@ That way, we can do a simple grep. return acc.reverse(); } - return CALL(f, [lst.first()], + return CALL(f, [lst.first], function(result) { if (result) { - return filterHelp(f, lst.rest(), - types.cons(lst.first(), acc)); + return filterHelp(f, lst.rest, + types.cons(lst.first, acc)); } else { - return filterHelp(f, lst.rest(), acc); + return filterHelp(f, lst.rest, acc); } }); } @@ -3055,22 +3055,22 @@ That way, we can do a simple grep. return curMaxElt; } - return CALL(f, [lst.first()], + return CALL(f, [lst.first], function(result) { check(result, isReal, 'argmax', 'procedure that returns real numbers', 1, args); if (jsnums.greaterThan(result, curMaxVal)) { - return argmaxHelp(lst.rest(), result, lst.first()); + return argmaxHelp(lst.rest, result, lst.first); } else { - return argmaxHelp(lst.rest(), curMaxVal, curMaxElt); + return argmaxHelp(lst.rest, curMaxVal, curMaxElt); } }); } - return CALL(f, [initList.first()], + return CALL(f, [initList.first], function(result) { check(result, isReal, 'argmax', 'procedure that returns real numbers', 1, args); - return argmaxHelp(initList.rest(), result, initList.first()); + return argmaxHelp(initList.rest, result, initList.first); }); }); @@ -3089,22 +3089,22 @@ That way, we can do a simple grep. return curMaxElt; } - return CALL(f, [lst.first()], + return CALL(f, [lst.first], function(result) { check(result, isReal, 'argmin', 'procedure that returns real numbers', 1, args); if (jsnums.lessThan(result, curMaxVal)) { - return argminHelp(lst.rest(), result, lst.first()); + return argminHelp(lst.rest, result, lst.first); } else { - return argminHelp(lst.rest(), curMaxVal, curMaxElt); + return argminHelp(lst.rest, curMaxVal, curMaxElt); } }); } - return CALL(f, [initList.first()], + return CALL(f, [initList.first], function(result) { check(result, isReal, 'argmin', 'procedure that returns real numbers', 1, args); - return argminHelp(initList.rest(), result, initList.first()); + return argminHelp(initList.rest, result, initList.first); }); }); @@ -3614,8 +3614,8 @@ That way, we can do a simple grep. var ret = []; while( !lst.isEmpty() ) { - ret.push(lst.first().val); - lst = lst.rest(); + ret.push(lst.first.val); + lst = lst.rest; } return types.string(ret); }); @@ -3728,8 +3728,8 @@ That way, we can do a simple grep. 'implode', 'list of 1-letter strings', 1); var ret = []; while ( !lst.isEmpty() ) { - ret.push( lst.first().toString() ); - lst = lst.rest(); + ret.push( lst.first.toString() ); + lst = lst.rest; } return types.string(ret); }); @@ -4139,8 +4139,8 @@ That way, we can do a simple grep. var ret = []; while ( !lst.isEmpty() ) { - ret.push(lst.first()); - lst = lst.rest(); + ret.push(lst.first); + lst = lst.rest; } return types.bytes(ret, true); }); diff --git a/js-assembler/runtime-src/runtime.js b/js-assembler/runtime-src/runtime.js index 282b085..e1cf282 100644 --- a/js-assembler/runtime-src/runtime.js +++ b/js-assembler/runtime-src/runtime.js @@ -8,16 +8,10 @@ if(this['plt'] === undefined) { this['plt'] = {}; } var runtime = {}; scope['runtime'] = runtime; + var helpers = plt.helpers; + var types = plt.types; - // Type helpers - // - // Defines inheritance between prototypes. - var heir = function(parentPrototype) { - var f = function() {} - f.prototype = parentPrototype; - return new f(); - }; // Consumes a class and creates a predicate that recognizes subclasses. var makeClassPredicate = function(aClass) { @@ -25,27 +19,35 @@ if(this['plt'] === undefined) { this['plt'] = {}; } }; - var isNumber = jsnums.isSchemeNumber; - var isNatural = function(x) { return (jsnums.isInteger(x) && - jsnums.greaterThanOrEqual(x, 0)); } - var isPair = function(x) { return (typeof(x) == 'object' && - x.length === 2 && - x.type !== 'vector') }; - var isList = function(x) { - while (x !== NULL) { - if (typeof(x) == 'object' && x.length === 2) { - x = x[1]; - } else { - return false; - } - } - return true; - }; - var isVector = function(x) { return (typeof(x) == 'object' && - x.type === 'vector') }; + + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + // We try to isolate the effect of external modules: all the identifiers we + // pull from external modules should be listed here, and should otherwise not + // show up outside this section! + var isNumber = types.isNumber; + var isNatural = types.isNatural; + var isPair = types.isPair; + var isList = types.isList; + var isVector = types.isVector; + var NULL = types.EMPTY; + var VOID = types.VOID; + + var makeVector = types.vector; + var makeList = types.list; + var makePair = types.pair; + + var heir = helpers.heir; + var toDomNode = helpers.toDomNode; + var toWrittenString = helpers.toWrittenString; + var toDisplayedString = helpers.toDisplayedString; + //////////////////////////////////////////////////////////////////////] + + + // This value will be dynamically determined. @@ -67,13 +69,25 @@ if(this['plt'] === undefined) { this['plt'] = {}; } // currentDisplayer: DomNode -> Void // currentDisplayer is responsible for displaying to the browser. - 'currentDisplayer': function(v) { - $(document.body).append(v); + 'currentDisplayer': function(MACHINE, domNode) { + $(domNode).appendTo(document.body); }, + // currentErrorDisplayer: DomNode -> Void + // currentErrorDisplayer is responsible for displaying errors to the browser. + 'currentErrorDisplayer': function(MACHINE, domNode) { + $(domNode).appendTo(document.body); + }, + + 'currentOutputPort': new StandardOutputPort(), + 'currentErrorPort': new StandardErrorPort(), 'currentSuccessHandler': function(MACHINE) {}, - 'currentErrorHandler': function(MACHINE, exn) {}, + 'currentErrorHandler': function(MACHINE, exn) { + MACHINE.params.currentErrorDisplayer( + MACHINE, + exn); + }, 'currentNamespace': {}, @@ -99,9 +113,9 @@ if(this['plt'] === undefined) { this['plt'] = {}; } var elt = MACHINE.env.pop(); var outputPort = MACHINE.params.currentOutputPort; - if (elt !== undefined) { - outputPort.write(MACHINE, elt); - outputPort.write(MACHINE, "\n"); + if (elt !== VOID) { + outputPort.writeDomNode(MACHINE, toDomNode(elt, 'print')); + outputPort.writeDomNode(MACHINE, toDomNode("\n", 'display')); } var frame = MACHINE.control.pop(); return frame.label(MACHINE); @@ -166,6 +180,9 @@ if(this['plt'] === undefined) { this['plt'] = {}; } }; + + + // A generic frame just holds marks. var Frame = function() { // The set of continuation marks. @@ -218,31 +235,30 @@ if(this['plt'] === undefined) { this['plt'] = {}; } - - - + // Output Ports var OutputPort = function() {}; var isOutputPort = makeClassPredicate(OutputPort); - var StandardOutputPort = function() {}; - StandardOutputPort.prototype = heir(OutputPort.prototype); - StandardOutputPort.prototype.write = function(MACHINE, v) { - var domNode; - // TODO: v must be coerced into a DOMNode in a more systematic way. - // This function may need to be a Closure. - if(typeof(v) === 'string' || - typeof(v) === 'number' || - typeof(v) === 'boolean' || - typeof(v) === 'null' || - typeof(v) === 'undefined') { - domNode = $('').text(String(v)).css('white-space', 'pre'); - } else { - domNode = $('').text(String(v)).css('white-space', 'pre'); - } - MACHINE.params['currentDisplayer'](domNode); + var StandardOutputPort = function() { + OutputPort.call(this); }; + StandardOutputPort.prototype = heir(OutputPort.prototype); + StandardOutputPort.prototype.writeDomNode = function(MACHINE, domNode) { + MACHINE.params['currentDisplayer'](MACHINE, domNode); + }; + + var StandardErrorPort = function() { + OutputPort.call(this); + }; + StandardErrorPort.prototype = heir(OutputPort.prototype); + StandardErrorPort.prototype.writeDomNode = function(MACHINE, domNode) { + MACHINE.params['currentErrorDisplayer'](MACHINE, domNode); + }; + + + @@ -250,8 +266,8 @@ if(this['plt'] === undefined) { this['plt'] = {}; } this.buf = []; }; OutputStringPort.prototype = heir(OutputPort.prototype); - OutputStringPort.prototype.write = function(MACHINE, v) { - this.buf.push(String(v)); + OutputStringPort.prototype.writeDomNode = function(MACHINE, v) { + this.buf.push($(v).text()); }; OutputStringPort.prototype.getOutputString = function() { return this.buf.join(''); @@ -310,7 +326,6 @@ if(this['plt'] === undefined) { this['plt'] = {}; } new ContinuationPromptTag("default-continuation-prompt-tag"); - var NULL = []; var raise = function(MACHINE, e) { @@ -348,7 +363,7 @@ if(this['plt'] === undefined) { this['plt'] = {}; } if (observed < minimum || observed > maximum) { raise(MACHINE, new Error(callerName + ": expected at least " + minimum + " arguments " - + " but received " + observer)); + + " but received " + observed)); } }; @@ -366,7 +381,7 @@ if(this['plt'] === undefined) { this['plt'] = {}; } raise(MACHINE, new Error(callerName + ": expected " + expectedTypeName + " as argument " + (argumentOffset + 1) - + " but received " + actualValue)); + + " but received " + helpers.toWrittenString(actualValue))); }; var raiseContextExpectedValuesError = function(MACHINE, expected) { @@ -378,23 +393,23 @@ if(this['plt'] === undefined) { this['plt'] = {}; } var raiseArityMismatchError = function(MACHINE, proc, expected, received) { raise(MACHINE, - new Error("expected " + expected + " values, received " + received + " values")); + new Error(proc.displayName + ": " + "expected " + expected + + " value(s), received " + received + " value(s)")); }; var raiseOperatorApplicationError = function(MACHINE, operator) { raise(MACHINE, - new Error("not a procedure: " + expected + - operator)); + new Error("not a procedure: " + helpers.toWrittenString(operator))); }; var raiseOperatorIsNotClosure = function(MACHINE, operator) { raise(MACHINE, - new Error("not a closure: " + operator)); + new Error("not a closure: " + helpers.toWrittenString(operator))); }; var raiseOperatorIsNotPrimitiveProcedure = function(MACHINE, operator) { raise(MACHINE, - new Error("not a primitive procedure: " + operator)); + new Error("not a primitive procedure: " + helpers.toWrittenString(operator))); }; @@ -448,8 +463,8 @@ if(this['plt'] === undefined) { this['plt'] = {}; } var lst = MACHINE.env[MACHINE.env.length - 1 - depth]; var vals = []; while(lst !== NULL) { - vals.push(lst[0]); - lst = lst[1]; + vals.push(lst.first); + lst = lst.rest; } vals.reverse(); MACHINE.env.splice.apply(MACHINE.env, @@ -462,7 +477,8 @@ if(this['plt'] === undefined) { this['plt'] = {}; } var lst = NULL; var i; for (i = 0; i < length; i++) { - lst = [MACHINE.env[MACHINE.env.length - depth - length + i], lst]; + lst = makePair(MACHINE.env[MACHINE.env.length - depth - length + i], + lst); } MACHINE.env.splice(MACHINE.env.length - depth - length, length, @@ -488,12 +504,12 @@ if(this['plt'] === undefined) { this['plt'] = {}; } return n >= arity.value; } else { while (arity !== NULL) { - if (typeof(arity[0]) === 'number') { - if (arity[0] === n) { return true; } + if (typeof(arity.first) === 'number') { + if (arity.first === n) { return true; } } else if (arity instanceof ArityAtLeast) { - if (n >= arity[0].value) { return true; } + if (n >= arity.first.value) { return true; } } - arity = arity[1]; + arity = arity.rest; } return false; } @@ -508,129 +524,119 @@ if(this['plt'] === undefined) { this['plt'] = {}; } // are coded here; several of them (including call/cc) are injected by // the bootstrapping code. var Primitives = {}; - Primitives['display'] = function(MACHINE) { - var firstArg = MACHINE.env[MACHINE.env.length-1]; - var outputPort = MACHINE.params.currentOutputPort; - if (MACHINE.argcount === 2) { - testArgument(MACHINE, - 'isOutputPort', - isOutputPort, - MACHINE.env.length-2, - 1, - 'display'); - outputPort = MACHINE.env[MACHINE.env.length-2]; - } - outputPort.write(MACHINE, firstArg); + + var installPrimitiveProcedure = function(name, arity, f) { + Primitives[name] = f; + Primitives[name].arity = arity; + Primitives[name].displayName = name; }; - Primitives['display'].arity = [1, [2, NULL]]; - Primitives['display'].displayName = 'display'; - - Primitives['newline'] = function(MACHINE) { - var outputPort = MACHINE.params.currentOutputPort; - if (MACHINE.argcount === 1) { - testArgument(MACHINE, - 'isOutputPort', - isOutputPort, - MACHINE.env.length-1, - 1, - 'newline'); - outputPort = MACHINE.env[MACHINE.env.length-1]; - } - outputPort.write(MACHINE, "\n"); + var makePrimitiveProcedure = function(name, arity, f) { + f.arity = arity; + f.displayName = name; + return f; }; - Primitives['newline'].arity = [0, [1, NULL]]; - Primitives['newline'].displayName = 'newline'; - - Primitives['displayln'] = function(MACHINE){ - var firstArg = MACHINE.env[MACHINE.env.length-1]; - var outputPort = MACHINE.params.currentOutputPort; - if (MACHINE.argcount === 2) { - testArgument(MACHINE, - 'isOutputPort', - isOutputPort, - MACHINE.env.length-2, - 1, - 'displayln'); - outputPort = MACHINE.env[MACHINE.env.length-2]; - } - outputPort.write(MACHINE, firstArg); - outputPort.write(MACHINE, "\n"); + var installPrimitiveConstant = function(name, v) { + Primitives[name] = v; }; - Primitives['displayln'].arity = [1, [2, NULL]]; - Primitives['displayln'].displayName = 'displayln'; + installPrimitiveConstant('pi', jsnums.pi); + installPrimitiveConstant('e', jsnums.e); - Primitives['current-print'] = function(MACHINE) { - return MACHINE.params['current-print']; - }; - Primitives['current-print'].arity = [0, [1, NULL]]; - Primitives['current-print'].displayName = "current-print"; - - -// // This should be attached to the module corresponding for print-values -// Primitives['print-values'] = new Closure( -// function(MACHINE) { -// var outputPort = MACHINE.params.currentOutputPort; -// var prependNewline = false; -// if (MACHINE.argcount > 0) { -// if (MACHINE.val !== undefined) { -// if (prependNewline) { -// outputPort.write(MACHINE, "\n"); -// } -// outputPort.write(MACHINE, MACHINE.val); -// prependNewline = true; -// } - -// for(var i = 0; i < MACHINE.argcount - 1; i++) { -// if (MACHINE.env[MACHINE.env.length - 1 - i] !== undefined) { -// if (prependNewline) { -// outputPort.write(MACHINE, "\n"); -// } -// outputPort.write(MACHINE, -// MACHINE.env[MACHINE.env.length - 1 - i]); -// prependNewline = true; -// } -// } -// outputPort.write(MACHINE, "\n"); -// } -// MACHINE.env.length = MACHINE.env.length - MACHINE.argcount; -// var frame = MACHINE.control.pop(); -// return frame.label(MACHINE); -// }, -// new ArityAtLeast(0), -// [], -// "print-values" -// ); - - - - Primitives['pi'] = jsnums.pi; - - Primitives['e'] = jsnums.e; - - Primitives['='] = function(MACHINE) { - var firstArg = MACHINE.env[MACHINE.env.length-1]; - testArgument(MACHINE, 'number', isNumber, firstArg, 0, '='); - for (var i = 0; i < MACHINE.argcount - 1; i++) { - testArgument(MACHINE, - 'number', - isNumber, - MACHINE.env[MACHINE.env.length - 1 - i], - i, - '='); - if (! (jsnums.equals(MACHINE.env[MACHINE.env.length - 1 - i], - MACHINE.env[MACHINE.env.length - 1 - i - 1]))) { - return false; + installPrimitiveProcedure( + 'display', makeList(1, 2), + function(MACHINE) { + var firstArg = MACHINE.env[MACHINE.env.length-1]; + var outputPort = MACHINE.params.currentOutputPort; + if (MACHINE.argcount === 2) { + testArgument(MACHINE, + 'isOutputPort', + isOutputPort, + MACHINE.env.length-2, + 1, + 'display'); + outputPort = MACHINE.env[MACHINE.env.length-2]; } - } - return true; - }; - Primitives['='].arity = new ArityAtLeast(2); - Primitives['='].displayName = '='; + outputPort.writeDomNode(MACHINE, toDomNode(firstArg, 'display')); + return VOID; + }); + installPrimitiveProcedure( + 'newline', makeList(0, 1), + function(MACHINE) { + var outputPort = MACHINE.params.currentOutputPort; + if (MACHINE.argcount === 1) { + testArgument(MACHINE, + 'isOutputPort', + isOutputPort, + MACHINE.env.length-1, + 1, + 'newline'); + outputPort = MACHINE.env[MACHINE.env.length-1]; + } + outputPort.writeDomNode(MACHINE, toDomNode("\n", 'display')); + return VOID; + }); + + installPrimitiveProcedure( + 'displayln', + makeList(1, 2), + function(MACHINE){ + var firstArg = MACHINE.env[MACHINE.env.length-1]; + var outputPort = MACHINE.params.currentOutputPort; + if (MACHINE.argcount === 2) { + testArgument(MACHINE, + 'isOutputPort', + isOutputPort, + MACHINE.env.length-2, + 1, + 'displayln'); + outputPort = MACHINE.env[MACHINE.env.length-2]; + } + outputPort.writeDomNode(MACHINE, toDomNode(firstArg, 'display')); + outputPort.writeDomNode(MACHINE, toDomNode("\n", 'display')); + return VOID; + }); + + + installPrimitiveProcedure( + 'current-print', + makeList(0, 1), + function(MACHINE) { + if (MACHINE.argcount === 1) { + MACHINE.params['current-print'] = MACHINE.env[MACHINE.env.length - 1]; + return VOID; + } else { + return MACHINE.params['current-print']; + } + }); + + + installPrimitiveProcedure( + '=', + new ArityAtLeast(2), + function(MACHINE) { + var firstArg = MACHINE.env[MACHINE.env.length-1]; + testArgument(MACHINE, 'number', isNumber, firstArg, 0, '='); + for (var i = 0; i < MACHINE.argcount - 1; i++) { + testArgument(MACHINE, + 'number', + isNumber, + MACHINE.env[MACHINE.env.length - 1 - i], + i, + '='); + if (! (jsnums.equals(MACHINE.env[MACHINE.env.length - 1 - i], + MACHINE.env[MACHINE.env.length - 1 - i - 1]))) { + return false; + } + } + return true; + }); + + + // TODO: use installPrimitiveProcedure for the rest... Primitives['<'] = function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; @@ -845,7 +851,7 @@ if(this['plt'] === undefined) { this['plt'] = {}; } Primitives['cons'] = function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; var secondArg = MACHINE.env[MACHINE.env.length-2]; - return [firstArg, secondArg]; + return makePair(firstArg, secondArg); }; Primitives['cons'].arity = 2; Primitives['cons'].displayName = 'cons'; @@ -854,8 +860,8 @@ if(this['plt'] === undefined) { this['plt'] = {}; } Primitives['list'] = function(MACHINE) { var result = NULL; for (var i = 0; i < MACHINE.argcount; i++) { - result = [MACHINE.env[MACHINE.env.length - (MACHINE.argcount - i)], - result]; + result = makePair(MACHINE.env[MACHINE.env.length - (MACHINE.argcount - i)], + result); } return result; }; @@ -870,7 +876,7 @@ if(this['plt'] === undefined) { this['plt'] = {}; } 0, 'car'); var firstArg = MACHINE.env[MACHINE.env.length-1]; - return firstArg[0]; + return firstArg.first; }; Primitives['car'].arity = 1; Primitives['car'].displayName = 'car'; @@ -883,7 +889,7 @@ if(this['plt'] === undefined) { this['plt'] = {}; } 0, 'cdr'); var firstArg = MACHINE.env[MACHINE.env.length-1]; - return firstArg[1]; + return firstArg.rest; }; Primitives['cdr'].arity = 1; Primitives['cdr'].displayName = 'cdr'; @@ -904,7 +910,8 @@ if(this['plt'] === undefined) { this['plt'] = {}; } 'set-car!'); var firstArg = MACHINE.env[MACHINE.env.length-1]; var secondArg = MACHINE.env[MACHINE.env.length-2]; - firstArg[0] = secondArg; + firstArg.first = secondArg; + return VOID; }; Primitives['set-car!'].arity = 2; Primitives['set-car!'].displayName = 'set-car!'; @@ -918,7 +925,8 @@ if(this['plt'] === undefined) { this['plt'] = {}; } 'set-cdr!'); var firstArg = MACHINE.env[MACHINE.env.length-1]; var secondArg = MACHINE.env[MACHINE.env.length-2]; - firstArg[1] = secondArg; + firstArg.rest = secondArg; + return VOID; }; Primitives['set-cdr!'].arity = 2; Primitives['set-cdr!'].displayName = 'set-cdr!'; @@ -945,8 +953,8 @@ if(this['plt'] === undefined) { this['plt'] = {}; } for (i = 0; i < MACHINE.argcount; i++) { result.push(MACHINE.env[MACHINE.env.length-1-i]); } - result.type = 'vector'; - return result; + var newVector = makeVector.apply(null, result); + return newVector; }; Primitives['vector'].arity = new ArityAtLeast(0); Primitives['vector'].displayName = 'vector'; @@ -958,11 +966,11 @@ if(this['plt'] === undefined) { this['plt'] = {}; } MACHINE.env[MACHINE.env.length - 1], 0, 'vector->list'); - var firstArg = MACHINE.env[MACHINE.env.length-1]; + var elts = MACHINE.env[MACHINE.env.length-1].elts; var i; var result = NULL; - for (i = 0; i < firstArg.length; i++) { - result = [firstArg[firstArg.length - 1 - i], result]; + for (i = 0; i < elts.length; i++) { + result = makePair(elts[elts.length - 1 - i], result); } return result; }; @@ -973,11 +981,10 @@ if(this['plt'] === undefined) { this['plt'] = {}; } var firstArg = MACHINE.env[MACHINE.env.length-1]; var result = []; while (firstArg !== NULL) { - result.push(firstArg[0]); - firstArg = firstArg[1]; + result.push(firstArg.first); + firstArg = firstArg.rest; } - result.type='vector'; - return result; + return makeVector.apply(null, result); }; Primitives['list->vector'].arity = 1; Primitives['list->vector'].displayName = 'list->vector'; @@ -989,9 +996,9 @@ if(this['plt'] === undefined) { this['plt'] = {}; } MACHINE.env[MACHINE.env.length - 1], 0, 'vector-ref'); - var firstArg = MACHINE.env[MACHINE.env.length-1]; - var secondArg = MACHINE.env[MACHINE.env.length-2]; - return firstArg[secondArg]; + var elts = MACHINE.env[MACHINE.env.length-1].elts; + var index = MACHINE.env[MACHINE.env.length-2]; + return elts[index]; }; Primitives['vector-ref'].arity = 2; Primitives['vector-ref'].displayName = 'vector-ref'; @@ -1003,11 +1010,17 @@ if(this['plt'] === undefined) { this['plt'] = {}; } MACHINE.env[MACHINE.env.length - 1], 0, 'vector-set!'); - var firstArg = MACHINE.env[MACHINE.env.length-1]; - var secondArg = jsnums.toFixnum(MACHINE.env[MACHINE.env.length-2]); - var thirdArg = MACHINE.env[MACHINE.env.length-3]; - firstArg[secondArg] = thirdArg; - return null; + testArgument(MACHINE, + 'natural', + isNatural, + MACHINE.env[MACHINE.env.length - 2], + 1, + 'vector-set!'); + var elts = MACHINE.env[MACHINE.env.length-1].elts; + var index = jsnums.toFixnum(MACHINE.env[MACHINE.env.length-2]); + var val = MACHINE.env[MACHINE.env.length-3]; + elts[index] = val; + return VOID; }; Primitives['vector-set!'].arity = 3; Primitives['vector-set!'].displayName = 'vector-set!'; @@ -1020,7 +1033,7 @@ if(this['plt'] === undefined) { this['plt'] = {}; } MACHINE.env[MACHINE.env.length - 1], 0, 'vector-length'); - var firstArg = MACHINE.env[jsnums.toFixnum(MACHINE.env.length-1)]; + var firstArg = MACHINE.env[MACHINE.env.length-1].elts; return firstArg.length; }; Primitives['vector-length'].arity = 1; @@ -1043,10 +1056,9 @@ if(this['plt'] === undefined) { this['plt'] = {}; } for(var i = 0; i < length; i++) { arr[i] = value; } - arr.type='vector'; - return arr; + return makeVector.apply(null, arr); }; - Primitives['make-vector'].arity = [1, [2, NULL]]; + Primitives['make-vector'].arity = makeList(1, 2); Primitives['make-vector'].displayName = 'make-vector'; @@ -1104,13 +1116,13 @@ if(this['plt'] === undefined) { this['plt'] = {}; } var firstArg = MACHINE.env[MACHINE.env.length-1]; var secondArg = MACHINE.env[MACHINE.env.length-2]; firstArg[0] = secondArg; - return; + return VOID; }; Primitives['set-box!'].arity = 2; Primitives['set-box!'].displayName = 'set-box!'; Primitives['void'] = function(MACHINE) { - return; + return VOID; }; Primitives['void'].arity = new ArityAtLeast(0); Primitives['void'].displayName = 'void'; @@ -1132,27 +1144,7 @@ if(this['plt'] === undefined) { this['plt'] = {}; } Primitives['equal?'].displayName = 'equal?'; - - var isEqual = function(firstArg, secondArg) { - var lset = [firstArg], rset = [secondArg]; - while (lset.length !== 0 && rset.length !== 0) { - var lhs = lset.pop(); - var rhs = rset.pop(); - if (lhs === rhs) { - continue; - } else if (typeof(lhs) === 'object' && - typeof(rhs) === 'object' && - typeof(lhs.length) === 'number' && - typeof(rhs.length) === 'number' && - lhs.length === rhs.length) { - lset.push.apply(lset, lhs); - rset.push.apply(rset, rhs); - } else { - return false; - } - } - return true; - }; + var isEqual = types.isEqual; Primitives['member'] = function(MACHINE) { @@ -1168,10 +1160,10 @@ if(this['plt'] === undefined) { this['plt'] = {}; } if (lst === NULL) { return false; } - if (isEqual(x, (lst[0]))) { + if (isEqual(x, (lst.first))) { return lst; } - lst = lst[1]; + lst = lst.rest; } }; Primitives['member'].arity = 2; @@ -1185,8 +1177,8 @@ if(this['plt'] === undefined) { this['plt'] = {}; } while(lst !== NULL) { testArgument(MACHINE, 'pair', isPair, lst, 0, 'reverse'); - rev = [lst[0], rev]; - lst = lst[1]; + rev = makePair(lst.first, rev); + lst = lst.rest; } return rev; }; @@ -1198,6 +1190,30 @@ if(this['plt'] === undefined) { this['plt'] = {}; } + // Extensions. A small experiment. + installPrimitiveProcedure( + 'viewport-width', + 0, + function(MACHINE) { + return $(window).width(); + }); + + installPrimitiveProcedure( + 'viewport-height', + 0, + function(MACHINE) { + return $(window).height(); + }); + + + installPrimitiveProcedure( + 'in-javascript-context?', + 0, + function(MACHINE) { + return true; + }); + + // recomputeGas: state number -> number @@ -1379,6 +1395,10 @@ if(this['plt'] === undefined) { this['plt'] = {}; } exports['currentMachine'] = new Machine(); exports['invokeMains'] = invokeMains; + + // installing new primitives + exports['installPrimitiveProcedure'] = installPrimitiveProcedure; + exports['makePrimitiveProcedure'] = makePrimitiveProcedure; exports['Primitives'] = Primitives; exports['ready'] = ready; @@ -1398,6 +1418,7 @@ if(this['plt'] === undefined) { this['plt'] = {}; } exports['DEFAULT_CONTINUATION_PROMPT_TAG'] = DEFAULT_CONTINUATION_PROMPT_TAG; exports['NULL'] = NULL; + exports['VOID'] = VOID; exports['testArgument'] = testArgument; exports['testArity'] = testArity; @@ -1423,6 +1444,7 @@ if(this['plt'] === undefined) { this['plt'] = {}; } exports['unspliceRestFromStack'] = unspliceRestFromStack; + // Type predicates exports['isNumber'] = isNumber; exports['isNatural'] = isNatural; exports['isPair'] = isPair; @@ -1432,6 +1454,17 @@ if(this['plt'] === undefined) { this['plt'] = {}; } exports['isOutputStringPort'] = isOutputStringPort; exports['isEqual'] = isEqual; + exports['toDomNode'] = toDomNode; + exports['toWrittenString'] = toWrittenString; + exports['toDisplayedString'] = toDisplayedString; + + + // Type constructors + exports['makeList'] = makeList; + exports['makePair'] = makePair; + exports['makeVector'] = makeVector; + + exports['ArityAtLeast'] = ArityAtLeast; exports['isArityMatching'] = isArityMatching; @@ -1441,5 +1474,7 @@ if(this['plt'] === undefined) { this['plt'] = {}; } exports['HaltError'] = HaltError; + + scope.link.announceReady('runtime'); })(this['plt']); \ No newline at end of file diff --git a/js-assembler/runtime-src/types.js b/js-assembler/runtime-src/types.js index ff70603..9bd9db8 100644 --- a/js-assembler/runtime-src/types.js +++ b/js-assembler/runtime-src/types.js @@ -47,8 +47,6 @@ if (! this['plt']) { this['plt'] = {}; } - - // Union/find for circular equality testing. var UnionFind = function() { @@ -85,7 +83,7 @@ if (! this['plt']) { this['plt'] = {}; } ////////////////////////////////////////////////////////////////////// - StructType = function(name, type, numberOfArgs, numberOfFields, firstField, + var StructType = function(name, type, numberOfArgs, numberOfFields, firstField, applyGuard, constructor, predicate, accessor, mutator) { this.name = name; this.type = type; @@ -698,17 +696,24 @@ if (! this['plt']) { this['plt'] = {}; } return b; }; - Cons = function(f, r) { - this.f = f; - this.r = r; + + + + ////////////////////////////////////////////////////////////////////// + + // Cons Pairs + + var Cons = function(f, r) { + this.first = f; + this.rest = r; }; Cons.prototype.reverse = function() { var lst = this; var ret = Empty.EMPTY; while (!lst.isEmpty()){ - ret = Cons.makeInstance(lst.first(), ret); - lst = lst.rest(); + ret = Cons.makeInstance(lst.first, ret); + lst = lst.rest; } return ret; }; @@ -717,28 +722,21 @@ if (! this['plt']) { this['plt'] = {}; } return new Cons(f, r); }; - // FIXME: can we reduce the recursion on this? Cons.prototype.isEqual = function(other, aUnionFind) { if (! (other instanceof Cons)) { return Logic.FALSE; } - return (isEqual(this.first(), other.first(), aUnionFind) && - isEqual(this.rest(), other.rest(), aUnionFind)); - }; - - Cons.prototype.first = function() { - return this.f; - }; - - Cons.prototype.rest = function() { - return this.r; + return (isEqual(this.first, other.first, aUnionFind) && + isEqual(this.rest, other.rest, aUnionFind)); }; + Cons.prototype.isEmpty = function() { return false; }; + // Cons.append: (listof X) -> (listof X) Cons.prototype.append = function(b){ if (b === Empty.EMPTY) @@ -746,8 +744,8 @@ if (! this['plt']) { this['plt'] = {}; } var ret = b; var lst = this.reverse(); while ( !lst.isEmpty() ) { - ret = Cons.makeInstance(lst.first(), ret); - lst = lst.rest(); + ret = Cons.makeInstance(lst.first, ret); + lst = lst.rest; } return ret; @@ -759,8 +757,8 @@ if (! this['plt']) { this['plt'] = {}; } var texts = []; var p = this; while ( p instanceof Cons ) { - texts.push(toWrittenString(p.first(), cache)); - p = p.rest(); + texts.push(toWrittenString(p.first, cache)); + p = p.rest; if (typeof(p) === 'object' && cache.containsKey(p)) { break; } @@ -779,8 +777,8 @@ if (! this['plt']) { this['plt'] = {}; } var texts = []; var p = this; while ( p instanceof Cons ) { - texts.push(toDisplayedString(p.first(), cache)); - p = p.rest(); + texts.push(toDisplayedString(p.first, cache)); + p = p.rest; if (typeof(p) === 'object' && cache.containsKey(p)) { break; } @@ -789,17 +787,6 @@ if (! this['plt']) { this['plt'] = {}; } texts.push('.'); texts.push(toDisplayedString(p, cache)); } - // while (true) { - // if ((!(p instanceof Cons)) && (!(p instanceof Empty))) { - // texts.push("."); - // texts.push(toDisplayedString(p, cache)); - // break; - // } - // if (p.isEmpty()) - // break; - // texts.push(toDisplayedString(p.first(), cache)); - // p = p.rest(); - // } return "(" + texts.join(" ") + ")"; }; @@ -811,8 +798,8 @@ if (! this['plt']) { this['plt'] = {}; } node.appendChild(document.createTextNode("(")); var p = this; while ( p instanceof Cons ) { - appendChild(node, toDomNode(p.first(), cache)); - p = p.rest(); + appendChild(node, toDomNode(p.first, cache)); + p = p.rest; if ( p !== Empty.EMPTY ) { appendChild(node, document.createTextNode(" ")); } @@ -825,27 +812,27 @@ if (! this['plt']) { this['plt'] = {}; } appendChild(node, document.createTextNode(" ")); appendChild(node, toDomNode(p, cache)); } - // while (true) { - // if ((!(p instanceof Cons)) && (!(p instanceof Empty))) { - // appendChild(node, document.createTextNode(" ")); - // appendChild(node, document.createTextNode(".")); - // appendChild(node, document.createTextNode(" ")); - // appendChild(node, toDomNode(p, cache)); - // break; - // } - // if (p.isEmpty()) - // break; - // appendChild(node, toDomNode(p.first(), cache)); - // p = p.rest(); - // if (! p.isEmpty()) { - // appendChild(node, document.createTextNode(" ")); - // } - // } + node.appendChild(document.createTextNode(")")); return node; }; + // isList: Any -> Boolean + // Returns true if x is a list (a chain of pairs terminated by EMPTY). + var isList = function(x) { + while (x !== Empty.EMPTY) { + if (x instanceof Cons){ + x = x.rest; + } else { + return false; + } + } + return true; + }; + + + ////////////////////////////////////////////////////////////////////// @@ -1955,22 +1942,22 @@ String.prototype.toDisplayedString = function(cache) { - var makeList = function(args) { + var makeList = function() { var result = Empty.EMPTY; - for(var i = args.length-1; i >= 0; i--) { - result = Cons.makeInstance(args[i], result); + for(var i = arguments.length-1; i >= 0; i--) { + result = Cons.makeInstance(arguments[i], result); } return result; }; - var makeVector = function(args) { - return Vector.makeInstance(args.length, args); + var makeVector = function() { + return Vector.makeInstance(arguments.length, arguments); }; - var makeVectorImmutable = function(args) { - var v = Vector.makeInstance(args.length, args); + var makeVectorImmutable = function() { + var v = Vector.makeInstance(arguments.length, arguments); v.mutable = false; return v; }; @@ -1996,27 +1983,27 @@ String.prototype.toDisplayedString = function(cache) { ' given ' + s.toString(), false); } - } + }; var makeHashEq = function(lst) { var newHash = new EqHashTable(); while ( !lst.isEmpty() ) { - newHash.hash.put(lst.first().first(), lst.first().rest()); - lst = lst.rest(); + newHash.hash.put(lst.first.first, lst.first.rest); + lst = lst.rest; } return newHash; - } + }; var makeHashEqual = function(lst) { var newHash = new EqualHashTable(); while ( !lst.isEmpty() ) { - newHash.hash.put(lst.first().first(), lst.first().rest()); - lst = lst.rest(); + newHash.hash.put(lst.first.first, lst.first.rest); + lst = lst.rest; } return newHash; - } + }; var Color = makeStructureType('color', false, 3, 0, false, false); @@ -2042,10 +2029,10 @@ String.prototype.toDisplayedString = function(cache) { } if (types.isPair(x)) { - var consPair = types.cons(x.first(), x.rest()); + var consPair = types.cons(x.first, x.rest); objectHash.put(x, consPair); - consPair.f = readerGraph(x.first(), objectHash, n+1); - consPair.r = readerGraph(x.rest(), objectHash, n+1); + consPair.f = readerGraph(x.first, objectHash, n+1); + consPair.r = readerGraph(x.rest, objectHash, n+1); return consPair; } @@ -2071,7 +2058,7 @@ String.prototype.toDisplayedString = function(cache) { } if (types.isStruct(x)) { - var aStruct = clone(x); + var aStruct = helpers.clone(x); objectHash.put(x, aStruct); for(var i = 0 ;i < x._fields.length; i++) { x._fields[i] = readerGraph(x._fields[i], objectHash, n+1); @@ -2088,22 +2075,6 @@ String.prototype.toDisplayedString = function(cache) { - // clone: object -> object - // Copies an object. The new object should respond like the old - // object, including to things like instanceof - var clone = function(obj) { - var C = function() {} - C.prototype = obj; - var c = new C(); - for (property in obj) { - if (obj.hasOwnProperty(property)) { - c[property] = obj[property]; - } - } - return c; - }; - - ////////////////////////////////////////////////////////////////////// @@ -2170,6 +2141,7 @@ String.prototype.toDisplayedString = function(cache) { types.isChar = function(x) { return x instanceof Char; }; types.isString = isString; types.isPair = function(x) { return x instanceof Cons; }; + types.isList = isList; types.isEmpty = function(x) { return x === Empty.EMPTY; }; types.isVector = function(x) { return x instanceof Vector; }; types.isBox = function(x) { return x instanceof Box; }; diff --git a/js.rkt b/js.rkt new file mode 100644 index 0000000..121fc34 --- /dev/null +++ b/js.rkt @@ -0,0 +1,3 @@ +#lang s-exp "lang/base.rkt" +(require "js/main.rkt") +(provide (all-from-out "js/main.rkt")) \ No newline at end of file diff --git a/js/js-impl.js b/js/js-impl.js new file mode 100644 index 0000000..a79a95f --- /dev/null +++ b/js/js-impl.js @@ -0,0 +1,36 @@ +EXPORTS['alert'] = + RUNTIME.makePrimitiveProcedure( + 'is-color?', + 1, + function(MACHINE) { + var elt = MACHINE.env[MACHINE.env.length - 1]; + alert(String(elt)); + return RUNTIME.VOID; + }); + + +EXPORTS['body'] = $(document.body); + +EXPORTS['$'] = + RUNTIME.makePrimitiveProcedure( + '$', + 1, + function(MACHINE) { + var obj = MACHINE.env[MACHINE.env.length - 1]; + return $(obj); + }); + +EXPORTS['call'] = + RUNTIME.makePrimitiveProcedure( + 'call', + new RUNTIME.ArityAtLeast(2), + function(MACHINE) { + var obj = MACHINE.env[MACHINE.env.length - 1]; + var methodName = MACHINE.env[MACHINE.env.length - 2]; + var args = []; + for (var i = 0; i < MACHINE.argcount - 2; i++) { + args.push(MACHINE.env[MACHINE.env.length -1 - 2 - i]); + } + var result = obj[methodName].apply(obj, args); + return result; + }); diff --git a/js/main.rkt b/js/main.rkt new file mode 100644 index 0000000..35d16af --- /dev/null +++ b/js/main.rkt @@ -0,0 +1,9 @@ +#lang s-exp "../lang/js/js.rkt" + +(declare-implementation + #:racket "racket-impl.rkt" + #:javascript ("js-impl.js") + #:provided-values (alert + body + call + $)) \ No newline at end of file diff --git a/js/racket-impl.rkt b/js/racket-impl.rkt new file mode 100644 index 0000000..a9c55af --- /dev/null +++ b/js/racket-impl.rkt @@ -0,0 +1,15 @@ +#lang s-exp "../lang/base.rkt" + +(provide alert body call $) + +(define (alert x) + (display x) + (newline)) + +(define body 'blah) + +(define (call object method . args) + 'not-done-yet) + +(define ($ name) + 'not-done-yet) \ No newline at end of file diff --git a/lang/base/reader.rkt b/lang/base/reader.rkt new file mode 100644 index 0000000..5ba9db5 --- /dev/null +++ b/lang/base/reader.rkt @@ -0,0 +1,9 @@ +#lang s-exp syntax/module-reader + +;; http://docs.racket-lang.org/planet/hash-lang-planet.html + +#:language (lambda (ip) + `(file ,(path->string base-lang-path))) + +(require racket/runtime-path) +(define-runtime-path base-lang-path "../base.rkt") diff --git a/lang/js-impl/js-impl.rkt b/lang/js-impl/js-impl.rkt deleted file mode 100644 index 06c69e7..0000000 --- a/lang/js-impl/js-impl.rkt +++ /dev/null @@ -1,57 +0,0 @@ -#lang racket/base - -;; Special language level where implementation is done in Javascript. - -(require (for-syntax racket/base) - (for-syntax racket/file) - (for-syntax syntax/modresolve) - (for-syntax "record.rkt")) - - -(define-for-syntax (read-implementation a-module-path) - (let ([a-path (parameterize ([current-directory (or (current-load-relative-directory) - (current-directory))]) - (resolve-module-path a-module-path #f))]) - (file->string a-path))) - - -(define-syntax (require-js stx) - (syntax-case stx () - [(_ path ...) - (andmap (compose string? syntax-e) (syntax->list #'(path ...))) - (with-syntax - ([(impl ...) (map (compose read-implementation syntax-e) - (syntax->list #'(path ...)))]) - (syntax/loc stx - (begin - (begin-for-syntax - (let* ([this-module (variable-reference->resolved-module-path (#%variable-reference))] - [key (resolved-module-path-name this-module)]) - (record-implementations! key (list (#%datum . impl) ...)))) - (void))))])) - - -(define-syntax (-provide stx) - (syntax-case stx () - [(_ name ...) - (andmap (compose symbol? syntax-e) (syntax->list #'(name ...))) - (syntax/loc stx - (begin - (begin-for-syntax - (let* ([this-module (variable-reference->resolved-module-path (#%variable-reference))] - [key (resolved-module-path-name this-module)]) - (record-exports! key (list (#%datum . name) ...)))) - (provide name ...) - (begin (define name (lambda args - (error (quote name) - "Must be evaluated within Javascript"))) ...)))])) - - - - - -(provide require-js - require - planet - (rename-out (-provide provide) - (#%plain-module-begin #%module-begin))) diff --git a/lang/js-impl/query.rkt b/lang/js-impl/query.rkt deleted file mode 100644 index 396956e..0000000 --- a/lang/js-impl/query.rkt +++ /dev/null @@ -1,39 +0,0 @@ -#lang racket/base - -(require racket/contract - racket/runtime-path - racket/list - syntax/modresolve) - - -(define-struct js-module (impls exports)) - -(provide/contract [query - (module-path? . -> . (or/c js-module? false/c))] - [struct js-module ([impls (listof string?)] - [exports (listof symbol?)])]) - - - - -(define-runtime-path record.rkt "record.rkt") -(define ns (make-base-empty-namespace)) - - - -;; query: module-path -> (listof string) -;; Given a module, see if it's implemented via Javascript. -(define (query a-module-path) - (let ([resolved-path (resolve-module-path a-module-path #f)]) - (parameterize ([current-namespace ns]) - (dynamic-require a-module-path (void)) ;; get the compile-time code running. - (let ([result - ((dynamic-require-for-syntax record.rkt 'lookup-implementations) resolved-path)]) - (cond - [(empty? result) - #f] - [else - (make-js-module result - ((dynamic-require-for-syntax record.rkt 'lookup-exports) resolved-path))]))))) - - diff --git a/lang/js-impl/record.rkt b/lang/js-impl/record.rkt deleted file mode 100644 index 0acf266..0000000 --- a/lang/js-impl/record.rkt +++ /dev/null @@ -1,61 +0,0 @@ -#lang racket/base - -(provide record-implementations! - record-exports! - lookup-implementations - lookup-exports) - -(define-struct record (path impls)) -(define-struct export (path exports)) - -(define records '()) -(define exports '()) - -;; record!: path (listof string) -> void -(define (record-implementations! a-path impls) - (set! records (cons (make-record a-path impls) - records))) - - -;; record-exports!: path (listof symbol) -> void -(define (record-exports! a-path export-names) - (set! exports (cons (make-export a-path export-names) - exports))) - - -(define (my-foldl f acc lst) - (cond - [(null? lst) - acc] - [else - (my-foldl f (f (car lst) acc) (cdr lst))])) - - -(define (my-filter f lst) - (cond - [(null? lst) - '()] - [(f (car lst)) - (cons (car lst) (my-filter f (cdr lst)))] - [else - (my-filter f (cdr lst))])) - - -;; lookup-implementations: path -> (listof string) -(define (lookup-implementations a-path) - (my-foldl (lambda (a-record perms) - (append (record-impls a-record) perms)) - '() - (my-filter (lambda (a-record) - (equal? a-path (record-path a-record))) - records))) - - -;; lookup-exports: path -> (listof symbol) -(define (lookup-exports a-path) - (my-foldl (lambda (an-export exports) - (append (export-exports an-export) exports)) - '() - (my-filter (lambda (an-export) - (equal? a-path (export-path an-export))) - exports))) \ No newline at end of file diff --git a/lang/js-conditional/js-conditional.rkt b/lang/js/js.rkt similarity index 74% rename from lang/js-conditional/js-conditional.rkt rename to lang/js/js.rkt index 150ddce..19905ee 100644 --- a/lang/js-conditional/js-conditional.rkt +++ b/lang/js/js.rkt @@ -17,10 +17,11 @@ (file->string a-path))) -(define-syntax (declare-conditional-implementation stx) +(define-syntax (declare-implementation stx) (syntax-parse stx [(_ #:racket racket-module-name - #:javascript (javascript-module-name ...)) + #:javascript (javascript-module-name ...) + #:provided-values (provided-name ...)) (with-syntax ([resolved-racket-module-name (my-resolve-path (syntax-e #'racket-module-name))] @@ -28,7 +29,8 @@ (string-join (map (compose read-implementation syntax-e) (syntax->list #'(javascript-module-name ...))) - "\n")]) + "\n")] + [(internal-name ...) (generate-temporaries #'(provided-name ...))]) (syntax/loc stx (begin @@ -42,11 +44,14 @@ [key (resolved-module-path-name this-module)]) (record-redirection! (#%datum . resolved-racket-module-name) key) - (record-javascript-implementation! key (#%datum . impl)))) + (record-javascript-implementation! key (#%datum . impl)) + ;;(record-exported-name! key 'internal-name 'provided-name) ... + )) (require racket-module-name) - (provide (all-from-out racket-module-name)))))])) + (define internal-name provided-name) ... + (provide (rename-out [internal-name provided-name] ...)))))])) -(provide declare-conditional-implementation +(provide declare-implementation (rename-out [#%plain-module-begin #%module-begin])) \ No newline at end of file diff --git a/lang/js-conditional/query.rkt b/lang/js/query.rkt similarity index 78% rename from lang/js-conditional/query.rkt rename to lang/js/query.rkt index ea8731a..f00eaa1 100644 --- a/lang/js-conditional/query.rkt +++ b/lang/js/query.rkt @@ -9,7 +9,8 @@ [has-javascript-implementation? (module-path? . -> . boolean?)] [redirected? (path? . -> . boolean?)] - [follow-redirection (path? . -> . path?)]) + [follow-redirection (path? . -> . path?)] + [collect-redirections-to (path? . -> . (listof path?))]) (define-runtime-path record.rkt "record.rkt") (define ns (make-base-empty-namespace)) @@ -49,3 +50,12 @@ ((dynamic-require-for-syntax record.rkt 'follow-redirection) resolved-path)))) + + +;; collect-redirections-to: module-path -> (listof path) +(define (collect-redirections-to a-module-path) + (let ([resolved-path (resolve-module-path a-module-path #f)]) + (parameterize ([current-namespace ns]) + (dynamic-require a-module-path (void)) ;; get the compile-time code running. + ((dynamic-require-for-syntax record.rkt 'collect-redirections-to) + resolved-path)))) diff --git a/lang/js-conditional/record.rkt b/lang/js/record.rkt similarity index 70% rename from lang/js-conditional/record.rkt rename to lang/js/record.rkt index 54452ed..7f3b235 100644 --- a/lang/js-conditional/record.rkt +++ b/lang/js/record.rkt @@ -5,7 +5,11 @@ lookup-javascript-implementation record-redirection! - follow-redirection) + follow-redirection + + record-exported-name! + + collect-redirections-to) (define-struct record (path impl)) @@ -59,4 +63,21 @@ [(equal? (redirection-from (car redirections)) a-path) (redirection-to (car redirections))] [else - (loop (cdr redirections))]))) \ No newline at end of file + (loop (cdr redirections))]))) + + +(define (record-exported-name! a-path internal-name external-name) + (printf "I need to remember to export ~s as ~s\n" internal-name external-name) + (void)) + + +;; collect-redirections-to: path -> (listof path) +(define (collect-redirections-to a-path) + (let loop ([redirections redirections]) + (cond + [(null? redirections) + '()] + [(equal? (redirection-to (car redirections)) a-path) + (redirection-from (car redirections))] + [else + (loop (cdr redirections))]))) diff --git a/lang/kernel.rkt b/lang/kernel.rkt index 91f5f11..47654b9 100644 --- a/lang/kernel.rkt +++ b/lang/kernel.rkt @@ -148,7 +148,7 @@ display newline - #;displayln +;; displayln ;; current-print ;; current-continuation-marks @@ -175,7 +175,7 @@ ;; compose ;; current-inexact-milliseconds ;; current-seconds -;; void + void ;; random ;; sleep ;; (identity -identity) @@ -394,6 +394,36 @@ ;; make-reader-graph ;; make-placeholder ;; placeholder-set! - - ) + + + + + +(provide + ;; FIXME: + ;; Extensions: these may need to be hidden in a JavaScript-implemented module + in-javascript-context? + viewport-width + viewport-height) + + + +;; in-javascript-context: -> boolean +;; Produces true if we're in a JavaScript context. +(define (in-javascript-context?) + #f) + + +;; viewport-width: -> natural +;; The viewport width in pixels. +(define (viewport-width) + (error 'viewport-width "Not available outside JavaScript context.")) + + +;; viewport-height: -> natural +;; The viewport height in pixels. +(define (viewport-height) + (error 'viewport-width "Not available outside JavaScript context.")) + + diff --git a/lang/reader.rkt b/lang/reader.rkt new file mode 100644 index 0000000..cab4e7e --- /dev/null +++ b/lang/reader.rkt @@ -0,0 +1,9 @@ +#lang s-exp syntax/module-reader + +;; http://docs.racket-lang.org/planet/hash-lang-planet.html + +#:language (lambda (ip) + `(file ,(path->string base-lang-path))) + +(require racket/runtime-path) +(define-runtime-path base-lang-path "base.rkt") diff --git a/get-dependencies.rkt b/make/get-dependencies.rkt similarity index 91% rename from get-dependencies.rkt rename to make/get-dependencies.rkt index 7483948..e2e29e2 100644 --- a/get-dependencies.rkt +++ b/make/get-dependencies.rkt @@ -1,7 +1,7 @@ #lang typed/racket/base -(require "compiler/expression-structs.rkt" - "compiler/lexical-structs.rkt" - "sets.rkt") +(require "../compiler/expression-structs.rkt" + "../compiler/lexical-structs.rkt" + "../sets.rkt") ;; Collect the complete list of dependencies for a module. diff --git a/make-structs.rkt b/make/make-structs.rkt similarity index 75% rename from make-structs.rkt rename to make/make-structs.rkt index 1939c2c..41e8d1a 100644 --- a/make-structs.rkt +++ b/make/make-structs.rkt @@ -1,8 +1,8 @@ #lang typed/racket/base -(require "compiler/il-structs.rkt" - "compiler/bootstrapped-primitives.rkt" - "compiler/expression-structs.rkt" +(require "../compiler/il-structs.rkt" + "../compiler/bootstrapped-primitives.rkt" + "../compiler/expression-structs.rkt" "get-dependencies.rkt") @@ -13,7 +13,9 @@ (define-type Source (U StatementsSource MainModuleSource ModuleSource - SexpSource)) + SexpSource + UninterpretedSource + )) (define-struct: StatementsSource ([stmts : (Listof Statement)]) #:transparent) @@ -23,10 +25,14 @@ #:transparent) (define-struct: SexpSource ([sexp : Any]) #:transparent) +(define-struct: UninterpretedSource ([datum : String]) + #:transparent) + (define-struct: Configuration - ([should-follow? : (Source Path -> Boolean)] + ([wrap-source : (Source -> Source)] + [should-follow-children? : (Source -> Boolean)] [on-module-statements : (Source (U Expression #f) (Listof Statement) @@ -40,7 +46,8 @@ (define debug-configuration (make-Configuration - (lambda (src p) #t) + (lambda (src) src) + (lambda (src) #t) (lambda (src ast stmt) (when (and ast (expression-module-path ast)) (printf "debug build configuration: visiting ~s\n" diff --git a/make.rkt b/make/make.rkt similarity index 83% rename from make.rkt rename to make/make.rkt index 4eb7133..fbde27f 100644 --- a/make.rkt +++ b/make/make.rkt @@ -1,22 +1,22 @@ #lang typed/racket/base -(require "compiler/compiler.rkt" - "compiler/il-structs.rkt" - "compiler/lexical-structs.rkt" - "compiler/compiler-structs.rkt" - "compiler/expression-structs.rkt" +(require "../compiler/compiler.rkt" + "../compiler/il-structs.rkt" + "../compiler/lexical-structs.rkt" + "../compiler/compiler-structs.rkt" + "../compiler/expression-structs.rkt" + "../parameters.rkt" + "../sets.rkt" "get-dependencies.rkt" - "parameters.rkt" - "sets.rkt" "make-structs.rkt" racket/list racket/match) -(require/typed "parser/parse-bytecode.rkt" +(require/typed "../parser/parse-bytecode.rkt" [parse-bytecode (Any -> Expression)]) -(require/typed "get-module-bytecode.rkt" +(require/typed "../get-module-bytecode.rkt" [get-module-bytecode ((U String Path Input-Port) -> Bytes)]) @@ -41,6 +41,9 @@ [(StatementsSource? a-source) (values #f (StatementsSource-stmts a-source))] + [(UninterpretedSource? a-source) + (values #f '())] + [(MainModuleSource? a-source) (let-values ([(ast stmts) (get-ast-and-statements (MainModuleSource-source a-source))]) @@ -82,6 +85,7 @@ (and path (? ModuleLocator?)) prefix requires + provides code)))) path] [else @@ -96,7 +100,8 @@ ((inst new-seteq Symbol))]) (match config - [(struct Configuration (should-follow? + [(struct Configuration (wrap-source + should-follow-children? on-module-statements after-module-statements after-last)) @@ -112,20 +117,19 @@ (cond [(eq? ast #f) empty] + [(not (should-follow-children? this-source)) + empty] [else (let* ([dependent-module-names (get-dependencies ast)] [paths (foldl (lambda: ([mp : ModuleLocator] [acc : (Listof Source)]) (let ([rp [ModuleLocator-real-path mp]]) - (cond [((current-kernel-module-locator?) mp) acc] - [(and (path? rp) - (should-follow? this-source rp) - (cons (make-ModuleSource rp) - acc))] + [(path? rp) + (cons (make-ModuleSource rp) acc)] [else acc]))) '() @@ -146,9 +150,8 @@ [(ast stmts) (get-ast-and-statements this-source)]) (on-module-statements this-source ast stmts) - (loop (append (collect-new-dependencies this-source ast) + (loop (append (map wrap-source (collect-new-dependencies this-source ast)) (rest sources))) (after-module-statements this-source ast stmts))]))) - (follow-dependencies sources)]))) - + (follow-dependencies (map wrap-source sources))]))) diff --git a/parameters.rkt b/parameters.rkt index bc141d4..98fbb86 100644 --- a/parameters.rkt +++ b/parameters.rkt @@ -11,7 +11,8 @@ current-root-path current-warn-unimplemented-kernel-primitive current-seen-unimplemented-kernel-primitives - current-kernel-module-locator?) + current-kernel-module-locator? + current-compress-javascript?) @@ -47,6 +48,9 @@ +(: current-compress-javascript? (Parameterof Boolean)) +(define current-compress-javascript? (make-parameter #f)) + diff --git a/parser/parse-bytecode-5.1.1.rkt b/parser/parse-bytecode-5.1.1.rkt index b5d5061..0c30050 100644 --- a/parser/parse-bytecode-5.1.1.rkt +++ b/parser/parse-bytecode-5.1.1.rkt @@ -163,12 +163,14 @@ (struct ModuleLocator ('self 'self)) module-prefix module-requires + module-provides module-code)))) (make-Top top-prefix (make-Module name (make-ModuleLocator name name) (current-module-path) module-prefix module-requires + module-provides module-code))] [else exp])) @@ -369,6 +371,7 @@ (make-ModuleLocator self-path self-path) (parse-prefix prefix) (parse-mod-requires self-modidx requires) + (parse-mod-provides self-modidx provides) (parse-mod-body body))] [else (let ([rewritten-path (rewrite-path self-path)]) @@ -379,6 +382,7 @@ (normalize-path self-path)) (parse-prefix prefix) (parse-mod-requires self-modidx requires) + (parse-mod-provides self-modidx provides) (parse-mod-body body))] [else (error 'parse-mod "Internal error: unable to resolve module path ~s" self-path)]))]))])) @@ -407,6 +411,34 @@ +(define (parse-mod-provides enclosing-module-path-index provides) + (let* ([resolver + (current-module-path-index-resolver)] + [enclosing-path + (resolver enclosing-module-path-index (current-module-path))] + [subresolver + (lambda (p) + (cond + [(symbol? enclosing-path) + (wrap-module-name (resolver p (current-module-path)))] + [(path? enclosing-path) + (wrap-module-name (resolver p enclosing-path))]))]) + (let loop ([provides provides]) + (cond + [(empty? provides) + empty] + [(= (first (first provides)) 0) + (let ([provided-values (second (first provides))]) + (for/list ([v provided-values]) + (match v + [(struct provided (name src src-name nom-mod + src-phase protected? insp)) + (make-ModuleProvide src-name name (subresolver src))])))] + [else + (loop (rest provides))])))) + + + diff --git a/simulator/simulator.rkt b/simulator/simulator.rkt index 2283769..c3997ae 100644 --- a/simulator/simulator.rkt +++ b/simulator/simulator.rkt @@ -312,10 +312,10 @@ (cond [(closure? clos) (if (arity-match? (closure-arity clos) - (ensure-natural (evaluate-oparg m (CheckClosureArity!-arity op)))) + (ensure-natural (evaluate-oparg m (CheckClosureArity!-num-args op)))) 'ok (error 'check-closure-arity "arity mismatch: passed ~s args to ~s" - (ensure-natural (evaluate-oparg m (CheckClosureArity!-arity op))) + (ensure-natural (evaluate-oparg m (CheckClosureArity!-num-args op))) (closure-display-name clos)))] [else (error 'check-closure-arity "not a closure: ~s" clos)]))] @@ -325,10 +325,10 @@ (cond [(primitive-proc? clos) (if (arity-match? (primitive-proc-arity clos) - (ensure-natural (evaluate-oparg m (CheckPrimitiveArity!-arity op)))) + (ensure-natural (evaluate-oparg m (CheckPrimitiveArity!-num-args op)))) 'ok (error 'check-primitive-arity "arity mismatch: passed ~s args to ~s" - (ensure-natural (evaluate-oparg m (CheckPrimitiveArity!-arity op))) + (ensure-natural (evaluate-oparg m (CheckPrimitiveArity!-num-args op))) (primitive-proc-display-name clos)))] [else (error 'check-primitive-arity "not a primitive: ~s" clos)]))] diff --git a/tests/browser-evaluate.rkt b/tests/browser-evaluate.rkt index 8b898d5..fbcc48a 100644 --- a/tests/browser-evaluate.rkt +++ b/tests/browser-evaluate.rkt @@ -85,6 +85,7 @@ [program (second javascript-compiler+program)]) (with-handlers ([exn:fail? (lambda (exn) + (displayln exn) (let ([sentinel (format #<vector (reverse nts)))))) + def-loop) + grammar + '())))) + (ind + (lambda (nt nts) + ((letrec ((loop + (lambda (i) + (if (>= i '0) (if (equal? (vector-ref nts i) nt) i (loop (- i '1))) '#f)))) + loop) + (- (vector-length nts) '1)))) + (nb-configurations + (lambda (grammar) + ((letrec ((def-loop + (lambda (defs nb-confs) + (if (pair? defs) + (let ((def (car defs))) + ((letrec ((rule-loop + (lambda (rules nb-confs) + (if (pair? rules) + (let ((rule (car rules))) + ((letrec ((loop + (lambda (l nb-confs) + (if (pair? l) + (loop (cdr l) (+ nb-confs '1)) + (rule-loop (cdr rules) (+ nb-confs '1)))))) + loop) + rule + nb-confs)) + (def-loop (cdr defs) nb-confs))))) + rule-loop) + (cdr def) + nb-confs)) + nb-confs)))) + def-loop) + grammar + '0)))) + (let ((nts (non-terminals grammar))) + (let ((nb-nts (vector-length nts))) + (let ((nb-confs (+ (nb-configurations grammar) nb-nts))) + (let ((starters (make-vector nb-nts '()))) + (let ((enders (make-vector nb-nts '()))) + (let ((predictors (make-vector nb-nts '()))) + (let ((steps (make-vector nb-confs '#f))) + (let ((names (make-vector nb-confs '#f))) + (letrec ((setup-tables + (lambda (grammar nts starters enders predictors steps names) + (letrec ((add-conf + (lambda (conf nt nts class) + (let ((i (ind nt nts))) + (vector-set! class i (cons conf (vector-ref class i))))))) + (let ((nb-nts (vector-length nts))) + ((letrec ((nt-loop + (lambda (i) + (if (>= i '0) + (begin + (vector-set! steps i (- i nb-nts)) + (vector-set! names i (list (vector-ref nts i) '0)) + (vector-set! enders i (list i)) + (nt-loop (- i '1))) + '#f)))) + nt-loop) + (- nb-nts '1)) + ((letrec ((def-loop + (lambda (defs conf) + (if (pair? defs) + (let ((def (car defs))) + (let ((head (car def))) + ((letrec ((rule-loop + (lambda (rules conf rule-num) + (if (pair? rules) + (let ((rule (car rules))) + (vector-set! + names + conf + (list head rule-num)) + (add-conf conf head nts starters) + ((letrec ((loop + (lambda (l conf) + (if (pair? l) + (let ((nt (car l))) + (vector-set! + steps + conf + (ind nt nts)) + (add-conf + conf + nt + nts + predictors) + (loop + (cdr l) + (+ conf '1))) + (begin + (vector-set! + steps + conf + (- + (ind head nts) + nb-nts)) + (add-conf + conf + head + nts + enders) + (rule-loop + (cdr rules) + (+ conf '1) + (+ rule-num '1))))))) + loop) + rule + conf)) + (def-loop (cdr defs) conf))))) + rule-loop) + (cdr def) + conf + '1))) + '#f)))) + def-loop) + grammar + (vector-length nts))))))) + (setup-tables grammar nts starters enders predictors steps names) + (let ((parser-descr (vector lexer nts starters enders predictors steps names))) + (lambda (input) + (letrec ((ind + (lambda (nt nts) + ((letrec ((loop + (lambda (i) + (if (>= i '0) + (if (equal? (vector-ref nts i) nt) i (loop (- i '1))) + '#f)))) + loop) + (- (vector-length nts) '1)))) + (comp-tok + (lambda (tok nts) + ((letrec ((loop + (lambda (l1 l2) + (if (pair? l1) + (let ((i (ind (car l1) nts))) + (if i (loop (cdr l1) (cons i l2)) (loop (cdr l1) l2))) + (cons (car tok) (reverse l2)))))) + loop) + (cdr tok) + '()))) + (input->tokens + (lambda (input lexer nts) + (list->vector (map (lambda (tok) (comp-tok tok nts)) (lexer input))))) + (make-states + (lambda (nb-toks nb-confs) + (let ((states (make-vector (+ nb-toks '1) '#f))) + ((letrec ((loop + (lambda (i) + (if (>= i '0) + (let ((v (make-vector (+ nb-confs '1) '#f))) + (vector-set! v '0 '-1) + (vector-set! states i v) + (loop (- i '1))) + states)))) + loop) + nb-toks)))) + (conf-set-get (lambda (state conf) (vector-ref state (+ conf '1)))) + (conf-set-get* + (lambda (state state-num conf) + (let ((conf-set (conf-set-get state conf))) + (if conf-set + conf-set + (let ((conf-set (make-vector (+ state-num '6) '#f))) + (vector-set! conf-set '1 '-3) + (vector-set! conf-set '2 '-1) + (vector-set! conf-set '3 '-1) + (vector-set! conf-set '4 '-1) + (vector-set! state (+ conf '1) conf-set) + conf-set))))) + (conf-set-merge-new! + (lambda (conf-set) + (vector-set! + conf-set + (+ (vector-ref conf-set '1) '5) + (vector-ref conf-set '4)) + (vector-set! conf-set '1 (vector-ref conf-set '3)) + (vector-set! conf-set '3 '-1) + (vector-set! conf-set '4 '-1))) + (conf-set-head (lambda (conf-set) (vector-ref conf-set '2))) + (conf-set-next (lambda (conf-set i) (vector-ref conf-set (+ i '5)))) + (conf-set-member? + (lambda (state conf i) + (let ((conf-set (vector-ref state (+ conf '1)))) + (if conf-set (conf-set-next conf-set i) '#f)))) + (conf-set-adjoin + (lambda (state conf-set conf i) + (let ((tail (vector-ref conf-set '3))) + (vector-set! conf-set (+ i '5) '-1) + (vector-set! conf-set (+ tail '5) i) + (vector-set! conf-set '3 i) + (if (< tail '0) + (begin + (vector-set! conf-set '0 (vector-ref state '0)) + (vector-set! state '0 conf)) + '#f)))) + (conf-set-adjoin* + (lambda (states state-num l i) + (let ((state (vector-ref states state-num))) + ((letrec ((loop + (lambda (l1) + (if (pair? l1) + (let ((conf (car l1))) + (let ((conf-set (conf-set-get* state state-num conf))) + (if (not (conf-set-next conf-set i)) + (begin + (conf-set-adjoin state conf-set conf i) + (loop (cdr l1))) + (loop (cdr l1))))) + '#f)))) + loop) + l)))) + (conf-set-adjoin** + (lambda (states states* state-num conf i) + (let ((state (vector-ref states state-num))) + (if (conf-set-member? state conf i) + (let ((state* (vector-ref states* state-num))) + (let ((conf-set* (conf-set-get* state* state-num conf))) + (if (not (conf-set-next conf-set* i)) + (conf-set-adjoin state* conf-set* conf i) + '#f) + '#t)) + '#f)))) + (conf-set-union + (lambda (state conf-set conf other-set) + ((letrec ((loop + (lambda (i) + (if (>= i '0) + (if (not (conf-set-next conf-set i)) + (begin + (conf-set-adjoin state conf-set conf i) + (loop (conf-set-next other-set i))) + (loop (conf-set-next other-set i))) + '#f)))) + loop) + (conf-set-head other-set)))) + (forw + (lambda (states state-num starters enders predictors steps nts) + (letrec ((predict + (lambda (state state-num conf-set conf nt starters enders) + ((letrec ((loop1 + (lambda (l) + (if (pair? l) + (let ((starter (car l))) + (let ((starter-set + (conf-set-get* + state + state-num + starter))) + (if (not + (conf-set-next + starter-set + state-num)) + (begin + (conf-set-adjoin + state + starter-set + starter + state-num) + (loop1 (cdr l))) + (loop1 (cdr l))))) + '#f)))) + loop1) + (vector-ref starters nt)) + ((letrec ((loop2 + (lambda (l) + (if (pair? l) + (let ((ender (car l))) + (if (conf-set-member? state ender state-num) + (let ((next (+ conf '1))) + (let ((next-set + (conf-set-get* + state + state-num + next))) + (conf-set-union + state + next-set + next + conf-set) + (loop2 (cdr l)))) + (loop2 (cdr l)))) + '#f)))) + loop2) + (vector-ref enders nt)))) + (reduce + (lambda (states state state-num conf-set head preds) + ((letrec ((loop1 + (lambda (l) + (if (pair? l) + (let ((pred (car l))) + ((letrec ((loop2 + (lambda (i) + (if (>= i '0) + (let ((pred-set + (conf-set-get + (vector-ref states i) + pred))) + (if pred-set + (let ((next (+ pred '1))) + (let ((next-set + (conf-set-get* + state + state-num + next))) + (conf-set-union + state + next-set + next + pred-set))) + '#f) + (loop2 + (conf-set-next + conf-set + i))) + (loop1 (cdr l)))))) + loop2) + head)) + '#f)))) + loop1) + preds)))) + (let ((state (vector-ref states state-num)) + (nb-nts (vector-length nts))) + ((letrec ((loop + (lambda () + (let ((conf (vector-ref state '0))) + (if (>= conf '0) + (let ((step (vector-ref steps conf))) + (let ((conf-set (vector-ref state (+ conf '1)))) + (let ((head (vector-ref conf-set '4))) + (vector-set! + state + '0 + (vector-ref conf-set '0)) + (conf-set-merge-new! conf-set) + (if (>= step '0) + (predict + state + state-num + conf-set + conf + step + starters + enders) + (let ((preds + (vector-ref + predictors + (+ step nb-nts)))) + (reduce + states + state + state-num + conf-set + head + preds))) + (loop)))) + '#f))))) + loop)))))) + (forward + (lambda (starters enders predictors steps nts toks) + (let ((nb-toks (vector-length toks))) + (let ((nb-confs (vector-length steps))) + (let ((states (make-states nb-toks nb-confs))) + (let ((goal-starters (vector-ref starters '0))) + (conf-set-adjoin* states '0 goal-starters '0) + (forw states '0 starters enders predictors steps nts) + ((letrec ((loop + (lambda (i) + (if (< i nb-toks) + (let ((tok-nts (cdr (vector-ref toks i)))) + (conf-set-adjoin* states (+ i '1) tok-nts i) + (forw + states + (+ i '1) + starters + enders + predictors + steps + nts) + (loop (+ i '1))) + '#f)))) + loop) + '0) + states)))))) + (produce + (lambda (conf i j enders steps toks states states* nb-nts) + (let ((prev (- conf '1))) + (if (if (>= conf nb-nts) (>= (vector-ref steps prev) '0) '#f) + ((letrec ((loop1 + (lambda (l) + (if (pair? l) + (let ((ender (car l))) + (let ((ender-set + (conf-set-get (vector-ref states j) ender))) + (if ender-set + ((letrec ((loop2 + (lambda (k) + (if (>= k '0) + (begin + (if (>= k i) + (if (conf-set-adjoin** + states + states* + k + prev + i) + (conf-set-adjoin** + states + states* + j + ender + k) + '#f) + '#f) + (loop2 + (conf-set-next ender-set k))) + (loop1 (cdr l)))))) + loop2) + (conf-set-head ender-set)) + (loop1 (cdr l))))) + '#f)))) + loop1) + (vector-ref enders (vector-ref steps prev))) + '#f)))) + (back + (lambda (states states* state-num enders steps nb-nts toks) + (let ((state* (vector-ref states* state-num))) + ((letrec ((loop1 + (lambda () + (let ((conf (vector-ref state* '0))) + (if (>= conf '0) + (let ((conf-set (vector-ref state* (+ conf '1)))) + (let ((head (vector-ref conf-set '4))) + (vector-set! state* '0 (vector-ref conf-set '0)) + (conf-set-merge-new! conf-set) + ((letrec ((loop2 + (lambda (i) + (if (>= i '0) + (begin + (produce + conf + i + state-num + enders + steps + toks + states + states* + nb-nts) + (loop2 + (conf-set-next conf-set i))) + (loop1))))) + loop2) + head))) + '#f))))) + loop1))))) + (backward + (lambda (states enders steps nts toks) + (let ((nb-toks (vector-length toks))) + (let ((nb-confs (vector-length steps))) + (let ((nb-nts (vector-length nts))) + (let ((states* (make-states nb-toks nb-confs))) + (let ((goal-enders (vector-ref enders '0))) + ((letrec ((loop1 + (lambda (l) + (if (pair? l) + (let ((conf (car l))) + (conf-set-adjoin** + states + states* + nb-toks + conf + '0) + (loop1 (cdr l))) + '#f)))) + loop1) + goal-enders) + ((letrec ((loop2 + (lambda (i) + (if (>= i '0) + (begin + (back + states + states* + i + enders + steps + nb-nts + toks) + (loop2 (- i '1))) + '#f)))) + loop2) + nb-toks) + states*))))))) + (parsed? + (lambda (nt i j nts enders states) + (let ((nt* (ind nt nts))) + (if nt* + (let ((nb-nts (vector-length nts))) + ((letrec ((loop + (lambda (l) + (if (pair? l) + (let ((conf (car l))) + (if (conf-set-member? + (vector-ref states j) + conf + i) + '#t + (loop (cdr l)))) + '#f)))) + loop) + (vector-ref enders nt*))) + '#f)))) + (deriv-trees + (lambda (conf i j enders steps names toks states nb-nts) + (let ((name (vector-ref names conf))) + (if name + (if (< conf nb-nts) + (list (list name (car (vector-ref toks i)))) + (list (list name))) + (let ((prev (- conf '1))) + ((letrec ((loop1 + (lambda (l1 l2) + (if (pair? l1) + (let ((ender (car l1))) + (let ((ender-set + (conf-set-get + (vector-ref states j) + ender))) + (if ender-set + ((letrec ((loop2 + (lambda (k l2) + (if (>= k '0) + (if (if (>= k i) + (conf-set-member? + (vector-ref states k) + prev + i) + '#f) + (let ((prev-trees + (deriv-trees + prev + i + k + enders + steps + names + toks + states + nb-nts)) + (ender-trees + (deriv-trees + ender + k + j + enders + steps + names + toks + states + nb-nts))) + ((letrec ((loop3 + (lambda (l3 l2) + (if (pair? l3) + (let ((ender-tree + (list + (car + l3)))) + ((letrec ((loop4 + (lambda (l4 + l2) + (if (pair? + l4) + (loop4 + (cdr + l4) + (cons + (append + (car + l4) + ender-tree) + l2)) + (loop3 + (cdr + l3) + l2))))) + loop4) + prev-trees + l2)) + (loop2 + (conf-set-next + ender-set + k) + l2))))) + loop3) + ender-trees + l2)) + (loop2 + (conf-set-next ender-set k) + l2)) + (loop1 (cdr l1) l2))))) + loop2) + (conf-set-head ender-set) + l2) + (loop1 (cdr l1) l2)))) + l2)))) + loop1) + (vector-ref enders (vector-ref steps prev)) + '())))))) + (deriv-trees* + (lambda (nt i j nts enders steps names toks states) + (let ((nt* (ind nt nts))) + (if nt* + (let ((nb-nts (vector-length nts))) + ((letrec ((loop + (lambda (l trees) + (if (pair? l) + (let ((conf (car l))) + (if (conf-set-member? + (vector-ref states j) + conf + i) + (loop + (cdr l) + (append + (deriv-trees + conf + i + j + enders + steps + names + toks + states + nb-nts) + trees)) + (loop (cdr l) trees))) + trees)))) + loop) + (vector-ref enders nt*) + '())) + '#f)))) + (nb-deriv-trees + (lambda (conf i j enders steps toks states nb-nts) + (let ((prev (- conf '1))) + (if (let ((or-part (< conf nb-nts))) + (if or-part or-part (< (vector-ref steps prev) '0))) + '1 + ((letrec ((loop1 + (lambda (l n) + (if (pair? l) + (let ((ender (car l))) + (let ((ender-set + (conf-set-get (vector-ref states j) ender))) + (if ender-set + ((letrec ((loop2 + (lambda (k n) + (if (>= k '0) + (if (if (>= k i) + (conf-set-member? + (vector-ref states k) + prev + i) + '#f) + (let ((nb-prev-trees + (nb-deriv-trees + prev + i + k + enders + steps + toks + states + nb-nts)) + (nb-ender-trees + (nb-deriv-trees + ender + k + j + enders + steps + toks + states + nb-nts))) + (loop2 + (conf-set-next ender-set k) + (+ + n + (* + nb-prev-trees + nb-ender-trees)))) + (loop2 + (conf-set-next ender-set k) + n)) + (loop1 (cdr l) n))))) + loop2) + (conf-set-head ender-set) + n) + (loop1 (cdr l) n)))) + n)))) + loop1) + (vector-ref enders (vector-ref steps prev)) + '0))))) + (nb-deriv-trees* + (lambda (nt i j nts enders steps toks states) + (let ((nt* (ind nt nts))) + (if nt* + (let ((nb-nts (vector-length nts))) + ((letrec ((loop + (lambda (l nb-trees) + (if (pair? l) + (let ((conf (car l))) + (if (conf-set-member? + (vector-ref states j) + conf + i) + (loop + (cdr l) + (+ + (nb-deriv-trees + conf + i + j + enders + steps + toks + states + nb-nts) + nb-trees)) + (loop (cdr l) nb-trees))) + nb-trees)))) + loop) + (vector-ref enders nt*) + '0)) + '#f))))) + (let ((lexer (vector-ref parser-descr '0))) + (let ((nts (vector-ref parser-descr '1))) + (let ((starters (vector-ref parser-descr '2))) + (let ((enders (vector-ref parser-descr '3))) + (let ((predictors (vector-ref parser-descr '4))) + (let ((steps (vector-ref parser-descr '5))) + (let ((names (vector-ref parser-descr '6))) + (let ((toks (input->tokens input lexer nts))) + (vector + nts + starters + enders + predictors + steps + names + toks + (backward + (forward starters enders predictors steps nts toks) + enders + steps + nts + toks) + parsed? + deriv-trees* + nb-deriv-trees*)))))))))))))))))))))))) + (define parse->parsed? + (lambda (parse nt i j) + (let ((nts (vector-ref parse '0))) + (let ((enders (vector-ref parse '2))) + (let ((states (vector-ref parse '7))) + (let ((parsed? (vector-ref parse '8))) (parsed? nt i j nts enders states))))))) + (define parse->trees + (lambda (parse nt i j) + (let ((nts (vector-ref parse '0))) + (let ((enders (vector-ref parse '2))) + (let ((steps (vector-ref parse '4))) + (let ((names (vector-ref parse '5))) + (let ((toks (vector-ref parse '6))) + (let ((states (vector-ref parse '7))) + (let ((deriv-trees* (vector-ref parse '9))) + (deriv-trees* nt i j nts enders steps names toks states)))))))))) + (define parse->nb-trees + (lambda (parse nt i j) + (let ((nts (vector-ref parse '0))) + (let ((enders (vector-ref parse '2))) + (let ((steps (vector-ref parse '4))) + (let ((toks (vector-ref parse '6))) + (let ((states (vector-ref parse '7))) + (let ((nb-deriv-trees* (vector-ref parse '10))) + (nb-deriv-trees* nt i j nts enders steps toks states))))))))) + (define test + (lambda (k) + (let ((p (make-parser '((s (a) (s s))) + (lambda (l) + (map (lambda (x) (list x x)) l))))) + (let ((x (p (vector->list (make-vector k 'a))))) + (display (length (parse->trees x 's '0 k))) + (newline))))) + (test '12)) diff --git a/tests/test-assemble.rkt b/tests/test-assemble.rkt index 5efb59a..42a6354 100644 --- a/tests/test-assemble.rkt +++ b/tests/test-assemble.rkt @@ -45,11 +45,12 @@ "(function() { " runtime + "var RUNTIME = plt.runtime;" "var MACHINE = new plt.runtime.Machine();\n" "return function(success, fail, params){" snippet - (format "success(String(~a)); };" inspector) + (format "success(plt.runtime.toDisplayedString(~a)); };" inspector) "});")]) (displayln snippet) (display code op)))))) @@ -63,6 +64,7 @@ [inspector (cdr a-statement+inspector)]) (display runtime op) + "var RUNTIME = plt.runtime;" (display "var MACHINE = new plt.runtime.Machine();\n" op) (display "(function() { " op) (display "var myInvoke = " op) @@ -70,7 +72,7 @@ (display ";" op) (fprintf op - "return function(succ, fail, params) { myInvoke(MACHINE, function(v) { succ(String(~a));}, fail, params); }" + "return function(succ, fail, params) { myInvoke(MACHINE, function(v) { succ(plt.runtime.toDisplayedString(~a));}, fail, params); }" inspector) (display "})" op)))))) (define (E-many stmts (inspector "MACHINE.val")) @@ -91,13 +93,13 @@ "Danny") ;; Assigning a cons (test (E-single (make-AssignImmediateStatement 'val (make-Const (cons 1 2)))) - "1,2") + "(1 . 2)") ;; Assigning a void (test (E-single (make-AssignImmediateStatement 'val (make-Const (void)))) - "null") + "#") ;; Assigning to proc means val should still be uninitialized. (test (E-single (make-AssignImmediateStatement 'proc (make-Const "Danny"))) - "undefined") + "#") ;; But we should see the assignment if we inspect MACHINE.proc. (test (E-single (make-AssignImmediateStatement 'proc (make-Const "Danny")) "MACHINE.proc") @@ -133,7 +135,7 @@ (make-AssignImmediateStatement (make-EnvLexicalReference 0 #f) (make-Const 12345))) "MACHINE.env[0]") - "undefined") + "#") (test (E-many (list (make-PushEnvironment 2 #f) (make-AssignImmediateStatement (make-EnvLexicalReference 1 #f) (make-Const 12345))) @@ -143,7 +145,7 @@ ;; Toplevel Environment loading (test (E-single (make-PerformStatement (make-ExtendEnvironment/Prefix! '(pi))) - "String(MACHINE.env[0]).slice(0, 5)") + "plt.runtime.toWrittenString(MACHINE.env[0]).slice(0, 5)") "3.141") @@ -210,7 +212,7 @@ (make-Const 0)) (make-GotoStatement (make-Label 'closureStart)) 'theEnd) - "String(MACHINE.env.length) + ',' + MACHINE.env[1] + ',' + MACHINE.env[0]") + "plt.runtime.toWrittenString(MACHINE.env.length) + ',' + MACHINE.env[1] + ',' + MACHINE.env[0]") "2,hello,world") diff --git a/tests/test-browser-evaluate.rkt b/tests/test-browser-evaluate.rkt index 204ff36..a4a5809 100644 --- a/tests/test-browser-evaluate.rkt +++ b/tests/test-browser-evaluate.rkt @@ -1,12 +1,12 @@ #lang racket (require "browser-evaluate.rkt" "../js-assembler/package.rkt" - "../make-structs.rkt") + "../make/make-structs.rkt") (printf "test-browser-evaluate.rkt\n") -(define should-follow? (lambda (src p) #t)) +(define should-follow? (lambda (src) #t)) (define evaluate (make-evaluate (lambda (program op) @@ -20,7 +20,7 @@ (fprintf op "var innerInvoke = ") (package-anonymous (make-SexpSource program) - #:should-follow? should-follow? + #:should-follow-children? should-follow? #:output-port op) (fprintf op "();\n") @@ -63,7 +63,7 @@ EOF (let ([r (evaluate s)]) (raise-syntax-error #f (format "Expected exception, but got ~s" r) #'stx)))]) - (unless (string=? exp (error-happened-str an-error-happened)) + (unless (regexp-match (regexp-quote exp) (error-happened-str an-error-happened)) (printf " error!\n") (raise-syntax-error #f (format "Expected ~s, got ~s" exp (error-happened-str an-error-happened)) #'stx)) @@ -99,20 +99,21 @@ EOF "7") (test/exn (evaluate '(+ "hello" 3)) - "Error: +: expected number as argument 1 but received hello") + "Error: +: expected number as argument 1 but received \"hello\"") (test '(display (/ 100 4)) "25") +;; fixme: symbols need to be represented separately from strings. (test/exn (evaluate '(/ 3 'four)) - "Error: /: expected number as argument 2 but received four") + "Error: /: expected number as argument 2 but received \"four\"") (test '(display (- 1)) "-1") (test/exn '(- 'one) - "Error: -: expected number as argument 1 but received one") + "Error: -: expected number as argument 1 but received \"one\"") (test '(display (- 5 4)) "1") @@ -121,7 +122,7 @@ EOF "51") (test/exn '(* "three" 17) - "Error: *: expected number as argument 1 but received three") + "Error: *: expected number as argument 1 but received \"three\"") (test '(display '#t) "true") @@ -154,13 +155,13 @@ EOF (test/exn '(displayln (add1 "0")) - "Error: add1: expected number as argument 1 but received 0") + "Error: add1: expected number as argument 1 but received \"0\"") (test '(displayln (sub1 1)) "0\n") (test/exn '(displayln (sub1 "0")) - "Error: sub1: expected number as argument 1 but received 0") + "Error: sub1: expected number as argument 1 but received \"0\"") (test '(displayln (< 1 2)) "true\n") @@ -381,7 +382,7 @@ EOF (test/exn '(let ([x 0]) (set! x "foo") (add1 x)) - "Error: add1: expected number as argument 1 but received foo") + "Error: add1: expected number as argument 1 but received \"foo\"") @@ -479,6 +480,31 @@ EOF "false\n") +;;(test '(displayln 2/3) +;; "2/3\n") + + +;;(test '(displayln -2/3) +;; "-2/3\n") + + +(test '(displayln -0.0) + "-0.0\n") + +(test '(displayln +nan.0) + "+nan.0\n") + +(test '(displayln +inf.0) + "+inf.0\n") + +(test '(displayln -inf.0) + "-inf.0\n") + + + + + + ;; Knuth's Man-or-boy-test. diff --git a/tests/test-compiler.rkt b/tests/test-compiler.rkt index 1bf1baf..2818d55 100644 --- a/tests/test-compiler.rkt +++ b/tests/test-compiler.rkt @@ -1368,7 +1368,6 @@ 20116) - (test '(let () (define (f x y z) (values y x z)) (call-with-values (lambda () (f 3 1 4)) @@ -1376,6 +1375,7 @@ '((1 3 4)) #:with-bootstrapping? #t) + (test '(let () (define (f x y z) (begin0 (values y x z) (display ""))) diff --git a/tests/test-conform-browser.rkt b/tests/test-conform-browser.rkt index 076384f..47c2816 100644 --- a/tests/test-conform-browser.rkt +++ b/tests/test-conform-browser.rkt @@ -1,7 +1,7 @@ #lang racket (require "browser-evaluate.rkt" "../js-assembler/package.rkt" - "../make-structs.rkt" + "../make/make-structs.rkt" racket/port racket/runtime-path) @@ -21,7 +21,7 @@ (fprintf op "var innerInvoke = ") (package-anonymous (make-SexpSource program) - #:should-follow? (lambda (src p) #t) + #:should-follow-children? (lambda (src) #t) #:output-port op) (fprintf op "();\n") diff --git a/tests/test-earley-browser.rkt b/tests/test-earley-browser.rkt index 8b7f6e8..37d406e 100644 --- a/tests/test-earley-browser.rkt +++ b/tests/test-earley-browser.rkt @@ -1,7 +1,7 @@ #lang racket (require "browser-evaluate.rkt" "../js-assembler/package.rkt" - "../make-structs.rkt" + "../make/make-structs.rkt" racket/port racket/runtime-path racket/runtime-path @@ -23,7 +23,7 @@ (fprintf op "var innerInvoke = ") (package-anonymous (make-SexpSource program) - #:should-follow? (lambda (src path) #t) + #:should-follow-children? (lambda (src) #t) #:output-port op) (fprintf op "();\n") diff --git a/tests/test-get-dependencies.rkt b/tests/test-get-dependencies.rkt index 5e1e8c9..325c885 100644 --- a/tests/test-get-dependencies.rkt +++ b/tests/test-get-dependencies.rkt @@ -1,5 +1,5 @@ #lang racket -(require "../get-dependencies.rkt" +(require "../make/get-dependencies.rkt" "../get-module-bytecode.rkt" "../parser/parse-bytecode.rkt" "../compiler/lexical-structs.rkt" @@ -11,7 +11,7 @@ (define-runtime-path get-dependencies-path - (build-path ".." "get-dependencies.rkt")) + (build-path ".." "make" "get-dependencies.rkt")) (define-runtime-path get-module-bytecode-path (build-path ".." "get-module-bytecode.rkt")) diff --git a/tests/test-package.rkt b/tests/test-package.rkt index e5ff135..d95a8e6 100644 --- a/tests/test-package.rkt +++ b/tests/test-package.rkt @@ -1,17 +1,17 @@ #lang racket/base (require "../js-assembler/package.rkt" - "../make-structs.rkt") + "../make/make-structs.rkt") (printf "test-package.rkt\n") -(define (follow? src p) +(define (follow? src) #t) (define (test s-exp) (package (make-SexpSource s-exp) - #:should-follow? follow? + #:should-follow-children? follow? #:output-port (open-output-string) #;(current-output-port))) diff --git a/whalesong.rkt b/whalesong.rkt index b405ca2..b026a5a 100755 --- a/whalesong.rkt +++ b/whalesong.rkt @@ -3,7 +3,7 @@ (require racket/list racket/string - "make-structs.rkt" + "make/make-structs.rkt" "js-assembler/package.rkt") diff --git a/world.rkt b/world.rkt new file mode 100644 index 0000000..a0b0b51 --- /dev/null +++ b/world.rkt @@ -0,0 +1,3 @@ +#lang s-exp "lang/base.rkt" +(require "world/main.rkt") +(provide (all-from-out "world/main.rkt")) \ No newline at end of file diff --git a/world/colordb.js b/world/colordb.js new file mode 100644 index 0000000..6f2bcf2 --- /dev/null +++ b/world/colordb.js @@ -0,0 +1,205 @@ +// Color database +var ColorDb = function() { + this.colors = {}; +}; + +var types = plt.types; + + +ColorDb.prototype.put = function(name, color) { + this.colors[name] = color; +}; + +ColorDb.prototype.get = function(name) { + return this.colors[name.toString().toUpperCase()]; +}; + + +// FIXME: update toString to handle the primitive field values. + +var colorDb = new ColorDb(); +colorDb.put("ORANGE", types.color(255, 165, 0)); +colorDb.put("RED", types.color(255, 0, 0)); +colorDb.put("ORANGERED", types.color(255, 69, 0)); +colorDb.put("TOMATO", types.color(255, 99, 71)); +colorDb.put("DARKRED", types.color(139, 0, 0)); +colorDb.put("RED", types.color(255, 0, 0)); +colorDb.put("FIREBRICK", types.color(178, 34, 34)); +colorDb.put("CRIMSON", types.color(220, 20, 60)); +colorDb.put("DEEPPINK", types.color(255, 20, 147)); +colorDb.put("MAROON", types.color(176, 48, 96)); +colorDb.put("INDIAN RED", types.color(205, 92, 92)); +colorDb.put("INDIANRED", types.color(205, 92, 92)); +colorDb.put("MEDIUM VIOLET RED", types.color(199, 21, 133)); +colorDb.put("MEDIUMVIOLETRED", types.color(199, 21, 133)); +colorDb.put("VIOLET RED", types.color(208, 32, 144)); +colorDb.put("VIOLETRED", types.color(208, 32, 144)); +colorDb.put("LIGHTCORAL", types.color(240, 128, 128)); +colorDb.put("HOTPINK", types.color(255, 105, 180)); +colorDb.put("PALEVIOLETRED", types.color(219, 112, 147)); +colorDb.put("LIGHTPINK", types.color(255, 182, 193)); +colorDb.put("ROSYBROWN", types.color(188, 143, 143)); +colorDb.put("PINK", types.color(255, 192, 203)); +colorDb.put("ORCHID", types.color(218, 112, 214)); +colorDb.put("LAVENDERBLUSH", types.color(255, 240, 245)); +colorDb.put("SNOW", types.color(255, 250, 250)); +colorDb.put("CHOCOLATE", types.color(210, 105, 30)); +colorDb.put("SADDLEBROWN", types.color(139, 69, 19)); +colorDb.put("BROWN", types.color(132, 60, 36)); +colorDb.put("DARKORANGE", types.color(255, 140, 0)); +colorDb.put("CORAL", types.color(255, 127, 80)); +colorDb.put("SIENNA", types.color(160, 82, 45)); +colorDb.put("ORANGE", types.color(255, 165, 0)); +colorDb.put("SALMON", types.color(250, 128, 114)); +colorDb.put("PERU", types.color(205, 133, 63)); +colorDb.put("DARKGOLDENROD", types.color(184, 134, 11)); +colorDb.put("GOLDENROD", types.color(218, 165, 32)); +colorDb.put("SANDYBROWN", types.color(244, 164, 96)); +colorDb.put("LIGHTSALMON", types.color(255, 160, 122)); +colorDb.put("DARKSALMON", types.color(233, 150, 122)); +colorDb.put("GOLD", types.color(255, 215, 0)); +colorDb.put("YELLOW", types.color(255, 255, 0)); +colorDb.put("OLIVE", types.color(128, 128, 0)); +colorDb.put("BURLYWOOD", types.color(222, 184, 135)); +colorDb.put("TAN", types.color(210, 180, 140)); +colorDb.put("NAVAJOWHITE", types.color(255, 222, 173)); +colorDb.put("PEACHPUFF", types.color(255, 218, 185)); +colorDb.put("KHAKI", types.color(240, 230, 140)); +colorDb.put("DARKKHAKI", types.color(189, 183, 107)); +colorDb.put("MOCCASIN", types.color(255, 228, 181)); +colorDb.put("WHEAT", types.color(245, 222, 179)); +colorDb.put("BISQUE", types.color(255, 228, 196)); +colorDb.put("PALEGOLDENROD", types.color(238, 232, 170)); +colorDb.put("BLANCHEDALMOND", types.color(255, 235, 205)); +colorDb.put("MEDIUM GOLDENROD", types.color(234, 234, 173)); +colorDb.put("MEDIUMGOLDENROD", types.color(234, 234, 173)); +colorDb.put("PAPAYAWHIP", types.color(255, 239, 213)); +colorDb.put("MISTYROSE", types.color(255, 228, 225)); +colorDb.put("LEMONCHIFFON", types.color(255, 250, 205)); +colorDb.put("ANTIQUEWHITE", types.color(250, 235, 215)); +colorDb.put("CORNSILK", types.color(255, 248, 220)); +colorDb.put("LIGHTGOLDENRODYELLOW", types.color(250, 250, 210)); +colorDb.put("OLDLACE", types.color(253, 245, 230)); +colorDb.put("LINEN", types.color(250, 240, 230)); +colorDb.put("LIGHTYELLOW", types.color(255, 255, 224)); +colorDb.put("SEASHELL", types.color(255, 245, 238)); +colorDb.put("BEIGE", types.color(245, 245, 220)); +colorDb.put("FLORALWHITE", types.color(255, 250, 240)); +colorDb.put("IVORY", types.color(255, 255, 240)); +colorDb.put("GREEN", types.color(0, 255, 0)); +colorDb.put("LAWNGREEN", types.color(124, 252, 0)); +colorDb.put("CHARTREUSE", types.color(127, 255, 0)); +colorDb.put("GREEN YELLOW", types.color(173, 255, 47)); +colorDb.put("GREENYELLOW", types.color(173, 255, 47)); +colorDb.put("YELLOW GREEN", types.color(154, 205, 50)); +colorDb.put("YELLOWGREEN", types.color(154, 205, 50)); +colorDb.put("MEDIUM FOREST GREEN", types.color(107, 142, 35)); +colorDb.put("OLIVEDRAB", types.color(107, 142, 35)); +colorDb.put("MEDIUMFORESTGREEN", types.color(107, 142, 35)); +colorDb.put("DARK OLIVE GREEN", types.color(85, 107, 47)); +colorDb.put("DARKOLIVEGREEN", types.color(85, 107, 47)); +colorDb.put("DARKSEAGREEN", types.color(143, 188, 139)); +colorDb.put("LIME", types.color(0, 255, 0)); +colorDb.put("DARK GREEN", types.color(0, 100, 0)); +colorDb.put("DARKGREEN", types.color(0, 100, 0)); +colorDb.put("LIME GREEN", types.color(50, 205, 50)); +colorDb.put("LIMEGREEN", types.color(50, 205, 50)); +colorDb.put("FOREST GREEN", types.color(34, 139, 34)); +colorDb.put("FORESTGREEN", types.color(34, 139, 34)); +colorDb.put("SPRING GREEN", types.color(0, 255, 127)); +colorDb.put("SPRINGGREEN", types.color(0, 255, 127)); +colorDb.put("MEDIUM SPRING GREEN", types.color(0, 250, 154)); +colorDb.put("MEDIUMSPRINGGREEN", types.color(0, 250, 154)); +colorDb.put("SEA GREEN", types.color(46, 139, 87)); +colorDb.put("SEAGREEN", types.color(46, 139, 87)); +colorDb.put("MEDIUM SEA GREEN", types.color(60, 179, 113)); +colorDb.put("MEDIUMSEAGREEN", types.color(60, 179, 113)); +colorDb.put("AQUAMARINE", types.color(112, 216, 144)); +colorDb.put("LIGHTGREEN", types.color(144, 238, 144)); +colorDb.put("PALE GREEN", types.color(152, 251, 152)); +colorDb.put("PALEGREEN", types.color(152, 251, 152)); +colorDb.put("MEDIUM AQUAMARINE", types.color(102, 205, 170)); +colorDb.put("MEDIUMAQUAMARINE", types.color(102, 205, 170)); +colorDb.put("TURQUOISE", types.color(64, 224, 208)); +colorDb.put("LIGHTSEAGREEN", types.color(32, 178, 170)); +colorDb.put("MEDIUM TURQUOISE", types.color(72, 209, 204)); +colorDb.put("MEDIUMTURQUOISE", types.color(72, 209, 204)); +colorDb.put("HONEYDEW", types.color(240, 255, 240)); +colorDb.put("MINTCREAM", types.color(245, 255, 250)); +colorDb.put("ROYALBLUE", types.color(65, 105, 225)); +colorDb.put("DODGERBLUE", types.color(30, 144, 255)); +colorDb.put("DEEPSKYBLUE", types.color(0, 191, 255)); +colorDb.put("CORNFLOWERBLUE", types.color(100, 149, 237)); +colorDb.put("STEEL BLUE", types.color(70, 130, 180)); +colorDb.put("STEELBLUE", types.color(70, 130, 180)); +colorDb.put("LIGHTSKYBLUE", types.color(135, 206, 250)); +colorDb.put("DARK TURQUOISE", types.color(0, 206, 209)); +colorDb.put("DARKTURQUOISE", types.color(0, 206, 209)); +colorDb.put("CYAN", types.color(0, 255, 255)); +colorDb.put("AQUA", types.color(0, 255, 255)); +colorDb.put("DARKCYAN", types.color(0, 139, 139)); +colorDb.put("TEAL", types.color(0, 128, 128)); +colorDb.put("SKY BLUE", types.color(135, 206, 235)); +colorDb.put("SKYBLUE", types.color(135, 206, 235)); +colorDb.put("CADET BLUE", types.color(96, 160, 160)); +colorDb.put("CADETBLUE", types.color(95, 158, 160)); +colorDb.put("DARK SLATE GRAY", types.color(47, 79, 79)); +colorDb.put("DARKSLATEGRAY", types.color(47, 79, 79)); +colorDb.put("LIGHTSLATEGRAY", types.color(119, 136, 153)); +colorDb.put("SLATEGRAY", types.color(112, 128, 144)); +colorDb.put("LIGHT STEEL BLUE", types.color(176, 196, 222)); +colorDb.put("LIGHTSTEELBLUE", types.color(176, 196, 222)); +colorDb.put("LIGHT BLUE", types.color(173, 216, 230)); +colorDb.put("LIGHTBLUE", types.color(173, 216, 230)); +colorDb.put("POWDERBLUE", types.color(176, 224, 230)); +colorDb.put("PALETURQUOISE", types.color(175, 238, 238)); +colorDb.put("LIGHTCYAN", types.color(224, 255, 255)); +colorDb.put("ALICEBLUE", types.color(240, 248, 255)); +colorDb.put("AZURE", types.color(240, 255, 255)); +colorDb.put("MEDIUM BLUE", types.color(0, 0, 205)); +colorDb.put("MEDIUMBLUE", types.color(0, 0, 205)); +colorDb.put("DARKBLUE", types.color(0, 0, 139)); +colorDb.put("MIDNIGHT BLUE", types.color(25, 25, 112)); +colorDb.put("MIDNIGHTBLUE", types.color(25, 25, 112)); +colorDb.put("NAVY", types.color(36, 36, 140)); +colorDb.put("BLUE", types.color(0, 0, 255)); +colorDb.put("INDIGO", types.color(75, 0, 130)); +colorDb.put("BLUE VIOLET", types.color(138, 43, 226)); +colorDb.put("BLUEVIOLET", types.color(138, 43, 226)); +colorDb.put("MEDIUM SLATE BLUE", types.color(123, 104, 238)); +colorDb.put("MEDIUMSLATEBLUE", types.color(123, 104, 238)); +colorDb.put("SLATE BLUE", types.color(106, 90, 205)); +colorDb.put("SLATEBLUE", types.color(106, 90, 205)); +colorDb.put("PURPLE", types.color(160, 32, 240)); +colorDb.put("DARK SLATE BLUE", types.color(72, 61, 139)); +colorDb.put("DARKSLATEBLUE", types.color(72, 61, 139)); +colorDb.put("DARKVIOLET", types.color(148, 0, 211)); +colorDb.put("DARK ORCHID", types.color(153, 50, 204)); +colorDb.put("DARKORCHID", types.color(153, 50, 204)); +colorDb.put("MEDIUMPURPLE", types.color(147, 112, 219)); +colorDb.put("CORNFLOWER BLUE", types.color(68, 64, 108)); +colorDb.put("MEDIUM ORCHID", types.color(186, 85, 211)); +colorDb.put("MEDIUMORCHID", types.color(186, 85, 211)); +colorDb.put("MAGENTA", types.color(255, 0, 255)); +colorDb.put("FUCHSIA", types.color(255, 0, 255)); +colorDb.put("DARKMAGENTA", types.color(139, 0, 139)); +colorDb.put("VIOLET", types.color(238, 130, 238)); +colorDb.put("PLUM", types.color(221, 160, 221)); +colorDb.put("LAVENDER", types.color(230, 230, 250)); +colorDb.put("THISTLE", types.color(216, 191, 216)); +colorDb.put("GHOSTWHITE", types.color(248, 248, 255)); +colorDb.put("WHITE", types.color(255, 255, 255)); +colorDb.put("WHITESMOKE", types.color(245, 245, 245)); +colorDb.put("GAINSBORO", types.color(220, 220, 220)); +colorDb.put("LIGHT GRAY", types.color(211, 211, 211)); +colorDb.put("LIGHTGRAY", types.color(211, 211, 211)); +colorDb.put("SILVER", types.color(192, 192, 192)); +colorDb.put("GRAY", types.color(190, 190, 190)); +colorDb.put("DARK GRAY", types.color(169, 169, 169)); +colorDb.put("DARKGRAY", types.color(169, 169, 169)); +colorDb.put("DIM GRAY", types.color(105, 105, 105)); +colorDb.put("DIMGRAY", types.color(105, 105, 105)); +colorDb.put("BLACK", types.color(0, 0, 0)); + + +EXPORTS['_colorDb'] = colorDb; diff --git a/world/js-impl.js b/world/js-impl.js new file mode 100644 index 0000000..b0a2078 --- /dev/null +++ b/world/js-impl.js @@ -0,0 +1,11 @@ + + +EXPORTS['is-color?'] = + plt.runtime.makePrimitiveProcedure( + 'is-color?', + 1, + function(MACHINE) { + var elt = MACHINE.env[MACHINE.env.length - 1]; + return (//(plt.runtime.isString(elt) || plt.runtime.isSymbol(elt)) && + typeof(colorDb.get(elt)) != 'undefined'); + }); diff --git a/world/kernel.js b/world/kernel.js new file mode 100644 index 0000000..a8f2642 --- /dev/null +++ b/world/kernel.js @@ -0,0 +1,1739 @@ +var world = {}; +world.Kernel = {}; + +EXPORTS['_kernel'] = world.Kernel; + +var types = plt.types; + + + + +var worldListeners = []; +var stopped; +var timerInterval = false; + + +// Inheritance from pg 168: Javascript, the Definitive Guide. +var heir = function(p) { + var f = function() {} + f.prototype = p; + return new f(); +} + + +// clone: object -> object +// Copies an object. The new object should respond like the old +// object, including to things like instanceof +var clone = function(obj) { + var C = function() {} + C.prototype = obj; + var c = new C(); + for (property in obj) { + if (obj.hasOwnProperty(property)) { + c[property] = obj[property]; + } + } + return c; +}; + + + + +var announceListeners = []; +world.Kernel.addAnnounceListener = function(listener) { + announceListeners.push(listener); +}; +world.Kernel.removeAnnounceListener = function(listener) { + var idx = announceListeners.indexOf(listener); + if (idx != -1) { + announceListeners.splice(idx, 1); + } +}; +world.Kernel.announce = function(eventName, vals) { + for (var i = 0; i < announceListeners.length; i++) { + try { + announceListeners[i](eventName, vals); + } catch (e) {} + } +}; + + + + + + + + + + +// changeWorld: world -> void +// Changes the current world to newWorld. +var changeWorld = function(newWorld) { + world = newWorld; + notifyWorldListeners(); +} + + +// updateWorld: (world -> world) -> void +// Public function: update the world, given the old state of the +// world. +world.Kernel.updateWorld = function(updater) { + var newWorld = updater(world); + changeWorld(newWorld); +} + + +world.Kernel.shutdownWorld = function() { + stopped = true; +}; + + +// notifyWorldListeners: -> void +// Tells all of the world listeners that the world has changed. +var notifyWorldListeners = function() { + var i; + for (i = 0; i < worldListeners.length; i++) { + worldListeners[i](world); + } +} + +// addWorldListener: (world -> void) -> void +// Adds a new world listener: whenever the world is changed, the aListener +// will be called with that new world. +var addWorldListener = function(aListener) { + worldListeners.push(aListener); +} + + +// getKeyCodeName: keyEvent -> String +// Given an event, try to get the name of the key. +var getKeyCodeName = function(e) { + var code = e.charCode || e.keyCode; + var keyname; + if (code == 37) { + keyname = "left"; + } else if (code == 38) { + keyname = "up"; + } else if (code == 39) { + keyname = "right"; + } else if (code == 40) { + keyname = "down"; + } else { + keyname = String.fromCharCode(code); + } + return keyname; +} + + +// resetWorld: -> void +// Resets all of the world global values. +var resetWorld = function() { + if (timerInterval) { + clearInterval(timerInterval); + timerInterval = false; + } + stopped = false; + worldListeners = []; +} + + +var getBigBangWindow = function(width, height) { + if (window.document.getElementById("canvas") != undefined) { + return window; + } + + var newWindow = window.open( + "big-bang.html", + "big-bang"); + //"toolbar=false,location=false,directories=false,status=false,menubar=false,width="+width+",height="+height); + if (newWindow == null) { + throw new Error("Error: Not allowed to create a new window."); } + + return newWindow; +} + + + +// scheduleTimerTick: -> void +// Repeatedly schedules an evaluation of the onTick until the program has stopped. +var scheduleTimerTick = function(window, config) { + timerInterval = window.setInterval( + function() { + if (stopped) { + window.clearTimeout(timerInterval); + timerInterval = false; + } + else { + world.Kernel.stimuli.onTick(); + } + }, + config.lookup('tickDelay')); +} + + + + +// Base class for all images. +var BaseImage = function(pinholeX, pinholeY) { + this.pinholeX = pinholeX; + this.pinholeY = pinholeY; +} +world.Kernel.BaseImage = BaseImage; + + +var isImage = function(thing) { + return (thing !== null && + thing !== undefined && + thing instanceof BaseImage); +} + + + +BaseImage.prototype.updatePinhole = function(x, y) { + var aCopy = clone(this); + aCopy.pinholeX = x; + aCopy.pinholeY = y; + return aCopy; +}; + + + +// render: context fixnum fixnum: -> void +// Render the image, where the upper-left corner of the image is drawn at +// (x, y). +// NOTE: the rendering should be oblivous to the pinhole. +BaseImage.prototype.render = function(ctx, x, y) { + throw new Error('BaseImage.render unimplemented!'); + // plt.Kernel.throwMobyError( + // false, + // "make-moby-error-type:generic-runtime-error", + // "Unimplemented method render"); +}; + + +// makeCanvas: number number -> canvas +// Constructs a canvas object of a particular width and height. +world.Kernel.makeCanvas = function(width, height) { + var canvas = document.createElement("canvas"); + canvas.width = width; + canvas.height = height; + + canvas.style.width = canvas.width + "px"; + canvas.style.height = canvas.height + "px"; + + // KLUDGE: IE compatibility uses /js/excanvas.js, and dynamic + // elements must be marked this way. + if (window && typeof window.G_vmlCanvasManager != 'undefined') { + canvas = window.G_vmlCanvasManager.initElement(canvas); + } + return canvas; +}; + + + +var withIeHack = function(canvas, f) { + // canvas.style.display = 'none'; + // document.body.appendChild(canvas); + // try { + var result = f(canvas); + // } catch(e) { + // document.body.removeChild(canvas); + // canvas.style.display = ''; + // throw e; + // } + // document.body.removeChild(canvas); + // canvas.style.display = ''; + return result; +}; + + +BaseImage.prototype.toDomNode = function(cache) { + var that = this; + var width = that.getWidth(); + var height = that.getHeight(); + var canvas = world.Kernel.makeCanvas(width, height); + + // KLUDGE: on IE, the canvas rendering functions depend on a + // context where the canvas is attached to the DOM tree. + + // We initialize an afterAttach hook; the client's responsible + // for calling this after the dom node is attached to the + // document. + canvas.afterAttach = function() { + var ctx = canvas.getContext("2d"); + that.render(ctx, 0, 0); + }; + + return canvas; +}; + + + + +BaseImage.prototype.toWrittenString = function(cache) { return ""; } +BaseImage.prototype.toDisplayedString = function(cache) { return ""; } + +BaseImage.prototype.isEqual = function(other, aUnionFind) { + return (this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY); +}; + + + + +// isScene: any -> boolean +// Produces true when x is a scene. +var isScene = function(x) { + return ((x != undefined) && (x != null) && (x instanceof SceneImage)); +}; + +// SceneImage: primitive-number primitive-number (listof image) -> Scene +var SceneImage = function(width, height, children, withBorder) { + BaseImage.call(this, 0, 0); + this.width = width; + this.height = height; + this.children = children; // arrayof [image, number, number] + this.withBorder = withBorder; +} +SceneImage.prototype = heir(BaseImage.prototype); + + +// add: image primitive-number primitive-number -> Scene +SceneImage.prototype.add = function(anImage, x, y) { + return new SceneImage(this.width, + this.height, + this.children.concat([[anImage, + x - anImage.pinholeX, + y - anImage.pinholeY]]), + this.withBorder); +}; + +// render: 2d-context primitive-number primitive-number -> void +SceneImage.prototype.render = function(ctx, x, y) { + var i; + var childImage, childX, childY; + // Clear the scene. + ctx.clearRect(x, y, this.width, this.height); + // Then ask every object to render itself. + for(i = 0; i < this.children.length; i++) { + childImage = this.children[i][0]; + childX = this.children[i][1]; + childY = this.children[i][2]; + ctx.save(); + childImage.render(ctx, childX + x, childY + y); + ctx.restore(); + + + } + // Finally, draw the black border if withBorder is true + if (this.withBorder) { + ctx.strokeStyle = 'black'; + ctx.strokeRect(x, y, this.width, this.height); + } +}; + +SceneImage.prototype.getWidth = function() { + return this.width; +}; + +SceneImage.prototype.getHeight = function() { + return this.height; +}; + +SceneImage.prototype.isEqual = function(other, aUnionFind) { + if (!(other instanceof SceneImage)) { + return false; + } + + if (this.pinholeX != other.pinholeX || + this.pinholeY != other.pinholeY || + this.width != other.width || + this.height != other.height || + this.children.length != other.children.length) { + return false; + } + + for (var i = 0; i < this.children.length; i++) { + var rec1 = this.children[i]; + var rec2 = other.children[i]; + if (rec1[1] !== rec2[1] || + rec1[2] !== rec2[2] || + !types.isEqual(rec1[0], + rec2[0], + aUnionFind)) { + return false; + } + } + return true; +}; + + +////////////////////////////////////////////////////////////////////// + + +var FileImage = function(src, rawImage) { + BaseImage.call(this, 0, 0); + var self = this; + this.src = src; + this.isLoaded = false; + if (rawImage && rawImage.complete) { + this.img = rawImage; + this.isLoaded = true; + this.pinholeX = self.img.width / 2; + this.pinholeY = self.img.height / 2; + } else { + // fixme: we may want to do something blocking here for + // onload, since we don't know at this time what the file size + // should be, nor will drawImage do the right thing until the + // file is loaded. + this.img = new Image(); + this.img.onload = function() { + self.isLoaded = true; + self.pinholeX = self.img.width / 2; + self.pinholeY = self.img.height / 2; + }; + this.img.onerror = function(e) { + self.img.onerror = ""; + self.img.src = "http://www.wescheme.org/images/broken.png"; + } + this.img.src = src; + } +} +FileImage.prototype = heir(BaseImage.prototype); +// world.Kernel.FileImage = FileImage; + + +var imageCache = {}; +FileImage.makeInstance = function(path, rawImage) { + if (! (path in imageCache)) { + imageCache[path] = new FileImage(path, rawImage); + } + return imageCache[path]; +}; + +FileImage.installInstance = function(path, rawImage) { + imageCache[path] = new FileImage(path, rawImage); +}; + +FileImage.installBrokenImage = function(path) { + imageCache[path] = new TextImage("Unable to load " + path, 10, + colorDb.get("red")); +}; + + + +FileImage.prototype.render = function(ctx, x, y) { + ctx.drawImage(this.img, x, y); +}; + + +FileImage.prototype.getWidth = function() { + return this.img.width; +}; + + +FileImage.prototype.getHeight = function() { + return this.img.height; +}; + +// Override toDomNode: we don't need a full-fledged canvas here. +FileImage.prototype.toDomNode = function(cache) { + return this.img.cloneNode(true); +}; + +FileImage.prototype.isEqual = function(other, aUnionFind) { + return (other instanceof FileImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.src == other.src); + // types.isEqual(this.img, other.img, aUnionFind)); +}; + + +////////////////////////////////////////////////////////////////////// + + +// OverlayImage: image image -> image +// Creates an image that overlays img1 on top of the +// other image. shiftX and shiftY are deltas off the first +// image's pinhole. +var OverlayImage = function(img1, img2, shiftX, shiftY) { + var deltaX = img1.pinholeX - img2.pinholeX + shiftX; + var deltaY = img1.pinholeY - img2.pinholeY + shiftY; + var left = Math.min(0, deltaX); + var top = Math.min(0, deltaY); + var right = Math.max(deltaX + img2.getWidth(), + img1.getWidth()); + var bottom = Math.max(deltaY + img2.getHeight(), + img1.getHeight()); + + BaseImage.call(this, + Math.floor((right-left) / 2), + Math.floor((bottom-top) / 2)); + this.img1 = img1; + this.img2 = img2; + this.width = right - left; + this.height = bottom - top; + + this.img1Dx = -left; + this.img1Dy = -top; + this.img2Dx = deltaX - left; + this.img2Dy = deltaY - top; +}; + +OverlayImage.prototype = heir(BaseImage.prototype); + + +OverlayImage.prototype.render = function(ctx, x, y) { + this.img2.render(ctx, x + this.img2Dx, y + this.img2Dy); + this.img1.render(ctx, x + this.img1Dx, y + this.img1Dy); +}; + + +OverlayImage.prototype.getWidth = function() { + return this.width; +}; + +OverlayImage.prototype.getHeight = function() { + return this.height; +}; + +OverlayImage.prototype.isEqual = function(other, aUnionFind) { + return ( other instanceof OverlayImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.width == other.width && + this.height == other.height && + this.img1Dx == other.img1Dx && + this.img1Dy == other.img1Dy && + this.img2Dx == other.img2Dx && + this.img2Dy == other.img2Dy && + types.isEqual(this.img1, other.img1, aUnionFind) && + types.isEqual(this.img2, other.img2, aUnionFind) ); +}; + + +////////////////////////////////////////////////////////////////////// + + +// rotate: angle image -> image +// Rotates image by angle degrees in a counter-clockwise direction. +// based on http://stackoverflow.com/questions/3276467/adjusting-div-width-and-height-after-rotated +var RotateImage = function(angle, img) { + var sin = Math.sin(angle * Math.PI / 180), + cos = Math.cos(angle * Math.PI / 180); + + // (w,0) rotation + var x1 = Math.floor(cos * img.getWidth()), + y1 = Math.floor(sin * img.getWidth()); + + // (0,h) rotation + var x2 = Math.floor(-sin * img.getHeight()), + y2 = Math.floor( cos * img.getHeight()); + + // (w,h) rotation + var x3 = Math.floor(cos * img.getWidth() - sin * img.getHeight()), + y3 = Math.floor(sin * img.getWidth() + cos * img.getHeight()); + + var minX = Math.min(0, x1, x2, x3), + maxX = Math.max(0, x1, x2, x3), + minY = Math.min(0, y1, y2, y3), + maxY = Math.max(0, y1, y2, y3); + + var rotatedWidth = maxX - minX, + rotatedHeight = maxY - minY; + + // resize the image + BaseImage.call(this, + Math.floor(rotatedWidth / 2), + Math.floor(rotatedHeight / 2)); + + this.img = img; + this.width = rotatedWidth; + this.height = rotatedHeight; + this.angle = angle; + this.translateX = -minX; + this.translateY = -minY; +}; + +RotateImage.prototype = heir(BaseImage.prototype); + + +// translate drawing point, so that this.img appears in the UL corner. Then rotate and render this.img. +RotateImage.prototype.render = function(ctx, x, y) { + ctx.translate(this.translateX, this.translateY); + ctx.rotate(this.angle * Math.PI / 180); + this.img.render(ctx, x, y); + ctx.restore(); +}; + + +RotateImage.prototype.getWidth = function() { + return this.width; +}; + +RotateImage.prototype.getHeight = function() { + return this.height; +}; + +RotateImage.prototype.isEqual = function(other, aUnionFind) { + return ( other instanceof RotateImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.width == other.width && + this.height == other.height && + this.angle == other.angle && + this.translateX == other.translateX && + this.translateY == other.translateY && + types.isEqual(this.img, other.img, aUnionFind) ); +}; + +////////////////////////////////////////////////////////////////////// + + +// ScaleImage: factor factor image -> image +// Scale an image +var ScaleImage = function(xFactor, yFactor, img) { + + // resize the image + BaseImage.call(this, + Math.floor((img.getWidth() * xFactor) / 2), + Math.floor((img.getHeight() * yFactor) / 2)); + + this.img = img; + this.width = img.getWidth() * xFactor; + this.height = img.getHeight() * yFactor; + this.xFactor = xFactor; + this.yFactor = yFactor; +}; + +ScaleImage.prototype = heir(BaseImage.prototype); + + +// scale the context, and pass it to the image's render function +ScaleImage.prototype.render = function(ctx, x, y) { + ctx.save(); + ctx.scale(this.xFactor, this.yFactor); + this.img.render(ctx, x, y); + ctx.restore(); +}; + + +ScaleImage.prototype.getWidth = function() { + return this.width; +}; + +ScaleImage.prototype.getHeight = function() { + return this.height; +}; + +ScaleImage.prototype.isEqual = function(other, aUnionFind) { + return ( other instanceof ScaleImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.width == other.width && + this.height == other.height && + this.xFactor == other.xFactor && + this.yFactor == other.yFactor && + types.isEqual(this.img, other.img, aUnionFind) ); +}; + +////////////////////////////////////////////////////////////////////// + + + +var colorString = function(aColor) { + return ("rgb(" + + types.colorRed(aColor) + "," + + types.colorGreen(aColor) + ", " + + types.colorBlue(aColor) + ")"); +}; + + + +var RectangleImage = function(width, height, style, color) { + BaseImage.call(this, width/2, height/2); + this.width = width; + this.height = height; + this.style = style; + this.color = color; +}; +RectangleImage.prototype = heir(BaseImage.prototype); + + +RectangleImage.prototype.render = function(ctx, x, y) { + if (this.style.toString().toLowerCase() == "outline") { + ctx.save(); + ctx.beginPath(); + ctx.strokeStyle = colorString(this.color); + ctx.strokeRect(x, y, this.width, this.height); + ctx.closePath(); + ctx.restore(); + } else { + ctx.save(); + ctx.beginPath(); + + ctx.fillStyle = colorString(this.color); + ctx.fillRect(x, y, this.width, this.height); + + ctx.closePath(); + ctx.restore(); + } +}; + +RectangleImage.prototype.getWidth = function() { + return this.width; +}; + + +RectangleImage.prototype.getHeight = function() { + return this.height; +}; + +RectangleImage.prototype.isEqual = function(other, aUnionFind) { + return (other instanceof RectangleImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.width == other.width && + this.height == other.height && + this.style == other.style && + types.isEqual(this.color, other.color, aUnionFind)); +}; + + +////////////////////////////////////////////////////////////////////// + +var TextImage = function(msg, size, color) { + BaseImage.call(this, 0, 0); + this.msg = msg; + this.size = size; + this.color = color; + this.font = this.size + "px Optimer"; + + + var canvas = world.Kernel.makeCanvas(0, 0); + var ctx = canvas.getContext("2d"); + ctx.font = this.font; + var metrics = ctx.measureText(msg); + + this.width = metrics.width; + // KLUDGE: I don't know how to get at the height. + this.height = ctx.measureText("m").width + 20; + +} + +TextImage.prototype = heir(BaseImage.prototype); + +TextImage.prototype.render = function(ctx, x, y) { + ctx.save(); + ctx.font = this.font; + ctx.textAlign = 'left'; + ctx.textBaseline = 'top'; + ctx.fillStyle = colorString(this.color); + ctx.fillText(this.msg, x, y); + ctx.restore(); +}; + +TextImage.prototype.getWidth = function() { + return this.width; +}; + + +TextImage.prototype.getHeight = function() { + return this.height; +}; + +TextImage.prototype.isEqual = function(other, aUnionFind) { + return (other instanceof TextImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.msg == other.msg && + this.size == other.size && + types.isEqual(this.color, other.color, aUnionFind) && + this.font == other.font); +}; + + +////////////////////////////////////////////////////////////////////// + +var CircleImage = function(radius, style, color) { + BaseImage.call(this, radius, radius); + this.radius = radius; + this.style = style; + this.color = color; +} +CircleImage.prototype = heir(BaseImage.prototype); + +CircleImage.prototype.render = function(ctx, x, y) { + ctx.save(); + ctx.beginPath(); + ctx.arc(x + this.radius, + y + this.radius, + this.radius, 0, 2*Math.PI, false); + ctx.closePath(); + if (this.style.toString().toLowerCase() == "outline") { + ctx.strokeStyle = colorString(this.color); + ctx.stroke(); + } else { + ctx.fillStyle = colorString(this.color); + ctx.fill(); + } + + ctx.restore(); +}; + +CircleImage.prototype.getWidth = function() { + return this.radius * 2; +}; + +CircleImage.prototype.getHeight = function() { + return this.radius * 2; +}; + +CircleImage.prototype.isEqual = function(other, aUnionFind) { + return (other instanceof CircleImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.radius == other.radius && + this.style == other.style && + types.isEqual(this.color, other.color, aUnionFind)); +}; + + + +////////////////////////////////////////////////////////////////////// + + +// StarImage: fixnum fixnum fixnum color -> image +var StarImage = function(points, outer, inner, style, color) { + BaseImage.call(this, + Math.max(outer, inner), + Math.max(outer, inner)); + this.points = points; + this.outer = outer; + this.inner = inner; + this.style = style; + this.color = color; + + this.radius = Math.max(this.inner, this.outer); +}; + +StarImage.prototype = heir(BaseImage.prototype); + +var oneDegreeAsRadian = Math.PI / 180; + +// render: context fixnum fixnum -> void +// Draws a star on the given context. +// Most of this code here adapted from the Canvas tutorial at: +// http://developer.apple.com/safari/articles/makinggraphicswithcanvas.html +StarImage.prototype.render = function(ctx, x, y) { + ctx.save(); + ctx.beginPath(); + for( var pt = 0; pt < (this.points * 2) + 1; pt++ ) { + var rads = ( ( 360 / (2 * this.points) ) * pt ) * oneDegreeAsRadian - 0.5; + var radius = ( pt % 2 == 1 ) ? this.outer : this.inner; + ctx.lineTo(x + this.radius + ( Math.sin( rads ) * radius ), + y + this.radius + ( Math.cos( rads ) * radius ) ); + } + ctx.closePath(); + if (this.style.toString().toLowerCase() == "outline") { + ctx.strokeStyle = colorString(this.color); + ctx.stroke(); + } else { + ctx.fillStyle = colorString(this.color); + ctx.fill(); + } + + ctx.restore(); +}; + +// getWidth: -> fixnum +StarImage.prototype.getWidth = function() { + return this.radius * 2; +}; + + +// getHeight: -> fixnum +StarImage.prototype.getHeight = function() { + return this.radius * 2; +}; + +StarImage.prototype.isEqual = function(other, aUnionFind) { + return (other instanceof StarImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.points == other.points && + this.outer == other.outer && + this.inner == other.inner && + this.style == other.style && + types.isEqual(this.color, other.color, aUnionFind)); +}; + + + + +////////////////////////////////////////////////////////////////////// +//Triangle +/////// +var TriangleImage = function(side, style, color) { + this.width = side; + this.height = Math.ceil(side * Math.sqrt(3) / 2); + + BaseImage.call(this, Math.floor(this.width/2), Math.floor(this.height/2)); + this.side = side; + this.style = style; + this.color = color; +} +TriangleImage.prototype = heir(BaseImage.prototype); + + +TriangleImage.prototype.render = function(ctx, x, y) { + var width = this.getWidth(); + var height = this.getHeight(); + ctx.save(); + ctx.beginPath(); + ctx.moveTo(x + this.side/2, y); + ctx.lineTo(x + width, y + height); + ctx.lineTo(x, y + height); + ctx.closePath(); + + if (this.style.toString().toLowerCase() == "outline") { + ctx.strokeStyle = colorString(this.color); + ctx.stroke(); + } + else { + ctx.fillStyle = colorString(this.color); + ctx.fill(); + } + ctx.restore(); +}; + + + +TriangleImage.prototype.getWidth = function() { + return this.width; +}; + +TriangleImage.prototype.getHeight = function() { + return this.height; +}; + +TriangleImage.prototype.isEqual = function(other, aUnionFind) { + return (other instanceof TriangleImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.side == other.side && + this.style == other.style && + types.isEqual(this.color, other.color, aUnionFind)); +}; + + + +////////////////////////////////////////////////////////////////////// +//Ellipse +var EllipseImage = function(width, height, style, color) { + BaseImage.call(this, Math.floor(width/2), Math.floor(height/2)); + this.width = width; + this.height = height; + this.style = style; + this.color = color; +}; + +EllipseImage.prototype = heir(BaseImage.prototype); + + +EllipseImage.prototype.render = function(ctx, aX, aY) { + ctx.save(); + ctx.beginPath(); + + // Most of this code is taken from: + // http://webreflection.blogspot.com/2009/01/ellipse-and-circle-for-canvas-2d.html + var hB = (this.width / 2) * .5522848, + vB = (this.height / 2) * .5522848, + eX = aX + this.width, + eY = aY + this.height, + mX = aX + this.width / 2, + mY = aY + this.height / 2; + ctx.moveTo(aX, mY); + ctx.bezierCurveTo(aX, mY - vB, mX - hB, aY, mX, aY); + ctx.bezierCurveTo(mX + hB, aY, eX, mY - vB, eX, mY); + ctx.bezierCurveTo(eX, mY + vB, mX + hB, eY, mX, eY); + ctx.bezierCurveTo(mX - hB, eY, aX, mY + vB, aX, mY); + ctx.closePath(); + if (this.style.toString().toLowerCase() == "outline") { + ctx.strokeStyle = colorString(this.color); + ctx.stroke(); + } + else { + ctx.fillStyle = colorString(this.color); + ctx.fill(); + } + + + ctx.restore(); +}; + +EllipseImage.prototype.getWidth = function() { + return this.width; +}; + +EllipseImage.prototype.getHeight = function() { + return this.height; +}; + +EllipseImage.prototype.isEqual = function(other, aUnionFind) { + return (other instanceof EllipseImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.width == other.width && + this.height == other.height && + this.style == other.style && + types.isEqual(this.color, other.color, aUnionFind)); +}; + + +////////////////////////////////////////////////////////////////////// +//Line +var LineImage = function(x, y, color) { + if (x >= 0) { + if (y >= 0) { + BaseImage.call(this, 0, 0); + } else { + BaseImage.call(this, 0, -y); + } + } else { + if (y >= 0) { + BaseImage.call(this, -x, 0); + } else { + BaseImage.call(this, -x, -y); + } + } + + + this.x = x; + this.y = y; + this.color = color; + this.width = Math.abs(x) + 1; + this.height = Math.abs(y) + 1; +} + +LineImage.prototype = heir(BaseImage.prototype); + + +LineImage.prototype.render = function(ctx, xstart, ystart) { + ctx.save(); + + if (this.x >= 0) { + if (this.y >= 0) { + ctx.moveTo(xstart, ystart); + ctx.lineTo((xstart + this.x), + (ystart + this.y)); + } else { + ctx.moveTo(xstart, ystart + (-this.y)); + ctx.lineTo(xstart + this.x, ystart); + } + } else { + if (this.y >= 0) { + ctx.moveTo(xstart + (-this.x), ystart); + ctx.lineTo(xstart, + (ystart + this.y)); + } else { + ctx.moveTo(xstart + (-this.x), ystart + (-this.y)); + ctx.lineTo(xstart, ystart); + } + } + ctx.strokeStyle = colorString(this.color); + ctx.stroke(); + ctx.restore(); +}; + + +LineImage.prototype.getWidth = function() { + return this.width; +}; + + +LineImage.prototype.getHeight = function() { + return this.height; +}; + +LineImage.prototype.isEqual = function(other, aUnionFind) { + return (other instanceof LineImage && + this.pinholeX == other.pinholeX && + this.pinholeY == other.pinholeY && + this.x == other.x && + this.y == other.y && + types.isEqual(this.color, other.color, aUnionFind)); +}; + + + + + +////////////////////////////////////////////////////////////////////// +// Effects + +/** + * applyEffect: compound-effect -> (arrayof (world -> world)) + + applyEffect applies all of the effects + + @param aCompEffect a compound effect is either a scheme list of + compound effects or a single primitive effect */ +world.Kernel.applyEffect = function(aCompEffect) { + if ( types.isEmpty(aCompEffect) ) { + // Do Nothing + } else if ( types.isPair(aCompEffect) ) { + var results = world.Kernel.applyEffect(aCompEffect.first()); + return results.concat(world.Kernel.applyEffect(aCompEffect.rest())); + } else { + var newResult = aCompEffect.run(); + if (newResult) { + return newResult; + } + } + return []; +} + +////////////////////////////////////////////////////////////////////////// + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/////////////////////////////////////////////////////////////// +// Exports + +world.Kernel.isImage = isImage; +world.Kernel.isScene = isScene; +world.Kernel.isColor = function(thing) { + return (types.isColor(thing) || + ((types.isString(thing) || types.isSymbol(thing)) && + typeof(colorDb.get(thing)) != 'undefined')); +}; +world.Kernel.colorDb = colorDb; + +world.Kernel.sceneImage = function(width, height, children, withBorder) { + return new SceneImage(width, height, children, withBorder); +}; +world.Kernel.circleImage = function(radius, style, color) { + return new CircleImage(radius, style, color); +}; +world.Kernel.starImage = function(points, outer, inner, style, color) { + return new StarImage(points, outer, inner, style, color); +}; +world.Kernel.rectangleImage = function(width, height, style, color) { + return new RectangleImage(width, height, style, color); +}; +world.Kernel.triangleImage = function(side, style, color) { + return new TriangleImage(side, style, color); +}; +world.Kernel.ellipseImage = function(width, height, style, color) { + return new EllipseImage(width, height, style, color); +}; +world.Kernel.lineImage = function(x, y, color) { + return new LineImage(x, y, color); +}; +world.Kernel.overlayImage = function(img1, img2, shiftX, shiftY) { + return new OverlayImage(img1, img2, shiftX, shiftY); +}; +world.Kernel.rotateImage = function(angle, img) { + return new RotateImage(angle, img); +}; +world.Kernel.scaleImage = function(xFactor, yFactor, img) { + return new ScaleImage(xFactor, yFactor, img); +}; +world.Kernel.textImage = function(msg, size, color) { + return new TextImage(msg, size, color); +}; +world.Kernel.fileImage = function(path, rawImage) { + return FileImage.makeInstance(path, rawImage); +}; + + +world.Kernel.isSceneImage = function(x) { return x instanceof SceneImage; }; +world.Kernel.isCircleImage = function(x) { return x instanceof CircleImage; }; +world.Kernel.isStarImage = function(x) { return x instanceof StarImage; }; +world.Kernel.isRectangleImage = function(x) { return x instanceof RectangleImage; }; +world.Kernel.isTriangleImage = function(x) { return x instanceof TriangleImage; }; +world.Kernel.isEllipseImage = function(x) { return x instanceof EllipseImage; }; +world.Kernel.isLineImage = function(x) { return x instanceof LineImage; }; +world.Kernel.isOverlayImage = function(x) { return x instanceof OverlayImage; }; +world.Kernel.isRotateImage = function(x) { return x instanceof RotateImage; }; +world.Kernel.isTextImage = function(x) { return x instanceof TextImage; }; +world.Kernel.isFileImage = function(x) { return x instanceof FileImage; }; + + + + + + + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// + + +// Feeds stimuli inputs into the world. The functions here +// are responsible for converting to Scheme values. +// +// NOTE and WARNING: make sure to really do the coersions, even for +// strings. Bad things happen otherwise, as in the sms stuff, where +// we're getting string-like values that aren't actually strings. + + + +world.stimuli = {}; +world.Kernel.stimuli = world.stimuli; + + +(function() { + var handlers = []; + + var doNothing = function() {}; + + + var StimuliHandler = function(config, caller, restarter) { + this.config = config; + this.caller = caller; + this.restarter = restarter; + handlers.push(this); + }; + + // StimuliHandler.prototype.failHandler = function(e) { + // this.onShutdown(); + // this.restarter(e); + // }; + + // doStimuli: CPS( (world -> effect) (world -> world) -> void ) + // + // Processes a stimuli by compute the effect and applying it, and + // computing a new world to replace the old. + StimuliHandler.prototype.doStimuli = function(computeEffectF, computeWorldF, restArgs, k) { + var effectUpdaters = []; + var that = this; + try { + that.change(function(w, k2) { + var args = [w].concat(restArgs); + var doStimuliHelper = function() { + if (computeWorldF) { + that.caller(computeWorldF, args, k2); + } else { + k2(w); + } + }; + doStimuliHelper(); + }, k); + // if (computeEffectF) { + // that.caller(computeEffectF, [args], + // function(effect) { + // effectUpdaters = applyEffect(effect); + // doStimuliHelper(); + // }, + // this.failHandler); + // } + // else { doStimuliHelper(); } + // }, + // function() { + // helpers.forEachK(effectUpdaters, + // function(effect, k2) { that.change(effect, k2); }, + // function(e) { throw e; }, + // k); + // }); + } catch (e) { + // if (console && console.log && e.stack) { + // console.log(e.stack); + // } + this.onShutdown(); + } + } + + + // Orientation change + // args: [azimuth, pitch, roll] + StimuliHandler.prototype.onTilt = function(args, k) { + var onTilt = this.lookup("onTilt"); + var onTiltEffect = this.lookup("onTiltEffect"); + this.doStimuli(onTiltEffect, onTilt, helpers.map(flt, args), k); + }; + + + // Accelerations + // args: [x, y, z] + StimuliHandler.prototype.onAcceleration = function(args, k) { + var onAcceleration = this.lookup('onAcceleration'); + var onAccelerationEffect = this.lookup('onAccelerationEffect'); + this.doStimuli(onAccelerationEffect, onAcceleration, helpers.map(flt, args), k); + }; + + + // Shakes + // args: [] + StimuliHandler.prototype.onShake = function(args, k) { + var onShake = this.lookup('onShake'); + var onShakeEffect = this.lookup('onShakeEffect'); + this.doStimuli(onShakeEffect, onShake, [], k); + }; + + + // Sms receiving + // args: [sender, message] + StimuliHandler.prototype.onSmsReceive = function(args, k) { + var onSmsReceive = this.lookup('onSmsReceive'); + var onSmsReceiveEffect = this.lookup('onSmsReceiveEffect'); + // IMPORTANT: must coerse to string by using x+"". Do not use + // toString(): it's not safe. + this.doStimuli(onSmsReceiveEffect, onSmsReceive, [args[0]+"", args[1]+""], k); + }; + + + // Locations + // args: [lat, lng] + StimuliHandler.prototype.onLocation = function(args, k) { + var onLocationChange = this.lookup('onLocationChange'); + var onLocationChangeEffect = this.lookup('onLocationChangeEffect'); + this.doStimuli(onLocationChangeEffect, onLocationChange, helpers.map(flt, args), k); + }; + + + + // Keystrokes + // args: [e] + StimuliHandler.prototype.onKey = function(args, k) { + // getKeyCodeName: keyEvent -> String + // Given an event, try to get the name of the key. + var getKeyCodeName = function(e) { + var code = e.charCode || e.keyCode; + var keyname; + switch(code) { + case 16: keyname = "shift"; break; + case 17: keyname = "control"; break; + case 19: keyname = "pause"; break; + case 27: keyname = "escape"; break; + case 33: keyname = "prior"; break; + case 34: keyname = "next"; break; + case 35: keyname = "end"; break; + case 36: keyname = "home"; break; + case 37: keyname = "left"; break; + case 38: keyname = "up"; break; + case 39: keyname = "right"; break; + case 40: keyname = "down"; break; + case 42: keyname = "print"; break; + case 45: keyname = "insert"; break; + case 46: keyname = String.fromCharCode(127); break; + case 106: keyname = "*"; break; + case 107: keyname = "+"; break; + case 109: keyname = "-"; break; + case 110: keyname = "."; break; + case 111: keyname = "/"; break; + case 144: keyname = "numlock"; break; + case 145: keyname = "scroll"; break; + case 186: keyname = ";"; break; + case 187: keyname = "="; break; + case 188: keyname = ","; break; + case 189: keyname = "-"; break; + case 190: keyname = "."; break; + case 191: keyname = "/"; break; + case 192: keyname = "`"; break; + case 219: keyname = "["; break; + case 220: keyname = "\\"; break; + case 221: keyname = "]"; break; + case 222: keyname = "'"; break; + default: if (code >= 96 && code <= 105) { + keyname = (code - 96).toString(); + } + else if (code >= 112 && code <= 123) { + keyname = "f" + (code - 111); + } + else { + keyname = String.fromCharCode(code).toLowerCase(); + } + break; + } + return keyname; + } + var keyname = getKeyCodeName(args[0]); + var onKey = this.lookup('onKey'); + var onKeyEffect = this.lookup('onKeyEffect'); + this.doStimuli(onKeyEffect, onKey, [keyname], k); + }; + + + + // // Time ticks + // // args: [] + // StimuliHandler.prototype.onTick = function(args, k) { + // var onTick = this.lookup('onTick'); + // var onTickEffect = this.lookup('onTickEffect'); + // this.doStimuli(onTickEffect, onTick, [], k); + // }; + + + + // Announcements + // args: [eventName, vals] + StimuliHandler.prototype.onAnnounce = function(args, k) { + var vals = args[1]; + var valsList = types.EMPTY; + for (var i = 0; i < vals.length; i++) { + valsList = types.cons(vals[vals.length - i - 1], valsList); + } + + var onAnnounce = this.lookup('onAnnounce'); + var onAnnounceEffect = this.lookup('onAnnounceEffect'); + this.doStimuli(onAnnounce, onAnnounceEffect, [args[0], valsList], k); + }; + + + + // The shutdown stimuli: special case that forces a world computation to quit. + // Also removes this instance from the list of handlers + StimuliHandler.prototype.onShutdown = function() { + var index = handlers.indexOf(this); + if (index != -1) { + handlers.splice(index, 1); + } + + var shutdownWorld = this.lookup('shutdownWorld'); + if (shutdownWorld) { + shutdownWorld(); + } + }; + + + ////////////////////////////////////////////////////////////////////// + // Helpers + var flt = types.float; + + StimuliHandler.prototype.lookup = function(s) { + return this.config.lookup(s); + }; + + StimuliHandler.prototype.change = function(f, k) { + if (this.lookup('changeWorld')) { + this.lookup('changeWorld')(f, k); + } + else { k(); } + }; + + // applyEffect: compound-effect: (arrayof (world -> world)) + var applyEffect = function(e) { + return world.Kernel.applyEffect(e); + }; + + var makeStimulusHandler = function(funName) { + return function() { + var args = arguments; + for (var i = 0; i < handlers.length; i++) { + (handlers[i])[funName](args, doNothing); + } + // helpers.forEachK(handlers, + // function(h, k) { h[funName](args, k); }, + // function(e) { throw e; }, + // doNothing); + } + }; + + ////////////////////////////////////////////////////////////////////// + // Exports + + world.stimuli.StimuliHandler = StimuliHandler; + + world.stimuli.onTilt = makeStimulusHandler('onTilt'); + world.stimuli.onAcceleration = makeStimulusHandler('onAcceleration'); + world.stimuli.onShake = makeStimulusHandler('onShake'); + world.stimuli.onSmsReceive = makeStimulusHandler('onSmsReceive'); + world.stimuli.onLocation = makeStimulusHandler('onLocation'); + world.stimuli.onKey = makeStimulusHandler('onKey'); + // world.stimuli.onTick = makeStimulusHandler('onTick'); + world.stimuli.onAnnounce = makeStimulusHandler('onAnnounce'); + + world.stimuli.massShutdown = function() { + for (var i = 0; i < handlers.length; i++) { + var shutdownWorld = handlers[i].lookup('shutdownWorld'); + if (shutdownWorld) { + shutdownWorld(); + } + } + handlers = []; + }; + + +})(); + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// + + + + + + + +(function() { + +// var make_dash_effect_colon_none = +// (plt.Kernel.invokeModule("moby/runtime/effect-struct") +// .EXPORTS['make-effect:none']); + + world.config = {}; + world.Kernel.config = world.config; + + + // augment: hash hash -> hash + // Functionally extend a hashtable with another one. + var augment = function(o, a) { + var oo = {}; + for (var e in o) { + if (o.hasOwnProperty(e)) { + oo[e] = o[e]; + } + } + for (var e in a) { + if (a.hasOwnProperty(e)) { + oo[e] = a[e]; + } + } + return oo; + } + + + + var WorldConfig = function() { + // The following handler values are initially false until they're updated + // by configuration. + + // A handler is a function: + // handler: world X Y ... -> Z + + + this.vals = { + // changeWorld: (world -> world) -> void + // When called, this will update the world based on the + // updater passed to it. + changeWorld: false, + + // shutdownWorld: -> void + // When called, this will shut down the world computation. + shutdownWorld: false, + + // initialEffect: effect + // The initial effect to invoke when the world computation + // begins. + initialEffect: false, + + + // onRedraw: world -> scene + onRedraw: false, + + // onDraw: world -> (sexpof dom) + onDraw: false, + + // onDrawCss: world -> (sexpof css-style) + onDrawCss: false, + + + // tickDelay: number + tickDelay: false, + // onTick: world -> world + onTick: false, + // onTickEffect: world -> effect + onTickEffect: false, + + // onKey: world key -> world + onKey: false, + // onKeyEffect: world key -> effect + onKeyEffect : false, + + // onTilt: world number number number -> world + onTilt: false, + // onTiltEffect: world number number number -> effect + onTiltEffect: false, + + // onAcceleration: world number number number -> world + onAcceleration: false, + // onAccelerationEffect: world number number number -> effect + onAccelerationEffect: false, + + // onShake: world -> world + onShake: false, + // onShakeEffect: world -> effect + onShakeEffect: false, + + // onSmsReceive: world -> world + onSmsReceive: false, + // onSmsReceiveEffect: world -> effect + onSmsReceiveEffect: false, + + // onLocationChange: world number number -> world + onLocationChange : false, + // onLocationChangeEffect: world number number -> effect + onLocationChangeEffect: false, + + + // onAnnounce: world string X ... -> world + onAnnounce: false, + // onAnnounce: world string X ... -> effect + onAnnounceEffect: false, + + // stopWhen: world -> boolean + stopWhen: false, + // stopWhenEffect: world -> effect + stopWhenEffect: false, + + + + ////////////////////////////////////////////////////////////////////// + // For universe game playing + + // connectToGame: string + // Registers with some universe, given an identifier + // which is a URL to a Universe server. + connectToGame: false, + onGameStart: false, + onOpponentTurn: false, + onMyTurn: false, + afterMyTurn: false, + onGameFinish: false + }; + } + + + // WorldConfig.lookup: string -> handler + // Looks up a value in the configuration. + WorldConfig.prototype.lookup = function(key) { +// plt.Kernel.check(key, plt.Kernel.isString, "WorldConfig.lookup", "string", 1); + if (key in this.vals) { + return this.vals[key]; + } else { + throw Error("Can't find " + key + " in the configuration"); + } + } + + + + // WorldConfig.updateAll: (hashof string handler) -> WorldConfig + WorldConfig.prototype.updateAll = function(aHash) { + var result = new WorldConfig(); + result.vals = augment(this.vals, aHash); + return result; + } + + + world.config.WorldConfig = WorldConfig; + + // The following global variable CONFIG is mutated by either + // big-bang from the regular world or the one in jsworld. + world.config.CONFIG = new WorldConfig(); + + + // A handler is a function that consumes a config and produces a + // config. + + + ////////////////////////////////////////////////////////////////////// + + var getNoneEffect = function() { + throw new Error("getNoneEffect: We should not be calling effects!"); + // return make_dash_effect_colon_none(); + } + + + + ////////////////////////////////////////////////////////////////////// + + world.config.Kernel = world.config.Kernel || {}; + world.config.Kernel.getNoneEffect = getNoneEffect; + + +/* + // makeSimplePropertyUpdater: (string (X -> boolean) string string) -> (X -> handler) + var makeSimplePropertyUpdater = function(propertyName, + propertyPredicate, + propertyTypeName, + updaterName) { + return function(val) { + plt.Kernel.check(val, propertyPredicate, updaterName, propertyTypeName, 1); + return addStringMethods( + function(config) { + return config.updateAll({propertyName: val }); + }, updaterName); + } + }; + + // connects to the game + world.config.Kernel.connect_dash_to_dash_game = + makeSimplePropertyUpdater('connectToGame', + plt.Kernel.isString, + "string", + "connect-to-game"); + + + // Registers a handler for game-start events. + world.config.Kernel.on_dash_game_dash_start = + makeSimplePropertyUpdater('onGameStart', + plt.Kernel.isFunction, + "function", + "on-game-start"); + + + // Registers a handler for opponent-turn events. + world.config.Kernel.on_dash_opponent_dash_turn = + makeSimplePropertyUpdater('onOpponentTurn', + plt.Kernel.isFunction, + "function", + "on-opponent-turn"); + + + // Registers a handler for my turn. + world.config.Kernel.on_dash_my_dash_turn = + makeSimplePropertyUpdater('onMyTurn', + plt.Kernel.isFunction, + "function", + "on-my-turn"); + + // Register a handler after I make a move. + world.config.Kernel.after_dash_my_dash_turn = + makeSimplePropertyUpdater('afterMyTurn', + plt.Kernel.isFunction, + "function", + "after-my-turn"); + + world.config.Kernel.on_dash_game_dash_finish = + makeSimplePropertyUpdater('onGameFinish', + plt.Kernel.isFunction, + "function", + "on-game-finish"); +*/ + + + +})(); diff --git a/world/main.rkt b/world/main.rkt new file mode 100644 index 0000000..dc9df3a --- /dev/null +++ b/world/main.rkt @@ -0,0 +1,8 @@ +#lang s-exp "../lang/js/js.rkt" + +(declare-implementation + #:racket "racket-impl.rkt" + #:javascript ("colordb.js" + "kernel.js" + "js-impl.js") + #:provided-values (is-color?)) \ No newline at end of file diff --git a/world/racket-impl.rkt b/world/racket-impl.rkt new file mode 100644 index 0000000..db5e07c --- /dev/null +++ b/world/racket-impl.rkt @@ -0,0 +1,6 @@ +#lang s-exp "../lang/base.rkt" + +(provide is-color?) + +(define (is-color? x) + true) \ No newline at end of file