wbnf.cpp   [plain text]


/*
 ******************************************************************************
 * Copyright (C) 2005-2007, International Business Machines Corporation and   *
 * others. All Rights Reserved.                                               *
 ******************************************************************************
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

#include "wbnf.h"
    
// Most of this code is meant to test the test code. It's a self test.
// Normally this isn't run.
#define TEST_WBNF_TEST 0

///////////////////////////////////////////////////////////
//
// Constants and the most basic helper classes
//
    
static const char DIGIT_CHAR[] = "0123456789";
static const char WHITE_SPACE[] = {'\t', ' ', '\r', '\n', 0};
static const char ALPHABET[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
static const char SPECIAL[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";

static inline UBool isInList(const char c /*in*/, const char list[] /*in*/){
    const char * p = list;
    for (;*p != 0 && *p != c; p++);
    return *p?TRUE:FALSE;
}
static inline UBool isDigit(char c) {return isInList(c, DIGIT_CHAR);}
static inline UBool isWhiteSpace(char c) {return isInList(c, WHITE_SPACE);}
static inline UBool isAlphabet(char c) {return isInList(c, ALPHABET);}
static inline UBool isSpecialAsciiChar(char c) {return isInList(c,SPECIAL);}

    

///////////////////////////////////////////////////////////
//
// Helper classes
//
    
class Buffer_byte{
// Utility class, can be treated as an auto expanded array. no boundary check.

    typedef char byte;
    byte * start;
    byte * current;
    int buffer_size; // size unit is byte
public:
    inline int content_size(){return current - start;} // size unit is byte

private:
    inline void expand(int add_size = 100){ // size unit is byte
        int new_size = buffer_size + add_size;

        int cs_snap = content_size();         
        start = (byte *) realloc(start, new_size);   // may change the value of start
        current = start + cs_snap;

        memset(current, 0, add_size);
        buffer_size = new_size;
    }

    inline void expand_to(int size){
        int r = size - buffer_size;
        if (r > 0) {
            expand(r);  // simply expand, no block alignment
        }
    }
    Buffer_byte(const Buffer_byte &);
    Buffer_byte & operator = (const Buffer_byte &);
public:
    Buffer_byte():start(NULL),current(start),buffer_size(0){
        expand();
    }
    ~Buffer_byte(){
        free(start);
    }

    inline void reset(){
        start != NULL ? memset(start, 0, buffer_size) : 0;
        current = start;
    }

    // Using memory copy method to append a C array to buffer, 
    inline void append(const void * c, int size){ // size unit is byte
        expand_to(content_size() + size) ;
        memcpy(current, c, size);
        current = current + size;
    }
    
    byte * buffer(){
        return start;
    }
};
    
/*
  The class(es) try to work as bulid-in array, so it overloads these two operators
    operator type *();
    type & operator[];
  The first is used to auto type convert, the latter is used to select member.

  A small trick is the class does not overload the address-of operator. This 
  behavior is different from bulid-in array, but it give us the opportunity 
  to get the address of the class itself.
*/
//template<typename type>
//    class BUFFER{
//       typedef BUFFER name;
#define BUFFER(type, name)\
    class name {\
    private:\
       Buffer_byte buf;\
    public:\
        name & reset() {buf.reset(); return *this;}\
        name & append(type c) {buf.append(&c, sizeof(type)); return *this;}\
        name & append_array(const type * p, int size) {buf.append(p, sizeof(type)*size); return *this;}\
        type & operator [] (int i) { return ((type *) buf.buffer())[i];}\
        operator type *(){return (type *) buf.buffer();} \
        int content_size(){return buf.content_size() / sizeof(type);}\
    }
    

class Pick{
/* The Pick is the basic language generator element*/
public:
    // generate a string accroding the syntax 
    // Return a null-terminated c-string. The buffer is owned by callee.
    virtual const char* next() = 0; 
    virtual ~Pick(){};
};

//typedef BUFFER<char> Buffer_char;
//typedef BUFFER<int> Buffer_int;
//typedef BUFFER<Pick *> Buffer_pPick;
BUFFER(char, Buffer_char);
BUFFER(int, Buffer_int);
BUFFER(Pick *, Buffer_pPick);

class SymbolTable{
/* Helper class.
* It's a mapping table between 'variable name' and its 'active Pick object'
*/
private:
    Buffer_char  name_buffer;   // var names storage space

    Buffer_int   names;         // points to name (offset in name_buffer)
    Buffer_pPick refs;          // points to Pick

    int get_index(const char *const var_name){
        int len = names.content_size();
        for (int i=0; i< len; i++){
            if (strcmp(var_name, name_buffer + names[i]) == 0){
                return i;
            }
        }
        return -1;
    }

public:
    enum RESULT {EMPTY, NO_VAR, NO_REF, HAS_REF};

    RESULT find(const char *const var_name /*[in] c-string*/, Pick * * ref = NULL /*[out] Pick* */){
        if (!var_name) return EMPTY; // NULL name

        int i = get_index(var_name);
        if (i == -1){
            return NO_VAR;   // new name
        }
        if (!refs[i]){  // exist name, no ref
            return NO_REF;
        } else {
            if (ref) {
                *ref = refs[i];
            }
            return HAS_REF;   // exist name, has ref
        }
    }

    void put(const char *const var_name, Pick *const var_ref = NULL){
        int i = get_index(var_name);
        switch(find(var_name)){
            case EMPTY:    // NULL name
                break;
            case NO_VAR:    // new name
                int offset;
                offset = name_buffer.content_size();
                name_buffer.append_array(var_name, strlen(var_name) + 1);
                names.append(offset);
                refs.append(var_ref);
                break;
            case NO_REF:    // exist name, no ref
                refs[i] = var_ref;    // link definition with variable
                break;
            case HAS_REF:    // exist name, has ref
                if (var_ref){
                    refs[i] = var_ref;
                }
                break;
            default:
                ; // ASSERT(FALSE);
        }
        return;
    }

    UBool is_complete(){
        int n = names.content_size();
        for (int i=0; i<n; ++i){
            if (refs[i] == NULL){
                return FALSE;
            }
        }
        return TRUE;
    }

    void reset(){
        names.reset();
        name_buffer.reset();

        // release memory here
        int s = refs.content_size();
        for (int i=0; i < s; i++){
            delete refs[i]; // TOFIX: point alias/recursion problem
        }
        refs.reset();
    }

