#ifndef _GRAMMAR_H_
#define _GRAMMAR_H_ 1

#include <wchar.h>

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

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

typedef int (*parsefn)(void);

#define LARGEST_ALT 8 // Max number of phrases in any Alt: 0 (Reserved), 1:7

#define NEGATED_PHRASE     (1U<<24U)
#define GUARD_PHRASE       (1U<<25U)
#define WHITESPACE_ALLOWED (1U<<26U)
#define GRAMMAR_TYPE_SHIFT 27U
#define GRAMMAR_TYPE_MASK  31U
#define BIP_TYPE         (1U <<27U)
#define PHRASE_TYPE      (2U <<27U)
#define SEMANTIC_TYPE    (3U <<27U)
#define KEYWORD_TYPE     (4U <<27U)
#define CHAR_TYPE        (5U <<27U)
#define UTF32CHAR_TYPE   (6U <<27U)
#define STRING_TYPE      (7U <<27U)
#define UTF32STRING_TYPE (8U <<27U)
#define REGEXP_TYPE      (9U <<27U)
#define OPTION_TYPE      (10U <<27U)
#define COUNT_OF_ALTS    (11U <<27U)
#define COUNT_OF_PHRASES (12U <<27U)
#define ALT_NUMBER       (13U <<27U)
#define INDEX_MASK       0x7FFFFFU
// (We have room for types 1..31U)
#define PhraseType(idx)  ((((idx)>>27U)&31U))


#define BIP_BASE 0
#define PHRASE_BASE 3
#define SEMANTIC_BASE 33
#define AST_BASE 33

#define NUM_BIPS 3
#define NUM_SIMPLE_PHRASES 30
#define NUM_SEMANTIC_PHRASES 0
#define NUM_PHRASES (NUM_BIPS+NUM_SIMPLE_PHRASES+NUM_SEMANTIC_PHRASES)

#define NUM_KEYWORDS 1
#define NUM_REGEXPS 16
#define NUM_GRAMMAR 164

#define B_EOF 0
#define B_ch 1
#define B_bnl 2
#define P_nl 3
#define P_int 4
#define P_opt_exponent 5
#define P_float 6
#define P_SS 7
#define P_item 8
#define P_more_items 9
#define P_non_empty_content 10
#define P_filename 11
#define P_comment 12
#define P_usemtl_options 13
#define P_usemtl 14
#define P_mtllib 15
#define P_v 16
#define P_opt_float 17
#define P_vt 18
#define P_vn 19
#define P_vp 20
#define P_vn_index 21
#define P_opt_f_vn 22
#define P_opt_vt_index 23
#define P_f_vt_vn_indices 24
#define P_v_index 25
#define P_more_f_groups 26
#define P_f_groups 27
#define P_f 28
#define P_g 29
#define P_s 30
#define P_o 31
#define P_l 32

extern const int bip_map[NUM_BIPS];
extern const int sequential_phrase_no_to_grammar_index[NUM_SIMPLE_PHRASES];
extern const wchar_t *phrasename[NUM_BIPS+NUM_SIMPLE_PHRASES+NUM_SEMANTIC_PHRASES];

extern const wchar_t *semantic_phrasename[NUM_SEMANTIC_PHRASES];
extern const wchar_t *semantic_code[NUM_SEMANTIC_PHRASES];
extern const wchar_t *ast_code[NUM_SIMPLE_PHRASES];
extern const wchar_t *xcomment[NUM_PHRASES];
extern const wchar_t *keyword[NUM_KEYWORDS];
extern const wchar_t *regexps[NUM_REGEXPS];

extern const int gram[NUM_GRAMMAR];
#define G_nl 0
#define G_int 3
#define G_opt_exponent 6
#define G_float 12
#define G_SS 16
#define G_item 22
#define G_more_items 49
#define G_non_empty_content 55
#define G_filename 61
#define G_comment 64
#define G_usemtl_options 68
#define G_usemtl 73
#define G_mtllib 78
#define G_v 82
#define G_opt_float 89
#define G_vt 93
#define G_vn 99
#define G_vp 105
#define G_vn_index 109
#define G_opt_f_vn 112
#define G_opt_vt_index 117
#define G_f_vt_vn_indices 121
#define G_v_index 127
#define G_more_f_groups 130
#define G_f_groups 136
#define G_f 141
#define G_g 145
#define G_s 149
#define G_o 156
#define G_l 160

extern parsefn parsetime[NUM_SEMANTIC_PHRASES];

