rgen 0.4.2 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +7 -0
- data/lib/rgen/metamodel_builder.rb +5 -4
- data/lib/rgen/metamodel_builder.rb.bak +196 -0
- data/lib/rgen/metamodel_builder/builder_extensions.rb +51 -38
- data/lib/rgen/metamodel_builder/builder_extensions.rb.bak +437 -0
- data/lib/rgen/metamodel_builder/builder_runtime.rb +2 -20
- data/lib/rgen/metamodel_builder/builder_runtime.rb.bak +73 -0
- data/lib/rgen/name_helper.rb.bak +37 -0
- data/lib/rgen/template_language.rb +8 -0
- data/lib/rgen/template_language.rb.bak +289 -0
- data/lib/rgen/template_language/directory_template_container.rb +11 -0
- data/lib/rgen/template_language/directory_template_container.rb.bak +69 -0
- data/lib/rgen/template_language/output_handler.rb +3 -2
- data/lib/rgen/template_language/output_handler.rb.bak +88 -0
- data/lib/rgen/template_language/template_container.rb +5 -4
- data/lib/rgen/template_language/template_container.rb.bak +196 -0
- data/lib/rgen/transformer.rb.bak +381 -0
- data/test/environment_test.rb.bak +52 -0
- data/test/metamodel_builder_test.rb +6 -0
- data/test/metamodel_builder_test.rb.bak +443 -0
- data/test/metamodel_roundtrip_test/TestModel_Regenerated.rb +34 -32
- data/test/metamodel_roundtrip_test/houseMetamodel_Regenerated.ecore +58 -58
- data/test/output_handler_test.rb +8 -0
- data/test/output_handler_test.rb.bak +50 -0
- data/test/template_language_test.rb +23 -0
- data/test/template_language_test.rb.bak +72 -0
- data/test/template_language_test/indentStringTestDefaultIndent.out +1 -0
- data/test/template_language_test/indentStringTestTabIndent.out +1 -0
- data/test/template_language_test/templates/indent_string_test.tpl +12 -0
- data/test/template_language_test/templates/null_context_test.tpl +12 -0
- data/test/transformer_test.rb.bak +223 -0
- metadata +65 -48
- data/lib/rgen/environment.rb.bak +0 -42
@@ -14,8 +14,8 @@ module BuilderRuntime
|
|
14
14
|
include NameHelper
|
15
15
|
|
16
16
|
def is_a?(c)
|
17
|
-
|
18
|
-
|
17
|
+
return super unless c.const_defined?(:ClassModule)
|
18
|
+
kind_of?(c::ClassModule)
|
19
19
|
end
|
20
20
|
|
21
21
|
def addGeneric(role, value)
|
@@ -34,24 +34,6 @@ module BuilderRuntime
|
|
34
34
|
send("#{role}")
|
35
35
|
end
|
36
36
|
|
37
|
-
def _unregister(element, target, target_role, kind)
|
38
|
-
return unless element and target and target_role
|
39
|
-
if kind == 'one'
|
40
|
-
target.send("#{target_role}=",nil)
|
41
|
-
elsif kind == 'many'
|
42
|
-
target.send("remove#{firstToUpper(target_role)}",element)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def _register(element, target, target_role, kind)
|
47
|
-
return unless element and target and target_role
|
48
|
-
if kind == 'one'
|
49
|
-
target.send("#{target_role}=",element)
|
50
|
-
elsif kind == 'many'
|
51
|
-
target.send("add#{firstToUpper(target_role)}",element)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
37
|
def _assignmentTypeError(target, value, expected)
|
56
38
|
text = ""
|
57
39
|
if target
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# RGen Framework
|
2
|
+
# (c) Martin Thiede, 2006
|
3
|
+
|
4
|
+
require 'rgen/name_helper'
|
5
|
+
|
6
|
+
module RGen
|
7
|
+
|
8
|
+
module MetamodelBuilder
|
9
|
+
|
10
|
+
# This module is mixed into MetamodelBuilder::MMBase.
|
11
|
+
# The methods provided by this module are used by the methods generated
|
12
|
+
# by the class methods of MetamodelBuilder::BuilderExtensions
|
13
|
+
module BuilderRuntime
|
14
|
+
include NameHelper
|
15
|
+
|
16
|
+
def is_a?(c)
|
17
|
+
return super unless c.respond_to?(:_class_module)
|
18
|
+
kind_of?(c._class_module)
|
19
|
+
end
|
20
|
+
|
21
|
+
def addGeneric(role, value)
|
22
|
+
send("add#{firstToUpper(role)}",value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def removeGeneric(role, value)
|
26
|
+
send("remove#{firstToUpper(role)}",value)
|
27
|
+
end
|
28
|
+
|
29
|
+
def setGeneric(role, value)
|
30
|
+
send("#{role}=",value)
|
31
|
+
end
|
32
|
+
|
33
|
+
def getGeneric(role)
|
34
|
+
send("#{role}")
|
35
|
+
end
|
36
|
+
|
37
|
+
def _unregister(element, target, target_role, kind)
|
38
|
+
return unless element and target and target_role
|
39
|
+
if kind == 'one'
|
40
|
+
target.send("#{target_role}=",nil)
|
41
|
+
elsif kind == 'many'
|
42
|
+
target.send("remove#{firstToUpper(target_role)}",element)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def _register(element, target, target_role, kind)
|
47
|
+
return unless element and target and target_role
|
48
|
+
if kind == 'one'
|
49
|
+
target.send("#{target_role}=",element)
|
50
|
+
elsif kind == 'many'
|
51
|
+
target.send("add#{firstToUpper(target_role)}",element)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def _assignmentTypeError(target, value, expected)
|
56
|
+
text = ""
|
57
|
+
if target
|
58
|
+
targetId = target.class.name
|
59
|
+
targetId += "(" + target.name + ")" if target.respond_to?(:name) and target.name
|
60
|
+
text += "In #{targetId} : "
|
61
|
+
end
|
62
|
+
valueId = value.class.name
|
63
|
+
valueId += "(" + value.name + ")" if value.respond_to?(:name) and value.name
|
64
|
+
valueId += "(:" + value.to_s + ")" if value.is_a?(Symbol)
|
65
|
+
text += "Can not use a #{valueId} where a #{expected} is expected"
|
66
|
+
StandardError.new(text)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# RGen Framework
|
2
|
+
# (c) Martin Thiede, 2006
|
3
|
+
|
4
|
+
module RGen
|
5
|
+
|
6
|
+
module NameHelper
|
7
|
+
|
8
|
+
def normalize(name)
|
9
|
+
name.gsub(/\W/,'_')
|
10
|
+
end
|
11
|
+
|
12
|
+
def className(object)
|
13
|
+
object.class.name =~ /::(\w+)$/; $1
|
14
|
+
end
|
15
|
+
|
16
|
+
def firstToUpper(str)
|
17
|
+
str[0..0].upcase + ( str[1..-1] || "" )
|
18
|
+
end
|
19
|
+
|
20
|
+
def firstToLower(str)
|
21
|
+
str[0..0].downcase + ( str[1..-1] || "" )
|
22
|
+
end
|
23
|
+
|
24
|
+
def saneClassName(str)
|
25
|
+
firstToUpper(normalize(str)).sub(/^Class$/, 'Clazz')
|
26
|
+
end
|
27
|
+
|
28
|
+
def saneMethodName(str)
|
29
|
+
firstToLower(normalize(str)).sub(/^class$/, 'clazz')
|
30
|
+
end
|
31
|
+
|
32
|
+
def camelize(str)
|
33
|
+
str.split(/[\W_]/).collect{|s| firstToUpper(s.downcase)}.join
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -145,6 +145,11 @@ module RGen
|
|
145
145
|
# Initial indentation defaults to 0. Normally <code><%iinc%></code> and
|
146
146
|
# <code><%idec%></code> are used to change the indentation.
|
147
147
|
# The current indentation is kept for expansion of subtemplates.
|
148
|
+
#
|
149
|
+
# The string which is used to realize one indentation step can be set using
|
150
|
+
# DirectoryTemplateContainer#indentString or with the template language +file+ command.
|
151
|
+
# The default is " " (3 spaces), the indentation string given at a +file+ command
|
152
|
+
# overwrites the container's default which in turn overwrites the overall default.
|
148
153
|
#
|
149
154
|
# Note that commands to ignore whitespace and newlines are still useful if output
|
150
155
|
# generated from multiple template lines should show up in one single output line.
|
@@ -242,6 +247,9 @@ module RGen
|
|
242
247
|
#
|
243
248
|
# The absolute position where the output file is created depends on the output
|
244
249
|
# root directory passed to DirectoryTemplateContainer as described below.
|
250
|
+
#
|
251
|
+
# As a second argument, the +file+ command can take the indentation string which is
|
252
|
+
# used to indent output lines (see Formatting).
|
245
253
|
#
|
246
254
|
# =Setting up the Generator
|
247
255
|
#
|
@@ -0,0 +1,289 @@
|
|
1
|
+
# RGen Framework
|
2
|
+
# (c) Martin Thiede, 2006
|
3
|
+
|
4
|
+
require 'rgen/template_language/directory_template_container'
|
5
|
+
require 'rgen/template_language/template_container'
|
6
|
+
|
7
|
+
module RGen
|
8
|
+
|
9
|
+
# The RGen template language has been designed to build complex generators.
|
10
|
+
# It is very similar to the EXPAND language of the Java based
|
11
|
+
# OpenArchitectureWare framework.
|
12
|
+
#
|
13
|
+
# =Templates
|
14
|
+
#
|
15
|
+
# The basic idea is to allow "templates" not only being template files
|
16
|
+
# but smaller parts. Those parts can be expanded from other parts very
|
17
|
+
# much like Ruby methods are called from other methods.
|
18
|
+
# Thus the term "template" refers to such a part within a "template file".
|
19
|
+
#
|
20
|
+
# Template files used by the RGen template language should have a
|
21
|
+
# filename with the postfix ".tpl". Those files can reside within (nested)
|
22
|
+
# template file directories.
|
23
|
+
#
|
24
|
+
# As an example a template directory could look like the following:
|
25
|
+
#
|
26
|
+
# templates/root.tpl
|
27
|
+
# templates/dbaccess/dbaccess.tpl
|
28
|
+
# templates/dbaccess/schema.tpl
|
29
|
+
# templates/headers/generic_headers.tpl
|
30
|
+
# templates/headers/specific/component.tpl
|
31
|
+
#
|
32
|
+
# A template is always called for a <i>context object</i>. The context object
|
33
|
+
# serves as the receiver of methods called within the template. Details are given
|
34
|
+
# below.
|
35
|
+
#
|
36
|
+
#
|
37
|
+
# =Defining Templates
|
38
|
+
#
|
39
|
+
# One or more templates can be defined in a template file using the +define+
|
40
|
+
# keyword as in the following example:
|
41
|
+
#
|
42
|
+
# <% define 'GenerateDBAdapter', :for => DBDescription do |dbtype| %>
|
43
|
+
# Content to be generated; use ERB syntax here
|
44
|
+
# <% end %>
|
45
|
+
#
|
46
|
+
# The template definition takes three kinds of parameters:
|
47
|
+
# 1. The name of the template within the template file as a String or Symbol
|
48
|
+
# 2. An optional class object describing the class of context objects for which
|
49
|
+
# this template is valid.
|
50
|
+
# 3. An arbitrary number of template parameters
|
51
|
+
# See RGen::TemplateLanguage::TemplateContainer for details about the syntax of +define+.
|
52
|
+
#
|
53
|
+
# Within a template, regular ERB syntax can be used. This is
|
54
|
+
# * <code><%</code> and <code>%></code> are used to embed Ruby code
|
55
|
+
# * <code><%=</code> and <code>%></code> are used to embed Ruby expressions with
|
56
|
+
# the expression result being written to the template output
|
57
|
+
# * <code><%#</code> and <code>%></code> are used for comments
|
58
|
+
# All content not within these tags is written to the template output verbatim.
|
59
|
+
# See below for details about output files and output formatting.
|
60
|
+
#
|
61
|
+
# All methods which are called from within the template are sent to the context
|
62
|
+
# object.
|
63
|
+
#
|
64
|
+
# Experience shows that one easily forgets the +do+ at the end of the first
|
65
|
+
# line of a template definition. This will result in an ERB parse error.
|
66
|
+
#
|
67
|
+
#
|
68
|
+
# =Expanding Templates
|
69
|
+
#
|
70
|
+
# Templates are normally expanded from within other templates. The only
|
71
|
+
# exception is the root template, which is expanded from the surrounding code.
|
72
|
+
#
|
73
|
+
# Template names can be specified in the following ways:
|
74
|
+
# * Non qualified name: use the template with the given name in the current template file
|
75
|
+
# * Relative qualified name: use the template within the template file specified by the relative path
|
76
|
+
# * Absolute qualified name: use the template within the template file specified by the absolute path
|
77
|
+
#
|
78
|
+
# The +expand+ keyword is used to expand templates.
|
79
|
+
#
|
80
|
+
# Here are some examples:
|
81
|
+
#
|
82
|
+
# <% expand 'GenerateDBAdapter', dbtype, :for => dbDesc %>
|
83
|
+
#
|
84
|
+
# <i>Non qualified</i>. Must be called within the file where 'GenerateDBAdapter' is defined.
|
85
|
+
# There is one template parameter passed in via variable +dbtype+.
|
86
|
+
# The context object is provided in variable +dbDesc+.
|
87
|
+
#
|
88
|
+
# <% expand 'dbaccess::ExampleSQL' %>
|
89
|
+
#
|
90
|
+
# <i>Qualified with filename</i>. Must be called from a file in the same directory as 'dbaccess.tpl'
|
91
|
+
# There are no parameters. The current context object will be used as the context
|
92
|
+
# object for this template expansion.
|
93
|
+
#
|
94
|
+
# <% expand '../headers/generic_headers::CHeader', :foreach => modules %>
|
95
|
+
#
|
96
|
+
# <i>Relatively qualified</i>. Must be called from a location from which the file
|
97
|
+
# 'generic_headers.tpl' is accessible via the relative path '../headers'.
|
98
|
+
# The template is expanded for each module in +modules+ (which has to be an Array).
|
99
|
+
# Each element of +modules+ will be the context object in turn.
|
100
|
+
#
|
101
|
+
# <% expand '/headers/generic_headers::CHeader', :foreach => modules %>
|
102
|
+
#
|
103
|
+
# Absolutely qualified: The same behaviour as before but with an absolute path from
|
104
|
+
# the template directory root (which in this example is 'templates', see above)
|
105
|
+
#
|
106
|
+
# Sometimes it is neccessary to generate some text (e.g. a ',') in between the single
|
107
|
+
# template expansion results from a <code>:foreach</code> expansion. This can be achieved by
|
108
|
+
# using the <code>:separator</code> keyword:
|
109
|
+
#
|
110
|
+
# <% expand 'ColumnName', :foreach => column, :separator => ', ' %>
|
111
|
+
#
|
112
|
+
# Note that the separator may also contain newline characters (\n). See below for
|
113
|
+
# details about formatting.
|
114
|
+
#
|
115
|
+
#
|
116
|
+
# =Formatting
|
117
|
+
#
|
118
|
+
# For many generator tools a formatting postprocess (e.g. using a pretty printer) is
|
119
|
+
# required in order to make the output readable. However, depending on the kind of
|
120
|
+
# generated output, such a tool might not be available.
|
121
|
+
#
|
122
|
+
# The RGen template language has been design for generators which do not need a
|
123
|
+
# postprocessing step. The basic idea is to eliminate all whitespace at the beginning
|
124
|
+
# of template lines (the indentation that makes the _template_ readable) and output
|
125
|
+
# newlines only after at least on character has been generated in the corresponding
|
126
|
+
# line. This way there are no empty lines in the output and each line will start with
|
127
|
+
# a non-whitspace character.
|
128
|
+
#
|
129
|
+
# Starting from this point one can add indentation and newlines as required by using
|
130
|
+
# explicit formatting commands:
|
131
|
+
# * <code><%nl%></code> (newline) starts a new line
|
132
|
+
# * <code><%iinc%></code> (indentation increment) increases the current indentation
|
133
|
+
# * <code><%idec%></code> (indentation decrement) decreases the current indentation
|
134
|
+
# * <code><%nonl%></code> (no newline) ignore next newline
|
135
|
+
# * <code><%nows%></code> (no whitespace) ignore next whitespace
|
136
|
+
#
|
137
|
+
# Indentation takes place for every new line in the output unless it is 0.
|
138
|
+
# The initial indentation can be specified with a root +expand+ command by using
|
139
|
+
# the <code>:indent</code> keyword.
|
140
|
+
#
|
141
|
+
# Here is an example:
|
142
|
+
#
|
143
|
+
# expand 'GenerateDBAdapter', dbtype, :for => dbDesc, :indent => 1
|
144
|
+
#
|
145
|
+
# Initial indentation defaults to 0. Normally <code><%iinc%></code> and
|
146
|
+
# <code><%idec%></code> are used to change the indentation.
|
147
|
+
# The current indentation is kept for expansion of subtemplates.
|
148
|
+
#
|
149
|
+
# Note that commands to ignore whitespace and newlines are still useful if output
|
150
|
+
# generated from multiple template lines should show up in one single output line.
|
151
|
+
#
|
152
|
+
# Here is an example of a template generating a C program:
|
153
|
+
#
|
154
|
+
# #include <stdio.h>
|
155
|
+
# <%nl%>
|
156
|
+
# int main() {<%iinc%>
|
157
|
+
# printf("Hello World\n");
|
158
|
+
# return 0;<%idec>
|
159
|
+
# }
|
160
|
+
#
|
161
|
+
# The result is:
|
162
|
+
#
|
163
|
+
# #include <stdio.h>
|
164
|
+
#
|
165
|
+
# int main() {
|
166
|
+
# printf("Hello World\n");
|
167
|
+
# return 0;
|
168
|
+
# }
|
169
|
+
#
|
170
|
+
# Note that without the explicit formatting commands, the output generated from the
|
171
|
+
# example above would not have any empty lines or whitespace in the beginning of lines.
|
172
|
+
# This may seem like unneccessary extra work for the example above which could also
|
173
|
+
# have been generated by passing the template to the output verbatimly.
|
174
|
+
# However in most cases templates will contain more template specific indentation and
|
175
|
+
# newlines which should be eliminated than formatting that should be visible in the
|
176
|
+
# output.
|
177
|
+
#
|
178
|
+
# Here is a more realistic example for generating C function prototypes:
|
179
|
+
#
|
180
|
+
# <% define 'Prototype', :for => CFunction do %>
|
181
|
+
# <%= getType.name %> <%= name %>(<%nows%>
|
182
|
+
# <% expand 'Signature', :foreach => argument, :separator => ', ' %>);
|
183
|
+
# <% end %>
|
184
|
+
#
|
185
|
+
# <% define 'Signature', :for => CFunctionArgument do %>
|
186
|
+
# <%= getType.name %> <%= name%><%nows%>
|
187
|
+
# <% end %>
|
188
|
+
#
|
189
|
+
# The result could look something like:
|
190
|
+
#
|
191
|
+
# void somefunc(int a, float b, int c);
|
192
|
+
# int otherfunc(short x);
|
193
|
+
#
|
194
|
+
# In this example a separator is used to join the single arguments of the C functions.
|
195
|
+
# Note that the template generating the argument type and name needs to contain
|
196
|
+
# a <code><%nows%></code> if the result should consist of a single line.
|
197
|
+
#
|
198
|
+
# Here is one more example for generating C array initializations:
|
199
|
+
#
|
200
|
+
# <% define 'Array', :for => CArray do %>
|
201
|
+
# <%= getType.name %> <%= name %>[<%= size %>] = {<%iinc%>
|
202
|
+
# <% expand 'InitValue', :foreach => initvalue, :separator => ",\n" %><%nl%><%idec%>
|
203
|
+
# };
|
204
|
+
# <% end %>
|
205
|
+
#
|
206
|
+
# <% define 'InitValue', :for => PrimitiveInitValue do %>
|
207
|
+
# <%= value %><%nows%>
|
208
|
+
# <% end %>
|
209
|
+
#
|
210
|
+
# The result could look something like:
|
211
|
+
#
|
212
|
+
# int myArray[3] = {
|
213
|
+
# 1,
|
214
|
+
# 2,
|
215
|
+
# 3
|
216
|
+
# };
|
217
|
+
#
|
218
|
+
# Note that in this example, the separator contains a newline. The current increment
|
219
|
+
# will be applied to each single expansion result since it starts in a new line.
|
220
|
+
#
|
221
|
+
#
|
222
|
+
# =Output Files
|
223
|
+
#
|
224
|
+
# Normally the generated content is to be written into one or more output files.
|
225
|
+
# The RGen template language facilitates this by means of the +file+ keyword.
|
226
|
+
#
|
227
|
+
# When the +file+ keyword is used to define a block, all output generated
|
228
|
+
# from template code within this block will be written to the specified file.
|
229
|
+
# This includes output generated from template expansions.
|
230
|
+
# Thus all output from templates expanded within this block is written to
|
231
|
+
# the same file as long as those templates do not use the +file+ keyword to
|
232
|
+
# define a new file context.
|
233
|
+
#
|
234
|
+
# Here is an example:
|
235
|
+
#
|
236
|
+
# <% file 'dbadapter/'+adapter.name+'.c' do %>
|
237
|
+
# all content within this block will be written to the specified file
|
238
|
+
# <% end %>
|
239
|
+
#
|
240
|
+
# Note that the filename itself can be calculated dynamically by an arbitrary
|
241
|
+
# Ruby expression.
|
242
|
+
#
|
243
|
+
# The absolute position where the output file is created depends on the output
|
244
|
+
# root directory passed to DirectoryTemplateContainer as described below.
|
245
|
+
#
|
246
|
+
# =Setting up the Generator
|
247
|
+
#
|
248
|
+
# Setting up the generator consists of 3 steps:
|
249
|
+
# * Instantiate DirectoryTemplateContainer passing one or more metamodel(s) and the output
|
250
|
+
# directory to the constructor.
|
251
|
+
# * Load the templates into the template container
|
252
|
+
# * Expand the root template to start generation
|
253
|
+
#
|
254
|
+
# Here is an example:
|
255
|
+
#
|
256
|
+
# module MyMM
|
257
|
+
# # metaclasses are defined here, e.g. using RGen::MetamodelBuilder
|
258
|
+
# end
|
259
|
+
#
|
260
|
+
# OUTPUT_DIR = File.dirname(__FILE__)+"/output"
|
261
|
+
# TEMPLATES_DIR = File.dirname(__FILE__)+"/templates"
|
262
|
+
#
|
263
|
+
# tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new(MyMM, OUTPUT_DIR)
|
264
|
+
# tc.load(TEMPLATES_DIR)
|
265
|
+
# # testModel should hold an instance of the metamodel class expected by the root template
|
266
|
+
# # the following line starts generation
|
267
|
+
# tc.expand('root::Root', :for => testModel, :indent => 1)
|
268
|
+
#
|
269
|
+
# The metamodel is the Ruby module which contains the metaclasses.
|
270
|
+
# This information is required for the template container in order to resolve the
|
271
|
+
# metamodel classes used within the template file.
|
272
|
+
# If several metamodels shall be used, an array of modules can be passed instead
|
273
|
+
# of a single module.
|
274
|
+
#
|
275
|
+
# The output path is prepended to the relative paths provided to the +file+
|
276
|
+
# definitions in the template files.
|
277
|
+
#
|
278
|
+
# The template directory should contain template files as described above.
|
279
|
+
#
|
280
|
+
# Finally the generation process is started by calling +expand+ in the same way as it
|
281
|
+
# is used from within templates.
|
282
|
+
#
|
283
|
+
# Also see the unit tests for more examples.
|
284
|
+
#
|
285
|
+
module TemplateLanguage
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|