    ~SymbolTable(){
        reset();
    }
};


/*
// Document of class Escaper
//
// ATTENTION: 
// From http://icu-project.org/userguide/Collate_Customization.html.
// We get the precedence of escape/quote operations
//
//     (highest) 1. backslash               \
//               2. two single quotes       ''
//               3. quoting                 ' '
//
// ICU Collation should accept following as the same string.
//
// 1)  'ab'c        _
// 2)  a\bc          \
// 3)  a'b'\c        |- They are equal.
// 4)  abc          _/
//
// From "two single quotes", we have following deductions
//    D1. empty quoting is illgal. (obviously)
//    D2. no contact operation between two quotings   
//              '.''.'      is not ..   it is .'.
//    D3. "two single quotes" cannot contact two quoting simultaneously
//              '..''''.'   is not ..'. it is ..''.
//       NOTICE:
//        "two single quotes" can contact before one quoting
//              '''.'       is '.
//        "two single quotes" can literally contact after one quoting
//        But, from syntax, it's one quoting including a "two single quotes"
//              '.'''       is .'
//    D4. "two single quotes" cannot solely be included in quoting
//              ''''        is not '    it is ''
//       NOTICE:  These are legal
//              '.''.'      is .'.
//              '.'''       is .'
//
//                 dicision
//                    /\
//                   /__\
//      output buffer    input buffer
// 
// To make our dicision (within an atom operation) without caring input and output buffer,
// following calling pattern (within an atom operation) shall be avoided
//
//    P1 open_quoting()  then close_quoting()    (direct violation)   D1
//    P2 close_quoting() then open_quoting()     (direct violation)   D2
//    P3 empty open_quoting()                    (indirect violation) D1, D4
//    P4 empty close_quoting()                   (indirect violation) D2, D3
//    P5 open_quoting()  then two single quotes  (indirect violation) D4
//    P6 close_quoting() then two single quotes  (indirect violation) D3
//
// two single quotes escaping will not open_ or close_ quoting()
// The choice will not lose some quoing forms.
//
// For open_quoting(), 
// we may get this form quoting     '''         P5
// It may raise a bug               ''''x
// If we expect
//      '''.'       let the next char open the quoting
//      '.''.'      the quoting is already opened by preceding char
//
// For close_quoting()
// we will get this form quoting    '.'''       P6
// It may raise a bug               '.''''.'
// If we expect          
//      '.'''\.     let the next char close the quoting
//      '.''''.'    the expectation is wrong!  using  '.'\''.' instead
//
// It's a hard work to re-adjust generation opportunity for various escaping form.
// We just simply ignore it.
*/
class Escaper{
public:
    enum CHOICE {YES, NO, RAND};
    enum ESCAPE_FORM {BSLASH_ONLY, QUOTE_ONLY, QUOTE_AND_BSLAH, RAND_ESC};
private:
    class Bool{ // A wrapper class for CHOICE, to auto adapter UBool class
        private:
            const CHOICE tag;
        public:
            Bool(CHOICE flag=RAND):tag(flag){}
            operator UBool() {   // conversion operator
                return tag == RAND ? rand()%2 : tag == YES;
                //if (tag == RAND){
                //    return rand()%2 == 1;
                //} else {
                //    return tag == YES ? TRUE : FALSE;
                //}
            }
    };
public:
    Escaper(CHOICE escapeLiteral = RAND,
        CHOICE twoQuotesEscape = RAND,
        ESCAPE_FORM escapeForm = RAND_ESC):
        escape_form(escapeForm),
        escape_literal(escapeLiteral),
        two_quotes_escape(twoQuotesEscape),
        is_quoting(FALSE){}
private:
    Buffer_char str;
    ESCAPE_FORM escape_form;
    Bool escape_literal;
    Bool two_quotes_escape;
    UBool quote_escape;
    UBool bslash_escape;
    UBool is_quoting;

    void set_options(){
        ESCAPE_FORM t = escape_form == RAND_ESC ? (ESCAPE_FORM) (rand()%3) : escape_form;
        switch (t){
                case BSLASH_ONLY :
                    bslash_escape = TRUE; quote_escape = FALSE; break;
                case QUOTE_ONLY:
                    bslash_escape = FALSE;quote_escape = TRUE;  break;
                case QUOTE_AND_BSLAH:
                    bslash_escape = TRUE; quote_escape = TRUE;  break;
                default:
                    ;// error
        }
    }

    void reset(){
        str.reset();
        is_quoting = FALSE;
    }

    inline void open_quoting(){ 
        if(is_quoting){
            // do nothing
        } else {
            str.append('\'');
            is_quoting = TRUE;
        }
    }
    inline void close_quoting(){
        if(is_quoting){
            str.append('\'');
            is_quoting = FALSE;
        } else {
            // do nothing
        }
    }

    // str  [in]    null-terminated c-string
    void append(const char * strToAppend){
        for(;*strToAppend != 0; strToAppend++){
            append(*strToAppend);
        }
    }

    inline void append(const char c){
        set_options();

        if (c == '\\'){
            quote_escape ? open_quoting() : close_quoting();
            //bslash_escape always true here
            str.append('\\');
            str.append('\\');
        } else if (c == '\''){
            if (two_quotes_escape){     // quoted using two single quotes
                // See documents in anonymous.design
                str.append('\'');
                str.append('\'');
            } else{
                quote_escape ? open_quoting() : close_quoting();
                //bslash_escape always true here
                str.append('\\');
                str.append('\'');
            }
        } else if (isSpecialAsciiChar(c) || isWhiteSpace(c)){
            quote_escape  ? open_quoting()   : close_quoting();
            if (bslash_escape) str.append('\\');
            str.append(c);
        } else { //if (isAlphabet(c) || isDigit(c) || TRUE){ // treat others as literal
            if (escape_literal){
                quote_escape  ? open_quoting()   : close_quoting();
                if (bslash_escape)  str.append('\\');
                str.append(c);
            } else {
                close_quoting();
                str.append(c);
            }
        }
    }

public:
    // Return a null-terminate c-string. The buffer is owned by callee.
    char * operator()(const char * literal /*c-string*/){
        str.reset();
        for(;*literal != 0; literal++){
            append(*literal);
        }
        close_quoting();    // P4 exception, to close whole quoting
        return str;
    }
};
    
