/* $Id$
 *
 * Main VHDL Parser.
 *
 * Copyright (C) 2007-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

%skeleton "lalr1.cc"                          /*  -*- C++ -*- */
%require "2.1a" /* need this for c++ skeleton */
%define "parser_class_name" "FAUhdlParser"
%defines
%error-verbose

%parse-param  {yy::ParserDriver &driver}
/* %lex-param {location_type* yylloc} */

%code requires {
/* this section will be part of the header */
#include <string>
#include <list>
#include "frontend/ast/Expression.hpp"
#include "frontend/ast/DiscreteRange.hpp"
#include "frontend/ast/SeqStat.hpp"
#include "frontend/ast/WaveFormElem.hpp"
#include "frontend/ast/NodeFactory.hpp"
#include "frontend/ast/ValDeclaration.hpp"
#include "frontend/ast/SubprogBody.hpp"
#include "frontend/ast/SimpleName.hpp"
#include "frontend/ast/Types.hpp"

/* forward declaration of class, we'd end up with circular includes
   if this was included here already.
 */
namespace yy {
	class ParserDriver;
};
}

%token t_EOF	0;
%token t_Ampersand;
%token t_Apostrophe;
%token t_LeftParen;
%token t_RightParen;
%token t_DoubleStar;
%token t_Star; 
%token t_Plus;
%token t_Comma;
%token t_Minus;
%token t_VarAsgn;
%token t_Colon;
%token t_Semicolon;
%token t_LESym;
%token t_GESym;
%token t_LTSym;
%token t_GTSym;
%token t_EQSym;
%token t_NESym;
%token t_Arrow;
%token t_Box;
%token t_Bar;
%token t_Dot;
%token t_Slash;
%token t_ABS;
%token t_ACCESS;
%token t_AFTER;
%token t_ALIAS;
%token t_ALL;
%token t_AND;
%token t_ARCHITECTURE;
%token t_ARRAY;
%token t_ASSERT;
%token t_ATTRIBUTE;
%token t_BEGIN;
%token t_BLOCK;
%token t_BODY;
%token t_BUFFER;
%token t_BUS;
%token t_CASE;
%token t_COMPONENT;
%token t_CONFIGURATION;
%token t_CONSTANT;
%token t_DISCONNECT;
%token t_DOWNTO;
%token t_ELSE;
%token t_ELSIF;
%token t_END;
%token t_ENTITY;
%token t_EXIT;
%token t_FILE;
%token t_FOR;
%token t_FUNCTION;
%token t_GENERATE;
%token t_GENERIC;
%token t_GROUP;
%token t_GUARDED;
%token t_IF;
%token t_IMPURE;
%token t_IN;
%token t_INERTIAL;
%token t_INOUT;
%token t_IS;
%token t_LABEL;
%token t_LIBRARY;
%token t_LINKAGE;
%token t_LITERAL;
%token t_LOOP;
%token t_MAP;
%token t_MOD;
%token t_NAND;
%token t_NEW;
%token t_NEXT;
%token t_NOR;
%token t_NOT;
%token t_NULL;
%token t_OF;
%token t_ON;
%token t_OPEN;
%token t_OR;
%token t_OTHERS;
%token t_OUT;
%token t_PACKAGE;
%token t_PORT;
%token t_POSTPONED;
%token t_PROCEDURE;
%token t_PROCESS;
%token t_PURE;
%token t_RANGE;
%token t_RECORD;
%token t_REGISTER;
%token t_REJECT;
%token t_REM;
%token t_REPORT;
%token t_RETURN;
%token t_ROL;
%token t_ROR;
%token t_SELECT;
%token t_SEVERITY;
%token t_SIGNAL;
%token t_SLA;
%token t_SLL;
%token t_SRA;
%token t_SRL;
%token t_SHARED;
%token t_SUBTYPE;
%token t_THEN;
%token t_TO;
%token t_TRANSPORT;
%token t_TYPE;
%token t_UNAFFECTED;
%token t_UNITS;
%token t_UNTIL;
%token t_USE;
%token t_VARIABLE;
%token t_WAIT;
%token t_WHEN;
%token t_WHILE;
%token t_WITH;
%token t_XNOR;
%token t_XOR;
%token t_INTEGER;
%token t_REAL;
%token t_CHAR;
%token t_STRING;

%token t_Identifier; /* any unresolved identifier */

%token ID_function;
%token ID_type;
%token ID_procedure;

%union
{
	/* terminal token types */
	std::string *s;
	universal_integer i;
	universal_real r;
	bool b;

	/* enumerated types from terminal tokens */
	enum ast::DiscreteRange::Direction direction;
	enum ast::ValDeclaration::ObjClass obj_class;
	enum ast::SubprogBody::ProgKind prog_kind;
	enum ast::ValDeclaration::Mode mode;
	enum ast::NodeFactory::entityClassE entity_class;

	/* AST nodes */
	ast::Aggregate *aggregate;
	ast::Architecture *architecture;
	ast::AssociationElement *association_element;
	ast::Callable *callable;
	ast::CaseAlternative *case_alternative;
	ast::ConcurrentStat *concurrent_stat;
	ast::ConstInteger *const_integer;
	ast::DiscreteRange *discrete_range;
	ast::ElementAssociation *element_association;
	ast::Entity *entity;
	ast::Expression *expression;
	ast::FunctionCall *function_call;
	ast::FunctionDeclaration *function_declaration;
	ast::LibUnit *library_unit;
	ast::LoopStat *loop_stat;
	ast::Name *name;
	ast::Package *package;
	ast::PackageBody *package_body;
	ast::PhysicalTypeUnit *physical_type_unit;
	ast::Process *process_statement;
	ast::ReturnStat *return_statement;
	ast::SeqStat *seq_stat;
	ast::SimpleName *simple_name;
	ast::SubtypeIndication *subtype_indication;
	ast::SymbolDeclaration *symbol_declaration;
	ast::WaveFormElem *waveform_element;

	/* lists of AST nodes */
	std::list<ast::AssociationElement*> *l_association_element;
	std::list<ast::CaseAlternative*>* l_case_alternative;
	std::list<ast::ConcurrentStat*> *l_concurrent_stat;
	std::list<ast::ConstantDeclaration*> *l_constantdecl;
	std::list<ast::DiscreteRange*> *l_discrete_range;
	std::list<ast::ElementAssociation*> *l_element_association;
	std::list<ast::Expression*> *l_expression;
	std::list<ast::FunctionDeclaration *> *l_function_declaration;
	std::list<ast::Name*> *l_name;
	std::list<ast::PhysicalTypeUnit*> *l_physical_type_unit;
	std::list<ast::RecordTypeElement*> *l_record_element;
	std::list<ast::SeqStat*>* l_seqstat;
	std::list<ast::SignalDeclaration*> *l_signaldecl;
	std::list<ast::SimpleName*> *l_simple_name;
	std::list<ast::SymbolDeclaration*> *l_symboldecl;
	std::list<ast::TypeDeclaration*> *l_typedecl;
	std::list<ast::ValDeclaration*> *l_valdecl;
	std::list<ast::WaveFormElem*> *l_waveform_element;

	/* helper structures found in NodeFactory and other lists */
	ast::NodeFactory::Constraint *h_constraint;
	ast::NodeFactory::ContextItemS *h_context_item;
	ast::NodeFactory::Identifier *identifier;
	ast::NodeFactory::IfHelperS *h_if_stat;
	ast::NodeFactory::TypeDeclHelper *h_type_decl_helper;

	std::list<std::string*> *l_string;
};

%{
/* this section will be part of the c++-code file */
/* now include the definition of ParserDriver */
#include "ParserDriver.hpp"
#include "frontend/ast/FunctionCall.hpp"
#include "frontend/ast/ConstInteger.hpp"
#include "frontend/ast/ConstReal.hpp"
#include "frontend/ast/NodeFactory.hpp"
#include "frontend/ast/Others.hpp"
#include "frontend/ast/Aggregate.hpp"
#include "frontend/ast/VarAssignStat.hpp"
#include "frontend/ast/SigAssignStat.hpp"
#include "frontend/ast/ProcCallStat.hpp"
#include "frontend/ast/CaseStat.hpp"
#include "frontend/ast/ForLoopStat.hpp"
#include "frontend/ast/WhileLoopStat.hpp"
#include "frontend/ast/NextStat.hpp"
#include "frontend/ast/ExitStat.hpp"
#include "frontend/ast/NullStat.hpp"
#include "frontend/ast/ReturnStat.hpp"
#include "frontend/ast/AssertStat.hpp"
#include "frontend/ast/WaitStat.hpp"
#include "frontend/ast/Process.hpp"
#include "frontend/ast/ProcedureDeclaration.hpp"
#include "frontend/ast/FunctionDeclaration.hpp"
#include "frontend/ast/EnumerationType.hpp"
#include "frontend/ast/PhysicalType.hpp"
#include "frontend/ast/UnconstrainedArrayType.hpp"
#include "frontend/ast/RangeConstraintType.hpp"
#include "frontend/ast/CompInstStat.hpp"
#include "frontend/ast/Entity.hpp"
#include "frontend/ast/PackageBody.hpp"
#include "frontend/ast/Architecture.hpp"
#include "frontend/ast/Package.hpp"
#include "frontend/ast/SubtypeIndication.hpp"
#include "frontend/ast/TemporaryName.hpp"
#include "frontend/ast/Slice.hpp"
#include "frontend/ast/Subscript.hpp"
#include "frontend/ast/SelectedName.hpp"
#include "frontend/ast/AttributeName.hpp"
#include "frontend/ast/ElementAssociation.hpp"
#include "frontend/ast/TypeConversion.hpp"
#include "frontend/reporting/ErrorRegistry.hpp"
#include "frontend/reporting/UndefinedSymbol.hpp"
#include "frontend/visitor/ResolveSymbols.hpp"
#include "frontend/ast/AttributeDeclaration.hpp"

/* nasty remap yylex to the driver's yylex */
#define yylex driver.yylex

%}

/* ----------- terminals -------------- */
%type <b>
	opt_guarded
	opt_postponed
	opt_shared
	opt_t_bus
	pure_or_impure
%type <i>
	t_INTEGER
%type <r> 
	t_REAL
%type <s>
	designator
	concurrent_statement_with_label_begin
	entity_statement_with_label_begin
	label
	operator_symbol
	opt_designator
	t_CHAR
	t_STRING
%type <direction>
	direction
%type <entity_class>
	entity_class
%type <mode>
	mode
	opt_mode
%type <obj_class>
	element_kind
	opt_element_kind
%type <prog_kind>
	opt_subprogram_kind
	subprogram_kind

/* -------- non-terminals ------------ */
%type <aggregate>
	aggregate
%type <architecture>
	architecture_body
	architecture_body_begin
%type <association_element>
	association_element
	function_association_element
%type <callable>
	subprogram_body
	subprogram_body_begin
	subprogram_specification
%type <case_alternative>
	case_statement_alternative
%type <concurrent_stat>
	component_instantiation_statement
	concurrent_signal_assignment_statement
	concurrent_statement
	concurrent_statement_label_mandatory
	concurrent_statement_label_optional
	conditional_signal_assignment
	entity_statement
	process_statement
	selected_signal_assignment
%type <const_integer>
	physical_literal_integer
%type <discrete_range>
	discrete_range
	range
	range_constraint
%type <element_association>
	element_association
	named_element_association
%type <entity>
	entity_decl_begin
	entity_declaration
%type <expression>
	actual_designator
	actual_part
	add_term
	and_relation_list
	attribute_name_begin
	choice
	condition
	expression
	factor
	literal
	opt_condition_clause
	opt_initializer
	opt_timeout_clause
	or_relation_list
	prefix
	primary
	relation
	relation_list_aoxxn
	shift_expression
	simple_expression
	string_literal
	term
	type_conversion
	xnor_relation_list
	xor_relation_list
	abstract_literal
	numeric_literal
	physical_literal
%type <function_call>
	character_literal
	function_call
	function_call_w_args
	function_call_wo_args
