Skip to content

Commit 6915188

Browse files
author
RayMorgan
committed
Enhanced the repl library.
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
1 parent 2f56ccb commit 6915188

File tree

1 file changed

+156
-18
lines changed

1 file changed

+156
-18
lines changed

lib/repl.js

Lines changed: 156 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,175 @@
11
// A repl library that you can include in your own code to get a runtime
22
// interface to your program. Just require("/repl.js").
33

4+
puts("Type '.help' for options.");
5+
46
node.stdio.open();
7+
node.stdio.addListener("data", readline);
58

69
var buffered_cmd = '';
710
var trimmer = /^\s*(.+)\s*$/m;
11+
var scopedVar = /^\s*var\s*([_\w\$]+)(.*)$/m;
12+
var scopeFunc = /^\s*function\s*([_\w\$]+)/;
813

914
exports.prompt = "node> ";
10-
11-
function displayPrompt () {
12-
print(buffered_cmd.length ? '... ' : exports.prompt);
13-
}
15+
exports.scope = {};
1416

1517
displayPrompt();
1618

17-
node.stdio.addListener("data", function (cmd) {
18-
var matches = trimmer.exec(cmd);
1919

20-
if (matches && matches.length == 2) {
21-
cmd = matches[1];
20+
/**
21+
* The main REPL function. This is called everytime the user enters
22+
* data on the command line.
23+
*/
24+
function readline (cmd) {
25+
cmd = trimWhitespace(cmd);
26+
27+
// Check to see if a REPL keyword was used. If it returns true,
28+
// display next prompt and return.
29+
if (parseREPLKeyword(cmd) === true) {
30+
return;
31+
}
32+
33+
// The catchall for errors
34+
try {
35+
buffered_cmd += cmd;
36+
// This try is for determining if the command is complete, or should
37+
// continue onto the next line.
2238
try {
23-
buffered_cmd += cmd;
24-
try {
25-
puts(JSON.stringify(eval(buffered_cmd)));
26-
buffered_cmd = '';
27-
} catch (e) {
28-
if (!(e instanceof SyntaxError))
29-
throw e;
39+
buffered_cmd = convertToScope(buffered_cmd);
40+
41+
// Scope the readline with exports.scope to provide "local" vars
42+
with (exports.scope) {
43+
var ret = eval(buffered_cmd);
44+
printValue(ret);
3045
}
31-
} catch (e) {
32-
puts('caught an exception: ' + e);
46+
3347
buffered_cmd = '';
48+
} catch (e) {
49+
if (!(e instanceof SyntaxError))
50+
throw e;
3451
}
52+
} catch (e) {
53+
// On error: Print the error and clear the buffer
54+
puts('caught an exception: ' + e);
55+
buffered_cmd = '';
3556
}
57+
3658
displayPrompt();
37-
});
59+
}
60+
61+
62+
/**
63+
* Used to display the prompt.
64+
*/
65+
function displayPrompt () {
66+
print(buffered_cmd.length ? '... ' : exports.prompt);
67+
}
68+
69+
/**
70+
* Used to parse and execute the Node REPL commands.
71+
*
72+
* @param {cmd} cmd The command entered to check
73+
* @returns {Boolean} If true it means don't continue parsing the command
74+
*/
75+
function parseREPLKeyword (cmd) {
76+
switch (cmd) {
77+
case ".break":
78+
buffered_cmd = '';
79+
displayPrompt();
80+
return true;
81+
case ".clear":
82+
puts("Clearing Scope...");
83+
buffered_cmd = '';
84+
exports.scope = {};
85+
displayPrompt();
86+
return true;
87+
case ".exit":
88+
node.stdio.close();
89+
return true;
90+
case ".help":
91+
puts(".break\tSometimes you get stuck in a place you can't get out... This will get you out.");
92+
puts(".clear\tBreak, and also clear the local scope.");
93+
puts(".exit\tExit the prompt");
94+
puts(".help\tShow repl options");
95+
displayPrompt();
96+
return true;
97+
}
98+
return 0;
99+
}
100+
101+
/**
102+
* Trims Whitespace from a line.
103+
*
104+
* @param {String} cmd The string to trim the whitespace from
105+
* @returns {String} The trimmed string
106+
*/
107+
function trimWhitespace (cmd) {
108+
var matches = trimmer.exec(cmd);
109+
if (matches && matches.length == 2) {
110+
return matches[1];
111+
}
112+
}
113+
114+
/**
115+
* Converts commands that use var and function <name>() to use the
116+
* local exports.scope when evaled. This provides a local scope
117+
* on the REPL.
118+
*
119+
* @param {String} cmd The cmd to convert
120+
* @returns {String} The converted command
121+
*/
122+
function convertToScope (cmd) {
123+
var matches;
124+
125+
// Replaces: var foo = "bar"; with: exports.scope.foo = bar;
126+
matches = scopedVar.exec(cmd);
127+
if (matches && matches.length == 3) {
128+
return "exports.scope." + matches[1] + matches[2];
129+
}
130+
131+
// Replaces: function foo() {}; with: foo = function foo() {};
132+
matches = scopeFunc.exec(buffered_cmd);
133+
if (matches && matches.length == 2) {
134+
return matches[1] + " = " + buffered_cmd;
135+
}
136+
137+
return cmd;
138+
}
139+
140+
/**
141+
* Echos the value of a value. Trys to print the value out
142+
* in the best way possible given the different types.
143+
*
144+
* @param {Object} value The object to print out
145+
*/
146+
function printValue (value) {
147+
if (value === 0) {
148+
puts("0");
149+
return;
150+
}
151+
152+
if (value === false) {
153+
puts("false");
154+
return;
155+
}
156+
157+
if (value === "") {
158+
puts('""');
159+
return;
160+
}
161+
162+
if (typeof(value) == "function") {
163+
puts("[Function]");
164+
return;
165+
}
166+
167+
try {
168+
puts(JSON.stringify(value));
169+
} catch (e) {
170+
if (e.message.search("circular"))
171+
puts("[Circular Object]");
172+
else
173+
throw e;
174+
}
175+
}

0 commit comments

Comments
 (0)