class WeightedRand{
// Return a random number in [0, size)
// Every number has different chance (aka weight) to be selected.
private:
    Buffer_int weights;
    double total;
    WeightedRand(const WeightedRand &);
    WeightedRand & operator = (const WeightedRand &);
public:
    WeightedRand(Buffer_int * weight_list = NULL, int size = 0){
        if ( weight_list == NULL){
            for (int i=0; i<size; ++i) weights.append(DEFAULT_WEIGHT);
        } else {
            int s = weight_list->content_size();
            if (s < size){
                weights.append_array( (*weight_list),s);
                for (int i=s; i<size; ++i) weights.append(DEFAULT_WEIGHT);
            } else { // s >= size
                weights.append_array( (*weight_list),size);
            }
        }
        total = 0;
        int c = weights.content_size();
        for (int i=0; i<c; ++i){
            total += weights[i];
        }
    }

    void append(int weight){
        weights.append(weight);
        total += weight;
    }

    // Give a random number with the consideration of weight.
    // Every random number is associated with a weight.
    // It identifies the chance to be selected, 
    // larger weight has more chance to be selected.
    //
    //
    //  ______________________   every slot has equal chance
    //
    //  [____][_][___][______]   each item has different slots, hence different chance
    //
    //
    //  The algorithms to generate the number is illustrated by preceding figure.
    //  First, a slot is selected by rand(). Then we translate the slot to corresponding item.
    //
    int next(){
        // get a random in [0,1]
        double reference_mark = (double)rand() / (double)RAND_MAX;

        // get the slot's index, 0 <= mark <= total;
        double mark = total * reference_mark;

        // translate the slot to corresponding item
        int i=0;
        for (;;){
            mark -= weights[i];  // 0 <= mark <= total
            if (mark <= 0)
                break;
            i++;
        }
        return i;
    }
};
    
///////////////////////////////////////////////////////////
//
// The parser result nodes
//
    
class Literal : public Pick {
public:
    virtual const char* next(){
        return str;
    }
    Literal(const char * s /*c-string*/){
        str.append_array(s, strlen(s) + 1);
    }
private:
    Buffer_char str; //null-terminated c-string
};

class Variable : public Pick {
public:
    Variable(SymbolTable * symbols, const char * varName, Pick * varRef = NULL){
        this->var_name.append_array(varName, strlen(varName) + 1);
        if ((symbol_table = symbols)){
            symbol_table->put(varName, varRef);
        }
    }

    operator const char *(){
        return var_name;
    }

    virtual const char* next(){
        if (symbol_table){
            Pick * var_ref = NULL;
            symbol_table->find(var_name, &var_ref);
            if (var_ref) {
                return var_ref->next();
            }
        }
        return "";  // dumb string
    }
private:
    Buffer_char var_name;
    SymbolTable * symbol_table;
};

class Quote : public Pick{
public:
    Quote(Pick & base):item(base),e(Escaper::NO, Escaper::NO, Escaper::BSLASH_ONLY){
    }
    virtual const char* next(){
        return e(item.next());
    }
private:
    Pick & item;
    Buffer_char str;
    Escaper e;
};


class Morph : public Pick{
/*
The difference between morph and an arbitrary random string is that 
a morph changes slowly. When we build collation rules, for example, 
it is a much better test if the strings we use are all in the same 
'neighborhood'; they share many common characters.
*/
public:
    Morph(Pick & base):item(base){}

    virtual const char* next(){
        current.reset();
        const char * s = item.next();
        current.append_array(s, strlen(s) + 1);
        if  (last.content_size() == 0) {
            str.reset();
            last.reset();
            str.append_array(current, current.content_size());
            last.append_array(current, current.content_size());
        } else {
            morph();
        }
        return str;
    }
private:
    Pick & item;
    Buffer_char str;
    Buffer_char last;
    Buffer_char current;

    char * p_last;
    char * p_curr;

    void copy_curr(){
        if (*p_curr) {
            str.append(*p_curr);
            p_curr++;
        }
    }

    void copy_last(){
        if (*p_last) {
            str.append(*p_last);
            p_last++;
        }
    }

    // copy 0, 1, or 2 character(s) to str
    void copy(){
        static WeightedRand wr(& Buffer_int().append(DEFAULT_WEIGHT * 10), 5);

        switch (wr.next()){
            case 0: // copy last  -- has 10 times chance than others
                copy_last();
                break;
            case 1: // copy both
                copy_curr();
                copy_last();
                break;
            case 2: // copy both
                copy_last();
                copy_curr();
                break;
            case 3:
                copy_curr();
                break;
            case 4:  // copy nothing
                break;
            default:
                // ASSERT(FALSE);
                ;
        }
    }

    void morph(void){
        int min = strlen(last);
        int max = strlen(current);
        if (min > max){
            int temp  = min;
            min = max;
            max = temp;
        }

        int len = min + rand()%(max - min + 1); // min + [0, diff]
        p_curr = current;
        p_last = last;
        str.reset();

        for (; str.content_size()<len && *p_curr && *p_last;){
            copy(); // copy 0, 1, or 2 character(s) to str
        }

        if (str.content_size() == len) {
            str.append(0);
            final();
            return;
        }

        if (str.content_size() > len) { // if the last copy copied two characters
            str[len]=0;
            final();
            return;
        }

        // str.content_size() < len
        if (*p_last) {
            for (; str.content_size() < len; copy_last());
        } else if (*p_curr){
            for (; str.content_size() < len; copy_curr());
        }

        int last_len = last.content_size();
        for (;str.content_size() < len;){
            str.append(last[rand()%last_len]);
        }
        str.append(0);
        final();
    }

    void final(){
        last.reset();
        last.append_array(current, current.content_size());
    }
};

class Sequence : public Pick {
public:
    virtual const char* next(){
        str.reset();
        int s = items.content_size();
        for(int i=0; i < s; i++){
            const char * t = items[i]->next();
            str.append_array(t, strlen(t));
        }
        str.append(0); // terminal null
        return str;
    }

    void append (Pick * node){
        items.append(node);
    }

    virtual ~Sequence(){
        int s = items.content_size();
        for(int i=0; i < s; i++){
            //How can assure the item is got from heap?
            //Let's assume it.
            delete items[i]; // TOFIX: point alias/recursion problem
            items[i] = NULL;
        }
    }
private:
    Buffer_pPick items;
    Buffer_char  str; //null-terminated c-string
};

