/*******************************************************************************
 *	Gram.y		BEAM IDL Lexical analyser
 *			T.Barnaby,	BEAM Ltd,	2/5/02
 *******************************************************************************
 */
%{
#include <stdio.h>
#include <stdlib.h>
#include <bidl.h>
#include <Node.h>
#include <BString.h>

#define DOECHO	0
#define	DEBUG	0

#define	YYSTYPE	Node*

int yylex();
void yyerror(char *s);

int		fflag;
BString		tfunc;
int		tflag;
BStringList	typeNames;

%}

%token MODULE INTERFACE BIDIRECTIONAL
%token IDENTIFIER
%token CONSTANT
%token STRING_LITERAL
%token TYPEDEF
%token ERROR BOOL INT8 UINT8 INT16 UINT16 INT32 UINT32 INT64 UINT64 CHAR STRING FLOAT32 FLOAT64
%token ID DATE TIME DATETIME TIMESTAMP COMPLEX COMPLEX32 COMPLEX64
%token TYPE_NAME
%token LIST ARRAY DICT IN INREF OUT INOUT
%token STRUCT ENUM
%token CLASS
%token TYPEDOMAIN APIVERSION APICMD
%token COMMENT
%token ONEWAY

%start start
%%

identifier
	: IDENTIFIER
		{
			dprintf("identifier: %s\n", $1->name().retStr());
			$$ = new Node(Node::TIDENT, $1->name());
		}
	;
type
	: ERROR
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| BOOL
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| INT8
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| UINT8
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| INT16
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| UINT16
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| INT32
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| UINT32
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| INT64
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| UINT64
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| FLOAT32
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| FLOAT64
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| CHAR
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| STRING
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| ID
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| DATE
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| TIME
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| DATETIME
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| TIMESTAMP
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| COMPLEX
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| COMPLEX32
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| COMPLEX64
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	| STRUCT  identifier
		{	$$ = new Node(Node::TTYPE, $2->name());	}
	| CLASS  identifier
		{	$$ = new Node(Node::TTYPE, $2->name());	}
	| LIST '<' type '>'
		{	$$ = new NodeOp1(Node::TTYPELIST, $1->name(), $3);	}
	| ARRAY '<' type '>'
		{	$$ = new NodeOp1(Node::TTYPEARRAY, $1->name(), $3);	}
	| DICT '<' type '>'
		{	$$ = new NodeOp1(Node::TTYPEDICT, $1->name(), $3);	}
	| TYPE_NAME
		{	$$ = new Node(Node::TTYPE, $1->name());	}
	;

structItem
	: type identifier ';'
		{	$$ = new NodeOp2(Node::TSTRUCTITEM, "structItem", $1, $2); }
	| type identifier ';' COMMENT
		{	$$ = new NodeOp2(Node::TSTRUCTITEM, "structItem", $1, $2); $$->append($4); }
	| type identifier '[' CONSTANT ']' ';'
		{	$$ = new NodeOp3(Node::TSTRUCTITEM_ARRAYFIXED, "structItem_arrayfixed", $1, $2, $4); }
	| type identifier '[' CONSTANT ']' ';' COMMENT
		{	$$ = new NodeOp3(Node::TSTRUCTITEM_ARRAYFIXED, "structItem_arrayfixed", $1, $2, $4); $$->append($7); }
	;

structItemList
	: structItem
		{	$$ = new NodeOp1(Node::TLIST, "structItemList", $1);	}
	| structItemList structItem
		{	$$ = $1->append($2);	}
	|
		{	$$ = new Node(Node::TLIST, "structItemList");		}
	
	;

enumerator
	: identifier
	| identifier '=' CONSTANT
		{	$$ = new NodeOp2(Node::TLIST, "enumeratorListItem", $1, $3);	}
	;

enumeratorList
	: enumerator
		{	$$ = new NodeOp1(Node::TLIST, "enumeratorList", $1);	}
	| enumeratorList ',' enumerator
		{	$$ = $1->append($3);	}
	;


parameter
	: IN type identifier
		{	$$ = new NodeOp3(Node::TFUNCPARAM, "param", new Node(Node::TDIR, "in"), $2, $3); }
	| INREF type identifier
		{	$$ = new NodeOp3(Node::TFUNCPARAM, "param", new Node(Node::TDIR, "inref"), $2, $3); }
	| OUT type identifier
		{	$$ = new NodeOp3(Node::TFUNCPARAM, "param", new Node(Node::TDIR, "out"), $2, $3); }
	| INOUT type identifier
		{	$$ = new NodeOp3(Node::TFUNCPARAM, "param", new Node(Node::TDIR, "inout"), $2, $3); }
	;

