Class: Papercraft::CompilerOld

Inherits:
Object
  • Object
show all
Defined in:
lib/papercraft/compiler_old.rb

Overview

The Compiler class compiles Papercraft templates

Constant Summary collapse

DEFAULT_CODE_BUFFER_CAPACITY =
8192
DEFAULT_EMIT_BUFFER_CAPACITY =
4096

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeCompilerOld

Returns a new instance of CompilerOld.



32
33
34
35
36
# File 'lib/papercraft/compiler_old.rb', line 32

def initialize
  @level = 0
  @code_buffer = String.new(capacity: DEFAULT_CODE_BUFFER_CAPACITY)
  @sub_templates = []
end

Instance Attribute Details

#code_bufferObject (readonly)

Returns the value of attribute code_buffer.



157
158
159
# File 'lib/papercraft/compiler_old.rb', line 157

def code_buffer
  @code_buffer
end

Class Method Details

.pp_ast(node, level = 0) ⇒ Object



686
687
688
689
690
691
692
693
694
695
696
697
698
699
# File 'lib/papercraft/compiler_old.rb', line 686

def self.pp_ast(node, level = 0)
  case node
  when RubyVM::AbstractSyntaxTree::Node
    puts "#{'  ' * level}#{node.type.inspect}"
    node.children.each { |c| pp_ast(c, level + 1) }
  when Array
    puts "#{'  ' * level}["
    node.each { |c| pp_ast(c, level + 1) }
    puts "#{'  ' * level}]"
  else
    puts "#{'  ' * level}#{node.inspect}"
    return
  end
end

Instance Method Details

#__html_encode__(str) ⇒ Object



106
107
108
# File 'lib/papercraft/compiler_old.rb', line 106

def __html_encode__(str)
  CGI.escapeHTML(str)
end

#__uri_encode__(str) ⇒ Object



110
111
112
# File 'lib/papercraft/compiler_old.rb', line 110

def __uri_encode__(str)
  EscapeUtils.escape_uri(str)
end

#compile(template, initial_level = 0) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
# File 'lib/papercraft/compiler_old.rb', line 145

def compile(template, initial_level = 0)
  @block = template.to_proc
  @level = initial_level
  ast = RubyVM::AbstractSyntaxTree.of(@block)
  Compiler.pp_ast(ast) if ENV['DEBUG'] == '1'
  @level += 1
  parse(ast)
  flush_emit_buffer
  @level -= 1
  self
end

#convert_sub_template(template) ⇒ Object



178
179
180
# File 'lib/papercraft/compiler_old.rb', line 178

def convert_sub_template(template)
  template.compile(@level + 2).to_code
end

#emit_bufferObject



51
52
53
# File 'lib/papercraft/compiler_old.rb', line 51

def emit_buffer
  @emit_buffer ||= String.new(capacity: DEFAULT_EMIT_BUFFER_CAPACITY)
end

#emit_code(code) ⇒ Object



132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/papercraft/compiler_old.rb', line 132

def emit_code(code)
  if flush_emit_buffer || @line_break
    emit_code_line_break if @line_break
    @code_buffer << "#{'  ' * @level}#{code}"
  else
    if @code_buffer.empty? || (@code_buffer[-1] == "\n")
      @code_buffer << "#{'  ' * @level}#{code}"
    else
      @code_buffer << "#{code}"
    end
  end
end

#emit_code_block(block) ⇒ Object



364
365
366
367
368
369
370
371
# File 'lib/papercraft/compiler_old.rb', line 364

def emit_code_block(block)
  emit_code(" {\n")
  @level += 1
  parse(block.children[2])
  flush_emit_buffer
  @level -= 1
  emit_code("}")
end

#emit_code_line_breakObject



44
45
46
47
48
49
# File 'lib/papercraft/compiler_old.rb', line 44

def emit_code_line_break
  return if @code_buffer.empty?

  @code_buffer << "\n" if @code_buffer[-1] != "\n"
  @line_break = nil
