Layering Extensions
The
CArray
extension we have created is a direct extension of Java
1.4. In practice, language extensions usually build on other extensions. For
example, Jif builds
on Java 1.4, while
Fabric
builds on Jif. This last section of the tutorial shows that extending an
already existing extension is as equally easy as extending the base language.
In fact, we will describe the process to make our implementation of
CArray
extend Java 5 or Java 7 instead.
For convenience, we will use the term existing extension to denote the
language extension to be extended, the term base language to denote the
language without any extension, i.e., Java 1.4 for Polyglot, and the term
higher-order extension to denote the language extension that extends
the existing extension.
There are only few differences when extending an existing extension rather than
the base language:
- The parser will be an extension of different grammar. That is, instead of extending the grammar of the base language, the grammar for the higher-order extension will extend the grammar of the existing extension.
- The language dispatcher of the higher-order extension will extend that of the existing extension instead of the base language.
- The scheduler of the higher-order extension will extend that of the existing extension instead of the base language.
- The extension information will be modified so that the extension factory of the existing extension is the next extension factory of the extension factory of the higher-order extension. This effectively changes the number and the order of node instances in a chain of objects that represents an AST node.
CArray
for the remainder of this
section.
Extending the grammar of an existing extension
In addition to extending a CUP file, PPG supports extending a PPG file. The
first line of a PPG file designates the grammar to be extended. Here is one
from
carray.ppg
that extends Java 1.4 grammar:
include "polyglot/parse/java12.cup"
To change CArray
to extend Java 5 grammar, the above line is
changed to
include "polyglot/ext/jl5/parse/jl5.ppg"
and the following to extend Java 7 grammar:
include "polyglot/ext/jl7/parse/jl7.ppg"
The definition of nonterminals usually also needs change when changing the
base extension. This change is often nontrivial as is left as a challenging
exercise. For instance, the array type must take generics into account.
Extending the language dispatcher of an existing extension
Changing the base extension entails changing the language dispatcher that the
higher-order extension will extend. That is, instead of this class declaration
for Java 1.4:
public class CArrayLang_c extends JLang_c implements CArrayLang { // ... }we use this class declaration to extend Java 5:
public class CArrayLang_c extends J5Lang_c implements CArrayLang { // ... }and this one to extend Java 7:
public class CArrayLang_c extends J7Lang_c implements CArrayLang { // ... }
Extending the scheduler of an existing extension
Changing the scheduler is similar to changing the language dispatcher to
be inherited from. That is, instead of this class declaration for Java 1.4:
public class CArrayScheduler extends JLScheduler { // ... }use this class declaration to extend Java 5:
public class CArrayScheduler extends JL5Scheduler { // ... }and this one to extend Java 7:
public class CArrayScheduler extends JL7Scheduler { // ... }It is possible that an existing extension might not have a scheduler associated with it. In this case, the existing extension must inherit a scheduler from its base extension, and the higher-order extension can safely extend that scheduler instead.
Modifying extension information
The only change to be made is to chain the extension factories differently by
nesting the extension factories of all the extensions that the higher-order
extension depends on, in order of layering. This chain of extension factories
is passed as an argument to the constructor for the higher-order extension's
node factory. This change is made in the implementation of
createNodeFactory
. For CArray
, instead of this
implementation when extending Java 1.4:
@Override protected NodeFactory createNodeFactory() { return new CArrayNodeFactory_c(CArrayLang_c.instance, new CArrayExtFactory_c()); }use this implementation to extend Java 5:
@Override protected NodeFactory createNodeFactory() { return new CArrayNodeFactory_c(CArrayLang_c.instance, new CArrayExtFactory_c( new JL5ExtFactory_c())); }and this one to extend Java 7:
@Override protected NodeFactory createNodeFactory() { return new CArrayNodeFactory_c(CArrayLang_c.instance, new CArrayExtFactory_c( new JL7ExtFactory_c( new JL5ExtFactory_c()))); }
Last but not least, there can be conflicting semantic changes when switching
the base extension. Careful scrutiny and thorough testing will help resolve
these conflicts. Nevertheless, this is typically not a problem if a higher-order
extension has been designed to extend a particular existing extension from the
beginning.