THE UCL MDA TOOLS


The GTL module

UCL Logo
Home
Sourceforge project page
Download
UCL UML
Documentation
EMOFOCL2 API
UML 1.5 API

SourceForge.net Logo

Generic Template Language (GTL)

GTL is a very simple but powerful template language. The source code for the GTL processor is only about 300 lines long. The downside to the power and simplicity is that the processor doesn't and can't do much checking to make sure that a template is well formed. It is therefore quite easy to write templates that produce junk output, or even that fail to terminate when evaluated.

GTL relies on the GNU regular expression library implementation, which is licenced under the LGPL.

A GTL input file is a text file with the following syntax:

Input_file ::=
        Replacement_rules "endPatterns" <TEXT> <EOF>

Replacement_rules ::=
        ( Replace | Replace_within )*

Replace ::= 
        "replace" <DOUBLE_QUOTE> <RE> <DOUBLE_QUOTE>
        <DOUBLE_QUOTE> <REPLACEMENT> <DOUBLE_QUOTE>
				
Replace_within ::=
        "replaceWithin" <DOUBLE_QUOTE> <RE> <DOUBLE_QUOTE>
        <DOUBLE_QUOTE> <RE> <DOUBLE_QUOTE>
        <DOUBLE_QUOTE> <RE> <DOUBLE_QUOTE>
        <DOUBLE_QUOTE> <REPLACEMENT> <DOUBLE_QUOTE>

The lexemes in this grammar follow the following rules:

