1 module vayne.source.parser;
2 
3 
4 import std.algorithm;
5 import std.array;
6 import std.conv;
7 import std.format;
8 import std.string;
9 
10 import vayne.ast.node;
11 import vayne.source.compress;
12 import vayne.source.context;
13 import vayne.source.lexer;
14 import vayne.source.source;
15 import vayne.source.token;
16 
17 
18 struct ParserOptions {
19 	CompressOptions compress;
20 }
21 
22 
23 class ParserErrorsException : Exception {
24 	this(string[] errors) {
25 		assert(!errors.empty);
26 		super(errors[0]);
27 
28 		this.errors = errors;
29 	}
30 
31 	string[] errors;
32 }
33 
34 
35 private class ParserException : Exception {
36 	this(SourceLoc loc, string msg) {
37 		super(msg);
38 
39 		this.loc = loc;
40 	}
41 
42 	SourceLoc loc;
43 }
44 
45 
46 Node parse(ref SourceManager mgr, uint id, ParserOptions options) {
47 	return Parser(mgr, id, options)();
48 }
49 
50 
51 private struct Parser {
52 	this(ref SourceManager mgr, uint id, ParserOptions options) {
53 		source_ = mgr.get(id);
54 		mgr_ = &mgr;
55 		options_ = options;
56 
57 		settings_.init("compress", (options.compress != CompressOptions.none).to!string);
58 	}
59 
60 	Node opCall() {
61 		return parse();
62 	}
63 
64 private:
65 	Node parse() {
66 		try {
67 			if (auto root = parse(source_))
68 				return root;
69 		} catch(Exception error) {
70 			if (auto ctxError = cast(ContextException)error) {
71 				errors_ ~= format("%s: %s", mgr_.loc(ctxError.loc), error.msg);
72 			} else if (auto parserError = cast(ParserException)error) {
73 				errors_ ~= format("%s: %s", mgr_.loc(parserError.loc), error.msg);
74 			} else {
75 				errors_ ~= error.msg;
76 			}
77 		}
78 
79 		throw new ParserErrorsException(errors_);
80 	}
81 
82 	Node parse(Source source) {
83 		auto context = new Context(source);
84 
85 		insert_ = new StatementBlock(Token(context.loc));
86 
87 		const end = source.buffer.length - 2;
88 
89 		while (context.cursor < end) {
90 			auto remaining = context.remaining();
91 			auto indexOpen = remaining.indexOf("{{");
92 			if (indexOpen == -1)
93 				break;
94 
95 			text(context, remaining[0..indexOpen]);
96 			context.advance(indexOpen);
97 
98 			const triple = ((indexOpen + 1 < remaining.length) && (remaining[indexOpen + 2] == '{'));
99 			const open = triple ? "{{{" : "{{";
100 			const contentStart = indexOpen + open.length;
101 			const close = triple ? "}}}" : "}}";
102 			auto indexClose = remaining.indexOf(close, contentStart);
103 			while (indexClose != -1) {
104 				if (balancedQuotes(remaining[contentStart..indexClose]))
105 					break;
106 
107 				indexClose = remaining.indexOf(close, indexClose + close.length);
108 			}
109 
110 			if (indexClose == -1)
111 				throw new ParserException(context.loc, format("missing '%s' to close tag '%s'", close, open));
112 
113 			context.advance(close.length);
114 			indexClose -= contentStart;
115 
116 			try {
117 				compile(context, source.buffer[context.cursor..context.cursor + indexClose], open, close);
118 			} catch (Exception error) {
119 				errors_ ~= format("%s: %s", mgr_.loc(context.loc), error.msg);
120 			}
121 
122 			context.advance(indexClose + close.length);
123 		}
124 		context.expectClosed();
125 
126 		if (context.cursor > 0) {
127 			text(context, context.remaining());
128 		} else {
129 			text(context, source.buffer);
130 		}
131 
132 		outputText(context);
133 
134 		context.advance(context.remaining.length);
135 
136 		assert(insertStack_.empty);
137 
138 		if (!errors_.empty)
139 			return null;
140 
141 		return new Module(Token(context.loc), [ insert_ ]);
142 	}
143 
144 	void compile(Context context, string content, string tagOpen, string tagClose) {
145 		void ensureSimpleTag(string tag) {
146 			if (tagOpen.length != 2)
147 				throw new ParserException(context.loc, format("'%s' not supported for tag '%s'", tagOpen, tag));
148 		}
149 
150 		if (content.length > 0) {
151 			auto tag = content[0..1];
152 			switch(tag) {
153 			case "*":
154 				ensureSimpleTag(tag);
155 				outputText(context);
156 				iterate(context, content);
157 				break;
158 			case "/":
159 				ensureSimpleTag(tag);
160 				if (context.open().tag != "@")
161 					outputText(context);
162 				close(context, content);
163 				break;
164 			case "?":
165 				ensureSimpleTag(tag);
166 				outputText(context);
167 				conditional(context, content);
168 				break;
169 			case ":":
170 				ensureSimpleTag(tag);
171 				outputText(context);
172 				orElse(context, content);
173 				break;
174 			case "~":
175 				outputText(context);
176 				translate(context, content, tagOpen.length == 2);
177 				break;
178 			case ";":
179 				ensureSimpleTag(tag);
180 				meta(context, content);
181 				break;
182 			case "!":
183 				break;
184 			case "#":
185 				ensureSimpleTag(tag);
186 				outputText(context);
187 				define(context, content);
188 				break;
189 			case "@":
190 				ensureSimpleTag(tag);
191 				withs(context, content);
192 				break;
193 			case "&":
194 				ensureSimpleTag(tag);
195 				break;
196 			default:
197 				outputText(context);
198 				interpolate(context, content, tagOpen.length == 2);
199 				break;
200 			}
201 		}
202 	}
203 
204 	void iterate(Context context, string content) {
205 		context.open(content[0..1], content[1..$]);
206 		content = content[1..$].strip();
207 
208 		auto loopStmt = parseLoop(Source(source_.id, source_.parent, content), context.loc);
209 		auto bodyBlock = create!StatementBlock(Token(context.loc));
210 		loopStmt.children[2] = bodyBlock;
211 		insert_.children ~= loopStmt;
212 
213 		insertStack_ ~= insert_;
214 		insert_ = bodyBlock;
215 	}
216 
217 	void close(Context context, string content) {
218 		context.close();
219 
220 		if (insertStack_.length) {
221 			insert_ = insertStack_.back;
222 			insertStack_.popBack;
223 		} else {
224 			assert(errors_.length);
225 		}
226 	}
227 
228 	void conditional(Context context, string content) {
229 		context.open(content[0..1], content[1..$]);
230 		content = content[1..$].strip;
231 
232 		auto ifStmt = create!IfStatement(Token(context.loc), parseExpr(Source(source_.id, source_.parent, content), context.loc), create!StatementBlock(Token(context.loc)), null);
233 		insert_.children ~= ifStmt;
234 
235 		insertStack_ ~= insert_;
236 		insert_ = cast(StatementBlock)ifStmt.children[1];
237 	}
238 
239 	void orElse(Context context, string content) {
240 		context.expectOpen("?", ":");
241 		content = content[1..$].strip;
242 
243 		if (insertStack_.length) {
244 			insert_ = insertStack_.back;
245 			insertStack_.popBack;
246 		} else {
247 			assert(errors_.length);
248 			auto ifStmt = cast(IfStatement)insert_.children.back;
249 			if (!ifStmt)
250 				insert_.children ~= create!IfStatement(Token(context.loc), null, create!StatementBlock(Token(context.loc)), null);
251 		}
252 
253 		auto ifStmt = cast(IfStatement)insert_.children.back;
254 		if (ifStmt is null)
255 			ifStmt = create!IfStatement(Token(context.loc), null, create!StatementBlock(Token(context.loc)), null);
256 
257 		while (ifStmt.children[2] !is null) {
258 			ifStmt = cast(IfStatement)ifStmt.children[2];
259 			if (ifStmt is null)
260 				throw new ParserException(context.loc, "unexpected else statement");
261 		}
262 
263 		if (content.length) {
264 			assert(ifStmt.children[2] is null);
265 
266 			auto elseIfStmt = create!IfStatement(Token(context.loc), parseExpr(Source(source_.id, source_.parent, content), context.loc), create!StatementBlock(Token(context.loc)), null);
267 			ifStmt.children[2] = elseIfStmt;
268 
269 			insertStack_ ~= insert_;
270 			insert_ = cast(StatementBlock)elseIfStmt.children[1];
271 		} else {
272 			assert(ifStmt.children[2] is null);
273 
274 			auto elseBlock = create!StatementBlock(Token(context.loc));
275 			ifStmt.children[2] = elseBlock;
276 
277 			insertStack_ ~= insert_;
278 			insert_ = cast(StatementBlock)ifStmt.children[2];
279 		}
280 	}
281 
282 	void meta(Context context, string content) {
283 		content = content[1..$].strip;
284 		auto values = content.splitter(':');
285 		auto type = values.front.strip;
286 		values.popFront;
287 
288 		switch (type) {
289 		case "src":
290 			auto id = values.front.to!uint;
291 			values.popFront;
292 
293 			auto line = values.front.to!uint;
294 			values.popFront;
295 
296 			auto column = values.front.splitter(' ').front.to!uint;
297 			context.loc = SourceLoc(id, line, column);
298 			break;
299 		default:
300 			throw new ParserException(context.loc, format("unknown meta type '%s'", type));
301 		}
302 	}
303 
304 	void define(Context context, string content) {
305 		auto lex = Lexer(Source(source_.id, source_.parent, content[1..$].strip));
306 		auto tok = lex.front;
307 		lex.popFront;
308 
309 		switch (tok.kindKeyword) with (Token.KeywordKind) {
310 		case Set:
311 			auto name = lex.front;
312 			lex.popFront;
313 
314 			auto value = lex.front;
315 			lex.popFront;
316 
317 			settings_.set(context, name.value, value.value);
318 			break;
319 		case Push:
320 			auto name = lex.front;
321 			lex.popFront;
322 
323 			auto value = lex.front;
324 			lex.popFront;
325 
326 			settings_.push(context, name.value, value.value);
327 			break;
328 		case Pop:
329 			auto name = lex.front;
330 			lex.popFront;
331 
332 			settings_.pop(context, name.value);
333 			break;
334 		default:
335 			throw new ParserException(context.loc, format("unknown compile-time operation '%s'", tok.kindKeyword));
336 		}
337 	}
338 
339 	void withs(Context context, string content) {
340 		context.open(content[0..1], content[1..$]);
341 		content = content[1..$].strip;
342 
343 		auto withStmt = parseWith(Source(source_.id, source_.parent, content), context.loc);
344 		auto bodyBlock = create!StatementBlock(Token(context.loc));
345 		withStmt.children[$-1] = bodyBlock;
346 		insert_.children ~= withStmt;
347 
348 		insertStack_ ~= insert_;
349 		insert_ = bodyBlock;
350 	}
351 
352 	Node escapeHTML(Node text, SourceLoc loc) {
353 		auto escape = create!Identifier(Token(Token.Kind.Identifier, "__escape", loc));
354 		auto html = cast(Node)create!Constant(Token("html", Token.LiteralKind.String, 0, 0, loc));
355 
356 		return cast(Node)create!FunctionCall(Token(loc), escape, [ text, html ]);
357 	}
358 
359 	void translate(Context context, string content, bool autoEscape) {
360 		content = content[1..$].strip;
361 
362 		auto args = cast(Node[])parseExprList(Source(source_.id, source_.parent, content), context.loc);
363 		auto translateFunc = create!Identifier(Token(Token.Kind.Identifier, "__translate", context.loc));
364 
365 		auto translated = cast(Node)create!FunctionCall(Token(context.loc), translateFunc, args);
366 		if (autoEscape)
367 			translated = escapeHTML(translated, context.loc);
368 		insert_.children ~= create!Output(Token(context.loc), translated);
369 	}
370 
371 	void interpolate(Context context, string content, bool autoEscape) {
372 		auto args = cast(Node[])parseExprList(Source(source_.id, source_.parent, content), context.loc);
373 		foreach (arg; args) {
374 			if (autoEscape)
375 				arg = escapeHTML(arg, context.loc);
376 			insert_.children ~= create!Output(Token(context.loc), arg);
377 		}
378 	}
379 
380 	void text(Context context, string content) {
381 		text_ ~= content;
382 	}
383 
384 	void outputText(Context context) {
385 		if (text_.length) {
386 			auto compressionEnabled = settings_.get!bool(context, "compress");
387 			auto text = (compressionEnabled ? compress(cast(string)text_, options_.compress).idup : text_.idup).replace("\r", "");
388 			if (text.length)
389 				insert_.children ~= create!Output(Token(context.loc), create!Constant(Token(text, Token.LiteralKind.String, 0, 0, context.loc)));
390 			text_.length = 0;
391 		}
392 	}
393 
394 	Source source_;
395 
396 	StatementBlock insert_;
397 	StatementBlock[] insertStack_;
398 
399 	SourceManager* mgr_;
400 	ParserOptions options_;
401 
402 	ParserSettingsStack settings_;
403 
404 	char[] text_;
405 	string[] errors_;
406 }
407 
408 
409 @property bool isFalsy(string x) {
410 	return (x.empty || (x == "0") || (x.toLower == "no") || (x.toLower == "false"));
411 }
412 
413 
414 @property bool isTruthy(string x) {
415 	return !x.isFalsy;
416 }
417 
418 
419 struct ParserSettingsStack {
420 	import std.traits : Unqual;
421 	auto get(T)(Context context, string name) {
422 		if (auto pstack = name in stack_) {
423 			static if (is(Unqual!T == bool)) {
424 				return (*pstack).back.isTruthy;
425 			} else {
426 				return (*pstack).back.to!T;
427 			}
428 		}
429 
430 		throw new ParserException(context.loc, format("unknown compile-time setting '%s'", name));
431 	}
432 
433 	void init(string name, string value) {
434 		stack_[name] = [ value ];
435 	}
436 
437 	void set(Context context, string name, string value) {
438 		if (auto pstack = name in stack_) {
439 			(*pstack).back = value;
440 			return;
441 		}
442 
443 		throw new ParserException(context.loc, format("unknown compile-time setting '%s'", name));
444 	}
445 
446 	void push(Context context, string name, string value) {
447 		if (auto pstack = name in stack_) {
448 			*pstack ~= value;
449 			return;
450 		}
451 
452 		throw new ParserException(context.loc, format("unknown compile-time setting '%s'", name));
453 	}
454 
455 	void pop(Context context, string name) {
456 		if (auto pstack = name in stack_) {
457 			if (pstack.length > 1) {
458 				(*pstack).popBack;
459 				return;
460 			} else {
461 				throw new ParserException(context.loc, format("compile-time setting stack underflow for '%s'", name));
462 			}
463 		}
464 
465 		throw new ParserException(context.loc, format("unknown compile-time setting '%s'", name));
466 	}
467 
468 	private string[][string] stack_;
469 }
470 
471 
472 private class ExprParserException : Exception {
473 	this(Token tok, string msg) {
474 		super(msg);
475 		this.tok = tok;
476 	}
477 
478 	Token tok;
479 }
480 
481 
482 auto parseExpr(Source source, SourceLoc loc) {
483 	auto parser = ExprParser(source, loc);
484 	scope (success) parser.ensureEndOfInput();
485 	return parser.parseExpression();
486 }
487 
488 
489 auto parseExprList(Source source, SourceLoc loc) {
490 	auto parser = ExprParser(source, loc);
491 	scope (success) parser.ensureEndOfInput();
492 	return parser.parseExpressionList();
493 }
494 
495 
496 auto parseLoop(Source source, SourceLoc loc) {
497 	auto parser = ExprParser(source, loc);
498 	scope (success) parser.ensureEndOfInput();
499 	return parser.parseLoopStatement(loc);
500 }
501 
502 
503 auto parseWith(Source source, SourceLoc loc) {
504 	auto parser = ExprParser(source, loc);
505 	scope (success) parser.ensureEndOfInput();
506 	return parser.parseWithStatement(loc);
507 }
508 
509 
510 private struct ExprParser {
511 	this(Source source, SourceLoc loc) {
512 		lexer_ = Lexer(source, loc);
513 
514 		warmUp();
515 	}
516 
517 	Expression parseExpression() {
518 		auto start = tok_;
519 
520 		if (auto left = parseExpressionPrimary()) {
521 			auto expr = parseBinaryOp(left, 0);
522 			if (!expr)
523 				expr = left;
524 
525 			if (auto cond = parseConditional(expr))
526 				return create!Expression(start, cond);
527 			return create!Expression(start, expr);
528 		}
529 		return null;
530 	}
531 
532 	Expression[] parseExpressionList() {
533 		if (auto expr = parseExpression) {
534 			Expression[] exprs;
535 			exprs ~= expr;
536 
537 			while (tok_.sep(',')) {
538 				eat();
539 				if (auto next = parseExpression()) {
540 					exprs ~= next;
541 					continue;
542 				}
543 				throw new ExprParserException(tok_, format("expected an expression following ',', not '%s'", tok_));
544 			}
545 			return exprs;
546 		}
547 		return null;
548 	}
549 
550 	WithStatement parseWithStatement(SourceLoc loc) {
551 		if (auto expr = parseWithExpression) {
552 			WithExpression[] exprs;
553 			exprs ~= expr;
554 
555 			while (tok_.sep(',')) {
556 				eat();
557 				if (auto next = parseWithExpression()) {
558 					exprs ~= next;
559 					continue;
560 				}
561 				throw new ExprParserException(tok_, format("expected a with expression following ',', not '%s'", tok_));
562 			}
563 			return create!WithStatement(Token(loc), cast(Node[])exprs, null);
564 		}
565 		return null;
566 	}
567 
568 	WithExpression parseWithExpression() {
569 		if (auto expr = parseExpression()) {
570 			Token name;
571 			if (tok_.keyword(Token.KeywordKind.As)) {
572 				eat();
573 				if (!tok_.ident())
574 					throw new ExprParserException(tok_, format("expected an identifier following 'as', not '%s'", tok_));
575 				name = eat();
576 			}
577 			return create!WithExpression(expr.tok, expr, name);
578 		}
579 		return null;
580 	}
581 
582 	LoopStatement parseLoopStatement(SourceLoc loc) {
583 		if (!tok_.ident)
584 			throw new ExprParserException(tok_, format("expected an identifier, not '%s'", tok_));
585 
586 		Token key = eat();
587 		Token name;
588 		if (tok_.sep(',')) {
589 			eat();
590 			if (!tok_.ident)
591 				throw new ExprParserException(tok_, format("expected an identifier, not '%s'", tok_));
592 			name = eat();
593 			if (!tok_.sep(';'))
594 				throw new ExprParserException(tok_, format("expected ';', not '%s'", tok_));
595 			eat();
596 		} else if (tok_.sep(';')) {
597 			eat();
598 			swap(key, name);
599 		} else {
600 			throw new ExprParserException(tok_, format("expected ';' or ',' followed by an identifier, not '%s'", tok_));
601 		}
602 
603 		Expression obj;
604 		Expression end;
605 
606 		obj = parseExpression();
607 		if (!obj)
608 			throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_));
609 
610 		if (tok_.name == "..") {
611 			eat();
612 			end = parseExpression();
613 			if (!end)
614 				throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_));
615 		}
616 
617 		return create!LoopStatement(Token(loc), key, name, obj, end, null);
618 	}
619 
620 	Node parseExpressionPrimary() {
621 		if (auto lexpr = parseExpressionPrimarySimple()) {
622 			if (tok_.sep()) {
623 				if (auto primary = parseSuffixOp(lexpr)) {
624 					while (!tok_.eoi() && tok_.sep()) {
625 						if (auto suffix = parseSuffixOp(primary)) {
626 							primary = suffix;
627 							continue;
628 						}
629 						return primary;
630 					}
631 					return primary;
632 				}
633 			}
634 			return lexpr;
635 		}
636 		return null;
637 	}
638 
639 	Node parseExpressionPrimarySimple() {
640 		switch (tok_.kind) with (Token.Kind) {
641 		case Identifier:
642 			return parseIdentifierExpr();
643 		case Separator:
644 			if (tok_.sep('(')) {
645 				auto start = eat();
646 				auto expr = parseExpression();
647 				if (!expr)
648 					throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_));
649 
650 				close(')');
651 				return create!Expression(start, expr, true);
652 			}
653 
654 			if (auto expr = parseUnaryOp())
655 				return expr;
656 			if (auto expr = parsePrefixOp())
657 				return expr;
658 			break;
659 		case Literal:
660 			return parseLiteralExpr();
661 		case Keyword:
662 			switch (tok_.kindKeyword) with (Token.KeywordKind) {
663 			case True:
664 				return create!Constant(eat());
665 			case False:
666 				return create!Constant(eat());
667 			case Null:
668 				return create!Constant(eat());
669 			default:
670 				throw new ExprParserException(tok_, format("unexpected '%s'", tok_));
671 			}
672 		case Undefined:
673 		case EndOfInput:
674 		default:
675 			break;
676 		}
677 
678 		return null;
679 	}
680 
681 	Node parseIdentifierExpr() {
682 		assert(tok_.ident());
683 		return create!Identifier(eat());
684 	}
685 
686 	bool isUnaryOp(Token tok) const {
687 		if (tok.sep()) {
688 			switch (tok.length) {
689 			case 1:
690 				switch(tok.front) {
691 				case '-':
692 				case '+':
693 				case '!':
694 				case '~':
695 				case '^':
696 				case '*':
697 					return true;
698 				default:
699 					break;
700 				}
701 				break;
702 			default:
703 				break;
704 			}
705 		}
706 		return false;
707 	}
708 
709 	UnaryOp parseUnaryOp() {
710 		assert(tok_.sep());
711 		if (isUnaryOp(tok_)) {
712 			auto op = eat();
713 			auto expr = parseExpressionPrimary();
714 			if (!expr)
715 				throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_));
716 			return create!UnaryOp(op, expr);
717 		}
718 		return null;
719 	}
720 
721 	Node parsePrefixOp() {
722 		switch(tok_.name) {
723 		case "--":
724 		case "++":
725 			auto op = eat();
726 			auto expr = parseExpressionPrimary();
727 			if (!expr)
728 				throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_));
729 			return create!PrefixOp(op, expr);
730 		default:
731 			break;
732 		}
733 		return null;
734 	}
735 
736 	Node parseSuffixOp(Node expr) {
737 		switch(tok_.name) {
738 		case "[":
739 			auto op = eat();
740 			auto index = parseExpression();
741 			Expression end;
742 			if (!index)
743 				throw new ExprParserException(tok_, format("expected an index expression, not '%s'", tok_));
744 
745 			if (tok_.sep("..")) {
746 				eat();
747 				end = parseExpression;
748 				if (!end)
749 					throw new ExprParserException(tok_, format("expected an expression following '..', not '%s'", tok_));
750 			}
751 			close(']');
752 
753 			if (end is null)
754 				return create!IndexOp(op, expr, index);
755 			return create!SliceOp(op, expr, index, end);
756 		case ".":
757 			auto op = eat();
758 			if (!tok_.ident())
759 				throw new ExprParserException(tok_, format("expected an identifier following '.', not '%s'", tok_));
760 			auto ident = eat();
761 			return create!DispatchOp(op, expr, ident);
762 		case "(":
763 			auto op = eat();
764 
765 			Node[] args;
766 
767 			if (!tok_.sep(')')) {
768 				while (true) {
769 					if (auto arg = parseExpression()) {
770 						args ~= arg;
771 						if (tok_.sep(')'))
772 							break;
773 						if (tok_.sep(',')) {
774 							eat();
775 							continue;
776 						}
777 
778 						throw new ExprParserException(tok_, format("expected ')' or ',' not '%s'", tok_));
779 					}
780 					break;
781 				}
782 			}
783 
784 			close(')');
785 			return create!FunctionCall(op, expr, args);
786 		case "!":
787 			auto op = eat();
788 
789 			Node[] args;
790 			if (tok_.literal()) {
791 				args ~= parseLiteralExpr();
792 			} else if (tok_.ident()) {
793 				args ~= parseIdentifierExpr();
794 			} else {
795 				throw new ExprParserException(tok_, format("expected a literal or an identifier following '!', not '%s'", tok_));
796 			}
797 
798 			return create!FunctionCall(op, expr, args);
799 		case "--":
800 		case "++":
801 			auto op = eat();
802 			return create!SuffixOp(op, expr);
803 		default:
804 			break;
805 		}
806 		return null;
807 	}
808 
809 	Constant parseLiteralExpr() {
810 		assert(tok_.literal());
811 		return create!Constant(eat());
812 	}
813 
814 	Node parseUnaryExpr() {
815 		return null;
816 	}
817 
818 	enum OperatorPriority : size_t {
819 		Logic		= 10,
820 		Compare		= 20,
821 		Arithmetic	= 30,
822 		Algebraic	= 40,
823 		Bitwise		= 50,
824 	}
825 
826 	size_t isBinaryOp(string name) const {
827 		switch(name.front) with (OperatorPriority) {
828 		case '&':
829 		case '|':
830 			return (name.length == 1) ? 0/*Bitwise*/ : Logic;
831 		case '>':   // >, >=, >>, >>=
832 		case '<':   // <, <=, <<, <<=
833 			return ((name.length == 1) || (name[1] == '=')) ? Compare : 0/*Bitwise*/;
834 		case '^':   // ^, ^=, ^^, ^^=
835 			return ((name.length == 1) || (name[1] == '=')) ? 0/*Bitwise*/ : Algebraic;
836 		case '+':
837 		case '-':
838 			return (name.length == 1) ? Arithmetic : 0;
839 		case '~':
840 		case '*':
841 		case '/':
842 		case '%':
843 			return (name.length == 1) ? Algebraic : 0;
844 		case '=':
845 			return (name.length == 2) ? Compare : 0;
846 		case '!':
847 			return (name.length == 2) ? Compare : 0;
848 		default:
849 			if ((name.length == 2) && (name == "in"))
850 				return Compare/*Bitwise*/;
851 			break;
852 		}
853 		return 0;
854 	}
855 
856 	Node parseBinaryOp(Node left, size_t prioExpr) {
857 		while (true) {
858 			auto prio = isBinaryOp(tok_.name);
859 			if (!prio || (prio < prioExpr))
860 				return left;
861 			auto op = eat();
862 			auto right = parseExpressionPrimary();
863 			if (!right)
864 				return null;
865 			auto prioNext = isBinaryOp(tok_.name);
866 			if (prio < prioNext) {
867 				right = parseBinaryOp(right, prio + 1);
868 				if (!right)
869 					return null;
870 			}
871 			left = create!BinaryOp(op, left, right);
872 		}
873 	}
874 
875 	Node parseConditional(Node expr) {
876 		if (tok_.sep('?')) {
877 			auto op = eat();
878 			Node trueCase = parseExpression();
879 			Node falseCase = null;
880 			if (trueCase) {
881 				if (tok_.sep(':')) {
882 					eat();
883 					falseCase = parseExpression();
884 					if (!falseCase)
885 						throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_));
886 				} else {
887 					throw new ExprParserException(tok_, format("expected ':', not '%s'", tok_));
888 				}
889 			} else {
890 				throw new ExprParserException(tok_, format("expected an expression, not '%s'", tok_));
891 			}
892 
893 			return create!ConditionalExpression(op, expr, trueCase, falseCase);
894 		}
895 
896 		return null;
897 	}
898 
899 	void warmUp() {
900 		tok_ = lexer_.front;
901 		lexer_.popFront;
902 
903 		if (!lexer_.empty) {
904 			foreach (ref atok; ahead_) {
905 				atok = lexer_.front;
906 				lexer_.popFront;
907 				if (lexer_.empty)
908 					return;
909 			}
910 		}
911 	}
912 
913 	Token eat() {
914 		if (!tok_.eoi) {
915 			foreach (i; 1..behind_.length)
916 				behind_[i] = behind_[i - 1];
917 
918 			behind_[0] = tok_;
919 			tok_ = ahead_[0];
920 			foreach (i; 1..ahead_.length)
921 				ahead_[i - 1] = ahead_[i];
922 
923 			if (!lexer_.empty) {
924 				ahead_[$ - 1] = lexer_.front;
925 				lexer_.popFront;
926 			}
927 
928 			return behind_[0];
929 		}
930 
931 		return tok_;
932 	}
933 
934 	void open(char separator, Token by) {
935 		if (!tok_.sep(separator)) {
936 			throw new ExprParserException(tok_, format("expected '%s' following '%s', not '%s'", separator, by, tok_));
937 		} else {
938 			eat();
939 		}
940 	}
941 
942 	void close(char separator) {
943 		if (!tok_.sep(separator)) {
944 			throw new ExprParserException(tok_, format("expected '%s', not '%s'", separator, tok_));
945 		} else {
946 			eat();
947 		}
948 	}
949 
950 	void ensureEndOfInput() {
951 		if (!tok_.eoi)
952 			throw new ExprParserException(tok_, format("unexpected '%s'", tok_));
953 	}
954 
955 private:
956 	Token tok_;
957 	Token[1] behind_;
958 	Token[1] ahead_;
959 	Lexer lexer_;
960 }