#ifndef SUPPRESS_DATA
const wchar_t *phrasename[NUM_BIPS+NUM_SIMPLE_PHRASES+NUM_SEMANTIC_PHRASES] = {
  /*0+0*/   L"EOF" /*0*/,
  /*0+1*/   L"ch" /*1*/,
  /*0+2*/   L"bnl" /*2*/,
  /*3+0*/   L"nl",
  /*3+1*/   L"int",
  /*3+2*/   L"opt-exponent",
  /*3+3*/   L"float",
  /*3+4*/   L"SS",
  /*3+5*/   L"item",
  /*3+6*/   L"more_items",
  /*3+7*/   L"non-empty-content",
  /*3+8*/   L"filename",
  /*3+9*/   L"comment",
  /*3+10*/   L"usemtl_options",
  /*3+11*/   L"usemtl",
  /*3+12*/   L"mtllib",
  /*3+13*/   L"v",
  /*3+14*/   L"opt-float",
  /*3+15*/   L"vt",
  /*3+16*/   L"vn",
  /*3+17*/   L"vp",
  /*3+18*/   L"vn-index",
  /*3+19*/   L"opt-f-vn",
  /*3+20*/   L"opt-vt-index",
  /*3+21*/   L"f-vt-vn-indices",
  /*3+22*/   L"v-index",
  /*3+23*/   L"more-f-groups",
  /*3+24*/   L"f-groups",
  /*3+25*/   L"f",
  /*3+26*/   L"g",
  /*3+27*/   L"s",
  /*3+28*/   L"o",
  /*3+29*/   L"l",
};
const wchar_t *phrasename_c[NUM_BIPS+NUM_SIMPLE_PHRASES+NUM_SEMANTIC_PHRASES] = {
  /*0+0*/   L"EOF" /*0*/,
  /*0+1*/   L"ch" /*1*/,
  /*0+2*/   L"bnl" /*2*/,
  /*3+0*/   L"nl",
  /*3+1*/   L"int",
  /*3+2*/   L"opt_exponent",
  /*3+3*/   L"float",
  /*3+4*/   L"SS",
  /*3+5*/   L"item",
  /*3+6*/   L"more_items",
  /*3+7*/   L"non_empty_content",
  /*3+8*/   L"filename",
  /*3+9*/   L"comment",
  /*3+10*/   L"usemtl_options",
  /*3+11*/   L"usemtl",
  /*3+12*/   L"mtllib",
  /*3+13*/   L"v",
  /*3+14*/   L"opt_float",
  /*3+15*/   L"vt",
  /*3+16*/   L"vn",
  /*3+17*/   L"vp",
  /*3+18*/   L"vn_index",
  /*3+19*/   L"opt_f_vn",
  /*3+20*/   L"opt_vt_index",
  /*3+21*/   L"f_vt_vn_indices",
  /*3+22*/   L"v_index",
  /*3+23*/   L"more_f_groups",
  /*3+24*/   L"f_groups",
  /*3+25*/   L"f",
  /*3+26*/   L"g",
  /*3+27*/   L"s",
  /*3+28*/   L"o",
  /*3+29*/   L"l",
};
const int bip_map[NUM_BIPS] = {
  /*0*/   0,
  /*1*/   1,
  /*2*/   2,
};
const int sequential_phrase_no_to_grammar_index[NUM_SIMPLE_PHRASES] = {
  G_nl,  /*0*/
  G_int,  /*3*/
  G_opt_exponent,  /*6*/
  G_float,  /*12*/
  G_SS,  /*16*/
  G_item,  /*22*/
  G_more_items,  /*49*/
  G_non_empty_content,  /*55*/
  G_filename,  /*61*/
  G_comment,  /*64*/
  G_usemtl_options,  /*68*/
  G_usemtl,  /*73*/
  G_mtllib,  /*78*/
  G_v,  /*82*/
  G_opt_float,  /*89*/
  G_vt,  /*93*/
  G_vn,  /*99*/
  G_vp,  /*105*/
  G_vn_index,  /*109*/
  G_opt_f_vn,  /*112*/
  G_opt_vt_index,  /*117*/
  G_f_vt_vn_indices,  /*121*/
  G_v_index,  /*127*/
  G_more_f_groups,  /*130*/
  G_f_groups,  /*136*/
  G_f,  /*141*/
  G_g,  /*145*/
  G_s,  /*149*/
  G_o,  /*156*/
  G_l,  /*160*/
};

const wchar_t *semantic_phrasename[NUM_SEMANTIC_PHRASES] = {
};

const wchar_t *semantic_code[NUM_SEMANTIC_PHRASES] = {
};

parsefn parsetime[NUM_SEMANTIC_PHRASES] = {
};

