1 |
|
2 | var debug = require('debug');
|
3 |
|
4 | var tks = require('./tokens');
|
5 | var nodestuff = require('./nodestuff');
|
6 | var error = require('./error');
|
7 | var namer = require('./util/fn-namer');
|
8 |
|
9 | var ProgramNode = namer(require('./nodes/program'));
|
10 | var TextNode = namer(require('./nodes/text'));
|
11 | var MarkupNode = namer(require('./nodes/markup'));
|
12 | var MarkupCommentNode = namer(require('./nodes/markupcomment'));
|
13 | var MarkupContentNode = namer(require('./nodes/markupcontent'));
|
14 | var MarkupAttributeNode = namer(require('./nodes/markupattribute'));
|
15 | var ExpressionNode = namer(require('./nodes/expression'));
|
16 | var ExplicitExpressionNode = namer(require('./nodes/explicitexpression'));
|
17 | var IndexExpressionNode = namer(require('./nodes/indexexpression'));
|
18 | var LocationNode = namer(require('./nodes/location'));
|
19 | var BlockNode = namer(require('./nodes/block'));
|
20 | var CommentNode = namer(require('./nodes/comment'));
|
21 | var RegexNode = namer(require('./nodes/regex'));
|
22 |
|
23 | function Parser(opts) {
|
24 | this.lg = debug('vash:parser');
|
25 | this.tokens = [];
|
26 | this.deferredTokens = [];
|
27 | this.node = null;
|
28 | this.stack = [];
|
29 | this.inputText = '';
|
30 | this.opts = opts || {};
|
31 | this.previousNonWhitespace = null
|
32 | }
|
33 |
|
34 | module.exports = Parser;
|
35 |
|
36 | Parser.prototype.decorateError = function(err, line, column) {
|
37 | err.message = ''
|
38 | + err.message
|
39 | + ' at template line ' + line
|
40 | + ', column ' + column + '\n\n'
|
41 | + 'Context: \n'
|
42 | + error.context(this.inputText, line, column, '\n')
|
43 | + '\n';
|
44 |
|
45 | return err;
|
46 | }
|
47 |
|
48 | Parser.prototype.write = function(tokens) {
|
49 | if (!Array.isArray(tokens)) tokens = [tokens];
|
50 | this.inputText += tokens.map(function(tok) { return tok.val; }).join('');
|
51 | this.tokens.unshift.apply(this.tokens, tokens.reverse());
|
52 | }
|
53 |
|
54 | Parser.prototype.read = function() {
|
55 | if (!this.tokens.length && !this.deferredTokens.length) return null;
|
56 |
|
57 | if (!this.node) {
|
58 | this.openNode(new ProgramNode());
|
59 | this.openNode(new MarkupNode(), this.node.body);
|
60 | this.node._finishedOpen = true;
|
61 | this.node.name = 'text';
|
62 | updateLoc(this.node, { line: 0, chr: 0 })
|
63 | this.openNode(new MarkupContentNode(), this.node.values);
|
64 | updateLoc(this.node, { line: 0, chr: 0 })
|
65 | }
|
66 |
|
67 | var curr = this.deferredTokens.pop() || this.tokens.pop();
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 | var nnwon = null;
|
74 |
|
75 | for (var i = this.deferredTokens.length-1; i >= 0; i--) {
|
76 | if (
|
77 | nnwon
|
78 | && nnwon.type !== tks.WHITESPACE
|
79 | && nnwon.type !== tks.NEWLINE
|
80 | ) break;
|
81 | nnwon = this.deferredTokens[i];
|
82 | }
|
83 |
|
84 | for (var i = this.tokens.length-1; i >= 0; i--) {
|
85 | if (
|
86 | nnwon
|
87 | && nnwon.type !== tks.WHITESPACE
|
88 | && nnwon.type !== tks.NEWLINE
|
89 | ) break;
|
90 | nnwon = this.tokens[i];
|
91 | }
|
92 |
|
93 | var next = this.deferredTokens.pop() || this.tokens.pop();
|
94 | var ahead = this.deferredTokens.pop() || this.tokens.pop();
|
95 |
|
96 | var dispatch = 'continue' + this.node.constructor.name;
|
97 |
|
98 | this.lg('Read: %s', dispatch);
|
99 | this.lg(' curr %s', curr);
|
100 | this.lg(' next %s', next);
|
101 | this.lg(' ahead %s', ahead);
|
102 | this.lg(' nnwon %s', nnwon);
|
103 |
|
104 | if (curr._considerEscaped) {
|
105 | this.lg(' Previous token was marked as escaping');
|
106 | }
|
107 |
|
108 | var consumed = this[dispatch](this.node, curr, next, ahead, nnwon);
|
109 |
|
110 | if (ahead) {
|
111 |
|
112 | this.deferredTokens.push(ahead);
|
113 | }
|
114 |
|
115 | if (next) {
|
116 |
|
117 | this.deferredTokens.push(next);
|
118 | }
|
119 |
|
120 | if (!consumed) {
|
121 | this.lg('Deferring curr %s', curr);
|
122 | this.deferredTokens.push(curr);
|
123 | } else {
|
124 |
|
125 | if (curr.type !== tks.WHITESPACE) {
|
126 | this.lg('set previousNonWhitespace %s', curr);
|
127 | this.previousNonWhitespace = curr;
|
128 | }
|
129 |
|
130 |
|
131 | if (curr.type === tks.NEWLINE) {
|
132 | this.lg('set previousNonWhitespace %s', null);
|
133 | this.previousNonWhitespace = null;
|
134 | }
|
135 |
|
136 | if (!curr._considerEscaped && curr.type === tks.BACKSLASH) {
|
137 | next._considerEscaped = true;
|
138 | }
|
139 | }
|
140 | }
|
141 |
|
142 | Parser.prototype.checkStack = function() {
|
143 |
|
144 | var i = this.stack.length-1;
|
145 | var node;
|
146 | var msg;
|
147 |
|
148 |
|
149 | while(i >= 2) {
|
150 | node = this.stack[i];
|
151 | if (node.endOk && !node.endOk()) {
|
152 |
|
153 | delete node.values;
|
154 | msg = 'Found unclosed ' + node.type;
|
155 | var err = new Error(msg);
|
156 | err.name = 'UnclosedNodeError';
|
157 | throw this.decorateError(
|
158 | err,
|
159 | node.startloc.line,
|
160 | node.startloc.column);
|
161 | }
|
162 | i--;
|
163 | }
|
164 | }
|
165 |
|
166 |
|
167 |
|
168 | Parser.prototype.flag = function(node, name, value) {
|
169 | var printVal = (value && typeof value === 'object')
|
170 | ? value.type
|
171 | : value;
|
172 | this.lg('Flag %s on node %s was %s now %s',
|
173 | name, node.type, node[name], printVal);
|
174 | node[name] = value;
|
175 | }
|
176 |
|
177 | Parser.prototype.dumpAST = function() {
|
178 | if (!this.stack.length) {
|
179 | var msg = 'No AST to dump.';
|
180 | throw new Error(msg);
|
181 | }
|
182 |
|
183 | return JSON.stringify(this.stack[0], null, ' ');
|
184 | }
|
185 |
|
186 | Parser.prototype.openNode = function(node, opt_insertArr) {
|
187 | this.stack.push(node);
|
188 | this.lg('Opened node %s from %s',
|
189 | node.type, (this.node ? this.node.type : null));
|
190 | this.node = node;
|
191 |
|
192 | if (opt_insertArr) {
|
193 | opt_insertArr.push(node);
|
194 | }
|
195 |
|
196 | return node;
|
197 | }
|
198 |
|
199 | Parser.prototype.closeNode = function(node) {
|
200 | var toClose = this.stack[this.stack.length-1];
|
201 | if (node !== toClose) {
|
202 | var msg = 'InvalidCloseAction: '
|
203 | + 'Expected ' + node.type + ' in stack, instead found '
|
204 | + toClose.type;
|
205 | throw new Error(msg);
|
206 | }
|
207 |
|
208 | this.stack.pop();
|
209 | var last = this.stack[this.stack.length-1];
|
210 |
|
211 | this.lg('Closing node %s (%s), returning to node %s',
|
212 | node.type, node.name, last.type)
|
213 |
|
214 | this.node = last;
|
215 | }
|
216 |
|
217 | Parser.prototype.continueCommentNode = function(node, curr, next) {
|
218 | var valueNode = ensureTextNode(node.values);
|
219 |
|
220 | if (curr.type === tks.AT_STAR_OPEN && !node._waitingForClose) {
|
221 | this.flag(node, '_waitingForClose', tks.AT_STAR_CLOSE)
|
222 | updateLoc(node, curr);
|
223 | return true;
|
224 | }
|
225 |
|
226 | if (curr.type === node._waitingForClose) {
|
227 | this.flag(node, '_waitingForClose', null)
|
228 | updateLoc(node, curr);
|
229 | this.closeNode(node);
|
230 | return true;
|
231 | }
|
232 |
|
233 | if (curr.type === tks.DOUBLE_FORWARD_SLASH && !node._waitingForClose){
|
234 | this.flag(node, '_waitingForClose', tks.NEWLINE);
|
235 | updateLoc(node, curr);
|
236 | return true;
|
237 | }
|
238 |
|
239 | appendTextValue(valueNode, curr);
|
240 | return true;
|
241 | }
|
242 |
|
243 | Parser.prototype.continueMarkupNode = function(node, curr, next) {
|
244 | var valueNode = node.values[node.values.length-1];
|
245 |
|
246 | if (curr.type === tks.LT_SIGN && !node._finishedOpen) {
|
247 | updateLoc(node, curr);
|
248 | return true;
|
249 | }
|
250 |
|
251 | if (
|
252 | !node._finishedOpen
|
253 | && curr.type !== tks.GT_SIGN
|
254 | && curr.type !== tks.LT_SIGN
|
255 | && curr.type !== tks.WHITESPACE
|
256 | && curr.type !== tks.NEWLINE
|
257 | && curr.type !== tks.HTML_TAG_VOID_CLOSE
|
258 | ) {
|
259 |
|
260 |
|
261 |
|
262 | if (
|
263 | curr.type === tks.AT
|
264 | && !curr._considerEscaped
|
265 | && next
|
266 | && next.type === tks.AT
|
267 | ) {
|
268 | next._considerEscaped = true;
|
269 | return true;
|
270 | }
|
271 |
|
272 | if (curr.type === tks.AT && !curr._considerEscaped) {
|
273 | this.flag(node, 'expression', this.openNode(new ExpressionNode()));
|
274 | updateLoc(node.expression, curr);
|
275 | return true;
|
276 | }
|
277 |
|
278 | node.name = node.name
|
279 | ? node.name + curr.val
|
280 | : curr.val;
|
281 | updateLoc(node, curr);
|
282 | return true;
|
283 | }
|
284 |
|
285 | if (curr.type === tks.GT_SIGN && !node._waitingForFinishedClose) {
|
286 | this.flag(node, '_finishedOpen', true);
|
287 |
|
288 | if (MarkupNode.isVoid(node.name)) {
|
289 | this.flag(node, 'isVoid', true);
|
290 | this.closeNode(node);
|
291 | updateLoc(node, curr);
|
292 | } else {
|
293 | valueNode = this.openNode(new MarkupContentNode(), node.values);
|
294 | updateLoc(valueNode, curr);
|
295 | }
|
296 |
|
297 | return true;
|
298 | }
|
299 |
|
300 | if (curr.type === tks.GT_SIGN && node._waitingForFinishedClose) {
|
301 | this.flag(node, '_waitingForFinishedClose', false);
|
302 | this.closeNode(node);
|
303 | updateLoc(node, curr);
|
304 | return true;
|
305 | }
|
306 |
|
307 |
|
308 | if (
|
309 | curr.type === tks.HTML_TAG_CLOSE
|
310 | && next
|
311 | && next.type === tks.IDENTIFIER
|
312 | && MarkupNode.isVoid(next.val)
|
313 | ) {
|
314 | throw newUnexpectedClosingTagError(this, curr, curr.val + next.val);
|
315 | }
|
316 |
|
317 |
|
318 | if (curr.type === tks.HTML_TAG_CLOSE) {
|
319 | this.flag(node, '_waitingForFinishedClose', true);
|
320 | this.flag(node, 'isClosed', true);
|
321 | return true;
|
322 | }
|
323 |
|
324 |
|
325 | if (curr.type === tks.HTML_COMMENT_CLOSE) {
|
326 | this.flag(node, '_waitingForFinishedClose', false);
|
327 | this.closeNode(node);
|
328 | return false;
|
329 | }
|
330 |
|
331 | if (curr.type === tks.HTML_TAG_VOID_CLOSE) {
|
332 | this.closeNode(node);
|
333 | this.flag(node, 'isVoid', true);
|
334 | this.flag(node, 'voidClosed', true);
|
335 | this.flag(node, 'isClosed', true);
|
336 | updateLoc(node, curr);
|
337 | return true;
|
338 | }
|
339 |
|
340 | if (node._waitingForFinishedClose) {
|
341 | this.lg('Ignoring %s while waiting for closing GT_SIGN',
|
342 | curr);
|
343 | return true;
|
344 | }
|
345 |
|
346 | if (
|
347 | (curr.type === tks.WHITESPACE || curr.type === tks.NEWLINE)
|
348 | && !node._finishedOpen
|
349 | && next.type !== tks.HTML_TAG_VOID_CLOSE
|
350 | && next.type !== tks.GT_SIGN
|
351 | && next.type !== tks.NEWLINE
|
352 | && next.type !== tks.WHITESPACE
|
353 | ) {
|
354 |
|
355 | valueNode = this.openNode(new MarkupAttributeNode(), node.attributes);
|
356 | updateLoc(valueNode, curr);
|
357 | return true;
|
358 | }
|
359 |
|
360 |
|
361 | if (
|
362 | (curr.type === tks.WHITESPACE || curr.type === tks.NEWLINE)
|
363 | && !node._finishedOpen
|
364 | ) {
|
365 | updateLoc(node, curr);
|
366 | return true;
|
367 | }
|
368 |
|
369 |
|
370 |
|
371 | if (node._finishedOpen) {
|
372 | valueNode = this.openNode(new MarkupContentNode(), this.node.values);
|
373 | updateLoc(valueNode, curr);
|
374 | return false;
|
375 | }
|
376 |
|
377 |
|
378 |
|
379 |
|
380 |
|
381 |
|
382 | }
|
383 |
|
384 | Parser.prototype.continueMarkupAttributeNode = function(node, curr, next) {
|
385 |
|
386 | var valueNode;
|
387 |
|
388 | if (
|
389 | curr.type === tks.AT
|
390 | && !curr._considerEscaped
|
391 | && next
|
392 | && next.type === tks.AT
|
393 | ) {
|
394 | next._considerEscaped = true;
|
395 | return true;
|
396 | }
|
397 |
|
398 | if (curr.type === tks.AT && !curr._considerEscaped) {
|
399 |
|
400 |
|
401 | valueNode = this.openNode(new ExpressionNode(), !node._finishedLeft
|
402 | ? node.left
|
403 | : node.right);
|
404 |
|
405 | updateLoc(valueNode, curr);
|
406 | return true;
|
407 | }
|
408 |
|
409 |
|
410 | if (
|
411 | !node._expectRight
|
412 | && (curr.type === tks.WHITESPACE
|
413 | || curr.type === tks.GT_SIGN
|
414 | || curr.type === tks.HTML_TAG_VOID_CLOSE)
|
415 | ) {
|
416 | this.flag(node, '_finishedLeft', true);
|
417 | updateLoc(node, curr);
|
418 | this.closeNode(node);
|
419 | return false;
|
420 | }
|
421 |
|
422 |
|
423 | if (curr.type === tks.EQUAL_SIGN && !node._finishedLeft) {
|
424 | this.flag(node, '_finishedLeft', true);
|
425 | this.flag(node, '_expectRight', true);
|
426 | return true;
|
427 | }
|
428 |
|
429 |
|
430 | if (
|
431 | node._expectRight
|
432 | && !node.rightIsQuoted
|
433 | && (curr.type === tks.DOUBLE_QUOTE
|
434 | || curr.type === tks.SINGLE_QUOTE)
|
435 | ) {
|
436 | this.flag(node, 'rightIsQuoted', curr.val);
|
437 | return true;
|
438 | }
|
439 |
|
440 |
|
441 | if (node.rightIsQuoted === curr.val) {
|
442 | updateLoc(node, curr);
|
443 | this.closeNode(node);
|
444 | return true;
|
445 | }
|
446 |
|
447 |
|
448 |
|
449 | if (!node._finishedLeft) {
|
450 | valueNode = ensureTextNode(node.left);
|
451 | } else {
|
452 | valueNode = ensureTextNode(node.right);
|
453 | }
|
454 |
|
455 | appendTextValue(valueNode, curr);
|
456 | return true;
|
457 | }
|
458 |
|
459 | Parser.prototype.continueMarkupContentNode = function(node, curr, next, ahead) {
|
460 | var valueNode = ensureTextNode(node.values);
|
461 |
|
462 | if (curr.type === tks.HTML_COMMENT_OPEN) {
|
463 | valueNode = this.openNode(new MarkupCommentNode(), node.values);
|
464 | updateLoc(valueNode, curr);
|
465 | return false;
|
466 | }
|
467 |
|
468 | if (curr.type === tks.HTML_COMMENT_CLOSE) {
|
469 | updateLoc(node, curr);
|
470 | this.closeNode(node);
|
471 | return false;
|
472 | }
|
473 |
|
474 | if (curr.type === tks.AT_COLON && !curr._considerEscaped) {
|
475 | this.flag(node, '_waitingForNewline', true);
|
476 | updateLoc(valueNode, curr);
|
477 | return true;
|
478 | }
|
479 |
|
480 | if (curr.type === tks.NEWLINE && node._waitingForNewline === true) {
|
481 | this.flag(node, '_waitingForNewline', false);
|
482 | appendTextValue(valueNode, curr);
|
483 | updateLoc(node, curr);
|
484 | this.closeNode(node);
|
485 | return true;
|
486 | }
|
487 |
|
488 | if (
|
489 | curr.type === tks.AT
|
490 | && !curr._considerEscaped
|
491 | && next.type === tks.BRACE_OPEN
|
492 | ) {
|
493 | valueNode = this.openNode(new BlockNode(), node.values);
|
494 | updateLoc(valueNode, curr);
|
495 | return true;
|
496 | }
|
497 |
|
498 | if (
|
499 | curr.type === tks.AT
|
500 | && !curr._considerEscaped
|
501 | && (next.type === tks.BLOCK_KEYWORD
|
502 | || next.type === tks.FUNCTION)
|
503 | ) {
|
504 | valueNode = this.openNode(new BlockNode(), node.values);
|
505 | updateLoc(valueNode, curr);
|
506 | return true;
|
507 | }
|
508 |
|
509 |
|
510 | if (
|
511 | curr.type === tks.AT
|
512 | && !curr._considerEscaped
|
513 | && next
|
514 | && (
|
515 | next.type === tks.AT_COLON
|
516 | || next.type === tks.AT
|
517 | || next.type === tks.AT_STAR_OPEN
|
518 | )
|
519 | ) {
|
520 | next._considerEscaped = true;
|
521 | return true;
|
522 | }
|
523 |
|
524 |
|
525 | if (curr.type === tks.AT && !curr._considerEscaped) {
|
526 | valueNode = this.openNode(new ExpressionNode(), node.values);
|
527 | updateLoc(valueNode, curr);
|
528 | return true;
|
529 | }
|
530 |
|
531 | if (curr.type === tks.AT_STAR_OPEN && !curr._considerEscaped) {
|
532 | this.openNode(new CommentNode(), node.values);
|
533 | return false;
|
534 | }
|
535 |
|
536 | var parent = this.stack[this.stack.length-2];
|
537 |
|
538 |
|
539 |
|
540 |
|
541 | if (
|
542 | curr.type === tks.HTML_TAG_CLOSE
|
543 | || (curr.type === tks.BRACE_CLOSE
|
544 | && parent && parent.type === 'VashBlock')
|
545 | ) {
|
546 | this.closeNode(node);
|
547 | updateLoc(node, curr);
|
548 | return false;
|
549 | }
|
550 |
|
551 | if (
|
552 | curr.type === tks.LT_SIGN
|
553 | && next
|
554 | && (
|
555 |
|
556 |
|
557 |
|
558 |
|
559 |
|
560 |
|
561 |
|
562 |
|
563 | (next.type === tks.IDENTIFIER
|
564 | && ahead
|
565 | && (
|
566 | ahead.type === tks.GT_SIGN
|
567 | || ahead.type === tks.WHITESPACE
|
568 | || ahead.type === tks.NEWLINE
|
569 | || ahead.type === tks.AT
|
570 | || ahead.type === tks.UNARY_OPERATOR
|
571 | || ahead.type === tks.COLON
|
572 | )
|
573 | )
|
574 | || next.type === tks.AT)
|
575 | ) {
|
576 |
|
577 |
|
578 | valueNode = this.openNode(new MarkupNode(), node.values);
|
579 | updateLoc(valueNode, curr);
|
580 | return false;
|
581 | }
|
582 |
|
583 |
|
584 |
|
585 |
|
586 |
|
587 | if (
|
588 | curr.type === tks.WHITESPACE
|
589 | && !node._waitingForNewline
|
590 | && !this.opts.favorText
|
591 | && parent
|
592 | && parent.type === 'VashBlock'
|
593 | ) {
|
594 | return true;
|
595 | }
|
596 |
|
597 | appendTextValue(valueNode, curr);
|
598 | return true;
|
599 | }
|
600 |
|
601 | Parser.prototype.continueMarkupCommentNode = function(node, curr, next) {
|
602 | var valueNode = node.values[node.values.length-1];
|
603 |
|
604 | if (curr.type === tks.HTML_COMMENT_OPEN) {
|
605 | this.flag(node, '_finishedOpen', true);
|
606 | this.flag(node, '_waitingForClose', tks.HTML_COMMENT_CLOSE);
|
607 | updateLoc(node, curr);
|
608 | valueNode = this.openNode(new MarkupContentNode(), node.values);
|
609 | return true;
|
610 | }
|
611 |
|
612 | if (curr.type === tks.HTML_COMMENT_CLOSE && node._finishedOpen) {
|
613 | this.flag(node, '_waitingForClose', null);
|
614 | updateLoc(node, curr);
|
615 | this.closeNode(node);
|
616 | return true;
|
617 | }
|
618 |
|
619 | valueNode = ensureTextNode(node.values);
|
620 | appendTextValue(valueNode, curr);
|
621 | return true;
|
622 | }
|
623 |
|
624 | Parser.prototype.continueExpressionNode = function(node, curr, next) {
|
625 | var valueNode = node.values[node.values.length-1];
|
626 | var pnw = this.previousNonWhitespace;
|
627 |
|
628 | if (
|
629 | curr.type === tks.AT
|
630 | && next.type === tks.HARD_PAREN_OPEN
|
631 | ) {
|
632 |
|
633 | updateLoc(node, curr);
|
634 | this.closeNode(node);
|
635 | return true;
|
636 | }
|
637 |
|
638 | if (curr.type === tks.PAREN_OPEN) {
|
639 | this.openNode(new ExplicitExpressionNode(), node.values);
|
640 | return false;
|
641 | }
|
642 |
|
643 | if (
|
644 | curr.type === tks.HARD_PAREN_OPEN
|
645 | && node.values[0]
|
646 | && node.values[0].type === 'VashExplicitExpression'
|
647 | ) {
|
648 |
|
649 | updateLoc(node, curr);
|
650 | this.closeNode(node);
|
651 | return false;
|
652 | }
|
653 |
|
654 | if (
|
655 | curr.type === tks.HARD_PAREN_OPEN
|
656 | && next.type === tks.HARD_PAREN_CLOSE
|
657 | ) {
|
658 |
|
659 | updateLoc(node, curr);
|
660 | this.closeNode(node);
|
661 | return false;
|
662 | }
|
663 |
|
664 | if (curr.type === tks.HARD_PAREN_OPEN) {
|
665 | this.openNode(new IndexExpressionNode(), node.values);
|
666 | return false;
|
667 | }
|
668 |
|
669 | if (
|
670 | curr.type === tks.FORWARD_SLASH
|
671 | && pnw
|
672 | && pnw.type === tks.AT
|
673 | ) {
|
674 | this.openNode(new RegexNode(), node.values)
|
675 | return false;
|
676 | }
|
677 |
|
678 |
|
679 |
|
680 |
|
681 | if (curr.type === tks.PERIOD && next && next.type === tks.IDENTIFIER) {
|
682 | valueNode = ensureTextNode(node.values);
|
683 | appendTextValue(valueNode, curr);
|
684 | return true;
|
685 | }
|
686 |
|
687 | if (curr.type === tks.IDENTIFIER) {
|
688 |
|
689 | if (node.values.length > 0 && valueNode && valueNode.type !== 'VashText') {
|
690 |
|
691 | this.closeNode(node);
|
692 | return false;
|
693 | }
|
694 |
|
695 | valueNode = ensureTextNode(node.values);
|
696 | appendTextValue(valueNode, curr);
|
697 | return true;
|
698 | } else {
|
699 | this.closeNode(node);
|
700 | return false;
|
701 | }
|
702 | }
|
703 |
|
704 | Parser.prototype.continueExplicitExpressionNode = function(node, curr, next) {
|
705 |
|
706 | var valueNode = node.values[node.values.length-1];
|
707 |
|
708 | if (
|
709 | node.values.length === 0
|
710 | && (curr.type === tks.AT || curr.type === tks.PAREN_OPEN)
|
711 | ) {
|
712 |
|
713 | this.flag(node, '_waitingForParenClose', true);
|
714 | updateLoc(node, curr);
|
715 | return true;
|
716 | }
|
717 |
|
718 | if (curr.type === tks.PAREN_OPEN && !node._waitingForEndQuote) {
|
719 |
|
720 | valueNode = this.openNode(new ExplicitExpressionNode(), node.values);
|
721 | updateLoc(valueNode, curr);
|
722 |
|
723 | return true;
|
724 | }
|
725 |
|
726 | if (curr.type === tks.PAREN_CLOSE && !node._waitingForEndQuote) {
|
727 |
|
728 | this.flag(node, '_waitingForParenClose', false);
|
729 | updateLoc(node, curr);
|
730 | this.closeNode(node);
|
731 |
|
732 | return true;
|
733 | }
|
734 |
|
735 | if (curr.type === tks.FUNCTION && !node._waitingForEndQuote) {
|
736 | valueNode = this.openNode(new BlockNode(), node.values);
|
737 | updateLoc(valueNode, curr);
|
738 | return false;
|
739 | }
|
740 |
|
741 | if (
|
742 | curr.type === tks.LT_SIGN
|
743 | && next.type === tks.IDENTIFIER
|
744 | && !node._waitingForEndQuote
|
745 | ) {
|
746 |
|
747 | valueNode = this.openNode(new MarkupNode(), node.values);
|
748 | updateLoc(valueNode, curr);
|
749 | return false;
|
750 | }
|
751 |
|
752 | var pnw = this.previousNonWhitespace;
|
753 |
|
754 | if (
|
755 | curr.type === tks.FORWARD_SLASH
|
756 | && !node._waitingForEndQuote
|
757 | && pnw
|
758 | && pnw.type !== tks.IDENTIFIER
|
759 | && pnw.type !== tks.NUMERAL
|
760 | && pnw.type !== tks.PAREN_CLOSE
|
761 | ) {
|
762 | valueNode = this.openNode(new RegexNode(), node.values);
|
763 | updateLoc(valueNode, curr);
|
764 | return false;
|
765 | }
|
766 |
|
767 |
|
768 | valueNode = ensureTextNode(node.values);
|
769 |
|
770 | if (
|
771 | !node._waitingForEndQuote
|
772 | && (curr.type === tks.SINGLE_QUOTE || curr.type === tks.DOUBLE_QUOTE)
|
773 | ) {
|
774 | this.flag(node, '_waitingForEndQuote', curr.val);
|
775 | appendTextValue(valueNode, curr);
|
776 | return true;
|
777 | }
|
778 |
|
779 | if (
|
780 | curr.val === node._waitingForEndQuote
|
781 | && !curr._considerEscaped
|
782 | ) {
|
783 | this.flag(node, '_waitingForEndQuote', null);
|
784 | appendTextValue(valueNode, curr);
|
785 | return true;
|
786 | }
|
787 |
|
788 | appendTextValue(valueNode, curr);
|
789 | return true;
|
790 | }
|
791 |
|
792 | Parser.prototype.continueRegexNode = function(node, curr, next) {
|
793 | var valueNode = ensureTextNode(node.values);
|
794 |
|
795 | if (
|
796 | curr.type === tks.FORWARD_SLASH
|
797 | && !node._waitingForForwardSlash
|
798 | && !curr._considerEscaped
|
799 | ) {
|
800 |
|
801 | this.flag(node, '_waitingForForwardSlash', true);
|
802 | appendTextValue(valueNode, curr);
|
803 | return true;
|
804 | }
|
805 |
|
806 | if (
|
807 | curr.type === tks.FORWARD_SLASH
|
808 | && node._waitingForForwardSlash
|
809 | && !curr._considerEscaped
|
810 | ) {
|
811 |
|
812 | this.flag(node, '_waitingForForwardSlash', null);
|
813 | this.flag(node, '_waitingForFlags', true);
|
814 | appendTextValue(valueNode, curr);
|
815 | return true;
|
816 | }
|
817 |
|
818 | if (node._waitingForFlags) {
|
819 | this.flag(node, '_waitingForFlags', null);
|
820 | this.closeNode(node);
|
821 |
|
822 | if (curr.type === tks.IDENTIFIER) {
|
823 | appendTextValue(valueNode, curr);
|
824 | return true;
|
825 | } else {
|
826 | return false;
|
827 | }
|
828 | }
|
829 |
|
830 | if (
|
831 | curr.type === tks.BACKSLASH
|
832 | && !curr._considerEscaped
|
833 | ) {
|
834 | next._considerEscaped = true;
|
835 | }
|
836 |
|
837 | appendTextValue(valueNode, curr);
|
838 | return true;
|
839 | }
|
840 |
|
841 | Parser.prototype.continueBlockNode = function(node, curr, next, ahead, nnwon) {
|
842 |
|
843 | var valueNode = node.values[node.values.length-1];
|
844 |
|
845 | if (curr.type === tks.AT_STAR_OPEN) {
|
846 | this.openNode(new CommentNode(), node.body);
|
847 | return false;
|
848 | }
|
849 |
|
850 | if (curr.type === tks.DOUBLE_FORWARD_SLASH && !node._waitingForEndQuote) {
|
851 | this.openNode(new CommentNode(), node.body);
|
852 | return false;
|
853 | }
|
854 |
|
855 | if (
|
856 | curr.type === tks.AT_COLON
|
857 | && (!node.hasBraces || node._reachedOpenBrace)
|
858 | ) {
|
859 | valueNode = this.openNode(new MarkupContentNode(), node.values);
|
860 | return false;
|
861 | }
|
862 |
|
863 | if (
|
864 | (curr.type === tks.BLOCK_KEYWORD || curr.type === tks.FUNCTION)
|
865 | && !node._reachedOpenBrace
|
866 | && !node.keyword
|
867 | ) {
|
868 | this.flag(node, 'keyword', curr.val);
|
869 | return true;
|
870 | }
|
871 |
|
872 | if (
|
873 | (curr.type === tks.BLOCK_KEYWORD || curr.type === tks.FUNCTION)
|
874 | && !node._reachedOpenBrace
|
875 | ) {
|
876 |
|
877 | this.flag(node, 'hasBraces', false);
|
878 | valueNode = this.openNode(new BlockNode(), node.values);
|
879 | updateLoc(valueNode, curr);
|
880 | return false;
|
881 | }
|
882 |
|
883 | if (
|
884 | (curr.type === tks.BLOCK_KEYWORD || curr.type === tks.FUNCTION)
|
885 | && !node._reachedCloseBrace
|
886 | && node.hasBraces
|
887 | && !node._waitingForEndQuote
|
888 | ) {
|
889 | valueNode = this.openNode(new BlockNode(), node.values);
|
890 | updateLoc(valueNode, curr);
|
891 | return false;
|
892 | }
|
893 |
|
894 | if (
|
895 | (curr.type === tks.BLOCK_KEYWORD || curr.type === tks.FUNCTION)
|
896 | && node._reachedCloseBrace
|
897 | && !node._waitingForEndQuote
|
898 | ) {
|
899 | valueNode = this.openNode(new BlockNode(), node.tail);
|
900 | updateLoc(valueNode, curr);
|
901 | return false;
|
902 | }
|
903 |
|
904 | if (
|
905 | curr.type === tks.BRACE_OPEN
|
906 | && !node._reachedOpenBrace
|
907 | && !node._waitingForEndQuote
|
908 | ) {
|
909 | this.flag(node, '_reachedOpenBrace', true);
|
910 | this.flag(node, 'hasBraces', true);
|
911 | if (this.opts.favorText) {
|
912 | valueNode = this.openNode(new MarkupContentNode(), node.values);
|
913 | updateLoc(valueNode, curr);
|
914 | }
|
915 | return true;
|
916 | }
|
917 |
|
918 | if (
|
919 | curr.type === tks.BRACE_OPEN
|
920 | && !node._waitingForEndQuote
|
921 | ) {
|
922 | valueNode = this.openNode(new BlockNode(), node.values);
|
923 | updateLoc(valueNode, curr);
|
924 | return false;
|
925 | }
|
926 |
|
927 | if (
|
928 | curr.type === tks.BRACE_CLOSE
|
929 | && node.hasBraces
|
930 | && !node._reachedCloseBrace
|
931 | && !node._waitingForEndQuote
|
932 | ) {
|
933 | updateLoc(node, curr);
|
934 | this.flag(node, '_reachedCloseBrace', true);
|
935 |
|
936 |
|
937 |
|
938 | if (
|
939 | nnwon
|
940 | && nnwon.type !== tks.BLOCK_KEYWORD
|
941 | ) {
|
942 | this.closeNode(node);
|
943 | }
|
944 |
|
945 | return true;
|
946 | }
|
947 |
|
948 | if (
|
949 | curr.type === tks.BRACE_CLOSE
|
950 | && !node.hasBraces
|
951 | ) {
|
952 |
|
953 |
|
954 | this.closeNode(node);
|
955 | updateLoc(node, curr);
|
956 | return false;
|
957 | }
|
958 |
|
959 | if (
|
960 | curr.type === tks.LT_SIGN
|
961 | && (next.type === tks.AT || next.type === tks.IDENTIFIER)
|
962 | && !node._waitingForEndQuote
|
963 | && node._reachedCloseBrace
|
964 | ) {
|
965 | this.closeNode(node);
|
966 | updateLoc(node, curr);
|
967 | return false;
|
968 | }
|
969 |
|
970 | if (
|
971 | curr.type === tks.LT_SIGN
|
972 | && (next.type === tks.AT || next.type === tks.IDENTIFIER)
|
973 | && !node._waitingForEndQuote
|
974 | && !node._reachedCloseBrace
|
975 | ) {
|
976 | valueNode = this.openNode(new MarkupNode(), node.values);
|
977 | updateLoc(valueNode, curr);
|
978 | return false;
|
979 | }
|
980 |
|
981 | if (curr.type === tks.HTML_TAG_CLOSE) {
|
982 | if (
|
983 | (node.hasBraces && node._reachedCloseBrace)
|
984 | || !node._reachedOpenBrace
|
985 | ) {
|
986 | updateLoc(node, curr);
|
987 | this.closeNode(node);
|
988 | return false;
|
989 | }
|
990 |
|
991 |
|
992 |
|
993 |
|
994 |
|
995 | if (
|
996 | next
|
997 | && next.type === tks.IDENTIFIER
|
998 | && MarkupNode.isVoid(next.val)
|
999 | ){
|
1000 | throw newUnexpectedClosingTagError(this, curr, curr.val + next.val);
|
1001 | }
|
1002 | }
|
1003 |
|
1004 | if (
|
1005 | curr.type === tks.AT
|
1006 | && (next.type === tks.BLOCK_KEYWORD
|
1007 | || next.type === tks.BRACE_OPEN
|
1008 | || next.type === tks.FUNCTION)
|
1009 | ) {
|
1010 |
|
1011 | valueNode = this.openNode(new BlockNode(), node.values);
|
1012 | updateLoc(valueNode, curr);
|
1013 |
|
1014 | return true;
|
1015 | }
|
1016 |
|
1017 | if (
|
1018 | curr.type === tks.AT && next.type === tks.PAREN_OPEN
|
1019 | ) {
|
1020 |
|
1021 | valueNode = this.openNode(new ExpressionNode(), node.values);
|
1022 | updateLoc(valueNode, curr);
|
1023 | return true;
|
1024 | }
|
1025 |
|
1026 | var attachmentNode;
|
1027 |
|
1028 | if (node._reachedOpenBrace && node._reachedCloseBrace) {
|
1029 | attachmentNode = node.tail;
|
1030 | } else if (!node._reachedOpenBrace) {
|
1031 | attachmentNode = node.head;
|
1032 | } else {
|
1033 | attachmentNode = node.values;
|
1034 | }
|
1035 |
|
1036 | valueNode = attachmentNode[attachmentNode.length-1];
|
1037 |
|
1038 | if (
|
1039 | curr.type === tks.AT
|
1040 | && next.type === tks.IDENTIFIER
|
1041 | && !node._waitingForEndQuote
|
1042 | ) {
|
1043 |
|
1044 | if (node._reachedCloseBrace) {
|
1045 | this.closeNode(node);
|
1046 | return false;
|
1047 | } else {
|
1048 |
|
1049 | valueNode = this.openNode(new MarkupContentNode(), attachmentNode);
|
1050 | updateLoc(valueNode, curr);
|
1051 | return false;
|
1052 | }
|
1053 | }
|
1054 |
|
1055 | if (
|
1056 | curr.type !== tks.BLOCK_KEYWORD
|
1057 | && curr.type !== tks.PAREN_OPEN
|
1058 | && curr.type !== tks.WHITESPACE
|
1059 | && curr.type !== tks.NEWLINE
|
1060 | && node.hasBraces
|
1061 | && node._reachedCloseBrace
|
1062 | ) {
|
1063 |
|
1064 | updateLoc(node, curr);
|
1065 | this.closeNode(node);
|
1066 | return false;
|
1067 | }
|
1068 |
|
1069 | if (curr.type === tks.PAREN_OPEN) {
|
1070 | valueNode = this.openNode(new ExplicitExpressionNode(), attachmentNode);
|
1071 | updateLoc(valueNode, curr);
|
1072 | return false;
|
1073 | }
|
1074 |
|
1075 | valueNode = ensureTextNode(attachmentNode);
|
1076 |
|
1077 | if (
|
1078 | curr.val === node._waitingForEndQuote
|
1079 | && !curr._considerEscaped
|
1080 | ) {
|
1081 | this.flag(node, '_waitingForEndQuote', null);
|
1082 | appendTextValue(valueNode, curr);
|
1083 | return true;
|
1084 | }
|
1085 |
|
1086 | if (
|
1087 | !node._waitingForEndQuote
|
1088 | && (curr.type === tks.DOUBLE_QUOTE || curr.type === tks.SINGLE_QUOTE)
|
1089 | ) {
|
1090 | this.flag(node, '_waitingForEndQuote', curr.val);
|
1091 | appendTextValue(valueNode, curr);
|
1092 | return true;
|
1093 | }
|
1094 |
|
1095 | var pnw = this.previousNonWhitespace;
|
1096 |
|
1097 | if (
|
1098 | curr.type === tks.FORWARD_SLASH
|
1099 | && !node._waitingForEndQuote
|
1100 | && pnw
|
1101 | && pnw.type !== tks.IDENTIFIER
|
1102 | && pnw.type !== tks.NUMERAL
|
1103 | ) {
|
1104 |
|
1105 | valueNode = this.openNode(new RegexNode(), attachmentNode);
|
1106 | updateLoc(valueNode, curr);
|
1107 | return false;
|
1108 | }
|
1109 |
|
1110 | appendTextValue(valueNode, curr);
|
1111 | return true;
|
1112 | }
|
1113 |
|
1114 |
|
1115 |
|
1116 |
|
1117 | Parser.prototype.continueIndexExpressionNode = function(node, curr, next) {
|
1118 | var valueNode = node.values[node.values.length-1];
|
1119 |
|
1120 | if (node._waitingForEndQuote) {
|
1121 | if (curr.val === node._waitingForEndQuote) {
|
1122 | this.flag(node, '_waitingForEndQuote', null);
|
1123 | }
|
1124 |
|
1125 | appendTextValue(valueNode, curr);
|
1126 | return true;
|
1127 | }
|
1128 |
|
1129 | if (
|
1130 | curr.type === tks.HARD_PAREN_OPEN
|
1131 | && !valueNode
|
1132 | ) {
|
1133 | this.flag(node, '_waitingForHardParenClose', true);
|
1134 | updateLoc(node, curr);
|
1135 | return true;
|
1136 | }
|
1137 |
|
1138 | if (curr.type === tks.HARD_PAREN_CLOSE) {
|
1139 | this.flag(node, '_waitingForHardParenClose', false);
|
1140 | this.closeNode(node);
|
1141 | updateLoc(node, curr);
|
1142 | return true;
|
1143 | }
|
1144 |
|
1145 | if (curr.type === tks.PAREN_OPEN) {
|
1146 | valueNode = this.openNode(new ExplicitExpressionNode(), node.values);
|
1147 | updateLoc(valueNode, curr);
|
1148 | return false;
|
1149 | }
|
1150 |
|
1151 | valueNode = ensureTextNode(node.values);
|
1152 |
|
1153 | if (!node._waitingForEndQuote
|
1154 | && (curr.type === tks.DOUBLE_QUOTE
|
1155 | || curr.type === tks.SINGLE_QUOTE)
|
1156 | ) {
|
1157 | this.flag(node, '_waitingForEndQuote', curr.val);
|
1158 | appendTextValue(valueNode, curr);
|
1159 | return true;
|
1160 | }
|
1161 |
|
1162 |
|
1163 |
|
1164 | appendTextValue(valueNode, curr);
|
1165 | return true;
|
1166 | }
|
1167 |
|
1168 | function updateLoc(node, token) {
|
1169 | var loc;
|
1170 | loc = new LocationNode();
|
1171 | loc.line = token.line;
|
1172 | loc.column = token.chr;
|
1173 |
|
1174 | if (node.startloc === null) {
|
1175 | node.startloc = loc;
|
1176 | }
|
1177 |
|
1178 | node.endloc = loc;
|
1179 | }
|
1180 |
|
1181 | function ensureTextNode(valueList) {
|
1182 | var valueNode = valueList[valueList.length-1];
|
1183 |
|
1184 | if (!valueNode || valueNode.type !== 'VashText') {
|
1185 | valueNode = new TextNode();
|
1186 | valueList.push(valueNode);
|
1187 | }
|
1188 |
|
1189 | return valueNode;
|
1190 | }
|
1191 |
|
1192 | function appendTextValue(textNode, token) {
|
1193 | if (!('value' in textNode)) {
|
1194 | var msg = 'Expected TextNode but found ' + textNode.type
|
1195 | + ' when appending token ' + token;
|
1196 | throw new Error(msg);
|
1197 | }
|
1198 |
|
1199 | textNode.value += token.val;
|
1200 | updateLoc(textNode, token);
|
1201 | }
|
1202 |
|
1203 | function newUnexpectedClosingTagError(parser, tok, tagName) {
|
1204 | var err = new Error(''
|
1205 | + 'Found a closing tag for a known void HTML element: '
|
1206 | + tagName + '.');
|
1207 | err.name = 'UnexpectedClosingTagError';
|
1208 | return parser.decorateError(
|
1209 | err,
|
1210 | tok.line,
|
1211 | tok.chr);
|
1212 | }
|
1213 |
|