chelsy 0.0.2 → 0.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 19521bbd4533094bff5ecf162e7d6f8ff8329ced
4
- data.tar.gz: 4a476989d47416c7db6050a369af9ce514139805
3
+ metadata.gz: 2e2b87298eca205d1b4c4783ab090559a971e749
4
+ data.tar.gz: f068070509af5a93eed85179a011bd9234f09f0d
5
5
  SHA512:
6
- metadata.gz: 5a4d6930ec401db7fbd8916494cd521d06dafa62545e58bbcd6d19b01203e2534ff528a06d485b290ccff2300eb627ad909f20e5ff51c4f0fe830407a4b6ed81
7
- data.tar.gz: f7e6be9b2e90d98b7c30a9b8278508e03f9b54dfe65f74860e6d89d900887217e65e931544ae111ccc3d6d9cdc79af1f014321c4eee1c9d0e75b547535e7a962
6
+ metadata.gz: a8a4defd43ff48249e937060ac12765ebd740e5a432acfcc97500136a124d4c930db949a6778d25da8b06d342bcdb8dd4da662857926d33852d63c0394fee335
7
+ data.tar.gz: a0b5f5c7e59a7ad34e9f49b7087f8c745240797c4008ea25ecbf4511be4aa2c6a60878aedcc7583eac32b3343fe1a2d3f9dc8fd4ce6a085fccd6c52351976b76
data/lib/chelsy/ast.rb CHANGED
@@ -1,24 +1,238 @@
1
+ require "chelsy/syntax"
2
+
1
3
  module Chelsy
2
4
 
3
5
  class Node
6
+ def initialize(**opts)
7
+ end
8
+ end
9
+
10
+ # The class must provide a method `items` and `validate_node`
11
+ module NodeList
12
+ include Enumerable
13
+
14
+ def each(&block)
15
+ items.each(&block)
16
+ self
17
+ end
18
+
19
+ def size; items.size end
20
+ def empty?; items.empty? end
21
+
22
+ def <<(node)
23
+ items << validate_node(node)
24
+ self
25
+ end
26
+ end
27
+
28
+ class Fragment < Node
4
29
  end
5
30
 
31
+ module Syntax
32
+ Fragment = Any.new('Fragment', [Fragment, String])
33
+ end
34
+
35
+ class FragmentList < Node
36
+ include NodeList
37
+
38
+ def initialize(**rest)
39
+ @fragments = []
40
+ super(**rest)
41
+ end
42
+
43
+ private
44
+ def items; @fragments end
45
+ def validate_node(node); Syntax::Fragment.ensure(node) end
46
+ end
47
+
48
+ # `Element` can have multiple `Fragment`s
49
+ #
50
+ # - `fragments` is an instace of `FragmentList` holds `Fragment`s which stands above `Element`.
51
+ # - `post_fragments` holds `Fragment`s which stands below `Element`.
6
52
  class Element < Node
