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 }