// TO DO:  1) replace tokenizing with PL version from pl2html
//         2) replace pl grammar with pl grammar from
//             http://www.antlr.org/grammar/1107752678378/PLSQLGrammar.g

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define ishex(ch) (int)strchr("0123456789ABCDEFabcdef", ch)
int hextobin(char x)
{
  // Too crude, just a quick hack.  Seldom called.
  if (('0' <= x) && (x <= '9')) {        return x-'0';
  } else if (('A' <= x) && (x <= 'F')) { return x-'A'+10;
  } else if (('a' <= x) && (x <= 'f')) { return x-'a'+10;
  } else {                               return 0;
    // ASSERT: INTERNAL ERROR!
  }
}
void indent(int depth, FILE *f)
{
  int i;
  for (i = 0; i < depth; i++) fprintf(f, "  ");
}

#define DEBUG_PARSER
#include "pl.h" // GENERATED GRAMMAR FROM takeon.c and pl.g

#ifndef FALSE
#define FALSE (0!=0)
#define TRUE (0==0)
#endif

char *progname;

int debug_parser = FALSE;

// Built-in phrase codes.  Must match with grammar file.
#define TYPE_EOF 0
#define TYPE_QUOTED_STRING 1
#define TYPE_IDENTIFIER 2
#define TYPE_KEYWORD 99
#define TYPE_CHAR 100

typedef struct sourceinfo { // ATOMS for processed input stream
  char *s; // string contents
  int l;   // lineno
  int col; // column
  int t;   // type - tag, "string", 'charconst', or char, so far
  char *f; // source or includefile  name
} sourceinfo;

int bestparse = -1; // for error reporting.
char *looking_for = "<UNKNOWN>";    // 'while looking for <PHRASENAME>' (or literal) ...

// C is the source character token stream
static sourceinfo *c = NULL;
int nextfree = 0, arraysize = 0;
char *onecharstr;

// A is the Analysis record.  Contents point to a C[] structure when the item is a BIP,
// otherwise the format is [phraseno] [altno] [no-of-subphrases] [subphrases and/or BIPs...]
// eg A[25] = 10 means that A[25] is C[10].
static int *A = NULL;
int next_free_a = 0, a_size = 0;


static void *makespace_(void *c, int nextfree, int *arraysize, int objsize) {
  if (nextfree >= *arraysize) {
    *arraysize = (*arraysize * 2) + 1;
    c = (void *)realloc(c, (*arraysize+1) * objsize); // 0:arraysize, inclusive. eg 0:15, 0:127 etc
    if (c == NULL) {fprintf(stderr, "tacctwo: %s\n", strerror(errno)); exit(errno);}
  }
  return c;
}
#define makespace(c, nextfree, arraysize) c = (typeof(c))makespace_(c, nextfree, &arraysize, sizeof(c[0]))

void stores(char *s, int lineno, int col, int type, char *fname) {
  makespace(c, nextfree, arraysize);
  c[nextfree].s = s; c[nextfree].l = lineno; c[nextfree].col = col;
  c[nextfree].f = fname; c[nextfree].t = type;
  nextfree++;
}

void storec(int ch, int lineno, int col, int type, char *fname) {
  onecharstr[ch*2] = ch; onecharstr[ch*2+1] = '\0';  // convert char to 1-char string before saving.
  stores(&onecharstr[ch*2], lineno, col, type, fname);
}

int iskeyword(char *s) {
  int i;
  for (i = 0; i < MAX_KEYWORD; i++) if (strcasecmp(s, keyword[i]) == 0) return TRUE;
  return FALSE;
}



//--------------------------------------------------------------------------

FILE *sourcefile;
char *curfile;
int startline = TRUE, whitespace = TRUE, lineno = 1, col = 0, ch, peek;

void line_reconstruction(void)  // PRE-PROCESS INPUT READY FOR PARSING
{
  for (;;) {
    ch = fgetc(sourcefile); if (ch == EOF) break;
    ch &= 255; // int, positive.

    peek = fgetc(sourcefile); ungetc(peek, sourcefile);

    if (isalpha(ch)) {
        int nextfree = 0, strsize = 0, startcol = col;
        char *token = NULL;

        whitespace = FALSE;
        for (;;) {
          makespace(token, nextfree, strsize);
          if (isalpha(ch) || isdigit(ch) || (ch == '_')) {
            // digits and '_' allowed after 1st char.
            col++;
            token[nextfree++] = ch;
          } else {
            token[nextfree] = '\0'; ungetc(ch, sourcefile);
            break;
          }
          ch = fgetc(sourcefile);
        }
        stores(strdup(token), lineno, startcol, iskeyword(token) ? TYPE_KEYWORD : TYPE_IDENTIFIER, curfile);
        free(token);
#ifdef NEVER
    } else if (isdigit(ch)) {
        int nextfree = 0, numsize = 0;
        char *number = NULL;

        // Store as a string...
        whitespace = FALSE;
        for (;;) {
          makespace(number, nextfree, numsize);
          if (isdigit(ch)) {
            col++;
            number[nextfree++] = ch;
          } else {
            number[nextfree] = '\0'; ungetc(ch, sourcefile);
            break;
          }
          ch = fgetc(sourcefile);
        }
        stores(strdup(number), lineno, col, TYPE_INT, curfile);
        free(number);
#endif
    } else switch (ch) {
//    case '\'': // Handle 'c' character constants
//    case '"': // Handle "string"
    case '\'': // Handle 'string'
      {
        int nextfree = 0, strsize = 0, quotech = ch;
        char *string = NULL;

        whitespace = FALSE;
        col++;
        for (;;) {
          ch = fgetc(sourcefile); // Newlines are allowed
          col++;
          makespace(string, nextfree, strsize);
          if (ch == '\\') {
            ch = fgetc(sourcefile); col++;
            if (ch == '\\') {           string[nextfree++] = ch;
            } else if (ch == '\'') {    string[nextfree++] = '\'';
            } else if (ch == '"') {     string[nextfree++] = '"';
            } else if (ch == 'n') {     string[nextfree++] = '\n';
            } else if (ch == 'r') {     string[nextfree++] = '\r';
            } else if (ch == 't') {     string[nextfree++] = '\t';
            } else if (ch == 'x') {
              int x, x1, x2;
              x1 = fgetc(sourcefile); col++;
              if (!ishex(x1)) {
                // WARN: Bad format
                continue;
              }
              x2 = fgetc(sourcefile); col++;
              if (!ishex(x2)) {
                // WARN: Bad format
                continue;
              }
              x = (hextobin(x1)<<4) | hextobin(x2);
              if (x == 0) {
                // WARN: embedded NUL in a string is asking for trouble...
              }
              string[nextfree++] = x;
            } else {
              // Warn of unknown (to me) \x escape.  Probably an error.
	      string[nextfree++] = '\\'; string[nextfree++] = ch;
            }
          } else if (ch != quotech) {   string[nextfree++] = ch;
          } else {
            string[nextfree] = '\0';
            break;
          }
        }

        if (quotech == '\'') {
          if (strlen(string) == 1) {
          } else if (strlen(string) <= 4) {
            // Warn that 'xx' as a 32-bit int is a non-standard extension
          } else {
            // Warn that this is probably a string with the wrong type of quote.
          }
        }
//        stores(strdup(string), lineno, col, (quotech == '\'' ? TYPE_CHARCONST : TYPE_STRING), curfile);
        stores(strdup(string), lineno, col, TYPE_QUOTED_STRING, curfile);
        free(string);
      }
      break;

    case '/': // COMMENTS (or just a divide symbol)
      col++;
      whitespace = FALSE;
      if (peek == '/') {
        // Handle line comment
        do {ch = fgetc(sourcefile);} while (ch != '\n');
        lineno++; col = 0; whitespace = TRUE;
      } else if (peek == '*') {
        /* Handle potential multi-line comment */
        ch = fgetc(sourcefile); // Now we have read '/*'
        for (;;) {
          col++;
          ch = fgetc(sourcefile); peek = fgetc(sourcefile);
          if ((ch == '*') && (peek == '/')) break;
          ungetc(peek, sourcefile);
        }
        col += 2;
        (void)fgetc(sourcefile); // Remove '/'
        // QUESTION: How does this affect # directives? 
      } else {
        storec(ch, lineno, col, TYPE_CHAR, curfile);
      }
      break;

    // WHITESPACE
    case '\n':      lineno++;
    case '\r':      startline = TRUE; col = 0; whitespace = TRUE;
      break;

    case '\t':
    case ' ':       col++; // Does not affect whitespace
      break;

    // DIRECTIVES
    case '#':
      // If we interpret any #-directives while lexing, we don't want to
      // do an expensive test on every token, so what we can do is set
      // a countdown timer on the introductory token (either this '#'
      // or the actual keyword such as 'ifdef') and then test that the
      // *previous* tokens match when the timer hits 0, eg
      // C[cp-3] == '#' && C[cp-2] == 'include' ... etc

      if (!whitespace) {
        // WARN: probably an error... should not be any '#' symbols in the
        // middle of a line. (This language uses "!=" or "<>" for not-equal)
      }
      // Drop through

    default:
      whitespace = FALSE;
      storec(ch, lineno, col++, TYPE_CHAR, curfile);
    }
  }
  // set up a dummy at the end because we sometimes look ahead by 1
  // in the parsing code and don't want to hit uninitialised data.
  c[nextfree].t = TYPE_EOF;
  c[nextfree].s = "<EOF>";
  c[nextfree].l = lineno;
  c[nextfree].col = col;
}

