Package railo.transformer.cfml.expression

Source Code of railo.transformer.cfml.expression.AbstrCFMLExprTransformer

package railo.transformer.cfml.expression;


import java.util.ArrayList;
import java.util.Iterator;

import railo.runtime.Component;
import railo.runtime.config.NullSupportHelper;
import railo.runtime.exp.CasterException;
import railo.runtime.exp.TemplateException;
import railo.runtime.functions.other.CreateUniqueId;
import railo.runtime.op.Caster;
import railo.runtime.type.scope.Scope;
import railo.runtime.type.scope.ScopeSupport;
import railo.runtime.type.util.UDFUtil;
import railo.transformer.bytecode.BytecodeException;
import railo.transformer.bytecode.Literal;
import railo.transformer.bytecode.Page;
import railo.transformer.bytecode.Position;
import railo.transformer.bytecode.cast.CastDouble;
import railo.transformer.bytecode.cast.CastString;
import railo.transformer.bytecode.expression.ClosureAsExpression;
import railo.transformer.bytecode.expression.ExprDouble;
import railo.transformer.bytecode.expression.ExprString;
import railo.transformer.bytecode.expression.Expression;
import railo.transformer.bytecode.expression.ExpressionInvoker;
import railo.transformer.bytecode.expression.Invoker;
import railo.transformer.bytecode.expression.var.Argument;
import railo.transformer.bytecode.expression.var.Assign;
import railo.transformer.bytecode.expression.var.BIF;
import railo.transformer.bytecode.expression.var.DataMember;
import railo.transformer.bytecode.expression.var.DynAssign;
import railo.transformer.bytecode.expression.var.FunctionMember;
import railo.transformer.bytecode.expression.var.Member;
import railo.transformer.bytecode.expression.var.NamedArgument;
import railo.transformer.bytecode.expression.var.UDF;
import railo.transformer.bytecode.expression.var.Variable;
import railo.transformer.bytecode.literal.Identifier;
import railo.transformer.bytecode.literal.LitBoolean;
import railo.transformer.bytecode.literal.LitDouble;
import railo.transformer.bytecode.literal.LitString;
import railo.transformer.bytecode.literal.Null;
import railo.transformer.bytecode.op.OPDecision;
import railo.transformer.bytecode.op.OpBool;
import railo.transformer.bytecode.op.OpContional;
import railo.transformer.bytecode.op.OpDouble;
import railo.transformer.bytecode.op.OpElvis;
import railo.transformer.bytecode.op.OpNegate;
import railo.transformer.bytecode.op.OpNegateNumber;
import railo.transformer.bytecode.op.OpString;
import railo.transformer.bytecode.op.OpVariable;
import railo.transformer.bytecode.statement.tag.Attribute;
import railo.transformer.bytecode.statement.udf.Closure;
import railo.transformer.bytecode.statement.udf.Function;
import railo.transformer.bytecode.util.ASMUtil;
import railo.transformer.cfml.Data;
import railo.transformer.cfml.TransfomerSettings;
import railo.transformer.cfml.evaluator.EvaluatorPool;
import railo.transformer.cfml.script.DocComment;
import railo.transformer.cfml.script.DocCommentTransformer;
import railo.transformer.cfml.tag.CFMLTransformer;
import railo.transformer.library.function.FunctionLib;
import railo.transformer.library.function.FunctionLibFunction;
import railo.transformer.library.function.FunctionLibFunctionArg;
import railo.transformer.library.tag.TagLibTag;
import railo.transformer.library.tag.TagLibTagAttr;
import railo.transformer.library.tag.TagLibTagScript;
import railo.transformer.util.CFMLString;

/**
*
*
  Der CFMLExprTransfomer implementiert das Interface ExprTransfomer,
  er bildet die Parser Grammatik ab, die unten definiert ist.
  Er erh¦lt als Eingabe CFML Code, als String oder CFMLString,
  der einen CFML Expression erh¦lt und liefert ein CFXD Element zurck,
  das diesen Ausdruck abbildet.
  Mithilfe der FunctionLibメs, kann er Funktionsaufrufe,
  die Teil eines Ausdruck sein k￶nnen, erkennen und validieren.
  Dies geschieht innerhalb der Methode function.
  Falls ein Funktionsaufruf, einer Funktion innerhalb einer FunctionLib entspricht,
  werden diese gegeneinander verglichen und der Aufruf wird als Build-In-Funktion bernommen,
  andernfalls wird der Funktionsaufruf als User-Defined-Funktion interpretiert.
  Die Klasse Cast, Operator und ElementFactory (siehe 3.2) helfen ihm beim erstellen des Ausgabedokument CFXD.

* <pre>
* Parser Grammatik EBNF (Extended Backus-Naur Form)

  transform      = spaces impOp;
  impOp          = eqvOp {"imp" spaces eqvOp};
  eqvOp          = xorOp {"eqv" spaces xorOp};
  xorOp          = orOp {"xor" spaces  orOp};
  orOp           = andOp {("or" | "||") spaces andOp};
      (* "||" Existiert in CFMX nicht *)
  andOp          = notOp {("and" | "&&") spaces notOp};
      (* "&&" Existiert in CFMX nicht *)
  notOp          = [("not"|"!") spaces] decsionOp;
      (* "!" Existiert in CFMX nicht *)
  decsionOp      = 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 *)
  concatOp       = plusMinusOp {"&" spaces plusMinusOp};
  plusMinusOp    = modOp {("-"|"+") spaces modOp};
  modOp          = divMultiOp {("mod" | "%") spaces divMultiOp};
                  (* modulus operator , "%" Existiert in CFMX nicht *)
  divMultiOp     = expoOp {("*"|"/") spaces expoOp};
  expoOp         = clip {("exp"|"^") spaces clip};
                  (*exponent operator, " exp " Existiert in CFMX nicht *)
  clip           = ("(" spaces impOp ")" spaces) | checker;
  checker        = string | number | dynamic | sharp;
  string         = ("'" {"##"|"''"|"#" impOp "#"| ?-"#"-"'" } "'") |
                   (""" {"##"|""""|"#" impOp "#"| ?-"#"-""" } """);
  number         = ["+"|"-"] digit {digit} {"." digit {digit}};
  digit          = "0"|..|"9";
  dynamic        = "true" | "false" | "yes" | "no" | startElement 
                   {("." identifier | "[" structElement "]")[function] };
  startElement   = identifier "(" functionArg ")" | scope | identifier;
  scope          = "variable" | "cgi" | "url" | "form" | "session" | "application" |
                   "arguments" | "cookie" | "client ";
  identifier     = (letter | "_") {letter | "_"|digit};
  structElement  = "[" impOp "]";
  functionArg    = [impOp{"," impOp}];
  sharp          = "#" checker "#";
  spaces         = {space};
  space          = "\s"|"\t"|"\f"|"\t"|"\n";
  letter         = "a"|..|"z"|"A"|..|"Z";

{"x"}= 0 bis n mal "x"
["x"]= 0 bis 1 mal "x"
("x" | "y")"z" = "xz" oder "yz"

</pre>
*
*/
public abstract class AbstrCFMLExprTransformer {

  private static final short STATIC=0;
  private static final short DYNAMIC=1;
  private static FunctionLibFunction JSON_ARRAY = null;
  private static FunctionLibFunction JSON_STRUCT = null;

