# RGen Framework
# (c) Martin Thiede, 2006
require 'rgen/template_language/directory_template_container'
require 'rgen/template_language/template_container'
module RGen
# The RGen template language has been designed to build complex generators.
# It is very similar to the EXPAND language of the Java based
# OpenArchitectureWare framework.
#
# =Templates
#
# The basic idea is to allow "templates" not only being template files
# but smaller parts. Those parts can be expanded from other parts very
# much like Ruby methods are called from other methods.
# Thus the term "template" refers to such a part within a "template file".
#
# Template files used by the RGen template language should have a
# filename with the postfix ".tpl". Those files can reside within (nested)
# template file directories.
#
# As an example a template directory could look like the following:
#
# templates/root.tpl
# templates/dbaccess/dbaccess.tpl
# templates/dbaccess/schema.tpl
# templates/headers/generic_headers.tpl
# templates/headers/specific/component.tpl
#
# A template is always called for a context object. The context object
# serves as the receiver of methods called within the template. Details are given
# below.
#
#
# =Defining Templates
#
# One or more templates can be defined in a template file using the +define+
# keyword as in the following example:
#
# <% define 'GenerateDBAdapter', :for => DBDescription do |dbtype| %>
# Content to be generated; use ERB syntax here
# <% end %>
#
# The template definition takes three kinds of parameters:
# 1. The name of the template within the template file as a String or Symbol
# 2. An optional class object describing the class of context objects for which
# this template is valid.
# 3. An arbitrary number of template parameters
# See RGen::TemplateLanguage::TemplateContainer for details about the syntax of +define+.
#
# Within a template, regular ERB syntax can be used. This is
# * <%
and %>
are used to embed Ruby code
# * <%=
and %>
are used to embed Ruby expressions with
# the expression result being written to the template output
# * <%#
and %>
are used for comments
# All content not within these tags is written to the template output verbatim.
# See below for details about output files and output formatting.
#
# All methods which are called from within the template are sent to the context
# object.
#
# Experience shows that one easily forgets the +do+ at the end of the first
# line of a template definition. This will result in an ERB parse error.
#
#
# =Expanding Templates
#
# Templates are normally expanded from within other templates. The only
# exception is the root template, which is expanded from the surrounding code.
#
# Template names can be specified in the following ways:
# * Non qualified name: use the template with the given name in the current template file
# * Relative qualified name: use the template within the template file specified by the relative path
# * Absolute qualified name: use the template within the template file specified by the absolute path
#
# The +expand+ keyword is used to expand templates.
#
# Here are some examples:
#
# <% expand 'GenerateDBAdapter', dbtype, :for => dbDesc %>
#
# Non qualified. Must be called within the file where 'GenerateDBAdapter' is defined.
# There is one template parameter passed in via variable +dbtype+.
# The context object is provided in variable +dbDesc+.
#
# <% expand 'dbaccess::ExampleSQL' %>
#
# Qualified with filename. Must be called from a file in the same directory as 'dbaccess.tpl'
# There are no parameters. The current context object will be used as the context
# object for this template expansion.
#
# <% expand '../headers/generic_headers::CHeader', :foreach => modules %>
#
# Relatively qualified. Must be called from a location from which the file
# 'generic_headers.tpl' is accessible via the relative path '../headers'.
# The template is expanded for each module in +modules+ (which has to be an Array).
# Each element of +modules+ will be the context object in turn.
#
# <% expand '/headers/generic_headers::CHeader', :foreach => modules %>
#
# Absolutely qualified: The same behaviour as before but with an absolute path from
# the template directory root (which in this example is 'templates', see above)
#
# Sometimes it is neccessary to generate some text (e.g. a ',') in between the single
# template expansion results from a :foreach
expansion. This can be achieved by
# using the :separator
keyword:
#
# <% expand 'ColumnName', :foreach => column, :separator => ', ' %>
#
# Note that the separator may also contain newline characters (\n). See below for
# details about formatting.
#
#
# =Formatting
#
# For many generator tools a formatting postprocess (e.g. using a pretty printer) is
# required in order to make the output readable. However, depending on the kind of
# generated output, such a tool might not be available.
#
# The RGen template language has been design for generators which do not need a
# postprocessing step. The basic idea is to eliminate all whitespace at the beginning
# of template lines (the indentation that makes the _template_ readable) and output
# newlines only after at least on character has been generated in the corresponding
# line. This way there are no empty lines in the output and each line will start with
# a non-whitspace character.
#
# Starting from this point one can add indentation and newlines as required by using
# explicit formatting commands:
# * <%nl%>
(newline) starts a new line
# * <%ws%>
(whitespace) adds an explicit space
# * <%iinc%>
(indentation increment) increases the current indentation
# * <%idec%>
(indentation decrement) decreases the current indentation
# * <%nonl%>
(no newline) ignore next newline
# * <%nows%>
(no whitespace) ignore next whitespace
#
# Indentation takes place for every new line in the output unless it is 0.
# The initial indentation can be specified with a root +expand+ command by using
# the :indent
keyword.
#
# Here is an example:
#
# expand 'GenerateDBAdapter', dbtype, :for => dbDesc, :indent => 1
#
# Initial indentation defaults to 0. Normally <%iinc%>
and
# <%idec%>
are used to change the indentation.
# The current indentation is kept for expansion of subtemplates.
#
# The string which is used to realize one indentation step can be set using
# DirectoryTemplateContainer#indentString or with the template language +file+ command.
# The default is " " (3 spaces), the indentation string given at a +file+ command
# overwrites the container's default which in turn overwrites the overall default.
#
# Note that commands to ignore whitespace and newlines are still useful if output
# generated from multiple template lines should show up in one single output line.
#
# Here is an example of a template generating a C program:
#
# #include
# <%nl%>
# int main() {<%iinc%>
# printf("Hello World\n");
# return 0;<%idec>
# }
#
# The result is:
#
# #include
#
# int main() {
# printf("Hello World\n");
# return 0;
# }
#
# Note that without the explicit formatting commands, the output generated from the
# example above would not have any empty lines or whitespace in the beginning of lines.
# This may seem like unneccessary extra work for the example above which could also
# have been generated by passing the template to the output verbatimly.
# However in most cases templates will contain more template specific indentation and
# newlines which should be eliminated than formatting that should be visible in the
# output.
#
# Here is a more realistic example for generating C function prototypes:
#
# <% define 'Prototype', :for => CFunction do %>
# <%= getType.name %> <%= name %>(<%nows%>
# <% expand 'Signature', :foreach => argument, :separator => ', ' %>);
# <% end %>
#
# <% define 'Signature', :for => CFunctionArgument do %>
# <%= getType.name %> <%= name%><%nows%>
# <% end %>
#
# The result could look something like:
#
# void somefunc(int a, float b, int c);
# int otherfunc(short x);
#
# In this example a separator is used to join the single arguments of the C functions.
# Note that the template generating the argument type and name needs to contain
# a <%nows%>
if the result should consist of a single line.
#
# Here is one more example for generating C array initializations:
#
# <% define 'Array', :for => CArray do %>
# <%= getType.name %> <%= name %>[<%= size %>] = {<%iinc%>
# <% expand 'InitValue', :foreach => initvalue, :separator => ",\n" %><%nl%><%idec%>
# };
# <% end %>
#
# <% define 'InitValue', :for => PrimitiveInitValue do %>
# <%= value %><%nows%>
# <% end %>
#
# The result could look something like:
#
# int myArray[3] = {
# 1,
# 2,
# 3
# };
#
# Note that in this example, the separator contains a newline. The current increment
# will be applied to each single expansion result since it starts in a new line.
#
#
# =Output Files
#
# Normally the generated content is to be written into one or more output files.
# The RGen template language facilitates this by means of the +file+ keyword.
#
# When the +file+ keyword is used to define a block, all output generated
# from template code within this block will be written to the specified file.
# This includes output generated from template expansions.
# Thus all output from templates expanded within this block is written to
# the same file as long as those templates do not use the +file+ keyword to
# define a new file context.
#
# Here is an example:
#
# <% file 'dbadapter/'+adapter.name+'.c' do %>
# all content within this block will be written to the specified file
# <% end %>
#
# Note that the filename itself can be calculated dynamically by an arbitrary
# Ruby expression.
#
# The absolute position where the output file is created depends on the output
# root directory passed to DirectoryTemplateContainer as described below.
#
# As a second argument, the +file+ command can take the indentation string which is
# used to indent output lines (see Formatting).
#
# =Setting up the Generator
#
# Setting up the generator consists of 3 steps:
# * Instantiate DirectoryTemplateContainer passing one or more metamodel(s) and the output
# directory to the constructor.
# * Load the templates into the template container
# * Expand the root template to start generation
#
# Here is an example:
#
# module MyMM
# # metaclasses are defined here, e.g. using RGen::MetamodelBuilder
# end
#
# OUTPUT_DIR = File.dirname(__FILE__)+"/output"
# TEMPLATES_DIR = File.dirname(__FILE__)+"/templates"
#
# tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new(MyMM, OUTPUT_DIR)
# tc.load(TEMPLATES_DIR)
# # testModel should hold an instance of the metamodel class expected by the root template
# # the following line starts generation
# tc.expand('root::Root', :for => testModel, :indent => 1)
#
# The metamodel is the Ruby module which contains the metaclasses.
# This information is required for the template container in order to resolve the
# metamodel classes used within the template file.
# If several metamodels shall be used, an array of modules can be passed instead
# of a single module.
#
# The output path is prepended to the relative paths provided to the +file+
# definitions in the template files.
#
# The template directory should contain template files as described above.
#
# Finally the generation process is started by calling +expand+ in the same way as it
# is used from within templates.
#
# Also see the unit tests for more examples.
#
module TemplateLanguage
end
end