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 }