CLASSPATH:=/net/aioli/contrib/:/net/aioli/classes:$CLASSPATH
formalism of exp is EXP ::= plus mult minus int; plus -> EXP EXP; minus -> EXP EXP; mult -> EXP EXP ; int -> implemented as INTEGER ; end
java aioli.metal.Compile lambda.metal It creates a file formalism.java that we can compile as a standard java file:
javac formalism.java
We get as expected the corresponding class file formalism.class. If you have any problem in completing the previous two steps, either you are using an old version of java (<1.1) or your CLASSPATH is not properly set.
prettyprinter std of exp is plus(*x, *y) -> [<hv 1,1,0> *x "+" *y]; mult(*x, *y) -> [< 1,1,0> *x "*" *y]; minus(*x, *y) -> [<hv 1,1,0> *x "-" *z]; int *x -> identitypp(*x); end prettyprinter
java aioli.ppml.Compile exp-std.ppml
We get two java files BBstd.java et PPstd.java. We then simply need to compile the latter:
javac PPstd.javaThe first improvement we present is to take into account the depth of the tree in the prettyprinting. We use the symbol "..." to elide subterms at a given depth. For this we add a rule that is triggered when the detail level reaches 0 (remember that detail level is decremented in the recursive call). The fact that this rule is the first one in the specification gives it the highest priority:
prettyprinter std of exp is *x!0 -> "..."; plus(*x, *y) -> [<hv 1,1,0> *x "+" *y]; mult(*x, *y) -> [< 1,1,0> *x "*" *y]; minus(*x, *y) -> [<hv 1,1,0> *x "-" *z]; int *x -> identitypp(*x); end prettyprinter
package lang.exp; import aioli.vtp.*; public class ppaux extends lang.ppaux { public static int prec(Tree tree) { String str = tree.operatorName(); if ("int".equals(str)) return 0; if ("mult".equals(str)) return 1; return 2; } public static boolean leftp (Tree tree1, Tree tree2) { if (prec(tree2) <= prec(tree1)) return false; return true; } public static boolean rightp (Tree tree1, Tree tree2) { if (prec(tree2) < prec(tree1)) return false; return true; } }
prettyprinter std of exp is auxillary package "lang.exp.ppaux"; *x!0 -> "..."; par:*x -> [<h 0> "(" *x ")"]; *z as plus(*x, *y) -> [<hv 1,1,0> if leftp(*z,*x) then par:*x else *x end "+" if rightp(*z,*y) then par:*y else *y end]; *z as mult(*x, *y) -> [<hv 1,1,0> if leftp(*z,*x) then par:*x else *x end "*" if rightp(*z,*y) then par:*y else *y end]; *z as minus(*x, *y) -> [<hv 1,1,0> if leftp(*z,*x) then par:*x else *x end "-" if rightp(*z,*y) then par:*y else *y end]; int *x -> identitypp(*x); end prettyprinter
aExpression: mExpression (("+"|"-") mExpression)* mExpression: fExpression ("*" fExpression)* fExpression: int | "(" aExpression ")" int: 0| ("1".."9")('0'..'9')*For the moment there is no mechanism to retrieve parser, parser can belong to arbitrary packages. So we arbitrary decide to write the parser in the exp subdirectory in the file exp.g. Adding a header and vtp actions to the previous definition gives:
header {package exp;} { import java.io.*; import aioli.vtp.*; } class ExpParser extends Parser; options { tokenVocabulary=Exp; vtpLanguage = "exp"; } { //class Main { public static Tree parse(InputStream ds) throws Exception { try { ExpLexer lexer = new ExpLexer(ds); ExpParser parser = new ExpParser(lexer); return parser.aExpression(); } catch(Exception e) { System.err.println("exception: "+e); throw e; // so we can get stack trace } } //} } aExpression returns [Tree tr] << tr1 == *; tr = *; >> : tr=mExpression (( PLUS <<tr=plus(*tr,*);>> | MINUS <<tr=minus(*tr,*);>> ) tr1=mExpression <<tr.2=*tr1;>> )* ; mExpression returns [Tree tr] << tr1 == *; tr = *; >> : tr=fExpression ( MULT <<tr=mult(*tr,*);>> tr1=fExpression <<tr.2=*tr1;>> )* ; fExpression returns [Tree tr] << tr = *;>> : id1:INTEGER <<tr = int *id1^;>> | LPAREN tr=aExpression RPAREN ; class ExpLexer extends Lexer; options { tokenVocabulary=Exp; } LPAREN : '(' ; RPAREN : ')' ; PLUS : '+'; MULT : '*'; MINUS : '-'; // MISC STUFF WS : (' ' | '\t' | '\n' { newline(); } | '\r') { _ttype = Token.SKIP; } ; INTEGER options { testLiterals=true; } : ('1'..'9') ('0'..'9')* ;
java antlr.Tool exp.gIt produces several java files. The main file we need to compile is the ExpParser:
javac ExpParser.java
package exp; import java.io.*; import java.awt.*; import java.awt.event.*; import aioli.vtp.*; import aioli.variable.*; import aioli.gfxobj.*; // The action associated with the text field class ReadExpListener implements ActionListener { Ctedit _ct; TextField _field; public ReadExpListener(Ctedit ct, TextField f) { _ct= ct; _field = f; } public void actionPerformed(ActionEvent ev) { try { if (ev.getID()==ActionEvent.ACTION_PERFORMED) { String exp=_field.getText(); InputStream in = new StringBufferInputStream(exp); // We parse the new input Tree tree = ExpParser.parse(in); if (tree == null) return; // We retrieve the variable Variable var = _ct.getVar(); // we replace the root of the variable with the new tree var.change(var.root(),tree); } } catch (FileNotFoundException e) { System.err.println("File Not Found"); } catch (java.lang.Exception e) { System.err.println("exception: "+e); e.printStackTrace(); } } } public class demo extends Frame { public static void main(String[] args) { new demo(); } public demo() { try { String init = "1+2"; InputStream in = new StringBufferInputStream(init); // we parse a default tree Tree tree = ExpParser.parse(in); // we put the tree in a fresh variable Variable var = new Variable(tree); // Now we can create the Ctedit Ctedit ct = new Ctedit(var,"std"); // We get the AWT component to create the window Component cn = ct.getComponent(); setLayout(new BorderLayout()); // A text file to type in new expression TextField field = new TextField(init,20); field.addActionListener(new ReadExpListener(ct,field)); // The text file is up add("North",field); // The ctedit is in the middle add("Center",cn); setSize(600,400); setTitle("Exp"); setVisible(true); // We set the default style ct.setStyle("DEFAULT","times-17","blue","white"); // We draw the result, pagelength=120, detail=20 ct.redraw(120,20); } catch(Exception e) { System.err.println("exception: "+e); } } }
javac demo.javaRunning the exp.demo class will give the expected result:
java exp.demoWe can refine this example by adding a button that evaluates the selected expression. For this we first need to define what is the evaluation. This is done in the file Eval.java:
package exp; import aioli.vtp.*; public class Eval { static int eval(Tree tr) { String name = tr.operatorName(); if ("int".equals(name)) return tr.atomValue().num(); int res1 = eval(tr.down(1)); int res2 = eval(tr.down(2)); if ("plus".equals(name)) return res1+res2; if ("minus".equals(name)) return res1-res2; if ("mult".equals(name)) return res1*res2; return -1; } }
package exp; import java.awt.*; import java.awt.event.*; import aioli.variable.Variable; import aioli.vtp.*; class EvalListener implements ActionListener { Formalism fm = FManager.getFormalism("exp"); Variable _var; Tree _tree; Tree _old ; EvalListener(Variable var) { _var=var; _tree=null; _old =null; } public void actionPerformed(ActionEvent ae) { String str=((Button)(ae.getSource())).getLabel(); // we get the current tree Tree tree = _var.getCurrentTree(); if (tree == null) return; // the result is an int note Tree res = fm.operator("int").tree(); // we set the value of the atomic note to be the evaluation of the // current tree res.atomReplace(new IAtomValue(Eval.eval(tree))); _var.change(tree,res); } } public class EvalBut extends Panel { protected void makebutton(String name, GridBagLayout gridbag, GridBagConstraints c, EvalListener el) { Button button = new Button(name); gridbag.setConstraints(button, c); button.addActionListener(el); add(button); } public EvalBut(Variable var) { GridBagLayout g = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); EvalListener el = new EvalListener(var); setLayout(g); c.fill = GridBagConstraints.BOTH; setLayout(g); c.weighty = 0.0; //reset to the default c.gridwidth = GridBagConstraints.REMAINDER; //end row c.gridheight = 1; //reset to the default makebutton("Eval",g,c,el); c.weighty = 1.0; //reset to the default java.awt.Canvas cv = new java.awt.Canvas(); g.setConstraints(cv, c); add(cv); } }
package exp; import java.io.*; import java.awt.*; import java.awt.event.*; import aioli.vtp.*; import aioli.variable.*; import aioli.gfxobj.*; class ReadExpListener implements ActionListener { Ctedit _ct; TextField _field; public ReadExpListener(Ctedit ct, TextField f) { _ct= ct; _field = f; } public void actionPerformed(ActionEvent ev) { try { if (ev.getID()==ActionEvent.ACTION_PERFORMED) { String exp=_field.getText(); InputStream in = new StringBufferInputStream(exp); Tree tree = ExpParser.parse(in); if (tree == null) return; Variable var = _ct.getVar(); var.change(var.root(),tree); } } catch (FileNotFoundException e) { System.err.println("File Not Found"); } catch (java.lang.Exception e) { System.err.println("exception: "+e); e.printStackTrace(); } } } public class demo extends Frame { public static void main(String[] args) { new demo(); } public demo() { try { String init = "1+2"; InputStream in = new StringBufferInputStream(init); Tree tree = ExpParser.parse(in); Variable var = new Variable(tree); Ctedit ct = new Ctedit(var,"std"); Component cn = ct.getComponent(); setLayout(new BorderLayout()); TextField field = new TextField(init,20); field.addActionListener(new ReadExpListener(ct,field)); add("North",field); add("Center",cn); // We add the eval but add("West",new EvalBut(var)); setSize(600,400); setTitle("Exp"); setVisible(true); ct.setStyle("DEFAULT","times-17","blue","white"); ct.redraw(120,20); } catch(Exception e) { System.err.println("exception: "+e); } } }