%type <function_declaration>
	enumeration_literal_decl
%type <library_unit>
	design_unit
	library_unit
	primary_unit
	secondary_unit
%type <loop_stat>
	iteration_scheme
	loop_statement_begin
%type <name>
	adding_operator
	any_selected_name
	attribute_name
	formal_designator
	formal_part
	function_formal_part
	function_name
	function_selected_name
	indexed_name
	instantiated_unit
	multiplying_operator
	normal_name
	opt_physical_type_simple_name
	opt_simple_declaration_name
	procedure_name
	procedure_selected_name
	range_attribute_name
	relational_operator
	selected_name
	shift_operator
	sign
	slice_name
	target
	type_name
	type_selected_name
%type <package>
	package_decl_begin
	package_declaration
%type <package_body>
	package_body
	package_body_begin
%type <physical_type_unit>
	primary_unit_definition
	secondary_unit_declaration
%type <process_statement>
	process_statement_begin
%type <return_statement>
	return_statement
%type <seq_stat>
	assertion
	assertion_statement
	case_statement
	conditional_waveforms
	exit_statement
	if_statement
	loop_statement
	next_statement
	null_statement
	procedure_call
	procedure_call_statement
	sequential_statement
	sequential_statement_without_label
	sequential_statement_with_label
	seq_stat
	signal_assign_statement
	variable_assign_statement
	wait_statement
%type <simple_name>
	attribute_designator
	entity_designator
	entity_tag
	function_simple_name
	logical_name
	opt_label
	procedure_simple_name
	simple_name
	suffix
	type_simple_name
%type <subtype_indication>
	element_subtype_definition
	index_subtype_definition
	subtype_indication
	type_mark
%type <symbol_declaration>
	attribute_specification
	subprogram_declaration
	subtype_declaration
%type <waveform_element>
	waveform_element
/* ------- list of non-terminals ----- */
%type <l_association_element>
	actual_parameter_part
	association_list
	function_actual_parameter_part
	function_association_list
	generic_map_aspect
	opt_generic_map_aspect
	opt_port_map_aspect
	port_map_aspect
%type <l_case_alternative>
	case_statement_alternative_list
	selected_waveform_list
	selected_waveforms
%type <l_concurrent_stat>
	architecture_statement_list
	architecture_statement_part
	entity_statement_list
	entity_statement_part
	opt_entity_statement_part
%type <l_constantdecl>
	generic_clause
	generic_interface_element
	generic_list
	opt_generic_clause
%type <l_discrete_range>
	discrete_range_list
	index_constraint
%type <l_element_association>
	element_association_list
%type <l_expression>
	choice_list
	expression_list
%type <l_function_declaration>
	enumeration_literal_list
%type <l_name>
	opt_sensitivity_clause
	opt_sensitivity_list
	selected_name_list
	sensitivity_list
	use_clause
%type <l_physical_type_unit>
	opt_secondary_unit_declaration_list
	secondary_unit_declaration_list
%type <l_record_element>
	element_declaration
	element_declaration_list
%type <l_seqstat>
	process_statement_part
	sequence_of_statements
	sequential_statement_list
	subprogram_statement_part
%type <l_signaldecl>
	opt_port_clause
	port_clause
	port_interface_element
	port_list
%type <l_simple_name>
	entity_designator_list
	entity_name_list
	library_clause
	logical_name_list
%type <l_symboldecl>
	architecture_declarative_part
	attribute_declaration
	block_declarative_item
	block_declarative_item_list
	constant_declaration
	entity_declarative_item
	entity_declarative_item_list
	entity_declarative_part
	package_body_declarative_item
	package_body_declarative_item_list
	package_body_declarative_part
	package_declarative_item
	package_declarative_item_list
	package_declarative_part
	process_declarative_part
	process_declarative_item
	process_declarative_item_list
	signal_declaration
	subprogram_declarative_item
	subprogram_declarative_item_list
	subprogram_declarative_part
	variable_declaration
%type <l_typedecl>
	index_subtype_definition_list
%type <l_valdecl>
	formal_func_parameter_list
	formal_proc_parameter_list
	func_interface_element
	opt_formal_func_parameter_list
	opt_formal_proc_parameter_list
	proc_interface_element
%type <l_waveform_element>
	waveform
	waveform_element_list
/* -------- helper types ----------- */
%type <h_constraint>
	constraint
%type <h_context_item>
	context_item
	context_item_list
%type <h_if_stat>
	elsif_list
%type <h_type_decl_helper>
	array_type_definition
	constrained_array_definition
	composite_type_definition
	enumeration_type_definition
	full_type_declaration
	number_type_definition
	physical_type_definition
	record_type_definition
	scalar_type_definition
	type_declaration
	type_definition
	unconstrained_array_definition
%type <identifier>
	architecture_body_begin_2
	entity_decl_begin_2
	ID_function
	ID_procedure
	ID_type
	t_Identifier
%type <l_string>
	identifier_list

%%
%start design_file;

/* --------- design units --------- */

design_file:
	design_unit_list
;

design_unit_list:
	design_unit {
		driver.registerLibUnit($1);

		// region of last lib unit.
		driver.symbolTable.popRegion();
		// region for next unit.
		driver.symbolTable.pushNewRegion();
	}
	| design_unit_list design_unit {
		driver.registerLibUnit($2);

		// region of last unit.
		driver.symbolTable.popRegion();
		// region for next design_unit
		driver.symbolTable.pushNewRegion();
	}
;

design_unit:
	context_item_list library_unit {
		$$ = $2;
		$2->libClauses = $1->libClauses;
		$2->useClauses = $1->useClauses;
		delete $1;
	}
	| library_unit {
		$$ = $1;
	}
;

context_item_list:
	context_item {
		$$ = $1; 
	} 
	| context_item_list context_item {
		$$ = &ast::NodeFactory::mergeContextItems(*$1, *$2);
	}
;

context_item:
	library_clause {
		$$ = new ast::NodeFactory::ContextItemS();
		$$->libClauses = $1;
		$$->useClauses = NULL;
		
		driver.nameLookup.registerLibClauses(*$1);
	}
	| use_clause {
		$$ = new ast::NodeFactory::ContextItemS();
		$$->useClauses = $1;
		$$->libClauses = NULL;

		driver.nameLookup.registerUseClauses(*$1);
	}
;

library_clause:
	t_LIBRARY {
		driver.lookupIdentifiers = false;
	} logical_name_list {
		driver.lookupIdentifiers = true;
	} t_Semicolon	{ $$ = $3; }
;

logical_name_list:
	logical_name {
		$$ = new std::list<ast::SimpleName*>();
		$$->push_back($1);
	}
	| logical_name_list t_Comma logical_name {
		$$ = $1;
		$$->push_back($3);
	}
;

logical_name:
	t_Identifier { 
		$$ = new ast::SimpleName($1->identifier, driver.bl(@1)); 
		delete $1;
	}
;

use_clause:
	t_USE selected_name_list t_Semicolon	{ $$ = $2; }
;

selected_name_list:
	any_selected_name {
		$$ = new std::list<ast::Name*>();
		$$->push_back($1);
	}
	| selected_name_list t_Comma any_selected_name {
		$$ = $1;
		$$->push_back($3);
	}
;

library_unit:
	primary_unit			{ $$ = $1; }
	| secondary_unit		{ $$ = $1; }
;

primary_unit:
	entity_declaration		{ $$ = $1; }
	| package_declaration		{ $$ = $1; }
/*	| configuration_declaration 
*/
;


secondary_unit:
	architecture_body		{ $$ = $1; }
	| package_body			{ $$ = $1; }
; 

/* ------------ names ------------- */

identifier_list:
	t_Identifier {
		$$ = new std::list<std::string*>();
		$$->push_back($1->identifier);
		delete $1;
	} 
	| identifier_list t_Comma t_Identifier {
		$$ = $1;
		$$->push_back($3->identifier);
		delete $3;
	}
;

any_selected_name:
	selected_name			{ $$ = $1; }
	| type_selected_name		{ $$ = $1; }
	| function_selected_name	{ $$ = $1; }
	| procedure_selected_name	{ $$ = $1; }
;


normal_name:
	simple_name		{ $$ = $1; }
	| selected_name		{ $$ = $1; }
	| attribute_name	{ $$ = $1; }
	| indexed_name		{ $$ = $1; }
	| slice_name		{ $$ = $1; }
/*	| operator_symbol	{ $$ = NULL; } */
;

type_name:
	type_simple_name 	{ $$ = $1; }
	| type_selected_name	{ $$ = $1; }
;

type_simple_name:
	ID_type {
		$$ = new ast::SimpleName($1->identifier, $1->candidates,
					driver.bl(@1));
	}
;

slice_name:
	normal_name t_LeftParen discrete_range t_RightParen {
		ast::Slice *s = new ast::Slice($1, $3, driver.bl(@1));
		$$ = new ast::TemporaryName(s, driver.bl(@1));

		// tricky: a slice will still result in an array, just
		//         propagate the candidates.
		$$->candidates = $1->candidates;
	}
	| function_call_w_args t_LeftParen discrete_range t_RightParen {
		ast::Slice *s = new ast::Slice($1, $3, driver.bl(@1));
		$$ = new ast::TemporaryName(s, driver.bl(@1));

		assert($1->subprog != NULL);
		$$->candidates = $1->subprog->candidates;
	}
	| function_name t_LeftParen discrete_range t_RightParen {
		//$1 must be a function call without arguments.
		ast::FunctionCall *f = new ast::FunctionCall($1, 
			new std::list<ast::AssociationElement*>(),
			driver.bl(@1));

		ast::Slice *s = new ast::Slice(f, $3, driver.bl(@2));
		$$ = new ast::TemporaryName(s, driver.bl(@1));
		$$->candidates = $1->candidates;
	}
	/* procedure_name not considered, that would make no sense */
	/* type_name not considered, that would make no sense */
;

indexed_name:
	normal_name t_LeftParen expression_list t_RightParen {
		ast::Subscript *s = new ast::Subscript($1, $3, driver.bl(@1));
		$$ = new ast::TemporaryName(s, driver.bl(@1));

		$$->candidates =
			ast::NameLookup::shiftSubscriptCands(
						$1->candidates, 
						driver.bl(@2));
		driver.nameLookup.isExpanded = false;
	}
	| function_call_w_args t_LeftParen expression_list t_RightParen {
		ast::Subscript *s = new ast::Subscript($1, $3, driver.bl(@1));
		$$ = new ast::TemporaryName(s, driver.bl(@1));

		assert($1->subprog != NULL);
		$$->candidates = 
			ast::NameLookup::shiftSubscriptCands(
						$1->subprog->candidates,
						driver.bl(@2));
		driver.nameLookup.isExpanded = false;
							
	}
	/* procedure_name not considered, that would make no sense */
	/* type_name not considered, that would make no sense */
	/* function_call_wo_args ( idx1, idx2, ... ) is already parsed as a 
	   function_call from primary. It *might* be an indexed name, instead 
	   of a function_call, but that cannot be determined w.o. type 
	   analysis.
	   Hence f(x) could either mean "call f with x as parameter" or 
	   "call f without parameter and take element x from the return type."
	 */
;

expression_list:
	expression {
		$$ = new std::list<ast::Expression*>();
		$$->push_back($1);
	}
	| expression_list t_Comma expression {
		$$ = $1;
		$$->push_back($3);
	}
;

simple_name:
	t_Identifier {
		$$ = new ast::SimpleName($1->identifier, $1->candidates, 
					driver.bl(@1));
	}
;

function_simple_name:
	ID_function {
		$$ = new ast::SimpleName($1->identifier, $1->candidates, 
					driver.bl(@1));
	}
;

procedure_simple_name:
	ID_procedure {
		$$ = new ast::SimpleName($1->identifier, $1->candidates, 
					driver.bl(@1));
	}
;

function_name:
	function_simple_name { 
		$$ = $1;
	}
	| function_selected_name { 
		$$ = $1;
	}
;

procedure_name:
	procedure_simple_name		{ $$ = $1; }
	| procedure_selected_name	{ $$ = $1; }

