yadriggy 1.1.0 → 1.2.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.
@@ -0,0 +1,11 @@
1
+ # Copyright (C) 2018- Shigeru Chiba. All rights reserved.
2
+
3
+ require 'yadriggy'
4
+ require 'yadriggy/py/python'
5
+
6
+ module Yadriggy
7
+ # Python language embedded in Ruby
8
+ module Py
9
+ # see py/python.rb
10
+ end
11
+ end
@@ -0,0 +1,457 @@
1
+ # Copyright (C) 2018- Shigeru Chiba. All rights reserved.
2
+
3
+ require 'yadriggy'
4
+
5
+ module Yadriggy
6
+ module Py
7
+ class CodeGen < Checker
8
+ # @return [Printer] the printer.
9
+ attr_reader :printer
10
+
11
+ # @param [Printer] printer
12
+ # @param [PyTypeChecker] checker
13
+ def initialize(printer, checker)
14
+ super()
15
+ @printer = printer
16
+ @typechecker = checker
17
+ end
18
+
19
+ # @api private
20
+ def error_group
21
+ 'code generation'
22
+ end
23
+
24
+ # Prints a given AST by {#printer}.
25
+ # @param [ASTree|ASTnode] an_ast the AST.
26
+ # @return [CodeGen] the `self` object.
27
+ def print(an_ast)
28
+ check_all(an_ast)
29
+ if errors?
30
+ error_messages.each do |m|
31
+ STDERR.puts(m)
32
+ end
33
+ raise RuntimeError.new('Python code generation failure')
34
+ end
35
+ self
36
+ end
37
+
38
+ # Starts a new line.
39
+ def newline
40
+ @printer.nl
41
+ end
42
+
43
+ # The name of the function for initializing free variables.
44
+ FreeVarInitName = 'yadpy_initialize'
45
+
46
+ # Prints a function for initializing free variables in Python.
47
+ # @return [Array<Object>] the arguments to the function.
48
+ def print_free_vars_initializer
49
+ @printer << 'def ' << FreeVarInitName << '(_yadpy_values):' << :nl
50
+ @printer << ' global '
51
+ @typechecker.references.each_with_index do |pair, i|
52
+ @printer << ', ' if i > 0
53
+ @printer << pair[1]
54
+ end
55
+ @printer << :nl
56
+ args = []
57
+ i = 0
58
+ @typechecker.references.each do |pair|
59
+ @printer << ' ' << pair[1] << ' = ' << '_yadpy_values[' << i.to_s << ']' << :nl
60
+ args << pair[0]
61
+ i += 1
62
+ end
63
+ @printer << :nl
64
+ return args
65
+ end
66
+
67
+ rule(Name) do
68
+ @printer << ast.name
69
+ end
70
+
71
+ rule(Label) do
72
+ @printer << ast.name
73
+ end
74
+
75
+ rule(IdentifierOrCall) do
76
+ t = @typechecker.type?(ast)
77
+ rt = ResultType.role(t)
78
+ unless rt.nil?
79
+ @printer << ast.name << '()'
80
+ else
81
+ it = InstanceType.role(t)
82
+ unless it.nil?
83
+ value = it.object
84
+ name = @typechecker.references[value]
85
+ if name.nil?
86
+ if value.is_a?(Numeric)
87
+ @printer << value.to_s
88
+ else
89
+ @printer << value.to_s.dump
90
+ end
91
+ else
92
+ @printer << name
93
+ end
94
+ else
95
+ @printer << ast.name
96
+ end
97
+ end
98
+ end
99
+
100
+ rule(Number) do
101
+ @printer << ast.value.to_s # complex numbrer?
102
+ end
103
+
104
+ rule(Super) do
105
+ @printer << 'super()'
106
+ end
107
+
108
+ rule(Reserved) do
109
+ case ast.name
110
+ when 'true'
111
+ @printer << 'True'
112
+ when 'false'
113
+ @printer << 'False'
114
+ when 'nil'
115
+ @printer << 'None'
116
+ else
117
+ @printer << ast.name
118
+ end
119
+ end
120
+
121
+ rule(Paren) do
122
+ @printer << '('
123
+ print(ast.expression)
124
+ @printer << ')'
125
+ end
126
+
127
+ rule(Exprs) do
128
+ ast.expressions.each do |e|
129
+ print(e)
130
+ @printer.nl
131
+ end
132
+ end
133
+
134
+ rule(StringLiteral) do
135
+ @printer << ast.value.dump
136
+ end
137
+
138
+ # Python List
139
+ rule(ArrayLiteral) do
140
+ @printer << '['
141
+ if ast.elements.size == 1 && ast.elements[0].is_a?(ForLoop)
142
+ loop = ast.elements[0]
143
+ print(loop.body)
144
+ @printer << ' for '
145
+ print_each(loop.vars, true)
146
+ @printer << ' in '
147
+ print(loop.set)
148
+ else
149
+ print_each(ast.elements, true)
150
+ end
151
+ @printer << ']'
152
+ end
153
+
154
+ rule(HashLiteral) do
155
+ @printer << '{'
156
+ print_each(ast.pairs, true) do |pair|
157
+ print(pair[0])
158
+ @printer << ': '
159
+ print(pair[1])
160
+ end
161
+ @printer << '}'
162
+ end
163
+
164
+ # Hash key.
165
+ rule(SymbolLiteral) do
166
+ @printer << ast.name.to_s.dump
167
+ end
168
+
169
+ # Slicing in Python like [1:n] is written
170
+ # as [1..n] in Ruby.
171
+ rule(ArrayRef) do
172
+ print(ast.array)
173
+ @printer << '['
174
+ idx = ast.indexes[0]
175
+ if idx.is_a?(Dots)
176
+ error!(ast, "#{idx.op} should be ..") unless idx.op == :'..'
177
+ print(idx.left) unless is_omitted?(idx.left)
178
+ @printer << ':'
179
+ print(idx.right) unless is_omitted?(idx.right)
180
+ else
181
+ print(ast.indexes[0])
182
+ end
183
+ @printer << ']'
184
+ end
185
+
186
+ # @api private
187
+ # [:n] in Python is written as [_..n] in Ruby.
188
+ def is_omitted?(ast)
189
+ ast.is_a?(Name) && ast.name == '_'
190
+ end
191
+
192
+ rule(Call) do
193
+ if ast.name.name == 'tuple' && ast.receiver.nil?
194
+ # tuple(), tuple(1, 2, 3) => (), (1, 2, 3)
195
+ print_tuple(ast)
196
+ elsif ast.name.name == 'in' && !ast.receiver.nil? && ast.args&.size == 1
197
+ # a .in b, a.in(b) => a in b
198
+ print_binary(ast, ' in ')
199
+ elsif ast.name.name == 'not_in' && !ast.receiver.nil? && ast.args&.size == 1
200
+ # a .not_in b, a.not_in(b) => a not in b
201
+ print_binary(ast, ' not in ')
202
+ elsif ast.name.name == 'idiv' && !ast.receiver.nil? && ast.args&.size == 1
203
+ # a .idiv b, a.idiv(b) => a // b
204
+ print_binary(ast, ' // ')
205
+ elsif ast.op == :"::" && ast.args.empty?
206
+ if ast.receiver
207
+ @printer << '('
208
+ print(ast.receiver)
209
+ @printer << ')'
210
+ end
211
+ @printer << '.'
212
+ @printer << ast.name.name
213
+ else
214
+ if ast.receiver
215
+ @printer << '('
216
+ print(ast.receiver)
217
+ @printer << ')'
218
+ end
219
+ unless ast.op.nil?
220
+ error!(ast, "#{ast.op} should be .") unless ast.op == :"."
221
+ @printer << ast.op
222
+ end
223
+ @printer << ast.name.name
224
+ @printer << '('
225
+ print_each(ast.args, true)
226
+ @printer << ')'
227
+ end
228
+ end
229
+
230
+ # @api private
231
+ def print_tuple(an_ast)
232
+ @printer << '('
233
+ print_each(an_ast.args, true)
234
+ @printer << ', ' if an_ast.args.size == 1
235
+ @printer << ')'
236
+ end
237
+
238
+ # @api private
239
+ def print_binary(an_ast, op)
240
+ @printer << '('
241
+ print(an_ast.receiver)
242
+ @printer << ')'
243
+ @printer << op
244
+ @printer << '('
245
+ print(an_ast.args[0])
246
+ @printer << ')'
247
+ end
248
+
249
+ # @api private
250
+ def print_each(array, first, &block)
251
+ array.each do |e|
252
+ if e
253
+ if first
254
+ first = false
255
+ else
256
+ @printer << ', '
257
+ end
258
+ if block.nil?
259
+ print(e)
260
+ else
261
+ block.call(e)
262
+ end
263
+ end
264
+ end
265
+ first
266
+ end
267
+
268
+ rule(Unary) do
269
+ if ast.real_operator == :!
270
+ @printer << 'not '
271
+ else
272
+ @printer << ast.real_operator
273
+ end
274
+ print(ast.operand)
275
+ end
276
+
277
+ rule(Binary) do
278
+ if ast.op == :'..'
279
+ @printer << 'range('
280
+ print(ast.left)
281
+ @printer << ', '
282
+ print(ast.right)
283
+ @printer << ')'
284
+ else
285
+ print(ast.left)
286
+ @printer << ' ' << python_binary_op(ast.op) << ' '
287
+ print(ast.right)
288
+ end
289
+ end
290
+
291
+ rule(Assign) do
292
+ if ast.left.is_a?(Array) && ast.right.is_a?(Array) &&
293
+ ast.left.size < ast.right.size
294
+ error!(ast, 'too many right operands')
295
+ end
296
+
297
+ if ast.left.is_a?(Array)
298
+ print_each(ast.left, true)
299
+ else
300
+ print(ast.left)
301
+ end
302
+ @printer << ' ' << python_binary_op(ast.op) << ' '
303
+ if ast.right.is_a?(Array)
304
+ print_each(ast.right, true)
305
+ else
306
+ print(ast.right)
307
+ end
308
+ end
309
+
310
+ def python_binary_op(op)
311
+ if op == :'&&'
312
+ 'and'
313
+ elsif op == :'||'
314
+ 'or'
315
+ else
316
+ op
317
+ end
318
+ end
319
+
320
+ rule(Lambda) do
321
+ print_lambda(ast)
322
+ end
323
+
324
+ def print_lambda(func)
325
+ @printer << '(lambda '
326
+ print_parameters(func.params)
327
+ @printer << ': '
328
+ print(func.body) # has to be a simple expression?
329
+ @printer << ')'
330
+ end
331
+
332
+ rule(:lambda_call) do
333
+ print_lambda(ast.block)
334
+ end
335
+
336
+ rule(Conditional) do
337
+ if ast.op == :if
338
+ @printer << ast.op << ' '
339
+ print(ast.cond)
340
+ @printer << ':'
341
+ @printer.down
342
+ print(ast.then)
343
+ @printer.up
344
+ ast.all_elsif.each do | expr |
345
+ @printer << 'elif '
346
+ print(expr[0])
347
+ @printer << ':'
348
+ @printer.down
349
+ print(expr[1])
350
+ @printer.up
351
+ end
352
+ if ast.else
353
+ @printer << 'else:'
354
+ @printer.down
355
+ print(ast.else)
356
+ @printer.up
357
+ end
358
+ else # :ifop
359
+ print(ast.then)
360
+ @printer << ' if '
361
+ print(ast.cond)
362
+ @printer << ' else '
363
+ print(ast.else)
364
+ end
365
+ end
366
+
367
+ rule(Loop) do
368
+ if ast.op == :while
369
+ @printer << 'while '
370
+ print(ast.cond)
371
+ @printer << ':'
372
+ @printer.down
373
+ print(ast.body)
374
+ @printer.up
375
+ # while else?
376
+ else
377
+ error!(ast, 'unsupported loop')
378
+ end
379
+ end
380
+
381
+ rule(ForLoop) do
382
+ @printer << 'for '
383
+ print_each(ast.vars, true)
384
+ @printer << ' in '
385
+ print(ast.set) # could be multiple expressions
386
+ @printer << ':'
387
+ @printer.down
388
+ print(ast.body)
389
+ @printer.up
390
+ # for .. else?
391
+ end
392
+
393
+ rule(Break) do
394
+ @printer << ast.op
395
+ error!(ast, "bad #{ast.op}") unless ast.values.nil?
396
+ end
397
+
398
+ rule(Return) do
399
+ @printer << 'return'
400
+ first = true
401
+ ast.values.each do |v|
402
+ if first
403
+ @printer << ' '
404
+ first = false
405
+ else
406
+ @printer << ', '
407
+ end
408
+ print(v)
409
+ end
410
+ end
411
+
412
+ rule(Def) do
413
+ @printer << 'def '
414
+ @printer << method_name(ast.name.name)
415
+ @printer << '('
416
+ print_parameters(ast.params)
417
+ @printer << '):' << :nl
418
+ @printer.down
419
+ print(ast.body)
420
+ @printer.up
421
+ @printer << :nl
422
+ end
423
+
424
+ # @api private
425
+ def print_parameters(params)
426
+ params.each_with_index do |p, i|
427
+ @printer << ', ' if i > 0
428
+ @printer << p.name
429
+ end
430
+ end
431
+
432
+ # @api private
433
+ def method_name(name)
434
+ if name == 'initialize'
435
+ '__init__'
436
+ else
437
+ name
438
+ end
439
+ end
440
+
441
+ rule(ClassDef) do
442
+ @printer << 'class '
443
+ print(ast.name)
444
+ if ast.superclass
445
+ @printer << '('
446
+ print(ast.superclass) # only single inheritance
447
+ @printer << ')'
448
+ end
449
+ @printer << ':'
450
+ @printer.down
451
+ print(ast.body)
452
+ @printer.up
453
+ end
454
+
455
+ end
456
+ end
457
+ end