class Repeat : public Pick {
private:
    Pick * item;
    Buffer_char str;
    WeightedRand wr;
    int min;
    int max;
    int select_a_count(){
        return min + wr.next();
    }
public:
    virtual const char* next(){
        str.reset();
        int c = select_a_count();
        for(int i=0; i< c; i++){
            const char * t = item->next();
            str.append_array(t, strlen(t));
        }
        str.append(0);
        return str;
    }

    Repeat(Pick * base, int minCount =0, int maxCount = 1, Buffer_int * weights = NULL):
        wr(weights, maxCount-minCount +1) {
        this->item = base;
        this->min = minCount;
        this->max = maxCount;
    }
    virtual ~Repeat(){
        delete item;  // TOFIX: point alias/recursion problem
        item = NULL;
    }
};


class Alternation : public Pick {
public:
    virtual const char* next(){
        str.reset();
        int i = wr.next();
        const char * t = items[i]->next();
        str.append_array(t, strlen(t) + 1);
        return str;
    }
    virtual ~Alternation(){
        int s = items.content_size();
        for(int i=0; i < s; i++){
            delete items[i];  // TOFIX: point alias/recursion problem
            items[i] = NULL;
        }
    }

    Alternation & append (Pick * node, int weight = DEFAULT_WEIGHT){
        items.append(node);
        wr.append(weight);
        return *this;
    }
private:
    Buffer_pPick items;
    Buffer_char str; // null-terminated c-string
    WeightedRand wr;
};
    
///////////////////////////////////////////////////////////
//
// The parser
//

enum TokenType {STRING, VAR, NUMBER, STREAM_END, ERROR, QUESTION, STAR, PLUS, LBRACE, RBRACE, LPAR, RPAR, SEMI, EQ, COMMA, BAR, AT, WAVE, PERCENT};

class Scanner{
friend int DumpScanner(Scanner & s, UBool dumb);
private:
    const char * source;
    const char * working;
    const char * history; // for debug
    enum StateType {START, IN_NUM, IN_VAR_FIRST, IN_VAR, IN_QUOTE, IN_QUOTE_BSLASH, IN_BSLASH, IN_STRING, DONE};
    StateType state;
    void terminated(TokenType t){
        working--;       // return the peeked character
        tokenType = t;
        token.append(0); // close buffer
        state = DONE;
    }
public:
    // the buffer of "source" is owned by caller
    Scanner(const char *src/*[in] c-string*/ = NULL):source(src){
        working = src;
        history = working;
        state = DONE;
        tokenType = ERROR;
    }

    //void setSource(const char *const src /*[in] c-string*/){
    //    *(&const_cast<const char *>(source)) = src;
    //}

    Buffer_char token;
    TokenType tokenType;

    TokenType getNextToken(){
        token.reset();
        state = START;
        history = working; // for debug
        while (state != DONE){
            char c = *working++;
            if (c == 0 && state != START){//avoid buffer overflow. for IN_QUOE, IN_ESCAPE
                terminated(ERROR);
                break; // while
            }
            switch(state){
                case START:
                    tokenType = ERROR;
                    switch(c){
                        case '?'  : tokenType = QUESTION; break;
                        case '*'  : tokenType = STAR; break;
                        case '+'  : tokenType = PLUS; break;
                        case '{'  : tokenType = LBRACE; break;
                        case '}'  : tokenType = RBRACE; break;
                        case '('  : tokenType = LPAR; break;
                        case ')'  : tokenType = RPAR; break;
                        case ';'  : tokenType = SEMI; break;
                        case '='  : tokenType = EQ; break;
                        case ','  : tokenType = COMMA; break;
                        case '|'  : tokenType = BAR; break;
                        case '@'  : tokenType = AT; break;
                        case '~'  : tokenType = WAVE; break;
                        case '%'  : tokenType = PERCENT; break;
                        case 0    : tokenType = STREAM_END; working-- /*avoid buffer overflow*/; break;
                    }
                    if (tokenType != ERROR){
                        token.append(c);
                        token.append(0);
                        state = DONE;
                        break; // START
                    }
                    switch(c){
                        case '$'  : state = IN_VAR_FIRST; token.append(c); break;
                        case '\'' : state = IN_QUOTE;     break;
                        case '\\' : state = IN_BSLASH;    break;
                        default:
                            if (isWhiteSpace(c)){    // state = START;   //do nothing
                            } else if (isDigit(c)){     state = IN_NUM;    token.append(c);
                            } else if (isAlphabet(c)){  state = IN_STRING; token.append(c);
                            } else {terminated(ERROR);}
                    }
                    break;//START
                case IN_NUM:
                    if (isDigit(c)){
                        token.append(c);
                    } else {
                        terminated(NUMBER);
                    }
                    break;//IN_NUM
                case IN_VAR_FIRST:
                    if (isAlphabet(c)){
                        token.append(c);
                        state = IN_VAR;
                    } else {
                        terminated(ERROR);
                    }
                    break; // IN_VAR_FISRT
                case IN_VAR:
                    if (isAlphabet(c) || isDigit(c)){
                        token.append(c);
                    } else {
                        terminated(VAR);
                    }
                    break;//IN_VAR
                case IN_STRING:
                    // About the scanner's behavior for STRING, AT, and ESCAPE:
                    // All of them can be contacted with each other. 
                    // This means the scanner will eat up as much as possible strings
                    //   (STRING, AT, and ESCAPE) at one time, with no regard of their
                    //   combining sequence.
                    //
                    if (c == '\''){
                        state = IN_QUOTE; // the first time we see single quote
                    } else if (c =='\\'){ // back slash character
                        state = IN_BSLASH;
                    } else if (isAlphabet(c) || isDigit(c)){
                        token.append(c);
                    } else{
                        terminated(STRING);
                    }
                    break;//IN_STRING
                case IN_QUOTE:
                    if (c == '\''){ // the second time we see single quote
                        state = IN_STRING; // see document in IN_STRING
                    } else if ( c== '\\') { // backslah escape in quote
                        state = IN_QUOTE_BSLASH;
                    } else {
                        token.append(c);  // eat up everything, includes back slash
                    }
                    break;//IN_QUOTE
                case IN_QUOTE_BSLASH:
                case IN_BSLASH:
                    switch (c){
                        case 'n'  : token.append('\n'); break;
                        case 'r'  : token.append('\r'); break;
                        case 't'  : token.append('\t'); break;
                        case '\'' : token.append('\''); break;
                        case '\\' : token.append('\\'); break;
                        default: token.append(c); // unknown escaping, treat it as literal
                    }
                    if (state == IN_BSLASH){
                        state = IN_STRING; // see document in IN_STRING
                    } else { // state == IN_QUOTE_BSLASH
                        state = IN_QUOTE;
                    }
                    break;//IN_BSLASH
                case DONE:  /* should never happen */
                default:
                    working--;
                    tokenType = ERROR;
                    state = DONE;
                    break;
            }//switch(state) 
        }//while (state != DONE)

        return tokenType;
    }
};//class Scanner

