// This file contains JavaScript functions to parse, display and manipulate // simple mathematical expressions. The emphasis is on translating from // readable ascii expressions to reasonable HTML. // version of Jan 24 2001, by Peter Jipsen, http://math.vanderbilt.edu/~pjipsen //check if the symbol font is available symb = navigator.appVersion.lastIndexOf('Win')!=-1 || navigator.appVersion.lastIndexOf('Mac')!=-1; vari = [ {name:"x", arity:0, output:"x"}, {name:"y", arity:0, output:"y"}, {name:"z", arity:0, output:"z"}, {name:"u", arity:0, output:"u"}, {name:"v", arity:0, output:"v"}, {name:"w", arity:0, output:"w"} ]; formulavari = [ {name:"P", arity:0, output:"P"}, {name:"Q", arity:0, output:"Q"}, {name:"R", arity:0, output:"R"} ]; function out(col,str,alt,sp) { return sp+""+str : (symb?" face=symbol>"+str : ">"+alt))+""+sp; } bl=" "; cons = [ zero = {name:"0", arity:0, output:"0"}, unit = {name:"e", arity:0, output:"e"}, one = {name:"1", arity:0, output:"1"}, topp = {name:"top", arity:0, output:"T"}, empty= {name:"{}", arity:0, output:out("","Æ","{}",""), tex:"emptyset"}, qed = {name:"qed", arity:0, output:out("","¨","[]",""), tex:"qed"}, alpha= {name:"al", output:out("","a","alpha",""), tex:"alpha"}, beta = {name:"be", output:out("","b","beta",""), tex:"beta"}, delta= {name:"de", output:out("","d","delta",""), tex:"delta"}, epsilon={name:"ep", output:out("","e","epsilon",""), tex:"epsilon"}, gamma= {name:"ga", output:out("","g","gamma",""), tex:"gamma"}, pi = {name:"pi", output:out("","p","pi",""), tex:"pi"}, dots = {name:"...", arity:0, output:"...", tex:"ldots"} ]; oper = [ sub = {name:"_", arity:2, priority:40, infix:true, output:"", endStr:"", tex:"_{", endTex:"}"}, sup = {name:"^", arity:2, priority:40, infix:true, output:"", endStr:"", tex:"^{", endTex:"}"}, si = {name:"si", output:out("","s","sigma",""), paren:true, tex:"sigma"}, boldface={name:"bf",arity:1, priority:38, output:"", endStr:"", tex:"mathbf"}, underline={name:"ul",arity:1, priority:38, output:"", endStr:"", tex:"underline"}, inverse = {name:"^-1", arity:1, priority:34, infix:true, output:out("Blue","-1","",""), tex:"^{-1}"}, tilde = {name:"~", arity:1, priority:34, infix:true, output:out("Blue","~","",""), tex:"^\\tilde"}, diamond={name:"<>",arity:1, priority:32, output:out("Blue","à","<>",""), tex:"diamond"}, box = {name:"[]", arity:1, priority:32, output:out("Blue","[]","",""), tex:"Box"}, dot = {name:".", arity:2, priority:31, infix:true, assoc:true, output:"", tex:""}, times = {name:"*", arity:2, priority:31, infix:true, assoc:true, output:out("Blue","*","*",""), tex:"star"}, over = {name:"/", arity:2, priority:30, infix:true, output:out("Blue","/","/","")}, under = {name:"\\", arity:2, priority:30, infix:true, output:'\\', tex:"backslash"}, rconj = {name:"-;", arity:2, priority:28, infix:true, output:' -; ', tex:"righttriangle"}, lconj = {name:";-", arity:2, priority:28, infix:true, output:' ;- ', tex:"lefttriangle"}, arrow = {name:"->", arity:2, priority:26, infix:true, output:out("Blue","®","->",bl), tex:"rightarrow"}, larrow = {name:"<-", arity:2, priority:26, infix:true, output:out("Blue","¬","<-",bl), tex:"leftarrow"}, plus = {name:"+", arity:2, priority:24, infix:true, assoc:true, output:out("Blue","+","",bl)}, minus = {name:"-", arity:2, priority:24, infix:true, output:out("Blue","-","-","")}, meet = {name:"^^", arity:2, priority:24, infix:true, assoc:true, output:out("Blue","Ù","^",bl), tex:"wedge"}, join = {name:"vv", arity:2, priority:24, infix:true, assoc:true, output:out("Blue","Ú","v",bl), tex:"vee"}, union = {name:"uu", arity:2, infix:true, priority:22, assoc:true, output:out("Blue","È","u",bl), tex:"cup"}, intersect = {name:"nn", arity:2, infix:true, priority:22, assoc:true, output:out("Blue","Ç","n",bl), tex:"cap"}, cross = {name:"cross", arity:2, infix:true, priority:22, assoc:true, output:out("Blue","´","cross",bl), tex:"times"}, norm = {name:"|", endName:"|", arity:1, priority:20, endSymb:true, output:"|", endStr:"|"}, floor = {name:"|_", endName:"_|", arity:1, priority:20, output:out("","ë","|_",""), tex:"lfloor", endTex:"rfloor", endStr:out("","û","_|","")}, {name:"_|", endSymb:true, output:"_|"}, ceiling = {name:"|~", endName:"~|", arity:1, priority:20, output:out("","é","|~",""), tex:"lceiling", endTex:"rceiling", endStr:out("","ù","~|","")}, {name:"~|", endSymb:true, output:"~|"}, tuple = {name:"<<", endName:">>", arity:1, priority:18, output:out("","á","<",""), tex:"langle", endTex:"rangle", endStr:out("","ñ",">","")}, comma = {name:",", arity:2, priority:18, infix:true, assoc:true, output:', '} ]; pred = [ eq = {name:"=", arity:2, infix:true, priority:16, assoc:true, output:out("Red","=","=",bl)}, noteq = {name:"!=", arity:2, infix:true, priority:16, assoc:true, output:out("Red","¹","!=",bl), tex:"noteq"}, leq = {name:"<=", arity:2, infix:true, priority:16, assoc:true, output:out("Red","£","<=",bl), tex:"leq"}, geq = {name:">=", arity:2, infix:true, priority:16, assoc:true, output:out("Red","³",">=",bl), tex:"geq"}, less = {name:"<", arity:2, infix:true, priority:16, assoc:true, output:' < '}, greater = {name:">", arity:2, infix:true, priority:16, assoc:true, output:' > '}, lcover = {name:"-<", arity:2, infix:true, priority:16, assoc:true, output:' -< ', tex:"prec"}, ucover = {name:">-", arity:2, infix:true, priority:16, assoc:true, output:' >- ', tex:"succ"}, elem = {name:"in", arity:2, infix:true, priority:16, assoc:true, output:out("Red","Î","in",bl)}, notelem = {name:"notin", arity:2, infix:true, priority:16, assoc:true, output:out("Red","Ï","notin",bl)}, subset = {name:"subset", arity:2, infix:true, priority:16, assoc:true, output:out("Red","Í","subset",bl), tex:"subseteq"} ]; conn = [ allSymbol = {name:"AA", arity:2, priority:14, output:'"':'>A')+'', tex:"forall"}, exSymbol = {name:"EE", arity:2, priority:14, output:'$':'>E')+'', tex:"exists"}, notSymbol = {name:"not", arity:1, priority:14, output:out("Blue","Ø","not",""), tex:"neg"}, // output:' not ', tex:"neg"}, andSymbol = {name:"and", arity:2, priority:12, infix:true, assoc:true, output:'  and  ', tex:"mathrm{\\quad and\\quad}"}, orSymbol = {name:"or", arity:2, priority:10, infix:true, assoc:true, output:'  or  ', tex:"mathrm{\\quad or\\quad}"}, impSymbol = {name:"implies", arity:2, priority:8, infix:true, output:out("Blue","Þ","=>",bl), tex:"Rightarrow"}, // output:'  implies  ', tex:"Rightarrow"}, iffSymbol = {name:"iff", arity:2, priority:6, infix:true, assoc:true, output:out("Blue","Û","<=>",bl+bl), tex:"LeftRightarrow"}, // output:'   iff   ', tex:"LeftRightarrow"}, colon = {name:":", arity:2, priority:4, infix:true, output:' : '}, set = {name:"{", endName:"}", arity:1, priority:2, output:"{", tex:"\\{", endTex:"\\}", endStr:"}"} ]; maxPriority=50; lang = vari.concat(formulavari,oper,cons,pred); for (var i=0; i=qis.priority && !q.symbol.paren && !qis.paren && q.symbol.endStr==null && (!q.symbol.infix && qis.infix || q.symbol.infix && !(qis.infix && q.arg[0].arg.length==1 && qis!=minus) && !(q.symbol==qis && qis.assoc))? "("+e2s(q.arg[0])+")" : e2s(q.arg[0])); var st=""; for (var i = 1; i < q.arg.length; i++) { if (q.symbol.paren) st = st+","; qis=q.arg[i].symbol; st = st + (!q.symbol.paren && qis.infix && p>=qis.priority && q.symbol.endStr==null ? "("+e2s(q.arg[i])+")" : e2s(q.arg[i])); } if (q.symbol.paren) return q.symbol.output + "(" + st0 + st + ")"; else if (q.symbol.infix && !(q.symbol==minus && q.arg.length==1)) return st0 + q.symbol.output + st + (q.symbol.endStr!=null?q.symbol.endStr:""); else if (q.symbol==allSymbol || q.symbol==exSymbol) return q.symbol.output + st0 + " " + st; return q.symbol.output + st0 + st + (q.symbol.endStr!=null?q.symbol.endStr:""); } function latex(sym) { var st=sym.tex; if (st==null) st=sym.name; if ((st[0]>="A" && st[0]<="Z" || st[0]>="a" && st[0]<="z") && st.length>1) return "\\"+st+" "; else return st; } function e2text(q,tex) {//convert expression to string using priority if (q==null) return "NULL"; if (q.arg.length==0) return (tex?latex(q.symbol):q.symbol.name); var p=q.symbol.priority; var qis=q.arg[0].symbol; var st0 = (p>=qis.priority && !q.symbol.paren && !qis.paren && q.symbol.endStr==null && (!q.symbol.infix && qis.infix || q.symbol.infix && !(qis.infix && q.arg[0].arg.length==1) && !(q.symbol==qis && qis.assoc))? "("+e2text(q.arg[0],tex)+")" : e2text(q.arg[0],tex)); var st=""; for (var i = 1; i < q.arg.length; i++) { if (q.symbol.paren) st = st+","; qis=q.arg[i].symbol; st = st + (!q.symbol.paren && qis.infix && p>=qis.priority && q.symbol.endStr==null ? "("+e2text(q.arg[i],tex)+")" : e2text(q.arg[i],tex)); } if (q.symbol.paren) return (tex?latex(q.symbol):q.symbol.name) + "(" + st0 + st + ")"; else if (q.symbol.infix && !(q.symbol==minus && q.arg.length==1)) return st0 + (tex?latex(q.symbol):(q.arg.length==1?q.symbol.name: " "+q.symbol.name+" ")) + st + (q.symbol.endStr!=null?(tex?latex(q.symbol.endTex):q.symbol.endName):""); else if (q.symbol==allSymbol || q.symbol==exSymbol) return (tex?latex(q.symbol):q.symbol.name) + st0 + " " + st; return (tex?latex(q.symbol):q.symbol.name) + st0 + st + (q.symbol.endStr!=null?(tex?latex(q.symbol.endTex):q.symbol.endName):""); } function exprtree(q) {//convert expression to string if (q==null) return "NULL"; var st=""; if (q.arg.length>0) { st = exprtree(q.arg[0]); for (var i = 1; i < q.arg.length; i++) st = st + "," + exprtree(q.arg[i]); return q.symbol.output + "(" + st + ")"; } return q.symbol.output; } //Parsing ascii strings into expressions //First we concatenate all the symbol arrays and sort the symbols function compareNames(s1,s2) { if (s1.name > s2.name) return 1 else return -1; } names=[]; other=[]; // additional list of symbols created during parsing function initSymbols() { symbols = vari.concat(formulavari,cons, oper, pred, conn, punc, other); symbols.sort(compareNames); names = []; for (var i=0; imaxPriority) maxPriority = symbols[i].priority; maxPriority++; } function newSymbol(st,outst){ if (outst=="") outst=st; if (outst.length==1 && (("A"<=outst && outst<="Z") || ("a"<=outst && outst<="z"))) outst=""+outst+""; add(other,{name:st, output:outst, paren:true, index:symbols.length}); var k=position(names,st,0); names=names.slice(0,k).concat([st],names.slice(k)); symbols=symbols.slice(0,k).concat(other.slice(other.length-1),symbols.slice(k)); } initSymbols(); //names should contain no blanks function removeCharsAndBlanks(str,n) { var st = str.slice(n); for (var i=0; i=n where str appears or would be inserted // assumes arr is sorted if (n==0) { var h,m; n=-1; h=arr.length; while (n+1> 1; if (arr[m]=str } function getSymbol(str) { //return maximal initial substring of str that appears in names //return null if there is none var k=0; //new pos var j=0; //old pos var mk; //match pos var st; var match=""; var more = true; for (var i=1; i<=str.length && more; i++) { st=str.slice(0,i); //initial substring of length i j=k; k=position(names, st, j); if (k=names[k]; } if (match!="") return symbols[mk]; // if str[0] is a letter return maxsubstring of letters followed by numbers k=1; st=str.slice(0,1); while (k<=str.length && ("A"<=st && st<="Z") || ("a"<=st && st<="z")) { st=str.slice(k,k+1); k++; } // if str[0] is a number return maxsubstring of numbers while (k<=str.length && "0"<=st && st<="9") { st=str.slice(k,k+1); k++; } st=str.slice(0,k-1); if (k>1) { newSymbol(st,st); return symbols[position(names, st, 0)]; } // otherwise return null return null; } //document.writeln("names=",names,"

