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 }