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.parser;
8 import wsf.ast.tag;
9 import wsf.streams.stream;
10 import wsf.ast.common;
11 
12 /**
13     WSF parser
14 */
15 struct Parser {
16 private:
17     Stream stream;
18 
19     WSFTag readTag() {
20         ubyte[] dat = new ubyte[1];
21         stream.read(dat);
22         return cast(WSFTag)dat[0];
23     }
24 
25     WSFTag peekTag() {
26         ubyte[] dat = new ubyte[1];
27         stream.peek(dat);
28         return cast(WSFTag)dat[0];
29     }
30 
31     bool verifyMagicBytes() {
32         ubyte[] strBuf = new ubyte[WSF_MAGIC_BYTES.length];
33         stream.read(strBuf);
34         return strBuf == WSF_MAGIC_BYTES;
35     }
36 
37     uint parseLength() {
38         ubyte[] len = new ubyte[4];
39         stream.read(len);
40         return decode!uint(len);
41     }
42 
43     string parseEntryName() {
44         uint len = parseLength();
45 
46         // Sequence entry value
47         if (len == 0) return null;
48 
49         // Normal entry values
50         ubyte[] strBuf = new ubyte[len];
51         stream.read(strBuf);
52         return cast(string)strBuf;
53     }
54 
55     Tag parseValue() {
56         switch(peekTag()) {
57             case WSFTag.CompoundStart:
58                 Tag compound = Tag.emptyCompound();
59                 parseCompound(compound);
60                 return compound;
61 
62             case WSFTag.Array:
63                 Tag array = Tag.emptyArray();
64                 parseArray(array);
65                 return array;
66 
67             case WSFTag.String:
68                 readTag();
69                 uint len = parseLength();
70                 
71                 // For empty strings
72                 if (len == 0) return new Tag("");
73 
74                 // String is not empty
75                 ubyte[] val = new ubyte[len];
76                 stream.read(val);
77                 return new Tag(cast(string)val);
78 
79             case WSFTag.Bool:
80                 readTag();
81                 ubyte[] val = new ubyte[1];
82                 stream.read(val);
83                 return new Tag(cast(bool)val[0]);
84 
85             case WSFTag.Int8:
86                 readTag();
87                 ubyte[] val = new ubyte[1];
88                 stream.read(val);
89                 return new Tag(val[0]);
90 
91             case WSFTag.Int16:
92                 readTag();
93                 ubyte[] val = new ubyte[2];
94                 stream.read(val);
95                 return new Tag(decode!ubyte(val));
96 
97             case WSFTag.Int32:
98                 readTag();
99                 ubyte[] val = new ubyte[4];
100                 stream.read(val);
101                 return new Tag(decode!uint(val));
102 
103             case WSFTag.Int64:
104                 readTag();
105                 ubyte[] val = new ubyte[8];
106                 stream.read(val);
107                 return new Tag(decode!ulong(val));
108 
109             case WSFTag.Floating:
110                 readTag();
111                 ubyte[] val = new ubyte[8];
112                 stream.read(val);
113                 return new Tag(decode!double(val));
114 
115             default: 
116                 throw new Exception("Unexpected tag");
117         }
118     }
119 
120     void parseEntry(ref Tag tag) {
121         if (readTag() != WSFTag.Entry) throw new Exception("Expected entry!");
122 
123         string name = parseEntryName();
124         if (name is null) {
125             tag.seq() ~= parseValue();
126         } else {
127             tag[name] = parseValue();
128         }
129     }
130 
131     void parseArray(ref Tag tag) {
132         if (readTag() != WSFTag.Array) throw new Exception("Expected entry!");
133         uint len = parseLength();
134         foreach(i; 0..len) {
135             tag ~= parseValue();
136         }
137     }
138 
139     void parseCompound(ref Tag tag) {
140         if (readTag() != WSFTag.CompoundStart) throw new Exception("Expected compound start!");
141         while(peekTag() != WSFTag.CompoundEnd) {
142             parseEntry(tag);
143         }
144 
145         // Read end tag for compound
146         readTag();
147     }
148 
149 public:
150     this(Stream stream) {
151         this.stream = stream;
152         verifyMagicBytes();
153     }
154 
155     /**
156         Parse the WSF file from the stream
157     */
158     Tag parse() {
159         Tag root = Tag.emptyCompound();
160         parseCompound(root);
161         return root;
162     }
163 }
164 
165 
166 /**
167     Parse the WSF file from the stream
168 */
169 Tag parse(Stream stream) {
170     return Parser(stream).parse();
171 }