Skip to content

compiler

Spellmaker edited this page Mar 31, 2015 · 7 revisions

Notes on the CoreASM Compiler

Usage

The CoreASM Compiler can currently be used in two different ways, described in the following paragraphs

  1. Using the eclipse plugin

The compiler can be launched via the eclipse integration of the general CoreASM Plugin. Right-clicking a specification and selecting export will open the eclipse export dialog. The compiler hides behind the CoreASM -> Export to Jar entry, which will show a configuration dialog.

  1. Instantiating the compiler via an external project

Even though the compiler package contains a CommandLineParser and a Main class, these are both outdated and cannot be used anymore without updating them. The compiler can still be called by creating an external project with the org.coreasm.engine project added as a dependency. This project can then instantiate the org.coreasm.compiler.CoreASMCompiler class by providing it a org.coreasm.compiler.CompilerOptions instance. This instance contains the settings for the compilation. The following fields should be set: -SpecificationName: The name of the source specification -enginePath: The path to a compiled CoreASM engine. This requires that the coreasm.engine project was build at least once. The engine can then be found in the "target" directory

Method 2 is a bit more difficult to use, but provides greater flexibility for the developer. If additional properties (such as the external plugin directory) need to be set, compiler needs to be instantiated with an initialized CoreASM Engine. A CoreASM Engine can be created using the org.coreasm.engine.CoreASMEngineFactory.createEngine() method. Properties can then be changed via the .setProperty(key, value) method. Property keys can be found in the EngineProperties class. The engine then needs to be initialized using the initialize() function.

Architecture

This section provides an overview of the general architecture of the compiler. More information about specific classes can be found in the javadoc comments.

Package overview:

Package structure

  • CompilerRuntime

The compiler uses several classes for each specification, regardless of the contents. As it reuses concepts of the interpreter (to stay as true as possible to its behaviour), some files are simply drawn from the CoreASM sources included in the engine.jar. A few runtime files are to different from the interpreter implementation, so they have been copied to this package and adapted to the compilers needs.

Some of the classes in the CompilerRuntime are original compiler creations. These are: -EvalStack The evaluation stack used during rule execution -LocalStack The local stack used for local variables -ProgramFunction The program function, which needs to be special -Rule The base class for all Rules -RuleParam Base class for rule parameters -RuleResult The result of a rule evaluation -Runtime Provides runtime-support for code execution -RuntimeProvider Provides access to the runtime

Future modifications could move the contents further into the compiler package. Modifications in the CoreASM classes should (if applicable) be introduced to the contents of this package aswell.

  • compiler

The compiler package is the actual root package of the compiler implementation. The main content is the CompilerEngine interfaces, which specifies the compiler api and the actual compiler implementation in the CoreASMCompiler class. The package also contains the configuration object for the compiler, the CompilerOptions class. Last but not least, the CodeType enumeration represents the different compilation contexts (The concept of compilation contexts is explained in in section

).

Last but not least, the package also contains a Main class, which was used during earlier development to run the compiler. This class is unfortunately a bit outdated (or to be more precise - the command line parser in the compiler.components.commandline package is rather outdated) and needs some love before the compiler can be launched via this entry point.

  • codefragment

This package houses the CodeFragment class. The goal of this class is to make code generation as easy as possible for plugin developers. This is achieved by providing different makros, which handle insertion, variable name generation and global naming of components.

  • exception

Contains most exception classes thrown in the compiler and its components.

  • interfaces

Contains the base interfaces (and in the case of the CompilerCodePlugin: base classes) for plugin types supported by the compiler. Most interfaces match a certain CoreASM plugin type (e.g. the CompilerOperatorPlugin is similar to the OperatorPlugin), but there are a few additional plugin types:

  • CompilerBackendProvider
  • CompilerFunctionPlugin
  • CompilerInitCodePlugin
  • CompilerMainClassProvider
  • CompilerMakroProvider
  • CompilerPathPlugin
  • CompilerPreprocessorPlugin Details about the different types can be found in the javadoc.
  • paths

The compilation will result in a bunch of source files (which will usually be compiled into a jar archive). These source files also adhere to a certain structure. The default structure is:

Project structure

Where Main.java is the main entry point, CompilerRuntime contains the runtime files and Rules the rules of the specification. The plugins package contains the subpackages dynamic (for specification dependent files) and static (which contains all files specific only to the plugin).

To allow for more customizability, the programs structure is configured using a path object.

  • plugins

Contains the implementation of different compiler plugins. The structure of the packages inside a plugin package is:

Plugin structure

Where the code subpackage contains the different code providers, the include package may contain additional includes and the preprocessor package preprocessor logic. Note though that the plugin structure is actually optional, but using this layout is recommended.

  • components

As mentioned above, the components package contains different components of the compiler. These components are:

*) backend

Contains the default backend and the backend interfaces for the compiler. Backend stands for everything which happens after the specifications parse tree has been processed and code has been generated for it in-memory. The default implementation simply dumps all these files to the hard drive, compiles the generated code using the java compiler installed on the system and then packages the resulting classes into an executable jar archive. More sophisticated backends might merge different projects together, e.g. to include the specification as a component of a larger system.

*) classlibrary

The classlibrary manages the in-memory representation of different compiler objects. At a basic level, there are currently two different types of compiler objects: JarIncludes, which represent files already existing on the hard drive in a jar archive and MemoryIncludes, which represent objects held completely in memory.

*) commandline

Contains the outdated command line parser used by the Main.java in the compiler root to launch the compiler.

