header { package com.web4math.maxima.parser; import antlr.collections.*; import java.io.*; import java.util.*; } class MaximaOMParser extends Parser; options { k = 3; //two token lookahead //codeGenMakeSwitchThreshold = 5; //codeGenBitsetTestThreshold = 6; buildAST=true; ASTLabelType = "antlr.CommonAST"; // change default of "AST" } tokens { UNARY_MINUS; /* helper token to match unary minus. */ UNARY_PLUS; /* helper token to match unary plus. */ AST_TOP; /* imaginary token */ VAR; DOLLARVAR; LISP_ID; LISP_SYMBOL; MLIST; FUNCTION; BINDING; NUMBER; SIMP_OP; } sExpr : maximaGR { #sExpr = #([AST_TOP, "AST_TOP"], sExpr); } ; maximaGR : NUMBER | ID | LISP_OP | "SIMP" | VAR | DOLLARVAR //| LAMBDA^ {#maximaGR= #([BINDING,"BINDING"],maximaGR);} | maximaList ; // //| LPAREN! LISP_OP SIMP! RPAREN! /* maximaAtom : INT // {#maximaAtom= #([INT,"INT"],maximaAtom);} | ID //{#maximaAtom= #([LISP_ID,"LISP_ID"],maximaAtom);} | LISP_OP //{#maximaAtom= #([LISP_SYMBOL,"LISP_SYMBOL"],maximaAtom);} | VAR //{#maximaAtom= #([VAR,"VAR"],maximaAtom);} | DOLLARVAR //{#maximaAtom= #([DOLLARVAR,"DOLLARVAR"],maximaAtom);} ; */ maximaList : LPAREN! m:maximaGR (maximaGR)* RPAREN! {#maximaList= #([FUNCTION,"APPLICATION"],maximaList);} | LPAREN! LPAREN! LAMBDA "SIMP"! RPAREN! (maximaGR)* RPAREN! {#maximaList= #([BINDING,"BINDING"],maximaList);} | SHARP_QUOTE maximaGR ; //| LPAREN (maximaGR)+ "." maximaGR RPAREN //| LPAREN! LPAREN! LAMBDA "SIMP"! RPAREN! (maximaGR)* RPAREN! {#maximaList= #([BINDING,"BINDING"],maximaList);} /** * The Maxima Lexer.

* * It reads in the input that is said to be maxima input. It tries * to read the tokens that valid maxima input should have. If it * succeeds it generates a sequence of tokens, which in turn can * be read by the maxima parser.

* * @author Olga Caprotti. * @version 1.0 */ class MaximaOMLexer extends Lexer; tokens { LAMBDA = "LAMBDA"; //SIMP = "SIMP"; } SHARP_QUOTE : '#' ; //DOLLAR_SIGN : '$' ; //BAR_SIGN : '|' ; LPAREN : '(' ; RPAREN : ')' ; SEMI : ';' ; LISP_OP : '%'! ID; VAR : '|'! '$'! ID '|'!; DOLLARVAR : '$'! (LISP_OP|ID); /** * Reads whitespace and throws it away.

* * @since 1.0 */ WS : (' '|'\t'|'\n'|'\r') { _ttype = Token.SKIP; } ; protected DIGIT : '0'..'9'; NUMBER : ('-' | ) (DIGIT)+ ('.' (DIGIT)* ('e' ('-'| ) (DIGIT)+ | ) | ); STRING_LITERAL : '"'! (~'"')* '"'! ; ID options { testLiterals = true; } : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')* ; /** * Tree walker.

* * Transform the AST-tree to an OpenMath object. * * @author * @version 1.0 */ class MaximaOMTreeWalker extends TreeParser; { /** * Stores the hashtable for CD matching.

* * This hashtable is used to lookup which codecs handles * a given Maxima prefix operator.

* * Eg. ((MPLUS SIMP) 1, 2 ) -> arith1 plus

*/ protected Hashtable mDecodeHashtable = new Hashtable(); protected Hashtable mDecodeHashtable2 = new Hashtable(); /** * Stores the Maxima operators translated by this parser.

* * @since 1.0 */ private String[] sNames = { "MABS", "GCD", "LCM", "MPLUS", "MEXPT", "MNCEXPT", "PRODUCT", "SUM", "MTIMES", "MNCTIMES", "RAT", "MEQUAL", "MGEQP", "MGREATERP", "MLEQP", "MLESSP", "MNOTEQUAL", "SIN", "COS", "TAN", "SEC", "CSC", "COT", "SINH", "COSH", "TANH", "SECH", "CSCH", "COTH", "ASIN", "ACOS", "ATAN", "ASEC", "ACSC", "ACOT", "ASINH", "ACOSH", "ATANH", "ASECH", "ACSCH", "ACOTH", "EXP", "LOG", "LAMBDA", }; public String[] sNames2 = { "MATRIX", "UND", "INF", "MINF", }; public void fillTable () { mDecodeHashtable.put( "MABS", "arith1:abs"); mDecodeHashtable.put( "GCD", "arith1:gcd" ); mDecodeHashtable.put( "LCM", "arith1:lcm" ); mDecodeHashtable.put( "MPLUS", "arith1:plus" ); mDecodeHashtable.put( "MEXPT", "arith1:power" ); mDecodeHashtable.put( "MNCEXPT", "arith1:power" ); mDecodeHashtable.put( "PRODUCT", "arith1:product" ); mDecodeHashtable.put( "SUM", "arith1:sum" ); mDecodeHashtable.put( "MTIMES", "arith1:times" ); mDecodeHashtable.put( "MNCTIMES", "arith1:times" ); mDecodeHashtable.put( "MQUOTIENT", "arith1:divide"); // internally does as //mDecodeHashtable.put( "MMINUS", "arith1:unary_minus" ); // internally done with mtimes -1 //mDecodeHashtable.put( "SQRT", "arith1" ); //internally done as exp mDecodeHashtable.put( "RAT", "nums1:rational"); // encode a rational as a integer division mDecodeHashtable.put( "MEQUAL", "relation1:equal" ); mDecodeHashtable.put( "MGEQP", "relation1:" ); mDecodeHashtable.put( "MGREATERP", "relation1" ); mDecodeHashtable.put( "MLEQP", "relation1" ); mDecodeHashtable.put( "MLESSP", "relation1" ); mDecodeHashtable.put( "MNOTEQUAL", "relation1" ); mDecodeHashtable.put( "SIN", "transc1:sin" ); mDecodeHashtable.put( "COS", "transc1:cos" ); mDecodeHashtable.put( "TAN", "transc1:tan" ); mDecodeHashtable.put( "SEC", "transc1:sec" ); mDecodeHashtable.put( "CSC", "transc1:csc"); mDecodeHashtable.put( "COT", "transc1:cot" ); mDecodeHashtable.put( "SINH", "transc1:sinh" ); mDecodeHashtable.put( "COSH", "transc1:cosh" ); mDecodeHashtable.put( "TANH", "transc1:tanh" ); mDecodeHashtable.put( "SECH", "transc1:sech" ); mDecodeHashtable.put( "CSCH", "transc1:csch" ); mDecodeHashtable.put( "COTH", "transc1:coth" ); mDecodeHashtable.put( "ASIN", "transc1:arcsin" ); mDecodeHashtable.put( "ACOS", "transc1:arccos" ); mDecodeHashtable.put( "ATAN", "transc1:arctan" ); mDecodeHashtable.put( "ASEC", "transc1:arcsec" ); mDecodeHashtable.put( "ACSC", "transc1:arccsc" ); mDecodeHashtable.put( "ACOT", "transc1:arccoth" ); mDecodeHashtable.put( "ASINH", "transc1:arcsinh" ); mDecodeHashtable.put( "ACOSH", "transc1:arccosh" ); mDecodeHashtable.put( "ATANH", "transc1:arctanh" ); mDecodeHashtable.put( "ASECH", "transc1:arcsech" ); mDecodeHashtable.put( "ACSCH", "transc1:arccsch" ); mDecodeHashtable.put( "ACOTH", "transc1:arccoth" ); mDecodeHashtable.put( "EXP", "transc1:exp" ); mDecodeHashtable.put( "LOG", "transc1:ln" ); mDecodeHashtable.put( "LAMBDA", "fns1:lambda" ); mDecodeHashtable.put( "INF", "nums1:infinity" ); mDecodeHashtable.put( "ForAll", "quant1" ); mDecodeHashtable.put( "INTEGRATE", "calculus1:int" ); mDecodeHashtable.put( "Derivative", "calculus1" ); mDecodeHashtable.put( "D", "calculus1" ); mDecodeHashtable.put( "DERIVATIVE", "calculus1:diff" ); mDecodeHashtable.put( "Arg", "complex1" ); mDecodeHashtable.put( "Complex", "complex1" ); mDecodeHashtable.put( "Conjugate", "complex1" ); mDecodeHashtable.put( "Im", "complex1" ); mDecodeHashtable.put( "REALPART", "complex1" ); mDecodeHashtable.put( "MFACTORIAL", "integer1:factorial" ); mDecodeHashtable.put( "Quotient", "integer1" ); mDecodeHashtable.put( "Mod", "integer1" ); mDecodeHashtable.put( "MLIST", "list1:list" ); mDecodeHashtable.put( "DETERMINANT", "linalg1:det" ); mDecodeHashtable.put( "GAMMA", "nums1:gamma" ); mDecodeHashtable.put( "INF", "nums1:infinity" ); mDecodeHashtable.put( "T", "logic1:true" ); mDecodeHashtable.put( "NIL", "logic1:false" ); mDecodeHashtable2.put( "MATRIX", "list1:list" ); mDecodeHashtable2.put( "UND", "nums1:NaN" ); mDecodeHashtable2.put( "INF", "nums1:infinity" ); mDecodeHashtable2.put( "MINF", "!\n\n\n\n" ); mDecodeHashtable2.put( "E", "nums1:e" ); mDecodeHashtable2.put( "PI", "nums1:pi" ); mDecodeHashtable2.put( "I", "nums1:i" ); } /** * Transform a string into a valid XML string (i.e., no "&", "<", ">"). */ private String xmlify(String s) { StringBuffer sb = new StringBuffer(s); replaceChar(sb, '&', "&"); replaceChar(sb, '<', "<"); replaceChar(sb, '>', ">"); return sb.toString(); } private void replaceChar(StringBuffer buf, char c, String s) { // Replace all c in buf by s. int i = buf.toString().lastIndexOf(c); while (i != -1) { buf.replace(i, i + 1, s); i = buf.toString().lastIndexOf(c, i-1); } } } sExpr returns [String tResult] { String tLeft,tRight; tResult = ""; } : tTop : AST_TOP { //mDecodeHashtable.fill(); AST tChild = tTop.getFirstChild(); tResult = "\n"; if ( tChild != null ) { tResult += this.sExpr( tChild ); } tResult += "\n"; } | tNumber : NUMBER { String text = tNumber.getText(); if (text.indexOf(".") == -1) { tResult = "" + text + ""; } else { tResult = "\n"; } } | tVar : VAR { String varName=tVar.getText(); /* if all letters are lowercase, transform the name into uppercase */ if (varName.toLowerCase().equals(varName)) { varName=varName.toUpperCase(); } tResult = "\n"; } | tSym : LAMBDA { tResult = "\n"; } | tList : AST_LIST { /* * Add each child to the list.

*/ AST tChild = tList.getFirstChild(); tResult = "\n" + "\n"; if ( tChild != null ) { tResult += this.sExpr( tChild ); tChild = tChild.getNextSibling(); while( tChild != null ) { tResult += this.sExpr( tChild ); tChild = tChild.getNextSibling(); } } tResult += "\n"; } | tBinding : BINDING { /* * A lambda expression */ // the binder AST tChild = tBinding.getFirstChild(); // the bound variables are the arguments of a list AST tVarsList = tChild.getNextSibling(); // the body AST tBody = tVarsList.getNextSibling(); tResult += "\n"; tResult += this.sExpr( tChild ); tResult += "\n"; // it is always a list, take the arguments only AST tListHead = tVarsList.getFirstChild(); AST tChildVar = tListHead.getNextSibling(); while( tChildVar != null ) { tResult += this.sExpr( tChildVar ); tChildVar = tChildVar.getNextSibling(); } //tResult += this.sExpr( tVars ); tResult += "\n"; tResult += this.sExpr( tBody ); tResult += "\n"; } | tFunction : FUNCTION { /* * A function call */ AST tChild = tFunction.getFirstChild(); AST tSChild = tChild.getNextSibling(); // there is a second child, tFunction is of the form ( FUNCTION LAMBDA SIMP ) if (tSChild != null) { String tSimp = tSChild.toStringTree(); //System.out.println("testing SIMP: " + tSimp + "for" + tChild.toStringTree()); if ( tSimp.equals(" SIMP") ) //where is the space coming from? tResult += this.sExpr( tChild ); else { tResult = "\n"; tResult += this.sExpr( tChild ); tChild = tSChild; while( tChild != null ) { tResult += this.sExpr( tChild ); tChild = tChild.getNextSibling(); } tResult += "\n"; } } // no second child, tFunction is of the form ( FUNCTION MEXPT ) else { //System.out.println("no second child: " + tChild.toStringTree() ); tResult += this.sExpr( tChild ); } } | tIdentifier : ID { if ( tIdentifier.getText().equals( "ERROR" ) ) { tResult = "\n"; } // some ID name else if ( mDecodeHashtable.get(tIdentifier.getText())!=null ) { String tOMS = (String) mDecodeHashtable.get(tIdentifier.getText()); int tIndex = tOMS.indexOf(':'); tResult = "\n"; } else tResult = "\n"; } | tOp: LISP_OP { if ( mDecodeHashtable.get(tOp.getText())!= null) { String tOMS = (String) mDecodeHashtable.get(tOp.getText()); int tIndex = tOMS.indexOf(':'); tResult = "\n"; } else tResult = "\n"; } | tDollarvar : DOLLARVAR { if ( mDecodeHashtable2.get(tDollarvar.getText())!=null ) { String tOMS = (String) mDecodeHashtable2.get(tDollarvar.getText()); if (tOMS.charAt(0) != '!') { int tIndex = tOMS.indexOf(':'); tResult = "\n"; } else { // encoded strings tResult = tOMS.substring(1); } } else { tResult = "\n"; } } | tString : AST_STRING { tResult = "" + xmlify( tString.getFirstChild().getText() ) + "\n"; } ;