Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/python-wasm
Path: blob/main/python/pylang/src/output/loops.py
1398 views
1
# vim:fileencoding=utf-8
2
# License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
3
from __python__ import hash_literals
4
5
from ast_types import AST_BaseCall, AST_SymbolRef, AST_Array, AST_Unary, AST_Number, has_calls, AST_Seq, AST_ListComprehension, is_node_type
6
from output.stream import OutputStream
7
8
9
def unpack_tuple(elems, output, in_statement):
10
for i, elem in enumerate(elems):
11
output.indent()
12
output.assign(elem)
13
output.print("ρσ_unpack")
14
output.with_square(lambda: output.print(i))
15
if not in_statement or i < elems.length - 1:
16
output.semicolon()
17
output.newline()
18
19
20
def print_do_loop(self, output):
21
output.print("do")
22
output.space()
23
self._do_print_body(output)
24
output.space()
25
output.print("while")
26
output.space()
27
output.with_parens(lambda: self.condition.print(output))
28
output.semicolon()
29
30
31
def print_while_loop(self, output):
32
output.print("while")
33
output.space()
34
output.with_parens(lambda: self.condition.print(output))
35
output.space()
36
self._do_print_body(output)
37
38
39
def is_simple_for_in(self):
40
# return true if this loop can be simplified into a basic for (i in j) loop
41
if (is_node_type(self.object, AST_BaseCall)
42
and is_node_type(self.object.expression, AST_SymbolRef)
43
and self.object.expression.name is "dir"
44
and self.object.args.length is 1):
45
return True
46
return False
47
48
49
def is_simple_for(self):
50
# returns true if this loop can be simplified into a basic for(i=n;i<h;i++) loop
51
if (is_node_type(self.object, AST_BaseCall)
52
and is_node_type(self.object.expression, AST_SymbolRef)
53
and self.object.expression.name is "range"
54
and not (is_node_type(self.init, AST_Array))):
55
a = self.object.args
56
l = a.length
57
if l < 3 or (is_node_type(a[2], AST_Number) or
58
(is_node_type(a[2], AST_Unary) and a[2].operator is '-'
59
and is_node_type(a[2].expression, AST_Number))):
60
if (l is 1 and not has_calls(a[0])) or (l > 1
61
and not has_calls(a[1])):
62
return True
63
return False
64
65
66
def print_for_loop_body(output):
67
self = this
68
69
def f_print_for_loop_body():
70
if not (self.simple_for_index or is_simple_for_in(self)):
71
# if we're using multiple iterators, unpack them
72
output.indent()
73
itervar = "ρσ_Index" + output.index_counter
74
if is_node_type(self.init, AST_Array):
75
flat = self.init.flatten()
76
output.assign("ρσ_unpack")
77
if flat.length > self.init.elements.length:
78
output.print('ρσ_flatten(' + itervar + ')')
79
else:
80
output.print(itervar)
81
output.end_statement()
82
unpack_tuple(flat, output)
83
else:
84
output.assign(self.init)
85
output.print(itervar)
86
output.end_statement()
87
88
output.index_counter += 1
89
if self.simple_for_index:
90
output.indent()
91
output.assign(self.init)
92
output.print(self.simple_for_index)
93
output.end_statement()
94
95
for stmt in self.body.body:
96
output.indent()
97
stmt.print(output)
98
output.newline()
99
100
output.with_block(f_print_for_loop_body)
101
102
103
def init_es6_itervar(output, itervar):
104
output.indent()
105
output.spaced(itervar, '=', '((typeof', itervar + '[Symbol.iterator]',
106
'===', '"function")', '?', '(' + itervar, 'instanceof',
107
'Map', '?', itervar + '.keys()', ':', itervar + ')', ':',
108
'Object.keys(' + itervar + '))')
109
output.end_statement()
110
111
112
def print_for_in(self, output):
113
def write_object():
114
if self.object.constructor is AST_Seq:
115
(AST_Array({'elements': self.object.to_array()})).print(output)
116
else:
117
self.object.print(output)
118
119
if is_simple_for(self):
120
# optimize range() into a simple for loop
121
increment = None
122
args = self.object.args
123
tmp_ = args.length
124
if tmp_ is 1:
125
start = 0
126
end = args[0]
127
elif tmp_ is 2:
128
start = args[0]
129
end = args[1]
130
elif tmp_ is 3:
131
start = args[0]
132
end = args[1]
133
increment = args[2]
134
135
self.simple_for_index = idx = 'ρσ_Index' + output.index_counter
136
output.index_counter += 1
137
output.print("for")
138
output.space()
139
140
def f_simple_for():
141
output.spaced('var', idx, '='), output.space()
142
start.print(output) if start.print else output.print(start)
143
output.semicolon()
144
output.space()
145
output.print(idx)
146
output.space()
147
output.print(">") if is_node_type(increment,
148
AST_Unary) else output.print("<")
149
output.space()
150
end.print(output)
151
output.semicolon()
152
output.space()
153
output.print(idx)
154
if increment and (not (is_node_type(increment, AST_Unary))
155
or increment.expression.value is not "1"):
156
if is_node_type(increment, AST_Unary):
157
output.print("-=")
158
increment.expression.print(output)
159
else:
160
output.print("+=")
161
increment.print(output)
162
else:
163
if is_node_type(increment, AST_Unary):
164
output.print("--")
165
else:
166
output.print("++")
167
168
output.with_parens(f_simple_for)
169
170
elif is_simple_for_in(self):
171
# optimize dir() into a simple for in loop
172
output.print("for")
173
output.space()
174
175
def f_simple_for_in():
176
self.init.print(output)
177
output.space()
178
output.print('in')
179
output.space()
180
self.object.args[0].print(output)
181
182
output.with_parens(f_simple_for_in)
183
else:
184
# regular loop
185
itervar = "ρσ_Iter" + output.index_counter
186
output.assign("var " + itervar)
187
write_object()
188
output.end_statement()
189
init_es6_itervar(output, itervar)
190
output.indent()
191
output.spaced('for', '(var', 'ρσ_Index' + output.index_counter, 'of',
192
itervar + ')')
193
194
output.space()
195
self._do_print_body(output)
196
197
198
def print_list_comprehension(self, output):
199
tname = self.constructor.name.slice(4)
200
result_obj = {
201
'ListComprehension': '[]',
202
'DictComprehension':
203
('Object.create(null)' if self.is_jshash else '{}'),
204
'SetComprehension': 'ρσ_set()'
205
}[tname]
206
is_generator = tname is 'GeneratorComprehension'
207
add_to_result = None
208
if tname is 'DictComprehension':
209
if self.is_pydict:
210
result_obj = 'ρσ_dict()'
211
212
def add_to_result0(output):
213
output.indent()
214
output.print('ρσ_Result.set')
215
216
def f_dict():
217
self.statement.print(output)
218
output.space()
219
output.print(',')
220
output.space()
221
222
def f_dict0():
223
if self.value_statement.constructor is AST_Seq:
224
output.with_square(
225
lambda: self.value_statement.print(output))
226
else:
227
self.value_statement.print(output)
228
229
output.with_parens(f_dict0)
230
231
output.with_parens(f_dict)
232
output.end_statement()
233
234
add_to_result = add_to_result0
235
236
else:
237
238
def add_to_result0(output):
239
output.indent()
240
output.print('ρσ_Result')
241
output.with_square(lambda: self.statement.print(output))
242
output.space(), output.print('='), output.space()
243
244
def f_result():
245
if self.value_statement.constructor is AST_Seq:
246
output.with_square(
247
lambda: self.value_statement.print(output))
248
else:
249
self.value_statement.print(output)
250
251
output.with_parens(f_result)
252
output.end_statement()
253
254
add_to_result = add_to_result0
255
else:
256
push_func = "ρσ_Result." + (
257
'push' if self.constructor is AST_ListComprehension else 'add')
258
if is_generator:
259
push_func = 'yield '
260
261
def add_to_result0(output):
262
output.indent()
263
output.print(push_func)
264
265
def f_output_statement():
266
if self.statement.constructor is AST_Seq:
267
output.with_square(lambda: self.statement.print(output))
268
else:
269
self.statement.print(output)
270
271
output.with_parens(f_output_statement)
272
output.end_statement()
273
274
add_to_result = add_to_result0
275
276
def f_body():
277
output.print("function")
278
output.print("()")
279
output.space()
280
281
def f_body0():
282
body_out = output
283
if is_generator:
284
body_out.indent()
285
body_out.print('function* js_generator()'), body_out.space(
286
), body_out.print('{')
287
body_out.newline()
288
previous_indentation = output.indentation()
289
output.set_indentation(output.next_indent())
290
body_out.indent()
291
body_out.assign("var ρσ_Iter")
292
self.object.print(body_out)
293
294
if result_obj:
295
body_out.comma()
296
body_out.assign("ρσ_Result")
297
body_out.print(result_obj)
298
# make sure to locally scope loop variables
299
if is_node_type(self.init, AST_Array):
300
for i in self.init.elements:
301
body_out.comma()
302
i.print(body_out)
303
else:
304
body_out.comma()
305
self.init.print(body_out)
306
body_out.end_statement()
307
308
init_es6_itervar(body_out, 'ρσ_Iter')
309
body_out.indent()
310
body_out.print("for")
311
body_out.space()
312
body_out.with_parens(
313
lambda: body_out.spaced('var', 'ρσ_Index', 'of', 'ρσ_Iter'))
314
body_out.space()
315
316
def f_body_out():
317
body_out.indent()
318
itervar = 'ρσ_Index'
319
if is_node_type(self.init, AST_Array):
320
flat = self.init.flatten()
321
body_out.assign("ρσ_unpack")
322
if flat.length > self.init.elements.length:
323
body_out.print('ρσ_flatten(' + itervar + ')')
324
else:
325
body_out.print(itervar)
326
body_out.end_statement()
327
unpack_tuple(flat, body_out)
328
else:
329
body_out.assign(self.init)
330
body_out.print(itervar)
331
body_out.end_statement()
332
333
if self.condition:
334
body_out.indent()
335
body_out.print("if")
336
body_out.space()
337
body_out.with_parens(
338
lambda: self.condition.print(body_out))
339
body_out.space()
340
body_out.with_block(lambda: add_to_result(body_out))
341
body_out.newline()
342
else:
343
add_to_result(body_out)
344
345
body_out.with_block(f_body_out)
346
body_out.newline()
347
if self.constructor is AST_ListComprehension:
348
body_out.indent()
349
body_out.spaced('ρσ_Result', '=',
350
'ρσ_list_constructor(ρσ_Result)')
351
body_out.end_statement()
352
if not is_generator:
353
body_out.indent()
354
body_out.print("return ρσ_Result")
355
body_out.end_statement()
356
if is_generator:
357
output.set_indentation(previous_indentation)
358
body_out.newline(), body_out.indent(), body_out.print(
359
'}') # end js_generator
360
output.newline(), output.indent()
361
output.spaced('var', 'result', '=', 'js_generator.call(this)')
362
output.end_statement()
363
# Python's generator objects use a separate method to send data to the generator
364
output.indent()
365
output.spaced('result.send', '=', 'result.next')
366
output.end_statement()
367
output.indent()
368
output.spaced('return', 'result')
369
output.end_statement()
370
371
output.with_block(f_body0)
372
373
output.with_parens(f_body)
374
output.print("()")
375
376
377
def print_ellipses_range(self, output):
378
output.print("ρσ_range(")
379
self.first.print(output)
380
output.print(",ρσ_operator_add(")
381
self.last.print(output)
382
output.print(",1))")
383
384