open_toontown_panda3d/pandatool/src/vrml/vrmlParser.yxx

568 lines
14 KiB
Plaintext

/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file vrmlParser.yxx
* @author drose
* @date 2004-10-01
*/
// *******************************************************
// VRML 2.0 Parser
// Copyright (C) 1996 Silicon Graphics, Inc.
//
// Author(s) : Gavin Bell
// Daniel Woods (first port, minor fixes)
// *******************************************************
//
%{
//
// Parser for VRML 2.0 files.
// This is a minimal parser that does NOT generate an in-memory scene graph.
//
// The original parser was developed on a Windows 95 PC with
// Borland's C++ 5.0 development tools. This was then ported
// to a Windows 95 PC with Microsoft's MSDEV C++ 4.0 development
// tools. The port introduced the ifdef's for
// USING_BORLAND_CPP_5 : since this provides a "std namespace",
// TWO_ARGUMENTS_FOR_STL_STACK : STL is a moving target. The stack template
// class takes either one or two arguments.
#include "pandatoolbase.h"
#include "vrmlLexerDefs.h"
#include "vrmlNodeType.h"
#include "vrmlNode.h"
#include "pnotify.h"
#include "plist.h"
#include <stack>
#include <stdio.h> // for sprintf()
//#define YYDEBUG 1
// Currently-being-define proto. Prototypes may be nested, so a stack
// is needed:
stack< VrmlNodeType*, plist<VrmlNodeType*> > currentProtoStack;
// This is used to keep track of which field in which type of node is being
// parsed. Field are nested (nodes are contained inside MFNode/SFNode fields)
// so a stack of these is needed:
typedef struct {
const VrmlNodeType *nodeType;
const char *fieldName;
const VrmlNodeType::NameTypeRec *typeRec;
} FieldRec;
stack< FieldRec*, plist<FieldRec*> > currentField;
// Similarly for the node entries (which contain the actual values for
// the fields as they are encountered):
stack< VrmlNode*, plist<VrmlNode*> > currentNode;
// This is used when the parser knows what kind of token it expects
// to get next-- used when parsing field values (whose types are declared
// and read by the parser) and at certain other places:
extern int expectToken;
// This is where we store the parsed scene.
VrmlScene *parsed_scene = NULL;
// Some helper routines defined below:
static void beginProto(const char *);
static void endProto();
int addField(const char *type, const char *name, const VrmlFieldValue *dflt = NULL);
int addEventIn(const char *type, const char *name, const VrmlFieldValue *dflt = NULL);
int addEventOut(const char *type, const char *name, const VrmlFieldValue *dflt = NULL);
int addExposedField(const char *type, const char *name, const VrmlFieldValue *dflt = NULL);
int add(void (VrmlNodeType::*func)(const char *, int, const VrmlFieldValue *),
const char *typeString, const char *name,
const VrmlFieldValue *dflt);
int fieldType(const char *type);
void enterNode(const char *);
VrmlNode *exitNode();
void inScript();
void enterField(const char *);
void storeField(const VrmlFieldValue &value);
void exitField();
void expect(int type);
extern void vrmlyyerror(const string &);
////////////////////////////////////////////////////////////////////
// Defining the interface to the parser.
////////////////////////////////////////////////////////////////////
void
vrml_init_parser(istream &in, const string &filename) {
//yydebug = 0;
vrml_init_lexer(in, filename);
}
void
vrml_cleanup_parser() {
}
%}
%union {
char *string;
VrmlFieldValue fv;
VrmlNode *node;
MFArray *mfarray;
SFNodeRef nodeRef;
VrmlScene *scene;
};
%type <fv> fieldValue
%type <node> node
%type <nodeRef> nodeDeclaration
%type <mfarray> mfnodeValue nodes
%type <scene> declarations
/*
* And types that will be needed by a true VRML implementation:
* %type <nodeList> vrmlscene declarations
*/
%token <string> IDENTIFIER
%token DEF USE PROTO EXTERNPROTO TO IS ROUTE SFN_NULL
%token EVENTIN EVENTOUT FIELD EXPOSEDFIELD
%token <fv> SFBOOL SFCOLOR SFFLOAT SFIMAGE SFINT32 SFNODE SFROTATION
%token <fv> SFSTRING SFTIME SFVEC2F SFVEC3F
%token <fv> MFCOLOR MFFLOAT MFINT32 MFROTATION MFSTRING MFVEC2F MFVEC3F
%token <fv> MFNODE
%%
vrmlscene: declarations
{
parsed_scene = $1;
}
;
declarations:
/* Empty is OK */
{
$$ = new VrmlScene;
}
| declarations nodeDeclaration
{
Declaration d;
d._node = $2;
$1->push_back(d);
$$ = $1;
}
| declarations protoDeclaration
| declarations routeDeclaration
;
nodeDeclaration:
node
{
$$._p = $1;
$$._type = SFNodeRef::T_unnamed;
$$._name = NULL;
}
| DEF IDENTIFIER node
{
$$._p = $3;
$$._type = SFNodeRef::T_def;
$$._name = $2;
}
| USE IDENTIFIER
{
$$._p = NULL;
$$._type = SFNodeRef::T_use;
$$._name = $2;
}
;
protoDeclaration:
proto
| externproto
;
proto:
PROTO IDENTIFIER { beginProto($2); }
'[' interfaceDeclarations ']'
'{' declarations '}' { endProto(); free($2);}
;
externproto:
EXTERNPROTO IDENTIFIER { beginProto($2); }
'[' externInterfaceDeclarations ']'
{ expect(MFSTRING); }
fieldValue { endProto(); free($2); }
;
interfaceDeclarations:
/* Empty is OK */
| interfaceDeclarations interfaceDeclaration
;
interfaceDeclaration:
EVENTIN IDENTIFIER IDENTIFIER { addEventIn($2, $3);
free($2); free($3); }
| EVENTOUT IDENTIFIER IDENTIFIER { addEventOut($2, $3);
free($2); free($3); }
| FIELD IDENTIFIER IDENTIFIER
{
int type = fieldType($2);
expect(type);
}
fieldValue
{
addField($2, $3, &($5));
free($2);
free($3);
}
| EXPOSEDFIELD IDENTIFIER IDENTIFIER
{
int type = fieldType($2);
expect(type);
}
fieldValue
{
addExposedField($2, $3, &($5));
free($2);
free($3);
}
;
externInterfaceDeclarations:
/* Empty is OK */
| externInterfaceDeclarations externInterfaceDeclaration
;
externInterfaceDeclaration:
EVENTIN IDENTIFIER IDENTIFIER { addEventIn($2, $3);
free($2); free($3); }
| EVENTOUT IDENTIFIER IDENTIFIER { addEventOut($2, $3);
free($2); free($3); }
| FIELD IDENTIFIER IDENTIFIER { addField($2, $3);
free($2); free($3); }
| EXPOSEDFIELD IDENTIFIER IDENTIFIER { addExposedField($2, $3);
free($2); free($3); }
;
routeDeclaration:
ROUTE IDENTIFIER '.' IDENTIFIER TO IDENTIFIER '.' IDENTIFIER
{ free($2); free($4); free($6); free($8); }
;
node:
IDENTIFIER { enterNode($1); }
'{' nodeGuts '}' { $$ = exitNode(); free($1);}
;
nodeGuts:
/* Empty is OK */
| nodeGuts nodeGut
;
nodeGut:
IDENTIFIER { enterField($1); }
fieldValue
{
storeField($3);
exitField();
free($1);
}
| routeDeclaration
| protoDeclaration
/* The following are only valid for Script nodes: */
| EVENTIN IDENTIFIER IDENTIFIER { inScript(); free($2); free($3); }
| EVENTOUT IDENTIFIER IDENTIFIER { inScript(); free($2); free($3); }
| FIELD IDENTIFIER IDENTIFIER { inScript();
int type = fieldType($2);
expect(type); }
fieldValue { free($2); free($3); }
| EVENTIN IDENTIFIER IDENTIFIER IS IDENTIFIER
{ inScript(); free($2); free($3); free($5); }
| EVENTOUT IDENTIFIER IDENTIFIER IS IDENTIFIER
{ inScript(); free($2); free($3); free($5); }
;
fieldValue:
SFBOOL
| SFCOLOR
| MFCOLOR
| SFFLOAT
| MFFLOAT
| SFIMAGE
| SFINT32
| MFINT32
| SFROTATION
| MFROTATION
| SFSTRING
| MFSTRING
| SFTIME
| SFVEC2F
| MFVEC2F
| SFVEC3F
| MFVEC3F
| SFNODE nodeDeclaration { $$._sfnode = $2; }
| SFNODE SFN_NULL
{
$$._sfnode._p = NULL;
$$._sfnode._type = SFNodeRef::T_null;
$$._sfnode._name = NULL;
}
| MFNODE mfnodeValue { $$._mf = $2; }
| IS IDENTIFIER { free($2); }
;
mfnodeValue:
'[' nodes ']'
{
$$ = $2;
}
| nodeDeclaration
{
$$ = new MFArray;
VrmlFieldValue v;
v._sfnode = $1;
$$->push_back(v);
}
;
nodes:
/* Empty is OK */
{
$$ = new MFArray;
}
| nodes nodeDeclaration
{
VrmlFieldValue v;
v._sfnode = $2;
$1->push_back(v);
$$ = $1;
}
;
%%
static void
beginProto(const char *protoName)
{
// Any protos in the implementation are in a local namespace:
VrmlNodeType::pushNameSpace();
VrmlNodeType *t = new VrmlNodeType(protoName);
currentProtoStack.push(t);
}
static void
endProto()
{
// Make any protos defined in implementation unavailable:
VrmlNodeType::popNameSpace();
// Add this proto definition:
if (currentProtoStack.empty()) {
cerr << "Error: Empty PROTO stack!\n";
}
else {
VrmlNodeType *t = currentProtoStack.top();
currentProtoStack.pop();
VrmlNodeType::addToNameSpace(t);
}
}
int
addField(const char *type, const char *name,
const VrmlFieldValue *dflt)
{
return add(&VrmlNodeType::addField, type, name, dflt);
}
int
addEventIn(const char *type, const char *name,
const VrmlFieldValue *dflt)
{
return add(&VrmlNodeType::addEventIn, type, name, dflt);
}
int
addEventOut(const char *type, const char *name,
const VrmlFieldValue *dflt)
{
return add(&VrmlNodeType::addEventOut, type, name, dflt);
}
int
addExposedField(const char *type, const char *name,
const VrmlFieldValue *dflt)
{
return add(&VrmlNodeType::addExposedField, type, name, dflt);
}
int
add(void (VrmlNodeType::*func)(const char *, int, const VrmlFieldValue *),
const char *typeString, const char *name,
const VrmlFieldValue *dflt)
{
int type = fieldType(typeString);
if (type == 0) {
cerr << "Error: invalid field type: " << type << "\n";
}
// Need to add support for Script nodes:
// if (inScript) ... ???
if (currentProtoStack.empty()) {
cerr << "Error: declaration outside of prototype\n";
return 0;
}
VrmlNodeType *t = currentProtoStack.top();
(t->*func)(name, type, dflt);
return type;
}
int
fieldType(const char *type)
{
if (strcmp(type, "SFBool") == 0) return SFBOOL;
if (strcmp(type, "SFColor") == 0) return SFCOLOR;
if (strcmp(type, "SFFloat") == 0) return SFFLOAT;
if (strcmp(type, "SFImage") == 0) return SFIMAGE;
if (strcmp(type, "SFInt32") == 0) return SFINT32;
if (strcmp(type, "SFNode") == 0) return SFNODE;
if (strcmp(type, "SFRotation") == 0) return SFROTATION;
if (strcmp(type, "SFString") == 0) return SFSTRING;
if (strcmp(type, "SFTime") == 0) return SFTIME;
if (strcmp(type, "SFVec2f") == 0) return SFVEC2F;
if (strcmp(type, "SFVec3f") == 0) return SFVEC3F;
if (strcmp(type, "MFColor") == 0) return MFCOLOR;
if (strcmp(type, "MFFloat") == 0) return MFFLOAT;
if (strcmp(type, "MFInt32") == 0) return MFINT32;
if (strcmp(type, "MFNode") == 0) return MFNODE;
if (strcmp(type, "MFRotation") == 0) return MFROTATION;
if (strcmp(type, "MFString") == 0) return MFSTRING;
if (strcmp(type, "MFVec2f") == 0) return MFVEC2F;
if (strcmp(type, "MFVec3f") == 0) return MFVEC3F;
cerr << "Illegal field type: " << type << "\n";
return 0;
}
void
enterNode(const char *nodeType)
{
const VrmlNodeType *t = VrmlNodeType::find(nodeType);
if (t == NULL) {
char tmp[1000];
sprintf(tmp, "Unknown node type '%s'", nodeType);
vrmlyyerror(tmp);
}
FieldRec *fr = new FieldRec;
fr->nodeType = t;
fr->fieldName = NULL;
fr->typeRec = NULL;
currentField.push(fr);
VrmlNode *node = new VrmlNode(t);
currentNode.push(node);
}
VrmlNode *
exitNode()
{
FieldRec *fr = currentField.top();
nassertr(fr != NULL, NULL);
currentField.pop();
VrmlNode *node = currentNode.top();
nassertr(node != NULL, NULL);
currentNode.pop();
// cerr << "Just defined node:\n" << *node << "\n\n";
delete fr;
return node;
}
void
inScript()
{
FieldRec *fr = currentField.top();
if (fr->nodeType == NULL ||
strcmp(fr->nodeType->getName(), "Script") != 0) {
vrmlyyerror("interface declaration outside of Script or prototype");
}
}
void
enterField(const char *fieldName)
{
FieldRec *fr = currentField.top();
nassertv(fr != NULL);
fr->fieldName = fieldName;
fr->typeRec = NULL;
if (fr->nodeType != NULL) {
// 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.
if (fr->nodeType->hasEventIn(fieldName) ||
fr->nodeType->hasEventOut(fieldName))
return;
const VrmlNodeType::NameTypeRec *typeRec =
fr->nodeType->hasField(fieldName);
if (typeRec != NULL) {
fr->typeRec = typeRec;
// Let the lexer know what field type to expect:
expect(typeRec->type);
}
else {
cerr << "Error: Nodes of type " << fr->nodeType->getName() <<
" do not have fields/eventIn/eventOut named " <<
fieldName << "\n";
// expect(ANY_FIELD);
}
}
// else expect(ANY_FIELD);
}
void
storeField(const VrmlFieldValue &value) {
FieldRec *fr = currentField.top();
nassertv(fr != NULL);
VrmlNode *node = currentNode.top();
nassertv(node != NULL);
if (fr->typeRec != NULL) {
node->_fields.push_back(VrmlNode::Field(fr->typeRec, value));
}
}
void
exitField()
{
FieldRec *fr = currentField.top();
nassertv(fr != NULL);
fr->fieldName = NULL;
fr->typeRec = NULL;
}
void
expect(int type)
{
expectToken = type;
}