1 module bamboo.astgen; 2 import std.conv; 3 import pegged.peg; 4 import bamboo.parser; 5 import bamboo.types; 6 import bamboo.util; 7 8 Module parseString(string source) 9 { 10 auto node = DClass(source); 11 auto completed = transform(node); 12 return completed; 13 } 14 15 package Module transformParseTree(ParseTree tree) 16 { 17 return transform(tree); 18 } 19 20 /// Given a path, parses a DC file. 21 /// Returns: a `Module`, representing the parsed file. 22 Module parseModule(string file) 23 { 24 import std.file : readText; 25 26 return parseString(readText(file)); 27 } 28 29 /// Destructively mutates `file`, appending members from `other`. 30 void combineWith(Module file, Module other) 31 { 32 ushort typeId = file.lastTypeId; 33 ushort fieldId = file.lastFieldId; 34 35 foreach (i, type; other.typesById) 36 { 37 type.id = ++typeId; 38 if (auto cls = cast(ClassDeclaration) type) 39 { 40 foreach (field; cls.fields) 41 { 42 field.id = ++fieldId; 43 } 44 file.classes ~= cls; 45 } 46 else if (auto strct = cast(StructDeclaration) type) 47 { 48 foreach (field; strct.parameters) 49 { 50 field.id = ++fieldId; 51 } 52 file.structs ~= strct; 53 } 54 else if (auto def = cast(AliasDeclaration) type) 55 { 56 file.aliases ~= def; 57 } 58 else if (auto keyword = cast(KeywordDeclaration) type) 59 { 60 file.keywords ~= keyword; 61 } 62 } 63 foreach (imprt; other.importDeclarations) 64 { 65 file.importDeclarations ~= imprt; 66 } 67 68 foreach (keyword; other.keywords) 69 { 70 file.keywords ~= keyword; 71 } 72 73 file.lastTypeId = typeId; 74 file.lastFieldId = fieldId; 75 } 76 77 private: 78 79 class SyntaxResolver : Visitor 80 { 81 enum Type[string] builtins = genbuiltins; 82 TypeDeclaration[string] aliases; 83 84 Module _module; 85 86 override void visit(Module file) 87 { 88 _module = file; 89 foreach (decl; file.aliases) 90 { 91 decl.visit(this); 92 } 93 foreach (type; file.typesById) 94 { 95 type.visit(this); 96 } 97 } 98 99 TypeDeclaration resolve(string type) 100 { 101 if (auto ret = type in builtins) 102 { 103 return new BuiltinType(-1, type, *ret); 104 } 105 if (auto ret = type in aliases) 106 { 107 return *ret; 108 } 109 return _module.findType(type); 110 } 111 112 Parameter createParameter(TypeDeclaration type, string name) 113 { 114 import std.conv : to; 115 116 switch (type.type) with (Type) 117 { 118 case int8: 119 case int16: 120 case int32: 121 case int64: 122 case uint8: 123 case uint16: 124 case uint32: 125 case uint64: 126 case char_: 127 case float32: 128 case float64: 129 return new NumericParameter(name, type.type.to!string, 1, double.nan, null, null); 130 case string_: 131 case blob: 132 return new SizedParameter(name, type.type.to!string, new SizeConstraint(0, 0), ""); 133 case struct_: 134 return new StructParameter(name, type.symbol); 135 case array: 136 return new ArrayParameter(name, type.type.to!string, new ArrayRange(0, 0)); 137 default: 138 break; 139 } 140 return null; 141 } 142 143 override void visit(ImportDeclaration node) 144 { 145 } 146 147 override void visit(KeywordList node) 148 { 149 } 150 151 override void visit(KeywordDeclaration node) 152 { 153 } 154 155 override void visit(BuiltinType node) 156 { 157 } 158 159 override void visit(StructDeclaration node) 160 { 161 foreach (field; node.parameters) 162 { 163 field.visit(this); 164 } 165 } 166 167 override void visit(ClassDeclaration node) 168 { 169 node.resolve(_module); 170 foreach (field; node.fields) 171 { 172 if (auto molecular = cast(MolecularField) field) 173 { 174 FieldDeclaration[] refs; 175 foreach (reference; molecular.referenceNames) 176 { 177 refs ~= node.getField(reference); 178 } 179 molecular.references = refs; 180 } 181 field.visit(this); 182 } 183 if (node.hasConstructor) 184 { 185 node.constructor.visit(this); 186 } 187 } 188 189 override void visit(AliasDeclaration node) 190 { 191 node.aliasedType = resolve(node.aliasedTypeName); 192 aliases[node.symbol] = node.aliasedType; 193 } 194 195 override void visit(MolecularField node) 196 { 197 198 } 199 200 override void visit(AtomicField node) 201 { 202 Parameter[] parameters; 203 204 foreach (parameter; node.parameters) 205 { 206 parameter.visit(this); 207 if (auto structPara = cast(StructParameter) parameter) 208 { 209 if (auto type = cast(BuiltinType) structPara.type) 210 { 211 auto newPara = createParameter(structPara.type, structPara.symbol); 212 if (newPara !is null) 213 { 214 parameters ~= newPara; 215 continue; 216 } 217 } 218 } 219 parameters ~= parameter; 220 } 221 222 node.parameters = parameters; 223 } 224 225 override void visit(ParameterField node) 226 { 227 node.parameter.visit(this); 228 if (StructParameter strct = cast(StructParameter) node.parameter) 229 { 230 import std.algorithm : canFind; 231 232 auto numerics = [ 233 Type.int8, Type.int16, Type.int32, Type.int64, Type.uint8, 234 Type.uint16, Type.uint32, Type.uint64, Type.char_, Type.float32, Type.float64 235 ]; 236 237 if (numerics.canFind(strct.parameterType)) 238 { 239 node.parameter = createParameter(strct.type, strct.symbol); 240 node.visit(this); 241 } 242 } 243 } 244 245 override void visit(NumericParameter node) 246 { 247 } 248 249 override void visit(NumericRange node) 250 { 251 } 252 253 override void visit(NumericTransform node) 254 { 255 } 256 257 override void visit(NumericConstant node) 258 { 259 } 260 261 override void visit(SizedParameter node) 262 { 263 } 264 265 override void visit(SizeConstraint node) 266 { 267 } 268 269 override void visit(StructParameter node) 270 { 271 node.type = resolve(node.typeName); 272 } 273 274 override void visit(ArrayParameter node) 275 { 276 node.elementType = resolve(node.type); 277 node.element = createParameter(node.elementType, ""); 278 node.element.visit(this); 279 } 280 281 override void visit(ArrayRange node) 282 { 283 } 284 285 } 286 287 Module transform(ParseTree node) 288 { 289 assert(node.name == "DClass"); 290 291 return transformModule(node.children[0]); 292 } 293 294 //dfmt off 295 enum SyntaxType 296 { 297 Module = "DClass.Module", 298 DCFile = "DClass.DCFile", 299 ParseDirective = "DClass.ParseDirective", 300 ImportDecl = "DClass.ImportDecl", 301 ImportList = "DClass.ImportList", 302 TypeDecl = "DClass.TypeDecl", 303 AliasType = "DClass.AliasType", 304 KeywordType = "DClass.KeywordType", 305 KeywordList = "DClass.KeywordList", 306 StructType = "DClass.StructType", 307 ClassType = "DClass.ClassType", 308 FieldDecl = "DClass.FieldDecl", 309 MolecularField = "DClass.MolecularField", 310 AtomicField = "DClass.AtomicField", 311 ParameterField = "DClass.ParameterField", 312 Parameter = "DClass.Parameter", 313 ParameterList = "DClass.ParameterList", 314 CharParameter = "DClass.CharParameter", 315 IntParameter = "DClass.IntParameter", 316 IntConstant = "DClass.IntConstant", 317 IntTransform = "DClass.IntTransform", 318 IntRange = "DClass.IntRange", 319 FloatParameter = "DClass.FloatParameter", 320 FloatConstant = "DClass.FloatConstant", 321 FloatTransform = "DClass.FloatTransform", 322 FloatRange = "DClass.FloatRange", 323 SizedParameter = "DClass.SizedParameter", 324 SizeConstraint = "DClass.SizeConstraint", 325 StructParameter = "DClass.StructParameter", 326 ArrayParameter = "DClass.ArrayParameter", 327 ArrayRange = "DClass.ArrayRange", 328 InterfaceMarker = "DClass.InterfaceMarker", 329 Identifier = "DClass.Identifier", 330 QualifiedIdentifier = "DClass.QualifiedIdentifier", 331 IdentifierList = "DClass.IdentifierList", 332 dataType = "DClass.dataType", 333 } 334 335 enum LiteralType 336 { 337 charLiteral = "DClass.charLiteral", 338 intLiteral = "DClass.intLiteral", 339 floatLiteral = "DClass.floatLiteral", 340 stringLiteral = "DClass.stringLiteral", 341 } 342 //dfmt on 343 344 ParseTree child(ParseTree node, int index) 345 { 346 if (node.children.length <= index) 347 { 348 return ParseTree("", false, [], "", 0, 0, []); 349 } 350 return node.children[index]; 351 } 352 353 Module transformModule(ParseTree node) 354 { 355 assert(node.name == SyntaxType.Module); 356 357 auto file = transformFile(node.children[0]); 358 359 SyntaxResolver resolver = new SyntaxResolver(); 360 resolver.visit(file); 361 362 return file; 363 } 364 365 Module transformFile(ParseTree node) 366 { 367 assert(node.name == SyntaxType.DCFile); 368 369 ushort id; 370 ushort fieldId; 371 int skip; 372 string name; 373 374 ImportDeclaration[] imports; 375 376 ClassDeclaration[] classes; 377 StructDeclaration[] structs; 378 KeywordDeclaration[] keywords; 379 AliasDeclaration[] aliases; 380 381 // Check if this module has a name. 382 if (node.children[0].name == SyntaxType.ParseDirective) 383 { 384 ParseTree child = node.children[0]; 385 string directive = transformIdentifier(child.children[0]); 386 string value = child.children[1].matches[0]; 387 388 if (directive == "module") 389 { 390 skip = 1; 391 name = value; 392 } 393 } 394 395 foreach (child; node.children[skip .. $]) 396 { 397 switch (cast(SyntaxType) child.name) with (SyntaxType) 398 { 399 case ParseDirective: 400 { 401 string directive = transformIdentifier(child.children[0]); 402 string value = child.children[1].matches[0]; 403 404 if (directive == "typeid") 405 { 406 id = value.to!ushort; 407 } 408 } 409 break; 410 case ImportDecl: 411 imports ~= transformImport(child); 412 break; 413 case TypeDecl: 414 child = child.children[0]; 415 switch (cast(SyntaxType) child.name) with (SyntaxType) 416 { 417 case KeywordType: 418 keywords ~= transformKeywordType(child, 0); 419 break; 420 case StructType: 421 structs ~= transformStructType(child, id++, fieldId); 422 break; 423 case ClassType: 424 classes ~= transformClassType(child, id++, fieldId); 425 break; 426 case AliasType: 427 aliases ~= transformAliasType(child, 0); 428 break; 429 default: 430 assert(0); 431 } 432 break; 433 default: 434 assert(0, "Expected ImportDecl or TypeDecl. Got " ~ child.name); 435 } 436 } 437 438 return new Module(name, imports, aliases, classes, structs, keywords, id, fieldId); 439 } 440 441 string transformIdentifier(SyntaxType type = SyntaxType.Identifier)(ParseTree node) 442 { 443 assert(node.name == type, node.name ~ " != " ~ type); 444 return node.matches[0]; 445 } 446 447 ImportDeclaration transformImport(ParseTree node) 448 { 449 assert(node.name == SyntaxType.ImportDecl); 450 string module_ = transformIdentifier!(SyntaxType.QualifiedIdentifier)(node.children[0]); 451 452 string[] symbols = transformImportList(node.children[1]); 453 454 return new ImportDeclaration(module_, symbols); 455 } 456 457 string[] transformImportList(ParseTree node) 458 { 459 assert(node.name == SyntaxType.ImportList); 460 // Expand symbol list from, e.g., LoginManager/AI/UD 461 // to LoginManager, LoginManagerAI, LoginManagerUD 462 string firstSymbol = transformIdentifier(node.children[0]); 463 string[] symbols; 464 465 symbols ~= firstSymbol; 466 foreach (child; node.children[1 .. $]) 467 { 468 symbols ~= firstSymbol ~ transformIdentifier(child); 469 } 470 471 return symbols; 472 } 473 474 AliasDeclaration transformAliasType(ParseTree node, ushort id) 475 { 476 assert(node.name == SyntaxType.AliasType); 477 string symbol = transformIdentifier(node.children[1]); 478 string type = node.children[0].matches[0]; 479 480 return new AliasDeclaration(id, symbol, type); 481 } 482 483 KeywordDeclaration transformKeywordType(ParseTree node, ushort id) 484 { 485 assert(node.name == SyntaxType.KeywordType); 486 string symbol = transformIdentifier(node.children[0]); 487 return new KeywordDeclaration(id, symbol); 488 } 489 490 KeywordList transformKeywordList(ParseTree node) 491 { 492 assert(node.name == SyntaxType.KeywordList); 493 494 string[] symbols; 495 496 foreach (child; node.children) 497 { 498 symbols ~= transformIdentifier(child); 499 } 500 501 return new KeywordList(symbols); 502 } 503 504 StructDeclaration transformStructType(ParseTree node, ushort id, ref ushort fieldId) 505 { 506 assert(node.name == SyntaxType.StructType); 507 508 string symbol = transformIdentifier(node.children[0]); 509 510 ParameterField[] members; 511 512 foreach (child; node.children[1 .. $]) 513 { 514 members ~= new ParameterField(fieldId++, transformParameter(child), new KeywordList([])); 515 } 516 517 return new StructDeclaration(id, symbol, members); 518 } 519 520 ClassDeclaration transformClassType(ParseTree node, ushort id, ref ushort fieldId) 521 { 522 assert(node.name == SyntaxType.ClassType); 523 524 int cur = 0; 525 bool isInterface; 526 527 if (node.child(cur).name == SyntaxType.InterfaceMarker) 528 { 529 cur++; 530 isInterface = true; 531 } 532 533 string symbol = transformIdentifier(node.children[cur++]); 534 string[] superclasses; 535 536 if (node.child(cur).name == SyntaxType.IdentifierList) 537 { 538 foreach (child; node.children[cur].children) 539 { 540 superclasses ~= transformIdentifier(child); 541 } 542 cur++; 543 } 544 545 FieldDeclaration[] members = []; 546 FieldDeclaration cotr; 547 548 if (cur < node.children.length - 1) 549 { 550 foreach (child; node.children[cur .. $]) 551 { 552 auto field = transformFieldDecl(child, fieldId++); 553 if (is(field == AtomicField)) 554 { 555 cotr = field; 556 } 557 else 558 { 559 members ~= field; 560 } 561 } 562 } 563 564 return new ClassDeclaration(id, symbol, superclasses, isInterface, cotr, members); 565 } 566 567 FieldDeclaration transformFieldDecl(ParseTree node, ushort id) 568 { 569 assert(node.name == SyntaxType.FieldDecl); 570 571 ParseTree child = node.children[0]; 572 573 switch (cast(SyntaxType) child.name) with (SyntaxType) 574 { 575 case AtomicField: 576 return transformAtomicField(child, id); 577 case MolecularField: 578 return transformMolecularField(child, id); 579 case ParameterField: 580 return transformParameterField(child, id); 581 default: 582 assert(0); 583 } 584 } 585 586 MolecularField transformMolecularField(ParseTree node, ref ushort id) 587 { 588 assert(node.name == SyntaxType.MolecularField); 589 590 string symbol = transformIdentifier(node.children[0]); 591 string[] references; 592 593 foreach (child; node.children[1].children) 594 { 595 references ~= transformIdentifier(child); 596 } 597 598 return new MolecularField(id, symbol, references); 599 } 600 601 AtomicField transformAtomicField(ParseTree node, ref ushort id) 602 { 603 assert(node.name == SyntaxType.AtomicField); 604 605 string symbol = transformIdentifier(node.children[0]); 606 607 Parameter[] parameters; 608 KeywordList keywords = new KeywordList([]); 609 610 int cur = 1; 611 612 if (node.child(cur).name == SyntaxType.ParameterList) 613 { 614 parameters = transformParameterList(node.children[cur++]); 615 } 616 if (node.child(cur).name == SyntaxType.KeywordList) 617 { 618 keywords = transformKeywordList(node.child(cur)); 619 } 620 621 return new AtomicField(id, symbol, parameters, keywords); 622 } 623 624 Parameter[] transformParameterList(ParseTree node) 625 { 626 assert(node.name == SyntaxType.ParameterList); 627 628 Parameter[] parameters; 629 630 foreach (child; node.children) 631 { 632 parameters ~= transformParameter(child); 633 } 634 635 return parameters; 636 } 637 638 ParameterField transformParameterField(ParseTree node, ref ushort id) 639 { 640 assert(node.name == SyntaxType.ParameterField); 641 642 Parameter parameter = transformParameter(node.children[0]); 643 KeywordList keywords = new KeywordList([]); 644 645 if (node.children.length == 2) 646 { 647 keywords = transformKeywordList(node.children[1]); 648 } 649 650 return new ParameterField(id, parameter, keywords); 651 } 652 653 Parameter transformParameter(ParseTree node) 654 { 655 assert(node.name == SyntaxType.Parameter); 656 657 ParseTree child = node.children[0]; 658 659 switch (child.name) with (SyntaxType) 660 { 661 case CharParameter: 662 return transformCharParameter(child); 663 case IntParameter: 664 case FloatParameter: 665 return transformNumericParameter(child); 666 case SizedParameter: 667 return transformSizedParameter(child); 668 case ArrayParameter: 669 return transformArrayParameter(child); 670 case StructParameter: 671 return transformStructParameter(child); 672 default: 673 assert(0); 674 } 675 } 676 677 //dfmt off 678 T readLiteral(T)(ParseTree node) 679 if(is(T == char) 680 || is(T == string) 681 || is(T == int) 682 || is(T == double) 683 ) 684 { 685 import std.conv : to; 686 687 static if (is(T == char)) 688 { 689 assert(node.name == LiteralType.charLiteral); 690 return node.children[0].matches[0][0]; 691 } 692 else static if (is(T == string)) 693 { 694 assert(node.name == LiteralType.stringLiteral); 695 return node.children[0].matches[0]; 696 } 697 else static if (is(T == int)) 698 { 699 assert(node.name == LiteralType.intLiteral); 700 return node.matches[0].to!int(); 701 } 702 else static if (is(T == double)) 703 { 704 //assert(node.name == LiteralType.floatLiteral); 705 return node.matches[0].to!double(); 706 } 707 else 708 { 709 static assert(0, "?"); 710 } 711 } 712 //dfmt on 713 714 NumericParameter transformCharParameter(ParseTree node) 715 { 716 assert(node.name == SyntaxType.CharParameter); 717 718 string symbol; 719 char defaultVal; 720 int valIndex; 721 722 if (node.children.length == 0) 723 { 724 return new NumericParameter(symbol, "char", 1, double.nan, null, null); 725 } 726 727 if (node.child(valIndex).name == SyntaxType.Identifier) 728 { 729 symbol = transformIdentifier(node.children[valIndex++]); 730 } 731 732 if (node.child(valIndex).name == LiteralType.charLiteral) 733 { 734 defaultVal = readLiteral!char(node.children[valIndex]); 735 } 736 737 return new NumericParameter(symbol, "char", 1, double.nan, null, 738 new NumericConstant(cast(double) defaultVal, null)); 739 } 740 741 NumericParameter transformNumericParameter(ParseTree node) 742 { 743 //assert(node.name == SyntaxType.IntParameter); 744 745 immutable string type = node.children[0].matches[0]; 746 int cur = 1; 747 748 NumericRange range; 749 NumericTransform transform; 750 string symbol; 751 NumericConstant defaultValue; 752 753 // dfmt off 754 if (node.child(cur).name == SyntaxType.IntRange 755 || node.child(cur).name == SyntaxType.FloatRange) 756 { 757 range = transformNumericRange(node.children[cur++]); 758 } 759 if (node.child(cur).name == SyntaxType.IntTransform 760 || node.child(cur).name == SyntaxType.FloatTransform) 761 { 762 transform = transformNumericTransform(node.children[cur++]); 763 } 764 if (node.child(cur).name == SyntaxType.Identifier) 765 { 766 symbol = transformIdentifier(node.children[cur++]); 767 } 768 if (node.child(cur).name == SyntaxType.IntConstant 769 || node.child(cur).name == SyntaxType.FloatConstant) 770 { 771 defaultValue = transformNumericConstant(node.children[cur++]); 772 } 773 // dfmt on 774 775 auto parameter = new NumericParameter(symbol, type, 1, double.nan, range, defaultValue); 776 if (transform !is null) 777 { 778 if (transform.operator == Operator.modulo) 779 { 780 parameter.modulus = transform.value; 781 } 782 else if (transform.operator == Operator.divide) 783 { 784 parameter.divisor = cast(uint) transform.value; 785 } 786 else if (transform.operator == Operator.multiply) 787 { 788 //parameter.divisor = (1 / transform.value); 789 } 790 } 791 return parameter; 792 } 793 794 NumericConstant transformNumericConstant(ParseTree node) 795 { 796 //assert(node.name == SyntaxType.IntConstant); 797 798 immutable double value = readLiteral!double(node.children[0]); 799 NumericTransform transform; 800 801 if (node.children.length > 1) 802 { 803 transform = transformNumericTransform(node.children[1]); 804 } 805 806 return new NumericConstant(value, transform); 807 } 808 809 NumericTransform transformNumericTransform(ParseTree node) 810 { 811 //assert(node.name == SyntaxType.IntTransform); 812 813 Operator op = cast(Operator) node.children[0].matches[0]; 814 immutable double value = readLiteral!double(node.children[1]); 815 NumericTransform next; 816 817 if (node.children.length > 2) 818 { 819 next = transformNumericTransform(node.children[2]); 820 } 821 822 return new NumericTransform(op, value, next); 823 } 824 825 NumericRange transformNumericRange(ParseTree node) 826 { 827 // assert(node.name == SyntaxType.IntRange); 828 829 int lower; 830 int upper = readLiteral!int(node.children[0]); 831 832 if (node.children.length == 2) 833 { 834 lower = upper; 835 upper = readLiteral!int(node.children[1]); 836 } 837 838 return new NumericRange(lower, upper); 839 } 840 841 void swap(T)(T* left, T* right) 842 { 843 const auto tmp = left; 844 left = right; 845 right = tmp; 846 } 847 848 SizedParameter transformSizedParameter(ParseTree node) 849 { 850 assert(node.name == SyntaxType.SizedParameter); 851 852 string type = node.children[0].matches[0]; 853 SizeConstraint size; 854 string symbol; 855 string defaultVal; 856 857 int cur = 1; 858 859 if (node.child(cur).name == SyntaxType.SizeConstraint) 860 { 861 size = transformSizeConstraint(node.children[cur++]); 862 } 863 if (node.child(cur).name == SyntaxType.Identifier) 864 { 865 symbol = transformIdentifier(node.children[cur++]); 866 } 867 if (node.child(cur).name == LiteralType.stringLiteral) 868 { 869 defaultVal = readLiteral!string(node.child(cur)); 870 } 871 872 return new SizedParameter(symbol, type, size, defaultVal); 873 } 874 875 SizeConstraint transformSizeConstraint(ParseTree node) 876 { 877 assert(node.name == SyntaxType.SizeConstraint); 878 879 int upper = readLiteral!int(node.children[0]); 880 immutable int lower = upper; 881 882 if (node.children.length == 2) 883 { 884 upper = readLiteral!int(node.children[1]); 885 } 886 887 return new SizeConstraint(lower, upper); 888 } 889 890 StructParameter transformStructParameter(ParseTree node) 891 { 892 assert(node.name == SyntaxType.StructParameter); 893 894 string type = transformIdentifier(node.children[0]); 895 string symbol; 896 897 if (node.children.length > 1) 898 { 899 symbol = transformIdentifier(node.children[1]); 900 } 901 902 return new StructParameter(symbol, type); 903 } 904 905 ArrayParameter transformArrayParameter(ParseTree node) 906 { 907 assert(node.name == SyntaxType.ArrayParameter); 908 909 string type = node.children[0].matches[0]; 910 string symbol; 911 ArrayRange size; 912 913 int cur = 1; 914 if (node.child(cur).name == SyntaxType.Identifier) 915 { 916 symbol = transformIdentifier(node.children[cur++]); 917 } 918 919 size = transformArrayRange(node.child(cur)); 920 921 return new ArrayParameter(symbol, type, size); 922 } 923 924 ArrayRange transformArrayRange(ParseTree node) 925 { 926 assert(node.name == SyntaxType.ArrayRange); 927 928 if (node.children.length == 0) 929 { 930 return new ArrayRange(0, 0); 931 } 932 933 int minLength; 934 int maxLength; 935 936 if (node.children.length == 1) 937 { 938 maxLength = readLiteral!int(node.children[0]); 939 minLength = maxLength; 940 } 941 else if (node.children.length == 2) 942 { 943 minLength = readLiteral!int(node.children[0]); 944 maxLength = readLiteral!int(node.children[1]); 945 } 946 947 return new ArrayRange(minLength, maxLength); 948 }