/* yacc.y parser for VRML97. C++ code!!! */
/* TODO: script body statement grammar needs to be checked with final 
 * version of the VRML'97 standard. */
%{
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define _VRML2_UTF8_PARSER_SOURCE_

#include "parser.H"

namespace vrml2_utf8 {

#define yyparse parser::yyparse
#define yylex parser::yylex
#define yyerror parser::yyerror
#define yylval parser::yylval
#define yychar parser::yychar
#define yydebug parser::yydebug
#define yynerrs parser::yynerrs



#define YYSTYPE DUPLICATE_YYSTYPE
#define YY_OMIT_DECLS
%}
%union { /* stack type */
	char	*s;
	double	f;
	long	i;
	enum fieldType t;
        struct fieldValueBase val;
	SFNode *node;
        MFNode *nodes;
	MFString *strings;
}
%token		tVRML tNODECATALOG
%token 		tDEF tNAME tUSE tPROTO teventIn teventOut tfield texposedField 
%token		tROUTE tTO tIS tEXTERNPROTO tTRUE tFALSE tNULL tScript
%token	<t>	tSIMPLEFIELD tSFNODE tMFNODE
%token	<s>	tID tSTRING
%token	<f>	tFLOAT
%token	<i>	tINT tHEX
%type	<t>	fieldType
%type	<s>	eventInId eventOutId fieldId nodeTypeId nodeNameId Id
%type	<val>	sfnodeValue mfnodeValue
%type	<node>	node nodeStatement rootNodeStatement
%type	<nodes> nodeStatements
%type	<strings> URLList mfstringValue strings
%%
/* ***************************** general ****************************** */
file:	  vrmlHeader vrmlScene
        ;
vrmlHeader: tVRML
	{
	  Start();
	}
        ;
vrmlScene: statements 
	;
statements: statement 
	| statement statements 
        | error statements { yyerrok; }
	| /* empty */ 
	;
statement: nodeStatement {}
        | protoStatement {}
        | routeStatement {}
	;
rootNodeStatement: node
        {
	  if ($1) AppendNode($1); $$ = $1;
	}
	| tDEF nodeNameId node
        {
	  if ($3) AppendNamedNode($2, $3); $$ = $3;
	}
	| tNAME nodeNameId node
        {
	  if ($3) DefineNamedNode($2, $3); $$ = $3;
	}
        ;
nodeStatement: rootNodeStatement
	| tUSE nodeNameId
	{
	  $$ = UseNamedNode($2);
	}
        | tNULL
        {
	  $$ = 0;
        }
	;
protoStatement: proto 
	| externproto 
	;
protoStatements: protoStatement 
	| protoStatement protoStatements 
        | error protoStatements {yyerrok;}
	| /* empty */ 
	;
proto:    protoInterface '{' protoBody '}' 
	{
	  EndProto();
	}
	;
protoInterface: tPROTO nodeTypeId '[' interfaceDeclarations ']'
        {
	  BeginProto($2);
	}
        ;
protoBody: protoStatements rootNodeStatement statements
	;
interfaceDeclarations: interfaceDeclaration 
        | interfaceDeclaration interfaceDeclarations 
        | error interfaceDeclarations {yyerrok;}
	| /* empty */ 
	;
restrictedInterfaceDeclaration: 
          teventIn fieldType eventInId 
	{
	  interface.append(new eventIn($3, $2));
	}
	| teventOut fieldType eventOutId
	{
	  interface.append(new eventOut($3, $2));
	}
	| tfield fieldTypeIdValueTriple
	{
	  interface.append(new field(curId, curValue));
	}
interfaceDeclaration: restrictedInterfaceDeclaration
	| texposedField fieldTypeIdValueTriple
	{
	  interface.append(new exposedField(curId, curValue));
	}
	;
fieldTypeIdValueTriple:
	  tSFNODE fieldId sfnodeValue	/* single node fieldValue */
	{
	  curId = $2;
	  curValue = fieldValue($3);
	}
	| tMFNODE fieldId mfnodeValue	/* node array fieldValue */
	{ 
	  curId = $2;
	  curValue = fieldValue($3);
	}
	| tSIMPLEFIELD fieldId '['	/* other array fieldValue */
	{
	  UnputToken();
	  curId = $2;
	  curValue = ParseValue($1);
	}
	| tSIMPLEFIELD fieldId fieldValueAtom	/* other simple fieldValue */
	{
	  UnputToken();
	  curId = $2;
	  curValue = ParseValue($1);
	}
        ;
externproto: tEXTERNPROTO nodeTypeId '[' externInterfaceDeclarations ']' URLList 
	{
	  NewExternProto($2, $6);
	}
	;
externInterfaceDeclarations: externInterfaceDeclaration
	| externInterfaceDeclaration externInterfaceDeclarations 
        | error externInterfaceDeclarations {yyerrok;}
	| /* empty */
	;
externInterfaceDeclaration: teventIn fieldType eventInId 
	{
	  interface.append(new eventIn($3, $2));
	}
	| teventOut fieldType eventOutId 
	{
	  interface.append(new eventOut($3, $2));
	}
	| tfield fieldType fieldId 
	{
	  interface.append(new field($3, fieldValue($2)));
	}
	| texposedField fieldType fieldId 
	{
	  interface.append(new exposedField($3, fieldValue($2)));
	}
	;
routeStatement: tROUTE nodeNameId '.' eventOutId tTO nodeNameId '.' eventInId 
	{
	  AddRoute($2, $4, $6, $8);
	}
	;
URLList:  mfstringValue
	;

/* ************************* nodes ***************************** */
node:	  scriptNodeTypeId '{' scriptBody '}'	
	{ 
	  $$ = EndScript(); 
	}
        | otherNodeTypeId '{' nodeBody '}' 	
	{
	  $$ = EndNodeInstance();
	}
	;
scriptNodeTypeId: tScript 	
	{ 
	  BeginScript(); 
	}
	;
otherNodeTypeId: nodeTypeId 	
	{ 
	  BeginNodeInstance($1); 
	}
	;
scriptBody: scriptBodyElement
        | scriptBodyElement scriptBody
	| /* empty */
	;
scriptBodyElement: scriptInterfaceDeclaration
        | nodeBodyElement
        ;
scriptInterfaceDeclaration: 
          teventIn scriptEventInDeclaration
        | teventOut scriptEventOutDeclaration
        | tfield scriptFieldDeclaration
        ;
scriptEventInDeclaration: fieldType eventInId 
        { 
	  AppendMember(new eventIn($2, $1)); 
	}
        | fieldType eventInId tIS eventInId 
        { 
	  AppendMember(new eventIn($2, $1)); MakeLink($2, $4); 
	}
        ;
scriptEventOutDeclaration: fieldType eventOutId
        { 
	  AppendMember(new eventOut($2, $1)); 
	}
        | fieldType eventOutId tIS eventOutId 
        { 
	  AppendMember(new eventOut($2, $1)); MakeLink($2, $4); 
	}
        ;
scriptFieldDeclaration: fieldTypeIdValueTriple
        { 
	  AppendMember(new field(curId, curValue)); 
	}
        | tSFNODE fieldId tIS fieldId 
        { 
	  AppendMember(new field($2, fieldValue($1))); MakeLink($2, $4); 
	}
        | tMFNODE fieldId tIS fieldId 
        { 
	  AppendMember(new field($2, fieldValue($1))); MakeLink($2, $4); 
	}
        | tSIMPLEFIELD fieldId tIS fieldId 
        { 
	  AppendMember(new field($2, fieldValue($1))); MakeLink($2, $4); 
	}
        ;
nodeBody: nodeBodyElement
	| nodeBodyElement nodeBody 
        | error nodeBody { yyerrok; }
	| /* empty */
	;
nodeBodyElement: fieldIdValuePair
	| Id tIS Id
        {
	  MakeLink($1, $3); 
	}
	| routeStatement 
	| protoStatement 
	;
fieldIdValuePair: fieldId fieldValueAtom /* single non-node fieldValue */
        { 
	  UnputToken(); NonNodeFieldIdValuePair($1); 
	}
	| fieldId '[' fieldValueAtom	/* non-empty non-node array fieldValue */
	{ 
	  UnputToken(); UnputString("["); NonNodeFieldIdValuePair($1); 
	}
	| fieldId '[' ']'		/* empty array fieldValue */
	{ 
	  UnputString("[]"); NonNodeFieldIdValuePair($1); 
	}
        | fieldId sfnodeValue		/* single node fieldValue */
	{ 
	  NodeFieldIdValuePair($1, $2); 
	}
        | fieldId mfnodeValue		/* non-empty node array fieldValue */
	{ 
	  NodeFieldIdValuePair($1, $2); 
	}
	;
nodeNameId: Id
	;
nodeTypeId: Id 
        | tScript
        {
	   $$ = "Script"; 
	}
	;
fieldId: Id 
	;
eventInId: Id 
	;
eventOutId: Id
	;
Id:	tID
	;

/* ********************** Field Types and Values *********************** */
fieldType: tSIMPLEFIELD
	| tSFNODE
	| tMFNODE
	;
fieldValueAtom: tTRUE {} | tFALSE {} | tSTRING {} | tFLOAT {} | tINT {} | tHEX {}
        ;
mfnodeValue: nodeStatement
        {
	  fieldValue val = new MFNode; if ($1) ((MFNode*)val)->append($1); $$ = val; 
	}
	| '[' ']' 
        { 
	  fieldValue val = new MFNode; $$ = val; 
	}
	| '[' nodeStatements ']' 
        { 
	  fieldValue val = $2; $$ = val; 
	}
	;
nodeStatements: nodeStatement
	{ 
	  $$ = new MFNode; if ($1) $$->append($1); 
	}
	| nodeStatements nodeStatement
	{
	  $$ = $1; if ($2) $$->append($2); 
	}
	;
sfnodeValue: nodeStatement
        {
	   fieldValue val = new SFNode *($1); $$ = val; 
	}
	| tNULL
        { 
	  fieldValue val = new SFNode *(0); $$ = val; 
	}
	;
mfstringValue: tSTRING
        { 
	  $$ = new MFString; $$->append((SFString)$1); 
	}
	| '[' ']' 
        { 
	  $$ = new MFString; 
	}
	| '[' strings ']' 
	{ 
	  $$ = $2; 
	}
	;
strings:  tSTRING
        { 
	  $$ = new MFString; $$->append((SFString)$1); 
	}
	| strings tSTRING
        { 
	  $$ = $1; $$->append((SFString)$2); 
	}
	;
%%

}  // namespace vrml2_utf8