3
|
1 module ddata.ddata;
|
|
2
|
|
3 //debug
|
|
4 //import std.stdio;
|
|
5 import std.string;
|
|
6
|
|
7 import std2.conv : to;
|
|
8 import std2.traits : isNumeric;
|
|
9
|
|
10 class DDataException : Exception
|
|
11 {
|
|
12 this(char[] msg)
|
|
13 {
|
|
14 super(msg);
|
|
15 }
|
|
16 this( uint loc, char c, char[] msg)
|
|
17 {
|
|
18 super( format( `parsing failed at string[`, loc-1, `] = '`,c,`'` ,msg));
|
|
19 }
|
|
20 }
|
|
21
|
|
22
|
|
23 private enum TOKEN{ BOF, SQUARE_L, SQUARE_R, COMMA, ECOMMA, VALUE, EOF}
|
|
24
|
|
25 private template Depth(T: T[]) { const Depth = 1 + Depth!(T); }
|
|
26 private template Depth(T) { const Depth = 0; }
|
|
27
|
|
28 private template BaseType(T: T[]) { alias BaseType!(T) BaseType; }
|
|
29 private template BaseType(T) { alias T BaseType; }
|
|
30
|
|
31 private template indexAssign(T: T[])
|
|
32 {
|
|
33 void indexAssign(ref T array, BaseType!(T) value, int[] indices)
|
|
34 {
|
|
35 static if( is( typeof(array[0]) == typeof(value)))
|
|
36 {
|
|
37 array[indices[0]] = value;
|
|
38 }
|
|
39 else
|
|
40 {
|
|
41 indexAssign!(T)( array[indices[0]], value, indices[1..$]);
|
|
42 }
|
|
43 }
|
|
44 }
|
|
45
|
|
46 private string ctToString(int i)
|
|
47 {
|
|
48 if (!i) return "0";
|
|
49 string res;
|
|
50 while (i) {
|
|
51 res = "0123456789"[i%10] ~ res;
|
|
52 i /= 10;
|
|
53 }
|
|
54 return res;
|
|
55 }
|
|
56
|
|
57 private string indexString(int len)
|
|
58 {
|
|
59 string res;
|
|
60 for (int i = 0; i < len; ++i)
|
|
61 res ~= "[index["~ctToString(i)~"]] ";
|
|
62 return res;
|
|
63 }
|
|
64
|
|
65 private string casesString(int depth, TOKEN type)
|
|
66 {
|
|
67 string res;
|
|
68 res ~= `switch( depth ){`;
|
|
69
|
|
70 for (int i = 0; i < depth; ++i)
|
|
71 {
|
|
72 switch(type)
|
|
73 {
|
|
74 case TOKEN.COMMA:
|
|
75 res ~=`case `~ctToString(i)~`:`~
|
|
76 `if(temp`~ indexString(i) ~`.length<=index[`~ctToString(i)~`] )temp`~ indexString(i) ~`.length=temp`~ indexString(i) ~`.length*2;`~
|
|
77 `break;`;
|
|
78 break;
|
|
79
|
|
80 case TOKEN.SQUARE_L:
|
|
81 res ~=`case `~ctToString(i)~`:`~
|
|
82 `temp`~ indexString(i) ~`.length=4;`~
|
|
83 `break;`;
|
|
84 break;
|
|
85
|
|
86 case TOKEN.SQUARE_R:
|
|
87 res ~=`case `~ctToString(i)~`:`~
|
|
88 `temp`~ indexString(i) ~`.length=index[`~ctToString(i)~`];`~
|
|
89 `break;`;
|
|
90 break;
|
|
91
|
|
92 default:
|
|
93 assert(false);
|
|
94 break;
|
|
95 }
|
|
96 }
|
|
97
|
|
98 res ~= `default:assert(false);break;}`;
|
|
99 return res;
|
|
100 }
|
|
101
|
|
102
|
|
103 bool toBool(char[] s)
|
|
104 {
|
|
105 if(s == `true`)
|
|
106 {
|
|
107 return true;
|
|
108 }
|
|
109 else if(s == `false`)
|
|
110 {
|
|
111 return false;
|
|
112 }
|
|
113 else
|
|
114 {
|
|
115 throw new Exception(`Cannot convert "`~s~`" to bool`);
|
|
116 }
|
|
117 return false;
|
|
118 }
|
|
119
|
|
120 public T toArray(T:D[],D)(char[] string)
|
|
121 {
|
|
122 int[ Depth!(T) ] index = 0;
|
|
123
|
|
124 T temp;
|
|
125 char c;
|
|
126
|
|
127 int depth = -1;
|
|
128
|
|
129 int sLoc1 = -1, sLoc2 = -1;
|
|
130 int loc = -1;
|
|
131
|
|
132 TOKEN last = TOKEN.BOF;
|
|
133
|
|
134 while(true)
|
|
135 {
|
|
136 loc++;
|
|
137 //debug
|
|
138 //writefln(loc);
|
|
139 if( loc >= string.length )
|
|
140 {
|
|
141 if( last != TOKEN.SQUARE_R) throw new DDataException( `unexpected end`);
|
|
142 if( depth != -1 ) throw new DDataException( `array depth not zero after parsing `);
|
|
143 //throw new DDataException(`EOF before end of parsing`);
|
|
144 return temp;
|
|
145 }
|
|
146
|
|
147 c = string[loc];
|
|
148 //debug
|
|
149 //writefln(c);
|
|
150 switch(c)
|
|
151 {
|
|
152 case ' ', '\t', '\v', '\r', '\n', '\f':
|
|
153 if( last == TOKEN.ECOMMA || last == TOKEN.SQUARE_L)
|
|
154 {
|
|
155 if(sLoc2 < 0)
|
|
156 {
|
|
157 sLoc1 = loc + 1;
|
|
158 }else{
|
|
159 sLoc2 = loc;
|
|
160 last = TOKEN.VALUE;
|
|
161 }
|
|
162 }
|
|
163 break;
|
|
164
|
|
165 case ',':
|
|
166 index[depth] ++;
|
|
167
|
|
168 // resize array if necessary
|
|
169 mixin ( casesString(Depth!(T), TOKEN.COMMA ) );
|
|
170
|
|
171 if( last == TOKEN.VALUE && depth == Depth!(T)-1 )
|
|
172 {
|
|
173 index[depth] --;
|
|
174 static if( is(BaseType!(T) == bool) )
|
|
175 {
|
|
176 indexAssign!(T[])( temp, toBool( string[ sLoc1..sLoc2] ), index);
|
|
177 }
|
|
178 else static if( isNumeric!( BaseType!(T) ) )
|
|
179 {
|
|
180 indexAssign!(T[])( temp, to!( BaseType!(T) )( string[ sLoc1..sLoc2] ), index);
|
|
181 }
|
|
182 else
|
|
183 {
|
|
184 assert(false);
|
|
185 }
|
|
186 index[depth] ++;
|
|
187 sLoc1 = -1;
|
|
188 sLoc2 = -1;
|
|
189 last = TOKEN.ECOMMA;
|
|
190 }else if( last == TOKEN.SQUARE_R ){
|
|
191 last = TOKEN.COMMA;
|
|
192 }else{
|
|
193 throw new DDataException( loc, c, `: Value, ']' or ',' expected.`);
|
|
194 }
|
|
195 break;
|
|
196
|
|
197 case '[':
|
|
198 if( last != TOKEN.COMMA && last != TOKEN.BOF && last != TOKEN.SQUARE_L ) throw new DDataException( loc, c, `: Beginning, '[' or ',' expected.`);
|
|
199
|
|
200 depth++;
|
|
201 if(depth > index.length) throw new DDataException( loc, c, `: Array too deep = `~toString(depth));
|
|
202 mixin ( casesString(Depth!(T), TOKEN.SQUARE_L ) );
|
|
203 index[depth] = 0;
|
|
204 last = TOKEN.SQUARE_L;
|
|
205 sLoc1 = -1;
|
|
206 sLoc2 = -1;
|
|
207 break;
|
|
208
|
|
209 case ']':
|
|
210 if( last == TOKEN.VALUE && depth == Depth!(T)-1 && sLoc2 != -1)
|
|
211 {
|
|
212 static if( is(BaseType!(T) == bool) )
|
|
213 {
|
|
214 indexAssign!(T[])( temp, toBool( string[ sLoc1..sLoc2]), index);
|
|
215 }
|
|
216 else static if( isNumeric!( BaseType!(T) ) )
|
|
217 {
|
|
218 indexAssign!(T[])( temp, to!( BaseType!(T) )(string[ sLoc1..sLoc2]), index);
|
|
219 }
|
|
220 else
|
|
221 {
|
|
222 assert(false);
|
|
223 }
|
|
224 sLoc1 = -1;
|
|
225 sLoc2 = -1;
|
|
226 index[depth] ++;
|
|
227 }
|
|
228 else if( last == TOKEN.SQUARE_L )
|
|
229 {
|
|
230 sLoc1 = -1;
|
|
231 sLoc2 = -1;
|
|
232 }
|
|
233 else if( last == TOKEN.SQUARE_R)
|
|
234 {
|
|
235 index[depth] ++;
|
|
236 }
|
|
237 else
|
|
238 {
|
|
239 throw new DDataException( loc, c, `: Value, '[' or ']' expected.`);
|
|
240 }
|
|
241
|
|
242 // set array length to index[depth]
|
|
243 mixin ( casesString(Depth!(T), TOKEN.SQUARE_R) );
|
|
244
|
|
245 index[depth] = 0;
|
|
246 depth--;
|
|
247 last = TOKEN.SQUARE_R;
|
|
248 break;
|
|
249
|
|
250 default:
|
|
251 if( last == TOKEN.ECOMMA || last == TOKEN.SQUARE_L || last == TOKEN.VALUE)
|
|
252 {
|
|
253 if( sLoc1 < 0 )
|
|
254 {
|
|
255 sLoc1 = loc;
|
|
256 sLoc2 = loc +1;
|
|
257 } else {
|
|
258 sLoc2 = loc +1;
|
|
259 }
|
|
260 last = TOKEN.VALUE;
|
|
261 break;
|
|
262 }
|
|
263 throw new DDataException( loc, c, `: Out of place char found `);
|
|
264 break;
|
|
265 }
|
|
266 }
|
|
267 }
|
|
268
|
|
269 unittest
|
|
270 {
|
|
271 writefln("DData UnitTest...");
|
|
272
|
|
273 int[][][] ia = [ [[1,2],[3,4],[5]] , [[6],[],[7,8,9]] , [[]] ];
|
|
274
|
|
275 char[] s = to!(char[])(ia);
|
|
276 int[][][] ia2;
|
|
277
|
|
278 ia2 = toArray!(typeof(ia2))(s);
|
|
279 assert( ia == ia2);
|
|
280
|
|
281 writefln("Done!");
|
|
282 }
|
|
283
|