hqlparse.cpp 73 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "hqlgram.hpp"
  14. #include "hqlfold.hpp"
  15. #include "jiter.ipp"
  16. #include "jptree.hpp"
  17. #include "hqlerrors.hpp"
  18. #include "hqlthql.hpp"
  19. #include "hqlexpr.hpp"
  20. #include "jdebug.hpp"
  21. #include "hqlexpr.ipp"
  22. #include "hqlgram.h"
  23. #define YY_NO_UNISTD_H
  24. #include "hqllex.hpp"
  25. #include "eclrtl.hpp"
  26. //#define TIMING_DEBUG
  27. #define MAX_LOOP_TIMES 250000
  28. // =========================== local helper functions ===================================
  29. static bool isInModule(HqlLookupContext & ctx, const char* module_name, const char* attr_name);
  30. static StringBuffer& mangle(IErrorReceiver* errReceiver,const char* src, StringBuffer& mangled,bool demangle);
  31. static const char * skipws(const char * str)
  32. {
  33. while (isspace(*str))
  34. str++;
  35. return str;
  36. }
  37. // =========================== CDummyScopeIterator ======================================
  38. class CDummyScopeIterator : public IIterator, public CInterface
  39. {
  40. Linked<IXmlScope> parent;
  41. public:
  42. IMPLEMENT_IINTERFACE;
  43. CDummyScopeIterator(IXmlScope *_parent) : parent(_parent)
  44. {
  45. }
  46. ~CDummyScopeIterator ()
  47. {
  48. }
  49. virtual bool first()
  50. {
  51. return true;
  52. };
  53. virtual bool next()
  54. {
  55. return true;
  56. }
  57. virtual bool isValid()
  58. {
  59. return true;
  60. }
  61. virtual IInterface & query()
  62. {
  63. return *parent;
  64. }
  65. virtual IInterface & get()
  66. {
  67. IInterface &ret = query(); ret.Link(); return ret;
  68. }
  69. };
  70. // ===================================== HqlLex ============================================
  71. class CHqlParserPseduoScope : public CHqlScope
  72. {
  73. protected:
  74. HqlGram * parser;
  75. public:
  76. CHqlParserPseduoScope(HqlGram * _parser) : CHqlScope(no_privatescope) { parser = _parser; }
  77. virtual IHqlExpression *lookupSymbol(IIdAtom * name, unsigned lookupFlags, HqlLookupContext & ctx)
  78. {
  79. attribute errpos;
  80. errpos.clearPosition();
  81. return parser->lookupSymbol(name, errpos);
  82. }
  83. virtual IHqlScope * queryConcreteScope() { return this; }
  84. virtual bool allBasesFullyBound() const { return true; }
  85. };
  86. // ===================================== HqlLex ============================================
  87. HqlLex::HqlLex(HqlGram *parser, IFileContents * contents, IXmlScope *_xmlScope, IHqlExpression *_macroExpr)
  88. : yyParser(parser), sourcePath(contents->querySourcePath()), xmlScope(LINK(_xmlScope)), macroExpr(_macroExpr)
  89. {
  90. assertex(parser);
  91. init(contents);
  92. }
  93. void HqlLex::init(IFileContents * _text)
  94. {
  95. text.set(_text);
  96. inmacro = NULL;
  97. parentLex = NULL;
  98. inComment = false;
  99. inSignature = false;
  100. inCpp = false;
  101. inMultiString = false;
  102. hasHashbreak = false;
  103. encrypted = false;
  104. loopTimes = 0;
  105. skipNesting = 0;
  106. macroGathering = 0;
  107. forLoop = NULL;
  108. size32_t len = _text->length();
  109. yyBuffer = new char[len+2]; // Include room for \0 and another \0 that we write beyond the end null while parsing
  110. memcpy(yyBuffer, text->getText(), len);
  111. yyBuffer[len] = '\0';
  112. yyBuffer[len+1] = '\0';
  113. yyLineNo = 1;
  114. yyPosition = 0;
  115. yyColumn = 1;
  116. yyStartPos = 0;
  117. lastToken = 0;
  118. eclyylex_init(&scanner);
  119. eclyy_scan_buffer(yyBuffer, len+2, scanner);
  120. }
  121. ///////////////////////////////////////////////////
  122. // public destructor
  123. //
  124. HqlLex::~HqlLex()
  125. {
  126. eclyylex_destroy(scanner);
  127. scanner = NULL;
  128. delete[] yyBuffer;
  129. ::Release(xmlScope);
  130. ::Release(macroExpr);
  131. if (inmacro) delete inmacro;
  132. ::Release(forLoop);
  133. }
  134. char* HqlLex::get_yyText(void)
  135. {
  136. if (inmacro)
  137. return inmacro->get_yyText();
  138. return eclyyget_text(scanner);
  139. }
  140. IFileContents* HqlLex::query_FileContents(void)
  141. {
  142. if (inmacro)
  143. return inmacro->query_FileContents();
  144. return text;
  145. }
  146. bool HqlLex::isMacroActive(IHqlExpression *expr)
  147. {
  148. if (expr==macroExpr)
  149. return true;
  150. else if (parentLex)
  151. return parentLex->isMacroActive(expr);
  152. else
  153. return false;
  154. }
  155. bool HqlLex::assertNext(attribute & returnToken, int expected, unsigned code, const char * msg)
  156. {
  157. //Pass LEXnone since only used for simple punctuation
  158. if (yyLex(returnToken, LEXnone, 0) != expected)
  159. {
  160. reportError(returnToken, code, "%s", msg);
  161. returnToken.release();
  162. return false;
  163. }
  164. return true;
  165. }
  166. bool HqlLex::assertNextOpenBra()
  167. {
  168. attribute tempToken;
  169. return assertNext(tempToken, '(', ERR_EXPECTED_LEFTCURLY, "( expected");
  170. }
  171. bool HqlLex::assertNextComma()
  172. {
  173. attribute tempToken;
  174. return assertNext(tempToken, ',', ERR_EXPECTED_COMMA, ", expected");
  175. }
  176. StringBuffer &HqlLex::getTokenText(StringBuffer &ret)
  177. {
  178. if (inmacro)
  179. return inmacro->getTokenText(ret);
  180. return ret.append(yyPosition - yyStartPos, yyBuffer+yyStartPos);
  181. }
  182. IHqlExpression *HqlLex::lookupSymbol(IIdAtom * name, const attribute& errpos)
  183. {
  184. return yyParser->lookupSymbol(name, errpos);
  185. }
  186. unsigned HqlLex::hex2digit(char c)
  187. {
  188. if (c >= 'a')
  189. return (c - 'a' + 10);
  190. else if (c >= 'A')
  191. return (c - 'A' + 10);
  192. return (c - '0');
  193. }
  194. __uint64 HqlLex::str2uint64(unsigned len, const char * digits, unsigned base)
  195. {
  196. __uint64 value = 0;
  197. while (len--)
  198. {
  199. char c = *digits++;
  200. value = value * base + hex2digit(c);
  201. }
  202. return value;
  203. }
  204. void HqlLex::hex2str(char * target, const char * digits, unsigned len)
  205. {
  206. while (len)
  207. {
  208. *target++ = (hex2digit(digits[0]) << 4) | hex2digit(digits[1]);
  209. len -= 2;
  210. digits += 2;
  211. }
  212. }
  213. IHqlExpression * HqlLex::createIntegerConstant(__int64 value, bool isSigned)
  214. {
  215. return createConstant(createIntValue(value, makeIntType(8, isSigned)));
  216. }
  217. void HqlLex::pushText(IFileContents * text, int startLineNo, int startColumn)
  218. {
  219. #ifdef TIMING_DEBUG
  220. MTIME_SECTION(timer, "HqlLex::pushText");
  221. #endif
  222. bool useLegacyImport = hasLegacyImportSemantics();
  223. bool useLegacyWhen = hasLegacyWhenSemantics();
  224. inmacro = new HqlLex(yyParser, text, NULL, NULL);
  225. inmacro->setLegacyImport(useLegacyImport);
  226. inmacro->setLegacyWhen(useLegacyWhen);
  227. inmacro->set_yyLineNo(startLineNo);
  228. inmacro->set_yyColumn(startColumn);
  229. }
  230. void HqlLex::pushText(const char *s, int startLineNo, int startColumn)
  231. {
  232. Owned<IFileContents> macroContents = createFileContentsFromText(s, sourcePath, yyParser->inSignedModule, yyParser->gpgSignature, 0);
  233. pushText(macroContents, startLineNo, startColumn);
  234. }
  235. void HqlLex::pushText(const char *s)
  236. {
  237. #ifdef TIMING_DEBUG
  238. MTIME_SECTION(timer, "HqlLex::pushText");
  239. #endif
  240. Owned<IFileContents> macroContents = createFileContentsFromText(s, sourcePath, yyParser->inSignedModule, yyParser->gpgSignature, 0);
  241. bool useLegacyImport = hasLegacyImportSemantics();
  242. bool useLegacyWhen = hasLegacyWhenSemantics();
  243. inmacro = new HqlLex(yyParser, macroContents, NULL, NULL);
  244. inmacro->setLegacyImport(useLegacyImport);
  245. inmacro->setLegacyWhen(useLegacyWhen);
  246. inmacro->set_yyLineNo(yyLineNo);
  247. inmacro->set_yyColumn(yyColumn);
  248. #if defined (TRACE_MACRO)
  249. DBGLOG("MACRO>> inmacro %p created for \"%s\" for macro parameters.\n",inmacro,s);
  250. #endif
  251. }
  252. bool HqlLex::hasLegacyImportSemantics() const
  253. {
  254. if (inmacro)
  255. return inmacro->hasLegacyImportSemantics();
  256. return legacyImportMode;
  257. }
  258. bool HqlLex::hasLegacyWhenSemantics() const
  259. {
  260. if (inmacro)
  261. return inmacro->hasLegacyWhenSemantics();
  262. return legacyWhenMode;
  263. }
  264. void HqlLex::setMacroParam(const attribute & errpos, IHqlExpression* funcdef, StringBuffer& curParam, IIdAtom * argumentId, unsigned& parmno,IProperties *macroParms)
  265. {
  266. IHqlExpression * formals = queryFunctionParameters(funcdef);
  267. IHqlExpression * defaults = queryFunctionDefaults(funcdef);
  268. unsigned numFormals = formals->numChildren();
  269. unsigned thisParam = (unsigned)-1;
  270. if (argumentId)
  271. {
  272. IAtom * argumentName = lower(argumentId);
  273. unsigned argNum = 0;
  274. for (unsigned i=0; i < numFormals; i++)
  275. {
  276. if (formals->queryChild(i)->queryName() == argumentName)
  277. {
  278. argNum = i+1;
  279. break;
  280. }
  281. }
  282. if (argNum == 0)
  283. reportError(errpos, ERR_NAMED_PARAM_NOT_FOUND, "Named parameter '%s' not found in macro", str(argumentId));
  284. else
  285. thisParam = argNum;
  286. }
  287. else
  288. thisParam = ++parmno;
  289. if (thisParam <= numFormals)
  290. {
  291. IHqlExpression* formal = formals->queryChild(thisParam-1);
  292. if (curParam.length()==0)
  293. {
  294. IHqlExpression* def = queryDefaultValue(defaults, thisParam-1);
  295. if (!def)
  296. {
  297. StringBuffer msg("Omitted parameter ");
  298. msg.append(parmno).append(" has no default value");
  299. reportError(errpos, ERR_PARAM_NODEFVALUE, "%s", msg.str());
  300. }
  301. else
  302. {
  303. if (!getFoldedConstantText(curParam, def))
  304. {
  305. StringBuffer msg("Default value for parameter ");
  306. msg.append(parmno).append(" should be a constant");
  307. reportError(errpos, ERR_PARAM_NODEFVALUE, "%s", msg.str());
  308. }
  309. }
  310. }
  311. macroParms->setProp(str(formal->queryName()), curParam.str());
  312. }
  313. curParam.clear();
  314. }
  315. /* This is pushing a macro definition. */
  316. void HqlLex::pushMacro(IHqlExpression *expr)
  317. {
  318. /* expr points to namedSymbol(no_funcdef):
  319. child(0)-> no_macro = macro body
  320. child(1) = parameters
  321. child(2) = defaults for parameters
  322. */
  323. attribute nextToken;
  324. int tok = yyLex(nextToken, LEXnone, 0);
  325. if (tok != '(')
  326. {
  327. //throw MakeStringException(2, "( expected");
  328. reportError(nextToken, ERR_EXPECTED_LEFTCURLY, "( expected");
  329. if (tok==EOF)
  330. return;
  331. // assume we've got an '(', we can continue parsing.
  332. }
  333. unsigned parenDepth = 1;
  334. StringBuffer curParam;
  335. Owned<IProperties> macroParms = createProperties();
  336. IHqlExpression * formals = expr->queryChild(1);
  337. IHqlExpression * defaults = expr->queryChild(2);
  338. unsigned formalParmCt = formals->numChildren();
  339. unsigned parmno = 0;
  340. IIdAtom * possibleName = NULL;
  341. IIdAtom * argumentName = NULL;
  342. while (parenDepth)
  343. {
  344. tok = yyLex(nextToken, LEXidentifier|LEXexpand, 0);
  345. switch(tok)
  346. {
  347. case '[':
  348. case '(':
  349. parenDepth++;
  350. curParam.append((char)tok);
  351. break;
  352. case ']':
  353. if (parenDepth > 1)
  354. parenDepth--;
  355. curParam.append((char)tok);
  356. break;
  357. case ')':
  358. parenDepth--;
  359. if (parenDepth)
  360. curParam.append(')');
  361. else if (formalParmCt>0 || curParam.length()>0) // handle last parameter
  362. setMacroParam(nextToken, expr, curParam, argumentName, parmno, macroParms);
  363. break;
  364. case ',':
  365. if (parenDepth==1)
  366. setMacroParam(nextToken, expr, curParam, argumentName, parmno, macroParms);
  367. else
  368. curParam.append(',');
  369. possibleName = NULL;
  370. argumentName = NULL;
  371. break;
  372. case ASSIGN:
  373. {
  374. bool done = false;
  375. if (parenDepth == 1 && possibleName)
  376. {
  377. //Horrible. Allow NAMED <id> := or <id> :=
  378. const char * text = curParam.str();
  379. while (isspace((byte)*text)) text++;
  380. if (memicmp(text, "NAMED", 5) == 0)
  381. text += 5;
  382. while (isspace((byte)*text)) text++;
  383. if (strlen(str(possibleName)) == strlen(text))
  384. {
  385. argumentName = possibleName;
  386. possibleName = NULL;
  387. curParam.clear();
  388. done = true;
  389. }
  390. }
  391. if (!done)
  392. getTokenText(curParam.append(' '));
  393. break;
  394. }
  395. case EOF:
  396. reportError(nextToken, ERR_MACRO_EOFINPARAM,"EOF encountered while gathering macro parameters");
  397. // no attempt to recover at the end of the file, but cleanup is needed.
  398. return;
  399. case UNKNOWN_ID:
  400. possibleName = nextToken.getId();
  401. curParam.append(' ').append(str(possibleName));
  402. break;
  403. default:
  404. curParam.append(' ');
  405. getTokenText(curParam);
  406. break;
  407. }
  408. nextToken.release();
  409. }
  410. if (parmno > formalParmCt)
  411. {
  412. StringBuffer msg("Too many actual parameters supplied to macro");
  413. if (expr->queryName())
  414. msg.append(' ').append(expr->queryName());
  415. msg.appendf(": expected %d, given %d", formalParmCt, parmno);
  416. reportError(nextToken, ERR_PARAM_TOOMANY, "%s", msg.str());
  417. }
  418. else if (parmno < formalParmCt)
  419. {
  420. for (unsigned idx = parmno; idx < formalParmCt; idx++)
  421. {
  422. IHqlExpression* formal = formals->queryChild(idx);
  423. if (!macroParms->queryProp(str(formal->queryName())))
  424. {
  425. IHqlExpression* def = queryDefaultValue(defaults, idx);
  426. if (def)
  427. {
  428. if (!getFoldedConstantText(curParam, def))
  429. {
  430. StringBuffer msg("Omitted parameter ");
  431. msg.append(idx+1);
  432. if (expr->queryName())
  433. msg.append(" to macro ").append(expr->queryName());
  434. msg.append(" should be a constant value");
  435. reportError(nextToken, ERR_PARAM_NODEFVALUE, "%s", msg.str());
  436. }
  437. macroParms->setProp(str(formal->queryName()), curParam.str());
  438. //DBGLOG("Set macro parm: %s", curParam.str());
  439. curParam.clear();
  440. }
  441. else
  442. {
  443. StringBuffer msg("Omitted parameter ");
  444. msg.append(idx+1);
  445. if (expr->queryName())
  446. msg.append(" to macro ").append(expr->queryName());
  447. msg.append(" has no default value");
  448. reportError(nextToken, ERR_PARAM_NODEFVALUE, "%s", msg.str());
  449. }
  450. }
  451. }
  452. }
  453. IHqlExpression *macroBodyExpr = expr->queryChild(0);
  454. IFileContents * macroContents = static_cast<IFileContents *>(macroBodyExpr->queryUnknownExtra());
  455. if (isMacroActive(expr))
  456. {
  457. StringBuffer msg;
  458. msg.append("recursive macro call: ").append(getMacroName());
  459. reportError(nextToken, ERR_MACRO_RECURSIVE, "%s", msg.str());
  460. // error recovery
  461. if (expr->isAction())
  462. pushText("0;");
  463. else if (expr->isDataset())
  464. pushText("{}");
  465. else
  466. pushText("0 ENDMACRO");
  467. }
  468. else
  469. {
  470. bool useLegacyImport = hasLegacyImportSemantics();
  471. bool useLegacyWhen = hasLegacyWhenSemantics();
  472. inmacro = new HqlLex(yyParser, macroContents, NULL, LINK(expr));
  473. inmacro->setLegacyImport(useLegacyImport);
  474. inmacro->setLegacyWhen(useLegacyWhen);
  475. #if defined(TRACE_MACRO)
  476. DBGLOG("MACRO>> inmacro %p created for \"%s\" at %d:%d\n",inmacro, s.str(),macroBodyExpr->getStartLine(),macroBodyExpr->getStartColumn());
  477. // DBGLOG("MACRO>> macro called at %d:%d\n", expr->getStartLine(),expr->getStartColumn());
  478. #endif
  479. /* set the lineno and column in the original source as the starting point */
  480. inmacro->yyLineNo = macroBodyExpr->getStartLine();
  481. inmacro->yyColumn = macroBodyExpr->getStartColumn();
  482. inmacro->setParentLex(this);
  483. inmacro->macroParms.setown(macroParms.getClear());
  484. inmacro->hashDollar = macroBodyExpr->queryBody()->queryName();
  485. }
  486. }
  487. void HqlLex::checkSignature(const attribute & dummyToken)
  488. {
  489. if (yyParser->lookupCtx.queryParseContext().ignoreSignatures)
  490. return;
  491. try
  492. {
  493. yyParser->gpgSignature.setown(::checkSignature(text->length(), text->getText()));
  494. yyParser->inSignedModule = true;
  495. }
  496. catch (IException *e)
  497. {
  498. StringBuffer msg;
  499. e->errorMessage(msg);
  500. reportWarning(CategorySecurity, dummyToken, WRN_SECURITY_SIGNERROR, "%s", msg.str());
  501. e->Release();
  502. }
  503. }
  504. /* Read encrypted syntax, and push the decrypted text as a macro. */
  505. void HqlLex::processEncrypted()
  506. {
  507. attribute nextToken;
  508. if (yyLex(nextToken, LEXnone, 0) != '(')
  509. {
  510. reportError(nextToken, ERR_EXPECTED_LEFTCURLY, "( expected");
  511. nextToken.release();
  512. return;
  513. }
  514. StringBuffer encoded64;
  515. for (;;)
  516. {
  517. if (yyLex(nextToken, LEXstring, 0) != STRING_CONST)
  518. {
  519. reportError(nextToken, ERR_EXPECTED, "String expected");
  520. nextToken.release();
  521. return;
  522. }
  523. OwnedHqlExpr str = nextToken.getExpr();
  524. getStringValue(encoded64, str);
  525. int next = yyLex(nextToken, LEXnone, 0);
  526. if (next == ')')
  527. break;
  528. if (next != ',')
  529. {
  530. reportError(nextToken, ERR_EXPECTED_COMMA, ", expected");
  531. nextToken.release();
  532. return;
  533. }
  534. }
  535. if (yyLex(nextToken, LEXnone, 0) != ';')
  536. {
  537. reportError(nextToken, ERR_EXPECTED, "; expected");
  538. nextToken.release();
  539. return;
  540. }
  541. MemoryBuffer decrypted;
  542. decryptEclAttribute(decrypted, encoded64.str());
  543. decrypted.append(0); // add a null terminator to the string...
  544. Owned<ISourcePath> sourcePath = createSourcePath("<encrypted>");
  545. Owned<IFileContents> decryptedContents = createFileContentsFromText((const char *)decrypted.toByteArray(), sourcePath, yyParser->inSignedModule, yyParser->gpgSignature, text->getTimeStamp());
  546. bool useLegacyImport = hasLegacyImportSemantics();
  547. bool useLegacyWhen = hasLegacyWhenSemantics();
  548. inmacro = new HqlLex(yyParser, decryptedContents, NULL, NULL);
  549. inmacro->setLegacyImport(useLegacyImport);
  550. inmacro->setLegacyWhen(useLegacyWhen);
  551. inmacro->setParentLex(this);
  552. inmacro->encrypted = true;
  553. }
  554. /* Return: true if more parameter(s) left. */
  555. bool HqlLex::getParameter(StringBuffer &curParam, const char* for_what, int* startLine, int* startCol)
  556. {
  557. unsigned parenDepth = 1;
  558. if (startLine) *startLine = -1;
  559. attribute nextToken;
  560. for (;;)
  561. {
  562. int tok = yyLex(nextToken, LEXnone, 0);
  563. if (startLine && *startLine == -1)
  564. {
  565. *startLine = nextToken.pos.lineno;
  566. *startCol = nextToken.pos.column;
  567. }
  568. switch(tok)
  569. {
  570. case '(':
  571. case '[':
  572. parenDepth++;
  573. curParam.append((char) tok);
  574. break;
  575. case ')':
  576. if (parenDepth==1)
  577. return false;
  578. // fallthrough
  579. case ']':
  580. parenDepth--;
  581. curParam.append((char) tok);
  582. break;
  583. case ',':
  584. if (parenDepth==1)
  585. return true;
  586. curParam.append(',');
  587. break;
  588. case EOF:
  589. {
  590. StringBuffer msg("EOF encountered while gathering parameters for ");
  591. msg.append(for_what);
  592. reportError(nextToken, ERR_TMPLT_EOFINPARAM, "%s", msg.str());
  593. }
  594. return false;
  595. default:
  596. curParam.append(' ');
  597. getTokenText(curParam);
  598. break;
  599. }
  600. nextToken.release();
  601. }
  602. }
  603. void HqlLex::doSkipUntilEnd(attribute & returnToken, const char * forwhat)
  604. {
  605. while (skipNesting)
  606. {
  607. int tok = yyLex(returnToken, LEXnone, 0);
  608. returnToken.release();
  609. if (tok == EOF)
  610. {
  611. StringBuffer msg;
  612. msg.appendf("Unexpected EOF in %s: #END expected",forwhat);
  613. reportError(returnToken, ERR_TMPLT_HASHENDEXPECTED, "%s", msg.str());
  614. clearNestedHash(); // prevent unnecessary more error messages
  615. break;
  616. }
  617. }
  618. }
  619. void HqlLex::doIf(attribute & returnToken, bool isElseIf)
  620. {
  621. StringBuffer forwhat;
  622. int line = returnToken.pos.lineno, col = returnToken.pos.column;
  623. forwhat.appendf("#IF(%d,%d)",returnToken.pos.lineno,returnToken.pos.column);
  624. int tok = yyLex(returnToken, LEXnone, 0);
  625. if (tok != '(')
  626. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "( expected"); // MORE - make it fatal!
  627. StringBuffer curParam("(");
  628. if (getParameter(curParam, forwhat.str()))
  629. {
  630. reportError(returnToken, ERR_OPERANDS_TOOMANY, "too many operands");
  631. StringBuffer dummy;
  632. while (getParameter(dummy,forwhat.str()))
  633. ;
  634. }
  635. curParam.append(')');
  636. Owned<IValue> value = parseConstExpression(returnToken, curParam, queryTopXmlScope(),line,col);
  637. if (value && !value->getBoolValue())
  638. {
  639. setHashEndFlags(0);
  640. skipNesting = 1;
  641. if (!isElseIf)
  642. doSkipUntilEnd(returnToken, forwhat);
  643. }
  644. else
  645. setHashEndFlags(HEFhadtrue);
  646. }
  647. int HqlLex::doElse(attribute & returnToken, LexerFlags lookupFlags, const short * activeState, bool isElseIf)
  648. {
  649. StringBuffer forwhat;
  650. forwhat.appendf("#%s(%d,%d)",isElseIf ? "ELSEIF" : "ELSE", returnToken.pos.lineno,returnToken.pos.column);
  651. if ((hashendKinds.ordinality() == 0) || (hashendKinds.tos() != HashStmtIf))
  652. {
  653. reportError(returnToken, ERR_TMPLT_EXTRAELSE,"#ELSE does not match a #IF");
  654. return SKIPPED;
  655. }
  656. unsigned flags = hashendFlags.tos();
  657. if (!isElseIf)
  658. {
  659. if (flags & HEFhadelse)
  660. reportError(returnToken, ERR_TMPLT_EXTRAELSE,"Multiple #ELSE for the same #IF");
  661. setHashEndFlags(flags|HEFhadelse);
  662. }
  663. switch (skipNesting)
  664. {
  665. case 0:
  666. skipNesting = 1;
  667. doSkipUntilEnd(returnToken, forwhat);
  668. return yyLex(returnToken, lookupFlags, activeState);
  669. case 1:
  670. if (flags & HEFhadtrue)
  671. {
  672. //Don't need to do anything
  673. }
  674. else
  675. {
  676. skipNesting = 0;
  677. if (isElseIf)
  678. doIf(returnToken, true);
  679. else
  680. setHashEndFlags(HEFhadtrue|HEFhadelse);
  681. }
  682. return SKIPPED; // looks wrong, but called within a doIf() loop, and first return is ignored
  683. default:
  684. return SKIPPED;
  685. }
  686. }
  687. int HqlLex::doEnd(attribute & returnToken, LexerFlags lookupFlags, const short * activeState)
  688. {
  689. if (hashendKinds.ordinality() != 0)
  690. {
  691. endNestedHash();
  692. if (skipNesting)
  693. {
  694. skipNesting -= 1;
  695. return(HASHEND);
  696. }
  697. }
  698. else
  699. reportError(returnToken, ERR_TMPLT_EXTRAEND,"#END doesn't match a # command");
  700. return yyLex(returnToken, lookupFlags, activeState);
  701. }
  702. void HqlLex::doDeclare(attribute & returnToken)
  703. {
  704. StringBuffer forwhat;
  705. forwhat.appendf("#DECLARE(%d,%d)",returnToken.pos.lineno,returnToken.pos.column);
  706. IIdAtom * name = NULL;
  707. if (yyLex(returnToken, LEXnone, 0) != '(')
  708. {
  709. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "( expected");
  710. returnToken.release();
  711. return;
  712. }
  713. for (;;)
  714. {
  715. int tok = yyLex(returnToken, LEXidentifier, 0);
  716. if (tok == EOF)
  717. {
  718. StringBuffer msg;
  719. msg.append("Unexpected EOF in ").append(forwhat.str()).append(": ')' expected");
  720. reportError(returnToken, ERR_TMPLT_HASHENDEXPECTED, "%s", msg.str());
  721. clearNestedHash(); // prevent unnecessary more error messages
  722. return;
  723. }
  724. if (tok != UNKNOWN_ID)
  725. {
  726. reportError(returnToken, ERR_EXPECTED_IDENTIFIER, "Identifier expected");
  727. returnToken.release();
  728. continue;
  729. }
  730. name = returnToken.getId();
  731. declareXmlSymbol(returnToken, str(name));
  732. tok = yyLex(returnToken, LEXnone, 0);
  733. if (tok == ')')
  734. break;
  735. else if (tok == ',')
  736. continue;
  737. else if (tok == EOF)
  738. {
  739. StringBuffer msg;
  740. msg.append("Unexpected EOF in ").append(forwhat.str()).append(": ) expected");
  741. reportError(returnToken, ERR_TMPLT_HASHENDEXPECTED, "%s", msg.str());
  742. clearNestedHash(); // prevent unnecessary more error messages
  743. }
  744. else
  745. {
  746. reportError(returnToken, ERR_EXPECTED, "',' or ')' expected");
  747. returnToken.release();
  748. }
  749. }
  750. }
  751. void HqlLex::doExpand(attribute & returnToken)
  752. {
  753. StringBuffer forwhat;
  754. forwhat.appendf("#DECLARE(%d,%d)",returnToken.pos.lineno,returnToken.pos.column);
  755. if (yyLex(returnToken, LEXnone, 0) != '(')
  756. {
  757. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "( expected");
  758. returnToken.release();
  759. return;
  760. }
  761. StringBuffer curParam("(");
  762. int startLine, startCol;
  763. if (getParameter(curParam,forwhat.str(),&startLine,&startCol))
  764. {
  765. reportError(returnToken, ERR_OPERANDS_TOOMANY, "Too many operands");
  766. StringBuffer dummy;
  767. while (getParameter(dummy,forwhat.str()))
  768. ;
  769. }
  770. curParam.append(')');
  771. Owned<IValue> value = parseConstExpression(returnToken, curParam, queryTopXmlScope(),startLine-1,startCol);
  772. if (value)
  773. {
  774. StringBuffer buf;
  775. value->getUTF8Value(buf);
  776. if (buf.length())
  777. pushText(buf.str());
  778. }
  779. }
  780. void HqlLex::doSet(attribute & returnToken, bool append)
  781. {
  782. StringBuffer forwhat;
  783. forwhat.appendf("%s(%d,%d)",append?"#APPEND":"#SET",returnToken.pos.lineno,returnToken.pos.column);
  784. IIdAtom * name = NULL;
  785. if (yyLex(returnToken, LEXnone, 0) != '(')
  786. {
  787. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "( expected");
  788. returnToken.release();
  789. return;
  790. }
  791. if (yyLex(returnToken, LEXidentifier, 0) != UNKNOWN_ID)
  792. {
  793. reportError(returnToken, ERR_EXPECTED_IDENTIFIER, "Identifier expected");
  794. returnToken.release();
  795. return;
  796. }
  797. name = returnToken.getId();
  798. if (yyLex(returnToken, LEXnone,0) != ',')
  799. {
  800. reportError(returnToken, ERR_EXPECTED_COMMA, ", expected");
  801. return;
  802. }
  803. StringBuffer curParam("(");
  804. int startLine, startCol;
  805. if (getParameter(curParam,forwhat.str(),&startLine,&startCol))
  806. {
  807. reportError(returnToken, ERR_OPERANDS_TOOMANY, "Too many operands");
  808. StringBuffer dummy;
  809. while (getParameter(dummy,forwhat.str()))
  810. ;
  811. }
  812. curParam.append(')');
  813. IValue *value = parseConstExpression(returnToken, curParam, queryTopXmlScope(),startLine-1,startCol);
  814. if (value)
  815. {
  816. StringBuffer buf;
  817. value->getStringValue(buf);
  818. setXmlSymbol(returnToken, str(name), buf.str(), append);
  819. value->Release();
  820. }
  821. }
  822. void HqlLex::doLine(attribute & returnToken)
  823. {
  824. StringBuffer forwhat;
  825. int line = returnToken.pos.lineno, col = returnToken.pos.column;
  826. forwhat.appendf("LINE(%d,%d)",returnToken.pos.lineno,returnToken.pos.column);
  827. if (yyLex(returnToken, LEXnone, 0) != '(')
  828. {
  829. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "( expected");
  830. returnToken.release();
  831. return;
  832. }
  833. StringBuffer curParam("(");
  834. bool moreParams = getParameter(curParam, forwhat.str(), &line, &col);
  835. curParam.append(')');
  836. IValue *value = parseConstExpression(returnToken, curParam, queryTopXmlScope(),line,col);
  837. if (value && value->getTypeCode()==type_int)
  838. {
  839. returnToken.pos.lineno = yyLineNo = (int)value->getIntValue();
  840. }
  841. else
  842. reportError(returnToken, ERR_EXPECTED_CONST, "Constant integer expression expected");
  843. ::Release(value);
  844. if (moreParams)
  845. {
  846. int startLine, startCol;
  847. if (getParameter(curParam,forwhat.str(),&startLine,&startCol))
  848. {
  849. reportError(returnToken, ERR_OPERANDS_TOOMANY, "Too many operands");
  850. StringBuffer dummy;
  851. while (getParameter(dummy,forwhat.str()))
  852. ;
  853. }
  854. curParam.append(')');
  855. IValue *value = parseConstExpression(returnToken, curParam, queryTopXmlScope(),startLine-1,startCol);
  856. if (value && value->getTypeCode()==type_string)
  857. {
  858. StringBuffer buf;
  859. value->getStringValue(buf);
  860. // MORE - set filename here
  861. value->Release();
  862. }
  863. else
  864. reportError(returnToken, ERR_EXPECTED_CONST, "Constant string expression expected");
  865. }
  866. }
  867. void HqlLex::doSlashSlashHash(attribute const & returnToken, const char * command)
  868. {
  869. if (inmacro)
  870. {
  871. inmacro->doSlashSlashHash(returnToken, command);
  872. return;
  873. }
  874. if (macroGathering)
  875. return;
  876. if (hasPrefix(command, "import", false))
  877. {
  878. const char * next = skipws(command + 6);
  879. if (*next == '(')
  880. {
  881. next = skipws(next + 1);
  882. const char * bra = strchr(next, ')');
  883. if (bra)
  884. {
  885. StringBuffer option(bra - next, next);
  886. option.clip();
  887. if (strieq(option, "legacy"))
  888. setLegacyImport(true);
  889. else if (strieq(option, "modern"))
  890. setLegacyImport(false);
  891. else
  892. reportError(returnToken, ERR_EXPECTED_ID, "Unknown #import option '%s' - expected legacy or modern", option.str());
  893. }
  894. else
  895. reportError(returnToken, ERR_EXPECTED_RIGHTCURLY, "Expected closing )");
  896. }
  897. else
  898. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "Expected (");
  899. }
  900. else if (hasPrefix(command, "when", false))
  901. {
  902. const char * next = skipws(command + 4);
  903. if (*next == '(')
  904. {
  905. next = skipws(next + 1);
  906. const char * bra = strchr(next, ')');
  907. if (bra)
  908. {
  909. StringBuffer option(bra - next, next);
  910. option.clip();
  911. if (strieq(option, "legacy"))
  912. setLegacyWhen(true);
  913. else if (strieq(option, "modern"))
  914. setLegacyWhen(false);
  915. else
  916. reportError(returnToken, ERR_EXPECTED_ID, "Unknown #import option '%s' - expected legacy or modern", option.str());
  917. }
  918. else
  919. reportError(returnToken, ERR_EXPECTED_RIGHTCURLY, "Expected closing )");
  920. }
  921. else
  922. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "Expected (");
  923. }
  924. //Ignore any unrecognised commands
  925. }
  926. void HqlLex::doError(attribute & returnToken, bool isError)
  927. {
  928. StringBuffer forwhat;
  929. forwhat.appendf("%s(%d,%d)",isError?"#ERROR":"#WARNING",returnToken.pos.lineno,returnToken.pos.column);
  930. if (yyLex(returnToken, LEXnone, 0) != '(')
  931. {
  932. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "( expected");
  933. returnToken.release();
  934. return;
  935. }
  936. StringBuffer curParam("(");
  937. int startLine, startCol;
  938. if (getParameter(curParam,forwhat.str(),&startLine,&startCol))
  939. {
  940. reportError(returnToken, ERR_OPERANDS_TOOMANY, "Too many operands");
  941. StringBuffer dummy;
  942. while (getParameter(dummy,forwhat.str()))
  943. ;
  944. }
  945. curParam.append(')');
  946. StringBuffer buf;
  947. OwnedIValue value = parseConstExpression(returnToken, curParam, queryTopXmlScope(),startLine-1,startCol);
  948. if (value)
  949. {
  950. value->getStringValue(buf);
  951. }
  952. else
  953. buf.append(curParam.length()-2, curParam.str()+1);
  954. if (isError)
  955. reportError(returnToken, ERR_HASHERROR, "#ERROR: %s", buf.str());
  956. else
  957. reportWarning(CategoryUnusual, returnToken, WRN_HASHWARNING, "#WARNING: %s", buf.str());
  958. }
  959. void HqlLex::doExport(attribute & returnToken, bool toXml)
  960. {
  961. StringBuffer forwhat;
  962. forwhat.appendf("#EXPORT(%d,%d)",returnToken.pos.lineno,returnToken.pos.column);
  963. IIdAtom * exportname = NULL;
  964. if (yyLex(returnToken, LEXnone, 0) != '(')
  965. {
  966. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "( expected");
  967. returnToken.release();
  968. return;
  969. }
  970. if (yyLex(returnToken, LEXidentifier, 0) != UNKNOWN_ID)
  971. {
  972. reportError(returnToken, ERR_EXPECTED_IDENTIFIER, "Identifier expected");
  973. returnToken.release();
  974. return;
  975. }
  976. exportname = returnToken.getId();
  977. if (yyLex(returnToken, LEXnone,0) != ',')
  978. {
  979. reportError(returnToken, ERR_EXPECTED_COMMA, ", expected");
  980. return;
  981. }
  982. IPropertyTree *data = createPTree("Data", ipt_caseInsensitive);
  983. for (;;)
  984. {
  985. StringBuffer curParam("SIZEOF(");
  986. bool more = getParameter(curParam,"#EXPORT");
  987. curParam.append(",MAX)");
  988. OwnedHqlExpr expr;
  989. Owned<IHqlScope> scope = new CHqlParserPseduoScope(yyParser);
  990. try
  991. {
  992. HqlLookupContext ctx(yyParser->lookupCtx);
  993. Owned<IFileContents> exportContents = createFileContentsFromText(curParam.str(), sourcePath, yyParser->inSignedModule, yyParser->gpgSignature, 0);
  994. expr.setown(parseQuery(scope, exportContents, ctx, xmlScope, NULL, true, false));
  995. if (expr && (expr->getOperator() == no_sizeof))
  996. {
  997. IHqlExpression * child = expr->queryChild(0);
  998. node_operator op = child->getOperator();
  999. if(op==no_table || op==no_usertable || op==no_newusertable || op == no_record || op == no_select || op == no_field)
  1000. exportData(data, child, true);
  1001. else if (child->queryRecord())
  1002. exportData(data, child->queryRecord(), true);
  1003. else
  1004. reportError(returnToken, ERR_EXPECTED_COMMA, "DATASET or TABLE expression expected");
  1005. }
  1006. else
  1007. reportError(returnToken, ERR_EXPECTED_COMMA, "Could not parse the argument passed to #EXPORT");
  1008. }
  1009. catch (...)
  1010. {
  1011. setXmlSymbol(returnToken, str(exportname), "", false);
  1012. IERRLOG("Unexpected exception in doExport()");
  1013. }
  1014. if (!more)
  1015. break;
  1016. }
  1017. StringBuffer buf;
  1018. toXML(data, buf, 0);
  1019. if (toXml)
  1020. ensureTopXmlScope()->loadXML(buf.str(), str(exportname));
  1021. else
  1022. setXmlSymbol(returnToken, str(exportname), buf.str(), false);
  1023. data->Release();
  1024. }
  1025. void HqlLex::doTrace(attribute & returnToken)
  1026. {
  1027. StringBuffer forwhat;
  1028. forwhat.appendf("#TRACE(%d,%d)",returnToken.pos.lineno,returnToken.pos.column);
  1029. if (yyLex(returnToken, LEXnone, 0) != '(')
  1030. {
  1031. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "( expected");
  1032. returnToken.release();
  1033. return;
  1034. }
  1035. StringBuffer curParam("(");
  1036. int startLine, startCol;
  1037. if (getParameter(curParam,forwhat.str(),&startLine,&startCol))
  1038. {
  1039. reportError(returnToken, ERR_OPERANDS_TOOMANY, "Too many operands");
  1040. StringBuffer dummy;
  1041. while (getParameter(dummy,forwhat.str()))
  1042. ;
  1043. }
  1044. curParam.append(')');
  1045. Owned<IValue> value = parseConstExpression(returnToken, curParam, queryTopXmlScope(),startLine-1,startCol);
  1046. if (value)
  1047. {
  1048. StringBuffer buf;
  1049. value->getStringValue(buf);
  1050. FILE *trace = fopen("hql.log", "at");
  1051. if (trace)
  1052. {
  1053. fwrite(buf.str(),buf.length(),1,trace);
  1054. fclose(trace);
  1055. }
  1056. }
  1057. }
  1058. void HqlLex::doFor(attribute & returnToken, bool doAll)
  1059. {
  1060. //MTIME_SECTION(timer, "HqlLex::doFor")
  1061. int startLine = -1, startCol = 0;
  1062. StringBuffer forwhat;
  1063. forwhat.appendf("#FOR(%d,%d)",returnToken.pos.lineno,returnToken.pos.column);
  1064. IIdAtom * name = NULL;
  1065. if (yyLex(returnToken, LEXnone, 0) != '(')
  1066. {
  1067. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "( expected");
  1068. returnToken.release();
  1069. return;
  1070. }
  1071. if (yyLex(returnToken, LEXidentifier, 0) != UNKNOWN_ID)
  1072. {
  1073. reportError(returnToken, ERR_EXPECTED_IDENTIFIER, "Identifier expected");
  1074. returnToken.release();
  1075. return;
  1076. }
  1077. name = returnToken.getId();
  1078. StringBuffer forFilterText;
  1079. // Note - we gather the for filter and body in skip mode (deferring evaluation of #if etc) since the context will be different each time...
  1080. skipNesting = 1;
  1081. int tok = yyLex(returnToken, LEXnone,0);
  1082. if (tok == '(')
  1083. {
  1084. forFilterText.append('(');
  1085. while (getParameter(forFilterText, forwhat.str()))
  1086. forFilterText.append(") AND (");
  1087. forFilterText.append(')');
  1088. tok = yyLex(returnToken, LEXnone, 0);
  1089. }
  1090. if (tok != ')')
  1091. {
  1092. reportError(returnToken, ERR_EXPECTED_RIGHTCURLY, ") expected");
  1093. // recovery: assume a ')' is here. And push back the token.
  1094. pushText(get_yyText());
  1095. returnToken.release();
  1096. }
  1097. // Now gather the tokens we are going to repeat...
  1098. StringBuffer forBodyText;
  1099. for (;;)
  1100. {
  1101. int tok = yyLex(returnToken, LEXnone,0);
  1102. if (startLine == -1)
  1103. {
  1104. startLine = returnToken.pos.lineno - 1;
  1105. startCol = returnToken.pos.column;
  1106. }
  1107. if (tok == EOF)
  1108. {
  1109. reportError(returnToken, ERR_TMPLT_HASHENDEXPECTED, "EOF encountered inside %s: #END expected", forwhat.str());
  1110. clearNestedHash(); // prevent unnecessary more error messages
  1111. return;
  1112. }
  1113. if (tok == HASHEND && !skipNesting)
  1114. break;
  1115. forBodyText.append(' ');
  1116. getTokenText(forBodyText);
  1117. returnToken.release();
  1118. }
  1119. ::Release(forLoop);
  1120. forLoop = getSubScopes(returnToken, str(name), doAll);
  1121. if (forFilterText.length())
  1122. forFilter.setown(createFileContentsFromText(forFilterText, sourcePath, yyParser->inSignedModule, yyParser->gpgSignature, 0));
  1123. forBody.setown(createFileContentsFromText(forBodyText, sourcePath, yyParser->inSignedModule, yyParser->gpgSignature, 0));
  1124. loopTimes = 0;
  1125. if (forLoop && forLoop->first()) // more - check filter
  1126. checkNextLoop(returnToken, true, startLine, startCol);
  1127. }
  1128. void HqlLex::doLoop(attribute & returnToken)
  1129. {
  1130. int startLine = -1, startCol = 0;
  1131. StringBuffer forwhat;
  1132. forwhat.appendf("#LOOP(%d,%d)",returnToken.pos.lineno,returnToken.pos.column);
  1133. // Now gather the tokens we are going to repeat...
  1134. StringBuffer forBodyText;
  1135. // Note - we gather the for filter and body in skip mode (deferring evaluation of #if etc) since the context will be different each time...
  1136. skipNesting = 1;
  1137. hasHashbreak = false;
  1138. for (;;)
  1139. {
  1140. int tok = yyLex(returnToken, LEXnone,0);
  1141. if (startLine == -1)
  1142. {
  1143. startLine = returnToken.pos.lineno-1;
  1144. startCol = returnToken.pos.column;
  1145. }
  1146. if (tok == EOF)
  1147. {
  1148. reportError(returnToken, ERR_TMPLT_HASHENDEXPECTED, "EOF encountered inside %s: #END expected",forwhat.str());
  1149. clearNestedHash(); // prevent unnecessary more error messages
  1150. return;
  1151. }
  1152. if (tok == HASHEND && !skipNesting)
  1153. break;
  1154. forBodyText.append(' ');
  1155. getTokenText(forBodyText);
  1156. returnToken.release();
  1157. }
  1158. if (!hasHashbreak)
  1159. {
  1160. reportError(returnToken, ERR_TMPLT_NOBREAKINLOOP,"No #BREAK inside %s: infinite loop will occur", forwhat.str());
  1161. return;
  1162. }
  1163. ::Release(forLoop);
  1164. forLoop = new CDummyScopeIterator(ensureTopXmlScope());
  1165. forFilter.clear();
  1166. forBody.setown(createFileContentsFromText(forBodyText, sourcePath, yyParser->inSignedModule, yyParser->gpgSignature, 0));
  1167. loopTimes = 0;
  1168. if (forLoop->first()) // more - check filter
  1169. checkNextLoop(returnToken, true,startLine,startCol);
  1170. }
  1171. void HqlLex::doGetDataType(attribute & returnToken)
  1172. {
  1173. int tok = yyLex(returnToken, LEXnone, 0);
  1174. if (tok != '(')
  1175. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "( expected"); // MORE - make it fatal!
  1176. StringBuffer curParam("(");
  1177. if (getParameter(curParam, "#GETDATATYPE"))
  1178. {
  1179. reportError(returnToken, ERR_OPERANDS_TOOMANY, "too many operands");
  1180. StringBuffer dummy;
  1181. while (getParameter(dummy,"#GETDATATYPE"))
  1182. ;
  1183. }
  1184. curParam.append(')');
  1185. StringBuffer type;
  1186. doGetDataType(type, curParam.str(), returnToken.pos.lineno, returnToken.pos.column);
  1187. pushText(type.str());
  1188. }
  1189. StringBuffer& HqlLex::doGetDataType(StringBuffer & type, const char * text, int lineno, int column)
  1190. {
  1191. OwnedHqlExpr expr = parseECL(text, queryTopXmlScope(), lineno, column);
  1192. if(expr)
  1193. {
  1194. type.append('\'');
  1195. if (expr->queryType())
  1196. expr->queryType()->getECLType(type);
  1197. type.append('\'');
  1198. }
  1199. else
  1200. type.append("'unknown_type'");
  1201. return type;
  1202. }
  1203. int HqlLex::doHashText(attribute & returnToken)
  1204. {
  1205. StringBuffer forwhat;
  1206. forwhat.appendf("#TEXT(%d,%d)",returnToken.pos.lineno,returnToken.pos.column);
  1207. if (yyLex(returnToken, LEXnone, 0) != '(')
  1208. {
  1209. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "( expected");
  1210. returnToken.release();
  1211. returnToken.setExpr(createBlankString());
  1212. return STRING_CONST;
  1213. }
  1214. StringBuffer parameterText;
  1215. bool moreParams = getParameter(parameterText, forwhat.str());
  1216. if (!moreParams)
  1217. {
  1218. while (parameterText.length() && parameterText.charAt(0)==' ')
  1219. parameterText.remove(0, 1);
  1220. }
  1221. else
  1222. {
  1223. reportError(returnToken, ERR_OPERANDS_TOOMANY, "Too many operands");
  1224. StringBuffer dummy;
  1225. while (getParameter(dummy,forwhat.str()))
  1226. ;
  1227. }
  1228. returnToken.setExpr(createConstant(parameterText));
  1229. return (STRING_CONST);
  1230. }
  1231. void HqlLex::doInModule(attribute & returnToken)
  1232. {
  1233. #ifdef TIMING_DEBUG
  1234. MTIME_SECTION(timer, "HqlLex::doInModule");
  1235. #endif
  1236. int tok = yyLex(returnToken, LEXnone, 0);
  1237. if (tok != '(')
  1238. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "( expected");
  1239. StringBuffer moduleName, attrName;
  1240. if (getParameter(moduleName,"#INMODULE"))
  1241. {
  1242. if (getParameter(attrName,"#INMODULE"))
  1243. {
  1244. reportError(returnToken, ERR_OPERANDS_TOOMANY, "too many operands");
  1245. /* skip the rest */
  1246. StringBuffer dummy;
  1247. while (getParameter(dummy,"#INMODULE"))
  1248. ;
  1249. }
  1250. }
  1251. else
  1252. {
  1253. reportError(returnToken, ERR_PARAM_TOOFEW,"Too few parameters: #INMODULE needs 2");
  1254. /* recovery */
  1255. pushText("true");
  1256. return;
  1257. }
  1258. if (isInModule(yyParser->lookupCtx, moduleName.str(),attrName.str()))
  1259. pushText("true");
  1260. else
  1261. pushText("false");
  1262. }
  1263. static bool isInModule(HqlLookupContext & ctx, const char* moduleName, const char* attrName)
  1264. {
  1265. if (!ctx.queryRepository())
  1266. return false;
  1267. try
  1268. {
  1269. //hack: get rid of the extra leading spaces
  1270. const char* pModule = moduleName;
  1271. while(*pModule==' ') pModule++;
  1272. const char* pAttr = attrName;
  1273. while(*pAttr==' ') pAttr++;
  1274. OwnedHqlExpr match = ctx.queryRepository()->queryRootScope()->lookupSymbol(createIdAtom(pModule), LSFpublic, ctx);
  1275. IHqlScope * scope = match ? match->queryScope() : NULL;
  1276. if (scope)
  1277. {
  1278. OwnedHqlExpr expr = scope->lookupSymbol(createIdAtom(pAttr), LSFpublic, ctx);
  1279. if (expr)
  1280. return true;
  1281. }
  1282. }
  1283. catch (...)
  1284. {
  1285. IERRLOG("Unexpected exception in doInModule()");
  1286. }
  1287. return false;
  1288. }
  1289. void HqlLex::doUniqueName(attribute & returnToken)
  1290. {
  1291. int tok = yyLex(returnToken, LEXnone, 0);
  1292. if (tok != '(')
  1293. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "( expected");
  1294. else
  1295. tok = yyLex(returnToken, LEXidentifier,0);
  1296. if (tok != UNKNOWN_ID)
  1297. {
  1298. reportError(returnToken, ERR_EXPECTED_IDENTIFIER, "Identifier expected");
  1299. returnToken.release();
  1300. }
  1301. else
  1302. {
  1303. IIdAtom * name = returnToken.getId();
  1304. StringAttr pattern("__#__$__");
  1305. tok = yyLex(returnToken, LEXnone,0);
  1306. if (tok == ',')
  1307. {
  1308. tok = yyLex(returnToken, LEXstring,0);
  1309. if (tok == STRING_CONST)
  1310. {
  1311. StringBuffer text;
  1312. OwnedHqlExpr str = returnToken.getExpr();
  1313. getStringValue(text, str);
  1314. pattern.set(text.str());
  1315. tok = yyLex(returnToken, LEXnone,0);
  1316. }
  1317. else
  1318. reportError(returnToken, ERR_EXPECTED, "string expected");
  1319. }
  1320. declareUniqueName(str(name), pattern);
  1321. }
  1322. if (tok != ')')
  1323. reportError(returnToken, ERR_EXPECTED_RIGHTCURLY, ") expected");
  1324. }
  1325. static int gUniqueId = 0;
  1326. void resetLexerUniqueNames() { gUniqueId = 0; }
  1327. void HqlLex::declareUniqueName(const char *name, const char * pattern)
  1328. {
  1329. IXmlScope *top = ensureTopXmlScope();
  1330. StringBuffer value;
  1331. if (!top->getValue(name,value))
  1332. top->declareValue(name);
  1333. StringBuffer uniqueName;
  1334. bool added = false;
  1335. for (const char * cur = pattern; *cur; cur++)
  1336. {
  1337. char next = *cur;
  1338. switch (next)
  1339. {
  1340. case '#':
  1341. uniqueName.append(name);
  1342. break;
  1343. case '$':
  1344. uniqueName.append(++gUniqueId);
  1345. added = true;
  1346. break;
  1347. default:
  1348. uniqueName.append(next);
  1349. break;
  1350. }
  1351. }
  1352. if (!added)
  1353. uniqueName.append(++gUniqueId);
  1354. //DBGLOG("Declaring unique name: %s",uniqueName.str());
  1355. top->setValue(name,uniqueName.str());
  1356. }
  1357. void HqlLex::doIsValid(attribute & returnToken)
  1358. {
  1359. int tok = yyLex(returnToken, LEXnone, 0);
  1360. if (tok != '(')
  1361. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "( expected");
  1362. StringBuffer curParam("(");
  1363. if (getParameter(curParam,"#ISVALID"))
  1364. {
  1365. reportError(returnToken, ERR_OPERANDS_TOOMANY, "too many operands");
  1366. StringBuffer dummy;
  1367. while (getParameter(dummy,"#ISVALID"))
  1368. ;
  1369. }
  1370. curParam.append(')');
  1371. IHqlExpression * expr = NULL;
  1372. IHqlScope *scope = createScope();
  1373. try
  1374. {
  1375. HqlLookupContext ctx(yyParser->lookupCtx);
  1376. ctx.errs.clear(); //Deliberately ignore any errors
  1377. Owned<IFileContents> contents = createFileContentsFromText(curParam.str(), sourcePath, yyParser->inSignedModule, yyParser->gpgSignature, 0);
  1378. expr = parseQuery(scope, contents, ctx, xmlScope, NULL, true, false);
  1379. if(expr)
  1380. {
  1381. pushText("true");
  1382. }
  1383. else
  1384. {
  1385. pushText("false");
  1386. }
  1387. }
  1388. catch (...)
  1389. {
  1390. pushText("false");
  1391. IERRLOG("Unexpected exception in doIsValid()");
  1392. }
  1393. ::Release(expr);
  1394. ::Release(closeScope(scope));
  1395. }
  1396. void HqlLex::checkNextLoop(const attribute & errpos, bool first, int startLine, int startCol)
  1397. {
  1398. if (yyParser->checkAborting())
  1399. return;
  1400. if (loopTimes++ > MAX_LOOP_TIMES)
  1401. {
  1402. reportError(errpos, ERR_TMPLT_LOOPEXCESSMAX,"The loop exceeded the limit: infinite loop is suspected");
  1403. return;
  1404. }
  1405. //printf("%d\r",loopTimes);
  1406. //assertex(forLoop);
  1407. while (first || forLoop->next())
  1408. {
  1409. bool filtered;
  1410. IXmlScope *subscope = (IXmlScope *) &forLoop->query();
  1411. if (forFilter)
  1412. {
  1413. #ifdef TIMING_DEBUG
  1414. MTIME_SECTION(timer, "HqlLex::checkNextLoopcond");
  1415. #endif
  1416. Owned<IValue> value = parseConstExpression(errpos, forFilter, subscope,startLine,startCol);
  1417. filtered = !value || !value->getBoolValue();
  1418. }
  1419. else
  1420. filtered = false;
  1421. if (!filtered)
  1422. {
  1423. pushText(forBody,startLine,startCol);
  1424. inmacro->xmlScope = LINK(subscope);
  1425. return;
  1426. }
  1427. first = false;
  1428. }
  1429. forLoop->Release();
  1430. forLoop = NULL;
  1431. }
  1432. void HqlLex::doPreprocessorLookup(const attribute & errpos, bool stringify, int extra)
  1433. {
  1434. StringBuffer out;
  1435. char *text = get_yyText() + 1;
  1436. unsigned len = (size32_t)strlen(text) - 1;
  1437. text += extra;
  1438. len -= (extra+extra);
  1439. StringBuffer in;
  1440. in.append(len, text);
  1441. bool matched = lookupXmlSymbol(errpos, in.str(), out);
  1442. if (stringify)
  1443. {
  1444. char *expanded = (char *) malloc(out.length()*2 + 3); // maximum it could be (might be a bit big for alloca)
  1445. char *s = expanded;
  1446. *s++='\'';
  1447. const char *finger = out.str();
  1448. for (;;)
  1449. {
  1450. char c = *finger++;
  1451. if (!c)
  1452. break;
  1453. switch(c)
  1454. {
  1455. case '\r':
  1456. *s++='\\'; *s++ ='r';
  1457. break;
  1458. case '\n':
  1459. *s++='\\'; *s++ ='n';
  1460. break;
  1461. case '\\':
  1462. case '\'':
  1463. *s++='\\';
  1464. // fallthrough
  1465. default:
  1466. *s++=c;
  1467. }
  1468. }
  1469. *s++ = '\'';
  1470. *s = '\0';
  1471. pushText(expanded);
  1472. free(expanded);
  1473. }
  1474. else
  1475. {
  1476. // a space is needed sometimes, e.g, #IF(true or %x%=2)
  1477. out.trim();
  1478. if (out.length())
  1479. {
  1480. out.insert(0," ");
  1481. pushText(out.str());
  1482. }
  1483. else
  1484. {
  1485. //Don't report errors accessing attributes, but do complain about missing symbols
  1486. if (!matched && (*text != '@'))
  1487. reportError(errpos, WRN_UNRESOLVED_SYMBOL, "Symbol %%%s not resolved", text);
  1488. pushText(" 0");
  1489. }
  1490. }
  1491. }
  1492. //Read the text of a parameter, but also have a good guess at whether it is defined.
  1493. bool HqlLex::getDefinedParameter(StringBuffer &curParam, attribute & returnToken, const char* for_what, SharedHqlExpr & resolved)
  1494. {
  1495. enum { StateStart, StateDot, StateSelectId, StateFailed } state = StateStart;
  1496. unsigned parenDepth = 1;
  1497. OwnedHqlExpr expr;
  1498. for (;;)
  1499. {
  1500. int tok = yyLex(returnToken, LEXidentifier|LEXexpand, 0);
  1501. switch(tok)
  1502. {
  1503. case '(':
  1504. case '[':
  1505. parenDepth++;
  1506. break;
  1507. case ')':
  1508. if (parenDepth-- == 1)
  1509. {
  1510. if (state == StateDot)
  1511. resolved.setown(expr.getClear());
  1512. return false;
  1513. }
  1514. break;
  1515. case ']':
  1516. parenDepth--;
  1517. break;
  1518. case ',':
  1519. if (parenDepth==1)
  1520. {
  1521. if (state == StateDot)
  1522. resolved.setown(expr.getClear());
  1523. return true;
  1524. }
  1525. break;
  1526. case EOF:
  1527. {
  1528. StringBuffer msg("EOF encountered while gathering parameters for ");
  1529. msg.append(for_what);
  1530. reportError(returnToken, ERR_TMPLT_EOFINPARAM, "%s", msg.str());
  1531. }
  1532. return false;
  1533. case UNKNOWN_ID:
  1534. if (parenDepth == 1)
  1535. {
  1536. switch (state)
  1537. {
  1538. case StateStart:
  1539. {
  1540. expr.setown(lookupSymbol(returnToken.getId(), returnToken));
  1541. state = expr ? StateDot : StateFailed;
  1542. break;
  1543. }
  1544. case StateDot:
  1545. {
  1546. state = StateFailed;
  1547. break;
  1548. }
  1549. case StateSelectId:
  1550. {
  1551. state = StateFailed;
  1552. if (expr->getOperator() == no_funcdef)
  1553. expr.set(expr->queryChild(0));
  1554. IHqlScope * scope = expr->queryScope();
  1555. if (scope)
  1556. {
  1557. expr.setown(yyParser->lookupSymbol(scope, returnToken.getId()));
  1558. if (expr)
  1559. state = StateDot;
  1560. }
  1561. break;
  1562. }
  1563. }
  1564. }
  1565. curParam.append(' ');
  1566. break;
  1567. case '.':
  1568. if (parenDepth == 1)
  1569. {
  1570. if (state == StateDot)
  1571. state = StateSelectId;
  1572. else
  1573. state = StateFailed;
  1574. }
  1575. break;
  1576. default:
  1577. curParam.append(' ');
  1578. break;
  1579. }
  1580. getTokenText(curParam);
  1581. returnToken.release();
  1582. }
  1583. }
  1584. bool HqlLex::doIsDefined(attribute & returnToken)
  1585. {
  1586. StringBuffer forwhat;
  1587. forwhat.appendf("#ISDEFINED(%d,%d)",returnToken.pos.lineno,returnToken.pos.column);
  1588. if (!assertNextOpenBra())
  1589. return false;
  1590. OwnedHqlExpr resolved;
  1591. StringBuffer paramText;
  1592. bool hasMore = getDefinedParameter(paramText, returnToken, forwhat.str(), resolved);
  1593. if (hasMore)
  1594. reportError(returnToken, ERR_EXPECTED, "Expected ')'");
  1595. return resolved != NULL;
  1596. }
  1597. void HqlLex::doDefined(attribute & returnToken)
  1598. {
  1599. StringBuffer forwhat;
  1600. forwhat.appendf("#DEFINED(%d,%d)",returnToken.pos.lineno,returnToken.pos.column);
  1601. if (!assertNextOpenBra())
  1602. return;
  1603. OwnedHqlExpr resolved;
  1604. StringBuffer param1Text;
  1605. StringBuffer param2Text;
  1606. bool hasMore = getDefinedParameter(param1Text, returnToken, forwhat.str(), resolved);
  1607. if (hasMore)
  1608. hasMore = getParameter(param2Text, forwhat.str());
  1609. if (hasMore)
  1610. reportError(returnToken, ERR_EXPECTED, "Expected ')'");
  1611. if (resolved)
  1612. pushText(param1Text);
  1613. else if (param2Text.length())
  1614. pushText(param2Text);
  1615. }
  1616. IHqlExpression *HqlLex::parseECL(IFileContents * contents, IXmlScope *xmlScope, int startLine, int startCol)
  1617. {
  1618. #ifdef TIMING_DEBUG
  1619. MTIME_SECTION(timer, "HqlLex::parseConstExpression");
  1620. #endif
  1621. Owned<IHqlScope> scope = createScope();
  1622. HqlGramCtx parentContext(yyParser->lookupCtx, yyParser->inSignedModule);
  1623. yyParser->saveContext(parentContext, false);
  1624. HqlGram parser(parentContext, scope, contents, xmlScope, true);
  1625. parser.getLexer()->set_yyLineNo(startLine);
  1626. parser.getLexer()->set_yyColumn(startCol);
  1627. return parser.yyParse(false, false);
  1628. }
  1629. IHqlExpression *HqlLex::parseECL(const char * text, IXmlScope *xmlScope, int startLine, int startCol)
  1630. {
  1631. Owned<IFileContents> contents = createFileContentsFromText(text, querySourcePath(), yyParser->inSignedModule, yyParser->gpgSignature, 0);
  1632. return parseECL(contents, xmlScope, startLine, startCol);
  1633. }
  1634. IValue *HqlLex::foldConstExpression(const attribute & errpos, IHqlExpression * expr, IXmlScope *xmlScope, int startLine, int startCol)
  1635. {
  1636. OwnedIValue value;
  1637. if (expr)
  1638. {
  1639. try
  1640. {
  1641. OwnedHqlExpr folded = foldHqlExpression(*yyParser, expr, HFOthrowerror|HFOfoldimpure|HFOforcefold);
  1642. if (folded)
  1643. {
  1644. if (folded->queryValue())
  1645. value.set(folded->queryValue());
  1646. }
  1647. }
  1648. catch (IException* except)
  1649. {
  1650. StringBuffer s;
  1651. reportError(errpos, except->errorCode(), "%s", except->errorMessage(s).str());
  1652. except->Release();
  1653. }
  1654. }
  1655. if (!value.get())
  1656. {
  1657. if (yyParser->lookupCtx.syntaxChecking() && yyParser->lookupCtx.hasCacheLocation())
  1658. {
  1659. reportError(errpos, ERR_EXPECTED_CONST, "Unable to expand constant expression when using cache. Try disabling cache."); // errpos could be better
  1660. }
  1661. else
  1662. {
  1663. reportError(errpos, ERR_EXPECTED_CONST, "Constant expression expected"); // errpos could be better
  1664. }
  1665. }
  1666. return value.getClear();
  1667. }
  1668. IValue *HqlLex::parseConstExpression(const attribute & errpos, StringBuffer &curParam, IXmlScope *xmlScope, int startLine, int startCol)
  1669. {
  1670. #ifdef TIMING_DEBUG
  1671. MTIME_SECTION(timer, "HqlLex::parseConstExpression");
  1672. #endif
  1673. OwnedHqlExpr expr = parseECL(curParam, xmlScope, startLine, startCol);
  1674. return foldConstExpression(errpos, expr, xmlScope, startLine, startCol);
  1675. }
  1676. IValue *HqlLex::parseConstExpression(const attribute & errpos, IFileContents * text, IXmlScope *xmlScope, int startLine, int startCol)
  1677. {
  1678. #ifdef TIMING_DEBUG
  1679. MTIME_SECTION(timer, "HqlLex::parseConstExpression");
  1680. #endif
  1681. OwnedHqlExpr expr = parseECL(text, xmlScope, startLine, startCol);
  1682. return foldConstExpression(errpos, expr, xmlScope, startLine, startCol);
  1683. }
  1684. int hexchar(char c)
  1685. {
  1686. if (c >= 'A' && c <= 'F')
  1687. return c - 'A' + 10;
  1688. else if (c >= 'a' && c <= 'f')
  1689. return c - 'a' + 10;
  1690. else
  1691. return c - '0';
  1692. }
  1693. void HqlLex::doApply(attribute & returnToken)
  1694. {
  1695. int tok = yyLex(returnToken, LEXnone, 0);
  1696. if (tok != '(')
  1697. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "( expected"); // MORE - make it fatal!
  1698. int line = returnToken.pos.lineno, col = returnToken.pos.column;
  1699. StringBuffer curParam("(");
  1700. if (getParameter(curParam, "#APPLY"))
  1701. {
  1702. reportError(returnToken, ERR_OPERANDS_TOOMANY, "too many operands");
  1703. StringBuffer dummy;
  1704. while (getParameter(dummy, "#APPLY"))
  1705. ;
  1706. }
  1707. curParam.append(')');
  1708. OwnedHqlExpr actions = parseECL(curParam, queryTopXmlScope(), line, col);
  1709. if (actions)
  1710. {
  1711. OwnedHqlExpr folded = foldHqlExpression(*yyParser, actions, HFOthrowerror|HFOfoldimpure|HFOforcefold);
  1712. }
  1713. else
  1714. reportError(returnToken, ERR_EXPECTED_CONST, "Constant expression expected");
  1715. }
  1716. void HqlLex::doMangle(attribute & returnToken, bool de)
  1717. {
  1718. int tok = yyLex(returnToken, LEXnone, 0);
  1719. if (tok != '(')
  1720. reportError(returnToken, ERR_EXPECTED_LEFTCURLY, "( expected"); // MORE - make it fatal!
  1721. int line = returnToken.pos.lineno, col = returnToken.pos.column;
  1722. StringBuffer curParam("(");
  1723. if (getParameter(curParam, de?"#DEMANGLE":"#MANGLE"))
  1724. {
  1725. reportError(returnToken, ERR_OPERANDS_TOOMANY, "too many operands");
  1726. StringBuffer dummy;
  1727. while (getParameter(dummy, de?"#DEMANGLE":"MANGLE"))
  1728. ;
  1729. }
  1730. curParam.append(')');
  1731. IValue *value = parseConstExpression(returnToken, curParam, queryTopXmlScope(), line, col);
  1732. if (value)
  1733. {
  1734. const char *str = value->getStringValue(curParam.clear());
  1735. value->Release();
  1736. StringBuffer mangled;
  1737. mangle(yyParser->errorHandler,str,mangled,de);
  1738. pushText(mangled.str());
  1739. }
  1740. else
  1741. reportError(returnToken, ERR_EXPECTED_CONST, "Constant expression expected");
  1742. }
  1743. static StringBuffer& mangle(IErrorReceiver* errReceiver,const char* src, StringBuffer& mangled,bool de)
  1744. {
  1745. mangled.append("'");
  1746. for (const char *finger = src; *finger!=0; finger++)
  1747. {
  1748. unsigned char c = *finger;
  1749. if (isalnum(c))
  1750. {
  1751. if (finger == src && isdigit(c)) // a leading digit
  1752. {
  1753. if (de)
  1754. {
  1755. //errReceiver->reportError(returnToken, ERR_EXPECTED_CONST, "Bad parameter to #DEMANGLE", "CppTemplate");
  1756. IERRLOG("Bad parameter to #DEMANGLE");
  1757. break;
  1758. }
  1759. else
  1760. mangled.appendf("_%02x",(int)c);
  1761. }
  1762. else
  1763. mangled.append(c);
  1764. }
  1765. else if (de)
  1766. {
  1767. if (c != '_')
  1768. {
  1769. //errReceiver->reportError(returnToken, ERR_EXPECTED_CONST, "Bad parameter to #DEMANGLE");
  1770. IERRLOG("Bad parameter to #DEMANGLE");
  1771. break;
  1772. }
  1773. c = hexchar(finger[1])*16 + hexchar(finger[2]);
  1774. finger += 2;
  1775. if (c=='\'')
  1776. mangled.append('\\');
  1777. mangled.append(c);
  1778. }
  1779. else
  1780. mangled.appendf("_%02x", (int) c);
  1781. }
  1782. mangled.append('\'');
  1783. return mangled;
  1784. }
  1785. int HqlLex::processStringLiteral(attribute & returnToken, char *CUR_TOKEN_TEXT, unsigned CUR_TOKEN_LENGTH, int oldColumn, int oldPosition)
  1786. {
  1787. MemoryAttr tempBuff;
  1788. char *b = (char *)tempBuff.allocate(CUR_TOKEN_LENGTH); // Escape sequence can only make is shorter...
  1789. char *bf = b;
  1790. const char *finger = CUR_TOKEN_TEXT;
  1791. type_t tc = type_string;
  1792. if (*finger != '\'')
  1793. {
  1794. if ((*finger == 'd') || (*finger == 'D'))
  1795. tc = type_data;
  1796. else if((*finger == 'q') || (*finger == 'Q'))
  1797. tc = type_qstring;
  1798. else if((*finger == 'v') || (*finger == 'V'))
  1799. tc = type_varstring;
  1800. finger++;
  1801. }
  1802. bool isMultiline = false;
  1803. if (finger[1]=='\'' && finger[2]=='\'')
  1804. {
  1805. isMultiline = true;
  1806. CUR_TOKEN_TEXT[CUR_TOKEN_LENGTH-2] = '\0';
  1807. finger += 2;
  1808. }
  1809. for (finger++; finger[1]; finger++)
  1810. {
  1811. unsigned char next = *finger;
  1812. size32_t delta = (size32_t)(finger-CUR_TOKEN_TEXT);
  1813. if (next == '\\')
  1814. {
  1815. next = finger[1];
  1816. if (finger[2]==0) // finger[1] must be '.
  1817. {
  1818. returnToken.setPosition(yyLineNo, oldColumn+delta, oldPosition+delta, querySourcePath());
  1819. StringBuffer msg("Can not terminate a string with escape char '\\': ");
  1820. msg.append(CUR_TOKEN_TEXT);
  1821. reportError(returnToken, RRR_ESCAPE_ENDWITHSLASH, "%s", msg.str());
  1822. if (checkAborting())
  1823. return EOF;
  1824. }
  1825. else if (next == '\'' || next == '\\' || next == '?' || next == '"')
  1826. {
  1827. finger++;
  1828. }
  1829. else if (next == '\n')
  1830. {
  1831. finger++;
  1832. continue; // A \ at end of line in a multiline constant means remove the end-of-line
  1833. }
  1834. else if (next == 'a')
  1835. {
  1836. next = '\a';
  1837. finger++;
  1838. }
  1839. else if (next == 'b')
  1840. {
  1841. next = '\b';
  1842. finger++;
  1843. }
  1844. else if (next == 'f')
  1845. {
  1846. next = '\f';
  1847. finger++;
  1848. }
  1849. else if (next == 'n')
  1850. {
  1851. next = '\n';
  1852. finger++;
  1853. }
  1854. else if (next == 'r')
  1855. {
  1856. next = '\r';
  1857. finger++;
  1858. }
  1859. else if (next == 't')
  1860. {
  1861. next = '\t';
  1862. finger++;
  1863. }
  1864. else if (next == 'v')
  1865. {
  1866. next = '\v';
  1867. finger++;
  1868. }
  1869. else if (isdigit(next) && next < '8')
  1870. {
  1871. //Allow octal constants for ^Z etc.
  1872. unsigned value = 0;
  1873. unsigned count;
  1874. for (count=0; count < 3; count++)
  1875. {
  1876. next = finger[count+1];
  1877. if (!isdigit(next) || next >= '8')
  1878. break;
  1879. value = value * 8 + (next - '0');
  1880. }
  1881. if(count != 3)
  1882. {
  1883. returnToken.setPosition(yyLineNo, oldColumn+delta, oldPosition+delta, querySourcePath());
  1884. StringBuffer msg;
  1885. msg.append("3-digit numeric escape sequence contained non-octal digit: ").append(next);
  1886. reportError(returnToken, ERR_ESCAPE_UNKNOWN, "%s", msg.str());
  1887. if (checkAborting())
  1888. return EOF;
  1889. }
  1890. *bf++ = value;
  1891. if(!(isValidAsciiLikeCharacter(value) || (tc == type_data)))
  1892. {
  1893. returnToken.setPosition(yyLineNo, oldColumn+delta, oldPosition+delta, querySourcePath());
  1894. reportWarning(CategoryCast, returnToken, ERR_STRING_NON_ASCII, "Character in string literal is not defined in encoding " ASCII_LIKE_CODEPAGE);
  1895. if (checkAborting())
  1896. return EOF;
  1897. }
  1898. finger += count;
  1899. continue;
  1900. }
  1901. else
  1902. {
  1903. StringBuffer msg;
  1904. msg.append("Unrecognized escape sequence: ");
  1905. msg.append("\\").append(finger[1]);
  1906. returnToken.setPosition(yyLineNo, oldColumn+delta, oldPosition+delta, querySourcePath());
  1907. reportError(returnToken, ERR_ESCAPE_UNKNOWN, "%s", msg.str());
  1908. if (checkAborting())
  1909. return EOF;
  1910. }
  1911. *bf++ = next;
  1912. }
  1913. else if (next == '\'' && !isMultiline)
  1914. {
  1915. returnToken.setPosition(yyLineNo, oldColumn+delta, oldPosition+delta, querySourcePath());
  1916. reportError(returnToken, ERR_STRING_NEEDESCAPE,"' needs to be escaped by \\ inside string");
  1917. if (checkAborting())
  1918. return EOF;
  1919. }
  1920. else if (next >= 128)
  1921. {
  1922. unsigned lenLeft = CUR_TOKEN_LENGTH - (size32_t)(finger - CUR_TOKEN_TEXT);
  1923. int extraCharsRead = rtlSingleUtf8ToCodepage(bf, lenLeft, finger, ASCII_LIKE_CODEPAGE);
  1924. if (extraCharsRead == -1)
  1925. {
  1926. //This really has to be an error, otherwise it will work most of the time, but will then sometimes fail
  1927. //because two characters > 128 are next to each other.
  1928. returnToken.setPosition(yyLineNo, oldColumn+delta, oldPosition+delta, querySourcePath());
  1929. reportError(returnToken, ERR_STRING_NON_ASCII, "Character in string literal is not legal UTF-8");
  1930. if (checkAborting())
  1931. return EOF;
  1932. *bf = next;
  1933. }
  1934. else
  1935. {
  1936. if (*bf == ASCII_LIKE_SUBS_CHAR)
  1937. {
  1938. returnToken.setPosition(yyLineNo, oldColumn+delta, oldPosition+delta, querySourcePath());
  1939. reportWarning(CategoryCast, returnToken, ERR_STRING_NON_ASCII, "Character in string literal is not defined in encoding " ASCII_LIKE_CODEPAGE ", try using a unicode constant");
  1940. }
  1941. finger += extraCharsRead;
  1942. }
  1943. bf++;
  1944. }
  1945. else
  1946. {
  1947. *bf++ = next;
  1948. if(!(isValidAsciiLikeCharacter(next) || (tc == type_data)))
  1949. {
  1950. returnToken.setPosition(yyLineNo, oldColumn+delta, oldPosition+delta, querySourcePath());
  1951. reportError(returnToken, ERR_STRING_NON_ASCII, "Character in string literal is not defined in encoding " ASCII_LIKE_CODEPAGE);
  1952. if (checkAborting())
  1953. return EOF;
  1954. }
  1955. }
  1956. }
  1957. returnToken.setPosition(yyLineNo, oldColumn, oldPosition, querySourcePath());
  1958. switch (tc)
  1959. {
  1960. case type_qstring:
  1961. {
  1962. Owned<ITypeInfo> qStrType = makeQStringType(UNKNOWN_LENGTH);
  1963. returnToken.setExpr(createConstant(qStrType->castFrom((size32_t)(bf-b), b)));
  1964. return (DATA_CONST);
  1965. }
  1966. case type_data:
  1967. {
  1968. returnToken.setExpr(createConstant(createDataValue(b, (size32_t)(bf-b))));
  1969. return (DATA_CONST);
  1970. }
  1971. case type_varstring:
  1972. {
  1973. returnToken.setExpr(createConstant(createVarStringValue((size32_t)(bf-b), b, makeVarStringType(UNKNOWN_LENGTH))));
  1974. return (DATA_CONST);
  1975. }
  1976. case type_string:
  1977. returnToken.setExpr(createConstant(createStringValue(b, (size32_t)(bf-b))));
  1978. return (STRING_CONST);
  1979. }
  1980. throwUnexpected();
  1981. }
  1982. //====================================== Error Reporting ======================================
  1983. bool HqlLex::checkAborting()
  1984. {
  1985. return yyParser->checkAborting();
  1986. }
  1987. void HqlLex::reportError(const attribute & returnToken, int errNo, const char *format, ...)
  1988. {
  1989. if (yyParser)
  1990. {
  1991. va_list args;
  1992. va_start(args, format);
  1993. yyParser->reportErrorVa(errNo, returnToken.pos, format, args);
  1994. va_end(args);
  1995. }
  1996. }
  1997. void HqlLex::reportWarning(WarnErrorCategory category, const attribute & returnToken, int warnNo, const char *format, ...)
  1998. {
  1999. if (yyParser)
  2000. {
  2001. va_list args;
  2002. va_start(args, format);
  2003. yyParser->reportWarningVa(category, warnNo, returnToken, format, args);
  2004. va_end(args);
  2005. }
  2006. }
  2007. //====================================== XML DB =============================================
  2008. IXmlScope *HqlLex::queryTopXmlScope()
  2009. {
  2010. IXmlScope *top = NULL;
  2011. HqlLex *inlex = this;
  2012. while (inlex->inmacro)
  2013. inlex = inlex->inmacro;
  2014. while (inlex && !top)
  2015. {
  2016. top = inlex->xmlScope;
  2017. inlex = inlex->parentLex;
  2018. }
  2019. return top;
  2020. }
  2021. IXmlScope *HqlLex::ensureTopXmlScope()
  2022. {
  2023. IXmlScope *top = queryTopXmlScope();
  2024. if (!top)
  2025. top = xmlScope = createXMLScope();
  2026. return top;
  2027. }
  2028. bool HqlLex::lookupXmlSymbol(const attribute & errpos, const char *name, StringBuffer &ret)
  2029. {
  2030. if (*name==0)
  2031. name=NULL;
  2032. IXmlScope *top = ensureTopXmlScope();
  2033. bool idFound = top->getValue(name, ret);
  2034. if (idFound)
  2035. return true;
  2036. HqlLex * lexer = parentLex;
  2037. while (lexer && !idFound)
  2038. {
  2039. if (lexer->xmlScope)
  2040. idFound = lexer->xmlScope->getValue(name, ret);
  2041. lexer = lexer->parentLex;
  2042. }
  2043. return idFound;
  2044. }
  2045. void HqlLex::setXmlSymbol(const attribute & errpos, const char *name, const char *value, bool append)
  2046. {
  2047. IXmlScope *top = ensureTopXmlScope();
  2048. bool ok;
  2049. if (append)
  2050. ok = top->appendValue(name, value);
  2051. else
  2052. ok = top->setValue(name, value);
  2053. if (!ok)
  2054. {
  2055. StringBuffer msg("Symbol has not been declared: ");
  2056. msg.append(name);
  2057. reportError(errpos, ERR_TMPLT_SYMBOLNOTDECLARED, "%s", msg.str());
  2058. }
  2059. }
  2060. void HqlLex::declareXmlSymbol(const attribute & errpos, const char *name)
  2061. {
  2062. IXmlScope *top = ensureTopXmlScope();
  2063. if (!top->declareValue(name))
  2064. {
  2065. StringBuffer msg("Symbol has already been declared: ");
  2066. msg.append(name);
  2067. reportError(errpos, ERR_TMPLT_SYMBOLREDECLARE, "%s", msg.str());
  2068. }
  2069. }
  2070. IIterator *HqlLex::getSubScopes(const attribute & errpos, const char *name, bool doAll)
  2071. {
  2072. IXmlScope *top = ensureTopXmlScope();
  2073. return top->getScopes(name, doAll);
  2074. }
  2075. void HqlLex::loadXML(const attribute & errpos, const char *name, const char * child)
  2076. {
  2077. if (xmlScope && child)
  2078. {
  2079. xmlScope->loadXML(name, child);
  2080. return;
  2081. }
  2082. if (false && inmacro)
  2083. {
  2084. inmacro->loadXML(errpos, name);
  2085. return;
  2086. }
  2087. // MORE - give an error if an XML scope is active...
  2088. ::Release(xmlScope);
  2089. try
  2090. {
  2091. xmlScope = ::loadXML(name);
  2092. }
  2093. catch (IException* e)
  2094. {
  2095. e->Release();
  2096. xmlScope = NULL;
  2097. }
  2098. catch (...)
  2099. {
  2100. xmlScope = NULL;
  2101. }
  2102. if (!xmlScope)
  2103. {
  2104. if (name && strlen(name))
  2105. {
  2106. StringBuffer msg;
  2107. msg.appendf("Load XML(\'%s\') failed",name);
  2108. reportError(errpos, ERR_TMPLT_LOADXMLFAILED, "%s", msg.str());
  2109. }
  2110. // recovery: create a default XML scope
  2111. xmlScope = createXMLScope();
  2112. }
  2113. }
  2114. const char * HqlLex::queryMacroScopeName()
  2115. {
  2116. if (inmacro)
  2117. {
  2118. const char * scope = inmacro->queryMacroScopeName();
  2119. if (scope)
  2120. return scope;
  2121. }
  2122. if (hashDollar)
  2123. return str(hashDollar);
  2124. return nullptr;
  2125. }
  2126. IPropertyTree * HqlLex::getClearJavadoc()
  2127. {
  2128. if (javaDocComment.length() == 0)
  2129. return NULL;
  2130. IPropertyTree * tree = createPTree("javadoc");
  2131. tree->addProp("content", javaDocComment.str());
  2132. javaDocComment.clear();
  2133. return tree;
  2134. }
  2135. unsigned HqlLex::getTypeSize(unsigned lengthTypeName)
  2136. {
  2137. const char * tok = get_yyText();
  2138. if (strlen(tok)> lengthTypeName)
  2139. return atoi(tok + lengthTypeName);
  2140. return UNKNOWN_LENGTH;
  2141. }
  2142. void HqlLex::enterEmbeddedMode()
  2143. {
  2144. if (inmacro)
  2145. inmacro->enterEmbeddedMode();
  2146. else
  2147. {
  2148. doEnterEmbeddedMode(scanner);
  2149. inCpp = true;
  2150. }
  2151. }
  2152. int HqlLex::yyLex(attribute & returnToken, LexerFlags lookupFlags, const short * activeState)
  2153. {
  2154. for (;;)
  2155. {
  2156. while (inmacro)
  2157. {
  2158. int ret = inmacro->yyLex(returnToken, lookupFlags, activeState);
  2159. if (ret > 0 && ret != HASHBREAK)
  2160. {
  2161. lastToken = ret;
  2162. return ret;
  2163. }
  2164. #if defined(TRACE_MACRO)
  2165. DBGLOG("MACRO>> inmacro %p deleted\n", inmacro);
  2166. #endif
  2167. delete inmacro;
  2168. inmacro = NULL;
  2169. if (ret == HASHBREAK)
  2170. {
  2171. if (forLoop)
  2172. {
  2173. forLoop->Release();
  2174. forLoop = NULL;
  2175. }
  2176. else
  2177. {
  2178. lastToken = ret;
  2179. return ret;
  2180. }
  2181. }
  2182. if (forLoop)
  2183. checkNextLoop(returnToken, false,0,0);
  2184. }
  2185. returnToken.clear();
  2186. yyStartPos = yyPosition;
  2187. int ret = doyyFlex(returnToken, scanner, this, lookupFlags, activeState);
  2188. if (ret == 0) ret = EOF;
  2189. if (ret == INTERNAL_READ_NEXT_TOKEN)
  2190. continue;
  2191. if (ret == EOF)
  2192. {
  2193. setTokenPosition(returnToken);
  2194. if (inComment)
  2195. reportError(returnToken, ERR_COMMENT_UNENDED,"Comment is not terminated");
  2196. else if (inSignature)
  2197. reportError(returnToken, ERR_COMMENT_UNENDED,"Signature is not terminated");
  2198. else if (inCpp)
  2199. reportError(returnToken, ERR_COMMENT_UNENDED,"BEGINC++ or EMBED is not terminated");
  2200. else if (inMultiString)
  2201. reportError(returnToken, ERR_COMMENT_UNENDED,"Multiline string constant is not terminated");
  2202. if (hashendKinds.ordinality())
  2203. {
  2204. StringBuffer msg("Unexpected EOF: ");
  2205. msg.append(hashendKinds.ordinality()).append(" more #END needed");
  2206. reportError(returnToken, ERR_TMPLT_HASHENDEXPECTED, "%s", msg.str());
  2207. clearNestedHash();
  2208. }
  2209. }
  2210. lastToken = ret;
  2211. return ret;
  2212. }
  2213. }