1 /++
2 Wrapper for SDLite.
3 
4 Noticable differences between SDLang and SDLite parser:
5 - Line breaking is not allowed ("title \", newline, "   'value'"), i.e:
6 ---
7 title \
8 	"Some title"
9 ---
10 +/
11 module sily.sdlang;
12 
13 import sdl = sdlite;
14 import std.range;
15 
16 import taggedalgebraic.taggedunion;
17 
18 /++
19 Representation of single sdlang node
20 Example:
21 ---
22 // Create SDLNode. SDLNode("name", SDLValue[] values, SDLAttribute[] attributes, SDLNode[] children)
23 SDLNode node = SDLNode("name", [SDLValue.text("values")], [], []);
24 // Get name
25 node.name;
26 // Get namespace
27 node.namespace;
28 // Get/Set qualified name (eq to namespace:name)
29 node.qualifiedName;
30 node.qualifiedName = "namespace:name";
31 // Get array of values (aka 'node 1 "b" v=2' -> returns 1 "b")
32 node.values;
33 // Get array of attributes (aka 'node 1 "b" v=2' -> returns v=2)
34 node.attributes;
35 // Get array of children
36 node.children;
37 // Gets attribute by qualified name
38 node.getAttribute("email")
39 // Gets attribute by qualified name with default value
40 node.getAttribute("email", SDLValue.text("mail@mail.com"))
41 ---
42 +/
43 alias SDLNode = sdl.SDLNode;
44 
45 /++
46 Value of sdlang node
47 Example:
48 ---
49 // Create new value
50 SDLValue val = SDLValue.double_(22.5);  
51 // Get value casted to int
52 val.value!int;
53 // Check type
54 val.kind == SDLType.text;
55 ---
56 +/
57 alias SDLValue = sdl.SDLValue;
58 
59 /++
60 Attribute of sdlang node (attr="val")
61 Example:
62 ---
63 // Create new attribute
64 SDLAttribute attr = SDLAttribute("qualifiedName", SDLValue.text("value"));
65 // Get name
66 attr.name;
67 // Get namepsace
68 attr.namespace;
69 // Get/Set qualified name (namespace:name)
70 attr.qualifiedName;
71 attr.qualifiedName = "namespace:name";
72 // Get/Set value
73 attr.value;
74 attr.value = SDLValue.text("new value")
75 ---
76 +/
77 alias SDLAttribute = sdl.SDLAttribute;
78 
79 /++
80 Alias to SDLValue.Kind. Represents type of SDLValue (might conflict with SDL library).
81 Example:
82 ---
83 node.values[0].kind == SDLType.float_;
84 node.values[0].kind == SDLFloat;
85 ---
86 Defined types (have aliases in form `SDLTypeName`, i.e SDLBinary or SDLDateTime):
87 ---
88 Void null_;
89 string text;
90 immutable(ubyte)[] binary;
91 int int_;
92 long long_;
93 long[2] decimal;
94 float float_;
95 double double_;
96 bool bool_;
97 SysTime dateTime;
98 Date date;
99 Duration duration;
100 ---
101 +/
102 alias SDLType = sdl.SDLValue.Kind;
103 /// Ditto
104 alias SDLNull = SDLType.null_;
105 /// Ditto
106 alias SDLString = SDLType.text;
107 /// Ditto
108 alias SDLBinary = SDLType.binary;
109 /// Ditto
110 alias SDLInt = SDLType.int_;
111 /// Ditto
112 alias SDLLong = SDLType.long_;
113 /// Ditto
114 alias SDLDecimal = SDLType.decimal;
115 /// Ditto
116 alias SDLFloat = SDLType.float_;
117 /// Ditto
118 alias SDLDouble = SDLType.double_;
119 /// Ditto
120 alias SDLBool = SDLType.bool_;
121 /// Ditto
122 alias SDLDateTime = SDLType.dateTime;
123 /// Ditto
124 alias SDLDate = SDLType.date;
125 /// Ditto
126 alias SDLDuration = SDLType.duration;
127 
128 /++
129 Parses SDL string into SDLNode[]
130 Example:
131 ---
132 import sily.sdlang;
133 import std.file;
134 SDLNode[] arr1 = parseSDL(readText("file.sdl"));
135 SDLNode[] arr2 = parseSDL("name \"Direct SDLang parsing\" cool=true");
136 SDLNode[] arr3 = parseSDL("name will print parsing error", true);
137 ---
138 +/
139 SDLNode[] parseSDL(string input, bool printOnError = false) {
140     SDLNode[] result;
141     try {
142         sdl.parseSDLDocument!((n) { result ~= n; })(input, "");
143     } catch (Exception e) {
144         if (printOnError) {
145             import std.stdio: writeln;
146             writeln("EXCEPTION: ", e.message);
147         }
148     }
149     return result;
150 }
151 
152 private alias generateSDLang = sdl.generateSDLang;
153 
154 /++
155 Writes SDL data into string
156 Example:
157 ---
158 import sily.sdlang;
159 import std.file;
160 SDLNode[] arr1 = parseSDL(readText("file.sdl"));
161 string out = arr1.generateSDL();
162 ---
163 +/
164 string generateSDL(SDLNode[] input) {
165     auto app = appender!string;
166     app.generateSDLang(input);
167     return app.data;
168 }
169 
170 /// Ditto
171 string generateSDL(SDLNode input) {
172     auto app = appender!string;
173     app.generateSDLang(input);
174     return app.data;
175 }
176 
177 /// Ditto
178 string generateSDL(SDLValue input) {
179     auto app = appender!string;
180     app.generateSDLang(input);
181     return app.data;
182 }
183 
184 /// Returns true is value `val` is type `T`
185 bool isType(SDLType T)(SDLValue val) {
186     return val.kind == T;
187 }
188 
189 /// Returns true if `node` has child with qualified name `qualifiedName`
190 bool hasNode(SDLNode node, string qualifiedName) {
191     foreach(child; node.children) {
192         if (child.qualifiedName == qualifiedName) return true;
193     }
194     return false;
195 }
196 
197 /// Returns child of `node` with qualified name `qualifiedName`
198 SDLNode getNode(SDLNode node, string qualifiedName) {
199     foreach(child; node.children) {
200         if (child.qualifiedName == qualifiedName) return child;
201     }
202     return SDLNode.init;
203 }
204 /// Ditto
205 alias node = getNode;
206 
207 /// Returns children of `node` with qualified name `qualifiedName`
208 SDLNode[] getNodes(SDLNode node, string qualifiedName) {
209     SDLNode[] arr = [];
210     foreach(child; node.children) {
211         if (child.qualifiedName == qualifiedName) arr ~= child;
212     }
213     return arr;
214 }
215 
216 /// Returns nodes from `nodes` array with qualified name `qualifiedName`
217 SDLNode[] getNodes(SDLNode[] nodes, string qualifiedName) {
218     SDLNode[] arr = [];
219     foreach(child; nodes) {
220         if (child.qualifiedName == qualifiedName) arr ~= child;
221     }
222     return arr;
223 }
224 
225 /// Alias to `getNodes(SDLNode, string)` and `getNodes(SDLNode[], string)`
226 alias nodes = getNodes;
227 
228 /// Returns true if `node` has attribute with qualified name `qualifiedName`
229 bool hasAttribute(SDLNode node, string qualifiedName) {
230     foreach(attrib; node.attributes) {
231         if (attrib.qualifiedName == qualifiedName) return true;
232     }
233     return false;
234 }
235 
236 /// Returns true if `node` has attribute with qualified name `qualifiedName` of type `T`
237 bool hasAttribute(SDLType T)(SDLNode node, string qualifiedName) {
238     foreach(attrib; node.attributes) {
239         if (attrib.qualifiedName == qualifiedName && attrib.value.kind == T) return true;
240     }
241     return false;
242 }
243 
244 /// Returns all values of type `S` as type `T` from node.
245 T[] getValues(T)(SDLNode node, SDLType type) {
246     T[] arr = [];
247     foreach (val; node.values) {
248         if (val.kind == type) arr ~= val.value!T;
249     }
250     return arr;
251 }
252 
253 /// Returns all values of type `S` as type `T` from nodes. Useful when dealing with matrices
254 T[] getValues(T)(SDLNode[] node, SDLType type) {
255     T[] arr = [];
256     foreach (n; node) {
257         foreach (val; n.values) {
258             if (val.kind == type) arr ~= val.value!T;
259         }
260     }
261     return arr;
262 }
263 
264 /// Alias to `getValues(T)(SDLNode, SDLType)` and `getValues(T)(SDLNode[], SDLType)`
265 alias values = getValues;
266 
267 /// Returns count of values of type `T`
268 size_t hasValues(SDLType T)(SDLNode node) {
269     size_t size = 0;
270     foreach (val; node.values) {
271         if (val.kind == type) ++size;
272     }
273     return size;
274 }
275 /// Ditto
276 size_t hasValues(SDLType T)(SDLNode[] node) {
277     size_t size = 0;
278     foreach (n; node) {
279         foreach (val; n.values) {
280             if (val.kind == type) ++size;
281         }
282     }
283     return size;
284 }
285 /// Ditto
286 alias value = hasValues;
287 
288 /// Returns attribute value with qualified name `qualifiedName` and type as `T`
289 T getAttribute(T)(SDLNode node, string qualifiedName) {
290     if (node.hasAttribute(qualifiedName)) { 
291         return node.getAttribute(qualifiedName).value!T;
292     }
293     return T.init;
294 }
295 
296 /// Returns attribute value with qualified name `qualifiedName` and type as `T`
297 T[] getAttributes(T)(SDLNode[] node, string qualifiedName) {
298     T[] arr = [];
299     foreach (n; node) {
300         if (n.hasAttribute(qualifiedName)) { 
301             arr ~= n.getAttribute(qualifiedName).value!T;
302         }
303     }
304     return arr;
305 }
306 
307 /// Alias to `getAttribute(T)(SDLNode, string)`
308 alias attribute = getAttribute;
309 
310 /// Alias to `getAttributes(T)(SDLNode[], string)`
311 alias attributes = getAttributes;
312 
313 /// Returns SDLang attribute value as T
314 T getValue(T)(SDLAttribute attr) {
315     return attr.value.value!T;
316 }
317 /// Ditto
318 alias value = getValue;