aboutsummaryrefslogtreecommitdiff
path: root/src/tools/mkunit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/mkunit.cpp')
-rw-r--r--src/tools/mkunit.cpp547
1 files changed, 547 insertions, 0 deletions
diff --git a/src/tools/mkunit.cpp b/src/tools/mkunit.cpp
new file mode 100644
index 0000000..43fab38
--- /dev/null
+++ b/src/tools/mkunit.cpp
@@ -0,0 +1,547 @@
1#include <bu/file.h>
2#include <bu/optparser.h>
3#include <bu/buffer.h>
4#include <bu/list.h>
5#include <bu/sio.h>
6#include <bu/variant.h>
7
8using namespace Bu;
9
10class Test
11{
12public:
13 Test() :
14 bExpectPass( true )
15 {
16 }
17
18 Bu::FString sName;
19 bool bExpectPass;
20};
21typedef Bu::List<Test> TestList;
22
23class Suite
24{
25public:
26 Bu::FString sName;
27 TestList lTest;
28};
29//typedef Bu::List<Suite> SuiteList;
30
31enum TokType
32{
33 tokFluff,
34 tokSuite,
35 tokTest,
36 tokChar,
37 tokBlock,
38 tokEof
39};
40
41Bu::Formatter &operator<<( Bu::Formatter &f, TokType t )
42{
43 switch( t )
44 {
45 case tokFluff: return f << "tokFluff";
46 case tokSuite: return f << "tokSuite";
47 case tokTest: return f << "tokTest";
48 case tokChar: return f << "tokChar";
49 case tokBlock: return f << "tokBlock";
50 case tokEof: return f << "tokEof";
51 }
52
53 return f;
54}
55
56Bu::Formatter &operator<<( Bu::Formatter &f, const Test &t )
57{
58 return f << "{" << t.sName << ", bExpectPass=" << t.bExpectPass << "}";
59}
60
61Bu::Formatter &operator<<( Bu::Formatter &f, const Suite &s )
62{
63 return f << "Suite[" << s.sName << "] = " << s.lTest << f.nl;
64}
65
66class Parser
67{
68public:
69 Parser( const Bu::FString &sFile ) :
70 fIn( sFile, File::Read ),
71 bIn( fIn ),
72 cBuf( 0 ),
73 bAvail( false ),
74 eMode( mRoot ),
75 iLine( 1 ),
76 iChar( 0 ),
77 iDepth( 0 )
78 {
79 }
80
81 char nextChar()
82 {
83 if( bAvail )
84 return cBuf;
85
86 if( bIn.read( &cBuf, 1 ) < 1 )
87 throw Bu::ExceptionBase("End of stream");
88 bAvail = true;
89
90 if( cBuf == '\n' )
91 {
92 iLine++;
93 iChar = 0;
94 }
95 else
96 iChar++;
97
98 return cBuf;
99 }
100
101 TokType nextToken( Variant &v, Bu::FString &sWsOut, int &iLineStart,
102 int &iCharStart )
103 {
104 Bu::FString sTok, sWs;
105
106 char buf;
107 try
108 {
109 buf = nextChar();
110 }
111 catch(...)
112 {
113 return tokEof;
114 }
115
116 for(;;)
117 {
118 if( buf == ' ' || buf == '\t' || buf == '\n' || buf == '\r' )
119 {
120 sWs += buf;
121 bAvail = false;
122 }
123 else
124 break;
125
126 try
127 {
128 buf = nextChar();
129 }
130 catch(...)
131 {
132 sWsOut = sWs;
133 return tokEof;
134 }
135 }
136
137 sWsOut = sWs;
138
139 iLineStart = iLine;
140 iCharStart = iChar;
141 bool bInStr = false;
142 bool bDblStr;
143
144 for(;;)
145 {
146 switch( eMode )
147 {
148 case mRoot:
149 if( buf == ' ' || buf == '\t' || buf == '\n'
150 || buf == '\r' )
151 {
152 if( sTok == "suite" )
153 return tokSuite;
154 else
155 {
156 v = sTok;
157 return tokFluff;
158 }
159 }
160 else if( buf == '(' || buf == ')' || buf == '{'
161 || buf == '}' || buf == ';' )
162 {
163 if( sTok.getSize() == 0 )
164 {
165 bAvail = false;
166 v = buf;
167 return tokChar;
168 }
169 else
170 {
171 v = sTok;
172 return tokFluff;
173 }
174 }
175 else
176 {
177 sTok += buf;
178 bAvail = false;
179 }
180 break;
181
182 case mSuite:
183 if( buf == ' ' || buf == '\t' || buf == '\n'
184 || buf == '\r' )
185 {
186 if( sTok == "test" )
187 return tokTest;
188 else
189 {
190 v = sTok;
191 return tokFluff;
192 }
193 }
194 else if( buf == '(' || buf == ')'
195 || buf == '}' || buf == ';' )
196 {
197 if( sTok.getSize() == 0 )
198 {
199 bAvail = false;
200 v = buf;
201 return tokChar;
202 }
203 else
204 {
205 v = sTok;
206 return tokFluff;
207 }
208 }
209 else if( buf == '{' )
210 {
211 if( sTok.getSize() > 0 )
212 {
213 v = sTok;
214 return tokFluff;
215 }
216 else
217 {
218 sTok += buf;
219 bAvail = false;
220 eMode = mBlock;
221 iDepth = 1;
222 }
223 }
224 else
225 {
226 sTok += buf;
227 bAvail = false;
228 }
229 break;
230
231 case mBlock:
232 if( bInStr )
233 {
234 if( buf == '\\' )
235 {
236 sTok += buf;
237 bAvail = false;
238 sTok += nextChar();
239 bAvail = false;
240 }
241 else if( bDblStr == true && buf == '\"' )
242 {
243 sTok += buf;
244 bAvail = false;
245 bInStr = false;
246 }
247 else if( bDblStr == false && buf == '\'' )
248 {
249 sTok += buf;
250 bAvail = false;
251 bInStr = false;
252 }
253 else
254 {
255 sTok += buf;
256 bAvail = false;
257 }
258 }
259 else
260 {
261 if( buf == '\"' )
262 {
263 bInStr = true;
264 bDblStr = true;
265 sTok += buf;
266 bAvail = false;
267 }
268 else if( buf == '\'' )
269 {
270 bInStr = true;
271 bDblStr = false;
272 sTok += buf;
273 bAvail = false;
274 }
275 else if( buf == '}' )
276 {
277 sTok += buf;
278 bAvail = false;
279 iDepth--;
280 if( iDepth == 0 )
281 {
282 v = sTok;
283 eMode = mSuite;
284 return tokBlock;
285 }
286 }
287 else if( buf == '{' )
288 {
289 sTok += buf;
290 bAvail = false;
291 iDepth++;
292 }
293 else
294 {
295 sTok += buf;
296 bAvail = false;
297 }
298 }
299 break;
300 }
301
302 buf = nextChar();
303 }
304 }
305
306 void firstPass()
307 {
308 Variant v;
309 Bu::FString sWs;
310 int iL, iC;
311 for(;;)
312 {
313 TokType t = nextToken( v, sWs, iL, iC );
314 if( t == tokEof )
315 return;
316 switch( eMode )
317 {
318 case mRoot:
319 if( t == tokSuite )
320 {
321 if( nextToken( v, sWs, iL, iC ) != tokFluff )
322 throw Bu::ExceptionBase("%d:%d: Expected string "
323 "following suite.", iL, iC );
324 s.sName = v.get<Bu::FString>();
325 if( nextToken( v, sWs, iL, iC ) != tokChar ||
326 v.get<char>() != '{' )
327 throw Bu::ExceptionBase("%d:%d: Expected {, got "
328 "'%s'", iL, iC, v.toString().getStr() );
329 eMode = mSuite;
330 }
331 break;
332
333 case mSuite:
334 switch( t )
335 {
336 case tokFluff:
337 break;
338
339 case tokBlock:
340 break;
341
342 case tokTest:
343 {
344 if( nextToken( v, sWs, iL, iC ) != tokFluff )
345 throw Bu::ExceptionBase("%d:%d: Expected "
346 "string following test.", iL, iC );
347 Test t;
348 t.sName = v.get<Bu::FString>();
349 if( nextToken( v, sWs, iL, iC ) != tokBlock )
350 throw Bu::ExceptionBase("%d:%d: Expected "
351 "{...} block.",
352 iL, iC );
353 s.lTest.append( t );
354 }
355 break;
356
357 case tokChar:
358 if( v.get<char>() == '}' )
359 {
360 eMode = mRoot;
361 }
362 else
363 {
364 }
365 break;
366
367 default:
368 sio << iL << ":" << iC << ": Unexpected "
369 << t << " found." << sio.nl;
370 return;
371 break;
372 }
373 break;
374
375 default:
376 sio << "???" << sio.nl;
377 break;
378 }
379 }
380 }
381
382 void secondPass( const Bu::FString &sOut )
383 {
384 File fOut( sOut, File::WriteNew );
385 Formatter f( fOut );
386 fIn.setPos( 0 );
387 bIn.stop();
388 bIn.start();
389 bAvail = false;
390 eMode = mRoot;
391 iLine = 1;
392 iChar = 0;
393 bool bHasIncluded = false;
394
395 Bu::FString sWs;
396 Variant v;
397 int iL, iC;
398 for(;;)
399 {
400 TokType t = nextToken( v, sWs, iL, iC );
401 switch( eMode )
402 {
403 case mRoot:
404 if( t == tokSuite )
405 {
406 fOut.write( sWs );
407 if( nextToken( v, sWs, iL, iC ) != tokFluff )
408 throw Bu::ExceptionBase("%d:%d: Expected string "
409 "following suite.", iL, iC );
410 s.sName = v.get<Bu::FString>();
411 if( nextToken( v, sWs, iL, iC ) != tokChar ||
412 v.get<char>() != '{' )
413 throw Bu::ExceptionBase("%d:%d: Expected {",
414 iL, iC );
415 eMode = mSuite;
416
417 if( bHasIncluded == false )
418 {
419 fOut.write("#include <bu/unitsuite.h>\n");
420 bHasIncluded = true;
421 }
422
423 Bu::FString sClass = "_UnitSuite_" + s.sName;
424 f << "class " << sClass
425 << " : public Bu::UnitSuite" << f.nl
426 << "{" << f.nl << "public:" << f.nl
427 << "\t" << sClass << "()" << f.nl
428 << "\t{" << f.nl
429 << "\t\tsetName(\"" << s.sName << "\");" << f.nl;
430 for( TestList::iterator i = s.lTest.begin(); i; i++ )
431 {
432 f << "\t\tadd( static_cast<Bu::UnitSuite::Test>("
433 "&" << sClass << "::" << (*i).sName << "), \""
434 << (*i).sName << "\", Bu::UnitSuite::"
435 "expectPass );" << f.nl;
436 }
437 f << "\t}" << f.nl << f.nl
438 << "\tvirtual ~" << sClass << "() { }" << f.nl
439 << f.nl;
440 }
441 else if( t == tokEof )
442 {
443 Bu::FString sClass = "_UnitSuite_" + s.sName;
444 f << sWs << f.nl << "int main( int argc, char *argv[] )"
445 << f.nl << "{" << f.nl << "\treturn " << sClass
446 << "().run( argc, argv );" << f.nl << "}" << f.nl;
447 }
448 else
449 {
450 fOut.write( sWs );
451 f << v;
452 }
453 break;
454
455 case mSuite:
456 switch( t )
457 {
458 case tokFluff:
459 fOut.write( sWs );
460 fOut.write( v.get<Bu::FString>() );
461 break;
462
463 case tokTest:
464 {
465 fOut.write( sWs );
466 if( nextToken( v, sWs, iL, iC ) != tokFluff )
467 throw Bu::ExceptionBase("%d:%d: Expected "
468 "string following test.", iL, iC );
469 Test t;
470 t.sName = v.get<Bu::FString>();
471 if( nextToken( v, sWs, iL, iC ) != tokBlock )
472 throw Bu::ExceptionBase("%d:%d: Expected "
473 "{...} block.",
474 iL, iC );
475
476 f << "\tvoid " << t.sName << "()"
477 << f.nl << "#line " << iL
478 << " \"" << sOut << "\"" << f.nl
479 << v << f.nl;
480 }
481 break;
482
483 case tokChar:
484 if( v.get<char>() == '}' )
485 {
486 f << "};" << f.nl << f.nl;
487 eMode = mRoot;
488 }
489 else
490 {
491 char buf = v.get<char>();
492 fOut.write( sWs );
493 fOut.write( &buf, 1 );
494 }
495 break;
496
497 case tokBlock:
498 fOut.write( sWs );
499 f << f.nl << "#line " << iL << " \"" << sOut
500 << "\"" << f.nl;
501 fOut.write( v.get<Bu::FString>() );
502
503 break;
504
505 default:
506 sio << iL << ":" << iC << ": Unexpected "
507 << t << " found." << sio.nl;
508 return;
509 break;
510 }
511 break;
512
513 default:
514 sio << "???" << sio.nl;
515 break;
516 }
517 if( t == tokEof )
518 return;
519 }
520 }
521
522private:
523 File fIn;
524 Buffer bIn;
525 char cBuf;
526 bool bAvail;
527 enum Mode
528 {
529 mRoot,
530 mSuite,
531 mBlock
532 };
533 Mode eMode;
534 int iLine, iChar;
535 int iDepth;
536 Suite s;
537};
538
539int main( int argc, char *argv[] )
540{
541 Parser p( argv[1] );
542
543 p.firstPass();
544
545 p.secondPass( argv[2] );
546}
547