plan9fox/sys/src/cmd/spin/spin.y
cinap_lenrek 28e9566dc5 spin: Update to most recent version. (thanks Ori_B)
from Ori_B:

There were a small number of changes needed from the tarball
on spinroot.org:

  - The mkfile needed to be updated
  - Memory.h needed to not be included
  - It needed to invoke /bin/cpp instead of gcc -E
  - It depended on `yychar`, which our yacc doesn't
    provide.

I'm still figuring out how to use spin, but it seems to do
the right thing when testing a few of the examples:

	% cd $home/src/Spin/Examples/
	% spin -a peterson.pml
	% pcc pan.c -D_POSIX_SOURCE
	% ./6.out

	(Spin Version 6.4.7 -- 19 August 2017)
		+ Partial Order Reduction

	Full statespace search for:
		never claim         	- (none specified)
		assertion violations	+
		acceptance   cycles 	- (not selected)
		invalid end states	+

	State-vector 32 byte, depth reached 24, errors: 0
	40 states, stored
	27 states, matched
	67 transitions (= stored+matched)
		0 atomic steps
	hash conflicts:         0 (resolved)

	Stats on memory usage (in Megabytes):
	0.002	equivalent memory usage for states (stored*(State-vector + overhead))
	0.292	actual memory usage for states
	128.000	memory used for hash table (-w24)
	0.534	memory used for DFS stack (-m10000)
	128.730	total actual memory usage


	unreached in proctype user
		/tmp/Spin/Examples/peterson.pml:20, state 10, "-end-"
		(1 of 10 states)

	pan: elapsed time 1.25 seconds
	pan: rate        32 states/second
2017-11-22 21:09:31 +01:00

1124 lines
29 KiB
Plaintext

