-
Notifications
You must be signed in to change notification settings - Fork 1
rALF API Usage
#rALF API Usage
The EMDW-MC project uses a simplified version of the ALF language to define the dynamic behavior of xtUML models. The rALF parser and generator API can be used to parse these action code segments, and generate C++ code fragments based off of them.
The usage of these API classes relies on the Google Guice dependency injection framework.
Tha rALF API at this early stage, consists of two main main interfaces. Each one of these interfaces has one reference implementation. Naturally, custom implementations can be defined by the user.
The IReducedAlfParser interface enables the user to create an ALF AST (Abstract Syntax Tree) based on a given ALF code segment, or behavioral unit containing the aforementioned code segment. The reference implementation (ReducedAlfParser.class) creates a synthetic resource for the rALF AST. If the resource is loaded, it is filled with the rALF AST. This approach is similar to the way Xtext based editors parse domain specific languages.
public interface IReducedAlfParser {
/**
* Creates a rALF AST based on the specified rALF code.
* @param behavior
* @return
*/
public Statements parse(String behavior);
/**
* Extracts the rALF code from a specified OpaqueBehavior instance, and creates the corresponding rALF AST.
* @param behavior
* @return
*/
public Statements parse(OpaqueBehavior behavior);
}The IReducedAlfGenerator interface provides support for the creation of C++ code snippets based on rALF ASTs or AST fragments. It also has methods for generating C++ snippets from rALF snippets in one step, using a specified IReducedALFParser instance.
public interface IReducedAlfGenerator {
/**
* Returns tha map that contains C++ snippets and their corresponding rALF fragments.
* @return
*/
public Map<String,String> getSnippetMap();
/**
* Creates a C++ snippet based on the defined rALF code using the provided rALF parser.
* @param behavior String containing the rALF code
* @param parser Parser used for parsing the rALF code
* @return
*/
public String createSnippet(String behavior, IReducedAlfParser parser);
/**
* Creates a C++ snippet based on the the rALF code, which is contained by the specified opaque behavior
* using the provided rALF parser.
* @param behavior Opaque behavior containing the rALF code
* @param parser Parser used for parsing the rALF code
* @return
*/
public String createSnippet(OpaqueBehavior behavior, IReducedAlfParser parser);
/**
* Creates a C++ snippet based on a given rALF AST.
* @param rootElement
* @return
*/
public String createSnippet(EObject rootElement);
}The usage of this programming interface, is demonstrated via the following example JUnit test: Example test location.
During the usage of this API the following main steps need to be undertaken:
- Define injector provider.
- This injector provider should create an injector consisting of the following modules:
- ReducedAlfLanguageRuntimeModule: This module is responsible for the initialization of the rALF parser among other language framework elements.
- A new module that defines the binding rules of the API classes (Note that the introduction of a module class that does this is planned, this way less boilerplate code is needed).
- Create an injector using the injector provider.
- Create API class instances using the injector.
- Use the API classes.
Google Guice injectors can be defined using modules, sets of binding rules. If the injector receives a request to instantiate a certain class or interface, it consults its binding definitions, and based on them, instantiates the correct instance. In the following section, the injector provider required to use the API is shown.
public class ReducedAlfLanguageJUnitInjectorProvider extends ReducedAlfLanguageInjectorProvider {
@Override
protected Injector internalCreateInjector() {
// register default ePackages
if (!Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().containsKey("ecore"))
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("ecore",
new org.eclipse.emf.ecore.xmi.impl.EcoreResourceFactoryImpl());
if (!Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().containsKey("xmi"))
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("xmi",
new org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl());
if (!Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().containsKey("xtextbin"))
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("xtextbin",
new org.eclipse.xtext.resource.impl.BinaryGrammarResourceFactoryImpl());
if (!EPackage.Registry.INSTANCE.containsKey(org.eclipse.xtext.XtextPackage.eNS_URI))
EPackage.Registry.INSTANCE.put(org.eclipse.xtext.XtextPackage.eNS_URI,
org.eclipse.xtext.XtextPackage.eINSTANCE);
//Create the base rALF module
Module runtimeModule = (Module) new com.incquerylabs.uml.ralf.ReducedAlfLanguageRuntimeModule();
//create a new module that binds the API classes
Module customizations = new Module() {
@Override
public void configure(Binder binder) {
binder.bind(IUMLContextProvider.class).toInstance(new TestUMLContextProvider());
binder.bind(ReducedAlfSnippetCompiler.class).toInstance(new ReducedAlfSnippetCompiler());
binder.bind(IReducedAlfGenerator.class).to(ReducedAlfGenerator.class);
binder.bind(IReducedAlfParser.class).to(ReducedAlfParser.class);
}
};
//create a new injector based off the modules
return Guice.createInjector(runtimeModule, customizations);
}
}Once the injector provider is created, it is time to use the API classes. As shown in the example test case below, create an injector using the created injector provider. Use this injector to instantiate the parser and generator classes. Finally, use the API to parse a rALF code fragment, and the generator to generate the C++ code snippets.
class ExampleTest {
// Injector that creates the parser and the generator
Injector injector;
IReducedAlfGenerator generator
IReducedAlfParser parser
@Before
def void init() {
// Get an injector instance from the provider defined by the user
// In this provider, we can define that certain which interface is implemented by which class.
// If the specified interface is required, the injector will then create an instance of the specified class.
var provider = new ReducedAlfLanguageJUnitInjectorProvider();
injector = provider.injector
generator = injector.getInstance(IReducedAlfGenerator)
parser = injector.getInstance(IReducedAlfParser)
}
@Test
def exampleTestCase() {
snippetCompilerTest(
'''
Integer x = (1 + 2) * 3 + -4;
++x;
Integer y = x;
y = x - 15;
if ((x > 3) && !(y < -5)) {
x--;
}''', '''
PrimitiveTypes::Integer x = (1 + 2) * 3 + -4;
++x;
PrimitiveTypes::Integer y = x;
y = x - 15;
if ((x > 3) && !(y < -5)) {
x--;
}''')
}
def snippetCompilerTest(String input, String expected) {
//the usage of the generator can be seen here.
val snippet = generator.createSnippet(input, parser)
assertEquals("The created snippet does not match the expected result", expected, snippet)
}
}