Software zum Installieren eines Smart-Mirror Frameworks , zum Nutzen von hochschulrelevanten Informationen, auf einem Raspberry-Pi.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

parser.js 35KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267
  1. 'use strict';
  2. function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); }
  3. function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
  4. var lexer = require('./lexer');
  5. var nodes = require('./nodes');
  6. var Obj = require('./object').Obj;
  7. var lib = require('./lib');
  8. var Parser = /*#__PURE__*/function (_Obj) {
  9. _inheritsLoose(Parser, _Obj);
  10. function Parser() {
  11. return _Obj.apply(this, arguments) || this;
  12. }
  13. var _proto = Parser.prototype;
  14. _proto.init = function init(tokens) {
  15. this.tokens = tokens;
  16. this.peeked = null;
  17. this.breakOnBlocks = null;
  18. this.dropLeadingWhitespace = false;
  19. this.extensions = [];
  20. };
  21. _proto.nextToken = function nextToken(withWhitespace) {
  22. var tok;
  23. if (this.peeked) {
  24. if (!withWhitespace && this.peeked.type === lexer.TOKEN_WHITESPACE) {
  25. this.peeked = null;
  26. } else {
  27. tok = this.peeked;
  28. this.peeked = null;
  29. return tok;
  30. }
  31. }
  32. tok = this.tokens.nextToken();
  33. if (!withWhitespace) {
  34. while (tok && tok.type === lexer.TOKEN_WHITESPACE) {
  35. tok = this.tokens.nextToken();
  36. }
  37. }
  38. return tok;
  39. };
  40. _proto.peekToken = function peekToken() {
  41. this.peeked = this.peeked || this.nextToken();
  42. return this.peeked;
  43. };
  44. _proto.pushToken = function pushToken(tok) {
  45. if (this.peeked) {
  46. throw new Error('pushToken: can only push one token on between reads');
  47. }
  48. this.peeked = tok;
  49. };
  50. _proto.error = function error(msg, lineno, colno) {
  51. if (lineno === undefined || colno === undefined) {
  52. var tok = this.peekToken() || {};
  53. lineno = tok.lineno;
  54. colno = tok.colno;
  55. }
  56. if (lineno !== undefined) {
  57. lineno += 1;
  58. }
  59. if (colno !== undefined) {
  60. colno += 1;
  61. }
  62. return new lib.TemplateError(msg, lineno, colno);
  63. };
  64. _proto.fail = function fail(msg, lineno, colno) {
  65. throw this.error(msg, lineno, colno);
  66. };
  67. _proto.skip = function skip(type) {
  68. var tok = this.nextToken();
  69. if (!tok || tok.type !== type) {
  70. this.pushToken(tok);
  71. return false;
  72. }
  73. return true;
  74. };
  75. _proto.expect = function expect(type) {
  76. var tok = this.nextToken();
  77. if (tok.type !== type) {
  78. this.fail('expected ' + type + ', got ' + tok.type, tok.lineno, tok.colno);
  79. }
  80. return tok;
  81. };
  82. _proto.skipValue = function skipValue(type, val) {
  83. var tok = this.nextToken();
  84. if (!tok || tok.type !== type || tok.value !== val) {
  85. this.pushToken(tok);
  86. return false;
  87. }
  88. return true;
  89. };
  90. _proto.skipSymbol = function skipSymbol(val) {
  91. return this.skipValue(lexer.TOKEN_SYMBOL, val);
  92. };
  93. _proto.advanceAfterBlockEnd = function advanceAfterBlockEnd(name) {
  94. var tok;
  95. if (!name) {
  96. tok = this.peekToken();
  97. if (!tok) {
  98. this.fail('unexpected end of file');
  99. }
  100. if (tok.type !== lexer.TOKEN_SYMBOL) {
  101. this.fail('advanceAfterBlockEnd: expected symbol token or ' + 'explicit name to be passed');
  102. }
  103. name = this.nextToken().value;
  104. }
  105. tok = this.nextToken();
  106. if (tok && tok.type === lexer.TOKEN_BLOCK_END) {
  107. if (tok.value.charAt(0) === '-') {
  108. this.dropLeadingWhitespace = true;
  109. }
  110. } else {
  111. this.fail('expected block end in ' + name + ' statement');
  112. }
  113. return tok;
  114. };
  115. _proto.advanceAfterVariableEnd = function advanceAfterVariableEnd() {
  116. var tok = this.nextToken();
  117. if (tok && tok.type === lexer.TOKEN_VARIABLE_END) {
  118. this.dropLeadingWhitespace = tok.value.charAt(tok.value.length - this.tokens.tags.VARIABLE_END.length - 1) === '-';
  119. } else {
  120. this.pushToken(tok);
  121. this.fail('expected variable end');
  122. }
  123. };
  124. _proto.parseFor = function parseFor() {
  125. var forTok = this.peekToken();
  126. var node;
  127. var endBlock;
  128. if (this.skipSymbol('for')) {
  129. node = new nodes.For(forTok.lineno, forTok.colno);
  130. endBlock = 'endfor';
  131. } else if (this.skipSymbol('asyncEach')) {
  132. node = new nodes.AsyncEach(forTok.lineno, forTok.colno);
  133. endBlock = 'endeach';
  134. } else if (this.skipSymbol('asyncAll')) {
  135. node = new nodes.AsyncAll(forTok.lineno, forTok.colno);
  136. endBlock = 'endall';
  137. } else {
  138. this.fail('parseFor: expected for{Async}', forTok.lineno, forTok.colno);
  139. }
  140. node.name = this.parsePrimary();
  141. if (!(node.name instanceof nodes.Symbol)) {
  142. this.fail('parseFor: variable name expected for loop');
  143. }
  144. var type = this.peekToken().type;
  145. if (type === lexer.TOKEN_COMMA) {
  146. // key/value iteration
  147. var key = node.name;
  148. node.name = new nodes.Array(key.lineno, key.colno);
  149. node.name.addChild(key);
  150. while (this.skip(lexer.TOKEN_COMMA)) {
  151. var prim = this.parsePrimary();
  152. node.name.addChild(prim);
  153. }
  154. }
  155. if (!this.skipSymbol('in')) {
  156. this.fail('parseFor: expected "in" keyword for loop', forTok.lineno, forTok.colno);
  157. }
  158. node.arr = this.parseExpression();
  159. this.advanceAfterBlockEnd(forTok.value);
  160. node.body = this.parseUntilBlocks(endBlock, 'else');
  161. if (this.skipSymbol('else')) {
  162. this.advanceAfterBlockEnd('else');
  163. node.else_ = this.parseUntilBlocks(endBlock);
  164. }
  165. this.advanceAfterBlockEnd();
  166. return node;
  167. };
  168. _proto.parseMacro = function parseMacro() {
  169. var macroTok = this.peekToken();
  170. if (!this.skipSymbol('macro')) {
  171. this.fail('expected macro');
  172. }
  173. var name = this.parsePrimary(true);
  174. var args = this.parseSignature();
  175. var node = new nodes.Macro(macroTok.lineno, macroTok.colno, name, args);
  176. this.advanceAfterBlockEnd(macroTok.value);
  177. node.body = this.parseUntilBlocks('endmacro');
  178. this.advanceAfterBlockEnd();
  179. return node;
  180. };
  181. _proto.parseCall = function parseCall() {
  182. // a call block is parsed as a normal FunCall, but with an added
  183. // 'caller' kwarg which is a Caller node.
  184. var callTok = this.peekToken();
  185. if (!this.skipSymbol('call')) {
  186. this.fail('expected call');
  187. }
  188. var callerArgs = this.parseSignature(true) || new nodes.NodeList();
  189. var macroCall = this.parsePrimary();
  190. this.advanceAfterBlockEnd(callTok.value);
  191. var body = this.parseUntilBlocks('endcall');
  192. this.advanceAfterBlockEnd();
  193. var callerName = new nodes.Symbol(callTok.lineno, callTok.colno, 'caller');
  194. var callerNode = new nodes.Caller(callTok.lineno, callTok.colno, callerName, callerArgs, body); // add the additional caller kwarg, adding kwargs if necessary
  195. var args = macroCall.args.children;
  196. if (!(args[args.length - 1] instanceof nodes.KeywordArgs)) {
  197. args.push(new nodes.KeywordArgs());
  198. }
  199. var kwargs = args[args.length - 1];
  200. kwargs.addChild(new nodes.Pair(callTok.lineno, callTok.colno, callerName, callerNode));
  201. return new nodes.Output(callTok.lineno, callTok.colno, [macroCall]);
  202. };
  203. _proto.parseWithContext = function parseWithContext() {
  204. var tok = this.peekToken();
  205. var withContext = null;
  206. if (this.skipSymbol('with')) {
  207. withContext = true;
  208. } else if (this.skipSymbol('without')) {
  209. withContext = false;
  210. }
  211. if (withContext !== null) {
  212. if (!this.skipSymbol('context')) {
  213. this.fail('parseFrom: expected context after with/without', tok.lineno, tok.colno);
  214. }
  215. }
  216. return withContext;
  217. };
  218. _proto.parseImport = function parseImport() {
  219. var importTok = this.peekToken();
  220. if (!this.skipSymbol('import')) {
  221. this.fail('parseImport: expected import', importTok.lineno, importTok.colno);
  222. }
  223. var template = this.parseExpression();
  224. if (!this.skipSymbol('as')) {
  225. this.fail('parseImport: expected "as" keyword', importTok.lineno, importTok.colno);
  226. }
  227. var target = this.parseExpression();
  228. var withContext = this.parseWithContext();
  229. var node = new nodes.Import(importTok.lineno, importTok.colno, template, target, withContext);
  230. this.advanceAfterBlockEnd(importTok.value);
  231. return node;
  232. };
  233. _proto.parseFrom = function parseFrom() {
  234. var fromTok = this.peekToken();
  235. if (!this.skipSymbol('from')) {
  236. this.fail('parseFrom: expected from');
  237. }
  238. var template = this.parseExpression();
  239. if (!this.skipSymbol('import')) {
  240. this.fail('parseFrom: expected import', fromTok.lineno, fromTok.colno);
  241. }
  242. var names = new nodes.NodeList();
  243. var withContext;
  244. while (1) {
  245. // eslint-disable-line no-constant-condition
  246. var nextTok = this.peekToken();
  247. if (nextTok.type === lexer.TOKEN_BLOCK_END) {
  248. if (!names.children.length) {
  249. this.fail('parseFrom: Expected at least one import name', fromTok.lineno, fromTok.colno);
  250. } // Since we are manually advancing past the block end,
  251. // need to keep track of whitespace control (normally
  252. // this is done in `advanceAfterBlockEnd`
  253. if (nextTok.value.charAt(0) === '-') {
  254. this.dropLeadingWhitespace = true;
  255. }
  256. this.nextToken();
  257. break;
  258. }
  259. if (names.children.length > 0 && !this.skip(lexer.TOKEN_COMMA)) {
  260. this.fail('parseFrom: expected comma', fromTok.lineno, fromTok.colno);
  261. }
  262. var name = this.parsePrimary();
  263. if (name.value.charAt(0) === '_') {
  264. this.fail('parseFrom: names starting with an underscore cannot be imported', name.lineno, name.colno);
  265. }
  266. if (this.skipSymbol('as')) {
  267. var alias = this.parsePrimary();
  268. names.addChild(new nodes.Pair(name.lineno, name.colno, name, alias));
  269. } else {
  270. names.addChild(name);
  271. }
  272. withContext = this.parseWithContext();
  273. }
  274. return new nodes.FromImport(fromTok.lineno, fromTok.colno, template, names, withContext);
  275. };
  276. _proto.parseBlock = function parseBlock() {
  277. var tag = this.peekToken();
  278. if (!this.skipSymbol('block')) {
  279. this.fail('parseBlock: expected block', tag.lineno, tag.colno);
  280. }
  281. var node = new nodes.Block(tag.lineno, tag.colno);
  282. node.name = this.parsePrimary();
  283. if (!(node.name instanceof nodes.Symbol)) {
  284. this.fail('parseBlock: variable name expected', tag.lineno, tag.colno);
  285. }
  286. this.advanceAfterBlockEnd(tag.value);
  287. node.body = this.parseUntilBlocks('endblock');
  288. this.skipSymbol('endblock');
  289. this.skipSymbol(node.name.value);
  290. var tok = this.peekToken();
  291. if (!tok) {
  292. this.fail('parseBlock: expected endblock, got end of file');
  293. }
  294. this.advanceAfterBlockEnd(tok.value);
  295. return node;
  296. };
  297. _proto.parseExtends = function parseExtends() {
  298. var tagName = 'extends';
  299. var tag = this.peekToken();
  300. if (!this.skipSymbol(tagName)) {
  301. this.fail('parseTemplateRef: expected ' + tagName);
  302. }
  303. var node = new nodes.Extends(tag.lineno, tag.colno);
  304. node.template = this.parseExpression();
  305. this.advanceAfterBlockEnd(tag.value);
  306. return node;
  307. };
  308. _proto.parseInclude = function parseInclude() {
  309. var tagName = 'include';
  310. var tag = this.peekToken();
  311. if (!this.skipSymbol(tagName)) {
  312. this.fail('parseInclude: expected ' + tagName);
  313. }
  314. var node = new nodes.Include(tag.lineno, tag.colno);
  315. node.template = this.parseExpression();
  316. if (this.skipSymbol('ignore') && this.skipSymbol('missing')) {
  317. node.ignoreMissing = true;
  318. }
  319. this.advanceAfterBlockEnd(tag.value);
  320. return node;
  321. };
  322. _proto.parseIf = function parseIf() {
  323. var tag = this.peekToken();
  324. var node;
  325. if (this.skipSymbol('if') || this.skipSymbol('elif') || this.skipSymbol('elseif')) {
  326. node = new nodes.If(tag.lineno, tag.colno);
  327. } else if (this.skipSymbol('ifAsync')) {
  328. node = new nodes.IfAsync(tag.lineno, tag.colno);
  329. } else {
  330. this.fail('parseIf: expected if, elif, or elseif', tag.lineno, tag.colno);
  331. }
  332. node.cond = this.parseExpression();
  333. this.advanceAfterBlockEnd(tag.value);
  334. node.body = this.parseUntilBlocks('elif', 'elseif', 'else', 'endif');
  335. var tok = this.peekToken();
  336. switch (tok && tok.value) {
  337. case 'elseif':
  338. case 'elif':
  339. node.else_ = this.parseIf();
  340. break;
  341. case 'else':
  342. this.advanceAfterBlockEnd();
  343. node.else_ = this.parseUntilBlocks('endif');
  344. this.advanceAfterBlockEnd();
  345. break;
  346. case 'endif':
  347. node.else_ = null;
  348. this.advanceAfterBlockEnd();
  349. break;
  350. default:
  351. this.fail('parseIf: expected elif, else, or endif, got end of file');
  352. }
  353. return node;
  354. };
  355. _proto.parseSet = function parseSet() {
  356. var tag = this.peekToken();
  357. if (!this.skipSymbol('set')) {
  358. this.fail('parseSet: expected set', tag.lineno, tag.colno);
  359. }
  360. var node = new nodes.Set(tag.lineno, tag.colno, []);
  361. var target;
  362. while (target = this.parsePrimary()) {
  363. node.targets.push(target);
  364. if (!this.skip(lexer.TOKEN_COMMA)) {
  365. break;
  366. }
  367. }
  368. if (!this.skipValue(lexer.TOKEN_OPERATOR, '=')) {
  369. if (!this.skip(lexer.TOKEN_BLOCK_END)) {
  370. this.fail('parseSet: expected = or block end in set tag', tag.lineno, tag.colno);
  371. } else {
  372. node.body = new nodes.Capture(tag.lineno, tag.colno, this.parseUntilBlocks('endset'));
  373. node.value = null;
  374. this.advanceAfterBlockEnd();
  375. }
  376. } else {
  377. node.value = this.parseExpression();
  378. this.advanceAfterBlockEnd(tag.value);
  379. }
  380. return node;
  381. };
  382. _proto.parseSwitch = function parseSwitch() {
  383. /*
  384. * Store the tag names in variables in case someone ever wants to
  385. * customize this.
  386. */
  387. var switchStart = 'switch';
  388. var switchEnd = 'endswitch';
  389. var caseStart = 'case';
  390. var caseDefault = 'default'; // Get the switch tag.
  391. var tag = this.peekToken(); // fail early if we get some unexpected tag.
  392. if (!this.skipSymbol(switchStart) && !this.skipSymbol(caseStart) && !this.skipSymbol(caseDefault)) {
  393. this.fail('parseSwitch: expected "switch," "case" or "default"', tag.lineno, tag.colno);
  394. } // parse the switch expression
  395. var expr = this.parseExpression(); // advance until a start of a case, a default case or an endswitch.
  396. this.advanceAfterBlockEnd(switchStart);
  397. this.parseUntilBlocks(caseStart, caseDefault, switchEnd); // this is the first case. it could also be an endswitch, we'll check.
  398. var tok = this.peekToken(); // create new variables for our cases and default case.
  399. var cases = [];
  400. var defaultCase; // while we're dealing with new cases nodes...
  401. do {
  402. // skip the start symbol and get the case expression
  403. this.skipSymbol(caseStart);
  404. var cond = this.parseExpression();
  405. this.advanceAfterBlockEnd(switchStart); // get the body of the case node and add it to the array of cases.
  406. var body = this.parseUntilBlocks(caseStart, caseDefault, switchEnd);
  407. cases.push(new nodes.Case(tok.line, tok.col, cond, body)); // get our next case
  408. tok = this.peekToken();
  409. } while (tok && tok.value === caseStart); // we either have a default case or a switch end.
  410. switch (tok.value) {
  411. case caseDefault:
  412. this.advanceAfterBlockEnd();
  413. defaultCase = this.parseUntilBlocks(switchEnd);
  414. this.advanceAfterBlockEnd();
  415. break;
  416. case switchEnd:
  417. this.advanceAfterBlockEnd();
  418. break;
  419. default:
  420. // otherwise bail because EOF
  421. this.fail('parseSwitch: expected "case," "default" or "endswitch," got EOF.');
  422. } // and return the switch node.
  423. return new nodes.Switch(tag.lineno, tag.colno, expr, cases, defaultCase);
  424. };
  425. _proto.parseStatement = function parseStatement() {
  426. var tok = this.peekToken();
  427. var node;
  428. if (tok.type !== lexer.TOKEN_SYMBOL) {
  429. this.fail('tag name expected', tok.lineno, tok.colno);
  430. }
  431. if (this.breakOnBlocks && lib.indexOf(this.breakOnBlocks, tok.value) !== -1) {
  432. return null;
  433. }
  434. switch (tok.value) {
  435. case 'raw':
  436. return this.parseRaw();
  437. case 'verbatim':
  438. return this.parseRaw('verbatim');
  439. case 'if':
  440. case 'ifAsync':
  441. return this.parseIf();
  442. case 'for':
  443. case 'asyncEach':
  444. case 'asyncAll':
  445. return this.parseFor();
  446. case 'block':
  447. return this.parseBlock();
  448. case 'extends':
  449. return this.parseExtends();
  450. case 'include':
  451. return this.parseInclude();
  452. case 'set':
  453. return this.parseSet();
  454. case 'macro':
  455. return this.parseMacro();
  456. case 'call':
  457. return this.parseCall();
  458. case 'import':
  459. return this.parseImport();
  460. case 'from':
  461. return this.parseFrom();
  462. case 'filter':
  463. return this.parseFilterStatement();
  464. case 'switch':
  465. return this.parseSwitch();
  466. default:
  467. if (this.extensions.length) {
  468. for (var i = 0; i < this.extensions.length; i++) {
  469. var ext = this.extensions[i];
  470. if (lib.indexOf(ext.tags || [], tok.value) !== -1) {
  471. return ext.parse(this, nodes, lexer);
  472. }
  473. }
  474. }
  475. this.fail('unknown block tag: ' + tok.value, tok.lineno, tok.colno);
  476. }
  477. return node;
  478. };
  479. _proto.parseRaw = function parseRaw(tagName) {
  480. tagName = tagName || 'raw';
  481. var endTagName = 'end' + tagName; // Look for upcoming raw blocks (ignore all other kinds of blocks)
  482. var rawBlockRegex = new RegExp('([\\s\\S]*?){%\\s*(' + tagName + '|' + endTagName + ')\\s*(?=%})%}');
  483. var rawLevel = 1;
  484. var str = '';
  485. var matches = null; // Skip opening raw token
  486. // Keep this token to track line and column numbers
  487. var begun = this.advanceAfterBlockEnd(); // Exit when there's nothing to match
  488. // or when we've found the matching "endraw" block
  489. while ((matches = this.tokens._extractRegex(rawBlockRegex)) && rawLevel > 0) {
  490. var all = matches[0];
  491. var pre = matches[1];
  492. var blockName = matches[2]; // Adjust rawlevel
  493. if (blockName === tagName) {
  494. rawLevel += 1;
  495. } else if (blockName === endTagName) {
  496. rawLevel -= 1;
  497. } // Add to str
  498. if (rawLevel === 0) {
  499. // We want to exclude the last "endraw"
  500. str += pre; // Move tokenizer to beginning of endraw block
  501. this.tokens.backN(all.length - pre.length);
  502. } else {
  503. str += all;
  504. }
  505. }
  506. return new nodes.Output(begun.lineno, begun.colno, [new nodes.TemplateData(begun.lineno, begun.colno, str)]);
  507. };
  508. _proto.parsePostfix = function parsePostfix(node) {
  509. var lookup;
  510. var tok = this.peekToken();
  511. while (tok) {
  512. if (tok.type === lexer.TOKEN_LEFT_PAREN) {
  513. // Function call
  514. node = new nodes.FunCall(tok.lineno, tok.colno, node, this.parseSignature());
  515. } else if (tok.type === lexer.TOKEN_LEFT_BRACKET) {
  516. // Reference
  517. lookup = this.parseAggregate();
  518. if (lookup.children.length > 1) {
  519. this.fail('invalid index');
  520. }
  521. node = new nodes.LookupVal(tok.lineno, tok.colno, node, lookup.children[0]);
  522. } else if (tok.type === lexer.TOKEN_OPERATOR && tok.value === '.') {
  523. // Reference
  524. this.nextToken();
  525. var val = this.nextToken();
  526. if (val.type !== lexer.TOKEN_SYMBOL) {
  527. this.fail('expected name as lookup value, got ' + val.value, val.lineno, val.colno);
  528. } // Make a literal string because it's not a variable
  529. // reference
  530. lookup = new nodes.Literal(val.lineno, val.colno, val.value);
  531. node = new nodes.LookupVal(tok.lineno, tok.colno, node, lookup);
  532. } else {
  533. break;
  534. }
  535. tok = this.peekToken();
  536. }
  537. return node;
  538. };
  539. _proto.parseExpression = function parseExpression() {
  540. var node = this.parseInlineIf();
  541. return node;
  542. };
  543. _proto.parseInlineIf = function parseInlineIf() {
  544. var node = this.parseOr();
  545. if (this.skipSymbol('if')) {
  546. var condNode = this.parseOr();
  547. var bodyNode = node;
  548. node = new nodes.InlineIf(node.lineno, node.colno);
  549. node.body = bodyNode;
  550. node.cond = condNode;
  551. if (this.skipSymbol('else')) {
  552. node.else_ = this.parseOr();
  553. } else {
  554. node.else_ = null;
  555. }
  556. }
  557. return node;
  558. };
  559. _proto.parseOr = function parseOr() {
  560. var node = this.parseAnd();
  561. while (this.skipSymbol('or')) {
  562. var node2 = this.parseAnd();
  563. node = new nodes.Or(node.lineno, node.colno, node, node2);
  564. }
  565. return node;
  566. };
  567. _proto.parseAnd = function parseAnd() {
  568. var node = this.parseNot();
  569. while (this.skipSymbol('and')) {
  570. var node2 = this.parseNot();
  571. node = new nodes.And(node.lineno, node.colno, node, node2);
  572. }
  573. return node;
  574. };
  575. _proto.parseNot = function parseNot() {
  576. var tok = this.peekToken();
  577. if (this.skipSymbol('not')) {
  578. return new nodes.Not(tok.lineno, tok.colno, this.parseNot());
  579. }
  580. return this.parseIn();
  581. };
  582. _proto.parseIn = function parseIn() {
  583. var node = this.parseIs();
  584. while (1) {
  585. // eslint-disable-line no-constant-condition
  586. // check if the next token is 'not'
  587. var tok = this.nextToken();
  588. if (!tok) {
  589. break;
  590. }
  591. var invert = tok.type === lexer.TOKEN_SYMBOL && tok.value === 'not'; // if it wasn't 'not', put it back
  592. if (!invert) {
  593. this.pushToken(tok);
  594. }
  595. if (this.skipSymbol('in')) {
  596. var node2 = this.parseIs();
  597. node = new nodes.In(node.lineno, node.colno, node, node2);
  598. if (invert) {
  599. node = new nodes.Not(node.lineno, node.colno, node);
  600. }
  601. } else {
  602. // if we'd found a 'not' but this wasn't an 'in', put back the 'not'
  603. if (invert) {
  604. this.pushToken(tok);
  605. }
  606. break;
  607. }
  608. }
  609. return node;
  610. } // I put this right after "in" in the operator precedence stack. That can
  611. // obviously be changed to be closer to Jinja.
  612. ;
  613. _proto.parseIs = function parseIs() {
  614. var node = this.parseCompare(); // look for an is
  615. if (this.skipSymbol('is')) {
  616. // look for a not
  617. var not = this.skipSymbol('not'); // get the next node
  618. var node2 = this.parseCompare(); // create an Is node using the next node and the info from our Is node.
  619. node = new nodes.Is(node.lineno, node.colno, node, node2); // if we have a Not, create a Not node from our Is node.
  620. if (not) {
  621. node = new nodes.Not(node.lineno, node.colno, node);
  622. }
  623. } // return the node.
  624. return node;
  625. };
  626. _proto.parseCompare = function parseCompare() {
  627. var compareOps = ['==', '===', '!=', '!==', '<', '>', '<=', '>='];
  628. var expr = this.parseConcat();
  629. var ops = [];
  630. while (1) {
  631. // eslint-disable-line no-constant-condition
  632. var tok = this.nextToken();
  633. if (!tok) {
  634. break;
  635. } else if (compareOps.indexOf(tok.value) !== -1) {
  636. ops.push(new nodes.CompareOperand(tok.lineno, tok.colno, this.parseConcat(), tok.value));
  637. } else {
  638. this.pushToken(tok);
  639. break;
  640. }
  641. }
  642. if (ops.length) {
  643. return new nodes.Compare(ops[0].lineno, ops[0].colno, expr, ops);
  644. } else {
  645. return expr;
  646. }
  647. } // finds the '~' for string concatenation
  648. ;
  649. _proto.parseConcat = function parseConcat() {
  650. var node = this.parseAdd();
  651. while (this.skipValue(lexer.TOKEN_TILDE, '~')) {
  652. var node2 = this.parseAdd();
  653. node = new nodes.Concat(node.lineno, node.colno, node, node2);
  654. }
  655. return node;
  656. };
  657. _proto.parseAdd = function parseAdd() {
  658. var node = this.parseSub();
  659. while (this.skipValue(lexer.TOKEN_OPERATOR, '+')) {
  660. var node2 = this.parseSub();
  661. node = new nodes.Add(node.lineno, node.colno, node, node2);
  662. }
  663. return node;
  664. };
  665. _proto.parseSub = function parseSub() {
  666. var node = this.parseMul();
  667. while (this.skipValue(lexer.TOKEN_OPERATOR, '-')) {
  668. var node2 = this.parseMul();
  669. node = new nodes.Sub(node.lineno, node.colno, node, node2);
  670. }
  671. return node;
  672. };
  673. _proto.parseMul = function parseMul() {
  674. var node = this.parseDiv();
  675. while (this.skipValue(lexer.TOKEN_OPERATOR, '*')) {
  676. var node2 = this.parseDiv();
  677. node = new nodes.Mul(node.lineno, node.colno, node, node2);
  678. }
  679. return node;
  680. };
  681. _proto.parseDiv = function parseDiv() {
  682. var node = this.parseFloorDiv();
  683. while (this.skipValue(lexer.TOKEN_OPERATOR, '/')) {
  684. var node2 = this.parseFloorDiv();
  685. node = new nodes.Div(node.lineno, node.colno, node, node2);
  686. }
  687. return node;
  688. };
  689. _proto.parseFloorDiv = function parseFloorDiv() {
  690. var node = this.parseMod();
  691. while (this.skipValue(lexer.TOKEN_OPERATOR, '//')) {
  692. var node2 = this.parseMod();
  693. node = new nodes.FloorDiv(node.lineno, node.colno, node, node2);
  694. }
  695. return node;
  696. };
  697. _proto.parseMod = function parseMod() {
  698. var node = this.parsePow();
  699. while (this.skipValue(lexer.TOKEN_OPERATOR, '%')) {
  700. var node2 = this.parsePow();
  701. node = new nodes.Mod(node.lineno, node.colno, node, node2);
  702. }
  703. return node;
  704. };
  705. _proto.parsePow = function parsePow() {
  706. var node = this.parseUnary();
  707. while (this.skipValue(lexer.TOKEN_OPERATOR, '**')) {
  708. var node2 = this.parseUnary();
  709. node = new nodes.Pow(node.lineno, node.colno, node, node2);
  710. }
  711. return node;
  712. };
  713. _proto.parseUnary = function parseUnary(noFilters) {
  714. var tok = this.peekToken();
  715. var node;
  716. if (this.skipValue(lexer.TOKEN_OPERATOR, '-')) {
  717. node = new nodes.Neg(tok.lineno, tok.colno, this.parseUnary(true));
  718. } else if (this.skipValue(lexer.TOKEN_OPERATOR, '+')) {
  719. node = new nodes.Pos(tok.lineno, tok.colno, this.parseUnary(true));
  720. } else {
  721. node = this.parsePrimary();
  722. }
  723. if (!noFilters) {
  724. node = this.parseFilter(node);
  725. }
  726. return node;
  727. };
  728. _proto.parsePrimary = function parsePrimary(noPostfix) {
  729. var tok = this.nextToken();
  730. var val;
  731. var node = null;
  732. if (!tok) {
  733. this.fail('expected expression, got end of file');
  734. } else if (tok.type === lexer.TOKEN_STRING) {
  735. val = tok.value;
  736. } else if (tok.type === lexer.TOKEN_INT) {
  737. val = parseInt(tok.value, 10);
  738. } else if (tok.type === lexer.TOKEN_FLOAT) {
  739. val = parseFloat(tok.value);
  740. } else if (tok.type === lexer.TOKEN_BOOLEAN) {
  741. if (tok.value === 'true') {
  742. val = true;
  743. } else if (tok.value === 'false') {
  744. val = false;
  745. } else {
  746. this.fail('invalid boolean: ' + tok.value, tok.lineno, tok.colno);
  747. }
  748. } else if (tok.type === lexer.TOKEN_NONE) {
  749. val = null;
  750. } else if (tok.type === lexer.TOKEN_REGEX) {
  751. val = new RegExp(tok.value.body, tok.value.flags);
  752. }
  753. if (val !== undefined) {
  754. node = new nodes.Literal(tok.lineno, tok.colno, val);
  755. } else if (tok.type === lexer.TOKEN_SYMBOL) {
  756. node = new nodes.Symbol(tok.lineno, tok.colno, tok.value);
  757. } else {
  758. // See if it's an aggregate type, we need to push the
  759. // current delimiter token back on
  760. this.pushToken(tok);
  761. node = this.parseAggregate();
  762. }
  763. if (!noPostfix) {
  764. node = this.parsePostfix(node);
  765. }
  766. if (node) {
  767. return node;
  768. } else {
  769. throw this.error("unexpected token: " + tok.value, tok.lineno, tok.colno);
  770. }
  771. };
  772. _proto.parseFilterName = function parseFilterName() {
  773. var tok = this.expect(lexer.TOKEN_SYMBOL);
  774. var name = tok.value;
  775. while (this.skipValue(lexer.TOKEN_OPERATOR, '.')) {
  776. name += '.' + this.expect(lexer.TOKEN_SYMBOL).value;
  777. }
  778. return new nodes.Symbol(tok.lineno, tok.colno, name);
  779. };
  780. _proto.parseFilterArgs = function parseFilterArgs(node) {
  781. if (this.peekToken().type === lexer.TOKEN_LEFT_PAREN) {
  782. // Get a FunCall node and add the parameters to the
  783. // filter
  784. var call = this.parsePostfix(node);
  785. return call.args.children;
  786. }
  787. return [];
  788. };
  789. _proto.parseFilter = function parseFilter(node) {
  790. while (this.skip(lexer.TOKEN_PIPE)) {
  791. var name = this.parseFilterName();
  792. node = new nodes.Filter(name.lineno, name.colno, name, new nodes.NodeList(name.lineno, name.colno, [node].concat(this.parseFilterArgs(node))));
  793. }
  794. return node;
  795. };
  796. _proto.parseFilterStatement = function parseFilterStatement() {
  797. var filterTok = this.peekToken();
  798. if (!this.skipSymbol('filter')) {
  799. this.fail('parseFilterStatement: expected filter');
  800. }
  801. var name = this.parseFilterName();
  802. var args = this.parseFilterArgs(name);
  803. this.advanceAfterBlockEnd(filterTok.value);
  804. var body = new nodes.Capture(name.lineno, name.colno, this.parseUntilBlocks('endfilter'));
  805. this.advanceAfterBlockEnd();
  806. var node = new nodes.Filter(name.lineno, name.colno, name, new nodes.NodeList(name.lineno, name.colno, [body].concat(args)));
  807. return new nodes.Output(name.lineno, name.colno, [node]);
  808. };
  809. _proto.parseAggregate = function parseAggregate() {
  810. var tok = this.nextToken();
  811. var node;
  812. switch (tok.type) {
  813. case lexer.TOKEN_LEFT_PAREN:
  814. node = new nodes.Group(tok.lineno, tok.colno);
  815. break;
  816. case lexer.TOKEN_LEFT_BRACKET:
  817. node = new nodes.Array(tok.lineno, tok.colno);
  818. break;
  819. case lexer.TOKEN_LEFT_CURLY:
  820. node = new nodes.Dict(tok.lineno, tok.colno);
  821. break;
  822. default:
  823. return null;
  824. }
  825. while (1) {
  826. // eslint-disable-line no-constant-condition
  827. var type = this.peekToken().type;
  828. if (type === lexer.TOKEN_RIGHT_PAREN || type === lexer.TOKEN_RIGHT_BRACKET || type === lexer.TOKEN_RIGHT_CURLY) {
  829. this.nextToken();
  830. break;
  831. }
  832. if (node.children.length > 0) {
  833. if (!this.skip(lexer.TOKEN_COMMA)) {
  834. this.fail('parseAggregate: expected comma after expression', tok.lineno, tok.colno);
  835. }
  836. }
  837. if (node instanceof nodes.Dict) {
  838. // TODO: check for errors
  839. var key = this.parsePrimary(); // We expect a key/value pair for dicts, separated by a
  840. // colon
  841. if (!this.skip(lexer.TOKEN_COLON)) {
  842. this.fail('parseAggregate: expected colon after dict key', tok.lineno, tok.colno);
  843. } // TODO: check for errors
  844. var value = this.parseExpression();
  845. node.addChild(new nodes.Pair(key.lineno, key.colno, key, value));
  846. } else {
  847. // TODO: check for errors
  848. var expr = this.parseExpression();
  849. node.addChild(expr);
  850. }
  851. }
  852. return node;
  853. };
  854. _proto.parseSignature = function parseSignature(tolerant, noParens) {
  855. var tok = this.peekToken();
  856. if (!noParens && tok.type !== lexer.TOKEN_LEFT_PAREN) {
  857. if (tolerant) {
  858. return null;
  859. } else {
  860. this.fail('expected arguments', tok.lineno, tok.colno);
  861. }
  862. }
  863. if (tok.type === lexer.TOKEN_LEFT_PAREN) {
  864. tok = this.nextToken();
  865. }
  866. var args = new nodes.NodeList(tok.lineno, tok.colno);
  867. var kwargs = new nodes.KeywordArgs(tok.lineno, tok.colno);
  868. var checkComma = false;
  869. while (1) {
  870. // eslint-disable-line no-constant-condition
  871. tok = this.peekToken();
  872. if (!noParens && tok.type === lexer.TOKEN_RIGHT_PAREN) {
  873. this.nextToken();
  874. break;
  875. } else if (noParens && tok.type === lexer.TOKEN_BLOCK_END) {
  876. break;
  877. }
  878. if (checkComma && !this.skip(lexer.TOKEN_COMMA)) {
  879. this.fail('parseSignature: expected comma after expression', tok.lineno, tok.colno);
  880. } else {
  881. var arg = this.parseExpression();
  882. if (this.skipValue(lexer.TOKEN_OPERATOR, '=')) {
  883. kwargs.addChild(new nodes.Pair(arg.lineno, arg.colno, arg, this.parseExpression()));
  884. } else {
  885. args.addChild(arg);
  886. }
  887. }
  888. checkComma = true;
  889. }
  890. if (kwargs.children.length) {
  891. args.addChild(kwargs);
  892. }
  893. return args;
  894. };
  895. _proto.parseUntilBlocks = function parseUntilBlocks() {
  896. var prev = this.breakOnBlocks;
  897. for (var _len = arguments.length, blockNames = new Array(_len), _key = 0; _key < _len; _key++) {
  898. blockNames[_key] = arguments[_key];
  899. }
  900. this.breakOnBlocks = blockNames;
  901. var ret = this.parse();
  902. this.breakOnBlocks = prev;
  903. return ret;
  904. };
  905. _proto.parseNodes = function parseNodes() {
  906. var tok;
  907. var buf = [];
  908. while (tok = this.nextToken()) {
  909. if (tok.type === lexer.TOKEN_DATA) {
  910. var data = tok.value;
  911. var nextToken = this.peekToken();
  912. var nextVal = nextToken && nextToken.value; // If the last token has "-" we need to trim the
  913. // leading whitespace of the data. This is marked with
  914. // the `dropLeadingWhitespace` variable.
  915. if (this.dropLeadingWhitespace) {
  916. // TODO: this could be optimized (don't use regex)
  917. data = data.replace(/^\s*/, '');
  918. this.dropLeadingWhitespace = false;
  919. } // Same for the succeeding block start token
  920. if (nextToken && (nextToken.type === lexer.TOKEN_BLOCK_START && nextVal.charAt(nextVal.length - 1) === '-' || nextToken.type === lexer.TOKEN_VARIABLE_START && nextVal.charAt(this.tokens.tags.VARIABLE_START.length) === '-' || nextToken.type === lexer.TOKEN_COMMENT && nextVal.charAt(this.tokens.tags.COMMENT_START.length) === '-')) {
  921. // TODO: this could be optimized (don't use regex)
  922. data = data.replace(/\s*$/, '');
  923. }
  924. buf.push(new nodes.Output(tok.lineno, tok.colno, [new nodes.TemplateData(tok.lineno, tok.colno, data)]));
  925. } else if (tok.type === lexer.TOKEN_BLOCK_START) {
  926. this.dropLeadingWhitespace = false;
  927. var n = this.parseStatement();
  928. if (!n) {
  929. break;
  930. }
  931. buf.push(n);
  932. } else if (tok.type === lexer.TOKEN_VARIABLE_START) {
  933. var e = this.parseExpression();
  934. this.dropLeadingWhitespace = false;
  935. this.advanceAfterVariableEnd();
  936. buf.push(new nodes.Output(tok.lineno, tok.colno, [e]));
  937. } else if (tok.type === lexer.TOKEN_COMMENT) {
  938. this.dropLeadingWhitespace = tok.value.charAt(tok.value.length - this.tokens.tags.COMMENT_END.length - 1) === '-';
  939. } else {
  940. // Ignore comments, otherwise this should be an error
  941. this.fail('Unexpected token at top-level: ' + tok.type, tok.lineno, tok.colno);
  942. }
  943. }
  944. return buf;
  945. };
  946. _proto.parse = function parse() {
  947. return new nodes.NodeList(0, 0, this.parseNodes());
  948. };
  949. _proto.parseAsRoot = function parseAsRoot() {
  950. return new nodes.Root(0, 0, this.parseNodes());
  951. };
  952. return Parser;
  953. }(Obj); // var util = require('util');
  954. // var l = lexer.lex('{%- if x -%}\n hello {% endif %}');
  955. // var t;
  956. // while((t = l.nextToken())) {
  957. // console.log(util.inspect(t));
  958. // }
  959. // var p = new Parser(lexer.lex('hello {% filter title %}' +
  960. // 'Hello madam how are you' +
  961. // '{% endfilter %}'));
  962. // var n = p.parseAsRoot();
  963. // nodes.printNodes(n);
  964. module.exports = {
  965. parse: function parse(src, extensions, opts) {
  966. var p = new Parser(lexer.lex(src, opts));
  967. if (extensions !== undefined) {
  968. p.extensions = extensions;
  969. }
  970. return p.parseAsRoot();
  971. },
  972. Parser: Parser
  973. };