//--------------------------------------------------------------------------

int cp = 0;  // code pointer.  Has to be global state.
int ap = 0;  // Analysis record pointer.

int parse(int pp, int depth) // depth is only for indentation in diags
{
  int saved_cp, saved_ap, i, gp, alts, match;

  gp = phrase_start[pp-512-MAX_BIP];
  alts = gram[gp];

  if (debug_parser) {
    fprintf(stdout, "\n");
    indent(depth, stdout);
    fprintf(stdout, "Phrase %s/%d (%d alternatives) = ", phrasename[pp-512], pp, alts);
    fflush(stdout);
  }
  gp++; // gp now points to first element (length) of first alt

  saved_cp = cp;
  saved_ap = ap;

  for (i = 0; i < alts; i++) {
    int each, phrases = gram[gp++], phrase_count, gap = 0;

    cp = saved_cp;
    ap = saved_ap;

    if (ap+3 > next_free_a) next_free_a = ap+3;
    makespace(A, next_free_a, a_size);
    A[ap++] = pp;   // record which phrase (could be done outside loop)
    A[ap++] = i;    // and which alt.


    // Count slots needed.  *Could* be precalculated and stored
    // in the grammar, either embedded (after the ALT) or as a
    // separate table

    for (each = 0; each < phrases; each++) if (gram[gp+each] >= 512) gap++;
    A[ap++] = gap;    // Count of alts (gap)
    // ap+gap now points to the slot after the space required, which
    // is where the first subphrase will be stored.
    ap = ap+gap; // recursive subphrases are stored after this phrase.
                 // ap is left updated if successful.


    // successfully parsed phrases are stored in A[saved_ap+3+n]

    if (saved_ap+3+gap > next_free_a) next_free_a = saved_ap+3+gap;
    makespace(A, next_free_a, a_size);

    // this loop is only for diagnostics
    if (debug_parser) {
      fprintf(stdout, "\n");
      indent(depth, stdout);
      fprintf(stdout, "Alternative %d: (%d phrases) ", i+1, phrases);
      for (each = 0; each < phrases; each++) {
        int phrase = gram[gp+each];
        if (phrase < 256) {
          fprintf(stdout, " '%c'", phrase);
        } else if (phrase < 512) {
          fprintf(stdout, " \"%s\"/%d", keyword[phrase-256], phrase-256);
        } else if (phrase < 512+MAX_BIP) {
          fprintf(stdout, " {%s/BIP%d}", phrasename[phrase-512], BIP[phrase-512]);
        } else {
          fprintf(stdout, " <%s:%d>", phrasename[phrase-512], phrase);
        }
      }
      fprintf(stdout, "\n");
      fflush(stdout);
    }

    match = TRUE; // stays true if all subphrases match
    phrase_count = 0; // only phrases which make it into the A record,
                      // i.e. excluding literals and keywords
    for (each = 0; each < phrases; each++) {
      int phrase = gram[gp+each];

      if (debug_parser) {
        indent(depth, stdout);
        fprintf(stdout, "Input token stream = '%s' '%s' '%s' ...\n",
          (cp < nextfree ? c[cp].s : "EOF"),
          (cp+1 < nextfree ? c[cp+1].s : "EOF"),
          (cp+2 < nextfree ? c[cp+2].s : "EOF"));
      }
      if (cp > bestparse) {
        static char s[128];
        if (phrase < 256) {
          sprintf(s, "'%c'", phrase);
        } else if (phrase < 512) {
          sprintf(s, "\"%s\"", keyword[phrase-256]);
        } else if (phrase < 512+MAX_BIP) {
          sprintf(s, "{%s}", phrasename[phrase-512]);
        } else {
          sprintf(s, "<%s>", phrasename[phrase-512]);
        }
        looking_for = s;
        bestparse = cp;
      }

      if (debug_parser) indent(depth, stdout);
      if (phrase < 256) {
        if (debug_parser) fprintf(stdout, "'%c'", phrase);
        if ((c[cp].t != TYPE_CHAR) || (c[cp].s[0] != phrase)) match = FALSE; else cp++;
        // Don't record literals
      } else if (phrase < 512) {
        if (debug_parser) fprintf(stdout, "\"%s\"/%d", keyword[phrase-256], phrase-256);
        if (strcasecmp(keyword[phrase-256], c[cp].s) != 0) match = FALSE; else cp++;
        // Don't record keywords
      } else if (phrase < 512+MAX_BIP) {
        int where = ap; // next phrase to be parsed will be stored at current 'ap'.
        if (debug_parser) fprintf(stdout, "{%s/BIP%d}", phrasename[phrase-512], BIP[phrase-512]);
        if (c[cp].t != BIP[phrase-512]) match = FALSE; else {
          A[ap++] = phrase;
          A[ap++] = 1;
          A[ap++] = 1;
          A[ap++] = cp++;
          A[saved_ap+3+phrase_count++] = where; // Record BIP
        }
      } else {
        int where = ap; // next phrase to be parsed will be stored at current 'ap'.
        if (debug_parser) fprintf(stdout, "<%s:%d>", phrasename[phrase-512], phrase);
        if (!parse(phrase, depth+1)) match = FALSE; else {
          A[saved_ap+3+phrase_count++] = where;
        }
      }
      if (debug_parser) {
        fprintf(stdout, "\n");
        indent(depth, stdout);
        fprintf(stdout, "Tried alternative %d: result was %s\n", each+1, (match ? "TRUE" : "FALSE"));
        fflush(stdout);
      }
      if (!match) break;
    }
    gp += phrases; // move over all phrases, to next alt
    if (match) break; else if (debug_parser) {
      indent(depth, stdout);
      fprintf(stdout, "** Alternative %d FAILED.\n", i+1);
    }
    // gp now points to first element (length) of next alt, or start of next phrase
  }
  return(match);
}

//--------------------------------------------------------------------------

typedef int TRIP;

//------------------------------------------------------------------------------------

/*
      In previous compilers, I had to write custom code for every tree-based
    optimisation, in order to walk down the tree to the right place to find
    the leaves to be optimised.  In this one, I have a generic tree-walking
    procedure which can walk the entire program, but it can be customised 
    so that it takes action only on specific phrases.  This is possible in
    this design only because each set of subphrases stores the count of
    subphrases befoe it - thus allowing a generic tree-walking procedure
    that doesn't have to know what each node consists of until it happens
    across a node of the type it is looking for.
 */

void walk(int ap, int depth, int wanted(int phraseno), void perform(int ap, int before, int depth))
{
  int i;

  if (wanted(A[ap])) perform(ap, TRUE, depth);
  for (i = 3; i < A[ap+2]+3; i++) {
    if (A[A[ap+i]] >= 512+MAX_BIP) walk(A[ap+i], depth+1, wanted, perform);
  }
  if (wanted(A[ap])) perform(ap, FALSE, depth);

}

int want_all(int phraseno) {
  return TRUE;
}

void print_all(int ap, int before, int depth) {
  int saved_ap = ap;
  int phrase = A[ap++];
  int alt = A[ap++];
  int phrases = A[ap++]; // defined subphrases
  int i;

  indent(depth, stderr);
  fprintf(stderr, "<%s%s:%d>  ", (before ? "" : "/"), phrasename[phrase-512], alt);
  for (i = 0; i < (3+phrases); i++) {
    fprintf(stderr, "A[%d] = %d, ", saved_ap+i, A[saved_ap+i]);
  }
  fprintf(stderr, "\n");
}

