-
Notifications
You must be signed in to change notification settings - Fork 30k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Now supports: - command options: .help, .break, .clear, .exit - local vars and global functions - ability to print 0, false and "" - when value is a function, prints [Function] - when object is circular, prints [Circular Object] instead of throwing an error
- Loading branch information
RayMorgan
committed
Sep 25, 2009
1 parent
2f56ccb
commit 6915188
Showing
1 changed file
with
156 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,175 @@ | ||
// A repl library that you can include in your own code to get a runtime | ||
// interface to your program. Just require("/repl.js"). | ||
|
||
puts("Type '.help' for options."); | ||
|
||
node.stdio.open(); | ||
node.stdio.addListener("data", readline); | ||
|
||
var buffered_cmd = ''; | ||
var trimmer = /^\s*(.+)\s*$/m; | ||
var scopedVar = /^\s*var\s*([_\w\$]+)(.*)$/m; | ||
var scopeFunc = /^\s*function\s*([_\w\$]+)/; | ||
|
||
exports.prompt = "node> "; | ||
|
||
function displayPrompt () { | ||
print(buffered_cmd.length ? '... ' : exports.prompt); | ||
} | ||
exports.scope = {}; | ||
|
||
displayPrompt(); | ||
|
||
node.stdio.addListener("data", function (cmd) { | ||
var matches = trimmer.exec(cmd); | ||
|
||
if (matches && matches.length == 2) { | ||
cmd = matches[1]; | ||
/** | ||
* The main REPL function. This is called everytime the user enters | ||
* data on the command line. | ||
*/ | ||
function readline (cmd) { | ||
cmd = trimWhitespace(cmd); | ||
|
||
// Check to see if a REPL keyword was used. If it returns true, | ||
// display next prompt and return. | ||
if (parseREPLKeyword(cmd) === true) { | ||
return; | ||
} | ||
|
||
// The catchall for errors | ||
try { | ||
buffered_cmd += cmd; | ||
// This try is for determining if the command is complete, or should | ||
// continue onto the next line. | ||
try { | ||
buffered_cmd += cmd; | ||
try { | ||
puts(JSON.stringify(eval(buffered_cmd))); | ||
buffered_cmd = ''; | ||
} catch (e) { | ||
if (!(e instanceof SyntaxError)) | ||
throw e; | ||
buffered_cmd = convertToScope(buffered_cmd); | ||
|
||
// Scope the readline with exports.scope to provide "local" vars | ||
with (exports.scope) { | ||
var ret = eval(buffered_cmd); | ||
printValue(ret); | ||
} | ||
} catch (e) { | ||
puts('caught an exception: ' + e); | ||
|
||
buffered_cmd = ''; | ||
} catch (e) { | ||
if (!(e instanceof SyntaxError)) | ||
throw e; | ||
} | ||
} catch (e) { | ||
// On error: Print the error and clear the buffer | ||
puts('caught an exception: ' + e); | ||
buffered_cmd = ''; | ||
} | ||
|
||
displayPrompt(); | ||
}); | ||
} | ||
|
||
|
||
/** | ||
* Used to display the prompt. | ||
*/ | ||
function displayPrompt () { | ||
print(buffered_cmd.length ? '... ' : exports.prompt); | ||
} | ||
|
||
/** | ||
* Used to parse and execute the Node REPL commands. | ||
* | ||
* @param {cmd} cmd The command entered to check | ||
* @returns {Boolean} If true it means don't continue parsing the command | ||
*/ | ||
function parseREPLKeyword (cmd) { | ||
switch (cmd) { | ||
case ".break": | ||
buffered_cmd = ''; | ||
displayPrompt(); | ||
return true; | ||
case ".clear": | ||
puts("Clearing Scope..."); | ||
buffered_cmd = ''; | ||
exports.scope = {}; | ||
displayPrompt(); | ||
return true; | ||
case ".exit": | ||
node.stdio.close(); | ||
return true; | ||
case ".help": | ||
puts(".break\tSometimes you get stuck in a place you can't get out... This will get you out."); | ||
puts(".clear\tBreak, and also clear the local scope."); | ||
puts(".exit\tExit the prompt"); | ||
puts(".help\tShow repl options"); | ||
displayPrompt(); | ||
return true; | ||
} | ||
return 0; | ||
} | ||
|
||
/** | ||
* Trims Whitespace from a line. | ||
* | ||
* @param {String} cmd The string to trim the whitespace from | ||
* @returns {String} The trimmed string | ||
*/ | ||
function trimWhitespace (cmd) { | ||
var matches = trimmer.exec(cmd); | ||
if (matches && matches.length == 2) { | ||
return matches[1]; | ||
} | ||
} | ||
|
||
/** | ||
* Converts commands that use var and function <name>() to use the | ||
* local exports.scope when evaled. This provides a local scope | ||
* on the REPL. | ||
* | ||
* @param {String} cmd The cmd to convert | ||
* @returns {String} The converted command | ||
*/ | ||
function convertToScope (cmd) { | ||
var matches; | ||
|
||
// Replaces: var foo = "bar"; with: exports.scope.foo = bar; | ||
matches = scopedVar.exec(cmd); | ||
if (matches && matches.length == 3) { | ||
return "exports.scope." + matches[1] + matches[2]; | ||
} | ||
|
||
// Replaces: function foo() {}; with: foo = function foo() {}; | ||
matches = scopeFunc.exec(buffered_cmd); | ||
if (matches && matches.length == 2) { | ||
return matches[1] + " = " + buffered_cmd; | ||
} | ||
|
||
return cmd; | ||
} | ||
|
||
/** | ||
* Echos the value of a value. Trys to print the value out | ||
* in the best way possible given the different types. | ||
* | ||
* @param {Object} value The object to print out | ||
*/ | ||
function printValue (value) { | ||
if (value === 0) { | ||
puts("0"); | ||
return; | ||
} | ||
|
||
if (value === false) { | ||
puts("false"); | ||
return; | ||
} | ||
|
||
if (value === "") { | ||
puts('""'); | ||
return; | ||
} | ||
|
||
if (typeof(value) == "function") { | ||
puts("[Function]"); | ||
return; | ||
} | ||
|
||
try { | ||
puts(JSON.stringify(value)); | ||
} catch (e) { | ||
if (e.message.search("circular")) | ||
puts("[Circular Object]"); | ||
else | ||
throw e; | ||
} | ||
} |