1 module bamboo.codegen.helpers;
2 
3 import bamboo.codegen;
4 
5 mixin template ParentConstructors()
6 {
7     import std.traits : Parameters, FunctionTypeOf;
8 
9     static if (is(super.ctor))
10     {
11         static if (!is(FunctionTypeOf!(super.__ctor) == void))
12         {
13             static foreach (ctor; __traits(getOverloads, typeof(super), "__ctor", true))
14             {
15                 this(Parameters!ctor args)
16                 {
17                     ctor(args);
18                 }
19             }
20         }
21     }
22 }
23 
24 string generateDefinition(Parameter parameter)
25 {
26     switch (parameter.syntaxKind)
27     {
28     case SyntaxKind.StructParameter:
29         return generateDefinition(cast(StructParameter) parameter);
30     case SyntaxKind.ArrayParameter:
31         return generateDefinition(cast(ArrayParameter) parameter);
32     case SyntaxKind.SizedParameter:
33         return generateDefinition(cast(SizedParameter) parameter);
34     case SyntaxKind.NumericParameter:
35         return generateDefinition(cast(NumericParameter) parameter);
36     default:
37         assert(0);
38     }
39 }
40 
41 string generateDefinition(StructParameter parameter)
42 {
43     enum string format = `${type} ${name}`;
44 
45     string type = mapType(parameter.type.symbol);
46     string name = generateName(parameter.symbol, type, 1);
47 
48     parameter.symbol = name;
49 
50     return mixin(interp!format);
51 }
52 
53 string generateDefinition(ArrayParameter array)
54 {
55     enum string format = `${type}[${maxLength}] ${name}`;
56 
57     string type = mapType(array.elementType.symbol);
58     string name = generateName(array.symbol, type, 2);
59     string maxLength = "";
60 
61     version (RespectFixedLength)
62     {
63         if (array.hasRange && array.size.isFixedLength)
64         {
65             maxLength = array.size.maxLength.to!string;
66         }
67     }
68 
69     array.symbol = name;
70 
71     return mixin(interp!format);
72 }
73 
74 string generateDefinition(SizedParameter array)
75 {
76     enum string format = `${type} ${name}`;
77 
78     string type;
79     string maxLength = "";
80     string defaultVal = "";
81 
82     version (RespectFixedLength)
83     {
84         if (array.hasRange && array.size.isFixedLength)
85         {
86             maxLength = array.size.maxLength.to!string;
87         }
88 
89         switch (array.parameterType) with (Type)
90         {
91         case string_:
92         case varstring:
93             type = "immutable char[${maxLength}]";
94             break;
95         case blob:
96         case varblob:
97             type = "immutable ubyte[${maxLength}]";
98             break;
99         default:
100             assert(0);
101         }
102         type = mixin(interp!type);
103     }
104     else
105     {
106         switch (array.parameterType) with (Type)
107         {
108         case string_:
109         case varstring:
110             type = "string";
111             break;
112         case blob:
113         case varblob:
114             type = "blob";
115             break;
116         default:
117             assert(0);
118         }
119     }
120 
121     type = mapType(type);
122 
123     string name = generateName(array.symbol, type, 3);
124 
125     if (array.defaultVal.length > 0)
126     {
127         defaultVal = mixin(interp!" = \"${array.defaultVal}\"");
128     }
129 
130     array.symbol = name;
131 
132     return mixin(interp!format);
133 }
134 
135 string generateDefinition(NumericParameter numeric)
136 {
137     enum string format = `${type} ${name}`;
138 
139     string type = mapType(numeric.type.to!string());
140     string name = generateName(numeric.symbol, type, 4);
141 
142     numeric.symbol = name;
143 
144     return mixin(interp!format);
145 }
146 
147 string mapType(string type)
148 {
149     // dfmt off
150     enum types = [
151             "int8": "byte", 
152             "int16": "short",
153             "int32": "int", 
154             "int64": "long", 
155             "uint8": "ubyte", 
156             "uint16": "ushort", 
157             "uint32": "uint", 
158             "uint64": "ulong", 
159             "float32": "float", 
160             "float64": "double", 
161             "char_": "char",
162             "varstring": "string", 
163             "blob": "ubyte[]", 
164             "varblob": "ubyte[]", 
165         ];
166     // dfmt on
167 
168     if (auto ret = type in types)
169     {
170         return *ret;
171     }
172     return type;
173 }
174 
175 string generateContractFor(Parameter parameter)
176 {
177     if (auto array = cast(SizedParameter) parameter)
178     {
179         return generateContract(array);
180     }
181     else if (auto array = cast(ArrayParameter) parameter)
182     {
183         return generateContract(array);
184     }
185     else if (auto numeric = cast(NumericParameter) parameter)
186     {
187         return generateContract(numeric);
188     }
189     return "";
190 }
191 
192 string generateContract(SizedParameter array)
193 {
194     enum string format = `
195     assert(${parameter}.length <= ${maxLength}, "${parameter} is oversized!");
196     assert(${parameter}.length >= ${minLength}, "${parameter} is undersized!");
197     `;
198 
199     if (!array.hasRange)
200     {
201         return "";
202     }
203 
204     string parameter = array.symbol;
205     string maxLength = array.size.maxSize.to!string;
206     string minLength = array.size.minSize.to!string;
207 
208     return mixin(interp!format);
209 }
210 
211 string generateContract(ArrayParameter array)
212 {
213     enum string format = `
214     assert(${parameter}.length <= ${maxLength}, "${parameter} is oversized!");
215     assert(${parameter}.length >= ${minLength}, "${parameter} is undersized!");
216     `;
217 
218     if (!array.hasRange)
219     {
220         return "";
221     }
222 
223     string parameter = array.symbol;
224     string maxLength = array.size.maxLength.to!string;
225     string minLength = array.size.minLength.to!string;
226 
227     return mixin(interp!format);
228 }
229 
230 string generateContract(NumericParameter numeric)
231 {
232     string formatStr(string parameter, string op, string value, string msg)
233     {
234         return `assert(` ~ parameter ~ ` ` ~ op ~ ` ` ~ value ~ `, "` ~ parameter
235             ~ ` is too ` ~ msg ~ `!");`;
236     }
237 
238     string format;
239 
240     if (!numeric.hasRange)
241     {
242         return "";
243     }
244 
245     string parameter = numeric.symbol;
246     string max = numeric.range.max.to!string;
247     string min;
248 
249     format ~= formatStr(parameter, "<=", max, "large");
250 
251     if (!isNaN(numeric.range.min))
252     {
253         min = numeric.range.min.to!string;
254         format ~= formatStr(parameter, ">=", min, "small");
255     }
256 
257     return format;
258 }
259 
260 string generateName(string name, string type, int counter)
261 {
262     if (name.length == 0)
263     {
264         name = mixin(interp!"_${type.toLower}${counter}");
265     }
266     return name;
267 }