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