/**
	\file "parser/instref-parse.yy.in"
	Quick little parser for parsing instance-references.  
	This will be used in prsim and other simulators, for example.  
	The grammar in here is an extremely small snippet
	of the hackt language grammar.  
	$Id: instref-parse.yy.in,v 1.10 2008/12/18 00:32:09 fang Exp $
 */

%{
#define	ENABLE_STACKTRACE		0

#include <iostream>

#include "config.h"
#include "AST/AST.h"
#include "parser/instref-prefix.h"
#include "util/using_ostream.h"
#include "parser/instref-union.h"
#include "parser/instref-parse-options.h"
#include "parser/instref-parse.output.h"
#include "lexer/instref-lex-options.h"
#include "util/stacktrace.h"

/** work-around for bison-1.875 and gcc-3.x, until bison is fixed **/
#if defined (__GNUC__) && (3 <= __GNUC__)
#define __attribute__(arglist)			/* empty */
#endif

#ifndef	YYBISON
#define	YYBISON		0
#endif

#include "lexer/flex_lexer_state.h"

#if YYBISON
#if defined(USING_BISON) && !USING_BISON
#error	Inconsistency in configuration: YYBISON && !USING_BISON
#endif

#elif defined(YYBYACC) && YYBYACC

#if defined(USING_BYACC) && !USING_BYACC && !USING_YACC
// on many systems, yacc == byacc
#error	Inconsistency in configuration: YYBYACC && !USING_BYACC && !USING_YACC
#endif
#else   // !YYBISON && !YYBYACC

#if defined(USING_YACC) && !USING_YACC
#error	Inconsistency in configuration: !YYBISON && !YYBYACC && !USING_YACC
#endif

#endif  // YYBISON

// extern FILE* yyin;

#ifdef	LIBBOGUS
static flex::lexer_state	_lexer_state;
#endif


#define	WRAP_LIST(left, list, right)	list->wrap(left, right)

#define	DELETE_TOKEN(tok)		delete tok

#define APPEND_LIST(list, delim, item)					\
	DELETE_TOKEN(delim); list->push_back(item)

using namespace HAC::lexer;
using namespace HAC::parser;

%}

%union {
	token_int*		_token_int;
/*	token_bool*		_token_bool; 		*/
	token_identifier*	_token_identifier;
	node_position*		_node_position;
	qualified_id*		_qualified_id;
	expr*			_expr;
	expr_list*		_expr_list;
	range*			_range;
	range_list*		_range_list;
	dense_range_list*	_dense_range_list;
	id_expr*		_id_expr;
	HAC::parser::index_expr*	_index_expr;
	member_expr*		_member_expr;
	inst_ref_expr*		_inst_ref_expr;
}

%{
// extern	int	yylex(YYSTYPE*);
extern	YY_DECL;

static void yyerror(const char* msg);

extern	ostream& yy_union_resolve_dump(const YYSTYPE&, const short, const short, ostream&);
extern	void yy_union_resolve_delete(const YYSTYPE&, const short, const short);
extern	ostream& yy_union_lookup_dump(const YYSTYPE&, const int, ostream&);
extern	void yy_union_lookup_delete(const YYSTYPE&, const int);

// worry about yy_union business later

static
void
yyfreestacks(const short* yyss, const short* yyssp,
		const YYSTYPE* yyvs, const YYSTYPE* yyvsp,
		const YYSTYPE yylval, const int yychar,
		const flex::lexer_state& _lexer_state);
%}

%type	<_node_position>	'[' ']' '.'
%token	<_node_position>	SCOPE
%token	<_token_identifier>	ID
%token	<_token_int>		INT

%type	<_inst_ref_expr>	instref member_index_expr
%type	<_index_expr>		index_expr
%type	<_member_expr>		member_expr
%type	<_range_list>		index_list
/* %type	<_expr_list>		index_list	*/
%type	<_range>		index
/* %type	<_expr>			index		*/
%type	<_id_expr>		id_expr
%type	<_qualified_id>		absolute_id relative_id

%start	instref

/* YACC_PURE_PARSER: conditionally substituted by configure */
@YACC_PURE_PARSER@

%%

/* grammar taken from pieces of "parser/hackt-parse.yy.in" */

instref
	: member_index_expr
		// for pure-parsing, we pass in the return YYSTYPE&
		// as yylval.  This MUST be set when returning from
		// yyparse (except when exception thrown).
		{ $$ = $1;
		if (!yychar) { yylval._inst_ref_expr = $1; }
		}
	;

member_index_expr
	: id_expr	{ $$ = $1; }
	| index_expr	{ $$ = $1; }
	| member_expr	{ $$ = $1; }
	;

index_expr
	: member_expr index_list
		{ $$ = new index_expr($1, $2); }
	| id_expr index_list
		{ $$ = new index_expr($1, $2); }
	;

member_expr
	: index_expr '.' ID
		{ $$ = new member_expr($1, $3); DELETE_TOKEN($2); }
	| member_expr '.' ID
		{ $$ = new member_expr($1, $3); DELETE_TOKEN($2); }
	| id_expr '.' ID
		{ $$ = new member_expr($1, $3); DELETE_TOKEN($2); }
	;

index_list
	: index_list index
		{ $$ = $1; $1->push_back($2); }
	| index
		{ $$ = new range_list($1); }
	/*	{ $$ = new expr_list($1); }	*/
	;

/* only integer constant expressions allowed... for now */
/* here we only allow single-valued indices, and no ranges */
/* multi-reference support can be added later when needed */
/* IDEA: for i in <inst_ref_collection> ; do ... ; done */
index
	: '[' INT ']'
		{ DELETE_TOKEN($1); $$ = new range($2); DELETE_TOKEN($3); }
	;

/**
// later: expression evaluation using constant symbols available in the module!
// can just use the context of the invoking module!  MUAHAHAAA!
expr
	: ...
	;

// paste expression grammar here:
// should probably 

**/

id_expr
	: absolute_id
		{ $$ = new id_expr($1); }
	| relative_id
		{ $$ = new id_expr($1); }
	;

absolute_id
	: SCOPE relative_id
		{ $$ = $2->force_absolute($1); }
	;

relative_id
	: relative_id SCOPE ID
		{ $$ = $1; APPEND_LIST($1, $2, $3); }
	| ID	{ $$ = new qualified_id($1); }
	;


%%

#include "parser/yyfreestacks.cc"		// we've moved!
#include "parser/yyerror.cc"			// we've moved!

static
void yyerror(const char* msg) {
	STACKTRACE_VERBOSE;
	__yyerror(msg);		// standard implementation
	THROW_EXIT;
}

