1 module hipddf.parser;
2 import std.stdio;
3 import hipddf.types;
4 import std.conv: to;
5 
6 
7 enum HipDDFTokenType
8 {
9     assignment,
10     comma,
11     colon,
12     semicolon,
13     openParenthesis,
14     closeParenthesis,
15     openSquareBrackets,
16     closeSquareBrackets,
17     openCurlyBrackets,
18     closeCurlyBrackets,
19     endOfStream,
20     symbol,
21     stringLiteral,
22     numberLiteral,
23     unknown,
24     error
25 }
26 
27 
28 
29 struct HipDDFToken
30 {
31     string str;
32     HipDDFTokenType type;
33     static HipDDFToken error(){return HipDDFToken("Error", HipDDFTokenType.error);}
34 
35     string toString()
36     {
37         string T;
38         swt: final switch(type)
39         {
40             static foreach(m; __traits(allMembers, HipDDFTokenType))
41             {
42                 case __traits(getMember, HipDDFTokenType, m):
43                     T = m.stringof;
44                     break swt;
45             }
46         }
47         return str~" ("~T~")";
48     }
49 }
50 
51 struct HipDDFStruct
52 {
53     string name;
54     string[] types;
55     string[] symbols;
56 }
57 
58 
59 struct HipDDFTokenizer
60 {
61     string str;
62     string filename;
63     ulong pos;
64     uint line;
65     HipDDFObjectInternal* obj;
66     string[] err;
67 
68     HipDDFToken setError(string err, string file = __FILE__, string func = __PRETTY_FUNCTION__, uint line = __LINE__)
69     {
70         string errFormat = err ~" at line "~to!string(this.line) ~" ("~file~":"~to!string(line)~")";
71         this.err~= errFormat;
72         return HipDDFToken(errFormat, HipDDFTokenType.error);
73     }
74     void showErrors()
75     {
76         foreach(e; err)
77             writeln(e);
78     }
79     bool hasVar(string str){return obj.hasVar(str);}
80     HipDDFVarInternal* getVar(string str){return str in obj.variables;}
81 
82     /** Returns str[pos] */
83     pragma(inline) @nogc nothrow @safe char get(){return str[pos];}
84     /** Returns str[pos+1], used for not needing to access every time its members */
85     pragma(inline) @nogc nothrow @safe char next(){return str[pos+1];}
86     /** Returns str.length - pos */
87     pragma(inline) @nogc nothrow @safe int restLength(){return cast(int)(str.length - pos);}
88 }
89 
90 alias HipDDFKeywordParse = HipDDFToken function(HipDDFTokenizer* tokenizer);
91 alias HipDDFBuiltinTypeCheck = bool function (string type, HipDDFToken tk);
92 
93 immutable(HipDDFKeywordParse[string]) keywords;
94 immutable(HipDDFBuiltinTypeCheck[string]) builtinTypes;
95 
96 
97 nothrow @safe @nogc
98 private void advanceWhitespace(HipDDFTokenizer* tokenizer)
99 {
100     while(tokenizer.restLength > 0)
101     {
102         if(isWhitespace(tokenizer.get))
103         {
104             if(tokenizer.get == '\n')
105                 tokenizer.line++;
106             tokenizer.pos++;
107         }
108         else if(tokenizer.get == '/' && tokenizer.restLength > 1 && tokenizer.next == '/')
109         {
110             while(!isEndOfLine(tokenizer.get))
111                 tokenizer.pos++;
112             tokenizer.line++;
113         }
114         else if(tokenizer.get == '/' && tokenizer.restLength > 1 && (tokenizer.next == '*' || tokenizer.next == '+'))
115         {
116             tokenizer.pos+= 2;
117             while(tokenizer.restLength && 
118             !((tokenizer.get == '*' || tokenizer.get == '+') && (tokenizer.restLength > 1 && tokenizer.next == '/')))
119             {
120                 if(tokenizer.get == '\n')
121                     tokenizer.line++;
122                 tokenizer.pos++;
123             }
124             tokenizer.pos+= 2;
125         }
126         else
127             break;
128     }
129 }
130 
131 HipDDFToken getToken(HipDDFTokenizer* tokenizer)
132 {
133     HipDDFToken ret;
134     advanceWhitespace(tokenizer);
135     if(tokenizer.pos == tokenizer.str.length)
136         return HipDDFToken("", HipDDFTokenType.endOfStream);
137     char C = tokenizer.get;
138     ulong start = tokenizer.pos;
139     tokenizer.pos++;
140 
141     switch(C)
142     {
143         case '=':  {ret.str = "=";ret.type = HipDDFTokenType.assignment; break;}
144         case ',':  {ret.str = ",";ret.type = HipDDFTokenType.comma; break;}
145         case ';':  {ret.str = ";";ret.type = HipDDFTokenType.semicolon; break;}
146         case ':':  {ret.str = ":";ret.type = HipDDFTokenType.colon; break;}
147         case '(':  {ret.str = "(";ret.type = HipDDFTokenType.openParenthesis; break;}
148         case ')':  {ret.str = ")";ret.type = HipDDFTokenType.closeParenthesis; break;}
149         case '[':  {ret.str = "[";ret.type = HipDDFTokenType.openSquareBrackets; break;}
150         case ']':  {ret.str = "]";ret.type = HipDDFTokenType.closeSquareBrackets; break;}
151         case '{':  {ret.str = "{";ret.type = HipDDFTokenType.openCurlyBrackets; break;}
152         case '}':  {ret.str = "}";ret.type = HipDDFTokenType.closeCurlyBrackets; break;}
153         case '\0':{ret.str = "\0";ret.type = HipDDFTokenType.endOfStream; break;}
154         case '"':
155 
156             while(tokenizer.restLength && tokenizer.get != '"')
157             {
158                 if(tokenizer.get == '\\')
159                     tokenizer.pos++;
160                 tokenizer.pos++;
161             }
162             tokenizer.pos++; //Advance the '"'
163             ret.str = tokenizer.str[start..tokenizer.pos]; //Remove the ""
164             ret.type = HipDDFTokenType.stringLiteral;
165             break;
166         default:
167             if(isNumeric(C)) //Check numeric literal
168             {
169                 while(tokenizer.get && isNumeric(tokenizer.get))
170                     tokenizer.pos++;
171                 ret.str = tokenizer.str[start..tokenizer.pos];
172                 ret.type = HipDDFTokenType.numberLiteral;
173             }
174             else if(isAlpha(C) || C == '_') //Check symbol
175             {
176                 while(tokenizer.get.isNumeric || tokenizer.get.isAlpha || tokenizer.get =='_')
177                     tokenizer.pos++;
178                 ret.str = tokenizer.str[start..tokenizer.pos];
179                 //I'll consider creating a function for that if it happens to have more special symbols
180                 auto kwParse = ret.str in keywords;
181                 if(kwParse is null)
182                     ret.type = HipDDFTokenType.symbol;
183                 else
184                     ret = (*kwParse)(tokenizer);
185             }
186             else
187             {
188                 ret.type = HipDDFTokenType.unknown;
189                 ret.str = ""~to!string((cast(int)C));
190             }
191 
192 
193     }
194 
195     return ret;
196 }
197 
198 /**
199 *   This state must be used as a cyclic state for parsing it correctly.
200 */
201 private enum HipDDFState
202 {
203     type,
204     symbol,
205     assignment
206 }
207 
208 /**
209 *   It must always find in the following order:
210 *   1: Type
211 *   2: Symbol
212 *   3: Assignment
213 *   4: Data
214 *   By following this order, the data format will be really simple to follow.
215 */
216 HipDDFObject parseHipDDF(string hdf, string filename = __FILE__)
217 {
218     HipDDFObjectInternal* obj = new HipDDFObjectInternal("");
219     HipDDFTokenizer tokenizer;
220     tokenizer.str = hdf;
221     tokenizer.filename = filename;
222     tokenizer.obj = obj;
223 
224     HipDDFToken tk = HipDDFToken("", HipDDFTokenType.unknown);
225     HipDDFState state = HipDDFState.type;
226     tk = getToken(&tokenizer);
227 
228     HipDDFVarInternal variable;
229     HipDDFVarInternal lastVar;
230     
231     while(tk.type != HipDDFTokenType.endOfStream)
232     {
233         final switch(state)
234         {
235             case HipDDFState.type:
236                 //Ask for symbol to be used as a type
237                 tk = parseType(variable, tk, &tokenizer);
238                 state = HipDDFState.symbol;
239                 break;
240             case HipDDFState.symbol: //No parsing should be required for the symbol.
241                 variable.symbol = tk.str;
242                 state = HipDDFState.assignment;
243                 if(!requireToken(&tokenizer, HipDDFTokenType.assignment, tk))
244                 {
245                     tk = tokenizer.setError("Expected variable assignment after the symbol '"~tk.toString);
246                 }
247                 break;
248             case HipDDFState.assignment:
249                 tk = parseAssignment(variable, tk, &tokenizer);
250                 obj.variables[variable.symbol] = variable;
251                 lastVar = variable;
252                 variable = HipDDFVarInternal.init;
253                 state = HipDDFState.type;
254                 break;
255         }
256         if(tk.type == HipDDFTokenType.error)
257         {
258             tokenizer.showErrors();
259             break;
260         }
261     }
262     return cast(HipDDFObject)obj;
263 }
264 
265 
266 /**
267 *   Checks if the type passed matches the string containing the value
268 */
269 bool checkTypeMatch(string type, string str)
270 {
271     if(type == "string")
272         return str[0].isAlpha || str[0] == '_';
273     else if(type == "int" || type == "float")
274         return str[0].isNumeric;
275     return false;
276 }
277 
278 bool checkTypeMatch(HipDDFVarInternal variable, HipDDFToken tk)
279 {
280     immutable(HipDDFBuiltinTypeCheck*) chk = variable.type in builtinTypes;
281     if(chk !is null)
282         return (*chk)(variable.type, tk);
283     return false;
284 }
285 
286 HipDDFToken parseValue(ref HipDDFVarInternal variable, HipDDFToken token, HipDDFTokenizer* tokenizer)
287 {
288     switch(token.type)
289     {
290         case HipDDFTokenType.stringLiteral:
291         case HipDDFTokenType.numberLiteral:
292             variable.value = token.str;
293             if(token.type == HipDDFTokenType.stringLiteral)
294                 variable.length = cast(uint)token.str.length;
295             return token;
296         case HipDDFTokenType.openCurlyBrackets:
297             token = parseStruct(variable, token, tokenizer);
298             return token;
299         case HipDDFTokenType.symbol:
300             if(token.isStructLiteral(tokenizer))
301                 token = parseStruct(variable, token, tokenizer);
302             else
303             {
304                 if(!tokenizer.hasVar(token.str))
305                 {
306                     return tokenizer.setError("Variable '"~token.str~"' is not defined at line "~to!string(tokenizer.line));
307                 }
308                 variable.value = tokenizer.getVar(token.str).value;
309             }
310             return token;
311         case HipDDFTokenType.openSquareBrackets:
312             variable.value = "[";
313             token = getToken(tokenizer);
314             if(variable.isAssociativeArray)
315             {
316                 while(token.type.isAssociativeArraySyntax)
317                 {
318                     if(token.type == HipDDFTokenType.colon)
319                     {
320                         variable.value~=":";
321                         token = getToken(tokenizer);
322                         //Right value
323                         HipDDFVarInternal tempVar;
324                         tempVar.type = variable.getValueType;
325                         token = parseValue(tempVar, token, tokenizer);
326                         variable.value~= tempVar.value;
327                     }
328                     else
329                         variable.value~= token.str;
330                     token = getToken(tokenizer);
331                 }
332             }
333             else
334             {
335                 int arrayCount = 0;
336                 while( token.type.isArraySyntax)
337                 {
338                     if(token.type == HipDDFTokenType.comma)
339                     {
340                         variable.value~= ",";
341                     }
342                     else if(token.type.isValueSyntax)
343                     {
344                         HipDDFVarInternal tempVar;
345                         tempVar.type = variable.getValueType;
346                         token = parseValue(tempVar, token, tokenizer);
347                         variable.value~= tempVar.value;
348                         arrayCount++;
349                     }
350                     
351                     token = getToken(tokenizer);
352                 }
353                 variable.length = arrayCount;
354             }
355             if(token.type != HipDDFTokenType.closeSquareBrackets)
356             {
357                 return tokenizer.setError("Expected ], but received "~token.toString~
358             " on variable "~variable.symbol);
359             }
360             variable.value~="]";
361             return token;
362         default: assert(0,  "Unexpected token after assignment: "~token.toString);
363     }
364 }
365 HipDDFToken parseAssignment(ref HipDDFVarInternal variable, HipDDFToken token, HipDDFTokenizer* tokenizer)
366 {
367     if(token.type != HipDDFTokenType.assignment)
368     {
369         return tokenizer.setError("Tried to parse a non assigment token: "~token.toString);
370     }
371     token = getToken(tokenizer);
372     token = parseValue(variable, token, tokenizer);
373     token = findToken(tokenizer, HipDDFTokenType.symbol);
374     return token;
375 }
376 
377 HipDDFToken parseStruct(ref HipDDFVarInternal variable, HipDDFToken token, HipDDFTokenizer* tokenizer)
378 {
379     if(token.isStructLiteral(tokenizer) && token.str == variable.type)
380     {
381         HipDDFStruct structure = tokenizer.obj.structs[token.str];
382         if(!requireToken(tokenizer, HipDDFTokenType.openParenthesis, token))
383         {
384             return tokenizer.setError("Expected a '(' after the "~token.str~" on line "~to!string(tokenizer.line));
385         }
386         int typeIndex = 0;
387         HipDDFToken lastToken;
388         variable.value = "(";
389 
390         //Advance the parenthesis for parsing values
391         token = getToken(tokenizer);
392 
393         while(token.type != HipDDFTokenType.closeParenthesis)
394         {
395             if(token.type == HipDDFTokenType.comma)
396             {
397                 // assert(checkTypeMatch(structure.types[typeIndex], lastToken.str));
398                 variable.value~= ",";
399                 typeIndex++;
400             }
401             else
402             {
403                 HipDDFVarInternal temp;
404                 temp.type = structure.types[typeIndex];
405                 token = parseValue(temp, token, tokenizer);
406                 variable.value~= temp.value;
407             }
408             lastToken = token;
409             token = getToken(tokenizer);
410         }
411         variable.value~=")";
412         return token;
413     }
414     else if(token.type == HipDDFTokenType.openCurlyBrackets)
415     {
416         HipDDFStruct structure = tokenizer.obj.structs[variable.type];
417         string[] values = new string[](structure.types.length);
418 
419         while(token.type != HipDDFTokenType.closeCurlyBrackets)
420         {
421             if(!requireToken(tokenizer, HipDDFTokenType.symbol, token) && token.type != HipDDFTokenType.closeCurlyBrackets)
422                 return tokenizer.setError("Expected a symbol on struct initialization ");
423             else
424             {
425                 HipDDFToken memberToken = token;
426                 int i = 0;
427                 while(i < structure.symbols.length && structure.symbols[i] != memberToken.str){i++;}
428                 if(i == structure.symbols.length)
429                     return tokenizer.setError("Member '"~memberToken.str~"' not found on type "~structure.name);
430 
431                 if(!requireToken(tokenizer, HipDDFTokenType.colon, token))
432                     return tokenizer.setError("Expected a : after symbol "~memberToken.str ~ "on line "~to!string(tokenizer.line));
433                 token = getToken(tokenizer);
434                 //Here could possibly be any value
435                 HipDDFVarInternal tempVar;
436                 token = parseValue(tempVar, token, tokenizer);
437                 values[i] = tempVar.value;
438             }
439             token = getToken(tokenizer);
440         }
441         variable.value = "(";
442         foreach(i, v; values)
443         {
444             if(i)
445                 variable.value~=",";
446             variable.value~= v;
447         }
448         variable.value~=")";
449         return token;
450     }
451     return tokenizer.setError("Could not parse struct with token: "~token.toString);
452 }
453 
454 /**
455 *   The token passed is assumed to contain the initial type symbol.
456 *   It will finish parsing by checking if it is an array, and (futurely) an associative array
457 */
458 HipDDFToken parseType(ref HipDDFVarInternal variable, HipDDFToken token, HipDDFTokenizer* tokenizer)
459 {
460     if(token.type != HipDDFTokenType.symbol)
461         return tokenizer.setError("Tried to parse a non type token: "~token.toString);
462     variable.type = token.str;
463     for(;;)
464     {
465         token = getToken(tokenizer);
466         switch(token.type)
467         {
468             case HipDDFTokenType.openSquareBrackets:
469                 token = getToken(tokenizer);
470                 if(token.type == HipDDFTokenType.closeSquareBrackets)
471                 {
472                     variable.type~= "[]";
473                     variable.isArray = true;
474                 }
475                 else if(token.type == HipDDFTokenType.numberLiteral)
476                 {
477                     variable.type~= "["~token.str;
478                     variable.length = to!uint(token.str);
479                     if(!requireToken(tokenizer, HipDDFTokenType.closeSquareBrackets, token))
480                         return tokenizer.setError("Expected ], received "~token.toString);
481                     variable.type~="]";
482                     variable.isArray = true;
483                 }
484                 else if(token.type == HipDDFTokenType.symbol)
485                 {
486                     variable.type~= "["~token.str;
487                     if(!requireToken(tokenizer, HipDDFTokenType.closeSquareBrackets, token))
488                         return tokenizer.setError("Expected ], received "~token.toString);
489                     variable.type~="]";
490                     variable.isAssociativeArray = true;
491                 }
492                 if(token.type != HipDDFTokenType.closeSquareBrackets)
493                     return tokenizer.setError("Expected ], received "~token.toString);
494                 if(!requireToken(tokenizer, HipDDFTokenType.symbol, token))
495                     return tokenizer.setError("Expected a variable name, received "~token.toString);
496                 return token;
497             case HipDDFTokenType.symbol:
498                 return token;
499             default: 
500                 return tokenizer.setError("Error occurred with token " ~ token.toString);
501         }
502     }
503 }
504 
505 
506 private HipDDFToken findToken(HipDDFTokenizer* tokenizer, HipDDFTokenType type)
507 {
508     HipDDFToken tk;
509     while(tokenizer.restLength > 0)
510     {
511         tk = getToken(tokenizer);
512         if(tk.type == type || tk.type == HipDDFTokenType.endOfStream)
513             return tk;
514     }
515     return HipDDFToken("", HipDDFTokenType.endOfStream);
516 }
517 
518 
519 /**
520 *   Mainly a syntax creator
521 */
522 private pragma(inline) bool requireToken(HipDDFTokenizer* tokenizer, HipDDFTokenType type, out HipDDFToken token)
523 {
524     token = getToken(tokenizer);
525     if(token.type != type)
526         return false;
527     return true;
528 }
529 
530 struct HipDDFVarInternal
531 {
532     string type;
533     string value;
534     string symbol;
535     bool isArray;
536     bool isAssociativeArray;
537     uint length;
538     pure string toString() const {return type~" "~symbol~" = "~value;}
539     pure string getKeyType() const
540     {
541         if((isArray || isAssociativeArray) && type != "")
542         {
543             int i = cast(int)type.length - 1;
544             while(type[i] != '['){i--;}
545             return type[i+1..$-1];
546         }
547         return "";
548     }
549     pure string getValueType() const
550     {
551         if((isArray || isAssociativeArray) && type != "")
552         {
553             int i = cast(int)type.length - 1;
554             while(type[i] != '['){i--;}
555             return type[0..i];
556         } return "";
557     }
558 }
559 
560 struct HipDDFObjectInternal
561 {
562     string symbol;
563     string filename;
564     HipDDFVarInternal[string] variables;
565     HipDDFStruct[string] structs;
566     bool hasVar(string str){return (str in variables) !is null;}
567 }
568 
569 shared static this()
570 {
571     keywords = 
572     [
573         "__LINE__" : function HipDDFToken (HipDDFTokenizer* tokenizer)
574         {
575             return HipDDFToken(to!string(tokenizer.line), HipDDFTokenType.numberLiteral);
576         },
577         "__FILE__" : function HipDDFToken (HipDDFTokenizer* tokenizer)
578         {
579             return HipDDFToken(tokenizer.filename, HipDDFTokenType.stringLiteral);
580         },
581         "struct" : function HipDDFToken (HipDDFTokenizer* tokenizer)
582         {
583             HipDDFToken tk;
584             if(!requireToken(tokenizer, HipDDFTokenType.symbol, tk))
585                 return tokenizer.setError("Expected symbol after struct keyword, received "~tk.toString);
586             string structName = tk.str;
587             if(!requireToken(tokenizer, HipDDFTokenType.openCurlyBrackets, tk))
588                 return tokenizer.setError("Expected '{', received "~tk.toString);
589             HipDDFStruct structure;
590             structure.name = structName;
591             while(tk.type != HipDDFTokenType.closeCurlyBrackets)
592             {
593                 string type;
594                 string sym;
595                 if(!requireToken(tokenizer, HipDDFTokenType.symbol, tk))
596                 {
597                     if(!tk.type == HipDDFTokenType.closeCurlyBrackets)
598                         return tokenizer.setError("Expected type name, received "~tk.toString);
599                     break;
600                 }
601                 type = tk.str;
602                 if(!requireToken(tokenizer, HipDDFTokenType.symbol, tk))
603                     return tokenizer.setError("Expected symbol declaration after type, received "~tk.toString);
604                 sym = tk.str;
605                 if(!requireToken(tokenizer, HipDDFTokenType.semicolon, tk))
606                     return tokenizer.setError("Expected ';', received " ~tk.toString);
607                 structure.types~= type;
608                 structure.symbols~= sym;
609             }
610             tokenizer.obj.structs[structure.name] = structure;
611             return getToken(tokenizer);
612         }
613     ];
614 
615     builtinTypes = [
616         "int" : function bool(string type, HipDDFToken tk)
617         {
618             return tk.type == HipDDFTokenType.numberLiteral;
619         },
620         "float" : function bool(string type, HipDDFToken tk)
621         {
622             return tk.type == HipDDFTokenType.numberLiteral;
623         },
624         "string" : function bool(string type, HipDDFToken tk)
625         {
626             return tk.type == HipDDFTokenType.stringLiteral;
627         }
628     ];
629 }
630 
631 
632 ///In this step, the token is already checked if it was a symbol
633 pragma(inline) bool isStructLiteral(HipDDFToken tk, HipDDFTokenizer* tokenizer)
634 {
635     return (tk.str in tokenizer.obj.structs) !is null;
636 }
637 pragma(inline) bool isAlpha(char c) pure nothrow @safe @nogc{return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');}
638 pragma(inline) bool isEndOfLine(char c) pure nothrow @safe @nogc{return c == '\n' || c == '\r';}
639 pragma(inline) bool isNumeric(char c) pure nothrow @safe @nogc{return (c >= '0' && c <= '9') || (c == '-');}
640 pragma(inline) bool isWhitespace(char c) pure nothrow @safe @nogc{return (c == ' ' || c == '\t' || c.isEndOfLine);}
641 pragma(inline) bool isLiteral(HipDDFTokenType type) pure nothrow @safe @nogc
642 {
643     return type == HipDDFTokenType.numberLiteral || type == HipDDFTokenType.stringLiteral;
644 }
645 
646 pragma(inline) bool isValueSyntax(HipDDFTokenType type) pure nothrow @safe @nogc
647 {
648     return type.isLiteral ||  //"string" 4938
649            type == HipDDFTokenType.symbol || //Struct/Any
650            type == HipDDFTokenType.openCurlyBrackets || //Struct
651            type == HipDDFTokenType.openSquareBrackets; //Array
652 }
653 pragma(inline) bool isAssociativeArraySyntax(HipDDFTokenType type) pure nothrow @safe @nogc
654 {
655     return type.isValueSyntax || type == HipDDFTokenType.colon || type == HipDDFTokenType.comma;
656 }
657 
658 pragma(inline) bool isArraySyntax(HipDDFTokenType type) pure nothrow @safe @nogc
659 {
660     return type.isValueSyntax ||
661     type == HipDDFTokenType.comma ||
662     // type == HipDDFTokenType.openParenthesis || //
663     type == HipDDFTokenType.symbol;
664 }
665 
666 private T stringToStruct(T)(HipDDFStruct structure, string str) pure
667 {
668     int typeIndex = 0;
669     static foreach(m; __traits(allMembers,  T))
670     {
671         if(typeof(__traits(getMember, T, m)).stringof != structure.types[typeIndex++])
672             return T.init;
673     }
674 
675     T ret;
676     string tempValue = "";
677     typeIndex = 0;
678 
679     for(int i = 1; i < cast(int)str.length; i++)
680     {
681         if(str[i] == ',' || str[i] == ')')
682         {
683             swt: switch(typeIndex)
684             {
685                 static foreach(mIndex, m; __traits(allMembers, T))
686                 {
687                     case mIndex:
688                         __traits(getMember, ret, m) = to!(typeof(__traits(getMember, ret, m)))(tempValue);
689                         break swt;
690                 }
691                 default:
692                     return ret;
693             }
694             tempValue = "";
695             typeIndex++;
696         }
697         else
698             tempValue~= str[i];
699     }
700     return ret;
701 }
702 
703 
704 /**
705 *   Given an array like [[5, 2, 3], [1, 2, 3]]
706 *   It will find the matching close character
707 */
708 pure nothrow @nogc @safe
709 int findMatchingCharacter(string str, char open, char close, int start)
710 {
711     int count = 0;
712     for(int i = start; i < str.length; i++)
713     {
714         if(str[i] == open)
715             count++;
716         else if(str[i] == close)
717         {
718             count--;
719             if(count == 0)
720                 return i;
721             else if(count < 0)
722                 return -1;
723         }
724     }
725     return -1;
726 }
727 
728 
729 alias ForeachValueExec = pure void delegate(string);
730 /**
731 *   Returns if the foreach executed successful
732 */
733 pure bool foreachValueOnArrayStringified(string arrayString, char open, char close, ForeachValueExec execute)
734 {
735     for(int i = 1; i < cast(int)arrayString.length - 1; i++)
736     {
737         if(arrayString[i] == open)
738         {
739             int newI = findMatchingCharacter(arrayString, open, close, i);
740             if(newI == -1)
741                 return false;
742             //Use that for using slices to be memory efficient
743             execute(arrayString[i..newI+1]);
744             i = newI;
745         }
746     }
747     return true;
748 }
749 
750 /**
751 *   This function may return a struct in the format Vector2(0,0)
752 *   A string literal "hello world"
753 *   Or a number 123456
754 */
755 pure string getValueFromString(string aa, int start, out int next)
756 {
757     if(start >= aa.length)
758     {
759         next = -1;
760         return "";
761     }
762     if(aa[start] == '(') //Symbol
763     {
764         int newI = findMatchingCharacter(aa, '(', ')', start);
765         string ret = aa[start..++newI];
766         next = newI;
767         return ret;
768     }
769     else if(aa[start].isNumeric) //Number
770     {
771         int newI = start;
772         while(aa[newI].isNumeric)
773             newI++;
774         string ret = aa[start..newI];
775         next = newI;
776         return ret;
777     }
778     else if(aa[start] == '"') //String
779     {
780         int newI = start+1;
781         while(aa[newI] != '"')
782         {
783             if(aa[newI] == '\\')
784                 newI++;
785             newI++;
786         }
787         newI++;//Include the '"'
788 
789         string ret = aa[start+1..newI-1];
790         next = newI;
791         return ret;
792     }
793     else if(aa[start] == '[') //Array of anything
794     {
795         //It means we need to search firstly for the (
796         int newI = findMatchingCharacter(aa, '[', ']', start);
797         string ret = aa[start+1..newI];
798         next = newI;
799         return ret;
800     }
801     next = -1;
802     return "";
803 }
804 
805 pure
806 {
807     //Var value
808     string parserVarType(const(void*) hddfvar){return (cast(HipDDFVarInternal*)hddfvar).type;}
809     string parserVarValue(const(void*) hddfvar){return (cast(HipDDFVarInternal*)hddfvar).value;}
810     string parserVarSymbol(const(void*) hddfvar){return (cast(HipDDFVarInternal*)hddfvar).symbol;}
811     bool parserIsVarArray(const(void*) hddfvar){return (cast(HipDDFVarInternal*)hddfvar).isArray;}
812     uint parserVarLength(const(void*) hddfvar){return (cast(HipDDFVarInternal*)hddfvar).length;}
813 
814     string parserObjSymbol(const(void*) hddfobj){return (cast(HipDDFObjectInternal*)hddfobj).symbol;}
815 
816     //Object
817     bool parserObjHasVar(const(void*) hddfobj, string name)
818     {
819         auto obj = cast(HipDDFObjectInternal*)hddfobj;
820         return (name in obj.variables) is null;
821     }
822     T parserObjGet(T)(const(void*)hddfobj, string name)
823     {
824         HipDDFObjectInternal* obj = cast(HipDDFObjectInternal*)hddfobj;
825 
826         HipDDFVarInternal* v = name in obj.variables;
827         if(v !is null)
828         {
829             import std.traits:isArray, isStaticArray, isAssociativeArray, KeyType, ValueType;
830             assert(v.type == T.stringof, "Data expected '"~T.stringof~"' differs from the HipDDF : '"~v.toString~"'");
831 
832             static if(!is(T == string) && isArray!T)
833             {
834                 assert(v.isArray,  "Tried to get an array of type "~T.stringof~" from HipDDF which is not an array: '"~v.toString~"'");
835                 T ret;
836                 //Means that the array has same value on every index
837 
838                 if(v.value[$-1] != ']')
839                 {
840                     static if(isStaticArray!T)
841                         ret = to!(typeof(T.init[0]))(v.value);
842                     else
843                         assert(0, "Tried to assign a single value to a dynamic array");
844                 }
845                 //Parse the values  
846                 else
847                 {
848                     //Parse struct arrays
849                     static if(is(T == U[], U) && is(U == struct))
850                     {
851                         bool success = foreachValueOnArrayStringified(v.value, '(', ')', (string strucStr)
852                         {
853                             ret~= stringToStruct!U(obj.structs[U.stringof], strucStr);
854                         });
855                         assert(success, "Wrong struct formatting?"~v.value);
856                     }
857                     else //Parse simple values on static and dynamic arrays
858                     {
859                         int i = 1;
860                         int index = 0;
861                         string stringVal = "";
862 
863                         while(i < cast(int)v.value.length - 1)
864                         {
865                             if(v.value[i] == ',')
866                             {
867                                 if(stringVal)
868                                 {
869                                     static if(!isStaticArray!T)
870                                         ret.length++;
871                                     else
872                                         ret[index++] = to!(typeof(T.init[0]))(stringVal);
873                                 }
874                                 stringVal = "";
875                             }
876                             else
877                                 stringVal~= v.value[i];
878                             i++;
879                         }
880                     }
881                 }
882                 return ret;
883             }
884             else static if(isAssociativeArray!T)
885             {
886                 assert(v.isAssociativeArray, "Tried to get associative array from variable "~v.toString);
887                 int i = 1;
888                 string keyString = "";
889                 string valueString = "";
890                 bool isCheckingForKey = true;
891                 T ret;
892                 scope void insertAA()
893                 {
894                     static if(is(T == V[K], K, V))
895                     {
896                         ret[to!(K)(keyString)] = stringToStruct!V(obj.structs[V.stringof], valueString);
897                     }
898                     else
899                         ret[to!(KeyType!T)(keyString)] = to!(ValueType!T)(valueString);
900                     keyString = "";
901                     valueString = "";
902                 }
903 
904                 int next;
905                 int start = 1;
906                 while(next != -1)
907                 {
908                     keyString = getValueFromString(v.value, start, next);
909                     if(next == -1)
910                         break;
911                     start = ++next;//Remove the ':'
912                     valueString = getValueFromString(v.value, start, next);
913                     if(next == -1)
914                         break;
915                     start = ++next;//Remove the ','
916                     insertAA();
917                 }
918                 return ret;
919             }
920             else static if(is(T == struct))
921                 return stringToStruct!(T)(obj.structs[T.stringof], v.value);
922             else
923                 return to!T(v.value);
924         }
925         assert(0, "Could not find variable named '"~name~"'");
926     }
927 }