53
+ attr_reader :fragments, :post_fragments
54
+
55
+ def initialize(**rest)
56
+ @fragments = FragmentList.new
57
+ @post_fragments = FragmentList.new
58
+
59
+ super(**rest)
60
+ end
61
+ end
62
+
63
+ class Declaration < Element
64
+ end
65
+
66
+ class Definition < Element
67
+ def initialize(extern: false, static: false, **rest)
68
+ @extern = !!extern
69
+ @static = !!static
70
+
71
+ super(**rest)
72
+ end
73
+
74
+ def extern?; @extern end
75
+ def static?; @static end
76
+ end
77
+
78
+ class Expr < Element
79
+ end
80
+
81
+ class Stmt < Element
82
+ end
83
+
84
+ module Syntax
85
+ Ident = Any.new('Identifier', [Symbol])
86
+ Expr = Any.new('Expression', [Expr, Symbol])
87
+ TopLevel = Any.new('TopLevel', [Definition, Declaration])
88
+ end
89
+
90
+ # `Document` represents a _translation unit_ (file).
91
+ class Document < Element
92
+ include NodeList
93
+
94
+ def initialize(**rest)
95
+ @items = []
96
+ super(**rest)
97
+ end
98
+
99
+ private
100
+ def items; @items end
101
+ def validate_node(node); Syntax::TopLevel.ensure(node) end
102
+ end
103
+
104
+ # = 6.2.5 Types
105
+ module Type
106
+ class Base < Element
107
+ def initialize(const: false, restrict: false, volatile: false, **rest)
108
+ @const = !!const
109
+ @restrict = !!restrict
110
+ @volatile = !!volatile
111
+
112
+ super(**rest)
113
+ end
114
+
115
+ def const?; @const end
116
+ def restrict?; @restrict end
117
+ def volatile?; @volatile end
118
+
119
+ def qualified?
120
+ @const || @restrict || @volatile
121
+ end
122
+ end
123
+
124
+ class Numeric < Base
125
+ end
126
+
127
+ # == _Bool
128
+ class Bool < Numeric
129
+ end
130
+
131
+ # == Integer types
132
+ class Integral < Numeric
133
+ def initialize(unsigned: false, **rest)
134
+ @unsigned = !!unsigned
135
+ super(**rest)
136
+ end
137
+
138
+ def unsigned?; @unsigned end
139
+ end
140
+
141
+ class Char < Integral
142
+ end
143
+
144
+ class Short < Integral
145
+ end
146
+
147
+ class Int < Integral
148
+ end
149
+
150
+ class Long < Integral
151
+ end
152
+
153
+ class LongLong < Integral
154
+ end
155
+
156
+ # == Real floating types
157
+ class Real < Numeric
158
+ end
159
+
160
+ class Float < Real
161
+ end
162
+
163
+ class Double < Real
164
+ end
165
+
166
+ class LongDouble < Real
167
+ end
168
+
169
+ # == Complex types
170
+ class Complex < Numeric
171
+ end
172
+
173
+ class FloatComplex < Complex
174
+ end
175
+
176
+ class DoubleComplex < Complex
177
+ end
178
+
179
+ class LongDoubleComplex < Complex
180
+ end
181
+
182
+ # == Derived types
183
+ class Derived < Base
184
+ end
185
+
186
+ class Pointer < Derived
187
+ attr_reader :pointee
188
+
189
+ def initialize(pointee, **rest)
190
+ @pointee = Syntax::Type.ensure(pointee)
191
+ super(**rest)
192
+ end
193
+ end
194
+
195
+ class Array < Derived
196
+ attr_reader :element_type, :size
197
+
198
+ def initialize(element_type, size = nil, **rest)
199
+ @element_type = element_type
200
+ @size = size
201
+
202
+ super(**rest)
203
+ end
204
+
205
+ # An array type of unknown size is an incomplete type.
206
+ def incomplete?; @size.nil? end
207
+ end
208
+
209
+ # TODO Function
210
+ # TODO Struct
211
+ # TODO Union
7
212
  end
8
213
 
214
+ module Syntax
215
+ Type = Any.new('TypeSpecifier', [Chelsy::Type::Base, :void])
216
+ end
217
+
218
+ # 6.4.4.1 Integer constants
9
219
  module Constant
220
+ class Base < Expr
221
+ end
10
222
 
11
- class Integral < Element
223
+ class Integral < Base
12
224
  attr_reader :value, :base
13
225
 
14
- def initialize(value, unsigned: false, base: 10)
226
+ def initialize(value, unsigned: false, base: 10, **rest)
15
227
  @value = value
16
- @unsigned = unsigned
228
+ @unsigned = !!unsigned
17
229
  @base = base
230
+
231
+ super(**rest)
18
232
  end
19
233
 
20
234
  def unsigned?
21
- !!@unsigned
235
+ @unsigned
22
236
  end
23
237
  end
24
238
 
@@ -33,4 +247,165 @@ module Chelsy
33
247
 
34
248
  end
35
249
 
