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.
- checksums.yaml +4 -4
- data/.yardopts +3 -0
- data/README.md +12 -9
- data/Rakefile +5 -0
- data/lib/yadriggy.rb +1 -1
- data/lib/yadriggy/algebra.rb +31 -3
- data/lib/yadriggy/assert.rb +8 -8
- data/lib/yadriggy/ast.rb +71 -15
- data/lib/yadriggy/ast_location.rb +1 -1
- data/lib/yadriggy/c/c.rb +5 -5
- data/lib/yadriggy/c/codegen.rb +12 -27
- data/lib/yadriggy/c/config.rb +1 -1
- data/lib/yadriggy/c/ctype.rb +5 -5
- data/lib/yadriggy/c/ctypecheck.rb +6 -6
- data/lib/yadriggy/c/ffi.rb +8 -8
- data/lib/yadriggy/checker.rb +60 -26
- data/lib/yadriggy/eval.rb +5 -1
- data/lib/yadriggy/eval_all.rb +13 -0
- data/lib/yadriggy/pretty_print.rb +14 -6
- data/lib/yadriggy/py.rb +11 -0
- data/lib/yadriggy/py/codegen.rb +457 -0
- data/lib/yadriggy/py/import.rb +90 -0
- data/lib/yadriggy/py/py_typechecker.rb +62 -0
- data/lib/yadriggy/py/python.rb +130 -0
- data/lib/yadriggy/ruby_typecheck.rb +96 -45
- data/lib/yadriggy/ruby_typeinfer.rb +60 -25
- data/lib/yadriggy/source_code.rb +27 -17
- data/lib/yadriggy/syntax.rb +23 -8
- data/lib/yadriggy/type.rb +38 -38
- data/lib/yadriggy/typecheck.rb +18 -5
- data/lib/yadriggy/version.rb +1 -1
- data/yadriggy.gemspec +2 -1
- metadata +24 -4
data/lib/yadriggy/py.rb
ADDED
@@ -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
|