def exportvisit(n): if not at_root(n): typespace.error( "Export statements cannot be within functions or classes", n) pi = n.parent.index(n) n.parent.remove(n) for n2 in n[:]: n.remove(n2) n.parent.insert(pi, n2) pi += 1 if not n.is_default: n2 = js_parse( """ $s = _es6_module.add_export('$s', $s); """, [n.name, n.name, n.name]) else: n2 = js_parse( """ $s = _es6_module.set_default_export('$s', $s); """, [n.name, n.name, n.name]) n.parent.insert(pi, n2)
def visit(n): if not at_root(n): typespace.error("Import statements cannot be within functions or classes", n) modname = n[0].val depends.add(modname) if len(n) == 1: #import module name n.parent.replace(n, js_parse(""" es6_import(_es6_module, '$s'); """, [modname])); else: slist = StatementList() n.parent.replace(n, slist) for n2 in n[1:]: if n2.name == "*": n3 = js_parse(""" var $s = es6_import(_es6_module, '$s'); """, [n2.bindname, modname]); slist.add(n3); else: n3 = js_parse(""" var $s = es6_import_item(_es6_module, '$s', '$s'); """, [n2.bindname, modname, n2.name]); slist.add(n3)
def frames_validate(frames): def gen_frame_validate(frames, tlevel=0): s = "" tstr = tab(tlevel+1) tstr2 = tab(tlevel+2) for f in frames: if type(f) == Frame: if f.node != None: cs = f.node.children f.node.children = f.node.children[:node._startcur] f.node.add(ExprNode([])) c = f.node.gen_js(tlevel+1).split("\n")[0].replace("{", "").replace("\n", "").replace("}", "").strip() if c.endswith(";"): c = c[:-1] s += tstr + c + " {\n" f.node.children = cs s += gen_frame_validate(f, tlevel+1) if f.node != None: s += tstr + "}\n" else: c = tstr + f.gen_js(tlevel+2) s += c if c.strip().endswith("}") == 0 and c.strip().endswith(";") == 0: s += ";" s += "\n" if tlevel == 0: c = node.gen_js(0).split("\n")[0] + "\n" s = c + s + "}\n" return s #print(node.gen_js(0)) #print(scope) #print_frames(frames) s = gen_frame_validate(frames) s2 = js_parse(s).gen_js(0).strip() s = node.gen_js(0).strip() s = js_parse(s, print_stack=False).gen_js(0).strip() print(s==s2) if s != s2: import difflib print(dir(difflib)) d = difflib.ndiff(s.split("\n"), s2.split("\n")) ds = "" for l in d: ds += l + "\n" #print(ds) line_print(s) line_print(s2)
def var_transform(self, cls, var, member): # print(cls, var, member) if type(member) != str: member = member.val p = cls.childmap[member] if type(p) == VarDeclNode: arr = -1 t = p.type while type(t) == StaticArrNode: t = t[0] if type(t) == BuiltinTypeNode: if t.type == "int": arr = INT_ARR elif t.type == "float": arr = FLOAT_ARR elif t.type == "byte": arr = BYTE_ARR if arr == -1: self.typespace.error("internal error", var) off = p.start ret = js_parse(var.gen_js(0) + "[%d][%d];" % (arr, off), start_node=ArrayRefNode) ret.type = p return ret elif type(p) in [MethodGetter, MethodSetter]: # see if we have a getter if member in cls.getters: p = cls.getters[member] cls = p.parent name = cls.name + "_get__" + p.name ret = js_parse(name + "($n)", [var], start_node=FuncCallNode) ret.src = p return ret else: cls = p.parent name = cls.name + "_set__" + p.name ret = js_parse(name + "($n)", [var], start_node=FuncCallNode) ret.src = p return ret return IdentNode("[implement me]")
def refactor(data): result = js_parse(data) if result == None: result = StatementList() """ def set_smap(n, smap): n.smap = smap for c in n: set_smap(c, smap) smap = SourceMap() set_smap(result, smap) result.gen_js(0) #""" typespace = JSTypeSpace() flatten_statementlists(result, typespace) for c in result: if type(c) == FuncCallNode and c[0].gen_js(0).strip() in ["inherit", "create_prototype"]: handle_potential_class(result, result.index(c), typespace) print(len(result)) buf = data return buf, result
def visit_breaks(n): wn = n if n in visit: return visit.add(n) while type(wn) not in [WhileNode, DoWhileNode, ForLoopNode]: if type(wn) == SwitchNode: typespace.error("Switches in generators not supported yet.", wn); wn = wn.parent if not wn: typespace.error("Invalid break statement.", n); if "frame" not in wn.__dict__: return f = wn.frame i = n.parent.index(n) n2 = js_parse("$s=$s;", ("$__state", f_next(f).label)) if "frame" in n.__dict__: n.frame.insert(n.frame.index(n), n2) else: n.parent.insert(i, n2)
def visit_rets1(n2): target = n2[0][0][0].val isyield = n2[0][0][1].val ni = n2.parent.index(n2) if target >= f_next(f.parent).label: n3 = js_parse("this.trystack.pop();")[0] n2.parent.insert(ni, n3)
def varvisit(n, startn): return n2 = js_parse(""" _es6_module.add_global('$s', $s); """, [n.val, n.val]); startn.parent.insert(startn.parent.index(startn)+1, n2); for n2 in n[2:]: varvisit(n2, startn);
def exportfromvisit(n): n2 = js_parse(""" import * as _$s1 from '$s1'; for (var k in _$s1) { _es6_module.add_export(k, _$s1[k], true); } """, [n.name.val]) n.parent.replace(n, n2)
def exportfromvisit(n): n2 = js_parse( """ import * as _$s1 from '$s1'; for (var k in _$s1) { _es6_module.add_export(k, _$s1[k], true); } """, [n.name.val]) n.parent.replace(n, n2)
def exportvisit(n): if not at_root(n): typespace.error("Export statements cannot be within functions or classes", n) pi = n.parent.index(n) n.parent.remove(n) for n2 in n[:]: n.remove(n2) n.parent.insert(pi, n2) pi += 1 if not n.is_default: n2 = js_parse(""" $s = _es6_module.add_export('$s', $s); """, [n.name, n.name, n.name]); else: n2 = js_parse(""" $s = _es6_module.set_default_export('$s', $s); """, [n.name, n.name, n.name]); n.parent.insert(pi, n2)
def handle_yields(node): slist = js_parse("""$__ret = this.ret;"""); is_empty = type(node[0]) == ExprNode and len(node[0]) == 0 if is_empty: slist.add(js_parse("""$s.value = undefined;""", ["$__ret"], start_node=AssignNode)); else: slist.add(js_parse("""$s.value = $n;""", ["$__ret", node[0]], start_node=AssignNode)) slen = len(slist) #print(slist) if node in node.parent: i = node.parent.index(node) node.parent.remove(node) for j in range(slen): node.parent.insert(i, slist[slen-j-1]) i = node.frame.index(node) node.frame.remove(node) for j in range(slen): node.frame.insert(i, slist[slen-j-1])
def visit(n): args = [] for c in n[0]: n2 = js_parse(""" _log_types($s1, $s2, $s3, $s4, $s5, $s6, $s7); """, ['"'+n.file+'"', str(n.line), '`'+buildkey(n)+'`', '`'+str(c.val)+'`', str(c.val), n.lexpos, n.lexpos2]); args.append(n2) for arg in args: if len(n) == 2 and type(n[1]) == StatementList: n[1].insert(0, arg) else: n.insert(1, arg)
def retvisit(n): val = None if len(n) == 0 or n[0].gen_js(0).strip() == "": val = IdentNode("undefined") else: val = n[0] n2 = js_parse("""_log_return($s1, $s2, $s3, $n4, $s5, $s6)""", ['"'+n.file+'"', str(n.line), '`'+buildkey(n)+'`', val, n.lexpos, n.lexpos2], start_node=FuncCallNode) if len(n) == 0: n.append(n2) else: n.replace(n[0], n2)
def visit_rets3(n2): p = n2.parent while not null_node(p) and p != f.subnode: if type(p) in [WhileNode, DoWhileNode, ForLoopNode]: break p = p.parent if p != f.subnode and not null_node(p): return #break corresponds to a loop internal to this frame p = find_parent_frame(f, [WhileNode, DoWhileNode, ForLoopNode], True) if p == None: typespace.error("Invalid break statement (switches within generators aren't supported yet)", n2) f2 = f_next(p) n3 = js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode); n2.parent.replace(n2, n3)
def visit_rets3(n2): p = n2.parent while not null_node(p) and p != f.subnode: p = p.parent if p != f.subnode and not null_node(p): return #continue corresponds to a loop internal to this frame p = f.parent while p != None: if type(p.node) in [WhileNode, DoWhileNode, ForLoopNode]: break; p = p.parent if p == None: typespace.error("Invalid continue statement") n3 = js_parse("return [$i, undefined];", [p.label], start_node=ReturnNode); n2.parent.replace(n2, n3)
def visit_continues(n): if n in visit: return visit.add(n) wn = n while wn != None and (type(wn) not in [WhileNode, DoWhileNode, ForLoopNode]): wn = wn.parent if wn == None: typespace.error("Invalid continue statement.", n); if "frame" not in wn.__dict__: return f = wn.frame i = n.parent.index(n) n2 = js_parse("$s=$s;", ("$__state", f.label)); n3 = BreakNode(); visit.add(n3) n.parent.remove(n) n.frame.replace(n, n2) n.frame.insert(n.frame.index(n2)+1, n3)
def class_visit(n): n2 = js_parse(""" _es6_module.add_class($s); """, [n.name]) n.parent.insert(n.parent.index(n)+1, n2);
def match(self, matchdata, data=None, start_node=None): if data != None: self.input(data) else: data = self.data[:] for k in self.smap: repl = self.smap[k] k = "$" + k f = data.find(k) while f >= 0: lastf = f if f < len(data) - len(k): if _debug: print(f, data[f+len(k)]) if is_word_char(data[f+len(k)]): lastf = f f = data[f+len(k):].find(k) if f < 0: break else: f += lastf + len(k) continue data = data[:f] + repl + data[f+len(k):] lastf = f f = data[f+len(repl):].find(k) if f < 0: break else: f += lastf + len(repl) match_lexer.input(data) """ t = match_lexer.token() while t != None: print(t) t = match_lexer.token() #""" ret = ast_match_parser.parse(data, lexer=match_lexer) if ret == None: raise MatchError("parse error") self.flatten_setnodes(ret) if _debug: print("\n") if _debug: print(data) if _debug: print(ret) def find_mref(n, mref, func): if func(n, mref): return True for c in n: ret = find_mref(c, mref, func) if ret: return True return False def do_mref(n, mref, func): ret = True try: ret = find_mref(n, mref, func) except MatchError: return False if ret == False: raise RuntimeError("Couldn't find special handler") return True self.process(ret) if type(matchdata) == str: node = js_parse(matchdata, start_node=start_node) else: node = matchdata for k in self.variants: n = js_parse(k, exit_on_err=False, validate=True) refs = self.variants[k] #print(k) if not node_structures_match(node, n): continue found = True for r in refs: rfunc = getattr(self, "do_"+r.type) if not do_mref(n, r, rfunc): found = False break if found: return True return False
def build_next(f, parent=None): if type(f) != Frame: return subnode = f.subnode if f.label >= 0: # and f.label < 3: n2 = js_parse("this.$s1 = 0;", [f_name(f)], start_node=AssignNode) n2.replace(n2[1], f.funcnode) f.funcnode.name = "(anonymous)" f.funcnode.is_anonymous = True node2.add(n2) #f.funcnode) if f.totframes > 0: if f.node != None and type(f.node) == WhileNode: f2 = f_next(f) f2 = f2.label if f2 != -1 else -1 n = js_parse( """ if (!"placeholder") { return [$i1, undefined]; } """, [f2]) if n == None: typespace.error("internal error", subnode) n2 = find_node(n, StrLitNode) n2.parent.replace(n2, f.node[0]) subnode.add(n) f2 = f_first(f) n.add( js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) elif f.node != None and type(f.node) == TryNode: n = StatementList() if n == None: typespace.error("internal error", subnode) f3 = f_raw_next(f) while f3 != -1 and type(f3.node) != CatchNode: f3 = f_raw_next(f3) if f3 == -1: typespace.error("Orphaned try block", f.node) f3name = "_nfothing" if len(f3.node) > 0: f3name = f3.node[0].gen_js(0).replace("scope.", "") n.add( js_parse( """ this.trystack.push([$i, "$s"]); """, [f3.label, f3name])) f2 = f_first(f) n.add( js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) subnode.add(n) f2.pop_trystack = True elif f.node != None and type(f.node) == IfNode: f2 = f_first(f) f1 = f_raw_next(f) while type( f1.node) != ElseNode and f1.label != len(flatframes): f1 = f_raw_next(f1) if f1.label == len(flatframes): f1 = f_next(f) n = js_parse( """ if (!("placeholder")) { return [$i1, undefined]; } else { return [$i2, undefined]; } """, [f1.label, f2.label]) n2 = find_node(n, StrLitNode) n2.parent.replace(n2, f.node[0].copy()) if n == None: typespace.error("internal error", subnode) f2 = f_first(f) n.add( js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) subnode.add(n) f2.pop_trystack = True elif f.node != None and type(f.node) == ElseNode: f2 = f_first(f) f1 = f_raw_next(f) while type( f1.node) != ElseNode and f1.label != len(flatframes): f1 = f_raw_next(f1) if f1.label == len(flatframes): f1 = f_next(f) n = js_parse( """ return [$i1, undefined]; """, [f2.label]) if n == None: typespace.error("internal error", subnode) f2 = f_first(f) subnode.add(n) elif f.node != None and type(f.node) == CatchNode: f2 = f_first(f) n = js_parse( """ return [$i1, undefined]; """, [f2.label]) if n == None: typespace.error("internal error", subnode) subnode.add(n) elif f.node != None and type(f.node) == ForLoopNode: f2 = f_first(f) f3 = f_next(f) f3 = f3.label if f2 != -1 else -1 f2 = f2.label if f2 != -1 else -1 n = js_parse( """ if ($n) { return [$i, undefined]; } else { return [$i, undefined]; } """, [f.node[0][1], f2, f3]) if n == None: typespace.error("internal error", subnode) subnode.add(n)
def bleh(): for frames in flatframes: fname = f_name(frames) n = js_parse(""" function $s1(scope) { if (_do_frame_debug) console.log("in $s1"); }""", (fname), start_node=FunctionNode) if type(n[1]) != StatementList: n.replace(n[1], StatementList()) n = n[1] func = n while type(func) != FunctionNode: func = func.parent excl = (type(frames.node) == StatementList and type(frames.parent.node) == FunctionNode) if frames.node != None and not excl and type( frames.node) != FunctionNode: f = frames sl = StatementList() f.node[f.node._startcur] = sl frames.funcnode = func frames.subnode = frames.funcnode local_frames = "[" totframes = 0 for i, f in enumerate(frames): if type(f) != Frame: frames.subnode.add(f) frames.leaf = True else: frames.leaf = False if len(local_frames) > 1: local_frames += ", " local_frames += f_ref(f) #.replace("this.", "") totframes += 1 if f.node != None and type(f.node) != FunctionNode: if len(f.node.children) > f.node._startcur + 1: do_conv(f.node, f) if frames.leaf: f2 = f_next(frames) f2 = f2.label if f2 != -1 else -1 frames.subnode.add( js_parse("return [$i, undefined];", [f2], start_node=ReturnNode)) local_frames = "%s_frames = " % f_ref(frames) + local_frames + "];" frames.frames = js_parse(local_frames) frames.totframes = totframes def build_next(f, parent=None): if type(f) != Frame: return subnode = f.subnode if f.label >= 0: # and f.label < 3: n2 = js_parse("this.$s1 = 0;", [f_name(f)], start_node=AssignNode) n2.replace(n2[1], f.funcnode) f.funcnode.name = "(anonymous)" f.funcnode.is_anonymous = True node2.add(n2) #f.funcnode) if f.totframes > 0: if f.node != None and type(f.node) == WhileNode: f2 = f_next(f) f2 = f2.label if f2 != -1 else -1 n = js_parse( """ if (!"placeholder") { return [$i1, undefined]; } """, [f2]) if n == None: typespace.error("internal error", subnode) n2 = find_node(n, StrLitNode) n2.parent.replace(n2, f.node[0]) subnode.add(n) f2 = f_first(f) n.add( js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) elif f.node != None and type(f.node) == TryNode: n = StatementList() if n == None: typespace.error("internal error", subnode) f3 = f_raw_next(f) while f3 != -1 and type(f3.node) != CatchNode: f3 = f_raw_next(f3) if f3 == -1: typespace.error("Orphaned try block", f.node) f3name = "_nfothing" if len(f3.node) > 0: f3name = f3.node[0].gen_js(0).replace("scope.", "") n.add( js_parse( """ this.trystack.push([$i, "$s"]); """, [f3.label, f3name])) f2 = f_first(f) n.add( js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) subnode.add(n) f2.pop_trystack = True elif f.node != None and type(f.node) == IfNode: f2 = f_first(f) f1 = f_raw_next(f) while type( f1.node) != ElseNode and f1.label != len(flatframes): f1 = f_raw_next(f1) if f1.label == len(flatframes): f1 = f_next(f) n = js_parse( """ if (!("placeholder")) { return [$i1, undefined]; } else { return [$i2, undefined]; } """, [f1.label, f2.label]) n2 = find_node(n, StrLitNode) n2.parent.replace(n2, f.node[0].copy()) if n == None: typespace.error("internal error", subnode) f2 = f_first(f) n.add( js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) subnode.add(n) f2.pop_trystack = True elif f.node != None and type(f.node) == ElseNode: f2 = f_first(f) f1 = f_raw_next(f) while type( f1.node) != ElseNode and f1.label != len(flatframes): f1 = f_raw_next(f1) if f1.label == len(flatframes): f1 = f_next(f) n = js_parse( """ return [$i1, undefined]; """, [f2.label]) if n == None: typespace.error("internal error", subnode) f2 = f_first(f) subnode.add(n) elif f.node != None and type(f.node) == CatchNode: f2 = f_first(f) n = js_parse( """ return [$i1, undefined]; """, [f2.label]) if n == None: typespace.error("internal error", subnode) subnode.add(n) elif f.node != None and type(f.node) == ForLoopNode: f2 = f_first(f) f3 = f_next(f) f3 = f3.label if f2 != -1 else -1 f2 = f2.label if f2 != -1 else -1 n = js_parse( """ if ($n) { return [$i, undefined]; } else { return [$i, undefined]; } """, [f.node[0][1], f2, f3]) if n == None: typespace.error("internal error", subnode) subnode.add(n) node2.insert( 1, js_parse(""" this[Symbol.iterator] = function() { return this; } """)[0]) for f in flatframes: build_next(f, f.parent) #process returns from within try nodes for f in flatframes: if f.parent != None and type(f.parent.node) == TryNode: def visit_rets1(n2): target = n2[0][0][0].val isyield = n2[0][0][1].val ni = n2.parent.index(n2) if target >= f_next(f.parent).label: n3 = js_parse("this.trystack.pop();")[0] n2.parent.insert(ni, n3) traverse(f.subnode, ReturnNode, visit_rets1, copy_children=True) #process yields for f in flatframes: f2 = f.parent set_yield = None def visit_rets2(n2): if set_yield != None: #print(n2) n2[0][0].replace(n2[0][0][1], set_yield) set_yield = find_node(f.subnode, YieldNode) if set_yield != None: set_yield.parent.remove(set_yield) set_yield = ArrayLitNode(ExprListNode([set_yield[0]])) traverse(f.subnode, ReturnNode, visit_rets2, copy_children=True) def find_parent_frame(f, ntypes, include_first=True): p = f if not include_first: p = p.parent while p != None: if type(p.node) in ntypes: return p p = p.parent return None #process breaks for f in flatframes: f2 = f.parent def visit_rets3(n2): p = n2.parent while not null_node(p) and p != f.subnode: if type(p) in [WhileNode, DoWhileNode, ForLoopNode]: break p = p.parent if p != f.subnode and not null_node(p): return #break corresponds to a loop internal to this frame p = find_parent_frame(f, [WhileNode, DoWhileNode, ForLoopNode], True) if p == None: typespace.error( "Invalid break statement (switches within generators aren't supported yet)", n2) f2 = f_next(p) n3 = js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode) n2.parent.replace(n2, n3) traverse(f.subnode, BreakNode, visit_rets3, copy_children=True) #process continues for f in flatframes: f2 = f.parent def visit_rets3(n2): p = n2.parent while not null_node(p) and p != f.subnode: p = p.parent if p != f.subnode and not null_node(p): return #continue corresponds to a loop internal to this frame p = f.parent while p != None: if type(p.node) in [WhileNode, DoWhileNode, ForLoopNode]: break p = p.parent if p == None: typespace.error("Invalid continue statement") n3 = js_parse("return [$i, undefined];", [p.label], start_node=ReturnNode) n2.parent.replace(n2, n3) traverse(f.subnode, ContinueNode, visit_rets3, copy_children=True) firstnode = js_parse("if (this.first) {\n}", start_node=IfNode) firstnode2 = js_parse("if (this.first) {\n}", start_node=IfNode) firstnode.replace(firstnode[1], StatementList()) firstnode2.replace(firstnode2[1], StatementList()) flatframes[0].subnode.add(firstnode) node2.insert(1, firstnode2[1]) firstnode = firstnode[1] firstnode2 = firstnode2[1] args = list(node.children[0]) for i3 in range(len(args)): argn = args[i3] while type(argn) not in [IdentNode, VarDeclNode]: argn = argn[0] args[i3] = argn.val scope = {} for f in flatframes: scope.update(f.scope) s = "{" j2 = 0 for j, v in enumerate(scope.keys()): if j2 > 0: s += ", " j2 += 1 if v in args: s += "%s:%s" % ("%s_%s" % (v, scope[v]), v) else: s += "%s:undefined" % ("%s_%s" % (v, scope[v])) s += "}" s = "this.scope = %s;\n" % s firstnode2.add(js_parse(s)[0]) #ensure all frames have returns for f in flatframes: if not find_node(f.subnode, ReturnNode): f.subnode.add( js_parse("return [$i, undefined];", [f_next(f).label], start_node=ReturnNode)) framelist = "[" for i, f in enumerate(flatframes): if i > 0: framelist += ", " framelist += "this.frame_%i" % f.label framelist = "this.frames = %s];" % framelist node2.add(js_parse(framelist)) node2.add( js_parse( """ this.cur = 1; this.trystack = new Array(); this.next = function() { var ret; while (this.cur < this.frames.length) { try { ret = this.frames[this.cur].call(this, this.scope); } catch (_generator_error) { if (this.trystack.length > 0) { var ts1 = this.trystack.pop(); this.scope[ts1[1]] = _generator_error; ret = [ts1[0], undefined]; } else { throw _generator_error; } } if (ret[0] == this.frames.length) { return {done : true, value : undefined}; break; } if (ret[0] == this.cur) { console.trace(); console.log("YEEK!") return {done : true, value : undefined}; } this.cur = ret[0]; if (ret[1] != undefined) { return {value : ret[1][0], done : false}; } else { return {value : undefined, done : false}; } } } """, [])) node.parent.replace(node, node2)
def visit_generators(node): if not node.is_generator: return def _remove_this(n): if n.val != "this": return if type(n.parent) != BinOpNode or n.parent.op != ".": #typespace.error("Can only reference members of 'this' in generators"); n.val = "__gen_this2" else: n.val = "__gen_this2" #n.parent.parent.replace(n.parent, n.parent[1]) def set_cur(n): if type(n) in [IfNode, WhileNode, DoWhileNode, ForLoopNode, CatchNode]: n._cur = 1 n._startcur = 1 else: n._cur = 0 n._startcur = 0 n._start = True n._has_yield = node_has_yield(n) for c in n: set_cur(c) def prior_if(n): if n.parent == None: return None sl = n.parent i = sl.children.index(n) - 1 while 1: while i >= 0: if type(sl[i]) == IfNode: break i -= 1 if i >= 0 or null_node(n.parent): break i = sl.parent.children.index(sl) sl = sl.parent if i < 0: typespace.error("Orphaned else node", n) sys.exit(-1) return sl[i] def prior_try(n): if n.parent == None: return None sl = n.parent i = sl.children.index(n) - 1 while 1: while i >= 0: if type(sl[i]) == TryNode: break i -= 1 if i >= 0 or null_node(n.parent): break i = sl.parent.children.index(sl) sl = sl.parent if i < 0: typespace.error("Orphaned catch node", n) sys.exit(-1) return sl[i] def is_stype(n): ret = type(n) in stypes # and (n._has_yield or n.parent._has_yield) return ret if type(n) == CatchNode: ret |= prior_try(n)._has_yield if type(n) == ElseNode: ret |= prior_if(n)._has_yield if type(n) in [IfNode, ElseNode]: p5 = n.parent while not null_node(p5): if hasattr(p5, "_has_yield") and p5._has_yield: ret = True break p5 = p5.parent return ret combine_if_else_nodes(node) traverse(node, ForCNode, unpack_for_c_loops, exclude=[FunctionNode], copy_children=True) traverse(node, IdentNode, _remove_this) traverse(node, VarDeclNode, _remove_this) frames = frame = Frame(node=node) stack = [c for c in node.children[1:]] stack.reverse() stypes = set([ ForLoopNode, WhileNode, DoWhileNode, IfNode, ElseNode, TryNode, CatchNode ]) for c in stack: set_cur(c) while len(stack) > 0: n = stack.pop(-1) if is_stype(n) or type(n) == StatementList: if n._start: if type(n) != StatementList: f = Frame(node=n) frame.append(f) frame = f n._start = False if n._cur < len(n.children): stack.append(n) stack.append(n[n._cur]) n._cur += 1 elif type(n) != StatementList: frame = frame.parent else: frame.append(n) def compact_frames(frames): i = 0 frm = None while i < len(frames): f1 = frames[i] if type(f1) == YieldNode: frm = None if type(f1) != Frame: if frm == None: frm = Frame() frames.insert(i, frm) frm.parent = frames i += 1 frames.remove(f1) i -= 1 frm.append(f1) else: compact_frames(f1) frm = None if type(f1) == YieldNode: frm = None i += 1 def label_frames(frames, cur=None): if cur == None: cur = [0] frames.label = cur[0] cur[0] += 1 for f in frames: if type(f) == Frame: if f.node != None: f.node.frame = f label_frames(f, cur) else: f.frame = f def prop_frame_refs(node, f): if hasattr(node, "frame"): f = node.frame else: node.frame = f for c in node.children: prop_frame_refs(c, f) def apply_frame_scope(n, scope, frames): if type(n) == IdentNode: if n.val in scope: n.val = "scope.%s_%d" % (n.val, scope[n.val]) else: p = n.parent n2 = n #check for implicit declarations within catch and loop nodes while not null_node(p): if type(p) in [CatchNode, WhileNode, ForLoopNode]: break n2 = p p = p.parent if not null_node(p) and n2 == p[0]: scope[n.val] = frames.label n.val = "scope.%s_%d" % (n.val, scope[n.val]) elif type(n) == VarDeclNode: n.local = False if "local" in n.modifiers: n.modifiers.remove("local") if hasattr(n.parent, "_c_loop_node"): frames = n.parent._c_loop_node.frame #print("yay", n.parent._c_loop_node.frame.label) if n.val not in scope: scope[n.val] = frames.label if n.val in scope: n.val = "scope.%s_%d" % (n.val, scope[n.val]) for c in n.children: #ignore expr functions, but not nested functions? if type(c) == FunctionNode and type(c.parent) == AssignNode: continue if type(n) == BinOpNode and n.op == "." and c == n[1] and type( c) == IdentNode: continue if type(n) == FuncCallNode and type(c) == IdentNode and c == n[0]: continue apply_frame_scope(c, scope, frames) def frame_scope(frames, scope, depth=0): frames.scope = scope for f in frames: ss = "-" fstr = "" if type(f) == Frame: if f.node != None: fstr = f.node.get_line_str() else: if type(f[0]) == Frame: fstr = f[0].node.get_line_str() else: fstr = f[0].get_line_str() if f.node != None: ss = "+" scope2 = dict(scope) for i in range(f.node._startcur): apply_frame_scope(f.node[i], scope2, f) frame_scope(f, scope2, depth + 1) else: frame_scope(f, scope, depth) else: fstr = f.get_line_str() apply_frame_scope(f, scope, frames) scope = {} for a in node.children[0]: scope[a.val] = 0 compact_frames(frames) label_frames(frames) prop_frame_refs(node, frames) frame_scope(frames, scope) #print_frames(frames) def frames_validate(frames): def gen_frame_validate(frames, tlevel=0): s = "" tstr = tab(tlevel + 1) tstr2 = tab(tlevel + 2) for f in frames: if type(f) == Frame: if f.node != None: cs = f.node.children f.node.children = f.node.children[:node._startcur] f.node.add(ExprNode([])) c = f.node.gen_js(tlevel + 1).split("\n")[0].replace( "{", "").replace("\n", "").replace("}", "").strip() if c.endswith(";"): c = c[:-1] s += tstr + c + " {\n" f.node.children = cs s += gen_frame_validate(f, tlevel + 1) if f.node != None: s += tstr + "}\n" else: c = tstr + f.gen_js(tlevel + 2) s += c if c.strip().endswith("}") == 0 and c.strip().endswith( ";") == 0: s += ";" s += "\n" if tlevel == 0: c = node.gen_js(0).split("\n")[0] + "\n" s = c + s + "}\n" return s #print(node.gen_js(0)) #print(scope) #print_frames(frames) s = gen_frame_validate(frames) s2 = js_parse(s).gen_js(0).strip() s = node.gen_js(0).strip() s = js_parse(s, print_stack=False).gen_js(0).strip() print(s == s2) if s != s2: import difflib print(dir(difflib)) d = difflib.ndiff(s.split("\n"), s2.split("\n")) ds = "" for l in d: ds += l + "\n" #print(ds) line_print(s) line_print(s2) #frames_validate(frames) flatframes = [] def flatten_frames(frames): flatframes.append(frames) for f in frames: if type(f) == Frame: flatten_frames(f) flatten_frames(frames) #print([f.label for f in flatframes]) def frames_transform(frames, node2): scope = frames.scope node2 = FunctionNode(node.name, node.lineno) node2.add(ExprListNode([])) for c in node.children[0]: node2[0].add(IdentNode(c.val)) frames2 = frames for j, frames in enumerate(flatframes[1:]): p = frames.parent f = frames frames.return_frame = 0 frames.return_frame_parent = 0 i = p.index(f) while i >= len(p) - 1 and p.parent != None: f = p p = p.parent i = p.index(f) if p.parent == None: frames.return_frame = 0 frames.return_frame_parent = p.label else: frames.return_frame = p[i + 1].label frames.return_frame_parent = p.label def f_name(f): return "frame_%d" % f.label def f_ref(f): return "this.frame_%d" % f.label def f_raw_next(f): if f.parent == None: f = Frame() f.label = len(flatframes) return f while f.parent != None: i = f.parent.index(f) + 1 while i < len(f.parent): if type(f.parent[i]) == Frame: return f.parent[i] i += 1 f = f.parent f = Frame() f.label = len(flatframes) return f def f_next(f, ignore_loops=False): if f.parent == None: if debug_gen: print("no f.parent! make frame") f = Frame() f.label = len(flatframes) return f while f.parent != None: i = f.parent.index(f) + 1 while i < len(f.parent): if type(f.parent[i]) == Frame: if type(f.parent[i].node) not in [CatchNode, ElseNode]: return f.parent[i] i += 1 if not ignore_loops and f.parent != None and \ type(f.parent.node) in \ [WhileNode, DoWhileNode, ForLoopNode]: if debug_gen: print("looper!", f.label, f.parent.label) return f.parent f = f.parent if debug_gen: print("made frame!", len(flatframes)) f = Frame() f.label = len(flatframes) return f def f_first(f): for f2 in f: if type(f2) == Frame: return f2 #return f def f_last(f): return f[-1] def has_parent(f, p): while f != p and f != None: f = f.parent return f == p def find_exit_points(f, p=None, vset=None): stack = [] if p == None: p = f if vset == None: vset = set() lst = [] """ lst = [] for f2 in f: if type(f2) == Frame: for f3 in f2.paths: if type(f3) == Frame: if not has_parent(f3, p) and f3.label not in vset: lst.append(f3) vset.add(f3.label) lst += find_exit_points(f3, p, vset) else: continue """ for f2 in f.paths: if not has_parent(f2, p) and f2.label not in vset: lst.append(f) vset.add(f.label) else: lst += find_exit_points(f2, p, vset) for f in lst: print(f.label) #sys.exit() return lst tot = len(node) - 1 for i in range(tot): node.pop(1) def param_var(k): for c in node[0]: val = c.gen_js(0) val = val.strip() k = k.strip() if k == val: return True return False #build generator state data scopestr = "{" for k in scope: if scopestr != "{": scopestr += ", " if param_var(k): scopestr += "%s_%i : %s" % (k, scope[k], k) else: scopestr += "%s_%i : %s" % (k, scope[k], "undefined") scopestr += "}" node.add(js_parse("this.scope = $s;", [scopestr], start_node=AssignNode)) node.add( js_parse("this.ret = {done : false, value : undefined};", start_node=AssignNode)) node.add(js_parse("this.state = 1;", start_node=AssignNode)) node.add(js_parse("this.trystack = [];", start_node=AssignNode)) node.add( js_parse(""" this.next = function() { var ret; var stack = this.trystack; try { ret = this._next(); } catch (err) { if (stack.length > 0) { var item = stack.pop(stack.length-1); this.state = item[0]; this.scope[item[1]] = err; return this.next(); } else { throw err; } } return ret; }""", start_node=AssignNode)) node.add( js_parse(""" this.push_trystack = function(catchstate, catchvar) { this.trystack.push([catchstate, catchvar]); }""", start_node=AssignNode)) node.add( js_parse(""" this.pop_trystack = function() { this.trystack.pop(this.trystack.length-1); }""", start_node=AssignNode)) #build next function keynode = IdentNode("$__state") sn = SwitchNode(keynode) slist = js_parse( "var $__ret = undefined; var $__state = this.state; var scope = this.scope;" ) slist2 = StatementList() slist2.add(sn) wn = WhileNode( BinOpNode(IdentNode("$__state"), NumLitNode(len(flatframes)), "<")) wn.add(slist2) wn[1].add( js_parse(""" if ($__ret != undefined) { break; } """, start_node=IfNode)) slist.add(wn) slist.add( js_parse(""" if ($__ret != undefined) { this.ret.value = $__ret.value; } else { this.ret.done = true; this.ret.value = undefined; } this.state = $__state; return this.ret; """)) next = js_parse("this._next = function() { };", start_node=AssignNode) next[1].add(slist) node.add(next) sn.line = slist.line = node.line sn.lexpos = slist.lexpos = node.lexpos #find leaves for f in flatframes: if len(f) > 0: f.leaf = True for c in f: if type(c) == Frame: f.leaf = False break #move control frame of dowhile statements to #after their statement body frames. visit = set() for i in range(len(flatframes)): if i in visit: continue f = flatframes[i] if f.leaf or type(f.node) != DoWhileNode: continue f2 = f_first(f) if f2 == None: continue last = f2.label while (f2 != f_next(f) and f2 != f): last = f2.label f2 = f_next(f2) last = ((last - 1) if last > i else last) + 1 flatframes.pop(i) flatframes.insert(last, f) visit.add(last) for i, f in enumerate(flatframes): f.label = i #set up case statements for f in flatframes: n2 = CaseNode(NumLitNode(f.label)) sl = StatementList() if debug_gen: sl.add(js_parse("console.log(\"in frame $s\");", [f.label])) #set line/lexpos data if f.node != None: n2.line = f.node.line n2.lexpos = f.node.lexpos sl.line = f.node.line sl.lexpos = f.node.lexpos f.case_sl = sl n2.add(sl) #add to switch statement sn.add(n2) def set_linepos(n, line, lexpos): n.line = line n.lexpos = lexpos for c in n: set_linepos(c, line, lexpos) for f in flatframes: if f.leaf: for c in f: c.frame = f else: f.node.frame = f #handle loop breaks/continues visit = set() def visit_breaks(n): wn = n if n in visit: return visit.add(n) while type(wn) not in [WhileNode, DoWhileNode, ForLoopNode]: if type(wn) == SwitchNode: typespace.error("Switches in generators not supported yet.", wn) wn = wn.parent if not wn: typespace.error("Invalid break statement.", n) if "frame" not in wn.__dict__: return f = wn.frame i = n.parent.index(n) n2 = js_parse("$s=$s;", ("$__state", f_next(f).label)) if "frame" in n.__dict__: n.frame.insert(n.frame.index(n), n2) else: n.parent.insert(i, n2) def visit_continues(n): if n in visit: return visit.add(n) wn = n while wn != None and (type(wn) not in [WhileNode, DoWhileNode, ForLoopNode]): wn = wn.parent if wn == None: typespace.error("Invalid continue statement.", n) if "frame" not in wn.__dict__: return f = wn.frame i = n.parent.index(n) n2 = js_parse("$s=$s;", ("$__state", f.label)) n3 = BreakNode() visit.add(n3) n.parent.remove(n) n.frame.replace(n, n2) n.frame.insert(n.frame.index(n2) + 1, n3) def handle_yields(node): slist = js_parse("""$__ret = this.ret;""") is_empty = type(node[0]) == ExprNode and len(node[0]) == 0 if is_empty: slist.add( js_parse("""$s.value = undefined;""", ["$__ret"], start_node=AssignNode)) else: slist.add( js_parse("""$s.value = $n;""", ["$__ret", node[0]], start_node=AssignNode)) slen = len(slist) #print(slist) if node in node.parent: i = node.parent.index(node) node.parent.remove(node) for j in range(slen): node.parent.insert(i, slist[slen - j - 1]) i = node.frame.index(node) node.frame.remove(node) for j in range(slen): node.frame.insert(i, slist[slen - j - 1]) #handle loop breaks for f in flatframes: if not f.leaf: continue for c in f: traverse(c, BreakNode, visit_breaks, exclude=FunctionNode) pass #handle loop continues for f in flatframes: if not f.leaf: continue for c in f: traverse(c, ContinueNode, visit_continues, exclude=FunctionNode) pass #handle yields for f in flatframes: if not f.leaf: continue for c in f: traverse(c, YieldNode, handle_yields, exclude=FunctionNode) def has_common_parent(n1, n2, p): while n1 != p and n1 != None: n1 = n1.parent while n2 != p and n2 != None: n2 = n2.parent if n1 == n2 and n1 == p: return True else: return False #build control code for f in flatframes: if f.leaf: continue n = f.node sl = f.case_sl if type(n) == IfNode: f2 = f_first(f) if f2 == None: #empty if node f2 = Frame() f2.label = len(flatframes) if len(n) > 2: f3 = n[2].frame else: f3 = f_next(f) f.paths += [f2, f3] n2 = js_parse(""" $s = ($n) ? $s : $s; """, ["$__state", n[0], f2.label, f3.label]) set_linepos(n2, n.line, n.lexpos) sl.add(n2) elif type(n) == ElseNode: f2 = f_first(f) if f2 == None: #empty else node f2 = Frame() f2.label = len(flatframes) f.paths += [f2] n2 = js_parse(";$s = $s;", ("$__state", str(f2.label))) set_linepos(n2, n.line, n.lexpos) sl.add(n2) elif type(n) == WhileNode: f.paths += [f_first(f), f_next(f, False)] n2 = js_parse(""" $s = ($n) ? $s : $s; """, ("$__state", n[0], f_first(f).label, f_next(f, False).label)) set_linepos(n2, n.line, n.lexpos) sl.add(n2) elif type(n) == ForLoopNode: #okay, why did I say to ignore loops here? f.paths += [f_first(f), f_next(f, False)] if type(n[0]) == ForCNode: n2 = js_parse(""" $s = ($n) ? $s : $s; """, ("$__state", n[0][1], f_first(f).label, f_next(f, False).label)) set_linepos(n2, n.line, n.lexpos) sl.add(n2) else: typespace.error( "process_generators expects unpacked iterator for loops", n) elif type(n) == DoWhileNode: f.paths += [f_first(f), f_next(f, False)] n2 = js_parse(""" $s = ($n) ? $s : $s; """, ("$__state", n[0], f_first(f).label, f_next(f, False).label), start_node=AssignNode) set_linepos(n2, n.line, n.lexpos) sl.add(n2) elif type(n) == TryNode: f.paths += [f_first(f)] cn = f_raw_next(f).node if type(cn) != CatchNode: typespace.error("Missing catch block", f.node) ident = cn[0].gen_js(0).replace("scope.", "") n2 = js_parse("$s = $s;", ("$__state", f_first(f).label), start_node=AssignNode) n3 = js_parse("this.push_trystack($s, \"$s\");", [f_raw_next(f).label, ident]) set_linepos(n2, n.line, n.lexpos) set_linepos(n3, n.line, n.lexpos) sl.add(n2) sl.add(n3) elif type(n) == CatchNode: f.paths += [f_first(f)] n2 = js_parse("$s = $s;", ("$__state", f_first(f).label), start_node=AssignNode) set_linepos(n2, n.line, n.lexpos) sl.add(n2) #build leaf code for f in flatframes: if not f.leaf: continue sl = f.case_sl for n in f: sl.add(n) f2 = f_next(f) sl.add(js_parse(";$s=$s;", ("$__state", str(f2.label)))) f.paths += [f2] #add in pop_trystack calls for f in flatframes: if type(f.node) != TryNode: continue f2 = f_last(f) ps = find_exit_points(f) for f2 in ps: f2.case_sl.add(js_parse("this.pop_trystack();")) #add case breaks for f in flatframes: bn = BreakNode() bn.line = f.case_sl.line bn.lexpos = f.case_sl.lexpos f.case_sl.add(bn) #add final state case cn = CaseNode(NumLitNode(len(flatframes))) sl2 = StatementList() sl2.add(BreakNode()) cn.add(sl2) sn.add(cn) #default case df = DefaultCaseNode() df.add( js_parse("console.log(\"Generator state error\"); console.trace();")) df[0].add(BreakNode()) sn.add(df) outernode = js_parse(""" function() { var __gen_this2 = this; function _generator_iter() { } return new _generator_iter(); } """, start_node=FunctionNode) #add a self-referencing [Symbol.iterator] method n = js_parse(""" this[Symbol.iterator] = function() { return this; } """) for c in n: node.add(c) #and, a es5.1-style forEach method n = js_parse(""" this.forEach = function(callback, thisvar) { if (thisvar == undefined) thisvar = self; var _i = 0; while (1) { var ret = this.next(); if (ret == undefined || ret.done || (ret._ret != undefined && ret._ret.done)) break; callback.call(thisvar, ret.value); if (_i++ > 100) { console.log("inf loop", ret); break; } } } """) for c in n: node.add(c) outernode.name = node.name if node.is_anonymous: outernode.is_anonymous = True outernode.replace(outernode[0], node[0]) node.parent.replace(node, outernode) node2 = outernode[2] cs = node[:] for c in cs[1:]: node2.add(c)
def visit_generators(node): if not node.is_generator: return def _remove_this(n): if n.val != "this": return if type(n.parent) != BinOpNode or n.parent.op != ".": #typespace.error("Can only reference members of 'this' in generators"); n.val = "__gen_this2" else: n.val = "__gen_this2" #n.parent.parent.replace(n.parent, n.parent[1]) def set_cur(n): if type(n) in [IfNode, WhileNode, DoWhileNode, ForLoopNode, CatchNode]: n._cur = 1; n._startcur = 1; else: n._cur = 0 n._startcur = 0 n._start = True n._has_yield = node_has_yield(n) for c in n: set_cur(c) def prior_if(n): if n.parent == None: return None sl = n.parent i = sl.children.index(n)-1 while 1: while i >= 0: if type(sl[i]) == IfNode: break i -= 1 if i >= 0 or null_node(n.parent): break i = sl.parent.children.index(sl); sl = sl.parent; if i < 0: typespace.error("Orphaned else node", n) sys.exit(-1) return sl[i] def prior_try(n): if n.parent == None: return None sl = n.parent i = sl.children.index(n)-1 while 1: while i >= 0: if type(sl[i]) == TryNode: break i -= 1 if i >= 0 or null_node(n.parent): break i = sl.parent.children.index(sl); sl = sl.parent; if i < 0: typespace.error("Orphaned catch node", n) sys.exit(-1) return sl[i] def is_stype(n): ret = type(n) in stypes # and (n._has_yield or n.parent._has_yield) return ret if type(n) == CatchNode: ret |= prior_try(n)._has_yield if type(n) == ElseNode: ret |= prior_if(n)._has_yield if type(n) in [IfNode, ElseNode]: p5 = n.parent while not null_node(p5): if hasattr(p5, "_has_yield") and p5._has_yield: ret = True; break p5 = p5.parent return ret combine_if_else_nodes(node) traverse(node, ForCNode, unpack_for_c_loops, exclude=[FunctionNode], copy_children=True); traverse(node, IdentNode, _remove_this) traverse(node, VarDeclNode, _remove_this) frames = frame = Frame(node=node) stack = [c for c in node.children[1:]] stack.reverse() stypes = set([ForLoopNode, WhileNode, DoWhileNode, IfNode, ElseNode, TryNode, CatchNode]) for c in stack: set_cur(c) while len(stack) > 0: n = stack.pop(-1) if is_stype(n) or type(n) == StatementList: if n._start: if type(n) != StatementList: f = Frame(node=n) frame.append(f) frame = f n._start = False if n._cur < len(n.children): stack.append(n) stack.append(n[n._cur]) n._cur += 1 elif type(n) != StatementList: frame = frame.parent else: frame.append(n) def compact_frames(frames): i = 0 frm = None while i < len(frames): f1 = frames[i] if type(f1) == YieldNode: frm = None if type(f1) != Frame: if frm == None: frm = Frame() frames.insert(i, frm) frm.parent = frames i += 1 frames.remove(f1) i -= 1 frm.append(f1) else: compact_frames(f1) frm = None if type(f1) == YieldNode: frm = None i += 1 def label_frames(frames, cur=None): if cur == None: cur = [0] frames.label = cur[0] cur[0] += 1 for f in frames: if type(f) == Frame: if f.node != None: f.node.frame = f label_frames(f, cur) else: f.frame = f def prop_frame_refs(node, f): if hasattr(node, "frame"): f = node.frame else: node.frame = f for c in node.children: prop_frame_refs(c, f) def apply_frame_scope(n, scope, frames): if type(n) == IdentNode: if n.val in scope: n.val = "scope.%s_%d" % (n.val, scope[n.val]) else: p = n.parent n2 = n #check for implicit declarations within catch and loop nodes while not null_node(p): if type(p) in [CatchNode, WhileNode, ForLoopNode]: break n2 = p p = p.parent if not null_node(p) and n2 == p[0]: scope[n.val] = frames.label n.val = "scope.%s_%d" % (n.val, scope[n.val]) elif type(n) == VarDeclNode: n.local = False; if "local" in n.modifiers: n.modifiers.remove("local") if hasattr(n.parent, "_c_loop_node"): frames = n.parent._c_loop_node.frame #print("yay", n.parent._c_loop_node.frame.label) if n.val not in scope: scope[n.val] = frames.label if n.val in scope: n.val = "scope.%s_%d" % (n.val, scope[n.val]) for c in n.children: #ignore expr functions, but not nested functions? if type(c) == FunctionNode and type(c.parent) == AssignNode: continue if type(n) == BinOpNode and n.op == "." and c == n[1] and type(c) == IdentNode: continue if type(n) == FuncCallNode and type(c) == IdentNode and c == n[0]: continue apply_frame_scope(c, scope, frames) def frame_scope(frames, scope, depth=0): frames.scope = scope for f in frames: ss = "-" fstr = "" if type(f) == Frame: if f.node != None: fstr = f.node.get_line_str() else: if type(f[0]) == Frame: fstr = f[0].node.get_line_str() else: fstr = f[0].get_line_str() if f.node != None: ss = "+" scope2 = dict(scope) for i in range(f.node._startcur): apply_frame_scope(f.node[i], scope2, f) frame_scope(f, scope2, depth+1) else: frame_scope(f, scope, depth) else: fstr = f.get_line_str() apply_frame_scope(f, scope, frames) scope = {} for a in node.children[0]: scope[a.val] = 0 compact_frames(frames) label_frames(frames) prop_frame_refs(node, frames) frame_scope(frames, scope) #print_frames(frames) def frames_validate(frames): def gen_frame_validate(frames, tlevel=0): s = "" tstr = tab(tlevel+1) tstr2 = tab(tlevel+2) for f in frames: if type(f) == Frame: if f.node != None: cs = f.node.children f.node.children = f.node.children[:node._startcur] f.node.add(ExprNode([])) c = f.node.gen_js(tlevel+1).split("\n")[0].replace("{", "").replace("\n", "").replace("}", "").strip() if c.endswith(";"): c = c[:-1] s += tstr + c + " {\n" f.node.children = cs s += gen_frame_validate(f, tlevel+1) if f.node != None: s += tstr + "}\n" else: c = tstr + f.gen_js(tlevel+2) s += c if c.strip().endswith("}") == 0 and c.strip().endswith(";") == 0: s += ";" s += "\n" if tlevel == 0: c = node.gen_js(0).split("\n")[0] + "\n" s = c + s + "}\n" return s #print(node.gen_js(0)) #print(scope) #print_frames(frames) s = gen_frame_validate(frames) s2 = js_parse(s).gen_js(0).strip() s = node.gen_js(0).strip() s = js_parse(s, print_stack=False).gen_js(0).strip() print(s==s2) if s != s2: import difflib print(dir(difflib)) d = difflib.ndiff(s.split("\n"), s2.split("\n")) ds = "" for l in d: ds += l + "\n" #print(ds) line_print(s) line_print(s2) #frames_validate(frames) flatframes = [] def flatten_frames(frames): flatframes.append(frames) for f in frames: if type(f) == Frame: flatten_frames(f) flatten_frames(frames) #print([f.label for f in flatframes]) def frames_transform(frames, node2): scope = frames.scope node2 = FunctionNode(node.name, node.lineno) node2.add(ExprListNode([])) for c in node.children[0]: node2[0].add(IdentNode(c.val)) frames2 = frames for j, frames in enumerate(flatframes[1:]): p = frames.parent f = frames frames.return_frame = 0 frames.return_frame_parent = 0 i = p.index(f) while i >= len(p)-1 and p.parent != None: f = p p = p.parent i = p.index(f) if p.parent == None: frames.return_frame = 0 frames.return_frame_parent = p.label else: frames.return_frame = p[i+1].label frames.return_frame_parent = p.label def f_name(f): return "frame_%d" % f.label def f_ref(f): return "this.frame_%d" % f.label def f_raw_next(f): if f.parent == None: f = Frame() f.label = len(flatframes) return f while f.parent != None: i = f.parent.index(f)+1 while i < len(f.parent): if type(f.parent[i]) == Frame: return f.parent[i] i += 1 f = f.parent f = Frame() f.label = len(flatframes) return f def f_next(f, ignore_loops=False): if f.parent == None: if debug_gen: print("no f.parent! make frame"); f = Frame() f.label = len(flatframes) return f while f.parent != None: i = f.parent.index(f)+1 while i < len(f.parent): if type(f.parent[i]) == Frame: if type(f.parent[i].node) not in [CatchNode, ElseNode]: return f.parent[i] i += 1 if not ignore_loops and f.parent != None and \ type(f.parent.node) in \ [WhileNode, DoWhileNode, ForLoopNode]: if debug_gen: print("looper!", f.label, f.parent.label) return f.parent f = f.parent if debug_gen: print("made frame!", len(flatframes)) f = Frame() f.label = len(flatframes) return f def f_first(f): for f2 in f: if type(f2) == Frame: return f2 #return f def f_last(f): return f[-1] def has_parent(f, p): while f != p and f != None: f = f.parent return f == p def find_exit_points(f, p=None, vset=None): stack = [] if p == None: p = f if vset == None: vset = set() lst = [] """ lst = [] for f2 in f: if type(f2) == Frame: for f3 in f2.paths: if type(f3) == Frame: if not has_parent(f3, p) and f3.label not in vset: lst.append(f3) vset.add(f3.label) lst += find_exit_points(f3, p, vset) else: continue """ for f2 in f.paths: if not has_parent(f2, p) and f2.label not in vset: lst.append(f) vset.add(f.label) else: lst += find_exit_points(f2, p, vset) for f in lst: print(f.label) #sys.exit() return lst tot = len(node)-1 for i in range(tot): node.pop(1) def param_var(k): for c in node[0]: val = c.gen_js(0) val = val.strip() k = k.strip() if k == val: return True return False #build generator state data scopestr = "{" for k in scope: if scopestr != "{": scopestr += ", "; if param_var(k): scopestr += "%s_%i : %s" % (k, scope[k], k); else: scopestr += "%s_%i : %s" % (k, scope[k], "undefined"); scopestr += "}" node.add(js_parse("this.scope = $s;", [scopestr], start_node=AssignNode)) node.add(js_parse("this.ret = {done : false, value : undefined};", start_node=AssignNode)) node.add(js_parse("this.state = 1;", start_node=AssignNode)) node.add(js_parse("this.trystack = [];", start_node=AssignNode)) node.add(js_parse(""" this.next = function() { var ret; var stack = this.trystack; try { ret = this._next(); } catch (err) { if (stack.length > 0) { var item = stack.pop(stack.length-1); this.state = item[0]; this.scope[item[1]] = err; return this.next(); } else { throw err; } } return ret; }""", start_node=AssignNode)) node.add(js_parse(""" this.push_trystack = function(catchstate, catchvar) { this.trystack.push([catchstate, catchvar]); }""", start_node=AssignNode)) node.add(js_parse(""" this.pop_trystack = function() { this.trystack.pop(this.trystack.length-1); }""", start_node=AssignNode)) #build next function keynode = IdentNode("$__state"); sn = SwitchNode(keynode); slist = js_parse("var $__ret = undefined; var $__state = this.state; var scope = this.scope;"); slist2 = StatementList() slist2.add(sn) wn = WhileNode(BinOpNode(IdentNode("$__state"), NumLitNode(len(flatframes)), "<")) wn.add(slist2) wn[1].add(js_parse(""" if ($__ret != undefined) { break; } """, start_node=IfNode)); slist.add(wn); slist.add(js_parse(""" if ($__ret != undefined) { this.ret.value = $__ret.value; } else { this.ret.done = true; this.ret.value = undefined; } this.state = $__state; return this.ret; """)); next = js_parse("this._next = function() { };", start_node=AssignNode) next[1].add(slist) node.add(next) sn.line = slist.line = node.line sn.lexpos = slist.lexpos = node.lexpos #find leaves for f in flatframes: if len(f) > 0: f.leaf = True for c in f: if type(c) == Frame: f.leaf = False break #move control frame of dowhile statements to #after their statement body frames. visit = set() for i in range(len(flatframes)): if i in visit: continue f = flatframes[i] if f.leaf or type(f.node) != DoWhileNode: continue f2 = f_first(f) if f2 == None: continue last = f2.label while (f2 != f_next(f) and f2 != f): last = f2.label f2 = f_next(f2) last = ((last-1) if last > i else last) + 1 flatframes.pop(i); flatframes.insert(last, f); visit.add(last) for i, f in enumerate(flatframes): f.label = i #set up case statements for f in flatframes: n2 = CaseNode(NumLitNode(f.label)) sl = StatementList() if debug_gen: sl.add(js_parse("console.log(\"in frame $s\");", [f.label])); #set line/lexpos data if f.node != None: n2.line = f.node.line n2.lexpos = f.node.lexpos sl.line = f.node.line sl.lexpos = f.node.lexpos f.case_sl = sl n2.add(sl) #add to switch statement sn.add(n2) def set_linepos(n, line, lexpos): n.line = line n.lexpos = lexpos for c in n: set_linepos(c, line, lexpos) for f in flatframes: if f.leaf: for c in f: c.frame = f else: f.node.frame = f #handle loop breaks/continues visit = set() def visit_breaks(n): wn = n if n in visit: return visit.add(n) while type(wn) not in [WhileNode, DoWhileNode, ForLoopNode]: if type(wn) == SwitchNode: typespace.error("Switches in generators not supported yet.", wn); wn = wn.parent if not wn: typespace.error("Invalid break statement.", n); if "frame" not in wn.__dict__: return f = wn.frame i = n.parent.index(n) n2 = js_parse("$s=$s;", ("$__state", f_next(f).label)) if "frame" in n.__dict__: n.frame.insert(n.frame.index(n), n2) else: n.parent.insert(i, n2) def visit_continues(n): if n in visit: return visit.add(n) wn = n while wn != None and (type(wn) not in [WhileNode, DoWhileNode, ForLoopNode]): wn = wn.parent if wn == None: typespace.error("Invalid continue statement.", n); if "frame" not in wn.__dict__: return f = wn.frame i = n.parent.index(n) n2 = js_parse("$s=$s;", ("$__state", f.label)); n3 = BreakNode(); visit.add(n3) n.parent.remove(n) n.frame.replace(n, n2) n.frame.insert(n.frame.index(n2)+1, n3) def handle_yields(node): slist = js_parse("""$__ret = this.ret;"""); is_empty = type(node[0]) == ExprNode and len(node[0]) == 0 if is_empty: slist.add(js_parse("""$s.value = undefined;""", ["$__ret"], start_node=AssignNode)); else: slist.add(js_parse("""$s.value = $n;""", ["$__ret", node[0]], start_node=AssignNode)) slen = len(slist) #print(slist) if node in node.parent: i = node.parent.index(node) node.parent.remove(node) for j in range(slen): node.parent.insert(i, slist[slen-j-1]) i = node.frame.index(node) node.frame.remove(node) for j in range(slen): node.frame.insert(i, slist[slen-j-1]) #handle loop breaks for f in flatframes: if not f.leaf: continue; for c in f: traverse(c, BreakNode, visit_breaks, exclude=FunctionNode) pass #handle loop continues for f in flatframes: if not f.leaf: continue; for c in f: traverse(c, ContinueNode, visit_continues, exclude=FunctionNode) pass #handle yields for f in flatframes: if not f.leaf: continue for c in f: traverse(c, YieldNode, handle_yields, exclude=FunctionNode); def has_common_parent(n1, n2, p): while n1 != p and n1 != None: n1 = n1.parent while n2 != p and n2 != None: n2 = n2.parent if n1 == n2 and n1 == p: return True else: return False #build control code for f in flatframes: if f.leaf: continue n = f.node sl = f.case_sl if type(n) == IfNode: f2 = f_first(f) if f2 == None: #empty if node f2 = Frame() f2.label = len(flatframes) if len(n) > 2: f3 = n[2].frame else: f3 = f_next(f) f.paths += [f2, f3] n2 = js_parse(""" $s = ($n) ? $s : $s; """, ["$__state", n[0], f2.label, f3.label]); set_linepos(n2, n.line, n.lexpos); sl.add(n2) elif type(n) == ElseNode: f2 = f_first(f) if f2 == None: #empty else node f2 = Frame() f2.label = len(flatframes) f.paths += [f2] n2 = js_parse(";$s = $s;", ("$__state", str(f2.label))) set_linepos(n2, n.line, n.lexpos); sl.add(n2) elif type(n) == WhileNode: f.paths += [f_first(f), f_next(f, False)] n2 = js_parse(""" $s = ($n) ? $s : $s; """, ("$__state", n[0], f_first(f).label, f_next(f, False).label)); set_linepos(n2, n.line, n.lexpos); sl.add(n2) elif type(n) == ForLoopNode: #okay, why did I say to ignore loops here? f.paths += [f_first(f), f_next(f, False)] if type(n[0]) == ForCNode: n2 = js_parse(""" $s = ($n) ? $s : $s; """, ("$__state", n[0][1], f_first(f).label, f_next(f, False).label)); set_linepos(n2, n.line, n.lexpos); sl.add(n2) else: typespace.error("process_generators expects unpacked iterator for loops") elif type(n) == DoWhileNode: f.paths += [f_first(f), f_next(f, False)] n2 = js_parse(""" $s = ($n) ? $s : $s; """, ("$__state", n[0], f_first(f).label, f_next(f, False).label), start_node=AssignNode) set_linepos(n2, n.line, n.lexpos) sl.add(n2) elif type(n) == TryNode: f.paths += [f_first(f)] cn = f_raw_next(f).node if type(cn) != CatchNode: typespace.error("Missing catch block", f.node) ident = cn[0].gen_js(0).replace("scope.", "") n2 = js_parse("$s = $s;", ("$__state", f_first(f).label), start_node=AssignNode) n3 = js_parse("this.push_trystack($s, \"$s\");", [f_raw_next(f).label, ident]) set_linepos(n2, n.line, n.lexpos) set_linepos(n3, n.line, n.lexpos) sl.add(n2) sl.add(n3) elif type(n) == CatchNode: f.paths += [f_first(f)] n2 = js_parse("$s = $s;", ("$__state", f_first(f).label), start_node=AssignNode) set_linepos(n2, n.line, n.lexpos) sl.add(n2) #build leaf code for f in flatframes: if not f.leaf: continue sl = f.case_sl for n in f: sl.add(n) f2 = f_next(f) sl.add(js_parse(";$s=$s;", ("$__state", str(f2.label)))) f.paths += [f2] #add in pop_trystack calls for f in flatframes: if type(f.node) != TryNode: continue f2 = f_last(f) ps = find_exit_points(f) for f2 in ps: f2.case_sl.add(js_parse("this.pop_trystack();")) #add case breaks for f in flatframes: bn = BreakNode() bn.line = f.case_sl.line bn.lexpos = f.case_sl.lexpos f.case_sl.add(bn); #add final state case cn = CaseNode(NumLitNode(len(flatframes))) sl2 = StatementList() sl2.add(BreakNode()) cn.add(sl2) sn.add(cn) #default case df = DefaultCaseNode() df.add(js_parse("console.log(\"Generator state error\"); console.trace();")) df[0].add(BreakNode()) sn.add(df) outernode = js_parse(""" function() { var __gen_this2 = this; function _generator_iter() { } return new _generator_iter(); } """, start_node=FunctionNode); #add a self-referencing [Symbol.iterator] method n = js_parse(""" this[Symbol.iterator] = function() { return this; } """); for c in n: node.add(c); #and, a es5.1-style forEach method n = js_parse(""" this.forEach = function(callback, thisvar) { if (thisvar == undefined) thisvar = self; var _i = 0; while (1) { var ret = this.next(); if (ret == undefined || ret.done || (ret._ret != undefined && ret._ret.done)) break; callback.call(thisvar, ret.value); if (_i++ > 100) { console.log("inf loop", ret); break; } } } """); for c in n: node.add(c); outernode.name = node.name; if node.is_anonymous: outernode.is_anonymous = True outernode.replace(outernode[0], node[0]) node.parent.replace(node, outernode); node2 = outernode[2] cs = node[:] for c in cs[1:]: node2.add(c)
if __name__ == "__main__": result = js_parse(""" function bleh(a, b) { c = 0; d = 2; return e; } var a = bleh() + 3; var r = /a/g; if (a == 0) { b = b==0 ? c : a; } else if (b == 2) { console.log(c); } var obj = {a: b, c: 2}; var arr = [0, 2, 3, 4]; switch (a) { case 2: break; default: break; } function GArray(input) { Array<T>.call(this); b = 0; } """)
def handle_potential_class(result, ni, typespace): c = result[ni] op = c[0].gen_js(0).strip(); name = c[1][0].gen_js(0).strip() if op == "inherit": parents = [c[1][1].gen_js(0).strip()]; else: parents = [] cls = ClassNode(name, parents) constructor = None i = ni-1 while i >= 0: n = result[i] if type(n) == FunctionNode: if n.name == name: constructor = n break i -= 1 if constructor == None: typespace.error("Could not find constructor for " + name + ".", result[ni]) members = {} i = ni; m1 = js_parse("obj.prototype.func = {}"); m2 = js_parse("obj.func = {}"); while i < len(result): n = result[i]; bad = type(n) != AssignNode bad |= type(n[1]) != FunctionNode if bad: i += 1 continue root = n[0] while type(root) == BinOpNode: root = root[0] bad = root.gen_js(0).strip() != name if bad: i += 1 continue is_static = False if root.parent[1].gen_js(0) == "prototype": method_name = root.parent.parent[1].gen_js(0).strip() else: method_name = root.parent[1].gen_js(0).strip() is_static = True f = n[1] print(method_name) m = MethodNode(method_name) m.add(f[0]) if len(f) > 2: s = StatementList() for c in f.children[1:]: s.add(c) else: m.add(f[1]) cls.add(m) i += 1 print(name, parents, constructor.name) if name != "ClassTest": sys.exit()
def module_transform(node, typespace): flatten_statementlists(node, typespace); depends = set() def at_root(n): p = n while p != None: if isinstance(p, FunctionNode): return False p = p.parent return True def varvisit(n, startn): return n2 = js_parse(""" _es6_module.add_global('$s', $s); """, [n.val, n.val]); startn.parent.insert(startn.parent.index(startn)+1, n2); for n2 in n[2:]: varvisit(n2, startn); def exportvisit(n): if not at_root(n): typespace.error("Export statements cannot be within functions or classes", n) pi = n.parent.index(n) n.parent.remove(n) for n2 in n[:]: n.remove(n2) n.parent.insert(pi, n2) pi += 1 if not n.is_default: n2 = js_parse(""" $s = _es6_module.add_export('$s', $s); """, [n.name, n.name, n.name]); else: n2 = js_parse(""" $s = _es6_module.set_default_export('$s', $s); """, [n.name, n.name, n.name]); n.parent.insert(pi, n2) def exportfromvisit(n): n2 = js_parse(""" import * as _$s1 from '$s1'; for (var k in _$s1) { _es6_module.add_export(k, _$s1[k], true); } """, [n.name.val]) n.parent.replace(n, n2) #print(node) #ahem. if I do this one first, I can use import statements in it :) #. . .also, how cool, it captures the dependencies, too traverse(node, ExportFromNode, exportfromvisit, copy_children=True); traverse(node, ExportNode, exportvisit, copy_children=True); #fetch explicit global variables globals = []; def kill_assignments(n): n.replace(n[0], ExprNode([])) for c in n: if type(c) == VarDeclNode: kill_assignments(c) def global_to_var(n): if type(n) == VarDeclNode and "global" in n.modifiers: n.modifiers.remove("global") n.modifiers.add("local") for c in n: global_to_var(c) def transform_into_assignments(n, parent, pi): if type(n) == VarDeclNode and not (type(n[0]) == ExprNode and len(n[0]) == 0): n2 = AssignNode(IdentNode(n.val), n[0]) n.parent.remove(n) parent.insert(pi, n2) for c in n: transform_into_assignments(c, parent, pi) for c in node: if type(c) == VarDeclNode and "global" in c.modifiers: c2 = c.copy() kill_assignments(c2); global_to_var(c2); globals.append(c2) transform_into_assignments(c, c.parent, c.parent.index(c)) #to maintain backward compatibility, add everything in module to #global namespace (for now). if 0: for n in node[:]: if type(n) in [ClassNode, FunctionNode]: n2 = js_parse(""" $s = _es6_module.add_global('$s', $s); """, [n.name, n.name, n.name]); n.parent.insert(n.parent.index(n)+1, n2) elif type(n) == VarDeclNode: varvisit(n, n); def visit(n): if not at_root(n): typespace.error("Import statements cannot be within functions or classes", n) modname = n[0].val depends.add(modname) if len(n) == 1: #import module name n.parent.replace(n, js_parse(""" es6_import(_es6_module, '$s'); """, [modname])); else: slist = StatementList() n.parent.replace(n, slist) for n2 in n[1:]: if n2.name == "*": n3 = js_parse(""" var $s = es6_import(_es6_module, '$s'); """, [n2.bindname, modname]); slist.add(n3); else: n3 = js_parse(""" var $s = es6_import_item(_es6_module, '$s', '$s'); """, [n2.bindname, modname, n2.name]); slist.add(n3) traverse(node, ImportNode, visit) flatten_statementlists(node, typespace) def class_visit(n): n2 = js_parse(""" _es6_module.add_class($s); """, [n.name]) n.parent.insert(n.parent.index(n)+1, n2); traverse(node, ClassNode, class_visit) flatten_statementlists(node, typespace) deps = "[" for i, d in enumerate(depends): if i > 0: deps += ", " deps += '"'+d+'"' deps += "]" fname = glob.g_file if "/" in fname or "\\" in fname: fname = os.path.split(fname)[1] fname = fname.strip().replace("/", "").replace("\\", "").replace(".js", "") safe_fname = "_" + fname.replace(" ", "_").replace(".", "_").replace("-", "_") + "_module"; header = "es6_module_define('"+fname+"', "+deps+", function " + safe_fname + "(_es6_module) {" header += "});"; #print(header) node2 = js_parse(header) func = node2[0][1][2] for n in node: func.add(n) node.children = [] for g in globals: node.add(g) node.add(node2)
def bleh(): for frames in flatframes: fname = f_name(frames) n = js_parse(""" function $s1(scope) { if (_do_frame_debug) console.log("in $s1"); }""", (fname), start_node=FunctionNode) if type(n[1]) != StatementList: n.replace(n[1], StatementList()) n = n[1] func = n while type(func) != FunctionNode: func = func.parent excl = (type(frames.node) == StatementList and type(frames.parent.node) == FunctionNode) if frames.node != None and not excl and type(frames.node) != FunctionNode: f = frames sl = StatementList() f.node[f.node._startcur] = sl frames.funcnode = func frames.subnode = frames.funcnode local_frames = "[" totframes = 0 for i, f in enumerate(frames): if type(f) != Frame: frames.subnode.add(f) frames.leaf = True else: frames.leaf = False if len(local_frames) > 1: local_frames += ", " local_frames += f_ref(f) #.replace("this.", "") totframes += 1 if f.node != None and type(f.node) != FunctionNode: if len(f.node.children) > f.node._startcur + 1: do_conv(f.node, f) if frames.leaf: f2 = f_next(frames) f2 = f2.label if f2 != -1 else -1 frames.subnode.add(js_parse("return [$i, undefined];", [f2], start_node=ReturnNode)); local_frames = "%s_frames = "%f_ref(frames) + local_frames + "];" frames.frames = js_parse(local_frames) frames.totframes = totframes def build_next(f, parent=None): if type(f) != Frame: return subnode = f.subnode if f.label >= 0: # and f.label < 3: n2 = js_parse("this.$s1 = 0;", [f_name(f)], start_node=AssignNode) n2.replace(n2[1], f.funcnode) f.funcnode.name = "(anonymous)" f.funcnode.is_anonymous = True node2.add(n2) #f.funcnode) if f.totframes > 0: if f.node != None and type(f.node) == WhileNode: f2 = f_next(f) f2 = f2.label if f2 != -1 else -1 n = js_parse(""" if (!"placeholder") { return [$i1, undefined]; } """, [f2]) if n == None: typespace.error("internal error", subnode); n2 = find_node(n, StrLitNode); n2.parent.replace(n2, f.node[0]) subnode.add(n) f2 = f_first(f); n.add(js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) elif f.node != None and type(f.node) == TryNode: n = StatementList() if n == None: typespace.error("internal error", subnode); f3 = f_raw_next(f) while f3 != -1 and type(f3.node) != CatchNode: f3 = f_raw_next(f3); if f3 == -1: typespace.error("Orphaned try block", f.node) f3name = "_nfothing" if len(f3.node) > 0: f3name = f3.node[0].gen_js(0).replace("scope.", "") n.add(js_parse(""" this.trystack.push([$i, "$s"]); """, [f3.label, f3name])) f2 = f_first(f); n.add(js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) subnode.add(n) f2.pop_trystack = True elif f.node != None and type(f.node) == IfNode: f2 = f_first(f) f1 = f_raw_next(f) while type(f1.node) != ElseNode and f1.label != len(flatframes): f1 = f_raw_next(f1) if f1.label == len(flatframes): f1 = f_next(f) n = js_parse(""" if (!("placeholder")) { return [$i1, undefined]; } else { return [$i2, undefined]; } """, [f1.label, f2.label]); n2 = find_node(n, StrLitNode) n2.parent.replace(n2, f.node[0].copy()) if n == None: typespace.error("internal error", subnode); f2 = f_first(f); n.add(js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) subnode.add(n) f2.pop_trystack = True elif f.node != None and type(f.node) == ElseNode: f2 = f_first(f) f1 = f_raw_next(f) while type(f1.node) != ElseNode and f1.label != len(flatframes): f1 = f_raw_next(f1) if f1.label == len(flatframes): f1 = f_next(f) n = js_parse(""" return [$i1, undefined]; """, [f2.label]); if n == None: typespace.error("internal error", subnode); f2 = f_first(f); subnode.add(n) elif f.node != None and type(f.node) == CatchNode: f2 = f_first(f) n = js_parse(""" return [$i1, undefined]; """, [f2.label]); if n == None: typespace.error("internal error", subnode); subnode.add(n) elif f.node != None and type(f.node) == ForLoopNode: f2 = f_first(f); f3 = f_next(f) f3 = f3.label if f2 != -1 else -1 f2 = f2.label if f2 != -1 else -1 n = js_parse(""" if ($n) { return [$i, undefined]; } else { return [$i, undefined]; } """, [f.node[0][1], f2, f3]) if n == None: typespace.error("internal error", subnode); subnode.add(n) node2.insert(1, js_parse(""" this[Symbol.iterator] = function() { return this; } """)[0]) for f in flatframes: build_next(f, f.parent) #process returns from within try nodes for f in flatframes: if f.parent != None and type(f.parent.node) == TryNode: def visit_rets1(n2): target = n2[0][0][0].val isyield = n2[0][0][1].val ni = n2.parent.index(n2) if target >= f_next(f.parent).label: n3 = js_parse("this.trystack.pop();")[0] n2.parent.insert(ni, n3) traverse(f.subnode, ReturnNode, visit_rets1, copy_children=True); #process yields for f in flatframes: f2 = f.parent set_yield = None def visit_rets2(n2): if set_yield != None: #print(n2) n2[0][0].replace(n2[0][0][1], set_yield); set_yield = find_node(f.subnode, YieldNode); if set_yield != None: set_yield.parent.remove(set_yield); set_yield = ArrayLitNode(ExprListNode([set_yield[0]])) traverse(f.subnode, ReturnNode, visit_rets2, copy_children=True); def find_parent_frame(f, ntypes, include_first=True): p = f if not include_first: p = p.parent while p != None: if type(p.node) in ntypes: return p p = p.parent return None #process breaks for f in flatframes: f2 = f.parent def visit_rets3(n2): p = n2.parent while not null_node(p) and p != f.subnode: if type(p) in [WhileNode, DoWhileNode, ForLoopNode]: break p = p.parent if p != f.subnode and not null_node(p): return #break corresponds to a loop internal to this frame p = find_parent_frame(f, [WhileNode, DoWhileNode, ForLoopNode], True) if p == None: typespace.error("Invalid break statement (switches within generators aren't supported yet)", n2) f2 = f_next(p) n3 = js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode); n2.parent.replace(n2, n3) traverse(f.subnode, BreakNode, visit_rets3, copy_children=True); #process continues for f in flatframes: f2 = f.parent def visit_rets3(n2): p = n2.parent while not null_node(p) and p != f.subnode: p = p.parent if p != f.subnode and not null_node(p): return #continue corresponds to a loop internal to this frame p = f.parent while p != None: if type(p.node) in [WhileNode, DoWhileNode, ForLoopNode]: break; p = p.parent if p == None: typespace.error("Invalid continue statement") n3 = js_parse("return [$i, undefined];", [p.label], start_node=ReturnNode); n2.parent.replace(n2, n3) traverse(f.subnode, ContinueNode, visit_rets3, copy_children=True); firstnode = js_parse("if (this.first) {\n}", start_node=IfNode) firstnode2 = js_parse("if (this.first) {\n}", start_node=IfNode) firstnode.replace(firstnode[1], StatementList()) firstnode2.replace(firstnode2[1], StatementList()) flatframes[0].subnode.add(firstnode); node2.insert(1, firstnode2[1]); firstnode = firstnode[1] firstnode2 = firstnode2[1] args = list(node.children[0]) for i3 in range(len(args)): argn = args[i3] while type(argn) not in [IdentNode, VarDeclNode]: argn = argn[0] args[i3] = argn.val scope = {} for f in flatframes: scope.update(f.scope) s = "{" j2 = 0 for j, v in enumerate(scope.keys()): if j2 > 0: s += ", " j2 += 1 if v in args: s += "%s:%s" % ("%s_%s"%(v, scope[v]), v) else: s += "%s:undefined" % ("%s_%s"%(v, scope[v])) s += "}" s = "this.scope = %s;\n" % s firstnode2.add(js_parse(s)[0]) #ensure all frames have returns for f in flatframes: if not find_node(f.subnode, ReturnNode): f.subnode.add(js_parse("return [$i, undefined];", [f_next(f).label], start_node=ReturnNode)); framelist = "[" for i, f in enumerate(flatframes): if i > 0: framelist += ", " framelist += "this.frame_%i" % f.label framelist = "this.frames = %s];"%framelist node2.add(js_parse(framelist)); node2.add(js_parse(""" this.cur = 1; this.trystack = new Array(); this.next = function() { var ret; while (this.cur < this.frames.length) { try { ret = this.frames[this.cur].call(this, this.scope); } catch (_generator_error) { if (this.trystack.length > 0) { var ts1 = this.trystack.pop(); this.scope[ts1[1]] = _generator_error; ret = [ts1[0], undefined]; } else { throw _generator_error; } } if (ret[0] == this.frames.length) { return {done : true, value : undefined}; break; } if (ret[0] == this.cur) { console.trace(); console.log("YEEK!") return {done : true, value : undefined}; } this.cur = ret[0]; if (ret[1] != undefined) { return {value : ret[1][0], done : false}; } else { return {value : undefined, done : false}; } } } """, [])) node.parent.replace(node, node2)
def __init__(self): self.functions = {} self.types = {} self.globals = {} self.logrec = [] self.logmap = {} self.builtin_typenames = builtin_types self.func_excludes = set(["inherit", "inherit_multiple", "define_static", "create_prototype", "prior"]) self.functions["inherit"] = js_parse( "function inherit(Object a, Object b) : void { };", start_node=FunctionNode ) for t in builtin_types: n = BuiltinTypeNode(t) n.ret = "class" n.members = {} # """ if t in builtin_code: fn = js_parse(builtin_code[t], exit_on_err=False, start_node=FunctionNode) if fn == None: sys.stderr.write("Warning: could not compile internal type code in JSTypeSpace.__init__\n") # self.error("js_parse error", n); glob.g_error = False return else: self.types[fn.name] = fn for n in fn.children[1:]: if ( type(n) == AssignNode and type(n[0]) == BinOpNode and n[0].op == "." and type(n[0][0]) == IdentNode and n[0][0].val == "this" and type(n[0][1]) == IdentNode ): if type(n[1]) == FunctionNode and n.type == None: n.type = VoidTypeNode() if n[1].type != None: fn.members[n[0][1].val] = n[1].type else: fn.members[n[0][1].val] = n.type else: if t != "Object": fn = js_parse("function %s(value) {};" % t, exit_on_err=False, start_node=FunctionNode) elif t != "undefined": fn = js_parse("function %s() {};" % t, exit_on_err=False, start_node=FunctionNode) """ fn = None #""" if fn == None or len(fn.children) == 0: sys.stderr.write("Warning: could not compile internal type code in JSTypeSpace.__init__\n") # self.error("js_parse error", n); glob.g_error = False return else: while fn != None and type(fn) != FunctionNode and len(fn.children) > 0: fn = fn.children[0] if type(fn) != FunctionNode: sys.stderr.write("\nWarning: could not compile internal type code in JSTypeSpace.__init__\n") return if t == "Array": fn.class_type = "array" else: fn.class_type = "class" fn.is_builtin = True fn.is_native = True fn.ret = n self.functions[t] = fn self.types[t] = fn funcs = self.functions funcs["Number"].add_class_child(funcs["Boolean"]) funcs["Array"].add_class_child(funcs["String"]) funcs["CanIterate"].add_class_child(funcs["Array"]) funcs["CanIterate"].add_class_child(funcs["ObjectMap"]) for k in funcs: if k == "Object": continue f = funcs[k] if f.class_parent == None: funcs["Object"].add_class_child(f)
def __init__(self): self.functions = {} self.types = {} self.globals = {} self.logrec = [] self.logmap = {} self.builtin_typenames = builtin_types self.func_excludes = set(["inherit", "inherit_multiple", "define_static", "create_prototype", "prior"]) self.functions["inherit"] = js_parse("function inherit(Object a, Object b) : void { };", start_node=FunctionNode) for t in builtin_types: n = BuiltinTypeNode(t) n.ret = "class" n.members = {} #""" if t in builtin_code: fn = js_parse(builtin_code[t], exit_on_err=False, start_node=FunctionNode); if fn == None: sys.stderr.write("Warning: could not compile internal type code in JSTypeSpace.__init__\n") #self.error("js_parse error", n); glob.g_error = False return else: self.types[fn.name] = fn for n in fn.children[1:]: if type(n) == AssignNode and type(n[0]) == BinOpNode and \ n[0].op == "." and type(n[0][0]) == IdentNode and n[0][0].val == "this" \ and type(n[0][1]) == IdentNode: if type(n[1]) == FunctionNode and n.type == None: n.type = VoidTypeNode() if n[1].type != None: fn.members[n[0][1].val] = n[1].type else: fn.members[n[0][1].val] = n.type else: if t != "Object": fn = js_parse("function %s(value) {};"%t, exit_on_err=False, start_node=FunctionNode); elif t != "undefined": fn = js_parse("function %s() {};"%t, exit_on_err=False, start_node=FunctionNode); """ fn = None #""" if fn == None or len(fn.children) == 0: sys.stderr.write("Warning: could not compile internal type code in JSTypeSpace.__init__\n") #self.error("js_parse error", n); glob.g_error = False return else: while fn != None and type(fn) != FunctionNode and len(fn.children) > 0: fn = fn.children[0] if type(fn) != FunctionNode: sys.stderr.write("\nWarning: could not compile internal type code in JSTypeSpace.__init__\n") return if t == "Array": fn.class_type = "array" else: fn.class_type = "class" fn.is_builtin = True fn.is_native = True fn.ret = n self.functions[t] = fn self.types[t] = fn funcs = self.functions funcs["Number"].add_class_child(funcs["Boolean"]) funcs["Array"].add_class_child(funcs["String"]) funcs["CanIterate"].add_class_child(funcs["Array"]) funcs["CanIterate"].add_class_child(funcs["ObjectMap"]) for k in funcs: if k == "Object": continue f = funcs[k] if f.class_parent == None: funcs["Object"].add_class_child(f)
def create_type_logger(node, typespace): flatten_statementlists(node, typespace) for i, s in enumerate(node): if i > 5: break; if s.gen_js(0).strip() in ["'no_type_logging'", '"no_type_logging"', "`no_type_logging`"]: return [] def visit(n): args = [] for c in n[0]: n2 = js_parse(""" _log_types($s1, $s2, $s3, $s4, $s5, $s6, $s7); """, ['"'+n.file+'"', str(n.line), '`'+buildkey(n)+'`', '`'+str(c.val)+'`', str(c.val), n.lexpos, n.lexpos2]); args.append(n2) for arg in args: if len(n) == 2 and type(n[1]) == StatementList: n[1].insert(0, arg) else: n.insert(1, arg) def retvisit(n): val = None if len(n) == 0 or n[0].gen_js(0).strip() == "": val = IdentNode("undefined") else: val = n[0] n2 = js_parse("""_log_return($s1, $s2, $s3, $n4, $s5, $s6)""", ['"'+n.file+'"', str(n.line), '`'+buildkey(n)+'`', val, n.lexpos, n.lexpos2], start_node=FuncCallNode) if len(n) == 0: n.append(n2) else: n.replace(n[0], n2) traverse(node, FunctionNode, visit) traverse(node, MethodNode, visit) traverse(node, MethodGetter, visit) traverse(node, MethodSetter, visit) traverse(node, ObjLitSetGet, visit) traverse(node, ReturnNode, retvisit) prefix = js_parse(loggercode) oldprefix = None if len(node) > 0 and "not_a_module" in node[0].gen_js(0): oldprefix = StrLitNode("'not_a_module'") node.prepend(prefix) if oldprefix is not None: node.prepend(oldprefix) flatten_statementlists(node, typespace);
def module_transform(node, typespace): flatten_statementlists(node, typespace) depends = set() def at_root(n): p = n while p != None: if isinstance(p, FunctionNode): return False p = p.parent return True def varvisit(n, startn): return n2 = js_parse(""" _es6_module.add_global('$s', $s); """, [n.val, n.val]) startn.parent.insert(startn.parent.index(startn) + 1, n2) for n2 in n[2:]: varvisit(n2, startn) def exportvisit(n): if not at_root(n): typespace.error( "Export statements cannot be within functions or classes", n) pi = n.parent.index(n) n.parent.remove(n) for n2 in n[:]: n.remove(n2) n.parent.insert(pi, n2) pi += 1 if not n.is_default: n2 = js_parse( """ $s = _es6_module.add_export('$s', $s); """, [n.name, n.name, n.name]) else: n2 = js_parse( """ $s = _es6_module.set_default_export('$s', $s); """, [n.name, n.name, n.name]) n.parent.insert(pi, n2) def exportfromvisit(n): n2 = js_parse( """ import * as _$s1 from '$s1'; for (var k in _$s1) { _es6_module.add_export(k, _$s1[k], true); } """, [n.name.val]) n.parent.replace(n, n2) #print(node) #ahem. if I do this one first, I can use import statements in it :) #. . .also, how cool, it captures the dependencies, too traverse(node, ExportFromNode, exportfromvisit, copy_children=True) traverse(node, ExportNode, exportvisit, copy_children=True) #fetch explicit global variables globals = [] def kill_assignments(n): n.replace(n[0], ExprNode([])) for c in n: if type(c) == VarDeclNode: kill_assignments(c) def global_to_var(n): if type(n) == VarDeclNode and "global" in n.modifiers: n.modifiers.remove("global") n.modifiers.add("local") for c in n: global_to_var(c) def transform_into_assignments(n, parent, pi): if type(n) == VarDeclNode and not (type(n[0]) == ExprNode and len(n[0]) == 0): n2 = AssignNode(IdentNode(n.val), n[0]) n.parent.remove(n) parent.insert(pi, n2) for c in n: transform_into_assignments(c, parent, pi) for c in node: if type(c) == VarDeclNode and "global" in c.modifiers: c2 = c.copy() kill_assignments(c2) global_to_var(c2) globals.append(c2) transform_into_assignments(c, c.parent, c.parent.index(c)) #to maintain backward compatibility, add everything in module to #global namespace (for now). if glob.g_autoglobalize: for n in node[:]: if type(n) in [ClassNode, FunctionNode, VarDeclNode]: if type(n) == VarDeclNode: nname = n.val else: nname = n.name n2 = js_parse( """ $s = _es6_module.add_global('$s', $s); """, [nname, nname, nname]) n.parent.insert(n.parent.index(n) + 1, n2) elif type(n) == VarDeclNode: varvisit(n, n) def visit(n): if not at_root(n): typespace.error( "Import statements cannot be within functions or classes", n) modname = n[0].val depends.add(modname) if len(n) == 1: #import module name n.parent.replace( n, js_parse(""" es6_import(_es6_module, '$s'); """, [modname])) else: slist = StatementList() n.parent.replace(n, slist) for n2 in n[1:]: if n2.name == "*": n3 = js_parse( """ var $s = es6_import(_es6_module, '$s'); """, [n2.bindname, modname]) slist.add(n3) else: n3 = js_parse( """ var $s = es6_import_item(_es6_module, '$s', '$s'); """, [n2.bindname, modname, n2.name]) slist.add(n3) traverse(node, ImportNode, visit) flatten_statementlists(node, typespace) def class_visit(n): n2 = js_parse(""" _es6_module.add_class($s); """, [n.name]) n.parent.insert(n.parent.index(n) + 1, n2) traverse(node, ClassNode, class_visit) flatten_statementlists(node, typespace) deps = "[" for i, d in enumerate(depends): if i > 0: deps += ", " deps += '"' + d + '"' deps += "]" fname = glob.g_file if "/" in fname or "\\" in fname: fname = os.path.split(fname)[1] fname = fname.strip().replace("/", "").replace("\\", "").replace(".js", "") safe_fname = "_" + fname.replace(" ", "_").replace(".", "_").replace( "-", "_") + "_module" header = "es6_module_define('" + fname + "', " + deps + ", function " + safe_fname + "(_es6_module) {" header += "});" #print(header) node2 = js_parse(header) func = node2[0][1][2] for n in node: func.add(n) node.children = [] for g in globals: node.add(g) node.add(node2)
def build_next(f, parent=None): if type(f) != Frame: return subnode = f.subnode if f.label >= 0: # and f.label < 3: n2 = js_parse("this.$s1 = 0;", [f_name(f)], start_node=AssignNode) n2.replace(n2[1], f.funcnode) f.funcnode.name = "(anonymous)" f.funcnode.is_anonymous = True node2.add(n2) #f.funcnode) if f.totframes > 0: if f.node != None and type(f.node) == WhileNode: f2 = f_next(f) f2 = f2.label if f2 != -1 else -1 n = js_parse(""" if (!"placeholder") { return [$i1, undefined]; } """, [f2]) if n == None: typespace.error("internal error", subnode); n2 = find_node(n, StrLitNode); n2.parent.replace(n2, f.node[0]) subnode.add(n) f2 = f_first(f); n.add(js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) elif f.node != None and type(f.node) == TryNode: n = StatementList() if n == None: typespace.error("internal error", subnode); f3 = f_raw_next(f) while f3 != -1 and type(f3.node) != CatchNode: f3 = f_raw_next(f3); if f3 == -1: typespace.error("Orphaned try block", f.node) f3name = "_nfothing" if len(f3.node) > 0: f3name = f3.node[0].gen_js(0).replace("scope.", "") n.add(js_parse(""" this.trystack.push([$i, "$s"]); """, [f3.label, f3name])) f2 = f_first(f); n.add(js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) subnode.add(n) f2.pop_trystack = True elif f.node != None and type(f.node) == IfNode: f2 = f_first(f) f1 = f_raw_next(f) while type(f1.node) != ElseNode and f1.label != len(flatframes): f1 = f_raw_next(f1) if f1.label == len(flatframes): f1 = f_next(f) n = js_parse(""" if (!("placeholder")) { return [$i1, undefined]; } else { return [$i2, undefined]; } """, [f1.label, f2.label]); n2 = find_node(n, StrLitNode) n2.parent.replace(n2, f.node[0].copy()) if n == None: typespace.error("internal error", subnode); f2 = f_first(f); n.add(js_parse("return [$i, undefined];", [f2.label], start_node=ReturnNode)) subnode.add(n) f2.pop_trystack = True elif f.node != None and type(f.node) == ElseNode: f2 = f_first(f) f1 = f_raw_next(f) while type(f1.node) != ElseNode and f1.label != len(flatframes): f1 = f_raw_next(f1) if f1.label == len(flatframes): f1 = f_next(f) n = js_parse(""" return [$i1, undefined]; """, [f2.label]); if n == None: typespace.error("internal error", subnode); f2 = f_first(f); subnode.add(n) elif f.node != None and type(f.node) == CatchNode: f2 = f_first(f) n = js_parse(""" return [$i1, undefined]; """, [f2.label]); if n == None: typespace.error("internal error", subnode); subnode.add(n) elif f.node != None and type(f.node) == ForLoopNode: f2 = f_first(f); f3 = f_next(f) f3 = f3.label if f2 != -1 else -1 f2 = f2.label if f2 != -1 else -1 n = js_parse(""" if ($n) { return [$i, undefined]; } else { return [$i, undefined]; } """, [f.node[0][1], f2, f3]) if n == None: typespace.error("internal error", subnode); subnode.add(n)