end

#emit_emit(args, block) ⇒ Object



319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/papercraft/compiler_old.rb', line 319

def emit_emit(args, block)
  value, *rest = args.children.compact
  case value.type
  when :STR, :LIT, :SYM
    value = value.children.first.to_s
    emit_output { emit_literal(value) }
  when :VCALL
    emit_code("__buffer__ << #{value.children.first}\n")
  when :DVAR
    emit_code("Papercraft.__emit__(#{value.children.first}, __buffer__")
    if !rest.empty?
      emit_code(", ")
      parse_list(args, false, 1..-1)
    end
    emit_code(")\n")
  when :CONST
    name = value.children.first.to_s
    value = get_const(name)
    case value
    when Papercraft::Template, Proc
      value = Papercraft.html(&value) if value.is_a?(Proc)
      @sub_templates << value
      idx = @sub_templates.size - 1
      @line_break = false
      emit_code("__sub_templates__[#{idx}].(__buffer__")
      if !rest.empty?
        emit_code(", ")
        parse_list(args, false, 1..-1)
      end  
      emit_code(")")
      emit_code_block(block) if block
      emit_code("\n")
    else
      emit_output { emit_literal(value) }
    end
  else
    raise NotImplementedError
  end

  # value = fcall_inner_text_from_args(args)
  # emit_output do
  #   emit_literal(value)
  # end
end

#emit_emit_yieldObject



373
374
375
# File 'lib/papercraft/compiler_old.rb', line 373

def emit_emit_yield
  emit_code("__block__.call\n")
end

#emit_expressionObject



114
115
116
117
118
119
120
121
122
# File 'lib/papercraft/compiler_old.rb', line 114

def emit_expression
  if @output_mode
    emit_literal('#{CGI.escapeHTML((')
    yield
    emit_literal(').to_s)}')
  else
    yield
  end
end

#emit_html5(node, block = nil) ⇒ Object



296
297
298
299
300
301
# File 'lib/papercraft/compiler_old.rb', line 296

def emit_html5(node, block = nil)
  emit_output do
    emit_literal('<!DOCTYPE html>')
  end
  emit_tag(:html, nil) { parse(block) } if block
end

#emit_if_code(cond, then_branch, else_branch) ⇒ Object



594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
# File 'lib/papercraft/compiler_old.rb', line 594

def emit_if_code(cond, then_branch, else_branch)
  emit_code('if ')
  parse(cond)
  emit_code("\n")
  @level += 1
  parse(then_branch)
  flush_emit_buffer
  @level -= 1
  if else_branch
    emit_code("else\n")
    @level += 1
    parse(else_branch)
    flush_emit_buffer
    @level -= 1
  end
  emit_code("end\n")
end

#emit_if_output(cond, then_branch, else_branch) ⇒ Object



570
571
572
573
574
575
576
577
578
579
580
# File 'lib/papercraft/compiler_old.rb', line 570

def emit_if_output(cond, then_branch, else_branch)
  parse(cond)
  emit_literal(" ? ")
  parse(then_branch)
  emit_literal(" : ")
  if else_branch
    parse(else_branch)
  else
    emit_literal(nil)
  end
end

#emit_literal(lit) ⇒ Object



55
56
57
58
59
60
61
62
# File 'lib/papercraft/compiler_old.rb', line 55

def emit_literal(lit)
  if @output_mode
    emit_code_line_break if @line_break
    emit_buffer << lit
  else
    emit_code(lit)
  end
end

#emit_outputObject



38
39
40
41
42
# File 'lib/papercraft/compiler_old.rb', line 38

def emit_output
  @output_mode = true
  yield
  @output_mode = false
end

#emit_tag(tag, atts, &block) ⇒ Object



303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/papercraft/compiler_old.rb', line 303

def emit_tag(tag, atts, &block)
  emit_output do
    if atts
      emit_literal("<#{tag}")
      emit_tag_attributes(atts)
      emit_literal(block ? '>' : '/>')
    else
      emit_literal(block ? "<#{tag}>" : "<#{tag}/>")
    end
  end
  if block
    block.call
    emit_output { emit_literal("</#{tag}>") }
  end
end

#emit_tag_attribute_key(key) ⇒ Object



400
401
402
403
404
405
406
407
408
409
410
411
# File 'lib/papercraft/compiler_old.rb', line 400

def emit_tag_attribute_key(key)
  case key.type
  when :STR
    emit_literal(key.children.first)
  when :LIT, :SYM
    emit_literal(key.children.first.to_s)
  when :NIL
    emit_literal('nil')
  else
    emit_expression { parse(key) }
  end
end

#emit_tag_attribute_value(value, key) ⇒ Object



413
414
415
416
417
418
419
420
421
422
423
424
425
# File 'lib/papercraft/compiler_old.rb', line 413

def emit_tag_attribute_value(value, key)
  case value.type
  when :STR
    type = key.type
    is_href_attr = (type == :LIT || type == :SYM) && (key.children.first == :href)
    encoding = is_href_attr ? :uri : :html
    emit_text(value.children.first, encoding: encoding)
  when :LIT, :SYM
    emit_text(value.children.first.to_s)
  else
    parse(value)
  end
end

#emit_tag_attributes(atts) ⇒ Object



377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
# File 'lib/papercraft/compiler_old.rb', line 377

def emit_tag_attributes(atts)
  list = atts.children.first.children
  while true
    key = list.shift
    break unless key

    value = list.shift
    value_type = value.type
    case value_type
    when :FALSE, :NIL
      next
    end

    emit_literal(' ')
    emit_tag_attribute_key(key)
    next if value_type == :TRUE

    emit_literal('=\"')
    emit_tag_attribute_value(value, key)
    emit_literal('\"')
  end
end

#emit_text(str, encoding: :html) ⇒ Object



64
65
66
67
# File 'lib/papercraft/compiler_old.rb', line 64

def emit_text(str, encoding: :html)
  emit_code_line_break if @line_break
  emit_buffer << encode(str, encoding).inspect[1..-2]
end

#emit_text_fcall(node) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/papercraft/compiler_old.rb', line 69

def emit_text_fcall(node)
  case node.type
  when :STR, :LIT, :SYM
    value = node.children.first.to_s
    emit_text(value, encoding: :html)
  when :VCALL
    emit_code("__buffer__ << CGI.escapeHTML((#{node.children.first}).to_s)\n")
  when :CONST
    name = node.children.first.to_s
    value = get_const(name)
    emit_text(value, encoding: :html)
  else
    raise NotImplementedError
  end
end

#emit_unless_code(cond, then_branch, else_branch) ⇒ Object



612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
# File 'lib/papercraft/compiler_old.rb', line 612

def emit_unless_code(cond, then_branch, else_branch)
  emit_code('unless ')
  parse(cond)
  emit_code("\n")
  @level += 1
  parse(then_branch)
  flush_emit_buffer
  @level -= 1
  if else_branch
    emit_code("else\n")
    @level += 1
    parse(else_branch)
    flush_emit_buffer
    @level -= 1
  end
  emit_code("end\n")
end

#emit_unless_output(cond, then_branch, else_branch) ⇒ Object



582
583
584
585
586
587
588
589
590
591
592
# File 'lib/papercraft/compiler_old.rb', line 582

def emit_unless_output(cond, then_branch, else_branch)
  parse(cond)
  emit_literal(" ? ")
  if else_branch
    parse(else_branch)
  else
    emit_literal(nil)
  end
  emit_literal(" : ")
  parse(then_branch)
end

#emit_when_clause_values(values) ⇒ Object



669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
# File 'lib/papercraft/compiler_old.rb', line 669

def emit_when_clause_values(values)
  if values.type != :LIST
    raise Papercraft::Error, "Expected LIST node, found #{values.type} node"
  end

  idx = 0
  list_items = values.children
  while idx < list_items.size
    value = list_items[idx]
    break if !value

    emit_code(', ') if idx > 0
    parse(value, idx > 0)
    idx += 1
  end
end

#encode(str, encoding) ⇒ Object



95
96
97
98
99
100
101
102
103
104
# File 'lib/papercraft/compiler_old.rb', line 95

def encode(str, encoding)
  case encoding
  when :html
    __html_encode__(str)
  when :uri
    __uri_encode__(str)
  else
    raise "Invalid encoding #{encoding.inspect}"
  end
end

#fcall_attributes_from_args(args) ⇒ Object



289
290
291
292
293
294
# File 'lib/papercraft/compiler_old.rb', line 289

def fcall_attributes_from_args(args)
  return nil if !args

  last = args.last
  (last.type == :HASH) ? last : nil
end

#fcall_inner_text_from_args(args) ⇒ Object



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/papercraft/compiler_old.rb', line 265

def fcall_inner_text_from_args(args)
  return nil if !args

  first = args.first
  case first.type
  when :STR
    first.children.first
  when :LIT, :SYM
    first.children.first.to_s
  when :HASH
    nil
  when :CONST
    const_name = first.children.first
    value = get_const(const_name)
    if value.is_a?(Papercraft::Template)
      value
    else
      value
    end
  else
    first
  end
end

#flush_emit_bufferObject



124
125
126
127
128
129
130
# File 'lib/papercraft/compiler_old.rb', line 124

def flush_emit_buffer
  return if !@emit_buffer

  @code_buffer << "#{'  ' * @level}__buffer__ << \"#{@emit_buffer}\"\n"
  @emit_buffer = nil
  true
end

#get_const(name) ⇒ Object



85
86
87
88
89
90
91
92
93
# File 'lib/papercraft/compiler_old.rb', line 85

def get_const(name)
  if @block.binding.eval("singleton_class.const_defined?(#{name.inspect})")
    @block.binding.eval("singleton_class.const_get(#{name.inspect})")
  elsif Papercraft.const_defined?(name)
    Papercraft.const_get(name)
  else
    raise NameError, "Constant #{name} not found"
  end
end

#parse(node, line_break = true) ⇒ Object



186
187
188
189
190
191
192
193
194
# File 'lib/papercraft/compiler_old.rb', line 186

def parse(node, line_break = true)
  @line_break = line_break && @last_node && node.first_lineno != @last_node.first_lineno
  @last_node = node
  method_name = :"parse_#{node.type.downcase}"
  if !respond_to?(method_name)
    raise Papercraft::Error, "Template compiler doesn't know how to convert #{node.type.inspect} node"
  end
  send(method_name, node)
end

#parse_begin(node) ⇒ Object



548
549
550
# File 'lib/papercraft/compiler_old.rb', line 548

def parse_begin(node)
  node.children.each { |c| parse(c) if c }
end

#parse_block(node) ⇒ Object



544
545
546
# File 'lib/papercraft/compiler_old.rb', line 544

def parse_block(node)
  node.children.each { |c| parse(c) }
end

#parse_call(node) ⇒ Object



427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
# File 'lib/papercraft/compiler_old.rb', line 427

def parse_call(node)
  receiver, method, args = node.children
  if receiver.type == :VCALL && receiver.children == [:context]
    emit_literal('__ctx__')
  else
    parse(receiver)
  end
  if method == :[]
    emit_literal('[')
    args = args.children.compact
    while true
      arg = args.shift
      break unless arg

      parse(arg)
      emit_literal(', ') if !args.empty?
    end
    emit_literal(']')
  else
    emit_literal('.')
    emit_literal(method.to_s)
    if args
      emit_literal('(')
      args = args.children.compact
      while true
        arg = args.shift
        break unless arg

        parse(arg)
        emit_literal(', ') if !args.empty?
      end
      emit_literal(')')
    end
  end
end

#parse_case(node) ⇒ Object



634
635
636
637
638
639
640
641
642
# File 'lib/papercraft/compiler_old.rb', line 634

def parse_case(node)
  value       = node.children[0]
  when_clause = node.children[1]
  emit_code("case ")
  parse(value)
  emit_code("\n")
  parse_when(when_clause)
  emit_code("end\n")
end

#parse_dvar(node) ⇒ Object



630
631
632
# File 'lib/papercraft/compiler_old.rb', line 630

def parse_dvar(node)
  emit_literal(node.children.first.to_s)
end

#parse_false(node) ⇒ Object



487
488
489
# File 'lib/papercraft/compiler_old.rb', line 487

def parse_false(node)
  emit_expression { emit_literal('true') }
end

#parse_fcall(node, block = nil) ⇒ Object



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/papercraft/compiler_old.rb', line 226

def parse_fcall(node, block = nil)
  tag, args = node.children
  args = args.children.compact if args

  case tag
  when :html5
    return emit_html5(node, block)
  when :emit
    return emit_emit(node.children[1], block)
  when :emit_yield
    return emit_emit_yield
  when :text
    return emit_text_fcall(args.first)
  end

  text = fcall_inner_text_from_args(args)
  atts = fcall_attributes_from_args(args)
  if block
    emit_tag(tag, atts) { parse(block) }
  elsif text
    emit_tag(tag, atts) do
      case text
      when Papercraft::Template
        @sub_templates << text
        idx = @sub_templates.size - 1
        emit_code("__sub_templates__[#{idx}].(__buffer__)\n")
      when String
        emit_output { emit_text(text) }
      when RubyVM::AbstractSyntaxTree::Node
        emit_output { emit_expression { parse(text) } }
      else
        emit_text(text.to_s)
      end
    end
  else
    emit_tag(tag, atts)
  end
end

#parse_hash(node, emit_container = false) ⇒ Object



508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
# File 'lib/papercraft/compiler_old.rb', line 508

def parse_hash(node, emit_container = false)
  emit_literal('{') if emit_container
  items = node.children.first.children
  idx = 0
  while idx < items.size
    k = items[idx]
    break if !k

    v = items[idx + 1]
    p k: k, v: v
    emit_literal(', ') if idx > 0
    idx += 2

    parse(k)
    emit_literal(' => ')
    parse(v)
  end
  emit_literal('}') if emit_container
end

#parse_if(node) ⇒ Object



552
553
554
555
556
557
558
559
# File 'lib/papercraft/compiler_old.rb', line 552

def parse_if(node)
  cond, then_branch, else_branch = node.children
  if @output_mode
    emit_if_output(cond, then_branch, else_branch)
  else
    emit_if_code(cond, then_branch, else_branch)
  end
end

#parse_integer(node) ⇒ Object



478
479
480
481
# File 'lib/papercraft/compiler_old.rb', line 478

def parse_integer(node)
  value = node.children.first
  emit_literal(value.inspect)
end

#parse_iter(node) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/papercraft/compiler_old.rb', line 204

def parse_iter(node)
  call, scope = node.children
  if call.type == :FCALL
    parse_fcall(call, scope)
  else
    parse(call)
    emit_code(" do")
    args = scope.children[0]
    emit_code(" |#{args.join(', ')}|") if args
    emit_code("\n")
    @level += 1
    parse(scope)
    flush_emit_buffer
    @level -= 1
    emit_code("end\n")
  end
end

#parse_ivar(node) ⇒ Object



222
223
224
# File 'lib/papercraft/compiler_old.rb', line 222

def parse_ivar(node)
  emit_literal(node.children.first.to_s)
end

#parse_list(node, emit_container = true, range = nil) ⇒ Object



491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
# File 'lib/papercraft/compiler_old.rb', line 491

def parse_list(node, emit_container = true, range = nil)
  emit_literal('[') if emit_container
  if range
    items = node.children[range].compact
  else
    items = node.children.compact
  end
  while true
    item = items.shift
    break unless item

    parse(item, emit_container)
    emit_literal(', ') if !items.empty?
  end
  emit_literal(']') if emit_container
