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