Skip to content

Commit 68cb593

Browse files
committed
Ch9 & Ch11
1 parent 2f27650 commit 68cb593

10 files changed

+649
-30
lines changed

Chapter11/Arrays.js

+175
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
function parseExpression(program) {
2+
program = skipSpace(program);
3+
var match, expr;
4+
if (match = /^"([^"]*)"/.exec(program))
5+
expr = {type: "value", value: match[1]};
6+
else if (match = /^\d+\b/.exec(program))
7+
expr = {type: "value", value: Number(match[0])};
8+
else if (match = /^[^\s(),"]+/.exec(program))
9+
expr = {type: "word", name: match[0]};
10+
else
11+
throw new SyntaxError("Unexpected syntax: " + program);
12+
13+
return parseApply(expr, program.slice(match[0].length));
14+
}
15+
16+
function skipSpace(string) {
17+
var first = string.search(/\S/);
18+
if (first == -1) return "";
19+
return string.slice(first);
20+
}
21+
22+
function parseApply(expr, program) {
23+
program = skipSpace(program);
24+
if (program[0] != "(")
25+
return {expr: expr, rest: program};
26+
27+
program = skipSpace(program.slice(1));
28+
expr = {type: "apply", operator: expr, args: []};
29+
while (program[0] != ")") {
30+
var arg = parseExpression(program);
31+
expr.args.push(arg.expr);
32+
program = skipSpace(arg.rest);
33+
if (program[0] == ",")
34+
program = skipSpace(program.slice(1));
35+
else if (program[0] != ")")
36+
throw new SyntaxError("Expected ',' or ')'");
37+
}
38+
return parseApply(expr, program.slice(1));
39+
}
40+
41+
function parse(program) {
42+
var result = parseExpression(program);
43+
if (skipSpace(result.rest).length > 0)
44+
throw new SyntaxError("Unexpected text after program");
45+
return result.expr;
46+
}
47+
48+
function evaluate(expr, env) {
49+
switch(expr.type) {
50+
case "value":
51+
return expr.value;
52+
53+
case "word":
54+
if (expr.name in env)
55+
return env[expr.name];
56+
else
57+
throw new ReferenceError("Undefined variable: " +
58+
expr.name);
59+
case "apply":
60+
if (expr.operator.type == "word" &&
61+
expr.operator.name in specialForms)
62+
return specialForms[expr.operator.name](expr.args,
63+
env);
64+
var op = evaluate(expr.operator, env);
65+
if (typeof op != "function")
66+
throw new TypeError("Applying a non-function.");
67+
return op.apply(null, expr.args.map(function(arg) {
68+
return evaluate(arg, env);
69+
}));
70+
}
71+
}
72+
73+
var specialForms = Object.create(null);
74+
75+
specialForms["if"] = function(args, env) {
76+
if (args.length != 3)
77+
throw new SyntaxError("Bad number of args to if");
78+
79+
if (evaluate(args[0], env) !== false)
80+
return evaluate(args[1], env);
81+
else
82+
return evaluate(args[2], env);
83+
};
84+
85+
specialForms["while"] = function(args, env) {
86+
if (args.length != 2)
87+
throw new SyntaxError("Bad number of args to while");
88+
89+
while (evaluate(args[0], env) !== false)
90+
evaluate(args[1], env);
91+
92+
// Since undefined does not exist in Egg, we return false,
93+
// for lack of a meaningful result.
94+
return false;
95+
};
96+
97+
specialForms["do"] = function(args, env) {
98+
var value = false;
99+
args.forEach(function(arg) {
100+
value = evaluate(arg, env);
101+
});
102+
return value;
103+
};
104+
105+
specialForms["define"] = function(args, env) {
106+
if (args.length != 2 || args[0].type != "word")
107+
throw new SyntaxError("Bad use of define");
108+
var value = evaluate(args[1], env);
109+
env[args[0].name] = value;
110+
return value;
111+
};
112+
113+
var topEnv = Object.create(null);
114+
115+
topEnv["true"] = true;
116+
topEnv["false"] = false;
117+
118+
["+", "-", "*", "/", "==", "<", ">"].forEach(function(op) {
119+
topEnv[op] = new Function("a, b", "return a " + op + " b;");
120+
});
121+
122+
topEnv["print"] = function(value) {
123+
console.log(value);
124+
return value;
125+
};
126+
127+
function run() {
128+
var env = Object.create(topEnv);
129+
var program = Array.prototype.slice
130+
.call(arguments, 0).join("\n");
131+
return evaluate(parse(program), env);
132+
}
133+
134+
specialForms["fun"] = function(args, env) {
135+
if (!args.length)
136+
throw new SyntaxError("Functions need a body");
137+
function name(expr) {
138+
if (expr.type != "word")
139+
throw new SyntaxError("Arg names must be words");
140+
return expr.name;
141+
}
142+
var argNames = args.slice(0, args.length - 1).map(name);
143+
var body = args[args.length - 1];
144+
145+
return function() {
146+
if (arguments.length != argNames.length)
147+
throw new TypeError("Wrong number of arguments");
148+
var localEnv = Object.create(env);
149+
for (var i = 0; i < arguments.length; i++)
150+
localEnv[argNames[i]] = arguments[i];
151+
return evaluate(body, localEnv);
152+
};
153+
};
154+
155+
topEnv["array"] = function() {
156+
return Array.prototype.slice.call(arguments, 0);
157+
};
158+
159+
topEnv["length"] = function(array) {
160+
return array.length;
161+
};
162+
163+
topEnv["element"] = function(array, n) {
164+
return array[n];
165+
};
166+
167+
run("do(define(sum, fun(array,",
168+
" do(define(i, 0),",
169+
" define(sum, 0),",
170+
" while(<(i, length(array)),",
171+
" do(define(sum, +(sum, element(array, i))),",
172+
" define(i, +(i, 1)))),",
173+
" sum))),",
174+
" print(sum(array(1, 2, 3))))");
175+
// → 6

Chapter11/Comments.js

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
function parseExpression(program) {
2+
program = skipSpace(program);
3+
var match, expr;
4+
if (match = /^"([^"]*)"/.exec(program))
5+
expr = {type: "value", value: match[1]};
6+
else if (match = /^\d+\b/.exec(program))
7+
expr = {type: "value", value: Number(match[0])};
8+
else if (match = /^[^\s(),"]+/.exec(program))
9+
expr = {type: "word", name: match[0]};
10+
else
11+
throw new SyntaxError("Unexpected syntax: " + program);
12+
13+
return parseApply(expr, program.slice(match[0].length));
14+
}
15+
16+
function parseApply(expr, program) {
17+
program = skipSpace(program);
18+
if (program[0] != "(")
19+
return {expr: expr, rest: program};
20+
21+
program = skipSpace(program.slice(1));
22+
expr = {type: "apply", operator: expr, args: []};
23+
while (program[0] != ")") {
24+
var arg = parseExpression(program);
25+
expr.args.push(arg.expr);
26+
program = skipSpace(arg.rest);
27+
if (program[0] == ",")
28+
program = skipSpace(program.slice(1));
29+
else if (program[0] != ")")
30+
throw new SyntaxError("Expected ',' or ')'");
31+
}
32+
return parseApply(expr, program.slice(1));
33+
}
34+
35+
function parse(program) {
36+
var result = parseExpression(program);
37+
if (skipSpace(result.rest).length > 0)
38+
throw new SyntaxError("Unexpected text after program");
39+
return result.expr;
40+
}
41+
42+
43+
function skipSpace(string) {
44+
var skip = /^(\s|#.*)*/.exec(string);
45+
return string.slice(skip[0].length);
46+
}
47+
48+
console.log(parse("# hello\nx"));
49+
// → {type: "word", name: "x"}
50+
51+
console.log(parse("a # one\n # two\n()"));
52+
// → {type: "apply",
53+
// operator: {type: "word", name: "a"},
54+
// args: []}

0 commit comments

Comments
 (0)