yhara-tickets 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }