chelsy 0.0.3 → 0.0.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.
@@ -5,9 +5,9 @@ module Chelsy
5
5
 
6
6
  DEFAULT_INDENT_STRING = ' '.freeze
7
7
 
8
- def initialize()
9
- @indent_string = DEFAULT_INDENT_STRING
10
- @indent_level = 0
8
+ def initialize(indent_string: DEFAULT_INDENT_STRING, indent_level: 0)
9
+ @indent_string = indent_string
10
+ @indent_level = indent_level
11
11
  end
12
12
 
13
13
  def translate(node)
@@ -41,8 +41,28 @@ module Chelsy
41
41
  case node
42
42
  when String
43
43
  node.to_s
44
+ when Comment::Multi
45
+ translate_comment_multi(node)
46
+ when Comment::Single
47
+ translate_comment_single(node)
44
48
  when Directive::Include
45
49
  translate_include(node)
50
+ when Directive::Define
51
+ translate_define(node)
52
+ when Directive::Undef
53
+ translate_undef(node)
54
+ when Directive::If
55
+ translate_if_directive(node)
56
+ when Directive::ElseIf
57
+ translate_elif_directive(node)
58
+ when Directive::Else
59
+ translate_else_directive(node)
60
+ when Directive::EndIf
61
+ translate_endif_directive(node)
62
+ when Directive::Line
63
+ translate_line_directive(node)
64
+ when Directive::Pragma
65
+ translate_pragma_directive(node)
46
66
  else
47
67
  raise ArgumentError, "Unrecognized AST fragment: #{node.inspect}"
48
68
  end
@@ -63,25 +83,56 @@ module Chelsy
63
83
  translate_integral(node)
64
84
  when Constant::String
65
85
  translate_string(node)
66
- when FunctionCall
67
- translate_function_call(node)
86
+ when Operator::Unary
87
+ translate_unary_operator(node)
88
+ when Operator::Binary
89
+ translate_binary_operator(node)
90
+ when Operator::Conditional
91
+ translate_ternary_conditional(node)
68
92
 
69
93
  # Statements
70
94
  when EmptyStmt
71
95
  translate_empty_stmt(node)
72
96
  when ExprStmt
73
97
  translate_expr_stmt(node)
98
+ when If
99
+ translate_if(node)
100
+ when Switch
101
+ translate_switch(node)
102
+ when While
103
+ translate_while(node)
104
+ when DoWhile
105
+ translate_do_while(node)
106
+ when For
107
+ translate_for(node)
108
+ when Break
109
+ translate_break(node)
110
+ when Continue
111
+ translate_continue(node)
112
+ when Case
113
+ translate_case(node)
114
+ when Labeled
115
+ translate_labeled(node)
116
+ when Goto
117
+ translate_goto(node)
74
118
  when Return
75
119
  translate_return(node)
76
120
  when Block
77
121
  translate_block(node)
78
122
 
79
123
  # Definition
124
+ when Declaration, Typedef
125
+ translate_declaration(node)
126
+ when BitField
127
+ translate_bit_field(node)
80
128
  when Function
81
129
  translate_function(node)
82
130
  when Param
83
- translate_function_param(node)
84
-
131
+ translate_param(node)
132
+ when Initializer
133
+ translate_initializer(node)
134
+ when InitializerList
135
+ translate_initializer_list(node)
85
136
  else
86
137
  raise ArgumentError, "Unrecognized AST element: #{node.inspect}"
87
138
  end
@@ -99,7 +150,13 @@ module Chelsy
99
150
  end
100
151
 
101
152
  def translate_document(node)
102
- node.map {|nd| translate(nd) }.join('')
153
+ node.map {|nd| translate(nd) }
154
+ .join("\n\n")
155
+ .tap do |src|
156
+ # Document's fragments and body should be separated by empty line for
157
+ # source code readability.
158
+ src.insert(0, "\n") unless src.empty? || node.fragments.empty?
159
+ end
103
160
  end
104
161
 
105
162
  def translate_ident(node)
@@ -114,28 +171,100 @@ module Chelsy
114
171
 
115
172
  def translate_typed_name(ty, name=nil)
116
173
  case ty
174
+ when Type::Pointer
175
+ translate_pointer_type(ty, name)
176
+ when Type::Array
177
+ translate_array_type(ty, name)
178
+ when Type::Function
179
+ translate_function_type(ty, name)
180
+ when Type::Struct
181
+ translate_struct_type(ty, name)
182
+ when Type::Union
183
+ translate_union_type(ty, name)
184
+ when Type::Enum
185
+ translate_enum_type(ty, name)
117
186
  when Type::Derived
118
- # TODO
119
187
  raise NotImplementedError
120
188
  else
121
- translate_primitive_type(ty).tap do |src|
122
- src << " #{name}" if name
123
- end
189
+ translate_primitive_type(ty, name)
190
+ end
191
+ .tap do |src|
192
+ src.strip!
193
+ end
194
+ end
195
+
196
+ def translate_pointer_type(ty, name=nil)
197
+ # qualifiers
198
+ src = ''.tap do |qualifier|
199
+ qualifier << '*'
200
+ qualifier << 'const ' if ty.const?
201
+ qualifier << 'volatile ' if ty.volatile?
202
+ qualifier << 'restrict ' if ty.restrict?
203
+ end
204
+
205
+ # name
206
+ src << name.to_s
207
+
208
+ # parenthesize if needed
209
+ case ty.pointee
210
+ when Type::Function, Type::Array
211
+ translate_typed_name(ty.pointee, "(#{src})")
212
+ else
213
+ translate_typed_name(ty.pointee, src)
214
+ end
215
+ end
216
+
217
+ def translate_array_type(ty, name=nil)
218
+ src = name.to_s.tap do |subscript|
219
+ subscript << '['
220
+ subscript << 'const ' if ty.const?
221
+ subscript << 'volatile ' if ty.volatile?
222
+ subscript << 'static ' if ty.static?
223
+ subscript << translate(ty.size) if ty.size
224
+ subscript << ']'
225
+ end
226
+
227
+ translate_typed_name(coerce_func_ptr(ty.element_type), src)
228
+ end
229
+
230
+ def translate_function_type(ty, name=nil)
231
+ src = name.to_s.tap do |params|
232
+ params << '('
233
+ params << ty.params.map {|p| translate(coerce_func_ptr(p)) }.join(', ')
234
+ params << ')'
124
235
  end
236
+
237
+ translate_typed_name(coerce_func_ptr(ty.return_type), src)
238
+ end
239
+
240
+ def translate_struct_type(ty, name=nil)
241
+ translate_taggable_type_members(ty, name)
125
242
  end
126
243
 
127
- def translate_primitive_type(ty)
244
+ def translate_union_type(ty, name=nil)
245
+ translate_taggable_type_members(ty, name)
246
+ end
247
+
248
+ def translate_enum_type(ty, name=nil)
249
+ translate_taggable_type_members(ty, name)
250
+ end
251
+
252
+ def translate_primitive_type(ty, name=nil)
253
+ src = case ty
254
+ when :void; 'void'
255
+ when Type::Integral
256
+ translate_integral_type(ty)
257
+ else
258
+ translate_numeric_type(ty)
259
+ end
128
260
  case ty
129
- when :void; 'void'
130
- when Type::Char; 'char'
131
- when Type::Short; 'short'
132
- when Type::Integral
133
- translate_integral_type(ty)
134
- end.tap do |src|
135
- # qualifiers
261
+ when Type::Base;
136
262
  src.insert(0, 'const ') if ty.const?
137
263
  src.insert(0, 'volatile ') if ty.volatile?
138
- src.insert(0, 'restrict ') if ty.restrict?
264
+ end
265
+
266
+ src.tap do |src|
267
+ src << " #{name}" if name
139
268
  end
140
269
  end
141
270
 
@@ -151,6 +280,21 @@ module Chelsy
151
280
  end
152
281
  end
153
282
 
283
+ def translate_numeric_type(ty)
284
+ case ty
285
+ when Type::Bool; '_Bool'
286
+ when Type::Float; 'float'
287
+ when Type::Double; 'double'
288
+ when Type::LongDouble; 'long double'
289
+ when Type::Complex; '_Complex'
290
+ when Type::FloatComplex; 'float _Complex'
291
+ when Type::DoubleComplex; 'double _Complex'
292
+ when Type::LongDoubleComplex; 'long double _Complex'
293
+ else
294
+ raise NotImplementedError
295
+ end
296
+ end
297
+
154
298
  # = Expressions
155
299
 
156
300
  def translate_integral(node)
@@ -165,37 +309,266 @@ module Chelsy
165
309
  end
166
310
  end
167
311
 
312
+ def translate_unary_operator(node)
313
+ case node
314
+ when Operator::Subscription
315
+ translate_subscription(node)
316
+ when Operator::Call
317
+ translate_function_call(node)
318
+ when Operator::Access
319
+ translate_member_access(node)
320
+ when Operator::Cast
321
+ translate_type_cast(node)
322
+ when Operator::SizeOf
323
+ translate_size_of(node)
324
+ when Operator::Postfix
325
+ translate_postfix_operator(node)
326
+ when Operator::Prefix
327
+ translate_prefix_operator(node)
328
+ when Operator::Defined
329
+ translate_defined_operator(node)
330
+ else
331
+ raise NotImplementedError, "Unrecognized unary operator: #{node.inspect}"
332
+ end
333
+ end
334
+
335
+ def translate_subscription(node)
336
+ subscriptee = expr(node.subscriptee, node)
337
+ index = translate(node.index)
338
+
339
+ "#{subscriptee}[#{index}]"
340
+ end
341
+
168
342
  def translate_function_call(node)
169
- callee = expr(node.callee)
170
- args = node.args.map {|a| expr(a) }.join(', ')
343
+ callee = expr(node.callee, node)
344
+ args = node.args.map {|a| translate(a) }.join(', ')
171
345
 
172
346
  "#{callee}(#{args})"
173
347
  end
174
348
 
349
+ def translate_member_access(node)
350
+ object = expr(node.object, node)
351
+ name = translate(node.name)
352
+
353
+ "#{object}#{node.class.operator}#{name}"
354
+ end
355
+
356
+ def translate_prefix_operator(node)
357
+ operand = expr(node.operand, node)
358
+ "#{node.class.operator}#{operand}"
359
+ end
360
+
361
+ def translate_postfix_operator(node)
362
+ operand = expr(node.operand, node)
363
+ "#{operand}#{node.class.operator}"
364
+ end
365
+
366
+ def translate_type_cast(node)
367
+ operand = expr(node.operand, node)
368
+ "(#{translate node.type})#{operand}"
369
+ end
370
+
371
+ def translate_size_of(node)
372
+ operand = translate(node.operand)
373
+ "sizeof(#{operand})"
374
+ end
375
+
376
+ def translate_defined_operator(node)
377
+ operand = translate(node.operand)
378
+ "defined #{operand}"
379
+ end
380
+
381
+ def translate_binary_operator(node)
382
+ lhs = expr(node.lhs, node)
383
+ rhs = expr(node.rhs, node)
384
+
385
+ case node
386
+ when Operator::Comma
387
+ "#{lhs}#{node.class.operator} #{rhs}"
388
+ else
389
+ "#{lhs} #{node.class.operator} #{rhs}"
390
+ end
391
+ end
392
+
393
+ def translate_ternary_conditional(node)
394
+ condition_expr = expr(node.condition, node)
395
+
396
+ # Expression between `?` and `:` must be parenthesized.
397
+ then_expr = case node.then
398
+ when Operator::Binary, Operator::Conditional
399
+ "(#{translate(node.then)})"
400
+ else
401
+ expr(node.then, node)
402
+ end
403
+ else_expr = expr(node.else, node)
404
+
405
+ "#{condition_expr} ? #{then_expr} : #{else_expr}"
406
+ end
407
+
175
408
  # = Statements
176
409
 
177
410
  def translate_empty_stmt(node)
178
- indent << ';'
411
+ ''
179
412
  end
180
413
 
181
414
  def translate_expr_stmt(node)
182
- indent << translate(node.expr) << ';'
415
+ translate(node.expr)
416
+ end
417
+
418
+ def translate_if(node)
419
+ "if (#{translate node.condition}) #{translate node.then}".tap do |src|
420
+ src << " else #{translate node.else}" if node.else
421
+ end
422
+ end
423
+
424
+ def translate_switch(node)
425
+ "switch (#{translate node.expr}) #{translate node.stmt}"
426
+ end
427
+
428
+ def translate_while(node)
429
+ "while (#{translate node.condition}) #{translate node.body}"
430
+ end
431
+
432
+ def translate_do_while(node)
433
+ "do #{translate node.body} while (#{translate node.condition})"
434
+ end
435
+
436
+ def translate_for(node)
437
+ init = node.init ? translate(node.init) : ''
438
+ cond = node.condition ? ' ' + translate(node.condition) : ''
439
+ loop_expr = node.loop ? ' ' + translate(node.loop) : ''
440
+
441
+ "for (#{init};#{cond};#{loop_expr}) #{translate node.body}"
442
+ end
443
+
444
+ def translate_break(node); 'break' end
445
+ def translate_continue(node); 'continue' end
446
+
447
+ def translate_goto(node)
448
+ "goto #{node.label}"
449
+ end
450
+
451
+ # We need labeled statement to indent differently.
452
+ def translate_case(node)
453
+ [
454
+ "case #{translate(node.expr)}",
455
+ translate(node.stmt),
456
+ ]
457
+ end
458
+
459
+ def translate_labeled(node)
460
+ [
461
+ node.label.to_s,
462
+ translate(node.stmt),
463
+ ]
183
464
  end
184
465
 
185
466
  def translate_return(node)
186
467
  if node.expr
187
- indent << 'return ' << translate(node.expr) << ';'
468
+ 'return ' << translate(node.expr)
188
469
  else
189
- indent << 'return;'
470
+ 'return'
190
471
  end
191
472
  end
192
473
 
193
474
  def translate_block(node)
194
- @indent_level += 1
195
- body = node.map {|item| translate(item) }.join("\n")
196
- @indent_level -= 1
475
+ translate_stmts_with_indent(node)
476
+ end
477
+
478
+ # = Declaration
479
+ def translate_declaration(node)
480
+ [
481
+ node.storage.to_s,
482
+ translate_typed_name(node.type, node.name),
483
+ ]
484
+ .tap {|src|
485
+ unless node.init.nil?
486
+ src << '='
487
+ src << translate(node.init)
488
+ end
489
+ }
490
+ .join(' ')
491
+ .strip
492
+ end
493
+
494
+ def translate_designator(node)
495
+ case node
496
+ when IndexDesignator
497
+ "[#{node.index}]"
498
+ when MemberDesignator
499
+ ".#{node.name}"
500
+ else
501
+ raise NotImplementedError, 'designator must be Index or Member'
502
+ end
503
+ end
504
+
505
+ def translate_initializer(node)
506
+ if node.designator
507
+ [
508
+ translate_designator(node.designator),
509
+ '=',
510
+ translate(node.value),
511
+ ]
512
+ .join(' ')
513
+ else
514
+ translate(node.value)
515
+ end
516
+ end
517
+
518
+ def translate_initializer_list(node)
519
+ node
520
+ .map {|m| translate(m)}
521
+ .join(', ')
522
+ .insert( 0, '{ ')
523
+ .insert(-1, ' }')
524
+ end
197
525
 
198
- "#{indent}{\n#{body}\n#{indent}}"
526
+ def translate_bit_field(node)
527
+ if node.declaration
528
+ "#{translate node.declaration} : #{translate node.bits}"
529
+ else
530
+ ": #{translate node.bits}"
531
+ end
532
+ end
533
+
534
+ # = Function
535
+ def translate_function(node)
536
+ params = node.params.map {|p| translate(p) }.join(', ')
537
+
538
+ [
539
+ node.storage.to_s,
540
+ translate(node.return_type),
541
+ "#{translate node.name}(#{params})",
542
+ translate(node.body),
543
+ ]
544
+ .join(' ')
545
+ .strip
546
+ end
547
+
548
+ def translate_param(node)
549
+ ty = coerce_func_ptr(node.type)
550
+ translate_typed_name(ty, node.name)
551
+ end
552
+
553
+ # = Comment
554
+ def translate_comment_multi(node)
555
+ case node.lines.size
556
+ when 0
557
+ ""
558
+ when 1
559
+ "/* #{node.lines[0]} */"
560
+ else
561
+ src =
562
+ node
563
+ .lines
564
+ .map {|line| "#{indent} * #{line}"}
565
+ .join("\n")
566
+ "/*\n#{src}\n#{indent} */"
567
+ end
568
+ end
569
+
570
+ def translate_comment_single(node)
571
+ "// #{node.body}"
199
572
  end
200
573
 
201
574
  # = Directives
@@ -207,27 +580,172 @@ module Chelsy
207
580
  end
208
581
  end
209
582
 
210
- # = Statements
583
+ def translate_define(node)
584
+ "#define #{node.name}".tap do |src|
585
+ if node.params
586
+ src << '('
587
+ src << node.params.map(&:to_s).join(", ")
588
+ src << ')'
589
+ end
211
590
 
212
- def translate_function(node)
213
- params = node.params.map {|p| translate(p) }.join(', ')
214
- "#{translate node.return_type} #{translate node.name}(#{params}) #{translate(node.body)}"
591
+ unless node.replacement.empty?
592
+ src << ' '
593
+ src << node.replacement.to_s
594
+ end
595
+ end
596
+ end
597
+
598
+ def translate_undef(node)
599
+ "#undef #{node.name}"
215
600
  end
216
601
 