"); /* blanks ( ) [ ] , are seperators (not allowed in names of symbols) Arguments may be separated by spaces or single commas. Parenthesis must be balanced. Symbols are either terminal (arity==0) or prefix or infix (unary infix defaults to postfix) expr ::= terminal | prefix expr1 ... exprN | prefix(expr1, ... , exprN) | expr infix | expr1 infix exprN | (expr) | [expr] Expressions are read from left to right, choosing the longest initial substring that matches the name of a symbol. Symbol priority is taken into account. */ function str2expr(str,pri) { //builds expr and returns [expr,substring] // If returned substring starts with "Error: ..." it contains information about a parsing error var arg=[]; var token=""; var symbol; var nextsymbol; var expr; var estr; var more; str=removeCharsAndBlanks(str,0); symbol=getSymbol(str); //either a symbol or a bracket or empty //document.writeln("@@",str,"#",symbol,"
"); if (symbol==null||symbol==rightBracket) return [null,"Error, '(' or symbol expected: "+str]; str=removeCharsAndBlanks(str,symbol.name.length); if (symbol==leftBracket) { //read (expr) estr=str2expr(str,0);expr=estr[0];str=estr[1]; if (str.slice(0,5)=="Error") return [null,str]; expr.priority = maxPriority; symbol=getSymbol(str); if (symbol==null) return [null,"Error, unknown symbol: "+str]; str=removeCharsAndBlanks(str,symbol.name.length); if (symbol!=rightBracket) return [null,"Error, ')' expected : "+str]; } else if (symbol.arity==0 || symbol.infix && symbol!=minus) {//read terminal expr=new Expr(symbol,[], maxPriority); } else { //read prefix expr1 ... exprN or prefix(...) nextsymbol = getSymbol(str); var commas=false; if (nextsymbol==leftBracket && symbol.paren) { str = removeCharsAndBlanks(str,nextsymbol.name.length); nextsymbol = getSymbol(str); commas=true; } more = nextsymbol!=null && (commas || !nextsymbol.infix && !nextsymbol.endSymb) && //nextsymbol!=norm) && nextsymbol!=rightBracket && nextsymbol!=comma; while (str!="" && more && (symbol.endName==null|| symbol.endName!=str.slice(0,symbol.endName.length))) { nextsymbol = getSymbol(str); //document.write(symbol.name,nextsymbol.name,"@",str,"
"); if (nextsymbol==null) return [null,"Error, incorrect symbol: "+str]; if (commas) estr=str2expr(str,-1); else estr=str2expr(str,symbol.priority); expr=estr[0];str=estr[1]; if (str.slice(0,5)=="Error") return [null,str]; add(arg,expr); nextsymbol = getSymbol(str); more = nextsymbol != null && (commas || !nextsymbol.infix && nextsymbol!=norm) && nextsymbol!=rightBracket; if (commas) if (str=="") return [null,"Error, ')' expected: "+str]; else if (str.charAt(0)==",") str=removeCharsAndBlanks(str,1); else if (str.charAt(0)==")") { more = false; str=removeCharsAndBlanks(str,1); } } expr=new Expr(symbol, arg, maxPriority); if (symbol.endName!=null) { //document.write(symbol.name,"#",str,"
"); if (symbol.endName!=str.slice(0,symbol.endName.length)) return [null,"Error, "+symbol.endName+" expected: "+str]; str=removeCharsAndBlanks(str,symbol.endName.length); } } //parse right argument of infix operation symbol = getSymbol(str);// infix op symb var insert=null; if (symbol==null || !symbol.infix || symbol.prioritysymbol.priority || (right.priority==symbol.priority && right.symbol.rightparen)) { expr=new Expr(symbol, [expr,right], symbol.priority); } else { insert=right; while (insert.arg[0].priority && insert.arg[0].prioritysymbol.priority || insert.arg[0].symbol.rightparen) { expr=new Expr(symbol, [expr,insert.arg[0]], symbol.priority); insert.arg[0]=expr; } else { expr=new Expr(symbol, [expr,insert.arg[0].arg[0]], symbol.priority); insert.arg[0].arg[0]=expr; } expr=right; } return [expr,str]; } function parse(str) { for (var i=str.length-1; i>=0 && str.charCodeAt(i)<=32; i--); str=str.slice(0,i+1); var temp = str2expr(str,0); if (temp[1].slice(0,5)=="Error") document.write("

"+temp[1]+"

"); else if (temp[1].slice(0,5)!="") { alert("Parse Leftover: >>>"+temp[1]+"<<<"); } //arities of expressions are not checked return temp[0]; } currentDefs = []; newSymbol("Defn",""); newSymbol("Axiom",""); mathdelimit="`"; function p(str) { var arr=str.split(mathdelimit); // string to delimit math expressions var expr=false; document.writeln("

"); for (var i=0; i"); document.writeln(" \""+ " onClick=\"applyRuleForward(",k,")\">"); document.write(e2s(ex.arg[0])); } else document.write(e2s(ex)); } // if (expr) document.write(exprtree(parse(arr[i]))); else document.writeln( (arr[i].charAt(0)==","||arr[i].charAt(0)=="."||arr[i].charAt(0)=="-"? "":"   "),arr[i],"   "); expr=!expr; } document.writeln(""); }