package railo.runtime.interpreter; import java.util.ArrayList; import java.util.Map; import org.apache.commons.collections.map.ReferenceMap; import railo.commons.lang.CFTypes; import railo.commons.lang.ParserString; import railo.runtime.PageContext; import railo.runtime.config.ConfigImpl; import railo.runtime.config.ConfigWebImpl; import railo.runtime.engine.ThreadLocalPageContext; import railo.runtime.exp.PageException; import railo.runtime.interpreter.ref.Ref; import railo.runtime.interpreter.ref.Set; import railo.runtime.interpreter.ref.cast.Casting; import railo.runtime.interpreter.ref.func.BIFCall; import railo.runtime.interpreter.ref.func.UDFCall; import railo.runtime.interpreter.ref.literal.LBoolean; import railo.runtime.interpreter.ref.literal.LFunctionValue; import railo.runtime.interpreter.ref.literal.LNumber; import railo.runtime.interpreter.ref.literal.LString; import railo.runtime.interpreter.ref.literal.LStringBuffer; import railo.runtime.interpreter.ref.literal.Literal; import railo.runtime.interpreter.ref.op.And; import railo.runtime.interpreter.ref.op.BigDiv; import railo.runtime.interpreter.ref.op.BigIntDiv; import railo.runtime.interpreter.ref.op.BigMinus; import railo.runtime.interpreter.ref.op.BigMod; import railo.runtime.interpreter.ref.op.BigMulti; import railo.runtime.interpreter.ref.op.BigPlus; import railo.runtime.interpreter.ref.op.CT; import railo.runtime.interpreter.ref.op.Concat; import railo.runtime.interpreter.ref.op.Cont; import railo.runtime.interpreter.ref.op.Div; import railo.runtime.interpreter.ref.op.EEQ; import railo.runtime.interpreter.ref.op.EQ; import railo.runtime.interpreter.ref.op.EQV; import railo.runtime.interpreter.ref.op.Elvis; import railo.runtime.interpreter.ref.op.Exp; import railo.runtime.interpreter.ref.op.GT; import railo.runtime.interpreter.ref.op.GTE; import railo.runtime.interpreter.ref.op.Imp; import railo.runtime.interpreter.ref.op.IntDiv; import railo.runtime.interpreter.ref.op.LT; import railo.runtime.interpreter.ref.op.LTE; import railo.runtime.interpreter.ref.op.Minus; import railo.runtime.interpreter.ref.op.Mod; import railo.runtime.interpreter.ref.op.Multi; import railo.runtime.interpreter.ref.op.NCT; import railo.runtime.interpreter.ref.op.NEEQ; import railo.runtime.interpreter.ref.op.NEQ; import railo.runtime.interpreter.ref.op.Negate; import railo.runtime.interpreter.ref.op.Not; import railo.runtime.interpreter.ref.op.Or; import railo.runtime.interpreter.ref.op.Plus; import railo.runtime.interpreter.ref.op.Xor; import railo.runtime.interpreter.ref.var.Assign; import railo.runtime.interpreter.ref.var.Bind; import railo.runtime.interpreter.ref.var.DynAssign; import railo.runtime.interpreter.ref.var.Variable; import railo.runtime.type.scope.Scope; import railo.runtime.type.scope.ScopeSupport; import railo.transformer.library.function.FunctionLib; import railo.transformer.library.function.FunctionLibFunction; import railo.transformer.library.function.FunctionLibFunctionArg; /** * * 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 class CFMLExpressionInterpreter { private static final LNumber PLUS_ONE = new LNumber(new Double(1)); private static final LNumber MINUS_ONE = new LNumber(new Double(-1)); protected static final short STATIC=0; private static final short DYNAMIC=1; private static FunctionLibFunction JSON_ARRAY = null; private static FunctionLibFunction JSON_STRUCT = null; //private static final int CASE_TYPE_UPPER = 0; //private static final int CASE_TYPE_LOWER = 1; //private static final int CASE_TYPE_ORIGINAL = 2; protected short mode=0; protected ParserString cfml; //protected Document doc; //protected FunctionLib[] fld; protected PageContext pc; private FunctionLib fld; protected boolean allowNullConstant=false; private boolean preciseMath; private boolean isJson; private final static Map<String,Ref> data=new ReferenceMap(ReferenceMap.SOFT,ReferenceMap.SOFT); public Object interpret(PageContext pc,String str) throws PageException { return interpret(pc,str,false); } public Object interpret(PageContext pc,String str, boolean preciseMath) throws PageException { //Ref ref = data.get(str+":"+preciseMath); //if(ref!=null)return ref.getValue(); this.cfml=new ParserString(str); this.preciseMath = preciseMath; this.pc=ThreadLocalPageContext.get(pc); if(pc!=null)fld=((ConfigImpl)pc.getConfig()).getCombinedFLDs(); if(JSON_ARRAY==null)JSON_ARRAY=fld.getFunction("_jsonArray"); if(JSON_STRUCT==null)JSON_STRUCT=fld.getFunction("_jsonStruct"); isJson=this instanceof JSONExpressionInterpreter; cfml.removeSpace(); Ref ref = assignOp(); cfml.removeSpace(); if(cfml.isAfterLast()) { //data.put(str+":"+preciseMath,ref); return ref.getValue(pc); } throw new InterpreterException("Syntax Error, invalid Expression ["+cfml.toString()+"]"); } /*private FunctionLibFunction getFLF(String name) { FunctionLibFunction flf=null; for (int i = 0; i < flds.length; i++) { flf = flds[i].getFunction(name); if (flf != null) break; } return flf; }*/ protected Object interpretPart(PageContext pc,ParserString cfml) throws PageException { this.cfml = cfml; this.pc=ThreadLocalPageContext.get(pc); if(pc!=null)fld=((ConfigImpl)pc.getConfig()).getCombinedFLDs(); cfml.removeSpace(); return assignOp().getValue(pc); } /** * Liest einen gelableten Funktionsparamter ein * <br /> * EBNF:<br /> * <code>assignOp [":" spaces assignOp];</code> * @return CFXD Element * @throws PageException */ private Ref functionArgDeclarationVarString() throws PageException { cfml.removeSpace(); StringBuffer str=new StringBuffer(); String id=null; while((id=identifier(false))!=null) { if(str.length()>0)str.append('.'); str.append(id); cfml.removeSpace(); if(!cfml.forwardIfCurrent('.')) break; cfml.removeSpace(); } cfml.removeSpace(); if(str.length()>0 && cfml.charAt(cfml.getPos()-1)!='.') return new LString(str.toString()); throw new InterpreterException("invalid variable name definition"); } /** * Liest einen gelableten Funktionsparamter ein * <br /> * EBNF:<br /> * <code>assignOp [":" spaces assignOp];</code> * @return CFXD Element * @throws PageException */ private Ref functionArgDeclaration() throws PageException { Ref ref = impOp(); if (cfml.forwardIfCurrent(':') || cfml.forwardIfCurrent('=')) { cfml.removeSpace(); ref=new LFunctionValue(ref,assignOp()); } return ref; } /** * Transfomiert Zuweisungs Operation. * <br /> * EBNF:<br /> * <code>eqvOp ["=" spaces assignOp];</code> * @return CFXD Element * @throws PageException */ protected Ref assignOp() throws PageException { Ref ref = contOp(); if (cfml.forwardIfCurrent('=')) { cfml.removeSpace(); if(mode==STATIC || ref instanceof Literal) { ref=new DynAssign(ref,assignOp()); } else { ref=new Assign(ref,assignOp()); } } return ref; } private Ref contOp() throws PageException { Ref ref = impOp(); while(cfml.forwardIfCurrent('?')) { cfml.removeSpace(); if(cfml.forwardIfCurrent(':')){ cfml.removeSpace(); Ref right = assignOp(); //if(!(ref instanceof Variable)) // throw new InterpreterException("left operant of the Elvis operator has to be a variable declaration "+ref.getClass().getName()); ref=new Elvis(ref,right); } else { Ref left = assignOp(); if(!cfml.forwardIfCurrent(':')) throw new InterpreterException("Syntax Error, invalid conditional operator ["+cfml.toString()+"]"); cfml.removeSpace(); Ref right = assignOp(); ref=new Cont(ref,left,right); } } return ref; } /** * Transfomiert eine Implication (imp) Operation. * <br /> * EBNF:<br /> * <code>eqvOp {"imp" spaces eqvOp};</code> * @return CFXD Element * @throws PageException */ private Ref impOp() throws PageException { Ref ref = eqvOp(); while(cfml.forwardIfCurrentAndNoWordAfter("imp")) { cfml.removeSpace(); ref=new Imp(ref,eqvOp()); } return ref; } /** * Transfomiert eine Equivalence (eqv) Operation. * <br /> * EBNF:<br /> * <code>xorOp {"eqv" spaces xorOp};</code> * @return CFXD Element * @throws PageException */ private Ref eqvOp() throws PageException { Ref ref = xorOp(); while(cfml.forwardIfCurrent("eqv")) { cfml.removeSpace(); ref=new EQV(ref,xorOp()); } return ref; } /** * Transfomiert eine Xor (xor) Operation. * <br /> * EBNF:<br /> * <code>orOp {"xor" spaces orOp};</code> * @return CFXD Element * @throws PageException */ private Ref xorOp() throws PageException { Ref ref = orOp(); while(cfml.forwardIfCurrent("xor")) { cfml.removeSpace(); ref=new Xor(ref,orOp()); } return ref; } /** * Transfomiert eine Or (or) Operation. Im Gegensatz zu CFMX , * werden "||" Zeichen auch als Or Operatoren anerkannt. * <br /> * EBNF:<br /> * <code>andOp {("or" | "||") spaces andOp}; (* "||" Existiert in CFMX nicht *)</code> * @return CFXD Element * @throws PageException */ private Ref orOp() throws PageException { Ref ref = andOp(); while(cfml.isValidIndex() && (cfml.forwardIfCurrent("||") || cfml.forwardIfCurrent("or"))) { cfml.removeSpace(); ref=new Or(ref,andOp()); } return ref; } /** * Transfomiert eine And (and) Operation. Im Gegensatz zu CFMX , * werden "&&" Zeichen auch als And Operatoren anerkannt. * <br /> * EBNF:<br /> * <code>notOp {("and" | "&&") spaces notOp}; (* "&&" Existiert in CFMX nicht *)</code> * @return CFXD Element * @throws PageException */ private Ref andOp() throws PageException { Ref ref = notOp(); while(cfml.isValidIndex() && (cfml.forwardIfCurrent("&&") || cfml.forwardIfCurrent("and"))) { cfml.removeSpace(); ref=new And(ref,notOp()); } return ref; } /** * Transfomiert eine Not (not) Operation. Im Gegensatz zu CFMX , * wird das "!" Zeichen auch als Not Operator anerkannt. * <br /> * EBNF:<br /> * <code>[("not"|"!") spaces] decsionOp; (* "!" Existiert in CFMX nicht *)</code> * @return CFXD Element * @throws PageException */ private Ref notOp() throws PageException { if(cfml.isValidIndex()) { if (cfml.isCurrent('!') && !cfml.isCurrent("!=")) { cfml.next(); cfml.removeSpace(); return new Not(decsionOp()); } else if (cfml.forwardIfCurrentAndNoWordAfter("not")) { cfml.removeSpace(); return new Not(decsionOp()); } } return decsionOp(); } /** * <font f>Transfomiert eine Vergleichs Operation. * <br /> * EBNF:<br /> * <code>concatOp {("neq"|"eq"|"gte"|"gt"|"lte"|"lt"|"ct"| "contains"|"nct"|"does not contain") spaces concatOp}; (* "ct"=conatains und "nct"=does not contain; Existiert in CFMX nicht *)</code> * @return CFXD Element * @throws PageException */ private Ref decsionOp() throws PageException { Ref ref = concatOp(); boolean hasChanged=false; // ct, contains if(cfml.isValidIndex()){ do { hasChanged=false; if(cfml.isCurrent('c')) { if (cfml.forwardIfCurrent("ct")) { cfml.removeSpace(); ref=new CT(ref,concatOp()); hasChanged=true; } else if (cfml.forwardIfCurrent("contains")){ cfml.removeSpace(); ref=new CT(ref,concatOp()); hasChanged=true; } } // does not contain else if (cfml.forwardIfCurrent("does","not","contain")){ cfml.removeSpace(); ref=new NCT(ref,concatOp()); hasChanged=true; } // equal, eq else if (cfml.isCurrent("eq") && !cfml.isCurrent("eqv")) { cfml.setPos(cfml.getPos()+2); cfml.forwardIfCurrent("ual"); cfml.removeSpace(); ref=new EQ(ref,concatOp()); hasChanged=true; } // == else if (cfml.forwardIfCurrent("==")) { if(cfml.forwardIfCurrent('=')) { cfml.removeSpace(); ref = new EEQ(ref,concatOp()); } else { cfml.removeSpace(); ref=new EQ(ref,concatOp()); } hasChanged=true; } // != else if (cfml.forwardIfCurrent("!=")) { if(cfml.forwardIfCurrent('=')) { cfml.removeSpace(); ref = new NEEQ(ref,concatOp()); } else { cfml.removeSpace(); ref=new NEQ(ref,concatOp()); } hasChanged=true; } // <=/</<> else if (cfml.forwardIfCurrent('<')) { if(cfml.forwardIfCurrent('=')) { cfml.removeSpace(); ref = new LTE(ref,concatOp()); } else if(cfml.forwardIfCurrent('>')) { cfml.removeSpace(); ref = new NEQ(ref,concatOp()); } else { cfml.removeSpace(); ref = new LT(ref,concatOp()); } hasChanged=true; } // >/>= else if (cfml.forwardIfCurrent('>')) { if(cfml.forwardIfCurrent('=')) { cfml.removeSpace(); ref = new GTE(ref,concatOp()); } else { cfml.removeSpace(); ref = new GT(ref,concatOp()); } hasChanged=true; } // gt, gte, greater than or equal to, greater than else if (cfml.isCurrent('g')) { if (cfml.forwardIfCurrent("gt")) { if(cfml.forwardIfCurrent('e')) { cfml.removeSpace(); ref=new GTE(ref,concatOp()); } else { cfml.removeSpace(); ref=new GT(ref,concatOp()); } hasChanged=true; } else if (cfml.forwardIfCurrent("greater","than")) { if(cfml.forwardIfCurrent("or" ,"equal", "to",true)) { cfml.removeSpace(); ref=new GTE(ref,concatOp()); } else { cfml.removeSpace(); ref=new GT(ref,concatOp()); } hasChanged=true; } else if (cfml.forwardIfCurrent("ge")) { cfml.removeSpace(); ref=new GTE(ref,concatOp()); hasChanged=true; } } // is, is not else if (cfml.forwardIfCurrent("is")) { if(cfml.forwardIfCurrent("not",true)) { cfml.removeSpace(); ref=new NEQ(ref,concatOp()); } else { cfml.removeSpace(); ref=new EQ(ref,concatOp()); } hasChanged=true; } // lt, lte, less than, less than or equal to else if (cfml.isCurrent('l')) { if (cfml.forwardIfCurrent("lt")) { if(cfml.forwardIfCurrent('e')) { cfml.removeSpace(); ref=new LTE(ref,concatOp()); } else { cfml.removeSpace(); ref=new LT(ref,concatOp()); } hasChanged=true; } else if (cfml.forwardIfCurrent("less","than")) { if(cfml.forwardIfCurrent("or", "equal", "to",true)) { cfml.removeSpace(); ref=new LTE(ref,concatOp()); } else { cfml.removeSpace(); ref=new LT(ref,concatOp()); } hasChanged=true; } else if (cfml.forwardIfCurrent("le")) { cfml.removeSpace(); ref=new LTE(ref,concatOp()); hasChanged=true; } } // neq, not equal, nct else if (cfml.isCurrent('n')) { // Not Equal if (cfml.forwardIfCurrent("neq")) { cfml.removeSpace(); ref=new NEQ(ref,concatOp()); hasChanged=true; } // Not Equal (Alias) else if (cfml.forwardIfCurrent("not","equal")){ cfml.removeSpace(); ref=new NEQ(ref,concatOp()); hasChanged=true; } // nct else if (cfml.forwardIfCurrent("nct")) { cfml.removeSpace(); ref=new NCT(ref,concatOp()); hasChanged=true; } } }while(hasChanged); } return ref; } /** * 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 PageException */ private Ref concatOp() throws PageException { Ref ref = plusMinusOp(); while(cfml.isCurrent('&') && !cfml.isNext('&')) { cfml.next(); ref=_concat(ref); //cfml.removeSpace(); //ref=new Concat(pc,ref,plusMinusOp()); } return ref; } /** * Transfomiert die mathematischen Operatoren Plus und Minus (1,-). * <br /> * EBNF:<br /> * <code>modOp [("-"|"+") spaces plusMinusOp];</code> * @return CFXD Element * @throws PageException */ private Ref plusMinusOp() throws PageException { Ref ref = modOp(); while(!cfml.isLast()) { // Plus Operation if (cfml.forwardIfCurrent('+')) { ref=_plus(ref); //cfml.removeSpace(); //ref=new Plus(ref,modOp()); } // Minus Operation else if (cfml.forwardIfCurrent('-')) { ref=_minus(ref); //cfml.removeSpace(); //ref=new Minus(ref,modOp()); } else break; } return ref; } private Ref _plus(Ref ref) throws PageException { // += if (cfml.isCurrent('=')) { cfml.next(); cfml.removeSpace(); Ref right = assignOp(); Ref res = preciseMath?new BigPlus(ref,right):new Plus(ref,right); ref=new Assign(ref,res); } /*/ ++ else if (cfml.isCurrent('+')) { cfml.next(); cfml.removeSpace(); Ref res = new Plus(ref,new LNumber(new Double(1))); ref=new Assign(ref,res); ref=new Minus(ref,new LNumber(new Double(1))); }*/ else { cfml.removeSpace(); ref=preciseMath?new BigPlus(ref,modOp()):new Plus(ref,modOp()); } return ref; } private Ref _minus(Ref ref) throws PageException { // -= if (cfml.isCurrent('=')) { cfml.next(); cfml.removeSpace(); Ref right = assignOp(); Ref res = preciseMath?new BigMinus(ref,right):new Minus(ref,right); ref=new Assign(ref,res); } /*/ -- else if (cfml.isCurrent('-')) { cfml.next(); cfml.removeSpace(); Ref res = new Minus(ref,new LNumber(new Double(1))); ref=new Assign(ref,res); ref=new Plus(ref,new LNumber(new Double(1))); }*/ else { cfml.removeSpace(); ref=preciseMath?new BigMinus(ref,modOp()):new Minus(ref,modOp()); } return ref; } private Ref _div(Ref ref) throws PageException { // /= if (cfml.forwardIfCurrent('=')) { cfml.removeSpace(); Ref right = assignOp(); Ref res = preciseMath?new BigDiv(ref, right):new Div(ref,right); ref=new Assign(ref,res); } else { cfml.removeSpace(); ref=preciseMath?new BigDiv(ref,expoOp()):new Div(ref,expoOp()); } return ref; } private Ref _intdiv(Ref ref) throws PageException { // \= if (cfml.forwardIfCurrent('=')) { cfml.removeSpace(); Ref right = assignOp(); Ref res = preciseMath?new BigIntDiv(ref,right):new IntDiv(ref,right); ref=new Assign(ref,res); } else { cfml.removeSpace(); ref=preciseMath?new BigIntDiv(ref,expoOp()):new IntDiv(ref,expoOp()); } return ref; } private Ref _mod(Ref ref) throws PageException { // %= if (cfml.forwardIfCurrent('=')) { cfml.removeSpace(); Ref right = assignOp(); Ref res = preciseMath?new BigMod(ref,right):new Mod(ref,right); ref=new Assign(ref,res); } else { cfml.removeSpace(); ref=preciseMath?new BigMod(ref,divMultiOp()):new Mod(ref,divMultiOp()); } return ref; } private Ref _concat(Ref ref) throws PageException { // &= if (cfml.forwardIfCurrent('=')) { cfml.removeSpace(); Ref right = assignOp(); Ref res = new Concat(ref,right); ref=new Assign(ref,res); } else { cfml.removeSpace(); ref=new Concat(ref,plusMinusOp()); } return ref; } private Ref _multi(Ref ref) throws PageException { // \= if (cfml.forwardIfCurrent('=')) { cfml.removeSpace(); Ref right = assignOp(); Ref res = preciseMath?new BigMulti(ref,right):new Multi(ref,right); ref=new Assign(ref,res); } else { cfml.removeSpace(); ref=preciseMath?new BigMulti(ref,expoOp()):new Multi(ref,expoOp()); } return ref; } /** * Transfomiert eine Modulus Operation. Im Gegensatz zu CFMX , * wird das "%" Zeichen auch als Modulus Operator anerkannt. * <br /> * EBNF:<br /> * <code>divMultiOp {("mod" | "%") spaces divMultiOp}; (* modulus operator , "%" Existiert in CFMX nicht *)</code> * @return CFXD Element * @throws PageException */ private Ref modOp() throws PageException { Ref ref = divMultiOp(); while(cfml.isValidIndex() && (cfml.forwardIfCurrent('%') || cfml.forwardIfCurrent("mod"))) { ref=_mod(ref); //cfml.removeSpace(); //ref=new Mod(ref,divMultiOp()); } return ref; } /** * Transfomiert die mathematischen Operatoren Mal und Durch (*,/). * <br /> * EBNF:<br /> * <code>expoOp {("*"|"/") spaces expoOp};</code> * @return CFXD Element * @throws PageException */ private Ref divMultiOp() throws PageException { Ref ref = expoOp(); while (!cfml.isLast()) { // Multiply Operation if(cfml.forwardIfCurrent('*')) { ref=_multi(ref); //cfml.removeSpace(); //ref=new Multi(ref,expoOp()); } // Divide Operation else if (cfml.isCurrent('/') && (!cfml.isCurrent("/>") )) { cfml.next(); ref=_div(ref); //cfml.removeSpace(); //ref=new Div(ref,expoOp()); } // Divide Operation else if (cfml.isCurrent('\\')) { cfml.next(); ref=_intdiv(ref); //cfml.removeSpace(); //ref=new IntDiv(ref,expoOp()); } else { break; } } return ref; } /** * Transfomiert den Exponent Operator (^,exp). Im Gegensatz zu CFMX , * werden die Zeichen " exp " auch als Exponent anerkannt. * <br /> * EBNF:<br /> * <code>clip {("exp"|"^") spaces clip};</code> * @return CFXD Element * @throws PageException */ private Ref expoOp() throws PageException { Ref ref = unaryOp(); while(cfml.isValidIndex() && (cfml.forwardIfCurrent('^') || cfml.forwardIfCurrent("exp"))) { cfml.removeSpace(); ref=new Exp(ref,unaryOp()); } return ref; } private Ref unaryOp() throws PageException { Ref ref = negateMinusOp(); if (cfml.forwardIfCurrent("--")) ref=_unaryOp(ref, false); else if (cfml.forwardIfCurrent("++")) ref=_unaryOp(ref, true); return ref; } private Ref _unaryOp(Ref ref,boolean isPlus) throws PageException { cfml.removeSpace(); Ref res = preciseMath?new BigPlus(ref,isPlus?PLUS_ONE:MINUS_ONE):new Plus(ref,isPlus?PLUS_ONE:MINUS_ONE); ref=new Assign(ref,res); return preciseMath?new BigPlus(ref,isPlus?MINUS_ONE:PLUS_ONE):new Plus(ref,isPlus?MINUS_ONE:PLUS_ONE); } /** * Liest die Vordlobe einer Zahl ein * @return CFXD Element * @throws PageException */ private Ref negateMinusOp() throws PageException { // And Operation if (cfml.forwardIfCurrent('-')) { if (cfml.forwardIfCurrent('-')) { cfml.removeSpace(); Ref expr = clip(); Ref res = preciseMath?new BigMinus(expr,new LNumber(new Double(1))):new Minus(expr,new LNumber(new Double(1))); return new Assign(expr,res); } cfml.removeSpace(); return new Negate(clip()); } if (cfml.forwardIfCurrent('+')) { if (cfml.forwardIfCurrent('+')) { cfml.removeSpace(); Ref expr = clip(); Ref res = preciseMath?new BigPlus(expr,new LNumber(new Double(1))):new Plus(expr,new LNumber(new Double(1))); return new Assign(expr,res); } cfml.removeSpace(); return new Casting("numeric",CFTypes.TYPE_NUMERIC,clip()); } return clip(); } /** * Verarbeitet Ausdrcke die inerhalb einer Klammer stehen. * <br /> * EBNF:<br /> * <code>("(" spaces impOp ")" spaces) | checker;</code> * @return CFXD Element * @throws PageException */ private Ref clip() throws PageException { return checker(); } /** * 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 PageException */ private Ref checker() throws PageException { Ref ref=null; // String if(cfml.isCurrentQuoter()) { // mode=STATIC; is at the end of the string function because must set after execution return string(); } // Number if(cfml.isCurrentDigit() || cfml.isCurrent('.')) { // mode=STATIC; is at the end of the string function because must set after execution return number(); } // Dynamic if((ref=dynamic())!=null) { mode=DYNAMIC; return ref; } // Sharp if((ref=sharp())!=null) { mode=DYNAMIC; return ref; } // JSON if((ref=json(JSON_ARRAY,'[',']'))!=null) { mode=DYNAMIC; return ref; } if((ref=json(JSON_STRUCT,'{','}'))!=null) { mode=DYNAMIC; return ref; } if(cfml.isAfterLast() && cfml.toString().trim().length()==0) return new LString(""); // else Error String str=cfml.toString(); int pos=cfml.getPos(); if(str.length()>100) { // Failure is in the beginning if(pos<=10) { str=str.substring(0,20)+" ..."; } // Failure is in the end else if((str.length()-pos)<=10) { str="... "+str.substring(str.length()-20,str.length()); } else { str="... "+str.substring(pos-10,pos+10)+" ..."; } } throw new InterpreterException("Syntax Error, Invalid Construct","at position "+(pos+1)+" in ["+str+"]"); } protected Ref json(FunctionLibFunction flf, char start, char end) throws PageException { //print.out("start:"+start+":"+cfml.getCurrent()); if(!cfml.isCurrent(start))return null; Ref[] args = functionArg(flf.getName(), false, flf,end); //if (!cfml.forwardIfCurrent(end)) // throw new InterpreterException("Invalid Syntax Closing ["+end+"] not found"); return new BIFCall(flf,args); } /** * Transfomiert einen lierale Zeichenkette. * <br /> * EBNF:<br /> * <code>("'" {"##"|"''"|"#" impOp "#"| ?-"#"-"'" } "'") | (""" {"##"|""""|"#" impOp "#"| ?-"#"-""" } """);</code> * @return CFXD Element * @throws PageException */ protected Ref string() throws PageException { // Init Parameter char quoter = cfml.getCurrentLower(); //String str=""; LStringBuffer str=new LStringBuffer(); Ref value=null; while(cfml.hasNext()) { cfml.next(); // check sharp if(cfml.isCurrent('#')) { if(cfml.isNext('#')){ cfml.next(); str.append('#'); } else { cfml.next(); cfml.removeSpace(); if(!str.isEmpty() || value!=null) str.append(assignOp()); else value=assignOp(); cfml.removeSpace(); if (!cfml.isCurrent('#')) throw new InterpreterException("Invalid Syntax Closing [#] not found"); } } else if(cfml.isCurrent(quoter)) { if(cfml.isNext(quoter)){ cfml.next(); str.append(quoter); } else { break; } } // all other character else { str.append(cfml.getCurrent()); } } if(!cfml.forwardIfCurrent(quoter)) throw new InterpreterException("Invalid String Literal Syntax Closing ["+quoter+"] not found"); cfml.removeSpace(); mode=STATIC; if(value!=null) { if(str.isEmpty()) return value; return new Concat(value,str); } return str; } /** * 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 PageException */ private Ref number() throws PageException { // check first character is a number literal representation //if(!cfml.isCurrentDigit()) return null; StringBuilder rtn=new StringBuilder(6); // get digit on the left site of the dot if(cfml.isCurrent('.')) rtn.append('0'); else digit(rtn); // read dot if exist if(cfml.forwardIfCurrent('.')) { rtn.append('.'); int before=cfml.getPos(); digit(rtn); if(before<cfml.getPos() && cfml.forwardIfCurrent('e')) { Boolean expOp=null; if(cfml.forwardIfCurrent('+')) expOp=Boolean.TRUE; else if(cfml.forwardIfCurrent('-')) expOp=Boolean.FALSE; if(cfml.isCurrentDigit()) { if(expOp==Boolean.FALSE) rtn.append("e-"); else if(expOp==Boolean.TRUE) rtn.append("e+"); else rtn.append('e'); digit(rtn); } else { if(expOp!=null) cfml.previous(); cfml.previous(); } } // read right side of the dot if(before==cfml.getPos()) throw new InterpreterException("Number can't end with [.]"); //rtn.append(rightSite); } cfml.removeSpace(); mode=STATIC; return new LNumber(rtn.toString()); } /** * Liest die reinen Zahlen innerhalb des CFMLString aus und gibt diese als Zeichenkette zurck. * <br /> * EBNF:<br /> * <code>"0"|..|"9";</code> * @param rtn */ private void digit(StringBuilder rtn) { while (cfml.isValidIndex()) { if(!cfml.isCurrentDigit())break; rtn.append(cfml.getCurrentLower()); cfml.next(); } } /** * 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 PageException */ private Ref dynamic() throws PageException { // 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 String name = identifier(false); if(name == null) { if (!cfml.forwardIfCurrent('('))return null; cfml.removeSpace(); Ref ref = assignOp(); if (!cfml.forwardIfCurrent(')')) throw new InterpreterException("Invalid Syntax Closing [)] not found"); cfml.removeSpace(); return subDynamic(ref); } //Element el; cfml.removeSpace(); //char first=name.charAt(0); // Boolean constant if(name.equalsIgnoreCase("TRUE")) { cfml.removeSpace(); return LBoolean.TRUE; } else if(name.equalsIgnoreCase("FALSE")) { cfml.removeSpace(); return LBoolean.FALSE; } else if(name.equalsIgnoreCase("YES")) { cfml.removeSpace(); return LBoolean.TRUE; } else if(name.equalsIgnoreCase("NO")){ cfml.removeSpace(); return LBoolean.FALSE; } else if(allowNullConstant && name.equalsIgnoreCase("NULL")){ cfml.removeSpace(); return new LString(null); } else if(name.equalsIgnoreCase("NEW")){ Ref res = newOp(); if(res!=null) return res; } // Extract Scope from the Variable //Object value = startElement(name); return subDynamic(startElement(name)); } private Ref subDynamic(Ref ref) throws PageException { String name=null; // Loop over nested Variables while (cfml.isValidIndex()) { // . if (cfml.forwardIfCurrent('.')) { // Extract next Var String cfml.removeSpace(); name = identifier(true); if(name==null) throw new InterpreterException("Invalid identifier"); cfml.removeSpace(); ref=new Variable(ref,name); } // [] else if (cfml.forwardIfCurrent('[')) { cfml.removeSpace(); ref=new Variable(ref,assignOp()); cfml.removeSpace(); if (!cfml.forwardIfCurrent(']')) throw new InterpreterException("Invalid Syntax Closing []] not found"); } // finish else { break; } cfml.removeSpace(); if (cfml.isCurrent('(')) { if(!(ref instanceof Set)) throw new InterpreterException("invalid syntax "+ref.getTypeName()+" can't called as function"); Set set=(Set) ref; ref=new UDFCall(set.getParent(pc),set.getKey(pc),functionArg(name,false, null,')')); } } if(ref instanceof railo.runtime.interpreter.ref.var.Scope) { railo.runtime.interpreter.ref.var.Scope s=(railo.runtime.interpreter.ref.var.Scope)ref; if(s.getScope()==Scope.SCOPE_ARGUMENTS || s.getScope()==Scope.SCOPE_LOCAL || s.getScope()==ScopeSupport.SCOPE_VAR) { ref=new Bind(s); } } return ref; } /** * 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 PageException */ private Ref startElement(String name) throws PageException { // check function if (cfml.isCurrent('(')) { FunctionLibFunction function = fld.getFunction(name); Ref[] arguments = functionArg(name,true, function,')'); //print.out(name+":"+(function!=null)); if(function!=null) return new BIFCall(function,arguments); Ref ref = new railo.runtime.interpreter.ref.var.Scope(Scope.SCOPE_UNDEFINED); return new UDFCall(ref,name,arguments); } //check scope return scope(name); } private Ref newOp() throws PageException { int start=cfml.getPos(); String name=null; cfml.removeSpace(); // first identifier name = identifier(true); Ref refName=null; if(name!=null) { StringBuilder fullName=new StringBuilder(); fullName.append(name); // Loop over addional identifier while (cfml.isValidIndex()) { if (cfml.forwardIfCurrent('.')) { cfml.removeSpace(); name = identifier(true); if(name==null) throw new InterpreterException("invalid Component declaration"); cfml.removeSpace(); fullName.append('.'); fullName.append(name); } else break; } refName=new LString(fullName.toString()); } else { if(cfml.isCurrentQuoter())refName=string(); if(refName==null){ cfml.setPos(start); return null; } } cfml.removeSpace(); if (cfml.isCurrent('(')) { FunctionLibFunction function = fld.getFunction("_createComponent"); Ref[] arguments = functionArg("_createComponent",true, function,')'); Ref[] args=new Ref[arguments.length+1]; for(int i=0;i<arguments.length;i++){ args[i]=arguments[i]; } args[args.length-1]=refName; BIFCall bif = new BIFCall(function,args); cfml.removeSpace(); return bif; } throw new InterpreterException("invalid Component declaration "); } /** * 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 idStr String identifier, * wird aus Optimierungszwechen nicht innerhalb dieser Funktion ausgelsen. * @return CFXD Variable Element oder null */ private Ref scope(String idStr) { if (idStr.equals("var")) { String name=identifier(false); if(name!=null){ cfml.removeSpace(); return new Variable(new railo.runtime.interpreter.ref.var.Scope(ScopeSupport.SCOPE_VAR),name); } } int scope = VariableInterpreter.scopeString2Int(idStr); if(scope==Scope.SCOPE_UNDEFINED) { return new Variable(new railo.runtime.interpreter.ref.var.Scope(Scope.SCOPE_UNDEFINED),idStr); } return new railo.runtime.interpreter.ref.var.Scope(scope); } /** * Liest einen Identifier aus und gibt diesen als String zurck. * <br /> * EBNF:<br /> * <code>(letter | "_") {letter | "_"|digit};</code> * @param firstCanBeNumber * @return Identifier. */ private String identifier(boolean firstCanBeNumber) { //int start = cfml.getPos(); if(!cfml.isCurrentLetter() && !cfml.isCurrentSpecial()) { if(!firstCanBeNumber)return null; else if(!cfml.isCurrentDigit())return null; } boolean doUpper = !isJson && ((ConfigWebImpl)pc.getConfig()).getDotNotationUpperCase(); StringBuilder sb=new StringBuilder(); sb.append(doUpper?cfml.getCurrentUpper():cfml.getCurrent()); do { cfml.next(); if(!(cfml.isCurrentLetter() || cfml.isCurrentDigit() || cfml.isCurrentSpecial())) { break; } sb.append(doUpper?cfml.getCurrentUpper():cfml.getCurrent()); } while (cfml.isValidIndex()); return sb.toString();//cfml.substringLower(start,cfml.getPos()-start); } /* * * Transfomiert ein Collection Element das in eckigen Klammern aufgerufen wird. * <br /> * EBNF:<br /> * <code>"[" impOp "]"</code> * @return CFXD Element * @throws PageException * / private Ref structElement() throws PageException { cfml.removeSpace(); Ref ref = new Casting(pc,"string",CFTypes.TYPE_STRING,assignOp()); cfml.removeSpace(); return ref; }*/ /** * 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. * @param flf FLD Function definition . * @return CFXD Element * @throws PageException */ private Ref[] functionArg(String name,boolean checkLibrary,FunctionLibFunction flf,char end) throws PageException { // get Function Library checkLibrary=checkLibrary && flf!=null; // Function Attributes ArrayList arr = new ArrayList(); ArrayList arrFuncLibAtt = null; int libLen = 0; if (checkLibrary) { arrFuncLibAtt = flf.getArg(); libLen = arrFuncLibAtt.size(); } int count = 0; Ref ref; do { cfml.next(); cfml.removeSpace(); // finish if (cfml.isCurrent(end)) break; // too many Attributes boolean isDynamic=false; int max=-1; if(checkLibrary) { isDynamic=isDynamic(flf); max=flf.getArgMax(); // Dynamic if(isDynamic) { if(max!=-1 && max <= count) throw new InterpreterException("too many Attributes in function [" + name + "]"); } // Fix else { if(libLen <= count) throw new InterpreterException("too many Attributes in function [" + name + "]"); } } if (checkLibrary && !isDynamic) { // current attribues from library FunctionLibFunctionArg funcLibAtt = (FunctionLibFunctionArg) arrFuncLibAtt.get(count); short type=CFTypes.toShort(funcLibAtt.getTypeAsString(),false,CFTypes.TYPE_UNKNOW); if(type==CFTypes.TYPE_VARIABLE_STRING) { arr.add(functionArgDeclarationVarString()); } else { ref = functionArgDeclaration(); arr.add(new Casting(funcLibAtt.getTypeAsString(),type,ref)); } } else { arr.add(functionArgDeclaration()); } // obj=andOrXor(); cfml.removeSpace(); count++; } while (cfml.isCurrent(',')); // end with ) ?? if (!cfml.forwardIfCurrent(end)) { if(name.startsWith("_json")) throw new InterpreterException("Invalid Syntax Closing ["+end+"] not found"); throw new InterpreterException("Invalid Syntax Closing ["+end+"] for function ["+ name + "] not found"); } // check min attributes if (checkLibrary && flf.getArgMin() > count) throw new InterpreterException("to less Attributes in function [" + name + "]"); cfml.removeSpace(); return (Ref[]) arr.toArray(new Ref[arr.size()]); } private boolean isDynamic(FunctionLibFunction flf) { return flf.getArgType()==FunctionLibFunction.ARG_DYNAMIC; } /** * 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 PageException */ private Ref sharp() throws PageException { if(!cfml.forwardIfCurrent('#')) return null; Ref ref; cfml.removeSpace(); ref = assignOp(); cfml.removeSpace(); if (!cfml.forwardIfCurrent('#')) throw new InterpreterException("Syntax Error, Invalid Construct"); cfml.removeSpace(); return ref; } /* * * Wandelt eine variable und ein key in eine reference um * @param value * @param name * @return cast a vlue in a reference * @throws PageException * / private Reference toReference(Object value, String name) { return NativeReference.getInstance(value, name); }*/ }