selected_name:
	prefix t_Dot suffix {
		$$ = driver.nameLookup.makeSelectedName(
					$1, new std::string(*$3->name),
					$3->candidates,
					driver.bl(@3));
		util::MiscUtil::terminate($3);
		driver.nameLookup.unshiftScope();
	}
	| function_call_w_args t_Dot {
		assert($1->subprog);
		driver.nameLookup.shiftSelectedScope(*$1->subprog);
	} suffix {
		$$ = new ast::SelectedName(new std::string(*$4->name), $1, 
			$4->candidates,
			driver.bl(@2));
		util::MiscUtil::terminate($4);
		driver.nameLookup.unshiftScope();
	} 
;

function_selected_name:
	prefix t_Dot ID_function {
		$$ = driver.nameLookup.makeSelectedName(
					$1, $3->identifier, $3->candidates,
					driver.bl(@3));
		driver.nameLookup.unshiftScope();
	}
;

procedure_selected_name:
	prefix t_Dot ID_procedure {
		$$ = driver.nameLookup.makeSelectedName(
					$1, $3->identifier, $3->candidates,
					driver.bl(@3));
		driver.nameLookup.unshiftScope();
	}
;

type_selected_name:
	prefix t_Dot ID_type {
		$$ = driver.nameLookup.makeSelectedName(
					$1, $3->identifier, $3->candidates,
					driver.bl(@3));
		driver.nameLookup.unshiftScope();
	}
	/* function_call_w_args doesn't make sense as prefix */
;

prefix:
	normal_name { 
		$$ = $1; 
		driver.nameLookup.shiftScope(*$1, false);
	}
	| type_name { 
		$$ = $1;
		driver.nameLookup.shiftScope(*$1, false);
	}
	| function_name	{
		$$ = $1; 
		driver.nameLookup.shiftScope(*$1, true);

		/* FIXME?? */
		if (! driver.nameLookup.isExpanded) {
			$$ = new ast::FunctionCall($1, 
				new std::list<ast::AssociationElement*>(),
				driver.bl(@1));
		}
	}
	| procedure_name {
		$$ = $1;
		driver.nameLookup.shiftScope(*$1, false);
	}
;

function_call:
	function_call_wo_args	{ $$ = $1; }
	| function_call_w_args	{ $$ = $1; }
	  /* FIXME this might as well be an indexed name. */
;

function_call_wo_args:
	function_name { 
		$$ = new ast::FunctionCall($1, 
			new std::list<ast::AssociationElement*>(),
			driver.bl(@1)); 
	}
;

function_call_w_args:
	function_name t_LeftParen 
	function_actual_parameter_part 
	t_RightParen {
		$$ = new ast::FunctionCall($1, $3, driver.bl(@1));
	}
;

function_actual_parameter_part:
	function_association_list 	{ $$ = $1; }
;
	

actual_parameter_part:
	/* paramert */ association_list { $$ = $1; }
;

suffix:
	simple_name { 
		$$ = $1;
	}
/*	| t_CHAR { 
		//FIXME
		//$$ = new ast::Name($1, driver.bl(@1));
		$$ = NULL;
		// enumeration element
	} */
	| t_ALL	{ 
		$$ = new ast::SimpleName("all", driver.bl(@1));
	}
	/* FIXME operator symbol */
;

attribute_name_begin:
	prefix t_Apostrophe { 
		$$ = $1; 
		/* TODO scopes for attributes? */
		driver.nameLookup.unshiftScope();
		driver.nameLookup.shiftAttributeScope();
	}
;

attribute_name:
	attribute_name_begin simple_name {
		$$ = new ast::AttributeName(new std::string(*$2->name), $1,
					$2->candidates, driver.bl(@2));

		util::MiscUtil::terminate($2);
		driver.nameLookup.unshiftScope();
	}
	| function_call_w_args t_Apostrophe simple_name {
		$$ = new ast::AttributeName(new std::string(*$3->name),
					$1, $3->candidates, driver.bl(@2));
		util::MiscUtil::terminate($3);
		driver.nameLookup.unshiftScope();
	}
;

/* FIXME not covered yet: 'REVERSE_RANGE */
range_attribute_name:
	attribute_name_begin t_RANGE {
		$$ = new ast::AttributeName(new std::string("range"), 
					$1,
					std::list<ast::Symbol*>(), 
					driver.bl(@2));
		driver.nameLookup.unshiftScope();
	}
	| function_call_w_args t_Apostrophe t_RANGE {
		$$ = new ast::AttributeName(new std::string("range"),
					$1, 
					std::list<ast::Symbol*>(),
					driver.bl(@2));
		driver.nameLookup.unshiftScope();
	}
;


/* ----------- expressions -------------- */



choice_list:
	choice {
		$$ = new std::list<ast::Expression*>();
		$$->push_back($1);
	}
	| choice_list t_Bar choice {
		$$ = $1;
		$$->push_back($3);
	}
;

/* FIXME */
choice:
	simple_expression		{ $$ = $1; }
	| discrete_range		{ $$ = $1; }
/*	| element_simple_name		{ $$ = NULL; } */
	| t_OTHERS { 
		$$ = new ast::Others(driver.bl(@1)); 
	}
;

expression:
	relation t_NAND relation {
		$$ = new ast::FunctionCall(
			driver.buildOperatorName("nand", @2),
			$1, $3, driver.bl(@2));
	}
	| relation t_NOR relation {
		$$ = new ast::FunctionCall(
			driver.buildOperatorName("nor", @2),
			$1, $3, driver.bl(@2));
	}
	| relation_list_aoxxn 	{ $$ = $1; }
	| relation 		{ $$ = $1; }
;

relation_list_aoxxn:
	and_relation_list	{ $$ = $1; }
	| or_relation_list	{ $$ = $1; }
	| xor_relation_list	{ $$ = $1; }
	| xnor_relation_list	{ $$ = $1; }
;

and_relation_list:
	relation t_AND relation {
		$$ = new ast::FunctionCall(
			driver.buildOperatorName("and", @2),
			$1, $3, driver.bl(@2));
	}
	| and_relation_list t_AND relation {
		$$ = new ast::FunctionCall(
			driver.buildOperatorName("and", @2),
			$1, $3, driver.bl(@2));
	}
;

or_relation_list:
	relation t_OR relation {
		$$ = new ast::FunctionCall(
			driver.buildOperatorName("or", @2),
			$1, $3, driver.bl(@2));
	}
	| or_relation_list t_OR relation {
		$$ = new ast::FunctionCall(
			driver.buildOperatorName("or", @2),
			$1, $3, driver.bl(@2));
	}
;

xor_relation_list:
	relation t_XOR relation {
		$$ = new ast::FunctionCall(
			driver.buildOperatorName("xor", @2),
			$1, $3, driver.bl(@2));
	}
	| xor_relation_list t_XOR relation {
		$$ = new ast::FunctionCall(
			driver.buildOperatorName("xor", @2),
			$1, $3, driver.bl(@2));
	}
;

xnor_relation_list:
	relation t_XNOR relation {
		$$ = new ast::FunctionCall(
			driver.buildOperatorName("xnor", @2),
			$1, $3, driver.bl(@2));
	}
	| xnor_relation_list t_XNOR relation {
		$$ = new ast::FunctionCall(
			driver.buildOperatorName("xnor", @2),
			$1, $3, driver.bl(@2));
	}
;

relation:
	shift_expression 	{ $$ = $1; }
	| shift_expression relational_operator shift_expression {
		$$ = new ast::FunctionCall($2, $1, $3, driver.bl(@2));
	}
;

shift_expression:
	simple_expression 	{ $$ = $1; }
	| simple_expression shift_operator simple_expression {
		$$ = new ast::FunctionCall($2, $1, $3, driver.bl(@2));
	}
;

simple_expression:
	sign add_term {
		$$ = new ast::FunctionCall($1, $2, driver.bl(@1));
	}
	| add_term 	{ $$ = $1; }
	| term 		{ $$ = $1; }
	| sign term	{ $$ = new ast::FunctionCall($1, $2, driver.bl(@1)); }
;

add_term:
	term adding_operator term	{
		$$ = new ast::FunctionCall($2, $1, $3, driver.bl(@2));
	}
	| add_term adding_operator term {
		$$ = new ast::FunctionCall($2, $1, $3, driver.bl(@2));
	}
;

term:
	factor		{ $$ = $1; }
	| term multiplying_operator factor {
		$$ = new ast::FunctionCall($2, $1, $3, driver.bl(@2));
	}
;

factor:
	primary	{
		$$ = $1; 
	}
	| primary t_DoubleStar primary {
		$$ = new ast::FunctionCall(
			driver.buildOperatorName("**", @2),
					$1, $3, driver.bl(@2));
	}
	| t_ABS primary {
		$$ = new ast::FunctionCall(
			driver.buildOperatorName("abs", @2),
					$2, driver.bl(@2));
	}
	| t_NOT primary {
		$$ = new ast::FunctionCall(
			driver.buildOperatorName("not", @2),
					$2, driver.bl(@2));
	}
;

relational_operator:
	t_EQSym		{ $$ = driver.buildOperatorName("=", @1); }
	| t_EQSym t_EQSym { 
		$$ = driver.buildOperatorName("=", @1); 
		ast::CompileError *ce = 
			new ast::CompileError(
				driver.bl(@2),
				"Use '=' instead of '=='.");
		ast::ErrorRegistry::addError(ce);
	}
	| t_NESym	{ $$ = driver.buildOperatorName("/=", @1); }
	| t_LTSym	{ $$ = driver.buildOperatorName("<", @1); }
	| t_LESym	{ $$ = driver.buildOperatorName("<=", @1); }
	| t_GTSym	{ $$ = driver.buildOperatorName(">", @1); }
	| t_GESym	{ $$ = driver.buildOperatorName(">=", @1); }
;

shift_operator:
	t_SLL		{ $$ = driver.buildOperatorName("sll", @1); }
	| t_SRL		{ $$ = driver.buildOperatorName("srl", @1); }
	| t_SLA		{ $$ = driver.buildOperatorName("sla", @1); }
	| t_SRA		{ $$ = driver.buildOperatorName("sra", @1); }
	| t_ROL		{ $$ = driver.buildOperatorName("rol", @1); }
	| t_ROR		{ $$ = driver.buildOperatorName("ror", @1); }
;

adding_operator:
	t_Plus		{ $$ = driver.buildOperatorName("+", @1); }
	| t_Minus	{ $$ = driver.buildOperatorName("-", @1); }
	| t_Ampersand	{ $$ = driver.buildOperatorName("&", @1); }
;

sign:
	t_Plus		{ $$ = driver.buildOperatorName("+", @1); }
	| t_Minus	{ $$ = driver.buildOperatorName("-", @1); }
;

multiplying_operator:
	t_Star		{ $$ = driver.buildOperatorName("*", @1); }
	| t_Slash	{ $$ = driver.buildOperatorName("/", @1); }
	| t_MOD		{ $$ = driver.buildOperatorName("mod", @1); }
	| t_REM		{ $$ = driver.buildOperatorName("rem", @1); }
;

primary:
	normal_name 					{ $$ = $1; }
	| t_LeftParen expression t_RightParen		{ $$ = $2; }
	| literal					{ $$ = $1; }
	| aggregate					{ $$ = $1; }
	| function_call					{ $$ = $1; }
	| type_conversion				{ $$ = $1; }
;

aggregate:
	t_LeftParen element_association_list t_RightParen {
		$$ = new ast::Aggregate($2, driver.bl(@1));
	}
	| t_LeftParen named_element_association t_RightParen {
		$$ = new ast::Aggregate(
			new std::list<ast::ElementAssociation*>(),
			driver.bl(@1));
		$$->associations->push_back($2);
	}
;

element_association_list:
	element_association t_Comma element_association {
		$$ = new std::list<ast::ElementAssociation*>();
		$$->push_back($1);
		$$->push_back($3);
	}
	| element_association_list t_Comma element_association {
		$$ = $1;
		$$->push_back($3);
	}
;