TRIP compile(int ap, int depth)
{
  int saved_ap = ap;
  int phrase = A[ap++];  // A[ap] is the phrase number. A[ap+1] is the alt.
  int alt = A[ap++]; // For consistency, in BIPs, the Alt should always be 1
                     // although that may not be the case at the moment :-(
  int phrases = A[ap++]; // defined subphrases
  int i;

  // The following ecce command executed on this file will generate pl.g:
  // ecce -c "(v.//\\.s..(v/ /e)?m,k)0;%c" pl.c pl.g
  // May later tweak takeon.c to read from pl.c rather than pl.g
  // thus simplifying build process and becoming more like yacc.

  switch (phrase) {

//\\ # BIPS (Built-in Phrases)are linked to the type-code returned
//\\ # by the line-reconstruction code (aka lexer)
//\\
//\\ # These *must* come first.

// See TYPE_* in first page for the values to use.

//   First, Built-in phrases:
//     'Quoted String'
//\\ B<QUOTED_STRING> = 1;
  case P_QUOTED_STRING:
    fprintf(stdout, "%s", c[A[ap]].s);
    break;

//     Token-like objects are grabbed as a unit and returned
//   either as an identifier or a keyword depending on whether
//   they are in the automatically-generated keyword dictionary.
//\\ B<IDENTIFIER> = 2;
  case P_IDENTIFIER:
    fprintf(stdout, "%s", c[A[ap]].s);
    break;

//   Comments are handled in the line reconstruction phase
//   and are attached to whichever token follows.  They are
//   not needed for compiling, but will be used in HTML display
//   or reformatting, and if we ever do a translator to another
//   language, we'll try to preserve them there too.

//   MAIN ENTRY POINT:
//   - this is OK for parsing whole programs but not for program
//     fragments, eg for use in a reformatter.  Also the SQLPlus
//     terminating '/' is not in the grammar.  May need to add it
//     to the line reconstruction later.

//\\ P<SS> = <ss_package>, <package_obj_body>;
  case P_SS:

//\\ P<ss_package> = <create_package> <create_package_list>;
  case P_ss_package:

//\\ P<create_package_list> = <create_package> <create_package_list>, ;
  case P_create_package_list:

//\\ P<create_package> = "create" <opt_or_replace> <package_spec_or_body>;
  case P_create_package:

//\\ P<opt_or_replace> = "or" "replace", ;
  case P_opt_or_replace:

//\\ P<package_spec_or_body> = <package_spec>, <package_body>;
  case P_package_spec_or_body:

//\\ P<package_spec> = "package" <package_name> <is_or_as> <package_obj_spec> <package_obj_spec_list> "end" <opt_package_name> <SEMI>;
  case P_package_spec:

//\\ P<is_or_as> = "is", "as";
  case P_is_or_as:

//\\ P<package_obj_spec_list> = <package_obj_spec> <package_obj_spec_list>, ;
  case P_package_obj_spec_list:

//\\ P<package_body> =
//\\         "package" <opt_body> <package_name> <is_or_as> <package_obj_body> <package_obj_body_list>
//\\         "begin"
//\\            <opt_seq_of_statements>
//\\         "end" <opt_package_name> <SEMI>;
  case P_package_body:

//\\ P<package_obj_body_list> = <package_obj_body> <package_obj_body_list>, ;
  case P_package_obj_body_list:

//\\ P<opt_body> = "body", ;
  case P_opt_body:

//\\ P<opt_package_name> = <package_name>, ;
  case P_opt_package_name:

//\\ P<package_name> = <opt_schema_name_DOT> <identifier>;
  case P_package_name:

//\\ P<opt_schema_name_DOT> = <schema_name> <DOT>, ;
  case P_opt_schema_name_DOT:

//\\ P<package_obj_spec> =
//\\           <variable_declaration>
//\\         , <subtype_declaration>
//\\         , <cursor_declaration>
//\\         , <cursor_spec>
//\\         , <exception_declaration>
//\\         , <record_declaration>
//\\         , <plsql_table_declaration>
//\\         , <procedure_spec>
//\\         , <function_spec>
//\\         ;
  case P_package_obj_spec:

//\\ P<variable_declaration> =
//\\         <variable_name> <opt_constant>
//\\         <type_spec> <opt_not_null>
//\\         <opt_assign_or_default_plsql_expression> <SEMI>
//\\        ;
  case P_variable_declaration:

//\\ P<opt_constant> = "constant", ;
  case P_opt_constant:

//\\ P<opt_not_null> = "not" "null", ;
  case P_opt_not_null:

//\\ P<assign_or_default> = <ASSIGNMENT_EQ>, "default";
  case P_assign_or_default:

//\\ P<opt_assign_or_default_plsql_expression> = <assign_or_default> <plsql_expression>, ;
  case P_opt_assign_or_default_plsql_expression:

//\\ P<subtype_declaration> =
//\\        "subtype" <type_name> "is" <type_spec> <SEMI>
//\\        ;      	
  case P_subtype_declaration:

//\\ P<cursor_declaration> =
//\\         "cursor" <cursor_name>
//\\         <opt_parameter_spec_list>
//\\        "is" <select_command> <SEMI>
//\\        ;
  case P_cursor_declaration:

//\\ P<opt_parameter_spec_list> =
//\\        <OPEN_PAREN> <parameter_spec> <parameter_spec_list> <CLOSE_PAREN>, ;
  case P_opt_parameter_spec_list:

//\\ P<parameter_spec_list> = <COMMA> <parameter_spec> <parameter_spec_list>, ;
  case P_parameter_spec_list:

//\\ P<package_obj_body> =
//\\           <variable_declaration>
//\\         , <subtype_declaration>
//\\         , <cursor_declaration>
//\\         , <exception_declaration>
//\\         , <record_declaration>
//\\         , <plsql_table_declaration>
//\\         , <procedure_body>
//\\         , <function_body>
//\\         ;
  case P_package_obj_body:

//\\ P<seq_of_statements> =
//\\         <statement> <SEMI> <seq_of_statements_list>;
  case P_seq_of_statements:

//\\ P<seq_of_statements_list> = 
//\\         <statement> <SEMI> <seq_of_statements_list>, ;
  case P_seq_of_statements_list:

//\\ P<statement> =
//\\           <assignment_statement>
//\\         , <exit_statement>
//\\         , <goto_statement>
//\\         , <if_statement>
//\\         , <loop_statement>
//\\         , <null_statement>
//\\         , <raise_statement>
//\\         , <return_statement>
//\\         , <sql_statement>
//\\         , <begin_block>
//\\         , <function_call>
//\\         ;
  case P_statement:

//\\ P<plsql_block> =
//\\         <opt_label>
//\\         <opt_declare_specs>
//\\         "begin"
//\\         <seq_of_statements>
//\\         <opt_exception_section>
//\\         <end_label>
//\\         ;
  case P_plsql_block:

//\\ P<opt_declare_specs> = <opt_declare> <declare_spec_list>, ;
  case P_opt_declare_specs:

//\\ P<opt_declare> = "declare", ; # SHOULD THIS REALLY BE OPTIONAL?
  case P_opt_declare:

//\\ P<declare_spec_list> = <declare_spec> <opt_declare_spec_list>;
  case P_declare_spec_list:

//\\ P<opt_declare_spec_list> = <declare_spec> <opt_declare_spec_list>, ;
  case P_opt_declare_spec_list:

//\\ P<end_label> = "end" <opt_label_name> <SEMI> ;
  case P_end_label:

//\\ P<opt_label_name> = <label_name>, ;
  case P_opt_label_name:

//\\ P<opt_exception_section> = "exception" <exception_handler> <opt_exception_handler_list>, ;
  case P_opt_exception_section:

//\\ P<opt_exception_handler_list> = <exception_handler> <opt_exception_handler_list>, ;
  case P_opt_exception_handler_list:

//\\ P<opt_label> = <START_LABEL> <label_name> <END_LABEL>, ;
  case P_opt_label:

//\\ P<declare_spec> =
//\\           <variable_declaration>
//\\         , <subtype_declaration>
//\\         , <cursor_declaration>
//\\         , <exception_declaration>
//\\         , <exception_pragma>
//\\         , <record_declaration>
//\\         , <plsql_table_declaration>
//\\         , <procedure_declaration>
//\\         , <function_declaration>
//\\         ;
  case P_declare_spec:

//\\ P<assignment_statement> =
//\\           <object> <ASSIGNMENT_EQ> <assignment_rhs>
//\\         , <function_call>
//\\         ;
  case P_assignment_statement:

//\\ P<assignment_rhs> = <function_call>, <plsql_expression>;
  case P_assignment_rhs:

//\\ P<object> =
//\\           <record_term> <DOT> <opt_record_object_dot> <field_name>
//\\         , <record_table_item>
//\\         , <variable_name>
//\\         , <COLON> <host_variable>
//\\         ;
  case P_object:

//\\ P<opt_record_object_dot> = <record_term> <DOT> <opt_record_object_dot>, ;

//\\ P<record_term> = <record_name>, <record_table_item>;

//\\ P<record_table_item> = <plsql_table_name> <OPEN_PAREN> <subscript> <CLOSE_PAREN>;

//\\ P<field_name> = <identifier>;
  case P_field_name:

//\\ P<subscript> = <plsql_expression>;
  case P_subscript:

//\\ P<host_variable> = <identifier>;
  case P_host_variable:

//\\ P<goto_statement> = "goto" <label_name>;
  case P_goto_statement:

//\\ P<label_name> = <identifier>;	
  case P_label_name:

//\\ P<exit_statement> = "exit" <opt_label_name> <opt_when>;
  case P_exit_statement:

//\\ P<opt_when> = "when" <plsql_condition>, ;
  case P_opt_when:

//\\ P<datatype> =
//\\          "binary_integer"
//\\        , "natural"
//\\        , "positive"
//\\        , "number" <opt_bracketed_number_list>
//\\        , "char" <opt_bracketed_number>
//\\        , "long" <optraw>
//\\        , "raw"
//\\        , "boolean"
//\\        , "date"
//\\        , "smallint"
//\\        , "real"
//\\        , "numeric"
//\\        , "int"
//\\        , "integer"
//\\        , "pls_integer"
//\\        , "double" "precision"
//\\        , "decimal"
//\\        , "dec"
//\\        , "varchar2" <opt_bracketed_number>
//\\        , "varchar" <opt_bracketed_number>
//\\        , "character" <opt_bracketed_number>
//\\        , "mlslabel"
//\\        ;
  case P_datatype:

//\\ P<optraw> = "raw", ;
  case P_optraw:

//\\ P<opt_bracketed_number> = <OPEN_PAREN> <NUMBER> <CLOSE_PAREN>, ;
  case P_opt_bracketed_number:

//\\ P<opt_bracketed_number_list> = <OPEN_PAREN> <NUMBER> <OPT_NUMBER_LIST> <CLOSE_PAREN>, ;
  case P_opt_bracketed_number_list:

//\\ P<OPT_NUMBER_LIST> = <COMMA> <NUMBER> <OPT_NUMBER_LIST>, ;
  case P_OPT_NUMBER_LIST:

//\\ P<type_spec> =
//\\          <datatype>
//\\        , <variable_name> <PERCENTAGE> "type"
//\\        , <table_name> <DOT> <column_name> <PERCENTAGE> "type"
//\\        , <table_name> <PERCENTAGE> "rowtype"
//\\        , <type_name>
//\\        ;
  case P_type_spec:

//\\ P<type_name> = 
//\\         <identifier> <opt_dot_identifier_list>
//\\         ;
  case P_type_name:

//\\ P<opt_dot_identifier_list> = <DOT> <identifier> <opt_dot_identifier_list>, ;
  case P_opt_dot_identifier_list:

//\\ P<parameter_spec> = <parameter_name> <opt_type_spec>;
  case P_parameter_spec:

//\\ P<opt_type_spec> = <type_spec>, ;
  case P_opt_type_spec:

//\\ P<parameter_name> = <identifier>;
  case P_parameter_name:

//\\ P<cursor_spec> =
//\\         "cursor" <cursor_name>
//\\         <opt_parameter_spec_list>
//\\         "return" <return_type> <SEMI>
//\\         ;
  case P_cursor_spec:

//\\ P<procedure_spec> =
//\\         "procedure" <procedure_name>
//\\         <opt_argument>
//\\         <SEMI>
//\\         ;
  case P_procedure_spec:

//\\ P<opt_argument> =
//\\         <OPEN_PAREN> <argument> <opt_argument_list> <CLOSE_PAREN>, ;
  case P_opt_argument:

//\\ P<opt_argument_list> =
//\\         <COMMA> <argument> <opt_argument_list>, ;
  case P_opt_argument_list:

//\\ P<function_spec> =
//\\         "function" <function_name> <OPEN_PAREN> <argument> <opt_argument_list> <CLOSE_PAREN>
//\\         "return" <return_type> <SEMI>
//\\         ;
  case P_function_spec:

//\\ P<exception_declaration> =
//\\        <exception_name> "exception" <SEMI>
//\\        ;      	
  case P_exception_declaration:

//\\ P<exception_name> =
//\\         <opt_exception_package_name> <identifier>
//\\         ;
  case P_exception_name:

//\\ P<opt_exception_package_name> = <exception_package_name> <DOT>, ;
  case P_opt_exception_package_name:

//\\ P<exception_package_name> =
//\\         <identifier>
//\\         ;
  case P_exception_package_name:

//\\ P<exception_pragma> =
//\\         "pragma" "exception_init" <OPEN_PAREN> <exception_name> <COMMA> <oracle_err_number> <CLOSE_PAREN> <SEMI>;
  case P_exception_pragma:

//\\ P<oracle_err_number> =
//\\         <NUMBER>
//\\         ;
  case P_oracle_err_number:

//\\ P<record_declaration> =
//\\          <record_type_dec>
//\\        , <record_var_dec>
//\\        ;
  case P_record_declaration:

//\\ P<record_type_dec> =
//\\        "type" <type_name> "is" "record"
//\\        <OPEN_PAREN> <field_spec> <opt_field_spec_list> <CLOSE_PAREN> <SEMI>
//\\        ;
  case P_record_type_dec:

//\\ P<opt_field_spec_list> = <COMMA> <field_spec> <opt_field_spec_list>, ;
  case P_opt_field_spec_list:

//\\ P<record_var_dec> =
//\\        <record_name> <type_name> <PERCENTAGE> "rowtype" <SEMI>
//\\        ;	
  case P_record_var_dec:

//\\ P<field_spec> =
//\\        <column_spec>
//\\        ;
  case P_field_spec:

//\\ P<plsql_table_declaration> =
//\\        <table_type_dec>, <table_var_dec>
//\\        ;
  case P_plsql_table_declaration:

//\\ P<table_type_dec> =
//\\        "type" <type_name> "is" "table"
//\\        "of" <field_spec_or_datatype>
//\\        <index_by> <datatype> <SEMI>
//\\        ;
  case P_table_type_dec:

//\\ P<index_by> = "index" "by", "indexed" "by";

//\\ P<field_spec_or_datatype> = <field_spec>, <datatype>;

//\\ P<table_var_dec> =
//\\        <plsql_table_name> <type_name> <SEMI>
//\\        ;
  case P_table_var_dec:

//\\ P<plsql_table_name> =
//\\        <identifier>
//\\        ;    	
  case P_plsql_table_name:

//\\ P<procedure_declaration> =
//\\        <procedure_body>
//\\        ;
  case P_procedure_declaration:

//\\ P<procedure_body> =
//\\        <opt_proc_fun_start> "procedure" <procedure_name> <opt_argument>
//\\        <opt_return_type> <is_or_as>
//\\        <opt_declare_specs>
//\\        "begin"
//\\        <opt_seq_of_statements>
//\\        <opt_exception>
//\\        "end" <opt_procedure_name> <SEMI>
//\\        ;
  case P_procedure_body:

//\\ P<opt_procedure_name> = <procedure_name>, ;
  case P_opt_procedure_name:

//\\ P<opt_return_type> = "return" <return_type>, ;
  case P_opt_return_type:

//\\ P<opt_proc_fun_start> = <proc_fun_start>, ;
  case P_opt_proc_fun_start:

//\\ P<opt_seq_of_statements> = <seq_of_statements>, ;       
  case P_opt_seq_of_statements:

//\\ P<opt_exception> = "exception" <opt_exception_handler_list>, ;
  case P_opt_exception:

//\\ P<begin_block> =
//\\        "begin"
//\\        <opt_seq_of_statements>
//\\        <opt_exception>
//\\        "end"
//\\        ;
  case P_begin_block:

//\\ P<exception_handler> =
//\\         "when" <exception_name> <opt_or_exception_name_list> "then" <seq_of_statements>;
  case P_exception_handler:

//\\ P<opt_or_exception_name_list> = "or" <exception_name> <opt_or_exception_name_list>, ;
  case P_opt_or_exception_name_list:
//\\ P<proc_fun_start> =
//\\         "create" <opt_or_replace>
//\\         ;
  case P_proc_fun_start:

//\\ P<function_body> =
//\\        <opt_proc_fun_start> "function" <function_name>
//\\        <opt_argument>
//\\        "return" <return_type> <is_or_as>
//\\	    <opt_declare_specs>
//\\        "begin"
//\\        <opt_seq_of_statements>
//\\        <opt_exception>
//\\        "end" <opt_function_name> <SEMI>
//\\         ;
  case P_function_body:

//\\ P<opt_declare_spec> = "declare" <opt_declare_spec_list>, ;
  case P_opt_declare_spec:

//\\ P<function_name> =
//\\        <opt_schema_name_DOT> <identifier>;
  case P_function_name:

//\\ P<opt_function_name> = <function_name>, ;
  case P_opt_function_name:

//\\ P<procedure_name> =
//\\        <opt_schema_name_DOT> <identifier>;
  case P_procedure_name:

//\\ P<argument> =
//\\        <argument_name> <opt_in_out> <opt_in_out> <opt_argument_type>
//\\        <ass_or_def_val>
//\\        ;
  case P_argument:

//\\ P<ass_or_def_val> = <assign_or_default> <value>, ;
  case P_ass_or_def_val:

//\\ P<opt_argument_type> = <argument_type>, ;
  case P_opt_argument_type:

//\\ P<opt_in_out> = "inout", "in" "out", "in", "out", ;
  case P_opt_in_out:

//\\ P<argument_name> = <identifier>;
  case P_argument_name:

//\\ P<argument_type> = <type_spec>;
  case P_argument_type:

//\\ P<value> = <opt_PLUS_or_MINUS> <NUMBER>, <quoted_string>, "null";  # NULL added by GT
  case P_value:

//\\ P<opt_PLUS_or_MINUS> = <PLUS>, <MINUS>, ;
  case P_opt_PLUS_or_MINUS:

//\\ P<return_type> =
//\\        <type_spec>
//\\        ;
  case P_return_type:

//\\ P<function_declaration> =
//\\        <function_body>
//\\        ;
  case P_function_declaration:

//\\ P<function_call> =
//\\         <user_defined_function> <OPEN_PAREN> <expression> <expression_list> <CLOSE_PAREN>
//\\         ;
  case P_function_call:

//\\ P<expression_list> = <COMMA> <expression> <expression_list>, ;
  case P_expression_list:

//\\ P<variable_name> =
//\\         <identifier>
//\\         ;
  case P_variable_name:

//\\ P<null_statement> =
//\\         "null"
//\\         ;
  case P_null_statement:

//\\ P<raise_statement> =
//\\         "raise" <opt_exception_name>
//\\         ;
  case P_raise_statement:

//\\ P<opt_exception_name> = <exception_name>, ;
  case P_opt_exception_name:

//\\ P<return_statement> =
//\\         "return" <opt_plsql_expression>
//\\         ;
  case P_return_statement:

//\\ P<opt_plsql_expression> = <plsql_expression>, ;
  case P_opt_plsql_expression:

//\\ P<loop_statement> =
//\\         <opt_label_name>
//\\         <opt_loop_control> "loop"
//\\         <seq_of_statements>
//\\         "end" "loop" <opt_label_name>
//\\         ;
  case P_loop_statement:

//\\ P<opt_loop_control> =
//\\           "while" <plsql_condition>
//\\         , "for" <cursor_or_numeric_loop_param>
//\\         , ;
  case P_opt_loop_control:

//\\ P<cursor_or_numeric_loop_param> = <cursor_loop_param>, <numeric_loop_param>;
  case P_cursor_or_numeric_loop_param:

//\\ P<plsql_condition> = <boolean_exp>;
  case P_plsql_condition:

//\\ P<boolean_exp> = <opt_not> <boolean_term> <opt_or_term_list>;
  case P_boolean_exp:

//\\ P<opt_not> = "not", ;
//\\ P<opt_or_term_list> = "or" <boolean_term> <opt_or_term_list>, ;
//\\ P<boolean_term> = <boolean_factor> <opt_and_factor_list>;
//\\ P<opt_and_factor_list> = "and" <boolean_factor> <opt_and_factor_list>, ;

//\\ P<boolean_factor> =
//\\	     <object> <DOT> "exists" <OPEN_PAREN> <plsql_expression> <CLOSE_PAREN>  # added by GT
//\\       , <plsql_expression> <plsql_expression_qualifier>
//\\       , <function> <opt_bracketed_plsql_exp_list>
//\\       , <boolean_literal>
//\\       , <variable_name>
//\\       , <OPEN_PAREN> <boolean_exp> <CLOSE_PAREN>
//\\       ;

//\\ P<opt_bracketed_plsql_exp_list> = <OPEN_PAREN> <plsql_exp_list> <CLOSE_PAREN>, ;
//\\ P<opt_percent> = <PERCENTAGE> "notfound", <PERCENTAGE> "found", <PERCENTAGE> "isopen", ;

//\\ P<plsql_expression_qualifier> =
//\\         <relational_op> <plsql_expression>
//\\       , "is" <opt_not> "null"
//\\       , <opt_not> "like" <match_string>
//\\       , <opt_not> "between" <plsql_expression> "and" <plsql_expression>
//\\       , <opt_not> "in" <OPEN_PAREN> <plsql_exp_list> <CLOSE_PAREN>
//\\       , <cursor_or_subquery> <percent>;

//\\ P<cursor_or_subquery> = <cursor_name>, <subquery>;
//\\ P<percent> = <PERCENTAGE> "notfound", <PERCENTAGE> "found", <PERCENTAGE> "isopen";

//\\ P<boolean_literal> = "true", "false", "null";
//\\         
//\\ P<numeric_loop_param> =
//\\         <index_name> "in" <opt_reverse> <integer_expr> <DOUBLEDOT> <integer_expr>;
//\\ P<opt_reverse> = "reverse", ;

//\\ P<index_name> = <identifier>;

//\\ P<integer_expr> =
//\\           <num_expression>
//\\         , <type_spec>
//\\         ;

//\\ P<cursor_name> =
//\\         <identifier>
//\\         ;
//\\         
//\\ P<cursor_loop_param> =
//\\         <record_name> "in" <cursor_or_select>
//\\         ;

//\\ P<cursor_or_select> =
//\\           <cursor_name> <opt_bracketed_plsql_exp_list>
//\\         , <OPEN_PAREN> <select_statement> <CLOSE_PAREN>
//\\         ;
//\\         
//\\ P<record_name> =
//\\         <identifier>
//\\         ;
//\\         
//\\ P<plsql_expression> =
//\\         , <boolean_expression>
//\\         , <num_expression>
//\\         , <char_expression>
//\\         , <date_expression>
//\\         ;

//\\ P<num_expression> =
//\\         <opt_PLUS_or_MINUS> <num_term> <opt_term_list>
//\\         ;
//\\ P<PLUS_or_MINUS> = <PLUS>, <MINUS>;
//\\ P<opt_term_list> = <PLUS_or_MINUS> <num_term> <opt_term_list>, ;

//\\ P<num_term> =
//\\         <num_factor> <opt_factor_list>
//\\         ;
//\\ P<MUL_or_DIV> = <ASTERISK>, <DIVIDE>;
//\\ P<opt_factor_list> = <MUL_or_DIV> <num_factor> <opt_factor_list>, ;

//\\ P<num_factor> =
//\\         <basic_factor> <opt_exp_int>
//\\         ;

//\\ P<basic_factor> =
//\\            <numeric_literal>
//\\          , <variable_name>
//\\          , <host_variable>
//\\          , <function> <opt_bracketed_plsql_exp_list>
//\\          , <function> <opt_bracketed_plsql_exp_list> <opt_bracketed_plsql_exp_list_list> # GT
//\\          , <OPEN_PAREN> <num_expression> <CLOSE_PAREN>
//\\          , "null"
//\\          , <cursor_or_subquery> <PERCENTAGE> "rowcount"
//\\          ;

//\\ P<opt_bracketed_plsql_exp_list_list> = <opt_bracketed_plsql_exp_list> <opt_bracketed_plsql_exp_list_list>, ;
//\\ # As in table_name(1)(2)

//\\ P<opt_exp_int> = '*' '*' <integer_expr>, ;

//\\ P<numeric_literal> =
//\\         <NUMBER>
//\\         ;
//\\         
//\\ P<boolean_expression> =
//\\           <identifier>
//\\         , <column_spec>
//\\         , <boolean_literal>
//\\         ;
//\\         
//\\ P<plsql_exp_list> =
//\\         <plsql_expression> <opt_plsql_exp_list>
//\\         ;
//\\ P<opt_plsql_exp_list> = <COMMA> <plsql_expression> <opt_plsql_exp_list>, ;

//\\ P<char_expression> =
//\\         <char_term> <opt_concat_char_term_list>
//\\         ;
//\\ P<opt_concat_char_term_list>= <VERTBAR> <VERTBAR> <char_term> <opt_concat_char_term_list>, ;
//\\         
//\\ P<char_term> =
//\\           <char_literal>
//\\         , <variable_name>
//\\         , <host_variable>
//\\         , <function> <opt_bracketed_plsql_exp_list>
//\\         , <OPEN_PAREN> <char_expression> <CLOSE_PAREN>
//\\         , "null"
//\\         ;
//\\         
//\\ P<char_literal> =
//\\         <QUOTED_STRING>
//\\         ;
//\\         
//\\ P<date_expression> =
//\\           <date_literal>
//\\         , <variable_name>
//\\         , <host_variable>
//\\         , <function> <opt_bracketed_plsql_exp_list>
//\\         , <OPEN_PAREN> <date_expression> <CLOSE_PAREN>
//\\         , "null"
//\\       ;

//\\ P<date_literal> =
//\\         <QUOTED_STRING>
//\\         ;

//\\ P<commit_statement> =
//\\         "commit"
//\\         ;

//\\ P<if_statement> =
//\\         "if" <plsql_condition> "then" <seq_of_statements>
//\\ 	     <opt_elsif_list>
//\\ 	     <opt_else>
//\\         "end" "if"
//\\         ;

//\\ P<opt_elsif_list> =
//\\         "elsif" <plsql_condition> "then" <seq_of_statements> <opt_elsif_list>, ;

//\\ P<opt_else> = "else" <seq_of_statements>, ;
//\\         

//\\ P<sql_statement> =
//\\         <sql_command>
//\\         ;

//\\ P<sql_command> =
//\\           <to_modify_data>
//\\         , <to_control_data>
//\\         ;

//\\ P<to_modify_data> =
//\\           <select_command>
//\\         , <insert_command>
//\\         , <update_command>
//\\         , <delete_command>
//\\         , <set_transaction_command>
//\\         ;

//\\ P<to_control_data> =
//\\           <close_statement>
//\\         , <commit_statement>
//\\         , <fetch_statement>
//\\         , <lock_table_statement>
//\\         , <open_statement>
//\\         , <rollback_statement>
//\\         , <savepoint_statement>
//\\         ;
//\\         

//\\ P<delete_command> =
//\\ 	"delete" "from" <table_alias>
//\\ 	"where" <condition>;



//\\ P<select_command> =
//\\         <select_statement> <opt_union_list>
//\\         ;
//\\ P<opt_union_list> = "union" <select_statement> <opt_union_list>, ;

//\\ P<select_statement> =
//\\           <OPEN_PAREN> <select_command> <CLOSE_PAREN>
//\\         , <select_expression>
//\\         ;

//\\ P<select_expression> =
//\\         "select" <opt_all_or_distinct> <select_list>
//\\         <opt_into_col>
//\\         <table_reference_list_from>
//\\         <opt_where_condition_whole>
//\\         <opt_connect_clause>
//\\         <opt_group_clause>
//\\         <opt_set_clause>
//\\         <opt_order_clause>
//\\         <opt_update_clause>
//\\         ;

//\\ P<opt_all_or_distinct> = "all", "distinct", ;
//\\ P<opt_into_col> = "into" <column_name>, ;
//\\ P<opt_where_condition_whole> = <where_condition_whole>, ;
//\\ P<opt_connect_clause> = <connect_clause>, ;
//\\ P<opt_group_clause> = <group_clause>, ;
//\\ P<opt_set_clause> = <set_clause>, ;
//\\ P<opt_order_clause> = <order_clause>, ;
//\\ P<opt_update_clause> = <update_clause>, ;

//\\ P<select_list> =
//\\           <displayed_column> <opt_displayed_column_list>
//\\         , <ASTERISK>
//\\         ;
//\\ P<opt_displayed_column_list> = <COMMA> <displayed_column> <opt_displayed_column_list>, ;
//\\         
//\\ P<table_reference_list_from> =
//\\         "from" <selected_table> <opt_selected_table_list>
//\\      ;
//\\ P<opt_selected_table_list> = <COMMA> <selected_table> <opt_selected_table_list>, ;

//\\ P<table_reference_list> =
//\\         <selected_table> <opt_selected_table_list>
//\\      ;
//\\      
//\\ P<where_condition_whole> =
//\\        "where" <condition>
//\\        ;

//\\ P<where_condition> =
//\\        <condition>
//\\      ;

//\\ P<displayed_column> =
//\\           <opt_schema_name_DOT> <opt_variable> <table_name> <DOT> <ASTERISK>
//\\         , <exp_simple> <opt_alias>
//\\         ;
//\\ P<opt_alias> = <alias>, ;
//\\ P<opt_variable> = <variable>, ;

//\\ P<schema_name> =
//\\         <identifier>
//\\         ;

//\\ P<table_name> =
//\\         <identifier>
//\\         ;

//\\ P<exp_simple> =
//\\         <expression>
//\\         ;

//\\ P<expression> =
//\\           <term> <opt_PM_term_list>
//\\         , <boolean_literal>
//\\         ;

//\\ P<opt_PM_term_list> = <PLUS_or_MINUS> <term> <opt_PM_term_list>, ;

//\\ P<alias> = <opt_as> <identifier>;
//\\ P<opt_as> = "as", ;

//\\ P<term> =
//\\         <factor> <opt_muldiv_factor_list>
//\\         ;
//\\ P<mul_or_div> = <multiply>, <DIVIDE>;
//\\ P<opt_muldiv_factor_list> = <mul_or_div> <factor> <opt_muldiv_factor_list>, ;

//\\ P<multiply> =
//\\         <ASTERISK>
//\\         ;

//\\ P<factor> =
//\\         <factor2> <opt_concat_factor2_list>
//\\         ;

//\\ P<opt_concat_factor2_list> =  <VERTBAR> <VERTBAR> <factor2> <opt_concat_factor2_list>, ;

//\\ P<factor2> =
//\\           <sql_literal>
//\\         , <PLUS_or_MINUS> <expression>
//\\         , <function> <OPEN_PAREN> <expression> <opt_expression_list> <CLOSE_PAREN>
//\\         , <group_function> <OPEN_PAREN> <opt_AST_all_or_distinct> <opt_expression> <CLOSE_PAREN>
//\\         , <user_defined_function> <OPEN_PAREN> <expression> <opt_expression_list> <CLOSE_PAREN>
//\\         , <OPEN_PAREN> <expression> <CLOSE_PAREN>
//\\         , <variable>
//\\         , <bracketed_expression_list>
//\\         ;
//\\ P<opt_AST_all_or_distinct> = <ASTERISK>, "all", "distinct", ;
//\\ P<opt_expression_list> = <COMMA> <expression> <opt_expression_list>, ;
//\\ P<opt_expression> = <expression>, ;

//\\ P<bracketed_expression_list> =
//\\         <OPEN_PAREN> <expression> <COMMA> <expression> <opt_expression_list> <CLOSE_PAREN>
//\\         ;

//\\ P<sql_literal> =
//\\         <NUMBER>, <QUOTED_STRING>, "null"
//\\         ;

//\\ P<variable> =
//\\           <column_spec> <opt_bracketed_plus>
//\\         ;
//\\ P<opt_bracketed_plus> = <OPEN_PAREN> <PLUS> <CLOSE_PAREN>, ;

//\\ P<column_spec> =
//\\         <opt_table_andor_schema_name_DOT> <column_name>
//\\         ;
//\\ P<opt_table_andor_schema_name_DOT> = <opt_schema_name_DOT> <table_name> <DOT>, ;

//\\ P<user_defined_function> =
//\\         <opt_package_andor_schema_name_DOT> <identifier>
//\\         ;
//\\ P<opt_package_andor_schema_name_DOT> = <opt_schema_name_DOT> <package_name> <DOT>, ;

//\\ P<column_name> =
//\\         <identifier>
//\\         ;

//\\ P<function> =
//\\           <number_function>
//\\         , <char_function>
//\\         , <group_function>
//\\         , <conversion_function>
//\\         , <other_function>
//\\         ;

//\\ P<number_function> =
//\\           "abs", "ceil", "floor", "mod", "power", "round"
//\\         , "sign", "sqrt", "trunc"
//\\         ;

//\\ P<char_function> =
//\\           "chr", "initcap", "lower", "lpad", "ltrim", "replace"
//\\         , "rpad", "rtrim", "soundex", "substr", "translate", "upper"
//\\         , "ascii", "instr", "length"
//\\         , "concat"
//\\      ;

//\\ P<group_function> =
//\\           "avg", "count", "max", "min", "stddev", "sum"
//\\         , "variance"
//\\         ;

//\\ P<conversion_function> =
//\\           "chartorowid", "convert", "hextoraw", "rawtohex", "rowidtochar"
//\\         , "to_char", "to_date", "to_number"
//\\         ;

//\\ P<other_function> =
//\\           "decode", "dump", "greatest", "least", "nvl"
//\\         , "uid", "userenv", "vsize"
//\\         ;

//\\ P<pseudo_column> =
//\\         "user", "sysdate"
//\\         ;

//\\ P<selected_table> =
//\\         <table_spec_or_subquery> <opt_alias>
//\\         ;
//\\ P<table_spec_or_subquery> = <table_spec>, <subquery>;

//\\ P<table_spec> =
//\\         <opt_schema_name_DOT> <table_name> <opt_AT_link_name>
//\\         ;
//\\ P<opt_AT_link_name> = <AT_SIGN> <link_name>, ;

//\\ P<table_alias> =
//\\         <opt_schema_name_DOT> <table_name> <opt_AT_link_name> <opt_alias>
//\\         ;

//\\ P<link_name> =
//\\         <identifier>
//\\         ;

//\\ P<condition> =
//\\         <logical_term> <opt_or_logical_term_list>
//\\         ;
//\\ P<opt_or_logical_term_list> = "or" <logical_term> <opt_or_logical_term_list>, ;

//\\ P<logical_term> =
//\\         <logical_factor> <opt_and_logical_factor_list>
//\\         ;
//\\ P<opt_and_logical_factor_list> = "and" <logical_factor> <opt_and_logical_factor_list>, ;

//\\ P<logical_factor> =
//\\           <opt_prior> <exp_simple> <relational_op> <opt_prior> <exp_simple>
//\\         , <exp_simple> <is_what>
//\\         , <quantified_factor>
//\\         , "not" <condition>
//\\         , <OPEN_PAREN> <condition> <CLOSE_PAREN>
//\\         ;
//\\ P<is_what> = 
//\\           <opt_not> <is_not_what>
//\\         , "is" <opt_not> "null";
//\\ P<opt_prior> = "prior",;

//\\ P<is_not_what> =
//\\           "in" <exp_set>
//\\         , "like" <expression> <opt_escape_string>
//\\         , "between" <exp_simple> "and" <exp_simple>
//\\         ;
//\\ P<opt_escape_string> = "escape" <QUOTED_STRING>, ;

//\\ P<quantified_factor> =
//\\           <exp_simple> <relational_op> <opt_all_or_any> <subquery>
//\\         , <opt_not> "exists" <subquery>
//\\         , <subquery>
//\\         ;
//\\ P<opt_all_or_any> = "all", "any", ;

//\\ P<relational_op> =
//\\         <EQ>, <LT>, <GT>, <NOT_EQ>, <LE>, <GE>
//\\         ;

//\\ P<exp_set> =
//\\           <exp_simple>
//\\         , <subquery>
//\\         ;

//\\ P<subquery> =
//\\         <OPEN_PAREN> <select_command> <CLOSE_PAREN>
//\\         ;

//\\ P<connect_clause> =
//\\         <opt_start_with>
//\\         "connect" "by"
//\\         <clause1>
//\\         <clause2>
//\\         <condition>
//\\         <opt_start_with>
//\\         ;
//\\ P<opt_start_with> =
//\\         "start" "with" <condition>, ;

//\\ P<clause1> =
//\\           "prior" <exp_simple> <relational_op> <exp_simple>
//\\         , <exp_simple> <relational_op> <exp_simple> "prior"
//\\         ;

//\\ P<clause2> =
//\\           <opt_prior> <condition>
//\\         , <exp_simple> <relational_op> <opt_prior> <exp_simple> <opt_and_condition>
//\\         ;
//\\ P<opt_and_condition> = "and" <condition>, ;

//\\ P<group_clause> =
//\\         "group" "by" <expression> <opt_expression_list> <opt_having_condition>
//\\         ;
//\\ P<opt_having_condition> = "having" <condition>, ;

//\\ P<set_clause> =
//\\         <set_qual> <select_command>
//\\         ;
//\\ P<set_qual> = <union_all>, "intersect", "minus";
//\\ P<union_all> = "union" "all";

//\\ P<order_clause> =
//\\         "order" "by" <sorted_def> <opt_sorted_def_list>
//\\         ;
//\\ P<opt_sorted_def_list> = <COMMA> <sorted_def> <opt_sorted_def_list>, ;

//\\ P<sorted_def> = <exp_or_num> <opt_asc_or_desc>;
//\\ P<exp_or_num> = <expression>, <NUMBER>;
//\\ P<opt_asc_or_desc> = "asc", "desc", ;

//\\ P<update_clause> =
//\\         "for" "update" <opt_of_col_name_list> <opt_nowait>
//\\         ;
//\\ P<opt_nowait> = "nowait", ;
//\\ P<opt_of_col_name_list> = "of" <column_name> <opt_col_name_list>, ;
//\\ P<opt_col_name_list> = <COMMA> <column_name> <opt_col_name_list>, ;

//\\ P<insert_command> =
//\\         "insert" "into" <table_reference_list> <opt_col_spec> <values_or_select>
//\\         ;
//\\ P<opt_var_name_list> = <COMMA> <variable_name> <opt_var_name_list>, ;
//\\ P<values_or_select> =
//\\           "values" <OPEN_PAREN> <variable_name> <opt_var_name_list> <CLOSE_PAREN>
//\\         , <select_statement>
//\\         ;

//\\ P<opt_col_spec> = <OPEN_PAREN> <column_spec> <column_spec_list> <CLOSE_PAREN>, ;   # is column_spec just one???

//\\ P<column_spec_list> = ',' <column_spec> <column_spec_list>, ;

//\\ P<update_command> =
//\\           <subquery_update>
//\\         , <simple_update>
//\\         ;

//\\ P<simple_update> =
//\\         "update" <table_alias>
//\\         "set" <column_spec> <EQ> <expr_or_subq> <opt_colspec_eq_list> <opt_colspec_eq_list>
//\\         "where" <condition>
//\\         ;
//\\ P<expr_or_subq> = <expression>, <subquery>;
//\\ P<opt_colspec_eq_list> = <COMMA> <column_spec> <EQ> <expr_or_subq> <opt_colspec_eq_list>, ;

//\\ P<subquery_update> =
//\\         "update" <table_alias>
//\\         "set" <OPEN_PAREN> <column_spec> <opt_col_spec_list> <CLOSE_PAREN> <EQ> <subquery>
//\\         "where" <condition>
//\\         ;
//\\ P<opt_col_spec_list> = <COMMA> <column_spec> <opt_col_spec_list>, ;
//\\ P<set_transaction_command> = "set" "transaction" "read" "only";
//\\         
//\\ P<close_statement> =
//\\       "close" <cursor_name>
//\\       ;

//\\ P<fetch_statement> =
//\\         "fetch" <cursor_name> "into" <varlist_or_record>
//\\         ;
//\\ P<varlist_or_record> =
//\\           <variable_name> <opt_var_list>
//\\         , <record_name>
//\\ 	;
//\\ P<opt_var_list> = <COMMA> <variable_name> <opt_var_list>, ;

//\\ P<lock_table_statement> =
//\\         "lock" "table" <table_reference_list> "in" <lock_mode> "mode" <opt_nowait>
//\\         ;

//\\ P<lock_mode> =
//\\           "row" "share"
//\\         , "row" "exclusive"
//\\         , "share" "update"
//\\         , "share" "row" "exclusive"
//\\         , "share"
//\\         , "exclusive"
//\\         ;

//\\ P<open_statement> =
//\\         "open" <cursor_name> <opt_bracketed_plsql_exp_list>
//\\         ;

//\\ P<rollback_statement> =
//\\         "rollback" <opt_work>
//\\         <opt_to_savepoint>
//\\         <opt_comment_list>
//\\         ;
//\\ P<opt_comment_list> = "comment" <quoted_string> <opt_comment_list>, ;
//\\ P<opt_work> = "work", ;
//\\ P<opt_to_savepoint> = "to" <opt_savepoint> <savepoint_name>, ;
//\\ P<opt_savepoint> = "savepoint", ;

//\\ P<savepoint_statement> =
//\\         "savepoint" <savepoint_name>
//\\         ;

//\\ P<savepoint_name> =
//\\         <identifier>
//\\         ;

//\\ #P<identifier> =
//\\ #        <IDENTIFIER>, <QUOTED_STRING>, <keyword>
//\\ #        ;

//\\ P<identifier> =
//\\         <IDENTIFIER>
//\\         ;

//\\ P<quoted_string> =
//\\         <QUOTED_STRING>
//\\         ;

//\\ P<match_string> =
//\\         <QUOTED_STRING>
//\\         ;

//\\ P<keyword> =
//\\           "abs"
//\\         , "ascii"
//\\         , "ceil"
//\\         , "chartorowid"
//\\         , "chr"
//\\         , "concat"
//\\         , "convert"
//\\         , "count"
//\\         , "decode"
//\\         , "dump"
//\\         , "floor"
//\\         , "greatest"
//\\         , "hextoraw"
//\\         , "initcap"
//\\         , "instr"
//\\         , "intersect"
//\\         , "least"
//\\         , "length"
//\\         , "lower"
//\\         , "lpad"
//\\         , "ltrim"
//\\         , "nvl"
//\\         , "power"
//\\         , "rawtohex"
//\\         , "replace"
//\\         , "round"
//\\         , "rowidtochar"
//\\         , "rpad"
//\\         , "rtrim"
//\\         , "sign"
//\\         , "soundex"
//\\         , "sqrt"
//\\         , "substr"
//\\         , "sysdate"
//\\         , "to_char"
//\\         , "to_date"
//\\         , "to_number"
//\\         , "translate"
//\\         , "trunc"
//\\         , "upper"
//\\         , "user"
//\\         , "userenv"
//\\         , "vsize"
//\\     ;

//\\ P<SEMI> = ';';
//\\ P<COMMA> = ',';
//\\ P<ASTERISK> = '*';
//\\ P<AT_SIGN> = '@';
//\\ P<OPEN_PAREN> = '(';
//\\ P<CLOSE_PAREN> = ')';
//\\ P<PLUS> = '+';
//\\ P<MINUS> = '-';
//\\ P<DIVIDE> = '/';
//\\ P<PERCENTAGE> = '%';
//\\ P<DOUBLEDOT> = '.' '.';
//\\ P<DOT> = '.';
//\\ P<START_LABEL> = '<' '<';
//\\ P<END_LABEL> = '>' '>';
//\\ P<ASSIGNMENT_EQ> = ':' '=';
//\\ P<COLON> = ':';
//\\ P<VERTBAR> = '|';
//\\ P<NOT_EQ> = '<' '>' , '!' '=',  '^' '=';
//\\ P<LE> = '<' '=';
//\\ P<LT> = '<';
//\\ P<GE> = '>' '=';
//\\ P<GT> = '>';
//\\ P<EQ> = '=';

//\\ P<NUMBER> =
//\\         <opt_plus_or_minus> <decimal> <opt_exponent>
//\\         ;
//\\ P<opt_exponent> = "e" <opt_plus_or_minus> <N>, ;
//\\ P<decimal> = <N> <opt_DOT_N>, <DOT> <N>;
//\\ P<opt_DOT_N> = <DOT> <N>, ;
//\\ P<opt_plus_or_minus> = <PLUS>, <MINUS>, ;

//\\ P<N> = <digit> <opt_digit_list>;
//\\ P<opt_digit_list> = <digit> <opt_digit_list>, ;
//\\ P<digit> = '0', '1', '2', '3', '4', '5', '6', '7', '8', '9';
  case P_digit:
    break;

//\\ E
  default:
    fprintf(stderr, "*** Internal error at line %d.  ap=%d  phrase=%d\n", __LINE__, ap, phrase); exit(2);
  }

  return(-1); // DUMMY TRIP, NOTHING TO RETURN

}