250
+ # 6.4.5 String literals
251
+ module Constant
252
+
253
+ class String < Base
254
+ attr_reader :value
255
+
256
+ def initialize(str, wide: false, **rest)
257
+ @value = str.dup.freeze
258
+ @wide = !!wide
259
+
260
+ super(**rest)
261
+ end
262
+
263
+ def wide?
264
+ @wide
265
+ end
266
+ end
267
+
268
+ end
269
+
270
+ # 6.5.2.2 Function calls
271
+ class FunctionCall < Expr
272
+ attr_reader :callee, :args
273
+
274
+ def initialize(callee, args, **rest)
275
+ @callee = Syntax::Expr.ensure(callee)
276
+ @args = args.map {|a| Syntax::Expr.ensure(a) }
277
+
278
+ super(**rest)
279
+ end
280
+ end
281
+
282
+ # = 6.8 Statements and blocks
283
+
284
+ # == 6.8.3 Expression and null statements
285
+
286
+ # A null statement (consisting of just a semicolon) performs no operations.
287
+ class EmptyStmt < Stmt
288
+ end
289
+
290
+ class ExprStmt < Stmt
291
+ attr_reader :expr
292
+
293
+ def initialize(expr, **rest)
294
+ @expr = Syntax::Expr.ensure(expr)
295
+
296
+ super(**rest)
297
+ end
298
+ end
299
+
300
+ # == 6.8.2 Compound statement
301
+ module Syntax
302
+ BlockItem = Any.new('BlockItem', [Stmt, Declaration])
303
+ end
304
+
305
+ class Block < Stmt
306
+ include NodeList
307
+
308
+ def initialize(**rest)
309
+ @items = []
310
+ super(**rest)
311
+ end
312
+
313
+ private
314
+ def items; @items end
315
+
316
+ # Implicit convertion from Expr to ExprStmt
317
+ def validate_node(node)
318
+ item = node
319
+ item = ExprStmt.new(node) if Syntax::Expr.accept?(node)
320
+
321
+ Syntax::BlockItem.ensure(item)
322
+ end
323
+ end
324
+
325
+ # == 6.8.6.4 Thereturnstatement
326
+ class Return < Stmt
327
+ attr_reader :expr
328
+
329
+ def initialize(expr=nil, **rest)
330
+ @expr = Syntax::Expr.ensure(expr) if expr
331
+
332
+ super(**rest)
333
+ end
334
+ end
335
+
336
+ # = 6.9 External definitions
337
+
338
+ # == 6.9.1 Function definition
339
+
340
+ # Param-List ::
341
+ # [] |
342
+ # [:void] |
343
+ # [Param] |
344
+ # [Param, ..., :"..."]
345
+ class Param < Element
346
+ attr_reader :name, :type
347
+
348
+ def initialize(name, type, register: false, **rest)
349
+ @name = Syntax::Ident.ensure(name)
350
+ @type = Syntax::Type.ensure(type)
351
+
352
+ super(**rest)
353
+ end
354
+ end
355
+
356
+ module Syntax
357
+ Param = Any.new('Parameter', [Param, :void, :"..."])
358
+ end
359
+
360
+ class ParamList < Element
361
+ include NodeList
362
+
363
+ def initialize(**rest)
364
+ @params = []
365
+ super(**rest)
366
+ end
367
+
368
+ private
369
+ def items; @params end
370
+ def validate_node(node); Syntax::Param.ensure(node) end
371
+ end
372
+
373
+ class Function < Definition
374
+ attr_reader :name, :return_type, :params, :body
375
+
376
+ def initialize(name, return_type, params, inline: false, **rest, &block)
377
+ @name = Syntax::Ident.ensure(name)
378
+ @return_type = Syntax::Type.ensure(return_type)
379
+
380
+ @params = ParamList.new.tap do |list|
381
+ params.map {|p| list << p }
382
+ end
383
+
384
+ @body = Block.new
385
+ block.call(@body)
386
+
387
+ super(**rest)
388
+ end
389
+ end
390
+
391
+ # = 6.10 Preprocessing directives
392
+ module Directive
393
+ class Base < Fragment
394
+ end
395
+
396
+ class Include < Base
397
+ attr_reader :location
398
+
399
+ def initialize(location, system: false, **rest)
400
+ @location = location.to_s.dup
401
+ @system = !!system
402
+ end
403
+
404
+ # If `true`, this fragment forms `#include <...>`.
405
+ # otherwise, this fragment forms `#include "..."`.
406
+ def system?; @system end
407
+ end
408
+ end
409
+
410
+
36
411
  end
@@ -0,0 +1,37 @@
1
+ module Chelsy; end
2
+
3
+ # Syntax rules
4
+ module Chelsy::Syntax
5
+
6
+ class Rule
7
+ attr_reader :name
8
+
9
+ def initialize(name)
10
+ @name = name.dup
11
+ end
12
+
13
+ def accept?(node)
14
+ false
15
+ end
16
+
17
+ def ensure(node)
18
+ if accept?(node)
19
+ node
20
+ else
21
+ raise ArgumentError, "#{node.class.name} is not one of #{@name}"
22
+ end
23
+ end
24
+ end
25
+
26
+ class Any < Rule
27
+ def initialize(name, classes)
28
+ @classes = classes
29
+ super name
30
+ end
31
+
32
+ def accept?(node)
33
+ @classes.any? {|klass| klass === node }
34
+ end
35
+ end
36
+
37
+ end
@@ -1,31 +1,245 @@
1
1
  module Chelsy
