Path: blob/main/python/pylang/src/output/loops.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_BaseCall, AST_SymbolRef, AST_Array, AST_Unary, AST_Number, has_calls, AST_Seq, AST_ListComprehension, is_node_type5from output.stream import OutputStream678def unpack_tuple(elems, output, in_statement):9for i, elem in enumerate(elems):10output.indent()11output.assign(elem)12output.print("ρσ_unpack")13output.with_square(lambda: output.print(i))14if not in_statement or i < elems.length - 1:15output.semicolon()16output.newline()171819def print_do_loop(self, output):20output.print("do")21output.space()22self._do_print_body(output)23output.space()24output.print("while")25output.space()26output.with_parens(lambda: self.condition.print(output))27output.semicolon()282930def print_while_loop(self, output):31output.print("while")32output.space()33output.with_parens(lambda: self.condition.print(output))34output.space()35self._do_print_body(output)363738def is_simple_for_in(self):39# return true if this loop can be simplified into a basic for (i in j) loop40if (is_node_type(self.object, AST_BaseCall)41and is_node_type(self.object.expression, AST_SymbolRef)42and self.object.expression.name is "dir"43and self.object.args.length is 1):44return True45return False464748def is_simple_for(self):49# returns true if this loop can be simplified into a basic for(i=n;i<h;i++) loop50if (is_node_type(self.object, AST_BaseCall)51and is_node_type(self.object.expression, AST_SymbolRef)52and self.object.expression.name is "range"53and not (is_node_type(self.init, AST_Array))):54a = self.object.args55l = a.length56if l < 3 or (is_node_type(a[2], AST_Number) or57(is_node_type(a[2], AST_Unary) and a[2].operator is '-'58and is_node_type(a[2].expression, AST_Number))):59if (l is 1 and not has_calls(a[0])) or (l > 160and not has_calls(a[1])):61return True62return False636465def print_for_loop_body(output):66self = this6768def f_print_for_loop_body():69if not (self.simple_for_index or is_simple_for_in(self)):70# if we're using multiple iterators, unpack them71output.indent()72itervar = "ρσ_Index" + output.index_counter73if is_node_type(self.init, AST_Array):74flat = self.init.flatten()75output.assign("ρσ_unpack")76if flat.length > self.init.elements.length:77output.print('ρσ_flatten(' + itervar + ')')78else:79output.print(itervar)80output.end_statement()81unpack_tuple(flat, output)82else:83output.assign(self.init)84output.print(itervar)85output.end_statement()8687output.index_counter += 188if self.simple_for_index:89output.indent()90output.assign(self.init)91output.print(self.simple_for_index)92output.end_statement()9394for stmt in self.body.body:95output.indent()96stmt.print(output)97output.newline()9899output.with_block(f_print_for_loop_body)100101102def init_es6_itervar(output, itervar):103output.indent()104output.spaced(itervar, '=', '((typeof', itervar + '[Symbol.iterator]',105'===', '"function")', '?', '(' + itervar, 'instanceof',106'Map', '?', itervar + '.keys()', ':', itervar + ')', ':',107'Object.keys(' + itervar + '))')108output.end_statement()109110111def print_for_in(self, output):112def write_object():113if self.object.constructor is AST_Seq:114(AST_Array({'elements': self.object.to_array()})).print(output)115else:116self.object.print(output)117118if is_simple_for(self):119# optimize range() into a simple for loop120increment = None121args = self.object.args122tmp_ = args.length123if tmp_ is 1:124start = 0125end = args[0]126elif tmp_ is 2:127start = args[0]128end = args[1]129elif tmp_ is 3:130start = args[0]131end = args[1]132increment = args[2]133134self.simple_for_index = idx = 'ρσ_Index' + output.index_counter135output.index_counter += 1136output.print("for")137output.space()138139def f_simple_for():140output.spaced('var', idx, '='), output.space()141start.print(output) if start.print else output.print(start)142output.semicolon()143output.space()144output.print(idx)145output.space()146output.print(">") if is_node_type(increment,147AST_Unary) else output.print("<")148output.space()149end.print(output)150output.semicolon()151output.space()152output.print(idx)153if increment and (not (is_node_type(increment, AST_Unary))154or increment.expression.value is not "1"):155if is_node_type(increment, AST_Unary):156output.print("-=")157increment.expression.print(output)158else:159output.print("+=")160increment.print(output)161else:162if is_node_type(increment, AST_Unary):163output.print("--")164else:165output.print("++")166167output.with_parens(f_simple_for)168169elif is_simple_for_in(self):170# optimize dir() into a simple for in loop171output.print("for")172output.space()173174def f_simple_for_in():175self.init.print(output)176output.space()177output.print('in')178output.space()179self.object.args[0].print(output)180181output.with_parens(f_simple_for_in)182else:183# regular loop184itervar = "ρσ_Iter" + output.index_counter185output.assign("var " + itervar)186write_object()187output.end_statement()188init_es6_itervar(output, itervar)189output.indent()190output.spaced('for', '(var', 'ρσ_Index' + output.index_counter, 'of',191itervar + ')')192193output.space()194self._do_print_body(output)195196197def print_list_comprehension(self, output):198tname = self.constructor.name.slice(4)199result_obj = {200'ListComprehension': '[]',201'DictComprehension':202('Object.create(null)' if self.is_jshash else '{}'),203'SetComprehension': 'ρσ_set()'204}[tname]205is_generator = tname is 'GeneratorComprehension'206add_to_result = None207if tname is 'DictComprehension':208if self.is_pydict:209result_obj = 'ρσ_dict()'210211def add_to_result0(output):212output.indent()213output.print('ρσ_Result.set')214215def f_dict():216self.statement.print(output)217output.space()218output.print(',')219output.space()220221def f_dict0():222if self.value_statement.constructor is AST_Seq:223output.with_square(224lambda: self.value_statement.print(output))225else:226self.value_statement.print(output)227228output.with_parens(f_dict0)229230output.with_parens(f_dict)231output.end_statement()232233add_to_result = add_to_result0234235else:236237def add_to_result0(output):238output.indent()239output.print('ρσ_Result')240output.with_square(lambda: self.statement.print(output))241output.space(), output.print('='), output.space()242243def f_result():244if self.value_statement.constructor is AST_Seq:245output.with_square(246lambda: self.value_statement.print(output))247else:248self.value_statement.print(output)249250output.with_parens(f_result)251output.end_statement()252253add_to_result = add_to_result0254else:255push_func = "ρσ_Result." + (256'push' if self.constructor is AST_ListComprehension else 'add')257if is_generator:258push_func = 'yield '259260def add_to_result0(output):261output.indent()262output.print(push_func)263264def f_output_statement():265if self.statement.constructor is AST_Seq:266output.with_square(lambda: self.statement.print(output))267else:268self.statement.print(output)269270output.with_parens(f_output_statement)271output.end_statement()272273add_to_result = add_to_result0274275def f_body():276output.print("function")277output.print("()")278output.space()279280def f_body0():281body_out = output282if is_generator:283body_out.indent()284body_out.print('function* js_generator()'), body_out.space(285), body_out.print('{')286body_out.newline()287previous_indentation = output.indentation()288output.set_indentation(output.next_indent())289body_out.indent()290body_out.assign("var ρσ_Iter")291self.object.print(body_out)292293if result_obj:294body_out.comma()295body_out.assign("ρσ_Result")296body_out.print(result_obj)297# make sure to locally scope loop variables298if is_node_type(self.init, AST_Array):299for i in self.init.elements:300body_out.comma()301i.print(body_out)302else:303body_out.comma()304self.init.print(body_out)305body_out.end_statement()306307init_es6_itervar(body_out, 'ρσ_Iter')308body_out.indent()309body_out.print("for")310body_out.space()311body_out.with_parens(312lambda: body_out.spaced('var', 'ρσ_Index', 'of', 'ρσ_Iter'))313body_out.space()314315def f_body_out():316body_out.indent()317itervar = 'ρσ_Index'318if is_node_type(self.init, AST_Array):319flat = self.init.flatten()320body_out.assign("ρσ_unpack")321if flat.length > self.init.elements.length:322body_out.print('ρσ_flatten(' + itervar + ')')323else:324body_out.print(itervar)325body_out.end_statement()326unpack_tuple(flat, body_out)327else:328body_out.assign(self.init)329body_out.print(itervar)330body_out.end_statement()331332if self.condition:333body_out.indent()334body_out.print("if")335body_out.space()336body_out.with_parens(337lambda: self.condition.print(body_out))338body_out.space()339body_out.with_block(lambda: add_to_result(body_out))340body_out.newline()341else:342add_to_result(body_out)343344body_out.with_block(f_body_out)345body_out.newline()346if self.constructor is AST_ListComprehension:347body_out.indent()348body_out.spaced('ρσ_Result', '=',349'ρσ_list_constructor(ρσ_Result)')350body_out.end_statement()351if not is_generator:352body_out.indent()353body_out.print("return ρσ_Result")354body_out.end_statement()355if is_generator:356output.set_indentation(previous_indentation)357body_out.newline(), body_out.indent(), body_out.print(358'}') # end js_generator359output.newline(), output.indent()360output.spaced('var', 'result', '=', 'js_generator.call(this)')361output.end_statement()362# Python's generator objects use a separate method to send data to the generator363output.indent()364output.spaced('result.send', '=', 'result.next')365output.end_statement()366output.indent()367output.spaced('return', 'result')368output.end_statement()369370output.with_block(f_body0)371372output.with_parens(f_body)373output.print("()")374375376def print_ellipses_range(self, output):377output.print("ρσ_range(")378self.first.print(output)379output.print(",ρσ_operator_add(")380self.last.print(output)381output.print(",1))")382383384