// Comments are stored so that they can be re-inserted, should
// we need to regenerate a grammar.g file from this header file.
const wchar_t *xcomment[NUM_PHRASES] = {
  /*  0*/   NULL,
  /*  1*/   NULL,
  /*  2*/   NULL,
  /*  3*/   NULL,
  /*  4*/   NULL,
  /*  5*/   NULL,
  /*  6*/   NULL,
  /*  7*/   NULL,
  /*  8*/   NULL,
  /*  9*/   NULL,
  /* 10*/   NULL,
  /* 11*/   NULL,
  /* 12*/   NULL,
  /* 13*/   NULL,
  /* 14*/   NULL,
  /* 15*/   NULL,
  /* 16*/   NULL,
  /* 17*/   NULL,
  /* 18*/   NULL,
  /* 19*/   NULL,
  /* 20*/   NULL,
  /* 21*/   NULL,
  /* 22*/   NULL,
  /* 23*/   NULL,
  /* 24*/   NULL,
  /* 25*/   NULL,
  /* 26*/   NULL,
  /* 27*/   NULL,
  /* 28*/   NULL,
  /* 29*/   NULL,
  /* 30*/   NULL,
  /* 31*/   NULL,
  /* 32*/   NULL,
};
const wchar_t *ast_code[NUM_SIMPLE_PHRASES] = {
  /*nl*/   L"",
  /*int*/   L"\n",
  /*opt_exponent*/   L"\n",
  /*float*/   L"\n",
  /*SS*/   L"\n",
  /*item*/   L"",
  /*more_items*/   L"",
  /*non_empty_content*/   L"\n",
  /*filename*/   L"\n",
  /*comment*/   L"\n"
             "  // Some comments may be worth parsing...\n"
             "\n"
             "  // # Vertices: 30\n"
             "  // # Texture vertices: 59\n"
             "  // # Normals: 30\n"
             "  // # Faces: 16\n"
             "      \n"
             "  //fwprintf(stdout, L\"COMMENT\\n\");\n",
  /*usemtl_options*/   L"\n",
  /*usemtl*/   L"\n"
             "  // usemtl [material name]\n"
             "  // usemtl NoName,1001\n"
             "  // Lines starting with \"usemtl\" specify which material to apply to a set of faces, and are often used in conjunction with .mtl files.\n"
             "\n"
             "  //fwprintf(stdout, L\"USEMTL\\n\");\n",
  /*mtllib*/   L"\n"
             "  // mtllib [external .mtl file name]\n"
             "  // mtllib supertank2.mtl\n"
             "      \n"
             "  //fwprintf(stdout, L\"MTLLIB\\n\");\n",
  /*v*/   L"\n"
             "  // List of geometric vertices, with (x, y, z, [w]) coordinates, w is optional and defaults to 1.0.\n"
             "  // v -0.002424 1.919356 -2.225567\n"
             "\n"
             "  //fwprintf(stdout, L\"V ...\\n\");\n",
  /*opt_float*/   L"\n",
  /*vt*/   L"\n"
             "  // List of texture coordinates, in (u, [v, w]) coordinates, these will vary between 0 and 1. v, w are optional and default to 0.\n"
             "  // vt 0.000000 0.000000\n"
             "  // vt 1.000000 1.000000\n"
             "  // vt 1.000000 0.002445\n"
             "\n"
             "  //fwprintf(stdout, L\"VT ...\\n\");\n",
  /*vn*/   L"\n"
             "  // Normals (normals might not be unit vectors)\n"
             "  // vn 0.000000 0.000000 1.000000\n"
             "  // vn -0.542794 0.689214 -0.479957\n"
             "\n"
             "  //fwprintf(stdout, L\"VN ...\\n\");\n",
  /*vp*/   L"\n"
             "  // Parameter space vertices in (u, [v, w]) form; free form geometry statement\n"
             "\n"
             "  //fwprintf(stdout, L\"VP ...\\n\");\n",
  /*vn_index*/   L"\n",
  /*opt_f_vn*/   L"\n",
  /*opt_vt_index*/   L"\n",
  /*f_vt_vn_indices*/   L"\n",
  /*v_index*/   L"\n",
  /*more_f_groups*/   L"\n",
  /*f_groups*/   L"\n"
             "\n"
             "/*\n"
             "      From https://en.wikipedia.org/wiki/Wavefront_.obj_file\n"
             "\n"
             "\n"
             "      Relative and absolute indices\n"
             "\n"
             "      OBJ files, due to their list structure, are able to reference vertices,\n"
             "      normals, etc. either by their absolute position (1 represents the first\n"
             "      defined vertex, N representing the Nth defined vertex), or by their relative\n"
             "      position (-1 represents the latest defined vertex). However, not all software\n"
             "      supports the latter approach, and conversely some software inherently writes\n"
             "      only the latter form (due to the convenience of appending elements without\n"
             "      needing to recalculate vertex offsets, etc.), leading to occasional incompatibilities.\n"
             "\n"
             "\n"
             "      Vertex indices\n"
             "\n"
             "      A valid vertex index matches the corresponding vertex elements of a previously\n"
             "      defined vertex list. If an index is positive then it refers to the offset\n"
             "      in that vertex list, starting at 1. If an index is negative then it relatively\n"
             "      refers to the end of the vertex list, -1 referring to the last element.\n"
             "\n"
             "      Each face can contain three or more vertices.\n"
             "\n"
             "      f v1 v2 v3 ....\n"
             "\n"
             "\n"
             "      Vertex texture coordinate indices\n"
             "\n"
             "      Optionally, texture coordinate indices can be used to specify texture coordinates\n"
             "      when defining a face. To add a texture coordinate index to a vertex index when\n"
             "      defining a face, one must put a slash immediately after the vertex index and then\n"
             "      put the texture coordinate index. No spaces are permitted before or after the slash.\n"
             "      A valid texture coordinate index starts from 1 and matches the corresponding element\n"
             "      in the previously defined list of texture coordinates. Each face can contain three\n"
             "      or more elements.\n"
             "\n"
             "      f v1/vt1 v2/vt2 v3/vt3 ...\n"
             "\n"
             "\n"
             "      Vertex normal indices\n"
             "\n"
             "      Optionally, normal indices can be used to specify normal vectors for vertices when\n"
             "      defining a face. To add a normal index to a vertex index when defining a face, one\n"
             "      must put a second slash after the texture coordinate index and then put the normal\n"
             "      index. A valid normal index starts from 1 and matches the corresponding element in\n"
             "      the previously defined list of normals. Each face can contain three or more elements.\n"
             "\n"
             "      f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ...\n"
             "\n"
             "\n"
             "      Vertex normal indices without texture coordinate indices\n"
             "\n"
             "      As texture coordinates are optional, one can define geometry without them, but one\n"
             "      must put two slashes after the vertex index before putting the normal index.\n"
             "\n"
             "      f v1//vn1 v2//vn2 v3//vn3 ...\n"
             "      \n"
             "*/\n",
  /*f*/   L"\n"
             "  // Polygonal face element:  Vertex/Texture/Normal\n"
             "  // f 6/11/6 7/12/7 8/14/8 5/8/5 ...\n"
             "  // Add 1 to face_count (face_count++) for each group of triangles in the line.  \n"
             "\n"
             "  //fwprintf(stdout, L\"F ...\\n\");\n",
  /*g*/   L"\n"
             "  // g [polygon group name]\n"
             "  // g Default\n"
             "\n"
             "  //fwprintf(stdout, L\"G ...\\n\");\n",
  /*s*/   L"\n"
             "  // Smoothing groups (or turn off)\n"
             "  // s 2\n"
             "  // usemtl NoName,1001\n"
             "  // ...\n"
             "  // s 1\n"
             "  // usemtl NoName,1000\n"
             "  // ...\n"
             "  // s off\n"
             "\n"
             "  //fwprintf(stdout, L\"S ...\\n\");\n",
  /*o*/   L"\n"
             "  // o [object name]\n"
             "\n"
             "  //fwprintf(stdout, L\"O ...\\n\");\n",
  /*l*/   L"\n"
             "  // Polygonal line element\n"
             "  // l 5 8 1 2 4 9\n"
             "\n"
             "  //fwprintf(stdout, L\"L ...\\n\");\n",
};

