%{
/****************************************************************************
 *
 * VRML 2.0 to RW3.0 Converter
 * Copyright (C) 1997 Criterion Technologies
 *
 * Author   : Damian Scallan
 *
 * Based on original parser code by Gavin Bell & 
 * Daniel Woods (first port) of Silicon Graphics, Inc.
 *
 ****************************************************************************/

/****************************************************************************
 Includes
 */

#include <string.h>
#include <assert.h>

#include "llinklist.h"
#include "stack.h"
#include "fieldrec.h"
#include "tokens.h"
#include "memory.h"
#include "utilities.h" /* for RwStrdup */

/****************************************************************************
 Globals (across program)
 */
extern int my_yyinput(void *buffer, int max_size);

extern int parsing_proto;
extern int parsing_script;
extern LLStack	currentField;
int currentLineNumber = 1;

/*
 *  The YACC parser sets this to a token to direct the lexer
 *  in cases where just syntax isn't enough:
 */
int expectToken = 0;

/****************************************************************************
 Local (static) Globals
 */

/* These are used when parsing SFImage fields: */
static int sfImageIntsParsed = 0;
static int sfImageIntsExpected = 0;
static int sfImageWidth = 0;
static int sfImageHeight = 0;
static int sfImageComponents = 0;
static char currentString[256];
/* True when parsing a multiple-valued field: */
static int parsing_mf = 0;

static LLinkList fieldValueList;
static int fieldValueType;

/****************************************************************************
 Local (Static) Prototypes
 */

/* Current line number */
void yyResetLineNumber() { currentLineNumber = 1; }
int yywrap() { BEGIN INITIAL; return 1; }

VRMLBool buildInt32List(char *text);
VRMLBool buildVec2fList(char *text);
VRMLBool buildVec3fList(char *text);
VRMLBool buildColorList(char *text);
VRMLBool buildStringList(char *text);
VRMLBool buildImageList(int w, int h, int c);
VRMLBool buildAddToImageList(char *text, int pos);
VRMLBool buildFloatList(char *text);
VRMLBool buildBoolList(char *text);
VRMLBool buildTimeList(char *text);
VRMLBool buildRotationList(char *text);
VRMLBool buildTypeList(char *text, void *(*ReadType_callback)(char *));
LLinkList *getCurrentFieldValuesList(void);

extern void yyerror(const char *);

%}

/* Normal state:  parsing nodes.  The initial start state is used */
/* only to recognize the VRML header. */
%x NODE

	/* Start tokens for all of the field types, */
    /* except for MFNode and SFNode, which are almost completely handled */
    /* by the parser: */
%x SFB SFC SFF SFIMG SFI SFR SFS SFT SFV2 SFV3
%x MFC MFF MFI MFR MFS MFV2 MFV3
%x IN_SFS IN_MFS IN_SFIMG

	/* Big hairy expression for floating point numbers: */
/*float ((-?(([0-9]+\.?)|([0-9]*\.?[0-9]+)|([0-9]+\.)([eE][+\-]?[0-9]+)?))\.?) */
float   -?((([0-9]+\.?)|([0-9]*\.[0-9]+))([eE][-\+]?[0-9]+)?)\.?


	/* Ints are decimal or hex (0x##): */
int (-?([0-9]+)|(0[xX][0-9a-fA-F]*))

	/* Whitespace.  Using this pattern can screw up currentLineNumber, */
    /* so it is only used wherever it is really convenient and it is */
    /* extremely unlikely that the user will put in a carriage return */
    /* (example: between the floats in an SFVec3f) */
