001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.interpreter;
020
021import java.util.ArrayList;
022import java.util.Map;
023
024import lucee.commons.lang.CFTypes;
025import lucee.commons.lang.ParserString;
026import lucee.runtime.MappingImpl;
027import lucee.runtime.PageContext;
028import lucee.runtime.PageSource;
029import lucee.runtime.config.ConfigImpl;
030import lucee.runtime.config.ConfigWebImpl;
031import lucee.runtime.engine.ThreadLocalPageContext;
032import lucee.runtime.exp.PageException;
033import lucee.runtime.interpreter.ref.Ref;
034import lucee.runtime.interpreter.ref.Set;
035import lucee.runtime.interpreter.ref.cast.Casting;
036import lucee.runtime.interpreter.ref.func.BIFCall;
037import lucee.runtime.interpreter.ref.func.UDFCall;
038import lucee.runtime.interpreter.ref.literal.LBoolean;
039import lucee.runtime.interpreter.ref.literal.LFunctionValue;
040import lucee.runtime.interpreter.ref.literal.LNumber;
041import lucee.runtime.interpreter.ref.literal.LString;
042import lucee.runtime.interpreter.ref.literal.LStringBuffer;
043import lucee.runtime.interpreter.ref.literal.Literal;
044import lucee.runtime.interpreter.ref.op.And;
045import lucee.runtime.interpreter.ref.op.BigDiv;
046import lucee.runtime.interpreter.ref.op.BigIntDiv;
047import lucee.runtime.interpreter.ref.op.BigMinus;
048import lucee.runtime.interpreter.ref.op.BigMod;
049import lucee.runtime.interpreter.ref.op.BigMulti;
050import lucee.runtime.interpreter.ref.op.BigPlus;
051import lucee.runtime.interpreter.ref.op.CT;
052import lucee.runtime.interpreter.ref.op.Concat;
053import lucee.runtime.interpreter.ref.op.Cont;
054import lucee.runtime.interpreter.ref.op.Div;
055import lucee.runtime.interpreter.ref.op.EEQ;
056import lucee.runtime.interpreter.ref.op.EQ;
057import lucee.runtime.interpreter.ref.op.EQV;
058import lucee.runtime.interpreter.ref.op.Elvis;
059import lucee.runtime.interpreter.ref.op.Exp;
060import lucee.runtime.interpreter.ref.op.GT;
061import lucee.runtime.interpreter.ref.op.GTE;
062import lucee.runtime.interpreter.ref.op.Imp;
063import lucee.runtime.interpreter.ref.op.IntDiv;
064import lucee.runtime.interpreter.ref.op.LT;
065import lucee.runtime.interpreter.ref.op.LTE;
066import lucee.runtime.interpreter.ref.op.Minus;
067import lucee.runtime.interpreter.ref.op.Mod;
068import lucee.runtime.interpreter.ref.op.Multi;
069import lucee.runtime.interpreter.ref.op.NCT;
070import lucee.runtime.interpreter.ref.op.NEEQ;
071import lucee.runtime.interpreter.ref.op.NEQ;
072import lucee.runtime.interpreter.ref.op.Negate;
073import lucee.runtime.interpreter.ref.op.Not;
074import lucee.runtime.interpreter.ref.op.Or;
075import lucee.runtime.interpreter.ref.op.Plus;
076import lucee.runtime.interpreter.ref.op.Xor;
077import lucee.runtime.interpreter.ref.var.Assign;
078import lucee.runtime.interpreter.ref.var.Bind;
079import lucee.runtime.interpreter.ref.var.DynAssign;
080import lucee.runtime.interpreter.ref.var.Variable;
081import lucee.runtime.type.scope.Scope;
082import lucee.runtime.type.scope.ScopeSupport;
083import lucee.transformer.library.function.FunctionLib;
084import lucee.transformer.library.function.FunctionLibFunction;
085import lucee.transformer.library.function.FunctionLibFunctionArg;
086
087import org.apache.commons.collections.map.ReferenceMap;
088
089/**
090 * 
091 * 
092    Der CFMLExprTransfomer implementiert das Interface ExprTransfomer, 
093    er bildet die Parser Grammatik ab, die unten definiert ist. 
094    Er erhaelt als Eingabe CFML Code, als String oder CFMLString, 
095    der einen CFML Expression erhaelt und liefert ein CFXD Element zurueck, 
096    das diesen Ausdruck abbildet.
097    Mithilfe der FunctionLibs, kann er Funktionsaufrufe, 
098    die Teil eines Ausdruck sein koennen, erkennen und validieren. 
099    Dies geschieht innerhalb der Methode function.
100    Falls ein Funktionsaufruf, einer Funktion innerhalb einer FunctionLib entspricht, 
101    werden diese gegeneinander verglichen und der Aufruf wird als Build-In-Funktion uebernommen, 
102    andernfalls wird der Funktionsaufruf als User-Defined-Funktion interpretiert.
103    Die Klasse Cast, Operator und ElementFactory (siehe 3.2) helfen ihm beim erstellen des Ausgabedokument CFXD.
104
105 * <pre>
106 * Parser Grammatik EBNF (Extended Backus-Naur Form) 
107
108    transform      = spaces impOp;
109    impOp          = eqvOp {"imp" spaces eqvOp};
110    eqvOp          = xorOp {"eqv" spaces xorOp};
111    xorOp          = orOp {"xor" spaces  orOp};
112    orOp           = andOp {("or" | "||") spaces andOp}; 
113            (* "||" Existiert in CFMX nicht *)
114    andOp          = notOp {("and" | "&&") spaces notOp}; 
115            (* "&&" Existiert in CFMX nicht *) 
116    notOp          = [("not"|"!") spaces] decsionOp; 
117            (* "!" Existiert in CFMX nicht *)
118    decsionOp      = concatOp {("neq"|"eq"|"gte"|"gt"|"lte"|"lt"|"ct"|
119                     "contains"|"nct"|"does not contain") spaces concatOp}; 
120            (* "ct"=conatains und "nct"=does not contain; Existiert in CFMX nicht *)
121    concatOp       = plusMinusOp {"&" spaces plusMinusOp};
122    plusMinusOp    = modOp {("-"|"+") spaces modOp};
123    
124    modOp          = divMultiOp {("mod" | "%") spaces divMultiOp}; 
125                    (* modulus operator , "%" Existiert in CFMX nicht *)
126    divMultiOp     = expoOp {("*"|"/") spaces expoOp};
127    expoOp         = clip {("exp"|"^") spaces clip}; 
128                    (*exponent operator, " exp " Existiert in CFMX nicht *)
129    clip           = ("(" spaces impOp ")" spaces) | checker;
130    checker        = string | number | dynamic | sharp;
131    string         = ("'" {"##"|"''"|"#" impOp "#"| ?-"#"-"'" } "'") | 
132                     (""" {"##"|""""|"#" impOp "#"| ?-"#"-""" } """);
133    number         = ["+"|"-"] digit {digit} {"." digit {digit}};
134    digit          = "0"|..|"9";
135    dynamic        = "true" | "false" | "yes" | "no" | startElement  
136                     {("." identifier | "[" structElement "]")[function] };
137    startElement   = identifier "(" functionArg ")" | scope | identifier;
138    scope          = "variable" | "cgi" | "url" | "form" | "session" | "application" | 
139                     "arguments" | "cookie" | " client";
140    identifier     = (letter | "_") {letter | "_"|digit};
141    structElement  = "[" impOp "]";
142    functionArg    = [impOp{"," impOp}];
143    sharp          = "#" checker "#";
144    spaces         = {space};
145    space          = "\s"|"\t"|"\f"|"\t"|"\n";
146    letter         = "a"|..|"z"|"A"|..|"Z";
147
148{"x"}= 0 bis n mal "x"
149["x"]= 0 bis 1 mal "x"
150("x" | "y")"z" = "xz" oder "yz"
151
152</pre>
153 *
154 */
155public class CFMLExpressionInterpreter {
156
157
158    private static final LNumber PLUS_ONE = new LNumber(new Double(1));
159    private static final LNumber MINUS_ONE = new LNumber(new Double(-1));
160        
161    protected static final short STATIC=0;
162    private static final short DYNAMIC=1;
163        private static FunctionLibFunction LITERAL_ARRAY = null;
164        private static FunctionLibFunction LITERAL_STRUCT = null;
165        private static FunctionLibFunction JSON_ARRAY = null;
166        private static FunctionLibFunction JSON_STRUCT = null;
167        
168    protected short mode=0;
169    
170    protected ParserString cfml;
171    protected PageContext pc;
172    private FunctionLib fld;
173        protected boolean allowNullConstant=false;
174        private boolean preciseMath;
175        private final boolean isJson;
176        private final boolean limited;
177        
178    private final static Map<String,Ref> data=new ReferenceMap(ReferenceMap.SOFT,ReferenceMap.SOFT);
179
180    public CFMLExpressionInterpreter() {
181        this.isJson=this instanceof JSONExpressionInterpreter;
182        this.limited=true; 
183    }
184    public CFMLExpressionInterpreter(boolean limited) {
185        this.isJson=this instanceof JSONExpressionInterpreter;
186        this.limited=limited || isJson; // json is always limited
187    }
188
189         
190    public Object interpret(PageContext pc,String str) throws PageException {
191        return interpret(pc,str,false);
192    }
193    
194
195    public Object interpret(PageContext pc,String str, boolean preciseMath) throws PageException { 
196        this.cfml=new ParserString(str);
197        this.preciseMath = preciseMath;
198        this.pc=ThreadLocalPageContext.get(pc);
199        if(pc!=null)fld=((ConfigImpl)pc.getConfig()).getCombinedFLDs();
200        
201        
202        
203        
204        
205        if(LITERAL_ARRAY==null)LITERAL_ARRAY=fld.getFunction("_literalArray");
206        if(LITERAL_STRUCT==null)LITERAL_STRUCT=fld.getFunction("_literalStruct");
207        if(JSON_ARRAY==null)JSON_ARRAY=fld.getFunction("_jsonArray");
208        if(JSON_STRUCT==null)JSON_STRUCT=fld.getFunction("_jsonStruct");
209        
210        
211                
212        cfml.removeSpace();
213        Ref ref = assignOp();
214        cfml.removeSpace();
215        
216        if(cfml.isAfterLast()) {
217            return ref.getValue(pc);
218        }
219        throw new InterpreterException("Syntax Error, invalid Expression ["+cfml.toString()+"]");
220    }
221
222    
223    protected Object interpretPart(PageContext pc,ParserString cfml) throws PageException { 
224        this.cfml = cfml;
225        this.pc=ThreadLocalPageContext.get(pc);
226        if(pc!=null)fld=((ConfigImpl)pc.getConfig()).getCombinedFLDs();
227        
228        cfml.removeSpace();
229        return assignOp().getValue(pc);
230    }
231
232    /**
233    * Liest einen gelableten  Funktionsparamter ein
234    * <br />
235    * EBNF:<br />
236    * <code>assignOp [":" spaces assignOp];</code>
237    * @return CFXD Element
238    * @throws PageException 
239    */
240    private Ref functionArgDeclarationVarString() throws PageException {
241        
242            cfml.removeSpace();
243            StringBuilder str=new StringBuilder();
244            String id=null;
245            while((id=identifier(false))!=null) {
246                if(str.length()>0)str.append('.');
247                str.append(id);
248                cfml.removeSpace();
249                if(!cfml.forwardIfCurrent('.')) break;
250                cfml.removeSpace();
251            }
252            cfml.removeSpace();
253            if(str.length()>0 && cfml.charAt(cfml.getPos()-1)!='.') 
254                return new LString(str.toString());
255
256        throw new InterpreterException("invalid variable name definition");
257    }
258
259    /**
260    * Liest einen gelableten  Funktionsparamter ein
261    * <br />
262    * EBNF:<br />
263    * <code>assignOp [":" spaces assignOp];</code>
264    * @return CFXD Element
265    * @throws PageException 
266    */
267    private Ref functionArgDeclaration() throws PageException {
268        Ref ref = impOp();
269        if (cfml.forwardIfCurrent(':') || cfml.forwardIfCurrent('=')) {
270            cfml.removeSpace();
271            ref=new LFunctionValue(ref,assignOp());
272        }
273        return ref;
274    }
275
276    /**
277    * Transfomiert Zuweisungs Operation.
278    * <br />
279    * EBNF:<br />
280    * <code>eqvOp ["=" spaces assignOp];</code>
281    * @return CFXD Element
282    * @throws PageException 
283    */
284    protected Ref assignOp() throws PageException {
285        Ref ref = contOp();
286
287        if (cfml.forwardIfCurrent('=')) {
288            cfml.removeSpace();
289            if(mode==STATIC || ref instanceof Literal) {
290                ref=new DynAssign(ref,assignOp(),limited);
291            }
292            else {
293                ref=new Assign(ref,assignOp(),limited);
294            }
295        }
296        return ref;
297    }
298    
299
300    private Ref contOp() throws PageException {
301        Ref ref = impOp();
302        while(cfml.forwardIfCurrent('?')) {
303            cfml.removeSpace();
304            if(cfml.forwardIfCurrent(':')){
305                cfml.removeSpace();
306                Ref right = assignOp();    
307                ref=new Elvis(ref,right);
308                
309            }
310            else {
311                    Ref left = assignOp();
312                    if(!cfml.forwardIfCurrent(':'))
313                        throw new InterpreterException("Syntax Error, invalid conditional operator ["+cfml.toString()+"]");
314                    cfml.removeSpace();
315                    Ref right = assignOp();
316                    ref=new Cont(ref,left,right);
317            }
318        }
319        return ref;
320    }
321
322
323        /**
324    * Transfomiert eine Implication (imp) Operation.
325    * <br />
326    * EBNF:<br />
327    * <code>eqvOp {"imp" spaces eqvOp};</code>
328    * @return CFXD Element
329    * @throws PageException 
330    */
331    private Ref impOp() throws PageException {
332        Ref ref = eqvOp();
333        while(cfml.forwardIfCurrentAndNoWordAfter("imp")) {
334            cfml.removeSpace();
335            ref=new Imp(ref,eqvOp());
336        }
337        return ref;
338    }
339
340    /**
341    * Transfomiert eine  Equivalence (eqv) Operation.
342    * <br />
343    * EBNF:<br />
344    * <code>xorOp {"eqv" spaces xorOp};</code>
345    * @return CFXD Element
346    * @throws PageException 
347    */
348    private Ref eqvOp() throws PageException {
349        Ref ref = xorOp();
350        while(cfml.forwardIfCurrent("eqv")) {
351            cfml.removeSpace();
352            ref=new EQV(ref,xorOp());
353        }
354        return ref;
355    }
356
357    /**
358    * Transfomiert eine  Xor (xor) Operation.
359    * <br />
360    * EBNF:<br />
361    * <code>orOp {"xor" spaces  orOp};</code>
362    * @return CFXD Element
363    * @throws PageException 
364    */
365    private Ref xorOp() throws PageException {
366        Ref ref = orOp();
367        while(cfml.forwardIfCurrent("xor")) {
368            cfml.removeSpace();
369            ref=new Xor(ref,orOp());
370        }
371        return ref;
372    }
373
374    /**
375    * Transfomiert eine  Or (or) Operation. Im Gegensatz zu CFMX ,
376    * werden "||" Zeichen auch als Or Operatoren anerkannt.
377    * <br />
378    * EBNF:<br />
379    * <code>andOp {("or" | "||") spaces andOp}; (* "||" Existiert in CFMX nicht *)</code>
380    * @return CFXD Element
381    * @throws PageException 
382    */
383    private Ref orOp() throws PageException {
384        Ref ref = andOp();
385        while(cfml.isValidIndex() && (cfml.forwardIfCurrent("||") || cfml.forwardIfCurrent("or"))) {
386            cfml.removeSpace();
387            ref=new Or(ref,andOp());
388        }
389        return ref;
390    }
391
392    /**
393    * Transfomiert eine  And (and) Operation. Im Gegensatz zu CFMX ,
394    * werden "&&" Zeichen auch als And Operatoren anerkannt.
395    * <br />
396    * EBNF:<br />
397    * <code>notOp {("and" | "&&") spaces notOp}; (* "&&" Existiert in CFMX nicht *)</code>
398    * @return CFXD Element
399    * @throws PageException 
400    */
401    private Ref andOp() throws PageException {
402        Ref ref = notOp();
403        while(cfml.isValidIndex() && (cfml.forwardIfCurrent("&&") || cfml.forwardIfCurrent("and"))) {
404            cfml.removeSpace();
405            ref=new And(ref,notOp());
406        }
407        return ref;
408    }
409
410    /**
411    * Transfomiert eine  Not (not) Operation. Im Gegensatz zu CFMX ,
412    * wird das "!" Zeichen auch als Not Operator anerkannt.
413    * <br />
414    * EBNF:<br />
415    * <code>[("not"|"!") spaces] decsionOp; (* "!" Existiert in CFMX nicht *)</code>
416    * @return CFXD Element
417    * @throws PageException 
418    */
419    private Ref notOp() throws PageException {
420        if(cfml.isValidIndex()) {
421                if (cfml.isCurrent('!') && !cfml.isCurrent("!=")) {
422                        cfml.next();
423                    cfml.removeSpace();
424                    return new Not(decsionOp());
425                }
426                else if (cfml.forwardIfCurrentAndNoWordAfter("not")) {
427                    cfml.removeSpace();
428                    return new Not(decsionOp());
429                }
430        }
431        return decsionOp();
432    }
433
434    /**
435    * <font f>Transfomiert eine Vergleichs Operation.
436    * <br />
437    * EBNF:<br />
438    * <code>concatOp {("neq"|"eq"|"gte"|"gt"|"lte"|"lt"|"ct"|
439                     "contains"|"nct"|"does not contain") spaces concatOp}; 
440            (* "ct"=conatains und "nct"=does not contain; Existiert in CFMX nicht *)</code>
441    * @return CFXD Element
442    * @throws PageException 
443    */
444    private Ref decsionOp() throws PageException {
445
446        Ref ref = concatOp();
447        boolean hasChanged=false;
448        // ct, contains
449        if(cfml.isValidIndex()){
450            do {
451                hasChanged=false;
452                if(cfml.isCurrent('c')) {
453                        if (cfml.forwardIfCurrent("ct")) {
454                            cfml.removeSpace();
455                            ref=new CT(ref,concatOp());
456                            hasChanged=true;
457                        } 
458                        else if (cfml.forwardIfCurrent("contains")){ 
459                            cfml.removeSpace();
460                            ref=new CT(ref,concatOp());
461                            hasChanged=true;
462                        }
463                }
464                // does not contain
465                else if (cfml.forwardIfCurrent("does","not","contain")){ 
466                    cfml.removeSpace();
467                    ref=new NCT(ref,concatOp());
468                    hasChanged=true;
469                }
470                
471                // equal, eq
472                else if (cfml.isCurrent("eq") && !cfml.isCurrent("eqv")) {
473                    cfml.setPos(cfml.getPos()+2);
474                    cfml.forwardIfCurrent("ual");
475                    cfml.removeSpace();
476                    ref=new EQ(ref,concatOp());
477                    hasChanged=true;
478                }
479                // ==
480                else if (cfml.forwardIfCurrent("==")) {
481                    if(cfml.forwardIfCurrent('='))              {
482                        cfml.removeSpace();
483                        ref = new EEQ(ref,concatOp());
484                    }
485                                else {
486                                        cfml.removeSpace();
487                        ref=new EQ(ref,concatOp());
488                                }
489                    hasChanged=true;
490                }
491                
492                // !=
493                else if (cfml.forwardIfCurrent("!=")) {
494                    if(cfml.forwardIfCurrent('=')) {
495                        cfml.removeSpace();
496                        ref = new NEEQ(ref,concatOp());
497                    }
498                                else {
499                                        cfml.removeSpace();
500                        ref=new NEQ(ref,concatOp());
501                                }
502                    hasChanged=true;
503                }
504
505                // <=/</<>
506                        else if (cfml.forwardIfCurrent('<')) {
507                                if(cfml.forwardIfCurrent('='))          {
508                                        cfml.removeSpace();
509                        ref = new LTE(ref,concatOp());
510                                }
511                                else if(cfml.forwardIfCurrent('>')) {
512                                        cfml.removeSpace();
513                        ref = new NEQ(ref,concatOp());
514                                }
515                                else                                                            {
516                                        cfml.removeSpace();
517                        ref = new LT(ref,concatOp());
518                                }
519                                hasChanged=true;
520                        }
521                // >/>=
522                        else if (cfml.forwardIfCurrent('>')) {
523                                if(cfml.forwardIfCurrent('='))          {
524                                        cfml.removeSpace();
525                        ref = new GTE(ref,concatOp());
526                                }
527                                else                                                            {
528                                        cfml.removeSpace();
529                        ref = new GT(ref,concatOp());
530                                }
531                                hasChanged=true;
532                        }
533                
534                // gt, gte, greater than or equal to, greater than
535                else if (cfml.isCurrent('g')) {
536                    if (cfml.forwardIfCurrent("gt")) {
537                        if(cfml.forwardIfCurrent('e')) {
538                            cfml.removeSpace();
539                            ref=new GTE(ref,concatOp());
540                        }
541                        else {
542                            cfml.removeSpace();
543                            ref=new GT(ref,concatOp());
544                        }
545                        hasChanged=true;
546                    } 
547                    else if (cfml.forwardIfCurrent("greater","than")) {
548                        if(cfml.forwardIfCurrent("or" ,"equal", "to",true)) {
549                            cfml.removeSpace();
550                            ref=new GTE(ref,concatOp());
551                        }
552                        else {
553                            cfml.removeSpace();
554                            ref=new GT(ref,concatOp());
555                        }
556                        hasChanged=true;
557                    }   
558                    else if (cfml.forwardIfCurrent("ge")) {
559                        cfml.removeSpace();
560                        ref=new GTE(ref,concatOp());
561                        hasChanged=true;
562                    }               
563                }
564                
565                // is, is not
566                else if (cfml.forwardIfCurrent("is")) {
567                    if(cfml.forwardIfCurrent("not",true)) {
568                        cfml.removeSpace();
569                        ref=new NEQ(ref,concatOp());
570                    }
571                    else {
572                        cfml.removeSpace();
573                        ref=new EQ(ref,concatOp());
574                    }
575                    hasChanged=true;
576                }
577                
578                // lt, lte, less than, less than or equal to
579                else if (cfml.isCurrent('l')) {
580                    if (cfml.forwardIfCurrent("lt")) {
581                        if(cfml.forwardIfCurrent('e')) {
582                            cfml.removeSpace();
583                            ref=new LTE(ref,concatOp());
584                        }
585                        else {
586                            cfml.removeSpace();
587                            ref=new LT(ref,concatOp());
588                        }
589                        hasChanged=true;
590                    } 
591                    else if (cfml.forwardIfCurrent("less","than")) {
592                        if(cfml.forwardIfCurrent("or", "equal", "to",true)) {
593                            cfml.removeSpace();
594                            ref=new LTE(ref,concatOp());
595                        }
596                        else {
597                            cfml.removeSpace();
598                            ref=new LT(ref,concatOp());
599                        }
600                        hasChanged=true;
601                    }   
602                    else if (cfml.forwardIfCurrent("le")) {
603                        cfml.removeSpace();
604                        ref=new LTE(ref,concatOp());
605                        hasChanged=true;
606                    }               
607                }
608                
609                // neq, not equal, nct
610                else if (cfml.isCurrent('n')) {
611                    // Not Equal
612                        if (cfml.forwardIfCurrent("neq"))   {
613                            cfml.removeSpace(); 
614                            ref=new NEQ(ref,concatOp());
615                            hasChanged=true;
616                        }
617                    // Not Equal (Alias)
618                        else if (cfml.forwardIfCurrent("not","equal")){ 
619                            cfml.removeSpace();
620                            ref=new NEQ(ref,concatOp());
621                            hasChanged=true; 
622                        }
623                    // nct
624                        else if (cfml.forwardIfCurrent("nct"))  { 
625                            cfml.removeSpace();
626                            ref=new NCT(ref,concatOp());
627                            hasChanged=true;
628                        }   
629                }
630            }while(hasChanged);
631        }
632        return ref;
633    }
634
635    /**
636    * Transfomiert eine  Konkatinations-Operator (&) Operation. Im Gegensatz zu CFMX ,
637    * wird das "!" Zeichen auch als Not Operator anerkannt.
638    * <br />
639    * EBNF:<br />
640    * <code>plusMinusOp {"&" spaces concatOp};</code>
641    * @return CFXD Element
642    * @throws PageException 
643    */
644    private Ref concatOp() throws PageException {
645        Ref ref = plusMinusOp();
646        
647        while(cfml.isCurrent('&') && !cfml.isNext('&')) {
648            cfml.next();
649            ref=_concat(ref);
650        }
651        return ref;
652    }
653
654    /**
655    * Transfomiert die mathematischen Operatoren Plus und Minus (1,-).
656    * <br />
657    * EBNF:<br />
658    * <code>modOp [("-"|"+") spaces plusMinusOp];</code>
659    * @return CFXD Element
660    * @throws PageException 
661    */
662    private Ref plusMinusOp() throws PageException {
663        Ref ref = modOp();
664        
665        while(!cfml.isLast()) {
666            // Plus Operation
667            if (cfml.forwardIfCurrent('+')) {
668                ref=_plus(ref);
669            }
670            // Minus Operation
671            else if (cfml.forwardIfCurrent('-')) {
672                ref=_minus(ref);
673            }
674            else break;
675        }
676        return ref;
677    }
678
679    private Ref _plus(Ref ref) throws PageException {
680                // +=
681                if (cfml.isCurrent('=')) {
682                        cfml.next();
683                        cfml.removeSpace();
684                        Ref right = assignOp();
685                        Ref res = preciseMath?new BigPlus(ref,right):new Plus(ref,right);
686                        ref=new Assign(ref,res,limited);
687                }
688                else {  
689            cfml.removeSpace();
690            ref=preciseMath?new BigPlus(ref,modOp()):new Plus(ref,modOp());
691                }
692                return ref;
693        }
694    
695    private Ref _minus(Ref ref) throws PageException {
696                // -=
697                if (cfml.isCurrent('=')) {
698                        cfml.next();
699                        cfml.removeSpace();
700                        Ref right = assignOp();
701                        Ref res = preciseMath?new BigMinus(ref,right):new Minus(ref,right);
702                        ref=new Assign(ref,res,limited);
703                }
704                else {  
705            cfml.removeSpace();
706            ref=preciseMath?new BigMinus(ref,modOp()):new Minus(ref,modOp());
707                }
708                return ref;
709        }
710    
711
712    private Ref _div(Ref ref) throws PageException {
713                // /=
714                if (cfml.forwardIfCurrent('=')) {
715                        cfml.removeSpace();
716                        Ref right = assignOp();
717                        Ref res = preciseMath?new BigDiv(ref, right):new Div(ref,right);
718                        ref=new Assign(ref,res,limited);
719                }
720                else {  
721            cfml.removeSpace();
722            ref=preciseMath?new BigDiv(ref,expoOp()):new Div(ref,expoOp());
723                }
724                return ref;
725        }
726    
727    private Ref _intdiv(Ref ref) throws PageException {
728                // \=
729                if (cfml.forwardIfCurrent('=')) {
730                        cfml.removeSpace();
731                        Ref right = assignOp();
732                        Ref res = preciseMath?new BigIntDiv(ref,right):new IntDiv(ref,right);
733                        ref=new Assign(ref,res,limited);
734                }
735                else {  
736            cfml.removeSpace();
737            ref=preciseMath?new BigIntDiv(ref,expoOp()):new IntDiv(ref,expoOp());
738                }
739                return ref;
740        }
741
742    private Ref _mod(Ref ref) throws PageException {
743                // %=
744                if (cfml.forwardIfCurrent('=')) {
745                        cfml.removeSpace();
746                        Ref right = assignOp();
747                        Ref res = preciseMath?new BigMod(ref,right):new Mod(ref,right);
748                        ref=new Assign(ref,res,limited);
749                }
750                else {  
751            cfml.removeSpace();
752            ref=preciseMath?new BigMod(ref,divMultiOp()):new Mod(ref,divMultiOp());
753                }
754                return ref;
755        }
756    private Ref _concat(Ref ref) throws PageException {
757                // &=
758                if (cfml.forwardIfCurrent('=')) {
759                        cfml.removeSpace();
760                        Ref right = assignOp();
761                        Ref res = new  Concat(ref,right);
762                        ref=new Assign(ref,res,limited);
763                }
764                else {  
765            cfml.removeSpace();
766            ref=new Concat(ref,plusMinusOp());
767                }
768                return ref;
769        }
770    
771    private Ref _multi(Ref ref) throws PageException {
772                // \=
773                if (cfml.forwardIfCurrent('=')) {
774                        cfml.removeSpace();
775                        Ref right = assignOp();
776                        Ref res = preciseMath?new BigMulti(ref,right):new Multi(ref,right);
777                        ref=new Assign(ref,res,limited);
778                }
779                else {  
780            cfml.removeSpace();
781            ref=preciseMath?new BigMulti(ref,expoOp()):new Multi(ref,expoOp());
782                }
783                return ref;
784        }
785    
786    /**
787    * Transfomiert eine Modulus Operation. Im Gegensatz zu CFMX ,
788    * wird das "%" Zeichen auch als Modulus Operator anerkannt.
789    * <br />
790    * EBNF:<br />
791    * <code>divMultiOp {("mod" | "%") spaces divMultiOp}; (* modulus operator , "%" Existiert in CFMX nicht *)</code>
792    * @return CFXD Element
793    * @throws PageException 
794    */
795    private Ref modOp() throws PageException {
796        Ref ref = divMultiOp();
797        
798        while(cfml.isValidIndex() && (cfml.forwardIfCurrent('%') || cfml.forwardIfCurrent("mod"))) {
799            ref=_mod(ref);
800        }
801        return ref;
802    }
803
804    /**
805    * Transfomiert die mathematischen Operatoren Mal und Durch (*,/).
806    * <br />
807    * EBNF:<br />
808    * <code>expoOp {("*"|"/") spaces expoOp};</code>
809    * @return CFXD Element
810    * @throws PageException 
811    */
812    private Ref divMultiOp() throws PageException {
813        Ref ref = expoOp();
814
815        while (!cfml.isLast()) {
816            // Multiply Operation
817            if(cfml.forwardIfCurrent('*')) {
818                ref=_multi(ref);
819            }
820            // Divide Operation
821            else if (cfml.isCurrent('/') && (!cfml.isCurrent("/>") )) {
822                cfml.next(); 
823                ref=_div(ref);
824            }
825            // Divide Operation
826            else if (cfml.isCurrent('\\')) {
827                cfml.next(); 
828                ref=_intdiv(ref);
829            }
830            else {
831                break;
832            }
833        }
834        return ref;
835    }
836
837    /**
838    * Transfomiert den Exponent Operator (^,exp). Im Gegensatz zu CFMX ,
839    * werden die Zeichen " exp " auch als Exponent anerkannt.
840    * <br />
841    * EBNF:<br />
842    * <code>clip {("exp"|"^") spaces clip};</code>
843    * @return CFXD Element
844    * @throws PageException 
845    */
846    private Ref expoOp() throws PageException {
847        Ref ref = unaryOp();
848
849        while(cfml.isValidIndex() && (cfml.forwardIfCurrent('^') || cfml.forwardIfCurrent("exp"))) {
850            cfml.removeSpace();
851            ref=new Exp(ref,unaryOp());
852        }
853        return ref;
854    }
855    
856
857    private Ref unaryOp() throws PageException {
858        Ref ref = negateMinusOp();
859        
860                if (cfml.forwardIfCurrent("--")) 
861                        ref=_unaryOp(ref, false);
862                
863                else if (cfml.forwardIfCurrent("++")) 
864                        ref=_unaryOp(ref, true);
865                return ref;
866        }
867    
868    private Ref _unaryOp(Ref ref,boolean isPlus) throws PageException {
869        cfml.removeSpace();
870                Ref res = preciseMath?new BigPlus(ref,isPlus?PLUS_ONE:MINUS_ONE):new Plus(ref,isPlus?PLUS_ONE:MINUS_ONE);
871                ref=new Assign(ref,res,limited);
872                return preciseMath?new BigPlus(ref,isPlus?MINUS_ONE:PLUS_ONE):new Plus(ref,isPlus?MINUS_ONE:PLUS_ONE);
873        }
874    
875
876    /**
877    * Liest die Vordlobe einer Zahl ein
878    * @return CFXD Element
879    * @throws PageException 
880    */
881    private Ref negateMinusOp() throws PageException {
882        // And Operation
883        if (cfml.forwardIfCurrent('-')) {
884                if (cfml.forwardIfCurrent('-')) {
885                        cfml.removeSpace();
886                                Ref expr = clip();
887                                Ref res = preciseMath?new BigMinus(expr,new LNumber(new Double(1))):new Minus(expr,new LNumber(new Double(1)));
888                                return new Assign(expr,res,limited);
889                        }       
890            cfml.removeSpace();
891            return new Negate(clip());
892                
893        }
894        if (cfml.forwardIfCurrent('+')) {
895                if (cfml.forwardIfCurrent('+')) {
896                        cfml.removeSpace();
897                                Ref expr = clip();
898                                Ref res = preciseMath?new BigPlus(expr,new LNumber(new Double(1))):new Plus(expr,new LNumber(new Double(1)));
899                                return new Assign(expr,res,limited);
900                        }
901                cfml.removeSpace();
902                return new Casting("numeric",CFTypes.TYPE_NUMERIC,clip());
903                
904        }
905        return clip();
906    }
907
908    /**
909    * Verarbeitet Ausdruecke die inerhalb einer Klammer stehen.
910    * <br />
911    * EBNF:<br />
912    * <code>("(" spaces impOp ")" spaces) | checker;</code>
913    * @return CFXD Element
914    * @throws PageException 
915    */
916    private Ref clip() throws PageException {
917        return checker();
918    }
919    
920    /**
921    * Hier werden die verschiedenen Moeglichen Werte erkannt 
922    * und jenachdem wird mit der passenden Methode weitergefahren
923    * <br />
924    * EBNF:<br />
925    * <code>string | number | dynamic | sharp;</code>
926    * @return CFXD Element
927    * @throws PageException 
928    */
929    private Ref checker() throws PageException {
930        
931        Ref ref=null;  
932        // String
933            if(cfml.isCurrentQuoter()) {
934                // mode=STATIC; is at the end of the string function because must set after execution
935                return string();
936            } 
937        // Number
938            if(cfml.isCurrentDigit() || cfml.isCurrent('.')) {
939                // mode=STATIC; is at the end of the string function because must set after execution
940                return number();
941            } 
942        // Dynamic
943            if(((ref=dynamic())!=null)) {
944                mode=DYNAMIC;
945                return ref;
946            } 
947        // Sharp
948            if(!limited &&(ref=sharp())!=null) {
949                mode=DYNAMIC;
950                return ref;
951            }  
952        // JSON
953            if((ref=json(isJson?JSON_ARRAY:LITERAL_ARRAY,'[',']'))!=null) {
954                                mode=DYNAMIC;
955                                return ref;
956                        } 
957                        if((ref=json(isJson?JSON_STRUCT:LITERAL_STRUCT,'{','}'))!=null) {
958                                mode=DYNAMIC;
959                                return ref;
960                        } 
961            
962                        if(cfml.isAfterLast() && cfml.toString().trim().length()==0)
963                                return new LString("");
964            
965        // else Error
966                        String str=cfml.toString();
967                        int pos=cfml.getPos();
968                        if(str.length()>100) {
969                                // Failure is in the beginning
970                                if(pos<=10) {
971                                        str=str.substring(0,20)+" ...";
972                                }
973                                // Failure is in the end
974                                else if((str.length()-pos)<=10) {
975                                        str="... "+str.substring(str.length()-20,str.length());
976                                }
977                                else {
978                                        str="... "+str.substring(pos-10,pos+10)+" ...";
979                                }
980                        }
981                        throw new InterpreterException("Syntax Error, Invalid Construct","at position "+(pos+1)+" in ["+str+"]");  
982    }
983    
984    
985    protected Ref json(FunctionLibFunction flf, char start, char end) throws PageException {
986                if(!cfml.isCurrent(start))return null;
987                
988                Ref[] args = functionArg(flf.getName(), false, flf,end);
989                
990                return new BIFCall(flf,args);
991        }
992    
993    /**
994    * Transfomiert einen lierale Zeichenkette.
995    * <br />
996    * EBNF:<br />
997    * <code>("'" {"##"|"''"|"#" impOp "#"| ?-"#"-"'" } "'") | 
998                     (""" {"##"|""""|"#" impOp "#"| ?-"#"-""" } """);</code>
999    * @return CFXD Element
1000    * @throws PageException 
1001    */
1002    protected Ref string() throws PageException {
1003                        
1004        // Init Parameter
1005        char quoter = cfml.getCurrentLower();
1006        LStringBuffer str=new LStringBuffer();
1007        Ref value=null;
1008        
1009        while(cfml.hasNext()) {
1010            cfml.next();
1011            // check sharp
1012            if(!limited && cfml.isCurrent('#')) {
1013                if(cfml.isNext('#')){
1014                    cfml.next();
1015                    str.append('#');
1016                }
1017                else {
1018                    cfml.next();
1019                    cfml.removeSpace();
1020                    if(!str.isEmpty() || value!=null) str.append(assignOp());
1021                    else value=assignOp();
1022                    cfml.removeSpace();
1023                    if (!cfml.isCurrent('#')) throw new InterpreterException("Invalid Syntax Closing [#] not found");
1024                }
1025            }
1026            else if(cfml.isCurrent(quoter)) {
1027                if(cfml.isNext(quoter)){
1028                    cfml.next();
1029                    str.append(quoter);
1030                }
1031                else {
1032                    break;
1033                }               
1034            }
1035            // all other character
1036            else {
1037                str.append(cfml.getCurrent());
1038            }
1039        }
1040        if(!cfml.forwardIfCurrent(quoter))
1041            throw new InterpreterException("Invalid String Literal Syntax Closing ["+quoter+"] not found");
1042        
1043        cfml.removeSpace();
1044        mode=STATIC;
1045        if(value!=null) {
1046            if(str.isEmpty()) return value;
1047            return new Concat(value,str);
1048        }
1049        return str;
1050    }
1051
1052    /**
1053    * Transfomiert einen numerische Wert. 
1054    * Die Laenge des numerischen Wertes interessiert nicht zu uebersetzungszeit, 
1055    * ein "Overflow" fuehrt zu einem Laufzeitfehler. 
1056    * Da die zu erstellende CFXD, bzw. dieser Transfomer, keine Vorwegnahme des Laufzeitsystems vornimmt. 
1057    * <br />
1058    * EBNF:<br />
1059    * <code>["+"|"-"] digit {digit} {"." digit {digit}};</code>
1060    * @return CFXD Element
1061    * @throws PageException 
1062    */
1063    private Ref number() throws PageException {
1064        // check first character is a number literal representation
1065        StringBuilder rtn=new StringBuilder(6);
1066        
1067        // get digit on the left site of the dot
1068        if(cfml.isCurrent('.')) rtn.append('0');
1069        else digit(rtn);
1070        // read dot if exist
1071        if(cfml.forwardIfCurrent('.')) {
1072            rtn.append('.');
1073            int before=cfml.getPos();
1074            digit(rtn);
1075
1076            if(before<cfml.getPos() && cfml.forwardIfCurrent('e')) {
1077                Boolean expOp=null;
1078                                if(cfml.forwardIfCurrent('+')) expOp=Boolean.TRUE;
1079                                else if(cfml.forwardIfCurrent('-')) expOp=Boolean.FALSE;
1080                                
1081                
1082                if(cfml.isCurrentDigit()) {
1083                        if(expOp==Boolean.FALSE) rtn.append("e-");
1084                                        else if(expOp==Boolean.TRUE) rtn.append("e+");
1085                                        else rtn.append('e');
1086                    digit(rtn);
1087                }
1088                else {
1089                        if(expOp!=null) cfml.previous();
1090                                cfml.previous();
1091                }
1092            }
1093            
1094            
1095            // read right side of the dot
1096            if(before==cfml.getPos())
1097                throw new InterpreterException("Number can't end with [.]");
1098        }
1099        cfml.removeSpace();
1100        mode=STATIC;
1101        return new LNumber(rtn.toString());
1102        
1103    }
1104    
1105    /**
1106    * Liest die reinen Zahlen innerhalb des CFMLString aus und gibt diese als Zeichenkette zurueck. 
1107    * <br />
1108    * EBNF:<br />
1109    * <code>"0"|..|"9";</code>
1110     * @param rtn
1111    */
1112    private void digit(StringBuilder rtn) {
1113        
1114        while (cfml.isValidIndex()) {
1115            if(!cfml.isCurrentDigit())break;
1116            rtn.append(cfml.getCurrentLower());
1117            cfml.next();
1118        }
1119    }
1120
1121    /**
1122    * Liest den folgenden idetifier ein und prueft ob dieser ein boolscher Wert ist. 
1123    * Im Gegensatz zu CFMX wird auch "yes" und "no" als bolscher <wert akzeptiert, 
1124    * was bei CFMX nur beim Umwandeln einer Zeichenkette zu einem boolschen Wert der Fall ist.<br />
1125    * Wenn es sich um keinen bolschen Wert handelt wird der folgende Wert eingelesen mit seiner ganzen Hirarchie.
1126    * <br />
1127    * EBNF:<br />
1128    * <code>"true" | "false" | "yes" | "no" | startElement  
1129                     {("." identifier | "[" structElement "]" )[function] };</code>
1130    * @return CFXD Element
1131    * @throws PageException 
1132    */
1133    private Ref dynamic() throws PageException {
1134        
1135        // get First Element of the Variable
1136        String name = identifier(false);
1137        if(name == null) {
1138            if (!cfml.forwardIfCurrent('('))return null;
1139            cfml.removeSpace();
1140            Ref ref = assignOp();
1141
1142            if (!cfml.forwardIfCurrent(')'))
1143                throw new InterpreterException("Invalid Syntax Closing [)] not found");
1144            cfml.removeSpace();
1145            return limited?ref:subDynamic(ref);
1146        }
1147
1148        cfml.removeSpace();
1149        
1150        // Boolean constant 
1151        if(name.equalsIgnoreCase("TRUE"))   {
1152            cfml.removeSpace();
1153            return LBoolean.TRUE;
1154        }
1155        else if(name.equalsIgnoreCase("FALSE")) {
1156            cfml.removeSpace();
1157            return LBoolean.FALSE;
1158        }   
1159        else if(!isJson && name.equalsIgnoreCase("YES"))   {
1160            cfml.removeSpace();
1161            return LBoolean.TRUE;
1162        }
1163        else if(!isJson && name.equalsIgnoreCase("NO")){
1164                cfml.removeSpace();
1165                return LBoolean.FALSE;
1166        }
1167        else if(allowNullConstant && name.equalsIgnoreCase("NULL")){
1168                cfml.removeSpace();
1169                return new  LString(null);
1170        }
1171        else if(!limited && name.equalsIgnoreCase("NEW")){
1172                Ref res = newOp();
1173                if(res!=null) return res;
1174        }  
1175        
1176        // Extract Scope from the Variable
1177        return limited?startElement(name):subDynamic(startElement(name));
1178
1179    }
1180    
1181   
1182    
1183    private Ref subDynamic(Ref ref) throws PageException {
1184        String name=null;
1185        
1186        // Loop over nested Variables
1187        while (cfml.isValidIndex()) {
1188            // .
1189            if (cfml.forwardIfCurrent('.')) {
1190                // Extract next Var String
1191                cfml.removeSpace();
1192                name = identifier(true);
1193                if(name==null) throw new InterpreterException("Invalid identifier");
1194                cfml.removeSpace();
1195                ref=new Variable(ref,name,limited);
1196            }
1197            // []
1198            else if (cfml.forwardIfCurrent('[')) {
1199                cfml.removeSpace();
1200                ref=new Variable(ref,assignOp(),limited);
1201                cfml.removeSpace();
1202                if (!cfml.forwardIfCurrent(']'))
1203                    throw new InterpreterException("Invalid Syntax Closing []] not found");
1204            }
1205            // finish
1206            else {
1207                break;
1208            }
1209
1210            cfml.removeSpace();
1211            
1212            if (cfml.isCurrent('(')) {
1213                if(!(ref instanceof Set)) throw new InterpreterException("invalid syntax "+ref.getTypeName()+" can't called as function");
1214                Set set=(Set) ref;
1215                ref=new UDFCall(set.getParent(pc),set.getKey(pc),functionArg(name,false, null,')'));
1216            }
1217        }
1218        if(ref instanceof lucee.runtime.interpreter.ref.var.Scope) { 
1219            lucee.runtime.interpreter.ref.var.Scope s=(lucee.runtime.interpreter.ref.var.Scope)ref;
1220            if(s.getScope()==Scope.SCOPE_ARGUMENTS || s.getScope()==Scope.SCOPE_LOCAL || s.getScope()==ScopeSupport.SCOPE_VAR) {
1221                ref=new Bind(s);
1222            }
1223        }
1224        return ref;
1225    }
1226
1227    /**
1228    * Extrahiert den Start Element einer Variale, 
1229    * dies ist entweder eine Funktion, eine Scope Definition oder eine undefinierte Variable. 
1230    * <br />
1231    * EBNF:<br />
1232    * <code>identifier "(" functionArg ")" | scope | identifier;</code>
1233    * @param name Einstiegsname
1234    * @return CFXD Element
1235    * @throws PageException 
1236    */
1237    private Ref startElement(String name) throws PageException {
1238        
1239        // check function
1240        if (!limited && cfml.isCurrent('(')) {
1241            FunctionLibFunction function = fld.getFunction(name);
1242            Ref[] arguments = functionArg(name,true, function,')');
1243                if(function!=null) return new BIFCall(function,arguments);
1244
1245            Ref ref = new lucee.runtime.interpreter.ref.var.Scope(Scope.SCOPE_UNDEFINED);
1246            return new UDFCall(ref,name,arguments);
1247        }
1248        //check scope
1249        return scope(name);
1250    }
1251    
1252        private Ref newOp() throws PageException {
1253                
1254                int start=cfml.getPos();
1255            String name=null;
1256            cfml.removeSpace();
1257            
1258            // first identifier
1259            name = identifier(true);
1260                Ref refName=null;
1261                if(name!=null) {
1262                        StringBuilder fullName=new StringBuilder();
1263                        fullName.append(name);
1264                        // Loop over addional identifier
1265                        while (cfml.isValidIndex()) {
1266                                if (cfml.forwardIfCurrent('.')) {
1267                        cfml.removeSpace();
1268                        name = identifier(true);
1269                        if(name==null) throw new InterpreterException("invalid Component declaration");
1270                        cfml.removeSpace();
1271                                        fullName.append('.');
1272                                        fullName.append(name);
1273                    }
1274                                else break;
1275                        }
1276                        refName=new LString(fullName.toString());
1277                }
1278                else {
1279                        if(cfml.isCurrentQuoter())refName=string();
1280                        if(refName==null){
1281                                cfml.setPos(start);
1282                                return null;
1283                        }
1284                }
1285                cfml.removeSpace();
1286        
1287        if (cfml.isCurrent('(')) {
1288                FunctionLibFunction function = fld.getFunction("_createComponent");
1289            Ref[] arguments = functionArg("_createComponent",true, function,')');
1290            Ref[] args=new Ref[arguments.length+1];
1291            for(int i=0;i<arguments.length;i++){
1292                args[i]=arguments[i];
1293            }
1294            args[args.length-1]=refName;
1295            BIFCall bif = new BIFCall(function,args);
1296                cfml.removeSpace();
1297                return bif;
1298                
1299                        
1300                        
1301                } 
1302        throw new InterpreterException("invalid Component declaration ");
1303                
1304        }
1305    
1306    
1307    
1308    
1309    /**
1310    * Liest einen CFML Scope aus, 
1311    * falls der folgende identifier keinem Scope entspricht, 
1312    * gibt die Variable null zurueck.
1313    * <br />
1314    * EBNF:<br />
1315    * <code>"variable" | "cgi" | "url" | "form" | "session" | "application" | "arguments" | "cookie" | " client";</code>
1316     * @param idStr String identifier, 
1317     * wird aus Optimierungszwechen nicht innerhalb dieser Funktion ausgelsen.
1318     * @return CFXD Variable Element oder null
1319    */
1320    private Ref scope(String idStr) {
1321        if (!limited && idStr.equals("var")) {
1322            String name=identifier(false);
1323            if(name!=null){
1324                cfml.removeSpace();
1325                return new Variable(new lucee.runtime.interpreter.ref.var.Scope(ScopeSupport.SCOPE_VAR),name,limited);
1326            }
1327        }
1328        int scope = limited?Scope.SCOPE_UNDEFINED:VariableInterpreter.scopeString2Int(idStr);
1329        if(scope==Scope.SCOPE_UNDEFINED) {
1330            return new Variable(new lucee.runtime.interpreter.ref.var.Scope(Scope.SCOPE_UNDEFINED),idStr,limited);
1331        }
1332        return new lucee.runtime.interpreter.ref.var.Scope(scope);
1333        
1334    }
1335    
1336    /**
1337    * Liest einen Identifier aus und gibt diesen als String zurueck.
1338    * <br />
1339    * EBNF:<br />
1340    * <code>(letter | "_") {letter | "_"|digit};</code>
1341     * @param firstCanBeNumber 
1342    * @return Identifier.
1343    */
1344    private String identifier(boolean firstCanBeNumber) {
1345        if(!cfml.isCurrentLetter() && !cfml.isCurrentSpecial()) {
1346            if(!firstCanBeNumber)return null;
1347            else if(!cfml.isCurrentDigit())return null;
1348        }
1349        boolean doUpper;
1350        PageSource ps = pc.getCurrentPageSource();
1351        if(ps!=null) doUpper= !isJson && ((MappingImpl)ps.getMapping()).getDotNotationUpperCase();
1352        else doUpper = !isJson && ((ConfigWebImpl)pc.getConfig()).getDotNotationUpperCase();
1353        StringBuilder sb=new StringBuilder();
1354        sb.append(doUpper?cfml.getCurrentUpper():cfml.getCurrent());
1355        do {
1356            cfml.next();
1357            if(!(cfml.isCurrentLetter()
1358                || cfml.isCurrentDigit()
1359                || cfml.isCurrentSpecial())) {
1360                    break;
1361                }
1362
1363            sb.append(doUpper?cfml.getCurrentUpper():cfml.getCurrent());
1364        }
1365        while (cfml.isValidIndex());
1366        return sb.toString();//cfml.substringLower(start,cfml.getPos()-start);
1367    }
1368
1369
1370    /**
1371    * Liest die Argumente eines Funktonsaufruf ein und prueft ob die Funktion 
1372    * innerhalb der FLD (Function Library Descriptor) definiert ist. 
1373    * Falls sie existiert wird die Funktion gegen diese geprueft und ein build-in-function CFXD Element generiert, 
1374    * ansonsten ein normales funcion-call Element.
1375    * <br />
1376    * EBNF:<br />
1377    * <code>[impOp{"," impOp}];</code>
1378    * @param name Identifier der Funktion als Zeichenkette
1379    * @param checkLibrary Soll geprueft werden ob die Funktion innerhalb der Library existiert.
1380    * @param flf FLD Function definition .
1381    * @return CFXD Element
1382    * @throws PageException 
1383    */
1384    private Ref[] functionArg(String name,boolean checkLibrary,FunctionLibFunction flf,char end) throws PageException {
1385
1386        // get Function Library
1387        checkLibrary=checkLibrary && flf!=null;     
1388        
1389
1390        // Function Attributes
1391        ArrayList arr = new ArrayList();
1392        
1393        ArrayList arrFuncLibAtt = null;
1394        int libLen = 0;
1395        if (checkLibrary) {
1396            arrFuncLibAtt = flf.getArg();
1397            libLen = arrFuncLibAtt.size();
1398        }
1399        int count = 0;
1400        Ref ref;
1401        do {
1402            cfml.next();
1403            cfml.removeSpace();
1404
1405            // finish
1406            if (cfml.isCurrent(end)) {
1407                if (isJson && !arr.isEmpty()) {
1408                    // LDEV-434 JSON does not allow trailing commas, which is what this must be
1409                    throw new InterpreterException("Invalid Syntax trailing comma found");
1410                }
1411                break;
1412            }
1413
1414            // too many Attributes
1415            boolean isDynamic=false;
1416            int max=-1;
1417            if(checkLibrary) {
1418                isDynamic=isDynamic(flf);
1419                max=flf.getArgMax();
1420            // Dynamic
1421                if(isDynamic) {
1422                    if(max!=-1 && max <= count)
1423                        throw new InterpreterException("too many Attributes in function [" + name + "]");
1424                }
1425            // Fix
1426                else {
1427                    if(libLen <= count)
1428                        throw new InterpreterException("too many Attributes in function [" + name + "]");
1429                }
1430            }
1431
1432            
1433            if (checkLibrary && !isDynamic) {
1434                // current attribues from library
1435                FunctionLibFunctionArg funcLibAtt = (FunctionLibFunctionArg) arrFuncLibAtt.get(count);
1436                short type=CFTypes.toShort(funcLibAtt.getTypeAsString(),false,CFTypes.TYPE_UNKNOW);
1437                if(type==CFTypes.TYPE_VARIABLE_STRING) {
1438                    arr.add(functionArgDeclarationVarString());
1439                }
1440                else {
1441                        ref = functionArgDeclaration();
1442                        arr.add(new Casting(funcLibAtt.getTypeAsString(),type,ref));
1443                }
1444            } 
1445            else {
1446                arr.add(functionArgDeclaration());
1447            }
1448
1449            cfml.removeSpace();
1450            count++;
1451        } 
1452        while (cfml.isCurrent(','));
1453
1454        // end with ) ??        
1455        if (!cfml.forwardIfCurrent(end)) {
1456            if(name.startsWith("_json")) throw new InterpreterException("Invalid Syntax Closing ["+end+"] not found");
1457            throw new InterpreterException("Invalid Syntax Closing ["+end+"] for function ["+ name + "] not found");
1458        }
1459
1460        // check min attributes
1461        if (checkLibrary && flf.getArgMin() > count)
1462            throw new InterpreterException("to less Attributes in function [" + name + "]");
1463
1464        cfml.removeSpace();
1465        return (Ref[]) arr.toArray(new Ref[arr.size()]);
1466    }
1467 
1468    
1469    private boolean isDynamic(FunctionLibFunction flf) {
1470        return flf.getArgType()==FunctionLibFunction.ARG_DYNAMIC;
1471    }
1472    
1473    /**
1474     * Sharps (#) die innerhalb von Expressions auftauchen haben in CFML keine weitere Beteutung 
1475     * und werden durch diese Methode einfach entfernt.
1476     * <br />
1477     * Beispiel:<br />
1478     * <code>arrayLen(#arr#)</code> und <code>arrayLen(arr)</code> sind identisch.
1479     * EBNF:<br />
1480     * <code>"#" checker "#";</code>
1481     * @return CFXD Element
1482     * @throws PageException 
1483    */
1484    private Ref sharp() throws PageException {
1485        if(!cfml.forwardIfCurrent('#'))
1486            return null;
1487        Ref ref;
1488        cfml.removeSpace();
1489        ref = assignOp();
1490        cfml.removeSpace();
1491        if (!cfml.forwardIfCurrent('#'))
1492            throw new InterpreterException("Syntax Error, Invalid Construct");
1493        cfml.removeSpace();
1494        return ref;
1495    }
1496}