/***** spin: spin.y *****/
/*
* This file is part of the public release of Spin. It is subject to the
* terms in the LICENSE file that is included in this source directory.
* Tool documentation is available at http://spinroot.com
*/
%{
#include "spin.h"
#include <sys/types.h>
#ifndef PC
#include <unistd.h>
#endif
#include <stdarg.h>
#define YYMAXDEPTH 20000 /* default is 10000 */
#define YYDEBUG 0
#define Stop nn(ZN,'@',ZN,ZN)
#define PART0 "place initialized declaration of "
#define PART1 "place initialized chan decl of "
#define PART2 " at start of proctype "
static Lextok *ltl_to_string(Lextok *);
extern Symbol *context, *owner;
extern Lextok *for_body(Lextok *, int);
extern void for_setup(Lextok *, Lextok *, Lextok *);
extern Lextok *for_index(Lextok *, Lextok *);
extern Lextok *sel_index(Lextok *, Lextok *, Lextok *);
extern void keep_track_off(Lextok *);
extern void safe_break(void);
extern void restore_break(void);
extern int u_sync, u_async, dumptab, scope_level;
extern int initialization_ok;
extern short has_sorted, has_random, has_enabled, has_pcvalue, has_np, has_priority;
extern short has_code, has_state, has_ltl, has_io;
extern void count_runs(Lextok *);
extern void no_internals(Lextok *);
extern void any_runs(Lextok *);
extern void explain(int);
extern void ltl_list(char *, char *);
extern void validref(Lextok *, Lextok *);
extern void sanity_check(Lextok *);
extern char yytext[];
int Mpars = 0; /* max nr of message parameters */
int nclaims = 0; /* nr of never claims */
int ltl_mode = 0; /* set when parsing an ltl formula */
int Expand_Ok = 0, realread = 1, IArgs = 0, NamesNotAdded = 0;
int in_for = 0, in_seq = 0, par_cnt = 0;
int dont_simplify = 0;
char *claimproc = (char *) 0;
char *eventmap = (char *) 0;
static char *ltl_name;
static int Embedded = 0, inEventMap = 0, has_ini = 0;
%}
%token ASSERT PRINT PRINTM PREPROC
%token C_CODE C_DECL C_EXPR C_STATE C_TRACK
%token RUN LEN ENABLED SET_P GET_P EVAL PC_VAL
%token TYPEDEF MTYPE INLINE RETURN LABEL OF
%token GOTO BREAK ELSE SEMI ARROW
%token IF FI DO OD FOR SELECT IN SEP DOTDOT
%token ATOMIC NON_ATOMIC D_STEP UNLESS
%token TIMEOUT NONPROGRESS
%token ACTIVE PROCTYPE D_PROCTYPE
%token HIDDEN SHOW ISLOCAL
%token PRIORITY PROVIDED
%token FULL EMPTY NFULL NEMPTY
%token CONST TYPE XU /* val */
%token NAME UNAME PNAME INAME /* sym */
%token STRING CLAIM TRACE INIT LTL /* sym */
%right ASGN
%left SND O_SND RCV R_RCV /* SND doubles as boolean negation */
%left IMPLIES EQUIV /* ltl */
%left OR
%left AND
%left ALWAYS EVENTUALLY /* ltl */
%left UNTIL WEAK_UNTIL RELEASE /* ltl */
%right NEXT /* ltl */
%left '|'
%left '^'
%left '&'
%left EQ NE
%left GT LT GE LE
%left LSHIFT RSHIFT
%left '+' '-'
%left '*' '/' '%'
%left INCR DECR
%right '~' UMIN NEG
%left DOT
%%
/** PROMELA Grammar Rules **/
program : units { yytext[0] = '\0'; }
;
units : unit
| units unit
;
unit : proc /* proctype { } */
| init /* init { } */
| claim /* never claim */
| ltl /* ltl formula */
| events /* event assertions */
| one_decl /* variables, chans */
| utype /* user defined types */
| c_fcts /* c functions etc. */
| ns /* named sequence */
| semi /* optional separator */
| error
;
l_par : '(' { par_cnt++; }
;
r_par : ')' { par_cnt--; }
;
proc : inst /* optional instantiator */
proctype NAME {
setptype($3, PROCTYPE, ZN);
setpname($3);
context = $3->sym;
context->ini = $2; /* linenr and file */
Expand_Ok++; /* expand struct names in decl */
has_ini = 0;
}
l_par decl r_par { Expand_Ok--;
if (has_ini)
fatal("initializer in parameter list", (char *) 0);
}
Opt_priority
Opt_enabler
body { ProcList *rl;
if ($1 != ZN && $1->val > 0)
{ int j;
rl = ready($3->sym, $6, $11->sq, $2->val, $10, A_PROC);
for (j = 0; j < $1->val; j++)
{ runnable(rl, $9?$9->val:1, 1);
announce(":root:");
}
if (dumptab) $3->sym->ini = $1;
} else
{ rl = ready($3->sym, $6, $11->sq, $2->val, $10, P_PROC);
}
if (rl && has_ini == 1) /* global initializations, unsafe */
{ /* printf("proctype %s has initialized data\n",
$3->sym->name);
*/
rl->unsafe = 1;
}
context = ZS;
}
;
proctype: PROCTYPE { $$ = nn(ZN,CONST,ZN,ZN); $$->val = 0; }
| D_PROCTYPE { $$ = nn(ZN,CONST,ZN,ZN); $$->val = 1; }
;
inst : /* empty */ { $$ = ZN; }
| ACTIVE { $$ = nn(ZN,CONST,ZN,ZN); $$->val = 1; }
| ACTIVE '[' const_expr ']' {
$$ = nn(ZN,CONST,ZN,ZN); $$->val = $3->val;
if ($3->val > 255)
non_fatal("max nr of processes is 255\n", "");
}
| ACTIVE '[' NAME ']' {
$$ = nn(ZN,CONST,ZN,ZN);
$$->val = 0;
if (!$3->sym->type)
fatal("undeclared variable %s",
$3->sym->name);
else if ($3->sym->ini->ntyp != CONST)
fatal("need constant initializer for %s\n",
$3->sym->name);
else
$$->val = $3->sym->ini->val;
}
;
init : INIT { context = $1->sym; }
Opt_priority
body { ProcList *rl;
rl = ready(context, ZN, $4->sq, 0, ZN, I_PROC);
runnable(rl, $3?$3->val:1, 1);
announce(":root:");
context = ZS;
}
;
ltl : LTL optname2 { ltl_mode = 1; ltl_name = $2->sym->name; }
ltl_body { if ($4) ltl_list($2->sym->name, $4->sym->name);
ltl_mode = 0; has_ltl = 1;
}
;
ltl_body: '{' full_expr OS '}' { $$ = ltl_to_string($2); }
| error { $$ = NULL; }
;
claim : CLAIM optname { if ($2 != ZN)
{ $1->sym = $2->sym; /* new 5.3.0 */
}
nclaims++;
context = $1->sym;
if (claimproc && !strcmp(claimproc, $1->sym->name))
{ fatal("claim %s redefined", claimproc);
}
claimproc = $1->sym->name;
}
body { (void) ready($1->sym, ZN, $4->sq, 0, ZN, N_CLAIM);
context = ZS;
}
;
optname : /* empty */ { char tb[32];
memset(tb, 0, 32);
sprintf(tb, "never_%d", nclaims);
$$ = nn(ZN, NAME, ZN, ZN);
$$->sym = lookup(tb);
}
| NAME { $$ = $1; }
;
optname2 : /* empty */ { char tb[32]; static int nltl = 0;
memset(tb, 0, 32);
sprintf(tb, "ltl_%d", nltl++);
$$ = nn(ZN, NAME, ZN, ZN);
$$->sym = lookup(tb);
}
| NAME { $$ = $1; }
;
events : TRACE { context = $1->sym;
if (eventmap)
non_fatal("trace %s redefined", eventmap);
eventmap = $1->sym->name;
inEventMap++;
}
body {
if (strcmp($1->sym->name, ":trace:") == 0)
{ (void) ready($1->sym, ZN, $3->sq, 0, ZN, E_TRACE);
} else
{ (void) ready($1->sym, ZN, $3->sq, 0, ZN, N_TRACE);
}
context = ZS;
inEventMap--;
}
;
utype : TYPEDEF NAME '{' { if (context)
{ fatal("typedef %s must be global",
$2->sym->name);
}
owner = $2->sym;
in_seq = $1->ln;
}
decl_lst '}' { setuname($5);
owner = ZS;
in_seq = 0;
}
;
nm : NAME { $$ = $1; }
| INAME { $$ = $1;
if (IArgs)
fatal("invalid use of '%s'", $1->sym->name);
}
;
ns : INLINE nm l_par { NamesNotAdded++; }
args r_par { prep_inline($2->sym, $5);
NamesNotAdded--;
}
;
c_fcts : ccode { /* leaves pseudo-inlines with sym of
* type CODE_FRAG or CODE_DECL in global context
*/
}
| cstate
;
cstate : C_STATE STRING STRING {
c_state($2->sym, $3->sym, ZS);
has_code = has_state = 1;
}
| C_TRACK STRING STRING {
c_track($2->sym, $3->sym, ZS);
has_code = has_state = 1;
}
| C_STATE STRING STRING STRING {
c_state($2->sym, $3->sym, $4->sym);
has_code = has_state = 1;
}
| C_TRACK STRING STRING STRING {
c_track($2->sym, $3->sym, $4->sym);
has_code = has_state = 1;
}
;
ccode : C_CODE { Symbol *s;
NamesNotAdded++;
s = prep_inline(ZS, ZN);
NamesNotAdded--;
$$ = nn(ZN, C_CODE, ZN, ZN);
$$->sym = s;
$$->ln = $1->ln;
$$->fn = $1->fn;
has_code = 1;
}
| C_DECL { Symbol *s;
NamesNotAdded++;
s = prep_inline(ZS, ZN);
NamesNotAdded--;
s->type = CODE_DECL;
$$ = nn(ZN, C_CODE, ZN, ZN);
$$->sym = s;
$$->ln = $1->ln;
$$->fn = $1->fn;
has_code = 1;
}
;
cexpr : C_EXPR { Symbol *s;
NamesNotAdded++;
s = prep_inline(ZS, ZN);
/* if context is 0 this was inside an ltl formula
mark the last inline added to seqnames */
if (!context)
{ mark_last();
}
NamesNotAdded--;
$$ = nn(ZN, C_EXPR, ZN, ZN);
$$->sym = s;
$$->ln = $1->ln;
$$->fn = $1->fn;
no_side_effects(s->name);
has_code = 1;
}
;
body : '{' { open_seq(1); in_seq = $1->ln; }
sequence OS { add_seq(Stop); }
'}' { $$->sq = close_seq(0); in_seq = 0;
if (scope_level != 0)
{ non_fatal("missing '}' ?", 0);
scope_level = 0;
}
}
;
sequence: step { if ($1) add_seq($1); }
| sequence MS step { if ($3) add_seq($3); }
;
step : one_decl { $$ = ZN; }
| XU vref_lst { setxus($2, $1->val); $$ = ZN; }
| NAME ':' one_decl { fatal("label preceding declaration,", (char *)0); }
| NAME ':' XU { fatal("label predecing xr/xs claim,", 0); }
| stmnt { $$ = $1; }
| stmnt UNLESS { if ($1->ntyp == DO) { safe_break(); } }
stmnt { if ($1->ntyp == DO) { restore_break(); }
$$ = do_unless($1, $4);
}
| error { printf("Not a Step\n"); }
;
vis : /* empty */ { $$ = ZN; }
| HIDDEN { $$ = $1; }
| SHOW { $$ = $1; }
| ISLOCAL { $$ = $1; }
;
asgn: /* empty */
| ASGN
;
one_decl: vis TYPE var_list { setptype($3, $2->val, $1);
$$ = $3;
}
| vis UNAME var_list { setutype($3, $2->sym, $1);
$$ = expand($3, Expand_Ok);
}
| vis TYPE asgn '{' nlst '}' {
if ($2->val != MTYPE)
fatal("malformed declaration", 0);
setmtype($5);
if ($1)
non_fatal("cannot %s mtype (ignored)",
$1->sym->name);
if (context != ZS)
fatal("mtype declaration must be global", 0);
}
;
decl_lst: one_decl { $$ = nn(ZN, ',', $1, ZN); }
| one_decl SEMI
decl_lst { $$ = nn(ZN, ',', $1, $3); }
;
decl : /* empty */ { $$ = ZN; }
| decl_lst { $$ = $1; }
;
vref_lst: varref { $$ = nn($1, XU, $1, ZN); }
| varref ',' vref_lst { $$ = nn($1, XU, $1, $3); }
;
var_list: ivar { $$ = nn($1, TYPE, ZN, ZN); }
| ivar ',' var_list { $$ = nn($1, TYPE, ZN, $3); }
;
c_list : CONST { $1->ntyp = CONST; $$ = $1; }
| CONST ',' c_list { $1->ntyp = CONST; $$ = nn($1, ',', $1, $3); }
;
ivar : vardcl { $$ = $1;
$1->sym->ini = nn(ZN,CONST,ZN,ZN);
$1->sym->ini->val = 0;
if (!initialization_ok)
{ Lextok *zx, *xz;
zx = nn(ZN, NAME, ZN, ZN);
zx->sym = $1->sym;
xz = nn(zx, ASGN, zx, $1->sym->ini);
keep_track_off(xz);
/* make sure zx doesnt turn out to be a STRUCT later */
add_seq(xz);
}
}
| vardcl ASGN '{' c_list '}' {
if (!$1->sym->isarray)
fatal("%s must be an array", $1->sym->name);
$$ = $1;
$1->sym->ini = $4;
has_ini = 1;
$1->sym->hidden |= (4|8); /* conservative */
if (!initialization_ok)
{ Lextok *zx = nn(ZN, NAME, ZN, ZN);
zx->sym = $1->sym;
add_seq(nn(zx, ASGN, zx, $4));
}
}
| vardcl ASGN expr { $$ = $1;
$1->sym->ini = $3;
if ($3->ntyp == CONST
|| ($3->ntyp == NAME && $3->sym->context))
{ has_ini = 2; /* local init */
} else
{ has_ini = 1; /* possibly global */
}
trackvar($1, $3);
if (any_oper($3, RUN))
{ fatal("cannot use 'run' in var init, saw", (char *) 0);
}
nochan_manip($1, $3, 0);
no_internals($1);
if (!initialization_ok)
{ Lextok *zx = nn(ZN, NAME, ZN, ZN);
zx->sym = $1->sym;
add_seq(nn(zx, ASGN, zx, $3));
}
}
| vardcl ASGN ch_init { $1->sym->ini = $3;
$$ = $1; has_ini = 1;
if (!initialization_ok)
{ non_fatal(PART1 "'%s'" PART2, $1->sym->name);
}
}
;
ch_init : '[' const_expr ']' OF
'{' typ_list '}' { if ($2->val)
u_async++;
else
u_sync++;
{ int i = cnt_mpars($6);
Mpars = max(Mpars, i);
}
$$ = nn(ZN, CHAN, ZN, $6);
$$->val = $2->val;
$$->ln = $1->ln;
$$->fn = $1->fn;
}
;
vardcl : NAME { $1->sym->nel = 1; $$ = $1; }
| NAME ':' CONST { $1->sym->nbits = $3->val;
if ($3->val >= 8*sizeof(long))
{ non_fatal("width-field %s too large",
$1->sym->name);
$3->val = 8*sizeof(long)-1;
}
$1->sym->nel = 1; $$ = $1;
}
| NAME '[' const_expr ']' { $1->sym->nel = $3->val; $1->sym->isarray = 1; $$ = $1; }
| NAME '[' NAME ']' { /* make an exception for an initialized scalars */
$$ = nn(ZN, CONST, ZN, ZN);
fprintf(stderr, "spin: %s:%d, warning: '%s' in array bound ",
$1->fn->name, $1->ln, $3->sym->name);
if ($3->sym->ini->val > 0)
{ fprintf(stderr, "evaluated as %d\n", $3->sym->ini->val);
$$->val = $3->sym->ini->val;
} else
{ fprintf(stderr, "evaluated as 8 by default (to avoid zero)\n");
$$->val = 8;
}
$1->sym->nel = $$->val;
$1->sym->isarray = 1;
$$ = $1;
}
;
varref : cmpnd { $$ = mk_explicit($1, Expand_Ok, NAME); }
;
pfld : NAME { $$ = nn($1, NAME, ZN, ZN);
if ($1->sym->isarray && !in_for)
{ non_fatal("missing array index for '%s'",
$1->sym->name);
}
}
| NAME { owner = ZS; }
'[' expr ']' { $$ = nn($1, NAME, $4, ZN); }
;
cmpnd : pfld { Embedded++;
if ($1->sym->type == STRUCT)
owner = $1->sym->Snm;
}
sfld { $$ = $1; $$->rgt = $3;
if ($3 && $1->sym->type != STRUCT)
$1->sym->type = STRUCT;
Embedded--;
if (!Embedded && !NamesNotAdded
&& !$1->sym->type)
fatal("undeclared variable: %s",
$1->sym->name);
if ($3) validref($1, $3->lft);
owner = ZS;
}
;
sfld : /* empty */ { $$ = ZN; }
| '.' cmpnd %prec DOT { $$ = nn(ZN, '.', $2, ZN); }
;
stmnt : Special { $$ = $1; initialization_ok = 0; }
| Stmnt { $$ = $1; initialization_ok = 0;
if (inEventMap) non_fatal("not an event", (char *)0);
}
;
for_pre : FOR l_par { in_for = 1; }
varref { trapwonly($4 /*, "for" */);
pushbreak(); /* moved up */
$$ = $4;
}
;
for_post: '{' sequence OS '}' ;
Special : varref RCV { Expand_Ok++; }
rargs { Expand_Ok--; has_io++;
$$ = nn($1, 'r', $1, $4);
trackchanuse($4, ZN, 'R');
}
| varref SND { Expand_Ok++; }
margs { Expand_Ok--; has_io++;
$$ = nn($1, 's', $1, $4);
$$->val=0; trackchanuse($4, ZN, 'S');
any_runs($4);
}
| for_pre ':' expr DOTDOT expr r_par {
for_setup($1, $3, $5); in_for = 0;
}
for_post { $$ = for_body($1, 1);
}
| for_pre IN varref r_par { $$ = for_index($1, $3); in_for = 0;
}
for_post { $$ = for_body($5, 1);
}
| SELECT l_par varref ':' expr DOTDOT expr r_par {
trapwonly($3 /*, "select" */);
$$ = sel_index($3, $5, $7);
}
| IF options FI { $$ = nn($1, IF, ZN, ZN);
$$->sl = $2->sl;
$$->ln = $1->ln;
$$->fn = $1->fn;
prune_opts($$);
}
| DO { pushbreak(); }
options OD { $$ = nn($1, DO, ZN, ZN);
$$->sl = $3->sl;
$$->ln = $1->ln;
$$->fn = $1->fn;
prune_opts($$);
}
| BREAK { $$ = nn(ZN, GOTO, ZN, ZN);
$$->sym = break_dest();
}
| GOTO NAME { $$ = nn($2, GOTO, ZN, ZN);
if ($2->sym->type != 0
&& $2->sym->type != LABEL) {
non_fatal("bad label-name %s",
$2->sym->name);
}
$2->sym->type = LABEL;
}
| NAME ':' stmnt { $$ = nn($1, ':',$3, ZN);
if ($1->sym->type != 0
&& $1->sym->type != LABEL) {
non_fatal("bad label-name %s",
$1->sym->name);
}
$1->sym->type = LABEL;
}
| NAME ':' { $$ = nn($1, ':',ZN,ZN);
if ($1->sym->type != 0
&& $1->sym->type != LABEL) {
non_fatal("bad label-name %s",
$1->sym->name);
}
$$->lft = nn(ZN, 'c', nn(ZN,CONST,ZN,ZN), ZN);
$$->lft->lft->val = 1; /* skip */
$1->sym->type = LABEL;
}
| error { $$ = nn(ZN, 'c', nn(ZN,CONST,ZN,ZN), ZN);
$$->lft->val = 1; /* skip */
}
;
Stmnt : varref ASGN full_expr { $$ = nn($1, ASGN, $1, $3);
trackvar($1, $3);
nochan_manip($1, $3, 0);
no_internals($1);
}
| varref INCR { $$ = nn(ZN,CONST, ZN, ZN); $$->val = 1;
$$ = nn(ZN, '+', $1, $$);
$$ = nn($1, ASGN, $1, $$);
trackvar($1, $1);
no_internals($1);
if ($1->sym->type == CHAN)
fatal("arithmetic on chan", (char *)0);
}
| varref DECR { $$ = nn(ZN,CONST, ZN, ZN); $$->val = 1;
$$ = nn(ZN, '-', $1, $$);
$$ = nn($1, ASGN, $1, $$);
trackvar($1, $1);
no_internals($1);
if ($1->sym->type == CHAN)
fatal("arithmetic on chan id's", (char *)0);
}
| SET_P l_par two_args r_par { $$ = nn(ZN, SET_P, $3, ZN); has_priority++; }
| PRINT l_par STRING { realread = 0; }
prargs r_par { $$ = nn($3, PRINT, $5, ZN); realread = 1; }
| PRINTM l_par varref r_par { $$ = nn(ZN, PRINTM, $3, ZN); }
| PRINTM l_par CONST r_par { $$ = nn(ZN, PRINTM, $3, ZN); }
| ASSERT full_expr { $$ = nn(ZN, ASSERT, $2, ZN); AST_track($2, 0); }
| ccode { $$ = $1; }
| varref R_RCV { Expand_Ok++; }
rargs { Expand_Ok--; has_io++;
$$ = nn($1, 'r', $1, $4);
$$->val = has_random = 1;
trackchanuse($4, ZN, 'R');
}
| varref RCV { Expand_Ok++; }
LT rargs GT { Expand_Ok--; has_io++;
$$ = nn($1, 'r', $1, $5);
$$->val = 2; /* fifo poll */
trackchanuse($5, ZN, 'R');
}
| varref R_RCV { Expand_Ok++; }
LT rargs GT { Expand_Ok--; has_io++; /* rrcv poll */
$$ = nn($1, 'r', $1, $5);
$$->val = 3; has_random = 1;
trackchanuse($5, ZN, 'R');
}
| varref O_SND { Expand_Ok++; }
margs { Expand_Ok--; has_io++;
$$ = nn($1, 's', $1, $4);
$$->val = has_sorted = 1;
trackchanuse($4, ZN, 'S');
any_runs($4);
}
| full_expr { $$ = nn(ZN, 'c', $1, ZN); count_runs($$); }
| ELSE { $$ = nn(ZN,ELSE,ZN,ZN);
}
| ATOMIC '{' { open_seq(0); }
sequence OS '}' { $$ = nn($1, ATOMIC, ZN, ZN);
$$->sl = seqlist(close_seq(3), 0);
$$->ln = $1->ln;
$$->fn = $1->fn;
make_atomic($$->sl->this, 0);
}
| D_STEP '{' { open_seq(0);
rem_Seq();
}
sequence OS '}' { $$ = nn($1, D_STEP, ZN, ZN);
$$->sl = seqlist(close_seq(4), 0);
$$->ln = $1->ln;
$$->fn = $1->fn;
make_atomic($$->sl->this, D_ATOM);
unrem_Seq();
}
| '{' { open_seq(0); }
sequence OS '}' { $$ = nn(ZN, NON_ATOMIC, ZN, ZN);
$$->sl = seqlist(close_seq(5), 0);
$$->ln = $1->ln;
$$->fn = $1->fn;
}
| INAME { IArgs++; }
l_par args r_par { initialization_ok = 0;
pickup_inline($1->sym, $4, ZN);
IArgs--;
}
Stmnt { $$ = $7; }
| varref ASGN INAME { IArgs++; }
l_par args r_par { initialization_ok = 0;
pickup_inline($3->sym, $6, $1);
IArgs--;
}
Stmnt { $$ = $9; }
| RETURN full_expr { $$ = return_statement($2); }
;
options : option { $$->sl = seqlist($1->sq, 0); }
| option options { $$->sl = seqlist($1->sq, $2->sl); }
;
option : SEP { open_seq(0); }
sequence OS { $$ = nn(ZN,0,ZN,ZN);
$$->sq = close_seq(6);
$$->ln = $1->ln;
$$->fn = $1->fn;
}
;
OS : /* empty */
| semi { /* redundant semi at end of sequence */ }
;
semi : SEMI
| ARROW
;
MS : semi { /* at least one semi-colon */ }
| MS semi { /* but more are okay too */ }
;
aname : NAME { $$ = $1; }
| PNAME { $$ = $1; }
;
const_expr: CONST { $$ = $1; }
| '-' const_expr %prec UMIN { $$ = $2; $$->val = -($2->val); }
| l_par const_expr r_par { $$ = $2; }
| const_expr '+' const_expr { $$ = $1; $$->val = $1->val + $3->val; }
| const_expr '-' const_expr { $$ = $1; $$->val = $1->val - $3->val; }
| const_expr '*' const_expr { $$ = $1; $$->val = $1->val * $3->val; }
| const_expr '/' const_expr { $$ = $1; $$->val = $1->val / $3->val; }
| const_expr '%' const_expr { $$ = $1; $$->val = $1->val % $3->val; }
;
expr : l_par expr r_par { $$ = $2; }
| expr '+' expr { $$ = nn(ZN, '+', $1, $3); }
| expr '-' expr { $$ = nn(ZN, '-', $1, $3); }
| expr '*' expr { $$ = nn(ZN, '*', $1, $3); }
| expr '/' expr { $$ = nn(ZN, '/', $1, $3); }
| expr '%' expr { $$ = nn(ZN, '%', $1, $3); }
| expr '&' expr { $$ = nn(ZN, '&', $1, $3); }
| expr '^' expr { $$ = nn(ZN, '^', $1, $3); }
| expr '|' expr { $$ = nn(ZN, '|', $1, $3); }
| expr GT expr { $$ = nn(ZN, GT, $1, $3); }
| expr LT expr { $$ = nn(ZN, LT, $1, $3); }
| expr GE expr { $$ = nn(ZN, GE, $1, $3); }
| expr LE expr { $$ = nn(ZN, LE, $1, $3); }
| expr EQ expr { $$ = nn(ZN, EQ, $1, $3); }
| expr NE expr { $$ = nn(ZN, NE, $1, $3); }
| expr AND expr { $$ = nn(ZN, AND, $1, $3); }
| expr OR expr { $$ = nn(ZN, OR, $1, $3); }
| expr LSHIFT expr { $$ = nn(ZN, LSHIFT,$1, $3); }
| expr RSHIFT expr { $$ = nn(ZN, RSHIFT,$1, $3); }
| '~' expr { $$ = nn(ZN, '~', $2, ZN); }
| '-' expr %prec UMIN { $$ = nn(ZN, UMIN, $2, ZN); }
| SND expr %prec NEG { $$ = nn(ZN, '!', $2, ZN); }
| l_par expr ARROW expr ':' expr r_par {
$$ = nn(ZN, OR, $4, $6);
$$ = nn(ZN, '?', $2, $$);
}
| RUN aname { Expand_Ok++;
if (!context)
fatal("used 'run' outside proctype",
(char *) 0);
}
l_par args r_par
Opt_priority { Expand_Ok--;
$$ = nn($2, RUN, $5, ZN);
$$->val = ($7) ? $7->val : 0;
trackchanuse($5, $2, 'A'); trackrun($$);
}
| LEN l_par varref r_par { $$ = nn($3, LEN, $3, ZN); }
| ENABLED l_par expr r_par { $$ = nn(ZN, ENABLED, $3, ZN); has_enabled++; }
| GET_P l_par expr r_par { $$ = nn(ZN, GET_P, $3, ZN); has_priority++; }
| varref RCV { Expand_Ok++; }
'[' rargs ']' { Expand_Ok--; has_io++;
$$ = nn($1, 'R', $1, $5);
}
| varref R_RCV { Expand_Ok++; }
'[' rargs ']' { Expand_Ok--; has_io++;
$$ = nn($1, 'R', $1, $5);
$$->val = has_random = 1;
}
| varref { $$ = $1; trapwonly($1 /*, "varref" */); }
| cexpr { $$ = $1; }
| CONST { $$ = nn(ZN,CONST,ZN,ZN);
$$->ismtyp = $1->ismtyp;
$$->val = $1->val;
}
| TIMEOUT { $$ = nn(ZN,TIMEOUT, ZN, ZN); }
| NONPROGRESS { $$ = nn(ZN,NONPROGRESS, ZN, ZN);
has_np++;
}
| PC_VAL l_par expr r_par { $$ = nn(ZN, PC_VAL, $3, ZN);
has_pcvalue++;
}
| PNAME '[' expr ']' '@' NAME
{ $$ = rem_lab($1->sym, $3, $6->sym); }
| PNAME '[' expr ']' ':' pfld
{ $$ = rem_var($1->sym, $3, $6->sym, $6->lft); }
| PNAME '@' NAME { $$ = rem_lab($1->sym, ZN, $3->sym); }
| PNAME ':' pfld { $$ = rem_var($1->sym, ZN, $3->sym, $3->lft); }
| ltl_expr { $$ = $1; /* sanity_check($1); */ }
;
Opt_priority: /* none */ { $$ = ZN; }
| PRIORITY CONST { $$ = $2; has_priority++; }
;
full_expr: expr { $$ = $1; }
| Expr { $$ = $1; }
;
ltl_expr: expr UNTIL expr { $$ = nn(ZN, UNTIL, $1, $3); }
| expr RELEASE expr { $$ = nn(ZN, RELEASE, $1, $3); }
| expr WEAK_UNTIL expr { $$ = nn(ZN, ALWAYS, $1, ZN);
$$ = nn(ZN, OR, $$, nn(ZN, UNTIL, $1, $3));
}
| expr IMPLIES expr {
$$ = nn(ZN, '!', $1, ZN);
$$ = nn(ZN, OR, $$, $3);
}
| expr EQUIV expr { $$ = nn(ZN, EQUIV, $1, $3); }
| NEXT expr %prec NEG { $$ = nn(ZN, NEXT, $2, ZN); }
| ALWAYS expr %prec NEG { $$ = nn(ZN, ALWAYS,$2, ZN); }
| EVENTUALLY expr %prec NEG { $$ = nn(ZN, EVENTUALLY, $2, ZN); }
;
/* an Expr cannot be negated - to protect Probe expressions */
Expr : Probe { $$ = $1; }
| l_par Expr r_par { $$ = $2; }
| Expr AND Expr { $$ = nn(ZN, AND, $1, $3); }
| Expr AND expr { $$ = nn(ZN, AND, $1, $3); }
| expr AND Expr { $$ = nn(ZN, AND, $1, $3); }
| Expr OR Expr { $$ = nn(ZN, OR, $1, $3); }
| Expr OR expr { $$ = nn(ZN, OR, $1, $3); }
| expr OR Expr { $$ = nn(ZN, OR, $1, $3); }
;
Probe : FULL l_par varref r_par { $$ = nn($3, FULL, $3, ZN); }
| NFULL l_par varref r_par { $$ = nn($3, NFULL, $3, ZN); }
| EMPTY l_par varref r_par { $$ = nn($3, EMPTY, $3, ZN); }
| NEMPTY l_par varref r_par { $$ = nn($3,NEMPTY, $3, ZN); }
;
Opt_enabler: /* none */ { $$ = ZN; }
| PROVIDED l_par full_expr r_par { if (!proper_enabler($3))
{ non_fatal("invalid PROVIDED clause",
(char *)0);
$$ = ZN;
} else
$$ = $3;
}
| PROVIDED error { $$ = ZN;
non_fatal("usage: provided ( ..expr.. )",
(char *)0);
}
;
basetype: TYPE { $$->sym = ZS;
$$->val = $1->val;
if ($$->val == UNSIGNED)
fatal("unsigned cannot be used as mesg type", 0);
}
| UNAME { $$->sym = $1->sym;
$$->val = STRUCT;
}
| error /* e.g., unsigned ':' const */
;
typ_list: basetype { $$ = nn($1, $1->val, ZN, ZN); }
| basetype ',' typ_list { $$ = nn($1, $1->val, ZN, $3); }
;
two_args: expr ',' expr { $$ = nn(ZN, ',', $1, $3); }
;
args : /* empty */ { $$ = ZN; }
| arg { $$ = $1; }
;
prargs : /* empty */ { $$ = ZN; }
| ',' arg { $$ = $2; }
;
margs : arg { $$ = $1; }
| expr l_par arg r_par { if ($1->ntyp == ',')
$$ = tail_add($1, $3);
else
$$ = nn(ZN, ',', $1, $3);
}
;
arg : expr { if ($1->ntyp == ',')
$$ = $1;
else
$$ = nn(ZN, ',', $1, ZN);
}
| expr ',' arg { if ($1->ntyp == ',')
$$ = tail_add($1, $3);
else
$$ = nn(ZN, ',', $1, $3);
}
;
rarg : varref { $$ = $1; trackvar($1, $1);
trapwonly($1 /*, "rarg" */); }
| EVAL l_par expr r_par { $$ = nn(ZN,EVAL,$3,ZN);
trapwonly($1 /*, "eval rarg" */); }
| CONST { $$ = nn(ZN,CONST,ZN,ZN);
$$->ismtyp = $1->ismtyp;
$$->val = $1->val;
}
| '-' CONST %prec UMIN { $$ = nn(ZN,CONST,ZN,ZN);
$$->val = - ($2->val);
}
;
rargs : rarg { if ($1->ntyp == ',')
$$ = $1;
else
$$ = nn(ZN, ',', $1, ZN);
}
| rarg ',' rargs { if ($1->ntyp == ',')
$$ = tail_add($1, $3);
else
$$ = nn(ZN, ',', $1, $3);
}
| rarg l_par rargs r_par { if ($1->ntyp == ',')
$$ = tail_add($1, $3);
else
$$ = nn(ZN, ',', $1, $3);
}
| l_par rargs r_par { $$ = $2; }
;
nlst : NAME { $$ = nn($1, NAME, ZN, ZN);
$$ = nn(ZN, ',', $$, ZN); }
| nlst NAME { $$ = nn($2, NAME, ZN, ZN);
$$ = nn(ZN, ',', $$, $1);
}
| nlst ',' { $$ = $1; /* commas optional */ }
;
%%
#define binop(n, sop) fprintf(fd, "("); recursive(fd, n->lft); \
fprintf(fd, ") %s (", sop); recursive(fd, n->rgt); \
fprintf(fd, ")");
#define unop(n, sop) fprintf(fd, "%s (", sop); recursive(fd, n->lft); \
fprintf(fd, ")");
static void
recursive(FILE *fd, Lextok *n)
{
if (n)
switch (n->ntyp) {
case NEXT:
unop(n, "X");
break;
case ALWAYS:
unop(n, "[]");
break;
case EVENTUALLY:
unop(n, "<>");
break;
case '!':
unop(n, "!");
break;
case UNTIL:
binop(n, "U");
break;
case WEAK_UNTIL:
binop(n, "W");
break;
case RELEASE: /* see http://en.wikipedia.org/wiki/Linear_temporal_logic */
binop(n, "V");
break;
case OR:
binop(n, "||");
break;
case AND:
binop(n, "&&");
break;
case IMPLIES:
binop(n, "->");
break;
case EQUIV:
binop(n, "<->");
break;
case C_EXPR:
fprintf(fd, "c_expr { %s }", put_inline(fd, n->sym->name));
break;
default:
comment(fd, n, 0);
break;
}
}
static Lextok *
ltl_to_string(Lextok *n)
{ Lextok *m = nn(ZN, 0, ZN, ZN);
char *retval;
char ltl_formula[2048];
FILE *tf = fopen(TMP_FILE1, "w+"); /* tmpfile() fails on Windows 7 */
/* convert the parsed ltl to a string
by writing into a file, using existing functions,
and then passing it to the existing interface for
conversion into a never claim
(this means parsing everything twice, which is
a little redundant, but adds only miniscule overhead)
*/
if (!tf)
{ fatal("cannot create temporary file", (char *) 0);
}
dont_simplify = 1;
recursive(tf, n);
dont_simplify = 0;
(void) fseek(tf, 0L, SEEK_SET);
memset(ltl_formula, 0, sizeof(ltl_formula));
retval = fgets(ltl_formula, sizeof(ltl_formula), tf);
fclose(tf);
(void) unlink(TMP_FILE1);
if (!retval)
{ printf("%p\n", retval);
fatal("could not translate ltl ltl_formula", 0);
}
if (1) printf("ltl %s: %s\n", ltl_name, ltl_formula);
m->sym = lookup(ltl_formula);
return m;
}
int
is_temporal(int t)
{
return (t == EVENTUALLY || t == ALWAYS || t == UNTIL
|| t == WEAK_UNTIL || t == RELEASE);
}
int
is_boolean(int t)
{
return (t == AND || t == OR || t == IMPLIES || t == EQUIV);
}
#if 0
/* flags correct formula like: ltl { true U (true U true) } */
void
sanity_check(Lextok *t) /* check proper embedding of ltl_expr */
{
if (!t) return;
sanity_check(t->lft);
sanity_check(t->rgt);
if (t->lft && t->rgt)
{ if (!is_boolean(t->ntyp)
&& (is_temporal(t->lft->ntyp)
|| is_temporal(t->rgt->ntyp)))
{ printf("spin: attempt to apply '");
explain(t->ntyp);
printf("' to '");
explain(t->lft->ntyp);
printf("' and '");
explain(t->rgt->ntyp);
printf("'\n");
/* non_fatal("missing parentheses?", (char *)0); */
} }
}
#endif
void
yyerror(char *fmt, ...)
{
non_fatal(fmt, (char *) 0);
}