//The contents of this file are subject to the Mozilla Public License 
//Version 1.1 (the "License"); you may not use this file except in 
//compliance with the License. You may obtain a copy of the License 
//at http://www.mozilla.org/MPL/
//Software distributed under the License is distributed on an "AS
//IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 
//implied. See the License for the specific language governing 
//rights and limitations under the License.
//The Original Code is Bloom, a rule-based development framework for 
//web applications. The Initial Developer of the Original Code is 
//Babelfish, Hilversum, The Netherlands. 
//All Rights Reserved.

//Structure Guide
//Term: 	{ofs:"..."} 
//TRS Var: 	{ofs:"@var", name:"..."}
//int, str:	itself
//module:	{ofs:"module", name:"...", 0:..., 1:..., ...}
//rule:		{ofs:"rule", lhs:..., rhs:...}
//appVar:	{ofs:"var", name:"...", value:...}
//term:		{ofs:"f", 0:..., ...}
//	ANY term may have additional attributes
//	module:	the module in which it is defined (top-level objects)
//Virtual Machine State:
//modules: object modName -> module
//rules:  array of all rules
//vars: object varName -> value

function isTerm(a) {
   return a && typeof a == 'object' && a.hasOwnProperty("ofs");
}
function Var(nm) {
   return {ofs: "@var", name: nm};
}
function termIsVar(a) {
   return a.ofs=="@var";
}
//to and from textual representation
function toString(t) {
   if (recDepth--<0) return"";
   if (isTerm(t)) {
      var res;
      if (arguments.length>1 && t.ofs == "module" || t.hasOwnProperty("module")) {//top level objects
         //so far, module, rule and var have specific syntax
         switch (t.ofs) {
          case "module":
            res = "["+t.name+"]\n";
            for (var i in t) {
               if (i!="ofs" && i != "name") {
                  res += toString(t[i],1)+";\n";
               }
            }
            return res+"\n";
          case "rule":
            return toString(t.lhs)+"="+toString(t.rhs);
          case "var": 
            return t.name+":"+toString(t.value);
      }  }
      if (termIsVar(t)) return t.name;//TRS var has specific syntax
      res=t.ofs;
      var x = "(";
      var k=0;
      for (var i in t) {
         if (t.hasOwnProperty(i) && i != "ofs" && i!="module") {
            if (i==k) {
               res += x + toString(t[i]); k++;
            } else {
               res += x + i + ":" + toString(t[i]);
            }
            x = ",";
      }  }
      if (x!="(") res += ")";
      return res;
   } else if (typeof(t)=='string') {
      return "\""+t.replace(/\\/g,'\\\\').replace(/\"/g,"\\\"").replace(/\n/g,"\\n")+"\"";
   } else {
      return t;
   }
}
function err(msg,res,stack) {
   msg += "Built: "+toString(res)+" Stack:";
   if (stack) {
      for (var i = 0; i<stack.length; i++) {
	 msg += toString(stack[i])+"| ";
   }  }
   throw(msg);
}
function parse(s,i,pt) {
//parses either a seq of module-chunks terminated with [end] returning a list 
//of module objects, or when pt is 1 a single term, returning that
   var c = s.charAt(i);
   var stack=[];
   var brackdepth=0;
   var modName = "";
   while (true) {
      var r = "";
      while (c==' '||c=='\n'||c=='\t'||c=='/' && s.charAt(i+1)=='/') {
	 if (c=='/') {
	    while(s.charAt(++i)!='\n') {}
	 }
	 c=s.charAt(++i);
      }
      if (c>='0' && c<= '9' || c=='-') {//int
         do {
            r+=c;
            c=s.charAt(++i);
         } while (c>='0'&&c<='9')
         stack.push(r);
      } else if (c=='"') {//str
	 if (s.charAt(i+1)=="*") {
	    i+=1;
	    r+=modName;
	 }
         stringParse:
         while (true) {
            c=s.charAt(++i);
            while (c=='\\') {
               switch (c=s.charAt(++i)) {
                  case '"': case '\\': break;
                  case 'n': c="\n"; break;
                  case 't': c="\t"; break;
               }
               r+=c;
               c=s.charAt(++i);               
            }
            if (c=='"') break stringParse;
            r+=c;
         }
         c=s.charAt(++i);
         stack.push(r);
      } else if ((c>='A'&&c<='Z'||c=='@')) {//TRS var
         while ((c>='a'&&c<='z')||(c>='A'&&c<='Z')||(c>='0'&&c<='9')||c=='_'||c=='@') {
            r+=c;
            c=s.charAt(++i);
         }
         stack.push(Var(r));
      } else if ((c>='a'&&c<='z')||c=='_'||c=='*') {//id
	 if (c=='*') {
	    r = modName;
            c=s.charAt(++i);
	 }
         while ((c>='a'&&c<='z')||(c>='A'&&c<='Z')||(c>='0'&&c<='9')||c=='_') {
            r+=c;
            c=s.charAt(++i);
         }
         if (c==':'||c=='('||c==']') {//1 char lookahead: fieldname/ofs/modName or not
	    if (c==']') modName = r;
            stack.push(r); 
         } else {//its an argumentless symbol
            stack.push({ofs:r});
         }
      } else if (c=='(') {
	 brackdepth++;
         stack.push(c);
         c=s.charAt(++i);
      } else if (c==')') {
	 brackdepth--;
         stack.push(c);
         c=s.charAt(++i);
      } else if (c==null||c=="") {
	 if (pt==1 && stack.length==1) return stack[0];
         err("unexpected eof","");
      } else if (c.search(/[\[\];=:,]/)!= -1) {//}){//other token
         stack.push(c);
         c=s.charAt(++i);
      } else err("unexpected symbol: "+c,"",stack);
      if (c!='('&&c!=':') while (true) {
         var top = stack.length-1;
         var res;
      //reductions
      //pattern mod*,[,end,]
         if (stack.length>=3 && stack[top]==']' && stack[top-1]=="end" 
	     && stack[top-2]=='[') {
            var mods = [];
	    stack.length -= 3;
            for (var i=0; i<stack.length; i++) {
               mods.push(stack[i]);
               
            }
            return mods;
      //pattern ...,[,id,],obj*/;,[
         } else if (stack.length>=3 && stack[top]=='[' 
		    && (stack[top-3]==']' || stack[top-3]==';')) {
            res = [];
            res.ofs="module";
            var j=top-1;
            while (j>=4 && stack[j]==";") {
               j -= 2;
            }
            if (stack[j]!=']' || stack[j-2]!='[') err("bad stack 1","",stack);
            for (var jj=j+1; jj<top; jj+=2) {
               res.push(stack[jj]);
            }
            res.name=stack[j-1];
	    if (isTerm(res.name)&&termIsVar(res.name)) res.name = res.name.name;
            stack[j-2] = res;
            stack.length = j-1;
            stack.push('[');
      //pattern ...,t,=,t
         } else if (stack.length>=3 && stack[top-1]=='=') {
            res = {ofs:"rule", lhs:stack[top-2], rhs:stack[top]};
            stack[top-2] = res;
            stack.length -= 2;
      //pattern ...,id,:,t
         } else if (stack.length>=3 && stack[top-1]==':' && brackdepth==0) {
            res = {ofs:"var", name:stack[top-2], value:stack[top]};
            stack[top-2] = res;
            stack.length -= 2;
      //pattern ...,id,(,t|id:t*/,,)
         } else if (stack.length>=3 && stack[top]==')') {
            var j=top-1;
            while (j>0 && stack[--j]!='(') {}
            if (j==0) err("bad stack 2.1 cannot find open paren for arglist","",stack);
            var jj=j; var k=0;
            res = {ofs:stack[j-1]};
            j++;
            while (j<top) {
               if (stack[j+1]==':') {
                  res[stack[j]]=stack[j+2];
                  j+=3;
               } else {
                  res[k++]=stack[j];
                  j+=1;
               }  
               if (stack[j]!=',' && j!=top) err("bad stack 2.2 comma or close paren missing\n",res,stack);
               j++;
            }
            stack[jj-1]=res;
            stack.length = jj;
            if (pt==1 && jj==1) return stack[0];
         } else break;
   }  }
}

