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 }