public List<Variable> getVariables(String command) { String[] cmds = command.split(" "); List<String> args = new ArrayList(Arrays.asList(cmds)); StringBuilder lastVar = new StringBuilder(); ArrayList<Variable> vars = new ArrayList<Variable>(); Variable v = null; for (int j = 0; j < cleft.size(); j++) { try { if (cleft.get(j).getCType() == ConstructType.VARIABLE) { if (((Variable) cleft.get(j)).getName().equals("$")) { for (int k = j; k < args.size(); k++) { lastVar.append(args.get(k).trim()).append(" "); } v = new Variable( ((Variable) cleft.get(j)).getName(), lastVar.toString().trim(), Target.UNKNOWN); } else { v = new Variable(((Variable) cleft.get(j)).getName(), args.get(j), Target.UNKNOWN); } } } catch (IndexOutOfBoundsException e) { v = new Variable( ((Variable) cleft.get(j)).getName(), ((Variable) cleft.get(j)).getDefault(), Target.UNKNOWN); } if (v != null) { vars.add(v); } } return vars; }
private boolean compileLeft() { cleft = new ArrayList<Construct>(); if (label != null && label.startsWith("!")) { if (label.length() > 1) { label = label.substring(1); } nolog = true; } for (int i = 0; i < left.size(); i++) { Token t = left.get(i); if (t.value.startsWith("/")) { cleft.add(new Command(t.val(), t.target)); } else if (t.type == Token.TType.VARIABLE) { cleft.add(new Variable(t.val(), null, t.target)); } else if (t.type.equals(TType.FINAL_VAR)) { Variable v = new Variable(t.val(), null, t.target); v.setFinal(true); cleft.add(v); } else if (t.type.equals(TType.LSQUARE_BRACKET)) { if (i + 2 < left.size() && left.get(i + 2).type.equals(TType.OPT_VAR_ASSIGN)) { Variable v = new Variable(left.get(i + 1).val(), left.get(i + 3).val(), t.target); v.setOptional(true); if (left.get(i + 1).type.equals(TType.FINAL_VAR)) { v.setFinal(true); } cleft.add(v); i += 4; } else { t = left.get(i + 1); Variable v = new Variable(t.val(), null, t.target); v.setOptional(true); if (t.val().equals("$")) { v.setFinal(true); } cleft.add(v); i += 2; } } else { cleft.add(new CString(t.val(), t.getTarget())); } } return true; }
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); } }
public void checkAmbiguous(List<Script> scripts) throws ConfigCompileException { // for (int i = 0; i < scripts.size(); i++) { List<Construct> thisCommand = this.cleft; for (int j = 0; j < scripts.size(); j++) { List<Construct> thatCommand = scripts.get(j).cleft; if (thatCommand == null) { // it hasn't been compiled yet. return; } if (this.cleft == scripts.get(j).cleft) { // Of course this command is going to match it's own signature continue; } boolean soFarAMatch = true; for (int k = 0; k < thisCommand.size(); k++) { try { Construct c1 = thisCommand.get(k); Construct c2 = thatCommand.get(k); if (c1.getCType() != c2.getCType() || ((c1 instanceof Variable) && !((Variable) c1).isOptional())) { soFarAMatch = false; } else { // It's a literal, check to see if it's the same literal if (c1.nval() == null || !c1.val().equals(c2.val())) { soFarAMatch = false; } } } catch (IndexOutOfBoundsException e) { /** * The two commands: /cmd $var1 [$var2] /cmd $var1 would cause this exception to be * thrown, but the signatures are the same, so the fact that they've matched this far * means they are ambiguous. However, /cmd $var1 $var2 /cmd $var1 is not ambiguous */ // thatCommand is the short one if (!(thisCommand.get(k) instanceof Variable) || (thisCommand.get(k) instanceof Variable && !((Variable) thisCommand.get(k)).isOptional())) { soFarAMatch = false; } } } if (thatCommand.size() > thisCommand.size()) { int k = thisCommand.size(); // thisCommand is the short one if (!(thatCommand.get(k) instanceof Variable) || (thatCommand.get(k) instanceof Variable && !((Variable) thatCommand.get(k)).isOptional())) { soFarAMatch = false; } } if (soFarAMatch) { String commandThis = ""; for (Construct c : thisCommand) { commandThis += c.val() + " "; } String commandThat = ""; for (Construct c : thatCommand) { commandThat += c.val() + " "; } scripts.get(j).compilerError = true; this.compilerError = true; throw new ConfigCompileException( "The command " + commandThis.trim() + " is ambiguous because it " + "matches the signature of " + commandThat.trim(), thisCommand.get(0).getTarget()); } } // //Also, check for undefined variables on the right, and unused variables on the left // ArrayList<String> left_copy = new ArrayList<String>(); // for (Map.Entry<String, Variable> v : left_vars.entrySet()) { // left_copy.add(v.getValue().getName()); // } // Arrays.asList(new String[]{}).toArray(new String[]{}); // for (ParseTree gtn : cright) { // GenericTree<Construct> tree = new GenericTree<Construct>(); // tree.setRoot(gtn); // List<ParseTree> builtTree = tree.build(GenericTreeTraversalOrderEnum.PRE_ORDER); // for (ParseTree c : builtTree) { // if (c.getData() instanceof Variable) { // for (Map.Entry<String, Variable> v : left_vars.entrySet()) { // if (v.getValue().getName().equals(((Variable) c.getData()).getName())) // { // //Found it, remove this from the left_copy, and break // left_copy.remove(v.getValue().getName()); // break; // //TODO: Layton! // } // } // } // } // } // } }
private boolean verifyLeft() throws ConfigCompileException { boolean inside_opt_var = false; boolean after_no_def_opt_var = false; String lastVar = null; // Go through our token list and readjust non-spaced symbols. Any time we combine a symbol, // the token becomes a string List<Token> tempLeft = new ArrayList<Token>(); for (int i = 0; i < left.size(); i++) { Token t = left.get(i); if (i == 0 && t.type == TType.NEWLINE) { continue; } if (t.type.isSymbol() && left.size() - 1 > i && left.get(i + 1).type != TType.WHITESPACE) { StringBuilder b = new StringBuilder(); b.append(t.value); i++; Token m = left.get(i); while (m.type.isSymbol() && m.type != TType.WHITESPACE) { b.append(m.value); i++; m = left.get(i); } if (m.type != TType.WHITESPACE && m.type != TType.LABEL) { b.append(m.value); } t = new Token(TType.STRING, b.toString(), t.target); if (m.type == TType.LABEL) { tempLeft.add(t); tempLeft.add(m); continue; } } // Go ahead and toString the other symbols too if (t.type.isSymbol()) { t = new Token(TType.STRING, t.value, t.target); } if (t.type != TType.WHITESPACE) { tempLeft.add(t); } } // Look through and concatenate all tokens before the label, if such exists. boolean hasLabel = false; for (int i = 0; i < tempLeft.size(); i++) { if (tempLeft.get(i).type == TType.LABEL) { hasLabel = true; break; } } if (hasLabel) { StringBuilder b = new StringBuilder(); int count = 0; while (tempLeft.get(count).type != TType.LABEL) { b.append(tempLeft.get(count).val()); count++; } tempLeft.set(0, new Token(TType.STRING, b.toString(), Target.UNKNOWN)); for (int i = 0; i < count - 1; i++) { tempLeft.remove(1); } } left = tempLeft; for (int j = 0; j < left.size(); j++) { Token t = left.get(j); // Token prev_token = j - 2 >= 0?c.tokens.get(j - 2):new Token(TType.UNKNOWN, "", t.line_num); Token last_token = j - 1 >= 0 ? left.get(j - 1) : new Token(TType.UNKNOWN, "", t.getTarget()); Token next_token = j + 1 < left.size() ? left.get(j + 1) : new Token(TType.UNKNOWN, "", t.getTarget()); Token after_token = j + 2 < left.size() ? left.get(j + 2) : new Token(TType.UNKNOWN, "", t.getTarget()); if (j == 0) { if (next_token.type == TType.LABEL) { this.label = t.val(); j--; left.remove(0); left.remove(0); continue; } } if (t.type == TType.LABEL) { continue; } if (t.type.equals(TType.FINAL_VAR) && left.size() - j >= 5) { throw new ConfigCompileException( "FINAL_VAR must be the last argument in the alias", t.target); } if (t.type.equals(TType.VARIABLE) || t.type.equals(TType.FINAL_VAR)) { Variable v = new Variable(t.val(), null, t.target); lastVar = t.val(); v.setOptional(last_token.type.equals(TType.LSQUARE_BRACKET)); left_vars.put(t.val(), v); if (v.isOptional()) { after_no_def_opt_var = true; } else { v.setDefault(""); } } // We're looking for a command up front if (j == 0 && !t.value.startsWith("/")) { if (!(next_token.type == TType.LABEL && after_token.type == TType.COMMAND)) { throw new ConfigCompileException( "Expected command (/command) at start of alias." + " Instead, found " + t.type + " (" + t.val() + ")", t.target); } } if (last_token.type.equals(TType.LSQUARE_BRACKET)) { inside_opt_var = true; if (!(t.type.equals(TType.FINAL_VAR) || t.type.equals(TType.VARIABLE))) { throw new ConfigCompileException( "Unexpected " + t.type.toString() + " (" + t.val() + "), was expecting" + " a $variable", t.target); } } if (after_no_def_opt_var && !inside_opt_var) { if (t.type.equals(TType.VARIABLE) || t.type.equals(TType.FINAL_VAR)) { throw new ConfigCompileException( "You cannot have anything other than optional arguments after your" + " first optional argument.", t.target); } } if (!t.type.equals(TType.LSQUARE_BRACKET) && !t.type.equals(TType.OPT_VAR_ASSIGN) && !t.type.equals(TType.RSQUARE_BRACKET) && !t.type.equals(TType.VARIABLE) && !t.type.equals(TType.LIT) && !t.type.equals(TType.COMMAND) && !t.type.equals(TType.FINAL_VAR)) { if (j - 1 > 0 && !( /*t.type.equals(TType.STRING) &&*/ left.get(j - 1).type.equals(TType.OPT_VAR_ASSIGN))) { throw new ConfigCompileException("Unexpected " + t.type + " (" + t.val() + ")", t.target); } } if (last_token.type.equals(TType.COMMAND)) { if (!(t.type.equals(TType.VARIABLE) || t.type.equals(TType.LSQUARE_BRACKET) || t.type.equals(TType.FINAL_VAR) || t.type.equals(TType.LIT))) { throw new ConfigCompileException( "Unexpected " + t.type + " (" + t.val() + ") after command", t.target); } } if (inside_opt_var && t.type.equals(TType.OPT_VAR_ASSIGN)) { if (!((next_token.type.equals(TType.STRING) || next_token.type.equals(TType.LIT)) && after_token.type.equals(TType.RSQUARE_BRACKET) || (next_token.type.equals(TType.RSQUARE_BRACKET)))) { throw new ConfigCompileException("Unexpected token in optional variable", t.target); } else if (next_token.type.equals(TType.STRING) || next_token.type.equals(TType.LIT)) { left_vars.get(lastVar).setDefault(next_token.val()); } } if (t.type.equals(TType.RSQUARE_BRACKET)) { if (!inside_opt_var) { throw new ConfigCompileException("Unexpected " + t.type.toString(), t.target); } inside_opt_var = false; // if (last_token.type.equals(TType.VARIABLE) // || last_token.type.equals(TType.FINAL_VAR)) { // after_no_def_opt_var = true; // } } } return true; }
public boolean match(String command) { if (cleft == null) { // The compilation error happened during the signature declaration, so // we can't match it, nor can we even tell if it's what they intended for us to run. return false; } boolean case_sensitive = Prefs.CaseSensitive(); String[] cmds = command.split(" "); List<String> args = new ArrayList(Arrays.asList(cmds)); boolean isAMatch = true; StringBuilder lastVar = new StringBuilder(); int lastJ = 0; try { for (int j = 0; j < cleft.size(); j++) { if (!isAMatch) { break; } lastJ = j; Construct c = cleft.get(j); String arg = args.get(j); if (c.getCType() != ConstructType.VARIABLE) { if (case_sensitive && !c.val().equals(arg) || !case_sensitive && !c.val().equalsIgnoreCase(arg)) { isAMatch = false; continue; } } else { // It's a variable. If it's optional, the rest of them are optional too, so as long as the // size of // args isn't greater than the size of cleft, it's a match if (((Variable) c).isOptional()) { if (args.size() <= cleft.size()) { return true; } else { Construct fin = cleft.get(cleft.size() - 1); if (fin instanceof Variable) { if (((Variable) fin).isFinal()) { return true; } } return false; } } } if (j == cleft.size() - 1) { if (cleft.get(j).getCType() == ConstructType.VARIABLE) { Variable lv = (Variable) cleft.get(j); if (lv.isFinal()) { for (int a = j; a < args.size(); a++) { if (lastVar.length() == 0) { lastVar.append(args.get(a)); } else { lastVar.append(" ").append(args.get(a)); } } } } } } } catch (IndexOutOfBoundsException e) { if (cleft.get(lastJ).getCType() != ConstructType.VARIABLE || cleft.get(lastJ).getCType() == ConstructType.VARIABLE && !((Variable) cleft.get(lastJ)).isOptional()) { isAMatch = false; } } boolean lastIsFinal = false; if (cleft.get(cleft.size() - 1) instanceof Variable) { Variable v = (Variable) cleft.get(cleft.size() - 1); if (v.isFinal()) { lastIsFinal = true; } } if ((cleft.get(lastJ) instanceof Variable && ((Variable) cleft.get(lastJ)).isOptional())) { return true; } if (cleft.size() != cmds.length && !lastIsFinal) { isAMatch = false; } // ArrayList<Variable> vars = new ArrayList<Variable>(); // Variable v = null; // for (int j = 0; j < cleft.size(); j++) { // try { // if (cleft.get(j).getCType() == ConstructType.VARIABLE) { // if (((Variable) cleft.get(j)).getName().equals("$")) { // v = new Variable(((Variable) cleft.get(j)).getName(), // lastVar.toString(), Target.UNKNOWN); // } else { // v = new Variable(((Variable) cleft.get(j)).getName(), // args.get(j), Target.UNKNOWN); // } // } // } catch (IndexOutOfBoundsException e) { // v = new Variable(((Variable) cleft.get(j)).getName(), // ((Variable) cleft.get(j)).getDefault(), Target.UNKNOWN); // } // if (v != null) { // vars.add(v); // } // } return isAMatch; }