예제 #1
0
    /**
     * __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;
      }
    }
예제 #2
0
  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;
    }
  }