element_association:
	named_element_association	{ $$ = $1; }
	| expression {
		$$ = new ast::ElementAssociation(NULL, $1, driver.bl(@1));
	}
;

named_element_association:
	choice_list t_Arrow expression {
		$$ = new ast::ElementAssociation($1, $3, driver.bl(@2));
		/* FIXME names of choice_list may not be accurate */
	}
;

type_conversion:
	type_mark t_LeftParen expression t_RightParen {
		$$ = new ast::TypeConversion($3, $1->declaration, 
						driver.bl(@2));
		util::MiscUtil::terminate($1);
	}
;

literal:
	numeric_literal		{ $$ = $1; }
	| string_literal	{ $$ = $1; }
	| character_literal	{ $$ = $1; }
	| t_NULL		{ $$ = NULL; /* FIXME? */}
;

numeric_literal:
	abstract_literal	{ $$ = $1; }
	| physical_literal	{ $$ = $1; }
;

physical_literal:
	t_INTEGER normal_name	{ 
		$$ = new ast::ConstInteger($1, $2, driver.bl(@2));
	}
	| t_REAL normal_name {
		$$ = new ast::ConstInteger($1, $2, driver.bl(@2));
	}
;

physical_literal_integer:
	t_INTEGER normal_name {
		$$ = new ast::ConstInteger($1, $2, driver.bl(@2));
	}
	| normal_name {
		$$ = new ast::ConstInteger(
			static_cast<universal_integer>(1), 
			$1, driver.bl(@1));
	}
;

abstract_literal:
	t_INTEGER { 
		$$ = new ast::ConstInteger($1, driver.bl(@1));
	}
	| t_REAL {
		$$ = new ast::ConstReal($1, driver.bl(@1));
	}
;

string_literal:
	t_STRING {
		$$ = ast::NodeFactory::makeAggregateFromString(*$1, 
							driver.bl(@1),
							driver.nameLookup);
	}
;

character_literal:
	t_CHAR {
		ast::SimpleName *sn = 
			new ast::SimpleName(
				$1,
				driver.nameLookup.lookup(*$1),
				driver.bl(@1));
		if (sn->candidates.empty()) {
			ast::UndefinedSymbol *us = 
				new ast::UndefinedSymbol(*$1, driver.bl(@1));
			ast::ErrorRegistry::addError(us);
		}
		$$ = new ast::FunctionCall(
			sn,
			new std::list<ast::AssociationElement*>(), 
			driver.bl(@1));
	}
;

direction:
	t_TO		{ $$ = ast::DiscreteRange::DIRECTION_UP; }
	| t_DOWNTO	{ $$ = ast::DiscreteRange::DIRECTION_DOWN; }
;

/* --------- sequential statements ---------- */

sequential_statement:
	sequential_statement_with_label		{ $$ = $1; }
	| sequential_statement_without_label 	{ $$ = $1; }
;

sequential_statement_without_label:
	seq_stat t_Semicolon 	{ 
		$$ = $1; 
		assert(driver.label == NULL);
	}
;

sequential_statement_with_label:
	t_Identifier t_Colon {
		driver.label = $1->identifier;
		delete $1;
	} seq_stat t_Semicolon {
		$$ = $4;
		assert(driver.label == NULL);
	}
;

seq_stat:
	wait_statement			{ $$ = $1; }
	| assertion_statement		{ $$ = $1; }
	| signal_assign_statement	{ $$ = $1; }
	| variable_assign_statement	{ $$ = $1; }
	| procedure_call_statement  	{ $$ = $1; }
	| if_statement 			{ $$ = $1; }
	| case_statement		{ $$ = $1; }
	| loop_statement		{ $$ = $1; }
	| next_statement		{ $$ = $1; }
	| exit_statement		{ $$ = $1; }
	| return_statement		{ $$ = $1; }
	| null_statement		{ $$ = $1; }
/*	| report_statement */
;

variable_assign_statement:
	target t_VarAsgn expression {
		$$ = new ast::VarAssignStat($1, $3, driver.bl(@2));
		driver.label = NULL;
	}
;

target:
	normal_name	{ $$ = $1; }
/*	| aggregate */
/* 	TODO left out for simplicity. each expression of the aggregate would
 *  	need to be a variable name */
;


label:
	t_Identifier { 
		$$ = $1->identifier; 
		delete $1;
	}
;

signal_assign_statement:
	target t_LESym opt_delaymechanism waveform {
		$$ = new ast::SigAssignStat($1, $4, driver.bl(@2));
		driver.label = NULL;
	}
;

opt_delaymechanism:
	/* nothing */
	| delay_mechanism {
		assert(false);
		//TODO
	}
;

delay_mechanism:
	t_TRANSPORT
	| t_REJECT expression t_INERTIAL
;

waveform:
	waveform_element_list { 
		$$ = $1; 
	}
	| t_UNAFFECTED {
		/* note: only legal in concurrent signal assign statements */
		assert(false); //TODO
		$$ = NULL;
	}
;

waveform_element_list:
	waveform_element {
		$$ = new std::list<ast::WaveFormElem*>();
		$$->push_back($1);
	}
	| waveform_element_list t_Comma waveform_element {
		$$ = $1;
		$$->push_back($3);
	}
;

waveform_element:
	expression {
		$$ = new ast::WaveFormElem($1, NULL, driver.bl(@1));
	}
	| expression t_AFTER expression {
		$$ = new ast::WaveFormElem($1, $3, driver.bl(@1));
	}
/*	| t_NULL
	| t_NULL t_AFTER expression 
	// t_NULL included within expression
*/
;

procedure_call_statement:
	procedure_call {
		$$ = $1;
		driver.label = NULL;
	}
;

procedure_call:
	procedure_name {
		$$ = new ast::ProcCallStat($1, 
				new std::list<ast::AssociationElement*>(), 
				driver.bl(@1));
	}
	| procedure_name t_LeftParen actual_parameter_part t_RightParen {
		$$ = new ast::ProcCallStat($1, $3, driver.bl(@1));
	}
;

if_statement_begin:
	t_IF {
		driver.label = NULL;
	}
;

if_statement:
	/* 1 */ if_statement_begin /* 2 */ condition /* 3 */ t_THEN
	/* 4 */ sequence_of_statements	/* 5 */ elsif_list
	/* 6 */ t_ELSE /* 7 */ sequence_of_statements 
	/* 8 */ if_statement_end {
		$$ = &ast::NodeFactory::createIfStat($2, $4, $5, $7,
							driver.bl(@1));
	}
	| /* 1 */ if_statement_begin /* 2 */ condition /* 3 */ t_THEN 
	/* 4 */ sequence_of_statements /* 5 */ t_ELSE 
	/* 6 */ sequence_of_statements /* 7 */ if_statement_end {
		$$ = &ast::NodeFactory::createIfStat($2, $4, NULL, $6, 
							driver.bl(@1));
	}
	| /* 1 */ if_statement_begin /* 2 */ condition /* 3 */ t_THEN 
	/* 4 */ sequence_of_statements /* 5 */ elsif_list
	/* 6 */ if_statement_end {
		$$ = &ast::NodeFactory::createIfStat($2, $4, $5, NULL,
							driver.bl(@1));
	}
	| /* 1 */ if_statement_begin /* 2 */ condition /* 3 */ t_THEN 
	/* 4 */ sequence_of_statements /* 5 */ if_statement_end {
		$$ = &ast::NodeFactory::createIfStat($2, $4, NULL, NULL,
							driver.bl(@1));
	}
;


if_statement_end:
	t_END t_IF 
	| t_END t_IF label {
		/* TODO label */
		assert(false);
	}
;

elsif_list:
	 t_ELSIF condition t_THEN sequence_of_statements {
	 	$$ = &ast::NodeFactory::createNestedElseIfs(NULL, $2, $4,
							driver.bl(@1));
	 }
	 | elsif_list t_ELSIF condition t_THEN sequence_of_statements {
	 	$$ = &ast::NodeFactory::createNestedElseIfs($1, $3, $5, 
							driver.bl(@2));
	 }
;

condition:
	expression 	{ $$ = $1; }
;

sequence_of_statements:
	/* nothing */ {
		$$ = new std::list<ast::SeqStat*>();
	}
	| sequential_statement_list {
		$$ = $1;
	}
;

sequential_statement_list:
	sequential_statement {
		$$ = new std::list<ast::SeqStat*>();
		$$->push_back($1);
	}
	| sequential_statement_list sequential_statement {
		$$ = $1;
		$$->push_back($2);
	}
;

case_statement_begin:
	t_CASE {
		driver.label = NULL;
	}
;

case_statement:
	case_statement_begin expression t_IS
	case_statement_alternative_list
	t_END t_CASE opt_label {
		$$ = new ast::CaseStat($2, $4, driver.bl(@1));
		/* FIXME opt_label */
	}
;


opt_label:
	/* nothing */ {
		$$ = NULL;
	}
	| label {
		$$ = new ast::SimpleName($1, driver.bl(@1));
	}
;

case_statement_alternative_list:
	case_statement_alternative {
		$$ = new std::list<ast::CaseAlternative*>();
		$$->push_back($1);
	}
	| case_statement_alternative_list case_statement_alternative {
		$$ = $1;
		$$->push_back($2);
	}
;

case_statement_alternative:
	t_WHEN choice_list t_Arrow sequence_of_statements {
		$$ = new ast::CaseAlternative($2, $4, driver.bl(@1));
	}
;

loop_statement:
	loop_statement_begin
	sequence_of_statements
	t_END t_LOOP opt_label {
		$$ = $1;
		$1->loopStats = $2;
		/* FIXME  opt_label */
		driver.symbolTable.popRegion();
	}
;

loop_statement_begin:
	iteration_scheme t_LOOP {
		$$ = $1;
		driver.label = NULL;
	}
	| t_LOOP {
		/* make while(true) from this.
		 */
		driver.symbolTable.pushStdStandard();
		ast::SimpleName *sn = 
			new ast::SimpleName(
					new std::string("true"), 
					driver.symbolTable.lookup("true"),
					driver.bl(@1));
		driver.symbolTable.popRegion();
		driver.symbolTable.popRegion();

		ast::FunctionCall *fc =
			new ast::FunctionCall(sn, 
				new std::list<ast::AssociationElement*>(),
				driver.bl(@1));
		
		$$ = new ast::WhileLoopStat(NULL, NULL,
				fc,
				driver.bl(@1));

		if (driver.label != NULL) {
			$$->name = driver.label;
			driver.symbolTable.registerSymbolWithRegion(
							ast::SYMBOL_LOOP,
							*$$);
		} else {
			driver.symbolTable.pushNewRegion();
		}
		driver.label = NULL;
	}
;

iteration_scheme:
	t_WHILE condition {
		$$ = new ast::WhileLoopStat(NULL, NULL, $2, driver.bl(@1));
		if (driver.label != NULL) {
			$$->name = driver.label;
			driver.symbolTable.registerSymbolWithRegion(
							ast::SYMBOL_LOOP,
							*$$);
		} else {
			driver.symbolTable.pushNewRegion();
		}
	}
	| t_FOR {
		driver.lookupIdentifiers = false;
	} t_Identifier {
		driver.lookupIdentifiers = true;
	} t_IN discrete_range {
		ast::ConstantDeclaration *c = new ast::ConstantDeclaration(
						$3->identifier, 
						NULL,
						NULL,
						false,
						driver.bl(@3));
		delete $3;
		$$ = new ast::ForLoopStat(NULL, NULL, c, $6, driver.bl(@1));

		if (driver.label != NULL) {
			$$->name = driver.label;
			driver.symbolTable.registerSymbolWithRegion(
							ast::SYMBOL_LOOP,
							*$$);
		} else {
			driver.symbolTable.pushNewRegion();
		}

		driver.symbolTable.registerSymbol(
						ast::SYMBOL_VARIABLE,
						*c);

	}
;

discrete_range:
	subtype_indication { 
		$$ = new ast::DiscreteRange($1, driver.bl(@1));
	}
	| range {
		$$ = $1;
	}
;


