• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  ******************************************************************************
3  * Copyright (C) 2005-2007, International Business Machines Corporation and   *
4  * others. All Rights Reserved.                                               *
5  ******************************************************************************
6  */
7 
8 #include <stdio.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <time.h>
12 
13 #include "wbnf.h"
14 
15 // Most of this code is meant to test the test code. It's a self test.
16 // Normally this isn't run.
17 #define TEST_WBNF_TEST 0
18 
19 ///////////////////////////////////////////////////////////
20 //
21 // Constants and the most basic helper classes
22 //
23 
24 static const char DIGIT_CHAR[] = "0123456789";
25 static const char WHITE_SPACE[] = {'\t', ' ', '\r', '\n', 0};
26 static const char ALPHABET[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
27 static const char SPECIAL[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
28 
isInList(const char c,const char list[])29 static inline UBool isInList(const char c /*in*/, const char list[] /*in*/){
30     const char * p = list;
31     for (;*p != 0 && *p != c; p++);
32     return *p?TRUE:FALSE;
33 }
isDigit(char c)34 static inline UBool isDigit(char c) {return isInList(c, DIGIT_CHAR);}
isWhiteSpace(char c)35 static inline UBool isWhiteSpace(char c) {return isInList(c, WHITE_SPACE);}
isAlphabet(char c)36 static inline UBool isAlphabet(char c) {return isInList(c, ALPHABET);}
isSpecialAsciiChar(char c)37 static inline UBool isSpecialAsciiChar(char c) {return isInList(c,SPECIAL);}
38 
39 
40 
41 ///////////////////////////////////////////////////////////
42 //
43 // Helper classes
44 //
45 
46 class Buffer_byte{
47 // Utility class, can be treated as an auto expanded array. no boundary check.
48 
49     typedef char byte;
50     byte * start;
51     byte * current;
52     int buffer_size; // size unit is byte
53 public:
content_size()54     inline int content_size(){return current - start;} // size unit is byte
55 
56 private:
expand(int add_size=100)57     inline void expand(int add_size = 100){ // size unit is byte
58         int new_size = buffer_size + add_size;
59 
60         int cs_snap = content_size();
61         start = (byte *) realloc(start, new_size);   // may change the value of start
62         current = start + cs_snap;
63 
64         memset(current, 0, add_size);
65         buffer_size = new_size;
66     }
67 
expand_to(int size)68     inline void expand_to(int size){
69         int r = size - buffer_size;
70         if (r > 0) {
71             expand(r);  // simply expand, no block alignment
72         }
73     }
74     Buffer_byte(const Buffer_byte &);
75     Buffer_byte & operator = (const Buffer_byte &);
76 public:
Buffer_byte()77     Buffer_byte():start(NULL),current(start),buffer_size(0){
78         expand();
79     }
~Buffer_byte()80     ~Buffer_byte(){
81         free(start);
82     }
83 
reset()84     inline void reset(){
85         start != NULL ? memset(start, 0, buffer_size) : 0;
86         current = start;
87     }
88 
89     // Using memory copy method to append a C array to buffer,
append(const void * c,int size)90     inline void append(const void * c, int size){ // size unit is byte
91         expand_to(content_size() + size) ;
92         memcpy(current, c, size);
93         current = current + size;
94     }
95 
buffer()96     byte * buffer(){
97         return start;
98     }
99 };
100 
101 /*
102   The class(es) try to work as bulid-in array, so it overloads these two operators
103     operator type *();
104     type & operator[];
105   The first is used to auto type convert, the latter is used to select member.
106 
107   A small trick is the class does not overload the address-of operator. This
108   behavior is different from bulid-in array, but it give us the opportunity
109   to get the address of the class itself.
110 */
111 //template<typename type>
112 //    class BUFFER{
113 //       typedef BUFFER name;
114 #define BUFFER(type, name)\
115     class name {\
116     private:\
117        Buffer_byte buf;\
118     public:\
119         name & reset() {buf.reset(); return *this;}\
120         name & append(type c) {buf.append(&c, sizeof(type)); return *this;}\
121         name & append_array(const type * p, int size) {buf.append(p, sizeof(type)*size); return *this;}\
122         type & operator [] (int i) { return ((type *) buf.buffer())[i];}\
123         operator type *(){return (type *) buf.buffer();} \
124         int content_size(){return buf.content_size() / sizeof(type);}\
125     }
126 
127 
128 class Pick{
129 /* The Pick is the basic language generator element*/
130 public:
131     // generate a string accroding the syntax
132     // Return a null-terminated c-string. The buffer is owned by callee.
133     virtual const char* next() = 0;
~Pick()134     virtual ~Pick(){};
135 };
136 
137 //typedef BUFFER<char> Buffer_char;
138 //typedef BUFFER<int> Buffer_int;
139 //typedef BUFFER<Pick *> Buffer_pPick;
140 BUFFER(char, Buffer_char);
141 BUFFER(int, Buffer_int);
142 BUFFER(Pick *, Buffer_pPick);
143 
144 class SymbolTable{
145 /* Helper class.
146 * It's a mapping table between 'variable name' and its 'active Pick object'
147 */
148 private:
149     Buffer_char  name_buffer;   // var names storage space
150 
151     Buffer_int   names;         // points to name (offset in name_buffer)
152     Buffer_pPick refs;          // points to Pick
153 
get_index(const char * const var_name)154     int get_index(const char *const var_name){
155         int len = names.content_size();
156         for (int i=0; i< len; i++){
157             if (strcmp(var_name, name_buffer + names[i]) == 0){
158                 return i;
159             }
160         }
161         return -1;
162     }
163 
164 public:
165     enum RESULT {EMPTY, NO_VAR, NO_REF, HAS_REF};
166 
find(const char * const var_name,Pick ** ref=NULL)167     RESULT find(const char *const var_name /*[in] c-string*/, Pick * * ref = NULL /*[out] Pick* */){
168         if (!var_name) return EMPTY; // NULL name
169 
170         int i = get_index(var_name);
171         if (i == -1){
172             return NO_VAR;   // new name
173         }
174         if (!refs[i]){  // exist name, no ref
175             return NO_REF;
176         } else {
177             if (ref) {
178                 *ref = refs[i];
179             }
180             return HAS_REF;   // exist name, has ref
181         }
182     }
183 
put(const char * const var_name,Pick * const var_ref=NULL)184     void put(const char *const var_name, Pick *const var_ref = NULL){
185         int i = get_index(var_name);
186         switch(find(var_name)){
187             case EMPTY:    // NULL name
188                 break;
189             case NO_VAR:    // new name
190                 int offset;
191                 offset = name_buffer.content_size();
192                 name_buffer.append_array(var_name, strlen(var_name) + 1);
193                 names.append(offset);
194                 refs.append(var_ref);
195                 break;
196             case NO_REF:    // exist name, no ref
197                 refs[i] = var_ref;    // link definition with variable
198                 break;
199             case HAS_REF:    // exist name, has ref
200                 if (var_ref){
201                     refs[i] = var_ref;
202                 }
203                 break;
204             default:
205                 ; // ASSERT(FALSE);
206         }
207         return;
208     }
209 
is_complete()210     UBool is_complete(){
211         int n = names.content_size();
212         for (int i=0; i<n; ++i){
213             if (refs[i] == NULL){
214                 return FALSE;
215             }
216         }
217         return TRUE;
218     }
219 
reset()220     void reset(){
221         names.reset();
222         name_buffer.reset();
223 
224         // release memory here
225         int s = refs.content_size();
226         for (int i=0; i < s; i++){
227             delete refs[i]; // TOFIX: point alias/recursion problem
228         }
229         refs.reset();
230     }
231 
~SymbolTable()232     ~SymbolTable(){
233         reset();
234     }
235 };
236 
237 
238 /*
239 // Document of class Escaper
240 //
241 // ATTENTION:
242 // From http://icu-project.org/userguide/Collate_Customization.html.
243 // We get the precedence of escape/quote operations
244 //
245 //     (highest) 1. backslash               \
246 //               2. two single quotes       ''
247 //               3. quoting                 ' '
248 //
249 // ICU Collation should accept following as the same string.
250 //
251 // 1)  'ab'c        _
252 // 2)  a\bc          \
253 // 3)  a'b'\c        |- They are equal.
254 // 4)  abc          _/
255 //
256 // From "two single quotes", we have following deductions
257 //    D1. empty quoting is illgal. (obviously)
258 //    D2. no contact operation between two quotings
259 //              '.''.'      is not ..   it is .'.
260 //    D3. "two single quotes" cannot contact two quoting simultaneously
261 //              '..''''.'   is not ..'. it is ..''.
262 //       NOTICE:
263 //        "two single quotes" can contact before one quoting
264 //              '''.'       is '.
265 //        "two single quotes" can literally contact after one quoting
266 //        But, from syntax, it's one quoting including a "two single quotes"
267 //              '.'''       is .'
268 //    D4. "two single quotes" cannot solely be included in quoting
269 //              ''''        is not '    it is ''
270 //       NOTICE:  These are legal
271 //              '.''.'      is .'.
272 //              '.'''       is .'
273 //
274 //                 dicision
275 //                    /\
276 //                   /__\
277 //      output buffer    input buffer
278 //
279 // To make our dicision (within an atom operation) without caring input and output buffer,
280 // following calling pattern (within an atom operation) shall be avoided
281 //
282 //    P1 open_quoting()  then close_quoting()    (direct violation)   D1
283 //    P2 close_quoting() then open_quoting()     (direct violation)   D2
284 //    P3 empty open_quoting()                    (indirect violation) D1, D4
285 //    P4 empty close_quoting()                   (indirect violation) D2, D3
286 //    P5 open_quoting()  then two single quotes  (indirect violation) D4
287 //    P6 close_quoting() then two single quotes  (indirect violation) D3
288 //
289 // two single quotes escaping will not open_ or close_ quoting()
290 // The choice will not lose some quoing forms.
291 //
292 // For open_quoting(),
293 // we may get this form quoting     '''         P5
294 // It may raise a bug               ''''x
295 // If we expect
296 //      '''.'       let the next char open the quoting
297 //      '.''.'      the quoting is already opened by preceding char
298 //
299 // For close_quoting()
300 // we will get this form quoting    '.'''       P6
301 // It may raise a bug               '.''''.'
302 // If we expect
303 //      '.'''\.     let the next char close the quoting
304 //      '.''''.'    the expectation is wrong!  using  '.'\''.' instead
305 //
306 // It's a hard work to re-adjust generation opportunity for various escaping form.
307 // We just simply ignore it.
308 */
309 class Escaper{
310 public:
311     enum CHOICE {YES, NO, RAND};
312     enum ESCAPE_FORM {BSLASH_ONLY, QUOTE_ONLY, QUOTE_AND_BSLAH, RAND_ESC};
313 private:
314     class Bool{ // A wrapper class for CHOICE, to auto adapter UBool class
315         private:
316             const CHOICE tag;
317         public:
Bool(CHOICE flag=RAND)318             Bool(CHOICE flag=RAND):tag(flag){}
operator UBool()319             operator UBool() {   // conversion operator
320                 return tag == RAND ? rand()%2 : tag == YES;
321                 //if (tag == RAND){
322                 //    return rand()%2 == 1;
323                 //} else {
324                 //    return tag == YES ? TRUE : FALSE;
325                 //}
326             }
327     };
328 public:
Escaper(CHOICE escapeLiteral=RAND,CHOICE twoQuotesEscape=RAND,ESCAPE_FORM escapeForm=RAND_ESC)329     Escaper(CHOICE escapeLiteral = RAND,
330         CHOICE twoQuotesEscape = RAND,
331         ESCAPE_FORM escapeForm = RAND_ESC):
332         escape_form(escapeForm),
333         escape_literal(escapeLiteral),
334         two_quotes_escape(twoQuotesEscape),
335         is_quoting(FALSE){}
336 private:
337     Buffer_char str;
338     ESCAPE_FORM escape_form;
339     Bool escape_literal;
340     Bool two_quotes_escape;
341     UBool quote_escape;
342     UBool bslash_escape;
343     UBool is_quoting;
344 
set_options()345     void set_options(){
346         ESCAPE_FORM t = escape_form == RAND_ESC ? (ESCAPE_FORM) (rand()%3) : escape_form;
347         switch (t){
348                 case BSLASH_ONLY :
349                     bslash_escape = TRUE; quote_escape = FALSE; break;
350                 case QUOTE_ONLY:
351                     bslash_escape = FALSE;quote_escape = TRUE;  break;
352                 case QUOTE_AND_BSLAH:
353                     bslash_escape = TRUE; quote_escape = TRUE;  break;
354                 default:
355                     ;// error
356         }
357     }
358 
reset()359     void reset(){
360         str.reset();
361         is_quoting = FALSE;
362     }
363 
open_quoting()364     inline void open_quoting(){
365         if(is_quoting){
366             // do nothing
367         } else {
368             str.append('\'');
369             is_quoting = TRUE;
370         }
371     }
close_quoting()372     inline void close_quoting(){
373         if(is_quoting){
374             str.append('\'');
375             is_quoting = FALSE;
376         } else {
377             // do nothing
378         }
379     }
380 
381     // str  [in]    null-terminated c-string
append(const char * strToAppend)382     void append(const char * strToAppend){
383         for(;*strToAppend != 0; strToAppend++){
384             append(*strToAppend);
385         }
386     }
387 
append(const char c)388     inline void append(const char c){
389         set_options();
390 
391         if (c == '\\'){
392             quote_escape ? open_quoting() : close_quoting();
393             //bslash_escape always true here
394             str.append('\\');
395             str.append('\\');
396         } else if (c == '\''){
397             if (two_quotes_escape){     // quoted using two single quotes
398                 // See documents in anonymous.design
399                 str.append('\'');
400                 str.append('\'');
401             } else{
402                 quote_escape ? open_quoting() : close_quoting();
403                 //bslash_escape always true here
404                 str.append('\\');
405                 str.append('\'');
406             }
407         } else if (isSpecialAsciiChar(c) || isWhiteSpace(c)){
408             quote_escape  ? open_quoting()   : close_quoting();
409             if (bslash_escape) str.append('\\');
410             str.append(c);
411         } else { //if (isAlphabet(c) || isDigit(c) || TRUE){ // treat others as literal
412             if (escape_literal){
413                 quote_escape  ? open_quoting()   : close_quoting();
414                 if (bslash_escape)  str.append('\\');
415                 str.append(c);
416             } else {
417                 close_quoting();
418                 str.append(c);
419             }
420         }
421     }
422 
423 public:
424     // Return a null-terminate c-string. The buffer is owned by callee.
operator ()(const char * literal)425     char * operator()(const char * literal /*c-string*/){
426         str.reset();
427         for(;*literal != 0; literal++){
428             append(*literal);
429         }
430         close_quoting();    // P4 exception, to close whole quoting
431         return str;
432     }
433 };
434 
435 class WeightedRand{
436 // Return a random number in [0, size)
437 // Every number has different chance (aka weight) to be selected.
438 private:
439     Buffer_int weights;
440     double total;
441     WeightedRand(const WeightedRand &);
442     WeightedRand & operator = (const WeightedRand &);
443 public:
WeightedRand(Buffer_int * weight_list=NULL,int size=0)444     WeightedRand(Buffer_int * weight_list = NULL, int size = 0){
445         if ( weight_list == NULL){
446             for (int i=0; i<size; ++i) weights.append(DEFAULT_WEIGHT);
447         } else {
448             int s = weight_list->content_size();
449             if (s < size){
450                 weights.append_array( (*weight_list),s);
451                 for (int i=s; i<size; ++i) weights.append(DEFAULT_WEIGHT);
452             } else { // s >= size
453                 weights.append_array( (*weight_list),size);
454             }
455         }
456         total = 0;
457         int c = weights.content_size();
458         for (int i=0; i<c; ++i){
459             total += weights[i];
460         }
461     }
462 
append(int weight)463     void append(int weight){
464         weights.append(weight);
465         total += weight;
466     }
467 
468     // Give a random number with the consideration of weight.
469     // Every random number is associated with a weight.
470     // It identifies the chance to be selected,
471     // larger weight has more chance to be selected.
472     //
473     //
474     //  ______________________   every slot has equal chance
475     //
476     //  [____][_][___][______]   each item has different slots, hence different chance
477     //
478     //
479     //  The algorithms to generate the number is illustrated by preceding figure.
480     //  First, a slot is selected by rand(). Then we translate the slot to corresponding item.
481     //
next()482     int next(){
483         // get a random in [0,1]
484         double reference_mark = (double)rand() / (double)RAND_MAX;
485 
486         // get the slot's index, 0 <= mark <= total;
487         double mark = total * reference_mark;
488 
489         // translate the slot to corresponding item
490         int i=0;
491         for (;;){
492             mark -= weights[i];  // 0 <= mark <= total
493             if (mark <= 0)
494                 break;
495             i++;
496         }
497         return i;
498     }
499 };
500 
501 ///////////////////////////////////////////////////////////
502 //
503 // The parser result nodes
504 //
505 
506 class Literal : public Pick {
507 public:
next()508     virtual const char* next(){
509         return str;
510     }
Literal(const char * s)511     Literal(const char * s /*c-string*/){
512         str.append_array(s, strlen(s) + 1);
513     }
514 private:
515     Buffer_char str; //null-terminated c-string
516 };
517 
518 class Variable : public Pick {
519 public:
Variable(SymbolTable * symbols,const char * varName,Pick * varRef=NULL)520     Variable(SymbolTable * symbols, const char * varName, Pick * varRef = NULL){
521         this->var_name.append_array(varName, strlen(varName) + 1);
522         if ((symbol_table = symbols)){
523             symbol_table->put(varName, varRef);
524         }
525     }
526 
operator const char*()527     operator const char *(){
528         return var_name;
529     }
530 
next()531     virtual const char* next(){
532         if (symbol_table){
533             Pick * var_ref = NULL;
534             symbol_table->find(var_name, &var_ref);
535             if (var_ref) {
536                 return var_ref->next();
537             }
538         }
539         return "";  // dumb string
540     }
541 private:
542     Buffer_char var_name;
543     SymbolTable * symbol_table;
544 };
545 
546 class Quote : public Pick{
547 public:
Quote(Pick & base)548     Quote(Pick & base):item(base),e(Escaper::NO, Escaper::NO, Escaper::BSLASH_ONLY){
549     }
next()550     virtual const char* next(){
551         return e(item.next());
552     }
553 private:
554     Pick & item;
555     Buffer_char str;
556     Escaper e;
557 };
558 
559 
560 class Morph : public Pick{
561 /*
562 The difference between morph and an arbitrary random string is that
563 a morph changes slowly. When we build collation rules, for example,
564 it is a much better test if the strings we use are all in the same
565 'neighborhood'; they share many common characters.
566 */
567 public:
Morph(Pick & base)568     Morph(Pick & base):item(base){}
569 
next()570     virtual const char* next(){
571         current.reset();
572         const char * s = item.next();
573         current.append_array(s, strlen(s) + 1);
574         if  (last.content_size() == 0) {
575             str.reset();
576             last.reset();
577             str.append_array(current, current.content_size());
578             last.append_array(current, current.content_size());
579         } else {
580             morph();
581         }
582         return str;
583     }
584 private:
585     Pick & item;
586     Buffer_char str;
587     Buffer_char last;
588     Buffer_char current;
589 
590     char * p_last;
591     char * p_curr;
592 
copy_curr()593     void copy_curr(){
594         if (*p_curr) {
595             str.append(*p_curr);
596             p_curr++;
597         }
598     }
599 
copy_last()600     void copy_last(){
601         if (*p_last) {
602             str.append(*p_last);
603             p_last++;
604         }
605     }
606 
607     // copy 0, 1, or 2 character(s) to str
copy()608     void copy(){
609         static WeightedRand wr(& Buffer_int().append(DEFAULT_WEIGHT * 10), 5);
610 
611         switch (wr.next()){
612             case 0: // copy last  -- has 10 times chance than others
613                 copy_last();
614                 break;
615             case 1: // copy both
616                 copy_curr();
617                 copy_last();
618                 break;
619             case 2: // copy both
620                 copy_last();
621                 copy_curr();
622                 break;
623             case 3:
624                 copy_curr();
625                 break;
626             case 4:  // copy nothing
627                 break;
628             default:
629                 // ASSERT(FALSE);
630                 ;
631         }
632     }
633 
morph(void)634     void morph(void){
635         int min = strlen(last);
636         int max = strlen(current);
637         if (min > max){
638             int temp  = min;
639             min = max;
640             max = temp;
641         }
642 
643         int len = min + rand()%(max - min + 1); // min + [0, diff]
644         p_curr = current;
645         p_last = last;
646         str.reset();
647 
648         for (; str.content_size()<len && *p_curr && *p_last;){
649             copy(); // copy 0, 1, or 2 character(s) to str
650         }
651 
652         if (str.content_size() == len) {
653             str.append(0);
654             final();
655             return;
656         }
657 
658         if (str.content_size() > len) { // if the last copy copied two characters
659             str[len]=0;
660             final();
661             return;
662         }
663 
664         // str.content_size() < len
665         if (*p_last) {
666             for (; str.content_size() < len; copy_last());
667         } else if (*p_curr){
668             for (; str.content_size() < len; copy_curr());
669         }
670 
671         int last_len = last.content_size();
672         for (;str.content_size() < len;){
673             str.append(last[rand()%last_len]);
674         }
675         str.append(0);
676         final();
677     }
678 
final()679     void final(){
680         last.reset();
681         last.append_array(current, current.content_size());
682     }
683 };
684 
685 class Sequence : public Pick {
686 public:
next()687     virtual const char* next(){
688         str.reset();
689         int s = items.content_size();
690         for(int i=0; i < s; i++){
691             const char * t = items[i]->next();
692             str.append_array(t, strlen(t));
693         }
694         str.append(0); // terminal null
695         return str;
696     }
697 
append(Pick * node)698     void append (Pick * node){
699         items.append(node);
700     }
701 
~Sequence()702     virtual ~Sequence(){
703         int s = items.content_size();
704         for(int i=0; i < s; i++){
705             //How can assure the item is got from heap?
706             //Let's assume it.
707             delete items[i]; // TOFIX: point alias/recursion problem
708             items[i] = NULL;
709         }
710     }
711 private:
712     Buffer_pPick items;
713     Buffer_char  str; //null-terminated c-string
714 };
715 
716 class Repeat : public Pick {
717 private:
718     Pick * item;
719     Buffer_char str;
720     WeightedRand wr;
721     int min;
722     int max;
select_a_count()723     int select_a_count(){
724         return min + wr.next();
725     }
726 public:
next()727     virtual const char* next(){
728         str.reset();
729         int c = select_a_count();
730         for(int i=0; i< c; i++){
731             const char * t = item->next();
732             str.append_array(t, strlen(t));
733         }
734         str.append(0);
735         return str;
736     }
737 
Repeat(Pick * base,int minCount=0,int maxCount=1,Buffer_int * weights=NULL)738     Repeat(Pick * base, int minCount =0, int maxCount = 1, Buffer_int * weights = NULL):
739         wr(weights, maxCount-minCount +1) {
740         this->item = base;
741         this->min = minCount;
742         this->max = maxCount;
743     }
~Repeat()744     virtual ~Repeat(){
745         delete item;  // TOFIX: point alias/recursion problem
746         item = NULL;
747     }
748 };
749 
750 
751 class Alternation : public Pick {
752 public:
next()753     virtual const char* next(){
754         str.reset();
755         int i = wr.next();
756         const char * t = items[i]->next();
757         str.append_array(t, strlen(t) + 1);
758         return str;
759     }
~Alternation()760     virtual ~Alternation(){
761         int s = items.content_size();
762         for(int i=0; i < s; i++){
763             delete items[i];  // TOFIX: point alias/recursion problem
764             items[i] = NULL;
765         }
766     }
767 
append(Pick * node,int weight=DEFAULT_WEIGHT)768     Alternation & append (Pick * node, int weight = DEFAULT_WEIGHT){
769         items.append(node);
770         wr.append(weight);
771         return *this;
772     }
773 private:
774     Buffer_pPick items;
775     Buffer_char str; // null-terminated c-string
776     WeightedRand wr;
777 };
778 
779 ///////////////////////////////////////////////////////////
780 //
781 // The parser
782 //
783 
784 enum TokenType {STRING, VAR, NUMBER, STREAM_END, ERROR, QUESTION, STAR, PLUS, LBRACE, RBRACE, LPAR, RPAR, SEMI, EQ, COMMA, BAR, AT, WAVE, PERCENT};
785 
786 class Scanner{
787 friend int DumpScanner(Scanner & s, UBool dumb);
788 private:
789     const char * source;
790     const char * working;
791     const char * history; // for debug
792     enum StateType {START, IN_NUM, IN_VAR_FIRST, IN_VAR, IN_QUOTE, IN_QUOTE_BSLASH, IN_BSLASH, IN_STRING, DONE};
793     StateType state;
terminated(TokenType t)794     void terminated(TokenType t){
795         working--;       // return the peeked character
796         tokenType = t;
797         token.append(0); // close buffer
798         state = DONE;
799     }
800 public:
801     // the buffer of "source" is owned by caller
Scanner(const char * src=NULL)802     Scanner(const char *src/*[in] c-string*/ = NULL):source(src){
803         working = src;
804         history = working;
805         state = DONE;
806         tokenType = ERROR;
807     }
808 
809     //void setSource(const char *const src /*[in] c-string*/){
810     //    *(&const_cast<const char *>(source)) = src;
811     //}
812 
813     Buffer_char token;
814     TokenType tokenType;
815 
getNextToken()816     TokenType getNextToken(){
817         token.reset();
818         state = START;
819         history = working; // for debug
820         while (state != DONE){
821             char c = *working++;
822             if (c == 0 && state != START){//avoid buffer overflow. for IN_QUOE, IN_ESCAPE
823                 terminated(ERROR);
824                 break; // while
825             }
826             switch(state){
827                 case START:
828                     tokenType = ERROR;
829                     switch(c){
830                         case '?'  : tokenType = QUESTION; break;
831                         case '*'  : tokenType = STAR; break;
832                         case '+'  : tokenType = PLUS; break;
833                         case '{'  : tokenType = LBRACE; break;
834                         case '}'  : tokenType = RBRACE; break;
835                         case '('  : tokenType = LPAR; break;
836                         case ')'  : tokenType = RPAR; break;
837                         case ';'  : tokenType = SEMI; break;
838                         case '='  : tokenType = EQ; break;
839                         case ','  : tokenType = COMMA; break;
840                         case '|'  : tokenType = BAR; break;
841                         case '@'  : tokenType = AT; break;
842                         case '~'  : tokenType = WAVE; break;
843                         case '%'  : tokenType = PERCENT; break;
844                         case 0    : tokenType = STREAM_END; working-- /*avoid buffer overflow*/; break;
845                     }
846                     if (tokenType != ERROR){
847                         token.append(c);
848                         token.append(0);
849                         state = DONE;
850                         break; // START
851                     }
852                     switch(c){
853                         case '$'  : state = IN_VAR_FIRST; token.append(c); break;
854                         case '\'' : state = IN_QUOTE;     break;
855                         case '\\' : state = IN_BSLASH;    break;
856                         default:
857                             if (isWhiteSpace(c)){    // state = START;   //do nothing
858                             } else if (isDigit(c)){     state = IN_NUM;    token.append(c);
859                             } else if (isAlphabet(c)){  state = IN_STRING; token.append(c);
860                             } else {terminated(ERROR);}
861                     }
862                     break;//START
863                 case IN_NUM:
864                     if (isDigit(c)){
865                         token.append(c);
866                     } else {
867                         terminated(NUMBER);
868                     }
869                     break;//IN_NUM
870                 case IN_VAR_FIRST:
871                     if (isAlphabet(c)){
872                         token.append(c);
873                         state = IN_VAR;
874                     } else {
875                         terminated(ERROR);
876                     }
877                     break; // IN_VAR_FISRT
878                 case IN_VAR:
879                     if (isAlphabet(c) || isDigit(c)){
880                         token.append(c);
881                     } else {
882                         terminated(VAR);
883                     }
884                     break;//IN_VAR
885                 case IN_STRING:
886                     // About the scanner's behavior for STRING, AT, and ESCAPE:
887                     // All of them can be contacted with each other.
888                     // This means the scanner will eat up as much as possible strings
889                     //   (STRING, AT, and ESCAPE) at one time, with no regard of their
890                     //   combining sequence.
891                     //
892                     if (c == '\''){
893                         state = IN_QUOTE; // the first time we see single quote
894                     } else if (c =='\\'){ // back slash character
895                         state = IN_BSLASH;
896                     } else if (isAlphabet(c) || isDigit(c)){
897                         token.append(c);
898                     } else{
899                         terminated(STRING);
900                     }
901                     break;//IN_STRING
902                 case IN_QUOTE:
903                     if (c == '\''){ // the second time we see single quote
904                         state = IN_STRING; // see document in IN_STRING
905                     } else if ( c== '\\') { // backslah escape in quote
906                         state = IN_QUOTE_BSLASH;
907                     } else {
908                         token.append(c);  // eat up everything, includes back slash
909                     }
910                     break;//IN_QUOTE
911                 case IN_QUOTE_BSLASH:
912                 case IN_BSLASH:
913                     switch (c){
914                         case 'n'  : token.append('\n'); break;
915                         case 'r'  : token.append('\r'); break;
916                         case 't'  : token.append('\t'); break;
917                         case '\'' : token.append('\''); break;
918                         case '\\' : token.append('\\'); break;
919                         default: token.append(c); // unknown escaping, treat it as literal
920                     }
921                     if (state == IN_BSLASH){
922                         state = IN_STRING; // see document in IN_STRING
923                     } else { // state == IN_QUOTE_BSLASH
924                         state = IN_QUOTE;
925                     }
926                     break;//IN_BSLASH
927                 case DONE:  /* should never happen */
928                 default:
929                     working--;
930                     tokenType = ERROR;
931                     state = DONE;
932                     break;
933             }//switch(state)
934         }//while (state != DONE)
935 
936         return tokenType;
937     }
938 };//class Scanner
939 
940 class Parser{
941 friend UBool TestParser();
942 friend class TestParserT;
943 friend class LanguageGenerator_impl;
944 private:
945     Scanner s;
946     TokenType & token;
947     int min_max;   // for the evil infinite
948 
match(TokenType expected)949     UBool match(TokenType expected){
950         if (token == expected) {
951             token = s.getNextToken();
952             return TRUE;
953         } else {
954             //s.dumpCurrentPoint();
955             return FALSE;
956         }
957     }
958 
weight(int & value)959     UBool weight(int & value){
960         if (token == NUMBER){
961             int temp = atoi(s.token);
962             match(NUMBER);
963             if (match(PERCENT)){
964                 value = temp;
965                 return TRUE;
966             }
967         }
968         return FALSE;
969     }
970 
repeat(Pick * & node)971     UBool repeat (Pick* &node /*in,out*/){
972         if (node == NULL) return FALSE;
973 
974         int count = -2;
975         int min = -2;
976         int max = -2;
977         UBool question = FALSE;
978         switch (token){
979             case QUESTION:
980                 match(QUESTION);
981                 min = 0;
982                 max = 1;
983                 count = 2;
984                 question = TRUE;
985                 break;
986             case STAR:
987                 match(STAR);
988                 min = 0;
989                 max = -1;
990                 count = -1;
991                 break;
992             case PLUS:
993                 match(PLUS);
994                 min = 1;
995                 max = -1;
996                 count = -1;
997                 break;
998             case LBRACE:
999                 match(LBRACE);
1000                 if (token != NUMBER){
1001                     return FALSE;
1002                 }else {
1003                     min = atoi(s.token);
1004                     match(NUMBER);
1005                     if (token == RBRACE){
1006                         match(RBRACE);
1007                         max = min;
1008                         count = 1;
1009                     } else if (token == COMMA) {
1010                         match(COMMA);
1011                         if (token == RBRACE){
1012                             match(RBRACE);
1013                             max = -1;
1014                             count = -1;
1015                         } else if (token == NUMBER) {
1016                             max = atoi(s.token);
1017                             match(NUMBER);
1018                             count = max - min + 1;
1019                             if (!match(RBRACE)) {
1020                                 return FALSE;
1021                             }
1022                         } else {
1023                             return FALSE;
1024                         }
1025                     } else {
1026                         return FALSE;
1027                     }
1028                 }
1029                 break;
1030             default:
1031                 return FALSE;
1032         }
1033 
1034         if (count == -2 || min == -2 || max == -2){
1035             //ASSERT(FALSE);
1036             return FALSE;
1037         }
1038 
1039         // eat up following weights
1040         Buffer_int weights;
1041         int w;
1042         while (weight(w)){
1043             weights.append(w);
1044         }
1045 
1046         // for the evil infinite
1047         min_max = min_max > min ? min_max : min;
1048         min_max = min_max > max ? min_max : max;
1049         if (min_max > PSEUDO_INFINIT){
1050             return FALSE; // PSEUDO_INFINIT is less than the real maximum
1051         }
1052         if (max == -1){ // the evil infinite
1053             max = PSEUDO_INFINIT;
1054         }
1055         // for the strange question mark
1056         if (question && weights.content_size() > 0){
1057             Buffer_int w2;
1058             w2.append(DEFAULT_WEIGHT - weights[0]).append(weights[0]);
1059             node = new Repeat(node,min,max,&w2);
1060             return TRUE;
1061         }
1062         node = new Repeat(node,min,max,&weights);
1063         return TRUE;
1064     }
1065 
core(Pick * & node)1066     UBool core(Pick* &node /*out*/){
1067         if (node != NULL) return FALSE; //assert node == NULL
1068 
1069         switch(token){
1070             case LPAR:
1071                 match(LPAR);
1072                 if(defination(node) && match(RPAR)){
1073                     return TRUE;
1074                 }
1075                 return FALSE;
1076             case VAR:
1077                 node = new Variable(&symbols, s.token);
1078                 match(VAR);
1079                 return TRUE;
1080             case STRING:
1081                 node = new Literal(s.token);
1082                 match(STRING);
1083                 return TRUE;
1084             default:
1085                 return FALSE;
1086         }
1087     }
modified(Pick * & node)1088     UBool modified(Pick* &node /*out*/){
1089         if (node != NULL) return FALSE; //assert node == NULL
1090 
1091         if (!core(node)) {
1092             return FALSE;
1093         }
1094 
1095         for (;;){
1096             switch(token){
1097                 case WAVE:
1098                     match(WAVE);
1099                     node = new Morph(*node);
1100                     break;
1101                 case AT:
1102                     match(AT);
1103                     node = new Quote(*node);
1104                     break;
1105                 case QUESTION:
1106                 case STAR:
1107                 case PLUS:
1108                 case LBRACE:
1109                     if (!repeat(node)) return FALSE;
1110                     break;
1111                 case SEMI:      // rule definiation closed
1112                 case RPAR:      // within parenthesis (core closed)
1113                 case BAR:       // in alternation
1114                 case NUMBER:    // in alternation, with weight
1115                 case LPAR:      // in sequence
1116                 case VAR:       // in sequence
1117                 case STRING:    // in sequence
1118                     return TRUE;
1119                 default:
1120                     return FALSE;
1121             }
1122         }
1123     }
1124 
1125 
sequence_list(Pick * & node)1126     UBool sequence_list(Pick* &node /*in,out*/){
1127         if (node == NULL) return FALSE; // assert node != NULL
1128 
1129         Sequence* seq = new Sequence();
1130         Pick * n = node;
1131 
1132         while (token == VAR || token == STRING || token == LPAR){
1133             seq->append(n);
1134             n = NULL;
1135             if (modified(n)){
1136                 // go on
1137             } else {
1138                 goto FAIL;
1139             }
1140         }
1141 
1142         if (token == SEMI || token == RPAR || token == BAR){
1143             seq->append(n);
1144             node = seq;
1145             return TRUE;
1146         }
1147 FAIL:
1148         delete seq;
1149         return FALSE;
1150 
1151     }
1152 
sequence(Pick * & node)1153     UBool sequence(Pick* &node /*out*/){
1154         if (node != NULL) return FALSE; //assert node == NULL
1155 
1156         if (!modified(node)) {
1157             return FALSE;
1158         }
1159 
1160         if (token == VAR || token == STRING || token == LPAR){
1161             return sequence_list(node);
1162         } else {
1163             return TRUE; // just a modified
1164         }
1165     }
1166 
alternation_list(Pick * & node)1167     UBool alternation_list(Pick* &node /*in,out*/){
1168         if (node == NULL) return FALSE; // assert node != NULL
1169 
1170         Alternation * alt = new Alternation();
1171         Pick * n = node;
1172         int w = DEFAULT_WEIGHT;
1173 
1174         while (token == NUMBER || token == BAR){
1175             if(token == NUMBER) {
1176                 if (weight(w)){
1177                     if (token == BAR){
1178                         // the middle item, go on
1179                     } else {
1180                         // the last item or encounter error
1181                         break; //while
1182                     }
1183                 } else {
1184                     goto FAIL;
1185                 }
1186             } // else token == BAR
1187             match(BAR);
1188             alt->append(n,w);
1189 
1190             n = NULL;
1191             w = DEFAULT_WEIGHT;
1192             if (sequence(n)){
1193                 // go on
1194             } else {
1195                 goto FAIL;
1196             }
1197         }
1198 
1199         if (token == SEMI || token == RPAR) {
1200             alt->append(n,w);
1201             node = alt;
1202             return TRUE;
1203         }
1204 FAIL:
1205         delete alt;
1206         return FALSE;
1207     }
1208 
alternation(Pick * & node)1209     UBool alternation(Pick* &node /*out*/){
1210         if (node != NULL) return FALSE; //assert node == NULL
1211 
1212         // 'sequence' has higher precedence than 'alternation'
1213         if (!sequence(node)){
1214             return FALSE;
1215         }
1216 
1217         if (token == BAR || token == NUMBER){ // find a real alternation1, create it.
1218             return alternation_list(node);
1219         } else {
1220             return TRUE;    // just a sequence_old
1221         }
1222     }
1223 
1224 
defination(Pick * & node)1225     UBool defination(Pick* &node /*out*/){
1226         if (node != NULL) return FALSE; //assert node == NULL
1227         return alternation(node);
1228     }
1229 
rule()1230     UBool rule(){
1231         if (token == VAR){
1232             Buffer_char name;
1233             name.append_array(s.token, strlen(s.token) + 1);
1234             match(VAR);
1235 
1236             if (match(EQ)){
1237                 Pick * t = NULL;
1238                 if(defination(t)){
1239                     symbols.put(name, t);
1240                     return match(SEMI);
1241                 }
1242             }
1243         }
1244         return FALSE;
1245     }
1246 public:
rules()1247     UBool rules(){
1248         symbols.reset();
1249         token = s.getNextToken();
1250         while (rule()){
1251         }
1252         if (token == STREAM_END){
1253             return TRUE;
1254         } else {
1255             //s.dumpCurrentPoint();
1256             return FALSE;
1257         }
1258     }
1259 
1260 public:
1261     SymbolTable symbols;
1262 
Parser(const char * const source)1263     Parser(const char *const source):s(source), token(s.tokenType){
1264         min_max = -2;
1265     }
parse()1266     UBool parse(){
1267         return rules();
1268     }
1269 
1270 }; // class Parser
1271 
1272 
1273 ///////////////////////////////////////////////////////////
1274 //
1275 //
1276 //
1277 
DumpScanner(Scanner & s,UBool dump=TRUE)1278 int DumpScanner(Scanner & s, UBool dump = TRUE){
1279     int len = strlen(s.source);
1280     int error_start_offset = s.history - s.source;
1281     if (dump){
1282         printf("\n=================== DumpScanner ================\n");
1283         fwrite(s.source, len, 1, stdout);
1284         printf("\n-----parsed-------------------------------------\n");
1285         fwrite(s.source, s.history - s.source, 1, stdout);
1286         printf("\n-----current------------------------------------\n");
1287         fwrite(s.history, s.working - s.history, 1, stdout);
1288         printf("\n-----unparsed-----------------------------------\n");
1289         fwrite(s.working, (s.source + len - s.working), 1, stdout);
1290         printf("\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
1291     }
1292     return error_start_offset;
1293 }
1294 
1295 class LanguageGenerator_impl{
1296 public:
LanguageGenerator_impl(const char * const bnf_definition,const char * const top_node)1297     LanguageGenerator_impl(const char *const bnf_definition, const char *const top_node)
1298         :par(bnf_definition), top_node_name(top_node){
1299         srand((unsigned)time( NULL ));
1300     }
1301 
parseBNF(UBool debug=TRUE)1302     LanguageGenerator::PARSE_RESULT parseBNF(UBool debug = TRUE){
1303         if (par.parse()){
1304             if (par.symbols.find(top_node_name, &top_node_ref) == SymbolTable::HAS_REF) {
1305                 if (par.symbols.is_complete()) {
1306                     return LanguageGenerator::OK;
1307                 } else {
1308                     if (debug) printf("The bnf definition is incomplete.\n");
1309                     return LanguageGenerator::INCOMPLETE;
1310                 }
1311             } else {
1312                 if (debug) printf("No top node is found.\n");
1313                 return LanguageGenerator::NO_TOP_NODE;
1314             }
1315         } else {
1316             if(debug) {
1317                 printf("The bnf definition is wrong\n");
1318                 DumpScanner(par.s, TRUE);
1319             }
1320             return LanguageGenerator::BNF_DEF_WRONG;
1321         }
1322     }
next()1323     const char * next(){
1324         return top_node_ref->next();
1325     }
1326 
1327 private:
1328     Parser par;
1329     const char *const top_node_name;
1330     Pick * top_node_ref;
1331 };
1332 
LanguageGenerator()1333 LanguageGenerator::LanguageGenerator():lang_gen(NULL){
1334 }
1335 
~LanguageGenerator()1336 LanguageGenerator::~LanguageGenerator(){
1337     delete lang_gen;
1338 }
1339 
parseBNF(const char * const bnf_definition,const char * const top_node,UBool debug)1340 LanguageGenerator::PARSE_RESULT LanguageGenerator::parseBNF(const char *const bnf_definition /*in*/, const char *const top_node/*in*/, UBool debug){
1341     if (lang_gen){
1342         delete lang_gen;
1343     }
1344     lang_gen = new LanguageGenerator_impl(bnf_definition, top_node);
1345     PARSE_RESULT r = lang_gen->parseBNF(debug);
1346     if (r != OK){
1347         delete lang_gen;
1348         lang_gen = NULL;
1349         return r;
1350     } else {
1351         return r;
1352     }
1353 }
next()1354 const char *LanguageGenerator::next(){ // Return a null-terminated c-string. The buffer is owned by callee.
1355     if (lang_gen){
1356         return lang_gen->next();
1357     }else {
1358         return "";
1359     }
1360 }
1361 
1362 ///////////////////////////////////////////////////////////
1363 //
1364 // The test code for WBNF
1365 //
1366 
1367 #define CALL(fun) \
1368     if (fun()){ \
1369         printf("Pass: " #fun "\n");\
1370     } else { \
1371         printf("FAILED: !!! " #fun " !!!\n"); \
1372     }
1373 
1374 #define DUMP_R(fun, var, times) \
1375     {printf("\n========= " #fun " =============\n"); \
1376     for (int i=0; i<times; i++) { \
1377         const char * t = var.next();\
1378         fwrite(t,strlen(t),1,stdout); \
1379         printf("\n");   \
1380     }   \
1381     printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");}
1382 
1383 
1384 
1385 #if TEST_WBNF_TEST
TestQuote()1386 static UBool TestQuote(){
1387     const char *const str = "This ' A !,z| qq [] .new\tline";
1388     //const char *const str_r = "This \\' A '!,'z'|' qq '[]' '.'new\tline";
1389     ////
1390     //// :(  we must quote our string to following C syntax
1391     ////     cannot type the literal here, it makes our code rather human unreadable
1392     ////     very very unconformable!
1393     ////
1394     ///*
1395     //*/
1396 
1397     //const char *const s1    =   "ab'c";
1398     //const char (* s1_r1) [] = { "ab''c",    // ab''c
1399     //                            "ab\\'c",   // ab\'c
1400     //                           };//
1401     ///*
1402     // .      '.'     \.
1403     // ..     \.\.    '.'\.   '.'\.   '..'    // '.''.'  wrong
1404     //*/
1405 
1406     //const char *const s2    =   "a..'.b";       // a..'.b
1407     //const char (*s2_r) []   = { "a'..''.'b"     // a'..''.'b
1408     //                           ,"a'..\\'.'b"    // a'..\'.'b
1409     //                           ,"a'..'\\''.'b"  // a'..'\''.'b
1410     //                          };//
1411 
1412     //const char *const s3    =   "a..\\.b";      // a..\.b
1413     //const char (*s3_r) []   = { "a'..\\\\.'b"   // a'..\\.'b
1414     //                           ,"a'..'\\\\'.'b" // a'..'\\'.'b
1415     //                          };//
1416 
1417     //                            // no catact operation, no choice, must be compact
1418 
1419     srand((unsigned)time( NULL ));
1420 
1421     //Escaper l(Escaper::NO, Escaper::NO, Escaper::RAND_ESC);
1422     Pick *p = new Literal(str);
1423     Quote q(*p);
1424 
1425     DUMP_R(TestQuote, (*p), 1);
1426     DUMP_R(TestQuote, q, 20);
1427     return FALSE;
1428 }
TestLiteral()1429 static UBool TestLiteral(){
1430     const char * s = "test string99.";
1431     Literal n(s);
1432     const char * r = n.next();
1433     return strcmp(s,r) == 0;
1434 }
1435 
TestSequence()1436 static UBool TestSequence(){
1437     Sequence seq;
1438     seq.append(new Literal("abc "));
1439     seq.append(new Literal(", s"));
1440 
1441     return strcmp(seq.next(), "abc , s") == 0;
1442 }
TestAlternation()1443 static UBool TestAlternation(){
1444     srand((unsigned)time( NULL ));
1445     Alternation alt;
1446     alt.append(new Literal("aaa_10%"),10);
1447     alt.append(new Literal("bbb_0%"),0);
1448     alt.append(new Literal("ccc_10%"),10);
1449     alt.append(new Literal("ddddddd_50%"),50);
1450 
1451     DUMP_R(TestAlternation, alt, 50);
1452 
1453     return FALSE;
1454 }
1455 
TestBuffer()1456 static UBool TestBuffer(){
1457     Buffer_int t;
1458     t.append(1).append(0).append(5);
1459     int s = t.content_size();
1460     for (int i=0; i<s; ++i){
1461         printf("%d\n", t[i]);
1462     }
1463     return FALSE;
1464 }
1465 
TestWeightedRand()1466 static UBool TestWeightedRand(){
1467     srand((unsigned)time( NULL ));
1468     Buffer_int t;
1469     t.append(1).append(0).append(5);
1470     WeightedRand wr(&Buffer_int().append(10).append(0).append(50),4);
1471 //    WeightedRand wr(&t,3);
1472     for (int i=0; i< 50; ++i){
1473         printf("%d\n", wr.next());
1474     }
1475     return FALSE;
1476 }
1477 
TestRepeat()1478 static UBool TestRepeat(){
1479     srand((unsigned)time( NULL ));
1480     Repeat rep(new Literal("aaa1-5 "), 1, 5);
1481     DUMP_R(TestRepeat, rep, 50);
1482 
1483     Repeat r2(new Literal("b{1,3}1%0%5% "), 1, 3, &Buffer_int().append(1).append(0).append(5));
1484     DUMP_R(TestRepeat, r2, 50);
1485 
1486     Repeat r3(new Literal("aaa5-5 "), 5, 5);
1487     DUMP_R(TestRepeat, r3, 50);
1488 
1489     return FALSE;
1490 }
1491 
TestVariable()1492 static UBool TestVariable(){
1493     SymbolTable tab;
1494     Pick * value = new Literal("string1");
1495     Variable var1(&tab, "x", value);
1496 
1497     Variable var2(&tab, "y");
1498 //    tab.put(var2, value); // TOFIX: point alias/recursion problem
1499     Pick * value2 = new Literal("string2");
1500     tab.put(var2, value2);
1501 
1502     Pick * value3 = new Literal("string3");
1503     Variable var3(&tab, "z");
1504     tab.put("z", value3);
1505 
1506     UBool pass;
1507     pass = strcmp(var1.next(), value->next()) == 0;
1508     pass = pass && strcmp(var2.next(), value2->next()) == 0;
1509     pass = pass && strcmp(var3.next(), value3->next()) == 0;
1510     return pass;
1511 }
1512 
TestSymbolTable()1513 static UBool TestSymbolTable(){
1514     Literal * n1 = new Literal("string1");
1515     Literal * n2 = new Literal("string2");
1516     SymbolTable t;
1517     t.put("abc", n1);
1518     t.put("$aaa", n2);
1519 //    t.put("alias", n1);  // TOFIX: point alias/recursion problem
1520     t.put("bbb");
1521 
1522     UBool pass;
1523     pass = t.find(NULL) == SymbolTable::EMPTY;
1524     pass = pass && t.find("ccc") == SymbolTable::NO_VAR;
1525     pass = pass && t.find("bbb") == SymbolTable::NO_REF;
1526     pass = pass && t.find("abc") == SymbolTable::HAS_REF;
1527     pass = pass && t.find("$aaa") == SymbolTable::HAS_REF;
1528 
1529     t.reset();
1530     pass = pass && t.find("abc") == SymbolTable::NO_VAR;
1531     return pass;
1532 }
1533 
1534 
TestScanner(void)1535 static UBool TestScanner(void){
1536     //const char str1[] = "$root = $command{0,5} $reset $mostRules{1,20};";
1537     //const char str1_r[][20] = {"$root", "=", "$command", "{", "0", ",", "5", "}",
1538     //    "$reset", "$mostRules", "{", "1", ",", "20", "}", ";"};
1539 
1540     const char str2[] = "$p2 =(\\\\ $s $string $s)? 25%;";
1541     const char str2_r[][20] = {"$p2", "=", "(", "\\", "$s", "$string", "$s", ")", "?", "25", "%", ";"};
1542 
1543     const char *str = str2;
1544     const char (*str_r)[20] = str2_r;
1545     int tokenNum = sizeof(str2_r)/sizeof(char[20]);
1546 
1547     Scanner t(str);
1548     UBool pass = TRUE;
1549     t.getNextToken();
1550     int i = 0;
1551     while (pass){
1552         if (t.tokenType == STREAM_END){
1553             pass = pass? i == tokenNum : FALSE;
1554             break;//while
1555         } else if (t.tokenType == ERROR){
1556             pass = FALSE;
1557             break;//while
1558         } else {
1559             pass = strcmp( &(t.token[0]), str_r[i++]) == 0;
1560             t.getNextToken();
1561         }
1562     }
1563 
1564     //const char ts[] = "$commandList = '['"
1565     //" ( alternate ' ' $alternateOptions"
1566     //" | backwards ' 2'"
1567     //" | normalization ' ' $onoff "
1568     //" | caseLevel ' ' $onoff "
1569     //" | hiraganaQ ' ' $onoff"
1570     //" | caseFirst ' ' $caseFirstOptions"
1571     //" | strength ' ' $strengthOptions"
1572     //" ) ']';" ;
1573 
1574     //Scanner t2(ts);
1575     //pass = TRUE;
1576     //do {
1577     //    t2.getNextToken();
1578     //    if (t2.tokenType == ERROR){
1579     //        DumpScanner(t2);
1580     //        return FALSE;
1581     //    }
1582     //}while (t.tokenType != STREAM_END);
1583 
1584     return pass;
1585 }
1586 
1587 class TestParserT {
1588 public:
operator ()(const char * const str,const int exp_error_offset=-1,const UBool dump=TRUE)1589 UBool operator () (const char *const str, const int exp_error_offset = -1, const UBool dump = TRUE){
1590     Parser par(str);
1591     if (par.rules()){
1592         if ( exp_error_offset == -1){
1593             return TRUE;
1594         }else {
1595             DumpScanner(par.s,dump);
1596             return FALSE;
1597         }
1598     }else {
1599         return DumpScanner(par.s, dump) == exp_error_offset;
1600     }
1601 }
1602 };
1603 
TestParser()1604 UBool TestParser(){
1605     TestParserT test;
1606 
1607     UBool pass = TRUE;
1608     pass = pass && test ("$s = ' ' ? 50%;");
1609     pass = pass && test("$x = ($var {1,2}) 3%;");         // legal
1610     pass = pass && test("$x = $var {1,2} 3% | b 4%;");    // legal
1611     pass = pass && test("$x = $var {1,2} 3%;");           // legal
1612     pass = pass && test("$m = $c ? 2% 4% | $r 5% | $n 25%;"); // legal
1613     pass = pass && test("$a = b ? 2% | c 5%;");               // legal
1614     pass = pass && test("$x = A B 5% C 10% | D;", 8, FALSE);  // illegal 5%
1615     pass = pass && test("$x = aa 45% | bb 5% cc;", 19, FALSE);// illegal cc
1616     pass = pass && test("$x = (b 5%) (c 6%);");               // legal
1617     pass = pass && test("$x = (b 5%) c 6%;", 13, FALSE);      // illegal 6%
1618     pass = pass && test("$x = b 5% (c 6%);", 9, FALSE);       // illegal (c 6%)
1619     pass = pass && test("$x = b 5% c 6%;", 9, FALSE);         // illegal c 6%
1620     pass = pass && test("$x = b 5%;");                        // legal
1621     pass = pass && test("$x = aa 45% | bb 5% cc;", 19, FALSE);// illegal cc
1622     pass = pass && test("$x = a | b  | c 4% | d 5%;");        // legal
1623     pass = pass && test("$s = ' ' ? 50% abc;");               // legal
1624     pass = pass && test("$s =  a | c d | e f;");              // legal
1625     pass = pass && test( "$z = q 0% | p 1% | r 100%;");         // legal How to check parsed tree??
1626 
1627     pass = pass && test("$s = ' ' ? 50%;");
1628     pass = pass && test("$relationList = '<' | '<<' |  ';' | '<<<' | ',' | '=';");
1629     pass = pass && test("$p1 = ($string $s '|' $s)? 25%;");
1630     pass = pass && test("$p2 = (\\\\ $s $string $s)? 25%;");
1631     pass = pass && test("$rel2 = $p1 $string $s $p2;");
1632     pass = pass && test("$relation = $relationList $s ($rel1 | $rel2) $crlf;");
1633     pass = pass && test("$command = $commandList $crlf;");
1634     pass = pass && test("$reset = '&' $s ($beforeList $s)? 10% ($positionList 100% | $string 10%) $crlf;");
1635     pass = pass && test("$mostRules = $command 1% | $reset 5% | $relation 25%;");
1636     pass = pass && test("$root = $command{0,5} $reset $mostRules{1,20};");
1637 
1638     const char collationBNF[] =
1639     "$s = ' '? 50%;"
1640     "$crlf = '\r\n';"
1641 
1642     "$alternateOptions = non'-'ignorable | shifted;"
1643     "$onoff = on | off;"
1644     "$caseFirstOptions = off | upper | lower;"
1645     "$strengthOptions = '1' | '2' | '3' | '4' | 'I';"
1646     "$commandList = '['"
1647     " ( alternate ' ' $alternateOptions"
1648     " | backwards ' 2'"
1649     " | normalization ' ' $onoff "
1650     " | caseLevel ' ' $onoff "
1651     " | hiraganaQ ' ' $onoff"
1652     " | caseFirst ' ' $caseFirstOptions"
1653     " | strength ' ' $strengthOptions"
1654     " ) ']';"
1655     "$command = $commandList $crlf;"
1656 
1657     "$ignorableTypes = (tertiary | secondary | primary) ' ' ignorable;"
1658     "$allTypes = variable | regular | implicit | trailing | $ignorableTypes;"
1659     "$positionList = '[' (first | last) ' ' $allTypes ']';"
1660 
1661     "$beforeList = '[before ' ('1' | '2' | '3') ']';"
1662 
1663     "$relationList = ("
1664     "   '<'"
1665     " | '<<'"
1666     " | ';'"
1667     " | '<<<'"
1668     " | ','"
1669     " | '='"
1670     ");"
1671     "$string = $magic;"
1672     "$rel1 = '[variable top]' $s;"
1673     "$p1 = ($string $s '|' $s)? 25%;"
1674     "$p2 = (\\\\ $s $string $s)? 25%;"
1675     "$rel2 = $p1 $string $s $p2;"
1676     "$relation = $relationList $s ($rel1 | $rel2) $crlf;"
1677 
1678     "$reset = '&' $s ($beforeList $s)? 10% ($positionList 1% | $string 10%) $crlf;"
1679     "$mostRules = $command 1% | $reset 5% | $relation 25%;"
1680     "$root = $command{0,5} $reset $mostRules{1,20};"
1681     ;
1682 
1683     pass = pass && test(collationBNF);
1684 
1685 
1686     return pass;
1687 }
1688 
TestMorph()1689 static UBool TestMorph(){
1690     srand((unsigned)time( NULL ));
1691 
1692     Alternation * alt = new Alternation();
1693 
1694     (*alt)
1695     .append(new Literal("a")).append(new Literal("b")).append(new Literal("c"))
1696     .append(new Literal("d")).append(new Literal("e")).append(new Literal("f"))
1697     .append(new Literal("g")).append(new Literal("h")).append(new Literal("i"))
1698     .append(new Literal("j")).append(new Literal("k")).append(new Literal("l"))
1699     .append(new Literal("m")).append(new Literal("n")).append(new Literal("o"))
1700     ;
1701 
1702     Repeat * rep = new Repeat( alt ,5,5 );
1703     Morph m( *rep);
1704 
1705 //    DUMP_R(TestMorph,(*rep),20);
1706     DUMP_R(TestMorph,m,100);
1707 
1708     return FALSE;
1709 }
1710 
1711 #endif
1712 
TestLanguageGenerator()1713 static UBool TestLanguageGenerator(){
1714     //LanguageGenerator g;
1715     //const char *const s = "$s = p 0% | q 1%;";
1716     //g.parseBNF(s, "$s");
1717     UBool pass;
1718     //= strcmp("q", g.next()) == 0;
1719 
1720     const char *const def =
1721         //"$a = $b;"
1722         //"$b = $c;"
1723         //"$c = $t;"
1724         //"$t = abc $z{1,2};"
1725         //"$k = a | b | c | d | e | f | g ;"
1726         //"$z = q 0% | p 1% | r 1%;"
1727         "$x = a ? 0%;"
1728         ; // end of string
1729 //    const char * s = "abczz";
1730 //
1731 //
1732     LanguageGenerator g;
1733     pass = g.parseBNF(def, "$x",TRUE);
1734 ////    LanguageGenerator g(collationBNF, "$root", "$magic", new MagicNode());
1735 //
1736     if (pass != LanguageGenerator::OK) return FALSE;
1737 
1738     DUMP_R(TestLanguageGenerator, g, 20);
1739     return pass;
1740 
1741     ////UBool pass = strcmp(s,r) == 0;
1742 
1743     //if (pass){
1744     //    printf("TestRandomLanguageGenerator passed.\n");
1745     //} else {
1746     //    printf("TestRandomLanguageGenerator FAILED!!!\n");
1747     //}
1748     //return pass;
1749 }
1750 
TestWbnf(void)1751 void TestWbnf(void){
1752     srand((unsigned)time( NULL ));
1753 
1754     //CALL(TestLiteral);
1755     //CALL(TestSequence);
1756     //CALL(TestSymbolTable);
1757     //CALL(TestVariable);
1758 
1759     //TestRepeat();
1760     //TestAlternation();
1761     //TestMorph();
1762 
1763     //TestQuote();
1764     //TestBuffer();
1765     //TestWeightedRand();
1766 
1767     //CALL(TestScanner);
1768     //CALL(TestParser);
1769     CALL(TestLanguageGenerator);
1770 }
1771 
1772