Path: blob/main/python/pylang/src/output/codegen.py
1398 views
# vim:fileencoding=utf-81# License: BSD Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>2from __python__ import hash_literals34# globals:console,writefile56from utils import noop7from parse import PRECEDENCE8from ast_types import (9AST_Array, AST_Assign, AST_BaseCall, AST_Binary, AST_BlockStatement,10AST_Break, AST_Class, AST_Conditional, AST_Constant, AST_Continue,11AST_Debugger, AST_Definitions, AST_Directive, AST_Do, AST_Dot,12is_node_type, AST_EllipsesRange, AST_EmptyStatement, AST_Exit,13AST_ExpressiveObject, AST_ForIn, AST_ForJS, AST_Function, AST_Hole, AST_If,14AST_Imports, AST_Infinity, AST_Lambda, AST_ListComprehension,15AST_LoopControl, AST_NaN, AST_New, AST_Node, AST_Number, AST_Object,16AST_ObjectKeyVal, AST_ObjectProperty, AST_PropAccess, AST_RegExp,17AST_Return, AST_Set, AST_Seq, AST_SimpleStatement, AST_Splice,18AST_Statement, AST_StatementWithBody, AST_String, AST_Sub, AST_ItemAccess,19AST_Symbol, AST_This, AST_Throw, AST_Toplevel, AST_Try, AST_Unary,20AST_UnaryPrefix, AST_Undefined, AST_Var, AST_VarDef, AST_Assert,21AST_Verbatim, AST_While, AST_With, AST_Yield, TreeWalker, AST_Existential)22from output.exceptions import print_try23from output.classes import print_class24from output.literals import print_array, print_obj_literal, print_object, print_set, print_regexp25from output.loops import print_do_loop, print_while_loop, print_for_loop_body, print_for_in, print_list_comprehension, print_ellipses_range26from output.modules import print_top_level, print_imports27from output.comments import print_comments28from output.operators import (print_getattr, print_getitem, print_rich_getitem,29print_splice_assignment, print_unary_prefix,30print_binary_op, print_assign, print_conditional,31print_seq, print_existential)32from output.functions import print_function, print_function_call33from output.statements import print_bracketed, first_in_statement, force_statement, print_with, print_assert34from output.utils import make_block, make_num353637# -----[ code generators ]-----38def generate_code():39# -----[ utils ]-----40def DEFPRINT(nodetype, generator):41nodetype.prototype._codegen = generator4243def f_print_generate(stream, force_parens):44self = this45generator = self._codegen46stream.push_node(self)47if force_parens or self.needs_parens(stream):48stream.with_parens(f_comments_then_generator)4950def f_comments_then_generator():51self.add_comments(stream)52generator(self, stream)53else:54self.add_comments(stream)55generator(self, stream)5657stream.pop_node()5859AST_Node.prototype.print = f_print_generate6061# -----[ comments ]-----62def add_comments(output):63if not is_node_type(this, AST_Toplevel):64print_comments(this, output)6566AST_Node.prototype.add_comments = add_comments6768# -----[ PARENTHESES ]-----69def PARENS(nodetype, func):70nodetype.prototype.needs_parens = func7172PARENS(AST_Node, lambda: False)73# a function expression needs parens around it when it's provably74# the first token to appear in a statement.75PARENS(AST_Function, first_in_statement)76# same goes for an object literal, because otherwise it would be77# interpreted as a block of code.78PARENS(AST_Object, first_in_statement)7980def f_unary(output):81p = output.parent()82return is_node_type(p, AST_PropAccess) and p.expression is this8384PARENS(AST_Unary, f_unary)8586def f_seq(output):87p = output.parent()88return is_node_type(p, AST_Unary) or is_node_type(89p, AST_VarDef) or is_node_type(p, AST_Dot) or is_node_type(90p, AST_ObjectProperty) or is_node_type(p, AST_Conditional)9192PARENS(AST_Seq, f_seq)9394def f_binary(output):95p = output.parent()96# (foo && bar)()97if is_node_type(p, AST_BaseCall) and p.expression is this:98return True99100# typeof (foo && bar)101if is_node_type(p, AST_Unary):102return True103104# (foo && bar)["prop"], (foo && bar).prop105if is_node_type(p, AST_PropAccess) and p.expression is this:106return True107108# this deals with precedence: 3 * (2 + 1)109if is_node_type(p, AST_Binary):110po = p.operator111pp = PRECEDENCE[po]112so = this.operator113sp = PRECEDENCE[so]114if pp > sp or pp is sp and this is p.right and not (115so is po and (so is "*" or so is "&&" or so is "||")):116return True117118PARENS(AST_Binary, f_binary)119120def f_prop_access(output):121p = output.parent()122if is_node_type(p, AST_New) and p.expression is this:123# i.e. new (foo.bar().baz)124#125# if there's one call into this subtree, then we need126# parens around it too, otherwise the call will be127# interpreted as passing the arguments to the upper New128# expression.129try:130131def error_on_base_call(node):132if is_node_type(node, AST_BaseCall):133raise p134135this.walk(TreeWalker(error_on_base_call))136except:137return True138139PARENS(AST_PropAccess, f_prop_access)140141def f_base_call(output):142p = output.parent()143return is_node_type(p, AST_New) and p.expression is this144145PARENS(AST_BaseCall, f_base_call)146147def f_new(output):148p = output.parent()149if this.args.length is 0 and (is_node_type(p, AST_PropAccess)150or is_node_type(p, AST_BaseCall)151and p.expression is this):152# (new foo)(bar)153return True154155PARENS(AST_New, f_new)156157def f_number(output):158p = output.parent()159if this.value < 0 and is_node_type(160p, AST_PropAccess) and p.expression is this:161return True162163PARENS(AST_Number, f_number)164165def f_nan(output):166p = output.parent()167if is_node_type(p, AST_PropAccess) and p.expression is this:168return True169170PARENS(AST_NaN, f_nan)171172def assign_and_conditional_paren_rules(output):173p = output.parent()174# !(a = false) → true175if is_node_type(p, AST_Unary):176return True177178# 1 + (a = 2) + 3 → 6, side effect setting a = 2179if is_node_type(p, AST_Binary) and not (is_node_type(p, AST_Assign)):180return True181182# (a = func)() —or— new (a = Object)()183if is_node_type(p, AST_BaseCall) and p.expression is this:184return True185186# bar if a = foo else baz187if is_node_type(p, AST_Conditional) and p.condition is this:188return True189190# (a = foo)["prop"] —or— (a = foo).prop191if is_node_type(p, AST_PropAccess) and p.expression is this:192return True193194PARENS(AST_Assign, assign_and_conditional_paren_rules)195PARENS(AST_Conditional, assign_and_conditional_paren_rules)196197# -----[ PRINTERS ]-----198def f_directive(self, output):199output.print_string(self.value)200output.semicolon()201202DEFPRINT(AST_Directive, f_directive)203204def f_debugger(self, output):205output.print("debugger")206output.semicolon()207208DEFPRINT(AST_Debugger, f_debugger)209210AST_StatementWithBody.prototype._do_print_body = lambda output: force_statement(211this.body, output)212213def f_statement(self, output):214self.body.print(output)215output.semicolon()216217DEFPRINT(AST_Statement, f_statement)218DEFPRINT(AST_Toplevel, print_top_level)219220DEFPRINT(AST_Imports, print_imports)221222def f_simple_statement(self, output):223if not (is_node_type(self.body, AST_EmptyStatement)):224self.body.print(output)225output.semicolon()226227DEFPRINT(AST_SimpleStatement, f_simple_statement)228DEFPRINT(AST_BlockStatement,229lambda self, output: print_bracketed(self, output))230231DEFPRINT(AST_EmptyStatement, lambda self, output: None)232233DEFPRINT(AST_Do, print_do_loop)234235DEFPRINT(AST_While, print_while_loop)236237AST_ForIn.prototype._do_print_body = print_for_loop_body238239DEFPRINT(AST_ForIn, print_for_in)240241def f_do_print_body(output):242self = this243244def f_print_stmt():245for stmt in self.body.body:246output.indent()247stmt.print(output)248output.newline()249250output.with_block(f_print_stmt)251252AST_ForJS.prototype._do_print_body = f_do_print_body253254def f_for_js(self, output):255output.print("for")256output.space()257output.with_parens(lambda: self.condition.print(output))258output.space()259self._do_print_body(output)260261DEFPRINT(AST_ForJS, f_for_js)262263DEFPRINT(AST_ListComprehension, print_list_comprehension)264265DEFPRINT(AST_EllipsesRange, print_ellipses_range)266267DEFPRINT(AST_With, print_with)268269DEFPRINT(AST_Assert, print_assert)270271AST_Lambda.prototype._do_print = print_function272273DEFPRINT(AST_Lambda, lambda self, output: self._do_print(output))274AST_Class.prototype._do_print = print_class275DEFPRINT(AST_Class, lambda self, output: self._do_print(output))276277# -----[ exits ]-----278def f_do_print_exit(output, kind):279self = this280output.print(kind)281if self.value:282output.space()283self.value.print(output)284285output.semicolon()286287AST_Exit.prototype._do_print = f_do_print_exit288289DEFPRINT(290AST_Yield, lambda self, output: self._do_print(291output, "yield" + ('*' if self.is_yield_from else '')))292DEFPRINT(AST_Return, lambda self, output: self._do_print(output, "return"))293DEFPRINT(AST_Throw, lambda self, output: self._do_print(output, "throw"))294295# -----[ loop control ]-----296def f_do_print_loop(output, kind):297output.print(kind)298if this.label:299output.space()300this.label.print(output)301302output.semicolon()303304AST_LoopControl.prototype._do_print = f_do_print_loop305306DEFPRINT(AST_Break, lambda self, output: self._do_print(output, "break"))307308DEFPRINT(AST_Continue,309lambda self, output: self._do_print(output, "continue"))310311# -----[ if ]-----312def make_then(self, output):313if output.options.bracketize:314make_block(self.body, output)315return316317# The squeezer replaces "block"-s that contain only a single318# statement with the statement itself; technically, the AST319# is correct, but this can create problems when we output an320# IF having an ELSE clause where the THEN clause ends in an321# IF *without* an ELSE block (then the outer ELSE would refer322# to the inner IF). This function checks for this case and323# adds the block brackets if needed.324if not self.body:325return output.force_semicolon()326327if is_node_type(self.body, AST_Do) and output.options.ie_proof:328# https://github.com/mishoo/RapydScript/issues/#issue/57 IE329# croaks with "syntax error" on code like this: if (foo)330# do ... while(cond); else ... we need block brackets331# around do/while332make_block(self.body, output)333return334335b = self.body336while True:337if is_node_type(b, AST_If):338if not b.alternative:339make_block(self.body, output)340return341342b = b.alternative343elif is_node_type(b, AST_StatementWithBody):344b = b.body345else:346break347348force_statement(self.body, output)349350def f_if(self, output):351output.print("if")352output.space()353output.with_parens(lambda: self.condition.print(output))354output.space()355if self.alternative:356make_then(self, output)357output.space()358output.print("else")359output.space()360force_statement(self.alternative, output)361else:362self._do_print_body(output)363364DEFPRINT(AST_If, f_if)365366# -----[ exceptions ]-----367DEFPRINT(AST_Try, print_try)368369# -----[ var/const ]-----370def f_do_print_definition(output, kind):371output.print(kind)372output.space()373for i, def_ in enumerate(this.definitions):374if i:375output.comma()376def_.print(output)377p = output.parent()378in_for = is_node_type(p, AST_ForIn)379avoid_semicolon = in_for and p.init is this380if not avoid_semicolon:381output.semicolon()382383AST_Definitions.prototype._do_print = f_do_print_definition384385DEFPRINT(AST_Var, lambda self, output: self._do_print(output, "var"))386387def parenthesize_for_noin(node, output, noin):388if not noin:389node.print(output)390else:391try:392# need to take some precautions here:393# https://github.com/mishoo/RapydScript2/issues/60394def f_for_noin(node):395if is_node_type(node,396AST_Binary) and node.operator is "in":397raise output398399node.walk(TreeWalker(f_for_noin))400node.print(output)401except:402node.print(output, True)403404def f_print_var_def(self, output):405self.name.print(output)406if self.value:407output.assign("")408# output.space()409# output.print("=")410# output.space()411p = output.parent(1)412noin = is_node_type(p, AST_ForIn)413parenthesize_for_noin(self.value, output, noin)414415DEFPRINT(AST_VarDef, f_print_var_def)416417# -----[ other expressions ]-----418DEFPRINT(AST_BaseCall, print_function_call)419420AST_Seq.prototype._do_print = print_seq421422DEFPRINT(AST_Seq, lambda self, output: self._do_print(output))423424DEFPRINT(AST_Dot, print_getattr)425426DEFPRINT(AST_Sub, print_getitem)427428DEFPRINT(AST_ItemAccess, print_rich_getitem)429430DEFPRINT(AST_Splice, print_splice_assignment)431432DEFPRINT(AST_UnaryPrefix, print_unary_prefix)433434DEFPRINT(AST_Binary, print_binary_op)435436DEFPRINT(AST_Existential, print_existential)437438DEFPRINT(AST_Assign, print_assign)439440DEFPRINT(AST_Conditional, print_conditional)441442# -----[ literals ]-----443DEFPRINT(AST_Array, print_array)444445DEFPRINT(AST_ExpressiveObject, print_obj_literal)446447DEFPRINT(AST_Object, print_object)448449DEFPRINT(AST_ObjectKeyVal, f_print_obj_key_val)450451def f_print_obj_key_val(self, output):452self.key.print(output)453output.colon()454self.value.print(output)455456DEFPRINT(AST_Set, print_set)457458AST_Symbol.prototype.definition = lambda: this.thedef459460DEFPRINT(AST_Symbol, f_print_symbol)461462def f_print_symbol(self, output):463def_ = self.definition()464output.print_name((465def_.mangled_name or def_.name) if def_ else self.name)466467DEFPRINT(AST_Undefined, lambda self, output: output.print("void 0"))468DEFPRINT(AST_Hole, noop)469470DEFPRINT(AST_Infinity, lambda self, output: output.print("1/0"))471DEFPRINT(AST_NaN, lambda self, output: output.print("0/0"))472DEFPRINT(AST_This, lambda self, output: output.print("this"))473DEFPRINT(AST_Constant, lambda self, output: output.print(self.value))474DEFPRINT(AST_String, lambda self, output: output.print_string(self.value))475DEFPRINT(AST_Verbatim, lambda self, output: output.print(self.value))476DEFPRINT(AST_Number,477lambda self, output: output.print(make_num(self.value)))478DEFPRINT(AST_RegExp, print_regexp)479480481