const wchar_t *keyword[NUM_KEYWORDS] = {
  /*  0*/   L"/",
};
const wchar_t *regexps[NUM_REGEXPS] = {
  /*0*/   L"^[0-9][0-9]*",
  /*1*/   L"^[\\-]?",
  /*2*/   L"^[\\-]?[0-9]*[\\.][0-9]*",
  /*3*/   L"^#",
  /*4*/   L"^[1-9][0-9]*",
  /*5*/   L"^usemtl",
  /*6*/   L"^mtllib",
  /*7*/   L"^v",
  /*8*/   L"^vt",
  /*9*/   L"^vn",
  /*10*/   L"^f",
  /*11*/   L"^g",
  /*12*/   L"^s",
  /*13*/   L"^off",
  /*14*/   L"^o",
  /*15*/   L"^l",
};
const int gram[NUM_GRAMMAR /* 164 */] = {

// P<nl> = ...;
  /*  0*/ COUNT_OF_ALTS    | 0x000001,
  /*  1*/ COUNT_OF_PHRASES | 0x000001,
  /*  2*/ BIP_TYPE         | B_bnl,

// P<int> = ...;
  /*  3*/ COUNT_OF_ALTS    | 0x000001,
  /*  4*/ COUNT_OF_PHRASES | 0x000001,
  /*  5*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x000000,  /* ^[0-9][0-9]* */

// P<opt-exponent> = ...;
  /*  6*/ COUNT_OF_ALTS    | 0x000002,
  /*  7*/ COUNT_OF_PHRASES | 0x000003,
  /*  8*/ CHAR_TYPE        | WHITESPACE_ALLOWED | 0x000065,  /* 'e' */
  /*  9*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x000001,  /* ^[\\-]? */
  /* 10*/ PHRASE_TYPE      | G_int,
  /* 11*/ COUNT_OF_PHRASES | 0x000000,

// P<float> = ...;
  /* 12*/ COUNT_OF_ALTS    | 0x000001,
  /* 13*/ COUNT_OF_PHRASES | 0x000002,
  /* 14*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x000002,  /* ^[\\-]?[0-9]*[\\.][0-9]* */
  /* 15*/ PHRASE_TYPE      | G_opt_exponent,

// P<SS> = ...;
  /* 16*/ COUNT_OF_ALTS    | 0x000001,
  /* 17*/ COUNT_OF_PHRASES | 0x000004,
  /* 18*/ PHRASE_TYPE      | G_item,
  /* 19*/ PHRASE_TYPE      | G_nl,
  /* 20*/ PHRASE_TYPE      | G_more_items,
  /* 21*/ BIP_TYPE         | WHITESPACE_ALLOWED | B_EOF,

// P<item> = ...;
  /* 22*/ COUNT_OF_ALTS    | 0x00000d,
  /* 23*/ COUNT_OF_PHRASES | 0x000001,
  /* 24*/ PHRASE_TYPE      | G_comment,
  /* 25*/ COUNT_OF_PHRASES | 0x000001,
  /* 26*/ PHRASE_TYPE      | G_usemtl,
  /* 27*/ COUNT_OF_PHRASES | 0x000001,
  /* 28*/ PHRASE_TYPE      | G_mtllib,
  /* 29*/ COUNT_OF_PHRASES | 0x000001,
  /* 30*/ PHRASE_TYPE      | G_v,
  /* 31*/ COUNT_OF_PHRASES | 0x000001,
  /* 32*/ PHRASE_TYPE      | G_vt,
  /* 33*/ COUNT_OF_PHRASES | 0x000001,
  /* 34*/ PHRASE_TYPE      | G_vn,
  /* 35*/ COUNT_OF_PHRASES | 0x000001,
  /* 36*/ PHRASE_TYPE      | G_f,
  /* 37*/ COUNT_OF_PHRASES | 0x000001,
  /* 38*/ PHRASE_TYPE      | G_g,
  /* 39*/ COUNT_OF_PHRASES | 0x000001,
  /* 40*/ PHRASE_TYPE      | G_s,
  /* 41*/ COUNT_OF_PHRASES | 0x000001,
  /* 42*/ PHRASE_TYPE      | G_vp,
  /* 43*/ COUNT_OF_PHRASES | 0x000001,
  /* 44*/ PHRASE_TYPE      | G_o,
  /* 45*/ COUNT_OF_PHRASES | 0x000001,
  /* 46*/ PHRASE_TYPE      | G_l,
  /* 47*/ COUNT_OF_PHRASES | 0x000001,
  /* 48*/ BIP_TYPE         | WHITESPACE_ALLOWED | B_EOF,

// P<more_items> = ...;
  /* 49*/ COUNT_OF_ALTS    | 0x000002,
  /* 50*/ COUNT_OF_PHRASES | 0x000003,
  /* 51*/ PHRASE_TYPE      | G_item,
  /* 52*/ PHRASE_TYPE      | G_nl,
  /* 53*/ PHRASE_TYPE      | G_more_items,
  /* 54*/ COUNT_OF_PHRASES | 0x000000,

// P<non-empty-content> = ...;
  /* 55*/ COUNT_OF_ALTS    | 0x000002,
  /* 56*/ COUNT_OF_PHRASES | 0x000003,
  /* 57*/ PHRASE_TYPE      | NEGATED_PHRASE     | G_nl,
  /* 58*/ BIP_TYPE         | B_ch,
  /* 59*/ PHRASE_TYPE      | G_non_empty_content,
  /* 60*/ COUNT_OF_PHRASES | 0x000000,

// P<filename> = ...;
  /* 61*/ COUNT_OF_ALTS    | 0x000001,
  /* 62*/ COUNT_OF_PHRASES | 0x000001,
  /* 63*/ PHRASE_TYPE      | G_non_empty_content,

// P<comment> = ...;
  /* 64*/ COUNT_OF_ALTS    | 0x000001,
  /* 65*/ COUNT_OF_PHRASES | 0x000002,
  /* 66*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x000003,  /* ^# */
  /* 67*/ PHRASE_TYPE      | G_non_empty_content,

// P<usemtl_options> = ...;
  /* 68*/ COUNT_OF_ALTS    | 0x000002,
  /* 69*/ COUNT_OF_PHRASES | 0x000002,
  /* 70*/ CHAR_TYPE        | WHITESPACE_ALLOWED | 0x00002c,  /* ',' */
  /* 71*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x000004,  /* ^[1-9][0-9]* */
  /* 72*/ COUNT_OF_PHRASES | 0x000000,

// P<usemtl> = ...;
  /* 73*/ COUNT_OF_ALTS    | 0x000001,
  /* 74*/ COUNT_OF_PHRASES | 0x000003,
  /* 75*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x000005,  /* ^usemtl */
  /* 76*/ PHRASE_TYPE      | G_filename,
  /* 77*/ PHRASE_TYPE      | G_usemtl_options,

// P<mtllib> = ...;
  /* 78*/ COUNT_OF_ALTS    | 0x000001,
  /* 79*/ COUNT_OF_PHRASES | 0x000002,
  /* 80*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x000006,  /* ^mtllib */
  /* 81*/ PHRASE_TYPE      | G_filename,

// P<v> = ...;
  /* 82*/ COUNT_OF_ALTS    | 0x000001,
  /* 83*/ COUNT_OF_PHRASES | 0x000005,
  /* 84*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x000007,  /* ^v */
  /* 85*/ PHRASE_TYPE      | G_float,
  /* 86*/ PHRASE_TYPE      | G_float,
  /* 87*/ PHRASE_TYPE      | G_float,
  /* 88*/ PHRASE_TYPE      | G_opt_float,

// P<opt-float> = ...;
  /* 89*/ COUNT_OF_ALTS    | 0x000002,
  /* 90*/ COUNT_OF_PHRASES | 0x000001,
  /* 91*/ PHRASE_TYPE      | G_float,
  /* 92*/ COUNT_OF_PHRASES | 0x000000,

// P<vt> = ...;
  /* 93*/ COUNT_OF_ALTS    | 0x000001,
  /* 94*/ COUNT_OF_PHRASES | 0x000004,
  /* 95*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x000008,  /* ^vt */
  /* 96*/ PHRASE_TYPE      | G_float,
  /* 97*/ PHRASE_TYPE      | G_float,
  /* 98*/ PHRASE_TYPE      | G_opt_float,

// P<vn> = ...;
  /* 99*/ COUNT_OF_ALTS    | 0x000001,
  /*100*/ COUNT_OF_PHRASES | 0x000004,
  /*101*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x000009,  /* ^vn */
  /*102*/ PHRASE_TYPE      | G_float,
  /*103*/ PHRASE_TYPE      | G_float,
  /*104*/ PHRASE_TYPE      | G_float,

// P<vp> = ...;
  /*105*/ COUNT_OF_ALTS    | 0x000001,
  /*106*/ COUNT_OF_PHRASES | 0x000002,
  /*107*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x000009,  /* ^vn */
  /*108*/ PHRASE_TYPE      | G_non_empty_content,

// P<vn-index> = ...;
  /*109*/ COUNT_OF_ALTS    | 0x000001,
  /*110*/ COUNT_OF_PHRASES | 0x000001,
  /*111*/ PHRASE_TYPE      | G_int,

// P<opt-f-vn> = ...;
  /*112*/ COUNT_OF_ALTS    | 0x000002,
  /*113*/ COUNT_OF_PHRASES | 0x000002,
  /*114*/ KEYWORD_TYPE     | WHITESPACE_ALLOWED | 0x000000,  /* / */
  /*115*/ PHRASE_TYPE      | G_vn_index,
  /*116*/ COUNT_OF_PHRASES | 0x000000,

// P<opt-vt-index> = ...;
  /*117*/ COUNT_OF_ALTS    | 0x000002,
  /*118*/ COUNT_OF_PHRASES | 0x000001,
  /*119*/ PHRASE_TYPE      | G_int,
  /*120*/ COUNT_OF_PHRASES | 0x000000,

// P<f-vt-vn-indices> = ...;
  /*121*/ COUNT_OF_ALTS    | 0x000002,
  /*122*/ COUNT_OF_PHRASES | 0x000003,
  /*123*/ KEYWORD_TYPE     | WHITESPACE_ALLOWED | 0x000000,  /* / */
  /*124*/ PHRASE_TYPE      | G_opt_vt_index,
  /*125*/ PHRASE_TYPE      | G_opt_f_vn,
  /*126*/ COUNT_OF_PHRASES | 0x000000,

// P<v-index> = ...;
  /*127*/ COUNT_OF_ALTS    | 0x000001,
  /*128*/ COUNT_OF_PHRASES | 0x000001,
  /*129*/ PHRASE_TYPE      | G_int,

// P<more-f-groups> = ...;
  /*130*/ COUNT_OF_ALTS    | 0x000002,
  /*131*/ COUNT_OF_PHRASES | 0x000003,
  /*132*/ PHRASE_TYPE      | G_v_index,
  /*133*/ PHRASE_TYPE      | G_f_vt_vn_indices,
  /*134*/ PHRASE_TYPE      | G_more_f_groups,
  /*135*/ COUNT_OF_PHRASES | 0x000000,

// P<f-groups> = ...;
  /*136*/ COUNT_OF_ALTS    | 0x000001,
  /*137*/ COUNT_OF_PHRASES | 0x000003,
  /*138*/ PHRASE_TYPE      | G_v_index,
  /*139*/ PHRASE_TYPE      | G_f_vt_vn_indices,
  /*140*/ PHRASE_TYPE      | G_more_f_groups,

// P<f> = ...;
  /*141*/ COUNT_OF_ALTS    | 0x000001,
  /*142*/ COUNT_OF_PHRASES | 0x000002,
  /*143*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x00000a,  /* ^f */
  /*144*/ PHRASE_TYPE      | G_f_groups,

// P<g> = ...;
  /*145*/ COUNT_OF_ALTS    | 0x000001,
  /*146*/ COUNT_OF_PHRASES | 0x000002,
  /*147*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x00000b,  /* ^g */
  /*148*/ PHRASE_TYPE      | G_non_empty_content,

// P<s> = ...;
  /*149*/ COUNT_OF_ALTS    | 0x000002,
  /*150*/ COUNT_OF_PHRASES | 0x000002,
  /*151*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x00000c,  /* ^s */
  /*152*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x000000,  /* ^[0-9][0-9]* */
  /*153*/ COUNT_OF_PHRASES | 0x000002,
  /*154*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x00000c,  /* ^s */
  /*155*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x00000d,  /* ^off */

// P<o> = ...;
  /*156*/ COUNT_OF_ALTS    | 0x000001,
  /*157*/ COUNT_OF_PHRASES | 0x000002,
  /*158*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x00000e,  /* ^o */
  /*159*/ PHRASE_TYPE      | G_filename,

// P<l> = ...;
  /*160*/ COUNT_OF_ALTS    | 0x000001,
  /*161*/ COUNT_OF_PHRASES | 0x000002,
  /*162*/ REGEXP_TYPE      | WHITESPACE_ALLOWED | 0x00000f,  /* ^l */
  /*163*/ PHRASE_TYPE      | G_non_empty_content,
};

