1 module bamboo.types;
2 
3 import std.array;
4 import std.algorithm;
5 import std.conv;
6 import std.range;
7 
8 import boilerplate;
9 
10 import bamboo.hashgen;
11 import bamboo.util;
12 
13 private
14 {
15     enum string generateVisit = `
16         override SyntaxKind syntaxKind() @property
17         {
18             return mixin("SyntaxKind." ~ (typeof(this).stringof));
19         }
20         override void visit(Visitor visitor)
21         {
22             visitor.visit(this);
23         }`;
24 
25     enum string generateSyntaxNode = `
26         mixin(GenerateThis);
27         mixin(generateVisit);
28         `;
29 }
30 
31 /// Represents the type of a SyntaxNode.
32 enum SyntaxKind
33 {
34     Module,
35     ImportDeclaration,
36     KeywordList,
37     BuiltinType,
38     KeywordDeclaration,
39     StructDeclaration,
40     ClassDeclaration,
41     AliasDeclaration,
42     MolecularField,
43     AtomicField,
44     ParameterField,
45     NumericParameter,
46     NumericRange,
47     NumericTransform,
48     NumericConstant,
49     SizedParameter,
50     SizeConstraint,
51     StructParameter,
52     ArrayParameter,
53     ArrayRange,
54 }
55 
56 //dfmt off
57 
58 /// Represents an operator in an $(D IntTransformation) or a $(D FloatTransformation).
59 enum Operator
60 {
61     modulo      = "%",
62     multiply    = "*",
63     add         = "+",
64     subtract    = "-",
65     divide      = "/",
66 }
67 //dfmt on
68 
69 // dfmt off
70 /// Represents the fundamental type of a node.
71 enum Type
72 {
73     int8, int16, int32, int64,
74     uint8, uint16, uint32, uint64,
75     char_,
76     float32, float64,
77 
78     string_,
79     varstring,
80     blob,
81     varblob,
82     array,
83     vararray,
84 
85     struct_,
86     method,
87     
88     invalid,
89 }
90 // dfmt on
91 
92 /// Represents an object which can act upon a $(D SyntaxNode).
93 abstract class Visitor
94 {
95     /// Visits the given $(D SyntaxNode).
96     void visitNode(SyntaxNode node)
97     {
98         final switch (node.syntaxKind)
99         {
100         case SyntaxKind.Module:
101             return visit(cast(Module) node);
102         case SyntaxKind.ImportDeclaration:
103             return visit(cast(ImportDeclaration) node);
104         case SyntaxKind.KeywordList:
105             return visit(cast(KeywordList) node);
106         case SyntaxKind.BuiltinType:
107             return visit(cast(BuiltinType) node);
108         case SyntaxKind.KeywordDeclaration:
109             return visit(cast(KeywordDeclaration) node);
110         case SyntaxKind.StructDeclaration:
111             return visit(cast(StructDeclaration) node);
112         case SyntaxKind.ClassDeclaration:
113             return visit(cast(ClassDeclaration) node);
114         case SyntaxKind.AliasDeclaration:
115             return visit(cast(AliasDeclaration) node);
116         case SyntaxKind.MolecularField:
117             return visit(cast(MolecularField) node);
118         case SyntaxKind.AtomicField:
119             return visit(cast(AtomicField) node);
120         case SyntaxKind.ParameterField:
121             return visit(cast(ParameterField) node);
122         case SyntaxKind.NumericParameter:
123             return visit(cast(NumericParameter) node);
124         case SyntaxKind.NumericRange:
125             return visit(cast(NumericRange) node);
126         case SyntaxKind.NumericTransform:
127             return visit(cast(NumericTransform) node);
128         case SyntaxKind.NumericConstant:
129             return visit(cast(NumericConstant) node);
130         case SyntaxKind.SizedParameter:
131             return visit(cast(SizedParameter) node);
132         case SyntaxKind.SizeConstraint:
133             return visit(cast(SizeConstraint) node);
134         case SyntaxKind.StructParameter:
135             return visit(cast(StructParameter) node);
136         case SyntaxKind.ArrayParameter:
137             return visit(cast(ArrayParameter) node);
138         case SyntaxKind.ArrayRange:
139             return visit(cast(ArrayRange) node);
140         }
141     }
142 
143     /// Visits the given $(D Module).
144     abstract void visit(Module file);
145 
146     /// Visits the given $(D ImportDeclaration).    
147     abstract void visit(ImportDeclaration node);
148 
149     /// Visits the given $(D KeywordList).    
150     abstract void visit(KeywordList node);
151 
152     /// Visits the given $(D BuiltinType).    
153     abstract void visit(BuiltinType node);
154 
155     /// Visits the given $(D KeywordDeclaration).    
156     abstract void visit(KeywordDeclaration node);
157 
158     /// Visits the given $(D StructDeclaration).    
159     abstract void visit(StructDeclaration node);
160 
161     /// Visits the given $(D ClassDeclaration).    
162     abstract void visit(ClassDeclaration node);
163 
164     /// Visits the given $(D AliasDeclaration).    
165     abstract void visit(AliasDeclaration node);
166 
167     /// Visits the given $(D MolecularField).    
168     abstract void visit(MolecularField node);
169 
170     /// Visits the given $(D AtomicField).    
171     abstract void visit(AtomicField node);
172 
173     /// Visits the given $(D ParameterField).    
174     abstract void visit(ParameterField node);
175 
176     /// Visits the given $(D IntParameter).    
177     abstract void visit(NumericParameter node);
178 
179     /// Visits the given $(D IntRange).    
180     abstract void visit(NumericRange node);
181 
182     /// Visits the given $(D IntTransform).    
183     abstract void visit(NumericTransform node);
184 
185     /// Visits the given $(D IntConstant).    
186     abstract void visit(NumericConstant node);
187 
188     /// Visits the given $(D SizedParameter).    
189     abstract void visit(SizedParameter node);
190 
191     /// Visits the given $(D SizeConstraint).    
192     abstract void visit(SizeConstraint node);
193 
194     /// Visits the given $(D StructParameter).    
195     abstract void visit(StructParameter node);
196 
197     /// Visits the given $(D ArrayParameter).    
198     abstract void visit(ArrayParameter node);
199 
200     /// Visits the given $(D ArrayRange).    
201     abstract void visit(ArrayRange node);
202 
203 }
204 
205 /// Represents an abstract syntax node.
206 abstract class SyntaxNode
207 {
208     /// Gets the $(D SyntaxKind) for this type of node.
209     abstract SyntaxKind syntaxKind() @property;
210 
211     /// Calls the $(S Visitor)'s analogous $(D Visitor.visit) method.
212     abstract void visit(Visitor visitor);
213 }
214 
215 /// Represents a module in the dclass system.
216 class Module : SyntaxNode
217 {
218     /// The optional name of this module.
219     string symbol;
220     /// Get the import statements in this module.
221     ImportDeclaration[] importDeclarations;
222 
223     /// The aliases defined in this module.
224     AliasDeclaration[] aliases;
225 
226     /// The classes defined in this module.
227     ClassDeclaration[] classes;
228 
229     /// The structs defined in this module.
230     StructDeclaration[] structs;
231 
232     /// The keywords defined in this module.
233     KeywordDeclaration[] keywords;
234 
235     /// The id of the last type defined in this module.
236     ushort lastTypeId;
237 
238     /// The id of the last field defined in this module.
239     ushort lastFieldId;
240 
241     mixin(generateSyntaxNode);
242 
243     /// Gets an array of `StructDeclaration` and `ClassDeclaration` objects
244     /// sorted by their id.
245     TypeDeclaration[] typesById()
246     {
247         import std.array : array;
248         import std.conv : to;
249         import std.range : chain;
250 
251         return (to!(TypeDeclaration[])(classes)).chain(to!(TypeDeclaration[])(structs))
252             .sort!("a.id < b.id").array;
253     }
254 
255     /// Finds a `ClassDeclaration` or a `StructDeclaration` with the given name.
256     TypeDeclaration findType(string name)
257     {
258         auto candidate = typesById.chain(aliases).filter!(x => x.symbol == name).takeOne.front;
259         return candidate;
260     }
261 
262     TypeDeclaration findType(int id)
263     {
264         return typesById.filter!(x => x.id == id).takeOne.front;
265     }
266 }
267 
268 /// Represents an import statement.
269 class ImportDeclaration : SyntaxNode
270 {
271     /// The package from which to import.
272     string packageName;
273 
274     /// The symbols to import.
275     string[] symbols;
276 
277     mixin(generateSyntaxNode);
278 }
279 
280 /// Base class for module-level type declarations.
281 /// A type is a keyword declaration, a class declaration,
282 /// a struct declaration, or an alias/typedef declaration.
283 abstract class TypeDeclaration : SyntaxNode
284 {
285     /// Represents a type of TypeDeclaration.
286     enum Kind
287     {
288         keywordDeclaration,
289         classDeclaration,
290         structDeclaration,
291         aliasDeclaration,
292         builtin,
293     }
294 
295     /// The id of this Type.
296     int id;
297 
298     /// The symbol name of this type.
299     string symbol;
300 
301     /// Gets the kind of TypeDeclaration this object represents.
302     abstract Kind kind();
303 
304     mixin(GenerateThis);
305 
306     /// Gets the `Type` associated with this `TypeDeclaration`.
307     abstract Type type() pure nothrow @property;
308 
309     /// Gets all fields declared on this type.
310     abstract FieldDeclaration[] getFields() pure nothrow @property;
311 
312     /// Find a field by its id.
313     final FieldDeclaration getField(int id)
314     {
315         return getFields.filter!(x => x.id == id).takeOne.front;
316     }
317 
318     /// Find a field with its name.
319     final FieldDeclaration getField(string symbol)
320     {
321         return getFields.filter!(x => x.name == symbol).takeOne.front;
322     }
323 }
324 
325 /// Represents a fundamental type.
326 class BuiltinType : TypeDeclaration
327 {
328     /// The type of this declaration.
329     Type _type;
330 
331     /// Ditto
332     override TypeDeclaration.Kind kind()
333     {
334         return TypeDeclaration.Kind.builtin;
335     }
336 
337     /// Ditto
338     override Type type() pure nothrow @property
339     {
340         return _type;
341     }
342 
343     /// Ditto.
344     override FieldDeclaration[] getFields() pure nothrow @property
345     {
346         return [];
347     }
348 
349     mixin(generateSyntaxNode);
350 }
351 
352 /// Represents a list of keywords attached to a field.
353 class KeywordList : SyntaxNode
354 {
355     /// The list of keywords.
356     string[] keywords;
357 
358     alias keywords this;
359 
360     mixin(generateSyntaxNode);
361 }
362 
363 /// Represents the value of a `keyword` statement.
364 class KeywordDeclaration : TypeDeclaration
365 {
366     /// Ditto
367     override TypeDeclaration.Kind kind()
368     {
369         return TypeDeclaration.Kind.keywordDeclaration;
370     }
371 
372     mixin(generateSyntaxNode);
373 
374     override FieldDeclaration[] getFields() pure nothrow @property
375     {
376         return [];
377     }
378 
379     /// Ditto
380     override Type type() pure nothrow @property
381     {
382         return Type.invalid;
383     }
384 }
385 
386 /// Represents a struct declaration.
387 class StructDeclaration : TypeDeclaration
388 {
389     override TypeDeclaration.Kind kind()
390     {
391         return TypeDeclaration.Kind.structDeclaration;
392     }
393 
394     /// The atomic parts of this struct.
395     ParameterField[] parameters;
396 
397     mixin(generateSyntaxNode);
398 
399     /// Ditto
400     override Type type() pure nothrow @property
401     {
402         return Type.struct_;
403     }
404 
405     override FieldDeclaration[] getFields() pure nothrow @property
406     {
407         return parameters.to!(FieldDeclaration[]);
408     }
409 }
410 
411 /// Represents a dclass declaration.
412 class ClassDeclaration : TypeDeclaration
413 {
414     override TypeDeclaration.Kind kind()
415     {
416         return TypeDeclaration.Kind.classDeclaration;
417     }
418 
419     /// The superclass for this type.
420     @(This.Exclude)
421     ClassDeclaration superclass;
422 
423     string superclassName;
424 
425     /// The constructor for this type.
426     FieldDeclaration constructor;
427 
428     /// The fields and RPC methods defined on this class.
429     FieldDeclaration[] fields;
430 
431     mixin(generateSyntaxNode);
432 
433     /// Ditto
434     override Type type() pure nothrow @property
435     {
436         return Type.struct_;
437     }
438 
439     /// Resolves superclasses.
440     void resolve(Module file)
441     {
442         if (hasSuperclass())
443         {
444             superclass = cast(ClassDeclaration) file.findType(superclassName);
445         }
446     }
447 
448     /// Checks whether this ClassDeclaration has a super class.
449     bool hasSuperclass() pure inout @nogc @property
450     {
451         return superclassName.length > 0;
452     }
453 
454     /// Get the chain of parents.  The closest parent is first.
455     ClassDeclaration[] parents() pure @property
456     {
457         auto current = superclass;
458 
459         ClassDeclaration[] parents;
460         while (current !is null)
461         {
462             parents ~= current;
463             current = current.superclass;
464         }
465 
466         return parents;
467     }
468 
469     /// Gets whether this class has a constructor.
470     bool hasConstructor() pure @nogc @property
471     {
472         return constructor !is null;
473     }
474 
475     /// Ditto.
476     override FieldDeclaration[] getFields() pure nothrow @property
477     {
478         return fields;
479     }
480 }
481 
482 /// Represents a typedef statement.
483 class AliasDeclaration : TypeDeclaration
484 {
485     override TypeDeclaration.Kind kind()
486     {
487         return TypeDeclaration.Kind.aliasDeclaration;
488     }
489 
490     /// The type to substitute the symbol for.
491     /// For example, `typedef doId uint32` would
492     /// set the aliasedTypeName to uint32.
493     string aliasedTypeName;
494 
495     @(This.Exclude)
496     TypeDeclaration aliasedType;
497 
498     mixin(generateSyntaxNode);
499 
500     /// Ditto
501     override Type type() pure nothrow @property
502     {
503         enum types = genbuiltins;
504 
505         if (auto ret = aliasedTypeName in types)
506         {
507             return *ret;
508         }
509 
510         return Type.invalid;
511     }
512 
513     /// Ditto.
514     override FieldDeclaration[] getFields() pure nothrow @property
515     {
516         return [];
517     }
518 }
519 
520 /// Abstract class representing a field.
521 abstract class FieldDeclaration : SyntaxNode
522 {
523     /// The id of this field.
524     int id;
525     mixin(GenerateThis);
526 
527     /// Gets the symbol name of this field.
528     abstract string name() @property;
529 
530     abstract string[] attributes() @property;
531 
532     final bool hasKeyword(string keyword)
533     {
534         return attributes.canFind(keyword);
535     }
536 }
537 
538 /// Represents a field which swizzles atomic fields.
539 class MolecularField : FieldDeclaration
540 {
541     /// The name of this field.
542     string symbol;
543 
544     /// The atomic fields which make up this field.
545     @(This.Exclude)
546     FieldDeclaration[] references;
547 
548     /// The names of the references. Used for resolution.
549     string[] referenceNames;
550 
551     mixin(generateSyntaxNode);
552 
553     /// Ditto
554     override string name() @property
555     {
556         return symbol;
557     }
558 
559     override string[] attributes() @property
560     {
561         return [];
562     }
563 }
564 
565 /// Represents a method.
566 class AtomicField : FieldDeclaration
567 {
568     /// The name of this field.
569     string symbol;
570 
571     /// The parameters for this field.
572     Parameter[] parameters;
573 
574     /// The keywords applied to this field.
575     KeywordList keywords;
576     mixin(generateSyntaxNode);
577 
578     /// Get the fundamental type of this field.
579     /// Returns: `Type.method`.
580     Type type()
581     {
582         return Type.method;
583     }
584 
585     /// Ditto
586     override string name() @property
587     {
588         return symbol;
589     }
590 
591     override string[] attributes() @property
592     {
593         return keywords;
594     }
595 }
596 
597 /// Represents a plain field.
598 class ParameterField : FieldDeclaration
599 {
600     /// Get the parameter for this field.
601     Parameter parameter;
602 
603     /// Get the keywords on this field.
604     KeywordList keywords;
605     mixin(generateSyntaxNode);
606 
607     /// Ditto
608     override string name() @property
609     {
610         return parameter.symbol;
611     }
612 
613     override string[] attributes() @property
614     {
615         return keywords;
616     }
617 }
618 
619 /// Abstract class representing a parameter.
620 class Parameter : SyntaxNode
621 {
622     /// Get the name of this parameter.
623     string symbol;
624 
625     /// Get the fundamental type of this parameter.
626     abstract Type parameterType() @property;
627 
628     mixin(GenerateThis);
629 }
630 
631 /// Represents a parameter of a numeric type.
632 class NumericParameter : Parameter
633 {
634     private @(This.Exclude)
635     {
636         double _origMod;
637     }
638 
639     /**
640      * The type of this parameter.
641      * 
642      * Remarks:
643      * May be `char`, `int8`, `int16`, `int32`,
644      * `int64`, `uint8`, `uint16`, `uint32`,
645      * `uint64`, `float32`, or `float64`.
646      */
647     string type;
648 
649     /// The divisor 
650     uint divisor = 1;
651 
652     private double _modulus;
653 
654     /// The possible range of this parameter.
655     /// This may be null.
656     NumericRange range;
657 
658     version (none)
659     {
660         // Represents any transforms present on this parameter.
661         // This may be null.    
662         NumericTransform transform;
663     }
664 
665     /// The default value of this parameter.
666     /// This may be null.    
667     NumericConstant defaultValue;
668 
669     mixin(generateSyntaxNode);
670 
671     bool hasRange()
672     {
673         import std.math : isNaN;
674 
675         return range !is null;
676     }
677 
678     double modulus() @property
679     {
680         return _modulus;
681     }
682 
683     bool modulus(double value) @property
684     {
685         import std.math : floor;
686 
687         if (value == double.nan)
688         {
689             _modulus = value;
690             return true;
691         }
692 
693         if (modulus <= 0.0)
694         {
695             return false;
696         }
697 
698         auto floatModulus = value * divisor;
699         auto uintModulus = cast(uint) floor(value * divisor + 0.5);
700 
701         switch (parameterType) with (Type)
702         {
703         case int8:
704             if (uintModulus < 1 || cast(ushort)(ubyte.max + 1 < uintModulus))
705             {
706                 return false;
707             }
708             _modulus = uintModulus;
709             break;
710         case int16:
711             if (uintModulus < 1 || cast(uint)(ushort.max + 1 < uintModulus))
712             {
713                 return false;
714             }
715             _modulus = uintModulus;
716             break;
717         case int32:
718             if (uintModulus < 1 || cast(ulong)(uint.max + 1 < uintModulus))
719             {
720                 return false;
721             }
722             _modulus = uintModulus;
723             break;
724         case int64:
725             if (uintModulus < 1)
726             {
727                 return false;
728             }
729             _modulus = uintModulus;
730             break;
731         case uint8:
732             if (uintModulus < 1 || cast(ushort)(ubyte.max + 1 < uintModulus))
733             {
734                 return false;
735             }
736             _modulus = uintModulus;
737             break;
738         case uint16:
739             if (uintModulus < 1 || cast(uint)(ushort.max + 1 < uintModulus))
740             {
741                 return false;
742             }
743             _modulus = uintModulus;
744             break;
745         case uint32:
746             if (uintModulus < 1 || cast(ulong)(uint.max + 1 < uintModulus))
747             {
748                 return false;
749             }
750             _modulus = uintModulus;
751             break;
752         case uint64:
753             if (uintModulus < 1)
754             {
755                 return false;
756             }
757             _modulus = uintModulus;
758             break;
759         case float32:
760         case float64:
761             _modulus = floatModulus;
762             break;
763         default:
764             assert(0);
765         }
766         _origMod = value;
767         return true;
768     }
769 
770     bool hasModulus()
771     {
772         import std.math : isNaN;
773 
774         return !isNaN(_modulus);
775     }
776 
777     override Type parameterType() @property
778     {
779         switch (type)
780         {
781         case "char":
782             return Type.char_;
783         case "int8":
784             return Type.int8;
785         case "int16":
786             return Type.int16;
787         case "int32":
788             return Type.int32;
789         case "int64":
790             return Type.int64;
791         case "uint8":
792             return Type.uint8;
793         case "uint16":
794             return Type.uint16;
795         case "uint32":
796             return Type.uint32;
797         case "uint64":
798             return Type.uint64;
799         case "float32":
800             return Type.float32;
801         case "float64":
802             return Type.float64;
803         default:
804             assert(0, "Invalid type " ~ type);
805         }
806     }
807 }
808 
809 /// Represents the minimum and maximum value of a NumericParameter.
810 class NumericRange : SyntaxNode
811 {
812     /// The minimum value of this range.
813     double min;
814 
815     /// The maximum value of this range.
816     double max;
817 
818     mixin(generateSyntaxNode);
819 }
820 
821 /// Represents a transformation on a NumericParameter or IntConstant.
822 class NumericTransform : SyntaxNode
823 {
824     /// The operator to apply.
825     Operator operator;
826 
827     /// The value to apply.
828     double value;
829 
830     /// The next transformation.
831     /// This may be null.    
832     NumericTransform next;
833 
834     /// Instantiates an IntTransform.
835     this(Operator op, double value, NumericTransform next)
836     {
837         operator = op;
838         this.value = value;
839         this.next = next;
840     }
841 
842     mixin(generateVisit);
843 }
844 
845 /// Represents an integer constant.
846 class NumericConstant : SyntaxNode
847 {
848     /// The value of this constant.
849     double value;
850 
851     /// Any transformations which need to be applied.
852     NumericTransform transform;
853 
854     mixin(generateSyntaxNode);
855 }
856 
857 /// Represents a blob or string parameter.
858 class SizedParameter : Parameter
859 {
860     /// The type of this parameter.
861     string type;
862 
863     /// The size constraints of this parameter.
864     /// If the minimum value equals the maximum value,
865     /// This is a fixed-sized parameter.  Otherwise,
866     /// it's a variable-sized parameter.
867     SizeConstraint size;
868 
869     /// The default value of this parameter.
870     /// This may be null.  This is only valid
871     /// on string-type parameters.
872     string defaultVal;
873 
874     mixin(generateSyntaxNode);
875 
876     bool hasRange()
877     {
878         // dfmt off
879         return size !is null 
880             && size.maxSize != 0;
881         // dfmt on
882     }
883 
884     override Type parameterType() @property
885     {
886         if (hasRange && size.isFixedLength)
887         {
888             if (type == "string")
889             {
890                 return Type.string_;
891             }
892             else if (type == "blob")
893             {
894                 return Type.blob;
895             }
896         }
897         else
898         {
899             if (type == "string")
900             {
901                 return Type.varstring;
902             }
903             else if (type == "blob")
904             {
905                 return Type.varblob;
906             }
907         }
908         assert(0);
909     }
910 }
911 
912 /// Represents the constraints on a SizedParameter.
913 class SizeConstraint : SyntaxNode
914 {
915     /// The minimum size of this parameter.
916     int minSize;
917 
918     /// The maximum size of this parameter.    
919     int maxSize;
920 
921     mixin(generateSyntaxNode);
922 
923     /// Gets whether this parameter is fixed-length.
924     bool isFixedLength() pure @property
925     {
926         return minSize == maxSize && maxSize > 1;
927     }
928 }
929 
930 class StructParameter : Parameter
931 {
932     string typeName;
933 
934     @(This.Exclude)
935     TypeDeclaration type;
936 
937     mixin(generateSyntaxNode);
938 
939     TypeDeclaration resolveType(Module mod)
940     {
941         return mod.findType(typeName);
942     }
943 
944     override Type parameterType() @property
945     {
946         return type.type;
947     }
948 
949 }
950 
951 class ArrayParameter : Parameter
952 {
953     string type;
954     ArrayRange size;
955 
956     @(This.Exclude)
957     TypeDeclaration elementType;
958 
959     mixin(generateSyntaxNode);
960 
961     TypeDeclaration resolveType(Module mod)
962     {
963         return mod.findType(type);
964     }
965 
966     bool hasRange()
967     {
968         // dfmt off
969         return size !is null 
970             && size.maxLength != 0;
971         // dfmt on
972     }
973 
974     override Type parameterType() @property
975     {
976         if (size.isFixedLength)
977         {
978             return Type.array;
979         }
980         else
981         {
982             return Type.vararray;
983         }
984     }
985 
986 }
987 
988 class ArrayRange : SyntaxNode
989 {
990     int minLength;
991     int maxLength;
992 
993     mixin(generateSyntaxNode);
994 
995     bool isFixedLength() pure @property
996     {
997         return minLength == maxLength && maxLength > 1;
998     }
999 }