*) logging

Contains a logging helper, which allows for easier access to logging methods (warning, error and debug) and provides the MessageListener interface, which allows components to listen for compiler messages (mainly used to implement the eclipse integration)

*) mainprogram

After the code for different rules has been generated, the compiler somehow needs to generate a class which glues everything together. The mainprogram packages contains the implementation of this class, which uses an internal state machine to represent the different states of the generated program (similar to the CoreASM interpreter states). The default implementation will generate a threadable state machine class for the specification and a Main class, which instantiates a thread using this state machine.

*) pluginloader

Contains the pluginloader interface and an implementation DummyLoader (which still fulfills all current requirements). As the actual plugin loading is already done by the CoreASM Engine, these classes mainly classify and convert CoreASM Plugins into Compiler versions.

*) preprocessor

Implements the basis for the analysis framework of the compiler. Using contributions from plugins, the preprocessor analyses the parse tree of the specification and attaches additional information to the nodes. The interfaces InheritRule and SynthesizeRule represent the basic rule interfaces for plugins. A Trigger is a condition, upon which a rule will fire and the Information class is an attempt to allow plugins to store arbitrary information in an organized way. Note that this attempt should be reworked.

*) variablemanager

The Variable Manager ensures, that the generated code will not contain conflicting variable declarations (as in int i; int i;). The manager can be invoked directly to open or close scopes or to create a new CompilerVariable instance. Usually though the class will be invoked indirectly by using a CodeFragment.

Developing a plugin

All plugins require a CoreASM Plugin version, because they will be initially loaded by a CoreASM Engine. Therefore, a plugin with minimal CoreASM actions will consist of at least two classes. One of them will extend the org.coreasm.engine.plugin.Plugin class (and will thereby be the entry point for the CoreASM engine). The other will implement the org.coreasm.compiler.interfaces.CompilerPlugin interface.

The two most important CoreASM plugin interfaces are the InterpreterPlugin (which allows to provide interpreter functionality), the ParserPlugin (which extends the parser) and the VocabularyExtender (which allows to introduce functions etc). This manual won't go into any more detail about CoreASM plugins. More information can be found in the Design Documentation.

The compiler interfaces can be found in the package org.coreasm.compiler.interfaces. They provide the following possibilities:

  • CompilerBackendProvider => allows to replace or extend the code generation and packaging parts of the compiler.

  • CompilerCodeHandler => used together with the CompilerCodePlugin class; handles code generation for a specific type of ASTNode in a specific context

  • CompilerCodePlugin => provides code generation for ASTNodes to the compiler

  • CompilerExtensionPointPlugin => provides extensions to the state machine of the generated program. extensions trigger on specified transitions of the machine

  • CompilerFunctionPlugin => simple optimization plugin, which replaces a function call with a specific piece of code

  • CompilerInitCodePlugin => adds code to the initialization of the state machine

  • CompilerMainClassProvider => allows to replace the main class of the generated program (the main class usually instantiates and runs the state machine)

  • CompilerMakroProvider => provides global makros (replacements) for CodeFragments

  • CompilerOperatorPlugin => provides operators. Operators can be overloaded, therefore they are handled differently from normal ASTNodes

  • CompilerPathPlugin => allows to replace the default path configuration

  • CompilerPlugin => base interface for all compilable plugins

  • CompilerPreprocessorPlugin => introduces additional analysis rules

  • CompilerVocabularyExtender => includes additional files into the generated program, which may or may not be linked into the state machine (see the documentation of the class org.coreasm.compiler.components.mainprogram.MainFileEntry for more information). Also, the helper class org.coreasm.compiler.components.classlibrary.JarIncludeHelper can make the inclusion of files from jar archives a lot more comfortable (and readable).

When writing a plugin, the first step is to identify which interfaces the plugin should implement. Furthermore, there are two possibilites on where to locate the plugin: It can either be included directly in the coreasm project (in the engine in the package org.coreasm.engine.plugins and in the compiler in the package org.coreasm.compiler.plugins) or it can be created as a new project. The new project should then be set to have the org.coreasm.engine project as a dependency (otherwise necessary classes won't be accessible). Testing or launching the latter plugin will require it to be placed into the external plugin folder of a coreasm engine. Testing or launching a plugin introduced into the coreasm.core project will require the addition of a plugin jar into the org.coreasm.engine/rsc/plugins folder. Such a plugin jar needs to contain a CoreASMPlugin.id file, which contains the name of the main plugin class. Building the org.coreasm.engine project using maven should than make the plugin accessible.

For code generating plugins, a detailed look into the usage of the CodeFragment class is recommended. Code generating plugins will extend the CompilerCodePlugin class, in which they will only need to implement the registerCodeHandlers method. This method declares the different code handlers provided by the plugin and assigns them to specific ASTNode types (the javadoc has a more detailed explanation). CodeHandlers are the actual code generating instances. They implement the CompilerCodeHandler interface. It is recommended to take a look at some code handlers of other plugins before starting the implementation.

Testing

The testing classes of the interpreter and the compiler can be found in the test/org.coreasm.engine.test package / folder. They make use of the test cases found in the test-rsc/plugins and test-rsc/without_test_class folders. Tests can be launched using JUnit (e.g. by selecting junit from the run as menu in eclipse). Some tests will fail for the compiler, simply because they use plugins not implemented for the compiler. Launching compiler tests require the project to be built first so that a engine.jar can be found by the test cases.

Clone this wiki locally