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}