/* FIXME */
range:
	range_attribute_name {
		$$ = new ast::DiscreteRange(NULL, NULL,
				ast::DiscreteRange::DIRECTION_UP,
				driver.bl(@1));
		$$->rangeName = $1;
	} 
	| simple_expression direction simple_expression {
		$$ = new ast::DiscreteRange($1, $3, $2, driver.bl(@2));
	}
;

next_statement_begin:
	t_NEXT {
		driver.label = NULL;
	}
;

next_statement:
	next_statement_begin opt_label {
		/* FIXME use true as condition? */
		$$ = new ast::NextStat(NULL, $2, driver.bl(@1));
	}
	| next_statement_begin opt_label t_WHEN condition {
		$$ = new ast::NextStat($4, $2, driver.bl(@1));
	}
;

exit_statement_begin:
	t_EXIT {
		driver.label = NULL;
	}
;

exit_statement:
	exit_statement_begin label t_WHEN condition {
		$$ = new ast::ExitStat(
			new ast::SimpleName($2, driver.bl(@2)),
			$4, driver.bl(@1));
	}
	| exit_statement_begin label {
		/* FIXME true as condition? */
		$$ = new ast::ExitStat(
			new ast::SimpleName($2, driver.bl(@2)),
			NULL, driver.bl(@1));
	}
	| exit_statement_begin t_WHEN condition {
		$$ = new ast::ExitStat(NULL, $3, driver.bl(@1));
	}
	| exit_statement_begin {
		/* FIXME true as condition? */
		$$ = new ast::ExitStat(NULL, NULL, driver.bl(@1));
	}
;


return_statement_begin:
	t_RETURN {
		driver.label = NULL;
	}
;

return_statement:
	return_statement_begin expression {
		$$ = new ast::ReturnStat($2, driver.bl(@1));
		if (driver.subprogStack.empty()) {
			ast::CompileError *err 
				= new ast::CompileError($$->location,
					"RETURN statementent outside of "
					"function or procedure");
			ast::ErrorRegistry::addError(err);
		} else {
			// check if not a function
			ast::Callable *c = driver.subprogStack.top();
			if (c->kind != ast::SubprogBody::PROG_KIND_FUNCTION) {
				ast::CompileError *err 
					= new ast::CompileError($$->location,
						"RETURN statement with "
						"expression in procedure");
				ast::ErrorRegistry::addError(err);		
			}

			$$->enclosingSubprog = c;
		}
	}
	| return_statement_begin {
		$$ = new ast::ReturnStat(NULL, driver.bl(@1));
		if (driver.subprogStack.empty()) {
			ast::CompileError *err 
				= new ast::CompileError($$->location,
					"RETURN statementent outside of "
					"function or procedure");
			ast::ErrorRegistry::addError(err);
		} else {
			ast::Callable *c = driver.subprogStack.top();
			$$->enclosingSubprog = c;
		}

	}
;

null_statement:
	t_NULL { 
		$$ = new ast::NullStat(driver.bl(@1));
		driver.label = NULL;
	}
;

assertion_statement:
	assertion {
		$$ = $1;
	}
;

assertion:
	t_ASSERT condition t_REPORT expression t_SEVERITY expression {
		$$ = new ast::AssertStat($2, $4, $6, driver.bl(@1));
		driver.label = NULL;
	}
	| t_ASSERT condition t_REPORT expression {
		$$ = new ast::AssertStat($2, $4, NULL, driver.bl(@1));
		driver.label = NULL;
	}
	| t_ASSERT condition t_SEVERITY expression {
		$$ = new ast::AssertStat($2, NULL, $4, driver.bl(@1));
		driver.label = NULL;
	}
	| t_ASSERT condition {
		$$ = new ast::AssertStat($2, NULL, NULL, driver.bl(@1));
		driver.label = NULL;
	}
;

wait_statement:
	t_WAIT opt_sensitivity_clause opt_condition_clause 
	opt_timeout_clause {
		$$ = new ast::WaitStat($2, $3, $4, driver.bl(@1));
		driver.label = NULL;
	}
;

opt_sensitivity_clause:
	/* nothing */ {
		$$ = new std::list<ast::Name*>();
	}
	| t_ON sensitivity_list {
		$$ = $2;
	}
;

sensitivity_list:
	normal_name /* signal name */ {
		$$ = new std::list<ast::Name*>();
		$$->push_back($1);
	}
	| sensitivity_list t_Comma normal_name {
		$$ = $1;
		$$->push_back($3);
	}
;

opt_condition_clause:
	/* nothing */ {
		$$ = NULL;
	}
	| t_UNTIL condition {
		$$ = $2;
	}
;

opt_timeout_clause:
	/* nothing */ {
		$$ = NULL;
	}
	| t_FOR expression {
		/* time_expression */
		$$ = $2;
	}
;

/* ------------ concurrent statements ----------- */

concurrent_statement_with_label_begin:
	t_Identifier t_Colon { 
		$$ = $1->identifier;
		delete $1;
		driver.label = $$;
	}
;

concurrent_statement:
	concurrent_statement_with_label_begin
		concurrent_statement_label_optional {
		$$ = $2;
		$$->name = $1;
		driver.label = NULL;
	}
	| concurrent_statement_with_label_begin
		concurrent_statement_label_mandatory {
		$$ = $2;
		$$->name = $1;
		driver.label = NULL;
	}
	| concurrent_statement_label_optional {
		$$ = $1;
	}
;

concurrent_statement_label_optional:
	process_statement				{ $$ = $1; }
	| concurrent_signal_assignment_statement	{ $$ = $1; }
/*	| concurrent_procedure_call_statement
	| concurrent_assertion_statement */
;

concurrent_statement_label_mandatory:
	component_instantiation_statement		{ $$ = $1; }
/*	| block_statement
	| generate_statement */
;


concurrent_signal_assignment_statement:
	t_POSTPONED conditional_signal_assignment {
		$$ = $2;
		assert(false); /* TODO postponed */
	}
	| conditional_signal_assignment {
		$$ = $1;
	}
	| t_POSTPONED selected_signal_assignment {
		$$ = $2;
		assert(false); /* TODO postponed */
	}
	| selected_signal_assignment {
		$$ = $1;
	}
;

conditional_signal_assignment:
	target t_LESym options conditional_waveforms t_Semicolon {
		$$ = &ast::NodeFactory::makeCondalSigAssign(*$1, *$4,
							driver.bl(@2));
	}
;

options:
	opt_guarded opt_delay_mechanism
;

opt_guarded:
	/* nothing */ 	{ $$ = false; }
	| t_GUARDED	{ $$ = true; assert(false); /* FIXME */ }
;

opt_delay_mechanism:
	/* nothing */
	| delay_mechanism {
		assert(false); /* TODO */ 
	}
;

conditional_waveforms:
	waveform {
		$$ = new ast::SigAssignStat(NULL, $1, driver.bl(@1));
	}
	| waveform t_WHEN condition {
		ast::SigAssignStat* s = new ast::SigAssignStat(NULL, $1, 
							driver.bl(@1));
		std::list<ast::SeqStat*>* ll = new std::list<ast::SeqStat*>();
		ll->push_back(s);

		$$ = new ast::IfStat($3, ll, NULL, driver.bl(@2));
	}
	| waveform t_WHEN condition t_ELSE conditional_waveforms {
		ast::SigAssignStat* s = new ast::SigAssignStat(NULL, $1, 
							driver.bl(@1));
		std::list<ast::SeqStat*>* ll = new std::list<ast::SeqStat*>();
		ll->push_back(s);

		std::list<ast::SeqStat*>* lr = new std::list<ast::SeqStat*>();
		lr->push_back($5);

		$$ = new ast::IfStat($3, ll, lr, driver.bl(@2));
	}
;

selected_signal_assignment:
	t_WITH expression t_SELECT target t_LESym options selected_waveforms
	t_Semicolon {
		ast::CaseStat* c = new ast::CaseStat($2, $7, driver.bl(@1));
		$$ = &ast::NodeFactory::makeCondalSigAssign(*$4, *c,
							driver.bl(@5));
	}
;

selected_waveforms:
	selected_waveform_list		{ $$ = $1; }
;

selected_waveform_list:
	waveform t_WHEN choice_list {
		ast::SigAssignStat* s = new ast::SigAssignStat(NULL, $1,
							driver.bl(@1));

		std::list<ast::SeqStat*>* l = new std::list<ast::SeqStat*>();
		l->push_back(s);

		$$ = new std::list<ast::CaseAlternative*>();
		$$->push_back(new ast::CaseAlternative($3, l, driver.bl(@2)));
	}
	| selected_waveform_list t_Comma waveform t_WHEN choice_list {
		ast::SigAssignStat* s = new ast::SigAssignStat(NULL, $3,
							driver.bl(@3));
		std::list<ast::SeqStat*>* l = new std::list<ast::SeqStat*>();
		l->push_back(s);
		
		$$ = $1;
		$$->push_back(new ast::CaseAlternative($5, l, driver.bl(@4)));
	}
;

opt_postponed:
	/* nothing */					{ $$ = false; }
	| t_POSTPONED {
		$$ = true;
		assert(false); /* FIXME */
	} 
;

opt_sensitivity_list:
	/* nothing */ 					{ $$ = NULL; }
	| t_LeftParen sensitivity_list t_RightParen 	{ $$ = $2; }
;

component_instantiation_statement:
	instantiated_unit opt_generic_map_aspect
	opt_port_map_aspect t_Semicolon {
		$$ = new ast::CompInstStat($1, $2, $3, driver.bl(@1));
		// lookup formal parts
		ast::ResolveSymbols rs = 
			ast::ResolveSymbols(driver.symbolTable);
		$$->accept(rs);
	}
;

instantiated_unit:
	t_COMPONENT normal_name 	{ $$ = $2; }
	| normal_name			{ $$ = $1; }
/*	| t_ENTITY name t_LeftParen t_Identifier t_RightParen 
	//FIXME conflict to name with braces
	| t_ENTITY name
	| t_CONFIGURATION name
//TODO
*/
;

opt_generic_map_aspect:
	/* nothing */		{ 
		$$ = new std::list<ast::AssociationElement *>(); 
	}
	| generic_map_aspect	{ $$ = $1; }
;

opt_port_map_aspect:
	/* nothing */		{ 
		$$ = new std::list<ast::AssociationElement*>();
	}
	| port_map_aspect	{ $$ = $1; }
;

generic_map_aspect:
	t_GENERIC t_MAP t_LeftParen association_list t_RightParen {
		$$ = $4;
	}
;

port_map_aspect:
	t_PORT t_MAP t_LeftParen association_list t_RightParen {
		$$ = $4;
	}
;

association_list:
	association_element {
		$$ = new std::list<ast::AssociationElement*>();
		$$->push_back($1);
	}
	| association_list t_Comma association_element {
		$$ = $1;
		$$->push_back($3);
	}
;

function_association_list:
	function_association_element {
		$$ = new std::list<ast::AssociationElement*>();
		$$->push_back($1);
	}
	| function_association_list t_Comma function_association_element {
		$$ = $1;
		$$->push_back($3);
	}
;

function_association_element:
	function_formal_part t_Arrow actual_part {
		$$ = new ast::AssociationElement($1, $3, driver.bl(@2));
	}
	| actual_part {
		$$ = new ast::AssociationElement(NULL, $1, driver.bl(@1));
	}
;

association_element:
	formal_part t_Arrow actual_part {
		$$ = new ast::AssociationElement($1, $3, driver.bl(@2));
	}
	| actual_part { 
		$$ = new ast::AssociationElement(NULL, $1, driver.bl(@1));
	}
;


function_formal_part:
	formal_designator { 
		$$ = $1; 
	} 
	/* no conversion function/type_conversion considered, as functions
	   can have only parameters of mode "in" */
;

formal_part:
	formal_designator	{ $$ = $1; }
/*	| function_name t_LeftParen formal_designator t_RightParen { $$ = $3; }
	| type_mark t_LeftParen formal_designator t_RightParen	   { $$ = $3; }
	*/
/* FIXME conflicts */
;