//--------------------------------------------------------------------------



int main(int argc, char **argv) {
char *s;

  // Get clean version of executable name.  Should work on most existing systems (2006)
  progname = argv[0];
  if ((s = strrchr(progname, '/')) != NULL) progname = s+1;  // Unix
  if ((s = strrchr(progname, '\\')) != NULL) progname = s+1; // M$
  if ((s = strrchr(progname, ']')) != NULL) progname = s+1;  // Dec
  if ((s = strrchr(progname, ';')) != NULL) *s = '\0';       // Version no's
  if (((s = strrchr(progname, '.')) != NULL) && (strcasecmp(s, ".exe") == 0)) *s = '\0';
  if (((s = strrchr(progname, '.')) != NULL) && (strcasecmp(s, ".com") == 0)) *s = '\0';

  if ((argc == 3) && strcmp(argv[1], "-d") == 0) {
    argv++; argc--; debug_parser = TRUE;
  }

  if (argc != 2) {
    fprintf(stderr, "syntax: %s [-d] filename\n", progname);
    exit(1);
  }

  sourcefile = fopen(argv[1], "r");
  if (sourcefile == NULL) {
    fprintf(stderr, "%s: %s - %s\n", progname, strerror(errno), argv[1]);
    exit(errno);
  }

  curfile = argv[1]; startline = TRUE; whitespace = TRUE;
  onecharstr = (char *)malloc(512);

  line_reconstruction();

  if (debug_parser) {
    int i; // DEBUG ONLY
    for (i = 0; i < nextfree; i++) {
      fprintf(stdout, "%s, line %d, col %d: [%0d] %s\n",
                       c[i].f, c[i].l, c[i].col, c[i].t, c[i].s);
    }
  }

  if (!parse(PHRASE_BASE, 0)) {
    if (bestparse == nextfree) {
      fprintf(stderr, "\"%s\", Line %d, Col %d: Premature end of file while looking for %s\n",
                       argv[1], c[bestparse].l, c[bestparse].col+1, looking_for);
    } else {
      int i;
      fprintf(stderr, "\"%s\", Line %d, Col %d: Syntax error while looking for %s near ",
                       argv[1], c[bestparse].l, c[bestparse].col+1, looking_for);
      for (i = bestparse; i < bestparse+3; i++) {
        if (i == nextfree) {
          fprintf(stderr, "<End of file>");
          break;
        }
        switch (c[i].t) {
//        case TYPE_HEXINT:
//          fprintf(stderr, "$"); // *OR* ... We could put the '$' back in front of the string
          /* drop through */    // and probably save much code whenever printing.  Use str+1
        case TYPE_IDENTIFIER:
        case TYPE_CHAR:
//        case TYPE_INT:
        case TYPE_KEYWORD:
          fprintf(stderr, "%s", c[i].s);
          break;
        case TYPE_QUOTED_STRING:
          fprintf(stderr, "'%s'", c[i].s);
          break;
//        case TYPE_CHARCONST:
//          fprintf(stderr, "'%s'", c[i].s);
//          break;
        }
        fprintf(stderr, (i == (bestparse+2) ? " ..." : " "));
      }
      fprintf(stderr, "\n");
    }
    exit(1);
  }

  fprintf(stderr, "Parsed OK, now compiling.\n");

  if (debug_parser) walk(0, 0, want_all, print_all); // Diags: print final parse tree

  // The structure at A[] is a concrete syntax tree.  We could
  // generate code directly from this tree, however for the
  // sake of cleaner design, we will first convert the concrete
  // syntax tree into an Abstract Syntax Tree (AST)

  {int program = compile(0, 0);
    // Now generate the output code from the AST.
    // codegen(program, -1, -1, -1)
  }
  exit(0); return(1);
}