1 /* 2 Copyright (c) 2016-2020 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 */ 28 29 module dagon.core.props; 30 31 import std.stdio; 32 import std.ascii; 33 import std.conv; 34 import dlib.core.memory; 35 import dlib.core.ownership; 36 import dlib.container.array; 37 import dlib.container.dict; 38 import dlib.text.utils; 39 import dlib.math.vector; 40 import dlib.image.color; 41 import dlib.text.lexer; 42 43 enum DPropType 44 { 45 Undefined, 46 Number, 47 Vector, 48 String 49 } 50 51 struct DProperty 52 { 53 DPropType type; 54 string name; 55 string data; 56 57 string toString() 58 { 59 return data; 60 } 61 62 double toDouble() 63 { 64 return to!double(data); 65 } 66 67 float toFloat() 68 { 69 return to!float(data); 70 } 71 72 int toInt() 73 { 74 return to!int(data); 75 } 76 77 int toUInt() 78 { 79 return to!uint(data); 80 } 81 82 bool toBool() 83 { 84 return cast(bool)cast(int)(to!float(data)); 85 } 86 87 Vector3f toVector3f() 88 { 89 return Vector3f(data); 90 } 91 92 Vector4f toVector4f() 93 { 94 return Vector4f(data); 95 } 96 97 Color4f toColor4f() 98 { 99 return Color4f(Vector4f(data)); 100 } 101 } 102 103 class Properties: Owner 104 { 105 Dict!(DProperty, string) props; 106 107 this(Owner o) 108 { 109 super(o); 110 props = dict!(DProperty, string); 111 } 112 113 bool parse(string input) 114 { 115 return parseProperties(input, this); 116 } 117 118 DProperty opIndex(string name) 119 { 120 if (name in props) 121 return props[name]; 122 else 123 return DProperty(DPropType.Undefined, ""); 124 } 125 126 void set(DPropType type, string name, string value) 127 { 128 auto p = name in props; 129 if (p) 130 { 131 Delete(p.name); 132 Delete(p.data); 133 auto nameCopy = copyStr(name); 134 auto valueCopy = copyStr(value); 135 props[nameCopy] = DProperty(type, nameCopy, valueCopy); 136 } 137 else 138 { 139 auto nameCopy = copyStr(name); 140 auto valueCopy = copyStr(value); 141 props[nameCopy] = DProperty(type, nameCopy, valueCopy); 142 } 143 } 144 145 DProperty opDispatch(string s)() 146 { 147 if (s in props) 148 return props[s]; 149 else 150 return DProperty(DPropType.Undefined, ""); 151 } 152 153 DProperty* opBinaryRight(string op)(string k) if (op == "in") 154 { 155 return (k in props); 156 } 157 158 void remove(string name) 159 { 160 if (name in props) 161 { 162 auto n = props[name].name; 163 Delete(props[name].data); 164 props.remove(name); 165 Delete(n); 166 } 167 } 168 169 int opApply(int delegate(string, ref DProperty) dg) 170 { 171 foreach(k, v; props) 172 { 173 dg(k, v); 174 } 175 176 return 0; 177 } 178 179 ~this() 180 { 181 foreach(k, v; props) 182 { 183 Delete(v.data); 184 Delete(v.name); 185 } 186 Delete(props); 187 } 188 } 189 190 bool isWhiteStr(string s) 191 { 192 bool res; 193 foreach(c; s) 194 { 195 res = false; 196 foreach(w; std.ascii.whitespace) 197 { 198 if (c == w) 199 res = true; 200 } 201 202 if (c == '\n' || c == '\r') 203 res = true; 204 } 205 return res; 206 } 207 208 bool isValidIdentifier(string s) 209 { 210 return (isAlpha(s[0]) || s[0] == '_'); 211 } 212 213 string copyStr(T)(T[] s) 214 { 215 auto res = New!(char[])(s.length); 216 foreach(i, c; s) 217 res[i] = c; 218 return cast(string)res; 219 } 220 221 bool parseProperties(string input, Properties props) 222 { 223 enum Expect 224 { 225 PropName, 226 Colon, 227 Semicolon, 228 Value, 229 String, 230 Vector, 231 Number 232 } 233 234 bool res = true; 235 auto lexer = New!Lexer(input, [":", ";", "\"", "[", "]", ","]); 236 237 lexer.ignoreNewlines = true; 238 239 Expect expect = Expect.PropName; 240 string propName; 241 Array!char propValue; 242 DPropType propType; 243 244 while(true) 245 { 246 auto lexeme = lexer.getLexeme(); 247 if (lexeme.length == 0) 248 { 249 if (expect != Expect.PropName) 250 { 251 writefln("Error: unexpected end of string"); 252 res = false; 253 } 254 break; 255 } 256 257 if (isWhiteStr(lexeme) && expect != Expect.String) 258 continue; 259 260 if (expect == Expect.PropName) 261 { 262 if (!isValidIdentifier(lexeme)) 263 { 264 writefln("Error: illegal identifier name \"%s\"", lexeme); 265 res = false; 266 break; 267 } 268 269 propName = lexeme; 270 expect = Expect.Colon; 271 } 272 else if (expect == Expect.Colon) 273 { 274 if (lexeme != ":") 275 { 276 writefln("Error: expected \":\", got \"%s\"", lexeme); 277 res = false; 278 break; 279 } 280 281 expect = Expect.Value; 282 } 283 else if (expect == Expect.Semicolon) 284 { 285 if (lexeme != ";") 286 { 287 writefln("Error: expected \";\", got \"%s\"", lexeme); 288 res = false; 289 break; 290 } 291 292 props.set(propType, propName, cast(string)propValue.data); 293 294 expect = Expect.PropName; 295 propName = ""; 296 propValue.free(); 297 } 298 else if (expect == Expect.Value) 299 { 300 if (lexeme == "\"") 301 { 302 propType = DPropType.String; 303 expect = Expect.String; 304 } 305 else if (lexeme == "[") 306 { 307 propType = DPropType.Vector; 308 expect = Expect.Vector; 309 propValue.append(lexeme); 310 } 311 else 312 { 313 propType = DPropType.Number; 314 propValue.append(lexeme); 315 expect = Expect.Semicolon; 316 } 317 } 318 else if (expect == Expect.String) 319 { 320 if (lexeme == "\"") 321 expect = Expect.Semicolon; 322 else 323 propValue.append(lexeme); 324 } 325 else if (expect == Expect.Vector) 326 { 327 if (lexeme == "]") 328 expect = Expect.Semicolon; 329 330 propValue.append(lexeme); 331 } 332 } 333 334 propValue.free(); 335 Delete(lexer); 336 337 return res; 338 }