<TEXT>
is any text
<DOUBLE_QUOTE>
is the double quote character (")
<RE>
Is a regular expression according to the syntax used by the GNU RE library. However, due to the need to include double quotes in some regular expressions, the double quote character and the backslash character must be escaped using a backslash (\") and (\\) whenever they are included in the regular expression. Backslash followed by any other character is equivalent to that character alone.
<REPLACEMENT>
Is replacement text following the same escaping rules as for regular expressions discussed above. Replacement text may also include dollar signs followed by digits to include capturing groups from a prior regular expression. To include a dollar alone, escape it with a backslash (\$).

A GTL input file is interpreted as follows:

  1. The input file is read into memory and forms the 'working document'.
  2. The replacement rules header section is read and removed from the working document.
  3. The replacement rules ('replace' and 'replaceWithin') are parsed into a rules list. The order of the rules is significant as this is the order in which they will be applied to the working document.
  4. Starting with the first replacement rule, the replacement rules are applied in order to the working document. The rules are applied as follows:
    • If the replacement rule is a 'replace' rule, then then every match for the regular expression parameter is replaced using the replacement text, with capturing-group fields completed as appropriate using capturing-group text from each match.
    • If the replacement rule is a 'replaceWithin' rule then it has three RE parameters and a replace parameter. Every match for the second RE is replaced with the replacement text, providing it follows a match for the first RE, with no intervening match for the third RE.
  5. If any modifications were made to the working document during the application of the most recent replacement rule attempted, then attempts to apply replacement rules resume with the first replacement rule. Otherwise they continue with the next.
  6. When attempts have been made to apply all replacement rules in order, none of which have resulted in any change of the working document, processing is complete.

The effect of processing a GTL file is therefore simply to apply a standard sequence of find and replace rules to the text of the file, until applying them has no further effect. Two outcomes are possible: either the file will always compile to the same output, or interpretation will fail to terminate.

Neither of these outcomes seem particularly useful for a template language. Shouldn't a template language allow a variety of outputs from a single template?

The reason GTL was designed like this is because most template languages make assumptions about the data structures forming the 'context' or the part that varies the output. GTL doesn't do that, and in fact eliminates the notion of context. To get it back, you use GTL files to generate generators in the programming language of your choice that work with the type of context that you want to use. You get the advantage of a flexible template syntax in the GTL file, combined with the ability to use any data structures (in any language!) as the context. There follows an example from the EMOFOCL project of a GTL file that generates a Java generator for a visitor for JMI data structures. The context used is JMI code for the UML metamodel. The example is initially confusing because it generates a program in Java for generating Java. This can be disentangled if you realise that the parts between the

<?java
and
?>
will eventually form part of the generator, with other Java code being what is generated. The example is intended to show that GTL can be used to produce templates that look a bit like JSP or velocity templates.
replace "#\\*.*?\\*#" ""
replace "##[^\\n]*\\n" ""
replaceWithin "\\?>" "\\r\\n" "<\\?" "\\n"
replaceWithin "\\?>" "\\n" "<\\?" "\\n"
replaceWithin "\\?>" "\\t" "<\\?" "\\t"
replace "\\?>\"" "?>\\\""
replaceWithin "\\?>" "([^\\\\])\"" "<\\?" "$1\\\""
replace "<\\?java([^?]*)\\?>" "<?$1?>"
replace "<\\?=(([^?]|\\?[^>])*)\\?>" "<?
    out.print($1);?>"
replace "\\?>(([^<]|<[^?])*)<\\?" "
    out.print(\"$1\");"
replace "\\A.*?<\\?(.*)\\?>.*\\Z" "$1"
endPatterns
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the template Factory.gtl, for generating a factory
 * for creating JMI repositories, generated from an EMOF meta-model.
 *
 * The Initial Developer of the Original Code is
 * James Skene.
 * Portions created by the Initial Developer are Copyright (C) 2005
 * University College London. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ******/
 #*
$Header: /cs/research/sse/home0/green/jwskene/cvs/private/
uclmda.sourceforge.net/gtl.html,v 1.1 2005/10/03 16:55:41 jwskene Exp $

$Log: gtl.html,v $
Revision 1.3  2006/05/18 17:53:24  jwskene
Ready to rock.

Revision 1.2  2006/05/17 15:15:54  jwskene
Update of UCLMDA website with UCL corporate identity.

Revision 1.1  2005/10/03 16:55:41  jwskene
First version of project documentation.

 *#
<?java

package uk.ac.ucl.cs.emofocl.jmigen.extended;

import java.io.PrintStream;

import uk.ac.ucl.cs.emofocl.jmi.extensions.Specification;

public class FactoryGenerator {

  public static void generate(JMIContext context,
    Specification specification, PrintStream out) {
    
    String modelName = JMIContext.capitalise(specification.getName());
    String modelType = context.getInterfacePackage(specification) + "." +
      JMIContext.capitalise(specification.getName()) + "Package";
    String modelImplType = context.getImplementationPackage(specification) +
      "." + JMIContext.capitalise(specification.getName()) + "_PackageImpl";
?>

package <?= context.getInterfacePrefix() ?>;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Iterator;
import java.util.logging.Logger;

import javax.jmi.reflect.RefPackage;
import javax.jmi.xmi.MalformedXMIException;

import uk.ac.ucl.cs.emofocl.jmi.extensions.Specification;
import uk.ac.ucl.cs.emofocl.jmi.extensions.SpecificationClass;

/**
 * @author jwskene
 */
public class <?= modelName ?>Factory {
  
  private static Logger LOGGER =
    Logger.getLogger(<?= modelName ?>Factory.class.getCanonicalName()) ;
    
    //  meta-meta-model classes
  private static uk.ac.ucl.cs.emofocl.jmi.EMOFOCLModelPackage m3Classes = null;
  
    //  meta-model
  private static Specification m2 = null;
  
  private static boolean metaLoaded = false;
  
  private static void loadMeta() {

    metaLoaded = true;
    try {
      
      LOGGER.info("Instantiate meta-meta-model");
<?java
    if(specification.getName().equals("EMOFOCLModel")) {
?>##
      m3Classes =
        (uk.ac.ucl.cs.emofocl.jmi.EMOFOCLModelPackage)
          uk.ac.ucl.cs.emofocl.jmi.EMOFOCLModelFactory.getModel(false);
<?java
    }
    else {
?>##
      m3Classes =
        (uk.ac.ucl.cs.emofocl.jmi.EMOFOCLModelPackage)
          uk.ac.ucl.cs.emofocl.jmi.EMOFOCLModelFactory.getModel(true);
<?java
    }
?>##
      LOGGER.info("Open meta-model");
      URL uRL =
        <?= modelName ?>Factory.class.getResource(
          "<?= modelName ?>.xmi");
      InputStream input = uRL.openStream();
          
      LOGGER.info("Read meta-model");
      uk.ac.ucl.cs.emofocl.jmi.EMOFOCLModelXMIReader reader =
        new uk.ac.ucl.cs.emofocl.jmi.EMOFOCLModelXMIReader();
      reader.read(input, null, m3Classes);
          
      LOGGER.info("Find Model package element");    
      SpecificationClass specificationProxy =
        m3Classes.getExtensions().getSpecification();
      Collection specifications = specificationProxy.refAllOfType();
      Iterator it = specifications.iterator();
      m2 = null;
      while(it.hasNext() && m2 == null) {
      
        Specification s = (Specification)it.next();
        if(s.getName().equals("<?= specification.getName() ?>")) m2 = s;
      }
      if(m2 == null) System.err.println("Specification not found");
    }
    catch(IOException iOE) {
        
      iOE.printStackTrace();
    }
    catch(MalformedXMIException mXMIE) {
        
      mXMIE.printStackTrace();
    }
  }
  
  public static Specification getMetaModel() {

    if(!metaLoaded) loadMeta();  
    return m2;
  }

  /**
   * @see uk.ac.ucl.cs.jmi.JMIFactory#getModel()
   */
  public static RefPackage getModel(boolean meta) {

    if(meta) {
      if(!metaLoaded) loadMeta();  
      return new <?= modelImplType ?>(m2);
    }
    else return new <?= modelImplType ?>(null);
  }
}
<?java
  }
}
?>

Back home