217
- def translate_function_param(node)
218
- translate_typed_name(node.type, node.name)
602
+ def translate_if_directive(node)
603
+ "#if #{translate node.condition}"
604
+ end
605
+
606
+ def translate_elif_directive(node)
607
+ "#elif #{translate node.condition}"
608
+ end
609
+
610
+ def translate_else_directive(node); "#else" end
611
+
612
+ def translate_endif_directive(node); "#endif" end
613
+
614
+ def translate_line_directive(node)
615
+ "#line #{node.lineno}".tap do |src|
616
+ src << " \"#{node.filename}\"" if node.filename
617
+ end
618
+ end
619
+
620
+ def translate_pragma_directive(node)
621
+ "#pragma #{node.pragma}"
219
622
  end
220
623
 
221
624
  private
222
625
 
223
- def indent
224
- @indent_string * @indent_level
626
+ def indent(indent_level=nil)
627
+ indent_level = @indent_level if indent_level.nil?
628
+ @indent_string * indent_level
225
629
  end
226
630
 
227
- # Expression: parenthesize if needed
228
- def expr(node)
229
- # TODO Pointer expression should be parenthesized.
230
- translate(node)
631
+ # Parenthesize if `node` has lower precedence than `parent` node.
632
+ def expr(node, parent)
633
+ expr = translate(node)
634
+
635
+ if node.is_a?(Operator::Base) && node.class.precedence < parent.class.precedence
636
+ "(#{expr})"
637
+ else
638
+ expr
639
+ end
640
+ end
641
+
642
+ def translate_taggable_type_members(ty, name=nil)
643
+ [].tap do |buffer|
644
+ buffer << 'const ' if ty.const?
645
+ buffer << 'volatile ' if ty.volatile?
646
+ buffer << case ty
647
+ when Type::Struct; 'struct'
648
+ when Type::Union; 'union'
649
+ when Type::Enum; 'enum'
650
+ end
651
+ buffer << ty.tag if ty.tag
652
+ buffer << name if name
653
+ end
654
+ .join(' ')
655
+ .tap do |src|
656
+ if ty.members
657
+ src << ' ' << translate_taggable_members(ty.members)
658
+ end
659
+ end
660
+ end
661
+
662
+ def translate_taggable_members(members)
663
+ case members
664
+ when StructOrUnionMemberList
665
+ translate_stmts_with_indent(members)
666
+ when EnumMemberList
667
+ translate_enum_members(members)
668
+ else
669
+ raise "Unrecognized members: #{members.inspect}"
670
+ end
671
+ end
672
+
673
+ def translate_enum_members(members)
674
+ @indent_level += 1
675
+
676
+ lines = members.map do |item|
677
+ case item
678
+ when EnumMember
679
+ if item.init
680
+ "#{translate item.name} = #{translate item.init}"
681
+ else
682
+ "#{translate item.name}"
683
+ end
684
+ when Symbol
685
+ "#{translate item}"
686
+ else
687
+ raise "Unrecognized enum member: #{item.inspect}"
688
+ end
689
+ end
690
+
691
+ body = lines.map {|line| indent << line }.join(",\n")
692
+ @indent_level -= 1
693
+
694
+ "{\n#{body}\n#{indent}}"
695
+ end
696
+
697
+ def should_terminate_with_semicolon(node)
698
+ case node
699
+ when If
700
+ if node.else
701
+ should_terminate_with_semicolon(node.else)
702
+ else
703
+ should_terminate_with_semicolon(node.then)
704
+ end
705
+ when While, For
706
+ should_terminate_with_semicolon(node.body)
707
+ when Block
708
+ false
709
+ else
710
+ true
711
+ end
712
+ end
713
+
714
+ def translate_stmts_with_indent(node)
715
+ @indent_level += 1
716
+
717
+ lines = node.map do |item|
718
+ src = translate(item)
719
+
720
+ if Array === src && src.size == 2
721
+ (label, stmt) = *src
722
+
723
+ src = "#{indent(@indent_level-1)}#{label}:\n"
724
+ src << "#{indent}#{stmt}"
725
+ else
726
+ src.insert 0, indent
727
+ end
728
+
729
+ # terminate ';' if needed
730
+ src << ';' if should_terminate_with_semicolon(item)
731
+ src
732
+ end
733
+
734
+ @indent_level -= 1
735
+
736
+ body = lines.join("\n")
737
+
738
+ "{\n#{body}\n#{indent}}"
739
+ end
740
+
741
+ # In some situation, function type shall be pointer to function type
742
+ def coerce_func_ptr(node)
743
+ case node
744
+ when Type::Function
745
+ Type::Pointer.new(node)
746
+ else
747
+ node
748
+ end
231
749
  end
232
750
 
233
751
  def integer_prefix(node)
@@ -259,6 +777,7 @@ module Chelsy
259
777
  suffix
260
778
  end
261
779
  end
780
+
262
781
  end
263
782
 
264
783
  end