-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathruntime.js
385 lines (352 loc) · 8.99 KB
/
runtime.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
// primitive datatypes
function Pair(car, cdr) {
this.car = car;
this.cdr = cdr;
}
// boxed so they can be mutable (JS strings are used as Scheme symbols)
function SchemeString(val) {
this.val = val;
}
// JS has no character type!
function SchemeChar(val) {
this.val = val;
}
// takes a JS length-1 string and returns the appropriate SchemeChar.
var interned_chars = new Object ();
function intern_char(c) {
if (interned_chars[c]===undefined) {
interned_chars[c] = new SchemeChar (c);
}
return interned_chars[c];
};
// ports
function SchemeInputPort(readfn, peekfn, readyfn, closefn) {
this.readfn = readfn;
this.peekfn = peekfn;
this.readyfn = readyfn;
this.closefn = closefn;
};
function SchemeOutputPort(writefn, closefn) {
this.writefn = writefn;
this.closefn = closefn;
};
function EOF () {};
var theEOF = new EOF ();
// multiple values are boxed as a list thus.
function MultipleValues(val) {
this.val = val;
}
function Nil(){}
var theNil = new Nil();
// compiler support
// turn VEC (skipping N elements) into a list.
function sinjs_restify(vec,n) {
var l = theNil, i;
for (i = vec.length - 1; i >= n; i -= 1) {
l = new Pair(vec[i],l);
}
return l;
}
// top level variables
var top_level_binding = new Object();
// stack management
//
// current stack depth
var sinjs_stack_depth;
// *rough* maximum stack depth
var sinjs_stack_max = 30;
// call PROC as the top of a sinjs stack. A sinjs stack is
// really just a stack, but frames never return.
// When we have made a bunch of stack frames, we eventually throw
// a SINJSrestartstack exception, which simply continues here. If
// we get #f as a restart procedure, we return the VAL member of the
// exception.
/*
function sinjs_start_stack(proc) {
var e = { restart: proc };
while (true) {
try {
sinjs_stack_depth = 0;
proc ();
} catch (newe) {
if (newe.name === "SINJSrestartstack") {
if (newe.proc === false) {
return newe.val;
}
else {
e = newe;
}
} else {
throw newe;
};
};
};
};
*/
function sinjs_start_stack (proc) {
while (true) {
proc = proc ();
};
}
// top level execution
// execute the Nth top-level procedure and proceed to the N+1th after.
function top_level_run (n) {
return function () {return (scheme_top_level_table[n])(function (){return top_level_run(n+1);});};
}
function scheme_top_level() {
try {
sinjs_start_stack (function () {return top_level_run(0);});
} catch (e) {
if (e.name === "SINJSreturn") {
return e.value;
} else {
throw e;
}
}
}
function scheme_top_level_done () {
throw { name: "SINJSreturn",
value: "sinjs-top-level-undefined" };
}
// top level REPL support
// fun is a function, do it!
function sinjs_repl_execute(fun) {
try {
sinjs_start_stack (fun);
} catch (e) {
if (e.name === "SINJSreturn") {
return e.value;
} else {
print ("sinjs exception [" + e.name + "]: " + e.obj + ": " + e.message);
return false;
}
};
};
// continuation for forms pumped to top level repl. write the result;
// then a newline, then throw out to the top.
// k for top level functions in repl: print answer, then escape
function sinjs_repl_k(answer) {
return function () {return (top_level_binding['write'])(sinjs_repl_print_newline, answer);};
}
function sinjs_repl_print_newline(ignored) {
return function () {return (top_level_binding['newline'])(scheme_top_level_done);};
};
// execute the top-level function for library code in repl (don't print answer)
function sinjs_repl_noprint_k(answer) {
return function () {scheme_top_level_done(answer)};
};
//
// Builtin procedures
//
// helper functions
function check_integer(a) {
if ((typeof(a) !== "number") || (a !== Math.floor(a)))
throw { name: "SINJStypeerror",
message: a + " is not an integer" };
}
function check_pair(a) {
if (a.constructor !== Pair)
throw { name: "SINJStypeerror",
message: a + " is not a pair" };
}
function check_string(a) {
if (a.constructor !== SchemeString)
throw { name: "SINJStypeerror",
message: a + " is not a string" };
}
function check_char(a) {
if (a.constructor !== SchemeChar)
throw { name: "SINJStypeerror",
message: a + " is not a char" };
}
function check_procedure (a) {
if (typeof(a) !== "function")
throw { name: "SINJStypeerror",
message: a + " is not a procedure" };
}
function check_pair_or_null (a) {
if ((a.constructor !== Pair) && (a !== theNil))
throw { name: "SINJStypeerror",
message: a + " is not a list" };
}
function check_input_port (a) {
if (a.constructor !== SchemeInputPort)
throw { name: "SINJStypeerror",
message: a + " is not an input port" };
}
function check_output_port (a) {
if (a.constructor !== SchemeOutputPort)
throw { name: "SINJStypeerror",
message: a + " is not an output port" };
}
top_level_binding['make-string'] = function (k, n) {
var init, s, i;
check_integer(n);
if (arguments.length > 2) {
check_char (arguments[2]);
init = arguments[2].val;
} else {
init = "!"; // should stand out nicely
};
s = '';
for (i = 0; i < n; i += 1) {
s = s + init;
};
print("make string returning " + s);
return k(new SchemeString(s));
};
top_level_binding['string'] = function (k) {
var s, i;
s = '';
for (i = 1; i < arguments.length; i += 1) {
check_char(arguments[i]);
s = s + arguments[i].val;
};
return k(new SchemeString(s));
};
top_level_binding['%%string'] = function (k) {
var s, i;
s = '';
for (i = 1; i < arguments.length; i += 1) {
s = s + arguments[i].val;
};
return k(new SchemeString(s));
};
top_level_binding['string-append'] = function (k) {
var s = "", i;
for (i = 1; i < arguments.length; i += 1) {
check_string (arguments[i]);
s = s + arguments[i].val;
}
return k(new SchemeString(s));
};
top_level_binding['%%string-append'] = function (k) {
var s = "", i;
for (i = 1; i < arguments.length; i += 1) {
s = s + arguments[i].val;
}
return k(new SchemeString(s));
};
top_level_binding['list->string'] = function (k, lis) {
var s = "";
while (lis !== theNil) {
check_pair(lis);
check_char(lis.car);
s = s + lis.car.val;
lis = lis.cdr;
};
return k(new SchemeString(s));
};
top_level_binding['string-fill!'] = function (k, string, c) {
var s, i;
check_string (string);
check_char (c);
s = '';
for (i = 0; i < string.val.length; i += 1) {
s = s + c.val;
};
string.val = s;
return k("string-fill! undefined value");
};
// 6.3.6 Scheme vectors are just JS arrays [from class Array]
top_level_binding['make-vector'] = function (k, n) {
var fill, a, i;
check_integer (n);
if (arguments.length > 2) {
fill = arguments[2];
} else {
fill = "make-vector undefined value";
};
a = [];
for (i = 0; i < n; i += 1) {
a[i] = fill;
};
return k(a);
};
top_level_binding['vector'] = function (k) {
var a, i;
a = [];
for (i = 1; i < arguments.length; i += 1) {
a[i-1] = arguments[i];
};
return k(a);
};
top_level_binding['list->vector'] = function (k, lis) {
var a, i;
a = [];
i = 0;
while (lis !== theNil) {
check_pair(lis);
a[i] = lis.car;
i += 1;
lis = lis.cdr;
};
return k(a);
};
top_level_binding['%%list->vector'] = function (k, lis) {
var a, i;
a = [];
i = 0;
while (lis !== theNil) {
a[i] = lis.car;
i += 1;
lis = lis.cdr;
};
return k(a);
};
sinjs_apply =
top_level_binding['apply'] = function (k, proc) {
var args = [], i, p;
check_procedure (proc)
args[0] = k;
for (i = 1; i < (arguments.length - 2); i += 1) {
args[i] = arguments[i + 1];
}
p = arguments[arguments.length - 1];
check_pair_or_null (p);
while (p !== theNil) {
args[i] = p.car;
i += 1;
p = p.cdr;
check_pair_or_null (p);
};
return proc.apply(null, args);
};
sinjs_apply =
top_level_binding['%%apply'] = function (k, proc) {
var args = [], i, p;
args[0] = k;
for (i = 1; i < (arguments.length - 2); i += 1) {
args[i] = arguments[i + 1];
}
p = arguments[arguments.length - 1];
while (p !== theNil) {
args[i] = p.car;
i += 1;
p = p.cdr;
};
return proc.apply(null, args);
};
// dynamic wind not yet implemented xxx
top_level_binding['call-with-current-continuation'] = function (k, proc) {
check_procedure (proc);
// can't just pass K as the arg to PROC, because it's a naked
// continuation. Boxed continuations will be called as Scheme
// procedures, which take an extra k (ignored in this case).
return proc(k,
(function (kont) {
kont = false; // in case some GC is looking at it?
if (arguments.length !== 2)
return k(new MultipleValues
(sinjs_restify(arguments, 1)));
else
return k(arguments[1]);
}));
};
// R5RS 6.6
//
// not implemented here (must be in platform-specific code):
// open-input-file open-output-file
//
// magical global vars, must be initialized in platform-specific code.
var sinjs_current_input_port, sinjs_current_output_port;