#ifdef INITCODE


extern char *parser_filename;

// objdraw.c is basically the same program, except with an ad-hoc parser
// and code to draw the model rather than output it as C structures.

// In this program we throw away any data that's not relevant to outputting a
// simple wireframe suitable for drawing on the Vectrex...

// APPMODULE: obj.c
// APPCOMMAND: walkobj

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

typedef struct {
  float x, y, z, w;
} Vertex;

typedef struct {
  float x, y, z;
} Normal;

typedef struct {
  float u, v, w;
} Texture;

typedef struct {
  float u, v, w;
} PSpace;

typedef struct {
  int v1, v2, v3;
} Face;

typedef struct {
  Vertex *vertices;
  int num_vertices;
  Face *faces;
  int num_faces;
} Model;

#include <wchar.h> // Unicode supported.
#include <math.h>

// Would be better to use FLEX package below:
#define MAX_ELEMENTS 1024
#define MAX_WSTR_LEN 1024

wchar_t *v[MAX_ELEMENTS];
static int next_free_v = 0;

wchar_t *vn[MAX_ELEMENTS];
static int next_free_vn = 0;

wchar_t *vt[MAX_ELEMENTS];
static int next_free_vt = 0;

wchar_t *f[MAX_ELEMENTS];
static int next_free_f = 0;

