/**
   * Liest einen Identifier aus und gibt diesen als String zurck. <br>
   * EBNF:<br>
   * <code>(letter | "_") {letter | "_"|digit};</code>
   *
   * @param firstCanBeNumber
   * @return Identifier.
   */
  private String identifier(boolean firstCanBeNumber) {
    // int start = cfml.getPos();
    if (!cfml.isCurrentLetter() && !cfml.isCurrentSpecial()) {
      if (!firstCanBeNumber) return null;
      else if (!cfml.isCurrentDigit()) return null;
    }

    StringBuffer sb = new StringBuffer();
    // if(CASE_TYPE_UPPER==caseType)
    sb.append(cfml.getCurrentUpper());
    /*else if(CASE_TYPE_ORIGINAL==caseType)
    	sb.append(cfml.getCurrent());
    else
    	sb.append(cfml.getCurrentLower());*/
    do {
      cfml.next();
      if (!(cfml.isCurrentLetter() || cfml.isCurrentDigit() || cfml.isCurrentSpecial())) {
        break;
      }

      // if(CASE_TYPE_UPPER==caseType)
      sb.append(cfml.getCurrentUpper());
      /*else if(CASE_TYPE_ORIGINAL==caseType)
      	sb.append(cfml.getCurrent());
      else
      	sb.append(cfml.getCurrentLower());*/

    } while (cfml.isValidIndex());
    return sb.toString(); // cfml.substringLower(start,cfml.getPos()-start);
  }
 /**
  * Transfomiert eine And (and) Operation. Im Gegensatz zu CFMX , werden "&&" Zeichen auch als And
  * Operatoren anerkannt. <br>
  * EBNF:<br>
  * <code>notOp {("and" | "&&") spaces notOp}; (* "&&" Existiert in CFMX nicht *)</code>
  *
  * @return CFXD Element
  * @throws PageException
  */
 private Ref andOp() throws PageException {
   Ref ref = notOp();
   while (cfml.isValidIndex() && (cfml.forwardIfCurrent("&&") || cfml.forwardIfCurrent("and"))) {
     cfml.removeSpace();
     ref = new And(ref, notOp());
   }
   return ref;
 }
 /**
  * Transfomiert eine Or (or) Operation. Im Gegensatz zu CFMX , werden "||" Zeichen auch als Or
  * Operatoren anerkannt. <br>
  * EBNF:<br>
  * <code>andOp {("or" | "||") spaces andOp}; (* "||" Existiert in CFMX nicht *)</code>
  *
  * @return CFXD Element
  * @throws PageException
  */
 private Ref orOp() throws PageException {
   Ref ref = andOp();
   while (cfml.isValidIndex() && (cfml.forwardIfCurrent("||") || cfml.forwardIfCurrent("or"))) {
     cfml.removeSpace();
     ref = new Or(ref, andOp());
   }
   return ref;
 }
  /**
   * Liest die reinen Zahlen innerhalb des CFMLString aus und gibt diese als Zeichenkette zurck.
   * <br>
   * EBNF:<br>
   * <code>"0"|..|"9";</code>
   *
   * @param rtn
   */
  private void digit(StringBuffer rtn) {

    while (cfml.isValidIndex()) {
      if (!cfml.isCurrentDigit()) break;
      rtn.append(cfml.getCurrentLower());
      cfml.next();
    }
  }
  /**
   * Transfomiert den Exponent Operator (^,exp). Im Gegensatz zu CFMX , werden die Zeichen " exp "
   * auch als Exponent anerkannt. <br>
   * EBNF:<br>
   * <code>clip {("exp"|"^") spaces clip};</code>
   *
   * @return CFXD Element
   * @throws PageException
   */
  private Ref expoOp() throws PageException {
    Ref ref = unaryOp();

    while (cfml.isValidIndex() && (cfml.forwardIfCurrent('^') || cfml.forwardIfCurrent("exp"))) {
      cfml.removeSpace();
      ref = new Exp(ref, unaryOp());
    }
    return ref;
  }
  /**
   * Transfomiert eine Modulus Operation. Im Gegensatz zu CFMX , wird das "%" Zeichen auch als
   * Modulus Operator anerkannt. <br>
   * EBNF:<br>
   * <code>
   * divMultiOp {("mod" | "%") spaces divMultiOp}; (* modulus operator , "%" Existiert in CFMX nicht *)
   * </code>
   *
   * @return CFXD Element
   * @throws PageException
   */
  private Ref modOp() throws PageException {
    Ref ref = divMultiOp();

    while (cfml.isValidIndex() && (cfml.forwardIfCurrent('%') || cfml.forwardIfCurrent("mod"))) {
      ref = _mod(ref);

      // cfml.removeSpace();
      // ref=new Mod(ref,divMultiOp());
    }
    return ref;
  }
 /**
  * Transfomiert eine Not (not) Operation. Im Gegensatz zu CFMX , wird das "!" Zeichen auch als Not
  * Operator anerkannt. <br>
  * EBNF:<br>
  * <code>[("not"|"!") spaces] decsionOp; (* "!" Existiert in CFMX nicht *)</code>
  *
  * @return CFXD Element
  * @throws PageException
  */
 private Ref notOp() throws PageException {
   if (cfml.isValidIndex()) {
     if (cfml.isCurrent('!') && !cfml.isCurrent("!=")) {
       cfml.next();
       cfml.removeSpace();
       return new Not(decsionOp());
     } else if (cfml.forwardIfCurrentAndNoWordAfter("not")) {
       cfml.removeSpace();
       return new Not(decsionOp());
     }
   }
   return decsionOp();
 }
  private Ref newOp() throws PageException {

    int start = cfml.getPos();
    String name = null;
    cfml.removeSpace();

    // first identifier
    name = identifier(true);
    Ref refName = null;
    if (name != null) {
      StringBuilder fullName = new StringBuilder();
      fullName.append(name);
      // Loop over addional identifier
      while (cfml.isValidIndex()) {
        if (cfml.forwardIfCurrent('.')) {
          cfml.removeSpace();
          name = identifier(true);
          if (name == null) throw new ExpressionException("invalid Component declaration");
          cfml.removeSpace();
          fullName.append('.');
          fullName.append(name);
        } else break;
      }
      refName = new LString(fullName.toString());
    } else {
      if (cfml.isCurrentQuoter()) refName = string();
      if (refName == null) {
        cfml.setPos(start);
        return null;
      }
    }
    cfml.removeSpace();

    if (cfml.isCurrent('(')) {
      FunctionLibFunction function = fld.getFunction("_createComponent");
      Ref[] arguments = functionArg("_createComponent", true, function, ')');
      Ref[] args = new Ref[arguments.length + 1];
      for (int i = 0; i < arguments.length; i++) {
        args[i] = arguments[i];
      }
      args[args.length - 1] = refName;
      BIFCall bif = new BIFCall(pc, function, args);
      cfml.removeSpace();
      return bif;
    }
    throw new ExpressionException("invalid Component declaration ");
  }
  private Ref subDynamic(Ref ref) throws PageException {
    String name = null;

    // Loop over nested Variables
    while (cfml.isValidIndex()) {
      // .
      if (cfml.forwardIfCurrent('.')) {
        // Extract next Var String
        cfml.removeSpace();
        name = identifier(true);
        if (name == null) throw new ExpressionException("Invalid identifier");
        cfml.removeSpace();
        ref = new Variable(pc, ref, name);
      }
      // []
      else if (cfml.forwardIfCurrent('[')) {
        cfml.removeSpace();
        ref = new Variable(pc, ref, assignOp());
        cfml.removeSpace();
        if (!cfml.forwardIfCurrent(']'))
          throw new ExpressionException("Invalid Syntax Closing []] not found");
      }
      // finish
      else {
        break;
      }

      cfml.removeSpace();

      if (cfml.isCurrent('(')) {
        if (!(ref instanceof Set))
          throw new ExpressionException(
              "invalid syntax " + ref.getTypeName() + " can't called as function");
        Set set = (Set) ref;
        ref = new UDFCall(pc, set.getParent(), set.getKey(), functionArg(name, false, null, ')'));
      }
    }
    if (ref instanceof railo.runtime.interpreter.ref.var.Scope) {
      railo.runtime.interpreter.ref.var.Scope s = (railo.runtime.interpreter.ref.var.Scope) ref;
      if (s.getScope() == Scope.SCOPE_ARGUMENTS
          || s.getScope() == Scope.SCOPE_LOCAL
          || s.getScope() == ScopeSupport.SCOPE_VAR) {
        ref = new Bind(s);
      }
    }
    return ref;
  }
  /**
   * <font f>Transfomiert eine Vergleichs Operation. <br>
   * EBNF:<br>
   * <code>concatOp {("neq"|"eq"|"gte"|"gt"|"lte"|"lt"|"ct"|
   * "contains"|"nct"|"does not contain") spaces concatOp};
   * (* "ct"=conatains und "nct"=does not contain; Existiert in CFMX nicht *)</code>
   *
   * @return CFXD Element
   * @throws PageException
   */
  private Ref decsionOp() throws PageException {

    Ref ref = concatOp();
    boolean hasChanged = false;
    // ct, contains
    if (cfml.isValidIndex()) {
      do {
        hasChanged = false;
        if (cfml.isCurrent('c')) {
          if (cfml.forwardIfCurrent("ct")) {
            cfml.removeSpace();
            ref = new CT(ref, concatOp());
            hasChanged = true;
          } else if (cfml.forwardIfCurrent("contains")) {
            cfml.removeSpace();
            ref = new CT(ref, concatOp());
            hasChanged = true;
          }
        }
        // does not contain
        else if (cfml.forwardIfCurrent("does", "not", "contain")) {
          cfml.removeSpace();
          ref = new NCT(ref, concatOp());
          hasChanged = true;
        }

        // equal, eq
        else if (cfml.isCurrent("eq") && !cfml.isCurrent("eqv")) {
          cfml.setPos(cfml.getPos() + 2);
          cfml.forwardIfCurrent("ual");
          cfml.removeSpace();
          ref = new EQ(ref, concatOp());
          hasChanged = true;
        }
        // ==
        else if (cfml.forwardIfCurrent("==")) {
          if (cfml.forwardIfCurrent('=')) {
            cfml.removeSpace();
            ref = new EEQ(ref, concatOp());
          } else {
            cfml.removeSpace();
            ref = new EQ(ref, concatOp());
          }
          hasChanged = true;
        }

        // !=
        else if (cfml.forwardIfCurrent("!=")) {
          if (cfml.forwardIfCurrent('=')) {
            cfml.removeSpace();
            ref = new NEEQ(ref, concatOp());
          } else {
            cfml.removeSpace();
            ref = new NEQ(ref, concatOp());
          }
          hasChanged = true;
        }

        // <=/</<>
        else if (cfml.forwardIfCurrent('<')) {
          if (cfml.forwardIfCurrent('=')) {
            cfml.removeSpace();
            ref = new LTE(ref, concatOp());
          } else if (cfml.forwardIfCurrent('>')) {
            cfml.removeSpace();
            ref = new NEQ(ref, concatOp());
          } else {
            cfml.removeSpace();
            ref = new LT(ref, concatOp());
          }
          hasChanged = true;
        }
        // >/>=
        else if (cfml.forwardIfCurrent('>')) {
          if (cfml.forwardIfCurrent('=')) {
            cfml.removeSpace();
            ref = new GTE(ref, concatOp());
          } else {
            cfml.removeSpace();
            ref = new GT(ref, concatOp());
          }
          hasChanged = true;
        }

        // gt, gte, greater than or equal to, greater than
        else if (cfml.isCurrent('g')) {
          if (cfml.forwardIfCurrent("gt")) {
            if (cfml.forwardIfCurrent('e')) {
              cfml.removeSpace();
              ref = new GTE(ref, concatOp());
            } else {
              cfml.removeSpace();
              ref = new GT(ref, concatOp());
            }
            hasChanged = true;
          } else if (cfml.forwardIfCurrent("greater", "than")) {
            if (cfml.forwardIfCurrent("or", "equal", "to", true)) {
              cfml.removeSpace();
              ref = new GTE(ref, concatOp());
            } else {
              cfml.removeSpace();
              ref = new GT(ref, concatOp());
            }
            hasChanged = true;
          } else if (cfml.forwardIfCurrent("ge")) {
            cfml.removeSpace();
            ref = new GTE(ref, concatOp());
            hasChanged = true;
          }
        }

        // is, is not
        else if (cfml.forwardIfCurrent("is")) {
          if (cfml.forwardIfCurrent("not", true)) {
            cfml.removeSpace();
            ref = new NEQ(ref, concatOp());
          } else {
            cfml.removeSpace();
            ref = new EQ(ref, concatOp());
          }
          hasChanged = true;
        }

        // lt, lte, less than, less than or equal to
        else if (cfml.isCurrent('l')) {
          if (cfml.forwardIfCurrent("lt")) {
            if (cfml.forwardIfCurrent('e')) {
              cfml.removeSpace();
              ref = new LTE(ref, concatOp());
            } else {
              cfml.removeSpace();
              ref = new LT(ref, concatOp());
            }
            hasChanged = true;
          } else if (cfml.forwardIfCurrent("less", "than")) {
            if (cfml.forwardIfCurrent("or", "equal", "to", true)) {
              cfml.removeSpace();
              ref = new LTE(ref, concatOp());
            } else {
              cfml.removeSpace();
              ref = new LT(ref, concatOp());
            }
            hasChanged = true;
          } else if (cfml.forwardIfCurrent("le")) {
            cfml.removeSpace();
            ref = new LTE(ref, concatOp());
            hasChanged = true;
          }
        }

        // neq, not equal, nct
        else if (cfml.isCurrent('n')) {
          // Not Equal
          if (cfml.forwardIfCurrent("neq")) {
            cfml.removeSpace();
            ref = new NEQ(ref, concatOp());
            hasChanged = true;
          }
          // Not Equal (Alias)
          else if (cfml.forwardIfCurrent("not", "equal")) {
            cfml.removeSpace();
            ref = new NEQ(ref, concatOp());
            hasChanged = true;
          }
          // nct
          else if (cfml.forwardIfCurrent("nct")) {
            cfml.removeSpace();
            ref = new NCT(ref, concatOp());
            hasChanged = true;
          }
        }
      } while (hasChanged);
    }
    return ref;
  }