class Parser{
friend UBool TestParser();
friend class TestParserT;
friend class LanguageGenerator_impl;
private:
    Scanner s;
    TokenType & token;
    int min_max;   // for the evil infinite
    
    UBool match(TokenType expected){
        if (token == expected) {
            token = s.getNextToken();
            return TRUE;
        } else {
            //s.dumpCurrentPoint();
            return FALSE;
        }
    }

    UBool weight(int & value){
        if (token == NUMBER){
            int temp = atoi(s.token);
            match(NUMBER);
            if (match(PERCENT)){
                value = temp;
                return TRUE;
            }
        }
        return FALSE;
    }

    UBool repeat (Pick* &node /*in,out*/){
        if (node == NULL) return FALSE;

        int count = -2;
        int min = -2;
        int max = -2;
        UBool question = FALSE;
        switch (token){
            case QUESTION:
                match(QUESTION);
                min = 0;
                max = 1;
                count = 2;
                question = TRUE;
                break;
            case STAR:
                match(STAR);
                min = 0;
                max = -1;
                count = -1;
                break;
            case PLUS:
                match(PLUS);
                min = 1;
                max = -1;
                count = -1;
                break;
            case LBRACE:
                match(LBRACE);
                if (token != NUMBER){
                    return FALSE;
                }else {
                    min = atoi(s.token);
                    match(NUMBER);
                    if (token == RBRACE){
                        match(RBRACE);
                        max = min;
                        count = 1;
                    } else if (token == COMMA) {
                        match(COMMA);
                        if (token == RBRACE){
                            match(RBRACE);
                            max = -1;
                            count = -1;
                        } else if (token == NUMBER) {
                            max = atoi(s.token);
                            match(NUMBER);
                            count = max - min + 1;
                            if (!match(RBRACE)) {
                                return FALSE;
                            }
                        } else {
                            return FALSE;
                        }
                    } else {
                        return FALSE;
                    }
                }
                break;
            default:
                return FALSE;
        }

        if (count == -2 || min == -2 || max == -2){
            //ASSERT(FALSE);
            return FALSE;
        }

        // eat up following weights
        Buffer_int weights;
        int w;
        while (weight(w)){
            weights.append(w);
        }

        // for the evil infinite
        min_max = min_max > min ? min_max : min;
        min_max = min_max > max ? min_max : max;
        if (min_max > PSEUDO_INFINIT){
            return FALSE; // PSEUDO_INFINIT is less than the real maximum
        }
        if (max == -1){ // the evil infinite
            max = PSEUDO_INFINIT;
        }
        // for the strange question mark
        if (question && weights.content_size() > 0){
            Buffer_int w2;
            w2.append(DEFAULT_WEIGHT - weights[0]).append(weights[0]);
            node = new Repeat(node,min,max,&w2);
            return TRUE;
        }
        node = new Repeat(node,min,max,&weights);
        return TRUE;
    }

    UBool core(Pick* &node /*out*/){
        if (node != NULL) return FALSE; //assert node == NULL

        switch(token){
            case LPAR:
                match(LPAR);
                if(defination(node) && match(RPAR)){
                    return TRUE;
                }
                return FALSE;
            case VAR:
                node = new Variable(&symbols, s.token);
                match(VAR);
                return TRUE;
            case STRING:
                node = new Literal(s.token);
                match(STRING);
                return TRUE;
            default:
                return FALSE;
        }
    }
    UBool modified(Pick* &node /*out*/){
        if (node != NULL) return FALSE; //assert node == NULL

        if (!core(node)) {
            return FALSE;
        }

        for (;;){
            switch(token){
                case WAVE:
                    match(WAVE);
                    node = new Morph(*node);
                    break;
                case AT:
                    match(AT);
                    node = new Quote(*node);
                    break;
                case QUESTION:
                case STAR:
                case PLUS:
                case LBRACE:
                    if (!repeat(node)) return FALSE;
                    break;
                case SEMI:      // rule definiation closed
                case RPAR:      // within parenthesis (core closed)
                case BAR:       // in alternation
                case NUMBER:    // in alternation, with weight
                case LPAR:      // in sequence
                case VAR:       // in sequence
                case STRING:    // in sequence
                    return TRUE;
                default:
                    return FALSE;
            }
        }
    }


    UBool sequence_list(Pick* &node /*in,out*/){
        if (node == NULL) return FALSE; // assert node != NULL

        Sequence* seq = new Sequence();
        Pick * n = node;

        while (token == VAR || token == STRING || token == LPAR){
            seq->append(n);
            n = NULL;
            if (modified(n)){
                // go on
            } else {
                goto FAIL;
            }
        }

        if (token == SEMI || token == RPAR || token == BAR){
            seq->append(n);
            node = seq;
            return TRUE;
        }
FAIL:
        delete seq;
        return FALSE;

    }

    UBool sequence(Pick* &node /*out*/){
        if (node != NULL) return FALSE; //assert node == NULL

        if (!modified(node)) {
            return FALSE;
        }

        if (token == VAR || token == STRING || token == LPAR){
            return sequence_list(node);
        } else {
            return TRUE; // just a modified
        }
    }

    UBool alternation_list(Pick* &node /*in,out*/){
        if (node == NULL) return FALSE; // assert node != NULL

        Alternation * alt = new Alternation();
        Pick * n = node;
        int w = DEFAULT_WEIGHT;

        while (token == NUMBER || token == BAR){
            if(token == NUMBER) {
                if (weight(w)){ 
                    if (token == BAR){ 
                        // the middle item, go on
                    } else {
                        // the last item or encounter error
                        break; //while
                    } 
                } else {
                    goto FAIL;
                }
            } // else token == BAR
            match(BAR);
            alt->append(n,w);

            n = NULL;
            w = DEFAULT_WEIGHT;
            if (sequence(n)){
                // go on
            } else {
                goto FAIL;
            }
        }

        if (token == SEMI || token == RPAR) {
            alt->append(n,w);
            node = alt;
            return TRUE;
        }
FAIL:
        delete alt;
        return FALSE;
    }