parameterList
	: parameter
		{	$$ = new NodeOp1(Node::TLIST, "parameterList", $1);	}
	| parameterList ',' parameter
		{	$$ = $1->append($3);	}
	;

derived
	: ':' TYPE_NAME
		{	$$ = new NodeOp1(Node::TDERIVED, "derived", $2);	}
	;

declareType
	: STRUCT identifier '{' structItemList '}'
		{
//			printf("AddType: %s\n", $2->name().retStr());
			gtypelist.append(Type($2->name(), $4));

			$$ = new NodeOp2(Node::TSTRUCT, $2->name(), $4, 0);
		}
	| STRUCT identifier derived '{' structItemList '}'
		{
//			printf("AddType: %s\n", $2->name().retStr());
			gtypelist.append(Type($2->name(), $5,gtypelist.search($3->name())));

			$$ = new NodeOp2(Node::TSTRUCT, $2->name(), $5, $3);
		}
	| CLASS identifier '{' structItemList '}'
		{
//			printf("AddType: %s\n", $2->name().retStr());
			gtypelist.append(Type($2->name(), $4));

			$$ = new NodeOp2(Node::TSTRUCT, $2->name(), $4, 0);
		}
	| CLASS identifier derived '{' structItemList '}'
		{
//			printf("AddType: %s\n", $2->name().retStr());
			gtypelist.append(Type($2->name(), $5, gtypelist.search($3->name())));

			$$ = new NodeOp2(Node::TSTRUCT, $2->name(), $5, $3);
		}
	| type identifier '(' parameterList ')'
		{	$$ = new NodeOp2(Node::TFUNC, $2->name(), $1, $4);	}
	| ONEWAY identifier '(' parameterList ')'
		{	$$ = new NodeOp2(Node::TFUNC, $2->name(), $1, $4);	}
	| type identifier '(' ')'
		{	$$ = new NodeOp2(Node::TFUNC, $2->name(), $1, new Node(Node::TLIST, "parameterList"));	}
	| ONEWAY identifier '(' ')'
		{	$$ = new NodeOp2(Node::TFUNC, $2->name(), $1, new Node(Node::TLIST, "parameterList"));	}
	| ENUM '{' enumeratorList '}'
		{	$$ = new NodeOp2(Node::TENUM, "enum", 0, $3);	}
	| ENUM identifier '{' enumeratorList '}'
		{
			$$ = new NodeOp2(Node::TENUM, "enum", $2, $4);
			gtypelist.append(Type($2->name(), $4));
		}
	;

/*******************************************************************************
 * Declarations
 *******************************************************************************
 */
declaration
	: declareType ';'
	| declareType ';' COMMENT
		{
			$$ = $1->append($3);
		}
	| COMMENT
	| INTERFACE identifier '{' declarationList '}' ';'
		{
			$$ = new NodeOp1(Node::TINTERFACE, $2->name(), $4);
		}
	| BIDIRECTIONAL identifier '{' declarationList '}' ';'
		{
			$$ = new NodeOp1(Node::TBIDIRECTIONAL, $2->name(), $4);
		}
	| TYPEDOMAIN '=' CONSTANT ';'
		{
			$$ = new Node(Node::TTYPEDOMAIN, $3->name());
		}
	| APIVERSION '=' CONSTANT ';'
		{
			$$ = new Node(Node::TAPIVERSION, $3->name());
		}
	| APICMD '=' CONSTANT ';'
		{
			$$ = new Node(Node::TAPICMD, $3->name());
		}
	;

declarationList
	: declaration
		{	$$ = new NodeOp1(Node::TLIST, "declarationList", $1);	}
	| declarationList declaration
		{	$$ = $1->append($2);	}
	;


module
	: MODULE identifier '{' declarationList '}' ';'
		{
			$$ = new NodeOp1(Node::TMODULE, $2->name(), $4);
		}
	| COMMENT
	;
	
file
	: module
		{	$$ = new NodeOp1(Node::TLIST, "fileList", $1);	}
	| file module
		{	$$ = $1->append($2);	}
	;
start
	: file
		{	result = $1;		}
	;
%%

extern char yytext[];
extern int column, line;

void yyerror(char *s){
	fflush(stdout);
#if	DOECHO
	printf("\nLine: %d\n\r%*s\n%*s\n", line, column, "^", column, s);
#else
	printf("Syntax Error: File(%s) Line(%d) Col(%d)\n", fileName.retStr(), line, column);
#endif
}
