papercraft 1.2 → 1.4
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +2 -0
- data/lib/papercraft/compiler.rb +205 -370
- data/lib/papercraft/compiler_old.rb +701 -0
- data/lib/papercraft/html.rb +9 -9
- data/lib/papercraft/tags.rb +32 -38
- data/lib/papercraft/template.rb +3 -3
- data/lib/papercraft/version.rb +1 -1
- data/lib/papercraft.rb +5 -5
- metadata +25 -14
@@ -0,0 +1,701 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cgi'
|
4
|
+
require 'escape_utils'
|
5
|
+
|
6
|
+
module Papercraft
|
7
|
+
def self.__cache_compiled_template__(value)
|
8
|
+
@compiled_template_cache ||= {}
|
9
|
+
|
10
|
+
if !(compiled = @compiled_template_cache[value])
|
11
|
+
value = Papercraft.html(&value) if value.is_a?(Proc)
|
12
|
+
compiled = value.compile.to_proc
|
13
|
+
@compiled_template_cache[value] = compiled
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.__emit__(value, __buffer__, *args)
|
18
|
+
case value
|
19
|
+
when Proc, Papercraft::Template
|
20
|
+
compiled = __cache_compiled_template__(value)
|
21
|
+
compiled.(__buffer__, *args)
|
22
|
+
else
|
23
|
+
__buffer__ << CGI.escapeHTML(value.to_s)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# The Compiler class compiles Papercraft templates
|
28
|
+
class CompilerOld
|
29
|
+
DEFAULT_CODE_BUFFER_CAPACITY = 8192
|
30
|
+
DEFAULT_EMIT_BUFFER_CAPACITY = 4096
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
@level = 0
|
34
|
+
@code_buffer = String.new(capacity: DEFAULT_CODE_BUFFER_CAPACITY)
|
35
|
+
@sub_templates = []
|
36
|
+
end
|
37
|
+
|
38
|
+
def emit_output
|
39
|
+
@output_mode = true
|
40
|
+
yield
|
41
|
+
@output_mode = false
|
42
|
+
end
|
43
|
+
|
44
|
+
def emit_code_line_break
|
45
|
+
return if @code_buffer.empty?
|
46
|
+
|
47
|
+
@code_buffer << "\n" if @code_buffer[-1] != "\n"
|
48
|
+
@line_break = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def emit_buffer
|
52
|
+
@emit_buffer ||= String.new(capacity: DEFAULT_EMIT_BUFFER_CAPACITY)
|
53
|
+
end
|
54
|
+
|
55
|
+
def emit_literal(lit)
|
56
|
+
if @output_mode
|
57
|
+
emit_code_line_break if @line_break
|
58
|
+
emit_buffer << lit
|
59
|
+
else
|
60
|
+
emit_code(lit)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def emit_text(str, encoding: :html)
|
65
|
+
emit_code_line_break if @line_break
|
66
|
+
emit_buffer << encode(str, encoding).inspect[1..-2]
|
67
|
+
end
|
68
|
+
|
69
|
+
def emit_text_fcall(node)
|
70
|
+
case node.type
|
71
|
+
when :STR, :LIT, :SYM
|
72
|
+
value = node.children.first.to_s
|
73
|
+
emit_text(value, encoding: :html)
|
74
|
+
when :VCALL
|
75
|
+
emit_code("__buffer__ << CGI.escapeHTML((#{node.children.first}).to_s)\n")
|
76
|
+
when :CONST
|
77
|
+
name = node.children.first.to_s
|
78
|
+
value = get_const(name)
|
79
|
+
emit_text(value, encoding: :html)
|
80
|
+
else
|
81
|
+
raise NotImplementedError
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def get_const(name)
|
86
|
+
if @block.binding.eval("singleton_class.const_defined?(#{name.inspect})")
|
87
|
+
@block.binding.eval("singleton_class.const_get(#{name.inspect})")
|
88
|
+
elsif Papercraft.const_defined?(name)
|
89
|
+
Papercraft.const_get(name)
|
90
|
+
else
|
91
|
+
raise NameError, "Constant #{name} not found"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def encode(str, encoding)
|
96
|
+
case encoding
|
97
|
+
when :html
|
98
|
+
__html_encode__(str)
|
99
|
+
when :uri
|
100
|
+
__uri_encode__(str)
|
101
|
+
else
|
102
|
+
raise "Invalid encoding #{encoding.inspect}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def __html_encode__(str)
|
107
|
+
CGI.escapeHTML(str)
|
108
|
+
end
|
109
|
+
|
110
|
+
def __uri_encode__(str)
|
111
|
+
EscapeUtils.escape_uri(str)
|
112
|
+
end
|
113
|
+
|
114
|
+
def emit_expression
|
115
|
+
if @output_mode
|
116
|
+
emit_literal('#{CGI.escapeHTML((')
|
117
|
+
yield
|
118
|
+
emit_literal(').to_s)}')
|
119
|
+
else
|
120
|
+
yield
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def flush_emit_buffer
|
125
|
+
return if !@emit_buffer
|
126
|
+
|
127
|
+
@code_buffer << "#{' ' * @level}__buffer__ << \"#{@emit_buffer}\"\n"
|
128
|
+
@emit_buffer = nil
|
129
|
+
true
|
130
|
+
end
|
131
|
+
|
132
|
+
def emit_code(code)
|
133
|
+
if flush_emit_buffer || @line_break
|
134
|
+
emit_code_line_break if @line_break
|
135
|
+
@code_buffer << "#{' ' * @level}#{code}"
|
136
|
+
else
|
137
|
+
if @code_buffer.empty? || (@code_buffer[-1] == "\n")
|
138
|
+
@code_buffer << "#{' ' * @level}#{code}"
|
139
|
+
else
|
140
|
+
@code_buffer << "#{code}"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def compile(template, initial_level = 0)
|
146
|
+
@block = template.to_proc
|
147
|
+
@level = initial_level
|
148
|
+
ast = RubyVM::AbstractSyntaxTree.of(@block)
|
149
|
+
Compiler.pp_ast(ast) if ENV['DEBUG'] == '1'
|
150
|
+
@level += 1
|
151
|
+
parse(ast)
|
152
|
+
flush_emit_buffer
|
153
|
+
@level -= 1
|
154
|
+
self
|
155
|
+
end
|
156
|
+
|
157
|
+
attr_reader :code_buffer
|
158
|
+
|
159
|
+
def to_code
|
160
|
+
pad = ' ' * @level
|
161
|
+
"#{pad}->(__buffer__#{proc_args}, &__block__) do\n#{prelude}#{@code_buffer}#{pad} __buffer__\n#{pad}end"
|
162
|
+
end
|
163
|
+
|
164
|
+
def proc_args
|
165
|
+
return nil if !@args
|
166
|
+
|
167
|
+
# ", #{@args.map { "#{_1}:" }.join(", ")}"
|
168
|
+
", #{@args.join(", ")}"
|
169
|
+
end
|
170
|
+
|
171
|
+
def prelude
|
172
|
+
return nil if @sub_templates.empty?
|
173
|
+
|
174
|
+
converted = @sub_templates.map { |t| convert_sub_template(t)}
|
175
|
+
"#{' ' * @level} __sub_templates__ = [\n#{converted.join(",\n")}\n ]\n"
|
176
|
+
end
|
177
|
+
|
178
|
+
def convert_sub_template(template)
|
179
|
+
template.compile(@level + 2).to_code
|
180
|
+
end
|
181
|
+
|
182
|
+
def to_proc
|
183
|
+
@block.binding.eval(to_code)
|
184
|
+
end
|
185
|
+
|
186
|
+
def parse(node, line_break = true)
|
187
|
+
@line_break = line_break && @last_node && node.first_lineno != @last_node.first_lineno
|
188
|
+
@last_node = node
|
189
|
+
method_name = :"parse_#{node.type.downcase}"
|
190
|
+
if !respond_to?(method_name)
|
191
|
+
raise Papercraft::Error, "Template compiler doesn't know how to convert #{node.type.inspect} node"
|
192
|
+
end
|
193
|
+
send(method_name, node)
|
194
|
+
end
|
195
|
+
|
196
|
+
def parse_scope(node)
|
197
|
+
args = node.children[0]
|
198
|
+
if args && !args.empty?
|
199
|
+
@args = args.compact
|
200
|
+
end
|
201
|
+
parse(node.children[2])
|
202
|
+
end
|
203
|
+
|
204
|
+
def parse_iter(node)
|
205
|
+
call, scope = node.children
|
206
|
+
if call.type == :FCALL
|
207
|
+
parse_fcall(call, scope)
|
208
|
+
else
|
209
|
+
parse(call)
|
210
|
+
emit_code(" do")
|
211
|
+
args = scope.children[0]
|
212
|
+
emit_code(" |#{args.join(', ')}|") if args
|
213
|
+
emit_code("\n")
|
214
|
+
@level += 1
|
215
|
+
parse(scope)
|
216
|
+
flush_emit_buffer
|
217
|
+
@level -= 1
|
218
|
+
emit_code("end\n")
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def parse_ivar(node)
|
223
|
+
emit_literal(node.children.first.to_s)
|
224
|
+
end
|
225
|
+
|
226
|
+
def parse_fcall(node, block = nil)
|
227
|
+
tag, args = node.children
|
228
|
+
args = args.children.compact if args
|
229
|
+
|
230
|
+
case tag
|
231
|
+
when :html5
|
232
|
+
return emit_html5(node, block)
|
233
|
+
when :emit
|
234
|
+
return emit_emit(node.children[1], block)
|
235
|
+
when :emit_yield
|
236
|
+
return emit_emit_yield
|
237
|
+
when :text
|
238
|
+
return emit_text_fcall(args.first)
|
239
|
+
end
|
240
|
+
|
241
|
+
text = fcall_inner_text_from_args(args)
|
242
|
+
atts = fcall_attributes_from_args(args)
|
243
|
+
if block
|
244
|
+
emit_tag(tag, atts) { parse(block) }
|
245
|
+
elsif text
|
246
|
+
emit_tag(tag, atts) do
|
247
|
+
case text
|
248
|
+
when Papercraft::Template
|
249
|
+
@sub_templates << text
|
250
|
+
idx = @sub_templates.size - 1
|
251
|
+
emit_code("__sub_templates__[#{idx}].(__buffer__)\n")
|
252
|
+
when String
|
253
|
+
emit_output { emit_text(text) }
|
254
|
+
when RubyVM::AbstractSyntaxTree::Node
|
255
|
+
emit_output { emit_expression { parse(text) } }
|
256
|
+
else
|
257
|
+
emit_text(text.to_s)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
else
|
261
|
+
emit_tag(tag, atts)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def fcall_inner_text_from_args(args)
|
266
|
+
return nil if !args
|
267
|
+
|
268
|
+
first = args.first
|
269
|
+
case first.type
|
270
|
+
when :STR
|
271
|
+
first.children.first
|
272
|
+
when :LIT, :SYM
|
273
|
+
first.children.first.to_s
|
274
|
+
when :HASH
|
275
|
+
nil
|
276
|
+
when :CONST
|
277
|
+
const_name = first.children.first
|
278
|
+
value = get_const(const_name)
|
279
|
+
if value.is_a?(Papercraft::Template)
|
280
|
+
value
|
281
|
+
else
|
282
|
+
value
|
283
|
+
end
|
284
|
+
else
|
285
|
+
first
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def fcall_attributes_from_args(args)
|
290
|
+
return nil if !args
|
291
|
+
|
292
|
+
last = args.last
|
293
|
+
(last.type == :HASH) ? last : nil
|
294
|
+
end
|
295
|
+
|
296
|
+
def emit_html5(node, block = nil)
|
297
|
+
emit_output do
|
298
|
+
emit_literal('<!DOCTYPE html>')
|
299
|
+
end
|
300
|
+
emit_tag(:html, nil) { parse(block) } if block
|
301
|
+
end
|
302
|
+
|
303
|
+
def emit_tag(tag, atts, &block)
|
304
|
+
emit_output do
|
305
|
+
if atts
|
306
|
+
emit_literal("<#{tag}")
|
307
|
+
emit_tag_attributes(atts)
|
308
|
+
emit_literal(block ? '>' : '/>')
|
309
|
+
else
|
310
|
+
emit_literal(block ? "<#{tag}>" : "<#{tag}/>")
|
311
|
+
end
|
312
|
+
end
|
313
|
+
if block
|
314
|
+
block.call
|
315
|
+
emit_output { emit_literal("</#{tag}>") }
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def emit_emit(args, block)
|
320
|
+
value, *rest = args.children.compact
|
321
|
+
case value.type
|
322
|
+
when :STR, :LIT, :SYM
|
323
|
+
value = value.children.first.to_s
|
324
|
+
emit_output { emit_literal(value) }
|
325
|
+
when :VCALL
|
326
|
+
emit_code("__buffer__ << #{value.children.first}\n")
|
327
|
+
when :DVAR
|
328
|
+
emit_code("Papercraft.__emit__(#{value.children.first}, __buffer__")
|
329
|
+
if !rest.empty?
|
330
|
+
emit_code(", ")
|
331
|
+
parse_list(args, false, 1..-1)
|
332
|
+
end
|
333
|
+
emit_code(")\n")
|
334
|
+
when :CONST
|
335
|
+
name = value.children.first.to_s
|
336
|
+
value = get_const(name)
|
337
|
+
case value
|
338
|
+
when Papercraft::Template, Proc
|
339
|
+
value = Papercraft.html(&value) if value.is_a?(Proc)
|
340
|
+
@sub_templates << value
|
341
|
+
idx = @sub_templates.size - 1
|
342
|
+
@line_break = false
|
343
|
+
emit_code("__sub_templates__[#{idx}].(__buffer__")
|
344
|
+
if !rest.empty?
|
345
|
+
emit_code(", ")
|
346
|
+
parse_list(args, false, 1..-1)
|
347
|
+
end
|
348
|
+
emit_code(")")
|
349
|
+
emit_code_block(block) if block
|
350
|
+
emit_code("\n")
|
351
|
+
else
|
352
|
+
emit_output { emit_literal(value) }
|
353
|
+
end
|
354
|
+
else
|
355
|
+
raise NotImplementedError
|
356
|
+
end
|
357
|
+
|
358
|
+
# value = fcall_inner_text_from_args(args)
|
359
|
+
# emit_output do
|
360
|
+
# emit_literal(value)
|
361
|
+
# end
|
362
|
+
end
|
363
|
+
|
364
|
+
def emit_code_block(block)
|
365
|
+
emit_code(" {\n")
|
366
|
+
@level += 1
|
367
|
+
parse(block.children[2])
|
368
|
+
flush_emit_buffer
|
369
|
+
@level -= 1
|
370
|
+
emit_code("}")
|
371
|
+
end
|
372
|
+
|
373
|
+
def emit_emit_yield
|
374
|
+
emit_code("__block__.call\n")
|
375
|
+
end
|
376
|
+
|
377
|
+
def emit_tag_attributes(atts)
|
378
|
+
list = atts.children.first.children
|
379
|
+
while true
|
380
|
+
key = list.shift
|
381
|
+
break unless key
|
382
|
+
|
383
|
+
value = list.shift
|
384
|
+
value_type = value.type
|
385
|
+
case value_type
|
386
|
+
when :FALSE, :NIL
|
387
|
+
next
|
388
|
+
end
|
389
|
+
|
390
|
+
emit_literal(' ')
|
391
|
+
emit_tag_attribute_key(key)
|
392
|
+
next if value_type == :TRUE
|
393
|
+
|
394
|
+
emit_literal('=\"')
|
395
|
+
emit_tag_attribute_value(value, key)
|
396
|
+
emit_literal('\"')
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
def emit_tag_attribute_key(key)
|
401
|
+
case key.type
|
402
|
+
when :STR
|
403
|
+
emit_literal(key.children.first)
|
404
|
+
when :LIT, :SYM
|
405
|
+
emit_literal(key.children.first.to_s)
|
406
|
+
when :NIL
|
407
|
+
emit_literal('nil')
|
408
|
+
else
|
409
|
+
emit_expression { parse(key) }
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
def emit_tag_attribute_value(value, key)
|
414
|
+
case value.type
|
415
|
+
when :STR
|
416
|
+
type = key.type
|
417
|
+
is_href_attr = (type == :LIT || type == :SYM) && (key.children.first == :href)
|
418
|
+
encoding = is_href_attr ? :uri : :html
|
419
|
+
emit_text(value.children.first, encoding: encoding)
|
420
|
+
when :LIT, :SYM
|
421
|
+
emit_text(value.children.first.to_s)
|
422
|
+
else
|
423
|
+
parse(value)
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
def parse_call(node)
|
428
|
+
receiver, method, args = node.children
|
429
|
+
if receiver.type == :VCALL && receiver.children == [:context]
|
430
|
+
emit_literal('__ctx__')
|
431
|
+
else
|
432
|
+
parse(receiver)
|
433
|
+
end
|
434
|
+
if method == :[]
|
435
|
+
emit_literal('[')
|
436
|
+
args = args.children.compact
|
437
|
+
while true
|
438
|
+
arg = args.shift
|
439
|
+
break unless arg
|
440
|
+
|
441
|
+
parse(arg)
|
442
|
+
emit_literal(', ') if !args.empty?
|
443
|
+
end
|
444
|
+
emit_literal(']')
|
445
|
+
else
|
446
|
+
emit_literal('.')
|
447
|
+
emit_literal(method.to_s)
|
448
|
+
if args
|
449
|
+
emit_literal('(')
|
450
|
+
args = args.children.compact
|
451
|
+
while true
|
452
|
+
arg = args.shift
|
453
|
+
break unless arg
|
454
|
+
|
455
|
+
parse(arg)
|
456
|
+
emit_literal(', ') if !args.empty?
|
457
|
+
end
|
458
|
+
emit_literal(')')
|
459
|
+
end
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
def parse_str(node)
|
464
|
+
str = node.children.first
|
465
|
+
emit_literal(str.inspect)
|
466
|
+
end
|
467
|
+
|
468
|
+
def parse_lit(node)
|
469
|
+
value = node.children.first
|
470
|
+
emit_literal(value.inspect)
|
471
|
+
end
|
472
|
+
|
473
|
+
def parse_sym(node)
|
474
|
+
value = node.children.first
|
475
|
+
emit_literal(value.inspect)
|
476
|
+
end
|
477
|
+
|
478
|
+
def parse_integer(node)
|
479
|
+
value = node.children.first
|
480
|
+
emit_literal(value.inspect)
|
481
|
+
end
|
482
|
+
|
483
|
+
def parse_true(node)
|
484
|
+
emit_expression { emit_literal('true') }
|
485
|
+
end
|
486
|
+
|
487
|
+
def parse_false(node)
|
488
|
+
emit_expression { emit_literal('true') }
|
489
|
+
end
|
490
|
+
|
491
|
+
def parse_list(node, emit_container = true, range = nil)
|
492
|
+
emit_literal('[') if emit_container
|
493
|
+
if range
|
494
|
+
items = node.children[range].compact
|
495
|
+
else
|
496
|
+
items = node.children.compact
|
497
|
+
end
|
498
|
+
while true
|
499
|
+
item = items.shift
|
500
|
+
break unless item
|
501
|
+
|
502
|
+
parse(item, emit_container)
|
503
|
+
emit_literal(', ') if !items.empty?
|
504
|
+
end
|
505
|
+
emit_literal(']') if emit_container
|
506
|
+
end
|
507
|
+
|
508
|
+
def parse_hash(node, emit_container = false)
|
509
|
+
emit_literal('{') if emit_container
|
510
|
+
items = node.children.first.children
|
511
|
+
idx = 0
|
512
|
+
while idx < items.size
|
513
|
+
k = items[idx]
|
514
|
+
break if !k
|
515
|
+
|
516
|
+
v = items[idx + 1]
|
517
|
+
p k: k, v: v
|
518
|
+
emit_literal(', ') if idx > 0
|
519
|
+
idx += 2
|
520
|
+
|
521
|
+
parse(k)
|
522
|
+
emit_literal(' => ')
|
523
|
+
parse(v)
|
524
|
+
end
|
525
|
+
emit_literal('}') if emit_container
|
526
|
+
end
|
527
|
+
|
528
|
+
def parse_vcall(node)
|
529
|
+
tag = node.children.first
|
530
|
+
case tag
|
531
|
+
when :emit_yield
|
532
|
+
return emit_emit_yield
|
533
|
+
end
|
534
|
+
emit_tag(tag, nil)
|
535
|
+
end
|
536
|
+
|
537
|
+
def parse_opcall(node)
|
538
|
+
left, op, right = node.children
|
539
|
+
parse(left)
|
540
|
+
emit_literal(" #{op} ")
|
541
|
+
right.children.compact.each { |c| parse(c) }
|
542
|
+
end
|
543
|
+
|
544
|
+
def parse_block(node)
|
545
|
+
node.children.each { |c| parse(c) }
|
546
|
+
end
|
547
|
+
|
548
|
+
def parse_begin(node)
|
549
|
+
node.children.each { |c| parse(c) if c }
|
550
|
+
end
|
551
|
+
|
552
|
+
def parse_if(node)
|
553
|
+
cond, then_branch, else_branch = node.children
|
554
|
+
if @output_mode
|
555
|
+
emit_if_output(cond, then_branch, else_branch)
|
556
|
+
else
|
557
|
+
emit_if_code(cond, then_branch, else_branch)
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
def parse_unless(node)
|
562
|
+
cond, then_branch, else_branch = node.children
|
563
|
+
if @output_mode
|
564
|
+
emit_unless_output(cond, then_branch, else_branch)
|
565
|
+
else
|
566
|
+
emit_unless_code(cond, then_branch, else_branch)
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
def emit_if_output(cond, then_branch, else_branch)
|
571
|
+
parse(cond)
|
572
|
+
emit_literal(" ? ")
|
573
|
+
parse(then_branch)
|
574
|
+
emit_literal(" : ")
|
575
|
+
if else_branch
|
576
|
+
parse(else_branch)
|
577
|
+
else
|
578
|
+
emit_literal(nil)
|
579
|
+
end
|
580
|
+
end
|
581
|
+
|
582
|
+
def emit_unless_output(cond, then_branch, else_branch)
|
583
|
+
parse(cond)
|
584
|
+
emit_literal(" ? ")
|
585
|
+
if else_branch
|
586
|
+
parse(else_branch)
|
587
|
+
else
|
588
|
+
emit_literal(nil)
|
589
|
+
end
|
590
|
+
emit_literal(" : ")
|
591
|
+
parse(then_branch)
|
592
|
+
end
|
593
|
+
|
594
|
+
def emit_if_code(cond, then_branch, else_branch)
|
595
|
+
emit_code('if ')
|
596
|
+
parse(cond)
|
597
|
+
emit_code("\n")
|
598
|
+
@level += 1
|
599
|
+
parse(then_branch)
|
600
|
+
flush_emit_buffer
|
601
|
+
@level -= 1
|
602
|
+
if else_branch
|
603
|
+
emit_code("else\n")
|
604
|
+
@level += 1
|
605
|
+
parse(else_branch)
|
606
|
+
flush_emit_buffer
|
607
|
+
@level -= 1
|
608
|
+
end
|
609
|
+
emit_code("end\n")
|
610
|
+
end
|
611
|
+
|
612
|
+
def emit_unless_code(cond, then_branch, else_branch)
|
613
|
+
emit_code('unless ')
|
614
|
+
parse(cond)
|
615
|
+
emit_code("\n")
|
616
|
+
@level += 1
|
617
|
+
parse(then_branch)
|
618
|
+
flush_emit_buffer
|
619
|
+
@level -= 1
|
620
|
+
if else_branch
|
621
|
+
emit_code("else\n")
|
622
|
+
@level += 1
|
623
|
+
parse(else_branch)
|
624
|
+
flush_emit_buffer
|
625
|
+
@level -= 1
|
626
|
+
end
|
627
|
+
emit_code("end\n")
|
628
|
+
end
|
629
|
+
|
630
|
+
def parse_dvar(node)
|
631
|
+
emit_literal(node.children.first.to_s)
|
632
|
+
end
|
633
|
+
|
634
|
+
def parse_case(node)
|
635
|
+
value = node.children[0]
|
636
|
+
when_clause = node.children[1]
|
637
|
+
emit_code("case ")
|
638
|
+
parse(value)
|
639
|
+
emit_code("\n")
|
640
|
+
parse_when(when_clause)
|
641
|
+
emit_code("end\n")
|
642
|
+
end
|
643
|
+
|
644
|
+
def parse_when(node)
|
645
|
+
values = node.children[0]
|
646
|
+
then_clause = node.children[1]
|
647
|
+
else_clause = node.children[2]
|
648
|
+
|
649
|
+
emit_code('when ')
|
650
|
+
last_value = nil
|
651
|
+
emit_when_clause_values(values)
|
652
|
+
emit_code("\n")
|
653
|
+
@level += 1
|
654
|
+
parse(then_clause)
|
655
|
+
@level -= 1
|
656
|
+
|
657
|
+
return if !else_clause
|
658
|
+
|
659
|
+
if else_clause.type == :WHEN
|
660
|
+
parse_when(else_clause)
|
661
|
+
else
|
662
|
+
emit_code("else\n")
|
663
|
+
@level += 1
|
664
|
+
@level -= 1
|
665
|
+
parse(else_clause)
|
666
|
+
end
|
667
|
+
end
|
668
|
+
|
669
|
+
def emit_when_clause_values(values)
|
670
|
+
if values.type != :LIST
|
671
|
+
raise Papercraft::Error, "Expected LIST node, found #{values.type} node"
|
672
|
+
end
|
673
|
+
|
674
|
+
idx = 0
|
675
|
+
list_items = values.children
|
676
|
+
while idx < list_items.size
|
677
|
+
value = list_items[idx]
|
678
|
+
break if !value
|
679
|
+
|
680
|
+
emit_code(', ') if idx > 0
|
681
|
+
parse(value, idx > 0)
|
682
|
+
idx += 1
|
683
|
+
end
|
684
|
+
end
|
685
|
+
|
686
|
+
def self.pp_ast(node, level = 0)
|
687
|
+
case node
|
688
|
+
when RubyVM::AbstractSyntaxTree::Node
|
689
|
+
puts "#{' ' * level}#{node.type.inspect}"
|
690
|
+
node.children.each { |c| pp_ast(c, level + 1) }
|
691
|
+
when Array
|
692
|
+
puts "#{' ' * level}["
|
693
|
+
node.each { |c| pp_ast(c, level + 1) }
|
694
|
+
puts "#{' ' * level}]"
|
695
|
+
else
|
696
|
+
puts "#{' ' * level}#{node.inspect}"
|
697
|
+
return
|
698
|
+
end
|
699
|
+
end
|
700
|
+
end
|
701
|
+
end
|