function match(t,p,e) {
   if (isTerm(p)) {
      if (termIsVar(p)) {
         if (e.hasOwnProperty(p.name)) {
            return match(t,e[p.name],e);
         } else {
            e[p.name] = t;
            return e;
         }
      } else {//compound
         if (!isTerm(t)) return null;
         //if (t.ofs != p.ofs) return null;
         for (var i in p) {
            if (t.hasOwnProperty(i)) {
               var ex = match(t[i],p[i],e);
               if (ex==null) return null;
               e = ex;
            } else return null;
         }
         return e;
      }
   } else {//int|str p
      return p==t?e:null;
   }
}
var redTrace = false;
//fullReduce reduces entire term bottom up
function fullReduce(t) {//rules[r1,...,rN]; rule{lhs:,rhs:}
   if (redTrace) tracewin.innerHTML+="F: "+toString(t).replace(/>/g,'&gt;')+"<br>";
   if (!isTerm(t)) return t;
   var res = {};
   if (t.ofs.charAt(0)=="_") {
      res.ofs=t.ofs; res.aosPat = t; res.aosTerm = null; res.aosEnv = {};
      return topReduce(res);
   }
   for (var i in t) {
      if (t.hasOwnProperty(i)) {
         res[i] = fullReduce(t[i]); 
   }  }
   return topReduce(res);
}
//instReduce instantiates pattern p with e reusing attributes in t
function instReduce(t,p,e,ate) {
   if (redTrace) { e.ofs="env";tracewin.innerHTML+="I: "+toString(t?t:"").replace(/>/g,'&gt;')+" "+toString(p).replace(/>/g,'&gt;').replace(/</g,'&lt;')+" "+toString(e)+"<br>"; }
   if (!isTerm(p)) return p;
   if (termIsVar(p)) return e.hasOwnProperty(p.name) ? e[p.name] : 
      p.name.charAt(0)=="@" ? mInstReduce(ate,p.name.substr(1)) : p;
   var res={};
   if (p.ofs=="eval") for (var i in e) res[i]=e[i];
   if (!p.hasOwnProperty("ofs")||typeof(p.ofs)!="string") {
      throw("oeps");//?????
   }
   if (p.ofs.charAt(0)=="_") {//is it a macro: build a thunk
      res.aosPat = p; res.aosEnv = e; res.aosTerm = t; res.ofs = p.ofs;
      return topReduce(res);
   }
   for (var i in p) {
      if (p.hasOwnProperty(i)) {
         res[i]=instReduce(isTerm(t)&&t.hasOwnProperty(i)?t[i]:null,p[i],e,ate);
   }  }
   return topReduce(res);
   // ToDo: what about reusing attributes of t?
}
//topReduce reduces assuming immediate children are normalised
function topReduce(t) {
   if (redTrace) tracewin.innerHTML+="T: "+toString(t).replace(/>/g,'&gt;')+"<br>";
   for (var i in rules) {
      if (i!="ofs"){
         var r = rules[i];
         var e = match(t,r.lhs,{});
         if (e!=null) {//if (isTerm(t)&&t.ofs=="html") //var x="x";
	    if (t.ofs.charAt(0)=="_") {
	       if (t.ofs.charAt(1)!="_") {//strategy rule
		  var res={};
		  for (var j in t) res[j]=t[j];
		  res.ofs = r.rhs.ofs;
		  for (var j in r.rhs) {
		     if (j!="ofs") {
			res[j]=mInstReduce(t,j); 
		  }  }
		  return topReduce(res);
	       } else {
		  return instReduce(t,r.rhs,e,t);
	       }
	    }
            return instReduce(t,r.rhs,e,null);
   }  }  }
   if (aos.hasOwnProperty(t.ofs)) {
      return aos[t.ofs].call(t);
   }
   return t;
}
function mInstReduce(T,k) {
   var t = T.aosTerm; var e = T.aosEnv; var p = T.aosPat;
   return instReduce(isTerm(t)&&t.hasOwnProperty(k)?t[k]:null,p[k],e,T);
}
function mGet(T,k) { return T.aosPat[k]; }
function mSet(T,k,v) { T.aosEnv[k]=v; }
function mHasProperty(T,k) { return T.aosPat.hasOwnProperty(k); }

