Ejemplo n.º 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;
      }
    }