#define NOT_SURE 1 // trick to get the code below to execute when converting the CST to an AST
wchar_t *str_walk(int P); // return the result of a walk of a subtree as a string (off the heap)

// This replaces the default tree-walking code 'walkobj' that is usually called after compiling the AST:

int walkobj(int P, int depth) { // walk default CST (concrete syntax tree...)

  // I would rather that this code was embedded in the grammar below but that would require a major
  // restructuring of the 'uparse' parser, where currently the embedded code is what builds the AST,
  // not what is executed when walking the completed CST.
  
  if (P == -1) return -1;

  int i;
  int AST_type = (int)((unsigned int)P&(((unsigned int)AST_type_mask)<<(unsigned int)AST_type_shift));
  int AST_index = P&AST_idx_mask;
  int op = AST(AST_index+1);
  int alt = AST(AST_index+2);
  int count = AST(AST_index+3);

  if (AST_type == PHRASE_TYPE) {   // DEBUG CHANGED AST_PHRASE TO PHRASE_TYPE
    for (i = 1; i <= count; i++) walkobj(SubPhraseIdx(P,i), depth+1);

    if (op == G_v) { // P<v> = «v» <float> <float> <float> {

      wchar_t *a,*b,*c;
      wchar_t tmp[MAX_WSTR_LEN];
      swprintf(tmp, MAX_WSTR_LEN, L"  {%ls, %ls, %ls},\n", a=str_walk(SubPhraseIdx(P,2)), b=str_walk(SubPhraseIdx(P,3)), c=str_walk(SubPhraseIdx(P,4)));
      free(a); free(b); free(c);
      v[next_free_v++] = wcsdup(tmp);
      
    } if (op == G_vn) { // P<vn> = «vn» <float> <float> <float> {
    
      wchar_t *a,*b,*c;
      wchar_t tmp[MAX_WSTR_LEN];
      swprintf(tmp, MAX_WSTR_LEN, L"  {%ls, %ls, %ls},\n", a=str_walk(SubPhraseIdx(P,2)), b=str_walk(SubPhraseIdx(P,3)), c=str_walk(SubPhraseIdx(P,4)));
      free(a); free(b); free(c);
      vn[next_free_vn++] = wcsdup(tmp);
      
    } if (op == G_vt) { // P<vt> = «vt» <float> <float> <opt-float> {
    
      wchar_t *a,*b;
      wchar_t tmp[MAX_WSTR_LEN];
      swprintf(tmp, MAX_WSTR_LEN, L"  {%ls, %ls},\n", a=str_walk(SubPhraseIdx(P,2)), b=str_walk(SubPhraseIdx(P,3)));
      free(a); free(b);
      vt[next_free_vt++] = wcsdup(tmp);
      
    } else if (op == G_f_groups) {
      // P<vn-index> = <int> {
      // P<opt-f-vn> = "/" <vn-index> | {
      // P<opt-vt-index> = <int> | {
      // P<f-vt-vn-indices> = "/" <opt-vt-index> <opt-f-vn> | {
      // P<v-index> = <int> {
      // P<more-f-groups> = <v-index> <f-vt-vn-indices> <more-f-groups> | {
      // P<f-groups> = <v-index> <f-vt-vn-indices> <more-f-groups> {
      
      wchar_t *prev_v_index = str_walk(SubPhraseIdx(P,1));
      wchar_t *first_v_index = prev_v_index;
      wchar_t tmp[MAX_WSTR_LEN];
      
      for (;;) {
        P = SubPhraseIdx(P,3); // <more-f-groups>
        
        AST_type = (int)((unsigned int)P&(((unsigned int)AST_type_mask)<<(unsigned int)AST_type_shift));
        AST_index = P&AST_idx_mask;
        op = AST(AST_index+1);
        alt = AST(AST_index+2);
        count = AST(AST_index+3);

        wchar_t *next_v_index;
        if (alt == 1) { // empty alt - end of path around face
          next_v_index = first_v_index; // close the loop on the path around this face.
        } else {
          next_v_index = str_walk(SubPhraseIdx(P,1)); // <v-index> in "P<more-f-groups> = <v-index> <f-vt-vn-indices> <more-f-groups>"
        }
        if (wcstol(prev_v_index, NULL, 10) < wcstol(next_v_index, NULL, 10)) {
          // enforce canonical order then let uniq remove duplicates after sorting.
          swprintf(tmp, MAX_WSTR_LEN, L"  {%5ls,%5ls },\n", prev_v_index, next_v_index);
        } else {
          swprintf(tmp, MAX_WSTR_LEN, L"  {%5ls,%5ls },\n", next_v_index, prev_v_index);
        }
        f[next_free_f++] = wcsdup(tmp);

        if (alt == 1) { // empty alt - end of path around face
          break;
        }


        prev_v_index = next_v_index;
      }

    } else if (op == G_SS) { // Top-level - a good place to output results after parsing and building up inernal tables of the OBJect..

      char *sp, *object_name = strdup(parser_filename);
      if (*object_name == '\0') object_name = strdup("object");
      if ((sp = strrchr(object_name, '.')) != NULL) *sp = '\0'; 
      if ((sp = strrchr(object_name, '/')) != NULL) sp += 1; else sp = object_name;
      fwprintf(stdout, L"const Vertex %s_v[] = {\n  {    0,0,0}, // Vertex indices in .OBJ files are based at 1.\n", sp/*, next_free_v*/);

      for (int i = 0; i < next_free_v; i++) {
        fwprintf(stdout, L"%ls", v[i]); free(v[i]);
      }
      fwprintf(stdout, L"  { -128,-128,-128}, /* End of list marker */\n};\n");

#ifdef NEVER
      fwprintf(stdout, L"const Vertex vn[%d] = {\n", next_free_vn);
      for (int i = 0; i < next_free_vn; i++) {
        fwprintf(stdout, L"%ls", vn[i]); free(vn[i]);
      }
      fwprintf(stdout, L"};\n");
#endif

#ifdef NEVER
      fwprintf(stdout, L"const Point vt[%d] = {\n", next_free_vt);
      for (int i = 0; i < next_free_vt; i++) {
        fwprintf(stdout, L"%ls", vt[i]); free(vt[i]);
      }
      fwprintf(stdout, L"};\n");
#endif

      fwprintf(stdout, L"const Edge %s_e[] = {\n", sp/*, next_free_f*/);
      
      fflush(stdout);
      // marginally easier than calling C's qsort:

      // Ordering is less important than eliminating duplicates...
      // Sort keys are very hard to get right in this context,
      // and also the ends of the lines should have been ordered
      // lowest to highest to eliminate duplicates caused by
      // adjacent faces with opposite winding orders.

      FILE *tmp = fopen("/tmp/xxx.xx", "w");
      for (int i = 0; i < next_free_f; i++) {
        fwprintf((tmp ? tmp : stdout), L"%ls", f[i]); free(f[i]);
      }
      if (tmp) {
        fclose(tmp);
        int rc = system("/usr/bin/sort -n -t' ' -k4 -k8 < /tmp/xxx.xx | /usr/bin/uniq"); // sort -u is broken
      }
      fflush(stdout);

      fwprintf(stdout, L"  {    0,    0 }  /* End of list. (Index 0 is never used) */\n};\n");
      free(object_name);
    }

  } else if (AST_type == AST_PHRASE) {
    for (i = 1; i <= count; i++) walkobj(SubPhraseIdx(P,i), depth+1);
  }
}