formal_designator:
	normal_name		{ $$ = $1; }
	| function_name		{ $$ = $1; }
	| type_name 		{ $$ = $1; }
	| procedure_name 	{ $$ = $1; }
;


/* FIXME handled via expression already. Is there some semantic difference
         anyways? */
actual_part:
	actual_designator	{ $$ = $1; }
/*	| function_name t_LeftParen actual_designator t_RightParen
	| type_mark t_LeftParen actual_designator t_RightParen */
;

actual_designator:
	expression		{ $$ = $1; }
/*	| file_name */
	| t_OPEN		{ $$ = NULL; }
;

process_statement_begin:
	opt_postponed t_PROCESS {
		$$ = new ast::Process(NULL, NULL, NULL, driver.bl(@2));
		if (driver.label != NULL) {
			$$->name = driver.label;
			driver.label = NULL;
			driver.symbolTable.registerSymbolWithRegion(
							ast::SYMBOL_PROCESS,
							*$$);
		} else {
			driver.symbolTable.pushNewRegion();
		}
	}
;

process_statement:
	process_statement_begin opt_sensitivity_list
	opt_t_is process_declarative_part
	t_BEGIN process_statement_part
	t_END opt_postponed t_PROCESS opt_label t_Semicolon {
		$$ = $1;
		$1->sensitivityList = $2;
		$1->declarations = $4;
		$1->seqStats = $6;
		driver.symbolTable.popRegion();
	}
;

opt_t_is:
	/* nothing */
	| t_IS
;

process_declarative_part:
	/* nothing */ { 
		$$ = new std::list<ast::SymbolDeclaration*>(); 
	}
	| process_declarative_item_list 	{ $$ = $1; }
;

process_statement_part:
	sequence_of_statements 	{ $$ = $1; }
;

process_declarative_item_list:
	process_declarative_item {
		$$ = $1; 
	}
	| process_declarative_item_list process_declarative_item {
		ast::NodeFactory::listCombine($1, $2);
		delete $2;
		$$ = $1;
	}
;

