SAL to Core Translation

Programmer's Guide

Author: Tamim Nassar
Last updated: 1/5/2005

In this document:

  1. Application level design
  2. Translation level design
  3. Unfinished Issues
  4. How to Extend the Translation
  5. Implementation Notes
  6. Packaging and Distribution

External Libraries and Packages

The external libraries used are as follows:

  1. xml-apis.jar: This library contains the classes that process the XML files (DOM interfaces).
  2. sal2xml.jar: This library is a part of the SAL application. It converts SAL files into the XML form.

Application level design

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:

  1. SAL text to xml transformation:
    This is done using the sal2xml library that comes with SAL. In this part, the sal2xml application translates the SAL file into an XML form.
  2. SAL xml to CDL xml translation:
    This is the heart of the application. This part runs the actual translation. The sequence of translation consists of three parts: Parsing the input xml file, translating the elements and printing the output. Therefore the translation process is as follows:
  3. CDL xml to html transformation:
    This is done using an XSL transfromation. The application uses the XSL transformation to create an Html file from the XML representation of the core program that was generated in the step 2.

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.

Translation level design

Here's a description of the packages of the application:

  1. sal2core: This is the main package, it has class Main which runs the application.
  2. sal2core.parser: This is the parser package. It contains the xml parser which parses the SAL xml file.
  3. sal2core.xml: This package contains classes which are used by the parser.
  4. sal2core.salElements: The classes that represent the SAL xml structure.
  5. sal2core.coreElements: The classes that represent the CDL xml structure.
  6. sal2core.structures: Contains tables that help in translating the types.

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.

Unfinished Issues

  1. Renaming: The core language does not contain renaming, and the translation problem is yet to be solved.
  2. Let Expressions:  The translation problem is yet to be solved.
  3. Nested types: Explicit declaration is required in all type declarations including arrays.
  4. Multisynchronous/Multiasynchronous composition
  5. Additional info.
  6. Function declarations and complex type (such as lists and trees) declarations cannot be translated.

 How to Extend the Translation

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.

Implementation Notes

Packaging and Distribution

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.