From 0ea0b052061446594e2fbe579c7b474811b4321e Mon Sep 17 00:00:00 2001 From: Danny Yoo Date: Mon, 27 Jun 2011 16:32:58 -0400 Subject: [PATCH] continuing to add --- js-assembler/runtime-src/helpers.js | 46 ++++--- js-assembler/runtime-src/runtime.js | 179 +++++++++++++++++++++++++--- lang/kernel.rkt | 7 +- tests/test-browser-evaluate.rkt | 12 ++ 4 files changed, 210 insertions(+), 34 deletions(-) diff --git a/js-assembler/runtime-src/helpers.js b/js-assembler/runtime-src/helpers.js index 8abd7ca..a14cd22 100644 --- a/js-assembler/runtime-src/helpers.js +++ b/js-assembler/runtime-src/helpers.js @@ -27,12 +27,13 @@ if (! this['plt']) { this['plt'] = {}; } - + // format: string [X ...] string -> string + // String formatting. var format = function(formatStr, args, functionName) { var throwFormatError = function() { - functionName = functionName || '#'; + functionName = functionName || 'format'; var matches = formatStr.match(new RegExp('~[sSaA]', 'g')); - var expectedNumberOfArgs = matches == null ? 0 : matches.length; + var expectedNumberOfArgs = (matches === null ? 0 : matches.length); var errorStrBuffer = [functionName + ': format string requires ' + expectedNumberOfArgs + ' arguments, given ' + args.length + '; arguments were:', toWrittenString(formatStr)]; @@ -40,38 +41,47 @@ if (! this['plt']) { this['plt'] = {}; } errorStrBuffer.push( toWrittenString(args[i]) ); } - raise( types.incompleteExn(types.exnFailContract, errorStrBuffer.join(' '), []) ); + throw new Error(errorStrBuffer.join(' ')); } - var pattern = new RegExp("~[sSaAneE%~]", "g"); - var buffer = args.slice(0);; - function f(s) { - if (s == "~~") { + var pattern = new RegExp("~[sSaAnevE%~]", "g"); + var buffer = args.slice(0); + var onTemplate = function(s) { + if (s === "~~") { return "~"; - } else if (s == '~n' || s == '~%') { + } else if (s === '~n' || s === '~%') { return "\n"; - } else if (s == '~s' || s == "~S") { - if (buffer.length == 0) { + } else if (s === '~s' || s === "~S") { + if (buffer.length === 0) { throwFormatError(); } return toWrittenString(buffer.shift()); - } else if (s == '~e' || s == "~E") { + } else if (s === '~e' || s === "~E") { // FIXME: we don't yet have support for the error-print // handler, and currently treat ~e just like ~s. - if (buffer.length == 0) { + if (buffer.length === 0) { throwFormatError(); } + return toWrittenString(buffer.shift()); + } + else if (s === '~v') { + if (buffer.length === 0) { + throwFormatError(); + } + // fprintf must do something more interesting here by + // printing the dom representation directly... return toWrittenString(buffer.shift()); - } else if (s == '~a' || s == "~A") { - if (buffer.length == 0) { + } else if (s === '~a' || s === "~A") { + if (buffer.length === 0) { throwFormatError(); } return toDisplayedString(buffer.shift()); } else { - throw types.internalError('format: string.replace matched invalid regexp', false); + throw new Error(functionName + + ': string.replace matched invalid regexp'); } } - var result = formatStr.replace(pattern, f); + var result = formatStr.replace(pattern, onTemplate); if (buffer.length > 0) { throwFormatError(); } @@ -79,6 +89,8 @@ if (! this['plt']) { this['plt'] = {}; } }; + + // forEachK: CPS( array CPS(array -> void) (error -> void) -> void ) // Iterates through an array and applies f to each element using CPS // If an error is thrown, it catches the error and calls f_error on it diff --git a/js-assembler/runtime-src/runtime.js b/js-assembler/runtime-src/runtime.js index 9c03af7..c9bf03a 100644 --- a/js-assembler/runtime-src/runtime.js +++ b/js-assembler/runtime-src/runtime.js @@ -113,7 +113,7 @@ 'numBouncesBeforeYield': 2000, // self-adjusting 'maxNumBouncesBeforeYield': 2000, // self-adjusting - 'current-print': new Closure( + 'currentPrint': new Closure( function(MACHINE) { if(--MACHINE.callsBeforeTrampoline<0) { throw arguments.callee; } @@ -562,7 +562,7 @@ var outputPort = MACHINE.params.currentOutputPort; if (MACHINE.argcount === 2) { testArgument(MACHINE, - 'isOutputPort', + 'output-port', isOutputPort, MACHINE.env.length-2, 1, @@ -579,7 +579,7 @@ var outputPort = MACHINE.params.currentOutputPort; if (MACHINE.argcount === 1) { testArgument(MACHINE, - 'isOutputPort', + 'output-port', isOutputPort, MACHINE.env.length-1, 1, @@ -598,7 +598,7 @@ var outputPort = MACHINE.params.currentOutputPort; if (MACHINE.argcount === 2) { testArgument(MACHINE, - 'isOutputPort', + 'output-port', isOutputPort, MACHINE.env.length-2, 1, @@ -611,19 +611,110 @@ }); + + installPrimitiveProcedure( + 'format', + new ArityAtLeast(1), + function(MACHINE) { + var args = [], i, formatString; + testArgument(MACHINE, + 'string', + isString, + MACHINE.env[MACHINE.env.length-1], + 0, + 'format'); + for(i = 0; i < MACHINE.argcount; i++) { + args.push(MACHINE.env[MACHINE.env.length - 1 - i]); + } + formatString = args.shift(); + return helpers.format(formatString, args, 'format'); + }); + + + + installPrimitiveProcedure( + 'printf', + new ArityAtLeast(1), + function(MACHINE) { + var args = [], i, formatString; + testArgument(MACHINE, + 'string', + isString, + MACHINE.env[MACHINE.env.length-1], + 0, + 'printf'); + for(i = 0; i < MACHINE.argcount; i++) { + args.push(MACHINE.env[MACHINE.env.length - 1 - i]); + } + formatString = args.shift(); + var result = helpers.format(formatString, args, 'format'); + var outputPort = MACHINE.params.currentOutputPort; + outputPort.writeDomNode(MACHINE, toDomNode(result, 'display')); + return VOID; + }); + + + installPrimitiveProcedure( + 'fprintf', + new ArityAtLeast(2), + function(MACHINE) { + var args = [], i, formatString; + testArgument(MACHINE, + 'output-port', + isOutputPort, + MACHINE.env[MACHINE.env.length-1], + 0, + 'fprintf'); + testArgument(MACHINE, + 'string', + isString, + MACHINE.env[MACHINE.env.length-2], + 1, + 'fprintf'); + for(i = 1; i < MACHINE.argcount; i++) { + args.push(MACHINE.env[MACHINE.env.length - 1 - i]); + } + formatString = args.shift(); + var result = helpers.format(formatString, args, 'format'); + var outputPort = MACHINE.env[MACHINE.env.length-1]; + outputPort.writeDomNode(MACHINE, toDomNode(result, '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]; + MACHINE.params['currentPrint'] = MACHINE.env[MACHINE.env.length - 1]; return VOID; } else { - return MACHINE.params['current-print']; + return MACHINE.params['currentPrint']; } }); + installPrimitiveProcedure( + 'current-output-port', + makeList(0, 1), + function(MACHINE) { + if (MACHINE.argcount === 1) { + MACHINE.params['currentOutputPort'] = MACHINE.env[MACHINE.env.length - 1]; + return VOID; + } else { + return MACHINE.params['currentOutputPort']; + } + }); + + + + + installPrimitiveProcedure( '=', new ArityAtLeast(2), @@ -1885,6 +1976,23 @@ + installPrimitiveProcedure( + 'format', + new ArityAtLeast(1), + function(MACHINE) { + var args = [], i, formatString; + testArgument(MACHINE, + 'string', + isString, + MACHINE.env[MACHINE.env.length-1], + 0, + 'format'); + for(i = 0; i < MACHINE.argcount; i++) { + args.push(MACHINE.env[MACHINE.env.length - 1 - i]); + } + formatString = args.shift(); + return helpers.format(formatString, args, 'format'); + }); @@ -1936,9 +2044,26 @@ }; - var HaltError = function() {} + var HaltError = function(onHalt) { + // onHalt: MACHINE -> void + this.onHalt = onHalt || function(MACHINE) {}; + }; + + + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + // The toplevel trampoline. + // + // + // trampoline: MACHINE (MACHINE -> void) -> void + // + // All evaluation in Racketland happens in the context of this + // trampoline. + // var trampoline = function(MACHINE, initialJump) { var thunk = initialJump; var startTime = (new Date()).valueOf(); @@ -1947,11 +2072,23 @@ MACHINE.params.maxNumBouncesBeforeYield; MACHINE.running = true; - while(thunk) { + while(true) { try { thunk(MACHINE); break; } catch (e) { + // There are a few kinds of things that can get thrown + // during racket evaluation: + // + // functions: this gets thrown if the Racket code realizes + // that the number of bounces has grown too large. The thrown + // function represents a restarter function. + // + // HaltError: causes evaluation to immediately halt. We schedule + // the onHalt function of the HaltError to call afterwards. + // + // everything else: otherwise, we send the exception value + // to the current error handler and exit. if (typeof(e) === 'function') { thunk = e; MACHINE.callsBeforeTrampoline = STACK_LIMIT_ESTIMATE; @@ -1961,24 +2098,36 @@ MACHINE, (new Date()).valueOf() - startTime); setTimeout( - function() { - trampoline(MACHINE, thunk); - }, + function() { trampoline(MACHINE, thunk); }, 0); return; - } + } else { + continue; + } } else if (e instanceof HaltError) { - // FIXME: work out what it really means to Halt. + MACHINE.running = false; + setTimeout( + function() { e.onHalt(MACHINE); }, + 0); return; } else { MACHINE.running = false; - return MACHINE.params.currentErrorHandler(MACHINE, e); + setTimeout( + function() { MACHINE.params.currentErrorHandler(MACHINE, e); }, + 0); + return; } } } MACHINE.running = false; - return MACHINE.params.currentSuccessHandler(MACHINE); + setTimeout( + function() { MACHINE.params.currentSuccessHandler(MACHINE); }, + 0); + return; }; + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// diff --git a/lang/kernel.rkt b/lang/kernel.rkt index 0216d63..344ba5a 100644 --- a/lang/kernel.rkt +++ b/lang/kernel.rkt @@ -145,6 +145,8 @@ ;; Racket's compiler can optimize these. (provide-stub-function + current-output-port + write display newline @@ -338,8 +340,9 @@ ;; string-copy ;; string->symbol ;; symbol->string -;; format -;; printf + format + printf + fprintf ;; build-string ;; string->immutable-string ;; string-set! diff --git a/tests/test-browser-evaluate.rkt b/tests/test-browser-evaluate.rkt index 277b418..c456943 100644 --- a/tests/test-browser-evaluate.rkt +++ b/tests/test-browser-evaluate.rkt @@ -623,6 +623,18 @@ EOF (test '(displayln (string->number "42")) "42\n") +(test '(displayln (format "The number is ~a" 42)) + "The number is 42\n") + + +(test '(printf "The number is ~a" 42) + "The number is 42") + +(test '(fprintf (current-output-port) "The number is ~a" 42) + "The number is 42") + + +