/*
 *
 * VRML to RW converter plug-in
 */

/****************************************************************************
 * 
 * VRML 2.0 to RW3.0 Converter
 * Copyright (C) 1998 Criterion Technologies
 *
 * Author  : Damian Scallan 
 *
 * Module  : parserback.c
 *                                                                         
 * Purpose : functions used by parser.c to build the abstarctNode tree
 *                                                                         
 ****************************************************************************/

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

#include <stdio.h>
#include <assert.h>

#include "rpplugin.h"
#include "parser.h"
#include "tokens.h"
#include "vrmlnodetype.h"
#include "abstractnode.h"
#include "converter.h"
#include "stack.h"
#include "llinklist.h"
#include "fieldrec.h"
#include "symtable.h"
#include "route.h"
#include "rpvrmlanim.h"
#include "rpvrml.h"

static const char __RWUNUSED__ rcsid[] =
    "@@(#)$Id: parserback.c,v 1.52 2001/02/05 11:50:51 johns Exp $";

/* Logging stuff */
#include "logfile.h"

extern RwStream    *yyin, *yyout;

extern RwInt32      currentLineNumber;

#if YYDEBUG != 0
extern RwInt32      yydebug;
#endif /* YYDEBUG != 0 */
extern RwInt32      yy_flex_debug;
extern RwInt32      expectToken;
extern RwInt32      parsing_proto;
extern RwInt32      parsing_script;

extern RwInt32      my_yyinput(void *buffer, RwInt32 max_size);
extern RwInt32      yyparse(void);
extern void         yyrestart(const RwStream * input_file);
extern RwInt32      yywrap(void);
extern void         yyResetLineNumber(void);
extern void         yylex_tidy_up(void);
extern void         resetCurrentFieldValuesList(RwInt32 type);
extern LLinkList   *getCurrentFieldValuesList(void);
extern void         destroyCurrentFieldValuesList(void);

/****************************************************************************
 Local Defines
 */

#define IS_STR  (RWSTRING(" IS "))
#define PRINTTREEx

typedef struct RpVrmlSNode RpVrmlSnode;
struct RpVrmlSNode
{
    const RwChar       *string;
};

RpVrmlSnode         snodes[] = {
#include "sn.wrl"
};

/****************************************************************************
 Globals (across program)
 */

RwChar              path[256];
extern LLStack      currentField; /* of FieldRec *//* In parser.c, hence extern */

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

RwChar              errorMsg[256];
RwInt32             parsing_standard_proto = FALSE;

LLinkList           rootAbstractNodeList; /* of AbstractNode */
LLStack             GAbstractNodeListStack; /* of lists of parsed AbstractNodes */

/*
 *  Currently-being-define proto.  Prototypes may be nested, so a stack
 *  is needed:
 */
LLStack             currentProtoStack; /* of VrmlNodeType */

static void
yyerror(const RwChar * msg)
{
    RWFUNCTION(RWSTRING("yyerror"));
    RWASSERT(msg);

    if (msg)
    {
        RWERROR((E_RP_VRML_PARSER, currentLineNumber + 1, msg));
    }
    expect(0);

    RWRETURNVOID();
}

static              RwBool
ANodeListPush(void)
{
    LLinkList          *anList;

    RWFUNCTION(RWSTRING("ANodeListPush"));

    anList = (LLinkList *) RwMalloc(sizeof(LLinkList));
    if (!anList)
    {
        RWRETURN(FALSE);
    }

    LLinkList_Init(anList);
    if (!Stack_Push(&GAbstractNodeListStack, anList))
    {
        RwFree(anList);

        RWRETURN(FALSE);
    }

    RWRETURN(TRUE);
}

static              RwBool
ANodeListPop(void)
{
    LLinkList          *anList;

    RWFUNCTION(RWSTRING("ANodeListPop"));

    anList = (LLinkList *) Stack_Pop(&GAbstractNodeListStack);
    if (!anList)
    {
        RWRETURN(FALSE);
    }

    LLinkList_Destroy(anList, DESTROYCALLBACK(AbstractNode_Destroy));
    RwFree(anList);

    RWRETURN(TRUE);
}

static LLinkList   *
ANodeListGet(void)
{
    LLinkList          *anList;

    RWFUNCTION(RWSTRING("ANodeListGet"));

    anList = (LLinkList *) Stack_Top(&GAbstractNodeListStack);

    RWRETURN(anList);
}

