%{
#include "read.h"
#include "pns.h"

#define YYERROR_VERBOSE 0
#define ENABLE_ERR_MSG 1 /* set to nonzero to see error reports from bison */

  extern FILE* yyin;

  void yyerror(const char *s);

  specs *pspecs;

%}

%token C_BLOCK // block of C code
%token INIT
%token INTEGER
%token LIVE
%token MAIN 
%token PLACES
%token PROCESS
 // %token SPR  // separator (such as semicolon and newline)
%token SELECT
%token SYNC
%token TO
%token TRANSITIONS
%token VAR  // variable 


%%

program:  |
program SPR |
program block |
error SPR { report_error($2.line, "syntax error"); };

block: proc_def |
proc_assignment |
constraint { commit_constraint(); } |
embedded_c |
command |
initialize |
liveness |
synchronization ;


proc_assignment: VAR '=' VAR SPR { copy_process(  -1, $3, $1); } |
         PROCESS VAR '=' VAR SPR { copy_process($1.i, $4, $2); } ;

proc_def: proc_decl '{' pblock '}' { release_process(); };

proc_decl: PROCESS VAR newlines { commit_process($1.i, $2); };

newlines:  |
newlines '\n';

SPR: '\n' { $$ = $1; } |
      ';' { $$ = $1; } ;

pblock: |
pblock SPR |
pblock pline SPR |
pblock pline error SPR { report_error($4.line, "missing semicolon"); }; | 
pblock error SPR { report_error($3.line, "syntax error"); };

pline: place_decl var_list { set_vartype(NONE); } |
trans_decl var_list { set_vartype(NONE); } |
arc_def;

arc_def: VAR TO VAR  { add_arc(&$1, 0, &$3, 0); } |
'(' VAR ',' VAR ')'  { add_arc(&$2, &$4, 0, 0); } |
'(' VAR ',' VAR ',' VAR ')'  { add_arc(&$2, &$4, &$6, 0); } |
'(' VAR ',' VAR ')' C_BLOCK { add_arc(&$2, &$4, 0, &$6); } |
'(' VAR ',' VAR ',' VAR ')' C_BLOCK { add_arc(&$2, &$4, &$6, &$8); } |
'(' error ')'  { report_error(0, "syntax error; an arc definition was expected"); };

place_decl: PLACES ':'  { set_vartype(PLACE); } ;

trans_decl: TRANSITIONS ':'  { set_vartype(TRANS); } ;

var_list: VAR { add_var($1); } |
var_list ',' VAR { add_var($3); } |
var_list VAR { add_var($2); } |
error SPR { report_error($2.line, "a variable list was expected"); };

initialize: INIT ':' init_var_list SPR;

init_var_list:    VAR '(' init_list ')' { init_pvar($1, $3); } |
init_var_list ',' VAR '(' init_list ')' { init_pvar($3, $5); } |
init_var_list     VAR '(' init_list ')' { init_pvar($2, $4); } ;

init_list:    VAR ':' INTEGER { $$ = rvalue_list(  0, $1, $3, 0); } |
init_list ',' VAR ':' INTEGER { $$ = rvalue_list(&$1, $3, $5, 0); } |
init_list     VAR ':' INTEGER { $$ = rvalue_list(&$1, $2, $4, 0); } ;

embedded_c: VAR '.' VAR C_BLOCK SPR { add_c_block($1, $3, $4, 'i'); } |
VAR '.' VAR '.'  SELECT C_BLOCK SPR { add_c_block($1, $3, $6, 's'); };

command: MAIN C_BLOCK SPR { insert_command(0, $1, $2); } |
VAR '.' MAIN C_BLOCK SPR  { insert_command(&$1, $3, $4); } ;  

constraint: term_list '<' '=' term_list SPR { add_ineq($1, $4, 1); } |
term_list '>' '=' term_list SPR { add_ineq($4, $1, 1); } |
term_list '>' term_list SPR     { add_ineq($3, $1, 2); } |
term_list '<' term_list SPR     { add_ineq($1, $3, 2); } |
term_list '=' term_list SPR     { add_ineq($1, $3, 0); };

term_list:  { $$ = set_ineq(0, 0, 0); } |
term { $$ = set_ineq(0, 0, &$1); } |
term_list '+' term  { $$ = set_ineq(&$1, 0, &$3); } |
term_list '-' term  { $$ = set_ineq(&$1, 1, &$3); };

term: subterm { $$ = set_coef(1, $1, $1.line, $1.lastline); } |
INTEGER { $$ = set_free_term($1); } |
subterm '*' INTEGER { $$ = set_coef($3.i, $1, $1.line, $3.lastline); } |
INTEGER '*' subterm { $$ = set_coef($1.i, $3, $1.line, $3.lastline); } |
INTEGER subterm     { $$ = set_coef($1.i, $2, $1.line, $2.lastline); };

subterm: VAR '.' VAR { $$ = add_ineq_var(&$1, &$3, 0); } |
 VAR '.' VAR '.' VAR { $$ = add_ineq_var(&$1, &$3, &$5); };

synchronization: sync_sym vlist SPR          { sync_trans($2,   0); } |
sync_sym vlist '{' assignments '}' SPR       { sync_trans($2, &$4); } |
sync_sym vlist '\n' '{' assignments '}' SPR  { sync_trans($2, &$5); } ;

assignments: |
assignments SPR |
assignments VAR '.' VAR '=' VAR '.' VAR ;

sync_sym: SYNC | SYNC ':' ;

vlist:    VAR '.' VAR { $$ = rvalue_list(  0, $1, $3, 1); } |
vlist ',' VAR '.' VAR { $$ = rvalue_list(&$1, $3, $5, 1); } |
vlist     VAR '.' VAR { $$ = rvalue_list(&$1, $2, $4, 1); } ;

liveness: LIVE ':' vlist SPR { live_trans($1, $3); } |
          LIVE     vlist SPR { live_trans($1, $2); } ;

%%

void yyerror(const char *s) {
  if(ENABLE_ERR_MSG)
    fprintf(stderr, "%s\n", s);
  return;
}


void parse_buffer(char* s) {
  yy_scan_string(s);
  yyparse();
}


specs* parse_file(FILE* f) {
  yyin = f;
  yyparse();
  return 0;
}