end

#parse_lit(node) ⇒ Object



468
469
470
471
# File 'lib/papercraft/compiler_old.rb', line 468

def parse_lit(node)
  value = node.children.first
  emit_literal(value.inspect)
end

#parse_opcall(node) ⇒ Object



537
538
539
540
541
542
# File 'lib/papercraft/compiler_old.rb', line 537

def parse_opcall(node)
  left, op, right = node.children
  parse(left)
  emit_literal(" #{op} ")
  right.children.compact.each { |c| parse(c) }
end

#parse_scope(node) ⇒ Object



196
197
198
199
200
201
202
# File 'lib/papercraft/compiler_old.rb', line 196

def parse_scope(node)
  args = node.children[0]
  if args && !args.empty?
    @args = args.compact
  end
  parse(node.children[2])
end

#parse_str(node) ⇒ Object



463
464
465
466
# File 'lib/papercraft/compiler_old.rb', line 463

def parse_str(node)
  str = node.children.first
  emit_literal(str.inspect)
end

#parse_sym(node) ⇒ Object



473
474
475
476
# File 'lib/papercraft/compiler_old.rb', line 473

def parse_sym(node)
  value = node.children.first
  emit_literal(value.inspect)
end

#parse_true(node) ⇒ Object



483
484
485
# File 'lib/papercraft/compiler_old.rb', line 483

def parse_true(node)
  emit_expression { emit_literal('true') }
end

#parse_unless(node) ⇒ Object



561
562
563
564
565
566
567
568
# File 'lib/papercraft/compiler_old.rb', line 561

def parse_unless(node)
  cond, then_branch, else_branch = node.children
  if @output_mode
    emit_unless_output(cond, then_branch, else_branch)
  else
    emit_unless_code(cond, then_branch, else_branch)
  end
end

#parse_vcall(node) ⇒ Object



528
529
530
531
532
533
534
535
# File 'lib/papercraft/compiler_old.rb', line 528

def parse_vcall(node)
  tag = node.children.first
  case tag
  when :emit_yield
    return emit_emit_yield
  end
  emit_tag(tag, nil)
end

#parse_when(node) ⇒ Object



644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
# File 'lib/papercraft/compiler_old.rb', line 644

def parse_when(node)
  values      = node.children[0]
  then_clause = node.children[1]
  else_clause = node.children[2]

  emit_code('when ')
  last_value = nil
  emit_when_clause_values(values)
  emit_code("\n")
  @level += 1
  parse(then_clause)
  @level -= 1
  
  return if !else_clause

  if else_clause.type == :WHEN
    parse_when(else_clause)
  else
    emit_code("else\n")
    @level += 1
    @level -= 1
    parse(else_clause)
  end
end

#preludeObject



171
172
173
174
175
176
# File 'lib/papercraft/compiler_old.rb', line 171

def prelude
  return nil if @sub_templates.empty?

  converted = @sub_templates.map { |t| convert_sub_template(t)}
  "#{'  ' * @level}  __sub_templates__ = [\n#{converted.join(",\n")}\n  ]\n"
end

#proc_argsObject



164
165
166
167
168
169
# File 'lib/papercraft/compiler_old.rb', line 164

def proc_args
  return nil if !@args
  
  # ", #{@args.map { "#{_1}:" }.join(", ")}"
  ", #{@args.join(", ")}"
end

#to_codeObject



159
160
161
162
# File 'lib/papercraft/compiler_old.rb', line 159

def to_code
  pad = '  ' * @level
  "#{pad}->(__buffer__#{proc_args}, &__block__) do\n#{prelude}#{@code_buffer}#{pad}  __buffer__\n#{pad}end"
end

#to_procObject



182
183
184
# File 'lib/papercraft/compiler_old.rb', line 182

def to_proc
  @block.binding.eval(to_code)
end