Skip to content

rALF API Usage

lunkpeter edited this page Jul 7, 2015 · 9 revisions

#rALF API Usage

Introduction

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.

rALF API classes

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.

IReducedAlfParser

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);
    
}

IReducedAlfGenerator

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);  
}

Examples

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.

Example Injector Provider

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);
    }

}

Example Test Case

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)
	}
}

Project Locations

Clone this wiki locally