    UBool alternation(Pick* &node /*out*/){
        if (node != NULL) return FALSE; //assert node == NULL

        // 'sequence' has higher precedence than 'alternation'
        if (!sequence(node)){
            return FALSE;
        }

        if (token == BAR || token == NUMBER){ // find a real alternation1, create it.
            return alternation_list(node);
        } else {
            return TRUE;    // just a sequence_old
        }
    }


    UBool defination(Pick* &node /*out*/){
        if (node != NULL) return FALSE; //assert node == NULL
        return alternation(node);
    }

    UBool rule(){
        if (token == VAR){
            Buffer_char name;
            name.append_array(s.token, strlen(s.token) + 1);
            match(VAR);

            if (match(EQ)){
                Pick * t = NULL;
                if(defination(t)){
                    symbols.put(name, t);
                    return match(SEMI);
                }
            }
        }
        return FALSE;
    }
public:
    UBool rules(){
        symbols.reset();
        token = s.getNextToken();
        while (rule()){
        }
        if (token == STREAM_END){
            return TRUE;
        } else {
            //s.dumpCurrentPoint();
            return FALSE;
        }
    }

public:
    SymbolTable symbols;

    Parser(const char *const source):s(source), token(s.tokenType){
        min_max = -2;
    }
    UBool parse(){
        return rules();
    }

}; // class Parser
        

///////////////////////////////////////////////////////////
//
// 
//

