zafu 0.5.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.
- data/.gitignore +2 -0
- data/History.txt +9 -0
- data/README.rdoc +56 -0
- data/Rakefile +56 -0
- data/lib/zafu/all.rb +17 -0
- data/lib/zafu/compiler.rb +7 -0
- data/lib/zafu/controller_methods.rb +49 -0
- data/lib/zafu/handler.rb +57 -0
- data/lib/zafu/info.rb +4 -0
- data/lib/zafu/markup.rb +186 -0
- data/lib/zafu/mock_helper.rb +42 -0
- data/lib/zafu/node_context.rb +96 -0
- data/lib/zafu/parser.rb +564 -0
- data/lib/zafu/parsing_rules.rb +257 -0
- data/lib/zafu/process/ajax.rb +90 -0
- data/lib/zafu/process/conditional.rb +45 -0
- data/lib/zafu/process/context.rb +45 -0
- data/lib/zafu/process/html.rb +168 -0
- data/lib/zafu/process/ruby_less.rb +145 -0
- data/lib/zafu/template.rb +25 -0
- data/lib/zafu/test_helper.rb +19 -0
- data/lib/zafu.rb +7 -0
- data/rails/init.rb +1 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/test/markup_test.rb +232 -0
- data/test/mock/params.rb +19 -0
- data/test/node_context_test.rb +190 -0
- data/test/ruby_less_test.rb +37 -0
- data/test/test_helper.rb +9 -0
- data/test/zafu_test.rb +57 -0
- data/zafu.gemspec +83 -0
- metadata +124 -0
data/lib/zafu/parser.rb
ADDED
@@ -0,0 +1,564 @@
|
|
1
|
+
module Zafu
|
2
|
+
|
3
|
+
def self.parser_with_rules(*modules)
|
4
|
+
parser = Class.new(Parser)
|
5
|
+
modules.flatten.each do |mod|
|
6
|
+
parser.send(:include, mod)
|
7
|
+
end
|
8
|
+
parser
|
9
|
+
end
|
10
|
+
|
11
|
+
class Parser
|
12
|
+
@@callbacks = {}
|
13
|
+
attr_accessor :text, :method, :pass, :options, :blocks, :ids, :defined_ids, :parent
|
14
|
+
# Method parameters "<r:show attr='name'/>" (params contains {'attr' => 'name'}).
|
15
|
+
attr_accessor :params
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def new_with_url(path, opts={})
|
19
|
+
helper = opts[:helper] || Zafu::MockHelper.new
|
20
|
+
text, fullpath, base_path = self.get_template_text(path, helper)
|
21
|
+
self.new(text, :helper => helper, :base_path => base_path, :included_history => [fullpath])
|
22
|
+
end
|
23
|
+
|
24
|
+
# Retrieve the template text in the current folder or as an absolute path.
|
25
|
+
# This method is used when 'including' text
|
26
|
+
def get_template_text(path, helper, base_path=nil)
|
27
|
+
res = helper.send(:get_template_text, path, base_path)
|
28
|
+
return [parser_error("template '#{path}' not found", 'include'), nil, nil] unless res
|
29
|
+
text, fullpath, base_path = *res
|
30
|
+
return res
|
31
|
+
end
|
32
|
+
|
33
|
+
def parser_error(message, method)
|
34
|
+
"<span class='parser_error'><span class='method'>#{method}</span> #{message}</span>"
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_accessor :before_process_callbacks
|
38
|
+
|
39
|
+
def before_process_callbacks
|
40
|
+
@before_process_callbacks ||= []
|
41
|
+
end
|
42
|
+
|
43
|
+
def before_process(*args)
|
44
|
+
self.before_process_callbacks += args
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize(text, opts={})
|
49
|
+
@stack = []
|
50
|
+
@ok = true
|
51
|
+
@blocks = []
|
52
|
+
|
53
|
+
@options = {:mode=>:void, :method=>'void'}.merge(opts)
|
54
|
+
@params = @options.delete(:params) || {}
|
55
|
+
@method = @options.delete(:method)
|
56
|
+
@ids = @options[:ids] ||= {}
|
57
|
+
original_ids = @ids.dup
|
58
|
+
@defined_ids = {} # ids defined in this node or this node's sub blocks
|
59
|
+
mode = @options.delete(:mode)
|
60
|
+
@parent = @options.delete(:parent)
|
61
|
+
|
62
|
+
if opts[:sub]
|
63
|
+
@text = text
|
64
|
+
else
|
65
|
+
@text = before_parse(text)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
start(mode)
|
70
|
+
|
71
|
+
# set name
|
72
|
+
@name ||= @options[:name] || @params[:id]
|
73
|
+
@options[:ids][@name] = self if @name
|
74
|
+
|
75
|
+
unless opts[:sub]
|
76
|
+
@text = after_parse(@text)
|
77
|
+
end
|
78
|
+
@ids.keys.each do |k|
|
79
|
+
if original_ids[k] != @ids[k]
|
80
|
+
@defined_ids[k] = @ids[k]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
@ok
|
84
|
+
end
|
85
|
+
|
86
|
+
def to_erb(context)
|
87
|
+
context[:helper] ||= @options[:helper]
|
88
|
+
process(context)
|
89
|
+
end
|
90
|
+
|
91
|
+
def start(mode)
|
92
|
+
enter(mode)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Hook called when replacing part of an included template with '<r:with part='main'>...</r:with>'
|
96
|
+
# This replaces the current object 'self' which is in the original included template, with the custom version 'obj'.
|
97
|
+
def replace_with(obj)
|
98
|
+
# keep @method (obj's method is always 'with')
|
99
|
+
@blocks = obj.blocks.empty? ? @blocks : obj.blocks
|
100
|
+
@params.merge!(obj.params)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Hook called when including a part "<r:include template='layout' part='title'/>"
|
104
|
+
def include_part(obj)
|
105
|
+
[obj]
|
106
|
+
end
|
107
|
+
|
108
|
+
def empty?
|
109
|
+
@blocks == [] && (@params == {} || @params == {:part => @params[:part]})
|
110
|
+
end
|
111
|
+
|
112
|
+
def process(context={})
|
113
|
+
if @name
|
114
|
+
# we pass the name as 'context' in the children tags
|
115
|
+
@context = context.merge(:name => @name)
|
116
|
+
else
|
117
|
+
@context = context
|
118
|
+
end
|
119
|
+
@result = ""
|
120
|
+
@out_post = ""
|
121
|
+
|
122
|
+
before_process
|
123
|
+
|
124
|
+
@pass = {} # used to pass information to the parent
|
125
|
+
res = nil
|
126
|
+
|
127
|
+
if respond_to?("r_#{@method}".to_sym)
|
128
|
+
res = do_method("r_#{@method}".to_sym)
|
129
|
+
else
|
130
|
+
res = do_method(:r_unknown)
|
131
|
+
end
|
132
|
+
|
133
|
+
# @text contains unparsed data (empty space)
|
134
|
+
after_process(res + @text)
|
135
|
+
end
|
136
|
+
|
137
|
+
def do_method(sym)
|
138
|
+
res = self.send(sym)
|
139
|
+
if res.kind_of?(String)
|
140
|
+
@result << res
|
141
|
+
elsif @result.blank?
|
142
|
+
@result << @method
|
143
|
+
end
|
144
|
+
@result + @out_post
|
145
|
+
end
|
146
|
+
|
147
|
+
def r_void
|
148
|
+
expand_with
|
149
|
+
end
|
150
|
+
|
151
|
+
def r_ignore
|
152
|
+
end
|
153
|
+
|
154
|
+
alias to_s r_void
|
155
|
+
|
156
|
+
def r_inspect
|
157
|
+
expand_with(:preflight=>true)
|
158
|
+
@blocks = []
|
159
|
+
@pass.merge!(@parts||{})
|
160
|
+
self.inspect
|
161
|
+
end
|
162
|
+
|
163
|
+
# basic rule to display errors
|
164
|
+
def r_unknown
|
165
|
+
sp = ""
|
166
|
+
@params.each do |k,v|
|
167
|
+
sp += " #{k}=#{v.inspect.gsub("'","TMPQUOTE").gsub('"',"'").gsub("TMPQUOTE",'"')}"
|
168
|
+
end
|
169
|
+
|
170
|
+
res = "<span class='parser_unknown'><r:#{@method}#{sp}"
|
171
|
+
inner = expand_with
|
172
|
+
if inner != ''
|
173
|
+
res + "></span>#{inner}<span class='parser_unknown'><r:/#{@method}></span>"
|
174
|
+
else
|
175
|
+
res + "/></span>"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Set context with variables (unsafe) from template.
|
180
|
+
def r_expand_with
|
181
|
+
hash = {}
|
182
|
+
@params.each do |k,v|
|
183
|
+
hash["exp_#{k}"] = v.inspect
|
184
|
+
end
|
185
|
+
expand_with(hash)
|
186
|
+
end
|
187
|
+
|
188
|
+
def before_process
|
189
|
+
self.class.before_process_callbacks.each do |callback|
|
190
|
+
self.send(callback)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def after_process(text)
|
195
|
+
text
|
196
|
+
end
|
197
|
+
|
198
|
+
def before_parse(text)
|
199
|
+
text
|
200
|
+
end
|
201
|
+
|
202
|
+
def after_parse(text)
|
203
|
+
text
|
204
|
+
end
|
205
|
+
|
206
|
+
def include_template
|
207
|
+
return parser_error("missing 'template' attribute", 'include') unless @params[:template]
|
208
|
+
if @options[:part] && @options[:part] == @params[:part]
|
209
|
+
# fetching only a part, do not open this element (same as original caller) as it is useless and will make us loop the loop.
|
210
|
+
@method = 'ignore'
|
211
|
+
enter(:void)
|
212
|
+
return
|
213
|
+
end
|
214
|
+
@method = 'void'
|
215
|
+
|
216
|
+
# fetch text
|
217
|
+
@options[:included_history] ||= []
|
218
|
+
|
219
|
+
included_text, absolute_url, base_path = self.class.get_template_text(@params[:template], @options[:helper], @options[:base_path])
|
220
|
+
|
221
|
+
if absolute_url
|
222
|
+
absolute_url += "::#{@params[:part].gsub('/','_')}" if @params[:part]
|
223
|
+
absolute_url += "??#{@options[:part].gsub('/','_')}" if @options[:part]
|
224
|
+
if @options[:included_history].include?(absolute_url)
|
225
|
+
included_text = parser_error("infinity loop: #{(@options[:included_history] + [absolute_url]).join(' --> ')}", 'include')
|
226
|
+
else
|
227
|
+
included_history = @options[:included_history] + [absolute_url]
|
228
|
+
end
|
229
|
+
end
|
230
|
+
res = self.class.new(included_text, :helper => @options[:helper], :base_path => base_path, :included_history => included_history, :part => @params[:part]) # we set :part to avoid loop failure when doing self inclusion
|
231
|
+
|
232
|
+
if @params[:part]
|
233
|
+
if iblock = res.ids[@params[:part]]
|
234
|
+
included_blocks = include_part(iblock)
|
235
|
+
# get all ids from inside the included part:
|
236
|
+
@ids.merge! iblock.defined_ids
|
237
|
+
else
|
238
|
+
included_blocks = [parser_error("'#{@params[:part]}' not found in template '#{@params[:template]}'", 'include')]
|
239
|
+
end
|
240
|
+
else
|
241
|
+
included_blocks = res.blocks
|
242
|
+
@ids.merge! res.ids
|
243
|
+
end
|
244
|
+
|
245
|
+
enter(:void) # normal scan on content
|
246
|
+
# replace 'with'
|
247
|
+
|
248
|
+
not_found = []
|
249
|
+
@blocks.each do |b|
|
250
|
+
next if b.kind_of?(String) || b.method != 'with'
|
251
|
+
if target = res.ids[b.params[:part]]
|
252
|
+
if target.kind_of?(String)
|
253
|
+
# error
|
254
|
+
elsif b.empty?
|
255
|
+
target.method = 'ignore'
|
256
|
+
else
|
257
|
+
target.replace_with(b)
|
258
|
+
end
|
259
|
+
else
|
260
|
+
# part not found
|
261
|
+
not_found << parser_error("'#{b.params[:part]}' not found in template '#{@params[:template]}'", 'with')
|
262
|
+
end
|
263
|
+
end
|
264
|
+
@blocks = included_blocks + not_found
|
265
|
+
end
|
266
|
+
|
267
|
+
# Return a hash of all descendants. Find a specific descendant with descendant['form'] for example.
|
268
|
+
def all_descendants
|
269
|
+
@all_descendants ||= begin
|
270
|
+
d = {}
|
271
|
+
@blocks.each do |b|
|
272
|
+
next if b.kind_of?(String)
|
273
|
+
b.public_descendants.each do |k,v|
|
274
|
+
d[k] ||= []
|
275
|
+
d[k] += v
|
276
|
+
end
|
277
|
+
# latest is used first: use direct children before grandchildren.
|
278
|
+
d[b.method] ||= []
|
279
|
+
d[b.method] << b
|
280
|
+
end
|
281
|
+
d
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# Find a direct child with +child[method]+.
|
286
|
+
def child
|
287
|
+
Hash[*@blocks.map do |b|
|
288
|
+
b.kind_of?(String) ? nil : [b.method, b]
|
289
|
+
end.compact.flatten]
|
290
|
+
end
|
291
|
+
|
292
|
+
def descendants(key)
|
293
|
+
all_descendants[key] || []
|
294
|
+
end
|
295
|
+
|
296
|
+
def ancestors
|
297
|
+
@ancestors ||= begin
|
298
|
+
if parent
|
299
|
+
parent.ancestors + [parent]
|
300
|
+
else
|
301
|
+
[]
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
alias public_descendants all_descendants
|
307
|
+
|
308
|
+
# Return the last defined parent for the given key.
|
309
|
+
def ancestor(key)
|
310
|
+
res = nil
|
311
|
+
ancestors.reverse_each do |a|
|
312
|
+
if key == a.method
|
313
|
+
res = a
|
314
|
+
break
|
315
|
+
end
|
316
|
+
end
|
317
|
+
res
|
318
|
+
end
|
319
|
+
|
320
|
+
# Return the last defined descendant for the given key.
|
321
|
+
def descendant(key)
|
322
|
+
descendants(key).last
|
323
|
+
end
|
324
|
+
|
325
|
+
# Return the root block (the one opened first).
|
326
|
+
def root
|
327
|
+
@root ||= parent ? parent.root : self
|
328
|
+
end
|
329
|
+
|
330
|
+
def success?
|
331
|
+
return @ok
|
332
|
+
end
|
333
|
+
|
334
|
+
def flush(str=@text)
|
335
|
+
return if str == ''
|
336
|
+
if @blocks.last.kind_of?(String)
|
337
|
+
@blocks[-1] << str
|
338
|
+
else
|
339
|
+
@blocks << str
|
340
|
+
end
|
341
|
+
@text = @text[str.length..-1]
|
342
|
+
end
|
343
|
+
|
344
|
+
# Build blocks
|
345
|
+
def store(obj)
|
346
|
+
if obj.kind_of?(String) && @blocks.last.kind_of?(String)
|
347
|
+
@blocks[-1] << obj
|
348
|
+
elsif obj != ''
|
349
|
+
@blocks << obj
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
# Output ERB code during ast processing.
|
354
|
+
def out(str)
|
355
|
+
@result << str
|
356
|
+
# Avoid double entry when this is the last call in a render method.
|
357
|
+
nil
|
358
|
+
end
|
359
|
+
|
360
|
+
# Output ERB code that will be inserted after @result.
|
361
|
+
def out_post(str)
|
362
|
+
@out_post << str
|
363
|
+
# Avoid double entry when this is the last call in a render method.
|
364
|
+
nil
|
365
|
+
end
|
366
|
+
|
367
|
+
# Advance parser.
|
368
|
+
def eat(arg)
|
369
|
+
if arg.kind_of?(String)
|
370
|
+
len = arg.length
|
371
|
+
elsif arg.kind_of?(Fixnum)
|
372
|
+
len = arg
|
373
|
+
else
|
374
|
+
raise
|
375
|
+
end
|
376
|
+
@text = @text[len..-1]
|
377
|
+
end
|
378
|
+
|
379
|
+
def enter(mode)
|
380
|
+
@stack << mode
|
381
|
+
# puts "ENTER(#{@method},:#{mode}) [#{@text}] #{@zafu_tag_count.inspect}"
|
382
|
+
if mode == :void
|
383
|
+
sym = :scan
|
384
|
+
else
|
385
|
+
sym = "scan_#{mode}".to_sym
|
386
|
+
end
|
387
|
+
while (@text != '' && @stack[-1] == mode)
|
388
|
+
# puts "CONTINUE(#{@method},:#{mode}) [#{@text}] #{@zafu_tag_count.inspect}"
|
389
|
+
self.send(sym)
|
390
|
+
end
|
391
|
+
# puts "LEAVE(#{@method},:#{mode}) [#{@text}] #{@zafu_tag_count.inspect}"
|
392
|
+
end
|
393
|
+
|
394
|
+
def make(mode, opts={})
|
395
|
+
if opts[:text]
|
396
|
+
custom_text = opts.delete(:text)
|
397
|
+
end
|
398
|
+
text = custom_text || @text
|
399
|
+
opts = @options.merge(opts).merge(:sub=>true, :mode=>mode, :parent => self)
|
400
|
+
new_obj = self.class.new(text,opts)
|
401
|
+
if new_obj.success?
|
402
|
+
@text = new_obj.text unless custom_text
|
403
|
+
new_obj.text = ""
|
404
|
+
store new_obj
|
405
|
+
else
|
406
|
+
flush @text[0..(new_obj.text.length - @text.length)] unless custom_text
|
407
|
+
end
|
408
|
+
# puts "MADE #{new_obj.inspect}"
|
409
|
+
# puts "TEXT #{@text.inspect}"
|
410
|
+
new_obj
|
411
|
+
end
|
412
|
+
|
413
|
+
def leave(mode=nil)
|
414
|
+
if mode.nil?
|
415
|
+
@stack = []
|
416
|
+
return
|
417
|
+
end
|
418
|
+
pop = true
|
419
|
+
while @stack != [] && pop
|
420
|
+
pop = @stack.pop
|
421
|
+
break if pop == mode
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
def fail
|
426
|
+
@ok = false
|
427
|
+
@stack = []
|
428
|
+
end
|
429
|
+
|
430
|
+
def check_params(*args)
|
431
|
+
missing = []
|
432
|
+
if args[0].kind_of?(Array)
|
433
|
+
# or groups
|
434
|
+
ok = false
|
435
|
+
args.each_index do |i|
|
436
|
+
unless args[i].kind_of?(Array)
|
437
|
+
missing[i] = [args[i]]
|
438
|
+
next
|
439
|
+
end
|
440
|
+
missing[i] = []
|
441
|
+
args[i].each do |arg|
|
442
|
+
missing[i] << arg.to_s unless @params[arg]
|
443
|
+
end
|
444
|
+
if missing[i] == []
|
445
|
+
ok = true
|
446
|
+
break
|
447
|
+
end
|
448
|
+
end
|
449
|
+
if ok
|
450
|
+
return true
|
451
|
+
else
|
452
|
+
out "[#{@method} parameter(s) missing:#{missing[0].sort.join(', ')}]"
|
453
|
+
return false
|
454
|
+
end
|
455
|
+
else
|
456
|
+
args.each do |arg|
|
457
|
+
missing << arg.to_s unless @params[arg]
|
458
|
+
end
|
459
|
+
end
|
460
|
+
if missing != []
|
461
|
+
out "[#{@method} parameter(s) missing:#{missing.sort.join(', ')}]"
|
462
|
+
return false
|
463
|
+
end
|
464
|
+
true
|
465
|
+
end
|
466
|
+
|
467
|
+
def expand_block(block, new_context={})
|
468
|
+
block.process(@context.merge(new_context))
|
469
|
+
end
|
470
|
+
|
471
|
+
def expand_with(acontext={})
|
472
|
+
blocks = acontext.delete(:blocks) || @blocks
|
473
|
+
res = ""
|
474
|
+
|
475
|
+
# FIXME: I think we can delete @pass and @parts stuff now (test first).
|
476
|
+
|
477
|
+
@pass = {} # current object sees some information from it's direct descendants
|
478
|
+
@parts = {}
|
479
|
+
only = acontext[:only]
|
480
|
+
new_context = @context.merge(acontext)
|
481
|
+
|
482
|
+
if acontext[:ignore]
|
483
|
+
new_context[:ignore] = (@context[:ignore] || []) + (acontext[:ignore] || []).uniq
|
484
|
+
end
|
485
|
+
|
486
|
+
if acontext[:no_ignore]
|
487
|
+
new_context[:ignore] = (new_context[:ignore] || []) - acontext[:no_ignore]
|
488
|
+
end
|
489
|
+
|
490
|
+
ignore = new_context[:ignore]
|
491
|
+
|
492
|
+
blocks.each do |b|
|
493
|
+
if b.kind_of?(String)
|
494
|
+
if (!only || only.include?(:string)) && (!ignore || !ignore.include?(:string))
|
495
|
+
res << b
|
496
|
+
end
|
497
|
+
elsif (!only || only.include?(b.method)) && (!ignore || !ignore.include?(b.method))
|
498
|
+
res << b.process(new_context.dup)
|
499
|
+
if pass = b.pass
|
500
|
+
if pass[:part]
|
501
|
+
@parts.merge!(pass[:part])
|
502
|
+
pass.delete(:part)
|
503
|
+
end
|
504
|
+
@pass.merge!(pass)
|
505
|
+
end
|
506
|
+
end
|
507
|
+
end
|
508
|
+
res
|
509
|
+
end
|
510
|
+
|
511
|
+
def inspect
|
512
|
+
attributes = []
|
513
|
+
params = []
|
514
|
+
(@params || {}).each do |k,v|
|
515
|
+
unless v.nil?
|
516
|
+
params << "#{k.inspect.gsub('"', "'")}=>'#{v}'"
|
517
|
+
end
|
518
|
+
end
|
519
|
+
attributes << " {= #{params.sort.join(', ')}}" unless params == []
|
520
|
+
|
521
|
+
context = []
|
522
|
+
(@context || {}).each do |k,v|
|
523
|
+
unless v.nil?
|
524
|
+
context << "#{k.inspect.gsub('"', "'")}=>'#{v}'"
|
525
|
+
end
|
526
|
+
end
|
527
|
+
attributes << " {> #{context.sort.join(', ')}}" unless context == []
|
528
|
+
|
529
|
+
pass = []
|
530
|
+
(@pass || {}).each do |k,v|
|
531
|
+
unless v.nil?
|
532
|
+
if v.kind_of?(Array)
|
533
|
+
pass << "#{k.inspect.gsub('"', "'")}=>#{v.inspect.gsub('"', "'")}"
|
534
|
+
elsif v.kind_of?(Parser)
|
535
|
+
pass << "#{k.inspect.gsub('"', "'")}=>['#{v}']"
|
536
|
+
else
|
537
|
+
pass << "#{k.inspect.gsub('"', "'")}=>#{v.inspect.gsub('"', "'")}"
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end
|
541
|
+
attributes << " {< #{pass.sort.join(', ')}}" unless pass == []
|
542
|
+
|
543
|
+
res = []
|
544
|
+
@blocks.each do |b|
|
545
|
+
if b.kind_of?(String)
|
546
|
+
res << b
|
547
|
+
else
|
548
|
+
res << b.inspect
|
549
|
+
end
|
550
|
+
end
|
551
|
+
result = "[#{@method}#{attributes.join('')}"
|
552
|
+
if res != []
|
553
|
+
result += "]#{res}[/#{@method}]"
|
554
|
+
else
|
555
|
+
result += "/]"
|
556
|
+
end
|
557
|
+
result + @text
|
558
|
+
end
|
559
|
+
|
560
|
+
def parser_error(message, method = @method)
|
561
|
+
self.class.parser_error(message, method)
|
562
|
+
end
|
563
|
+
end # Parser
|
564
|
+
end # Zafu
|