2
2
 
3
3
  class Translator
4
+ attr_accessor :indent_string, :indent_level
5
+
6
+ DEFAULT_INDENT_STRING = ' '.freeze
7
+
8
+ def initialize()
9
+ @indent_string = DEFAULT_INDENT_STRING
10
+ @indent_level = 0
11
+ end
12
+
4
13
  def translate(node)
5
14
  case node
15
+ when Element
16
+ translate_element(node)
17
+ when Node
18
+ translate_node(node)
19
+ when Symbol
20
+ translate_ident(node)
21
+ when String
22
+ translate_fragment(node)
23
+ else
24
+ raise ArgumentError, "Unrecognized AST node: #{node.inspect}"
25
+ end
26
+ end
27
+
28
+ protected
29
+
30
+ def translate_node(node)
31
+ case node
32
+ # Fragment
33
+ when Fragment
34
+ translate_fragment(node)
35
+ else
36
+ raise ArgumentError, "Unrecognized AST node: #{node.inspect}"
37
+ end
38
+ end
39
+
40
+ def translate_fragment(node)
41
+ case node
42
+ when String
43
+ node.to_s
44
+ when Directive::Include
45
+ translate_include(node)
46
+ else
47
+ raise ArgumentError, "Unrecognized AST fragment: #{node.inspect}"
48
+ end
49
+ end
50
+
51
+ def translate_element(node)
52
+ case node
53
+ # Document
54
+ when Document
55
+ translate_document(node)
56
+
57
+ # Types
58
+ when Type::Base
59
+ translate_type(node)
60
+
61
+ # Expressions
6
62
  when Constant::Integral
7
63
  translate_integral(node)
64
+ when Constant::String
65
+ translate_string(node)
66
+ when FunctionCall
67
+ translate_function_call(node)
68
+
69
+ # Statements
70
+ when EmptyStmt
71
+ translate_empty_stmt(node)
72
+ when ExprStmt
73
+ translate_expr_stmt(node)
74
+ when Return
75
+ translate_return(node)
76
+ when Block
77
+ translate_block(node)
78
+
79
+ # Definition
80
+ when Function
81
+ translate_function(node)
82
+ when Param
83
+ translate_function_param(node)
84
+
8
85
  else
9
- raise "Unrecognized AST node: #{node}"
86
+ raise ArgumentError, "Unrecognized AST element: #{node.inspect}"
87
+ end
88
+ .tap do |src|
89
+ # Fragments
90
+ unless node.fragments.empty?
91
+ src.insert 0, "\n"
92
+ src.insert 0, node.fragments.map {|f| translate_fragment(f) }.join("\n")
93
+ end
94
+ unless node.post_fragments.empty?
95
+ src << "\n"
96
+ src << node.post_fragments.map {|f| translate_fragment(f) }.join("\n")
97
+ end
10
98
  end
11
99
  end
12
100
 
13
- protected
101
+ def translate_document(node)
102
+ node.map {|nd| translate(nd) }.join('')
103
+ end
104
+
105
+ def translate_ident(node)
106
+ node.to_s
107
+ end
108
+
109
+ # = Types
110
+
111
+ def translate_type(ty)
112
+ translate_typed_name(ty)
113
+ end
114
+
115
+ def translate_typed_name(ty, name=nil)
116
+ case ty
117
+ when Type::Derived
118
+ # TODO
119
+ raise NotImplementedError
120
+ else
121
+ translate_primitive_type(ty).tap do |src|
122
+ src << " #{name}" if name
123
+ end
124
+ end
125
+ end
126
+
127
+ def translate_primitive_type(ty)
128
+ 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
136
+ src.insert(0, 'const ') if ty.const?
137
+ src.insert(0, 'volatile ') if ty.volatile?
138
+ src.insert(0, 'restrict ') if ty.restrict?
139
+ end
140
+ end
141
+
142
+ def translate_integral_type(ty)
143
+ case ty
144
+ when Type::Char; 'char'
145
+ when Type::Short; 'short'
146
+ when Type::Int; 'int'
147
+ when Type::Long; 'long'
148
+ when Type::LongLong; 'long long'
149
+ end.tap do |src|
150
+ src.insert(0, 'unsigned ') if ty.unsigned?
151
+ end
152
+ end
153
+
154
+ # = Expressions
14
155
 