//aos acts as a namespace to avoid clashes (for instance this.select already exists).
var aos = {}; 

aos._for = function() {//_for(obj/idx:Var,<val>,(collector:<ofs>,)? <body>) for var in lst do body res in coll
   var set = mInstReduce(this,0);
   var cltr = null;
   var objItr = mHasProperty(this,"obj")
   var itr = (objItr ? mGet(this,"obj") : mGet(this,"idx")).name;
   if (mHasProperty(this,"collector")) { cltr = []; cltr["ofs"] = mInstReduce(this,"collector"); }
   for (var i in set) {
      if (i != "ofs") {
	 var itv = objItr ? set[i] : i;
	 mSet(this,itr,itv);
	 var res = mInstReduce(this,1);
	 if (cltr) cltr.push(res);
   }  }
   return cltr ? cltr : "";
}

//reduce(t)=normal form of t [select, mapSelect, and others do not normalise]
aos.reduce = function() {
   return fullReduce(this[0]);
}
//map(f,g(k1:s1,...,kN:sN),t1,...,1M)=g(k1:f(k1,s1,t1,...,tM),...,kN:f(kN,sN,t1,...,tM))
aos.map = function() {
   var f = this[0];
   var res = {ofs:this[1].ofs}
   for (var i in this[1]) {
      if (i!="ofs") {
	 res[i]={ofs:f};
	 res[i][0]=i;
	 res[i][1]=this[1][i];
	 var k=2;
	 for (var j in this) {
	    if (j!=0&&j!=1&&j!="ofs") {
	       res[i][k++]=this[j];
	 }  }
	 res[i]=topReduce(res[i]);
   }  }  
   return topReduce(res);
}
//select(pat)=cat(o1,...,oN) all top-level objects matching pat
aos.select = function() {
   var res = select(this[0]);
   res.ofs = "cat";
   return res;
}
//mapSelect(pat,f,lvl)=cat(f(o1),...,f(oN)); lvl=1:topred each f(),2:fullreduce
//note that non-nf or special printing patterns should be quoted 
aos.mapSelect = function() {
   var res = select(typeof(this[0])=="string"?parse(this[0],0,1):this[0]);
   for (var i in res) {
      res[i] = {ofs:this[1].ofs,0:res[i]};
      if (this[2]==1) res[i]=topReduce(res[i]);
   }
   res.ofs = "cat";
   if (this[2]==2) res=fullReduce(res);
   return res;
}
//ofs(f(s))="f"
aos.ofs = function() {
         return this[0].ofs;
}
//debug(trace:0/1) tracing;
//debug(break:any) break when set
//debug(depth:n) break when depth n is reached
var tracewin;
var recDepth;
aos.debug = function() {
   tracewin = window.frames[0].document.getElementById("traceFrame");
   tracewin.style.display = "";
   if (this.hasOwnProperty("trace")) redTrace = this["trace"]==1;
   if (this.hasOwnProperty("break")&&this["break"]==1) {
      dbg = "set breakpoint here";
   }
   if (this.hasOwnProperty("depth")) recDepth = this["depth"];
   return "debug";
}
//eval(str) evaluate the Javascript string
aos.eval = function() {
   var str = this[0].replace(/\$([A-Z][A-Za-z0-9_]*)/g,"this['$1']");
   var res = eval(str);
   return res;
}
//at(f(...,i1:g(...,i2:h(...,(((...,iN:s,...))),...),...),...),i1,i2,...,iN)=s
aos.at = function() {
   var res = this[0];
   for (var i=1; this.hasOwnProperty(i); i++) {
      res = res[this[i]];
   }
   return res;
}
//concat(str1,...,strN)=str1+...+strN
aos.concat = function() {
   var res = "";
   for (i in this) {
      if (i!="ofs") res += this[i];
   }
   return res;
}
//topString(s1,...,sN)=concatenation of toString of each
aos.topString = function() {
   var res = "";
   for (var i=0; this.hasOwnProperty(i); i++) {
      res += isTerm(this[i])?toString(this[i],1):this[i];
   }
   return res;
}
//varGet(name:"n")=s when var n:s has been set latest
aos.varGet = function() {
   if (!vars.hasOwnProperty(this.name)) throw("no such variable:"+this.name);
   return vars[this.name];
}
//varSet(name:"n",value:t,i1,i2,...,iN) replaces s by t
//when var n:f(...,i1:g(...,i2:h(...,(((...,iN:s,...))),...),...),...)
aos.varSet = function() {
   var ctr = vars;
   var trail = this.name;
   for (var i=0; this.hasOwnProperty(i); i++) {
      ctr = ctr[trail];
      trail = this[i];
   }
   return ctr[trail]=this.value;
}
//htmlElementGetValue(nm) gets the named HTML element.value
aos.htmlElementGetValue = function() {
   var ctr = window.frames[0].document.getElementById(this[0]);
   return ctr.value;
}
//htmlElementSetValue(nm,val) sets the named HTML element.value to val
aos.htmlElementSetValue = function() {
   var ctr = window.frames[0].document.getElementById(this[0]);
   return ctr.value = this[1];
}
//htmlElementSetInnerHTML(nm,val,?2) sets or if there is a ?2 appends to 
//the named HTML element.InnerHTML to val
aos.htmlElementSetInnerHTML = function() {
   var ctr = window.frames[0].document.getElementById(this[0]);
   if (!this.hasOwnProperty(2)) {
      ctr.innerHTML = "";
   }
   var x = catLiterals(this[1]);
   ctr.innerHTML += x;
}
//htmlElementSetDisplayStyle(nm,val) sets the named HTML element.style.display to val
aos.htmlElementSetDisplayStyle = function() {
   var ctr = window.frames[0].document.getElementById(this[0]);
   return ctr.style.display = this[1];
}
//loadFile(fn) 
aos.loadFile = function () {
   var content;
   try {//ie?
      var fso = new ActiveXObject("Scripting.FileSystemObject");
      var file = fso.OpenTextFile(this[0],1);
      content = file.ReadAll();
      file.Close();
      if (content!=false) return content;
   } catch(e) {
      //throw(e.toString());
   }
//ff?
   if(window.Components)
      try {
	 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
	 var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
	 file.initWithPath(this[0]);
	 if (!file.exists())
	    throw("No such file: "+this[0]);
	 var inputStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
	    .createInstance(Components.interfaces.nsIFileInputStream);
	 inputStream.init(file, 0x01, 00004, null);
	 var sInputStream = Components.classes["@mozilla.org/scriptableinputstream;1"]
	    .createInstance(Components.interfaces.nsIScriptableInputStream);
	 sInputStream.init(inputStream);
	 return(sInputStream.read(sInputStream.available()));
      } catch(e) {
	 throw(this[0]+e.toString());
      }
   throw("loadFile failed: "+this[0]);
}
//saveFile(fn,txt) 
aos.saveFile = function () {
   try {//ie?
      var fso = new ActiveXObject("Scripting.FileSystemObject");
      var file = fso.OpenTextFile(this[0],2,-1,0);
      file.Write(this[1]);
      file.Close();
      return;
   } catch(e) {
      throw(this[0]+e.toString());
   }//ff?
   if(window.Components)
      try {
	 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
	 var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
	 file.initWithPath(this[0]);
	 if (!file.exists())
	    file.create(0, 0664);
	 var out = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
	 out.init(file, 0x20 | 0x02, 00004,null);
	 out.write(this[1], this[1].length);
	 out.flush();
	 out.close();
	 return;
      } catch(e) {
	 throw(e.toString());
      }
   throw("saveFile failed: "+this[0]);
}
//loadFullSourceEditor() 
aos.loadFullSourceEditor = function () {
   var editorTextArea = window.frames[0].document.getElementById('fullSourceEditor');
   editorTextArea.textContent = "";
   for (var i in modules) {
      editorTextArea.textContent += toString(modules[i],1);
   }
   editorTextArea.textContent += "[end]";
}
//submitSpec(text,fn) parses and submits a spec fn==0 init otherwise replace whats there
aos.submitSpec = function() {
   var chunks = parse(this[0],0,0);
   var fn=this[1];
   var mods = {};
   for (var i in chunks) {
      var chunk = chunks[i];
      var mods;
      if (mods.hasOwnProperty(chunk.name)) {
	 mod = mods[chunk.name];
      } else {
	 mods[chunk.name] = mod = []; mod.ofs = "module"; mod.name=chunk.name;
      }
      for (var j in chunk) {
         if (j!="ofs" && j!="name") {
            chunk[j].module = mod.name;
	    mod.push(chunk[j]);
   }  }  }
   if (fn==0) { modules = {}; }
   for (var i in mods) {
      modules[i] = mods[i];
   }
   rules = select({ofs:"rule"});
   if (fn==0) vars = {};
   var vs = select({ofs:"var"});
   for (var i in vs) {
      vars[vs[i].name] = vs[i].value;
   } 
}
//render(t) renders the leafs of the subject
aos.render = function() {
   documentWriteLiterals(window.frames[0],"document","write",this[0]);
   window.frames[0].document.close();
}
//tellServer(cb,url,s1,...,sn) submits strings s1..sn to server url, applying cb to result
aos.tellServer = function() {
   var cb = this[0];
   var url = this[1];
   var lastPing = 0;
   var req;
   if (window.XMLHttpRequest) {
      req = new XMLHttpRequest();
   } else if (window.ActiveXObject) {
      try { 	// Try ActiveX
	 req = new ActiveXObject("Msxml2.XMLHTTP");
      } catch (e1) { 
	 try {
	    req = new ActiveXObject("Microsoft.XMLHTTP");
	 } catch (e2) {
	    throw("XML HTTP request creation methods failed");
   }  }  }
   req.open("POST", url, true);
   req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
   var i; var msg="";
   for (i=2; this.hasOwnProperty(i); i++) {
      msg += "a"+(i-2)+"="+this[i]+"&";
   }
   msg += "len="+(i-2);
   //callback handler inner because call & callback share req. A gloVar allows
   //only one instance; this multiple.  
   req.onreadystatechange 
      = function () {
	 if (req.readyState == 4) {
	    if (req.status == 200) {
               var reXML = req.responseXML;
	       var info = reXML.getElementsByTagName("info")[0];
	       var len = Number(info.getAttribute("len"));
	       var res = {ofs:"cat"};
	       if (len==0) throw(info.getAttribute("err"));
	       for (var i=0; i<len; i++) {
		  res[i] = info.childNodes[i].textContent;
	       }
	       //the rest is pro tem
	       var ctr = window.frames[0].document.getElementById(cb);
	       ctr.value = toString(res);
	    } else {
	       throw("bad reply: "+ req.status);
      	    }
	 }
      };
   req.send(msg);
   return "";
}

