continuing to add

This commit is contained in:
Danny Yoo 2011-06-27 16:32:58 -04:00
parent 48f8acf121
commit 0ea0b05206
4 changed files with 210 additions and 34 deletions

View File

@ -27,12 +27,13 @@ if (! this['plt']) { this['plt'] = {}; }
// format: string [X ...] string -> string
// String formatting.
var format = function(formatStr, args, functionName) { var format = function(formatStr, args, functionName) {
var throwFormatError = function() { var throwFormatError = function() {
functionName = functionName || '#<procedure>'; functionName = functionName || 'format';
var matches = formatStr.match(new RegExp('~[sSaA]', 'g')); 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 var errorStrBuffer = [functionName + ': format string requires ' + expectedNumberOfArgs
+ ' arguments, given ' + args.length + '; arguments were:', + ' arguments, given ' + args.length + '; arguments were:',
toWrittenString(formatStr)]; toWrittenString(formatStr)];
@ -40,38 +41,47 @@ if (! this['plt']) { this['plt'] = {}; }
errorStrBuffer.push( toWrittenString(args[i]) ); errorStrBuffer.push( toWrittenString(args[i]) );
} }
raise( types.incompleteExn(types.exnFailContract, errorStrBuffer.join(' '), []) ); throw new Error(errorStrBuffer.join(' '));
} }
var pattern = new RegExp("~[sSaAneE%~]", "g"); var pattern = new RegExp("~[sSaAnevE%~]", "g");
var buffer = args.slice(0);; var buffer = args.slice(0);
function f(s) { var onTemplate = function(s) {
if (s == "~~") { if (s === "~~") {
return "~"; return "~";
} else if (s == '~n' || s == '~%') { } else if (s === '~n' || s === '~%') {
return "\n"; return "\n";
} else if (s == '~s' || s == "~S") { } else if (s === '~s' || s === "~S") {
if (buffer.length == 0) { if (buffer.length === 0) {
throwFormatError(); throwFormatError();
} }
return toWrittenString(buffer.shift()); 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 // FIXME: we don't yet have support for the error-print
// handler, and currently treat ~e just like ~s. // handler, and currently treat ~e just like ~s.
if (buffer.length == 0) { if (buffer.length === 0) {
throwFormatError(); 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()); return toWrittenString(buffer.shift());
} else if (s == '~a' || s == "~A") { } else if (s === '~a' || s === "~A") {
if (buffer.length == 0) { if (buffer.length === 0) {
throwFormatError(); throwFormatError();
} }
return toDisplayedString(buffer.shift()); return toDisplayedString(buffer.shift());
} else { } 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) { if (buffer.length > 0) {
throwFormatError(); throwFormatError();
} }
@ -79,6 +89,8 @@ if (! this['plt']) { this['plt'] = {}; }
}; };
// forEachK: CPS( array CPS(array -> void) (error -> void) -> void ) // forEachK: CPS( array CPS(array -> void) (error -> void) -> void )
// Iterates through an array and applies f to each element using CPS // 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 // If an error is thrown, it catches the error and calls f_error on it

View File

@ -113,7 +113,7 @@
'numBouncesBeforeYield': 2000, // self-adjusting 'numBouncesBeforeYield': 2000, // self-adjusting
'maxNumBouncesBeforeYield': 2000, // self-adjusting 'maxNumBouncesBeforeYield': 2000, // self-adjusting
'current-print': new Closure( 'currentPrint': new Closure(
function(MACHINE) { function(MACHINE) {
if(--MACHINE.callsBeforeTrampoline<0) { throw arguments.callee; } if(--MACHINE.callsBeforeTrampoline<0) { throw arguments.callee; }
@ -562,7 +562,7 @@
var outputPort = MACHINE.params.currentOutputPort; var outputPort = MACHINE.params.currentOutputPort;
if (MACHINE.argcount === 2) { if (MACHINE.argcount === 2) {
testArgument(MACHINE, testArgument(MACHINE,
'isOutputPort', 'output-port',
isOutputPort, isOutputPort,
MACHINE.env.length-2, MACHINE.env.length-2,
1, 1,
@ -579,7 +579,7 @@
var outputPort = MACHINE.params.currentOutputPort; var outputPort = MACHINE.params.currentOutputPort;
if (MACHINE.argcount === 1) { if (MACHINE.argcount === 1) {
testArgument(MACHINE, testArgument(MACHINE,
'isOutputPort', 'output-port',
isOutputPort, isOutputPort,
MACHINE.env.length-1, MACHINE.env.length-1,
1, 1,
@ -598,7 +598,7 @@
var outputPort = MACHINE.params.currentOutputPort; var outputPort = MACHINE.params.currentOutputPort;
if (MACHINE.argcount === 2) { if (MACHINE.argcount === 2) {
testArgument(MACHINE, testArgument(MACHINE,
'isOutputPort', 'output-port',
isOutputPort, isOutputPort,
MACHINE.env.length-2, MACHINE.env.length-2,
1, 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( installPrimitiveProcedure(
'current-print', 'current-print',
makeList(0, 1), makeList(0, 1),
function(MACHINE) { function(MACHINE) {
if (MACHINE.argcount === 1) { 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; return VOID;
} else { } 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( installPrimitiveProcedure(
'=', '=',
new ArityAtLeast(2), 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 trampoline = function(MACHINE, initialJump) {
var thunk = initialJump; var thunk = initialJump;
var startTime = (new Date()).valueOf(); var startTime = (new Date()).valueOf();
@ -1947,11 +2072,23 @@
MACHINE.params.maxNumBouncesBeforeYield; MACHINE.params.maxNumBouncesBeforeYield;
MACHINE.running = true; MACHINE.running = true;
while(thunk) { while(true) {
try { try {
thunk(MACHINE); thunk(MACHINE);
break; break;
} catch (e) { } 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') { if (typeof(e) === 'function') {
thunk = e; thunk = e;
MACHINE.callsBeforeTrampoline = STACK_LIMIT_ESTIMATE; MACHINE.callsBeforeTrampoline = STACK_LIMIT_ESTIMATE;
@ -1961,24 +2098,36 @@
MACHINE, MACHINE,
(new Date()).valueOf() - startTime); (new Date()).valueOf() - startTime);
setTimeout( setTimeout(
function() { function() { trampoline(MACHINE, thunk); },
trampoline(MACHINE, thunk);
},
0); 0);
return; return;
} } else {
continue;
}
} else if (e instanceof HaltError) { } else if (e instanceof HaltError) {
// FIXME: work out what it really means to Halt. MACHINE.running = false;
setTimeout(
function() { e.onHalt(MACHINE); },
0);
return; return;
} else { } else {
MACHINE.running = false; MACHINE.running = false;
return MACHINE.params.currentErrorHandler(MACHINE, e); setTimeout(
function() { MACHINE.params.currentErrorHandler(MACHINE, e); },
0);
return;
} }
} }
} }
MACHINE.running = false; MACHINE.running = false;
return MACHINE.params.currentSuccessHandler(MACHINE); setTimeout(
function() { MACHINE.params.currentSuccessHandler(MACHINE); },
0);
return;
}; };
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////

View File

@ -145,6 +145,8 @@
;; Racket's compiler can optimize these. ;; Racket's compiler can optimize these.
(provide-stub-function (provide-stub-function
current-output-port
write write
display display
newline newline
@ -338,8 +340,9 @@
;; string-copy ;; string-copy
;; string->symbol ;; string->symbol
;; symbol->string ;; symbol->string
;; format format
;; printf printf
fprintf
;; build-string ;; build-string
;; string->immutable-string ;; string->immutable-string
;; string-set! ;; string-set!

View File

@ -623,6 +623,18 @@ EOF
(test '(displayln (string->number "42")) (test '(displayln (string->number "42"))
"42\n") "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")