1 module dud.sdlang.parser;
2 
3 import std.typecons : RefCounted, refCounted;
4 import std.format : format;
5 import dud.sdlang.ast;
6 import dud.sdlang.tokenmodule;
7 
8 import dud.sdlang.lexer;
9 
10 import dud.sdlang.exception;
11 
12 struct Parser {
13 @safe pure:
14 
15 	import std.array : appender;
16 
17 	import std.format : formattedWrite;
18 
19 	Lexer lex;
20 
21 	this(Lexer lex) {
22 		this.lex = lex;
23 	}
24 
25 	bool firstRoot() const pure @nogc @safe {
26 		return this.firstTags()
27 			 || this.firstTagTerminator()
28 			 || this.lex.front.type == TokenType.eof;
29 	}
30 
31 	Root parseRoot() {
32 		try {
33 			return this.parseRootImpl();
34 		} catch(ParseException e) {
35 			throw new ParseException(
36 				"While parsing a Root an Exception was thrown.",
37 				e, __FILE__, __LINE__
38 			);
39 		}
40 	}
41 
42 	Root parseRootImpl() {
43 		string[] subRules;
44 		subRules = ["T"];
45 		if(this.firstTags()) {
46 			Tags tags = this.parseTags();
47 			subRules = ["T"];
48 			if(this.lex.front.type == TokenType.eof) {
49 				this.lex.popFront();
50 
51 				return new Root(RootEnum.T
52 					, tags
53 				);
54 			}
55 			auto app = appender!string();
56 			formattedWrite(app, 
57 				"In 'Root' found a '%s' while looking for", 
58 				this.lex.front
59 			);
60 			throw new ParseException(app.data,
61 				__FILE__, __LINE__,
62 				subRules,
63 				["eof"]
64 			);
65 
66 		} else if(this.firstTagTerminator()) {
67 			this.parseTagTerminator();
68 			subRules = ["TT"];
69 			if(this.firstTags()) {
70 				Tags tags = this.parseTags();
71 				subRules = ["TT"];
72 				if(this.lex.front.type == TokenType.eof) {
73 					this.lex.popFront();
74 
75 					return new Root(RootEnum.TT
76 						, tags
77 					);
78 				}
79 				auto app = appender!string();
80 				formattedWrite(app, 
81 					"In 'Root' found a '%s' while looking for", 
82 					this.lex.front
83 				);
84 				throw new ParseException(app.data,
85 					__FILE__, __LINE__,
86 					subRules,
87 					["eof"]
88 				);
89 
90 			}
91 			auto app = appender!string();
92 			formattedWrite(app, 
93 				"In 'Root' found a '%s' while looking for", 
94 				this.lex.front
95 			);
96 			throw new ParseException(app.data,
97 				__FILE__, __LINE__,
98 				subRules,
99 				["ident -> Tag","lcurly -> Tag","value -> Tag"]
100 			);
101 
102 		} else if(this.lex.front.type == TokenType.eof) {
103 			this.lex.popFront();
104 
105 			return new Root(RootEnum.E
106 			);
107 		}
108 		auto app = appender!string();
109 		formattedWrite(app, 
110 			"In 'Root' found a '%s' while looking for", 
111 			this.lex.front
112 		);
113 		throw new ParseException(app.data,
114 			__FILE__, __LINE__,
115 			subRules,
116 			["ident -> Tag","lcurly -> Tag","value -> Tag","eol","semicolon","eof"]
117 		);
118 
119 	}
120 
121 	bool firstTags() const pure @nogc @safe {
122 		return this.firstTag();
123 	}
124 
125 	Tags parseTags() {
126 		try {
127 			return this.parseTagsImpl();
128 		} catch(ParseException e) {
129 			throw new ParseException(
130 				"While parsing a Tags an Exception was thrown.",
131 				e, __FILE__, __LINE__
132 			);
133 		}
134 	}
135 
136 	Tags parseTagsImpl() {
137 		string[] subRules;
138 		subRules = ["Tag", "TagFollow"];
139 		if(this.firstTag()) {
140 			Tag cur = this.parseTag();
141 			subRules = ["TagFollow"];
142 			if(this.firstTags()) {
143 				Tags follow = this.parseTags();
144 
145 				return new Tags(TagsEnum.TagFollow
146 					, cur
147 					, follow
148 				);
149 			}
150 			return new Tags(TagsEnum.Tag
151 				, cur
152 			);
153 		}
154 		auto app = appender!string();
155 		formattedWrite(app, 
156 			"In 'Tags' found a '%s' while looking for", 
157 			this.lex.front
158 		);
159 		throw new ParseException(app.data,
160 			__FILE__, __LINE__,
161 			subRules,
162 			["ident -> IDFull","lcurly -> OptChild","value -> Values"]
163 		);
164 
165 	}
166 
167 	bool firstTag() const pure @nogc @safe {
168 		return this.firstIDFull()
169 			 || this.firstValues()
170 			 || this.firstOptChild();
171 	}
172 
173 	Tag parseTag() {
174 		try {
175 			return this.parseTagImpl();
176 		} catch(ParseException e) {
177 			throw new ParseException(
178 				"While parsing a Tag an Exception was thrown.",
179 				e, __FILE__, __LINE__
180 			);
181 		}
182 	}
183 
184 	Tag parseTagImpl() {
185 		string[] subRules;
186 		subRules = ["IA", "IAO", "IAOT", "IAT", "IE", "IET", "IO", "IOT", "IV", "IVA", "IVAO", "IVAOT", "IVAT", "IVO", "IVOT", "IVT"];
187 		if(this.firstIDFull()) {
188 			IDFull id = this.parseIDFull();
189 			subRules = ["IV", "IVA", "IVAO", "IVAOT", "IVAT", "IVO", "IVOT", "IVT"];
190 			if(this.firstValues()) {
191 				Values vals = this.parseValues();
192 				subRules = ["IVA", "IVAO", "IVAOT", "IVAT"];
193 				if(this.firstAttributes()) {
194 					Attributes attrs = this.parseAttributes();
195 					subRules = ["IVAO", "IVAOT"];
196 					if(this.firstOptChild()) {
197 						OptChild oc = this.parseOptChild();
198 						subRules = ["IVAOT"];
199 						if(this.firstTagTerminator()) {
200 							this.parseTagTerminator();
201 
202 							return new Tag(TagEnum.IVAOT
203 								, id
204 								, vals
205 								, attrs
206 								, oc
207 							);
208 						}
209 						return new Tag(TagEnum.IVAO
210 							, id
211 							, vals
212 							, attrs
213 							, oc
214 						);
215 					} else if(this.firstTagTerminator()) {
216 						this.parseTagTerminator();
217 
218 						return new Tag(TagEnum.IVAT
219 							, id
220 							, vals
221 							, attrs
222 						);
223 					}
224 					return new Tag(TagEnum.IVA
225 						, id
226 						, vals
227 						, attrs
228 					);
229 				} else if(this.firstOptChild()) {
230 					OptChild oc = this.parseOptChild();
231 					subRules = ["IVOT"];
232 					if(this.firstTagTerminator()) {
233 						this.parseTagTerminator();
234 
235 						return new Tag(TagEnum.IVOT
236 							, id
237 							, vals
238 							, oc
239 						);
240 					}
241 					return new Tag(TagEnum.IVO
242 						, id
243 						, vals
244 						, oc
245 					);
246 				} else if(this.firstTagTerminator()) {
247 					this.parseTagTerminator();
248 
249 					return new Tag(TagEnum.IVT
250 						, id
251 						, vals
252 					);
253 				}
254 				return new Tag(TagEnum.IV
255 					, id
256 					, vals
257 				);
258 			} else if(this.firstAttributes()) {
259 				Attributes attrs = this.parseAttributes();
260 				subRules = ["IAO", "IAOT"];
261 				if(this.firstOptChild()) {
262 					OptChild oc = this.parseOptChild();
263 					subRules = ["IAOT"];
264 					if(this.firstTagTerminator()) {
265 						this.parseTagTerminator();
266 
267 						return new Tag(TagEnum.IAOT
268 							, id
269 							, attrs
270 							, oc
271 						);
272 					}
273 					return new Tag(TagEnum.IAO
274 						, id
275 						, attrs
276 						, oc
277 					);
278 				} else if(this.firstTagTerminator()) {
279 					this.parseTagTerminator();
280 
281 					return new Tag(TagEnum.IAT
282 						, id
283 						, attrs
284 					);
285 				}
286 				return new Tag(TagEnum.IA
287 					, id
288 					, attrs
289 				);
290 			} else if(this.firstOptChild()) {
291 				OptChild oc = this.parseOptChild();
292 				subRules = ["IOT"];
293 				if(this.firstTagTerminator()) {
294 					this.parseTagTerminator();
295 
296 					return new Tag(TagEnum.IOT
297 						, id
298 						, oc
299 					);
300 				}
301 				return new Tag(TagEnum.IO
302 					, id
303 					, oc
304 				);
305 			} else if(this.firstTagTerminator()) {
306 				this.parseTagTerminator();
307 
308 				return new Tag(TagEnum.IET
309 					, id
310 				);
311 			}
312 			return new Tag(TagEnum.IE
313 				, id
314 			);
315 		} else if(this.firstValues()) {
316 			Values vals = this.parseValues();
317 			subRules = ["VA", "VAO", "VAOT", "VAT"];
318 			if(this.firstAttributes()) {
319 				Attributes attrs = this.parseAttributes();
320 				subRules = ["VAO", "VAOT"];
321 				if(this.firstOptChild()) {
322 					OptChild oc = this.parseOptChild();
323 					subRules = ["VAOT"];
324 					if(this.firstTagTerminator()) {
325 						this.parseTagTerminator();
326 
327 						return new Tag(TagEnum.VAOT
328 							, vals
329 							, attrs
330 							, oc
331 						);
332 					}
333 					return new Tag(TagEnum.VAO
334 						, vals
335 						, attrs
336 						, oc
337 					);
338 				} else if(this.firstTagTerminator()) {
339 					this.parseTagTerminator();
340 
341 					return new Tag(TagEnum.VAT
342 						, vals
343 						, attrs
344 					);
345 				}
346 				return new Tag(TagEnum.VA
347 					, vals
348 					, attrs
349 				);
350 			} else if(this.firstOptChild()) {
351 				OptChild oc = this.parseOptChild();
352 				subRules = ["VOT"];
353 				if(this.firstTagTerminator()) {
354 					this.parseTagTerminator();
355 
356 					return new Tag(TagEnum.VOT
357 						, vals
358 						, oc
359 					);
360 				}
361 				return new Tag(TagEnum.VO
362 					, vals
363 					, oc
364 				);
365 			} else if(this.firstTagTerminator()) {
366 				this.parseTagTerminator();
367 
368 				return new Tag(TagEnum.VT
369 					, vals
370 				);
371 			}
372 			return new Tag(TagEnum.V
373 				, vals
374 			);
375 		} else if(this.firstOptChild()) {
376 			OptChild oc = this.parseOptChild();
377 			subRules = ["OT"];
378 			if(this.firstTagTerminator()) {
379 				this.parseTagTerminator();
380 
381 				return new Tag(TagEnum.OT
382 					, oc
383 				);
384 			}
385 			return new Tag(TagEnum.O
386 				, oc
387 			);
388 		}
389 		auto app = appender!string();
390 		formattedWrite(app, 
391 			"In 'Tag' found a '%s' while looking for", 
392 			this.lex.front
393 		);
394 		throw new ParseException(app.data,
395 			__FILE__, __LINE__,
396 			subRules,
397 			["ident","value","lcurly"]
398 		);
399 
400 	}
401 
402 	bool firstIDFull() const pure @nogc @safe {
403 		return this.lex.front.type == TokenType.ident;
404 	}
405 
406 	IDFull parseIDFull() {
407 		try {
408 			return this.parseIDFullImpl();
409 		} catch(ParseException e) {
410 			throw new ParseException(
411 				"While parsing a IDFull an Exception was thrown.",
412 				e, __FILE__, __LINE__
413 			);
414 		}
415 	}
416 
417 	IDFull parseIDFullImpl() {
418 		string[] subRules;
419 		subRules = ["L", "S"];
420 		if(this.lex.front.type == TokenType.ident) {
421 			Token cur = this.lex.front;
422 			this.lex.popFront();
423 			subRules = ["L"];
424 			if(this.lex.front.type == TokenType.colon) {
425 				this.lex.popFront();
426 				subRules = ["L"];
427 				if(this.firstIDFull()) {
428 					IDFull follow = this.parseIDFull();
429 
430 					return new IDFull(IDFullEnum.L
431 						, cur
432 						, follow
433 					);
434 				}
435 				auto app = appender!string();
436 				formattedWrite(app, 
437 					"In 'IDFull' found a '%s' while looking for", 
438 					this.lex.front
439 				);
440 				throw new ParseException(app.data,
441 					__FILE__, __LINE__,
442 					subRules,
443 					["ident"]
444 				);
445 
446 			}
447 			return new IDFull(IDFullEnum.S
448 				, cur
449 			);
450 		}
451 		auto app = appender!string();
452 		formattedWrite(app, 
453 			"In 'IDFull' found a '%s' while looking for", 
454 			this.lex.front
455 		);
456 		throw new ParseException(app.data,
457 			__FILE__, __LINE__,
458 			subRules,
459 			["ident"]
460 		);
461 
462 	}
463 
464 	bool firstValues() const pure @nogc @safe {
465 		return this.lex.front.type == TokenType.value;
466 	}
467 
468 	Values parseValues() {
469 		try {
470 			return this.parseValuesImpl();
471 		} catch(ParseException e) {
472 			throw new ParseException(
473 				"While parsing a Values an Exception was thrown.",
474 				e, __FILE__, __LINE__
475 			);
476 		}
477 	}
478 
479 	Values parseValuesImpl() {
480 		string[] subRules;
481 		subRules = ["Value", "ValueFollow"];
482 		if(this.lex.front.type == TokenType.value) {
483 			Token cur = this.lex.front;
484 			this.lex.popFront();
485 			subRules = ["ValueFollow"];
486 			if(this.firstValues()) {
487 				Values follow = this.parseValues();
488 
489 				return new Values(ValuesEnum.ValueFollow
490 					, cur
491 					, follow
492 				);
493 			}
494 			return new Values(ValuesEnum.Value
495 				, cur
496 			);
497 		}
498 		auto app = appender!string();
499 		formattedWrite(app, 
500 			"In 'Values' found a '%s' while looking for", 
501 			this.lex.front
502 		);
503 		throw new ParseException(app.data,
504 			__FILE__, __LINE__,
505 			subRules,
506 			["value"]
507 		);
508 
509 	}
510 
511 	bool firstAttributes() const pure @nogc @safe {
512 		return this.firstAttribute();
513 	}
514 
515 	Attributes parseAttributes() {
516 		try {
517 			return this.parseAttributesImpl();
518 		} catch(ParseException e) {
519 			throw new ParseException(
520 				"While parsing a Attributes an Exception was thrown.",
521 				e, __FILE__, __LINE__
522 			);
523 		}
524 	}
525 
526 	Attributes parseAttributesImpl() {
527 		string[] subRules;
528 		subRules = ["Attribute", "AttributeFollow"];
529 		if(this.firstAttribute()) {
530 			Attribute cur = this.parseAttribute();
531 			subRules = ["AttributeFollow"];
532 			if(this.firstAttributes()) {
533 				Attributes follow = this.parseAttributes();
534 
535 				return new Attributes(AttributesEnum.AttributeFollow
536 					, cur
537 					, follow
538 				);
539 			}
540 			return new Attributes(AttributesEnum.Attribute
541 				, cur
542 			);
543 		}
544 		auto app = appender!string();
545 		formattedWrite(app, 
546 			"In 'Attributes' found a '%s' while looking for", 
547 			this.lex.front
548 		);
549 		throw new ParseException(app.data,
550 			__FILE__, __LINE__,
551 			subRules,
552 			["ident -> IDFull"]
553 		);
554 
555 	}
556 
557 	bool firstAttribute() const pure @nogc @safe {
558 		return this.firstIDFull();
559 	}
560 
561 	Attribute parseAttribute() {
562 		try {
563 			return this.parseAttributeImpl();
564 		} catch(ParseException e) {
565 			throw new ParseException(
566 				"While parsing a Attribute an Exception was thrown.",
567 				e, __FILE__, __LINE__
568 			);
569 		}
570 	}
571 
572 	Attribute parseAttributeImpl() {
573 		string[] subRules;
574 		subRules = ["A"];
575 		if(this.firstIDFull()) {
576 			IDFull id = this.parseIDFull();
577 			subRules = ["A"];
578 			if(this.lex.front.type == TokenType.assign) {
579 				this.lex.popFront();
580 				subRules = ["A"];
581 				if(this.lex.front.type == TokenType.value) {
582 					Token value = this.lex.front;
583 					this.lex.popFront();
584 
585 					return new Attribute(AttributeEnum.A
586 						, id
587 						, value
588 					);
589 				}
590 				auto app = appender!string();
591 				formattedWrite(app, 
592 					"In 'Attribute' found a '%s' while looking for", 
593 					this.lex.front
594 				);
595 				throw new ParseException(app.data,
596 					__FILE__, __LINE__,
597 					subRules,
598 					["value"]
599 				);
600 
601 			}
602 			auto app = appender!string();
603 			formattedWrite(app, 
604 				"In 'Attribute' found a '%s' while looking for", 
605 				this.lex.front
606 			);
607 			throw new ParseException(app.data,
608 				__FILE__, __LINE__,
609 				subRules,
610 				["assign"]
611 			);
612 
613 		}
614 		auto app = appender!string();
615 		formattedWrite(app, 
616 			"In 'Attribute' found a '%s' while looking for", 
617 			this.lex.front
618 		);
619 		throw new ParseException(app.data,
620 			__FILE__, __LINE__,
621 			subRules,
622 			["ident"]
623 		);
624 
625 	}
626 
627 	bool firstOptChild() const pure @nogc @safe {
628 		return this.lex.front.type == TokenType.lcurly;
629 	}
630 
631 	OptChild parseOptChild() {
632 		try {
633 			return this.parseOptChildImpl();
634 		} catch(ParseException e) {
635 			throw new ParseException(
636 				"While parsing a OptChild an Exception was thrown.",
637 				e, __FILE__, __LINE__
638 			);
639 		}
640 	}
641 
642 	OptChild parseOptChildImpl() {
643 		string[] subRules;
644 		subRules = ["E", "E2", "T"];
645 		if(this.lex.front.type == TokenType.lcurly) {
646 			this.lex.popFront();
647 			subRules = ["E", "T"];
648 			if(this.firstTagTerminator()) {
649 				this.parseTagTerminator();
650 				subRules = ["T"];
651 				if(this.firstTags()) {
652 					Tags tags = this.parseTags();
653 					subRules = ["T"];
654 					if(this.lex.front.type == TokenType.rcurly) {
655 						this.lex.popFront();
656 
657 						return new OptChild(OptChildEnum.T
658 							, tags
659 						);
660 					}
661 					auto app = appender!string();
662 					formattedWrite(app, 
663 						"In 'OptChild' found a '%s' while looking for", 
664 						this.lex.front
665 					);
666 					throw new ParseException(app.data,
667 						__FILE__, __LINE__,
668 						subRules,
669 						["rcurly"]
670 					);
671 
672 				} else if(this.lex.front.type == TokenType.rcurly) {
673 					this.lex.popFront();
674 
675 					return new OptChild(OptChildEnum.E
676 					);
677 				}
678 				auto app = appender!string();
679 				formattedWrite(app, 
680 					"In 'OptChild' found a '%s' while looking for", 
681 					this.lex.front
682 				);
683 				throw new ParseException(app.data,
684 					__FILE__, __LINE__,
685 					subRules,
686 					["ident -> Tag","lcurly -> Tag","value -> Tag","rcurly"]
687 				);
688 
689 			} else if(this.lex.front.type == TokenType.rcurly) {
690 				this.lex.popFront();
691 
692 				return new OptChild(OptChildEnum.E2
693 				);
694 			}
695 			auto app = appender!string();
696 			formattedWrite(app, 
697 				"In 'OptChild' found a '%s' while looking for", 
698 				this.lex.front
699 			);
700 			throw new ParseException(app.data,
701 				__FILE__, __LINE__,
702 				subRules,
703 				["eol","semicolon","rcurly"]
704 			);
705 
706 		}
707 		auto app = appender!string();
708 		formattedWrite(app, 
709 			"In 'OptChild' found a '%s' while looking for", 
710 			this.lex.front
711 		);
712 		throw new ParseException(app.data,
713 			__FILE__, __LINE__,
714 			subRules,
715 			["lcurly"]
716 		);
717 
718 	}
719 
720 	bool firstTagTerminator() const pure @nogc @safe {
721 		return this.lex.front.type == TokenType.eol
722 			 || this.lex.front.type == TokenType.semicolon;
723 	}
724 
725 	TagTerminator parseTagTerminator() {
726 		try {
727 			return this.parseTagTerminatorImpl();
728 		} catch(ParseException e) {
729 			throw new ParseException(
730 				"While parsing a TagTerminator an Exception was thrown.",
731 				e, __FILE__, __LINE__
732 			);
733 		}
734 	}
735 
736 	TagTerminator parseTagTerminatorImpl() {
737 		string[] subRules;
738 		subRules = ["E", "EF"];
739 		if(this.lex.front.type == TokenType.eol) {
740 			this.lex.popFront();
741 			subRules = ["EF"];
742 			if(this.firstTagTerminator()) {
743 				this.parseTagTerminator();
744 
745 				return new TagTerminator(TagTerminatorEnum.EF
746 				);
747 			}
748 			return new TagTerminator(TagTerminatorEnum.E
749 			);
750 		} else if(this.lex.front.type == TokenType.semicolon) {
751 			this.lex.popFront();
752 			subRules = ["SF"];
753 			if(this.firstTagTerminator()) {
754 				this.parseTagTerminator();
755 
756 				return new TagTerminator(TagTerminatorEnum.SF
757 				);
758 			}
759 			return new TagTerminator(TagTerminatorEnum.S
760 			);
761 		}
762 		auto app = appender!string();
763 		formattedWrite(app, 
764 			"In 'TagTerminator' found a '%s' while looking for", 
765 			this.lex.front
766 		);
767 		throw new ParseException(app.data,
768 			__FILE__, __LINE__,
769 			subRules,
770 			["eol","semicolon"]
771 		);
772 
773 	}
774 
775 }