//Things to tell a server
//getChallenge(user) => challenge
//login(user,pwd/reply) =/ logs in the user, creates a session /=> session id
//getModules(<nm,auth>*) =/ where auth is a user or group id /=> mod*
//putModules(mod*) =/ 
//sync() =/ fetches changes by other users /=> mods*
//rawSQL(str) =/ req's admin; executes this query /=> ?
//createGroup() => gid

function newXMLHttpRequest() {
   var xmlreq = false;
   if (window.XMLHttpRequest) {
      xmlreq = new XMLHttpRequest();
   } else if (window.ActiveXObject) {
      try { 	// Try ActiveX
	 xmlreq = new ActiveXObject("Msxml2.XMLHTTP");
      } catch (e1) { 
	 try {
	    xmlreq = new ActiveXObject("Microsoft.XMLHTTP");
	 } catch (e2) {
	    throw("XML HTTP request creation methods failed");
   }  }  }
   return xmlreq;
} 


function select(pat) {
   var res = [];
   for (var i in modules) {
      var mod = modules[i];
      if (match(mod,pat,{})!=null) res.push(mod);
      for (var j in mod) {
	 if (match(mod[j],pat,{})!=null) {
            res.push(mod[j]);
   }  }  }
   return res;
}

function documentWriteLiterals(d,f,m,t) {
   if (isTerm(t)) {
      for (var i in t) {
         if (i!="ofs" && i!="module") {
            documentWriteLiterals(d,f,m,t[i]);
      }  }
   } else {
      if (m=="write") {
	 d[f].write(t);
      } else {
	 d[f] += t;
   }  }
}
function catLiterals(t) {
   var res = "";
   if (isTerm(t)) {
      for (var i in t) {
         if (i!="ofs" && i!="module") {
            res += catLiterals(t[i]);
      }  }
      return res;
   } else {
      return t;
   }
}

