yhara-tickets 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1798 @@
1
+ //
2
+ // Heap based scheme from 3imp.pdf
3
+ //
4
+
5
+ // default definition of puts: should be overriden for console interpreters
6
+ function puts(str, no_newline){
7
+ var text = (str + (no_newline ? "" : "\n")).escapeHTML();
8
+ var span = document.createElement("span");
9
+ span.innerHTML = text.replace(/\n/g,"<br>").replace(/ /g,"&nbsp;");
10
+ $('bs-console').insert(span);
11
+ }
12
+ function p(/*args*/){
13
+ puts("p> "+$A(arguments).map(Object.inspect).join(" "));
14
+ }
15
+
16
+ if( typeof(BiwaScheme)!='object' ) BiwaScheme={}; with(BiwaScheme) {
17
+ /* --------------------------------------- namespace webscheme */
18
+
19
+ //
20
+ // javascript extention
21
+ //
22
+
23
+ // Object.prototype.inspect = function() {
24
+ // var a = [];
25
+ // for(var k in this){
26
+ // //if(this.prototype[k]) continue;
27
+ // a.push( k.toString() );//+" => "+this[k].toString() );
28
+ // }
29
+ // return "#<Object{"+a.join(",")+"}>";
30
+ // }
31
+ Function.prototype.to_write = function() {
32
+ return "#<Function "+(this.fname ? this.fname : this.toSource().truncate(40))+">";
33
+ }
34
+ String.prototype.to_write = function(){
35
+ return '"' +
36
+ this.replace(/\\|\"/g,function($0){return'\\'+$0;})
37
+ .replace(/\x07/g, "\\a")
38
+ .replace(/\x08/g, "\\b")
39
+ .replace(/\t/g, "\\t")
40
+ .replace(/\n/g, "\\n")
41
+ .replace(/\v/g, "\\v")
42
+ .replace(/\f/g, "\\f")
43
+ .replace(/\r/g, "\\r") +
44
+ '"';
45
+ }
46
+ //Number.prototype.inspect = function() { return this.toString(); }
47
+ Array.prototype.to_write = function(){
48
+ var a = [];
49
+ for(var i=0; i<this.length; i++)
50
+ a.push(to_write(this[i]));
51
+ return '#(' + a.join(" ") + ')';
52
+ }
53
+ Array.prototype.to_list = function(){
54
+ var list = nil;
55
+ for(var i=this.length-1; i>=0; i--){
56
+ list = new Pair(this[i], list);
57
+ }
58
+ return list;
59
+ }
60
+ // Array.prototype.memq = function(x){
61
+ // for(var i=this.length-1; i>=0; i--){
62
+ // if(this[i] === x)
63
+ // return true;
64
+ // }
65
+ // return false;
66
+ // }
67
+
68
+ //
69
+ // utility functions
70
+ //
71
+ BiwaScheme.to_write = function(obj){
72
+ if(obj === undefined)
73
+ return "undefined";
74
+ else if(typeof(obj.to_write) == 'function')
75
+ return obj.to_write();
76
+ else if(isNaN(obj) && typeof(obj) == 'number')
77
+ return "+nan.0";
78
+ else{
79
+ switch(obj){
80
+ case true: return "#t";
81
+ case false: return "#f";
82
+ case nil: return "()";
83
+ case Infinity: return "+inf.0";
84
+ case -Infinity: return "-inf.0";
85
+ }
86
+ }
87
+ return Object.inspect(obj);
88
+ }
89
+ BiwaScheme.to_display = function(obj){
90
+ if(typeof(obj.valueOf()) == "string")
91
+ return obj;
92
+ else if(obj instanceof Symbol)
93
+ return obj.name;
94
+ else if(obj instanceof Array)
95
+ return '#(' + obj.map(function(x){ return to_display(x) }).join(' ') + ')';
96
+ else if(obj instanceof Pair)
97
+ return obj.inspect(to_display);
98
+ else if(obj instanceof Char)
99
+ return obj.value;
100
+ else
101
+ return to_write(obj);
102
+ }
103
+
104
+ // write/ss (write with substructure)
105
+ // example: > (let ((x (list 'a))) (list x x)) // (#0=(a) #0#)
106
+ // 2-pass algorithm.
107
+ // (1) detect all the objects which appears more than once
108
+ // (find_cyclic, reduce_cyclic_info)
109
+ // (2) write object using this information
110
+ // * add prefix '#n=' for first appearance
111
+ // * just write '#n#' for other appearance
112
+
113
+ //TODO: support Values
114
+ BiwaScheme.write_ss = function(obj, array_mode){
115
+ var known = [obj], used = [false];
116
+ find_cyclic(obj, known, used);
117
+ var cyclic = reduce_cyclic_info(known, used);
118
+ var appeared = new Array(cyclic.length);
119
+ for(var i=cyclic.length-1; i>=0; i--) appeared[i] = false;
120
+
121
+ return to_write_ss(obj, cyclic, appeared, array_mode);
122
+ }
123
+ BiwaScheme.to_write_ss = function(obj, cyclic, appeared, array_mode){
124
+ var ret = "";
125
+ var i = cyclic.indexOf(obj);
126
+ if(i >= 0){
127
+ if(appeared[i]){
128
+ return "#"+i+"#";
129
+ }
130
+ else{
131
+ appeared[i] = true;
132
+ ret = "#"+i+"=";
133
+ }
134
+ }
135
+
136
+ if(obj instanceof Pair && obj != nil){
137
+ var a = [];
138
+ a.push(to_write_ss(obj.car, cyclic, appeared, array_mode));
139
+ for(var o=obj.cdr; o != nil; o=o.cdr){
140
+ if(!(o instanceof Pair) || cyclic.indexOf(o) >= 0){
141
+ a.push(".");
142
+ a.push(to_write_ss(o, cyclic, appeared, array_mode));
143
+ break;
144
+ }
145
+ a.push(to_write_ss(o.car, cyclic, appeared, array_mode));
146
+ }
147
+ ret += "(" + a.join(" ") + ")";
148
+ }
149
+ else if(obj instanceof Array){
150
+ var a = obj.map(function(item){
151
+ return to_write_ss(item, cyclic, appeared, array_mode);
152
+ })
153
+ if(array_mode)
154
+ ret += "[" + a.join(", ") + "]";
155
+ else
156
+ ret += "#(" + a.join(" ") + ")";
157
+ }
158
+ else{
159
+ ret += to_write(obj);
160
+ }
161
+ return ret;
162
+ }
163
+ BiwaScheme.reduce_cyclic_info = function(known, used){
164
+ var n_used = 0;
165
+ for(var i=0; i<used.length; i++){
166
+ if(used[i]){
167
+ known[n_used] = known[i];
168
+ n_used++;
169
+ }
170
+ }
171
+ return known.slice(0, n_used);
172
+ }
173
+ BiwaScheme.find_cyclic = function(obj, known, used){
174
+ var items = (obj instanceof Pair) ? [obj.car, obj.cdr] :
175
+ (obj instanceof Array) ? obj :
176
+ null;
177
+ if(!items) return;
178
+
179
+ items.each(function(item){
180
+ if(typeof(item)=='number' || typeof(item)=='string' ||
181
+ item === undef || item === true || item === false ||
182
+ item === nil || item instanceof Symbol) return;
183
+
184
+ var i = known.indexOf(item);
185
+ if(i >= 0)
186
+ used[i] = true;
187
+ else{
188
+ known.push(item);
189
+ used.push(false);
190
+ find_cyclic(item, known, used);
191
+ }
192
+ });
193
+ }
194
+
195
+ //
196
+ // variables
197
+ //
198
+ BiwaScheme.TopEnv = {};
199
+ BiwaScheme.CoreEnv = {};
200
+
201
+ // (eof-object)
202
+ BiwaScheme.eof = new Object;
203
+
204
+ //
205
+ // Set - set of string
206
+ // contents must be string (or at least sortable)
207
+ //
208
+ BiwaScheme.Set = Class.create({
209
+ initialize : function(/*args*/){
210
+ this.arr = [];
211
+ var i;
212
+ for(i=0; i<arguments.length; i++)
213
+ this.arr[i] = arguments[i];
214
+ },
215
+
216
+ equals : function(other){
217
+ if(this.arr.length != other.arr.length)
218
+ return false;
219
+
220
+ var a1 = this.arr.clone();
221
+ var a2 = other.arr.clone();
222
+ a1.sort();
223
+ a2.sort();
224
+ for(var i=0; i<this.arr.length; i++){
225
+ if(a1[i] != a2[i]) return false;
226
+ }
227
+ return true;
228
+ },
229
+ set_cons : function(item){
230
+ var o = new Set(item);
231
+ o.arr = this.arr.clone();
232
+ o.arr.push(item);
233
+ return o;
234
+ },
235
+ set_union : function(/*args*/){
236
+ var o = new Set();
237
+ o.arr = this.arr.clone();
238
+
239
+ for(var k=0; k<arguments.length; k++){
240
+ var s2 = arguments[k];
241
+ if(!s2 instanceof Set) throw new Error("set_union: arguments must be a set");
242
+ for(var i=0; i<s2.arr.length; i++)
243
+ o.add(s2.arr[i]);
244
+ }
245
+ return o;
246
+ },
247
+ set_intersect : function(s2){
248
+ if(!s2 instanceof Set) throw new Error("set_union: arguments must be a set");
249
+ var o = new Set();
250
+ for(var i=0; i<this.arr.length; i++)
251
+ if(s2.member(this.arr[i]))
252
+ o.add(this.arr[i]);
253
+ return o;
254
+ },
255
+ set_minus : function(s2){
256
+ if(!s2 instanceof Set) throw new Error("set_union: arguments must be a set");
257
+ var o = new Set();
258
+ for(var i=0; i<this.arr.length; i++)
259
+ if(!s2.member(this.arr[i]))
260
+ o.add(this.arr[i]);
261
+ return o;
262
+ },
263
+ add : function(item){
264
+ if(!this.member(item)){
265
+ this.arr.push(item);
266
+ }
267
+ },
268
+ member : function(item){
269
+ for(var i=0; i<this.arr.length; i++)
270
+ if(this.arr[i] == item) return true;
271
+
272
+ return false;
273
+ },
274
+ rindex : function(item){
275
+ for(var i=this.arr.length-1; i>=0 ; i--)
276
+ if(this.arr[i] == item) return (this.arr.length-1-i);
277
+
278
+ return null;
279
+ },
280
+ index : function(item){
281
+ for(var i=0; i<this.arr.length; i++)
282
+ if(this.arr[i] == item) return i;
283
+
284
+ return null;
285
+ },
286
+ inspect : function(){
287
+ return "Set(" + this.arr.join(", ") + ")";
288
+ },
289
+ toString : function(){
290
+ return this.inspect();
291
+ },
292
+ size : function(){
293
+ return this.arr.length;
294
+ }
295
+ });
296
+
297
+ //
298
+ // Classes
299
+ //
300
+
301
+ BiwaScheme.Error = Class.create({
302
+ initialize: function(msg){
303
+ this.message = "Error: "+msg;
304
+ },
305
+ toString: function(){
306
+ return this.message;
307
+ }
308
+ });
309
+
310
+ BiwaScheme.Bug = Class.create(Object.extend(new Error(), {
311
+ initialize: function(msg){
312
+ this.message = "[BUG] "+msg;
313
+ }
314
+ }));
315
+
316
+ //
317
+ // Pair
318
+ // cons cell
319
+ //
320
+
321
+ BiwaScheme.Pair = Class.create({
322
+ initialize: function(car, cdr){
323
+ this.car = car;
324
+ this.cdr = cdr;
325
+ },
326
+
327
+ caar: function(){ return this.car.car; },
328
+ cadr: function(){ return this.cdr.car; },
329
+ cdar: function(){ return this.cdr.car; },
330
+ cddr: function(){ return this.cdr.cdr; },
331
+
332
+ first: function(){ return this.car; },
333
+ second: function(){ return this.cdr.car; },
334
+ third: function(){ return this.cdr.cdr.car; },
335
+ fourth: function(){ return this.cdr.cdr.cdr.car; },
336
+ fifth: function(){ return this.cdr.cdr.cdr.cdr.car; },
337
+
338
+ // returns array containing all the car's of list
339
+ // '(1 2 3) => [1,2,3]
340
+ // '(1 2 . 3) => [1,2]
341
+ to_array: function(){
342
+ var ary = [];
343
+ for(var o = this; o instanceof Pair && o != nil; o=o.cdr){
344
+ ary.push(o.car);
345
+ }
346
+ return ary;
347
+ },
348
+
349
+ to_set: function(){
350
+ var set = new Set();
351
+ for(var o = this; o instanceof Pair && o != nil; o=o.cdr){
352
+ set.add(o.car);
353
+ }
354
+ return set;
355
+ },
356
+
357
+ length: function(){
358
+ var n = 0;
359
+ for(var o = this; o instanceof Pair && o != nil; o=o.cdr){
360
+ n++;
361
+ }
362
+ return n;
363
+ },
364
+
365
+ // calls the given func passing each car of list
366
+ // returns cdr of last Pair
367
+ foreach: function(func){
368
+ for(var o = this; o instanceof Pair && o != nil; o=o.cdr){
369
+ func(o.car);
370
+ }
371
+ return o;
372
+ },
373
+
374
+ // returns human-redable string of pair
375
+ inspect: function(conv){
376
+ conv || (conv = Object.inspect);
377
+ var a = [];
378
+ var last = this.foreach(function(o){
379
+ a.push(conv(o));
380
+ });
381
+ if(last != nil){
382
+ a.push(".");
383
+ a.push(conv(last));
384
+ }
385
+ return "(" + a.join(" ") + ")";
386
+ },
387
+ toString : function(){
388
+ return this.inspect();
389
+ },
390
+
391
+ to_write: function(){
392
+ return this.inspect(BiwaScheme.to_write);
393
+ }
394
+ });
395
+ BiwaScheme.List = function(){
396
+ return $A(arguments).to_list();
397
+ }
398
+
399
+ //
400
+ // Values
401
+ //
402
+ BiwaScheme.Values = Class.create({
403
+ initialize: function(values){
404
+ this.content = values;
405
+ },
406
+ to_write: function(){
407
+ return "#<Values " + this.content.map(function(x){ return to_write(x) }).join(" ") + ">";
408
+ }
409
+ });
410
+
411
+ //
412
+ // Dumper - graphical state dumper
413
+ //
414
+
415
+ BiwaScheme.Dumper = Class.create({
416
+ initialize: function(){
417
+ },
418
+
419
+ is_opc: function(obj){
420
+ return (obj instanceof Array && typeof(obj[0]) == 'string');
421
+ },
422
+
423
+ dump_pad: "&nbsp;&nbsp;&nbsp;",
424
+ dump_opc: function(obj, level){
425
+ var s="";
426
+ var pad1="", pad2="";
427
+ var level = level || 0;
428
+ level.times(function(){ pad1 += this.dump_pad; }.bind(this));
429
+ (level+1).times(function(){ pad2 += this.dump_pad; }.bind(this));
430
+
431
+ s += pad1 + '[<span class="dump_opecode">' + obj[0] + '</span>';
432
+ var i = 1;
433
+ while(!(obj[i] instanceof Array) && i<obj.length){
434
+ if(obj[0] == "constant")
435
+ s += "&nbsp;<span class='dump_constant'>" + this.dump_obj(obj[i]) + "</span>";
436
+ else
437
+ s += "&nbsp;" + this.dump_obj(obj[i]);
438
+ i++;
439
+ }
440
+ if(i < obj.length) s += '<br>\n';
441
+ for(; i<obj.length; i++){
442
+ if(this.is_opc(obj[i])){
443
+ s += this.dump_opc(obj[i], (i == obj.length-1 ? level : level+1));
444
+ }
445
+ else{
446
+ s += (i == obj.length-1) ? pad1 : pad2;
447
+ s += this.dump_obj(obj[i]); //String(obj[i]).escapeHTML();
448
+ }
449
+ if(i != obj.length-1) s += "<br>\n";
450
+ }
451
+ s += "]";
452
+ return (level==0 ? this.add_fold(s) : s);
453
+ },
454
+
455
+ fold_limit: 20,
456
+ n_folds: 0,
457
+ add_fold: function(s){
458
+ var lines = s.split(/<br>/gmi);
459
+
460
+ if(lines.length > this.fold_limit){
461
+ var fold_btn = " <span style='text-decoration:underline; color:blue; cursor:pointer;'" +
462
+ "onclick='BiwaScheme.Dumper.toggle_fold("+this.n_folds+")'>more</span>"
463
+ var fold_start = "<div style='display:none' id='fold"+this.n_folds+"'>";
464
+ var fold_end = "</div>"
465
+ this.n_folds++;
466
+ return lines.slice(0, this.fold_limit).join("<br>") + fold_btn +
467
+ fold_start + lines.slice(this.fold_limit+1).join("<br>") + fold_end;
468
+ }
469
+ else{
470
+ return s;
471
+ }
472
+ },
473
+
474
+ stack_max_len: 80,
475
+ dump_stack: function(stk, size){
476
+ if(stk === null || stk === undefined) return Object.inspect(stk)
477
+ var s = "<table>";
478
+ for(var i=stk.length-1; i >= 0; i--){
479
+ if(i < size){
480
+ s += "<tr><td class='dump_stknum'>[" + i + "]</td>" +
481
+ "<td>" + this.dump_obj(stk[i]).truncate(this.stack_max_len) + "</td></tr>";
482
+ }
483
+ else{
484
+ s += "<tr><td class='dump_dead'>[" + i + "]</td>" +
485
+ "<td class='dump_dead'>" + this.dump_obj(stk[i]).truncate(this.stack_max_len) + "</td></tr>";
486
+ }
487
+ }
488
+ return s + "</table>";
489
+ },
490
+
491
+ dump_object: function(obj){
492
+ var a = [];
493
+ for(var k in obj){
494
+ //if(this.prototype[k]) continue;
495
+ a.push( k.toString() );//+" => "+this[k].toString() );
496
+ }
497
+ return "#<Object{"+a.join(",")+"}>";
498
+ },
499
+
500
+ closures: [],
501
+ dump_closure: function(cls){
502
+ if(cls.length == 0) return "[]";
503
+
504
+ var cls_num = null;
505
+ for(var i=0; i<this.closures.length; i++){
506
+ if(this.closures[i] == cls) cls_num = i;
507
+ }
508
+ if(cls_num == null){
509
+ cls_num = this.closures.length;
510
+ this.closures.push(cls);
511
+ }
512
+
513
+ var c = cls.clone ? cls.clone() : [c];
514
+ var body = c.shift();
515
+ return "c"+cls_num+" <span class='dump_closure'>free vars :</span> " + this.dump_obj(c) + " <span class='dump_closure'>body :</span> " + this.dump_obj(body).truncate(100);
516
+ },
517
+
518
+ dump_obj: function(obj){
519
+ if(obj && typeof(obj.to_html) == 'function')
520
+ return obj.to_html();
521
+ else{
522
+ var s = write_ss(obj, true); //true=Array mode
523
+ if(s == "[object Object]") s = this.dump_object(obj);
524
+ return s.escapeHTML();
525
+ }
526
+ },
527
+
528
+ n_dumps: 0,
529
+ dump: function(obj){
530
+ var dumpitem = document.createElement("div");
531
+ var s = "";
532
+ if(obj instanceof Hash){
533
+ s += "<table>"
534
+ s += "<tr><td colspan='4'>#"+this.n_dumps+"</td></tr>"
535
+ obj.each(function(pair){
536
+ if(pair.key!="x" && pair.key != "stack"){
537
+ var value = (pair.key=="c" ? this.dump_closure(pair.value) : this.dump_obj(pair.value))
538
+ s += "<tr><td>" + pair.key + ": </td><td colspan='3'>" + value + "</td></tr>";
539
+ }
540
+ }.bind(this));
541
+ s += "<tr><td>x:</td><td>" + (this.is_opc(obj.get("x")) ? this.dump_opc(obj.get("x")) : this.dump_obj(obj.get("x"))) + "</td>";
542
+ s += "<td style='border-left: 1px solid black'>stack:</td><td>" + this.dump_stack(obj.get("stack"), obj.get("s")) + "</td></tr>";
543
+ s += "</table>";
544
+ }
545
+ else{
546
+ s = Object.inspect(obj).escapeHTML() + "<br>\n";
547
+ }
548
+ dumpitem.id = "dump" + this.n_dumps;
549
+ dumpitem.innerHTML = s;
550
+ $('dumparea').appendChild(dumpitem);
551
+ Element.hide(dumpitem);
552
+ this.n_dumps++;
553
+ },
554
+
555
+ cur: -1,
556
+ dump_move: function(dir){
557
+ if(0 <= this.cur && this.cur < this.n_dumps)
558
+ Element.hide($("dump"+this.cur));
559
+
560
+ if(0 <= this.cur+dir && this.cur+dir < this.n_dumps)
561
+ this.cur += dir;
562
+
563
+ Element.show($("dump"+this.cur));
564
+ },
565
+
566
+ is_folded: true,
567
+ dump_toggle_fold: function(){
568
+ if(this.is_folded){ //open all
569
+ for(var i=0; i<this.n_dumps; i++)
570
+ Element.show($("dump"+i));
571
+ }
572
+ else{ //close all
573
+ for(var i=0; i<this.n_dumps; i++)
574
+ if(i!=this.cur) Element.hide($("dump"+i));
575
+ }
576
+ this.is_folded = (!this.is_folded);
577
+ }
578
+ })
579
+ BiwaScheme.Dumper.toggle_fold = function(n){
580
+ Element.toggle("fold"+n);
581
+ }
582
+
583
+
584
+ //
585
+ // Nil
586
+ // javascript representation of empty list( '() )
587
+ //
588
+ BiwaScheme.nil = new Pair(null, null);
589
+ BiwaScheme.nil.toString = function(){ return "nil"; }
590
+
591
+ //
592
+ // #<undef> (The undefined value)
593
+ //
594
+ BiwaScheme.undef = new Object();
595
+ BiwaScheme.undef.toString = function(){ return "#<undef>"; }
596
+
597
+ //
598
+ // Symbol
599
+ //
600
+
601
+ BiwaScheme.Symbol = Class.create({
602
+ initialize: function(str){
603
+ this.name = str;
604
+ Symbols[ str ] = this;
605
+ },
606
+
607
+ inspect: function(){
608
+ return "'"+this.name;
609
+ //return "#<Symbol '"+this.name+"'>";
610
+ },
611
+
612
+ toString: function(){
613
+ return "'"+this.name;
614
+ },
615
+
616
+ to_write: function(){
617
+ return this.name;
618
+ }
619
+ });
620
+ BiwaScheme.Symbols = {};
621
+ BiwaScheme.Sym = function(name,leaveCase){
622
+ if( Symbols[name] === undefined ){
623
+ return new Symbol(name);
624
+ }
625
+ else if( ! (Symbols[name] instanceof Symbol) ){ //pre-defined member (like 'eval' in Firefox)
626
+ return new Symbol(name);
627
+ }
628
+ else{
629
+ return Symbols[name];
630
+ }
631
+ }
632
+
633
+ BiwaScheme.gensyms = 0;
634
+ BiwaScheme.gensym = function(){
635
+ BiwaScheme.gensyms++;
636
+ return Sym("__gensym_" + BiwaScheme.gensyms);
637
+ }
638
+
639
+ //
640
+ // Char
641
+ //
642
+
643
+ BiwaScheme.Char = Class.create({
644
+ initialize: function(c){
645
+ Chars[ this.value = c ] = this;
646
+ },
647
+ to_write: function(){
648
+ switch(this.value){
649
+ case '\n': return "#\\newline";
650
+ case ' ': return "#\\space";
651
+ case '\t': return "#\\tab";
652
+ default: return "#\\"+this.value;
653
+ }
654
+ },
655
+ inspect: function(){
656
+ return this.to_write();
657
+ }
658
+ });
659
+ BiwaScheme.Chars = {};
660
+ BiwaScheme.Char.get = function(c) {
661
+ if(typeof(c) != "string") {
662
+ throw new Bug("Char.get: " + Object.inspect(c) + " is not a string");
663
+ }
664
+ if( Chars[c] === undefined )
665
+ return new Char(c);
666
+ else
667
+ return Chars[c];
668
+ }
669
+
670
+ //
671
+ // Port
672
+ //
673
+ BiwaScheme.Port = Class.create({
674
+ initialize: function(is_in, is_out){
675
+ this.is_binary = false; //??
676
+ this.is_input = is_in;
677
+ this.is_output = is_out;
678
+ },
679
+ close: function(){
680
+ // close port
681
+ },
682
+ inspect: function(){
683
+ return "#<Port>";
684
+ },
685
+ to_write: function(){
686
+ return "#<Port>";
687
+ }
688
+ });
689
+ BiwaScheme.Port.BrowserInput = Class.create(Port, {
690
+ initialize: function($super){
691
+ $super(true, false);
692
+ },
693
+ get_string: function(after){
694
+ var form = document.createElement("div")
695
+ form.innerHTML = "<input id='webscheme-read-line' type='text'><input id='webscheme-read-line-submit' type='button' value='ok'>";
696
+ $('bs-console').appendChild(form)
697
+
698
+ return new BiwaScheme.Pause(function(pause){
699
+ Event.observe($('webscheme-read-line-submit'), 'click', function(){
700
+ var input = $('webscheme-read-line').value;
701
+ form.parentNode.removeChild(form);
702
+ puts(input);
703
+ pause.resume(after(input));
704
+ });
705
+ });
706
+ }
707
+ })
708
+ BiwaScheme.Port.DefaultOutput = Class.create(Port, {
709
+ initialize: function($super){
710
+ $super(false, true);
711
+ },
712
+ put_string: function(str){
713
+ puts(str, true);
714
+ }
715
+ })
716
+ BiwaScheme.Port.current_input = new Port.BrowserInput();
717
+ BiwaScheme.Port.current_output = new Port.DefaultOutput();
718
+ BiwaScheme.Port.current_error = new Port.DefaultOutput();
719
+
720
+ //
721
+ // Syntax
722
+ //
723
+ BiwaScheme.Syntax = Class.create({
724
+ initialize: function(func){
725
+ this.func = func;
726
+ },
727
+ transform: function(x){
728
+ return this.func(x);
729
+ }
730
+ })
731
+
732
+ //
733
+ // Parser
734
+ // copied from jsScheme - should be rewrriten (support #0=, etc)
735
+ //
736
+ BiwaScheme.Parser = Class.create({
737
+ initialize: function(txt){
738
+ this.tokens = this.tokenize(txt);
739
+ this.i = 0;
740
+ },
741
+
742
+ inspect: function(){
743
+ return [
744
+ "#<Parser:",
745
+ this.i, "/", this.tokens.length, " ",
746
+ Object.inspect(this.tokens),
747
+ ">"
748
+ ].join("");
749
+ },
750
+
751
+ tokenize: function(txt) {
752
+ var tokens = new Array(), oldTxt=null;
753
+ var in_srfi_30_comment = 0;
754
+
755
+ while( txt != "" && oldTxt != txt ) {
756
+ oldTxt = txt;
757
+ txt = txt.replace( /^\s*(;[^\r\n]*(\r|\n|$)|#;|#\||#\\[^\w]|#?(\(|\[|{)|\)|\]|}|\'|`|,@|,|\+inf\.0|-inf\.0|\+nan\.0|\"(\\(.|$)|[^\"\\])*(\"|$)|[^\s()\[\]{}]+)/,
758
+ function($0,$1) {
759
+ var t = $1;
760
+
761
+ if (t == "#|") {
762
+ in_srfi_30_comment++;
763
+ return "";
764
+ }
765
+ else if (in_srfi_30_comment > 0) {
766
+ if ( /(.*\|#)/.test(t) ) {
767
+ in_srfi_30_comment--;
768
+ if (in_srfi_30_comment < 0) {
769
+ throw new Error("Found an extra comment terminator: `|#'")
770
+ }
771
+ // Push back the rest substring to input stream.
772
+ return t.substring(RegExp.$1.length, t.length);
773
+ }
774
+ else {
775
+ return "";
776
+ }
777
+ }
778
+ else {
779
+ if( t.charAt(0) != ';' ) tokens[tokens.length]=t;
780
+ return "";
781
+ }
782
+ } );
783
+ }
784
+ return tokens;
785
+ },
786
+
787
+ sexpCommentMarker: new Object,
788
+ getObject: function() {
789
+ var r = this.getObject0();
790
+
791
+ if (r != this.sexpCommentMarker)
792
+ return r;
793
+
794
+ r = this.getObject();
795
+ if (r == null)
796
+ throw new Error("Readable object not found after S exression comment");
797
+
798
+ r = this.getObject();
799
+ return r;
800
+ },
801
+
802
+ getList: function( close ) {
803
+ var list = nil, prev = list;
804
+ while( this.i < this.tokens.length ) {
805
+
806
+ this.eatObjectsInSexpComment("Input stream terminated unexpectedly(in list)");
807
+
808
+ if( this.tokens[ this.i ] == ')' || this.tokens[ this.i ] == ']' || this.tokens[ this.i ] == '}' ) {
809
+ this.i++; break;
810
+ }
811
+
812
+ if( this.tokens[ this.i ] == '.' ) {
813
+ this.i++;
814
+ var o = this.getObject();
815
+ if( o != null && list != nil ) {
816
+ prev.cdr = o;
817
+ }
818
+ } else {
819
+ var cur = new Pair( this.getObject(), nil);
820
+ if( list == nil ) list = cur;
821
+ else prev.cdr = cur;
822
+ prev = cur;
823
+ }
824
+ }
825
+ return list;
826
+ },
827
+
828
+ getVector: function( close ) {
829
+ var arr = new Array();
830
+ while( this.i < this.tokens.length ) {
831
+
832
+ this.eatObjectsInSexpComment("Input stream terminated unexpectedly(in vector)");
833
+
834
+ if( this.tokens[ this.i ] == ')' ||
835
+ this.tokens[ this.i ] == ']' ||
836
+ this.tokens[ this.i ] == '}' ) { this.i++; break; }
837
+ arr[ arr.length ] = this.getObject();
838
+ }
839
+ return arr;
840
+ },
841
+
842
+ eatObjectsInSexpComment: function(err_msg) {
843
+ while( this.tokens[ this.i ] == '#;' ) {
844
+ this.i++;
845
+ if ((this.getObject() == null) || (this.i >= this.tokens.length))
846
+ throw new Error(err_msg);
847
+ }
848
+ },
849
+
850
+ getObject0: function() {
851
+ if( this.i >= this.tokens.length )
852
+ return Parser.EOS;
853
+
854
+ var t = this.tokens[ this.i++ ];
855
+ // if( t == ')' ) return null;
856
+
857
+ if (t == '#;')
858
+ return this.sexpCommentMarker;
859
+
860
+ var s = t == "'" ? 'quote' :
861
+ t == "`" ? 'quasiquote' :
862
+ t == "," ? 'unquote' :
863
+ t == ",@" ? 'unquote-splicing' : false;
864
+
865
+ if( s || t == '(' || t == '#(' || t == '[' || t == '#[' || t == '{' || t == '#{' ) {
866
+ return s ? new Pair( Sym(s), new Pair( this.getObject(), nil ))
867
+ : (t=='(' || t=='[' || t=='{') ? this.getList(t) : this.getVector(t);
868
+ }
869
+ else {
870
+ switch(t){
871
+ case "+inf.0" : return Infinity;
872
+ case "-inf.0" : return -Infinity;
873
+ case "+nan.0" : return NaN;
874
+ }
875
+
876
+ var n;
877
+ if( /^#x[0-9a-z]+$/i.test(t) ) { // #x... Hex
878
+ n = new Number('0x'+t.substring(2,t.length) );
879
+ }
880
+ else if( /^#d[0-9\.]+$/i.test(t) ) { // #d... Decimal
881
+ n = new Number( t.substring(2,t.length) );
882
+ }
883
+ else{
884
+ n = new Number(t); // use constrictor as parser
885
+ }
886
+
887
+ if( ! isNaN(n) ) {
888
+ return n.valueOf();
889
+ } else if( t == '#f' || t == '#F' ) {
890
+ return false;
891
+ } else if( t == '#t' || t == '#T' ) {
892
+ return true;
893
+ } else if( t.toLowerCase() == '#\\newline' ) {
894
+ return Char.get('\n');
895
+ } else if( t.toLowerCase() == '#\\space' ) {
896
+ return Char.get(' ');
897
+ } else if( t.toLowerCase() == '#\\tab' ) {
898
+ return Char.get('\t');
899
+ } else if( /^#\\.$/.test(t) ) {
900
+ return Char.get( t.charAt(2) );
901
+ } else if( /^\"(\\(.|$)|[^\"\\])*\"?$/.test(t) ) {
902
+ return t.replace(/(\r?\n|\\n)/g, "\n").replace( /^\"|\\(.|$)|\"$/g, function($0,$1) {
903
+ return $1 ? $1 : '';
904
+ } );
905
+ } else return Sym(t); // 2Do: validate !!
906
+ }
907
+ }
908
+ });
909
+ // indicates end of source file
910
+ BiwaScheme.Parser.EOS = new Object();
911
+
912
+ ///
913
+ /// Compiler
914
+ ///
915
+
916
+ BiwaScheme.Compiler = Class.create({
917
+ initialize: function(){
918
+ },
919
+
920
+ is_tail: function(x){
921
+ return (x[0] == "return");
922
+ },
923
+
924
+ //free: set
925
+ //e: env(= [locals, frees])
926
+ //next: opc
927
+ //ret: opc["refer_*", n, ["argument",
928
+ // ["refer_*", n, ... ["argument", next]
929
+ collect_free: function(free, e, next){
930
+ var vars = free;
931
+ var opc = next;
932
+ var arr = vars.arr;
933
+ for(var i=0; i<arr.length; i++){
934
+ opc = this.compile_refer(arr[i], e, ["argument", opc]);
935
+ }
936
+ //puts("collect_free "+free.inspect()+" / "+e.inspect()+" => "+opc.inspect());
937
+ return opc;
938
+ },
939
+
940
+ //x: Symbol
941
+ //e: env [set of locals, set of frees]
942
+ //ret: opc
943
+ compile_refer: function(x, e, next){
944
+ return this.compile_lookup(x, e,
945
+ function(n){ return ["refer-local", n, next] },
946
+ function(n){ return ["refer-free", n, next] },
947
+ function(sym){ return ["refer-global", sym, next] });
948
+ },
949
+
950
+ compile_lookup: function(x, e, return_local, return_free, return_global){
951
+ var locals = e[0], free = e[1];
952
+ if((n = locals.index(x)) != null){
953
+ //puts("compile_refer:"+x.inspect()+" in "+e.inspect()+" results refer-local "+n);
954
+ return return_local(n);
955
+ }
956
+ else if((n = free.index(x)) != null){
957
+ //puts("compile_refer:"+x.inspect()+" in "+e.inspect()+" results refer-free "+n);
958
+ return return_free(n);
959
+ }
960
+ else{
961
+ var sym = x.name;
962
+ return return_global(sym);
963
+ }
964
+ //throw new Error("undefined symbol `" + sym + "'");
965
+ },
966
+
967
+ //generate boxing code (intersection of sets & vars)
968
+ //if no need of boxing, just returns next
969
+ // sets(Set): assigned variables
970
+ // vars(List): used variables
971
+ // next(opc):
972
+ // ret(opc):
973
+ make_boxes: function(sets, vars, next){
974
+ var vars = vars;
975
+ var n = 0;
976
+ var a = [];
977
+ while(vars instanceof Pair && vars != nil){
978
+ if(sets.member(vars.car))
979
+ a.push(n);
980
+ n++;
981
+ vars = vars.cdr;
982
+ }
983
+ var opc = next;
984
+ for(var i=a.length-1; i>=0; i--)
985
+ opc = ["box", a[i], opc];
986
+ return opc;
987
+ },
988
+
989
+ // Enumerate variables which (could be assigned && included in v)
990
+ // x: exp
991
+ // v: set(vars)
992
+ // ret: set
993
+ find_sets: function(x, v){
994
+ //puts("find_sets: " + to_write(x) + " " + to_write(v))
995
+ var ret=null;
996
+ if(x instanceof Symbol){
997
+ ret = new Set();
998
+ }
999
+ else if(x instanceof Pair){
1000
+ switch(x.first()){
1001
+ case Sym("define"):
1002
+ var exp=x.third();
1003
+ ret = this.find_sets(exp, v);
1004
+ case Sym("begin"):
1005
+ ret = this.find_sets(x.cdr, v); //(ignores improper list)
1006
+ break;
1007
+ case Sym("quote"):
1008
+ ret = new Set();
1009
+ break;
1010
+ case Sym("lambda"):
1011
+ var vars=x.second(), body=x.cdr.cdr;
1012
+ ret = this.find_sets(body, v.set_minus(vars.to_set()));
1013
+ break;
1014
+ case Sym("if"):
1015
+ var testc=x.second(), thenc=x.third(), elsec=x.fourth();
1016
+ ret = this.find_sets(testc, v).set_union(
1017
+ this.find_sets(thenc, v),
1018
+ this.find_sets(elsec, v));
1019
+ break;
1020
+ case Sym("set!"):
1021
+ var vari=x.second(), xx=x.third();
1022
+ if(v.member(vari))
1023
+ ret = this.find_sets(xx, v).set_cons(vari);
1024
+ else
1025
+ ret = this.find_sets(xx, v);
1026
+ break;
1027
+ case Sym("call/cc"):
1028
+ var exp=x.second();
1029
+ ret = this.find_sets(exp, v);
1030
+ break;
1031
+ default:
1032
+ var set = new Set();
1033
+ for(var p=x; p instanceof Pair && p != nil; p=p.cdr){
1034
+ set = set.set_union(this.find_sets(p.car, v));
1035
+ }
1036
+ ret = set;
1037
+ break;
1038
+ }
1039
+ }
1040
+ else{
1041
+ ret = new Set();
1042
+ }
1043
+
1044
+ if(ret == null)
1045
+ throw new Bug("find_sets() exited in unusual way");
1046
+ else
1047
+ return ret;
1048
+ },
1049
+
1050
+ // find_free(): find free variables in x
1051
+ // these variables are collected by collect_free().
1052
+ // x: expression
1053
+ // b: set of local vars (= variables which are not free)
1054
+ // f: set of free var candidates
1055
+ // (local vars of outer lambdas)
1056
+ // ret: set of free vars
1057
+ find_free: function(x, b, f){
1058
+ var ret=null;
1059
+ if(x instanceof Symbol){
1060
+ if(f.member(x))
1061
+ ret = new Set(x);
1062
+ else
1063
+ ret = new Set();
1064
+ }
1065
+ else if(x instanceof Pair){
1066
+ switch(x.first()){
1067
+ case Sym("define"):
1068
+ var exp=x.third();
1069
+ ret = this.find_free(exp, b, f);
1070
+ break;
1071
+ case Sym("begin"):
1072
+ ret = this.find_free(x.cdr, b, f); //(ignores improper list)
1073
+ break;
1074
+ case Sym("quote"):
1075
+ ret = new Set();
1076
+ break;
1077
+ case Sym("lambda"):
1078
+ var vars=x.second(), body=x.cdr.cdr;
1079
+ ret = this.find_free(body, b.set_union(vars.to_set()), f);
1080
+ break;
1081
+ case Sym("if"):
1082
+ var testc=x.second(), thenc=x.third(), elsec=x.fourth();
1083
+ ret = this.find_free(testc, b, f).set_union(
1084
+ this.find_free(thenc, b, f),
1085
+ this.find_free(elsec, b, f));
1086
+ break;
1087
+ case Sym("set!"):
1088
+ var vari=x.second(), exp=x.third();
1089
+ if(f.member(vari))
1090
+ ret = this.find_free(exp, b, f).set_cons(vari);
1091
+ else
1092
+ ret = this.find_free(exp, b, f)
1093
+ break;
1094
+ case Sym("call/cc"):
1095
+ var exp=x.second();
1096
+ ret = this.find_free(exp, b, f);
1097
+ break;
1098
+ default:
1099
+ var set = new Set();
1100
+ for(var p=x; p instanceof Pair && p != nil; p=p.cdr){
1101
+ set = set.set_union(this.find_free(p.car, b, f));
1102
+ }
1103
+ ret = set;
1104
+ break;
1105
+ }
1106
+ }
1107
+ else{
1108
+ ret = new Set();
1109
+ }
1110
+ //p("find_free "+x.inspect()+" / "+b.inspect()+" => "+ret.inspect());
1111
+
1112
+ if(ret == null)
1113
+ throw new Bug("find_free() exited in unusual way");
1114
+ else
1115
+ return ret;
1116
+ },
1117
+
1118
+ find_dot_pos: function(x){
1119
+ var idx = 0;
1120
+ for (; x instanceof Pair && x != nil; x = x.cdr, ++idx)
1121
+ ;
1122
+ if (x != nil) {
1123
+ return idx;
1124
+ } else {
1125
+ return -1;
1126
+ }
1127
+ },
1128
+
1129
+ last_pair: function(x){
1130
+ if (x instanceof Pair && x != nil){
1131
+ for (; x.cdr instanceof Pair && x.cdr != nil; x = x.cdr)
1132
+ ;
1133
+ }
1134
+ return x;
1135
+ },
1136
+
1137
+ // dotted list -> proper list
1138
+ dotted2proper: function(ls){
1139
+ var nreverse = function(ls){
1140
+ var res = nil;
1141
+ for (; ls instanceof Pair && ls != nil; ){
1142
+ var d = ls.cdr;
1143
+ ls.cdr = res;
1144
+ res = ls;
1145
+ ls = d;
1146
+ }
1147
+ return res;
1148
+ }
1149
+ var copy_list = function(ls){
1150
+ var res = nil;
1151
+ for (; ls instanceof Pair && ls != nil; ls = ls.cdr){
1152
+ res = new Pair(ls.car, res);
1153
+ }
1154
+ return nreverse(res);
1155
+ }
1156
+
1157
+ if (ls instanceof Pair) {
1158
+ var last = this.last_pair(ls);
1159
+ if (last instanceof Pair && last.cdr == nil){
1160
+ return ls;
1161
+ } else {
1162
+ var copied = copy_list(ls);
1163
+ this.last_pair(copied).cdr = new Pair(last.cdr, nil);
1164
+ return copied;
1165
+ }
1166
+ } else {
1167
+ return new Pair(ls, nil);
1168
+ }
1169
+ },
1170
+
1171
+ // x: exp(list of symbol or integer or..)
1172
+ // e: env (= [locals, frees])
1173
+ // s: vars might be set!
1174
+ // next: opc
1175
+ // ret: opc
1176
+ compile: function(x, e, s, f, next){
1177
+ //p(x);
1178
+ var ret = null;
1179
+
1180
+ while(1){
1181
+ if(x instanceof Symbol){
1182
+ return this.compile_refer(x, e, (s.member(x) ? ["indirect", next] : next));
1183
+ }
1184
+ else if(x instanceof Pair){
1185
+ switch(x.first()){
1186
+ case Sym("define"):
1187
+ var left = x.cdr.car;
1188
+ var exp = x.cdr.cdr;
1189
+
1190
+ //define variable
1191
+ if(left instanceof Symbol){
1192
+ x = exp.car;
1193
+ TopEnv[left.name] = BiwaScheme.undef;
1194
+ next = ["assign-global", left.name, next]; //should raise for improper list?
1195
+ }
1196
+ //define function
1197
+ else if(left instanceof Pair){
1198
+ var fname=left.car, args=left.cdr;
1199
+ var lambda = new Pair(Sym("lambda"), new Pair(args, exp));
1200
+ x = lambda;
1201
+ TopEnv[fname.name] = BiwaScheme.undef;
1202
+ next = ["assign-global", fname.name, next];
1203
+ }
1204
+ //error
1205
+ else{
1206
+ throw new Error("compile: define needs a leftbol or pair: got "+left);
1207
+ }
1208
+ break;
1209
+ case Sym("begin"):
1210
+ var a = [];
1211
+ for(var p=x.cdr; p instanceof Pair && p!=nil; p=p.cdr)
1212
+ a.push(p.car);
1213
+
1214
+ //compile each expression (in reverse order)
1215
+ var c = next;
1216
+ for(var i=a.length-1; i>=0; i--){
1217
+ c = this.compile(a[i], e, s, f, c);
1218
+ }
1219
+ return c;
1220
+ case Sym("quote"):
1221
+ var obj=x.second();
1222
+ return ["constant", obj, next];
1223
+ case Sym("lambda"):
1224
+ // x = '(lambda (x y) x y)
1225
+ // x = '(lambda vars x y)
1226
+ var vars = x.cdr.car;
1227
+ var body = new Pair(Sym("begin"), x.cdr.cdr); //tenuki
1228
+
1229
+ var dotpos = this.find_dot_pos(vars);
1230
+ var proper = this.dotted2proper(vars);
1231
+ var free = this.find_free(body, proper.to_set(), f); //free variables
1232
+ var sets = this.find_sets(body, proper.to_set()); //local variables
1233
+
1234
+ var do_body = this.compile(body,
1235
+ [proper.to_set(), free],
1236
+ sets.set_union(s.set_intersect(free)),
1237
+ f.set_union(proper.to_set()),
1238
+ ["return"]);
1239
+ var do_close = ["close",
1240
+ free.size(),
1241
+ this.make_boxes(sets, proper, do_body),
1242
+ next,
1243
+ dotpos];
1244
+ return this.collect_free(free, e, do_close);
1245
+ case Sym("if"):
1246
+ var testc=x.second(), thenc=x.third(), elsec=x.fourth();
1247
+ var thenc = this.compile(thenc, e, s, f, next);
1248
+ var elsec = this.compile(elsec, e, s, f, next);
1249
+ x = testc;
1250
+ next = ["test", thenc, elsec];
1251
+ break;
1252
+ case Sym("set!"):
1253
+ var v=x.second(), x=x.third();
1254
+ var do_assign = this.compile_lookup(v, e,
1255
+ function(n){ return ["assign-local", n, next]; },
1256
+ function(n){ return ["assign-free", n, next]; },
1257
+ function(sym){ return ["assign-global",sym, next]; });
1258
+ next = do_assign;
1259
+ break;
1260
+ case Sym("call/cc"):
1261
+ var x=x.second();
1262
+ var c = ["conti",
1263
+ (this.is_tail(next) ? (e[0].size() + 1) : 0), //number of args for outer lambda
1264
+ ["argument",
1265
+ ["constant", 1,
1266
+ ["argument",
1267
+ this.compile(x, e, s,f,
1268
+ (this.is_tail(next) ? ["shift", 1, ["apply"]]
1269
+ : ["apply"]))]]]];
1270
+ //note: proc for call/cc takes 1 argument (= ["apply", 1])
1271
+ return this.is_tail(next) ? c : ["frame", c, next];
1272
+ default:
1273
+ //apply
1274
+ //x = (func 1 2)
1275
+ //x.car = func = '(lambda (x) ..) or Symbol
1276
+ //x.cdr = args = '(1 2)
1277
+ var func = x.car;
1278
+ var args = x.cdr;
1279
+ var c = this.compile(func, e, s,f,
1280
+ this.is_tail(next) ? ["shift", args.length(), ["apply"]]
1281
+ : ["apply"]);
1282
+
1283
+ // VM will push the number of arguments to the stack.
1284
+ c = this.compile(args.length(), e, s, f, ["argument", c]);
1285
+ for(var p=args; p instanceof Pair && p!=nil; p=p.cdr){
1286
+ c = this.compile(p.car, e, s, f, ["argument", c]);
1287
+ }
1288
+ return this.is_tail(next) ? c : ["frame", c, next];
1289
+ }
1290
+ }
1291
+ else{
1292
+ return ["constant", x, next];
1293
+ }
1294
+ }
1295
+ //p("result of " + x.inspect() + ":");
1296
+ //p(ret);
1297
+ //dump(new Hash({"ret":ret, "x":x, "e":e, "s":s, "next":next, "stack":[]}));
1298
+ // if(ret == null)
1299
+ // throw new Bug("compile() exited in unusual way");
1300
+ // else
1301
+ // return ret;
1302
+ },
1303
+ run: function(expr){
1304
+ return this.compile(expr, [new Set(), new Set()], new Set(), new Set(), ["halt"]);
1305
+ }
1306
+ });
1307
+ BiwaScheme.Compiler.compile = function(expr, next){
1308
+ expr = (new Interpreter).expand(expr);
1309
+ return (new Compiler).run(expr, next);
1310
+ }
1311
+
1312
+ ///
1313
+ /// Interpreter
1314
+ ///
1315
+
1316
+ //
1317
+ // pause object (facility to stop/resume interpreting)
1318
+ //
1319
+ BiwaScheme.Pause = Class.create({
1320
+ //new (on_pause: javascript function calling setTimeout, Ajax.Request, ..)
1321
+ initialize: function(on_pause){
1322
+ this.on_pause = on_pause;
1323
+ },
1324
+
1325
+ //save state of interpreter
1326
+ set_state: function(intp, x, f, c, s){
1327
+ this.interpreter = intp;
1328
+ this.x = x;
1329
+ this.f = f;
1330
+ this.c = c;
1331
+ this.s = s;
1332
+ },
1333
+
1334
+ //call this when ready (to fire setTimeout, Ajax.Request..)
1335
+ ready: function(){
1336
+ this.on_pause(this);
1337
+ },
1338
+
1339
+ //restart calculation
1340
+ resume: function(value){
1341
+ return this.interpreter.resume(true, value, this.x, this.f, this.c, this.s)
1342
+ }
1343
+ });
1344
+
1345
+ BiwaScheme.Call = Class.create({
1346
+ initialize: function(proc, args, after){
1347
+ this.proc = proc;
1348
+ this.args = args;
1349
+ this.after = after || function(ar){
1350
+ // just return result which closure returned
1351
+ return ar[0];
1352
+ };
1353
+ }
1354
+ })
1355
+
1356
+ BiwaScheme.Interpreter = Class.create({
1357
+ initialize: function(on_error){
1358
+ this.stack = [] //(make-vector 1000)
1359
+ this.on_error = on_error || function(e){};
1360
+ this.after_evaluate = Prototype.emptyFunction;
1361
+ },
1362
+
1363
+ inspect: function(){
1364
+ return [
1365
+ "#<Interpreter: stack size=>",
1366
+ this.stack.length, " ",
1367
+ "after_evaluate=",
1368
+ Object.inspect(this.after_evaluate),
1369
+ ">"
1370
+ ].join("");
1371
+ },
1372
+
1373
+ push: function(x, s){
1374
+ this.stack[s] = x;
1375
+ return s+1;
1376
+ },
1377
+
1378
+ //s: depth of stack to save
1379
+ //ret: saved(copied) stack
1380
+ save_stack: function(s){
1381
+ var v = [];
1382
+ for(var i=0; i<s; i++){
1383
+ v[i] = this.stack[i];
1384
+ }
1385
+ return v;
1386
+ },
1387
+
1388
+ //v: stack array to restore
1389
+ //ret: lenght of restored stack
1390
+ restore_stack: function(v){
1391
+ var s = v.length;
1392
+ for(var i=0; i<s; i++){
1393
+ this.stack[i] = v[i];
1394
+ }
1395
+ return s;
1396
+ },
1397
+
1398
+ //s: depth of stack to save
1399
+ //n: number of args(for outer lambda) to remove (= 0 unless tail position)
1400
+ //ret: closure array
1401
+ continuation: function(s, n){
1402
+ // note: implementation of this function for final version doesn't exist in 3imp.pdf..
1403
+ var ss = this.push(n, s);
1404
+ return this.closure(["refer-local", 0,
1405
+ ["nuate", this.save_stack(ss),
1406
+ ["return"]]],
1407
+ 0, //n (number of frees)
1408
+ null, //s (stack position to get frees)
1409
+ -1); // dotpos
1410
+ },
1411
+
1412
+ // shift stack
1413
+ // n: number of items to skip (from stack top)
1414
+ // m: number of items to shift
1415
+ // s: stack pointer (= index of stack top + 1)
1416
+ shift_args: function(n, m, s){
1417
+ for(var i = n-1; i >= -1; i--){
1418
+ this.index_set(s, i+m+1, this.index(s, i));
1419
+ }
1420
+ return s-m-1;
1421
+ },
1422
+
1423
+ index: function(s, i){
1424
+ return this.stack[s-i-2];
1425
+ },
1426
+
1427
+ index_set: function(s, i, v){
1428
+ this.stack[s-i-2] = v;
1429
+ },
1430
+
1431
+ //ret: [body, stack[s-1], stack[s-2], .., stack[s-n], dotpos]
1432
+ closure: function(body, n, s, dotpos){
1433
+ var v = []; //(make-vector n+1+1)
1434
+ v[0] = body;
1435
+ for(var i=0; i<n; i++)
1436
+ v[i+1] = this.index(s, i-1);
1437
+ v[n+1] = dotpos;
1438
+
1439
+ v.closure_p = true;
1440
+
1441
+ return v;
1442
+ },
1443
+
1444
+ execute: function(a, x, f, c, s){
1445
+ var ret = null;
1446
+ try{
1447
+ ret = this._execute(a, x, f, c, s);
1448
+ }
1449
+ catch(e){
1450
+ return this.on_error(e);
1451
+ }
1452
+ return ret;
1453
+ },
1454
+
1455
+ _execute: function(a, x, f, c, s){
1456
+ var ret = null;
1457
+ //puts("executing "+x[0]);
1458
+
1459
+ while(true){ //x[0] != "halt"){
1460
+ dump(new Hash({"a":a, "f":f, "c":c, "s":s, "x":x, "stack":this.stack}))
1461
+ switch(x[0]){
1462
+ case "halt":
1463
+ return a;
1464
+ case "refer-local":
1465
+ var n=x[1], x=x[2];
1466
+ a = this.index(f, n);
1467
+ break;
1468
+ case "refer-free":
1469
+ var n=x[1], x=x[2];
1470
+ a = c[n+1];
1471
+ break;
1472
+ case "refer-global":
1473
+ var sym=x[1], x=x[2];
1474
+ if(TopEnv.hasOwnProperty(sym)) var val = TopEnv[sym];
1475
+ else if(CoreEnv.hasOwnProperty(sym)) var val = CoreEnv[sym];
1476
+ else throw new Error("execute: unbound symbol: "+Object.inspect(sym));
1477
+
1478
+ a = val;
1479
+ break;
1480
+ case "indirect":
1481
+ var x=x[1];
1482
+ a = a[0]; //unboxing
1483
+ break;
1484
+ case "constant":
1485
+ var obj=x[1], x=x[2];
1486
+ a = obj;
1487
+ break;
1488
+ case "close":
1489
+ var ox=x;
1490
+ var n=ox[1], body=ox[2], x=ox[3], dotpos=ox[4];
1491
+ a = this.closure(body, n, s, dotpos);
1492
+ s -= n;
1493
+ break;
1494
+ case "box":
1495
+ var n=x[1], x=x[2];
1496
+ this.index_set(s, n, [this.index(s, n)]); //boxing
1497
+ break;
1498
+ case "test":
1499
+ var thenc=x[1], elsec=x[2];
1500
+ x = ((a!==false) ? thenc : elsec);
1501
+ break;
1502
+ case "assign-global":
1503
+ var name=x[1], x=x[2];
1504
+ if(!TopEnv.hasOwnProperty(name))
1505
+ throw new Error("global variable '"+name+"' is not defined");
1506
+
1507
+ TopEnv[name] = a;
1508
+ a = name;
1509
+ break;
1510
+ case "assign-local":
1511
+ var n=x[1], x=x[2];
1512
+ var box = this.index(f, n);
1513
+ box[0] = a;
1514
+ break;
1515
+ case "assign-free":
1516
+ var n=x[1], x=x[2];
1517
+ var box = c[n+1];
1518
+ box[0] = a;
1519
+ break;
1520
+ case "conti":
1521
+ var n=x[1], x=x[2];
1522
+ a = this.continuation(s, n);
1523
+ break;
1524
+ case "nuate":
1525
+ var stack=x[1], x=x[2];
1526
+ s = this.restore_stack(stack);
1527
+ break;
1528
+ case "frame":
1529
+ var ret = x[2];
1530
+ x = x[1];
1531
+ s = this.push(ret, this.push(f, this.push(c, s)));
1532
+ break;
1533
+ case "argument":
1534
+ var x=x[1];
1535
+ s = this.push(a, s);
1536
+ break;
1537
+ case "shift":
1538
+ var n=x[1], x=x[2];
1539
+
1540
+ // the number of arguments in the last call
1541
+ var n_args = this.index(s, n);
1542
+
1543
+ s = this.shift_args(n, n_args, s);
1544
+ break;
1545
+ case "apply": //extended: n_args as second argument
1546
+ var func = a; //, n_args = x[1];
1547
+
1548
+ // the number of arguments in the last call is
1549
+ // pushed to the stack.
1550
+ var n_args = this.index(s, -1);
1551
+ if(func instanceof Array){ //closure
1552
+ a = func;
1553
+ x = func[0];
1554
+
1555
+ // The position of dot in the parameter list.
1556
+ var dotpos = func[func.length-1];
1557
+
1558
+ if (dotpos >= 0) {
1559
+ // The dot is found
1560
+ // ----------------
1561
+ // => Process the &rest args: packing the rest args into a list.
1562
+ var ls = nil;
1563
+ for (var i=n_args; --i>=dotpos; ) {
1564
+ ls = new Pair(this.index(s, i), ls);
1565
+ }
1566
+ if (dotpos >= n_args) {
1567
+ // No rest argument is passed to this closure.
1568
+ // However, the closure expects the caller passes the rest argument.
1569
+ // In such case this VM prepares an empty list as the rest argument.
1570
+ // --------------------------------------------------------------
1571
+ // => We extend the stack to put the empty list.
1572
+ for(var i = -1; i < n_args; i++){
1573
+ this.index_set(s, i-1, this.index(s, i));
1574
+ }
1575
+ s++;
1576
+ // => Update the number of arguments
1577
+ this.index_set(s, -1, this.index(s, -1) + 1);
1578
+ }
1579
+ this.index_set(s, dotpos, ls);
1580
+ }
1581
+ f = s;
1582
+ c = a;
1583
+ }
1584
+ else if(func instanceof Function){
1585
+ var args = [];
1586
+ for(var i=0; i<n_args; i++)
1587
+ args.push(this.index(s, i));
1588
+
1589
+ var result = func(args, this);
1590
+
1591
+ if(result instanceof Pause){
1592
+ var pause = result;
1593
+ pause.set_state(this, ["return"], f, c, s);
1594
+ pause.ready();
1595
+ return pause;
1596
+ }
1597
+ else if(result instanceof Call){
1598
+ // [frame,
1599
+ // [constant... (args)
1600
+ // [constant, proc
1601
+ // [apply]]]]
1602
+ // [frame,
1603
+ // [constant, after
1604
+ // [apply 1]]]]
1605
+ // x
1606
+ var call_after = ["frame",
1607
+ ["argument",
1608
+ ["constant", 1,
1609
+ ["argument",
1610
+ ["constant", result.after,
1611
+ ["apply"]]]]],
1612
+ ["return"]];
1613
+ var call_proc = ["constant", result.args.length,
1614
+ ["argument",
1615
+ ["constant", result.proc,
1616
+ ["apply", result.args.length]]]];
1617
+ var push_args = result.args.inject(call_proc, function(opc, arg){
1618
+ // (foo 1 2) => first push 2, then 1
1619
+ // [constant 2 ... [constant 1 ... ]
1620
+ return ["constant", arg,
1621
+ ["argument",
1622
+ opc]];
1623
+ })
1624
+ x = ["frame",
1625
+ push_args,
1626
+ call_after]
1627
+ }
1628
+ else{
1629
+ a = result;
1630
+ x = ["return"];
1631
+ }
1632
+ }
1633
+ else{
1634
+ throw new Error("execute: unknown function type: "+Object.inspect(func));
1635
+ }
1636
+ break;
1637
+ case "return":
1638
+ var n=this.index(s, -1);
1639
+ var ss=s-n;
1640
+ x = this.index(ss, 0),
1641
+ f = this.index(ss, 1),
1642
+ c = this.index(ss, 2),
1643
+ s = ss-3-1;
1644
+ break;
1645
+ default:
1646
+ throw new Bug("unknown opecode type: "+x[0]);
1647
+ }
1648
+ }
1649
+
1650
+ // if(ret === null)
1651
+ // throw new Bug("interpreter exited in unusual way");
1652
+ // else
1653
+ // return ret;
1654
+ return a
1655
+ },
1656
+
1657
+ // expand macro forms (recursively)
1658
+ expand: function(x, flag){
1659
+ flag || (flag = {})
1660
+ var ret = null;
1661
+ if(x instanceof Symbol){
1662
+ ret = x;
1663
+ }
1664
+ else if(x instanceof Pair){
1665
+ switch(x.car){
1666
+ case Sym("define"):
1667
+ var left = x.cdr.car, exp = x.cdr.cdr;
1668
+ ret = new Pair(Sym("define"),
1669
+ new Pair(left, this.expand(exp, flag)));
1670
+ break;
1671
+ case Sym("begin"):
1672
+ ret = new Pair(Sym("begin"), this.expand(x.cdr, flag));
1673
+ break;
1674
+ case Sym("quote"):
1675
+ ret = x;
1676
+ break;
1677
+ case Sym("lambda"):
1678
+ var vars=x.cdr.car, body=x.cdr.cdr;
1679
+ ret = new Pair(Sym("lambda"),
1680
+ new Pair(vars, this.expand(body, flag)));
1681
+ break;
1682
+ case Sym("if"):
1683
+ var testc=x.second(), thenc=x.third(), elsec=x.fourth();
1684
+ ret = [Sym("if"), this.expand(testc, flag), this.expand(thenc, flag), this.expand(elsec, flag)].to_list();
1685
+ break;
1686
+ case Sym("set!"):
1687
+ var v=x.second(), x=x.third();
1688
+ ret = [Sym("set!"), v, this.expand(x, flag)].to_list();
1689
+ break;
1690
+ case Sym("call-with-current-continuation"):
1691
+ case Sym("call/cc"):
1692
+ var x=x.second();
1693
+ ret = [Sym("call/cc"), this.expand(x, flag)].to_list();
1694
+ break;
1695
+ default: //apply
1696
+ // if x is a macro call ...
1697
+ if(x.car instanceof Symbol && TopEnv[x.car.name] instanceof Syntax){
1698
+ var transformer = TopEnv[x.car.name];
1699
+ flag["modified"] = true;
1700
+ ret = transformer.transform(x);
1701
+
1702
+ if(BiwaScheme.Debug){
1703
+ var before = to_write(x), after = to_write(ret);
1704
+ if(before != after) puts("expand: " + before + " => " + after)
1705
+ }
1706
+
1707
+ var fl;
1708
+ for(;;){
1709
+ ret = this.expand(ret, fl={});
1710
+ if(!fl["modified"])
1711
+ break;
1712
+ }
1713
+ }
1714
+ else if(x == nil)
1715
+ ret = nil;
1716
+ else{
1717
+ ret = new Pair(this.expand(x.car, flag), x.cdr.to_array().map(function(item){return this.expand(item, flag)}.bind(this)).to_list());
1718
+ }
1719
+ }
1720
+ }
1721
+ else{
1722
+ ret = x;
1723
+ }
1724
+ return ret;
1725
+ },
1726
+
1727
+ evaluate: function(str, after_evaluate){
1728
+ this.parser = new Parser(str);
1729
+ this.compiler = new Compiler();
1730
+ if(after_evaluate)
1731
+ this.after_evaluate = after_evaluate;
1732
+
1733
+ if(BiwaScheme.Debug) puts("executing: " + str);
1734
+
1735
+ this.is_top = true;
1736
+ this.file_stack = [];
1737
+ return this.resume(false);
1738
+ },
1739
+
1740
+ resume: function(is_resume, a, x, f, c, s){
1741
+ var ret = BiwaScheme.undef;
1742
+
1743
+ for(;;){
1744
+ if(is_resume){
1745
+ ret = this.execute(a, x, f, c, s);
1746
+ is_resume = false;
1747
+ }
1748
+ else{
1749
+ if(!this.parser) break; // adhoc: when Pause is used via invoke_closure
1750
+ var expr = this.parser.getObject();
1751
+ if(expr === Parser.EOS) break;
1752
+
1753
+ // expand
1754
+ expr = this.expand(expr);
1755
+
1756
+ // compile
1757
+ var opc = this.compiler.run(expr);
1758
+ //if(BiwaScheme.Debug) p(opc);
1759
+
1760
+ // execute
1761
+ ret = this.execute(expr, opc, 0, [], 0);
1762
+ }
1763
+
1764
+ if(ret instanceof Pause){ //suspend evaluation
1765
+ return ret;
1766
+ }
1767
+ }
1768
+
1769
+ // finished executing all forms
1770
+ this.after_evaluate(ret);
1771
+ return ret;
1772
+ },
1773
+
1774
+ invoke_closure: function(closure, args){
1775
+ args || (args = []);
1776
+ var n_args = args.length;
1777
+
1778
+ var x = ["constant", n_args, ["argument", ["constant", closure, ["apply"]]]]
1779
+ for(var i=0; i<n_args; i++)
1780
+ x = ["constant", args[i], ["argument", x]]
1781
+
1782
+ return this.execute(closure, ["frame", x, ["halt"]], 0, closure, 0);
1783
+ },
1784
+
1785
+ // only compiling (for debug use only)
1786
+ compile: function(str){
1787
+ var obj = new Parser(str).getObject();
1788
+ var opc = Compiler.compile(obj);
1789
+ return opc;
1790
+ }
1791
+ });
1792
+ BiwaScheme.Interpreter.read = function(str){
1793
+ var parser = new Parser(str);
1794
+ return parser.getObject();
1795
+ }
1796
+
1797
+ /* --------------------------------------- namespace webscheme */
1798
+ }