*** empty log message ***
[m17n/m17n-lib-js.git] / xex.js
1 // -* coding: utf-8; -*
2
3 var Xex = {};
4
5 Xex.Variable = function (domain, name, val)
6 {
7   this.domain = domain;
8   this.name = name;
9   this.val = val;
10 }
11
12 Xex.Variable.prototype.clone = function () {
13   return new Xex.Variable (this.domain, this.name, this.value);
14 }
15     
16 Xex.Variable.prototype.Equals = function (obj) {
17   return ((obj instanceof Xex.Variable)
18           && obj.name == this.name);
19 }
20
21 Xex.Variable.prototype.SetValue = function (term) {
22   this.val = term;
23   return term;
24 }
25
26 Xex.Function = function (name, with_var, min_args, max_args) {
27   this.name = name;
28   this.with_var = with_var;
29   this.min_args = min_args;
30   this.max_args = max_args;
31 };  
32
33 Xex.Subrountine = function (builtin, name, with_var, min_args, max_args) {
34   this.name = name;
35   this.with_var = with_var;
36   this.min_args = min_args;
37   this.max_args = max_args;
38   this.builtin = builtin;
39 }
40
41 Xex.Subrountine.prototype.Call = function (domain, vari, args)
42 {
43   newargs = new Array ();
44   for (var i = 0; i < args.length; i++)
45     {
46       newargs[i] = args[i].Eval (domain);
47       if (domain.Thrown ())
48         return newargs[i];
49     }
50   return this.builtin (domain, vari, newargs)
51 }
52
53 Xex.SpecialForm = function (builtin, name, with_var, min_args, max_args)
54 {
55   this.name = name;
56   this.with_var = with_var;
57   this.min_args = min_args;
58   this.max_args = max_args;
59   this.builtin = builtin;
60 }
61
62 Xex.SpecialForm.prototype.Call = function (domain, vari, args)
63 {
64   return this.builtin (domain, vari, args)
65 }
66
67 Xex.Lambda = function (name, min_args, max_args, args, body)
68 {
69   this.name = name;
70   this.with_var = with_var;
71   this.min_args = min_args;
72   this.max_args = max_args;
73   this.args = args;
74   this.body = body;
75 }
76
77 Xex.Lambda.prototype.Call = function (domain, vari, args)
78 {
79   var current = domain.bindings;
80   var result = Xex.Zero;
81   var limit = max_args >= 0 ? args.length : args.length - 1;
82   var i;
83   
84   try {
85     for (i = 0; i < limit; i++)
86       {
87         result = args[i].Eval (domain);
88         if (domain.Thrown ())
89           return result;
90         domain.Bind (this.args[i], result);
91       }
92     if (max_args < 0)
93       {
94         var list = new Array ();
95         for (i = 0; i < args[limit].length; i++)
96           {
97             result = args[limit].Eval (domain);
98             if (domain.Thrown ())
99               return result;
100             list[i] = result;
101           }
102         domain.Bind (this.args[limit], list);
103       }
104     try {
105       domain.Catch (Xex.CatchTag.Return);
106       for (var term in this.body)
107         {
108           result = term.Eval (domain);
109           if (domain.Thrown ())
110             return result;
111         }
112     } finally {
113       domain.Uncatch ();
114     }
115   } finally {
116     domain.UnboundTo (current);
117   }
118   return result;
119 }
120
121 Xex.Macro = function (name, min_args, max_args, args, body)
122 {
123   this.name = name;
124   this.with_var = with_var;
125   this.min_args = min_args;
126   this.max_args = max_args;
127   this.args = args;
128   this.body = body;
129 }
130
131 Xex.Macro.prototype.Call = function (domain, vari, args)
132 {
133   var current = domain.bindings;
134   var result = Xex.Zero;
135   var i;
136
137   try {
138     for (i = 0; i < args.length; i++)
139       domain.Bind (this.args[i], args[i]);
140     try {
141       domain.Catch (Xex.CatchTag.Return);
142       for (var term in body)
143         {
144           result = term.Eval (domain);
145           if (domain.Thrown ())
146             break;
147         }
148     } finally {
149       domain.Uncatch ();
150     }
151   } finally {
152     domain.UnboundTo (current);
153   }
154   return result;
155 }
156
157 Xex.Bindings = function (vari)
158 {
159   this.vari = vari;
160   this.old_value = vari.val;
161 }
162
163 Xex.Bindings.prototype.UnboundTo = function (boundary)
164 {
165   for (var b = this; b != boundary; b = b.next)
166     b.vari.val = b.old_value;
167   return boundary;
168 }
169
170 Xex.Bind = function (bindings, vari, val)
171 {
172   var b = new Xex.Bindings (vari);
173   b.vari.val = val;
174   b.next = bindings;
175   return b;
176 }
177
178 Xex.CatchTag = {
179   Return: 0,
180   Break: 1
181 }
182
183 Xex.Domain = function (name, parent, context)
184 {
185   this.name = name;
186   this.context = context;
187   this.depth = 0;
188   this.termtypes = parent ? parent.termtypes.slice () : new Array ();
189   this.functions = parent ? parent.functions.slice () : new Array ();
190   this.variables = parent ? parent.variable.slice () : new Array ();
191
192   var call_stack = new Array ();
193   var bindings;
194   var catch_stack = new Array ();
195   var catch_count = 0;
196
197   if (this.CallStackCount)
198     return;
199
200   var proto = Xex.Domain.prototype;
201
202   proto.CallStackCount = function () { return call_stack.length; }
203   proto.CallStackPush = function (term) { call_stack.push (term); }
204   proto.CallStackPop = function () { call_stack.pop (); }
205   proto.Bind = function (vari, val)
206   {
207     bindings = Xex.Bind (bindings, vari, val);
208   }
209   proto.UnboundTo = function (boundary)
210   {
211     if (bindings)
212       bindings = bindings.UnboundTo (boundary);
213   }
214   proto.Catch = function (tag) { catch_stack.push (tag); catch_count++; }
215   proto.Uncatch = function ()
216   {
217     catch_stack.pop ();
218     if (catch_count > catch_stack.length)
219       catch_count--;
220   }
221   proto.Thrown = function ()
222   {
223     var i = catch_stack.length - catch_count;
224     return (i > 0 ? i - 1 : false);
225   }
226   proto.ThrowReturn = function ()
227   {
228     for (var i = catch_stack.length - 1; i >= 0; i--)
229       {
230         catch_count--;
231         if (catch_stack[i] == Xex.CatchTag.Return)
232           break;
233       }
234   }
235   proto.ThrowBreak = function ()
236   {
237     if (catch_stack[catch_stack.length - 1] != Xex.CatchTag.Break)
238       throw new Xex.Error (Xex.Error.NoLoopToBreak,
239                            "No surrounding loop to break");
240     catch_count--;
241   }
242   proto.ThrowSymbol = function (tag)
243   {
244     var i = catch_count;
245     for (var j = catch_stack.length - 1; j >= 0; j--)
246       {
247         i--;
248         if (Xex.CatchTag.Matches (catch_stack[i], tag))
249           {
250             catch_count = i;
251             return;
252           }
253       }
254     throw new Xex.Error (Xex.Error.UncaughtThrow,
255                          "No corresponding catch: " + tag);
256   }
257   proto.DefType = function (obj)
258   {
259     var type = obj.type;
260     if (this.termtypes[type])
261       throw new Xex.Error (Xex.Error.TermTypeInvalid,
262                            "Already defined: " + type);
263     if (this.functions[type])
264       throw new Xex.Error (Xex.Error.TermTypeInvalid,
265                            "Already defined as a funciton or a macro: "
266                            + type);
267     this.termtypes[type] = obj.Parser;
268   }
269
270   proto.DefSubr = function (builtin, name, with_var, min_args, max_args)
271   {
272     this.functions[name] = new Xex.Subrountine (builtin, name, with_var,
273                                                 min_args, max_args);
274   }
275   proto.DefSpecial = function (builtin, name, with_var, min_args, max_args)
276   {
277     this.functions[name] = new Xex.SpecialForm (builtin, name, with_var,
278                                                 min_args, max_args);
279   }
280   proto.Defun = function (name, min_args, max_args, args, body)
281   {
282     this.functions[name] = new Xex.Lambda (name, min_args, max_args,
283                                            args, body);
284   }
285   proto.DefunByFunc = function (func) { this.functions[func.Name] = func; }
286   proto.Defmacro = function (name, min_args, max_args, args, body)
287   {
288     this.functions[name] = new Xex.Macro (name, min_args, max_args, args, body);
289   }
290   proto.DefAlias = function (alias, fname)
291   {
292     var func = this.functions[fname];
293
294     if (this.termtypes[alias])
295       throw new Xex.Error (Xex.Error.FunctionConflict,
296                            "Already defined as a term type: " + alias);
297     if (this.functions[alias])
298       throw new Xex.Error (Xex.Error.FunctionConflict,
299                            "Already defined as a function: " + alias);
300     if (! func)
301       throw new Xex.Error (Xex.Error.UnknownFunction, fname);
302     this.functions[alias] = func;
303   }
304   proto.Defvar = function (name)
305   {
306     var vari = this.variables[name];
307     if (vari)
308       {
309         if (vari.Typed)
310           throw new Xex.Error (Xex.Error.VariableTypeConflict,
311                                "Not a non-typed variable: " + name);
312       }
313     else
314       {
315         vari = new Xex.Variable (this, name, Xex.Zero);
316         this.variables[name] = vari;
317       }
318     return vari;
319   }
320   proto.GetFunc = function (name) { return this.functions[name]; }
321   proto.CopyFunc = function (domain, name)
322   {
323     var func = this.functions[name];
324     domain.DefunByFunc (func);
325     return true;
326   }
327   proto.CopyFuncAll = function (domain)
328   {
329     for (var i = this.functions.length - 1; i >= 0; i--)
330       domain.DefunByFunc (this.functions[i]);
331   }
332   proto.GetVarCreate = function (name)
333   {
334     var vari = this.variables[name];
335     if (! vari)
336       this.variables[name] = vari
337         = new Xex.Variable (this, name, Xex.Zero);
338     return vari;
339   }
340   proto.GetVar = function (name) { return this.variables[name]; }
341   proto.SaveValues = function ()
342   {
343     values = new Array ()
344     for (var i = this.variables.length - 1; i >= 0; i--)
345       {
346         var vari = this.variables[i];
347         values[vari.Name] = vari.val.Clone ();
348       }
349     return values;
350   }
351   proto.RestoreValues = function (values)
352   {
353     var name;
354     for (name in values)
355       {
356         var vari = this.variables[name];
357         vari.val = values[name];
358       }
359   }
360 };
361
362 Xex.Term = function () { }
363 Xex.Term.prototype = {
364   IsTrue: true,
365   Eval: function (domain) { return this.Clone (); },
366   Equals: function (obj)
367   {
368     return (this.type == obj.type
369             && this.val
370             && obj.val == this.val);
371   },
372   Matches: function (obj) { return this.Equals (obj); },
373   toString: function ()
374   {
375     if (this.val != undefined)
376       return '<' + this.type + '>' + this.val + '</' + this.type + '>';
377     return '<' + this.type + '/>';
378   }
379 };
380
381 Xex.ParseTerm = function (domain, node)
382 {
383   var name = node.nodeName;
384   var parser = domain.termtypes[name];
385
386   if (parser)
387     return parser (domain, node);
388   if (name == 'defun' || name == 'defmacro')
389     {
390       name = parse_defun_head (domain, node);
391       parse_defun_body (domain, node);
392       return new Xex.StrTerm (name);
393     }
394   if (name == 'defvar')
395     {
396       name = parse_defvar (doamin, node);
397       return new Xex.StrTerm (nanme);
398     }
399
400   return new Xex.Funcall.prototype.Parser (domain, node);
401 }
402
403 Xex.Varref = function (vname)
404 {
405   var vari;
406
407   this.val = vname;
408   if (this.type)
409     return;
410   var proto = Xex.Varref.prototype;
411
412   proto.type = 'varref';
413
414   proto.Clone = function () { return new Xex.Varref (this.val); }
415   proto.Eval = function (domain)
416   {
417     if (! vari || vari.domain != domain)
418       vari = domain.GetVarCreate (this.val);
419     return vari.val;
420   }
421
422   proto.Parser = function (domain, node)
423   {
424     return new Xex.Varref (node.attributes['vname'].nodeValue);
425   }
426 }
427
428 Xex.Varref.prototype = new Xex.Term ();
429
430 var null_args = new Array ();
431   
432 Xex.Funcall = function (func, vari, args)
433 {
434   this.func = func;
435   if (! args)
436     args = null_args;
437   this.vari = vari;
438   this.args = args;
439   if (this.type)
440     return;
441
442   var proto = Xex.Funcall.prototype;
443
444   proto.type = 'funcall';
445
446   proto.Parser = function (domain, node)
447   {
448     var fname = node.nodeName;
449     var attr;
450
451     if (fname == 'funcall')
452       fname = node.attributes['fname']
453     var func = domain.GetFunc (fname);
454     var vari;
455     attr = node.attributes['vname'];
456     vari = attr != undefined ? domain.GetVarCreate (attr.nodeValue) : false;
457     var args = new Array ();
458     for (node = node.firstChild; node; node = node.nextSibling)
459       if (node.nodeType == 1)
460         args.push (Xex.ParseTerm (domain, node));
461     return new Xex.Funcall (func, vari, args);
462   }
463
464   proto.Eval = function (domain)
465   {
466     return this.func.Call (domain, this.vari, this.args);
467   }
468
469   proto.Clone = function ()
470   {
471     return new Xex.Funcall (this.func, this.vari, this.args);
472   }
473
474   proto.Equals = function (obj)
475   {
476     return (obj.type == 'funcall'
477             && obj.func == this.func
478             && obj.vari.Equals (this.vari)
479             && obj.args.length == this.func.length);
480   }
481
482   proto.toString = function ()
483   {
484     var arglist = ''
485     var len = this.args.length;
486     if (len == 0)
487       return '<' + this.func.name + '/>';
488     for (var i = 0; i < len; i++)
489       arglist += this.args[i].toString ();
490     return '<' + this.func.name + '>' + arglist + '</' + this.func.name + '>';
491   }
492 }
493
494 Xex.Funcall.prototype = new Xex.Term ();
495
496 Xex.ErrTerm = function (ename, message, stack)
497 {
498   if (this.type)
499     return;
500
501   var proto = Xex.ErrTerm.prototype;
502
503   proto.type = 'error';
504
505   proto.Parser = function (domain, node)
506   {
507     return new Xex.ErrTerm (node.attributes['ename'].nodeValue,
508                               node.innerText, false);
509   }
510
511   proto.CallStack = function () { return stack; }
512
513   proto.SetCallStack = function (value) { statck = value; }
514
515   proto.Clone = function ()
516   {
517     return new Xex.ErrTerm (ename, message, false);
518   }
519
520   proto.Equals = function (obj)
521   {
522     return (obj.type == 'error'
523             && obj.ename == ename && obj.message == message
524             && (obj.stack ? (stack && stack.length == obj.stack.length)
525                 : ! stack));
526   }
527
528   proto.Matches = function (obj)
529   {
530     return (obj.type == 'error' && obj.ename == ename);
531   }
532 }
533
534 Xex.ErrTerm.prototype = new Xex.Term ();
535
536 Xex.IntTerm = function (num)
537 {
538   this.val = num;
539   this.IsTrue = num != 0;
540   if (this.type)
541     return;
542   var proto = Xex.IntTerm.prototype;
543   proto.type = 'integer';
544   proto.Clone = function () { return new Xex.IntTerm (this.val); }
545   proto.Parser = function (domain, node)
546   {
547     return new Xex.IntTerm (parseInt (node.firstChild.nodeValue));
548   }
549 }
550
551 Xex.IntTerm.prototype = new Xex.Term ();
552
553 Xex.StrTerm = function (str)
554 {
555   this.val = str;
556   this.IsTrue = str && str.length > 0;
557   if (this.type)
558     return;
559   var proto = Xex.StrTerm.prototype;
560   proto.type = 'string';
561   proto.Clone = function () { return new Xex.StrTerm (this.val); }
562   proto.Parser = function (domain, node)
563   {
564     return new Xex.StrTerm (node.firstChild.nodeValue);
565   }
566 }
567
568 Xex.StrTerm.prototype = new Xex.Term ();
569
570 Xex.LstTerm = function (list)
571 {
572   this.val = list;
573   this.IsTrue = list && list.length > 0;
574   if (this.type)
575     return;
576
577   var proto = Xex.LstTerm.prototype;
578
579   proto.type = 'list';
580   
581   proto.Clone = function () { return new Xex.LstTerm (this.val.slice ()); }
582
583   proto.Equals = function (obj)
584   {
585     if (obj.type != 'list' || obj.val.length != this.val.length)
586       return false;
587     var i, len = this.val.length;
588     for (i = 0; i < len; i++)
589       if (! this.val[i].Equals (obj.val[i]))
590         return false;
591     return true;
592   }
593
594   proto.Parser = function (domain, node)
595   {
596     var list = new Array ();
597
598     for (node = node.firstChild; node; node = node.nextSibling)
599       if (node.nodeType == 1)
600         list.push (Xex.ParseTerm (domain, node));
601     return new Xex.LstTerm (list);
602   }
603
604   proto.toString = function ()
605   {
606     var len = this.val.length;
607
608     if (len == 0)
609       return '<list/>';
610     var str = '<list>';
611     for (i = 0; i < len; i++)
612       str += this.val[i].toString ();
613     return str + '</list>';
614   }
615 }
616
617 Xex.LstTerm.prototype = new Xex.Term ();
618
619 (function () {
620   var basic = new Xex.Domain ('basic');
621
622   function Fset (domain, vari, args)
623   {
624     return vari.SetValue (args[0]);
625   }
626
627   function maybe_set_intvar (vari, n)
628   {
629     var term = new Xex.IntTerm (n);
630     if (vari != null)
631       vari.SetValue (term);
632     return term;
633   }
634
635   function Fadd (domain, vari, args)
636   {
637     var n = vari ? vari.val.val : 0;
638     var len = args.length;
639
640     for (var i = 0; i < len; i++)
641       n += args[i].val;
642     return maybe_set_intvar (vari, n);
643   }
644
645   function Fand (domain, vari, args)
646   {
647     var len = args.length;
648     for (var i = 0; i < len; i++)
649     {
650       var result = args[i].Eval (domain);
651       if (domain.Thrown ())
652         return result;
653       if (! result.IsTrue)
654         return Xex.Zero;
655     }
656     return Xex.One;
657   }
658
659   function For (domain, vari, args)
660   {
661     var len = args.length;
662     for (var i = 0; i < len; i++)
663     {
664       var result = args[i].Eval (domain);
665       if (domain.Thrown ())
666         return result;
667       if (result.IsTrue)
668         return Xex.One;
669     }
670     return Xex.Zero;
671   }
672
673   function Fprogn (domain, vari, args)
674   {
675     var result = Xex.One;
676     var len = args.length;
677
678     for (var i = 0; i < len; i++)
679       {
680         result = args[i].Eval (domain);
681         if (domain.Thrown ())
682           return result;
683       }
684     return result;
685   }
686
687   function Fif (domain, vari, args)
688   {
689     var result = args[0].Eval (domain);
690
691     if (domain.Thrown ())
692       return result;
693     if (result.IsTrue)
694       return args[1].Eval (domain);
695     if (args.Length == 2)
696       return Zero;
697     return args[2].Eval (domain);
698   }
699
700   basic.DefSubr (Fset, "set", true, 1, 1);
701   basic.DefSubr (Fadd, "add", true, 1, -1);
702   basic.DefSpecial (Fand, "and", false, 1, -1);
703   basic.DefSpecial (For, "or", false, 1, -1);
704   basic.DefAlias ("=", "set");
705   basic.DefSpecial (Fprogn, "progn", false, 1, -1);
706   basic.DefSpecial (Fif, "if", false, 2, 3);
707   basic.DefType (new Xex.Funcall ());
708   basic.DefType (new Xex.Varref ());
709   basic.DefType (new Xex.ErrTerm ());
710   basic.DefType (new Xex.IntTerm ());
711   basic.DefType (new Xex.StrTerm ());
712   basic.DefType (new Xex.LstTerm ());
713
714   Xex.Domain.basic = basic;
715 }) ();
716
717 Xex.Zero = new Xex.IntTerm (0);
718 Xex.One = new Xex.IntTerm (1);
719
720 var obj = new XMLHttpRequest ();
721 obj.open ('GET', 'xex.xml', false);
722 obj.send ('');
723 var body = obj.responseXML.firstChild;
724 var domain = Xex.Domain.basic;
725 document.ATTR = body;
726 alert (Xex.ParseTerm (domain, body).Eval (domain));