yadriggy 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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