int DumpScanner(Scanner & s, UBool dump = TRUE){
    int len = strlen(s.source);
    int error_start_offset = s.history - s.source;
    if (dump){
        printf("\n=================== DumpScanner ================\n");
        fwrite(s.source, len, 1, stdout);
        printf("\n-----parsed-------------------------------------\n");
        fwrite(s.source, s.history - s.source, 1, stdout);
        printf("\n-----current------------------------------------\n");
        fwrite(s.history, s.working - s.history, 1, stdout);
        printf("\n-----unparsed-----------------------------------\n");
        fwrite(s.working, (s.source + len - s.working), 1, stdout);
        printf("\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
    }
    return error_start_offset;
}

class LanguageGenerator_impl{
public:
    LanguageGenerator_impl(const char *const bnf_definition, const char *const top_node)
        :par(bnf_definition), top_node_name(top_node){
        srand((unsigned)time( NULL ));
    }

    LanguageGenerator::PARSE_RESULT parseBNF(UBool debug = TRUE){
        if (par.parse()){
            if (par.symbols.find(top_node_name, &top_node_ref) == SymbolTable::HAS_REF) {
                if (par.symbols.is_complete()) {
                    return LanguageGenerator::OK;
                } else {
                    if (debug) printf("The bnf definition is incomplete.\n");
                    return LanguageGenerator::INCOMPLETE;
                }
            } else {
                if (debug) printf("No top node is found.\n");
                return LanguageGenerator::NO_TOP_NODE;
            }
        } else {
            if(debug) {
                printf("The bnf definition is wrong\n");
                DumpScanner(par.s, TRUE);
            }
            return LanguageGenerator::BNF_DEF_WRONG;
        }
    }
    const char * next(){
        return top_node_ref->next();
    }

private:
    Parser par;
    const char *const top_node_name;
    Pick * top_node_ref;
};

LanguageGenerator::LanguageGenerator():lang_gen(NULL){
}

LanguageGenerator::~LanguageGenerator(){
    delete lang_gen;
}

LanguageGenerator::PARSE_RESULT LanguageGenerator::parseBNF(const char *const bnf_definition /*in*/, const char *const top_node/*in*/, UBool debug){
    if (lang_gen){
        delete lang_gen;
    }
    lang_gen = new LanguageGenerator_impl(bnf_definition, top_node);
    PARSE_RESULT r = lang_gen->parseBNF(debug);
    if (r != OK){
        delete lang_gen;
        lang_gen = NULL;
        return r;
    } else {
        return r;
    }
}
const char *LanguageGenerator::next(){ // Return a null-terminated c-string. The buffer is owned by callee.
    if (lang_gen){
        return lang_gen->next();
    }else { 
        return "";
    }
}

///////////////////////////////////////////////////////////
//
// The test code for WBNF
//

#define CALL(fun) \
    if (fun()){ \
        printf("Pass: " #fun "\n");\
    } else { \
        printf("FAILED: !!! " #fun " !!!\n"); \
    }

#define DUMP_R(fun, var, times) \
    {printf("\n========= " #fun " =============\n"); \
    for (int i=0; i<times; i++) { \
        const char * t = var.next();\
        fwrite(t,strlen(t),1,stdout); \
        printf("\n");   \
    }   \
    printf("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");}



#if TEST_WBNF_TEST    
static UBool TestQuote(){
    const char *const str = "This ' A !,z| qq [] .new\tline";
    //const char *const str_r = "This \\' A '!,'z'|' qq '[]' '.'new\tline";
    ////
    //// :(  we must quote our string to following C syntax
    ////     cannot type the literal here, it makes our code rather human unreadable
    ////     very very unconformable!
    ////
    ///* 
    //*/

    //const char *const s1    =   "ab'c";
    //const char (* s1_r1) [] = { "ab''c",    // ab''c
    //                            "ab\\'c",   // ab\'c
    //                           };//
    ///*
    // .      '.'     \.
    // ..     \.\.    '.'\.   '.'\.   '..'    // '.''.'  wrong
    //*/

    //const char *const s2    =   "a..'.b";       // a..'.b
    //const char (*s2_r) []   = { "a'..''.'b"     // a'..''.'b   
    //                           ,"a'..\\'.'b"    // a'..\'.'b
    //                           ,"a'..'\\''.'b"  // a'..'\''.'b
    //                          };//

    //const char *const s3    =   "a..\\.b";      // a..\.b
    //const char (*s3_r) []   = { "a'..\\\\.'b"   // a'..\\.'b
    //                           ,"a'..'\\\\'.'b" // a'..'\\'.'b
    //                          };//

    //                            // no catact operation, no choice, must be compact

    srand((unsigned)time( NULL ));
    
    //Escaper l(Escaper::NO, Escaper::NO, Escaper::RAND_ESC);
    Pick *p = new Literal(str);
    Quote q(*p);

    DUMP_R(TestQuote, (*p), 1);
    DUMP_R(TestQuote, q, 20);
    return FALSE;
}
static UBool TestLiteral(){
    const char * s = "test string99.";
    Literal n(s);
    const char * r = n.next();
    return strcmp(s,r) == 0;
}

static UBool TestSequence(){
    Sequence seq;
    seq.append(new Literal("abc "));
    seq.append(new Literal(", s"));

    return strcmp(seq.next(), "abc , s") == 0;
}
static UBool TestAlternation(){
    srand((unsigned)time( NULL ));
    Alternation alt;
    alt.append(new Literal("aaa_10%"),10);
    alt.append(new Literal("bbb_0%"),0);
    alt.append(new Literal("ccc_10%"),10);
    alt.append(new Literal("ddddddd_50%"),50);

    DUMP_R(TestAlternation, alt, 50);

    return FALSE;
}

static UBool TestBuffer(){
    Buffer_int t;
    t.append(1).append(0).append(5);
    int s = t.content_size();
    for (int i=0; i<s; ++i){
        printf("%d\n", t[i]);
    }
    return FALSE;
}

static UBool TestWeightedRand(){
    srand((unsigned)time( NULL ));
    Buffer_int t;
    t.append(1).append(0).append(5);
    WeightedRand wr(&Buffer_int().append(10).append(0).append(50),4);
//    WeightedRand wr(&t,3);
    for (int i=0; i< 50; ++i){
        printf("%d\n", wr.next());
    }
    return FALSE;
}

static UBool TestRepeat(){
    srand((unsigned)time( NULL ));
    Repeat rep(new Literal("aaa1-5 "), 1, 5);
    DUMP_R(TestRepeat, rep, 50);

    Repeat r2(new Literal("b{1,3}1%0%5% "), 1, 3, &Buffer_int().append(1).append(0).append(5));
    DUMP_R(TestRepeat, r2, 50);

    Repeat r3(new Literal("aaa5-5 "), 5, 5);
    DUMP_R(TestRepeat, r3, 50);

    return FALSE;
}

static UBool TestVariable(){
    SymbolTable tab;
    Pick * value = new Literal("string1");
    Variable var1(&tab, "x", value);

    Variable var2(&tab, "y");
//    tab.put(var2, value); // TOFIX: point alias/recursion problem
    Pick * value2 = new Literal("string2");
    tab.put(var2, value2);

    Pick * value3 = new Literal("string3");
    Variable var3(&tab, "z");
    tab.put("z", value3);

    UBool pass;
    pass = strcmp(var1.next(), value->next()) == 0;
    pass = pass && strcmp(var2.next(), value2->next()) == 0;
    pass = pass && strcmp(var3.next(), value3->next()) == 0;
    return pass;
}

static UBool TestSymbolTable(){
    Literal * n1 = new Literal("string1");
    Literal * n2 = new Literal("string2");
    SymbolTable t;
    t.put("abc", n1);
    t.put("$aaa", n2);
//    t.put("alias", n1);  // TOFIX: point alias/recursion problem
    t.put("bbb");

    UBool pass;
    pass = t.find(NULL) == SymbolTable::EMPTY;
    pass = pass && t.find("ccc") == SymbolTable::NO_VAR;
    pass = pass && t.find("bbb") == SymbolTable::NO_REF;
    pass = pass && t.find("abc") == SymbolTable::HAS_REF;
    pass = pass && t.find("$aaa") == SymbolTable::HAS_REF;

    t.reset();
    pass = pass && t.find("abc") == SymbolTable::NO_VAR;
    return pass;
}


static UBool TestScanner(void){
    //const char str1[] = "$root = $command{0,5} $reset $mostRules{1,20};";
    //const char str1_r[][20] = {"$root", "=", "$command", "{", "0", ",", "5", "}", 
    //    "$reset", "$mostRules", "{", "1", ",", "20", "}", ";"};

    const char str2[] = "$p2 =(\\\\ $s $string $s)? 25%;";
    const char str2_r[][20] = {"$p2", "=", "(", "\\", "$s", "$string", "$s", ")", "?", "25", "%", ";"};

    const char *str = str2;
    const char (*str_r)[20] = str2_r;
    int tokenNum = sizeof(str2_r)/sizeof(char[20]);

    Scanner t(str);
    UBool pass = TRUE;
    t.getNextToken();
    int i = 0;
    while (pass){
        if (t.tokenType == STREAM_END){
            pass = pass? i == tokenNum : FALSE;
            break;//while
        } else if (t.tokenType == ERROR){
            pass = FALSE;
            break;//while
        } else {
            pass = strcmp( &(t.token[0]), str_r[i++]) == 0;
            t.getNextToken();
        }
    }

    //const char ts[] = "$commandList = '['"
    //" ( alternate ' ' $alternateOptions"
    //" | backwards ' 2'"
    //" | normalization ' ' $onoff "
    //" | caseLevel ' ' $onoff "
    //" | hiraganaQ ' ' $onoff"
    //" | caseFirst ' ' $caseFirstOptions"
    //" | strength ' ' $strengthOptions"
    //" ) ']';" ;

    //Scanner t2(ts);
    //pass = TRUE;
    //do {
    //    t2.getNextToken();
    //    if (t2.tokenType == ERROR){
    //        DumpScanner(t2);
    //        return FALSE;
    //    }
    //}while (t.tokenType != STREAM_END);

    return pass;
}

class TestParserT {
public:
UBool operator () (const char *const str, const int exp_error_offset = -1, const UBool dump = TRUE){
    Parser par(str);
    if (par.rules()){
        if ( exp_error_offset == -1){
            return TRUE;
        }else {
            DumpScanner(par.s,dump);
            return FALSE;
        }
    }else {
        return DumpScanner(par.s, dump) == exp_error_offset;
    }
}
};

UBool TestParser(){
    TestParserT test;

    UBool pass = TRUE;
    pass = pass && test ("$s = ' ' ? 50%;");
    pass = pass && test("$x = ($var {1,2}) 3%;");         // legal
    pass = pass && test("$x = $var {1,2} 3% | b 4%;");    // legal
    pass = pass && test("$x = $var {1,2} 3%;");           // legal
    pass = pass && test("$m = $c ? 2% 4% | $r 5% | $n 25%;"); // legal
    pass = pass && test("$a = b ? 2% | c 5%;");               // legal
    pass = pass && test("$x = A B 5% C 10% | D;", 8, FALSE);  // illegal 5%
    pass = pass && test("$x = aa 45% | bb 5% cc;", 19, FALSE);// illegal cc
    pass = pass && test("$x = (b 5%) (c 6%);");               // legal
    pass = pass && test("$x = (b 5%) c 6%;", 13, FALSE);      // illegal 6%
    pass = pass && test("$x = b 5% (c 6%);", 9, FALSE);       // illegal (c 6%)
    pass = pass && test("$x = b 5% c 6%;", 9, FALSE);         // illegal c 6%
    pass = pass && test("$x = b 5%;");                        // legal
    pass = pass && test("$x = aa 45% | bb 5% cc;", 19, FALSE);// illegal cc
    pass = pass && test("$x = a | b  | c 4% | d 5%;");        // legal
    pass = pass && test("$s = ' ' ? 50% abc;");               // legal
    pass = pass && test("$s =  a | c d | e f;");              // legal
    pass = pass && test( "$z = q 0% | p 1% | r 100%;");         // legal How to check parsed tree??

    pass = pass && test("$s = ' ' ? 50%;");
    pass = pass && test("$relationList = '<' | '<<' |  ';' | '<<<' | ',' | '=';");
    pass = pass && test("$p1 = ($string $s '|' $s)? 25%;");
    pass = pass && test("$p2 = (\\\\ $s $string $s)? 25%;");
    pass = pass && test("$rel2 = $p1 $string $s $p2;");
    pass = pass && test("$relation = $relationList $s ($rel1 | $rel2) $crlf;");
    pass = pass && test("$command = $commandList $crlf;");
    pass = pass && test("$reset = '&' $s ($beforeList $s)? 10% ($positionList 100% | $string 10%) $crlf;");
    pass = pass && test("$mostRules = $command 1% | $reset 5% | $relation 25%;");
    pass = pass && test("$root = $command{0,5} $reset $mostRules{1,20};");

    const char collationBNF[] =
    "$s = ' '? 50%;" 
    "$crlf = '\r\n';" 

    "$alternateOptions = non'-'ignorable | shifted;" 
    "$onoff = on | off;" 
    "$caseFirstOptions = off | upper | lower;" 
    "$strengthOptions = '1' | '2' | '3' | '4' | 'I';" 
    "$commandList = '['"
    " ( alternate ' ' $alternateOptions"
    " | backwards ' 2'"
    " | normalization ' ' $onoff "
    " | caseLevel ' ' $onoff "
    " | hiraganaQ ' ' $onoff"
    " | caseFirst ' ' $caseFirstOptions"
    " | strength ' ' $strengthOptions"
    " ) ']';" 
    "$command = $commandList $crlf;" 

    "$ignorableTypes = (tertiary | secondary | primary) ' ' ignorable;" 
    "$allTypes = variable | regular | implicit | trailing | $ignorableTypes;" 
    "$positionList = '[' (first | last) ' ' $allTypes ']';"

    "$beforeList = '[before ' ('1' | '2' | '3') ']';"

    "$relationList = ("
    "   '<'"
    " | '<<'"
    " | ';'" 
    " | '<<<'"
    " | ','" 
    " | '='"
    ");"
    "$string = $magic;" 
    "$rel1 = '[variable top]' $s;" 
    "$p1 = ($string $s '|' $s)? 25%;" 
    "$p2 = (\\\\ $s $string $s)? 25%;" 
    "$rel2 = $p1 $string $s $p2;" 
    "$relation = $relationList $s ($rel1 | $rel2) $crlf;" 

    "$reset = '&' $s ($beforeList $s)? 10% ($positionList 1% | $string 10%) $crlf;" 
    "$mostRules = $command 1% | $reset 5% | $relation 25%;"
    "$root = $command{0,5} $reset $mostRules{1,20};"
    ;
    
    pass = pass && test(collationBNF);


    return pass;
}

static UBool TestMorph(){
    srand((unsigned)time( NULL ));

    Alternation * alt = new Alternation();

    (*alt)
    .append(new Literal("a")).append(new Literal("b")).append(new Literal("c"))
    .append(new Literal("d")).append(new Literal("e")).append(new Literal("f"))
    .append(new Literal("g")).append(new Literal("h")).append(new Literal("i"))
    .append(new Literal("j")).append(new Literal("k")).append(new Literal("l"))
    .append(new Literal("m")).append(new Literal("n")).append(new Literal("o"))
    ;

    Repeat * rep = new Repeat( alt ,5,5 );
    Morph m( *rep);

//    DUMP_R(TestMorph,(*rep),20);
    DUMP_R(TestMorph,m,100);

    return FALSE;
}

#endif

static UBool TestLanguageGenerator(){
    //LanguageGenerator g;
    //const char *const s = "$s = p 0% | q 1%;";
    //g.parseBNF(s, "$s");
    UBool pass;
    //= strcmp("q", g.next()) == 0;

    const char *const def = 
        //"$a = $b;"
        //"$b = $c;"
        //"$c = $t;"
        //"$t = abc $z{1,2};"
        //"$k = a | b | c | d | e | f | g ;"
        //"$z = q 0% | p 1% | r 1%;"
        "$x = a ? 0%;"
        ; // end of string
//    const char * s = "abczz";
//
//
    LanguageGenerator g;
    pass = g.parseBNF(def, "$x",TRUE);
////    LanguageGenerator g(collationBNF, "$root", "$magic", new MagicNode());
//  
    if (pass != LanguageGenerator::OK) return FALSE;
    
    DUMP_R(TestLanguageGenerator, g, 20);
    return pass;

    ////UBool pass = strcmp(s,r) == 0;

    //if (pass){
    //    printf("TestRandomLanguageGenerator passed.\n");
    //} else {
    //    printf("TestRandomLanguageGenerator FAILED!!!\n");
    //}
    //return pass;
}

void TestWbnf(void){
    srand((unsigned)time( NULL ));

    //CALL(TestLiteral);
    //CALL(TestSequence);
    //CALL(TestSymbolTable);
    //CALL(TestVariable);

    //TestRepeat();
    //TestAlternation();
    //TestMorph();

    //TestQuote();
    //TestBuffer();
    //TestWeightedRand();

    //CALL(TestScanner);
    //CALL(TestParser);
    CALL(TestLanguageGenerator);
}