void AppendAtomStr(wchar_t *s, int Literal) {
  for (int i = atom(Literal).start; i < atom(Literal).end; i++) swprintf(s+wcslen(s), 1024, L"%lc", source(i).ch);
}

int str(wchar_t *s, int P) { // walk but don't print default AST
  if (P == -1) return -1;

  int i, AST_type = (int)((unsigned int)P&(((unsigned int)AST_type_mask)<<(unsigned int)AST_type_shift));
  int AST_index = P&AST_idx_mask;
  int op = AST(AST_index+1);
  int alt = AST(AST_index+2);
  int count = AST(AST_index+3);

  if (AST_type == PHRASE_TYPE) {
    if (op == G_float) {
      float val;
      wchar_t floatval[128];
      str(floatval, SubPhraseIdx(P,1));
      swscanf(wcsrchr(floatval, ' ') ? wcsrchr(floatval, ' ') : floatval, L"%f", &val);
      swprintf(s+wcslen(s), 1024, L"%5d /*%10ls */", (int)round(val*16.0), wcsrchr(floatval, ' ') ? wcsrchr(floatval, ' ') : floatval);
    } else {
      for (i = 1; i <= count; i++) str(s, SubPhraseIdx(P,i));
    }
  } else if (AST_type == AST_PHRASE) {   // DEBUG CHANGED AST_PHRASE TO PHRASE_TYPE
    for (i = 1; i <= count; i++) str(s, SubPhraseIdx(P,i));
  } else if (AST_type == AST_ATOM_LIT) {
    AppendAtomStr(s, AST_index);
  }
}

wchar_t *str_walk(int P) { // walk but don't print default AST
  wchar_t tmp[1024] = { L'\0' };
  str(tmp, P); // dangerous, could exceed capacity.  Quick hack.
  return wcsdup(tmp);
}


#endif //INITCODE

// B<EOF> = 0;

// B<ch> = 1;

// B<bnl> = 2;

// E
#endif  // SUPPRESS_DATA
#endif  // _GRAMMAR_H_
