Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/python-wasm
Path: blob/main/python/pylang/src/output/functions.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_ClassCall, AST_New, has_calls, AST_Dot, AST_PropAccess, AST_SymbolRef, is_node_type
6
from output.stream import OutputStream
7
from output.statements import print_bracketed
8
from output.utils import create_doctring
9
from output.operators import print_getattr
10
11
anonfunc = 'ρσ_anonfunc'
12
module_name = 'null'
13
14
15
def set_module_name(x):
16
nonlocal module_name
17
module_name = '"' + x + '"' if x else 'null'
18
19
20
def decorate(decorators, output, func):
21
pos = 0
22
23
def wrap():
24
nonlocal pos
25
if pos < decorators.length:
26
decorators[pos].expression.print(output)
27
pos += 1
28
output.with_parens(wrap)
29
else:
30
func()
31
32
wrap()
33
34
35
def function_args(argnames, output, strip_first):
36
def f():
37
if argnames and argnames.length and (
38
argnames.is_simple_func is True
39
or argnames.is_simple_func is undefined):
40
for i, arg in enumerate(
41
(argnames.slice(1) if strip_first else argnames)):
42
if i:
43
output.comma()
44
arg.print(output)
45
46
output.with_parens(f)
47
output.space()
48
49
50
def function_preamble(node, output, offset):
51
a = node.argnames
52
if not a or a.is_simple_func:
53
return
54
# If this function has optional parameters/*args/**kwargs declare it differently
55
fname = node.name.name if node.name else anonfunc
56
kw = 'arguments[arguments.length-1]'
57
# Define all formal parameters
58
for c, arg in enumerate(a):
59
i = c - offset
60
if i >= 0:
61
output.indent()
62
output.print("var")
63
output.space()
64
output.assign(arg)
65
if Object.prototype.hasOwnProperty.call(a.defaults, arg.name):
66
output.spaced('(arguments[' + i + ']', '===', 'undefined',
67
'||', '(', i, '===', 'arguments.length-1', '&&',
68
kw, '!==', 'null', '&&', 'typeof', kw, '===',
69
'"object"', '&&', kw, '[ρσ_kwargs_symbol]',
70
'===', 'true))', '?', '')
71
output.print(fname + '.__defaults__.'), arg.print(output)
72
output.space(), output.print(':'), output.space()
73
else:
74
output.spaced('(', i, '===', 'arguments.length-1', '&&', kw,
75
'!==', 'null', '&&', 'typeof', kw, '===',
76
'"object"', '&&', kw, '[ρσ_kwargs_symbol]',
77
'===', 'true)', '?', 'undefined', ':', '')
78
output.print('arguments[' + i + ']')
79
output.end_statement()
80
if a.kwargs or a.has_defaults:
81
# Look for an options object
82
kw = a.kwargs.name if a.kwargs else 'ρσ_kwargs_obj'
83
output.indent()
84
output.spaced('var', kw, '=', 'arguments[arguments.length-1]')
85
output.end_statement()
86
# Ensure kwargs is the options object
87
output.indent()
88
output.spaced('if', '(' + kw, '===', 'null', '||', 'typeof', kw, '!==',
89
'"object"', '||', kw, '[ρσ_kwargs_symbol]', '!==',
90
'true)', kw, '=', '{}')
91
output.end_statement()
92
# Read values from the kwargs object for any formal parameters
93
if a.has_defaults:
94
for dname in Object.keys(a.defaults):
95
output.indent()
96
output.spaced(
97
'if', '(Object.prototype.hasOwnProperty.call(' + kw + ',',
98
'"' + dname + '"))')
99
100
def f():
101
output.indent()
102
output.spaced(dname, '=', kw + '.' + dname)
103
output.end_statement()
104
if a.kwargs:
105
output.indent()
106
output.spaced('delete', kw + '.' + dname)
107
output.end_statement()
108
109
output.with_block(f)
110
output.newline()
111
112
if a.starargs is not undefined:
113
# Define the *args parameter, putting in whatever is left after assigning the formal parameters and the options object
114
nargs = a.length - offset
115
output.indent()
116
output.spaced('var', a.starargs.name, '=',
117
'Array.prototype.slice.call(arguments,', nargs + ')')
118
output.end_statement()
119
# Remove the options object, if present
120
output.indent()
121
output.spaced('if', '(' + kw, '!==', 'null', '&&', 'typeof', kw, '===',
122
'"object"', '&&', kw, '[ρσ_kwargs_symbol]', '===',
123
'true)', a.starargs.name)
124
output.print('.pop()')
125
output.end_statement()
126
127
128
def has_annotations(self):
129
if self.return_annotation:
130
return True
131
for arg in self.argnames:
132
if arg.annotation:
133
return True
134
return False
135
136
137
def function_annotation(self, output, strip_first, name):
138
fname = name or (self.name.name if self.name else anonfunc)
139
props = Object.create(None)
140
141
# Create __annotations__
142
# TODO: These are completely disabled, since to really using
143
# them the typings module has to be properly implemented...
144
# otherwise trying to use them with a mocked typings module
145
# doesn't work at all. For now we just ignore this data
146
# and mock typings, so you can fully typecheck code with mypy
147
# while still *running* it with pylang.
148
if self.annotations and has_annotations(self):
149
150
def annotations():
151
output.print('{')
152
if self.argnames and self.argnames.length:
153
for i, arg in enumerate(self.argnames):
154
if arg.annotation:
155
arg.print(output)
156
output.print(':'), output.space()
157
arg.annotation.print(output)
158
if i < self.argnames.length - 1 or self.return_annotation:
159
output.comma()
160
if self.return_annotation:
161
output.print('return:'), output.space()
162
self.return_annotation.print(output)
163
output.print('}')
164
165
props.__annotations__ = annotations
166
167
# Create __defaults__
168
defaults = self.argnames.defaults
169
dkeys = Object.keys(self.argnames.defaults)
170
if dkeys.length:
171
172
def __defaults__():
173
output.print('{')
174
for i, k in enumerate(dkeys):
175
output.print(k + ':'), defaults[k].print(output)
176
if i is not dkeys.length - 1:
177
output.comma()
178
output.print('}')
179
180
props.__defaults__ = __defaults__
181
182
# Create __handles_kwarg_interpolation__
183
if not self.argnames.is_simple_func:
184
185
def handle():
186
output.print('true')
187
188
props.__handles_kwarg_interpolation__ = handle
189
190
# Create __argnames__
191
if self.argnames.length > (1 if strip_first else 0):
192
193
def argnames():
194
output.print('[')
195
for i, arg in enumerate(self.argnames):
196
if strip_first and i is 0:
197
continue
198
output.print(JSON.stringify(arg.name))
199
if i is not self.argnames.length - 1:
200
output.comma()
201
output.print(']')
202
203
props.__argnames__ = argnames
204
205
# Create __doc__
206
if output.options.keep_docstrings and self.docstrings and self.docstrings.length:
207
208
def doc():
209
output.print(JSON.stringify(create_doctring(self.docstrings)))
210
211
props.__doc__ = doc
212
213
def module():
214
output.print(module_name)
215
216
props.__module__ = module
217
218
for name in props:
219
output.print(f"{fname}.{name} = ")
220
props[name]() # calling this prints it out
221
output.end_statement()
222
223
output.print(
224
"undefined"
225
) # so defining function in repl doesn't print out last assignment above.
226
output.end_statement()
227
228
229
def function_definition(self, output, strip_first, as_expression):
230
as_expression = as_expression or self.is_expression or self.is_anonymous
231
if as_expression:
232
orig_indent = output.indentation()
233
output.set_indentation(output.next_indent())
234
output.spaced('(function()', '{'), output.newline()
235
output.indent(), output.spaced('var', anonfunc, '='), output.space()
236
output.print("function"), output.space()
237
if self.name:
238
self.name.print(output)
239
240
if self.is_generator:
241
output.print('()'), output.space()
242
243
def output_generator():
244
output.indent()
245
output.print('function* js_generator')
246
function_args(self.argnames, output, strip_first)
247
print_bracketed(self, output, True, function_preamble)
248
249
output.newline()
250
output.indent()
251
output.spaced('var', 'result', '=', 'js_generator.apply(this,',
252
'arguments)')
253
output.end_statement()
254
# Python's generator objects use a separate method to send data to the generator
255
output.indent()
256
output.spaced('result.send', '=', 'result.next')
257
output.end_statement()
258
output.indent()
259
output.spaced('return', 'result')
260
output.end_statement()
261
262
output.with_block(output_generator)
263
else:
264
function_args(self.argnames, output, strip_first)
265
print_bracketed(self, output, True, function_preamble)
266
267
if as_expression:
268
output.end_statement()
269
function_annotation(self, output, strip_first, anonfunc)
270
output.indent(), output.spaced('return',
271
anonfunc), output.end_statement()
272
output.set_indentation(orig_indent)
273
output.indent(), output.print("})()")
274
275
276
def print_function(output):
277
self = this
278
279
if self.decorators and self.decorators.length:
280
output.print("var")
281
output.space()
282
output.assign(self.name.name)
283
284
def output_function_definition():
285
function_definition(self, output, False, True)
286
287
decorate(self.decorators, output, output_function_definition)
288
output.end_statement()
289
else:
290
function_definition(self, output, False)
291
if not self.is_expression and not self.is_anonymous:
292
output.end_statement()
293
function_annotation(self, output, False)
294
295
296
def find_this(expression):
297
if is_node_type(expression, AST_Dot):
298
return expression.expression
299
if not is_node_type(expression, AST_SymbolRef):
300
return expression
301
302
303
def print_this(expression, output):
304
obj = find_this(expression)
305
if obj:
306
obj.print(output)
307
else:
308
output.print('this')
309
310
311
def print_function_call(self, output):
312
is_prototype_call = False
313
314
def print_function_name(no_call):
315
nonlocal is_prototype_call
316
if is_node_type(self, AST_ClassCall):
317
# class methods are called through the prototype unless static
318
if self.static:
319
self['class'].print(output)
320
output.print(".")
321
output.print(self.method)
322
else:
323
is_prototype_call = True
324
self['class'].print(output)
325
output.print(".prototype.")
326
output.print(self.method)
327
if not no_call:
328
output.print(".call")
329
else:
330
if not is_repeatable:
331
output.print('ρσ_expr_temp')
332
if is_node_type(self.expression, AST_Dot):
333
print_getattr(self.expression, output, True)
334
elif not is_new and is_node_type(self.expression, AST_SymbolRef):
335
# Easy special case where we can make the __call__
336
# operator work. We are not doing the general case yet,
337
# which is difficult because of this binding.
338
# (f?.__call__?.bind(f) ?? f)
339
# We will likely instead do the general case by making
340
# classes ES6 classes that are just plain callable.
341
output.print('(')
342
self.expression.print(output)
343
output.print("?.__call__?.bind(")
344
self.expression.print(output)
345
output.print(') ?? ')
346
self.expression.print(output)
347
output.print(')')
348
else:
349
self.expression.print(output)
350
351
def print_kwargs():
352
output.print('ρσ_desugar_kwargs(')
353
if has_kwarg_items:
354
for i, kwname in enumerate(self.args.kwarg_items):
355
if i > 0:
356
output.print(',')
357
output.space()
358
kwname.print(output)
359
if has_kwarg_formals:
360
output.print(',')
361
output.space()
362
363
if has_kwarg_formals:
364
output.print('{')
365
for i, pair in enumerate(self.args.kwargs):
366
if i: output.comma()
367
pair[0].print(output)
368
output.print(':')
369
output.space()
370
pair[1].print(output)
371
output.print('}')
372
output.print(')')
373
374
def print_new(apply):
375
output.print('ρσ_interpolate_kwargs_constructor.call(')
376
output.print('Object.create('), self.expression.print(
377
output), output.print('.prototype)')
378
output.comma()
379
output.print('true' if apply else 'false')
380
output.comma()
381
382
def do_print_this():
383
if not is_repeatable:
384
output.print('ρσ_expr_temp')
385
else:
386
print_this(self.expression, output)
387
output.comma()
388
389
def print_positional_args():
390
# basic arguments
391
i = 0
392
while i < self.args.length:
393
expr = self.args[i]
394
is_first = i is 0
395
if not is_first:
396
output.print('.concat(')
397
if expr.is_array:
398
expr.print(output)
399
i += 1
400
else:
401
output.print('[')
402
while i < self.args.length and not self.args[i].is_array:
403
self.args[i].print(output)
404
if i + 1 < self.args.length and not self.args[i +
405
1].is_array:
406
output.print(',')
407
output.space()
408
i += 1
409
output.print(']')
410
if not is_first:
411
output.print(')')
412
413
has_kwarg_items = self.args.kwarg_items and self.args.kwarg_items.length
414
has_kwarg_formals = self.args.kwargs and self.args.kwargs.length
415
has_kwargs = has_kwarg_items or has_kwarg_formals
416
is_new = is_node_type(self, AST_New)
417
is_repeatable = True
418
419
if is_new and not self.args.length and not has_kwargs and not self.args.starargs:
420
output.print('new'), output.space()
421
print_function_name()
422
return # new A is the same as new A() in javascript
423
424
if not has_kwargs and not self.args.starargs:
425
# A simple function call, do nothing special
426
def print_args():
427
for i, a in enumerate(self.args):
428
if i:
429
output.comma()
430
a.print(output)
431
432
if is_new:
433
output.print('new'), output.space()
434
print_function_name()
435
output.with_parens(print_args)
436
return
437
438
is_repeatable = is_new or not has_calls(self.expression)
439
if not is_repeatable:
440
output.assign('(ρσ_expr_temp'), print_this(self.expression,
441
output), output.comma()
442
443
if has_kwargs:
444
if is_new:
445
print_new(False)
446
else:
447
output.print('ρσ_interpolate_kwargs.call(')
448
do_print_this()
449
print_function_name(True)
450
output.comma()
451
else:
452
if is_new:
453
print_new(True)
454
print_function_name(True)
455
output.comma()
456
else:
457
print_function_name(True)
458
output.print('.apply(')
459
do_print_this()
460
461
if is_prototype_call and self.args.length > 1:
462
self.args.shift()
463
464
print_positional_args()
465
466
if has_kwargs:
467
if self.args.length:
468
output.print('.concat(')
469
output.print('[')
470
print_kwargs()
471
output.print(']')
472
if self.args.length:
473
output.print(')')
474
475
output.print(')')
476
if not is_repeatable:
477
output.print(')')
478
479