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; }
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! // } // } // } // } // } // } }
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; } }