public static ParseTree getParseTree(ParseTree child, FileOptions fo, Target t) { CFunction ac = new CFunction(new __autoconcat__().getName(), t); ParseTree tree = new ParseTree(ac, fo); List<ParseTree> children = new ArrayList<ParseTree>(); children.add(child); tree.setChildren(children); return tree; }
public void run(final List<Variable> vars, Environment myEnv, final MethodScriptComplete done) { // Some things, such as the label are determined at compile time this.CurrentEnv = myEnv; this.CurrentEnv.getEnv(GlobalEnv.class).SetLabel(this.label); MCCommandSender p = myEnv.getEnv(CommandHelperEnvironment.class).GetCommandSender(); if (!hasBeenCompiled || compilerError) { Target target = Target.UNKNOWN; if (left.size() >= 1) { try { target = new Target(left.get(0).line_num, left.get(0).file, left.get(0).column); } catch (NullPointerException e) { // Oh well, we tried to get more information } } throw new ConfigRuntimeException( "Unable to run command, script not yet compiled, or a compiler error occured for that command." + " To see the compile error, run /reloadaliases", null, target); } if (p instanceof MCPlayer) { if (CurrentEnv.getEnv(GlobalEnv.class).GetLabel() != null) { PermissionsResolver perms = CurrentEnv.getEnv(GlobalEnv.class).GetPermissionsResolver(); String[] groups = CurrentEnv.getEnv(GlobalEnv.class).GetLabel().split("/"); for (String group : groups) { if (group.startsWith("-") && perms.inGroup(((MCPlayer) p).getName(), group.substring(1))) { // negative permission throw new ConfigRuntimeException( "You do not have permission to use that command", ExceptionType.InsufficientPermissionException, Target.UNKNOWN); } else if (perms.inGroup(((MCPlayer) p).getName(), group)) { // They do have permission. break; } } } } try { for (ParseTree rootNode : cright) { for (Construct tempNode : rootNode.getAllData()) { if (tempNode instanceof Variable) { if (left_vars == null) { throw new ConfigRuntimeException( "$variables may not be used in this context. Only @variables may be.", null, tempNode.getTarget()); } ((Variable) tempNode) .setVal( new CString( Static.resolveDollarVar( left_vars.get(((Variable) tempNode).getName()), vars) .toString(), tempNode.getTarget())); } } MethodScriptCompiler.registerAutoIncludes(CurrentEnv, this); MethodScriptCompiler.execute(rootNode, CurrentEnv, done, this); } } catch (ConfigRuntimeException ex) { // We don't know how to handle this really, so let's pass it up the chain. throw ex; } catch (CancelCommandException e) { // p.sendMessage(e.getMessage()); // The message in the exception is actually empty } catch (LoopBreakException e) { if (p != null) { p.sendMessage("The break() function must be used inside a for() or foreach() loop"); } System.out.println("The break() function must be used inside a for() or foreach() loop"); } catch (LoopContinueException e) { if (p != null) { p.sendMessage("The continue() function must be used inside a for() or foreach() loop"); } System.out.println("The continue() function must be used inside a for() or foreach() loop"); } catch (FunctionReturnException e) { if (myEnv.getEnv(CommandHelperEnvironment.class).GetEvent() != null) { // Oh, we're running in an event handler. Those know how to catch it too. throw e; } if (p != null) { p.sendMessage("The return() function must be used inside a procedure."); } System.out.println("The return() function must be used inside a procedure."); } catch (Throwable t) { System.out.println("An unexpected exception occured during the execution of a script."); t.printStackTrace(); if (p != null) { p.sendMessage( "An unexpected exception occured during the execution of your script. Please check the console for more information."); } } if (done != null) { done.done(null); } }
/** * __autoconcat__ has special optimization techniques needed, since it's really a part of the * compiler itself, and not so much a function. It being a function is merely a convenience, so * we can defer processing until after parsing. While it is tightly coupled with the compiler, * this is ok, since it's really a compiler mechanism more than a function. * * @param t * @param list * @return */ public ParseTree optimizeSpecial(List<ParseTree> list, boolean returnSConcat) throws ConfigCompileException { // If any of our nodes are CSymbols, we have different behavior boolean inSymbolMode = false; // caching this can save Xn // Assignment // Note that we are walking the array in reverse, because multiple assignments, // say @a = @b = 1 will break if they look like assign(assign(@a, @b), 1), // they need to be assign(@a, assign(@b, 1)). As a variation, we also have // to support something like 1 + @a = 2, which will turn into add(1, assign(@a, 2), // and 1 + @a = @b + 3 would turn into add(1, assign(@a, add(@b, 3))). for (int i = list.size() - 2; i >= 0; i--) { ParseTree node = list.get(i + 1); if (node.getData() instanceof CSymbol && ((CSymbol) node.getData()).isAssignment()) { CSymbol sy = (CSymbol) node.getData(); String conversionFunction = sy.convertAssignment(); ParseTree lhs = list.get(i); if (conversionFunction != null) { ParseTree conversion = new ParseTree( new CFunction(conversionFunction, node.getTarget()), node.getFileOptions()); // grab the entire right side, and turn it into an operation with the left side. // We have to take the entire right up to the next construct not followed by an // operator (or the end) try { ParseTree rhs; if (i < list.size() - 3) { // Need to autoconcat ParseTree ac = new ParseTree( new CFunction("__autoconcat__", Target.UNKNOWN), lhs.getFileOptions()); int index = i + 2; ac.addChild(list.get(index)); list.remove(index); while (true) { if (list.size() > index && list.get(index).getData() instanceof CSymbol) { // Add the next two children, (the symbol then the item) // and continue. ac.addChild(list.get(index)); ac.addChild(list.get(index + 1)); list.remove(index); list.remove(index); continue; } else { break; } } // Set this subset into the correct slot, the rest of the // code will grab it correctly that way. list.add(i + 2, ac); } rhs = list.get(i + 2); conversion.addChild(lhs); conversion.addChild(rhs); list.set(i + 2, conversion); } catch (IndexOutOfBoundsException e) { throw new ConfigCompileException("Invalid symbol listed", node.getTarget()); } } // Simple assignment now ParseTree assign = new ParseTree(new CFunction("assign", node.getTarget()), node.getFileOptions()); ParseTree rhs; if (i < list.size() - 3) { // Need to autoconcat ParseTree ac = new ParseTree( new CFunction("__autoconcat__", Target.UNKNOWN), lhs.getFileOptions()); int index = i + 2; ac.addChild(list.get(index)); list.remove(index); while (true) { if (list.size() > index && list.get(index).getData() instanceof CSymbol) { // Add the next two children, (the symbol then the item) // and continue. ac.addChild(list.get(index)); ac.addChild(list.get(index + 1)); list.remove(index); list.remove(index); continue; } else { break; } } // Set this subset into the correct slot, the rest of the // code will grab it correctly that way. list.add(i + 2, ac); } rhs = list.get(i + 2); assign.addChild(lhs); assign.addChild(rhs); list.set(i, assign); list.remove(i + 1); list.remove(i + 1); } } // postfix for (int i = 0; i < list.size(); i++) { ParseTree node = list.get(i); if (node.getData() instanceof CSymbol) { inSymbolMode = true; } if (node.getData() instanceof CSymbol && ((CSymbol) node.getData()).isPostfix()) { if (i - 1 >= 0) { // && list.get(i - 1).getData() instanceof IVariable) { CSymbol sy = (CSymbol) node.getData(); ParseTree conversion; if (sy.val().equals("++")) { conversion = new ParseTree(new CFunction("postinc", node.getTarget()), node.getFileOptions()); } else { conversion = new ParseTree(new CFunction("postdec", node.getTarget()), node.getFileOptions()); } conversion.addChild(list.get(i - 1)); list.set(i - 1, conversion); list.remove(i); i--; } } } if (inSymbolMode) { try { // look for unary operators for (int i = 0; i < list.size() - 1; i++) { ParseTree node = list.get(i); if (node.getData() instanceof CSymbol && ((CSymbol) node.getData()).isUnary()) { ParseTree conversion; if (node.getData().val().equals("-") || node.getData().val().equals("+")) { // These are special, because if the values to the left isn't a symbol, // it's not unary if ((i == 0 || list.get(i - 1).getData() instanceof CSymbol) && !(list.get(i + 1).getData() instanceof CSymbol)) { if (node.getData().val().equals("-")) { // We have to negate it conversion = new ParseTree( new CFunction("neg", node.getTarget()), node.getFileOptions()); } else { conversion = new ParseTree(new CFunction("p", node.getTarget()), node.getFileOptions()); } } else { continue; } } else { conversion = new ParseTree( new CFunction(((CSymbol) node.getData()).convert(), node.getTarget()), node.getFileOptions()); } conversion.addChild(list.get(i + 1)); list.set(i, conversion); list.remove(i + 1); i--; } } for (int i = 0; i < list.size() - 1; i++) { ParseTree next = list.get(i + 1); if (next.getData() instanceof CSymbol) { if (((CSymbol) next.getData()).isExponential()) { ParseTree conversion = new ParseTree( new CFunction(((CSymbol) next.getData()).convert(), next.getTarget()), next.getFileOptions()); conversion.addChild(list.get(i)); conversion.addChild(list.get(i + 2)); list.set(i, conversion); list.remove(i + 1); list.remove(i + 1); i--; } } } // Multiplicative for (int i = 0; i < list.size() - 1; i++) { ParseTree next = list.get(i + 1); if (next.getData() instanceof CSymbol) { CSymbol nextData = (CSymbol) next.getData(); if (nextData.isMultaplicative() && !nextData.isAssignment()) { ParseTree conversion = new ParseTree( new CFunction(((CSymbol) next.getData()).convert(), next.getTarget()), next.getFileOptions()); conversion.addChild(list.get(i)); conversion.addChild(list.get(i + 2)); list.set(i, conversion); list.remove(i + 1); list.remove(i + 1); i--; } } } // Additive for (int i = 0; i < list.size() - 1; i++) { ParseTree next = list.get(i + 1); if (next.getData() instanceof CSymbol && ((CSymbol) next.getData()).isAdditive() && !((CSymbol) next.getData()).isAssignment()) { ParseTree conversion = new ParseTree( new CFunction(((CSymbol) next.getData()).convert(), next.getTarget()), next.getFileOptions()); conversion.addChild(list.get(i)); conversion.addChild(list.get(i + 2)); list.set(i, conversion); list.remove(i + 1); list.remove(i + 1); i--; } } // relational for (int i = 0; i < list.size() - 1; i++) { ParseTree node = list.get(i + 1); if (node.getData() instanceof CSymbol && ((CSymbol) node.getData()).isRelational()) { CSymbol sy = (CSymbol) node.getData(); ParseTree conversion = new ParseTree( new CFunction(sy.convert(), node.getTarget()), node.getFileOptions()); conversion.addChild(list.get(i)); conversion.addChild(list.get(i + 2)); list.set(i, conversion); list.remove(i + 1); list.remove(i + 1); i--; } } // equality for (int i = 0; i < list.size() - 1; i++) { ParseTree node = list.get(i + 1); if (node.getData() instanceof CSymbol && ((CSymbol) node.getData()).isEquality()) { CSymbol sy = (CSymbol) node.getData(); ParseTree conversion = new ParseTree( new CFunction(sy.convert(), node.getTarget()), node.getFileOptions()); conversion.addChild(list.get(i)); conversion.addChild(list.get(i + 2)); list.set(i, conversion); list.remove(i + 1); list.remove(i + 1); i--; } } // logical and for (int i = 0; i < list.size() - 1; i++) { ParseTree node = list.get(i + 1); if (node.getData() instanceof CSymbol && ((CSymbol) node.getData()).isLogicalAnd()) { CSymbol sy = (CSymbol) node.getData(); ParseTree conversion = new ParseTree( new CFunction(sy.convert(), node.getTarget()), node.getFileOptions()); conversion.addChild(list.get(i)); conversion.addChild(list.get(i + 2)); list.set(i, conversion); list.remove(i + 1); list.remove(i + 1); i--; } } // logical or for (int i = 0; i < list.size() - 1; i++) { ParseTree node = list.get(i + 1); if (node.getData() instanceof CSymbol && ((CSymbol) node.getData()).isLogicalOr()) { CSymbol sy = (CSymbol) node.getData(); ParseTree conversion = new ParseTree( new CFunction(sy.convert(), node.getTarget()), node.getFileOptions()); conversion.addChild(list.get(i)); conversion.addChild(list.get(i + 2)); list.set(i, conversion); list.remove(i + 1); list.remove(i + 1); i--; } } } catch (IndexOutOfBoundsException e) { throw new ConfigCompileException( "Unexpected symbol (" + list.get(list.size() - 1).getData().val() + "). Did you forget to quote your symbols?", list.get(list.size() - 1).getTarget()); } } // Look for a CEntry here if (list.size() >= 1) { ParseTree node = list.get(0); if (node.getData() instanceof CLabel) { ParseTree value = new ParseTree( new CFunction("__autoconcat__", node.getTarget()), node.getFileOptions()); for (int i = 1; i < list.size(); i++) { value.addChild(list.get(i)); } ParseTree ce = new ParseTree(new CFunction("centry", node.getTarget()), node.getFileOptions()); ce.addChild(node); ce.addChild(value); return ce; } } // We've eliminated the need for __autoconcat__ either way, however, if there are still // arguments // left, it needs to go to sconcat, which MAY be able to be further optimized, but that will // be handled in MethodScriptCompiler's optimize function. Also, we must scan for // CPreIdentifiers, // which may be turned into a function if (list.size() == 1) { return list.get(0); } else { for (int i = 0; i < list.size(); i++) { if (list.get(i).getData().getCType() == Construct.ConstructType.IDENTIFIER) { if (i == 0) { // Yup, it's an identifier CFunction identifier = new CFunction(list.get(i).getData().val(), list.get(i).getTarget()); list.remove(0); ParseTree child = list.get(0); if (list.size() > 1) { child = new ParseTree(new CFunction("sconcat", Target.UNKNOWN), child.getFileOptions()); child.setChildren(list); } try { Function f = (Function) FunctionList.getFunction(identifier); ParseTree node = new ParseTree( f.execs(identifier.getTarget(), null, null, child), child.getFileOptions()); return node; } catch (Exception e) { throw new Error("Unknown function " + identifier.val() + "?"); } } else { // Hmm, this is weird. I'm not sure what condition this can happen in throw new ConfigCompileException( "Unexpected IDENTIFIER? O.o Please report a bug," + " and include the script you used to get this error.", Target.UNKNOWN); } } } ParseTree tree; FileOptions options = new FileOptions(new HashMap<String, String>()); if (!list.isEmpty()) { options = list.get(0).getFileOptions(); } if (returnSConcat) { tree = new ParseTree(new CFunction("sconcat", Target.UNKNOWN), options); } else { tree = new ParseTree(new CFunction("concat", Target.UNKNOWN), options); } tree.setChildren(list); return tree; } }
public Construct eval(ParseTree c, final Environment env) throws CancelCommandException { final Construct m = c.getData(); CurrentEnv = env; // TODO: Reevaluate if this line is needed. The script doesn't know the label inherently, the // environment does, and setting it this way taints the environment. CurrentEnv.getEnv(GlobalEnv.class).SetLabel(this.label); if (m.getCType() == ConstructType.FUNCTION) { env.getEnv(GlobalEnv.class).SetScript(this); if (m.val().matches("^_[^_].*")) { // Not really a function, so we can't put it in Function. Procedure p = getProc(m.val()); if (p == null) { throw new ConfigRuntimeException( "Unknown procedure \"" + m.val() + "\"", ExceptionType.InvalidProcedureException, m.getTarget()); } Environment newEnv = env; try { newEnv = env.clone(); } catch (Exception e) { } ProfilePoint pp = env.getEnv(GlobalEnv.class).GetProfiler().start(m.val() + " execution", LogLevel.INFO); Construct ret = p.cexecute(c.getChildren(), newEnv, m.getTarget()); pp.stop(); return ret; } final Function f; try { f = (Function) FunctionList.getFunction(m); } catch (ConfigCompileException e) { // Turn it into a config runtime exception. This shouldn't ever happen though. throw new ConfigRuntimeException("Unable to find function " + m.val(), m.getTarget()); } // We have special handling for loop and other control flow functions if (f instanceof assign) { if (c.getChildAt(0).getData() instanceof CFunction) { CFunction test = (CFunction) c.getChildAt(0).getData(); if (test.val().equals("array_get")) { env.getEnv(GlobalEnv.class).SetFlag("array_get_alt_mode", true); Construct arrayAndIndex = eval(c.getChildAt(0), env); env.getEnv(GlobalEnv.class).ClearFlag("array_get_alt_mode"); return ((assign) f) .array_assign(m.getTarget(), env, arrayAndIndex, eval(c.getChildAt(1), env)); } } } if (f.useSpecialExec()) { ProfilePoint p = null; if (f.shouldProfile() && env.getEnv(GlobalEnv.class).GetProfiler() != null && env.getEnv(GlobalEnv.class).GetProfiler().isLoggable(f.profileAt())) { p = env.getEnv(GlobalEnv.class) .GetProfiler() .start(f.profileMessageS(c.getChildren()), f.profileAt()); } Construct ret = f.execs(m.getTarget(), env, this, c.getChildren().toArray(new ParseTree[] {})); if (p != null) { p.stop(); } return ret; } ArrayList<Construct> args = new ArrayList<Construct>(); for (ParseTree c2 : c.getChildren()) { args.add(eval(c2, env)); } if (f.isRestricted()) { boolean perm = Static.hasCHPermission(f.getName(), env); if (!perm) { throw new ConfigRuntimeException( "You do not have permission to use the " + f.getName() + " function.", ExceptionType.InsufficientPermissionException, m.getTarget()); } } Object[] a = args.toArray(); Construct[] ca = new Construct[a.length]; for (int i = 0; i < a.length; i++) { ca[i] = (Construct) a[i]; // CArray, CBoolean, CDouble, CInt, CNull, CString, CVoid, CEntry, CLabel (only to sconcat). if (!(ca[i] instanceof CArray || ca[i] instanceof CBoolean || ca[i] instanceof CDouble || ca[i] instanceof CInt || ca[i] instanceof CNull || ca[i] instanceof CString || ca[i] instanceof CVoid || ca[i] instanceof IVariable || ca[i] instanceof CEntry || ca[i] instanceof CLabel) && (!f.getName().equals("__autoconcat__") && (ca[i] instanceof CLabel))) { throw new ConfigRuntimeException( "Invalid Construct (" + ca[i].getClass() + ") being passed as an argument to a function (" + f.getName() + ")", null, m.getTarget()); } if (env.getEnv(GlobalEnv.class).GetFlag("array_get_alt_mode") == Boolean.TRUE && i == 0) { continue; } while (f.preResolveVariables() && ca[i] instanceof IVariable) { IVariable cur = (IVariable) ca[i]; ca[i] = env.getEnv(GlobalEnv.class).GetVarList().get(cur.getName(), cur.getTarget()).ival(); } } { // It takes a moment to generate the toString of some things, so lets not do it // if we actually aren't going to profile ProfilePoint p = null; if (f.shouldProfile() && env.getEnv(GlobalEnv.class).GetProfiler() != null && env.getEnv(GlobalEnv.class).GetProfiler().isLoggable(f.profileAt())) { p = env.getEnv(GlobalEnv.class).GetProfiler().start(f.profileMessage(ca), f.profileAt()); } Construct ret = f.exec(m.getTarget(), env, ca); if (p != null) { p.stop(); } return ret; } } else if (m.getCType() == ConstructType.VARIABLE) { return new CString(m.val(), m.getTarget()); } else { return m; } }