X-Git-Url: http://git.chise.org/gitweb/?p=m17n%2Fm17n-lib-js.git;a=blobdiff_plain;f=xex.js;h=e5f9c84673edc845a6ab9f91d85b09459a03f2e9;hp=8be7f9d670df56ee08ba3e57eb4d615344bd0eb9;hb=d64052eb9999afb5618be26c44d033cc8ce33c54;hpb=068ab538bbcee88357aa4787fb15833a005c1075 diff --git a/xex.js b/xex.js index 8be7f9d..e5f9c84 100644 --- a/xex.js +++ b/xex.js @@ -1,35 +1,102 @@ -// -* coding: utf-8; -* +// xex.js -- Xex (XML Expression) interpreter +// Copyright (C) 2010 +// National Institute of Advanced Industrial Science and Technology (AIST) +// Registration Number H15PRO112 -var Xex = {}; +// This file is part of the m17n database; a sub-part of the m17n +// library. -Xex.Alist = function () -{ - this.count = 0; -} +// The m17n library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. -Xex.Alist.prototype.put = function (key, val) -{ - this.count++; - return (this[key] = val); -} -Xex.Alist.prototype.clone = function () -{ - var alist = new Xex.Alist (); - for (key in this) - alist[key] = this[key]; - return alist; -} -Xex.Alist.prototype.toString = function () -{ - var str = 'alist:'; - for (key in this) - str += '"' + key + '"'; - return str; -} +// The m17n library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with the m17n library; if not, write to the Free +// Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. + +// Please note that the code is not yet matured. + +var Xex = {}; + +(function () { // Logging + // The logging node containing tracing information. + var log = null; + // Number of lines. + var lines; + // Style properties of the logging node. + var styles = { border: '1px solid black', + font: 'normal normal normal small monospace', + width: '100%', + minHeight: '300px', + maxHeight: '300px', + overflow: 'auto' }; + + // Toggle logging on and off. PARENT if non-null specifies the + // parent of the log node. The log node is appended to PARENT. + // If PARENT is null, 'body' node is assumed. + Xex.LogToggle = function (parent) + { + if (log) + { + log.parentNode.removeChild (log); + log = null; + return; + } + if (! parent) + parent = document.getElementsByTagName ('body')[0]; + log = document.createElement ('ol'); + for (var prop in styles) + log.style[prop] = styles[prop]; + parent.appendChild (log); + lines = 0; + return log; + } -// Xex.alist = new Xex.Alist (); -// Xex.alist.put ('abc', "ABC"); -// alert (Xex.alist['abc']); + // Log ARG (string). INDENT if specified is a number of columns to + // indent. If INDENT is -1, ARG is appended to the last log. + Xex.Log = function (arg, indent) + { + if (! log) + return; + if (! arg) + { + while (log.childNodes.length > 0) + log.removeChild (log.firstChild); + lines = 0; + } + else + { + var node; + if (indent == -1) + log.lastChild.innerText += arg; + else + { + lines++; + if (lines >= 256) + { + node = log.firstElement (); + log.start = lines - 254; + } + else + node = document.createElement ('li'); + if (indent != undefined) + node.style.textIndent = (indent + 1) + 'em'; + else + node.style.textIndent = '0px'; + node.innerText = arg; + log.appendChild (node); + log.scrollTop = log.scrollHeight; + } + } + } +}) (); Xex.Error = { UnknownError: "unknown-error", @@ -45,7 +112,8 @@ Xex.Error = { UnknownFunction: "unknown-function", MacroExpansionError: "macro-expansion-error", - NoVariableName: "no-variable-anme", + NoVariableName: "no-variable-name", + NoFunctionName: "no-funcion-name", // Run time errors. ArithmeticError: "arithmetic-error", @@ -56,45 +124,52 @@ Xex.Error = { UncaughtThrow: "uncaught-throw" }; -Xex.Variable = function (domain, name, val) +Xex.Variable = function (name, desc, val, range) { - this.domain = domain; - this.name = name; + if (name) + this.name = name; + if (desc) + this.desc = desc; this.val = val; + if (range) + this.range = range; } -Xex.Variable.prototype.clone = function () { - return new Xex.Variable (this.domain, this.name, this.value); -} - -Xex.Variable.prototype.Equals = function (obj) { - return ((obj instanceof Xex.Variable) - && obj.name == this.name); -} +Xex.Variable.prototype = { + clone: function () + { + return new Xex.Variable (this.name, this.desc, this.val, this.range); + }, + equals: function (obj) + { + return ((obj instanceof Xex.Variable) + && obj.name == this.name); + }, + SetValue: function (term) + { + this.val = term; + return term; + } +} -Xex.Variable.prototype.SetValue = function (term) { - this.val = term; - return term; -} -Xex.Function = function (name, with_var, min_args, max_args) { +Xex.Function = function (name, with_var, min_args, max_args) +{ this.name = name; this.with_var = with_var; this.min_args = min_args; this.max_args = max_args; }; -Xex.Subrountine = function (builtin, name, with_var, min_args, max_args) { - this.name = name; - this.with_var = with_var; - this.min_args = min_args; - this.max_args = max_args; +Xex.Subrountine = function (builtin, name, with_var, min_args, max_args) +{ + Xex.Function.apply (this, [name, with_var, min_args, max_args]); this.builtin = builtin; } Xex.Subrountine.prototype.Call = function (domain, vari, args) { - newargs = new Array (); + var newargs = new Array (); for (var i = 0; i < args.length; i++) { newargs[i] = args[i].Eval (domain); @@ -106,10 +181,7 @@ Xex.Subrountine.prototype.Call = function (domain, vari, args) Xex.SpecialForm = function (builtin, name, with_var, min_args, max_args) { - this.name = name; - this.with_var = with_var; - this.min_args = min_args; - this.max_args = max_args; + Xex.Function.apply (this, [name, with_var, min_args, max_args]); this.builtin = builtin; } @@ -120,10 +192,7 @@ Xex.SpecialForm.prototype.Call = function (domain, vari, args) Xex.Lambda = function (name, min_args, max_args, args, body) { - this.name = name; - this.with_var = with_var; - this.min_args = min_args; - this.max_args = max_args; + Xex.Function.apply (this, [name, false, min_args, max_args]); this.args = args; this.body = body; } @@ -174,10 +243,7 @@ Xex.Lambda.prototype.Call = function (domain, vari, args) Xex.Macro = function (name, min_args, max_args, args, body) { - this.name = name; - this.with_var = with_var; - this.min_args = min_args; - this.max_args = max_args; + Xex.Function.apply (this, [name, false, min_args, max_args]); this.args = args; this.body = body; } @@ -193,9 +259,9 @@ Xex.Macro.prototype.Call = function (domain, vari, args) domain.Bind (this.args[i], args[i]); try { domain.Catch (Xex.CatchTag.Return); - for (var term in body) + for (var i in this.body) { - result = term.Eval (domain); + result = this.body[i].Eval (domain); if (domain.Thrown ()) break; } @@ -254,7 +320,11 @@ Xex.Domain = function (name, parent, context) for (elt in parent.functions) this.functions[elt] = parent.functions[elt]; for (elt in parent.variables) - this.variables[elt] = parent.variables[elt]; + { + var vari = parent.variables[elt]; + this.variables[elt] = new Xex.Variable (vari.name, vari.desc, + vari.val, vari.range); + } } this.call_stack = new Array (); @@ -352,7 +422,7 @@ Xex.Domain.prototype = { this.functions[name] = new Xex.Lambda (name, min_args, max_args, args, body); }, - DefunByFunc: function (func) { this.functions[func.Name] = func; }, + DefunByFunc: function (func) { this.functions[func.name] = func; }, Defmacro: function (name, min_args, max_args, args, body) { this.functions[name] = new Xex.Macro (name, min_args, max_args, @@ -362,30 +432,20 @@ Xex.Domain.prototype = { { var func = this.functions[fname]; + if (! func) + throw new Xex.ErrTerm (Xex.Error.UnknownFunction, fname); if (this.termtypes[alias]) throw new Xex.ErrTerm (Xex.Error.FunctionConflict, "Already defined as a term type: " + alias); if (this.functions[alias]) throw new Xex.ErrTerm (Xex.Error.FunctionConflict, "Already defined as a function: " + alias); - if (! func) - throw new Xex.ErrTerm (Xex.Error.UnknownFunction, fname); this.functions[alias] = func; }, - Defvar: function (name) + Defvar: function (name, desc, val, range) { - var vari = this.variables[name]; - if (vari) - { - if (vari.Typed) - throw new Xex.ErrTerm (Xex.Error.VariableTypeConflict, - "Not a non-typed variable: " + name); - } - else - { - vari = new Xex.Variable (this, name, Xex.Zero); - this.variables[name] = vari; - } + var vari = new Xex.Variable (name, desc, val, range); + this.variables[name] = vari; return vari; }, GetFunc: function (name) @@ -393,7 +453,7 @@ Xex.Domain.prototype = { var func = this.functions[name]; if (! func) throw new Xex.ErrTerm (Xex.Error.UnknownFunction, - "Unknown function: " + this + ':' + name); + "Unknown function: " + name); return func; }, CopyFunc: function (domain, name) @@ -411,7 +471,8 @@ Xex.Domain.prototype = { { var vari = this.variables[name]; if (! vari) - vari = this.variables[name] = new Xex.Variable (this, name, Xex.Zero); + vari = this.variables[name] = new Xex.Variable (name, null, + Xex.Zero, null); return vari; }, GetVar: function (name) { return this.variables[name]; }, @@ -438,64 +499,194 @@ Xex.Term.prototype = { IsTrue: function () { return true; }, Eval: function (domain) { return this.Clone (); }, Clone: function (domain) { return this; }, - Equals: function (obj) + equals: function (obj) { return (this.type == obj.type - && this.val + && this.val != undefined && obj.val == this.val); }, - Matches: function (obj) { return this.Equals (obj); }, + Matches: function (obj) { return this.equals (obj); }, toString: function () { if (this.val != undefined) return '<' + this.type + '>' + this.val + ''; return '<' + this.type + '/>'; - } + }, + Intval: function () { throw new Xex.ErrTerm (Xex.Error.WrongType, + "Not an integer"); }, + Strval: function () { throw new Xex.ErrTerm (Xex.Error.WrongType, + "Not a string"); } }; -Xex.ParseTerm = function (domain, node) +Node.prototype.firstElement = function () { - var name = node.nodeName; - var parser = domain.termtypes[name]; - - if (parser) - return parser (domain, node); - if (name == 'defun' || name == 'defmacro') - { - name = parse_defun_head (domain, node); - parse_defun_body (domain, node); - return new Xex.StrTerm (name); - } - if (name == 'defvar') - { - name = parse_defvar (doamin, node); - return new Xex.StrTerm (nanme); - } - - return new Xex.Funcall.prototype.Parser (domain, node); + for (var n = this.firstChild; n; n = n.nextSibling) + if (n.nodeType == 1) + return n; + return null; } -Xex.ParseTermList = function (domain, node) +Node.prototype.nextElement = function () { - for (var n = node; n; n = n.nextSibling) + for (var n = this.nextSibling; n; n = n.nextSibling) if (n.nodeType == 1) + return n; + return null; +}; + +(function () { + function parse_defvar (domain, node) + { + var name = node.attributes['vname'].nodeValue; + if (! name) + throw new Xex.ErrTerm (Xex.Error.NoVariableName, node, ''); + var vari = domain.variables[name]; + var desc, val = null, range; + if (vari) { - if (n.nodeName == 'defun' || n.nodeName == 'defmacro') - Xex.parse_defun_head (domain, n); + desc = vari.description; + val = vari.val; + range = vari.range; } - var terms = new Array (); - for (var n = node; n; n = n.nextSibling) - if (n.nodeType == 1) + node = node.firstElement (); + if (node && node.nodeName == 'description') + { + desc = node.firstChild.nodeValue; + node = node.nextElement (); + } + if (node) + { + val = Xex.Term.Parse (domain, node); + node = node.nextElement (); + if (node && node.nodeName == 'possible-values') + for (node = node.firstElement (); node; node = node.nextElement ()) + { + var pval; + if (node.nodeName == 'range') + { + if (! val.IsInt) + throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid, + 'Range not allowed for ' + name); + pval = new Array (); + for (var n = node.firstElement (); n; n = n.nextElement ()) + { + var v = Xex.Term.Parse (domain, n); + if (! v.IsInt) + throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid, + 'Invalid range value: ' + val); + pval.push (v); + } + } + else + { + pval = Xex.Term.Parse (domain, node); + if (val.type != pval.type) + throw new Xex.ErrTerm (Xex.Error.TermTypeInvalid, + 'Invalid possible value: ' + pval); + } + if (! range) + range = new Array (); + range.push (pval); + } + } + if (val == null) + val = Xex.Zero; + domain.Defvar (name, desc, val, range); + return name; + } + + function parse_defun_head (domain, node) + { + var name = node.attributes['fname'].nodeValue; + if (! name) + throw new Xex.ErrTerm (Xex.Error.NoFunctionName, node, ''); + var args = new Array (); + var nfixed = 0, noptional = 0, nrest = 0; + + node = node.firstElement (); + if (node && node.nodeName == 'args') + { + var n; + for (n = n.firstElement (); n; n = n.nextElement ()) + { + if (n.nodeName == 'fixed') + nfixed++; + else if (n.nodeName == 'optional') + noptional++; + else if (n.nodeName == 'rest') + nrest++; + else + throw new Xex.ErrTerm (Xex.Error.WrongType, n, n.nodeName); + } + if (nrest > 1) + throw new Xex.ErrTerm (Xex.Error.WrongType, n, 'Too many '); + for (n = node.firstElement (); n; n = n.nextElement ()) + args.push (domain.DefVar (n.attributes['vname'].nodeValue)); + } + args.min_args = nfixed; + args.max_args = nrest == 0 ? nfixed + noptional : -1; + + if (node.nodeName == 'defun') + domain.Defun (name, args, null); + else + domain.Defmacro (name, args, null); + return name; + } + + function parse_defun_body (domain, node) + { + var name = node.attributes['fname'].nodeValue; + var func = domain.GetFunc (name); + var body; + for (node = node.firstElement (); node; node = node.nextElement ()) + if (node.nodeName != 'description' && node.nodeName != 'args') + break; + body = Xex.Term.Parse (domain, node, null); + func.body = body; + } + + Xex.Term.Parse = function (domain, node, stop) + { + if (arguments.length == 2) + { + var name = node.nodeName; + var parser = domain.termtypes[name]; + + if (parser) + return parser (domain, node); + if (name == 'defun' || name == 'defmacro') + { + name = parse_defun_head (domain, node); + parse_defun_body (domain, node); + return new Xex.StrTerm (name); + } + if (name == 'defvar') + { + name = parse_defvar (domain, node); + return new Xex.StrTerm (name); + } + return new Xex.Funcall.prototype.Parser (domain, node); + } + for (var n = node; n && n != stop; n = n.nextElement ()) + if (n.nodeName == 'defun' || n.nodeName == 'defmacro') + parse_defun_head (domain, n); + var terms = null; + for (var n = node; n && n != stop; n = n.nextElement ()) { if (n.nodeName == 'defun' || n.nodeName == 'defmacro') - Xex.parse_defun_body (domain, n); + parse_defun_body (domain, n); else if (n.nodeName == 'defvar') - Xex.parse_defvar (domain, n); + parse_defvar (domain, n); else - terms.push (Xex.ParseTerm (domain, n)); + { + if (! terms) + terms = new Array (); + terms.push (Xex.Term.Parse (domain, n)); + } } - return terms; -} + return terms; + } +}) (); Xex.Varref = function (vname) { @@ -508,9 +699,9 @@ Xex.Varref = function (vname) proto.Clone = function () { return new Xex.Varref (this.val); } proto.Eval = function (domain) { - if (! this.vari || this.vari.domain != domain) - this.vari = domain.GetVarCreate (this.val); - return this.vari.val; + var vari = domain.GetVarCreate (this.val); + Xex.Log (this.ToString () + '=>' + vari.val, domain.depth); + return vari.val; } proto.Parser = function (domain, node) @@ -518,15 +709,20 @@ Xex.Varref = function (vname) return new Xex.Varref (node.attributes['vname'].nodeValue); } + proto.ToString = function () + { + return ''; + } + Xex.Varref.prototype = proto; }) (); var null_args = new Array (); -Xex.Funcall = function (func, vari, args) +Xex.Funcall = function (func, vname, args) { this.func = func; - this.vari = vari; + this.vname = vname; this.args = args || null_args; }; @@ -539,20 +735,19 @@ Xex.Funcall = function (func, vari, args) var attr; if (fname == 'funcall') - fname = node.attributes['fname'] + fname = node.attributes['fname'].nodeValue; var func = domain.GetFunc (fname); - var vari; + var vname; attr = node.attributes['vname']; - vari = attr != undefined ? domain.GetVarCreate (attr.nodeValue) : false; - var args = Xex.ParseTermList (domain, node.firstChild); - return new Xex.Funcall (func, vari, args); + vname = attr != undefined ? attr.nodeValue : null; + var args = Xex.Term.Parse (domain, node.firstElement (), null); + return new Xex.Funcall (func, vname, args); } proto.New = function (domain, fname, vname, args) { var func = domain.GetFunc (fname); - var vari = vname ? domain.GetVarCreate (vname) : null; - var funcall = new Xex.Funcall (func, vari, args); + var funcall = new Xex.Funcall (func, vname, args); if (func instanceof Xex.Macro) funcall = funcall.Eval (domain); return funcall; @@ -560,7 +755,19 @@ Xex.Funcall = function (func, vari, args) proto.Eval = function (domain) { - return this.func.Call (domain, this.vari, this.args); + Xex.Log (this, domain.depth); + var vari; + if (this.vname) + vari = domain.GetVarCreate (this.vname); + domain.depth++; + var result; + try { + result = this.func.Call (domain, vari, this.args); + } finally { + Xex.Log (' => ' + result, --domain.depth, + this.func instanceof Xex.Subrountine); + } + return result; } proto.Clone = function () @@ -568,11 +775,11 @@ Xex.Funcall = function (func, vari, args) return new Xex.Funcall (this.func, this.vari, this.args); } - proto.Equals = function (obj) + proto.equals = function (obj) { return (obj.type == 'funcall' && obj.func == this.func - && obj.vari.Equals (this.vari) + && obj.vari.equals (this.vari) && obj.args.length == this.func.length); } @@ -580,11 +787,18 @@ Xex.Funcall = function (func, vari, args) { var arglist = '' var len = this.args.length; + var str = '<' + this.func.name; + if (this.vname) + str += ' vname="' + this.vname + '"'; if (len == 0) - return '<' + this.func.name + '/>'; - for (var i = 0; i < len; i++) - arglist += this.args[i].toString (); - return '<' + this.func.name + '>' + arglist + ''; + return str + '/>'; + if (this.func instanceof Xex.Subrountine) + for (var i = 0; i < len; i++) + arglist += this.args[i].toString (); + else + for (var i = 0; i < len; i++) + arglist += '.'; + return str + '>' + arglist + ''; } Xex.Funcall.prototype = proto; @@ -617,7 +831,7 @@ Xex.ErrTerm = function (ename, message, stack) return new Xex.ErrTerm (ename, message, false); } - proto.Equals = function (obj) + proto.equals = function (obj) { return (obj.IsError && obj.ename == ename && obj.message == message @@ -642,6 +856,7 @@ Xex.IntTerm = function (num) { this.val = num; }; (function () { var proto = new Xex.Term ('integer'); proto.IsInt = true; + proto.Intval = function () { return this.val; }; proto.IsTrue = function () { return this.val != 0; } proto.Clone = function () { return new Xex.IntTerm (this.val); } proto.Parser = function (domain, node) @@ -659,11 +874,12 @@ Xex.StrTerm = function (str) { this.val = str; }; (function () { var proto = new Xex.Term ('string'); proto.IsStr = true; + proto.Strval = function () { return this.val; }; proto.IsTrue = function () { return this.val.length > 0; } proto.Clone = function () { return new Xex.StrTerm (this.val); } proto.Parser = function (domain, node) { - return new Xex.StrTerm (node.firstChild.nodeValue); + return new Xex.StrTerm (node.firstChild ? node.firstChild.nodeValue : ''); } Xex.StrTerm.prototype = proto; }) (); @@ -688,20 +904,20 @@ Xex.LstTerm = function (list) { this.val = list; }; proto.IsTrue = function () { return this.val.length > 0; } proto.Clone = function () { return new Xex.LstTerm (this.val.slice (0)); } - proto.Equals = function (obj) + proto.equals = function (obj) { if (obj.type != 'list' || obj.val.length != this.val.length) return false; var i, len = this.val.length; for (i = 0; i < len; i++) - if (! this.val[i].Equals (obj.val[i])) + if (! this.val[i].equals (obj.val[i])) return false; return true; } proto.Parser = function (domain, node) { - var list = Xex.ParseTermList (domain, node.firstChild); + var list = Xex.Term.Parse (domain, node.firstElement (), null); return new Xex.LstTerm (list); } @@ -724,27 +940,128 @@ Xex.LstTerm = function (list) { this.val = list; }; function Fset (domain, vari, args) { - return vari.SetValue (args[0]); + if (! vari) + throw new Xex.ErrTerm (Xex.Error.NoVariableName, + 'No variable name to set'); + vari.SetValue (args[0]); + return args[0]; + } + + function Fnot (domain, vari, args) + { + return (args[0].IsTrue () ? Xex.Zero : Xex.One); } function maybe_set_intvar (vari, n) { var term = new Xex.IntTerm (n); - if (vari != null) + if (vari) vari.SetValue (term); return term; } function Fadd (domain, vari, args) { - var n = vari ? vari.val.val : 0; + var n = vari ? vari.val.Intval () : 0; var len = args.length; for (var i = 0; i < len; i++) - n += args[i].val; + n += args[i].Intval (); + return maybe_set_intvar (vari, n); + } + + function Fmul (domain, vari, args) + { + var n = vari ? vari.val.Intval () : 1; + for (var i = 0; i < args.length; i++) + n *= args[i].Intval (); + return maybe_set_intvar (vari, n); + } + + function Fsub (domain, vari, args) + { + var n, i; + + if (! vari) + { + n = args[0].Intval (); + i = 1; + } + else + { + n = vari.val.Intval (); + i = 0; + } + while (i < args.length) + n -= args[i++].Intval (); + return maybe_set_intvar (vari, n); + } + + function Fdiv (domain, vari, args) + { + var n, i; + + if (! vari == null) + { + n = args[0].Intval (); + i = 1; + } + else + { + n = vari.val.Intval (); + i = 0; + } + while (i < args.length) + n /= args[i++].Intval (); + return maybe_set_intvar (vari, n); + } + + function Fmod (domain, vari, args) + { + return maybe_set_intvar (vari, args[0].Intval () % args[1].Intval ()); + } + + function Flogior (domain, vari, args) + { + var n = vari == null ? 0 : vari.val; + for (var i = 0; i < args.length; i++) + n |= args[i].val; + return maybe_set_intvar (vari, n); + } + + function Flogand (domain, vari, args) + { + var n, i; + if (vari == null) + { + Xex.Log ("logand arg args[0]" + args[0]); + n = args[0].Intval () + i = 1; + } + else + { + Xex.Log ("logand arg var " + vari); + n = vari.val.Intval (); + i = 0; + } + while (n > 0 && i < args.length) + { + Xex.Log ("logand arg " + args[i]); + n &= args[i++].val; + } return maybe_set_intvar (vari, n); } + function Flsh (domain, vari, args) + { + return maybe_set_intvar (vari, args[0].Intval () << args[1].Intval ()); + } + + function Frsh (domain, vari, args) + { + return maybe_set_intvar (vari, args[0].Intval () >> args[1].Intval ()); + } + function Fand (domain, vari, args) { var len = args.length; @@ -753,7 +1070,7 @@ Xex.LstTerm = function (list) { this.val = list; }; var result = args[i].Eval (domain); if (domain.Thrown ()) return result; - if (! result.IsTrue) + if (! result.IsTrue ()) return Xex.Zero; } return Xex.One; @@ -767,12 +1084,78 @@ Xex.LstTerm = function (list) { this.val = list; }; var result = args[i].Eval (domain); if (domain.Thrown ()) return result; - if (result.IsTrue) + if (result.IsTrue ()) return Xex.One; } return Xex.Zero; } + function Feq (domain, vari, args) + { + for (var i = 1; i < args.length; i++) + if (! args[i - 1].equals (args[i])) + return Xex.Zero; + return Xex.One; + } + + function Fnoteq (domain, vari, args) + { + return (Feq (domain, vari, args) == Xex.One ? Xex.Zero : Xex.One); + } + + function Flt (domain, vari, args) + { + var n = args[0].Intval (); + + for (var i = 1; i < args.length; i++) + { + var n1 = args[i].Intval (); + if (n >= n1) + return Xex.Zero; + n = n1; + } + return Xex.One; + } + + function Fle (domain, vari, args) + { + var n = args[0].Intval (); + for (var i = 1; i < args.length; i++) + { + var n1 = args[i].Intval (); + if (n > n1) + return Xex.Zero; + n = n1; + } + return Xex.One; + } + + function Fgt (domain, vari, args) + { + var n = args[0].Intval (); + for (var i = 1; i < args.length; i++) + { + var n1 = args[i].Intval (); + if (n <= n1) + return Xex.Zero; + n = n1; + } + return Xex.One; + } + + function Fge (domain, vari, args) + { + var n = args[0].Intval (); + for (var i = 1; i < args.length; i++) + { + var n1 = args[i].Intval (); + if (n < n1) + return Xex.Zero; + n = n1; + } + return Xex.One; + } + function Fprogn (domain, vari, args) { var result = Xex.One; @@ -793,13 +1176,35 @@ Xex.LstTerm = function (list) { this.val = list; }; if (domain.Thrown ()) return result; - if (result.IsTrue) + if (result.IsTrue ()) return args[1].Eval (domain); - if (args.Length == 2) - return Zero; + if (args.length == 2) + return Xex.Zero; return args[2].Eval (domain); } + function Fcond (domain, vari, args) + { + for (var i = 0; i < args.length; i++) + { + var list = args[i]; + var result = list.val[0].Eval (domain); + if (result.IsTrue ()) + { + for (var j = 1; j < list.val.length; j++) + { + domain.depth++; + result = list.val[j].Eval (domain); + domain.depth--; + if (domain.Thrown ()) + return result; + } + return result; + } + } + return Xex.Zero; + } + function eval_terms (domain, terms, idx) { var result = Xex.Zero; @@ -871,14 +1276,65 @@ Xex.LstTerm = function (list) { this.val = list; }; Xex.BasicDomain = basic; basic.DefSubr (Fset, "set", true, 1, 1); + basic.DefAlias ("=", "set"); + basic.DefSubr (Fnot, "not", false, 1, 1); + basic.DefAlias ("!", "not"); basic.DefSubr (Fadd, "add", true, 1, -1); + basic.DefSubr (Fmul, "mul", true, 1, -1); + basic.DefAlias ("*", "mul"); + basic.DefSubr (Fsub, "sub", true, 1, -1); + basic.DefAlias ("-", "sub"); + basic.DefSubr (Fdiv, "div", true, 1, -1); + basic.DefAlias ("/", "div"); + basic.DefSubr (Fmod, "mod", true, 1, 2); + basic.DefAlias ("%", "mod"); + basic.DefSubr (Flogior, "logior", true, 1, -1); + basic.DefAlias ('|', "logior"); + basic.DefSubr (Flogand, "logand", true, 1, -1); + basic.DefAlias ("&", "logand"); + basic.DefSubr (Flsh, "lsh", true, 1, 2); + basic.DefAlias ("<<", "lsh"); + basic.DefSubr (Frsh, "rsh", true, 1, 2); + basic.DefAlias (">>", "rsh"); + basic.DefSubr (Feq, "eq", false, 2, -1); + basic.DefAlias ("==", "eq"); + basic.DefSubr (Fnoteq, "noteq", false, 2, 2); + basic.DefAlias ("!=", "noteq"); + basic.DefSubr (Flt, "lt", false, 2, -1); + basic.DefAlias ("<", "lt"); + basic.DefSubr (Fle, "le", false, 2, -1); + basic.DefAlias ("<=", "le"); + basic.DefSubr (Fgt, "gt", false, 2, -1); + basic.DefAlias (">", "gt"); + basic.DefSubr (Fge, "ge", false, 2, -1); + basic.DefAlias (">=", "ge"); basic.DefSubr (Fthrow, "throw", false, 1, 2); + //basic.DefSubr (Fappend, "append", true, 0, -1); + //basic.DefSubr (Fconcat, "concat", true, 0, -1); + //basic.DefSubr (Fnth, "nth", false, 2, 2); + //basic.DefSubr (Fcopy, "copy", false, 1, 1); + //basic.DefSubr (Fins, "ins", true, 2, 2); + //basic.DefSubr (Fdel, "del", true, 2, 2); + //basic.DefSubr (Feval, "eval", false, 1, 1); + //basic.DefSubr (Fbreak, "break", false, 0, 1); + //basic.DefSubr (Freturn, "return", false, 0, 1); + //basic.DefSubr (Fthrow, "throw", false, 1, 2); + basic.DefSpecial (Fand, "and", false, 1, -1); + basic.DefAlias ("&&", "and"); basic.DefSpecial (For, "or", false, 1, -1); - basic.DefAlias ("=", "set"); + basic.DefAlias ("||", "or"); basic.DefSpecial (Fprogn, "progn", false, 1, -1); + basic.DefAlias ("expr", "progn"); basic.DefSpecial (Fif, "if", false, 2, 3); + //basic.DefSpecial (Fwhen, "when", false, 1, -1); + //basic.DefSpecial (Floop, "loop", false, 1, -1); + //basic.DefSpecial (Fwhile, "while", false, 1, -1); + basic.DefSpecial (Fcond, "cond", false, 1, -1); + //basic.DefSpecial (Fforeach, "foreach", true, 2, -1); + //basic.DefSpecial (Fquote, "quote", false, 1, 1); + //basic.DefSpecial (Ftype, "type", false, 1, 1); basic.DefSpecial (Fcatch, "catch", true, 2, -1); basic.DefType (Xex.Funcall.prototype); @@ -893,6 +1349,7 @@ Xex.LstTerm = function (list) { this.val = list; }; Xex.Zero = new Xex.IntTerm (0); Xex.One = new Xex.IntTerm (1); +Xex.nil = new Xex.SymTerm ('nil'); Xex.Load = function (server, file) { @@ -901,1331 +1358,31 @@ Xex.Load = function (server, file) obj.open ('GET', url, false); obj.overrideMimeType ('text/xml'); obj.send (''); - return obj.responseXML.firstChild; -} + return (obj.responseXML && obj.responseXML.firstChild); +}; -var MIM = { - // URL of the input method server. - server: "http://www.m17n.org/common/mim-js", - // Boolean flag to tell if MIM is active or not. - enabled: true, - // Boolean flag to tell if MIM is running in debug mode or not. - debug: false, - // List of registered input methods. - im_list: {}, - // Global input method data - im_global: null, - // Currently selected input method. - current: false, - - // enum - LoadStatus: { NotLoaded:0, Loading:1, Loaded:2, Error:-1 }, - ChangedStatus: { - None: 0x00, - StateTitle: 0x01, - PreeditText:0x02, - CursorPos: 0x04, - CandidateList:0x08, - CandidateIndex:0x10, - CandidateShow:0x20, - Preedit: 0x06, // PreeditText | CursorPos - Candidate: 0x38 // CandidateList | CandidateIndex | CandidateShow - }, - KeyModifier: { - SL: 0x00400000, - SR: 0x00800000, - S: 0x00C00000, - CL: 0x01000000, - CR: 0x02000000, - C: 0x03000000, - AL: 0x04000000, - AR: 0x08000000, - A: 0x0C000000, - ML: 0x04000000, - MR: 0x08000000, - M: 0x0C000000, - G: 0x10000000, - s: 0x20000000, - H: 0x40000000, - High: 0x70000000, - All: 0x7FC00000 - }, - Error: { - ParseError: "parse-error" +(function () { + function getxml (event) + { + var parser = new DOMParser (); + Xex.xml = parser.parseFromString (event.data, 'text/xml'); + }; + + Xex.LoadTesting = function (server, file) + { + var body = document.getElementsByTagName ('body')[0]; + Xex.xml = undefined; + window.addEventListener ('message', getxml, false); + var iframe = document.createElement ('iframe'); + iframe.src = server + '/loadxml.html#' + file; + alert ('iframe created'); + body.appendChild (iframe); +/* + while (! Xex.xml) + alert ('wait loading ' + file); + window.removeEventListener ('message', getxml, false); + body.removeChild (iframe); + return (Xex.xml.firstChild); +*/ } -}; - -(function () { - var keysyms = new Array (); - keysyms["bs"] = "backspace"; - keysyms["lf"] = "linefeed"; - keysyms["cr"] = keysyms["enter"] = "return"; - keysyms["esc"] = "escape"; - keysyms["spc"] = "space"; - keysyms["del"] = "delete"; - - function decode_keysym (str) { - var parts = str.split ("-"); - var len = parts.length, i; - var has_modifier = len > 1; - - for (i = 0; i < len - 1; i++) - if (! MIM.KeyModifier.hasOwnProperty (parts[i])) - return false; - var key = parts[len - 1]; - if (key.length > 1) - { - key = keysyms[key.toLowerCase ()]; - if (key) - { - if (len > 1) - { - str = parts[0]; - for (i = 1; i < len - 1; i++) - str += '-' + parts[i]; - str += '-' + key; - } - else - str = key; - } - } - if (has_modifier) - { - parts = new Array (); - parts.push (str); - return parts; - } - return str; - } - - MIM.Key = function (val) - { - this.key; - this.has_modifier = false; - if (typeof val == 'string' || val instanceof String) - { - this.key = decode_keysym (val); - if (! this.key) - throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + val); - if (this.key instanceof Array) - { - this.key = this.key[0]; - this.has_modifier = true; - } - } - else if (typeof val == 'number' || val instanceof Number) - this.key = String.fromCharCode (val); - else - throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + val); - } - - MIM.Key.prototype.toString = function () { return this.key; }; -}) (); - -(function () { - MIM.KeySeq = function (seq) - { - this.val = new Array (); - this.has_modifier = false; - - if (seq) - { - if (seq.IsList) - { - var len = seq.val.length; - for (var i = 0; i < len; i++) - { - var v = seq.val[i]; - if (v.type != 'string' && v.type != 'integer' - && v.type != 'symbol') - throw new Xex.ErrTerm (MIM.Error.ParseError, - "Invalid key: " + v); - var key = new MIM.Key (v.val); - this.val.push (key); - if (key.has_modifier) - this.has_modifier = true; - } - } - else if (seq.IsStr) - { - var len = seq.val.length; - for (var i = 0; i < len; i++) - this.val.push (new MIM.Key (seq.val.charCodeAt (i))); - } - else - throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid key: " + seq); - } - } - - var proto = new Xex.Term ('keyseq'); - proto.Clone = function () { return this; } - proto.Parser = function (domain, node) - { - var seq = new Array (); - for (node = node.firstChild; node; node = node.nextSibling) - if (node.nodeType == 1) - { - var term = Xex.ParseTerm (domain, node); - return new MIM.KeySeq (term); - } - throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid keyseq"); - } - proto.toString = function () - { - var len = this.val.length; - if (len == 0) - return ''; - var first = true; - var str = ''; - for (var i = 0; i < len; i++) - { - if (first) - first = false; - else if (this.has_modifier) - str += ' '; - str += this.val[i].toString (); - } - return str + ''; - } - - MIM.KeySeq.prototype = proto; -}) (); - -(function () { - MIM.Marker = function () { } - MIM.Marker.prototype = new Xex.Term ('marker'); - MIM.Marker.prototype.CharAt = function (ic) - { - var p = this.Position (ic); - if (p < 0) - return ic.GetSurroundingChar (p); - else if (pos >= ic.preedit.length) - return ic.GetSurroundingChar (p - ic.preedit.length); - return ic.preedit.charCodeAt (p); - } - - MIM.NamedMarker = function (name) { this.val = name; } - MIM.NamedMarker.prototype = new MIM.Marker (); - MIM.NamedMarker.prototype.Position = function (ic) - { - var p = ic.marker_positions[this.val]; - return (p == undefined ? 0 : p); - } - MIM.NamedMarker.prototype.Mark = function (ic) - { - ic.marker_positions[this.val] = ic.cursor_pos; - } - - MIM.PredefinedMarker = function (name) { this.val = name; } - MIM.PredefinedMarker.prototype = new MIM.Marker (); - MIM.PredefinedMarker.prototype.Position = function (ic) - { - if (typeof this.pos == 'number') - return this.pos; - return this.pos (ic); - } - - var predefined = { } - - function def_predefined (name, position) - { - predefined[name] = new MIM.PredefinedMarker (name); - predefined[name].pos = position; - } - - def_predefined ('@<', 0); - def_predefined ('@>', function (ic) { return ic.preedit.length; }); - def_predefined ('@-', function (ic) { return ic.cursor_pos - 1; }); - def_predefined ('@+', function (ic) { return ic.cursor_pos + 1; }); - def_predefined ('@[', function (ic) { - if (ic.cursor_pos > 0) - { - var pos = ic.cursor_pos; - return ic.preedit.FindProp ('candidates', pos - 1).from; - } - return 0; - }); - def_predefined ('@]', function (ic) { - if (ic.cursor_pos < ic.preedit.length - 1) - { - var pos = ic.cursor_pos; - return ic.preedit.FindProp ('candidates', pos).to; - } - return ic.preedit.length; - }); - for (var i = 0; i < 10; i++) - def_predefined ("@" + i, i); - predefined['@first'] = predefined['@<']; - predefined['@last'] = predefined['@>']; - predefined['@previous'] = predefined['@-']; - predefined['@next'] = predefined['@+']; - predefined['@previous-candidate-change'] = predefined['@[']; - predefined['@next-candidate-change'] = predefined['@]']; - - MIM.SurroundMarker = function (name) - { - this.val = name; - this.distance = parseInt (name.slice (2)); - if (isNaN (this.distance)) - throw new Xex.ErrTerm (MIM.Error.ParseError, "Invalid marker: " + name); - } - MIM.SurroundMarker.prototype = new MIM.Marker (); - MIM.SurroundMarker.prototype.Position = function (ic) - { - return ic.cursor_pos + this.distance; - } - - MIM.Marker.prototype.Parser = function (domain, node) - { - var name = node.firstChild.nodeValue; - if (name.charAt (0) == '@') - { - var n = predefined[name]; - if (n) - return n; - if (name.charAt (1) == '-') - return new MIM.SurroundMarker (name); - throw new Xex.ErrTerm (MIM.Error.ParseError, - "Invalid marker: " + name); - } - return new MIM.NamedMarker (name); - } -}) (); - -MIM.Selector = function (name) -{ - this.val = name; -} -MIM.Selector.prototype = new Xex.Term ('selector'); -(function () { - var selectors = {}; - selectors["@<"] = selectors["@first"] = new MIM.Selector ('@<'); - selectors["@="] = selectors["@current"] = new MIM.Selector ('@='); - selectors["@>"] = selectors["@last"] = new MIM.Selector ('@>'); - selectors["@-"] = selectors["@previous"] = new MIM.Selector ('@-'); - selectors["@+"] = selectors["@next"] = new MIM.Selector ('@+'); - selectors["@["] = selectors["@previous-candidate-change"] - = new MIM.Selector ('@['); - selectors["@]"] = selectors["@next-candidate-change"] - = new MIM.Selector ('@]'); - - MIM.Selector.prototype.Parser = function (domain, node) - { - var name = node.firstChild.nodeValue; - var s = selectors[name]; - if (! s) - throw new Xex.ErrTerm (MIM.Error.ParseError, - "Invalid selector: " + name); - return s; - } -}) (); - -MIM.Rule = function (keyseq, actions) -{ - this.keyseq = keyseq; - this.actions = actions; -} -MIM.Rule.prototype = new Xex.Term ('rule'); -MIM.Rule.prototype.Parser = function (domain, node) -{ - var n; - for (n = node.firstChild; n && n.nodeType != 1; n = n.nextSibling); - if (! n) - throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid rule:" + node); - var keyseq = Xex.ParseTerm (domain, n); - if (keyseq.type != 'keyseq') - throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid rule:" + node); - var actions = Xex.ParseTermList (domain, n.nextSibling); - return new MIM.Rule (keyseq, actions); -} -MIM.Rule.prototype.toString = function () -{ - return ''; -} - -MIM.Map = function (name) -{ - this.name = name; - this.rules = new Array (); -}; -(function () { - var proto = new Xex.Term ('map'); - - proto.Parser = function (domain, node) - { - var name = node.attributes['mname'].nodeValue; - if (! name) - throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid map"); - var map = new MIM.Map (name); - for (var n = node.firstChild; n; n = n.nextSibling) - if (n.nodeType == 1) - map.rules.push (Xex.ParseTerm (domain, n)); - return map; - } - - proto.toString = function () - { - var str = ''; - var len = this.rules.length; - for (i = 0; i < len; i++) - str += this.rules[i]; - return str + ''; - } - - MIM.Map.prototype = proto; -}) (); - -Xex.CatchTag._mimtag = new Xex.SymTerm ('@mimtag'); - -MIM.Action = function (domain, terms) -{ - var args = new Array (); - args.push (Xex.CatchTag_.mimtag); - for (var i = 0; i < terms.length; i++) - args.push (terms[i]); - this.action = Xex.Funcall.prototype.New (domain, 'catch', null, args); -} - -MIM.Action.prototype.Run = function (domain) -{ - var result = this.action.Eval (domain); - if (result.type == 'error') - { - domain.context.Error = result.toString (); - return false; - } - return (result != Xex.CatchTag._mimtag); -} - -MIM.Keymap = function () -{ - this.submaps = null; - this.actions = null; -}; -(function () { - var proto = {}; - - function add_rule (keymap, rule) - { - var keyseq = rule.keyseq; - var len = keyseq.val.length; - - for (var i = 0; i < len; i++) - { - var key = keyseq.val[i]; - var sub = false; - - if (! keymap.submaps) - keymap.submaps = {}; - else - sub = keymap.submaps[key.key]; - if (! sub) - keymap.submaps[key.key] = sub = new MIM.Keymap (); - keymap = sub; - } - keymap.actions = rule.actions; - } - - proto.Add = function (map) - { - var rules = map.rules; - var len = rules.length; - - for (var i = 0; i < len; i++) - add_rule (this, rules[i]); - } - proto.Lookup = function (keys, index) - { - var sub; - - if (index < keys.val.length && this.submaps - && (sub = this.submaps[keys.val[index].key])) - { - index++; - return sub.Lookup (keys, index); - } - return { map: this, index: index }; - } - - MIM.Keymap.prototype = proto; -}) (); - -MIM.State = function (name) -{ - this.name = name; - this.keymap = new MIM.Keymap (); -}; -(function () { - var proto = new Xex.Term ('state'); - - proto.Parser = function (domain, node) - { - var map_list = domain.map_list; - var name = node.attributes['sname'].nodeValue; - if (! name) - throw new Xex.ErrTerm (MIM.Error.ParseError, "invalid map"); - var state = new MIM.State (name); - for (node = node.firstChild; node; node = node.nextSibling) - { - if (node.nodeType != 1) - continue; - if (node.nodeName == 'branch') - { - state.keymap.Add (map_list[node.attributes['mname'].nodeValue]); - state.keymap.actions = Xex.ParseTermList (domain, node.firstChild); - } - else if (node.nodeName == 'state-hook') - state.enter_actions = Xex.ParseTermList (domain, node.firstChild); - else if (node.nodeName == 'catch-all-branch') - state.fallback_actions = Xex.ParseTermList (domain, node.firstChild); - else if (node.nodeName == 'title') - state.title = node.firstChild.nodeValue; - } - return state; - } - - proto.toString = function () - { - return '' + this.keymap + ''; - } - - MIM.State.prototype = proto; -}) (); - -MIM.im_domain = new Xex.Domain ('input-method', null, null); -MIM.im_domain.DefType (MIM.KeySeq.prototype); -MIM.im_domain.DefType (MIM.Marker.prototype); -MIM.im_domain.DefType (MIM.Selector.prototype); -MIM.im_domain.DefType (MIM.Rule.prototype); -MIM.im_domain.DefType (MIM.Map.prototype); -MIM.im_domain.DefType (MIM.State.prototype); - -(function () { - var im_domain = MIM.im_domain; - - function Finsert (domain, vari, args) - { - domain.context.insert (args[0].val, null); - } - - im_domain.DefSubr (Finsert, "insert", false, 1, 1); -}) (); - -(function () { - var parsers = { }; - parsers['description'] = function (node) - { - this.description = node.firstChild.nodeValue; - } - parsers['title'] = function (node) - { - this.title = node.firstChild.nodeValue; - } - parsers['map-list'] = function (node) - { - for (node = node.firstChild; node; node = node.nextSibling) - { - if (node.nodeType != 1 || node.nodeName != 'map') - continue; - var map = Xex.ParseTerm (this.domain, node); - this.map_list[map.name] = map; - } - } - parsers['state-list'] = function (node) - { - this.domain.map_list = this.map_list; - for (node = node.firstChild; node; node = node.nextSibling) - { - if (node.nodeType != 1 || node.nodeName != 'state') - continue; - var state = Xex.ParseTerm (this.domain, node); - if (! state.title) - state.title = this.title; - if (! this.initial_state) - this.initial_state = state; - this.state_list[state.name] = state; - } - delete this.domain.map_list; - } - - MIM.IM = function (lang, name, extra_id, file) - { - this.lang = lang; - this.name = name; - this.extra_id = extra_id; - this.file = file; - this.load_status = MIM.LoadStatus.NotLoaded; - this.domain = new Xex.Domain (this.lang + '-' + this.name, - MIM.im_domain, null); - } - - var proto = { - Load: function () - { - var node = Xex.Load (null, this.file); - if (! node) - { - this.load_status = MIM.LoadStatus.Error; - return false; - } - this.map_list = {}; - this.initial_state = null; - this.state_list = {}; - for (node = node.firstChild; node; node = node.nextSibling) - { - if (node.nodeType != 1) - continue; - var name = node.nodeName; - var parser = parsers[name]; - if (parser) - parser.call (this, node); - } - this.load_status = MIM.LoadStatus.Loaded; - return true; - } - } - - MIM.IM.prototype = proto; - - MIM.IC = function (im) - { - if (im.load_status == MIM.LoadStatus.NotLoaded) - im.Load (); - if (im.load_status != MIM.LoadStatus.Loaded) - alert ('im:' + im.name + ' error:' + im.load_status); - this.im = im; - this.domain = new Xex.Domain ('context', im.domain, this); - this.active = true; - this.reset (); - this.spot = 0; - } - - MIM.CandidateTable = function () - { - this.table = new Array (); - } - - MIM.CandidateTable.prototype.get = function (from) - { - for (var i = 0; i < this.table.length; i++) - { - var elt = this.table[i]; - if (elt.from <= from && elt.to > from) - return elt.val; - } - } - - MIM.CandidateTable.prototype.put = function (from, to, candidates) - { - for (var i = 0; i < this.table.length; i++) - { - var elt = this.table[i]; - if (elt.from >= from && elt.from < to - || elt.to >= from && elt.to < to) - { - elt.from = from; - elt.to = to; - elt.val = candidates; - return; - } - } - this.table.push ({ from: from, to: to, val: candidates }); - } - - MIM.CandidateTable.prototype.adjust = function (from, to, inserted) - { - var diff = inserted - (to - from); - for (var i = 0; i < this.table.length; i++) - { - var elt = this.table[i]; - if (elt.from >= to) - { - elt.from += diff; - elt.to += diff; - } - } - } - - MIM.CandidateTable.prototype.clear = function () - { - this.table.length = 0; - } - - function set_cursor (prefix, pos) - { - this.cursor_pos = pos; - if (pos > 0) - this.candidates = this.candidate_table.get (pos - 1); - else - this.candidates = null; - } - - function save_state () - { - this.state_var_values = this.domain.SaveValues (); - this.state_preedit = this.preedit; - this.state_key_head = this.key_head; - this.state_pos = this.cursor_pos; - } - - function restore_state () - { - this.domain.RestoreValues (this.state_var_values); - this.preedit = this.state_preedit; - set_cursor.call (this, "restore", this.state_pos); - } - - function handle_key () - { - var out = this.keymap.Lookup (this.keys, this.key_head); - var sub = out.map; - var branch_actions = this.state.keymap.actions; - - MIM.log ('handling ' + this.keys.val[this.key_head] - + ' in ' + this.state.name); - this.key_head = out.index; - if (sub != this.keymap) - { - - restore_state.call (this); - this.keymap = sub; - MIM.log ('submap found'); - if (this.keymap.actions != null) - { - MIM.log ('taking map actions:'); - if (! this.take_actions (this.keymap.actions)) - return false; - } - else if (this.keymap.submaps != null) - { - MIM.log ('no map actions, inserting key:'); - for (var i = this.state_key_head; i < this.key_head; i++) - this.preedit_replace (this.cursor_pos, this.cursor_pos, - this.keys.val[i].key, null); - } - if (this.keymap.submaps == null) - { - MIM.log ('terminal:'); - if (this.keymap.branch_actions != null) - { - MIM.log ('branch actions:'); - if (! this.take_actions (branch_actions)) - return false; - } - if (this.keymap != this.state.keymap) - this.shift (this.state); - } - } - else - { - MIM.log ("no submap"); - var current_state = this.state; - - if (branch_actions) - { - MIM.log ("branch actions"); - if (! this.take_actions (this.keymap.branch_actions)) - return false; - } - if (this.state == current_state) - { - if (this.state == this.initial_state - && this.key_head < this.keys.val.length) - return false; - if (this.keymap != this.state.keymap) - this.shift (this.state); - else if (this.keymap.branch_actions == null) - this.shift (this.initial_state); - } - } - return true; - } - - proto = { - init: function () - { - this.produced = null; - this.preedit = ''; - this.cursor_pos = 0; - this.marker_positions = {}; - this.candidates = null; - this.candidate_show = false; - this.state = null; - this.prev_state = null; - this.initial_state = this.im.initial_state; - this.title = this.initial_state.title; - this.state_preedit = ''; - this.state_key_head = 0; - this.state_var_values = {}; - this.state_pos = 0; - this.keymap = null; - this.keys = new MIM.KeySeq (); - this.key_head = 0; - this.key_unhandled = false; - this.unhandled_key = null; - this.changed = MIM.ChangedStatus.None; - this.error_message = ''; - this.title = this.initial_state.title; - this.produced = null; - this.preedit = ''; - this.marker_positions = {}; - this.candidate_table = new MIM.CandidateTable (); - this.candidates = null; - this.candidate_show = false; - }, - - reset: function () - { - this.init (); - this.state_var_values = {}; - this.shift (this.initial_state); - }, - - catch_args: new Array (Xex.CatchTag._mimtag, null), - - take_actions: function (actions) - { - var func_progn = this.domain.GetFunc ('progn'); - var func_catch = this.domain.GetFunc ('catch'); - this.catch_args[1] = new Xex.Funcall (func_progn, null, actions); - var term = new Xex.Funcall (func_catch, null, this.catch_args); - term = term.Eval (this.domain); - return (! term.IsSymbol || term.val != '@mimtag'); - }, - - GetSurroundingChar: function (pos) - { - if (pos < 0 ? this.caret_pos < - pos : this.target.value.length < pos) - return 0; - return this.target.value.charCodeAt (this.caret_pos + pos); - }, - - adjust_markers: function (from, to, inserted) - { - var diff = inserted - (to - from); - - for (var m in this.marker_positions) - if (this.marker_positions[m] > from) - this.marker_positions[m] = (this.marker_positions[m] >= to - ? pos + diff : from); - if (this.cursor_pos >= to) - set_cursor.call (this, 'adjust', this.cursor_pos + diff); - else if (this.cursor_pos > from) - set_cursor.call (this, 'adjust', from) - }, - - preedit_replace: function (from, to, text, candidates) - { - this.preedit = (this.preedit.substring (0, from) - + text + this.preedit.substring (to)); - this.adjust_markers (from, to, text.length); - this.candidate_table.adjust (from, to, text.length); - if (candidates) - this.candidate_table.put (from, from + text.length, candidates) - }, - - insert: function (text, candidates) - { - this.preedit_replace (this.cursor_pos, this.cursor_pos, text, candidates); - this.changed = MIM.ChangedStatus.Preedit | MIM.ChangedStatus.CursorPos; - }, - - del: function (pos) - { - if (pos < 0) - { - this.DelSurroundText (pos); - pos = 0; - } - else if (pos > this.preedit.length) - { - this.DelSurroundText (pos - this.preedit.length); - pos = this.preedit.length; - } - if (pos < this.cursor_pos) - this.preedit = (this.predit.substring (0, pos) - + this.preedit.substring (this.cursor_pos)); - else - this.preedit = (this.preedit.substring (0, this.cursor_pos) - + this.predit.substring (pos)); - }, - - show: function () - { - this.candidate_show = true; - this.changed |= MIM.ChangedStatus.CandidateShow; - }, - - hide: function () - { - this.candidate_show = false; - this.changed |= MIM.ChangedStatus.CandidateShow; - }, - - move: function (pos) - { - if (pos < 0) - pos = 0; - else if (pos > this.preedit.length) - pos = this.preedit.length; - if (pos != this.cursor_pos) - { - set_cursor.call (this, 'move', pos); - this.changed |= MIM.ChangedStatus.Preedit; - } - }, - - pushback: function (n) - { - if (n instanceof MIM.KeySeq) - { - if (this.key_head > 0) - this.key_head--; - if (this.key_head < this.keys.val.length) - this.keys.val.splice (this.key_head, - this.keys.val.length - this.key_head); - for (var i = 0; i < n.val.length; i++) - this.keys.val.push (n.val[i]); - return; - } - if (n > 0) - { - this.key_head -= n; - if (this.key_head < 0) - this.key_head = 0; - } - else if (n == 0) - this.key_head = 0; - else - { - this.key_head = - n; - if (this.key_head > this.keys.val.length) - this.key_head = this.keys.val.length; - } - }, - - pop: function () - { - if (this.key_head < this.keys.val.length) - this.keys.val.splice (this.key_head, 1); - }, - - commit: function () - { - if (this.preedit.length > 0) - { - this.candidate_table.clear (); - this.produced += this.preedit; - this.preedit_replace.call (this, 0, this.preedit.Length, '', null); - } - }, - - shift: function (state) - { - if (state == null) - { - MIM.log ("shifting back to previous"); - if (this.prev_state == null) - return; - state = this.prev_state; - } - else - MIM.log ("shifting to " + state.name); - - if (state == this.initial_state) - { - this.commit (); - this.keys.val.splice (0, this.key_head); - this.key_head = 0; - if (state != this.state) - { - this.domain.RestoreValues (this.state_initial_var_values); - if (state.enter_actions != null) - take_actions.call (state.enter_actions); - } - this.prev_state = null; - } - else - { - if (state != this.state && state.enter_actions != null) - take_actions.call (state.enter_actions); - this.prev_state = this.state; - } - save_state.call (this); - if (! this.state || this.state.title != state.title) - this.changed |= MIM.ChangedStatus.StateTitle; - this.state = state; - this.keymap = state.keymap; - }, - - Filter: function (key) - { - if (! this.active) - { - this.key_unhandled = true; - this.unhandled_key = key; - return false; - } - if (key.key == '_reload') - return true; - this.changed = MIM.ChangedStatus.None; - this.produced = ''; - this.key_unhandled = false; - this.keys.val.push (key); - var count = 0; - while (this.key_head < this.keys.val.length) - { - if (! handle_key.call (this)) - { - this.unhandled_key = this.keys.val[this.key_head++]; - this.key_unhandled = true; - break; - } - if (++count == 10) - break; - } - this.keys.val.splice (0, this.key_head); - this.key_head = 0; - return (! this.key_unhandled && this.produced.length == 0); - } - } - - MIM.IC.prototype = proto; - - var node = Xex.Load (null, "imlist.xml"); - for (node = node.firstChild; node; node = node.nextSibling) - if (node.nodeName == 'input-method') - { - var lang, name, extra_id, file; - - for (var n = node.firstChild; n; n = n.nextSibling) - { - if (n.nodeName == 'language') - lang = n.firstChild.nodeValue; - else if (n.nodeName == 'name') - name = n.firstChild.nodeValue; - else if (n.nodeName == 'extra-id') - extra_id = n.firstChild.nodeValue; - else if (n.nodeName == 'filename') - file = n.firstChild.nodeValue; - } - if (! MIM.im_list[lang]) - MIM.im_list[lang] = {}; - MIM.im_list[lang][name] = new MIM.IM (lang, name, extra_id, file); - } - node = undefined; }) (); - -(function () { - var keys = new Array (); - keys[0x09] = 'tab'; - keys[0x08] = 'backspace'; - keys[0x0D] = 'return'; - keys[0x1B] = 'escape'; - keys[0x20] = 'space'; - keys[0x21] = 'pageup'; - keys[0x22] = 'pagedown'; - keys[0x23] = 'end'; - keys[0x24] = 'home'; - keys[0x25] = 'left'; - keys[0x26] = 'up'; - keys[0x27] = 'right'; - keys[0x28] = 'down'; - keys[0x2D] = 'insert'; - keys[0x2E] = 'delete'; - for (var i = 1; i <= 12; i++) - keys[111 + i] = "f" + i; - keys[0x90] = "numlock"; - keys[0xF0] = "capslock"; - - MIM.decode_key_event = function (event) - { - var key = ((event.type == 'keydown' || event.keyCode) ? event.keyCode - : event.charCode ? event.charCode - : false); - if (! key) - return false; - if (event.type == 'keydown') - { - key = keys[key]; - if (! key) - return false; - if (event.shiftKey) key = "S-" + key ; - } - else - key = String.fromCharCode (key); - if (event.altKey) key = "A-" + key ; - if (event.ctrlKey) key = "C-" + key ; - return new MIM.Key (key); - } -}) (); - -MIM.add_event_listener - = (window.addEventListener - ? function (target, type, listener) { - target.addEventListener (type, listener, false); - } - : window.attachEvent - ? function (target, type, listener) { - target.attachEvent ('on' + type, - function() { - listener.call (target, window.event); - }); - } - : function (target, type, listener) { - target['on' + type] - = function (e) { listener.call (target, e || window.event); }; - }); - -MIM.log = function (msg) -{ - var node = document.getElementById ('log'); - node.value += msg + "\n"; - var len = node.value.length; - node.setSelectionRange (len, len); -} - -MIM.debug_print = function (event, ic) -{ - if (! MIM.debug) - return; - if (! MIM.debug_nodes) - { - MIM.debug_nodes = new Array (); - MIM.debug_nodes['keydown'] = document.getElementById ('keydown'); - MIM.debug_nodes['keypress'] = document.getElementById ('keypress'); - MIM.debug_nodes['status0'] = document.getElementById ('status0'); - MIM.debug_nodes['status1'] = document.getElementById ('status1'); - MIM.debug_nodes['keyseq0'] = document.getElementById ('keyseq0'); - MIM.debug_nodes['keyseq1'] = document.getElementById ('keyseq1'); - MIM.debug_nodes['preedit0'] = document.getElementById ('preedit0'); - MIM.debug_nodes['preedit1'] = document.getElementById ('preedit1'); - } - var target = event.target; - var code = event.keyCode; - var ch = event.type == 'keydown' ? 0 : event.charCode; - var key = MIM.decode_key_event (event); - var index; - - MIM.debug_nodes[event.type].innerHTML = "" + code + "/" + ch + " : " + key; - index = (event.type == 'keydown' ? '0' : '1'); - if (ic) - MIM.debug_nodes['status' + index].innerHTML = ic.im.load_status; - else - MIM.debug_nodes['status' + index].innerHTML = 'no IM'; - MIM.debug_nodes['keyseq' + index].innerHTML = ic.keys; - MIM.debug_nodes['preedit' + index].innerHTML = ic.preedit; -}; - -MIM.get_range = function (target, range) -{ - if (target.selectionStart != null) // for Mozilla - { - range[0] = target.selectionStart; - range[1] = target.selectionEnd; - } - else // for IE - { - var r = document.selection.createRange (); - var rr = r.duplicate (); - - rr.moveToElementText (target); - rr.setEndPoint ('EndToEnd', range); - range[0] = rr.text.length - r.text.length; - range[1] = rr.text.length; - } -} - -MIM.set_caret = function (target, ic) -{ - if (target.selectionStart != null) // Mozilla - { - target.focus (); - target.setSelectionRange (ic.spot, ic.spot + ic.preedit.length); - } - else // IE - { - var range = target.createTextRange (); - range.move ('character', pos); - range.select (); - } -}; - -(function () { - var range = new Array (); - - MIM.check_range = function (target, ic) - { - MIM.get_range (target, range); - if (range[0] != ic.spot || range[1] - range[0] != ic.preedit.length) - { - ic.reset (); - ic.spot = range[0]; - } - } -}) (); - -MIM.update = function (target, ic, prevlen) -{ - var text = target.value; - target.value = (text.substring (0, ic.spot) - + ic.produced - + ic.preedit - + text.substring (ic.spot + prevlen)); - ic.spot += ic.produced.length; - MIM.set_caret (target, ic); -}; - -MIM.reset_ic = function (event) -{ - var ic = event.target.mim_ic; - if (ic) - ic.reset (); -}; - -MIM.keydown = function (event) -{ - var target = event.target; - if (! (target.type == "text" || target.type == "textarea")) - return; - - var ic = target.mim_ic; - if (! ic || ic.im != MIM.current) - { - ic = new MIM.IC (MIM.current); - target.mim_ic = ic; - MIM.add_event_listener (target, 'blur', MIM.reset_ic); - } - if (ic.im.load_status != MIM.LoadStatus.Loaded) - return; - MIM.check_range (target, ic); - MIM.debug_print (event, ic); - ic.key = MIM.decode_key_event (event); -}; - -MIM.keypress = function (event) -{ - if (! (event.target.type == "text" || event.target.type == "textarea")) - return; - - var ic = event.target.mim_ic; - var i; - - try { - MIM.log (ic.im.name); - if (ic.im.load_status != MIM.LoadStatus.Loaded) - return; - if (! ic.key) - ic.key = MIM.decode_key_event (event); - if (! ic.key) - { - ic.reset (); - return; - } - - var prevlen = ic.preedit.length; - MIM.log ("filtering " + ic.key); - var result = ic.Filter (ic.key); - MIM.update (target, ic, prevlen); - } finally { - MIM.debug_print (event, ic); - } - return; -}; - -MIM.select_im = function (event) -{ - var target = event.target.parentNode; - while (target.tagName != "SELECT") - target = target.parentNode; - var idx = 0; - var im = false; - for (var lang in MIM.list) - for (var name in MIM.list[lang]) - if (idx++ == target.selectedIndex) - { - im = MIM.list[lang][name]; - break; - } - document.getElementsByTagName ('body')[0].removeChild (target); - target.target.focus (); - if (im && im != MIM.current) - MIM.current = MIM.load_sync (im); -}; - -MIM.destroy_menu = function (event) -{ - if (event.target.tagName == "SELECT") - document.getElementsByTagName ('body')[0].removeChild (event.target); -}; - -MIM.select_menu = function (event) -{ - var target = event.target; - - if (! ((target.type == "text" || target.type == "textarea") - && event.which == 1 && event.ctrlKey)) - return; - - var sel = document.createElement ('select'); - sel.onclick = MIM.select_im; - sel.onmouseout = MIM.destroy_menu; - sel.style.position='absolute'; - sel.style.left = (event.clientX - 10) + "px"; - sel.style.top = (event.clientY - 10) + "px"; - sel.target = target; - var idx = 0; - for (var lang in MIM.list) - for (var name in MIM.list[lang]) - { - var option = document.createElement ('option'); - var imname = lang + "-" + name; - option.appendChild (document.createTextNode (imname)); - option.value = imname; - sel.appendChild (option); - if (MIM.list[lang][name] == MIM.current) - sel.selectedIndex = idx; - idx++; - } - sel.size = idx; - document.getElementsByTagName ('body')[0].appendChild (sel); -}; - -MIM.test = function () -{ - var im = MIM.im_list['t']['latn-post']; - var ic = new MIM.IC (im); - - ic.Filter (new MIM.Key ('a')); - ic.Filter (new MIM.Key ("'")); - - if (true) - document.getElementById ('text').value = ic.produced + ic.preedit; - else { - try { - document.getElementById ('text').value - = Xex.ParseTerm (domain, body).Eval (domain).toString (); - } catch (e) { - if (e instanceof Xex.ErrTerm) - alert (e); - throw e; - } - } -} - - -MIM.init = function () -{ - MIM.add_event_listener (window, 'keydown', MIM.keydown); - MIM.add_event_listener (window, 'keypress', MIM.keypress); - MIM.add_event_listener (window, 'mousedown', MIM.select_menu); - if (window.location == 'http://localhost/mim/index.html') - MIM.server = 'http://localhost/mim'; - MIM.current = MIM.im_list['t']['latn-post']; -}; - -MIM.init_debug = function () -{ - MIM.debug = true; - MIM.init (); -};