/* * parser.y - a YACC grammar for parsing VRML 2.0 files * * Copyright (C) 1999 Stephen F. White * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file "COPYING" for details); if * not, write to the Free Software Foundation, Inc., 675 Mass Ave, * Cambridge, MA 02139, USA. */ %{ #include #include #include "config.h" #ifdef _WIN32 /* Ugly hack: FLOAT and INT are typedef'ed in gl/gl.h for _WIN32. We preserve the parser's value and hope the best for GL. */ #define SAVED_FLOAT FLOAT #define SAVED_INT INT #undef FLOAT #undef INT /* yet another ugly hack: redefine alloca to malloc */ #define alloca malloc #endif #include "stdafx.h" #ifdef _WIN32 #define FLOAT SAVED_FLOAT #undef SAVED_FLOAT #define INT SAVED_INT #undef SAVED_INT #endif #include "parser.h" #include "Scene.h" #include "Element.h" #include "EventIn.h" #include "EventOut.h" #include "ExposedField.h" #include "Field.h" #include "FieldValue.h" #include "SFMFTypes.h" #include "Node.h" #include "NodeScript.h" #include "NodeComment.h" #include "Proto.h" #include "Stack.h" extern void stopProto(void); #define SYMB(id) (scene->getSymbol(id)) Scene *scene; #ifdef HAVE_LIBZ gzFile inputFile; #else FILE *inputFile; #endif int isInProtoLibrary = 0; int lineno = 1; Node *targetNode; static Stack nodeStack; static Stack protoStack; static int defName = -1; static int currentType; static NodeList commentNodeList; static List commentList; static void route(const MyString &srcNode, const MyString &srcField, const MyString &dstNode, const MyString &dstField); static Node *newNode(const MyString &nodeType); static int checkField(Node *node, const MyString &fieldName); static void setField(Node *node, int index, FieldValue *value); static void isField(Node *node, const MyString &fieldName, const MyString &isName); static FieldValue *intsToType(IntArray *floats, int type); static FieldValue *floatsToType(DoubleArray *floats, int type); static FieldValue *stringToType(const char* string, int type); static DoubleArray *intsToFloats(IntArray *ints); static FieldValue *emptyMF(int type); static FieldValue *emptyMFNodeOrNULL(int type); static FieldValue *SillyDefaultValue(int type); static MyString uniqName(const MyString name); static MyString checkName(const MyString name); static void repairURL(Node* node); static void addCommentsToNode(Node* node); static void addCommentsToNodeList(NodeList *nodelist); class nameTranslation { public: MyString oldName; MyString newName; nameTranslation(MyString newN,MyString oldN) {newName=newN;oldName=oldN;} }; Array NameTranslation; void vrml1error(void); %} %union { int int32; int id; Node *node; NodeList *nodeList; Element *element; FieldValue *value; float sffloat; StringArray *stringArray; IntArray *intArray; DoubleArray *doubleArray; }; %token BRACKET_ON BRACKET_OFF WING_BRACKET_ON WING_BRACKET_OFF %token VRML1 %token ID STRING %token INT %token FLOAT %token SCRIPT %token DEF EXTERNPROTO FALSE_TOK IS NULL_TOK PROTO ROUTE TO TRUE_TOK USE %token EVENT_IN EVENT_OUT EXPOSED_FIELD FIELD %type node nodeStatement statement %type nodeStatements statements %type externInterfaceDeclaration restrictedInterfaceDeclaration %type scriptBodyElement interfaceDeclaration %type nodeName nodeType %type fieldName fieldType %type fieldValue sfboolValue mfstringValue URLList %type strings %type ints %type floats %% vrmlScene: statements { nodeComment(); addCommentsToNodeList($1); scene->addNodes(targetNode, $1); } | VRML1 statements { vrml1error(); } | error empty /* on error, skip until file is read */ ; statements: statements statement { if ($2 != NULL) $2->appendTo($1); $$ = $1; } | empty { $$ = new NodeList(); } ; statement: nodeStatement { $$ = $1; } | protoStatement { $$ = NULL; } | routeStatement { $$ = NULL; } ; nodeStatement: node { scene->updateURLs($1); $$ = $1; } | DEF nodeName { defName = $2; } node { $$ = $4; scene->updateURLs($4);} | USE nodeName { $$ = scene->use(checkName(SYMB($2))); } ; protoStatement: proto | externproto ; protoStatements: protoStatements protoStatement | empty ; proto: PROTO nodeType { protoStack.push(new Proto(scene, SYMB($2))); if (!scene->addProtoName(SYMB($2))) { char buf[1024]; mysnprintf(buf,1024, "proto already definied: %s", (const char*)SYMB($2)); yyerror(buf); } } BRACKET_ON interfaceDeclarations BRACKET_OFF WING_BRACKET_ON protoBody WING_BRACKET_OFF { scene->addProto(SYMB($2),protoStack.pop()); stopProto(); } ; protoBody: protoStatements nodeStatement statements { protoStack.peek()->define($2, $3); } | empty ; interfaceDeclarations: interfaceDeclarations interfaceDeclaration { if (protoStack.empty()) yyerror("syntax error"); else protoStack.peek()->addElement($2); } | empty ; restrictedInterfaceDeclaration: EVENT_IN fieldType ID { $$ = new EventIn($2, SYMB($3)); } | EVENT_OUT fieldType ID { $$ = new EventOut($2, SYMB($3)); } | FIELD fieldType { currentType = $2; } ID fieldValue { $$ = new Field($2, SYMB($4), $5); } ; interfaceDeclaration: restrictedInterfaceDeclaration | EXPOSED_FIELD fieldType { currentType = $2; } ID fieldValue { $$ = new ExposedField($2, SYMB($4), $5); } ; externproto: EXTERNPROTO nodeType { protoStack.push(new Proto(scene, SYMB($2))); if (!scene->addProtoName(SYMB($2))) { char buf[1024]; mysnprintf(buf,1024, "proto already definied: %s", (const char*)SYMB($2)); yyerror(buf); } } BRACKET_ON externInterfaceDeclarations BRACKET_OFF URLList { scene->addProto(SYMB($2), protoStack.pop()); stopProto(); } ; externInterfaceDeclarations: externInterfaceDeclarations externInterfaceDeclaration { if (protoStack.empty()) yyerror("syntax error"); else protoStack.peek()->addElement($2); } | empty ; externInterfaceDeclaration: EVENT_IN fieldType ID { $$ = new EventIn($2, SYMB($3)); } | EVENT_OUT fieldType ID { $$ = new EventOut($2, SYMB($3)); } | FIELD fieldType ID { $$ = new Field($2, SYMB($3), SillyDefaultValue($2)); } | EXPOSED_FIELD fieldType ID { $$ = new ExposedField($2, SYMB($3), SillyDefaultValue($2)); } ; routeStatement: ROUTE nodeName '.' ID TO nodeName '.' ID { route(checkName(SYMB($2)), SYMB($4), checkName(SYMB($6)), SYMB($8)); } ; URLList: mfstringValue ; empty: ; node: nodeType WING_BRACKET_ON { $$ = newNode(SYMB($1)); addCommentsToNode($$); nodeStack.push($$); if (defName != -1) { scene->def(uniqName(SYMB(defName)), $$); defName = -1; } } nodeBody WING_BRACKET_OFF { $$ = nodeStack.pop(); } | SCRIPT WING_BRACKET_ON { $$ = new NodeScript(scene); addCommentsToNode($$); nodeStack.push($$); if (defName != -1) { scene->def(uniqName(SYMB(defName)), $$); defName = -1; } } scriptBody WING_BRACKET_OFF { $$ = nodeStack.pop(); ((NodeScript *) $$)->update(); } ; nodeBody: nodeBody nodeBodyElement | empty ; scriptBody: scriptBody scriptBodyElement { if (nodeStack.empty()) yyerror("syntax error"); else if ($2) { nodeStack.peek()->getProto()->addElement($2); nodeStack.peek()->update(); if ($2->getElementType() == EL_FIELD_DEF) { Field *field = (Field *)$2; // search for number of field to use setField Proto *proto = nodeStack.peek()->getProto(); for (int i=0; i < proto->getNumFields(); i++) if (proto->getField(i)->getName() == field->getName()) setField(nodeStack.peek(), i, field->getDefault()); } } } | empty ; scriptBodyElement: nodeBodyElement { $$ = NULL; } | restrictedInterfaceDeclaration | EVENT_IN fieldType ID IS ID { if (protoStack.empty()) yyerror("IS statement used outside PROTO"); $$ = new EventIn($2, SYMB($3)); } | EVENT_OUT fieldType ID IS ID { if (protoStack.empty()) yyerror("IS statement used outside PROTO"); $$ = new EventOut($2, SYMB($3)); } ; nodeBodyElement: fieldName fieldValue { setField(nodeStack.peek(), $1, $2); } | ID IS ID { isField(nodeStack.peek(), SYMB($1), SYMB($3)); } | routeStatement | protoStatement ; fieldName: ID { $$ = checkField(nodeStack.peek(), SYMB($1)); } ; nodeName: ID ; nodeType: ID ; fieldType: ID { $$ = typeStringToEnum(SYMB($1)); } ; fieldValue: sfboolValue | STRING { $$ = stringToType(SYMB($1), currentType); } | BRACKET_ON strings BRACKET_OFF { $$ = new MFString($2); } | nodeStatement { $$ = new SFNode($1); } | NULL_TOK { $$ = emptyMFNodeOrNULL(currentType); } | BRACKET_ON nodeStatements BRACKET_OFF { $$ = new MFNode($2); } | ints { $$ = intsToType($1, currentType); } | floats { $$ = floatsToType($1, currentType); } | BRACKET_ON ints BRACKET_OFF { $$ = intsToType($2, currentType); } | BRACKET_ON floats BRACKET_OFF { $$ = floatsToType($2, currentType); } | BRACKET_ON BRACKET_OFF { $$ = emptyMF(currentType); } | IS ID { $$ = NULL; } ; sfboolValue: TRUE_TOK { $$ = new SFBool(true); } | FALSE_TOK { $$ = new SFBool(false); } ; ints: ints INT { $1->append($2); $$ = $1; } | INT { $$ = new IntArray(); $$->append($1); } ; floats: floats FLOAT { $1->append($2); $$ = $1; } | floats INT { $1->append((float) $2); $$ = $1; } | ints FLOAT { $$ = intsToFloats($1); $$->append($2); delete $1; } | FLOAT { $$ = new DoubleArray(); $$->append($1); } ; mfstringValue: STRING { $$ = new MFString(SYMB($1)); } | BRACKET_ON strings BRACKET_OFF { $$ = new MFString($2); } ; strings: STRING { $$ = new StringArray(); $$->append(SYMB($1)); } | strings STRING { $1->append(SYMB($2)); $$ = $1; } ; nodeStatements: nodeStatement { $$ = new NodeList(); if ($1) $1->appendTo($$); } | nodeStatements nodeStatement { if ($2) $2->appendTo($1); $$ = $1; } ; %% int yywrap(void) { return 1; } void yyerror(const char *s) { scene->setErrorLineNumber(lineno); #ifdef HAVE_LIBZ scene->errorf("%s in line %d\n", s, lineno); #else if (strcmp(s,"parse error")==0) scene->errorf("%s (or unsupported compression (no gzip in this version)) in line %d\n", s, lineno); else scene->errorf("%s in line %d\n", s, lineno); #endif } void vrml1error(void) { scene->setErrorLineNumber(1); scene->errorf("VRML Version 1 not supported\n"); } static Node * newNode(const MyString &nodeType) { Proto *proto; if (scene->getProtoPrefix() != NULL) { proto = scene->getProto(scene->getNodeWithPrefix(nodeType)); if (proto) return proto->create(scene); } proto = scene->getProto(nodeType); if (!proto) { scene->errorf("invalid node type %s in %d\n", (const char *) nodeType, lineno); return NULL; } else { return proto->create(scene); } } static FieldValue * intsToType(IntArray *ints, int type) { FieldValue *r = NULL; const int *data = ints->getData(); int len = ints->size(); switch(type) { case SFCOLOR: if (len != 3) { yyerror("SFColor must have 3 values"); } else { r = new SFColor((float) data[0], (float) data[1], (float) data[2]); } break; case SFFLOAT: if (len != 1) { yyerror("SFFloat must have 1 float value"); } else { r = new SFFloat((float) data[0]); } break; case SFIMAGE: if (len < 3) { yyerror("SFImage must have at least 3 values"); } else { int width = data[0]; int height = data[1]; int depth = data[2]; if (len - 3 != width * height) { char buf[1024]; sprintf(buf, "SFImage data must have %d values\n", width * height); yyerror(buf); } else { r = new SFImage(width, height, depth, data + 3); } } break; case SFINT32: if (len != 1) { yyerror("SFInt32 must have 1 integer value"); } else { r = new SFInt32(data[0]); } break; case SFROTATION: if (len != 4) { yyerror("SFRotation must have 4 values"); } else { r = new SFRotation((float) data[0], (float) data[1], (float) data[2], (float) data[3]); } break; case SFTIME: if (len != 1) { yyerror("SFTime must have 1 value"); } else { r = new SFTime((double) data[0]); } break; case SFVEC2F: if (len != 2) { yyerror("SFVec2f must have 2 values"); } else { r = new SFVec2f((float) data[0], (float) data[1]); } break; case SFVEC3F: if (len != 3) { yyerror("SFVec3f must have 3 values"); } else { r = new SFVec3f((float) data[0], (float) data[1], (float) data[2]); } break; case MFINT32: r = new MFInt32(ints->extractData(), len); break; case MFCOLOR: case MFFLOAT: case MFROTATION: case MFTIME: case MFVEC2F: case MFVEC3F: r = floatsToType(intsToFloats(ints), type); break; default: yyerror("type mismatch"); break; } delete ints; return r; } static FieldValue * floatsToType(DoubleArray *floats, int type) { FieldValue *r = NULL; const double *data = floats->getData(); int len = floats->size(); switch(type) { case SFCOLOR: if (len != 3) { yyerror("SFColor must have 3 values"); } else { r = new SFColor(data[0], data[1], data[2]); } break; case SFFLOAT: if (len != 1) { yyerror("SFFloat must have 1 float value"); } else { r = new SFFloat(data[0]); } break; case SFROTATION: if (len != 4) { yyerror("SFRotation must have 4 values"); } else { r = new SFRotation(data[0], data[1], data[2], data[3]); } break; case SFTIME: if (len != 1) { yyerror("SFTime must have 1 value"); } else { r = new SFTime((double) data[0]); } break; case SFVEC2F: if (len != 2) { yyerror("SFVec2f must have 2 values"); } else { r = new SFVec2f(data[0], data[1]); } break; case SFVEC3F: if (len != 3) { yyerror("SFVec3f must have 3 values"); } else { r = new SFVec3f(data[0], data[1], data[2]); } break; case MFCOLOR: if (len % 3 != 0) { yyerror("MFColor must be a multiple of 3 values"); } else { r = new MFColor(floats->extractData(), len); } break; case MFFLOAT: r = new MFFloat(floats->extractData(), len); break; case MFROTATION: if (len % 4 != 0) { yyerror("MFRotation must be a multiple of 4 values"); } else { r = new MFRotation(floats->extractData(), len); } break; case MFTIME: r = new MFTime(data, len); break; case MFVEC2F: if (len % 2 != 0) { yyerror("MFVec2f must be a multiple of 2 values"); } else { r = new MFVec2f(floats->extractData(), len); } break; case MFVEC3F: if (len % 3 != 0) { yyerror("MFVec3f must be a multiple of 2 values"); } else { r = new MFVec3f(floats->extractData(), len); } break; default: yyerror("type mismatch"); break; } delete floats; return r; } static FieldValue * stringToType(const char *string, int type) { FieldValue *r = NULL; switch(type) { case SFSTRING: r=new SFString(string); break; case MFSTRING: r=new MFString(string); break; default: yyerror("type mismatch"); break; } return r; } static FieldValue * emptyMFNodeOrNULL(int type) { if (type==SFNODE) return new SFNode(NULL); else if (type==MFNODE) return emptyMF(type); else { /* NULL only allowed for Node types see VRML97 Grammar http://www.web3d.org/x3d/spec/vrml/ISO_IEC_14772-All/part1/grammar.html#Fields sfnodeValue ::= nodeStatement | NULL ; */ yyerror("NULL only allowed for Node types, assuming \"[]\""); return emptyMF(type); } } static FieldValue * emptyMF(int type) { switch(type) { case MFCOLOR: return new MFColor(); case MFFLOAT: return new MFFloat(); case MFINT32: return new MFInt32(); case MFNODE: return new MFNode(); case MFROTATION: return new MFRotation(); case MFSTRING: return new MFString(); case MFTIME: return new MFTime(); case MFVEC2F: return new MFVec2f(); case MFVEC3F: return new MFVec3f(); default: yyerror("type mismatch"); return NULL; } } /* * Currently, EXTERNPROTO Definitions are not read * field need a senseful default value */ static FieldValue * SillyDefaultValue(int type) { if (scene->hasExternProtoWarning()) { fprintf(stderr,"Warning: Currently, EXTERNPROTO definitions are not read\n"); fprintf(stderr,"Warning: Default field values of this EXTERNPROTO are incorrect\n"); } if (FieldValue *value=typeDefaultValue(type)) return value; else { yyerror("intern error: type no supported"); return NULL; } } // // checkField() - verify a field reference // // check that the node "node" has the field "fieldName" // if not, print an error // if so, stash its type in the lexer, and return its index static int checkField(Node *node, const MyString &fieldName) { if (!node) return INVALID_INDEX; Proto *proto = node->getProto(); if (!proto) return INVALID_INDEX; int index = proto->lookupField(fieldName); if (index == INVALID_INDEX) { scene->invalidField(proto->getName(), fieldName); } else { currentType = proto->getField(index)->getType(); } return index; } static void setField(Node *node, int index, FieldValue *value) { FieldValue *newValue; if (!node || !value || index < 0) return; Proto *proto = node->getProto(); if (!proto) return; Field *field = proto->getField(index); if (value->getType() == SFSTRING && field->getType() == MFSTRING) { newValue = new MFString(((SFString *) value)->getValue()); delete value; value = newValue; } else if (value->getType() == SFNODE && field->getType() == MFNODE) { NodeList *list = new NodeList(); list->append(((SFNode *) value)->getValue()); newValue = new MFNode(list); delete value; value = newValue; } if (value->getType() != field->getType()) { scene->errorf("type mismatch: field \"%s\"\n", (const char *) field->getName()); delete value; } else { node->setField(index, value); } } static void isField(Node *node, const MyString &fieldName, const MyString &isName) { int srcField, srcEventIn, srcEventOut, srcExposedField; int dstField, dstEventIn, dstEventOut, dstExposedField; if (!node) return; Proto *source = node->getProto(); if (!source) return; if (protoStack.empty()) { scene->errorf("IS statement used outside PROTO"); return; } Proto *proto = protoStack.peek(); if ((srcExposedField = source->lookupExposedField(fieldName)) != -1) { dstEventIn = proto->lookupEventIn(isName); dstEventOut = proto->lookupEventOut(isName); if ((dstField = proto->lookupField(isName)) != -1) { // FIXME: do something useful } else if ((dstEventIn = proto->lookupEventIn(isName)) != -1) { // FIXME: do something useful } else if ((dstEventOut = proto->lookupEventOut(isName)) != -1) { // FIXME: do something useful } else if ((dstExposedField = proto->lookupExposedField(isName)) != -1) { // FIXME: do something useful } else { scene->invalidField(source->getName(), isName); } } else if ((srcField = source->lookupField(fieldName)) != -1) { dstField = proto->lookupField(isName); if (dstField != -1) { // FIXME: do something useful } else { scene->invalidField(source->getName(), isName); } } else if ((srcEventIn = source->lookupEventIn(fieldName)) != -1) { dstEventIn = proto->lookupEventIn(isName); if (dstEventIn != -1) { // FIXME: do something useful } else { scene->invalidField(source->getName(), isName); } } else if ((srcEventOut = source->lookupEventOut(fieldName)) != -1) { dstEventOut = proto->lookupEventOut(isName); if (dstEventOut != -1) { // FIXME: do something useful } else { scene->invalidField(source->getName(), isName); } } } static void route(const MyString &srcNode, const MyString &srcField, const MyString &dstNode, const MyString &dstField) { int eventIn; int eventOut; bool valid = true; Node *src = scene->use(srcNode); if (!src) { scene->invalidNode(srcNode); valid = false; } else { eventOut = src->lookupEventOut(srcField); if (eventOut == INVALID_INDEX) { scene->errorf("node \"%s\" has no eventOut \"%s\"", (const char *) srcNode, (const char *) srcField); valid = false; } } Node *dst = scene->use(dstNode); if (!dst) { scene->invalidNode(dstNode); valid = false; } else { eventIn = dst->lookupEventIn(dstField); if (eventIn == INVALID_INDEX) { scene->errorf("node \"%s\" has no eventIn \"%s\"", (const char *) dstNode, (const char *) dstField); valid = false; } } if (valid) { src->update(); dst->update(); if (!scene->addRoute(src, eventOut, dst, eventIn)) scene->errorf("invalid ROUTE command in line %d\n",lineno); } } static DoubleArray * intsToFloats(IntArray *ints) { if (ints == NULL ) return NULL; int len = ints->size(); DoubleArray *r = new DoubleArray(len); const int *d = ints->getData(); for (int i = 0; i < len; i++) { r->set(i, (float) d[i]); } return r; } /* avoid double DEFs while file import */ static MyString uniqName(const MyString name) { int i=0; if (scene->hasAlreadyName(name)) { while (true) { char* buf=(char*) malloc(strlen((const char*) name)+512); sprintf(buf, "%s_%d", (const char*) name, i++); MyString* newName=new MyString(buf); free(buf); if (!scene->hasAlreadyName(*newName)) { NameTranslation.append(new nameTranslation(*newName,name)); return *newName; } } } return name; } /* replace double DEFs while file import */ static MyString checkName(const MyString name) { for (int i=NameTranslation.size()-1;i>=0;i--) if (name==NameTranslation[i]->oldName) { return NameTranslation[i]->newName; } return name; } static void addCommentsToNode(Node* node) { if (commentNodeList.size() > 0) { for (int i = 0;i < commentNodeList.size(); i++) node->appendComment(commentNodeList[i]); commentNodeList.resize(0); } } static void addCommentsToNodeList(NodeList *nodelist) { if (commentNodeList.size() > 0) { for (int i = 0;i < commentNodeList.size(); i++) nodelist->append(commentNodeList[i]); commentNodeList.resize(0); } } void addToCurrentComment(char *string) { if (strlen(string) > 0) commentList.append(string); } void nodeComment(void) { if (commentList.size()!=0) { StringArray *mfString = new StringArray(); for (List::Iterator* commentpointer = commentList.first(); commentpointer != NULL; commentpointer = commentpointer->next() ) { const char *string = commentpointer->item(); bool isVRMLheader = false; if (strcmp(string, "#VRML V2.0 utf8") == 0) isVRMLheader = true; if (!isVRMLheader) { if (string[0] == '\n') string++; mfString->append(strdup(++string)); } } if (mfString->size() > 0) { NodeComment *node = (NodeComment *) scene->createNode("#"); node->comment(new MFString(mfString)); commentNodeList.append(node); } commentList.removeAll(); } } void insideNodeComment(void) { } void MFComment(void) { } void insideMFComment(void) { }