Path: blob/main/python/pylang/src/output/functions.py
1398 views
# vim:fileencoding=utf-81# License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>2from __python__ import hash_literals34from ast_types import AST_ClassCall, AST_New, has_calls, AST_Dot, AST_PropAccess, AST_SymbolRef, is_node_type5from output.stream import OutputStream6from output.statements import print_bracketed7from output.utils import create_doctring8from output.operators import print_getattr910anonfunc = 'ρσ_anonfunc'11module_name = 'null'121314def set_module_name(x):15nonlocal module_name16module_name = '"' + x + '"' if x else 'null'171819def decorate(decorators, output, func):20pos = 02122def wrap():23nonlocal pos24if pos < decorators.length:25decorators[pos].expression.print(output)26pos += 127output.with_parens(wrap)28else:29func()3031wrap()323334def function_args(argnames, output, strip_first):35def f():36if argnames and argnames.length and (37argnames.is_simple_func is True38or argnames.is_simple_func is undefined):39for i, arg in enumerate(40(argnames.slice(1) if strip_first else argnames)):41if i:42output.comma()43arg.print(output)4445output.with_parens(f)46output.space()474849def function_preamble(node, output, offset):50a = node.argnames51if not a or a.is_simple_func:52return53# If this function has optional parameters/*args/**kwargs declare it differently54fname = node.name.name if node.name else anonfunc55kw = 'arguments[arguments.length-1]'56# Define all formal parameters57for c, arg in enumerate(a):58i = c - offset59if i >= 0:60output.indent()61output.print("var")62output.space()63output.assign(arg)64if Object.prototype.hasOwnProperty.call(a.defaults, arg.name):65output.spaced('(arguments[' + i + ']', '===', 'undefined',66'||', '(', i, '===', 'arguments.length-1', '&&',67kw, '!==', 'null', '&&', 'typeof', kw, '===',68'"object"', '&&', kw, '[ρσ_kwargs_symbol]',69'===', 'true))', '?', '')70output.print(fname + '.__defaults__.'), arg.print(output)71output.space(), output.print(':'), output.space()72else:73output.spaced('(', i, '===', 'arguments.length-1', '&&', kw,74'!==', 'null', '&&', 'typeof', kw, '===',75'"object"', '&&', kw, '[ρσ_kwargs_symbol]',76'===', 'true)', '?', 'undefined', ':', '')77output.print('arguments[' + i + ']')78output.end_statement()79if a.kwargs or a.has_defaults:80# Look for an options object81kw = a.kwargs.name if a.kwargs else 'ρσ_kwargs_obj'82output.indent()83output.spaced('var', kw, '=', 'arguments[arguments.length-1]')84output.end_statement()85# Ensure kwargs is the options object86output.indent()87output.spaced('if', '(' + kw, '===', 'null', '||', 'typeof', kw, '!==',88'"object"', '||', kw, '[ρσ_kwargs_symbol]', '!==',89'true)', kw, '=', '{}')90output.end_statement()91# Read values from the kwargs object for any formal parameters92if a.has_defaults:93for dname in Object.keys(a.defaults):94output.indent()95output.spaced(96'if', '(Object.prototype.hasOwnProperty.call(' + kw + ',',97'"' + dname + '"))')9899def f():100output.indent()101output.spaced(dname, '=', kw + '.' + dname)102output.end_statement()103if a.kwargs:104output.indent()105output.spaced('delete', kw + '.' + dname)106output.end_statement()107108output.with_block(f)109output.newline()110111if a.starargs is not undefined:112# Define the *args parameter, putting in whatever is left after assigning the formal parameters and the options object113nargs = a.length - offset114output.indent()115output.spaced('var', a.starargs.name, '=',116'Array.prototype.slice.call(arguments,', nargs + ')')117output.end_statement()118# Remove the options object, if present119output.indent()120output.spaced('if', '(' + kw, '!==', 'null', '&&', 'typeof', kw, '===',121'"object"', '&&', kw, '[ρσ_kwargs_symbol]', '===',122'true)', a.starargs.name)123output.print('.pop()')124output.end_statement()125126127def has_annotations(self):128if self.return_annotation:129return True130for arg in self.argnames:131if arg.annotation:132return True133return False134135136def function_annotation(self, output, strip_first, name):137fname = name or (self.name.name if self.name else anonfunc)138props = Object.create(None)139140# Create __annotations__141# TODO: These are completely disabled, since to really using142# them the typings module has to be properly implemented...143# otherwise trying to use them with a mocked typings module144# doesn't work at all. For now we just ignore this data145# and mock typings, so you can fully typecheck code with mypy146# while still *running* it with pylang.147if self.annotations and has_annotations(self):148149def annotations():150output.print('{')151if self.argnames and self.argnames.length:152for i, arg in enumerate(self.argnames):153if arg.annotation:154arg.print(output)155output.print(':'), output.space()156arg.annotation.print(output)157if i < self.argnames.length - 1 or self.return_annotation:158output.comma()159if self.return_annotation:160output.print('return:'), output.space()161self.return_annotation.print(output)162output.print('}')163164props.__annotations__ = annotations165166# Create __defaults__167defaults = self.argnames.defaults168dkeys = Object.keys(self.argnames.defaults)169if dkeys.length:170171def __defaults__():172output.print('{')173for i, k in enumerate(dkeys):174output.print(k + ':'), defaults[k].print(output)175if i is not dkeys.length - 1:176output.comma()177output.print('}')178179props.__defaults__ = __defaults__180181# Create __handles_kwarg_interpolation__182if not self.argnames.is_simple_func:183184def handle():185output.print('true')186187props.__handles_kwarg_interpolation__ = handle188189# Create __argnames__190if self.argnames.length > (1 if strip_first else 0):191192def argnames():193output.print('[')194for i, arg in enumerate(self.argnames):195if strip_first and i is 0:196continue197output.print(JSON.stringify(arg.name))198if i is not self.argnames.length - 1:199output.comma()200output.print(']')201202props.__argnames__ = argnames203204# Create __doc__205if output.options.keep_docstrings and self.docstrings and self.docstrings.length:206207def doc():208output.print(JSON.stringify(create_doctring(self.docstrings)))209210props.__doc__ = doc211212def module():213output.print(module_name)214215props.__module__ = module216217for name in props:218output.print(f"{fname}.{name} = ")219props[name]() # calling this prints it out220output.end_statement()221222output.print(223"undefined"224) # so defining function in repl doesn't print out last assignment above.225output.end_statement()226227228def function_definition(self, output, strip_first, as_expression):229as_expression = as_expression or self.is_expression or self.is_anonymous230if as_expression:231orig_indent = output.indentation()232output.set_indentation(output.next_indent())233output.spaced('(function()', '{'), output.newline()234output.indent(), output.spaced('var', anonfunc, '='), output.space()235output.print("function"), output.space()236if self.name:237self.name.print(output)238239if self.is_generator:240output.print('()'), output.space()241242def output_generator():243output.indent()244output.print('function* js_generator')245function_args(self.argnames, output, strip_first)246print_bracketed(self, output, True, function_preamble)247248output.newline()249output.indent()250output.spaced('var', 'result', '=', 'js_generator.apply(this,',251'arguments)')252output.end_statement()253# Python's generator objects use a separate method to send data to the generator254output.indent()255output.spaced('result.send', '=', 'result.next')256output.end_statement()257output.indent()258output.spaced('return', 'result')259output.end_statement()260261output.with_block(output_generator)262else:263function_args(self.argnames, output, strip_first)264print_bracketed(self, output, True, function_preamble)265266if as_expression:267output.end_statement()268function_annotation(self, output, strip_first, anonfunc)269output.indent(), output.spaced('return',270anonfunc), output.end_statement()271output.set_indentation(orig_indent)272output.indent(), output.print("})()")273274275def print_function(output):276self = this277278if self.decorators and self.decorators.length:279output.print("var")280output.space()281output.assign(self.name.name)282283def output_function_definition():284function_definition(self, output, False, True)285286decorate(self.decorators, output, output_function_definition)287output.end_statement()288else:289function_definition(self, output, False)290if not self.is_expression and not self.is_anonymous:291output.end_statement()292function_annotation(self, output, False)293294295def find_this(expression):296if is_node_type(expression, AST_Dot):297return expression.expression298if not is_node_type(expression, AST_SymbolRef):299return expression300301302def print_this(expression, output):303obj = find_this(expression)304if obj:305obj.print(output)306else:307output.print('this')308309310def print_function_call(self, output):311is_prototype_call = False312313def print_function_name(no_call):314nonlocal is_prototype_call315if is_node_type(self, AST_ClassCall):316# class methods are called through the prototype unless static317if self.static:318self['class'].print(output)319output.print(".")320output.print(self.method)321else:322is_prototype_call = True323self['class'].print(output)324output.print(".prototype.")325output.print(self.method)326if not no_call:327output.print(".call")328else:329if not is_repeatable:330output.print('ρσ_expr_temp')331if is_node_type(self.expression, AST_Dot):332print_getattr(self.expression, output, True)333elif not is_new and is_node_type(self.expression, AST_SymbolRef):334# Easy special case where we can make the __call__335# operator work. We are not doing the general case yet,336# which is difficult because of this binding.337# (f?.__call__?.bind(f) ?? f)338# We will likely instead do the general case by making339# classes ES6 classes that are just plain callable.340output.print('(')341self.expression.print(output)342output.print("?.__call__?.bind(")343self.expression.print(output)344output.print(') ?? ')345self.expression.print(output)346output.print(')')347else:348self.expression.print(output)349350def print_kwargs():351output.print('ρσ_desugar_kwargs(')352if has_kwarg_items:353for i, kwname in enumerate(self.args.kwarg_items):354if i > 0:355output.print(',')356output.space()357kwname.print(output)358if has_kwarg_formals:359output.print(',')360output.space()361362if has_kwarg_formals:363output.print('{')364for i, pair in enumerate(self.args.kwargs):365if i: output.comma()366pair[0].print(output)367output.print(':')368output.space()369pair[1].print(output)370output.print('}')371output.print(')')372373def print_new(apply):374output.print('ρσ_interpolate_kwargs_constructor.call(')375output.print('Object.create('), self.expression.print(376output), output.print('.prototype)')377output.comma()378output.print('true' if apply else 'false')379output.comma()380381def do_print_this():382if not is_repeatable:383output.print('ρσ_expr_temp')384else:385print_this(self.expression, output)386output.comma()387388def print_positional_args():389# basic arguments390i = 0391while i < self.args.length:392expr = self.args[i]393is_first = i is 0394if not is_first:395output.print('.concat(')396if expr.is_array:397expr.print(output)398i += 1399else:400output.print('[')401while i < self.args.length and not self.args[i].is_array:402self.args[i].print(output)403if i + 1 < self.args.length and not self.args[i +4041].is_array:405output.print(',')406output.space()407i += 1408output.print(']')409if not is_first:410output.print(')')411412has_kwarg_items = self.args.kwarg_items and self.args.kwarg_items.length413has_kwarg_formals = self.args.kwargs and self.args.kwargs.length414has_kwargs = has_kwarg_items or has_kwarg_formals415is_new = is_node_type(self, AST_New)416is_repeatable = True417418if is_new and not self.args.length and not has_kwargs and not self.args.starargs:419output.print('new'), output.space()420print_function_name()421return # new A is the same as new A() in javascript422423if not has_kwargs and not self.args.starargs:424# A simple function call, do nothing special425def print_args():426for i, a in enumerate(self.args):427if i:428output.comma()429a.print(output)430431if is_new:432output.print('new'), output.space()433print_function_name()434output.with_parens(print_args)435return436437is_repeatable = is_new or not has_calls(self.expression)438if not is_repeatable:439output.assign('(ρσ_expr_temp'), print_this(self.expression,440output), output.comma()441442if has_kwargs:443if is_new:444print_new(False)445else:446output.print('ρσ_interpolate_kwargs.call(')447do_print_this()448print_function_name(True)449output.comma()450else:451if is_new:452print_new(True)453print_function_name(True)454output.comma()455else:456print_function_name(True)457output.print('.apply(')458do_print_this()459460if is_prototype_call and self.args.length > 1:461self.args.shift()462463print_positional_args()464465if has_kwargs:466if self.args.length:467output.print('.concat(')468output.print('[')469print_kwargs()470output.print(']')471if self.args.length:472output.print(')')473474output.print(')')475if not is_repeatable:476output.print(')')477478479