rtext 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,112 @@
1
+ module RText
2
+
3
+ # ContextElementBuilder build a partial model from a set of context lines.
4
+ #
5
+ # Context lines are lines from an RText file which contain a (context) command and all
6
+ # the parent commands wrapped around it. Any sibling commands can be omitted as well as
7
+ # any lines containing closing braces and brackets.
8
+ #
9
+ # The resulting partial model contains a (context) model element and all its parent
10
+ # elements. Further references are not resolved.
11
+ #
12
+ module ContextElementBuilder
13
+
14
+ class << self
15
+
16
+ # Instantiates the context element based on a set of +content_lines+. Content lines
17
+ # are the RText lines containing the nested command headers in the original order.
18
+ # The last line of +context_lines+ is the one which will create the context element.
19
+ # +position_in_line+ is the cursor column position within the last line
20
+ def build_context_element(language, context_lines, position_in_line)
21
+ context_info = fix_context(context_lines)
22
+ return nil unless context_info
23
+ element = instantiate_context_element(language, context_info)
24
+ unless element
25
+ fix_current_line(context_info, position_in_line)
26
+ element = instantiate_context_element(language, context_info)
27
+ end
28
+ element
29
+ end
30
+
31
+ private
32
+
33
+ def instantiate_context_element(language, context_info)
34
+ root_elements = []
35
+ problems = []
36
+ Instantiator.new(language).instantiate(context_info.lines.join("\n"),
37
+ :root_elements => root_elements, :problems => problems)
38
+ if root_elements.size > 0
39
+ find_leaf_child(root_elements.first, context_info.num_elements-1)
40
+ else
41
+ nil
42
+ end
43
+ end
44
+
45
+ def find_leaf_child(element, num_required_children)
46
+ childs = element.class.ecore.eAllReferences.select{|r| r.containment}.collect{|r|
47
+ element.getGenericAsArray(r.name)}.flatten
48
+ if childs.size > 0
49
+ find_leaf_child(childs.first, num_required_children-1)
50
+ elsif num_required_children == 0
51
+ element
52
+ else
53
+ nil
54
+ end
55
+ end
56
+
57
+ ContextInfo = Struct.new(:lines, :num_elements, :pos_leaf_element)
58
+
59
+ # extend +context_lines+ into a set of lines which can be processed by the RText
60
+ # instantiator: cut of curly brace from current line if present and add missing
61
+ # closing curly braces and square brackets
62
+ # returns a ContextInfo containing the new set of lines, the number of model elements
63
+ # contained in this model snipped and the number of the line containing the leaf element
64
+ def fix_context(context_lines)
65
+ context_lines = context_lines.dup
66
+ line = context_lines.last
67
+ return nil if line.nil? || is_non_element_line(line)
68
+ context_lines << strip_curly_brace(context_lines.pop)
69
+ pos_leaf_element = context_lines.size-1
70
+ num_elements = 1
71
+ context_lines.reverse.each do |l|
72
+ if l =~ /\{\s*$/
73
+ context_lines << "}"
74
+ num_elements += 1
75
+ elsif l =~ /\[\s*$/
76
+ context_lines << "]"
77
+ end
78
+ end
79
+ ContextInfo.new(context_lines, num_elements, pos_leaf_element)
80
+ end
81
+
82
+ def is_non_element_line(line)
83
+ line = line.strip
84
+ line == "" || line == "}" || line == "]" || line =~ /^#/ || line =~ /^\w+:$/
85
+ end
86
+
87
+ def strip_curly_brace(line)
88
+ line.sub(/\{\s*$/,'')
89
+ end
90
+
91
+ def fix_current_line(context_info, pos_in_line)
92
+ context_info.lines[context_info.pos_leaf_element] =
93
+ cut_current_argument(context_info.lines[context_info.pos_leaf_element], pos_in_line)
94
+ end
95
+
96
+ def cut_current_argument(line, pos_in_line)
97
+ left_comma_pos = line.rindex(",", pos_in_line-1)
98
+ if left_comma_pos
99
+ line[0..left_comma_pos-1]
100
+ elsif line =~ /^\s*\w+/
101
+ $&
102
+ else
103
+ ""
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ end
110
+
111
+ end
112
+
@@ -0,0 +1,137 @@
1
+ require 'rgen/environment'
2
+ require 'rgen/util/file_change_detector'
3
+ require 'rgen/fragment/model_fragment'
4
+ require 'rtext/instantiator'
5
+
6
+ module RText
7
+
8
+ # Loads RText files into a FragmentedModel.
9
+ #
10
+ # A glob pattern or file provider specifies the files which should be loaded. The load method
11
+ # can be called to load and to reload the model. Only changed files will be reloaded.
12
+ #
13
+ # Optionally, the loader can use a fragment cache to speed up loading.
14
+ #
15
+ class DefaultLoader
16
+
17
+ # Create a default loader for +language+, loading into +fragmented_model+.
18
+ # It will find files either by evaluating the glob pattern given with +:pattern+
19
+ # (see Dir.glob) or by means of a +file_provider+. Options:
20
+ #
21
+ # :pattern
22
+ # a glob pattern or an array of glob patterns
23
+ # alternatively, a +:file_provider+ may be specified
24
+ #
25
+ # :file_provider
26
+ # a proc which is called without any arguments and should return the files to load
27
+ # this is an alternative to providing +:pattern+
28
+ #
29
+ # :cache
30
+ # a fragment cache to be used for loading
31
+ #
32
+ def initialize(language, fragmented_model, options={})
33
+ @lang = language
34
+ @model = fragmented_model
35
+ @change_detector = RGen::Util::FileChangeDetector.new(
36
+ :file_added => method(:file_added),
37
+ :file_removed => method(:file_removed),
38
+ :file_changed => method(:file_changed))
39
+ @cache = options[:cache]
40
+ @fragment_by_file = {}
41
+ pattern = options[:pattern]
42
+ @file_provider = options[:file_provider] || proc { Dir.glob(pattern) }
43
+ end
44
+
45
+ # Loads or reloads model fragments from files using the file patterns or file provider
46
+ # specified in the constructor. Options:
47
+ #
48
+ # :before_load
49
+ # a proc which is called before a file is actually loaded, receives the fragment to load
50
+ # into and a symbol indicating the kind of loading: :load, :load_cached, :load_update_cache
51
+ # default: no before load proc
52
+ #
53
+ def load(options={})
54
+ @before_load_proc = options[:before_load]
55
+ files = @file_provider.call
56
+ @change_detector.check_files(files)
57
+ @model.resolve(:fragment_provider => method(:fragment_provider),
58
+ :use_target_type => @lang.per_type_identifier)
59
+ end
60
+
61
+ private
62
+
63
+ def file_added(file)
64
+ fragment = RGen::Fragment::ModelFragment.new(file,
65
+ :identifier_provider => @lang.identifier_provider)
66
+ load_fragment_cached(fragment)
67
+ @model.add_fragment(fragment)
68
+ @fragment_by_file[file] = fragment
69
+ end
70
+
71
+ def file_removed(file)
72
+ @model.remove_fragment(@fragment_by_file[file])
73
+ @fragment_by_file.delete(file)
74
+ end
75
+
76
+ def file_changed(file)
77
+ file_removed(file)
78
+ file_added(file)
79
+ end
80
+
81
+ def fragment_provider(element)
82
+ fr = @lang.fragment_ref(element)
83
+ fr && fr.fragment
84
+ end
85
+
86
+ def load_fragment_cached(fragment)
87
+ if @cache
88
+ begin
89
+ result = @cache.load(fragment)
90
+ rescue ArgumentError => e
91
+ # Marshal#load raises an ArgumentError if required classes are not present
92
+ if e.message =~ /undefined class\/module/
93
+ result = :invalid
94
+ else
95
+ raise
96
+ end
97
+ end
98
+ if result == :invalid
99
+ @before_load_proc && @before_load_proc.call(fragment, :load_update_cache)
100
+ load_fragment(fragment)
101
+ @cache.store(fragment)
102
+ else
103
+ @before_load_proc && @before_load_proc.call(fragment, :load_cached)
104
+ end
105
+ else
106
+ @before_load_proc && @before_load_proc.call(fragment, :load)
107
+ load_fragment(fragment)
108
+ end
109
+ end
110
+
111
+ def load_fragment(fragment)
112
+ env = RGen::Environment.new
113
+ urefs = []
114
+ problems = []
115
+ root_elements = []
116
+ inst = RText::Instantiator.new(@lang)
117
+ File.open(fragment.location) do |f|
118
+ inst.instantiate(f.read,
119
+ :env => env,
120
+ :unresolved_refs => urefs,
121
+ :problems => problems,
122
+ :root_elements => root_elements,
123
+ :fragment_ref => fragment.fragment_ref,
124
+ :file_name => fragment.location)
125
+ end
126
+ fragment.data = {:problems => problems}
127
+ fragment.set_root_elements(root_elements,
128
+ :unresolved_refs => urefs,
129
+ :elements => env.elements)
130
+ fragment.build_index
131
+ fragment.resolve_local(:use_target_type => @lang.per_type_identifier)
132
+ end
133
+
134
+ end
135
+
136
+ end
137
+
@@ -0,0 +1,153 @@
1
+ module RText
2
+
3
+ class DefaultServiceProvider
4
+
5
+ def initialize(language, fragmented_model, model_loader)
6
+ @lang = language
7
+ @model = fragmented_model
8
+ @loader = model_loader
9
+ @element_name_index = nil
10
+ @model.add_fragment_change_listener(proc {|fragment, kind|
11
+ @element_name_index = nil
12
+ })
13
+ end
14
+
15
+ def load_model
16
+ @loader.load
17
+ end
18
+
19
+ ReferenceCompletionOption = Struct.new(:identifier, :type)
20
+ def get_reference_completion_options(reference, context)
21
+ if @model.environment
22
+ targets = @model.environment.find(:class => reference.eType.instanceClass)
23
+ else
24
+ clazz = reference.eType.instanceClass
25
+ targets = @model.index.values.flatten.select{|e| e.is_a?(clazz)}
26
+ end
27
+ targets.collect{|t|
28
+ ident = @lang.identifier_provider.call(t, context)
29
+ if ident
30
+ ReferenceCompletionOption.new(ident, t.class.ecore.name)
31
+ else
32
+ nil
33
+ end
34
+ }.compact.sort{|a,b| a.identifier <=> b.identifier}
35
+ end
36
+
37
+ ReferenceTarget = Struct.new(:file, :line, :display_name)
38
+ def get_reference_targets(identifier, context, lines, linepos)
39
+ result = []
40
+ identifier = @lang.qualify_reference(identifier, context)
41
+ targets = @model.index[identifier]
42
+ if targets && @lang.per_type_identifier
43
+ current_line = lines.last
44
+ linestart = current_line[0..linepos-1]
45
+ if linestart =~ /\s*(\w+)\s+(?:[^,]+,)*\s*(\w+):\s*(\S*)$/
46
+ command, fn, prefix = $1, $2, $3
47
+ clazz = @lang.class_by_command(command)
48
+ feature = clazz && @lang.non_containments(clazz.ecore).find{|f| f.name == fn}
49
+ if feature
50
+ targets = targets.select{|t| t.is_a?(feature.eType.instanceClass)}
51
+ end
52
+ end
53
+ end
54
+ targets && targets.each do |t|
55
+ if @lang.fragment_ref(t)
56
+ path = File.expand_path(@lang.fragment_ref(t).fragment.location)
57
+ result << ReferenceTarget.new(path, @lang.line_number(t), "#{identifier} [#{t.class.ecore.name}]")
58
+ end
59
+ end
60
+ result
61
+ end
62
+
63
+ def get_referencing_elements(identifier, context)
64
+ result = []
65
+ targets = @model.index[@lang.identifier_provider.call(context, nil)]
66
+ if targets && @lang.per_type_identifier
67
+ targets = targets.select{|t| t.class == context.class}
68
+ end
69
+ if targets && targets.size == 1
70
+ target = targets.first
71
+ elements = target.class.ecore.eAllReferences.select{|r|
72
+ r.eOpposite && !r.containment && !r.eOpposite.containment}.collect{|r|
73
+ target.getGenericAsArray(r.name)}.flatten
74
+ elements.each do |e|
75
+ if @lang.fragment_ref(e)
76
+ path = File.expand_path(@lang.fragment_ref(e).fragment.location)
77
+ display_name = ""
78
+ ident = @lang.identifier_provider.call(e, nil)
79
+ display_name += "#{ident} " if ident
80
+ display_name += "[#{e.class.ecore.name}]"
81
+ result << ReferenceTarget.new(path, @lang.line_number(e), display_name)
82
+ end
83
+ end
84
+ end
85
+ result
86
+ end
87
+
88
+ FileProblems = Struct.new(:file, :problems)
89
+ Problem = Struct.new(:severity, :line, :message)
90
+ def get_problems
91
+ load_model
92
+ result = []
93
+ @model.fragments.sort{|a,b| a.location <=> b.location}.each do |fragment|
94
+ problems = []
95
+ if fragment.data && fragment.data[:problems]
96
+ fragment.data[:problems].each do |p|
97
+ problems << Problem.new("Error", p.line, p.message)
98
+ end
99
+ end
100
+ fragment.unresolved_refs.each do |ur|
101
+ # TODO: where do these proxies come from?
102
+ next unless ur.proxy.targetIdentifier
103
+ problems << Problem.new("Error", @lang.line_number(ur.element), "unresolved reference #{ur.proxy.targetIdentifier}")
104
+ end
105
+ if problems.size > 0
106
+ result << FileProblems.new(File.expand_path(fragment.location), problems)
107
+ end
108
+ end
109
+ result
110
+ end
111
+
112
+ OpenElementChoice = Struct.new(:display_name, :file, :line)
113
+ def get_open_element_choices(pattern)
114
+ result = []
115
+ return result unless pattern
116
+ sub_index = element_name_index[pattern[0..0].downcase]
117
+ sub_index && sub_index.each_pair do |ident, elements|
118
+ if ident.split(/\W/).last.downcase.index(pattern.downcase) == 0
119
+ elements.each do |e|
120
+ if @lang.fragment_ref(e)
121
+ non_word_index = ident.rindex(/\W/)
122
+ if non_word_index
123
+ name = ident[non_word_index+1..-1]
124
+ scope = ident[0..non_word_index-1]
125
+ else
126
+ name = ident
127
+ scope = ""
128
+ end
129
+ display_name = "#{name} [#{e.class.ecore.name}]"
130
+ display_name += " - #{scope}" if scope.size > 0
131
+ path = File.expand_path(@lang.fragment_ref(e).fragment.location)
132
+ result << OpenElementChoice.new(display_name, path, @lang.line_number(e))
133
+ end
134
+ end
135
+ end
136
+ end
137
+ result.sort{|a,b| a.display_name <=> b.display_name}
138
+ end
139
+
140
+ def element_name_index
141
+ return @element_name_index if @element_name_index
142
+ @element_name_index = {}
143
+ @model.index.each_pair do |ident, elements|
144
+ key = ident.split(/\W/).last[0..0].downcase
145
+ @element_name_index[key] ||= {}
146
+ @element_name_index[key][ident] = elements
147
+ end
148
+ @element_name_index
149
+ end
150
+
151
+ end
152
+
153
+ end
@@ -0,0 +1,284 @@
1
+ require 'rgen/ecore/ecore_ext'
2
+ require 'rgen/instantiator/reference_resolver'
3
+ require 'rtext/parser'
4
+
5
+ module RText
6
+
7
+ class Instantiator
8
+
9
+ # A problem found during instantiation
10
+ # if the file is not known, it will be nil
11
+ InstantiatorProblem = Struct.new(:message, :file, :line)
12
+
13
+ # Creates an instantiator for RText::Language +language+
14
+ #
15
+ def initialize(language)
16
+ @lang = language
17
+ end
18
+
19
+ # instantiate +str+, +options+ include:
20
+ #
21
+ # :env
22
+ # environment to which model elements will be added
23
+ #
24
+ # :problems
25
+ # an array to which problems will be appended
26
+ #
27
+ # :unresolved_refs
28
+ # an array to which unresolved references will be appended
29
+ #
30
+ # :root_elements
31
+ # an array which will hold the root elements
32
+ #
33
+ # :file_name
34
+ # name of the file being instantiated, will be set on model elements
35
+ #
36
+ # :fragment_ref
37
+ # object which references the fragment being instantiated, will be set on model elements
38
+ #
39
+ def instantiate(str, options={})
40
+ @line_numbers = {}
41
+ @env = options[:env]
42
+ @problems = options[:problems] || []
43
+ @unresolved_refs = options[:unresolved_refs]
44
+ @root_elements = options[:root_elements] || []
45
+ @file_name = options[:file_name]
46
+ @fragment_ref = options[:fragment_ref]
47
+ parser = Parser.new(@lang.reference_regexp)
48
+ begin
49
+ @root_elements.clear
50
+ parser.parse(str) do |*args|
51
+ if args[0]
52
+ create_element(*args)
53
+ else
54
+ unassociated_comments(args[3])
55
+ end
56
+ end
57
+ rescue Parser::Error => e
58
+ problem(e.message, e.line)
59
+ @unresolved_refs.clear if @unresolved_refs
60
+ @root_elements.clear
61
+ end
62
+ if @unresolved_refs
63
+ @unresolved_refs.each do |ur|
64
+ ur.proxy.targetIdentifier = @lang.qualify_reference(ur.proxy.targetIdentifier, ur.element)
65
+ end
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def unassociated_comments(comments)
72
+ comments.each do |c|
73
+ handle_comment(c, nil)
74
+ end
75
+ end
76
+
77
+ def create_element(command, arg_list, element_list, comments, is_root)
78
+ clazz = @lang.class_by_command(command.value)
79
+ if !clazz
80
+ problem("Unknown command '#{command.value}'", command.line)
81
+ return
82
+ end
83
+ if clazz.ecore.abstract
84
+ problem("Unknown command '#{command.value}' (metaclass is abstract)", command.line)
85
+ return
86
+ end
87
+ element = clazz.new
88
+ @env << element if @env
89
+ @root_elements << element if is_root
90
+ unlabled_args = @lang.unlabled_arguments(clazz.ecore).name
91
+ di_index = 0
92
+ defined_args = {}
93
+ arg_list.each do |a|
94
+ if is_labeled(a)
95
+ set_argument(element, a[0].value, a[1], defined_args, command.line)
96
+ else
97
+ if di_index < unlabled_args.size
98
+ set_argument(element, unlabled_args[di_index], a, defined_args, command.line)
99
+ di_index += 1
100
+ else
101
+ problem("Unexpected unlabled argument, #{unlabled_args.size} unlabled arguments expected", command.line)
102
+ end
103
+ end
104
+ end
105
+ element_list.each do |e|
106
+ if is_labeled(e)
107
+ add_children(element, e[1], e[0].value, e[0].line)
108
+ else
109
+ add_children(element, e, nil, nil)
110
+ end
111
+ end
112
+ set_line_number(element, command.line)
113
+ set_file_name(element)
114
+ set_fragment_ref(element)
115
+ comments.each do |c|
116
+ handle_comment(c, element)
117
+ end
118
+ element
119
+ end
120
+
121
+ def add_children(element, children, role, role_line)
122
+ if role
123
+ feature = @lang.feature_by_name(element.class.ecore, role)
124
+ if !feature
125
+ problem("Unknown child role '#{role}'", role_line)
126
+ return
127
+ end
128
+ if !feature.is_a?(RGen::ECore::EReference) || !feature.containment
129
+ problem("Role '#{role}' can not take child elements", role_line)
130
+ return
131
+ end
132
+ children = [children] unless children.is_a?(Array)
133
+ children.compact!
134
+ if children.size == 0
135
+ return
136
+ end
137
+ if !feature.many &&
138
+ (element.getGenericAsArray(role).size > 0 || children.size > 1)
139
+ problem("Only one child allowed in role '#{role}'", line_number(children[0]))
140
+ return
141
+ end
142
+ expected_type = @lang.concrete_types(feature.eType)
143
+ children.each do |c|
144
+ if !expected_type.include?(c.class.ecore)
145
+ problem("Role '#{role}' can not take a #{c.class.ecore.name}, expected #{expected_type.name.join(", ")}", line_number(c))
146
+ else
147
+ element.setOrAddGeneric(feature.name, c)
148
+ end
149
+ end
150
+ else
151
+ raise "if there is no role, children must not be an Array" if children.is_a?(Array)
152
+ child = children
153
+ return if child.nil?
154
+ feature = @lang.containments_by_target_type(element.class.ecore, child.class.ecore)
155
+ if feature.size == 0
156
+ problem("This kind of element can not be contained here", line_number(child))
157
+ return
158
+ end
159
+ if feature.size > 1
160
+ problem("Role of element is ambiguous, use a role label", line_number(child))
161
+ return
162
+ end
163
+ feature = feature[0]
164
+ if element.getGenericAsArray(feature.name).size > 0 && !feature.many
165
+ problem("Only one child allowed in role '#{feature.name}'", line_number(child))
166
+ return
167
+ end
168
+ element.setOrAddGeneric(feature.name, child)
169
+ end
170
+ end
171
+
172
+ def set_argument(element, name, value, defined_args, line)
173
+ feature = @lang.feature_by_name(element.class.ecore, name)
174
+ if !feature
175
+ problem("Unknown argument '#{name}'", line)
176
+ return
177
+ end
178
+ if feature.is_a?(RGen::ECore::EReference) && feature.containment
179
+ problem("Argument '#{name}' can only take child elements", line)
180
+ return
181
+ end
182
+ if defined_args[name]
183
+ problem("Argument '#{name}' already defined", line)
184
+ return
185
+ end
186
+ value = [value] unless value.is_a?(Array)
187
+ if value.size > 1 && !feature.many
188
+ problem("Argument '#{name}' can take only one value", line)
189
+ return
190
+ end
191
+ expected_kind = expected_token_kind(feature)
192
+ value.each do |v|
193
+ if !expected_kind.include?(v.kind)
194
+ problem("Argument '#{name}' can not take a #{v.kind}, expected #{expected_kind.join(", ")}", line)
195
+ elsif feature.eType.is_a?(RGen::ECore::EEnum)
196
+ if !feature.eType.eLiterals.name.include?(v.value)
197
+ problem("Argument '#{name}' can not take value #{v.value}, expected #{feature.eType.eLiterals.name.join(", ")}", line)
198
+ else
199
+ element.setOrAddGeneric(feature.name, v.value.to_sym)
200
+ end
201
+ elsif feature.is_a?(RGen::ECore::EReference)
202
+ proxy = RGen::MetamodelBuilder::MMProxy.new(v.value)
203
+ if @unresolved_refs
204
+ @unresolved_refs <<
205
+ RGen::Instantiator::ReferenceResolver::UnresolvedReference.new(element, feature.name, proxy)
206
+ end
207
+ element.setOrAddGeneric(feature.name, proxy)
208
+ else
209
+ element.setOrAddGeneric(feature.name, v.value)
210
+ end
211
+ end
212
+ defined_args[name] = true
213
+ end
214
+
215
+ def handle_comment(comment_desc, element)
216
+ if @lang.comment_handler
217
+ kind = comment_desc[1]
218
+ if kind == :eol
219
+ comment = comment_desc[0].value
220
+ else
221
+ comment = comment_desc[0].collect{|c| c.value}.join("\n")
222
+ end
223
+ success = @lang.comment_handler.call(comment, kind, element, @env)
224
+ if !success
225
+ if element.nil?
226
+ problem("Unassociated comment not allowed", comment_desc[0][0].line)
227
+ else
228
+ problem("This kind of element can not take this comment", line_number(element))
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ def is_labeled(a)
235
+ a.is_a?(Array) && a[0].respond_to?(:kind) && a[0].kind == :label
236
+ end
237
+
238
+ def expected_token_kind(feature)
239
+ if feature.is_a?(RGen::ECore::EReference)
240
+ [:reference, :identifier]
241
+ elsif feature.eType.is_a?(RGen::ECore::EEnum)
242
+ [:identifier, :string]
243
+ else
244
+ { String => [:string, :identifier],
245
+ Integer => [:integer],
246
+ Float => [:float],
247
+ RGen::MetamodelBuilder::DataTypes::Boolean => [:boolean]
248
+ }[feature.eType.instanceClass]
249
+ end
250
+ end
251
+
252
+ def set_line_number(element, line)
253
+ @line_numbers[element] = line
254
+ if @lang.line_number_attribute && element.respond_to?("#{@lang.line_number_attribute}=")
255
+ element.send("#{@lang.line_number_attribute}=", line)
256
+ end
257
+ end
258
+
259
+ def set_file_name(element)
260
+ if @file_name &&
261
+ @lang.file_name_attribute && element.respond_to?("#{@lang.file_name_attribute}=")
262
+ element.send("#{@lang.file_name_attribute}=", @file_name)
263
+ end
264
+ end
265
+
266
+ def set_fragment_ref(element)
267
+ if @fragment_ref &&
268
+ @lang.fragment_ref_attribute && element.respond_to?("#{@lang.fragment_ref_attribute}=")
269
+ element.send("#{@lang.fragment_ref_attribute}=", @fragment_ref)
270
+ end
271
+ end
272
+
273
+ def line_number(e)
274
+ @line_numbers[e]
275
+ end
276
+
277
+ def problem(msg, line)
278
+ @problems << InstantiatorProblem.new(msg, @file_name, line)
279
+ end
280
+
281
+ end
282
+
283
+ end
284
+