1 2 // Copyright Luna & Cospec 2019. 3 // Distributed under the Boost Software License, Version 1.0. 4 // (See accompanying file LICENSE_1_0.txt or copy at 5 // https://www.boost.org/LICENSE_1_0.txt) 6 7 module wsf.ast.builder; 8 import wsf.ast.tag; 9 import wsf.streams.stream; 10 import wsf.ast.common; 11 12 /** 13 Struct capable of writing a WSF Tag sequence in to a Stream 14 */ 15 struct Builder { 16 private: 17 Stream stream; 18 Tag root; 19 20 void writeTagIdForKind(ref Tag tag) { 21 switch(tag.kind) { 22 case TagKind.byte_: writeTagId(WSFTag.Int8); return; 23 case TagKind.short_: writeTagId(WSFTag.Int16); return; 24 case TagKind.int_: writeTagId(WSFTag.Int32); return; 25 case TagKind.long_: writeTagId(WSFTag.Int64); return; 26 case TagKind.double_: writeTagId(WSFTag.Floating); return; 27 case TagKind.bool_: writeTagId(WSFTag.Bool); return; 28 case TagKind.string_: writeTagId(WSFTag.String); return; 29 default: throw new Exception("Unexpected tag type!"); 30 } 31 } 32 33 void writeTagId(WSFTag id) { 34 stream.write([cast(ubyte)id]); 35 } 36 37 void writeString(string text) { 38 ubyte[] textBuf = cast(ubyte[])text; 39 stream.write(encode!uint(cast(uint)textBuf.length)); 40 stream.write(textBuf); 41 } 42 43 void buildValue(ref Tag tag) { 44 switch(tag.kind()) { 45 case TagKind.compound_: 46 buildCompound(tag); 47 return; 48 49 case TagKind.array_: 50 buildArray(tag); 51 return; 52 53 case TagKind.string_: 54 writeTagIdForKind(tag); 55 writeString(tag.get!string); 56 return; 57 58 case TagKind.bool_: 59 writeTagIdForKind(tag); 60 stream.write([tag.get!bool]); 61 return; 62 63 case TagKind.byte_: 64 writeTagIdForKind(tag); 65 stream.write([tag.get!ubyte]); 66 return; 67 68 case TagKind.short_: 69 writeTagIdForKind(tag); 70 stream.write(encode!ushort(tag.get!ushort)); 71 return; 72 73 case TagKind.int_: 74 writeTagIdForKind(tag); 75 stream.write(encode!uint(tag.get!uint)); 76 return; 77 78 case TagKind.long_: 79 writeTagIdForKind(tag); 80 stream.write(encode!ulong(tag.get!ulong)); 81 return; 82 83 case TagKind.double_: 84 writeTagIdForKind(tag); 85 stream.write(encode!double(tag.get!double)); 86 return; 87 88 default: throw new Exception("Unexpected tag!"); 89 90 } 91 } 92 93 void buildArray(ref Tag tag) { 94 writeTagId(WSFTag.Array); 95 stream.write(encode!uint(cast(uint)tag.length)); 96 foreach(member; tag) { 97 buildValue(member); 98 } 99 } 100 101 void buildEntry(string key, ref Tag tag) { 102 writeTagId(WSFTag.Entry); 103 writeString(key); 104 buildValue(tag); 105 } 106 107 void buildSeq(ref Tag[] seq) { 108 foreach(child; seq) { 109 writeTagId(WSFTag.Entry); 110 stream.write(encode!uint(0)); 111 buildValue(child); 112 } 113 } 114 115 void buildCompound(ref Tag tag) { 116 if (!tag.isKindCompound) throw new Exception("Expected compound tag!"); 117 writeTagId(WSFTag.CompoundStart); 118 119 foreach(string name, child; tag) { 120 // Put sequential types AFTER the main types 121 if (name == "seq") continue; 122 123 buildEntry(name, child); 124 125 } 126 127 buildSeq(tag.seq); 128 129 writeTagId(WSFTag.CompoundEnd); 130 } 131 132 public: 133 this(Stream stream, Tag root) { 134 this.stream = stream; 135 this.root = root; 136 } 137 138 void build() { 139 stream.write(cast(ubyte[])WSF_MAGIC_BYTES); 140 buildCompound(root); 141 stream.flush(); 142 } 143 } 144 145 /** 146 Builds the WSF sequence and writes it to the stream 147 */ 148 void build(Tag root, Stream toStream) { 149 Builder(toStream, root).build(); 150 }