process_declarative_item:
	subprogram_declaration {
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| subprogram_body {
		$$ = new std::list<ast::SymbolDeclaration*>();
		if (! $1->seen) {
			$$->push_back($1);
		}
	}
	| type_declaration {
		$$ = $1->flatten();
		delete $1;
	}
	| subtype_declaration {
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| constant_declaration		{ $$ = $1; }
	| variable_declaration		{ $$ = $1; }
	| attribute_specification { 
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| attribute_declaration		{ $$ = $1; }
/*	| file_declaration
	| alias_declaration
	| use_clause
	| group_template_declaration
	| group_declaration */
;

attribute_declaration:
	t_ATTRIBUTE designator t_Colon type_mark t_Semicolon {
		ast::AttributeDeclaration *a = 
			new ast::AttributeDeclaration(
					$2,
					$4,
					driver.bl(@1));
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back(a);
		driver.symbolTable.registerSymbol(ast::SYMBOL_ATTRIBUTE, *a);
	}
;

subprogram_declaration:
	subprogram_specification t_Semicolon {
		$$ = $1;
		if ($1->kind == ast::SubprogBody::PROG_KIND_FUNCTION) {
			driver.symbolTable.registerSymbolWithRegion(
						ast::SYMBOL_FUNCTION,
						*$1);
		} else {
			driver.symbolTable.registerSymbolWithRegion(
						ast::SYMBOL_PROCEDURE,
						*$1);
		}
		/* register parameter */
		if ($1->arguments) {
			driver.registerSymbolDecls(
					$1->arguments, 
					ast::SYMBOL_PARAMETER);
		}
		/* leave the region immediately again */
		driver.symbolTable.popRegion();
	}
;

subprogram_body_begin:
	subprogram_specification t_IS {
		$$ = driver.nameLookup.findOrRegisterSubprog($1);
		driver.subprogStack.push($$);
	}
;
	

subprogram_body:
	subprogram_body_begin
	subprogram_declarative_part
	t_BEGIN 
	subprogram_statement_part
	t_END opt_subprogram_kind opt_designator t_Semicolon {
		$$ = $1;
		$1->definition = new ast::SubprogBody($2, $4, driver.bl(@1));
		/* FIXME opt_designator */

		/* leave region of subprogram again */
		driver.symbolTable.popRegion();
		driver.subprogStack.pop();
	}
;

opt_designator:
	/* nothing */	{ $$ = NULL; }
	| designator	{ $$ = $1; }
;

opt_subprogram_kind:
	/* nothing */	{ $$ = ast::SubprogBody::PROG_KIND_UNKNOWN; }
	| subprogram_kind { $$ = $1; }
;

subprogram_kind:
	t_PROCEDURE	{ $$ = ast::SubprogBody::PROG_KIND_PROCEDURE; }
	| t_FUNCTION	{ $$ = ast::SubprogBody::PROG_KIND_FUNCTION; }

;

pure_or_impure:
	/* nothing */	{ $$ = true; }
	| t_PURE	{ $$ = true; }
	| t_IMPURE	{ $$ = false; }
;

subprogram_specification:
	/* 1 */ t_PROCEDURE {
		driver.lookupIdentifiers = false;
	} /* 3 */ designator {
		driver.lookupIdentifiers = true;
	} /* 5 */ opt_formal_proc_parameter_list {
		$$ = new ast::ProcedureDeclaration($3, $5, driver.bl(@1));
	}
	| /* 1 */ pure_or_impure /* 2 */ t_FUNCTION {
		driver.lookupIdentifiers = false;
	} /* 4 */ designator {
		driver.lookupIdentifiers = true;
	} /* 6 */ opt_formal_func_parameter_list /* 7 */ t_RETURN 
	/* 8 */ type_mark {
		$$ = new ast::FunctionDeclaration($4, $6, $8, $1, driver.bl(@2));
	}
;

opt_formal_func_parameter_list:
	/* nothing */						{ $$ = NULL; }
	| t_LeftParen formal_func_parameter_list t_RightParen	{ $$ = $2; }
;

opt_formal_proc_parameter_list:
	/* nothing */						{ $$ = NULL; }
	| t_LeftParen formal_proc_parameter_list t_RightParen	{ $$ = $2; }
;


designator:
	t_Identifier		{ $$ = $1->identifier; }
	| operator_symbol	{ $$ = $1; }
	| ID_function		{ $$ = $1->identifier; }
	| ID_procedure		{ $$ = $1->identifier; }
;

operator_symbol:
	t_STRING 		{ $$ = $1; }
;

type_mark:
	type_name {
		/* type name or subtype name */
		$$ = new ast::SubtypeIndication($1, driver.bl(@1));
	}
;

subprogram_declarative_part:
	/* nothing */ 				{ $$ = NULL; }
	| subprogram_declarative_item_list 	{ $$ = $1; }
;

subprogram_declarative_item_list:
	subprogram_declarative_item {
		$$ = $1;
	}
	| subprogram_declarative_item_list subprogram_declarative_item {
		$$ = $1;
		ast::NodeFactory::listCombine($1, $2);
		delete $2;
	}
;

subprogram_declarative_item:
	subprogram_declaration {
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| subprogram_body {
		$$ = new std::list<ast::SymbolDeclaration*>();
		if (! $1->seen) {
			$$->push_back($1);
		}
	}
	| type_declaration {
		$$ = $1->flatten();
		delete $1;
	}
	| subtype_declaration {
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| constant_declaration	{ $$ = $1; }
	| variable_declaration	{ $$ = $1; }
	| attribute_specification { 
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| attribute_declaration	{ $$ = $1; }
/*	| file_declaration
	| alias_declaration
	| use_clause
	| group_template_declaration
	| group_declaration
TODO */
;

subprogram_statement_part:
	sequence_of_statements	{ $$ = $1; }
;

formal_proc_parameter_list:
	proc_interface_element	{ $$ = $1; }
	| formal_proc_parameter_list t_Semicolon proc_interface_element {
		$$ = $1;
		ast::NodeFactory::listCombine($1, $3);
		delete $3;
	}

;

formal_func_parameter_list:
	func_interface_element	{ $$ = $1; }
	| formal_func_parameter_list t_Semicolon func_interface_element {
		$$ = $1;
		ast::NodeFactory::listCombine($1, $3);
		delete $3;
	}
;


func_interface_element:
	/* 1 */ opt_element_kind /* 2 */ identifier_list 
	/* 3 */ t_Colon /* 4 */ opt_mode 
	/* 5 */ subtype_indication /* 6 */ opt_bus /* 7 */ opt_initializer {
		$$ = &ast::NodeFactory::makeFuncInterfaceDecls(
							*$2, $7, $4, $1, $5,
							driver.bl(@2));
	}
;

proc_interface_element:
	/* 1 */ opt_element_kind /* 2 */ identifier_list 
	/* 3 */ t_Colon /* 4 */ opt_mode
	/* 5 */ subtype_indication /* 6 */ opt_bus /* 7 */ opt_initializer {
		$$ = &ast::NodeFactory::makeProcInterfaceDecls(
			*$2, $7, $4, $1, $5, driver.bl(@2));
	}
;

opt_element_kind:
	/* nothing */	{ $$ = ast::ValDeclaration::OBJ_CLASS_UNSPECIFIED; }
	| element_kind	{ $$ = $1; }
;

element_kind:	
	t_CONSTANT 	{ $$ = ast::ValDeclaration::OBJ_CLASS_CONSTANT; }
	| t_VARIABLE	{ $$ = ast::ValDeclaration::OBJ_CLASS_VARIABLE; } 
	| t_SIGNAL	{ $$ = ast::ValDeclaration::OBJ_CLASS_SIGNAL; }
	/* | t_FILE */
;

opt_mode:
	/* nothing */	{ $$ = ast::ValDeclaration::MODE_IN; }
	| mode		{ $$ = $1; }
;

opt_bus:
	/* nothing */
	| t_BUS
;

opt_initializer:
	/* nothing */		{ $$ = NULL; }
	| t_VarAsgn expression	{ $$ = $2; }
;

mode:
	t_IN		{ $$ = ast::ValDeclaration::MODE_IN; }
	| t_OUT		{ $$ = ast::ValDeclaration::MODE_OUT; }
	| t_INOUT	{ $$ = ast::ValDeclaration::MODE_INOUT; }
/*	| t_BUFFER
	| t_LINKAGE */
;

subtype_indication:
	type_mark { 
		$$ = $1;
	}
	| type_mark constraint {
		$$ = $1;
		$$->constraint = $2->rangeConstraint;
		$$->indexConstraint = $2->indexConstraint;
		delete $2;
	}
	| function_name type_mark {
		$$ = $2;
		$$->resolutionFunction = $1;
	}
	| function_name type_mark constraint {
		$$ = $2;

		$$->resolutionFunction = $1;
		$$->constraint = $3->rangeConstraint;
		$$->indexConstraint = $3->indexConstraint;
		delete $3;
	}
;

constraint:
	range_constraint { 
		$$ = new ast::NodeFactory::Constraint();
		$$->rangeConstraint = $1;
		$$->indexConstraint = NULL;
	}
	| index_constraint { 
		$$ = new ast::NodeFactory::Constraint();
		$$->rangeConstraint = NULL;
		$$->indexConstraint = $1;
	}
;

range_constraint:
	t_RANGE discrete_range	{ $$ = $2; }
;

subtype_declaration:
	/* 1 */ t_SUBTYPE {
		driver.lookupIdentifiers = false;
	} /* 3 */ t_Identifier /* 4 */ t_IS {
		driver.lookupIdentifiers = true;
	} /* 6 */ subtype_indication /* 7 */ t_Semicolon {
		$$ = $6;
		$$->name = $3->identifier;
		delete $3;
		$$->location = driver.bl(@1);
		driver.symbolTable.registerSymbol(ast::SYMBOL_TYPE, *$$);
	}
;

/* ----------------------- types ---------------------- */

type_declaration:
	full_type_declaration		{ $$ = $1; }
/*	| incomplete_type_definition TODO */
;

full_type_declaration:
	t_TYPE {
		driver.lookupIdentifiers = false;
	} t_Identifier {
		driver.lookupIdentifiers = true;
	} t_IS type_definition t_Semicolon {
		$$ = $6;
		$$->registerType(driver.symbolTable, $3);
	}
;

type_definition:
	scalar_type_definition		{ $$ = $1; }
	| composite_type_definition	{ $$ = $1; }
/*	| access_type_definition
	| file_type_definition
	| protected_type_definition
TODO */
;

scalar_type_definition:
	enumeration_type_definition	{ $$ = $1; }
	| number_type_definition	{ $$ = $1; }
	| physical_type_definition	{ $$ = $1; }
;

enumeration_type_definition:
	t_LeftParen {
		driver.lookupIdentifiers = false;
	} enumeration_literal_list t_RightParen {
		$$ = new ast::NodeFactory::TypeDeclHelper(
			new ast::EnumerationType(NULL, $3, driver.bl(@1)));
		driver.lookupIdentifiers = true;
	}
;

enumeration_literal_list:
	enumeration_literal_decl {
		$$ = new std::list<ast::FunctionDeclaration*>();
		$$->push_back($1);
	}
	| enumeration_literal_list t_Comma enumeration_literal_decl {
		$$ = $1;
		$$->push_back($3);
	}
;

enumeration_literal_decl:
	t_Identifier {
		$$ = new ast::FunctionDeclaration(
						$1->identifier,
						NULL,
						NULL, /* will be set later */
						true,
						driver.bl(@1));
		delete $1;
	}
	| t_CHAR {
		$$ = new ast::FunctionDeclaration(
						$1,
						NULL,
						NULL, /* will be set later */
						true,
						driver.bl(@1));
	}
;


number_type_definition:
	range_constraint {
		$$ = new ast::NodeFactory::TypeDeclHelper(
			new ast::RangeConstraintType(NULL, $1, driver.bl(@1))
			);
	}
;

physical_type_definition:
	/* 1 */ range_constraint /* 2 */ t_UNITS {
		driver.lookupIdentifiers = false;
	} /* 4 */ primary_unit_definition 
	/* 5 */ opt_secondary_unit_declaration_list
	/* 6 */ t_END {
		driver.lookupIdentifiers = true;
	} /* 8 */ t_UNITS /* 9 */ opt_physical_type_simple_name {
		$5->push_front($4);
		$$ = new ast::NodeFactory::TypeDeclHelper(
			new ast::PhysicalType(NULL, $1, $5, driver.bl(@4)));
	}
;

opt_physical_type_simple_name:
	/* nothing */		{ $$ = NULL; }
	| simple_name		{ $$ = $1; }
;

primary_unit_definition:
	t_Identifier t_Semicolon {
		$$ = new ast::PhysicalTypeUnit($1->identifier, NULL, 
						driver.bl(@1));
		driver.symbolTable.registerSymbol(ast::SYMBOL_UNIT, *$$);
		std::cerr << "registering " << *$1->identifier << std::endl;
	}
;

opt_secondary_unit_declaration_list:
	/* nothing */ { 
		$$ = new std::list<ast::PhysicalTypeUnit*>(); 
	}
	| secondary_unit_declaration_list 	{ $$ = $1; }
;

secondary_unit_declaration_list:
	secondary_unit_declaration { 
		$$ = new std::list<ast::PhysicalTypeUnit*>();
		$$->push_back($1);
	}
	| secondary_unit_declaration_list secondary_unit_declaration {
		$$ = $1;
		$$->push_back($2);
	}

secondary_unit_declaration:
	t_Identifier {
		driver.lookupIdentifiers = true;
	} t_EQSym physical_literal_integer {
		driver.lookupIdentifiers = false;
	} t_Semicolon {
		$$ = new ast::PhysicalTypeUnit($1->identifier, $4, 
						driver.bl(@1));
		driver.symbolTable.registerSymbol(ast::SYMBOL_UNIT, *$$);
	}
;

composite_type_definition:
	array_type_definition		{ $$ = $1; }
	| record_type_definition	{ $$ = $1; }
;

array_type_definition:
	unconstrained_array_definition	{ $$ = $1; }
	| constrained_array_definition	{ $$ = $1; }
;

unconstrained_array_definition:
	t_ARRAY t_LeftParen index_subtype_definition_list t_RightParen
	t_OF subtype_indication {
		$$ = new ast::NodeFactory::TypeDeclHelper(
			new ast::UnconstrainedArrayType(
				NULL, $3, $6, driver.bl(@1)));
	}
;

index_subtype_definition_list:
	index_subtype_definition {
		$$ = new std::list<ast::TypeDeclaration*>();
		$$->push_back($1);
	}
	| index_subtype_definition_list t_Comma index_subtype_definition {
		$$ = $1;
		$$->push_back($3);
	}
;

index_subtype_definition:
	type_mark t_RANGE t_Box {
		$$ = $1;
	}
;

constrained_array_definition:
	t_ARRAY index_constraint t_OF subtype_indication {
		$$ = new ast::NodeFactory::TypeDeclHelper($2, $4, 
							driver.bl(@1),
							driver.symbolTable);
	}
;

index_constraint:
	t_LeftParen discrete_range_list t_RightParen 	{ $$ = $2; }
;

discrete_range_list:
	discrete_range {
		$$ = new std::list<ast::DiscreteRange*>();
		$$->push_back($1);
	}
	| discrete_range_list t_Comma discrete_range {
		$$ = $1;
		$$->push_back($3);
	}
;

record_type_definition:
	t_RECORD {
		/* register symbol */
		driver.symbolTable.pushNewRegion();
	} element_declaration_list t_END t_RECORD {
		$$ = new ast::NodeFactory::TypeDeclHelper(
			new ast::RecordType(NULL, $3, driver.bl(@1)));
	}
;


element_declaration_list:
	element_declaration {
		$$ = $1;
	}
	| element_declaration_list element_declaration {
		$$ = $1;
		ast::NodeFactory::listCombine($1, $2);
		delete $2;

	}
;

element_declaration:
	identifier_list t_Colon element_subtype_definition t_Semicolon {
		$$ = &ast::NodeFactory::makeRecordTypeElements(*$1, *$3,
							driver.bl(@1));
		driver.registerSymbolDecls($$, ast::SYMBOL_ELEMENT);
		delete $1;
	}
;

element_subtype_definition:
	subtype_indication 	{ $$ = $1; }
;

/* ---------------------- various declarative items --------------------- */

/* FIXME lrm 2.6, 4.3.1.1: deferred constants get redefined in the package 
	 body, and lack an initializer in the package itself.
*/
constant_declaration:
	t_CONSTANT identifier_list t_Colon
	subtype_indication t_VarAsgn expression 
	t_Semicolon {
		std::list<ast::ConstantDeclaration*> *l;
		l = &ast::NodeFactory::makeConstantDecls(*$2, $6, $4,
							driver.bl(@1));
		delete $2;
		$$ = ast::NodeFactory::listCast<ast::SymbolDeclaration*>(l);
		driver.registerSymbolDecls($$, ast::SYMBOL_VARIABLE);

	}
;

variable_declaration:
	/* 1 */ opt_shared /* 2 */ t_VARIABLE /* 3 */ identifier_list
	/* 4 */ t_Colon
	/* 5 */ subtype_indication /* 6 */ opt_initializer 
	/* 7 */ t_Semicolon {
		$$ = &ast::NodeFactory::makeVarDecls(*$3, $6, $5, 
							driver.bl(@2));
		driver.registerSymbolDecls($$, ast::SYMBOL_VARIABLE);
		delete $3;
	}
;


signal_declaration:
	/* 1 */ t_SIGNAL /* 2 */ identifier_list /* 3 */ t_Colon
	/* 4 */ subtype_indication /* 5 */ opt_signal_kind 
	/* 6 */ opt_initializer /* 7 */	t_Semicolon {

		std::list<ast::SignalDeclaration*>* l;
		l = &ast::NodeFactory::makeSignalDecls(*$2,
					ast::ValDeclaration::MODE_INOUT, 
					false, $6, $4, driver.bl(@1));
		delete $2;
		$$ = ast::NodeFactory::listCast<ast::SymbolDeclaration*>(l);
		driver.registerSymbolDecls($$, ast::SYMBOL_SIGNAL);
	}
;

opt_signal_kind:
	/* nothing */
	| t_REGISTER {
		assert(false); /* FIXME */
	}
	| t_BUS {
		assert(false); /* FIXME */
	}
;

opt_shared:
	/* nothing */	{ $$ = false; }
	| t_SHARED 	{ $$ = true; assert(false); /* TODO */ }
;

entity_decl_begin_1:
	t_ENTITY {
		driver.lookupIdentifiers = false;
	}
;

entity_decl_begin_2:
	t_Identifier t_IS {
		$$ = $1;
		driver.lookupIdentifiers = true;
	}
;

entity_decl_begin:
	entity_decl_begin_1 entity_decl_begin_2 {
		$$ = new ast::Entity($2->identifier, NULL, NULL, NULL, NULL, 
					NULL, driver.bl(@1));
		driver.symbolTable.lateRegisterAttach(
					ast::SYMBOL_ENTITY, *$$);
					
	}

entity_declaration:
	entity_decl_begin opt_generic_clause opt_port_clause
	entity_declarative_part opt_entity_statement_part t_END 
	opt_t_entity opt_simple_declaration_name t_Semicolon {
		$$ = $1;
		$$->generics = $2;
		$$->ports = $3;
		$$->declarations = $4;
		/* FIXME entity_statement_part */
	}
;

opt_simple_declaration_name:
	/* nothing */ 	{ $$ = NULL; }
	| simple_name	{ $$ = $1; }
;

opt_t_entity:
	/* nothing */
	| t_ENTITY
;

opt_entity_statement_part:
	/* nothing */	{ $$ = NULL; }
	| t_BEGIN entity_statement_part {
		/* FIXME entity_statement_part */
		assert(false);
		$$ = $2;
	}
;

opt_generic_clause:
	/* nothing */		{ 
		$$ = new std::list<ast::ConstantDeclaration *>(); 
	}
	| generic_clause	{ $$ = $1; }
;

opt_port_clause:
	/* nothing */ { 
		$$ = new std::list<ast::SignalDeclaration*>(); 
	}
	| port_clause		{ $$ = $1; }
;

generic_clause:
	t_GENERIC t_LeftParen generic_list t_RightParen t_Semicolon { 
		$$ = $3;
	}
;

port_clause:
	t_PORT t_LeftParen port_list t_RightParen t_Semicolon {
		$$ = $3;
	}
;

generic_list:
	generic_interface_element {
		$$ = $1;
	}
	| generic_list t_Semicolon generic_interface_element {
		$$ = $1;
		ast::NodeFactory::listCombine($1, $3);
		delete $3;
	}
;

generic_interface_element:
	/* 1 */ opt_t_constant /* 2 */ identifier_list
	/* 3 */ t_Colon /* 4 */ opt_t_in 
	/* 5 */ subtype_indication /* 6 */ opt_initializer {
		$$ = &ast::NodeFactory::makeConstantDecls(*$2, $6, $5,
							  driver.bl(@2));
		driver.registerSymbolDecls($$, ast::SYMBOL_PARAMETER);
		delete $2;

		/* generics can change for each instantiated component... */
		for (std::list<ast::ConstantDeclaration*>::iterator i = 
			$$->begin(); i != $$->end(); i++) {

			(*i)->fixedValue = false;
		}
	}
;

port_list:
	port_interface_element {
		$$ = $1; 
	} 
	| port_list t_Semicolon port_interface_element {
		$$ = $1;
		ast::NodeFactory::listCombine($1, $3);
		delete $3;
	}
;

opt_t_constant:
	/* nothing */
	| t_CONSTANT
;

opt_t_in:
	/* nothing */
	| t_IN
;

port_interface_element:
	/* 1 */ opt_t_signal /* 2 */ identifier_list 
	/* 3 */ t_Colon /* 4 */ opt_mode 
	/* 5 */ subtype_indication /* 6 */ opt_t_bus /* 7 */ opt_initializer {
		$$ = &ast::NodeFactory::makeSignalDecls(*$2, $4, $6, $7, $5,
							driver.bl(@2));
		/* FIXME signal? */
		driver.registerSymbolDecls($$, ast::SYMBOL_PORT);
		delete $2;
	}
	| /* just for better error messages ($5 must be a
	     type_name/subtype_indication) */
	opt_t_signal identifier_list t_Colon opt_mode t_Identifier opt_t_bus
	opt_initializer {
		std::string *msg = 
			new std::string("'");
		*msg += *$5->identifier;
		*msg += "' does not denote a type.";
		driver.error(@5, *msg);
		$$ = NULL;
	}
;

opt_t_bus:
	/* nothing */	{ $$ = false; }
	| t_BUS		{ $$ = true; }
;

opt_t_signal:
	/* nothing */
	| t_SIGNAL
;

entity_declarative_part:
	/* nothing */			{ $$ = NULL; }
	| entity_declarative_item_list	{ $$ = $1; }
;

entity_declarative_item_list:
	entity_declarative_item	{ 
		$$ = $1;
	}
	| entity_declarative_item_list entity_declarative_item {
		$$ = $1;
		ast::NodeFactory::listCombine($1, $2);
		delete $2;
	}
;

entity_declarative_item:
	subprogram_declaration {
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| subprogram_body {
		$$ = new std::list<ast::SymbolDeclaration*>();
		if (! $1->seen) {
			$$->push_back($1);
		}
	}
	| type_declaration {
		$$ = $1->flatten();
		delete $1;
	}
	| subtype_declaration {
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| constant_declaration 		{ $$ = $1; }
	| signal_declaration		{ $$ = $1; }
	| attribute_specification { 
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| attribute_declaration		{ $$ = $1; }
/*	| shared_variable_declaration
	| file_declaration
	| alias_declaration
	| disconnection_specification
	| use_clause
	| group_template_declaration
	| group_declaration
TODO
*/
;

entity_statement_part:
	/* nothing */			{ $$ = NULL; }
	| entity_statement_list		{ $$ = $1; }
;

entity_statement_list:
	entity_statement {
		$$ = new std::list<ast::ConcurrentStat*>();
		$$->push_back($1);
	}
	| entity_statement_list entity_statement {
		$$ = $1;
		$$->push_back($2);
	}
;

entity_statement_with_label_begin:
	t_Identifier t_Colon { 
		$$ = $1->identifier;
		delete $1;
		driver.label = $$;
	}
;

entity_statement:
	process_statement			{ $$ = $1; }
	| entity_statement_with_label_begin process_statement {
		$$ = $2;
		$$->name = $1;
		driver.label = NULL;
	}
/*	| concurrent_assertion_stat
	| label t_Colon concurrent_assertion_stat
	| concurrent_procedure_call 
	| label t_Colon concurrent_procedure_call
*/
;

t_package_no_lookup:
	t_PACKAGE {
		driver.lookupIdentifiers = false;
	}
;

package_decl_begin:
	/* 1 */ t_package_no_lookup /* 2 */ t_Identifier /* 3 */ t_IS {
		driver.lookupIdentifiers = true;
		$$ = new ast::Package($2->identifier, NULL, NULL, NULL, 
					driver.bl(@1));

		driver.symbolTable.lateRegisterAttach(
						ast::SYMBOL_PACKAGE,
						*$$);
	}
;

package_declaration:
	package_decl_begin package_declarative_part
	t_END opt_t_package opt_package_simple_name t_Semicolon {
		$$ = $1;
		$$->declarations = $2;
	}
;

opt_t_package:
	/* nothing */
	| t_PACKAGE
;

opt_package_simple_name:
	/* nothing */
	| t_Identifier
;

package_declarative_part:
	/* nothing */				{ $$ = NULL; }
	| package_declarative_item_list		{ $$ = $1; }
;

package_declarative_item_list:
	package_declarative_item {
		$$ = $1; 
	} 
	| package_declarative_item_list package_declarative_item {
		$$ = $1;
		ast::NodeFactory::listCombine($1, $2);
		delete $2;
	}
;

package_declarative_item:
	subprogram_declaration {
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| type_declaration {
		$$ = $1->flatten();
		delete $1;
	}
	| subtype_declaration {
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| constant_declaration		{ $$ = $1; }
	| signal_declaration		{ $$ = $1; }
	| variable_declaration 		{ $$ = $1; }
	| attribute_specification { 
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| attribute_declaration 	{ $$ = $1; }
/*	| file_declaration
	| alias_declaration
	| component_declaration
	| disconnection_specification
	| use_clause
	| group_template_declaration
	| group_declaration
*/
;

architecture_body_begin_1:
	t_ARCHITECTURE {
		driver.lookupIdentifiers = false;
	}
;

architecture_body_begin_2:
	t_Identifier t_OF {
		$$ = $1;
		driver.lookupIdentifiers = true;
	}
;

architecture_body_begin:
	architecture_body_begin_1 architecture_body_begin_2 normal_name t_IS {
		$$ = new ast::Architecture($2->identifier, $3, NULL, NULL, 
						NULL, NULL, driver.bl(@1));

		driver.nameLookup.openRegion($3);
		driver.symbolTable.flipStack();
		driver.symbolTable.lateRegisterAttach(
					ast::SYMBOL_ARCHITECTURE,
					*$$);
	}
;

architecture_body:
	architecture_body_begin
	architecture_declarative_part
	t_BEGIN
	architecture_statement_part
	t_END
	opt_t_architecture opt_simple_declaration_name t_Semicolon {
		$$ = $1;
		$$->declarations = $2;
		$$->concurrentStats = $4;

		//region of architecture
		driver.symbolTable.popRegion();
		//region of entity handled by design unit
	}
;

opt_t_architecture:
	/* nothing */
	| t_ARCHITECTURE
;

architecture_declarative_part:
	/* nothing */ { 
		$$ = new std::list<ast::SymbolDeclaration*>();
	}
	| block_declarative_item_list	{ $$ = $1; }
;

block_declarative_item_list:
	block_declarative_item {
		$$ = $1;
	}
	| block_declarative_item_list block_declarative_item {
		$$ = $1;
		ast::NodeFactory::listCombine($1, $2);
		delete $2;
	}
;

block_declarative_item: 
	subprogram_declaration {
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| subprogram_body {
		$$ = new std::list<ast::SymbolDeclaration*>();
		if (! $1->seen) {
			$$->push_back($1);
		}
	}
	| type_declaration {
		$$ = $1->flatten();
		delete $1;
	}
	| subtype_declaration {
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| constant_declaration	{ $$ = $1; }
	| signal_declaration	{ $$ = $1; }
	| variable_declaration	{ $$ = $1; }
	| attribute_specification { 
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| attribute_declaration	{ $$ = $1; }
/*	| file_declaration
	| alias_declaration
	| component_declaration
	| configuration_specification
	| disconnection_specification
	| use_clause
	| group_template_declaration
	| group_declaration 
*/
;

architecture_statement_part:
	/* nothing */			{ $$ = NULL; }
	| architecture_statement_list	{ $$ = $1; }
;

architecture_statement_list:
	concurrent_statement { 
		$$ = new std::list<ast::ConcurrentStat*>();
		$$->push_back($1);
	} 
	| architecture_statement_list concurrent_statement {
		$$ = $1; 
		$$->push_back($2);
	}
;

package_body_begin:
	/* 1 */ t_package_no_lookup /* 2 */ t_BODY {
		driver.lookupIdentifiers = true;
	} /* 4 */ simple_name /* 5 */ t_IS {
		$$ = new ast::PackageBody($4->name, NULL, NULL, NULL,
					driver.bl(@1)); 
		driver.nameLookup.openRegion($4);
		driver.symbolTable.flipStack();
	}

package_body:
	package_body_begin
	package_body_declarative_part
	t_END opt_t_package opt_simple_declaration_name t_Semicolon {
		$$ = $1;
		$$->declarations = $2;
		// package body
		driver.symbolTable.popRegion();
		// package itself stays on the stack, handled by design_unit
	}
;

package_body_declarative_part:
	/* nothing */				{ $$ = NULL; }
	| package_body_declarative_item_list	{ $$ = $1; }
;

package_body_declarative_item_list:
	package_body_declarative_item {
		$$ = $1;
	}
	| package_body_declarative_item_list package_body_declarative_item {
		$$ = $1;
		ast::NodeFactory::listCombine($1, $2);
		delete $2;
	}
;

package_body_declarative_item:
	subprogram_declaration {
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| subprogram_body {
		$$ = new std::list<ast::SymbolDeclaration*>();
		if (! $1->seen) {
			$$->push_back($1);
		}
	}
	| type_declaration {
		$$ = $1->flatten();
		delete $1;
	}
	| subtype_declaration {
		$$ = new std::list<ast::SymbolDeclaration*>();
		$$->push_back($1);
	}
	| constant_declaration 	{ $$ = $1; }
	| variable_declaration	{ $$ = $1; }
/*	| file_declaration
	| alias_declaration
	| use_clause
	| group_template_declaration
	| group_declaration */
;

attribute_specification:
	/* 1 */ t_ATTRIBUTE /* 2 */ attribute_designator /* 3 */ t_OF 
	/* 4 */ entity_name_list /* 5 */ t_Colon /* 6 */ entity_class
	/* 7 */ t_IS /* 8 */ expression /* 9 */ t_Semicolon {
		
		$$ = ast::NodeFactory::handleAttributeSpecs($2, *$4, $6, $8,
							driver.symbolTable);
		delete $4;
	}
;

entity_class:
	t_ENTITY 		{ $$ = ast::NodeFactory::EC_ENTITY; }
	| t_ARCHITECTURE	{ $$ = ast::NodeFactory::EC_ARCHITECTURE; }
	| t_CONFIGURATION	{ $$ = ast::NodeFactory::EC_CONFIGURATION; }
	| t_PROCEDURE		{ $$ = ast::NodeFactory::EC_PROCEDURE; }
	| t_FUNCTION		{ $$ = ast::NodeFactory::EC_FUNCTION; }
	| t_PACKAGE		{ $$ = ast::NodeFactory::EC_PACKAGE; }
	| t_TYPE		{ $$ = ast::NodeFactory::EC_TYPE; }
	| t_SUBTYPE		{ $$ = ast::NodeFactory::EC_SUBTYPE; }
	| t_CONSTANT		{ $$ = ast::NodeFactory::EC_CONSTANT; }
	| t_SIGNAL		{ $$ = ast::NodeFactory::EC_SIGNAL; }
	| t_VARIABLE		{ $$ = ast::NodeFactory::EC_VARIABLE; }
	| t_COMPONENT		{ $$ = ast::NodeFactory::EC_COMPONENT; }
	| t_LABEL		{ $$ = ast::NodeFactory::EC_LABEL; }
	| t_LITERAL 		{ $$ = ast::NodeFactory::EC_LITERAL; }
	| t_UNITS		{ $$ = ast::NodeFactory::EC_UNITS; }
	| t_GROUP 		{ $$ = ast::NodeFactory::EC_GROUP; }
	| t_FILE		{ $$ = ast::NodeFactory::EC_FILE; }
;

/* FIXME */
entity_name_list:
	entity_designator_list		{ $$ = $1; }
/*	| t_OTHERS */
/*	| t_ALL */
;

entity_designator_list:
	entity_designator {
		$$ = new std::list<ast::SimpleName*>();
		$$->push_back($1);
	}
	| entity_designator_list t_Comma entity_designator {
		$$ = $1;
		$$->push_back($3);
	}
;

entity_designator:
	entity_tag /* opt_signature */ 	{ $$ = $1; }
;

entity_tag:
	simple_name		{ $$ = $1; }
/*	| character_literal
	| operator_symbol
*/
;

attribute_designator:
	simple_name		{ $$ = $1; }
;


%%

/* forward to driver */
void
yy::FAUhdlParser::error(const yy::FAUhdlParser::location_type& l,
                        const std::string& m)
{
	driver.error(l, m);
}
