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 }