ws ([ \t\r\n,]|(#.*))+
	/* And the same pattern without the newline */
wsnnl ([ \t\r,]|(#.*))

	/* Legal characters to start an identifier */
idStartChar ([^\x30-\x39\x00-\x20\x22\x23\x27\x2b-\x2e\x5b-\x5d\x7b\x7d])
	/* Legal other characters in an identifier */
idRestChar      ([^\x00-\x20\x22\x23\x27\x2c\x2e\x5b-\x5d\x7b\x7d])
%%

%{
	/* Switch into a new start state if the parser */
    /* just told us that we've read a field name */
    /* and should expect a field value (or IS) */
	if (expectToken != 0) {
      if (yy_flex_debug)
          fprintf(stderr,"LEX--> Start State %d\n", expectToken);
      
      /*
       * Annoying.  This big switch is necessary because
       * LEX wants to assign particular numbers to start
       * tokens, and YACC wants to define all the tokens
       * used, too.  Sigh.
       */
      switch(expectToken) {
       	case SFVRMLBool: BEGIN SFB; break;
        case SFCOLOR: BEGIN SFC; break;
        case SFFLOAT: BEGIN SFF; break;
        case SFIMAGE: BEGIN SFIMG; break;
        case SFINT32: BEGIN SFI; break;
        case SFROTATION: BEGIN SFR; break;
        case SFSTRING: BEGIN SFS; break;
        case SFTIME: BEGIN SFT; break;
        case SFVEC2F: BEGIN SFV2; break;
        case SFVEC3F: BEGIN SFV3; break;
        case MFCOLOR: BEGIN MFC; break;
        case MFFLOAT: BEGIN MFF; break;
        case MFINT32: BEGIN MFI; break;
        case MFROTATION: BEGIN MFR; break;
        case MFSTRING: BEGIN MFS; break;
        case MFVEC2F: BEGIN MFV2; break;
        case MFVEC3F: BEGIN MFV3; break;

        /* SFNode and MFNode are special.  Here the lexer just returns */
        /* "marker tokens" so the parser knows what type of field is */
        /* being parsed; unlike the other fields, parsing of SFNode/MFNode */
        /* field happens in the parser. */
        case MFNODE: expectToken = 0; return MFNODE;
        case SFNODE: expectToken = 0; return SFNODE;
        
        default: yyerror("ACK: Bad expectToken"); break;
      }
    }
%}

	/* This is more complicated than they really need to be because */
    /* I was ambitious and made the whitespace-matching rule aggressive */
<INITIAL>"#VRML V2.0 utf8".*\n{wsnnl}*	{ BEGIN NODE; }

	/* The lexer is in the NODE state when parsing nodes, either */
    /* top-level nodes in the .wrl file, in a prototype implementation, */
    /* or when parsing the contents of SFNode or MFNode fields. */
<NODE>PROTO			{ return PROTO; }
<NODE>EXTERNPROTO	{ return EXTERNPROTO; }
<NODE>DEF			{ return DEF; }
<NODE>USE			{ return USE; }
<NODE>TO			{ return TO; }
<NODE>IS			{ return IS; }
<NODE>ROUTE			{ return ROUTE; }
<NODE>NULL			{ return SFN_NULL; }
<NODE>eventIn		{ return EVENTIN; }
<NODE>eventOut		{ return EVENTOUT; }
<NODE>field			{ return UNEXPOSEDFIELD; }
<NODE>exposedField	{ return EXPOSEDFIELD; }

	/* Legal identifiers: */
<NODE>{idStartChar}{idRestChar}*	{ yylval.string = (char *)strdup(yytext);
									  return IDENTIFIER; }

	/* All fields may have an IS declaration: */
<SFB,SFC,SFF,SFIMG,SFI,SFR,SFS,SFT,SFV2,SFV3>IS		{ BEGIN NODE;
													  expectToken = 0;
                                                      yyless(0);
                                                    }
<MFC,MFF,MFI,MFR,MFS,MFV2,MFV3>IS     { BEGIN NODE;
									    expectToken = 0;
                                        yyless(0); /* put back the IS */
                                      }

    /* All MF field types other than MFNode are completely parsed here */
    /* in the lexer, and one token is returned to the parser.  They all */
    /* share the same rules for open and closing brackets: */
<MFC,MFF,MFI,MFR,MFS,MFV2,MFV3>\[ 	{ if (parsing_mf) yyerror("Double [");
									  parsing_mf = 1;
                                    }
<MFC,MFF,MFI,MFR,MFS,MFV2,MFV3>\] 	{ int fieldType;
                                      if (!parsing_mf) yyerror("Unmatched ]");
									  fieldType = expectToken;
									  BEGIN NODE;
                                      parsing_mf = 0;
                                      expectToken = 0;
                                      return fieldType;
                                    }
                                      
<SFB>TRUE		{ BEGIN NODE; 
                  buildBoolList(yytext);
                  expectToken = 0; 
                  return SFVRMLBool; 
                }
<SFB>FALSE		{ BEGIN NODE;
                  buildBoolList(yytext);
                  expectToken = 0; 
                  return SFVRMLBool; 
                }
<SFI>{int}		{ BEGIN NODE;
                  buildInt32List(yytext);
                  expectToken = 0; 
                  return SFINT32; 
                }
<MFI>{int}		{ if (parsing_mf) buildInt32List(yytext);
                  else {
                    BEGIN NODE;
					buildInt32List(yytext);
                    expectToken = 0; 
                    return MFINT32;
                  }
                }

	/* All the floating-point types are pretty similar: */
<SFF>{float}		{ BEGIN NODE;
                      buildFloatList(yytext);
                      expectToken = 0; 
                      return SFFLOAT; 
                    }
<MFF>{float}		{ if (parsing_mf) buildFloatList(yytext);
                  	  else {
                      	/* No open bracket means a single value: */
                        BEGIN NODE;
                        buildFloatList(yytext);
                        expectToken = 0; 
                        return MFFLOAT;
                      }
                    }
<SFV2>{float}{ws}{float}    { BEGIN NODE; 
                              buildVec2fList(yytext); 
                              expectToken = 0; 
                              return SFVEC2F;
                            }
<MFV2>{float}{ws}{float}	{ if (parsing_mf) buildVec2fList(yytext);
              		  		  else {
                              	BEGIN NODE;
                                buildVec2fList(yytext);
                                expectToken = 0; 
                                return MFVEC2F;
                              }
                            }
<SFV3>({float}{ws}){2}{float}	{ BEGIN NODE; 
                                  buildVec3fList(yytext); 
                                  expectToken = 0; 
                                  return SFVEC3F; 
                                }
<MFV3>({float}{ws}){2}{float}	{ if (parsing_mf ) buildVec3fList(yytext);
								else {
									BEGIN NODE;
                                    buildVec3fList(yytext);
                                    expectToken = 0; 
                                    return MFVEC3F;
								}
								}
<SFR>({float}{ws}){3}{float}    { BEGIN NODE;
                                  buildRotationList(yytext);
                                  expectToken = 0;
                                  return SFROTATION; 
                                }
<MFR>({float}{ws}){3}{float}	{ if (parsing_mf) buildRotationList(yytext);
			              		  else {
                                    BEGIN NODE;
                                    buildRotationList(yytext);
                                    expectToken = 0; 
                                    return MFROTATION;
                                  }
                                }
<SFC>({float}{ws}){2}{float}    { BEGIN NODE;
                                  buildColorList(yytext);
                                  expectToken = 0; 
                                  return SFCOLOR; 
                                }
<MFC>({float}{ws}){2}{float}	{ if (parsing_mf) buildColorList(yytext);
			              		  else {
                                    BEGIN NODE;
                                    buildColorList(yytext);
                                    expectToken = 0; 
                                    return MFCOLOR;
                                  }
                                }
<SFT>{float}	    { BEGIN NODE;
                      buildTimeList(yytext);
                      expectToken = 0; 
                      return SFTIME; 
                    }
               
    /* SFString/MFString */
<SFS>\"				{ BEGIN IN_SFS;}
<MFS>\"				{ BEGIN IN_MFS;}
	/* Anything besides open-quote (or whitespace) is an error: */
<SFS>[^ \"\t\r\,\n]+	{ yyerror("String missing open-quote");
						  BEGIN NODE; expectToken = 0; return SFSTRING;
                        }
    /* Expect open-quote, open-bracket, or whitespace: */
<MFS>[^ \[\]\"\t\r\,\n]+	{ yyerror("String missing open-quote");
						  	  BEGIN NODE; expectToken = 0; return MFSTRING;
                            }
    /* Backslashed-quotes are OK: */
<IN_SFS,IN_MFS>\\\"	   	;
    /* Gobble up anything besides quotes and newlines. */
    /* Newlines are legal in strings, but we exclude them here so */
    /* that line number are counted correctly by the catch-all newline */
    /* rule that applies to everything. */
<IN_SFS,IN_MFS>[^\"\n]+	{strcpy(currentString, yytext);}
	/* Quote ends the string: */
<IN_SFS>\"				{ BEGIN NODE;
                          buildStringList(currentString);
                          expectToken = 0; 
                          return SFSTRING; 
                        }
<IN_MFS>\"				{ if (parsing_mf) { 
                            BEGIN MFS; 
                            buildStringList(currentString); 
                          }
                          else {
                            BEGIN NODE;
                            buildStringList(currentString);
                            expectToken = 0; 
                            return MFSTRING;
                          }
                        }

    /* SFImage: width height numComponents then width*height integers: */
<SFIMG>{int}{ws}{int}   { int w, h;
                          sscanf(yytext, "%i %i", &w, &h);
                          sfImageWidth = w;
                          sfImageHeight = h;
                          sfImageIntsParsed = 0;           
                          BEGIN IN_SFIMG;
                        }
<IN_SFIMG>{int}         {
                          if (sfImageIntsParsed == 0)
						  {
                              int c;
                              sscanf(yytext, "%i", &c);
                              sfImageComponents = c;
                              sfImageIntsExpected = sfImageWidth * sfImageHeight;
                              if (sfImageIntsExpected > 0)
                              {
                                  buildImageList(sfImageWidth, sfImageHeight ,sfImageComponents);
                              }
                          }
                          if (sfImageIntsParsed == sfImageIntsExpected)
                          {
                              if (sfImageIntsExpected > 0)
                              {
                                  buildAddToImageList(yytext, sfImageIntsParsed - 1);
                              }
                              BEGIN NODE; 
                              expectToken = 0;
                              return SFIMAGE;
                          }
                          else if (sfImageIntsParsed > 0)
                          {
                              buildAddToImageList(yytext, sfImageIntsParsed - 1);
                          }
                          ++sfImageIntsParsed;
                        }

	/* Whitespace and catch-all rules apply to all start states (except IN_SFS & IN_MFS): */
    /* <*>{wsnnl}+		; */
<NODE,SFB,SFC,SFF,SFIMG,SFI,SFR,SFS,SFT,SFV2,SFV3,MFC,MFF,MFI,MFR,MFS,MFV2,MFV3,IN_SFIMG>{wsnnl}+		;
	/* This is also whitespace, but we'll keep track of line number */
    /* to report in errors: */
    /* <*>{wsnnl}*\n{wsnnl}*		{ ++currentLineNumber; } */
<NODE,SFB,SFC,SFF,SFIMG,SFI,SFR,SFS,SFT,SFV2,SFV3,MFC,MFF,MFI,MFR,MFS,MFV2,MFV3,IN_SFIMG>{wsnnl}*\n{wsnnl}*		{ ++currentLineNumber; }

	/* This catch-all rule catches anything not covered by any of */
    /* the above: */
<*>. 			{ return yytext[0]; }

%%

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    Lexer implementation functions

   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

LLinkList *getCurrentFieldValuesList(void)
{
    LLinkList *list = NULL;
    FieldRec *fr;
	
    fr = (FieldRec *)Stack_Top(&currentField);
    if (fr)
    {
        list = &fr->fieldValueList;
    }

	return (list);
}

void resetCurrentFieldValuesList(int type)
{
    FieldRec *fr;
	
    fr = (FieldRec *)Stack_Top(&currentField);
    if (fr)
    {
        LLinkList_Init(&fr->fieldValueList);
    }
}

void destroyCurrentFieldValuesList(void)
{
    LLinkList *list;

    list = getCurrentFieldValuesList();
    if (list)
    {
        LLinkList_Destroy(list, NULL);
    }
}

VRMLBool buildInt32List(char *text)
{
    if (!parsing_script)
    {
        sfint32 *index;

        if ((index = sfint32_Create()))
        {
            sscanf(text, "%i", index);
            if (LLinkList_AddData(getCurrentFieldValuesList(), index))
            {
                return (TRUE);
            }
        }

        return (FALSE);
    }

    return (TRUE);
}

VRMLBool buildVec2fList(char *text)
{
    if (!parsing_script)
    {
        sfvec2f *point;

        if ((point = sfvec2f_Create()))
        {
            sscanf(yytext, "%f %f", &point->x, &point->y);
            if (LLinkList_AddData(getCurrentFieldValuesList(), point))
            {
                return (TRUE);
            }
        }

        return (FALSE);
    }

    return (TRUE);
}

VRMLBool buildVec3fList(char *text)
{
#if 0
    sfvec3f *point;

    if ((point = sfvec3f_Create()))
    {
        sscanf(text, "%f %f %f", &point->x, &point->y, &point->z);
        if (LLinkList_AddData(getCurrentFieldValuesList(), point))
        {
            return (TRUE);
        }
    }

    return (FALSE);
#endif /* 0 */
    return (buildTypeList(text, (void *(*)(char *))sfvec3f_Read));
}

VRMLBool buildColorList(char *text)
{
    if (!parsing_script)
    {
        sfcolor *color;

        if ((color = sfcolor_Create()))
        {
            sscanf(text, "%f %f %f", &color->r, &color->g, &color->b);
            if (LLinkList_AddData(getCurrentFieldValuesList(), color))
            {
                return (TRUE);
            }
        }

        return (FALSE);
    }

    return (TRUE);
}

VRMLBool buildImageList(int w, int h, int c)
{
    if (!parsing_script)
    {
        sfimage *image;

        if ((image = sfimage_Create()))
        {
            image->width = (unsigned short)w;
            image->height = (unsigned short)h;
            image->components = (char)c;
            if (!(image->pixels = vrmlMalloc(w * h * c * sizeof(char))))
            {
                sfimage_Destroy(image);

                return (FALSE);
            }

            if (LLinkList_AddData(getCurrentFieldValuesList(), image))
            {
                return (TRUE);
            }
        }

        return (FALSE);
    }

    return (TRUE);
}

VRMLBool buildAddToImageList(char *text, int pos)
{
    if (!parsing_script)
    {
        LLinkList *fieldList = NULL;
        sfimage *image;
        int start = pos * sfImageComponents;

        fieldList = getCurrentFieldValuesList();
        if (fieldList)
        {
            image = (sfimage *)LLinkList_GetItem(fieldList, 0);
            if (image)
            {
                int pixel;
                int mask = 0xFF << (sfImageComponents - 1) * 8;
                int compNum, component;

                sscanf(text, "%i", &pixel);
                for (compNum = sfImageComponents; compNum > 0; compNum--)
                {
                    component = pixel & mask;
                    component = component >> ((compNum - 1) * 8);
                    image->pixels[start + sfImageComponents - compNum] = component;
                    mask >>= 8;
                }

                return (TRUE);
            }
        }

        return (FALSE);
    }

    return (TRUE);
}

VRMLBool buildStringList(char *text)
{
    if (!parsing_script)
    {
        sfstring *string;

        if ((string = sfstring_Create()))
        {
            *string = RwStrdup(text);
            if (LLinkList_AddData(getCurrentFieldValuesList(), string))
            {
                return (TRUE);
            }
        }

        return (FALSE);
    }

    return (TRUE);
}

VRMLBool buildFloatList(char *text)
{
    if (!parsing_script)
    {
        sffloat *real;

        if ((real = sffloat_Create()))
        {
            sscanf(text, "%f", real);
            if (LLinkList_AddData(getCurrentFieldValuesList(), real))
            {
                return (TRUE);
            }
        }

        return (FALSE);
    }

    return (TRUE);
}

VRMLBool buildBoolList(char *text)
{
    if (!parsing_script)
    {
        sfbool *boolean;

        if ((boolean = sfbool_Create()))
        {
            if (strcmp(text, "TRUE") == 0)
            {
                *boolean = TRUE;
            }
            else
            {
                assert(strcmp(text, "FALSE") == 0);
                *boolean = FALSE;
            }
            if (LLinkList_AddData(getCurrentFieldValuesList(), boolean))
            {
                return (TRUE);
            }
        }

        return (FALSE);
    }

    return (TRUE);
}

VRMLBool buildTimeList(char *text)
{
    if (!parsing_script)
    {
        sftime *time;
        float sft;

        if ((time = sftime_Create()))
        {
            sscanf(text, "%f", &sft);
            *time = (double)sft;
            if (LLinkList_AddData(getCurrentFieldValuesList(), time))
            {
                return (TRUE);
            }
        }

        return (FALSE);
    }

    return (TRUE);
}

VRMLBool buildRotationList(char *text)
{
    if (!parsing_script)
    {
        sfrotation *rotation;

        if ((rotation = sfrotation_Create()))
        {
            sscanf(text, "%f %f %f %f", &rotation->x, 
                &rotation->y, &rotation->z, &rotation->angle);
            if (LLinkList_AddData(getCurrentFieldValuesList(), rotation))
            {
                return (TRUE);
            }
        }

        return (FALSE);
    }

    return (TRUE);
}

VRMLBool buildTypeList(char *text, void *(*ReadType_callback)(char *))
{
    if (!parsing_script)
    {
        void *data;

        if ((data = ReadType_callback(text)))
        {
            if (LLinkList_AddData(getCurrentFieldValuesList(), data))
            {
                return (TRUE);
            }
        }

        return (FALSE);
    }

    return (TRUE);
}

void 
yylex_tidy_up(void)
{
    if (yy_current_buffer)
    {
        yy_delete_buffer(yy_current_buffer);
        yy_current_buffer = NULL;
    }
}


