AÏOLI

The exp contribution

We present here a simple interactive calculator written using Aļoli. The contribution starts with three description files:
  1. exp.metal for the definition of the abstract syntax,
  2. exp-std.ppml for the definition of the prettyprinting,
  3. exp.g for the definition of the parser.
We then give an example of how to integrate all these different components. All the files that are described here are in the contrib directory of the Aļoli distribution. In order to be able to run the examples below make sure that your classpath variable is properly set. It must contain both the path to the directory contrib plus the standard path to the Aļoli distribution. If for example Aļoli is installed under the directory /net, we have
CLASSPATH:=/net/aioli/contrib/:/net/aioli/classes:$CLASSPATH

Metal

Because Aļoli uses a package convention to be able to retrieve automatically formalism definition, the metal definition has to be done in the lang/exp subdirectory. The formalism is defined in the exp.metal file. There is nothing much to be said about the definition. We simply define three binary nodes and an atomic node to hold integer value:
formalism of exp is
      EXP ::= plus mult minus int;
      plus  -> EXP EXP;
      minus  -> EXP EXP;
      mult -> EXP EXP ;
      int  -> implemented as INTEGER ;
end 

To compile this file we first need to produce an intermediate java code. It is done using the metal compiler

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.

Ppml

Aļoli uses a package convention to retrieve automatically prettyprinters, so ppml definition of exp must be put inside the lang/exp subdirectory. We first write in the exp-std.ppml file a naive version of the prettyprinter than we will later refine:
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

To compile a specification we follow the same steps than for metal but this time using the ppml compiler. We first produce intermediate java sources:

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.java
The 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

The second improvement is to produce a correct parenthesing for arithmetic expressions. We first need to define auxiliary functions that associate precedences to operators. This is done in ppaux.java:
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;
  }
}

We can now use the functions of the ppaux class to implement correct parenthesing. We use the context par to add parenthesis and a test in each binary node to check whether or not parenthesing is needed.
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

Antlr

The next step is to write a parser for Exp. Here is the simple definition of the parser:
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')*
	;

To compile this file, we use the antlr tool:
java antlr.Tool exp.g
It produces several java files. The main file we need to compile is the ExpParser:
javac ExpParser.java

Example

We first define in the file demo.java a simple application that pops up a window with a text field where the user can type in arithmetic expressions and a Ctedit below that displays the corresponding parsed trees using the ppml definition:
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);
    }
  }
}

We compile the file:
 javac demo.java
Running the exp.demo class will give the expected result:
 java exp.demo
We 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;
  }
}

We now define the class EvalBut the button whose action will be to evaluate the current selection
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);
   }
}

We can now add the button to the window in demo.java
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);
    }
  }
}