Rhinoでgoto Posted 2013年7月22日 by uchida & filed under いろいろ. Tweet 隣に座ってる人がRhinoにgotoが欲しいって言ってたので作りました。rhino1_7R4。 function traverseFn(node, fn) { var parents = []; (function f(node) { fn(node, parents); parents.push(node); for (var c in Iterator(node)) { f(c); } parents.pop(); })(node); } function printNode(node, indent) { var Token = org.mozilla.javascript.Token; indent = indent || 0; var space = Array(indent*2+1).join(' '); print(space + Token.typeToName(node.type) + ' ' + node.getClass()); for (var c in Iterator(node)) { printNode(c, indent+1); } } function transform(node) { var Token = org.mozilla.javascript.Token; var labels = Object.create(null); traverseFn(node, function(node, parents) { if (node.type == Token.LABEL) { var name = node.getName(); if (name.startsWith('$$')) { labels[name] = node; } } }); var gotos = []; traverseFn(node, function(node, parents) { if (node.type == Token.GETELEM) { var target = node.getFirstChild(); var element = node.getLastChild(); if (target && target.type == Token.NAME && target.getIdentifier() == 'goto' && element && element.type == Token.NAME && element.getIdentifier().startsWith('$$')) { var label = labels[element.getIdentifier()]; if (!label) { throw new SyntaxError('not found label "' + element.getIdentifier() + '"'); } gotos.push([label, parents[parents.length-1], parents[parents.length-2]]); } } }); gotos.forEach(function([label, old, parent]) { var l = new org.mozilla.javascript.ast.Jump(Token.GOTO); var target = label.newTarget(); l.target = target; label.addChildToBack(target); parent.replaceChild(old, l); }); } function evalWithGoto(fn) { var context = new org.mozilla.javascript.Context(); var compilerEnv = new org.mozilla.javascript.CompilerEnvirons(); compilerEnv.initFromContext(context); var ast = new org.mozilla.javascript.Parser(compilerEnv).parse(fn.toSource().replace(/^\(function *\( *\) *{|}\)*$/g, ''), '', 1); var irf = new org.mozilla.javascript.IRFactory(compilerEnv); var tree = irf.transformTree(ast); transform(tree); var interpreter = new org.mozilla.javascript.Interpreter(); var bytecode = interpreter.compile(compilerEnv, tree, tree.getEncodedSource(), false); return interpreter.createScriptObject(bytecode, null)(); } evalWithGoto(function() { var i = 0; $$label1: if (i < 5) { print(i++); goto [$$label1]; } }); // 0 // 1 // 2 // 3 // 4 evalWithGoto(function() { for (var i = 0; i < 100; i++) { for (var j = 0; j < 100; j++) { if (j == 3) { goto [$$break1]; } print('i: ' + i + ', j: ' + j); } $$break1: if (i == 5) { goto [$$break2]; } } $$break2: 0; }); // i: 0, j: 0 // i: 0, j: 1 // i: 0, j: 2 // i: 1, j: 0 // i: 1, j: 1 // i: 1, j: 2 // i: 2, j: 0 // i: 2, j: 1 // i: 2, j: 2 // i: 3, j: 0 // i: 3, j: 1 // i: 3, j: 2 // i: 4, j: 0 // i: 4, j: 1 // i: 4, j: 2 // i: 5, j: 0 // i: 5, j: 1 // i: 5, j: 2 evalWithGoto(function() { var i = 5; goto [$$intoLoop]; while (i > 0) { print(i); $$intoLoop: i--; } }); // 4 // 3 // 2 // 1 RhinoはIRFactoryの吐く中間表現ではbreak,continue,switchなどをToken.GOTOとして表現しているので、validなコードから中間表現を作成して特定の構文をToken.GOTOにしてやればそれっぽく動きます。動きました。動かなくても泣かない人向け。 Tweet関連文書:関連文書は見つからんがな
最近のコメント