var modules = {};
var vars={};
var rules = [];

function act(script) {
   var as = arguments;
   script = script.replace(/\$([0-9])/g,function(s,i) {return as[i];});
   script = script.replace(/`/g,'"');
   var p = parse(script,0,1);
   fullReduce(p);
}

//DEPRECATED

//aos._if = function() {//_if(bool:b,then:t,else:e)=t(!=-1)/e(b=-1)
//   var b = mInstReduce(this,"bool");
//   var res = "";
//   if (b!=-1) return mInstReduce(this,"then");
//   if (this.hasOwnProperty("else")) return mInstReduce(this,"else");
//   return null;
//}

//apply(f,t1,...,1N)=f(t1,...,tN)
//aos.apply = function() {
//   var res = {ofs:this[0]};
//   for (var i in res) {
//      if (i!="ofs"&&i!=0) {
//	 res[i]=this[i];
//      }
//   }
//   return fullReduce(res);
//}

//split(str,char)=cat(s1,...,sN) where str=s1+char+...+char+sN
//aos.split = function() {
//   var res = this[0].split(this[1]);
//   res["ofs"]="cat";
//   return res;
//}


//scan(str,pat)=i where str=s1+char+...+char+sN
//aos.scan = function() {
//   var res = this[0].search(new RegExp(this[1]));
//   return res;
//}