  public static final short CTX_OTHER = TagLibTagScript.CTX_OTHER;
  public static final short CTX_NONE = TagLibTagScript.CTX_NONE;
  public static final short CTX_IF = TagLibTagScript.CTX_IF;
  public static final short CTX_ELSE_IF = TagLibTagScript.CTX_ELSE_IF;
  public static final short CTX_ELSE = TagLibTagScript.CTX_ELSE;
  public static final short CTX_FOR = TagLibTagScript.CTX_FOR;
  public static final short CTX_WHILE = TagLibTagScript.CTX_WHILE;
  public static final short CTX_DO_WHILE = TagLibTagScript.CTX_DO_WHILE;
  public static final short CTX_CFC = TagLibTagScript.CTX_CFC;
  public static final short CTX_INTERFACE = TagLibTagScript.CTX_INTERFACE;
  public static final short CTX_FUNCTION =TagLibTagScript.CTX_FUNCTION;
  public static final short CTX_BLOCK = TagLibTagScript.CTX_BLOCK;
  public static final short CTX_FINALLY = TagLibTagScript.CTX_FINALLY;
  public static final short CTX_SWITCH = TagLibTagScript.CTX_SWITCH;
  public static final short CTX_TRY = TagLibTagScript.CTX_TRY;
  public static final short CTX_CATCH = TagLibTagScript.CTX_CATCH;
  public static final short CTX_TRANSACTION = TagLibTagScript.CTX_TRANSACTION;
  public static final short CTX_THREAD = TagLibTagScript.CTX_THREAD;
  public static final short CTX_SAVECONTENT = TagLibTagScript.CTX_SAVECONTENT;
  public static final short CTX_LOCK = TagLibTagScript.CTX_LOCK;
  public static final short CTX_LOOP = TagLibTagScript.CTX_LOOP;
  public static final short CTX_QUERY = TagLibTagScript.CTX_QUERY;
  public static final short CTX_ZIP = TagLibTagScript.CTX_ZIP;
 
 
  private DocCommentTransformer docCommentTransformer= new DocCommentTransformer();
 

  protected short ATTR_TYPE_NONE=TagLibTagAttr.SCRIPT_SUPPORT_NONE;
  protected short ATTR_TYPE_OPTIONAL=TagLibTagAttr.SCRIPT_SUPPORT_OPTIONAL;
  protected short ATTR_TYPE_REQUIRED=TagLibTagAttr.SCRIPT_SUPPORT_REQUIRED;
 
  protected static final Expression NULL = LitString.toExprString("NULL");
  protected static final Attribute ANY = new Attribute(false,"type",LitString.toExprString("any"),"string");


  protected static EndCondition SEMI_BLOCK=new EndCondition() {
    public boolean isEnd(ExprData data) {
      return data.cfml.isCurrent('{') || data.cfml.isCurrent(';');
    }
  };
  protected static EndCondition SEMI=new EndCondition() {
    public boolean isEnd(ExprData data) {
      return data.cfml.isCurrent(';');
    }
  };
  protected static EndCondition COMMA_ENDBRACKED=new EndCondition() {
    public boolean isEnd(ExprData data) {
      return data.cfml.isCurrent(',') || data.cfml.isCurrent(')');
    }
  };

  public static interface EndCondition {
    public boolean isEnd(ExprData data);
  }
 
  /*private short mode=0;
  protected CFMLString cfml;
  protected FunctionLib[] fld;
  private boolean ignoreScopes=false;
  private boolean allowLowerThan;*/
 
  public class ExprData extends Data {
   
    private short mode=0;
    private boolean ignoreScopes=false;
    private boolean allowLowerThan;
    public boolean insideFunction;
    public String tagName;
    public boolean isCFC;
    public boolean isInterface;
    public short context=CTX_NONE;
    public DocComment docComment;
   
    public ExprData(Page page, EvaluatorPool ep, CFMLString cfml, FunctionLib[] flibs, TransfomerSettings settings,boolean allowLowerThan,TagLibTag[] scriptTags) {
      super(page,cfml,ep,settings,flibs,scriptTags);
      this.allowLowerThan=allowLowerThan;
    }
  }
 
