%name cl_ucsim_parser

%{
#include "cmdlexcl.h"
#include "memcl.h"
#include "globals.h"
#include "stypes.h"
%}

%pure_parser

%define INHERIT : public cl_base

%define MEMBERS class cl_ucsim_lexer *lexer_object;\
virtual ~YY_cl_ucsim_parser_CLASS(void) { delete lexer_object; }

%define CONSTRUCTOR_PARAM \
class cl_ucsim_lexer *the_lexer

%define CONSTRUCTOR_CODE \
lexer_object= the_lexer;

%token PTOK_PLUS PTOK_MINUS PTOK_ASTERIX PTOK_SLASH PTOK_EQUAL
%token PTOK_LEFT_PAREN PTOK_RIGHT_PAREN
%token PTOK_LEFT_BRACKET PTOK_RIGHT_BRACKET
%token PTOK_DOT PTOK_AMPERSAND

%token <memory_object> PTOK_MEMORY_OBJECT
%token <memory> PTOK_MEMORY
%token <number> PTOK_NUMBER
%token <bit> PTOK_BIT

%right PTOK_EQUAL
%left PTOK_MINUS PTOK_PLUS
%left PTOK_ASTERIX PTOK_SLASH
%nonassoc UNARYMINUS PTOK_AMPERSAND
%nonassoc PTOK_LEFT_PAREN PTOK_RIGHT_PAREN

%type <number> ucsim_grammar assignment expression address_of_expression
%type <memory> memory
%type <bit> bit

%union {
  long number;
  class cl_memory *memory_object;
  struct {
    class cl_memory *memory;
    long address;
  } memory;
  struct {
    class cl_memory *memory;
    long mem_address, bit_address;
    long mask;
  } bit;
}

%%

ucsim_grammar:
	expression { application->dd_printf("%d\n", $1); }
	;

assignment:
	  memory PTOK_EQUAL expression
	  {
	    $1.memory->write($1.address, $3);
	    $$= $3;
	  }
	| bit PTOK_EQUAL expression
	  {
	    if ($3)
	      {
		$1.memory->write($1.mem_address,
				 $1.memory->read($1.mem_address) | $1.mask);
		$$= 1;
	      }
	    else
	      {
		$1.memory->write($1.mem_address,
				 $1.memory->read($1.mem_address) & ~($1.mask));
		$$= 0;
	      }
	  }
	;

expression:
	  assignment { $$= $1; }
	| expression PTOK_PLUS expression { $$= $1 + $3; }
	| expression PTOK_MINUS expression { $$= $1 - $3; }
	| expression PTOK_ASTERIX expression { $$= $1 * $3; }
	| expression PTOK_SLASH expression
	  {
	    if ($3 == 0)
	      yyerror((char *)"Divide by zero");
	    else
	      $$= $1 / $3;
	  }
	| PTOK_MINUS expression %prec UNARYMINUS { $$= -$2; }
	| address_of_expression { $$= $1; }
	| PTOK_LEFT_PAREN expression PTOK_RIGHT_PAREN { $$= $2; }
	| PTOK_NUMBER { $$= $1; }
	| memory { $$= $1.memory->read($1.address); }
	| bit { $$= ($1.memory->read($1.mem_address) & $1.mask)?1:0; }
	;

address_of_expression:
	  PTOK_AMPERSAND memory { $$= $2.address; }
	| PTOK_AMPERSAND bit
	{
	  $$= $2.bit_address;
	  if ($$ < 0)
	    {
	      yyerror((char *)"Bit has no address.");
	      $$= 0;
	    }
	}
	;

memory:
	  PTOK_MEMORY
	| PTOK_MEMORY_OBJECT PTOK_LEFT_BRACKET expression PTOK_RIGHT_BRACKET
	  {
	    $$.memory= $1;
	    $$.address= $3;
	  }

bit:
	  PTOK_BIT
	| memory PTOK_DOT expression
	  {
	    $$.memory= $1.memory;
	    $$.mem_address= $1.address;
	    $$.mask= 1 << $3;
	    $$.bit_address= -1;
	    class cl_uc *uc= application->get_uc();
	    if (uc)
	      $$.bit_address= uc->bit_address($1.memory, $1.address, $3);
	  }
	;

%%

int
YY_cl_ucsim_parser_CLASS::yylex(YY_cl_ucsim_parser_STYPE *yylval)
{
  lexer_object->activate_lexer_to_parse_into(yylval);
  return(lexer_object->yylex());
}

void
YY_cl_ucsim_parser_CLASS::yyerror(char *msg)
{
  application->dd_printf("Parser error: %s\n", msg);
}
