Author: Tamim Nassar
Last updated: 1/5/2005
The external libraries used are as follows:
The main method of the application is in class Main. The application can be configured, by editing config.xml. The translation sequence has three parts:
Class Main is responsible for launching the application. Its main(String[]) method receives two parameters: The name of the input file and the name of the output file. The method runs the transformations on the input file and produces two output files one in XML format and the other in HTML.
Here's a description of the packages of the application:
How does it work?
After parsing the SAL xml file, each SAL element loads the proper information from the DOM structure by using the implemented "parse" method in each SAL element.
Starting at the top SAL element (context), the translation process starts by invoking the "translate" method. Each SAL element knows which possible elements it may contain, and therefore it knows how to translate its children to the proper Core elements.
Class Main is also a singleton that holds the head of the CDL tree. This gives the SAL elements the ability to add modules or get modules from the Core program in order to view/update their information.

The structures package is responsible for holding a table that contains types and their translations. For example, the type translation table will hold type information for basic types such as Natural, Integer and Real which will be translated as Integers. However, the translation is not mono-morphic. Some SAL types can be translated into several types in Core. For example, a record will be translated into several types, and all these types will be stored in the type translation table for that record.
Array [1..N] OF INTEGER is allowed
if N a constant.Array [x:INTEGER | x<N] is not
allowed.The design of the translator is very simple. There's a package with all the elements of the SAL, and another with the elements of the Core. In order to change or extend an element in the translation, the programmer must find the specific location of that element in the SAL tree and then make the proper changes in the way it is translated into a core element.
The root of the transformation process is a method called "translateElement", but for expressions there's another method called "toCoreElement". The programmer has to start in these methods.
The "translateElement" method is responsible for invoking the translation process of the element. It may be very simple, such as in the following example. In this example which is from class BaseModule, the "translateElement" method only invokes the "translateElement" method's of its children.
public java.util.List translateElement(CoreElement parent) {
System.out.println("Translating: "+ID);
Iterator it = elements.iterator();
while (it.hasNext()){
SalElement e = (SalElement)it.next();
e.translateElement(parent);
}
return null;
}
|
However, the translateElement method may be more complex. For example, in the TransDecl element, the element needs to identify the children's types, and then it invokes special auxiliary methods that handle the translation of the identified elements.
public java.util.List translateElement(CoreElement parent) {
System.out.println("Translating: "+ID);
Iterator it = iterator();
while (it.hasNext()){
SalElement element = (SalElement)it.next();
if (element instanceof ForallDefinition){
handleForAllDefinition((ForallDefinition)element, parent);
}
if (element instanceof SimpleDefinition){
handleSimpleDefinition((SimpleDefinition)element,parent);
}
if (element instanceof SomeCommands){
handleSomeCommands((SomeCommands)element,parent);
}
}
setCondSequenceTransition((CoreModule)parent);
return null;
}
|
Some elements need to be translated to a common element type. For example, conditional expressions, applications numeral and other element types in Sal need to be translated into core expressions. Therefore, a method called "toCoreExpression" should be implemented. The following example is a simple example of "toCoreExpression" from class Numeral.
public CoreExpr toCoreExpression() {
CoreNumber num = new CoreNumber(numeral);
CoreGenconst gc = new CoreGenconst(num);
CoreConstant c = new CoreConstant(gc);
CoreExpr expr = new CoreExpr(c);
return expr;
}
|
However this method can be much more complex such as in the case of element Application. In this example we can see that there are different kinds of Applications. Some have only one parameter and others have two. The "toCoreExpression" method checks the children and decides how to translate the element.
public CoreExpr toCoreExpression() {
if (firstExpr == null || !(firstExpr instanceof NameExpression)){
System.out.println(ID+" First exptession is not NameExpression");
return null;
}
if (secondExpr == null || !(secondExpr instanceof TupleLiteral)){
System.out.println(ID+" Second exptession is not TupleLiteral");
return null;
}
NameExpression nameExpr = (NameExpression)firstExpr;
TupleLiteral tuple = (TupleLiteral)secondExpr;
if (tuple.expressions.size() == 2){
CoreExpr expr1 = ((Expression)tuple.expressions.get(0)).toCoreExpression();
CoreExpr expr2 = ((Expression)tuple.expressions.get(1)).toCoreExpression();
CoreExpr ret = null;
if (nameExpr.getName().equals(EQ)){
ret = new CoreExpr(new CoreEQUAL(expr1,expr2));
}
else if (nameExpr.getName().equals(NEQ)){
ret = new CoreExpr(new CoreNOTEQUAL(expr1,expr2));
}
else if (nameExpr.getName().equals(PLUS)){
ret = new CoreExpr(new CorePLUS(expr1,expr2));
}
else if (nameExpr.getName().equals(MINUS)){
ret = new CoreExpr(new CoreMINUS(expr1,expr2));
}
else if (nameExpr.getName().equals(TIMES)){
ret = new CoreExpr(new CoreTIMES(expr1,expr2));
}
else if (nameExpr.getName().equals(DIVIDE)){
ret = new CoreExpr(new CoreDIVIDE(expr1,expr2));
}
else if (nameExpr.getName().equals(AND)){
ret = new CoreExpr(new CoreAND(expr1,expr2));
}
else if (nameExpr.getName().equals(OR)){
ret = new CoreExpr(new CoreOR(expr1,expr2));
}
else if (nameExpr.getName().equals(LE)){
ret = new CoreExpr(new CoreLE(expr1,expr2));
}
else if (nameExpr.getName().equals(LT)){
ret = new CoreExpr(new CoreLT(expr1,expr2));
}
else if (nameExpr.getName().equals(GE)){
ret = new CoreExpr(new CoreGE(expr1,expr2));
}
else if (nameExpr.getName().equals(GT)){
ret = new CoreExpr(new CoreGT(expr1,expr2));
}
else if (nameExpr.getName().equals(MOD)){
ret = new CoreExpr(new CoreMOD(expr1,expr2));
}
if (ret != null)
return ret;
}
if (tuple.expressions.size() == 1){
List list = ((Expression)tuple.expressions.get(0)).translateElement(null);
CoreExpr expr1 = (list.isEmpty())?null:(CoreExpr)list.get(0);
CoreExpr ret = null;
if (nameExpr.getName().equals(MINUS)){
ret = new CoreExpr(new CoreMINUS(expr1));
}
if (ret != null)
return ret;
}
return new CoreConstant("F#"+nameExpr.getName()+"#",false).toExpr();
}
|
The "toCoreExpression" method and similar getter methods are very useful, because they can be called from many locations and give a convenient result. Application elements can be found in conditional expressions, in definitions, in assignments, in other applications and other elements.
How can translated core elements be placed in the proper places?
The "translateElement" method receives a core element "parent". This element lets the receiver element add its translation results to it in a convenient way. For example if we need to add an assignment that was found in a sequence of assignments:
private void addAssignment(CoreModule parent, Expression lhs, Expression rhs){
if (parent.defaultTransition == null){
parent.defaultTransition = new CoreTransition();
parent.defaultTransition.enable = new CoreEnable(new CoreTRUE().toExpr());
parent.addTransition(parent.defaultTransition);
parent.defaultTransition.assign = new CoreAssign();
parent.defaultTransition.setTransitionName("T#"+counter++);
}
CoreAssignment assignment = new CoreAssignment(lhs.toCoreExpression(),rhs.toCoreExpression());
if (parent.defaultTransition.assign==null) parent.defaultTransition.assign = new CoreAssign();
parent.defaultTransition.assign.addAssignment(assignment);
}
|
In this example, the parent is passed to the "addAssignment" method, so TransDecl can add the translation directly to the parent model.
The application should be packaged inside one single ZIP file called sal2core.zip which should contain the following files:
| sal2core.jar | The jar file generated when building the project. |
| translate | The application's executable file (under c-shell). |
| translate.bat | The application's executable file (under DOS). |
| config.xml | Configuration file. |
| cdl.dtd | DTD of the Core language. |
| sal.dtd | DTD of the SAL language. |
| xml2cdl.xsl | XSL transformation script for translating Core XML to Core html. |
| lib\sal2xml.jar | Library for transforming SAL text into SAL XML. |
| lib\xml-apis.jar | DOM library for XML processing. |
| lib\xml-apis.LICENSE.txt | License for the xml-apis library. |