RwBool
beginProto(const RwChar * protoName)
{
    VrmlNodeType       *t;

    RWFUNCTION(RWSTRING("beginProto"));
    RWASSERT(protoName);

    if (protoName)
    {
        FieldRec           *fr;

        parsing_proto = TRUE;
        /* 
         * * Any protos in the implementation 
         * * are in a local namespace:
         */
        TypeList_pushNameSpace();
        t = VrmlNodeType_Create(protoName);
        t->userProto = !parsing_standard_proto;
        Stack_Push(&currentProtoStack, (void *) t);

        /* read default field values */
        fr = FieldRec_Create();
        fr->nodeType = t;
        fr->type = PROTO;
        Stack_Push(&currentField, fr);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
endProto(void)
{
    RWFUNCTION(RWSTRING("endProto"));

    /* Make any protos defined in implementation unavailable: */
    TypeList_popNameSpace();
    /* Add this proto definition: */
    if (Stack_IsEmpty(&currentProtoStack))
    {
        RWERROR((E_RP_VRML_EMPTYPROTOSTACK, currentLineNumber));

        RWRETURN(FALSE);
    }
    else
    {
        VrmlNodeType       *t;
        FieldRec           *fr;

        t = (VrmlNodeType *) Stack_Pop(&currentProtoStack);
        TypeList_addToNameSpace(t);
        if (Stack_IsEmpty(&currentProtoStack))
        {
            parsing_proto = FALSE;
        }
        if ((fr = (FieldRec *) Stack_Pop(&currentField)))
        {
            FieldRec_Destroy(fr);
        }
    }

    RWRETURN(TRUE);
}

RwInt32
addField(const RwChar * type, const RwChar * name)
{
    RWFUNCTION(RWSTRING("addField"));
    RWASSERT(type);
    RWASSERT(name);

    if (type && name)
    {
        RWRETURN(add((Vrml_addCB) VrmlNodeType_addField, type, name));
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(0);
}

RwInt32
addEventIn(const RwChar * type, const RwChar * name)
{
    RWFUNCTION(RWSTRING("addEventIn"));
    RWASSERT(type);
    RWASSERT(name);

    if (type && name)
    {
        RWRETURN(add((Vrml_addCB) VrmlNodeType_addEventIn, type, name));
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(0);
}

RwInt32
addEventOut(const RwChar * type, const RwChar * name)
{
    RWFUNCTION(RWSTRING("addEventOut"));
    RWASSERT(type);
    RWASSERT(name);

    if (type && name)
    {
        RWRETURN(add
                 ((Vrml_addCB) VrmlNodeType_addEventOut, type, name));
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(-1);
}

RwInt32
addExposedField(const RwChar * type, const RwChar * name)
{
    RWFUNCTION(RWSTRING("addExposedField"));
    RWASSERT(type);
    RWASSERT(name);

    if (type && name)
    {
        RWRETURN(add
                 ((Vrml_addCB) VrmlNodeType_addExposedField, type,
                  name));
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(0);
}

RwInt32
add(Vrml_addCB vrmlAdd, const RwChar * typeString, const RwChar * name)
{
    RWFUNCTION(RWSTRING("add"));
    RWASSERT(typeString);
    RWASSERT(name);

    if (typeString && name)
    {
        RwInt32             type = fieldType(typeString);
        VrmlNodeType       *t;

        if (type == 0)
        {
            rwsprintf(errorMsg,
                      RWSTRING("Error: invalid field type: %d \n"),
                      type);
            yyerror(errorMsg);
        }

        /*
         * Need to add support for Script nodes:
         * if (inScript) ... ???
         */

        if (Stack_IsEmpty(&currentProtoStack))
        {
            yyerror(RWSTRING
                    ("Error: declaration outside of prototype\n"));
        }

        t = (VrmlNodeType *) Stack_Top(&currentProtoStack);
        vrmlAdd(t, name, type);

        RWRETURN(type);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(0);
}

RwInt32
fieldType(const RwChar * type)
{
    RWFUNCTION(RWSTRING("fieldType"));
    RWASSERT(type);

    if (type)
    {
        if (rwstrcmp(type, RWSTRING("SFBool")) == 0)
            RWRETURN(SFVRMLBool);
        if (rwstrcmp(type, RWSTRING("SFColor")) == 0)
            RWRETURN(SFCOLOR);
        if (rwstrcmp(type, RWSTRING("SFFloat")) == 0)
            RWRETURN(SFFLOAT);
        if (rwstrcmp(type, RWSTRING("SFImage")) == 0)
            RWRETURN(SFIMAGE);
        if (rwstrcmp(type, RWSTRING("SFInt32")) == 0)
            RWRETURN(SFINT32);
        if (rwstrcmp(type, RWSTRING("SFNode")) == 0)
            RWRETURN(SFNODE);
        if (rwstrcmp(type, RWSTRING("SFRotation")) == 0)
            RWRETURN(SFROTATION);
        if (rwstrcmp(type, RWSTRING("SFString")) == 0)
            RWRETURN(SFSTRING);
        if (rwstrcmp(type, RWSTRING("SFTime")) == 0)
            RWRETURN(SFTIME);
        if (rwstrcmp(type, RWSTRING("SFVec2f")) == 0)
            RWRETURN(SFVEC2F);
        if (rwstrcmp(type, RWSTRING("SFVec3f")) == 0)
            RWRETURN(SFVEC3F);
        if (rwstrcmp(type, RWSTRING("MFColor")) == 0)
            RWRETURN(MFCOLOR);
        if (rwstrcmp(type, RWSTRING("MFFloat")) == 0)
            RWRETURN(MFFLOAT);
        if (rwstrcmp(type, RWSTRING("MFInt32")) == 0)
            RWRETURN(MFINT32);
        if (rwstrcmp(type, RWSTRING("MFNode")) == 0)
            RWRETURN(MFNODE);
        if (rwstrcmp(type, RWSTRING("MFRotation")) == 0)
            RWRETURN(MFROTATION);
        if (rwstrcmp(type, RWSTRING("MFString")) == 0)
            RWRETURN(MFSTRING);
        if (rwstrcmp(type, RWSTRING("MFVec2f")) == 0)
            RWRETURN(MFVEC2F);
        if (rwstrcmp(type, RWSTRING("MFVec3f")) == 0)
            RWRETURN(MFVEC3F);

        rwsprintf(errorMsg, "Illegal field type: %s\n", type);
        yyerror(errorMsg);

        RWRETURN(0);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(0);
}

RwBool
enterNode(const RwChar * nodeType)
{
    RWFUNCTION(RWSTRING("enterNode"));
    RWASSERT(nodeType);

    if (nodeType)
    {
        AbstractNode       *an;
        VrmlNodeType       *t;
        FieldRec           *fr;

        t = TypeList_find(nodeType);
        if (t == NULL)
        {
            RwChar              tmp[256];

            rwsprintf(tmp, "Unknown node type '%s'", nodeType);
            yyerror(tmp);

            RWRETURN(FALSE);
        }

        fr = FieldRec_Create();
        fr->nodeType = t;

        an = AbstractNode_Create(nodeType);
        an->lineNum = currentLineNumber + 1;
        fr->abstractNode = an;
        an->userProto = fr->nodeType->userProto;

        Stack_Push(&currentField, fr);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
enterUse(const RwChar * name)
{
    RWFUNCTION(RWSTRING("enterUse"));
    RWASSERT(name);

    if (name)
    {
        AbstractNode       *an;
        AbstractNode       *anTop;
        FieldRec           *frTop;
        AbstractField      *af;

        an = SymTab_GetEntry(name);
        if (!an)
        {
            RWRETURN(FALSE);
        }

        if (!parsing_script)
        {
            frTop = (FieldRec *) Stack_Top(&currentField);
            anTop = frTop->abstractNode;
            RWASSERT((frTop->type == SFNODE)
                     || (frTop->type == MFNODE));

            af = frTop->abstractField;
            RWASSERT(af);
#if (0)
            LLinkList_AddData(&af->fieldValueList,
                              AbstractNode_AddRef(an));
#endif /* (0) */
            LLinkList_AddData(getCurrentFieldValuesList(),
                              AbstractNode_AddRef(an));
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static AbstractNode *
resolveIsName(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("resolveIsName"));
    RWASSERT(an);

    if (an)
    {
        AbstractField      *af;

        LLinkList_IteratorReset(&an->fields);
        while ((af =
                (AbstractField *) LLinkList_IteratorPrev(&an->fields)))
        {
            if ((af->type != SFNODE) && (af->type != MFNODE))
            {
                AbstractField      *afDec;
                RwChar             *isStr;

                if ((isStr = rwstrstr(af->name, IS_STR)))
                {
                    RwChar              isName[256];
                    RwChar              fieldName[256];
                    RwInt32             isPos = isStr - af->name;

                    rwstrncpy(fieldName, af->name, isPos);
                    fieldName[isPos] = '\0';
                    rwstrcpy(isName,
                             af->name + isPos + rwstrlen(IS_STR));

                    /* check the IS field exists in the definition */
                    afDec = AbstractNode_GetAbstractField(an, isName);
                    if (afDec)
                    {
                        /* rename the field */
                        RwFree(afDec->name);
                        rwstrdup(afDec->name, fieldName);
                    }
                }
            }
        }

        RWRETURN(an);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractNode *)NULL);
}

RwBool
instanceIs(const RwChar * name)
{
    RWFUNCTION(RWSTRING("instanceIs"));
    RWASSERT(name);

    if (name)
    {
        FieldRec           *frTop;
        AbstractField      *af;
        VrmlNodeType       *t;
        LLinkList          *fields;
        AbstractField      *userAf = (AbstractField *)NULL;

        frTop = (FieldRec *) Stack_Top(&currentField);
        if (!frTop)
        {
            RWRETURN(FALSE);
        }

        af = frTop->abstractField;
        if (!af)
        {
            RWRETURN(FALSE);
        }

        if (af->type == 0)
        {
            RWRETURN(FALSE);
        }

        /* has field <name> */
        t = (VrmlNodeType *) Stack_Top(&currentProtoStack);
        RWASSERT(t);
        if (!t)
        {
            RWRETURN(FALSE);
        }

        /* check the field exists */
        fields = &t->fields;
        LLinkList_IteratorReset(fields);
        while ((userAf =
                (AbstractField *) LLinkList_IteratorNext(fields)))
        {
            if ((userAf != NULL) && (rwstrcmp(userAf->name, name) == 0))
            {
                break;
            }
        }

        if (!userAf)
        {
            /* check the eventIns exists */
            fields = &t->eventIns;
            LLinkList_IteratorReset(fields);
            while ((userAf =
                    (AbstractField *) LLinkList_IteratorNext(fields)))
            {
                if ((userAf != NULL)
                    && (rwstrcmp(userAf->name, name) == 0))
                {
                    break;
                }
            }
        }
        if (!userAf)
        {
            /* check the eventOuts exists */
            fields = &t->eventOuts;
            LLinkList_IteratorReset(fields);
            while ((userAf =
                    (AbstractField *) LLinkList_IteratorNext(fields)))
            {
                if ((userAf != NULL)
                    && (rwstrcmp(userAf->name, name) == 0))
                {
                    break;
                }
            }
        }

        RWASSERT(userAf);
        if (userAf)
        {
            RwChar              IsName[256];

            rwstrcpy(IsName, frTop->fieldName);
            rwstrcat(IsName, IS_STR);
            rwstrcat(IsName, name);
            /* rename the field to its IS name */
            rwstrdup(frTop->fieldName, IsName);
            /*RwFree */
            RwFree(frTop->abstractField->name);
            rwstrdup(frTop->abstractField->name, IsName);
        }
        else
        {
            RWRETURN(FALSE);
        }                      /* print error and quit */

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static AbstractNode *
copyNonNodeFields(AbstractNode * anDst, AbstractNode * anSrc)
{
    RWFUNCTION(RWSTRING("copyNonNodeFields"));
    RWASSERT(anDst);
    RWASSERT(anSrc);

    if (anDst && anSrc)
    {
        AbstractField      *af;
        LLLink             *iterator;

        iterator = LLinkList_IteratorReset(&anSrc->fields);
        while ((af =
                (AbstractField *) LLinkList_IteratorPrev(&anSrc->
                                                         fields)))
        {
            if ((af->type != SFNODE) && (af->type != MFNODE))
            {
                LLinkList_AddData(&anDst->fields,
                                  AbstractField_AddRef(af));
            }
        }
        LLinkList_IteratorRestore(&anSrc->fields, iterator);

        RWRETURN(anDst);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractNode *)NULL);
}

static AbstractField *
copyUserProtoAfII(AbstractField * af)
{
    RWFUNCTION(RWSTRING("copyUserProtoAfII"));
    RWASSERT(af);

    if (af)
    {
        Field              *field, *fieldNew;
        AbstractNode       *an, *anNew;
        AbstractField      *afNew;

        afNew = AbstractField_Create(af->name, af->type);
        if (!afNew)
        {
            RWRETURN((AbstractField *)NULL);
        }

        fieldNew = AbstractField_GetField(afNew);

        fieldNew->fieldArray = (sfAny *) RwMalloc(sizeof(sfAny));
        if (!fieldNew->fieldArray)
        {
            AbstractField_Destroy(afNew);

            RWRETURN(FALSE);
        }

        /* get the AbstractNode from the field */
        field = AbstractField_GetField(af);
        an = FieldSfnode_GetValue(field);

        anNew = AbstractNode_Copy(an);
        if (!anNew)
        {
            AbstractField_Destroy(afNew);

            RWRETURN(FALSE);
        }

        fieldNew->items = 1;
        fieldNew->fieldArray->element = anNew;
        afNew->Items = 1;

        RWRETURN(afNew);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractField *)NULL);
}

static AbstractNode *
copyUserProtoFields(AbstractNode * anDst, AbstractNode * anSrc)
{
    RWFUNCTION(RWSTRING("copyUserProtoFields"));
    RWASSERT(anDst);
    RWASSERT(anSrc);

    if (anDst && anSrc)
    {
        AbstractField      *af;
        LLLink             *iterator;

        iterator = LLinkList_IteratorReset(&anSrc->fields);
        while ((af =
                (AbstractField *) LLinkList_IteratorPrev(&anSrc->
                                                         fields)))
        {
            if ((af->type == SFNODE) || (af->type == MFNODE))
            {
                Field              *field;
                AbstractNode       *anChild;

                field = AbstractField_GetField(af);
                anChild = FieldSfnode_GetValue(field);
                if (anChild->userProto)
                {
                    AbstractField      *newAf;

                    newAf = copyUserProtoAfII(af);
                    if (newAf)
                    {
                        AbstractNode       *newNode;

                        field = AbstractField_GetField(newAf);
                        newNode = FieldSfnode_GetValue(field);

                        /* inherit any fields */
                        copyNonNodeFields(newNode, anDst);

                        LLinkList_AddData(&anDst->fields,
                                          AbstractField_AddRef(newAf));
                    }
                }
            }
        }
        LLinkList_IteratorRestore(&anSrc->fields, iterator);

        RWRETURN(anDst);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractNode *)NULL);
}

static AbstractNode *
instanceUserProtoFields(AbstractNode * an)
{
    RWFUNCTION(RWSTRING("instanceUserProtoFields"));
    RWASSERT(an);

    if (an)
    {
        AbstractField      *af;
        LLLink             *iterator;

        iterator = LLinkList_IteratorReset(&an->fields);
        while ((af =
                (AbstractField *) LLinkList_IteratorPrev(&an->fields)))
        {
            if ((af->type == SFNODE) || (af->type == MFNODE))
            {
                Field              *field;
                AbstractNode       *anChild;

                field = AbstractField_GetField(af);
                anChild = FieldSfnode_GetValue(field);
                if (anChild->userProto)
                {
                    instanceUserProto(anChild);
                }
            }
        }
        LLinkList_IteratorRestore(&an->fields, iterator);

        RWRETURN(an);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractNode *)NULL);
}

AbstractNode       *
instanceClone(AbstractNode * anDef, AbstractNode * anProto,
              VrmlNodeType * t)
{
    RWFUNCTION(RWSTRING("instanceClone"));
    RWASSERT(anDef);
    RWASSERT(anProto);
    RWASSERT(t);

    if (anDef && anProto && t)
    {
        copyNonNodeFields(anProto, anDef);
        resolveIsName(anProto);
        copyUserProtoFields(anProto, anDef);
        instanceUserProtoFields(anProto);

        RWRETURN(anProto);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractNode *)NULL);
}

static RwStream    *
UrlGetStream(const RwChar * url)
{
    RWFUNCTION(RWSTRING("UrlGetStream"));
    RWASSERT(url);

    if (url)
    {
        RwStream           *stream;
        RwChar              pathname[256];

        /* nieve */
        rwstrcpy(pathname, path);
        rwstrcat(pathname, url);

        stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, pathname);

        RWRETURN(stream);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((RwStream *)NULL);
}

static              RwBool
ParseExternNodeFile(RwStream * stream)
{
    RWFUNCTION(RWSTRING("ParseExternNodeFile"));
    RWASSERT(stream);

    if (stream)
    {
        RwInt32             errcode;

        /* turn off debug info */
#if YYDEBUG != 0
        yydebug = 0;
#endif /* YYDEBUG != 0 */
        yy_flex_debug = 0;

        /* get rid of any previous state */
        yyrestart((const RwStream *)NULL);
        yyResetLineNumber();

        ANodeListPush();
        errcode = yyparse();

        /* close the stream */
        RwStreamClose(stream, NULL);

        if (errcode != 0)
        {
            RWRETURN(FALSE);
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static              RwBool
checkExternProtoDef(VrmlNodeType * vtDec, VrmlNodeType * vtDef)
{
    RWFUNCTION(RWSTRING("checkExternProtoDef"));
    RWASSERT(vtDec);
    RWASSERT(vtDef);

    if (vtDec && vtDef)
    {
        /* to impleament properly in VrmlNodeType.c */
        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static RwChar      *
urlProtoSplit(const RwChar * urlProto)
{
    RWFUNCTION(RWSTRING("urlProtoSplit"));
    RWASSERT(urlProto);

    if (urlProto)
    {
        RwChar             *proto;

        proto = rwstrrchr(urlProto, '#');
        if (proto)
        {
            *proto = '\0';
            proto++;

            RWRETURN(proto);
        }

        RWRETURN((char *)NULL);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((char *)NULL);
}

static AbstractNode *
instanceExternProto(AbstractNode * anProto)
{
    RWFUNCTION(RWSTRING("instanceExternProto"));
    RWASSERT(anProto);

    if (anProto)
    {
        VrmlNodeType       *t, *vtDef;
        AbstractNode       *anDef;
        AbstractField      *af;
        Field              *field;
        sfstring           *url;
        RwStream           *stream = (RwStream *) NULL;
        RwChar             *protoName = (RwChar *) NULL;
        RwInt32             i;

        /* get the ExternProto url */
        t = TypeList_find(anProto->name);
        if (!t)
        {
            RwChar              tmp[1000];

            rwsprintf(tmp, RWSTRING("Unknown node type '%s'"),
                      anProto->name);
            yyerror(tmp);

            RWRETURN((AbstractNode *)NULL);
        }

        af = t->afUrl;
        if (!af)
        {
            RWRETURN((AbstractNode *)NULL);
        }

        field = AbstractField_GetField(af);
        for (i = 0; i < field->items; i++)
        {
            RwChar             *proto;

            url = FieldMfstring_GetValue(field, i);
            proto = urlProtoSplit(*url);

            stream = UrlGetStream(*url);
            if (stream)
            {
                protoName = proto;
                break;
            }
        }

        if (!stream)
        {
            RWRETURN((AbstractNode *)NULL);
        }

        if (!protoName)
        {
            protoName = anProto->name;
        }

        /* new proto context */
        TypeList_pushNameSpace();

        if (!ParseExternNodeFile(stream))
        {
            /* pop proto context */
            TypeList_popNameSpace();

            RWRETURN((AbstractNode *)NULL);
        }

        /* get the newly parsed proto, for now assume has same name */
        vtDef = TypeList_find(protoName);
        if (!vtDef)
        {
            /* pop proto context */
            TypeList_popNameSpace();

            RWRETURN((AbstractNode *)NULL);
        }

        if (!checkExternProtoDef(t, vtDef))
        {
            /* pop proto context */
            TypeList_popNameSpace();

            RWRETURN((AbstractNode *)NULL);
        }

        /* get the def node */
        anDef = VrmlNodeType_GetBaseAbstractNode(vtDef);
        if (!anDef)
        {
            /* pop proto context */
            TypeList_popNameSpace();

            RWRETURN((AbstractNode *)NULL);
        }

        /* attach it to the protoNode */
        LLinkList_AddData(&t->aNodeDefs, AbstractNode_AddRef(anDef));

        /* pop proto context */
        TypeList_popNameSpace();

        RWRETURN(anDef);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN((AbstractNode *)NULL);
}

AbstractNode       *
instanceUserProto(AbstractNode * anProto)
{
    RWFUNCTION(RWSTRING("instanceUserProto"));
    RWASSERT(anProto);

    if (anProto)
    {
        VrmlNodeType       *t;
        AbstractNode       *anDef, *anUser;

        t = TypeList_find(anProto->name);
        if (!t)
        {
            RWRETURN((AbstractNode *)NULL);
        }

        /* assumes only one item in list */
        anDef = (AbstractNode *) LLinkList_GetItem(&t->aNodeDefs, 0);
        if (!anDef)
        {
            anDef = instanceExternProto(anProto);
        }
        if (!anDef)
        {
            RWERROR((E_RP_VRML_PROTODEF, anProto->name));

            RWRETURN((AbstractNode *)NULL);
        }

        anUser = instanceClone(anDef, anProto, t);

        RWRETURN(anUser);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

AbstractNode       *
exitNode(void)
{
    AbstractNode       *an = (AbstractNode *) NULL;
    FieldRec           *fr;

    RWFUNCTION(RWSTRING("exitNode"));

    if ((fr = (FieldRec *) Stack_Pop(&currentField)))
    {
        an = fr->abstractNode;

        if (an->userProto)
        {
            /* replace by its instanced definition */
#if (0)
            an = instanceUserProto(an);
#endif /* (0) */
        }
        if (!an)
        {
            RWRETURN((AbstractNode *)NULL);
        }

        if (!Stack_IsEmpty(&currentField))
        {
            AbstractNode       *anTop;
            FieldRec           *frTop;
            AbstractField      *af;

            frTop = (FieldRec *) Stack_Top(&currentField);
            anTop = frTop->abstractNode;

            switch (frTop->type)
            {
                case SFNODE:
                case MFNODE:
                    {
                        AbstractField      *afOld;

                        /* child node so add to parent */
                        afOld = frTop->abstractField;
                        LLinkList_IteratorReset(&anTop->fields);
                        af = (AbstractField *)
                            LLinkList_IteratorNext(&anTop->fields);
                        RWASSERT(af);
                        LLinkList_AddData(getCurrentFieldValuesList(),
                                          AbstractNode_AddRef(an));
                        break;
                    }
                case PROTO:
                    {
                        /* user proto definition so add to proto's list of definitions */
                        LLinkList_AddData(&frTop->nodeType->aNodeDefs,
                                          AbstractNode_AddRef(an));
                        break;
                    }
                default:
                    {
                        RWASSERT(FALSE);
                        RWRETURN((AbstractNode *)NULL);
                    }
            }
        }
        else
        {
            if (isSceneGraph(an->name))
            {
                /* scene graph root node so add to root node list */
                LLinkList_AddData(ANodeListGet(),
                                  AbstractNode_AddRef(an));
            }
        }
    }

    RWASSERT(fr);
    FieldRec_Destroy(fr);
    RWRETURN(an);
}

RwBool
enterScript(void)
{
    FieldRec           *fr;

    RWFUNCTION(RWSTRING("enterScript"));

    fr = (FieldRec *) Stack_Top(&currentField);
    if ((fr->nodeType == NULL)
        || (rwstrcmp(fr->nodeType->name, RWSTRING("Script")) != 0))
    {
        yyerror(RWSTRING
                ("interface declaration outside of Script or prototype"));

        RWRETURN(FALSE);
    }

    parsing_script = TRUE;

    RWRETURN(TRUE);
}

void
exitScript(void)
{
    RWFUNCTION(RWSTRING("exitScript"));

    parsing_script = FALSE;

    RWRETURNVOID();
}

RwBool
enterField(const RwChar * fieldName)
{
    RWFUNCTION(RWSTRING("enterField"));
    RWASSERT(fieldName);

    if (fieldName)
    {
        AbstractNode       *an;
        AbstractField      *af;
        FieldRec           *fr;
        RwInt32             type;

        fr = (FieldRec *) Stack_Top(&currentField);
        if (!fr)
        {
            RWRETURN(FALSE);
        }

        fr->fieldName = (RwChar *) fieldName;

        if (fr->nodeType != NULL)
        {
            VrmlNodeType       *nt = (VrmlNodeType *) fr->nodeType;

            /*
             * enterField is called when parsing eventIn and eventOut IS
             * declarations, in which case we don't need to do anything 
             * special -- the IS IDENTIFIER will be returned from the
             & lexer normally.
             */

            type = VrmlNodeType_hasField(nt, fieldName);

            resetCurrentFieldValuesList(type);

            /* create new AbstractField in the current node, ready for lexer to fill */
            an = fr->abstractNode;
            af = AbstractField_Create(fieldName, type);
            af->lineNum = currentLineNumber + 1;
            LLinkList_AddData(&an->fields, af);
            fr->abstractField = af;
            fr->type = af->type;

            if (VrmlNodeType_hasEventIn(nt, fieldName) ||
                VrmlNodeType_hasEventOut(nt, fieldName))
            {
                RWRETURN(TRUE);
            }

            if (type != 0)
            {
                /* let the lexer know what field type to expect: */
                expect(type);
            }
            else
            {
                rwsprintf(errorMsg,
                          RWSTRING
                          ("Error: Node's of type %s do not have fields/eventIn/eventOut named %s\n"),
                          nt->name, fieldName);
                yyerror(errorMsg);
                /* expect(ANY_FIELD); */
            }
        }
        /* else expect(ANY_FIELD); */

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

void
exitField()
{
    FieldRec           *fr;
    AbstractField      *af;
    RwInt32             numItems;
    LLinkList          *fieldValueList;

    RWFUNCTION(RWSTRING("exitField"));

    fr = (FieldRec *) Stack_Top(&currentField);
    RWASSERT(fr);

    /* copy the linklist into an array */
    af = fr->abstractField;
    if (af)
    {
        fieldValueList = getCurrentFieldValuesList();
        numItems = LLinkList_NumItems(fieldValueList);
        af->Items = numItems;
        Field_InitFormFieldList(af->field, fieldValueList);
        destroyCurrentFieldValuesList();
    }

    /* clean up old state */
    fr->fieldName = (char *)NULL;

    RWRETURNVOID();
}

void
expect(RwInt32 type)
{
    RWFUNCTION(RWSTRING("expect"));

    expectToken = type;

    RWRETURNVOID();
}

RwBool
DoConversion(LLinkList * anodeList)
{
    RWFUNCTION(RWSTRING("DoConversion"));
    RWASSERT(anodeList);

    if (anodeList)
    {
        /* SymbolRouteEnt_Set(); */
        SymTab_ProtoInstance();

        if (!LLinkList_IsEmpty(anodeList))
        {
#ifdef PRINTTREE
            {
                /* until I change the function use this wrapper */
                AbstractNode       *an;

                LLinkList_IteratorReset(anodeList);
                while (an =
                       (AbstractNode *)
                       LLinkList_IteratorNext(anodeList))
                {
                    AbstractNode_Print(an);
                }
            }
#endif /* PRINTTREE */
            Convert_AbstractNodeList(anodeList);

            RWRETURN(TRUE);
        }

        RWRETURN(FALSE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
isSceneGraph(RwChar * name)
{
    RWFUNCTION(RWSTRING("isSceneGraph"));
    RWASSERT(name);

    if (name)
    {
        if (rwstrcmp(name, RWSTRING("Anchor")) == 0)
            RWRETURN(TRUE);
        if (rwstrcmp(name, RWSTRING("Billboard")) == 0)
            RWRETURN(TRUE);
        if (rwstrcmp(name, RWSTRING("Collision")) == 0)
            RWRETURN(TRUE);
        if (rwstrcmp(name, RWSTRING("Group")) == 0)
            RWRETURN(TRUE);
        if (rwstrcmp(name, RWSTRING("Shape")) == 0)
            RWRETURN(TRUE);
        if (rwstrcmp(name, RWSTRING("Transform")) == 0)
            RWRETURN(TRUE);
        if (rwstrcmp(name, RWSTRING("Inline")) == 0)
            RWRETURN(TRUE);

        RWRETURN(FALSE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

static RwChar      *
GetStandardNodesString(void)
{
    RwInt32             numElements, i;
    RwInt32             size = 0;
    RwChar             *snstring;

    RWFUNCTION(RWSTRING("GetStandardNodesString"));

    numElements = sizeof(snodes) / sizeof(RwChar *);
    for (i = 0; i < numElements; i++)
    {
        size += rwstrlen(snodes[i].string) + 1;
    }

    snstring = (RwChar *) RwMalloc(sizeof(RwChar) * (size + 1));
    if (!snstring)
    {
        RWRETURN((char *)NULL);
    }

    snstring[0] = '\0';
    for (i = 0; i < numElements; i++)
    {
        rwstrcat(snstring, snodes[i].string);
        rwstrcat(snstring, " ");
    }

    RWRETURN(snstring);
}

RwBool
ParserBegin(void)
{
    RwMemory            mem;
    RwChar             *snstring;

    RWFUNCTION(RWSTRING("ParserBegin"));

    Stack_Init(&currentProtoStack);
    Stack_Init(&currentField);
    Stack_Init(&GAbstractNodeListStack);

    ANodeListPush();
    TypeList_Init();
    LLinkList_Init(&rootAbstractNodeList);

    snstring = GetStandardNodesString();
    if (!snstring)
    {
        RWRETURN(FALSE);
    }

    mem.start = (RwUInt8 *) snstring;
    mem.length = sizeof(RwChar) * (rwstrlen(snstring) + 1);
    yyin = RwStreamOpen(rwSTREAMMEMORY, rwSTREAMREAD, &mem);

    /* turn of debug info for standard nodes */
#if YYDEBUG != 0
    yydebug = 0;
#endif /* YYDEBUG != 0 */
    yy_flex_debug = 0;

    parsing_standard_proto = TRUE;
    yyparse();
    parsing_standard_proto = FALSE;
    yyResetLineNumber();
    RwStreamClose(yyin, &mem);
    RwFree(snstring);

    RWRETURN(TRUE);
}

static              RwBool
SetCurrentPath(const RwChar * filename)
{
    RWFUNCTION(RWSTRING("SetCurrentPath"));
    RWASSERT(filename);

    if (filename)
    {
        RwChar             *slash;

        rwstrcpy(path, filename);
        slash = rwstrrchr(path, '\\');

        if (slash)
        {
            slash++;
            *slash = '\0';
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
ParseFile(const RwChar * filename)
{
    RWFUNCTION(RWSTRING("ParseFile"));
    RWASSERT(filename);

    if (filename)
    {
        RwInt32             errcode;
        LLinkList          *anList;

        /* setup the path from the filename */
        SetCurrentPath(filename);

        /* now parse & convert input vrml file */
        yyin = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename);
        if (!yyin)
        {
            RWERROR((E_RP_VRML_FILEOPEN, filename));

            RWRETURN(FALSE);
        }

        if (!AbstractNodePushBlockContext())
        {
            RWRETURN(FALSE);
        }

        /* turn off debug info */
#if YYDEBUG != 0
        yydebug = 0;
#endif /* YYDEBUG != 0 */
        yy_flex_debug = 0;

        /* get rid of any previous state */
        yywrap();
        yyrestart(yyin);
        yyResetLineNumber();
        Stack_Init(&currentProtoStack);
        Stack_Init(&currentField);
        Build_Reset();

        /*
         *  For this little test application, pushing and popping the node
         *  namespace isn't really necessary.  But each VRML .wrl file is
         *  a separate namespace for PROTOs (except for things predefined
         *  in the spec), and pushing/popping the namespace when reading each
         *  file is a good habit to get into:
         */

        ANodeListPush();
        SymTab_Create();
        TypeList_pushNameSpace();
        errcode = yyparse();

        /* close the stream */
        RwStreamClose(yyin, NULL);

        anList = ANodeListGet();
        if (!anList)
        {
            RWRETURN(FALSE);
        }

        DoConversion(anList);
        TypeList_popNameSpace();
        SymTab_Destroy();
        ANodeListPop();

        AbstractNodePopBlockContext();

        if (errcode != 0)
        {
#if (0)
            LLinkList_Destroy(&rootAbstractNodeList,
                              DESTROYCALLBACK(AbstractNode_Destroy));
#endif /* (0) */
            RWRETURN(FALSE);
        }

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

AbstractNode       *
ParseInlineFile(const RwChar * url)
{
    RWFUNCTION(RWSTRING("ParseInlineFile"));
    RWASSERT(url);

    if (url)
    {
        RwInt32             errcode;
        LLinkList          *anList;
        AbstractNode       *an;

        yyin = UrlGetStream(url);
        if (!yyin)
        {
            RWRETURN((AbstractNode *)NULL);
        }

        /* turn off debug info */
#if YYDEBUG != 0
        yydebug = 0;
#endif /* YYDEBUG != 0 */
        yy_flex_debug = 0;

        /* get rid of any previous state */
        yyrestart((const RwStream *)NULL);
        yyResetLineNumber();

        ANodeListPush();
        TypeList_pushNameSpace();
        errcode = yyparse();

        /* close the stream */
        RwStreamClose(yyin, NULL);

        TypeList_popNameSpace();

        if (errcode != 0)
        {
            RWRETURN(FALSE);
        }

        anList = ANodeListGet();
        if (!anList)
        {
            RWRETURN((AbstractNode *)NULL);
        }

        an = (AbstractNode *) LLinkList_GetItem(anList, 0);

        RWRETURN(an);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

void
ParserEnd(void)
{
    RWFUNCTION(RWSTRING("ParserEnd"));

    TypeList_Destroy();
    ANodeListPop();
    LOGCLOSE();

    /* Tidy up lex itself with a handy little
     * patch - frees up the input buffer - ROB
     */
    yylex_tidy_up();

    RWRETURNVOID();
}

RwBool
enterProtoField(const RwChar * name)
{
    RWFUNCTION(RWSTRING("enterProtoField"));
    RWASSERT(name);

    if (name)
    {
        FieldRec           *fr = (FieldRec *) Stack_Top(&currentField);
        VrmlNodeType       *nt = (VrmlNodeType *) fr->nodeType;
        RwInt32             type;

        type = VrmlNodeType_hasField(nt, name);
        resetCurrentFieldValuesList(type);

        LLinkList_IteratorReset(&nt->fields);
        fr->abstractField = (AbstractField *)
            LLinkList_IteratorNext(&nt->fields);

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

void
exitProtoField(void)
{
    FieldRec           *fr = (FieldRec *) Stack_Top(&currentField);
    AbstractField      *af;
    LLinkList          *fieldValueList;

    RWFUNCTION(RWSTRING("exitProtoField"));

    af = fr->abstractField;
    fieldValueList = getCurrentFieldValuesList();

    af->Items = LLinkList_NumItems(fieldValueList);
    Field_InitFormFieldList(af->field, fieldValueList);

    destroyCurrentFieldValuesList();

    RWRETURNVOID();
}

RwBool
enterExternProtoUrlField(const RwChar * name)
{
    RWFUNCTION(RWSTRING("enterExternProtoUrlField"));
    RWASSERT(name);

    if (name)
    {
        FieldRec           *fr;
        VrmlNodeType       *nt;

        fr = (FieldRec *) Stack_Top(&currentField);
        if (!fr)
        {
            RWRETURN(FALSE);
        }

        nt = (VrmlNodeType *) fr->nodeType;
        if (!nt)
        {
            RWRETURN(FALSE);
        }

        resetCurrentFieldValuesList(MFSTRING);
        if (!nt->afUrl)
        {
            nt->afUrl = AbstractField_Create(name, MFSTRING);
        }
        if (!nt->afUrl)
        {
            RWRETURN(FALSE);
        }

        fr->abstractField = nt->afUrl;

        RWRETURN(TRUE);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(FALSE);
}

RwBool
exitExternProtoUrlField(void)
{
    FieldRec           *fr = (FieldRec *) Stack_Top(&currentField);
    AbstractField      *af;
    LLinkList          *fieldValueList;

    RWFUNCTION(RWSTRING("exitExternProtoUrlField"));

    fr = (FieldRec *) Stack_Top(&currentField);
    if (!fr)
    {
        RWRETURN(FALSE);
    }

    af = fr->abstractField;
    if (!af)
    {
        RWRETURN(FALSE);
    }

    fieldValueList = getCurrentFieldValuesList();
    af->Items = LLinkList_NumItems(fieldValueList);
    Field_InitFormFieldList(af->field, fieldValueList);

    destroyCurrentFieldValuesList();

    RWRETURN(TRUE);
}

RwInt32
my_yyinput(void *buffer, RwInt32 max_size)
{
    RWFUNCTION(RWSTRING("my_yyinput"));
    RWASSERT(buffer);

    if (buffer)
    {
        RwInt32             n;

        n = RwStreamRead(yyin, (void *) buffer, (RwUInt32) max_size);

        RWRETURN(n);
    }

    RWERROR((E_RW_NULLP));
    RWRETURN(0);
}
