root/trunk/minid/misc.d

Revision 423, 7.9 kB (checked in by JarrettBillingsley, 3 weeks ago)

Fixed some formatting stuff regarding floats. The compiler will now also parse large (>32-bit) integers correctly. toJSON should no longer cause D heap allocations.

Line 
1 /******************************************************************************
2 This holds miscellaneous functionality used in the internal library and also
3 as part of the extended API.  There are no public functions in here yet.
4
5 License:
6 Copyright (c) 2008 Jarrett Billingsley
7
8 This software is provided 'as-is', without any express or implied warranty.
9 In no event will the authors be held liable for any damages arising from the
10 use of this software.
11
12 Permission is granted to anyone to use this software for any purpose,
13 including commercial applications, and to alter it and redistribute it freely,
14 subject to the following restrictions:
15
16     1. The origin of this software must not be misrepresented; you must not
17     claim that you wrote the original software. If you use this software in a
18     product, an acknowledgment in the product documentation would be
19     appreciated but is not required.
20
21     2. Altered source versions must be plainly marked as such, and must not
22     be misrepresented as being the original software.
23
24     3. This notice may not be removed or altered from any source distribution.
25 ******************************************************************************/
26
27 module minid.misc;
28
29 import Integer = tango.text.convert.Integer;
30 import tango.io.Print;
31 import tango.stdc.ctype;
32 import tango.stdc.stdlib;
33 import tango.text.convert.Utf;
34 import tango.text.Util;
35
36 import minid.alloc;
37 import minid.interpreter;
38 import minid.types;
39 import minid.utils;
40
41 package void formatImpl(MDThread* t, uword numParams, uint delegate(char[]) sink)
42 {
43     void output(char[] fmt, uword param, bool isRaw)
44     {
45         if(fmt[1] == 'r' || isdigit(fmt[1]))
46         {
47             auto begin = 2;
48
49             for(; begin < fmt.length; begin++)
50                 if(fmt[begin] != 'r' && !isdigit(fmt[begin]))
51                     break;
52
53             auto tmp = (cast(char*) alloca(fmt.length - begin + 1))[0 .. fmt.length - begin + 1];
54             tmp[0] = '{';
55             tmp[1 .. $] = fmt[begin .. $];
56            
57             switch(type(t, param))
58             {
59                 case MDValue.Type.Int:   t.vm.formatter.convert(sink, tmp, getInt(t, param)); break;
60                 case MDValue.Type.Float: t.vm.formatter.convert(sink, tmp, getFloat(t, param)); break;
61                 case MDValue.Type.Char:  t.vm.formatter.convert(sink, tmp, getChar(t, param)); break;
62
63                 default:
64                     pushToString(t, param, isRaw);
65                     t.vm.formatter.convert(sink, tmp, getString(t, -1));
66                     pop(t);
67                     break;
68             }
69         }
70         else
71         {
72             switch(type(t, param))
73             {
74                 case MDValue.Type.Int:   t.vm.formatter.convert(sink, fmt, getInt(t, param)); break;
75                 case MDValue.Type.Float: t.vm.formatter.convert(sink, fmt, getFloat(t, param)); break;
76                 case MDValue.Type.Char:  t.vm.formatter.convert(sink, fmt, getChar(t, param)); break;
77
78                 default:
79                     pushToString(t, param, isRaw);
80                     t.vm.formatter.convert(sink, fmt, getString(t, -1));
81                     pop(t);
82                     break;
83             }
84         }
85     }
86
87     if(numParams > 64)
88         throwException(t, "Too many parameters to format (maximum of 64)");
89
90     bool[64] used;
91
92     for(uword paramIndex = 1; paramIndex <= numParams; paramIndex++)
93     {
94         if(used[paramIndex])
95             continue;
96
97         if(!isString(t, paramIndex))
98         {
99             output("{}", paramIndex, false);
100             continue;
101         }
102
103         auto formatStr = getString(t, paramIndex);
104         auto formatStrIndex = paramIndex;
105         auto autoIndex = paramIndex + 1;
106         uword begin = 0;
107
108         while(begin < formatStr.length)
109         {
110             auto fmtBegin = formatStr.locate('{', begin);
111
112             t.vm.formatter.convert(sink, "{}", formatStr[begin .. fmtBegin]);
113             begin = fmtBegin;
114
115             if(fmtBegin == formatStr.length)
116                 break;
117
118             auto fmtEnd = formatStr.locate('}', fmtBegin + 1);
119
120             if(fmtEnd == formatStr.length)
121             {
122                 t.vm.formatter.convert(sink, "{{missing or misplaced '}'}{}", formatStr[fmtBegin .. $]);
123                 break;
124             }
125
126             fmtEnd++;
127
128             auto fmtSpec = formatStr[fmtBegin .. fmtEnd];
129            
130             bool isRaw = false;
131
132             if(fmtSpec[1] == 'r')
133                 isRaw = true;
134
135             auto index = autoIndex;
136
137             if(fmtSpec.length > 2 && (isRaw ? isdigit(fmtSpec[2]) : isdigit(fmtSpec[1])))
138             {
139                 uword j = 2;
140
141                 for(; j < fmtSpec.length && isdigit(fmtSpec[j]); j++)
142                 {}
143
144                 auto offset = Integer.atoi(fmtSpec[2 .. j]);
145                 index = formatStrIndex + offset + 1;
146             }
147             else
148                 autoIndex++;
149
150             if(index > numParams)
151                 t.vm.formatter.convert(sink, "{}", "{invalid index}");
152             else
153             {
154                 used[index] = true;
155                 output(fmtSpec, index, isRaw);
156             }
157
158             begin = fmtEnd;
159         }
160     }
161 }
162
163 // Expects root to be at the top of the stack
164 package void toJSONImpl(T)(MDThread* t, word root, bool pretty, Print!(T) printer)
165 {
166     root = absIndex(t, root);
167     auto cycles = newTable(t);
168
169     word indent = 0;
170
171     void newline(word dir = 0)
172     {
173         printer.newline;
174
175         if(dir > 0)
176             indent++;
177         else if(dir < 0)
178             indent--;
179
180         for(word i = indent; i > 0; i--)
181             printer.print("\t");
182     }
183
184     void delegate(word) outputValue;
185
186     void outputTable(word tab)
187     {
188         printer.print("{");
189
190         if(pretty)
191             newline(1);
192
193         bool first = true;
194         dup(t, tab);
195
196         foreach(word k, word v; foreachLoop(t, 1))
197         {
198             if(!isString(t, k))
199                 throwException(t, "All keys in a JSON table must be strings");
200
201             if(first)
202                 first = false;
203             else
204             {
205                 printer.print(",");
206
207                 if(pretty)
208                     newline();
209             }
210
211             outputValue(k);
212
213             if(pretty)
214                 printer.print(": ");
215             else
216                 printer.print(":");
217
218             outputValue(v);
219         }
220
221         if(pretty)
222             newline(-1);
223
224         printer.print("}");
225     }
226
227     void outputArray(word arr)
228     {
229         printer.print("[");
230
231         auto l = len(t, arr);
232
233         for(word i = 0; i < l; i++)
234         {
235             if(i > 0)
236             {
237                 if(pretty)
238                     printer.print(", ");
239                 else
240                     printer.print(",");
241             }
242
243             outputValue(idxi(t, arr, i));
244             pop(t);
245         }
246
247         printer.print("]");
248     }
249    
250     void outputChar(dchar c)
251     {
252         switch(c)
253         {
254             case '\b': printer.print("\\b"); return;
255             case '\f': printer.print("\\f"); return;
256             case '\n': printer.print("\\n"); return;
257             case '\r': printer.print("\\r"); return;
258             case '\t': printer.print("\\t"); return;
259
260             case '"', '\\', '/':
261                 printer.print("\\");
262                 printer.print(c);
263                 return;
264
265             default:
266                 if(c > 0x7f)
267                     printer.format("\\u{:x4}", cast(int)c);
268                 else
269                     printer.print(c);
270
271                 return;
272         }
273     }
274
275     void _outputValue(word idx)
276     {
277         switch(type(t, idx))
278         {
279             case MDValue.Type.Null:
280                 printer.print("null");
281                 break;
282
283             case MDValue.Type.Bool:
284                 printer.print(getBool(t, idx) ? "true" : "false");
285                 break;
286
287             case MDValue.Type.Int:
288                 printer.format("{}", getInt(t, idx));
289                 break;
290
291             case MDValue.Type.Float:
292                 printer.format("{}", getFloat(t, idx));
293                 break;
294
295             case MDValue.Type.Char:
296                 printer.print('"');
297                 outputChar(getChar(t, idx));
298                 printer.print('"');
299                 break;
300
301             case MDValue.Type.String:
302                 printer.print('"');
303
304                 foreach(dchar c; getString(t, idx))
305                     outputChar(c);
306
307                 printer.print('"');
308                 break;
309
310             case MDValue.Type.Table:
311                 if(opin(t, idx, cycles))
312                     throwException(t, "Table is cyclically referenced");
313
314                 dup(t, idx);
315                 pushBool(t, true);
316                 idxa(t, cycles);
317
318                 scope(exit)
319                 {
320                     dup(t, idx);
321                     pushNull(t);
322                     idxa(t, cycles);
323                 }
324
325                 outputTable(idx);
326                 break;
327
328             case MDValue.Type.Array:
329                 if(opin(t, idx, cycles))
330                     throwException(t, "Array is cyclically referenced");
331
332                 dup(t, idx);
333                 pushBool(t, true);
334                 idxa(t, cycles);
335
336                 scope(exit)
337                 {
338                     dup(t, idx);
339                     pushNull(t);
340                     idxa(t, cycles);
341                 }
342
343                 outputArray(idx);
344                 break;
345
346             default:
347                 pushTypeString(t, idx);
348                 throwException(t, "Type '{}' is not a valid type for conversion to JSON", getString(t, -1));
349         }
350     }
351
352     outputValue = &_outputValue;
353
354     if(isArray(t, root))
355         outputArray(root);
356     else if(isTable(t, root))
357         outputTable(root);
358     else
359     {
360         pushTypeString(t, root);
361         throwException(t, "Root element must be either a table or an array, not a '{}'", getString(t, -1));
362     }
363
364     printer.flush();
365     pop(t);
366 }
Note: See TracBrowser for help on using the browser.