  protected Expression transformAsString(ExprData data,String[] breakConditions) throws TemplateException {
    Expression el=null;
   
    // parse the houle Page String
        comments(data);   
       
    // String
      if((el=string(data))!=null) {
        data.mode=STATIC;
        return el;
      }
    // Sharp
      if((el=sharp(data))!=null) {
        data.mode=DYNAMIC;
        return el;
     
    // Simple
      return simple(data,breakConditions);
  }
 
 

  /**
   * Initialmethode, wird aufgerufen um den internen Zustand des Objektes zu setzten.
   * @param fld Function Libraries zum validieren der Funktionen
   * @param cfml CFML Code der transfomiert werden soll.
   */
  protected ExprData init(Page page,EvaluatorPool ep,FunctionLib[] fld,TagLibTag[] scriptTags, CFMLString cfml, TransfomerSettings settings, boolean allowLowerThan) {
    ExprData data = new ExprData(page,ep,cfml,fld,settings,allowLowerThan,scriptTags);
    if(JSON_ARRAY==null)JSON_ARRAY=getFLF(data,"_jsonArray");
    if(JSON_STRUCT==null)JSON_STRUCT=getFLF(data,"_jsonStruct");
    return data;
    //this.allowLowerThan=allowLowerThan;
    //this.fld = fld;
    //this.cfml = cfml;
  }
 
  /**
   * Startpunkt zum transfomieren einer Expression, ohne dass das Objekt neu initialisiert wird,
   * dient vererbten Objekten als Einstiegspunkt.
   * @return Element
   * @throws TemplateException
   */
  protected Expression expression(ExprData data) throws TemplateException {
    return assignOp(data);
  }

  /**
  * Liest einen gelableten  Funktionsparamter ein
  * <br />
  * EBNF:<br />
  * <code>assignOp [":" spaces assignOp];</code>
  * @return CFXD Element
  * @throws TemplateException
  */
  private Argument functionArgument(ExprData data, boolean varKeyUpperCase) throws TemplateException {
    return functionArgument(data,null,varKeyUpperCase);
  }
 
  private Argument functionArgument(ExprData data,String type, boolean varKeyUpperCase) throws TemplateException {
    Expression expr = assignOp(data);
    try{
      if (data.cfml.forwardIfCurrent(":")) {
        comments(data);
        return new NamedArgument(expr,assignOp(data),type,varKeyUpperCase);
      }
      else if(expr instanceof DynAssign){
        DynAssign da=(DynAssign) expr;
        return new NamedArgument(da.getName(),da.getValue(),type,varKeyUpperCase);
      }
      else if(expr instanceof Assign && !(expr instanceof OpVariable)){
        Assign a=(Assign) expr;
        return new NamedArgument(a.getVariable(),a.getValue(),type,varKeyUpperCase);
      }
    }
    catch(BytecodeException be) {
      throw new TemplateException(data.cfml,be.getMessage());
    }
    return new Argument(expr,type);
  }

 
 
 
  /**
  * Transfomiert Zuweisungs Operation.
  * <br />
  * EBNF:<br />
  * <code>eqvOp ["=" spaces assignOp];</code>
  * @return CFXD Element
  * @throws TemplateException
  */
  protected Expression assignOp(ExprData data) throws TemplateException {
       
    Expression expr = conditionalOp(data);
        if (data.cfml.forwardIfCurrent('=')) {
         
            comments(data);
            if(data.mode==STATIC) expr=new DynAssign(expr,assignOp(data));
      else {
        if(expr instanceof Variable)
          expr=new Assign((Variable)expr,assignOp(data));
        else if(expr instanceof Null) {
          expr=new Assign(((Null)expr).toVariable(),assignOp(data));
        }
        else
          throw new TemplateException(data.cfml,"invalid assignment left-hand side ("+expr.getClass().getName()+")");
      }
    }
    return expr;
  }
 
  private Expression conditionalOp(ExprData data) throws TemplateException {
       
    Expression expr = impOp(data);
        if (data.cfml.forwardIfCurrent('?')) {
          comments(data);
          // Elvis
          if(data.cfml.forwardIfCurrent(':')) {
            comments(data);
              Expression right = assignOp(data);
           
            if(!(expr instanceof Variable) )
              throw new TemplateException(data.cfml,"left operant of the Elvis operator has to be a variable or a function call");
           
            return OpElvis.toExpr((Variable)expr, right);
          }
         
          Expression left = assignOp(data);
          comments(data);
          if(!data.cfml.forwardIfCurrent(':'))throw new TemplateException("invalid conditional operator");
          comments(data);
          Expression right = assignOp(data);
         
            expr=OpContional.toExpr(expr, left, right);
    }
    return expr;
  }

  /**
  * Transfomiert eine Implication (imp) Operation.
  * <br />
  * EBNF:<br />
  * <code>eqvOp {"imp" spaces eqvOp};</code>
  * @return CFXD Element
  * @throws TemplateException
  */
  private Expression impOp(ExprData data) throws TemplateException {
    Expression expr = eqvOp(data);
    while(data.cfml.forwardIfCurrentAndNoWordAfter("imp")) {
      comments(data);
            expr=OpBool.toExprBoolean(expr, eqvOp(data), OpBool.IMP);
    }
    return expr;
  }

  /**
  * Transfomiert eine  Equivalence (eqv) Operation.
  * <br />
  * EBNF:<br />
  * <code>xorOp {"eqv" spaces xorOp};</code>
  * @return CFXD Element
  * @throws TemplateException
  */
  private Expression eqvOp(ExprData data) throws TemplateException {
    Expression expr = xorOp(data);
    while(data.cfml.forwardIfCurrentAndNoWordAfter("eqv")) {
      comments(data);
            expr=OpBool.toExprBoolean(expr, xorOp(data), OpBool.EQV);
    }
    return expr;
  }

  /**
  * Transfomiert eine  Xor (xor) Operation.
  * <br />
  * EBNF:<br />
  * <code>orOp {"xor" spaces  orOp};</code>
  * @return CFXD Element
  * @throws TemplateException
  */
  private Expression xorOp(ExprData data) throws TemplateException {
    Expression expr = orOp(data);
    while(data.cfml.forwardIfCurrentAndNoWordAfter("xor")) {
      comments(data);
            expr=OpBool.toExprBoolean(expr, orOp(data), OpBool.XOR);
    }
    return expr;
  }

  /**
  * 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 TemplateException
  */
  private Expression orOp(ExprData data) throws TemplateException {
    Expression expr = andOp(data);
   
    while(data.cfml.forwardIfCurrent("||") || data.cfml.forwardIfCurrentAndNoWordAfter("or")) {
      comments(data);
            expr=OpBool.toExprBoolean(expr, andOp(data), OpBool.OR);
    }
    return expr;
  }

  /**
  * 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 TemplateException
  */
  private Expression andOp(ExprData data) throws TemplateException {
    Expression expr = notOp(data);
   
    while(data.cfml.forwardIfCurrent("&&") || data.cfml.forwardIfCurrentAndNoWordAfter("and")) {
      comments(data);
          expr=OpBool.toExprBoolean(expr, notOp(data), OpBool.AND);
    }
    return expr;
  }

  /**
  * 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 TemplateException
  */
  private Expression notOp(ExprData data) throws TemplateException {
    // And Operation
    Position line = data.cfml.getPosition();
    if (data.cfml.isCurrent('!') && !data.cfml.isCurrent("!=")) {
      data.cfml.next();
      comments(data);
      return OpNegate.toExprBoolean(notOp(data),line,data.cfml.getPosition());
    }
    else if (data.cfml.forwardIfCurrentAndNoWordAfter("not")) {
      comments(data);
      return OpNegate.toExprBoolean(notOp(data),line,data.cfml.getPosition());
    }
    return decsionOp(data);
  }

  /**
  * <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 TemplateException
  */
  private Expression decsionOp(ExprData data) throws TemplateException {

    Expression expr = concatOp(data);
    boolean hasChanged=false;
    // ct, contains
    do {
      hasChanged=false;
      if(data.cfml.isCurrent('c')) {
          if (data.cfml.forwardIfCurrent("ct",false,true)) {expr = decisionOpCreate(data,OPDecision.CT,expr);hasChanged=true;}
          else if (data.cfml.forwardIfCurrent("contains",false,true)){ expr = decisionOpCreate(data,OPDecision.CT,expr);hasChanged=true;}
      }
      // does not contain
      else if (data.cfml.forwardIfCurrent("does","not","contain",false,true)){ expr = decisionOpCreate(data,OPDecision.NCT,expr); hasChanged=true;}

      // equal, eq
      else if (data.cfml.isCurrent("eq") && !data.cfml.isCurrent("eqv")) {
        int plus=2;
        data.cfml.setPos(data.cfml.getPos()+2);
        if(data.cfml.forwardIfCurrent("ual"))plus=5;
       
        if(data.cfml.isCurrentVariableCharacter()) {
          data.cfml.setPos(data.cfml.getPos()-plus);
        }
        else {
          expr = decisionOpCreate(data,OPDecision.EQ,expr);
          hasChanged=true;
        }
       
      }
      // ==
      else if (data.cfml.forwardIfCurrent("==")) {
        if(data.cfml.forwardIfCurrent('='))     expr = decisionOpCreate(data,OPDecision.EEQ,expr);
        else expr = decisionOpCreate(data,OPDecision.EQ,expr);
        hasChanged=true;
      }
      // !=
      else if (data.cfml.forwardIfCurrent("!=")) {
        if(data.cfml.forwardIfCurrent('='))     expr = decisionOpCreate(data,OPDecision.NEEQ,expr);
        else expr = decisionOpCreate(data,OPDecision.NEQ,expr);
        hasChanged=true;
      }
      // <=/</<>
      else if (data.cfml.isCurrent('<')) {
        hasChanged=true;
        if(data.cfml.isNext('='))  {
          data.cfml.next();data.cfml.next();
          expr = decisionOpCreate(data,OPDecision.LTE,expr);
        }
        else if(data.cfml.isNext('>')) {
          data.cfml.next();data.cfml.next();
          expr = decisionOpCreate(data,OPDecision.NEQ,expr);
        }
        else if(data.cfml.isNext('/')) {
          hasChanged=false;
        }
        else  {
          data.cfml.next();
          expr = decisionOpCreate(data,OPDecision.LT,expr);
        }
      }
      // >=/>
      else if (data.allowLowerThan && data.cfml.forwardIfCurrent('>')) {
        if(data.cfml.forwardIfCurrent('='))   expr = decisionOpCreate(data,OPDecision.GTE,expr);
        else               expr = decisionOpCreate(data,OPDecision.GT,expr);
        hasChanged=true;
      }
     
      // gt, gte, greater than or equal to, greater than
      else if (data.cfml.isCurrent('g')) {
        if (data.cfml.forwardIfCurrent("gt")) {
          if(data.cfml.forwardIfCurrentAndNoWordAfter("e")) {
            if(data.cfml.isCurrentVariableCharacter()) {
              data.cfml.setPos(data.cfml.getPos()-3);
            }
            else {
              expr = decisionOpCreate(data,OPDecision.GTE,expr);
              hasChanged=true;
            }
          }
          else {
            if(data.cfml.isCurrentVariableCharacter()) {
              data.cfml.setPos(data.cfml.getPos()-2);
            }
            else {
              expr = decisionOpCreate(data,OPDecision.GT,expr);
              hasChanged=true;
            }
          }
        }
        else if (data.cfml.forwardIfCurrent("greater", "than",false,true)) {
          if(data.cfml.forwardIfCurrent("or","equal", "to",true,true)) expr = decisionOpCreate(data,OPDecision.GTE,expr);
          else expr = decisionOpCreate(data,OPDecision.GT,expr);
          hasChanged=true;
       
        else if (data.cfml.forwardIfCurrent("ge",false,true)) {
          expr = decisionOpCreate(data,OPDecision.GTE,expr);
          hasChanged=true;
        }       
      }
     
      // is, is not
      else if (data.cfml.forwardIfCurrent("is",false,true)) {
        if(data.cfml.forwardIfCurrent("not",true,true)) expr = decisionOpCreate(data,OPDecision.NEQ,expr);
        else expr = decisionOpCreate(data,OPDecision.EQ,expr);
        hasChanged=true;
      }
     
      // lt, lte, less than, less than or equal to
      else if (data.cfml.isCurrent('l')) {
        if (data.cfml.forwardIfCurrent("lt")) {
          if(data.cfml.forwardIfCurrentAndNoWordAfter("e")) {
            if(data.cfml.isCurrentVariableCharacter()) {
              data.cfml.setPos(data.cfml.getPos()-3);
            }
            else {
              expr = decisionOpCreate(data,OPDecision.LTE,expr);
              hasChanged=true;
            }
          }
          else {
            if(data.cfml.isCurrentVariableCharacter()) {
              data.cfml.setPos(data.cfml.getPos()-2);
            }
            else {
              expr = decisionOpCreate(data,OPDecision.LT,expr);
              hasChanged=true;
            }
          }
        }
        else if (data.cfml.forwardIfCurrent("less","than",false,true)) {
          if(data.cfml.forwardIfCurrent("or", "equal", "to",true,true)) expr = decisionOpCreate(data,OPDecision.LTE,expr);
          else expr = decisionOpCreate(data,OPDecision.LT,expr);
          hasChanged=true;
       
        else if (data.cfml.forwardIfCurrent("le",false,true)) {
          expr = decisionOpCreate(data,OPDecision.LTE,expr);
          hasChanged=true;
        }       
      }
     
      // neq, not equal, nct
      else if (data.cfml.isCurrent('n')) {
        // Not Equal
          if (data.cfml.forwardIfCurrent("neq",false,true)){ expr = decisionOpCreate(data,OPDecision.NEQ,expr); hasChanged=true;}
        // Not Equal (Alias)
          else if (data.cfml.forwardIfCurrent("not","equal",false,true)){ expr = decisionOpCreate(data,OPDecision.NEQ,expr);hasChanged=true; }
        // nct
          else if (data.cfml.forwardIfCurrent("nct",false,true)){ expr = decisionOpCreate(data,OPDecision.NCT,expr); hasChanged=true;
      }
     
    }
    while(hasChanged);
    return expr;
  }
  private Expression decisionOpCreate(ExprData data,int operation, Expression left) throws TemplateException {
        comments(data);
        return OPDecision.toExprBoolean(left, concatOp(data), operation);
  }

  /**
  * Transfomiert eine  Konkatinations-Operator (&) Operation. Im Gegensatz zu CFMX ,
  * wird das "!" Zeichen auch als Not Operator anerkannt.
  * <br />
  * EBNF:<br />
  * <code>plusMinusOp {"&" spaces concatOp};</code>
  * @return CFXD Element
  * @throws TemplateException
  */
  private Expression concatOp(ExprData data) throws TemplateException {
    Expression expr = plusMinusOp(data);
   
    while(data.cfml.isCurrent('&') && !data.cfml.isCurrent("&&")) {
      data.cfml.next();
     
      // &=
      if (data.cfml.isCurrent('=') && expr instanceof Variable) {
        data.cfml.next();
        comments(data);
        Expression right = assignOp(data);
        ExprString res = OpString.toExprString(expr, right);
        expr=new OpVariable((Variable)expr,res);
      }
      else {
              comments(data);
              expr=OpString.toExprString(expr, plusMinusOp(data));
      }
     
    }
    return expr;
  }

  /**
  * Transfomiert die mathematischen Operatoren Plus und Minus (1,-).
  * <br />
  * EBNF:<br />
  * <code>modOp [("-"|"+") spaces plusMinusOp];</code>
  * @return CFXD Element
  * @throws TemplateException
  */
  private Expression plusMinusOp(ExprData data) throws TemplateException {
    Expression expr = modOp(data);
   
    while(!data.cfml.isLast()) {
     
      // Plus Operation
      if (data.cfml.forwardIfCurrent('+'))      expr=_plusMinusOp(data,expr,OpDouble.PLUS);
      // Minus Operation
      else if (data.cfml.forwardIfCurrent('-'))  expr=_plusMinusOp(data,expr,OpDouble.MINUS);
      else break;
    }
    return expr;
  }
 
 

  private Expression _plusMinusOp(ExprData data,Expression expr,int opr) throws TemplateException {
    // +=
    if (data.cfml.isCurrent('=') && expr instanceof Variable) {
      data.cfml.next();
      comments(data);
      Expression right = assignOp(data);
      ExprDouble res = OpDouble.toExprDouble(expr, right,opr);
      expr=new OpVariable((Variable)expr,res);
    }
    /*/ ++
    else if (data.cfml.isCurrent(opr==OpDouble.PLUS?'+':'-') && expr instanceof Variable) {
      data.cfml.next();
      comments(data);
      ExprDouble res = OpDouble.toExprDouble(expr, LitDouble.toExprDouble(1D,-1),opr);
      expr=new OpVariable((Variable)expr,res);
      expr=OpDouble.toExprDouble(expr,LitDouble.toExprDouble(1D, -1),opr==OpDouble.PLUS? OpDouble.MINUS:OpDouble.PLUS);
      //comments(data);
    }*/
    else {
      comments(data);
            expr=OpDouble.toExprDouble(expr, modOp(data), opr)
    }
    return expr;
  }
 

  /**
  * 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 TemplateException
  */
  private Expression modOp(ExprData data) throws TemplateException {
    Expression expr = divMultiOp(data);
   
    // Modulus Operation
    while(data.cfml.forwardIfCurrent('%') || data.cfml.forwardIfCurrentAndNoWordAfter("mod")) {
      expr=_modOp(data,expr);
      //comments(data);
            //expr=OpDouble.toExprDouble(expr, divMultiOp(), OpDouble.MODULUS);
    }
    return expr;
  }
 
  private Expression _modOp(ExprData data,Expression expr) throws TemplateException {
    if (data.cfml.isCurrent('=') && expr instanceof Variable) {
      data.cfml.next();
      comments(data);
      Expression right = assignOp(data);
      ExprDouble res = OpDouble.toExprDouble(expr, right,OpDouble.MODULUS);
      return new OpVariable((Variable)expr,res);
    }
        comments(data);
        return OpDouble.toExprDouble(expr, expoOp(data), OpDouble.MODULUS);
  }

  /**
  * Transfomiert die mathematischen Operatoren Mal und Durch (*,/).
  * <br />
  * EBNF:<br />
  * <code>expoOp {("*"|"/") spaces expoOp};</code>
  * @return CFXD Element
  * @throws TemplateException
  */
  private Expression divMultiOp(ExprData data) throws TemplateException {
    Expression expr = expoOp(data);

    while (!data.cfml.isLast()) {
     
        // Multiply Operation
        if(data.cfml.forwardIfCurrent('*')) {
          expr=_divMultiOp(data,expr,OpDouble.MULTIPLY);
          //comments(data);
                    //expr=OpDouble.toExprDouble(expr, expoOp(), OpDouble.MULTIPLY);
        }
        // Divide Operation
        else if (data.cfml.isCurrent('/') && (!data.cfml.isCurrent('/','>') )) {
          data.cfml.next();
          expr=_divMultiOp(data,expr,OpDouble.DIVIDE);
          //comments(data);
                    //expr=OpDouble.toExprDouble(expr, expoOp(), OpDouble.DIVIDE);
        }
        // Divide Operation
        else if (data.cfml.isCurrent('\\')) {
          data.cfml.next();
          expr=_divMultiOp(data,expr,OpDouble.INTDIV);
          //comments(data);
                    //expr=OpDouble.toExprDouble(expr, expoOp(), OpDouble.INTDIV);
        }
        else {
          break;
        }
     
    }
    return expr;
  }

  private Expression _divMultiOp(ExprData data,Expression expr, int iOp) throws TemplateException {
    if (data.cfml.isCurrent('=') && expr instanceof Variable) {
      data.cfml.next();
      comments(data);
      Expression right = assignOp(data);
      ExprDouble res = OpDouble.toExprDouble(expr, right,iOp);
      return new OpVariable((Variable)expr,res);
    }
        comments(data);
        return OpDouble.toExprDouble(expr, expoOp(data), iOp);
  }

  /**
  * 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 TemplateException
  */
  private Expression expoOp(ExprData data) throws TemplateException {
    Expression expr = unaryOp(data);

    // Modulus Operation
    while(data.cfml.forwardIfCurrent('^') || data.cfml.forwardIfCurrentAndNoWordAfter("exp")) {
      comments(data);
            expr=OpDouble.toExprDouble(expr, unaryOp(data), OpDouble.EXP);
    }
    return expr;
  }
 
  private Expression unaryOp(ExprData data) throws TemplateException {
    Expression expr = negatePlusMinusOp(data);
   
    // Plus Operation
    if (data.cfml.forwardIfCurrent("++") && expr instanceof Variable)     
      expr=_unaryOp(data,expr,OpDouble.PLUS);
    // Minus Operation
    else if (data.cfml.forwardIfCurrent("--") && expr instanceof Variable
      expr=_unaryOp(data,expr,OpDouble.MINUS);
    return expr;
  }
 
  private Expression _unaryOp(ExprData data,Expression expr,int opr) throws TemplateException {
    Position leftEnd = expr.getEnd(),start=null,end=null;
    comments(data);
    if(leftEnd!=null){
      start=leftEnd;
      end=new Position(leftEnd.line, leftEnd.column+2, leftEnd.pos+2);
    }
   
    ExprDouble res = OpDouble.toExprDouble(expr, LitDouble.toExprDouble(1D,start,end),opr);
    expr=new OpVariable((Variable)expr,res);
    return OpDouble.toExprDouble(expr,LitDouble.toExprDouble(1D,start,end),opr==OpDouble.PLUS? OpDouble.MINUS:OpDouble.PLUS);
  }
 
 
 

  /**
  * Negate Numbers
  * @return CFXD Element
  * @throws TemplateException
  */
  private Expression negatePlusMinusOp(ExprData data) throws TemplateException {
    // And Operation
    Position line=data.cfml.getPosition();
    if (data.cfml.forwardIfCurrent('-')) {
      if (data.cfml.forwardIfCurrent('-')) {
        comments(data);
        Expression expr = clip(data);
        ExprDouble res = OpDouble.toExprDouble(expr, LitDouble.toExprDouble(1D),OpDouble.MINUS);
        return new OpVariable((Variable)expr,res);
      }
      comments(data);
      return OpNegateNumber.toExprDouble(clip(data),OpNegateNumber.MINUS,line,data.cfml.getPosition());
     
    }
    else if (data.cfml.forwardIfCurrent('+')) {
      if (data.cfml.forwardIfCurrent('+')) {
        comments(data);
        Expression expr = clip(data);
        ExprDouble res = OpDouble.toExprDouble(expr, LitDouble.toExprDouble(1D),OpDouble.PLUS);
        return new OpVariable((Variable)expr,res);
      }
      comments(data);
      return CastDouble.toExprDouble(clip(data));//OpNegateNumber.toExprDouble(clip(),OpNegateNumber.PLUS,line);
    }
    return clip(data);
  }

  /**
  * Verarbeitet Ausdrcke die inerhalb einer Klammer stehen.
  * <br />
  * EBNF:<br />
  * <code>("(" spaces impOp ")" spaces) | checker;</code>
  * @return CFXD Element
  * @throws TemplateException
  */
  private Expression clip(ExprData data) throws TemplateException {
      return checker(data);
  }
  /**
  * Hier werden die verschiedenen M￶glichen Werte erkannt
  * und jenachdem wird mit der passenden Methode weitergefahren
  * <br />
  * EBNF:<br />
  * <code>string | number | dynamic | sharp;</code>
  * @return CFXD Element
  * @throws TemplateException
  */
  private Expression checker(ExprData data) throws TemplateException {
    Expression expr=null;
      // String
      if((expr=string(data))!=null) {
        expr = subDynamic(data,expr);
        data.mode=STATIC;//(expr instanceof Literal)?STATIC:DYNAMIC;// STATIC
        return expr;
      }
    // Number
      if((expr=number(data))!=null) {
        expr = subDynamic(data,expr);
        data.mode=STATIC;//(expr instanceof Literal)?STATIC:DYNAMIC;// STATIC
        return expr;
      }
      // closure
      if((expr=closure(data))!=null) {
        data.mode=DYNAMIC;
        return expr;
      }
     
    // Dynamic
      if((expr=dynamic(data))!=null) {
        expr = newOp(data, expr);
        //if(res==expr)
          expr = subDynamic(data,expr);
        //else expr=res;
        data.mode=DYNAMIC;
        return expr;
      }
    // Sharp
      if((expr=sharp(data))!=null) {
        data.mode=DYNAMIC;
        return expr;
      }
    // JSON
      if((expr=json(data,JSON_ARRAY,'[',']'))!=null) {
        data.mode=DYNAMIC;
        return expr;
      }
      if((expr=json(data,JSON_STRUCT,'{','}'))!=null) {
        data.mode=DYNAMIC;
        return expr;
      }
    // else Error
      throw new TemplateException(data.cfml,"Syntax Error, Invalid Construct")
  }
 
  /*private Expression variable(Data data) throws TemplateException {
    Expression expr=null;
   
    // Dynamic
    if((expr=dynamic(data))!=null) {
      expr = subDynamic(data,expr);
      data.mode=DYNAMIC;
      return expr;
    }
    return null;
  }*/
 
  /**
  * Transfomiert einen lierale Zeichenkette.
  * <br />
  * EBNF:<br />
  * <code>("'" {"##"|"''"|"#" impOp "#"| ?-"#"-"'" } "'") |
                   (""" {"##"|""""|"#" impOp "#"| ?-"#"-""" } """);</code>
   * @param data
  * @return CFXD Element
  * @throws TemplateException
  */
  protected Expression string(ExprData data) throws TemplateException {
   
    // check starting character for a string literal
    if(!data.cfml.isCurrent('"')&& !data.cfml.isCurrent('\''))
      return null;
    Position line = data.cfml.getPosition();
   
    // Init Parameter
    char quoter = data.cfml.getCurrentLower();
    StringBuffer str=new StringBuffer();
    Expression expr=null;
   
    while(data.cfml.hasNext()) {
      data.cfml.next();
      // check sharp
      if(data.cfml.isCurrent('#')) {
       
        // Ecaped sharp
        if(data.cfml.isNext('#')){
          data.cfml.next();
          str.append('#');
        }
        // get Content of sharp
        else {
          data.cfml.next();
                    comments(data);
          Expression inner=assignOp(data);
                    comments(data);
          if (!data.cfml.isCurrent('#'))
            throw new TemplateException(data.cfml,"Invalid Syntax Closing [#] not found");
         
          ExprString exprStr=null;
          if(str.length()!=0) {
            exprStr=new LitString(str.toString(),line,data.cfml.getPosition());
            if(expr!=null){
              expr = OpString.toExprString(expr, exprStr);
            }
            else expr=exprStr;
            str=new StringBuffer();
          }
          if(expr==null) {
            expr=inner;
          }
          else  {
            expr = OpString.toExprString(expr, inner);
         
        }
      }
      // check quoter
      else if(data.cfml.isCurrent(quoter)) {
        // Ecaped sharp
        if(data.cfml.isNext(quoter)){
          data.cfml.next();
          str.append(quoter);
        }
        // finsish
        else {
          break;
        }       
      }
      // all other character
      else {
        str.append(data.cfml.getCurrent());
      }
    }
    if(!data.cfml.forwardIfCurrent(quoter))
      throw new TemplateException(data.cfml,"Invalid Syntax Closing ["+quoter+"] not found");
   
    if(expr==null)
      expr=new LitString(str.toString(),line,data.cfml.getPosition());
    else if(str.length()!=0) {
      expr = OpString.toExprString(expr, new LitString(str.toString(),line,data.cfml.getPosition()));
    }
        comments(data);
       
        if(expr instanceof Variable) {
          Variable var=(Variable) expr;
          var.setFromHash(true);
        }
       
    return expr;
   
  }

  /**
  * Transfomiert einen numerische Wert.
  * Die L¦nge des numerischen Wertes interessiert nicht zu ᅵbersetzungszeit,
  * ein "Overflow" fhrt zu einem Laufzeitfehler.
  * Da die zu erstellende CFXD, bzw. dieser Transfomer, keine Vorwegnahme des Laufzeitsystems vornimmt.
  * <br />
  * EBNF:<br />
  * <code>["+"|"-"] digit {digit} {"." digit {digit}};</code>
  * @return CFXD Element
  * @throws TemplateException
  */
  private LitDouble number(ExprData data) throws TemplateException {
    // check first character is a number literal representation
    if(!(data.cfml.isCurrentBetween('0','9') || data.cfml.isCurrent('.'))) return null;
   
    Position line = data.cfml.getPosition();
    StringBuffer rtn=new StringBuffer();
   
    // get digit on the left site of the dot
    if(data.cfml.isCurrent('.')) rtn.append('0');
    else rtn.append(digit(data));
    // read dot if exist
    if(data.cfml.forwardIfCurrent('.')) {
      rtn.append('.');
      String rightSite=digit(data);
      if(rightSite.length()> 0 && data.cfml.forwardIfCurrent('e')) {
        Boolean expOp=null;
        if(data.cfml.forwardIfCurrent('+')) expOp=Boolean.TRUE;
        else if(data.cfml.forwardIfCurrent('-')) expOp=Boolean.FALSE;
       
        if(data.cfml.isCurrentBetween('0','9')) {
          if(expOp==Boolean.FALSE) rightSite+="e-";
          else if(expOp==Boolean.TRUE) rightSite+="e+";
          else rightSite+="e";
              rightSite+=digit(data);
          }
          else {
            if(expOp!=null) data.cfml.previous();
              data.cfml.previous();
          }
      }
      // read right side of the dot
      if(rightSite.length()==0)
        rightSite="0";//throw new TemplateException(cfml, "Number can't end with [.]"); // DIFF 23
      rtn.append(rightSite);
    }
        comments(data);
       
    try {
      return LitDouble.toExprDouble(Caster.toDoubleValue(rtn.toString()),line,data.cfml.getPosition());
    } catch (CasterException e) {
      throw new TemplateException(data.cfml,e.getMessage());
    }
   
  }
 
 
 
  /**
  * Liest die reinen Zahlen innerhalb des CFMLString aus und gibt diese als Zeichenkette zurck.
  * <br />
  * EBNF:<br />
  * <code>"0"|..|"9";</code>
  * @return digit Ausgelesene Zahlen als Zeichenkette.
  */
  private String digit(ExprData data) {
    String rtn="";
    while (data.cfml.isValidIndex()) {
      if(!data.cfml.isCurrentBetween('0','9'))break;
      rtn+=data.cfml.getCurrentLower();
      data.cfml.next();
    }
    return rtn;
  }

  /**
  * Liest den folgenden idetifier ein und prft ob dieser ein boolscher Wert ist.
  * Im Gegensatz zu CFMX wird auch "yes" und "no" als bolscher <wert akzeptiert,
  * was bei CFMX nur beim Umwandeln einer Zeichenkette zu einem boolschen Wert der Fall ist.<br />
  * Wenn es sich um keinen bolschen Wert handelt wird der folgende Wert eingelesen mit seiner ganzen Hirarchie.
  * <br />
  * EBNF:<br />
  * <code>"true" | "false" | "yes" | "no" | startElement {("." identifier | "[" structElement "]" )[function] };</code>
  * @return CFXD Element
  * @throws TemplateException
  */
  private Expression dynamic(ExprData data) throws TemplateException {
    // Die Implementation weicht ein wenig von der Grammatik ab,
    // aber nicht in der Logik sondern rein wie es umgesetzt wurde.
   
     
     
    // get First Element of the Variable
    Position line = data.cfml.getPosition();
    Identifier id = identifier(data,false,true);
    if(id == null) {
        if (!data.cfml.forwardIfCurrent('(')) return null;
       
            comments(data);
      Expression expr = assignOp(data);

      if (!data.cfml.forwardIfCurrent(')'))
        throw new TemplateException(
          data.cfml,
          "Invalid Syntax Closing [)] not found");
            comments(data);
            return expr;//subDynamic(expr);
           
    }
     
    Variable var;
        comments(data);
   
    // Boolean constant
    if(id.getString().equalsIgnoreCase("TRUE"))  {// || name.equals("YES"))  {
      comments(data);
      return new LitBoolean(true,line,data.cfml.getPosition());
    }
    else if(id.getString().equalsIgnoreCase("FALSE"))  {// || name.equals("NO"))  {
      comments(data);
      return new LitBoolean(false,line,data.cfml.getPosition());
    }
    else if(NullSupportHelper.full() && id.getString().equalsIgnoreCase("NULL"))  {
      comments(data);
      return new Null(line,data.cfml.getPosition());
    }
   
    // Extract Scope from the Variable
    //int c=data.cfml.getColumn();
    Position l=data.cfml.getPosition();
    var = startElement(data,id,line);
    var.setStart(l);
    var.setEnd(data.cfml.getPosition());
    return var;
  }
 

 
  private Expression json(ExprData data,FunctionLibFunction flf, char start, char end) throws TemplateException {
    if(!data.cfml.forwardIfCurrent(start))return null;
   
    Position line = data.cfml.getPosition();
    BIF bif=new BIF(flf.getName(),flf);
    bif.setArgType(flf.getArgType());
    bif.setClass(flf.getClazz());
    bif.setReturnType(flf.getReturnTypeAsString());
   
    do {
      comments(data);
      if (data.cfml.isCurrent(end))break;
     
      bif.addArgument(functionArgument(data,data.settings.dotNotationUpper));
      comments(data);
    }
    while (data.cfml.forwardIfCurrent(','));
    comments(data);
     
    if (!data.cfml.forwardIfCurrent(end))
      throw new TemplateException(data.cfml,"Invalid Syntax Closing ["+end+"] not found");
    comments(data);
    Variable var=new Variable(line,data.cfml.getPosition());
    var.addMember(bif);
    return var;
  }
 
  private Expression closure(ExprData data) throws TemplateException {
    if(!data.cfml.forwardIfCurrent("function",'('))return null;
    data.cfml.previous();
    return new ClosureAsExpression((Closure) closurePart(data, "closure_"+CreateUniqueId.invoke(), Component.ACCESS_PUBLIC, "any", data.cfml.getPosition(),true));
  }
 
  protected  abstract Function closurePart(ExprData data, String id, int access, String rtnType, Position line,boolean closure) throws TemplateException;

 
  protected FunctionLibFunction getFLF(ExprData data,String name) {
    FunctionLibFunction flf=null;
    for (int i = 0; i < data.flibs.length; i++) {
      flf = data.flibs[i].getFunction(name);
      if (flf != null)
        break;
    }
    return flf;
  }

  private Expression subDynamic(ExprData data,Expression expr) throws TemplateException {
   
   
   

      String name=null;
      Invoker invoker=null;
    // Loop over nested Variables
    while (data.cfml.isValidIndex()) {
      ExprString nameProp = null,namePropUC = null;
      // .
      if (data.cfml.forwardIfCurrent('.')) {
        // Extract next Var String
                comments(data);
                Position line=data.cfml.getPosition();
                name = identifier(data,true);
        if(name==null)
          throw new TemplateException(data.cfml, "Invalid identifier");
                comments(data);
        nameProp=Identifier.toIdentifier(name,line,data.cfml.getPosition());
        namePropUC=Identifier.toIdentifier(name,data.settings.dotNotationUpper?Identifier.CASE_UPPER:Identifier.CASE_ORIGNAL,line,data.cfml.getPosition());
      }
      // []
      else if (data.cfml.forwardIfCurrent('[')) {
       
        // get Next Var
        nameProp = structElement(data);
        namePropUC=nameProp;
        // Valid Syntax ???
        if (!data.cfml.forwardIfCurrent(']'))
          throw new TemplateException(
            data.cfml,
            "Invalid Syntax Closing []] not found");
      }
      /* / :
      else if (data.cfml.forwardIfCurrent(':')) {
        // Extract next Var String
                comments(data);
                int line=data.cfml.getLine();
        name = identifier(true,true);
        if(name==null)
          throw new TemplateException(cfml, "Invalid identifier");
                comments(data);
               
        nameProp=LitString.toExprString(name,line);
      }*/
      // finish
      else {
        break;
      }

            comments(data);
           
            if(expr instanceof Invoker)  {
              invoker=(Invoker) expr;
            }
            else {
              invoker=new ExpressionInvoker(expr);
              expr=invoker;
            }
      // Method
      if (data.cfml.isCurrent('(')) {
        if(nameProp==null && name!=null)nameProp=Identifier.toIdentifier(name, Identifier.CASE_ORIGNAL,null,null);// properly this is never used
        invoker.addMember(getFunctionMember(data,nameProp, false));
      }
     
      // property
      else invoker.addMember(new DataMember(namePropUC));
     
    }
   
    return expr; 
  }
 
  private Expression newOp(ExprData data,Expression expr) throws TemplateException {
    if(!(expr instanceof Variable)) return expr;
    Variable var=(Variable) expr;
    Member m= var.getFirstMember();
    if(!(m instanceof DataMember)) return expr;
   
    ExprString n = ((DataMember)m).getName();
    if(!(n instanceof LitString)) return expr;
   
    LitString ls=(LitString) n;
   
   
    if(!"new".equalsIgnoreCase(ls.getString())) return expr;
   
    int start=data.cfml.getPos();
      String name=null;
     
     
      // first identifier
      name = identifier(data,true);
     
   
    ExprString exprName;
    if(name!=null)  {
      StringBuilder fullName=new StringBuilder();
      fullName.append(name);
      // Loop over addional identifier
      while (data.cfml.isValidIndex()) {
        if (data.cfml.forwardIfCurrent('.')) {
          comments(data);
                  name = identifier(data,true);
          if(name==null) {
            data.cfml.setPos(start);
            return expr;//throw new TemplateException(data.cfml,"invalid Component declaration ");
          }
          fullName.append('.');
          fullName.append(name);
          comments(data);
        }
        else break;
      }
     
      exprName=LitString.toExprString(fullName.toString());
    }
    else {
     
      Expression str=string(data);
      if(str!=null){
        exprName=CastString.toExprString(str);
      }
      else {
        data.cfml.setPos(start);
        return expr;
      }
    }

        comments(data);
       
        if (data.cfml.isCurrent('(')) {
      FunctionMember func = getFunctionMember(data,Identifier.toIdentifier("_createComponent",Identifier.CASE_ORIGNAL,null,null), true);
      func.addArgument(new Argument(exprName,"string"));
      Variable v=new Variable(expr.getStart(),expr.getEnd());
      v.addMember(func);
            comments(data);
      return v;
    }
        data.cfml.setPos(start);
        return expr;//throw new TemplateException(data.cfml,"invalid Component declaration ");
   
  }
 
 
 
 
 
  /**
  * Extrahiert den Start Element einer Variale,
  * dies ist entweder eine Funktion, eine Scope Definition oder eine undefinierte Variable.
  * <br />
  * EBNF:<br />
  * <code>identifier "(" functionArg ")" | scope | identifier;</code>
  * @param name Einstiegsname
  * @return CFXD Element
  * @throws TemplateException
  */
  private Variable startElement(ExprData data,Identifier name, Position line) throws TemplateException {
   
   
   
   
    // check function
    if (data.cfml.isCurrent('(')) {
      FunctionMember func = getFunctionMember(data,name, true);
     
      Variable var=new Variable(line,data.cfml.getPosition());
      var.addMember(func);
            comments(data);
      return var;
    }
   
    //check scope
    Variable var = scope(data,name,line);
    if(var!=null) return var;
   
    // undefined variable
    var=new Variable(line,data.cfml.getPosition());
    var.addMember(new DataMember(name));

        comments(data);
    return var;
   
  }
 
  /**
  * Liest einen CFML Scope aus,
  * falls der folgende identifier keinem Scope entspricht,
  * gibt die Variable null zurck.
  * <br />
  * EBNF:<br />
  * <code>"variable" | "cgi" | "url" | "form" | "session" | "application" | "arguments" | "cookie" | " client";</code>
   * @param id String identifier,
   * wird aus Optimierungszwechen nicht innerhalb dieser Funktion ausgelsen.
   * @return CFXD Variable Element oder null
   * @throws TemplateException
  */
  private Variable scope(ExprData data,Identifier id, Position line) throws TemplateException {
    String idStr=id.getUpper();
    if(data.ignoreScopes)return null;
    if (idStr.equals("CGI"))         return new Variable(Scope.SCOPE_CGI,line,data.cfml.getPosition());
    else if (idStr.equals("ARGUMENTS"))    return new Variable(Scope.SCOPE_ARGUMENTS,line,data.cfml.getPosition());
    else if (idStr.equals("REQUEST"))    return new Variable(Scope.SCOPE_REQUEST,line,data.cfml.getPosition());
    else if (idStr.equals("SESSION"))    return new Variable(Scope.SCOPE_SESSION,line,data.cfml.getPosition());
    else if (idStr.equals("APPLICATION"))  return new Variable(Scope.SCOPE_APPLICATION,line,data.cfml.getPosition());
    else if (idStr.equals("VARIABLES"))    return new Variable(Scope.SCOPE_VARIABLES,line,data.cfml.getPosition());
    else if (idStr.equals("FORM"))       return new Variable(Scope.SCOPE_FORM,line,data.cfml.getPosition());
    else if (idStr.equals("URL"))      return new Variable(Scope.SCOPE_URL,line,data.cfml.getPosition());
    else if (idStr.equals("SERVER"))     return new Variable(Scope.SCOPE_SERVER,line,data.cfml.getPosition());
    else if (idStr.equals("CLIENT"))    return new Variable(Scope.SCOPE_CLIENT,line,data.cfml.getPosition());
    else if (idStr.equals("COOKIE"))    return new Variable(Scope.SCOPE_COOKIE,line,data.cfml.getPosition());
    else if (idStr.equals("CLUSTER"))    return new Variable(Scope.SCOPE_CLUSTER,line,data.cfml.getPosition());
    else if (idStr.equals("LOCAL"))      return new Variable(Scope.SCOPE_LOCAL,line,data.cfml.getPosition());
    else if (idStr.equals("VAR")) {
      Identifier _id = identifier(data,false,true);
      if(_id!=null){
        comments(data);
        Variable local = new Variable(ScopeSupport.SCOPE_VAR,line,data.cfml.getPosition());
        if(!"LOCAL".equalsIgnoreCase(_id.getString()))local.addMember(new DataMember(_id));
        else {
          local.ignoredFirstMember(true);
        }
        return local;
      }
    }
    return null;
  }
   
  /**
  * Liest einen Identifier aus und gibt diesen als String zurck.
  * <br />
  * EBNF:<br />
  * <code>(letter | "_") {letter | "_"|digit};</code>
   * @param firstCanBeNumber
   * @param upper
  * @return Identifier.
  */
  protected Identifier identifier(ExprData data,boolean firstCanBeNumber,boolean upper) {
    Position start = data.cfml.getPosition();
    if(!data.cfml.isCurrentLetter() && !data.cfml.isCurrentSpecial() ) {
        if(!firstCanBeNumber) return null;
            else if(!data.cfml.isCurrentBetween('0','9'))return null;
        }
    do {
      data.cfml.next();
      if(!(data.cfml.isCurrentLetter()
        || data.cfml.isCurrentBetween('0','9')
        || data.cfml.isCurrentSpecial())) {
          break;
        }
    }
    while (data.cfml.isValidIndex());
    return Identifier.toIdentifier(data.cfml.substring(start.pos,data.cfml.getPos()-start.pos),
        upper && data.settings.dotNotationUpper?Identifier.CASE_UPPER:Identifier.CASE_ORIGNAL, start,data.cfml.getPosition());
  }
 
  protected String identifier(ExprData data,boolean firstCanBeNumber) {
    int start = data.cfml.getPos();
    if(!data.cfml.isCurrentLetter() && !data.cfml.isCurrentSpecial() ) {
        if(!firstCanBeNumber) return null;
            else if(!data.cfml.isCurrentBetween('0','9'))return null;
        }
    do {
      data.cfml.next();
      if(!(data.cfml.isCurrentLetter()
        || data.cfml.isCurrentBetween('0','9')
        || data.cfml.isCurrentSpecial())) {
          break;
        }
    }
    while (data.cfml.isValidIndex());
    return data.cfml.substring(start,data.cfml.getPos()-start);
  }

  /**
  * Transfomiert ein Collection Element das in eckigen Klammern aufgerufen wird.
  * <br />
  * EBNF:<br />
  * <code>"[" impOp "]"</code>
  * @return CFXD Element
  * @throws TemplateException
  */
  private ExprString structElement(ExprData data) throws TemplateException {
        comments(data);
        ExprString name = CastString.toExprString(assignOp(data));
    if(name instanceof LitString)((LitString)name).fromBracket(true);
        comments(data);
    return name;
  }

  /**
  * Liest die Argumente eines Funktonsaufruf ein und prft ob die Funktion
  * innerhalb der FLD (Function Library Descriptor) definiert ist.
  * Falls sie existiert wird die Funktion gegen diese geprft und ein build-in-function CFXD Element generiert,
  * ansonsten ein normales funcion-call Element.
  * <br />
  * EBNF:<br />
  * <code>[impOp{"," impOp}];</code>
  * @param name Identifier der Funktion als Zeichenkette
  * @param checkLibrary Soll geprft werden ob die Funktion innerhalb der Library existiert.
  * @return CFXD Element
  * @throws TemplateException
  */
  private FunctionMember getFunctionMember(ExprData data,
      final ExprString name,
    boolean checkLibrary)
    throws TemplateException {

    // get Function Library
    checkLibrary=checkLibrary && data.flibs!=null;
    FunctionLibFunction flf = null;
    if (checkLibrary) {
      if(!(name instanceof Literal))
        throw new TemplateException(data.cfml,"syntax error"); // should never happen!
     
      for (int i = 0; i < data.flibs.length; i++) {
        flf = data.flibs[i].getFunction(((Literal)name).getString());
        if (flf != null)break;
      }
      if (flf == null) {
        checkLibrary = false;
      }
    }
    // Element Function
    FunctionMember fm;
    if(checkLibrary) {
      BIF bif=new BIF(name,flf);
      bif.setArgType(flf.getArgType());
      bif.setClass(flf.getClazz());
      bif.setReturnType(flf.getReturnTypeAsString());
      fm=bif;
     
      if(flf.getArgType()== FunctionLibFunction.ARG_DYNAMIC && flf.hasDefaultValues()){
            ArrayList<FunctionLibFunctionArg> args = flf.getArg();
        Iterator<FunctionLibFunctionArg> it = args.iterator();
            FunctionLibFunctionArg arg;
            while(it.hasNext()){
              arg=it.next();
              if(arg.getDefaultValue()!=null)
                bif.addArgument(
                    new NamedArgument(
                        LitString.toExprString(arg.getName()),
                        LitString.toExprString(arg.getDefaultValue()),
                        arg.getTypeAsString(),false
                        ));
            }
      }
    }
    else {
      fm = new UDF(name);
    }
   
   
   

    // Function Attributes
    ArrayList<FunctionLibFunctionArg> arrFuncLibAtt = null;
    int libLen = 0;
    if (checkLibrary) {
      arrFuncLibAtt = flf.getArg();
      libLen = arrFuncLibAtt.size();
    }
    int count = 0;
    do {
      data.cfml.next();
            comments(data);

      // finish
      if (count==0 && data.cfml.isCurrent(')'))
        break;

      // too many Attributes
      boolean isDynamic=false;
      int max=-1;
      if(checkLibrary) {
        isDynamic=flf.getArgType()==FunctionLibFunction.ARG_DYNAMIC;
        max=flf.getArgMax();
      // Dynamic
        if(isDynamic) {
          if(max!=-1 && max <= count)
            throw new TemplateException(
              data.cfml,
              "too many Attributes in function [" + ASMUtil.display(name) + "]");
        }
      // Fix
        else {
          if(libLen <= count){
           
            TemplateException te = new TemplateException(
              data.cfml,
              "too many Attributes in function call [" + ASMUtil.display(name) + "]");
            UDFUtil.addFunctionDoc(te, flf);
            throw te;
          }
        }
       
      }
     
      //Argument arg;
      if (checkLibrary && !isDynamic) {
        // current attribues from library
        FunctionLibFunctionArg funcLibAtt =arrFuncLibAtt.get(count);
        fm.addArgument(functionArgument(data,funcLibAtt.getTypeAsString(),false))
      }
      else {
        fm.addArgument(functionArgument(data,false));
      }

            comments(data);
      count++;
      if (data.cfml.isCurrent(')'))
        break;
    }
    while (data.cfml.isCurrent(','));

    // end with ) ??   
    if (!data.cfml.forwardIfCurrent(')'))
      throw new TemplateException(
        data.cfml,
        "Invalid Syntax Closing [)] for function ["
          + ASMUtil.display(name)
          + "] not found");

    // check min attributes
    if (checkLibrary && flf.getArgMin() > count){
      TemplateException te = new TemplateException(
        data.cfml,
        "too few attributes in function [" + ASMUtil.display(name) + "]");
      if(flf.getArgType()==FunctionLibFunction.ARG_FIX) UDFUtil.addFunctionDoc(te, flf);
      throw te;
    }

        comments(data);
       
        // evaluator
        if(checkLibrary && flf.hasTteClass()){
          flf.getEvaluator().evaluate((BIF) fm, flf);
        }
       
    return fm;
  }
 
  /**
   * Sharps (#) die innerhalb von Expressions auftauchen haben in CFML keine weitere Beteutung
   * und werden durch diese Methode einfach entfernt.
   * <br />
   * Beispiel:<br />
   * <code>arrayLen(#arr#)</code> und <code>arrayLen(arr)</code> sind identisch.
   * EBNF:<br />
   * <code>"#" checker "#";</code>
   * @return CFXD Element
   * @throws TemplateException
  */
  private Expression sharp(ExprData data) throws TemplateException {
    if(!data.cfml.forwardIfCurrent('#'))
      return null;
    Expression expr;
        comments(data);
        boolean old=data.allowLowerThan;
        data.allowLowerThan=true;
    expr = assignOp(data);
    data.allowLowerThan=old;
        comments(data);
    if (!data.cfml.forwardIfCurrent('#'))
      throw new TemplateException(
        data.cfml,
        "Syntax Error, Invalid Construct "+(data.cfml.length()<30?data.cfml.toString():""));
        comments(data);
    return expr;
  }
 
  /**
   * @param data
   * @return parsed Element
   * @throws TemplateException
   */
  private Expression simple(ExprData data,String[] breakConditions) throws TemplateException {
    StringBuffer sb=new StringBuffer();
    Position line = data.cfml.getPosition();
    outer:while(data.cfml.isValidIndex()) {
      for(int i=0;i<breakConditions.length;i++){
        if(data.cfml.isCurrent(breakConditions[i]))break outer;
      }
     
      //if(data.cfml.isCurrent(' ') || data.cfml.isCurrent('>') || data.cfml.isCurrent("/>")) break;
     
      if(data.cfml.isCurrent('"') || data.cfml.isCurrent('#') || data.cfml.isCurrent('\'')) {
        throw new TemplateException(data.cfml,"simple attribute value can't contain ["+data.cfml.getCurrent()+"]");
      }
      sb.append(data.cfml.getCurrent());
      data.cfml.next();
    }
        comments(data);
   
    return LitString.toExprString(sb.toString(),line,data.cfml.getPosition());
  }
   

    /**
     *  Liest alle folgenden Komentare ein.
      * <br />
     * EBNF:<br />
     * <code>{?-"\n"} "\n";</code>
     * @param data
     * @throws TemplateException
     */
    protected void comments(ExprData data) throws TemplateException {
        data.cfml.removeSpace();
        while(comment(data)){data.cfml.removeSpace();}
    }
   
    /**
     *  Liest einen Einzeiligen Kommentar ein.
      * <br />
     * EBNF:<br />
     * <code>{?-"\n"} "\n";</code>
     * @return bool Wurde ein Kommentar entfernt?
     * @throws TemplateException
     */
    private boolean comment(ExprData data) throws TemplateException {
        if(singleLineComment(data.cfml) || multiLineComment(data) || CFMLTransformer.comment(data.cfml)) return true;
        return false;
    }

    /**
     * Liest einen Mehrzeiligen Kommentar ein.
     * <br />
     * EBNF:<br />
     * <code>?-"*<!-- -->/";</code>
     * @return bool Wurde ein Kommentar entfernt?
     * @throws TemplateException
     */
    private boolean multiLineComment(ExprData data) throws TemplateException {
       CFMLString cfml = data.cfml;
      if(!cfml.forwardIfCurrent("/*")) return false;
        int pos=cfml.getPos();
        boolean isDocComment=cfml.isCurrent('*');
        while(cfml.isValidIndex()) {
            if(cfml.isCurrent("*/")) break;
            cfml.next();
        }
        if(!cfml.forwardIfCurrent("*/")){
            cfml.setPos(pos);
            throw new TemplateException(cfml,"comment is not closed");
        }
        if(isDocComment) {
          String comment = cfml.substring(pos-2,cfml.getPos()-pos);
          data.docComment=docCommentTransformer.transform(comment);
        }
        return true;
    }
   
   
   
    /**
     *  Liest einen Einzeiligen Kommentar ein.
      * <br />
     * EBNF:<br />
     * <code>{?-"\n"} "\n";</code>
     * @return bool Wurde ein Kommentar entfernt?
     */
    private boolean singleLineComment(CFMLString cfml) {
        if(!cfml.forwardIfCurrent("//")) return false;
        return cfml.nextLine();
    }

}
TOP

Related Classes of railo.transformer.cfml.expression.AbstrCFMLExprTransformer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.