%{
/*
Copyright (c) 2009 Peter Andrew Jamieson (jamieson.peter@gmail.com)

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/ 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "types.h"
#include "parse_making_ast.h"
 
#define PARSE {printf("here\n");}

#ifndef YYLINENO
int yylineno;
#define YYLINENO yylineno
#else
extern int yylineno;
#endif

void yyerror(const char *str);
int yywrap();
int yyparse();
int yylex(void);

 // RESPONCE in an error
void yyerror(const char *str)
{
	fprintf(stderr,"error in parsing: %s - on line number %d\n",str, yylineno);
	exit(-1);
}
 
// point of continued file reading
int yywrap()
{
	return 1;
}

%}

%union{
	char *id_name;
	char *num_value;
	ast_node_t *node;
} 

%token <id_name> vSYMBOL_ID 
%token <num_value> vNUMBER_ID
%token <num_value> vDELAY_ID
%token vALWAYS vAND vASSIGN vBEGIN vCASE vDEFAULT vDEFINE vELSE vEND vENDCASE 
%token vENDMODULE vIF vINOUT vINPUT vMODULE vNAND vNEGEDGE vNOR vNOT vOR 
%token vOUTPUT vPARAMETER vPOSEDGE vREG vWIRE vXNOR vXOR vDEFPARAM voANDAND 
%token voOROR voLTE voGTE voSLEFT voSRIGHT voEQUAL voNOTEQUAL voCASEEQUAL 
%token voCASENOTEQUAL voXNOR voNAND voNOR vNOT_SUPPORT
%token '?' ':' '|' '^' '&' '<' '>' '+' '-' '*' '/' '%' '(' ')' '{' '}' '[' ']'

%right '?' ':'  
%left voOROR
%left voANDAND    
%left '|'
%left '^' voXNOR voNOR
%left '&' voNAND
%left voEQUAL voNOTEQUAL voCASEEQUAL voCASENOTEQUAL 
%left voGTE voLTE '<' '>'
%left voSLEFT voSRIGHT 
%left '+' '-'   
%left '*' '/' '%'
%left '~' '!'
%left '{' '}'
%left UOR
%left UAND
%left UNOT
%left UNAND
%left UNOR
%left UXNOR
%left UXOR
%left ULNOT
%left UADD
%left UMINUS

%nonassoc LOWER_THAN_ELSE
%nonassoc vELSE

%type <node> source_text items define module list_of_module_items module_item 
%type <node> parameter_declaration input_declaration output_declaration 
%type <node> inout_declaration variable_list variable net_declaration 
%type <node> continuous_assign gate_declaration gate_instance 
%type <node> module_instantiation module_instance list_of_module_connections 
%type <node> module_connection always statement blocking_assignment 
%type <node> non_blocking_assignment case_item_list case_items seq_block 
%type <node> stmt_list delay_control event_expression_list event_expression 
%type <node> expression primary expression_list module_parameter
%type <node> list_of_module_parameters

%%

// 1 Source Text
source_text: items								{next_parsed_verilog_file($1);}
	;

items: items module								{
											if ($1 != NULL)
											{
												$$ = newList_entry($1, $2);
											}
											else
											{
												$$ = newList(FILE_ITEMS, $2);
											}
										}
	| items define								{$$ = $1;}
	| module								{$$ = newList(FILE_ITEMS, $1);} 
	| define								{$$ = NULL;} 
	;

define: vDEFINE vSYMBOL_ID vNUMBER_ID						{$$ = NULL; newConstant($2, $3, yylineno);}
	;

module: vMODULE vSYMBOL_ID '(' variable_list ')' ';' list_of_module_items vENDMODULE	{$$ = newModule($2, $4, $7, yylineno);}
	| vMODULE vSYMBOL_ID '(' variable_list ',' ')' ';' list_of_module_items vENDMODULE	{$$ = newModule($2, $4, $8, yylineno);}
	| vMODULE vSYMBOL_ID '(' ')' ';' list_of_module_items vENDMODULE		{$$ = newModule($2, NULL, $6, yylineno);}	
	;

list_of_module_items: list_of_module_items module_item				{$$ = newList_entry($1, $2);}
	| module_item								{$$ = newList(MODULE_ITEMS, $1);}
	;

module_item: parameter_declaration						{$$ = $1;}
	| input_declaration							{$$ = $1;}
	| output_declaration							{$$ = $1;}
	| inout_declaration							{$$ = $1;}
	| net_declaration							{$$ = $1;}
	| continuous_assign							{$$ = $1;}
	| gate_declaration							{$$ = $1;}
	| module_instantiation							{$$ = $1;}
	| always								{$$ = $1;}
	;

// 2 Declarations	{$$ = NULL;}
parameter_declaration: vPARAMETER variable_list ';'				{$$ = markAndProcessSymbolListWith(PARAMETER, $2);}
	;

input_declaration: vINPUT variable_list ';'					{$$ = markAndProcessSymbolListWith(INPUT, $2);}
	;

output_declaration: vOUTPUT variable_list ';'					{$$ = markAndProcessSymbolListWith(OUTPUT, $2);}
	;

inout_declaration: vINOUT variable_list ';'					{$$ = markAndProcessSymbolListWith(INOUT, $2);}
	;

net_declaration: vWIRE variable_list ';'					{$$ = markAndProcessSymbolListWith(WIRE, $2);}
	| vREG variable_list ';'						{$$ = markAndProcessSymbolListWith(REG, $2);}
	;

variable_list: variable_list ',' variable					{$$ = newList_entry($1, $3);}
	| variable								{$$ = newList(VAR_DECLARE_LIST, $1);}
	;

variable: vSYMBOL_ID								{$$ = newVarDeclare($1, NULL, NULL, NULL, NULL, NULL, yylineno);}
	| '[' expression ':' expression ']' vSYMBOL_ID				{$$ = newVarDeclare($6, $2, $4, NULL, NULL, NULL, yylineno);}
	| '[' expression ':' expression ']' vSYMBOL_ID '[' expression ':' expression ']'	{$$ = newVarDeclare($6, $2, $4, $8, $10, NULL, yylineno);}
	| '[' expression ':' expression ']' vSYMBOL_ID '=' primary		{$$ = newVarDeclare($6, $2, $4, NULL, NULL, $8, yylineno);} // ONLY FOR PARAMETER
	| vSYMBOL_ID '=' primary		 				{$$ = newVarDeclare($1, NULL, NULL, NULL, NULL, $3, yylineno);} // ONLY FOR PARAMETER
	;

continuous_assign: vASSIGN blocking_assignment ';'				{$$ = newAssign($2, yylineno);}
	;

// 3 Primitive Instances	{$$ = NULL;}
gate_declaration: vAND gate_instance ';'					{$$ = newGate(BITWISE_AND, $2, yylineno);}
	| vNAND	gate_instance ';'						{$$ = newGate(BITWISE_NAND, $2, yylineno);}
	| vNOR gate_instance ';'						{$$ = newGate(BITWISE_NOR, $2, yylineno);}
	| vNOT gate_instance ';'						{$$ = newGate(BITWISE_NOT, $2, yylineno);}
	| vOR gate_instance ';'							{$$ = newGate(BITWISE_OR, $2, yylineno);}
	| vXNOR gate_instance ';'						{$$ = newGate(BITWISE_XNOR, $2, yylineno);}
	| vXOR gate_instance ';'						{$$ = newGate(BITWISE_XOR, $2, yylineno);}
	;

gate_instance: vSYMBOL_ID '(' expression ',' expression ',' expression ')'	{$$ = newGateInstance($1, $3, $5, $7, yylineno);}
	| '(' expression ',' expression ',' expression ')'			{$$ = newGateInstance(NULL, $2, $4, $6, yylineno);}
	| vSYMBOL_ID '(' expression ',' expression ')'				{$$ = newGateInstance($1, $3, $5, NULL, yylineno);}
	| '(' expression ',' expression ')'					{$$ = newGateInstance(NULL, $2, $4, NULL, yylineno);}
	;

// 4 MOdule Instantiations	{$$ = NULL;}
module_instantiation: vSYMBOL_ID module_instance				{$$ = newModuleInstance($1, $2, yylineno);}
	;

module_instance: vSYMBOL_ID '(' list_of_module_connections ')' ';'		{$$ = newModuleNamedInstance($1, $3, NULL, yylineno);}
	| '#' '(' list_of_module_parameters ')' vSYMBOL_ID '(' list_of_module_connections ')' ';'	{$$ = newModuleNamedInstance($5, $7, $3, yylineno); }
	| vSYMBOL_ID '(' ')' ';'						{$$ = newModuleNamedInstance($1, NULL, NULL, yylineno);}
	| '#' '(' list_of_module_parameters ')' vSYMBOL_ID '(' ')' ';'		{$$ = newModuleNamedInstance($5, NULL, $3, yylineno);}
 	;

list_of_module_connections: list_of_module_connections ',' module_connection	{$$ = newList_entry($1, $3);}
	| module_connection							{$$ = newList(MODULE_CONNECT_LIST, $1);}
	;

module_connection: '.' vSYMBOL_ID '(' expression ')'				{$$ = newModuleConnection($2, $4, yylineno);}
	| expression								{$$ = newModuleConnection(NULL, $1, yylineno);}
	;

list_of_module_parameters: list_of_module_parameters ',' module_parameter	{$$ = newList_entry($1, $3);}
	| module_parameter							{$$ = newList(MODULE_PARAMETER_LIST, $1);}
	;

module_parameter: '.' vSYMBOL_ID '(' expression ')'				{$$ = newModuleParameter($2, $4, yylineno);}
	| expression								{$$ = newModuleParameter(NULL, $1, yylineno);}
	;

// 5 Behavioral Statements	{$$ = NULL;}
always: vALWAYS delay_control statement 					{$$ = newAlways($2, $3, yylineno);}
	;

statement: seq_block								{$$ = $1;}
	| blocking_assignment ';'						{$$ = $1;}
	| non_blocking_assignment ';'						{$$ = $1;}
	| vIF '(' expression ')' statement %prec LOWER_THAN_ELSE 		{$$ = newIf($3, $5, NULL, yylineno);}
	| vIF '(' expression ')' statement vELSE statement 			{$$ = newIf($3, $5, $7, yylineno);}
	| vCASE '(' expression ')' case_item_list vENDCASE			{$$ = newCase($3, $5, yylineno);}
	| ';'									{$$ = NULL;}
	;
	 
blocking_assignment: primary '=' expression 					{$$ = newBlocking($1, $3, yylineno);}
	| primary '=' vDELAY_ID expression					{$$ = newBlocking($1, $4, yylineno);}
	;

non_blocking_assignment: primary voLTE expression 				{$$ = newNonBlocking($1, $3, yylineno);}
	| primary voLTE vDELAY_ID expression					{$$ = newNonBlocking($1, $4, yylineno);}
	;

case_item_list: case_item_list case_items					{$$ = newList_entry($1, $2);}
	| case_items								{$$ = newList(CASE_LIST, $1);}
	;

case_items: expression ':' statement						{$$ = newCaseItem($1, $3, yylineno);}
	| vDEFAULT ':' statement						{$$ = newDefaultCase($3, yylineno);}
	;

seq_block: vBEGIN stmt_list vEND 						{$$ = $2;}
	;

stmt_list: stmt_list statement 							{$$ = newList_entry($1, $2);}
	| statement								{$$ = newList(BLOCK, $1);}
	;

delay_control: '@' '(' event_expression_list ')' 				{$$ = $3;}
	| '@' '(' '*' ')'							{$$ = NULL;}
	;

// 7 Expressions	{$$ = NULL;}
event_expression_list: event_expression_list vOR event_expression		{$$ = newList_entry($1, $3);}
	| event_expression							{$$ = newList(DELAY_CONTROL, $1);}
	;

event_expression: primary							{$$ = $1;}
	| vPOSEDGE vSYMBOL_ID							{$$ = newPosedgeSymbol($2, yylineno);}
	| vNEGEDGE vSYMBOL_ID							{$$ = newNegedgeSymbol($2, yylineno);}
	;

expression: primary								{$$ = $1;}
	| '+' expression %prec UADD						{$$ = newUnaryOperation(ADD, $2, yylineno);}
	| '-' expression %prec UMINUS						{$$ = newUnaryOperation(MINUS, $2, yylineno);}
	| '~' expression %prec UNOT						{$$ = newUnaryOperation(BITWISE_NOT, $2, yylineno);}
	| '&' expression %prec UAND						{$$ = newUnaryOperation(BITWISE_AND, $2, yylineno);}
	| '|' expression %prec UOR						{$$ = newUnaryOperation(BITWISE_OR, $2, yylineno);}
	| voNAND expression %prec UNAND						{$$ = newUnaryOperation(BITWISE_NAND, $2, yylineno);}
	| voNOR expression %prec UNOR						{$$ = newUnaryOperation(BITWISE_NOR, $2, yylineno);}
	| voXNOR  expression %prec UXNOR					{$$ = newUnaryOperation(BITWISE_XNOR, $2, yylineno);}
	| '!' expression %prec ULNOT						{$$ = newUnaryOperation(LOGICAL_NOT, $2, yylineno);}
	| '^' expression %prec UXOR						{$$ = newUnaryOperation(BITWISE_XOR, $2, yylineno);}
	| expression '^' expression						{$$ = newBinaryOperation(BITWISE_XOR, $1, $3, yylineno);}
	| expression '*' expression						{$$ = newBinaryOperation(MULTIPLY, $1, $3, yylineno);}
	| expression '/' expression						{$$ = newBinaryOperation(DIVIDE, $1, $3, yylineno);}
	| expression '%' expression						{$$ = newBinaryOperation(MODULO, $1, $3, yylineno);}
	| expression '+' expression						{$$ = newBinaryOperation(ADD, $1, $3, yylineno);}
	| expression '-' expression						{$$ = newBinaryOperation(MINUS, $1, $3, yylineno);}
	| expression '&' expression						{$$ = newBinaryOperation(BITWISE_AND, $1, $3, yylineno);}
	| expression '|' expression						{$$ = newBinaryOperation(BITWISE_OR, $1, $3, yylineno);}
	| expression voNAND expression						{$$ = newBinaryOperation(BITWISE_NAND, $1, $3, yylineno);}
	| expression voNOR expression						{$$ = newBinaryOperation(BITWISE_NOR, $1, $3, yylineno);}
	| expression voXNOR expression						{$$ = newBinaryOperation(BITWISE_XNOR, $1, $3, yylineno);}
	| expression '<' expression						{$$ = newBinaryOperation(LT, $1, $3, yylineno);}
	| expression '>' expression						{$$ = newBinaryOperation(GT, $1, $3, yylineno);}
	| expression voSRIGHT expression					{$$ = newBinaryOperation(SR, $1, $3, yylineno);}
	| expression voSLEFT expression						{$$ = newBinaryOperation(SL, $1, $3, yylineno);}
	| expression voEQUAL expression						{$$ = newBinaryOperation(LOGICAL_EQUAL, $1, $3, yylineno);}
	| expression voNOTEQUAL expression					{$$ = newBinaryOperation(NOT_EQUAL, $1, $3, yylineno);}
	| expression voLTE expression						{$$ = newBinaryOperation(LTE, $1, $3, yylineno);}
	| expression voGTE expression						{$$ = newBinaryOperation(GTE, $1, $3, yylineno);}
	| expression voCASEEQUAL expression					{$$ = newBinaryOperation(CASE_EQUAL, $1, $3, yylineno);}
	| expression voCASENOTEQUAL expression					{$$ = newBinaryOperation(CASE_NOT_EQUAL, $1, $3, yylineno);}
	| expression voOROR expression						{$$ = newBinaryOperation(LOGICAL_OR, $1, $3, yylineno);}
	| expression voANDAND expression					{$$ = newBinaryOperation(LOGICAL_AND, $1, $3, yylineno);}
	| expression '?' expression ':' expression				{$$ = newIfQuestion($1, $3, $5, yylineno);}
	| '(' expression ')'							{$$ = $2;}
										/* rule below is strictly unnecessary, causes a bison conflict, 
										but simplifies the AST so that expression_lists with one child 
										(i.e. just an expression) does not create a CONCATENATE parent) */
	| '{' expression '{' expression '}' '}'					{$$ = newListReplicate( $2, $4 ); }
	| '{' expression '{' expression_list '}' '}'				{$$ = newListReplicate( $2, $4 ); }
	;

primary: vNUMBER_ID								{$$ = newNumberNode($1, yylineno);}
	| vSYMBOL_ID								{$$ = newSymbolNode($1, yylineno);}
	| vSYMBOL_ID '[' expression ']'						{$$ = newArrayRef($1, $3, yylineno);}
	| vSYMBOL_ID '[' expression ':' expression ']'				{$$ = newRangeRef($1, $3, $5, yylineno);}
	| '{' expression_list '}' 						{$$ = $2; ($2)->types.concat.num_bit_strings = -1;}
	;
	 
expression_list: expression_list ',' expression					{$$ = newList_entry($1, $3); /* note this will be in order lsb = greatest to msb = 0 in the node child list */}
	| expression								{$$ = newList(CONCATENATE, $1);}
	;

%%
