1 module dadt.parser;
2 import pegged.grammar;
3 import std.algorithm;
4 import std.format;
5 import std.string;
6 import std.stdio;
7 import std.array;
8 import std.ascii;
9 import dadt.util;
10 
11 mixin(grammar(`
12 DADT:
13   TypeDeclare < "type" BaseConstructor "=" ConstructorList Deriving?
14 
15   BaseConstructor < TypeNameWithArgs / TypeNameWithoutArgs
16 
17   TypeName <~ !Keyword [A-Z_] [a-zA-Z0-9_]*
18 
19   Field < FieldOfDelegate / FieldOfFunction / FieldOfArray / FieldWithArgs / FieldName
20   FieldArgs < "()" / :"(" Field ("," Field)* :")"
21   FieldWithArgs < FieldName "!" FieldArgs
22   FieldOfArray < (FieldWithArgs / FieldName) ArrayBracket+
23   FieldOfDelegate < "[" Field ("->" Field)+ "]"
24   FieldOfFunction < "<" Field ("->" Field)+ ">"
25   FieldName <~ !Keyword [a-zA-Z_] [a-zA-Z0-9_]*
26 
27   ArrayBracket < UnsizedBracket / SizedBracket
28 
29   UnsizedBracket < "[]"
30   SizedBracket < "[" ArraySize "]"
31   ArraySize <~ [a-zA-Z0-9_]*
32 
33   TypeNameWithoutArgs < TypeName
34   TypeNameWithArgs < TypeName ParameterList
35   ParameterList < "()" / :"(" TypeName ("," TypeName)* :")"
36 
37   RecordField < FieldName ":" Field
38   RecordFields < (RecordField ";"?)+
39   Record < "{" RecordFields "}"
40 
41   ConstructorWithRecord< "|"? TypeName "of" Record
42   ConstructorWithField < "|"? TypeName "of" Field ("*" Field)*
43   Constructor <  "|"? TypeName
44   ConstructorDeclare < ConstructorWithRecord / ConstructorWithField / Constructor
45   ConstructorList < ConstructorDeclare+
46 
47   Deriving < "[@@deriving" DerivingArgs "]"
48   DerivingArgs < DerivingArg ("," DerivingArg)*
49   DerivingArg <~ !Keyword [a-zA-Z_] [a-zA-Z0-9_]*
50 
51   Keyword <~ "of"
52   Integer <~ digit+
53 `));
54 
55 interface ASTElement {
56 }
57 
58 class BaseConstructor : ASTElement {
59   TypeName baseName;
60   ParameterList parameterList;
61 
62   this(TypeName baseName) {
63     this.baseName = baseName;
64     this.parameterList = new ParameterList([]);
65   }
66 
67   this(TypeName baseName, ParameterList parameterList) {
68     this.baseName = baseName;
69     this.parameterList = parameterList;
70   }
71 }
72 
73 class TypeName : ASTElement {
74   string name;
75 
76   this(string name) {
77     this.name = name;
78   }
79 }
80 
81 interface Field : ASTElement {
82   const string typeString();
83 }
84 
85 class FieldName : Field {
86   string fieldName;
87 
88   this(string fieldName) {
89     this.fieldName = fieldName;
90   }
91 
92   override const string typeString() {
93     return fieldName;
94   }
95 }
96 
97 class FieldArgs : ASTElement {
98   Field[] fields;
99 
100   this(Field[] fields) {
101     this.fields = fields;
102   }
103 }
104 
105 class FieldWithArgs : Field {
106   FieldName fieldName;
107   FieldArgs fieldArgs;
108 
109   this(FieldName fieldName, FieldArgs fieldArgs) {
110     this.fieldName = fieldName;
111     this.fieldArgs = fieldArgs;
112   }
113 
114   override const string typeString() {
115     const string baseType = fieldName.typeString;
116     string[] fields;
117     string argsStr;
118     foreach (field; fieldArgs.fields) {
119       fields ~= field.typeString;
120     }
121     if (fields.length) {
122       argsStr = "!(" ~ fields.join(", ") ~ ")";
123 
124       return baseType ~ argsStr;
125     } else {
126       return baseType;
127     }
128   }
129 }
130 
131 interface ArrayBracket : ASTElement {
132   const bool isSized();
133 }
134 
135 class ArraySize : ASTElement {
136   string size;
137 
138   this(string size) {
139     this.size = size;
140   }
141 }
142 
143 class SizedBracket : ArrayBracket {
144   ArraySize size;
145 
146   this(ArraySize size) {
147     this.size = size;
148   }
149 
150   override const bool isSized() {
151     return true;
152   }
153 }
154 
155 class UnsizedBracket : ArrayBracket {
156   this() {
157   }
158 
159   override const bool isSized() {
160     return false;
161   }
162 }
163 
164 class FieldOfArray : Field {
165   Field field;
166   ArrayBracket bracket;
167 
168   this(Field field, ArrayBracket bracket) {
169     this.field = field;
170     this.bracket = bracket;
171   }
172 
173   override const string typeString() {
174     string baseType = field.typeString;
175 
176     if (this.bracket.isSized) {
177       SizedBracket sb = cast(SizedBracket)bracket;
178       return baseType ~ "[%s]".format(sb.size.size);
179     } else {
180       return baseType ~ "[]";
181     }
182   }
183 }
184 
185 class FieldOfDelegate : Field {
186   Field retType;
187   Field[] args;
188 
189   this(Field retType, Field[] args) {
190     this.retType = retType;
191     this.args = args;
192   }
193 
194   override const string typeString() {
195     string args_str = this.args.map!(arg => arg.typeString()).join(", ");
196     if (args_str == "void") {
197       args_str = "";
198     }
199     return "%s delegate(%s)".format(this.retType.typeString(), args_str);
200   }
201 }
202 
203 class FieldOfFunction : Field {
204   Field retType;
205   Field[] args;
206 
207   this(Field retType, Field[] args) {
208     this.retType = retType;
209     this.args = args;
210   }
211 
212   override const string typeString() {
213     string args_str = this.args.map!(arg => arg.typeString()).join(", ");
214     if (args_str == "void") {
215       args_str = "";
216     }
217     return "%s function(%s)".format(this.retType.typeString(), args_str);
218   }
219 }
220 
221 class ParameterList : ASTElement {
222   TypeName[] parameters;
223 
224   this(TypeName[] parameters) {
225     this.parameters = parameters;
226   }
227 }
228 
229 class TypeDeclare : ASTElement {
230   BaseConstructor baseConstructor;
231   ConstructorList constructorList;
232   Deriving deriving;
233 
234   this(BaseConstructor baseConstructor, ConstructorList constructorList) {
235     this.baseConstructor = baseConstructor;
236     this.constructorList = constructorList;
237   }
238 
239   this(BaseConstructor baseConstructor, ConstructorList constructorList, Deriving deriving) {
240     this.baseConstructor = baseConstructor;
241     this.constructorList = constructorList;
242     this.deriving = deriving;
243   }
244 }
245 
246 class ConstructorList : ASTElement {
247   Constructor[] constructors;
248 
249   this(Constructor[] constructors) {
250     this.constructors = constructors;
251   }
252 }
253 
254 class RecordField : ASTElement {
255   FieldName name;
256   Field field;
257 
258   this(FieldName name, Field field) {
259     this.name = name;
260     this.field = field;
261   }
262 
263   string getFieldTypeString() const {
264     return this.field.typeString();
265   }
266 
267   string getFieldName() const {
268     return this.name.fieldName;
269   }
270 }
271 
272 class RecordFields : ASTElement {
273   RecordField[] fields;
274   this(RecordField[] fields) {
275     this.fields = fields;
276   }
277 }
278 
279 enum ConstructorType {
280   Single,
281   Fields,
282   Record
283 }
284 
285 class Constructor : ASTElement {
286   TypeName typeName;
287   Field[] fields;
288   RecordFields record;
289   ConstructorType type;
290 
291   this(TypeName typeName) {
292     this.typeName = typeName;
293     this.type = ConstructorType.Single;
294   }
295 
296   this(TypeName typeName, Field[] fields) {
297     this.typeName = typeName;
298     this.fields = fields;
299     this.type = ConstructorType.Fields;
300   }
301 
302   this(TypeName typeName, RecordFields record) {
303     this.typeName = typeName;
304     this.record = record;
305     this.type = ConstructorType.Record;
306   }
307 
308   string getTypeName() const {
309     return this.typeName.name;
310   }
311 }
312 
313 enum DerivingType {
314   Show,
315   Ord,
316   Eq
317 }
318 
319 enum DerivingMap = [
320     "show" : DerivingType.Show, "ord" : DerivingType.Ord, "eq" : DerivingType.Eq
321   ];
322 
323 class DerivingArg : ASTElement {
324   DerivingType type;
325 
326   this(DerivingType type) {
327     this.type = type;
328   }
329 }
330 
331 class DerivingArgs : ASTElement {
332   DerivingArg[] args;
333 
334   this(DerivingArg[] args) {
335     this.args = args;
336   }
337 }
338 
339 class Deriving : ASTElement {
340   DerivingArgs args;
341 
342   this(DerivingArgs args) {
343     this.args = args;
344   }
345 }
346 
347 ASTElement buildAST(ParseTree p) {
348   /*
349   if (!__ctfe) {
350     writeln("p.name : ", p.name);
351   }
352   */
353 
354   final switch (p.name) {
355   case "DADT":
356     auto e = p.children[0];
357     return buildAST(e);
358   case "DADT.BaseConstructor":
359     auto e = p.children[0];
360     return buildAST(e);
361   case "DADT.TypeDeclare":
362     BaseConstructor baseConstructor = cast(BaseConstructor)buildAST(p.children[0]);
363 
364     if (baseConstructor is null) {
365       throw new Error("Error in %s!".format(p.name));
366     }
367 
368     ConstructorList constructorList = cast(ConstructorList)buildAST(p.children[1]);
369 
370     if (constructorList is null) {
371       throw new Error("Error in %s!".format(p.name));
372     }
373 
374     if (p.children.length == 2) {
375       return new TypeDeclare(baseConstructor, constructorList);
376     } else {
377       Deriving deriving = cast(Deriving)buildAST(p.children[2]);
378 
379       if (deriving is null) {
380         throw new Error("Error in %s!".format(p.name));
381       }
382 
383       return new TypeDeclare(baseConstructor, constructorList, deriving);
384     }
385   case "DADT.TypeName":
386     string typeName = p.matches[0];
387     return new TypeName(typeName);
388   case "DADT.TypeNameWithoutArgs":
389     TypeName baseName = cast(TypeName)buildAST(p.children[0]);
390 
391     if (baseName is null) {
392       throw new Error("Error in %s!".format(p.name));
393     }
394 
395     return new BaseConstructor(baseName);
396   case "DADT.TypeNameWithArgs":
397     TypeName baseName = cast(TypeName)buildAST(p.children[0]);
398     ParameterList parameterList = cast(ParameterList)buildAST(p.children[1]);
399     if (baseName is null || parameterList is null) {
400       throw new Error("Error in %s!".format(p.name));
401     }
402 
403     return new BaseConstructor(baseName, parameterList);
404 
405   case "DADT.Field":
406     auto e = p.children[0];
407     return buildAST(e);
408   case "DADT.FieldWithArgs":
409     FieldName fieldName = cast(FieldName)buildAST(p.children[0]);
410     if (fieldName is null) {
411       throw new Error("Error in %s!".format(p.name));
412     }
413 
414     FieldArgs fieldArgs = cast(FieldArgs)buildAST(p.children[1]);
415     if (fieldArgs is null) {
416       throw new Error("Error in %s!".format(p.name));
417     }
418 
419     return new FieldWithArgs(fieldName, fieldArgs);
420   case "DADT.FieldArgs":
421     Field[] fields;
422 
423     foreach (child; p.children) {
424       Field field = cast(Field)buildAST(child);
425       if (field is null) {
426         throw new Error("Error in %s!".format(p.name));
427       }
428       fields ~= field;
429     }
430 
431     return new FieldArgs(fields);
432   case "DADT.FieldOfArray":
433     Field field = cast(Field)buildAST(p.children[0]);
434     if (field is null) {
435       throw new Error("Error in %s!".format(p.name));
436     }
437 
438     ArrayBracket ab = cast(ArrayBracket)buildAST(p.children[1]);
439     if (ab is null) {
440       throw new Error("Error in %s!".format(p.name));
441     }
442 
443     return new FieldOfArray(field, ab);
444   case "DADT.ArrayBracket":
445     auto e = p.children[0];
446     return buildAST(e);
447   case "DADT.ArraySize":
448     string size = p.matches[0];
449     return new ArraySize(size);
450   case "DADT.SizedBracket":
451     ArraySize size = cast(ArraySize)buildAST(p.children[0]);
452     if (size is null) {
453       throw new Error("Error in %s!".format(p.name));
454     }
455 
456     return new SizedBracket(size);
457   case "DADT.UnsizedBracket":
458     return new UnsizedBracket;
459   case "DADT.FieldOfDelegate":
460     Field[] fields;
461     foreach (child; p.children) {
462       Field field = cast(Field)buildAST(child);
463       if (field is null) {
464         throw new Error("Error in %s!".format(p.name));
465       }
466       fields ~= field;
467     }
468     Field retType = fields[$ - 1];
469     return new FieldOfDelegate(retType, fields[0 .. $ - 1]);
470   case "DADT.FieldOfFunction":
471     Field[] fields;
472     foreach (child; p.children) {
473       Field field = cast(Field)buildAST(child);
474       if (field is null) {
475         throw new Error("Error in %s!".format(p.name));
476       }
477       fields ~= field;
478     }
479     Field retType = fields[$ - 1];
480     return new FieldOfFunction(retType, fields[0 .. $ - 1]);
481   case "DADT.FieldName":
482     string fieldName = p.matches[0];
483     return new FieldName(fieldName);
484   case "DADT.ParameterList":
485     TypeName[] parameters;
486     foreach (child; p.children) {
487       TypeName typeName = cast(TypeName)buildAST(child);
488       if (typeName is null) {
489         throw new Error("Error in %s!".format(p.name));
490       }
491 
492       parameters ~= typeName;
493     }
494 
495     return new ParameterList(parameters);
496   case "DADT.ConstructorList":
497     Constructor[] constructors;
498     foreach (child; p.children) {
499       Constructor constructor = cast(Constructor)buildAST(child);
500       if (constructor is null) {
501         throw new Error("Error in %s!".format(p.name));
502       }
503       constructors ~= constructor;
504     }
505 
506     return new ConstructorList(constructors);
507   case "DADT.ConstructorDeclare":
508     auto e = p.children[0];
509     return buildAST(e);
510   case "DADT.RecordField":
511     FieldName name = cast(FieldName)buildAST(p.children[0]);
512     if (name is null) {
513       throw new Error("Error in %s!".format(p.name));
514     }
515     Field field = cast(Field)buildAST(p.children[1]);
516     if (field is null) {
517       throw new Error("Error in %s!".format(p.name));
518     }
519 
520     return new RecordField(name, field);
521   case "DADT.RecordFields":
522     RecordField[] fields;
523     foreach (child; p.children) {
524       RecordField field = cast(RecordField)buildAST(child);
525       if (field is null) {
526         throw new Error("Error in %s!".format(p.name));
527       }
528       fields ~= field;
529     }
530     return new RecordFields(fields);
531   case "DADT.Record":
532     return buildAST(p.children[0]);
533   case "DADT.ConstructorWithRecord":
534     TypeName constructorName = cast(TypeName)buildAST(p.children[0]);
535     if (constructorName is null) {
536       throw new Error("Error in %s!".format(p.name));
537     }
538     // RecordFields
539     RecordFields record = cast(RecordFields)buildAST(p.children[1]);
540     if (record is null) {
541       throw new Error("Error in %s!".format(p.name));
542     }
543     return new Constructor(constructorName, record);
544   case "DADT.ConstructorWithField":
545     TypeName constructorName = cast(TypeName)buildAST(p.children[0]);
546     if (constructorName is null) {
547       throw new Error("Error in %s!".format(p.name));
548     }
549     Field[] fields;
550     foreach (child; p.children[1 .. $]) {
551       Field field = cast(Field)buildAST(child);
552       if (field is null) {
553         throw new Error("Error in %s!".format(p.name));
554       }
555       fields ~= field;
556     }
557 
558     return new Constructor(constructorName, fields);
559   case "DADT.Constructor":
560     TypeName constructorName = cast(TypeName)buildAST(p.children[0]);
561     if (constructorName is null) {
562       throw new Error("Error in %s!".format(p.name));
563     }
564     return new Constructor(constructorName);
565   case "DADT.Deriving":
566     DerivingArgs args = cast(DerivingArgs)buildAST(p.children[0]);
567 
568     if (args is null) {
569       throw new Error("Error in %s!".format(p.name));
570     }
571 
572     return new Deriving(args);
573   case "DADT.DerivingArg":
574     string k = p.matches[0].toLower;
575 
576     if (k in DerivingMap) {
577       return new DerivingArg(DerivingMap[k]);
578     } else {
579       throw new Error("Unkown deriving target was given : %s".format(p.matches[0]));
580     }
581   case "DADT.DerivingArgs":
582     DerivingArg[] args;
583 
584     foreach (child; p.children) {
585       DerivingArg arg = cast(DerivingArg)buildAST(child);
586 
587       if (arg is null) {
588         throw new Error("Error in %s!".format(p.name));
589       }
590 
591       args ~= arg;
592     }
593 
594     return new DerivingArgs(args);
595   }
596 }
597 
598 string genCode(const TypeDeclare td) {
599   string code;
600   string interface_name = td.baseConstructor.baseName.name;
601   const string[] interface_args = td.baseConstructor.parameterList.parameters.map!(
602       (const TypeName tn) => tn.name).array;
603   string args_str;
604   string interface_args_str;
605   if (interface_args.length) {
606     interface_args_str = "(" ~ interface_args.join(", ") ~ ")";
607     args_str = "!" ~ interface_args_str;
608   }
609 
610   string[] enum_elements;
611   foreach (i, constructor; td.constructorList.constructors) {
612     if (i > 0) {
613       enum_elements ~= "  " ~ constructor.getTypeName();
614     } else {
615       enum_elements ~= constructor.getTypeName();
616     }
617   }
618 
619   // dfmt off
620   code ~=
621 `
622 enum #{interface_name}#Type {
623   #{enum_elements}#
624 }
625 `.patternReplaceWithTable([
626   "interface_name" : interface_name,
627   "enum_elements"  : enum_elements.join(",\n")
628 ]);
629   // dfmt on
630 
631   // dfmt off
632   code ~=
633 `
634 interface #{interface_name}##{interface_args_str}# {
635   #{interface_name}#Type type();
636 }
637 `.patternReplaceWithTable([
638       "interface_name"     : interface_name,
639       "interface_args_str" : interface_args_str]);
640   // dfmt on
641 
642   foreach (constructor; td.constructorList.constructors) {
643     string constructor_name = constructor.getTypeName();
644 
645     string[] field_names;
646     string[string] field_info;
647     string field_code;
648     string[] field_type_and_names;
649 
650     if (constructor.type == ConstructorType.Record) {
651       foreach (field; constructor.record.fields) {
652         string field_name = field.getFieldName();
653         field_names ~= field_name;
654         field_info[field_name] = field.getFieldTypeString();
655       }
656     } else {
657       foreach (i, fieldType; constructor.fields) {
658         string field_name = "_%d".format(i);
659         field_names ~= field_name;
660         field_info[field_name] = fieldType.typeString();
661       }
662     }
663 
664     foreach (field_name; field_names) {
665       string field_type = field_info[field_name];
666       field_type_and_names ~= "%s %s".format(field_type, field_name);
667     }
668     foreach (field_type_and_name; field_type_and_names) {
669       field_code ~= field_type_and_name ~ ";";
670     }
671 
672     string this_code;
673     string this_argument = field_type_and_names.join(", ");
674     string initialize_list;
675     foreach (field_name; field_names) {
676       initialize_list ~= "this.%s = %s;".format(field_name, field_name);
677     }
678 
679     // dfmt off
680     this_code = `
681   this(#{this_argument}#) {
682     #{initialize_list}#
683   }`.patternReplaceWithTable([
684         "this_argument"   : this_argument,
685         "initialize_list" : initialize_list]);
686 
687     string type_code = `
688   #{interface_name}#Type type() {
689     return #{interface_name}#Type.#{constructor_name}#;
690   }
691 `.patternReplaceWithTable([
692   "interface_name"   : interface_name,
693   "constructor_name" : constructor_name
694 ]);
695 
696     code ~= `
697 class #{constructor_name}##{interface_args_str}# : #{interface_name}##{args_str}# {
698   #{field_code}#
699   #{this_code}#
700   #{type_code}#
701 }`.patternReplaceWithTable([
702         "constructor_name"   : constructor_name,
703         "interface_args_str" : interface_args_str,
704         "interface_name"     : interface_name,
705         "args_str"           : args_str,
706         "field_code"         : field_code,
707         "this_code"          : this_code,
708         "type_code"          : type_code]);
709     // dfmt on
710 
711     string helper_code;
712     string helper_returnType = constructor_name ~ args_str;
713     string interface_returnType = interface_name ~ args_str;
714     string helper_name = constructor_name.toLower;
715     string helper_typeParameters;
716 
717     if (interface_args.length) {
718       helper_typeParameters = "(" ~ interface_args.join(", ") ~ ")";
719     }
720 
721     string[] helper_arguments;
722     string[] helper_variables;
723 
724     if (constructor.type == ConstructorType.Record) {
725       foreach (field; constructor.record.fields) {
726         helper_arguments ~= "%s %s".format(field.getFieldTypeString(), field.getFieldName());
727         helper_variables ~= "%s".format(field.getFieldName());
728       }
729     } else {
730       foreach (i, field; constructor.fields) {
731         helper_arguments ~= "%s _%d".format(field.typeString(), i);
732         helper_variables ~= "_%d".format(i);
733       }
734     }
735 
736     // dfmt off
737     helper_code = `
738 #{interface_returnType}# #{helper_name}##{helper_typeParameters}#(#{helper_arguments}#) {
739   return new #{helper_returnType}#(#{helper_variables}#);
740 }
741 `.patternReplaceWithTable([
742       "helper_returnType"     : helper_returnType,
743       "interface_returnType"  : interface_returnType,
744       "helper_name"           : helper_name,
745       "helper_typeParameters" : helper_typeParameters,
746       "helper_arguments"      : helper_arguments.join(", "),
747       "helper_variables"      : helper_variables.join(", ")]);
748     // dfmt on
749 
750     code ~= helper_code;
751   }
752 
753   string match_returnType = "_RETURN_TYPE_OF_MATCH_WITH_%s".format(interface_name);
754   // dfmt off
755   string match_header = "#{match_returnType}# matchWith#{interface_name}#(#{match_returnType}#, #{interface_args}# choices...)(#{interface_name}##{args_str}# arg) {".patternReplaceWithTable([
756       "match_returnType" : match_returnType,
757       "interface_name"   : interface_name,
758       "interface_args"   : interface_args.join(", ") ~ (interface_args.length > 0 ? "," : ""),
759       "interface_name"   : interface_name, "args_str":args_str]);
760   // dfmt on
761 
762   string match_static_routers;
763 
764   foreach (constructor; td.constructorList.constructors) {
765     string type_signature = constructor.getTypeName() ~ args_str;
766     string[] field_names;
767     if (constructor.type == ConstructorType.Record) {
768       foreach (field; constructor.record.fields) {
769         field_names ~= field.getFieldName();
770       }
771     } else {
772       foreach (i, fieldType; constructor.fields) {
773         string field_name = "x._%d".format(i);
774         field_names ~= field_name;
775       }
776     }
777 
778     // dfmt off
779     match_static_routers ~= `
780       static if (is(#{type_signature}# == params[0])) {
781         #{type_signature}# x = cast(#{type_signature}#)arg;
782 
783         static if (is(ReturnType!(choice) == #{match_returnType}#)) {
784           static if (is(#{match_returnType}# == void)) {
785             choice(x);
786           } else {
787             return choice(x);
788           }
789         } else {
790           static if (isCallable!(ReturnType!(choice))) {
791             return cast(#{match_returnType}#)choice(x)#{field_args}#;
792           } else {
793             return cast(#{match_returnType}#)choice(x);
794           }
795         }
796       }
797 `.patternReplaceWithTable([
798   "type_signature"   : type_signature,
799   "match_returnType" : match_returnType,
800   "field_args"       : field_names.length > 0 ? `(` ~ field_names.join(", ") ~ `)` : "()"]);
801     // dfmt on
802   }
803 
804   // dfmt off
805   string match_code = `
806 #{match_header}#
807   import std.traits;
808   #{match_returnType}# delegate() otherwise = null;
809 
810   foreach (choice; choices) {
811     alias params = Parameters!choice;
812     static if (params.length < 1) {
813       otherwise = () => choice();
814     }
815 
816     if (cast(params[0])(arg) !is null) {
817       #{match_static_routers}#
818     }
819   }
820 
821   if (otherwise !is null) {
822     static if (is(#{match_returnType}# == void)) {
823       otherwise();
824       return;
825     } else {
826       return otherwise();
827     }
828   }
829 
830   static if (!is(#{match_returnType}# == void)) {
831     return null;
832   }
833 }
834 `.patternReplaceWithTable([
835   "match_header"         : match_header,
836   "match_returnType"     : match_returnType,
837   "match_static_routers" : match_static_routers
838 ]);
839   // dfmt on
840   code ~= match_code;
841 
842   if (td.deriving !is null) {
843     bool ord_is_generated = false;
844 
845     foreach (arg; td.deriving.args.args) {
846       string deriving_code;
847       final switch (arg.type) with (DerivingType) {
848       case Show:
849         string show_code;
850 
851         // dfmt off
852         string show_header = `string show_#{interface_name}##{interface_args_str}#(#{interface_name}##{args_str}# arg, string function(#{interface_name}##{args_str}#) conv = null) {`.patternReplaceWithTable([
853           "interface_name"     : interface_name,
854           "interface_args_str" : interface_args_str,
855           "args_str"           : args_str]);
856 
857         string show_middle_header =
858 `  if (conv !is null) {
859     return conv(arg);
860   }
861   import std.string;
862   string[] interface_args = [#{interface_args}#];
863   string constructor_arg;
864   if (interface_args.length) {
865     constructor_arg = "!(" ~ interface_args.join(", ") ~ ")";
866   }
867 `.patternReplaceWithTable(["interface_args" : interface_args.map!(iarg => iarg ~ ".stringof").join(", ")]);
868         // dfmt on
869 
870         string show_constructors = `
871   final switch (arg.type) {`;
872 
873         foreach (constructor; td.constructorList.constructors) {
874           string constructor_name = constructor.getTypeName();
875           string type_signature = constructor.getTypeName() ~ args_str;
876           string[] field_names;
877           if (constructor.type == ConstructorType.Record) {
878             foreach (field; constructor.record.fields) {
879               field_names ~= "x." ~ field.getFieldName();
880             }
881           } else {
882             foreach (i, fieldType; constructor.fields) {
883               string field_name = "x._%d".format(i);
884               field_names ~= field_name;
885             }
886           }
887 
888           string ret_line;
889 
890           if (field_names.length) {
891             string[] field_formatters;
892             foreach (_; field_names) {
893               field_formatters ~= "%s";
894             }
895             // dfmt off
896             ret_line = `return "#{constructor_name}#" ~ constructor_arg ~ "(#{field_formatters}#)".format(#{field_names}#);`.patternReplaceWithTable([
897               "constructor_name"    : constructor_name,
898               "field_formatters" : field_formatters.join(", "),
899               "field_names"      : field_names.join(", ")]);
900             // dfmt on
901           } else {
902             ret_line = `return "%s" ~ constructor_arg;`.format(constructor_name);
903           }
904 
905           // dfmt off
906           show_constructors ~= `
907     case #{interface_name}#Type.#{constructor_name}#:
908       #{type_signature}# x = cast(#{type_signature}#)arg;
909 
910       #{ret_line}#`.patternReplaceWithTable([
911   "interface_name"   : interface_name,
912   "constructor_name" : constructor_name,
913   "type_signature"   : type_signature,
914   "ret_line"         : ret_line]);
915           // dfmt on
916         }
917 
918         string show_footer = `  }`;
919         // dfmt off
920         show_code =
921 `
922 #{show_header}#
923 #{show_middle_header}#
924 #{show_constructors}#
925 #{show_footer}#
926 }
927 `.patternReplaceWithTable([
928   "show_header"        : show_header,
929   "show_middle_header" : show_middle_header,
930   "show_constructors"  : show_constructors,
931   "show_footer"        : show_footer
932 ]);
933         // dfmt on
934         deriving_code ~= show_code;
935         break;
936       case Ord:
937         if (ord_is_generated) {
938           break;
939         }
940         ord_is_generated = true;
941         // dfmt off
942         string ord_header = `int compare_#{interface_name}##{interface_args_str}#(#{interface_name}##{args_str}# _lhs, #{interface_name}##{args_str}# _rhs) {`.patternReplaceWithTable([
943           "interface_name"     : interface_name,
944           "interface_args_str" : interface_args_str,
945           "args_str"           : args_str]);
946         // dfmt on
947         string ord_precomp = `
948   #{interface_name}#Type lhs_type = _lhs.type,
949       rhs_type = _rhs.type;
950 
951   if (lhs_type < rhs_type) {
952     return -1;
953   }
954   if (lhs_type > rhs_type) {
955     return 1;
956   }
957 `.patternReplaceWithTable([
958             "interface_name": interface_name
959             ]);
960 
961         string ord_compbody = `
962   final switch (lhs_type) {`;
963 
964         foreach (constructor; td.constructorList.constructors) {
965           string constructor_name = constructor.getTypeName();
966           string type_signature = constructor.getTypeName() ~ args_str;
967           string field_comps;
968 
969           if (constructor.type == ConstructorType.Record) {
970             foreach (field; constructor.record.fields) {
971               string field_name = field.getFieldName();
972 
973               field_comps ~= `
974       if (lhs.#{field_name}# < rhs.#{field_name}#) {
975         return -1;
976       }
977       if (lhs.#{field_name}# > rhs.#{field_name}#) {
978         return 1;
979       }`.patternReplaceWithTable([
980                   "field_name": field_name
981                   ]);
982             }
983           } else {
984             foreach (i, fieldType; constructor.fields) {
985               string field_name = "_%d".format(i);
986 
987               field_comps ~= `
988       if (lhs.#{field_name}# < rhs.#{field_name}#) {
989         return -1;
990       }
991       if (lhs.#{field_name}# > rhs.#{field_name}#) {
992         return 1;
993       }`.patternReplaceWithTable([
994                   "field_name": field_name
995                   ]);
996             }
997           }
998 
999           // dfmt off
1000           ord_compbody ~= `
1001     case #{interface_name}#Type.#{constructor_name}#:
1002       #{type_signature}# lhs = cast(#{type_signature}#)_lhs,
1003               rhs = cast(#{type_signature}#)_rhs;
1004 
1005       #{field_comps}#
1006 
1007       return 0;`.patternReplaceWithTable([
1008   "interface_name"   : interface_name,
1009   "constructor_name" : constructor_name,
1010   "type_signature"   : type_signature,
1011   "field_comps"      : field_comps
1012 ]);
1013           // dfmt on
1014         }
1015 
1016         string ord_footer = `  }
1017 }`;
1018         // dfmt off
1019         string ord_code = `
1020 #{ord_header}#
1021 #{ord_precomp}#
1022 #{ord_compbody}#
1023 #{ord_footer}#
1024 `.patternReplaceWithTable([
1025   "ord_header"      : ord_header,
1026   "ord_precomp"     : ord_precomp,
1027   "ord_compbody"    : ord_compbody,
1028   "ord_footer"    : ord_footer
1029 ]);
1030         // dfmt on
1031         deriving_code ~= ord_code;
1032         break;
1033       case Eq:
1034         // dfmt off
1035         string eq_header = `bool equal_#{interface_name}##{interface_args_str}#(#{interface_name}##{args_str}# _lhs, #{interface_name}##{args_str}# _rhs) {`.patternReplaceWithTable([
1036           "interface_name"     : interface_name,
1037           "interface_args_str" : interface_args_str,
1038           "args_str"           : args_str]);
1039         string eq_body = `  return compare_#{interface_name}##{args_str}#(_lhs, _rhs) == 0;`.patternReplaceWithTable([
1040           "interface_name"     : interface_name,
1041           "args_str"           : args_str]);
1042         string eq_footer = "}";
1043         string eq_code =
1044 `
1045 #{eq_header}#
1046 #{eq_body}#
1047 #{eq_footer}#
1048 `.patternReplaceWithTable([
1049   "eq_header" : eq_header,
1050   "eq_body"   : eq_body,
1051   "eq_footer" : eq_footer]);
1052         deriving_code ~= eq_code;
1053         // dfmt on
1054         if (!ord_is_generated) {
1055           goto case Ord;
1056         } else {
1057           break;
1058         }
1059       }
1060       code ~= deriving_code;
1061     }
1062   }
1063 
1064   return code;
1065 }
1066 
1067 string genCodeFromSource(string source) {
1068   auto ast = DADT(source);
1069   TypeDeclare td = cast(TypeDeclare)buildAST(ast);
1070   return genCode(td);
1071 }