15
156
  def translate_integral(node)
16
157
  integer_prefix(node) + node.value.to_s(node.base) + integer_suffix(node)
17
158
  end
18
159
 
160
+ def translate_string(node)
161
+ if node.wide?
162
+ 'L' + node.value.dump
163
+ else
164
+ node.value.dump
165
+ end
166
+ end
167
+
168
+ def translate_function_call(node)
169
+ callee = expr(node.callee)
170
+ args = node.args.map {|a| expr(a) }.join(', ')
171
+
172
+ "#{callee}(#{args})"
173
+ end
174
+
175
+ # = Statements
176
+
177
+ def translate_empty_stmt(node)
178
+ indent << ';'
179
+ end
180
+
181
+ def translate_expr_stmt(node)
182
+ indent << translate(node.expr) << ';'
183
+ end
184
+
185
+ def translate_return(node)
186
+ if node.expr
187
+ indent << 'return ' << translate(node.expr) << ';'
188
+ else
189
+ indent << 'return;'
190
+ end
191
+ end
192
+
193
+ def translate_block(node)
194
+ @indent_level += 1
195
+ body = node.map {|item| translate(item) }.join("\n")
196
+ @indent_level -= 1
197
+
198
+ "#{indent}{\n#{body}\n#{indent}}"
199
+ end
200
+
201
+ # = Directives
202
+ def translate_include(node)
203
+ if node.system?
204
+ "#include <#{node.location}>"
205
+ else
206
+ %Q{#include "#{node.location}"}
207
+ end
208
+ end
209
+
210
+ # = Statements
211
+
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)}"
215
+ end
216
+
217
+ def translate_function_param(node)
218
+ translate_typed_name(node.type, node.name)
219
+ end
220
+
19
221
  private
20
222
 
223
+ def indent
224
+ @indent_string * @indent_level
225
+ end
226
+
227
+ # Expression: parenthesize if needed
228
+ def expr(node)
229
+ # TODO Pointer expression should be parenthesized.
230
+ translate(node)
231
+ end
232
+
21
233
  def integer_prefix(node)
22
234
  case node.base
23
- when 16
24
- "0x"
25
235
  when 8
26
236
  "0"
27
- else
237
+ when 10
28
238
  ""
239
+ when 16
240
+ "0x"
241
+ else
242
+ raise ArgumentError, "Unsupported radix: #{node.base}"
29
243
  end
30
244
  end
31
245
 
@@ -1,3 +1,3 @@
1
1
  module Chelsy
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -0,0 +1,6 @@
1
+ #include <stdio.h>
2
+
3
+ int main(void) {
4
+ printf("Hello, Chelsy!\n");
5
+ return 0;
6
+ }
@@ -0,0 +1,15 @@
1
+ require 'chelsy'
2
+
3
+ include Chelsy
4
+
5
+ doc = Document.new
6
+
7
+ doc.fragments << Directive::Include.new("stdio.h", system: true)
8
+ doc.fragments << ''
9
+
10
+ doc << Function.new(:main, Type::Int.new, [:void]) do |b|
11
+ b << FunctionCall.new(:printf, [Constant::String.new("Hello, Chelsy!\n")])
12
+ b << Return.new(Constant::Int.new(0))
13
+ end
14
+
15
+ puts Translator.new.translate(doc)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chelsy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Takanori Ishikawa
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-12 00:00:00.000000000 Z
11
+ date: 2016-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -84,8 +84,11 @@ files:
84
84
  - chelsy.gemspec
85
85
  - lib/chelsy.rb
86
86
  - lib/chelsy/ast.rb
87
+ - lib/chelsy/syntax.rb
87
88
  - lib/chelsy/translator.rb
88
89
  - lib/chelsy/version.rb
90
+ - sample/hello_chelsy.c
91
+ - sample/hello_chelsy.rb
89
92
  homepage: https://github.com/ishikawa/chelsy
90
93
  licenses:
91
94
  - MIT