1 /*
2 Copyright (c) 2016-2018 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.resource.props;
30 
31 import std.stdio;
32 import std.ascii;
33 import std.conv;
34 import dlib.core.memory;
35 import dlib.container.array;
36 import dlib.container.dict;
37 import dlib.text.utils;
38 import dlib.math.vector;
39 import dlib.image.color;
40 import dlib.text.lexer;
41 import dagon.core.ownership;
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     protected 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     DProperty opIndexAssign(DProperty v, string name)
127     {
128         if (name in props)
129         {
130             Delete(props[name].name);
131             Delete(props[name].data);
132             props[name] = v;
133         }
134         else
135         {
136             props[name] = v;
137         }
138         return v;
139     }
140 */
141 
142     void set(DPropType type, string name, string value)
143     {
144         auto p = name in props;
145         if (p)
146         {
147             Delete(p.name);
148             Delete(p.data);
149             auto nameCopy = copyStr(name);
150             auto valueCopy = copyStr(value);
151             props[nameCopy] = DProperty(type, nameCopy, valueCopy);
152             //props[name] = v;
153         }
154         else
155         {
156             //props[name] = v;
157             auto nameCopy = copyStr(name);
158             auto valueCopy = copyStr(value);
159             props[nameCopy] = DProperty(type, nameCopy, valueCopy);
160         }
161     }
162 
163     DProperty opDispatch(string s)()
164     {
165         if (s in props)
166             return props[s];
167         else
168             return DProperty(DPropType.Undefined, "");
169     }
170     
171     DProperty* opIn_r(string k)
172     {
173         return (k in props);
174     }
175 
176     void remove(string name)
177     {
178         if (name in props)
179         {
180             auto n = props[name].name;
181             Delete(props[name].data);
182             props.remove(name);
183             Delete(n);
184         }
185     }
186     
187     int opApply(int delegate(string, ref DProperty) dg)
188     {
189         foreach(k, v; props)
190         {
191             dg(k, v);
192         }
193 
194         return 0;
195     }
196     
197     ~this()
198     {
199         foreach(k, v; props)
200         {
201             Delete(v.data);
202             Delete(v.name);
203         }
204         Delete(props);
205     }
206 }
207 
208 bool isWhiteStr(string s)
209 {
210     bool res;
211     foreach(c; s)
212     {
213         res = false;
214         foreach(w; std.ascii.whitespace)
215         {
216             if (c == w)
217                 res = true;
218         }
219         
220         if (c == '\n' || c == '\r')
221             res = true;
222     }
223     return res;
224 }
225 
226 bool isValidIdentifier(string s)
227 {
228     return (isAlpha(s[0]) || s[0] == '_');
229 }
230 
231 string copyStr(T)(T[] s)
232 {
233     auto res = New!(char[])(s.length);
234     foreach(i, c; s)
235         res[i] = c;
236     return cast(string)res;
237 }
238 
239 bool parseProperties(string input, Properties props)
240 {
241     enum Expect
242     {
243         PropName,
244         Colon,
245         Semicolon,
246         Value,
247         String,
248         Vector,
249         Number
250     }
251 
252     bool res = true;
253     auto lexer = New!Lexer(input, [":", ";", "\"", "[", "]", ","]);
254     
255     lexer.ignoreNewlines = true;
256     
257     Expect expect = Expect.PropName;
258     string propName;
259     DynamicArray!char propValue;
260     DPropType propType;
261     
262     while(true)
263     {
264         auto lexeme = lexer.getLexeme();
265         if (lexeme.length == 0) 
266         {
267             if (expect != Expect.PropName)
268             {
269                 writefln("Error: unexpected end of string");
270                 res = false;
271             }
272             break;
273         }
274         
275         if (isWhiteStr(lexeme) && expect != Expect.String)
276             continue;
277         
278         if (expect == Expect.PropName)
279         {
280             if (!isValidIdentifier(lexeme))
281             {
282                 writefln("Error: illegal identifier name \"%s\"", lexeme);
283                 res = false;
284                 break;
285             }
286             
287             propName = lexeme;
288             expect = Expect.Colon;
289         }
290         else if (expect == Expect.Colon)
291         {
292             if (lexeme != ":")
293             {
294                 writefln("Error: expected \":\", got \"%s\"", lexeme);
295                 res = false;
296                 break;
297             }
298             
299             expect = Expect.Value;
300         }
301         else if (expect == Expect.Semicolon)
302         {
303             if (lexeme != ";")
304             {
305                 writefln("Error: expected \";\", got \"%s\"", lexeme);
306                 res = false;
307                 break;
308             }
309             
310             //auto nameCopy = copyStr(propName);
311             //auto valueCopy = copyStr(propValue.data);
312 
313             //props[nameCopy] = DProperty(propType, nameCopy, valueCopy);
314             props.set(propType, propName, cast(string)propValue.data);
315             
316             expect = Expect.PropName;
317             propName = "";
318             propValue.free();
319         }
320         else if (expect == Expect.Value)
321         {
322             if (lexeme == "\"")
323             {
324                 propType = DPropType.String;
325                 expect = Expect.String;
326             }
327             else if (lexeme == "[")
328             {
329                 propType = DPropType.Vector;
330                 expect = Expect.Vector;
331                 propValue.append(lexeme);
332             }
333             else
334             {
335                 propType = DPropType.Number;
336                 propValue.append(lexeme);
337                 expect = Expect.Semicolon;
338             }
339         }
340         else if (expect == Expect.String)
341         {
342             if (lexeme == "\"")
343                 expect = Expect.Semicolon;
344             else
345                 propValue.append(lexeme);
346         }
347         else if (expect == Expect.Vector)
348         {
349             if (lexeme == "]")
350                 expect = Expect.Semicolon;
351 
352             propValue.append(lexeme);
353         }
354     }
355     
356     propValue.free();
357     Delete(lexer);
358 
359     return res;
360 }