/* Miranda token declarations and syntax rules for "YACC" */ /************************************************************************** * Copyright (C) Research Software Limited 1985-90. All rights reserved. * * The Miranda system is distributed as free software under the terms in * * the file "COPYING" which is included in the distribution. * * * * Revised to C11 standard and made 64bit compatible, January 2020 * *------------------------------------------------------------------------*/ /* miranda symbols */ %token VALUE EVAL WHERE IF TO LEFTARROW COLONCOLON COLON2EQ TYPEVAR NAME CNAME CONST DOLLAR2 OFFSIDE ELSEQ ABSTYPE WITH DIAG EQEQ FREE INCLUDE EXPORT TYPE OTHERWISE SHOWSYM PATHNAME BNF LEX ENDIR ERRORSY ENDSY EMPTYSY READVALSY LEXDEF CHARCLASS ANTICHARCLASS LBEGIN %right ARROW %right PLUSPLUS ':' MINUSMINUS %nonassoc DOTDOT %right VEL %right '&' %nonassoc '>' GE '=' NE LE '<' %left '+' '-' %left '*' '/' REM DIV %right '^' %left '.' /* fiddle to make '#' behave */ %left '!' %right INFIXNAME INFIXCNAME %token CMBASE /* placeholder to start combinator values - see combs.h */ %{ /* the following definition has to be kept in line with the token declarations above */ char *yysterm[]= { 0, "VALUE", "EVAL", "where", "if", "&>", "<-", "::", "::=", "TYPEVAR", "NAME", "CONSTRUCTOR-NAME", "CONST", "$$", "OFFSIDE", "OFFSIDE =", "abstype", "with", "//", "==", "%free", "%include", "%export", "type", "otherwise", "show", "PATHNAME", "%bnf", "%lex", "%%", "error", "end", "empty", "readvals", "NAME", "`char-class`", "`char-class`", "%%begin", "->", "++", "--", "..", "\\/", ">=", "~=", "<=", "mod", "div", "$NAME", "$CONSTRUCTOR"}; %} /* Miranda syntax rules */ /* the associated semantic actions perform the compilation */ %{ #include "data.h" #include "big.h" #include "lex.h" extern word nill,k_i,Void; extern word message,standardout; extern word big_one; #define isltmess_t(t) (islist_t(t)&&tl[t]==message) #define isstring_t(t) (islist_t(t)&&tl[t]==char_t) extern word SYNERR,errs,echoing,gvars; extern word listdiff_fn,indent_fn,outdent_fn; word lastname=0; word suppressids=NIL; word idsused=NIL; word tvarscope=0; word includees=NIL,embargoes=NIL,exportfiles=NIL,freeids=NIL,exports=NIL; word lexdefs=NIL,lexstates=NIL,inlex=0,inexplist=0; word inbnf=0,col_fn=0,fnts=NIL,eprodnts=NIL,nonterminals=NIL,sreds=0; word ihlist=0,ntspecmap=NIL,ntmap=NIL,lasth=0; word obrct=0; void evaluate(x) word x; { word t; t=type_of(x); if(t==wrong_t)return; lastexp=x; x=codegen(x); if(polyshowerror)return; if(process()) /* setup new process for each evaluation */ { (void)signal(SIGINT,(sighandler)dieclean); /* if interrupted will flush output etc before going */ compiling=0; resetgcstats(); output(isltmess_t(t)?x: cons(ap(standardout,isstring_t(t)?x :ap(mkshow(0,0,t),x)),NIL)); (void)signal(SIGINT,SIG_IGN);/* otherwise could do outstats() twice */ putchar('\n'); outstats(); exit(0); } } void obey(x) /* like evaluate but no fork, no stats, no extra '\n' */ word x; { word t=type_of(x); x=codegen(x); if(polyshowerror)return; compiling=0; output(isltmess_t(t)?x: cons(ap(standardout,isstring_t(t)?x:ap(mkshow(0,0,t),x)),NIL)); } int isstring(x) word x; { return(x==NILS||tag[x]==CONS&&is_char(hd[x])); } word compose(x) /* used in compiling 'cases' */ word x; { word y=hd[x]; if(hd[y]==OTHERWISE)y=tl[y]; /* OTHERWISE was just a marker - lose it */ else y=tag[y]==LABEL?label(hd[y],ap(tl[y],FAIL)): ap(y,FAIL); /* if all guards false result is FAIL */ x = tl[x]; if(x!=NIL) { while(tl[x]!=NIL)y=label(hd[hd[x]],ap(tl[hd[x]],y)), x=tl[x]; y=ap(hd[x],y); /* first alternative has no label - label of enclosing rhs applies */ } return(y); } int eprod(word); word starts(x) /* x is grammar rhs - returns list of nonterminals in start set */ word x; { L: switch(tag[x]) { case ID: return(cons(x,NIL)); case LABEL: case LET: case LETREC: x=tl[x]; goto L; case AP: switch(hd[x]) { case G_SYMB: case G_SUCHTHAT: case G_RULE: return(NIL); case G_OPT: case G_FBSTAR: case G_STAR: x=tl[x]; goto L; default: if(hd[x]==outdent_fn) { x=tl[x]; goto L; } if(tag[hd[x]]==AP) if(hd[hd[x]]==G_ERROR) { x=tl[hd[x]]; goto L; } if(hd[hd[x]]==G_SEQ) { if(eprod(tl[hd[x]])) return(UNION(starts(tl[hd[x]]),starts(tl[x]))); x=tl[hd[x]]; goto L; } else if(hd[hd[x]]==G_ALT) return(UNION(starts(tl[hd[x]]),starts(tl[x]))); else if(hd[hd[x]]==indent_fn) { x=tl[x]; goto L; } } default: return(NIL); } } int eprod(x) /* x is grammar rhs - does x admit empty production? */ word x; { L: switch(tag[x]) { case ID: return(member(eprodnts,x)); case LABEL: case LET: case LETREC: x=tl[x]; goto L; case AP: switch(hd[x]) { case G_SUCHTHAT: case G_ANY: case G_SYMB: return(0); case G_RULE: return(1); case G_OPT: case G_FBSTAR: case G_STAR: return(1); default: if(hd[x]==outdent_fn) { x=tl[x]; goto L; } if(tag[hd[x]]==AP) if(hd[hd[x]]==G_ERROR) { x=tl[hd[x]]; goto L; } if(hd[hd[x]]==G_SEQ) return(eprod(tl[hd[x]])&&eprod(tl[x])); else if(hd[hd[x]]==G_ALT) return(eprod(tl[hd[x]])||eprod(tl[x])); else if(hd[hd[x]]==indent_fn) { x=tl[x]; goto L; } } default: return(x==G_STATE||x==G_UNIT); /* G_END is special case, unclear whether it counts as an e-prodn. decide no for now, sort this out later */ } } word add_prod(d,ps,hr) word d,ps,hr; { word p,n=dlhs(d); for(p=ps;p!=NIL;p=tl[p]) if(dlhs(hd[p])==n) if(dtyp(d)==undef_t&&dval(hd[p])==UNDEF) { dval(hd[p])=dval(d); return(ps); } else if(dtyp(d)!=undef_t&&dtyp(hd[p])==undef_t) { dtyp(hd[p])=dtyp(d); return(ps); } else errs=hr, printf( "%ssyntax error: conflicting %s of nonterminal \"%s\"\n", echoing?"\n":"", dtyp(d)==undef_t?"definitions":"specifications", get_id(n)), acterror(); return(cons(d,ps)); } /* clumsy - this algorithm is quadratic in number of prodns - fix later */ word getloc(nt,prods) /* get here info for nonterminal */ word nt,prods; { while(prods!=NIL&&dlhs(hd[prods])!=nt)prods=tl[prods]; if(prods!=NIL)return(hd[dval(hd[prods])]); return(0); /* should not happen, but just in case */ } void findnt(nt) /* set errs to here info of undefined nonterminal */ word nt; { word p=ntmap; while(p!=NIL&&hd[hd[p]]!=nt)p=tl[p]; if(p!=NIL) { errs=tl[hd[p]]; return; } p=ntspecmap; while(p!=NIL&&hd[hd[p]]!=nt)p=tl[p]; if(p!=NIL)errs=tl[hd[p]]; } #define isap2(fn,x) (tag[x]==AP&&tag[hd[x]]==AP&&hd[hd[x]]==(fn)) #define firstsymb(term) tl[hd[term]] void binom(rhs,x) /* performs the binomial optimisation on rhs of nonterminal x x: x alpha1| ... | x alphaN | rest ||need not be in this order ==> x: rest (alpha1|...|alphaN)* */ word rhs,x; { word *p= &tl[rhs]; /* rhs is of form label(hereinf, stuff) */ word *lastp=0,*holdrhs,suffix,alpha=NIL; if(tag[*p]==LETREC)p = &tl[*p]; /* ignore trailing `where defs' */ if(isap2(G_ERROR,*p))p = &tl[hd[*p]]; holdrhs=p; while(isap2(G_ALT,*p)) if(firstsymb(tl[hd[*p]])==x) alpha=cons(tl[tl[hd[*p]]],alpha), *p=tl[*p],p = &tl[*p]; else lastp=p,p = &tl[tl[*p]]; /* note each (G_ALT a b) except the outermost is labelled */ if(lastp&&firstsymb(*p)==x) alpha=cons(tl[*p],alpha), *lastp=tl[hd[*lastp]]; if(alpha==NIL)return; suffix=hd[alpha],alpha=tl[alpha]; while(alpha!=NIL) suffix=ap2(G_ALT,hd[alpha],suffix), alpha=tl[alpha]; *holdrhs=ap2(G_SEQ,*holdrhs,ap(G_FBSTAR,suffix)); } /* should put some labels on the alpha's - fix later */ word getcol_fn() { extern char *dicp,*dicq; if(!col_fn) strcpy(dicp,"bnftokenindentation"), dicq=dicp+20, col_fn=name(); return(col_fn); } void startbnf() { ntspecmap=ntmap=nonterminals=NIL; if(fnts==0)col_fn=0; /* reinitialise, a precaution */ } word ih_abstr(x) /* abstract inherited attributes from grammar rule */ word x; { word ih=ihlist; while(ih!=NIL) /* relies on fact that ihlist is reversed */ x=lambda(hd[ih],x),ih=tl[ih]; return(x); } int can_elide(x) /* is x of the form $1 applied to ih attributes in order? */ word x; { word ih; if(ihlist) for(ih=ihlist;ih!=NIL&&tag[x]==AP;ih=tl[ih],x=hd[x]) if(hd[ih]!=tl[x])return(0); return(x==mkgvar(1)); } int e_re(x) /* does regular expression x match empty string ? */ word x; { L: if(tag[x]==AP) { if(hd[x]==LEX_STAR||hd[x]==LEX_OPT)return(1); if(hd[x]==LEX_STRING)return(tl[x]==NIL); if(tag[hd[x]]!=AP)return(0); if(hd[hd[x]]==LEX_OR) { if(e_re(tl[hd[x]]))return(1); x=tl[x]; goto L; } else if(hd[hd[x]]==LEX_SEQ) { if(!e_re(tl[hd[x]]))return(0); x=tl[x]; goto L; } else if(hd[hd[x]]==LEX_RCONTEXT) { x=tl[hd[x]]; goto L; } } return(0); } %} %% entity: /* the entity to be parsed is either a definition script or an expression (the latter appearing as a command line) */ error| script = { lastname=0; /* outstats(); */ }| /* statistics not usually wanted after compilation */ /* MAGIC exp '\n' script = { lastexp=$2; }| /* change to magic scripts 19.11.2013 */ VALUE exp = { lastexp=$2; }| /* next line of `$+' */ EVAL exp = { if(!SYNERR&&yychar==0) { evaluate($2); } }| EVAL exp COLONCOLON /* boring problem - how to make sure no junk chars follow here? likewise TO case -- trick used above doesn't work, yychar is here always -1 Why? Too fiddly to bother with just now */ = { word t=type_of($2); if(t!=wrong_t) { lastexp=$2; if(tag[$2]==ID&&id_type($2)==wrong_t)t=wrong_t; out_type(t); putchar('\n'); } }| EVAL exp TO = { FILE *fil=NULL,*efil; word t=type_of($2); char *f=token(),*ef; if(f)keep(f); ef=token(); /* wasteful of dic space, FIX LATER */ if(f){ fil= fopen(f,$3?"a":"w"); if(fil==NULL) printf("cannot open \"%s\" for writing\n",f); } else printf("filename missing after \"&>\"\n"); if(ef) { efil= fopen(ef,$3?"a":"w"); if(efil==NULL) printf("cannot open \"%s\" for writing\n",ef); } if(t!=wrong_t)$2=codegen(lastexp=$2); if(!polyshowerror&&t!=wrong_t&&fil!=NULL&&(!ef||efil)) { int pid;/* launch a concurrent process to perform task */ sighandler oldsig; oldsig=signal(SIGINT,SIG_IGN); /* ignore interrupts */ if(pid=fork()) { /* "parent" */ if(pid==-1)perror("cannot create process"); else printf("process %d\n",pid); fclose(fil); if(ef)fclose(efil); (void)signal(SIGINT,oldsig); }else { /* "child" */ (void)signal(SIGQUIT,SIG_IGN); /* and quits */ #ifndef SYSTEM5 (void)signal(SIGTSTP,SIG_IGN); /* and stops */ #endif close(1); dup(fileno(fil)); /* subvert stdout */ close(2); dup(fileno(ef?efil:fil)); /* subvert stderr */ /* FUNNY BUG - if redirect stdout stderr to same file by two calls to freopen, their buffers get conflated - whence do by subverting underlying file descriptors, as above (fix due to Martin Guy) */ /* formerly used dup2, but not present in system V */ fclose(stdin); /* setbuf(stdout,NIL); /* not safe to change buffering of stream already in use */ /* freopen would have reset the buffering automatically */ lastexp = NIL; /* what else should we set to NIL? */ /*atcount= 1; */ compiling= 0; resetgcstats(); output(isltmess_t(t)?$2: cons(ap(standardout,isstring_t(t)?$2: ap(mkshow(0,0,t),$2)),NIL)); putchar('\n'); outstats(); exit(0); } } }; script: /* empty */| defs; exp: op | /* will later suppress in favour of (op) in arg */ e1; op: '~' = { $$ = NOT; }| '#' = { $$ = LENGTH; }| diop; diop: '-' = { $$ = MINUS; }| diop1; diop1: '+' = { $$ = PLUS; }| PLUSPLUS = { $$ = APPEND; }| ':' = { $$ = P; }| MINUSMINUS = { $$ = listdiff_fn; }| VEL = { $$ = OR; }| '&' = { $$ = AND; }| relop | '*' = { $$ = TIMES; }| '/' = { $$ = FDIV; }| DIV = { $$ = INTDIV; }| REM = { $$ = MOD; }| '^' = { $$ = POWER; }| '.' = { $$ = B; }| '!' = { $$ = ap(C,SUBSCRIPT); }| INFIXNAME| INFIXCNAME; relop: '>' = { $$ = GR; }| GE = { $$ = GRE; }| eqop = { $$ = EQ; }| NE = { $$ = NEQ; }| LE = { $$ = ap(C,GRE); }| '<' = { $$ = ap(C,GR); }; eqop: EQEQ| /* silently accept for benefit of Haskell users */ '='; rhs: cases WHERE ldefs = { $$ = block($3,compose($1),0); }| exp WHERE ldefs = { $$ = block($3,$1,0); }| exp| cases = { $$ = compose($1); }; cases: exp ',' if exp = { $$ = cons(ap2(COND,$4,$1),NIL); }| exp ',' OTHERWISE = { $$ = cons(ap(OTHERWISE,$1),NIL); }| cases reindent ELSEQ alt = { $$ = cons($4,$1); if(hd[hd[$1]]==OTHERWISE) syntax("\"otherwise\" must be last case\n"); }; alt: here exp = { errs=$1, syntax("obsolete syntax, \", otherwise\" missing\n"); $$ = ap(OTHERWISE,label($1,$2)); }| here exp ',' if exp = { $$ = label($1,ap2(COND,$5,$2)); }| here exp ',' OTHERWISE = { $$ = ap(OTHERWISE,label($1,$2)); }; if: /* empty */ = { extern word strictif; if(strictif)syntax("\"if\" missing\n"); }| IF; indent: /* empty */ = { if(!SYNERR){layout(); setlmargin();} }; /* note that because of yacc's one symbol look ahead, indent must usually be invoked one symbol earlier than the non-terminal to which it applies - see `production:' for an exception */ outdent: separator = { unsetlmargin(); }; separator: OFFSIDE | ';' ; reindent: /* empty */ = { if(!SYNERR) { unsetlmargin(); layout(); setlmargin(); } }; liste: /* NB - returns list in reverse order */ exp = { $$ = cons($1,NIL); }| liste ',' exp /* left recursive so as not to eat YACC stack */ = { $$ = cons($3,$1); }; e1: '~' e1 %prec '=' = { $$ = ap(NOT,$2); }| e1 PLUSPLUS e1 = { $$ = ap2(APPEND,$1,$3); }| e1 ':' e1 = { $$ = cons($1,$3); }| e1 MINUSMINUS e1 = { $$ = ap2(listdiff_fn,$1,$3); }| e1 VEL e1 = { $$ = ap2(OR,$1,$3); }| e1 '&' e1 = { $$ = ap2(AND,$1,$3); }| reln | e2; es1: /* e1 or presection */ '~' e1 %prec '=' = { $$ = ap(NOT,$2); }| e1 PLUSPLUS e1 = { $$ = ap2(APPEND,$1,$3); }| e1 PLUSPLUS = { $$ = ap(APPEND,$1); }| e1 ':' e1 = { $$ = cons($1,$3); }| e1 ':' = { $$ = ap(P,$1); }| e1 MINUSMINUS e1 = { $$ = ap2(listdiff_fn,$1,$3); }| e1 MINUSMINUS = { $$ = ap(listdiff_fn,$1); }| e1 VEL e1 = { $$ = ap2(OR,$1,$3); }| e1 VEL = { $$ = ap(OR,$1); }| e1 '&' e1 = { $$ = ap2(AND,$1,$3); }| e1 '&' = { $$ = ap(AND,$1); }| relsn | es2; e2: '-' e2 %prec '-' = { $$ = ap(NEG,$2); }| '#' e2 %prec '.' = { $$ = ap(LENGTH,$2); }| e2 '+' e2 = { $$ = ap2(PLUS,$1,$3); }| e2 '-' e2 = { $$ = ap2(MINUS,$1,$3); }| e2 '*' e2 = { $$ = ap2(TIMES,$1,$3); }| e2 '/' e2 = { $$ = ap2(FDIV,$1,$3); }| e2 DIV e2 = { $$ = ap2(INTDIV,$1,$3); } | e2 REM e2 = { $$ = ap2(MOD,$1,$3); }| e2 '^' e2 = { $$ = ap2(POWER,$1,$3); } | e2 '.' e2 = { $$ = ap2(B,$1,$3); }| e2 '!' e2 = { $$ = ap2(SUBSCRIPT,$3,$1); }| e3; es2: /* e2 or presection */ '-' e2 %prec '-' = { $$ = ap(NEG,$2); }| '#' e2 %prec '.' = { $$ = ap(LENGTH,$2); }| e2 '+' e2 = { $$ = ap2(PLUS,$1,$3); }| e2 '+' = { $$ = ap(PLUS,$1); }| e2 '-' e2 = { $$ = ap2(MINUS,$1,$3); }| e2 '-' = { $$ = ap(MINUS,$1); }| e2 '*' e2 = { $$ = ap2(TIMES,$1,$3); }| e2 '*' = { $$ = ap(TIMES,$1); }| e2 '/' e2 = { $$ = ap2(FDIV,$1,$3); }| e2 '/' = { $$ = ap(FDIV,$1); }| e2 DIV e2 = { $$ = ap2(INTDIV,$1,$3); } | e2 DIV = { $$ = ap(INTDIV,$1); } | e2 REM e2 = { $$ = ap2(MOD,$1,$3); }| e2 REM = { $$ = ap(MOD,$1); }| e2 '^' e2 = { $$ = ap2(POWER,$1,$3); } | e2 '^' = { $$ = ap(POWER,$1); } | e2 '.' e2 = { $$ = ap2(B,$1,$3); }| e2 '.' = { $$ = ap(B,$1); }| e2 '!' e2 = { $$ = ap2(SUBSCRIPT,$3,$1); }| e2 '!' = { $$ = ap2(C,SUBSCRIPT,$1); }| es3; e3: comb INFIXNAME e3 = { $$ = ap2($2,$1,$3); }| comb INFIXCNAME e3 = { $$ = ap2($2,$1,$3); }| comb; es3: /* e3 or presection */ comb INFIXNAME e3 = { $$ = ap2($2,$1,$3); }| comb INFIXNAME = { $$ = ap($2,$1); }| comb INFIXCNAME e3 = { $$ = ap2($2,$1,$3); }| comb INFIXCNAME = { $$ = ap($2,$1); }| comb; comb: comb arg = { $$ = ap($1,$2); }| arg; reln: e2 relop e2 = { $$ = ap2($2,$1,$3); }| reln relop e2 = { word subject; subject = hd[hd[$1]]==AND?tl[tl[$1]]:tl[$1]; $$ = ap2(AND,$1,ap2($2,subject,$3)); }; /* EFFICIENCY PROBLEM - subject gets re-evaluated (and retypechecked) - fix later */ relsn: /* reln or presection */ e2 relop e2 = { $$ = ap2($2,$1,$3); }| e2 relop = { $$ = ap($2,$1); }| reln relop e2 = { word subject; subject = hd[hd[$1]]==AND?tl[tl[$1]]:tl[$1]; $$ = ap2(AND,$1,ap2($2,subject,$3)); }; /* EFFICIENCY PROBLEM - subject gets re-evaluated (and retypechecked) - fix later */ arg: { if(!SYNERR)lexstates=NIL,inlex=1; } LEX lexrules ENDIR = { inlex=0; lexdefs=NIL; if(lexstates!=NIL) { word echoed=0; for(;lexstates!=NIL;lexstates=tl[lexstates]) { if(!echoed)printf(echoing?"\n":""),echoed=1; if(!(tl[hd[lexstates]]&1)) printf("warning: lex state %s is never entered\n", get_id(hd[hd[lexstates]])); else if(!(tl[hd[lexstates]]&2)) printf("warning: lex state %s has no associated rules\n", get_id(hd[hd[lexstates]])); } } if($3==NIL)syntax("%lex with no rules\n"); else tag[$3]=LEXER; /* result is lex-list, in reverse order, of items of the form cons(scstuff,cons(matcher,rhs)) where scstuff is of the form cons(0-or-list-of-startconditions,1+newstartcondition) */ $$ = $3; }| NAME | CNAME | CONST | READVALSY = { $$ = readvals(0,0); }| SHOWSYM = { $$ = show(0,0); }| DOLLAR2 = { $$ = lastexp; if(lastexp==UNDEF) syntax("no previous expression to substitute for $$\n"); }| '[' ']' = { $$ = NIL; }| '[' exp ']' = { $$ = cons($2,NIL); }| '[' exp ',' exp ']' = { $$ = cons($2,cons($4,NIL)); }| '[' exp ',' exp ',' liste ']' = { $$ = cons($2,cons($4,reverse($6))); }| '[' exp DOTDOT exp ']' = { $$ = ap3(STEPUNTIL,big_one,$4,$2); }| '[' exp DOTDOT ']' = { $$ = ap2(STEP,big_one,$2); }| '[' exp ',' exp DOTDOT exp ']' = { $$ = ap3(STEPUNTIL,ap2(MINUS,$4,$2),$6,$2); }| '[' exp ',' exp DOTDOT ']' = { $$ = ap2(STEP,ap2(MINUS,$4,$2),$2); }| '[' exp '|' qualifiers ']' = { $$ = SYNERR?NIL:compzf($2,$4,0); }| '[' exp DIAG qualifiers ']' = { $$ = SYNERR?NIL:compzf($2,$4,1); }| '(' op ')' /* RSB */ = { $$ = $2; }| '(' es1 ')' /* presection or parenthesised e1 */ = { $$ = $2; }| '(' diop1 e1 ')' /* postsection */ = { $$ = (tag[$2]==AP&&hd[$2]==C)?ap(tl[$2],$3): /* optimisation */ ap2(C,$2,$3); }| '(' ')' = { $$ = Void; }| /* the void tuple */ '(' exp ',' liste ')' = { if(tl[$4]==NIL)$$=pair($2,hd[$4]); else { $$=pair(hd[tl[$4]],hd[$4]); $4=tl[tl[$4]]; while($4!=NIL)$$=tcons(hd[$4],$$),$4=tl[$4]; $$ = tcons($2,$$); } /* representation of the tuple (a1,...,an) is tcons(a1,tcons(a2,...pair(a(n-1),an))) */ }; lexrules: lexrules lstart here re indent { if(!SYNERR)inlex=2; } ARROW exp lpostfix { if(!SYNERR)inlex=1; } outdent = { if($9<0 && e_re($4)) errs=$3, syntax("illegal lex rule - lhs matches empty\n"); $$ = cons(cons(cons($2,1+$9),cons($4,label($3,$8))),$1); }| lexdefs = { $$ = NIL; }; lstart: /* empty */ = { $$ = 0; }| '<' cnames '>' = { word ns=NIL; for(;$2!=NIL;$2=tl[$2]) { word *x = &lexstates,i=1; while(*x!=NIL&&hd[hd[*x]]!=hd[$2])i++,x = &tl[*x]; if(*x == NIL)*x = cons(cons(hd[$2],2),NIL); else tl[hd[*x]] |= 2; ns = add1(i,ns); } $$ = ns; }; cnames: CNAME = { $$=cons($1,NIL); }| cnames CNAME = { if(member($1,$2)) printf("%ssyntax error: repeated name \"%s\" in start conditions\n", echoing?"\n":"",get_id($2)), acterror(); $$ = cons($2,$1); }; lpostfix: /* empty */ = { $$ = -1; }| LBEGIN CNAME = { word *x = &lexstates,i=1; while(*x!=NIL&&hd[hd[*x]]!=$2)i++,x = &tl[*x]; if(*x == NIL)*x = cons(cons($2,1),NIL); else tl[hd[*x]] |= 1; $$ = i; }| LBEGIN CONST = { if(!isnat($2)||get_int($2)!=0) syntax("%begin not followed by IDENTIFIER or 0\n"); $$ = 0; }; lexdefs: lexdefs LEXDEF indent '=' re outdent = { lexdefs = cons(cons($2,$5),lexdefs); }| /* empty */ = { lexdefs = NIL; }; re: /* regular expression */ re1 '|' re { $$ = ap2(LEX_OR,$1,$3); }| re1; re1: lterm '/' lterm { $$ = ap2(LEX_RCONTEXT,$1,$3); }| lterm '/' { $$ = ap2(LEX_RCONTEXT,$1,0); }| lterm; lterm: lfac lterm { $$ = ap2(LEX_SEQ,$1,$2); }| lfac; lfac: lunit '*' { if(e_re($1)) syntax("illegal regular expression - arg of * matches empty\n"); $$ = ap(LEX_STAR,$1); }| lunit '+' { $$ = ap2(LEX_SEQ,$1,ap(LEX_STAR,$1)); }| lunit '?' { $$ = ap(LEX_OPT,$1); }| lunit; lunit: '(' re ')' = { $$ = $2; }| CONST = { if(!isstring($1)) printf("%ssyntax error - unexpected token \"", echoing?"\n":""), out(stdout,$1),printf("\" in regular expression\n"), acterror(); $$ = $1==NILS?ap(LEX_STRING,NIL): tl[$1]==NIL?ap(LEX_CHAR,hd[$1]): ap(LEX_STRING,$1); }| CHARCLASS = { if($1==NIL) syntax("empty character class `` cannot match\n"); $$ = tl[$1]==NIL?ap(LEX_CHAR,hd[$1]):ap(LEX_CLASS,$1); }| ANTICHARCLASS = { $$ = ap(LEX_CLASS,cons(ANTICHARCLASS,$1)); }| '.' = { $$ = LEX_DOT; }| name = { word x=lexdefs; while(x!=NIL&&hd[hd[x]]!=$1)x=tl[x]; if(x==NIL) printf( "%ssyntax error: undefined lexeme %s in regular expression\n", echoing?"\n":"", get_id($1)), acterror(); else $$ = tl[hd[x]]; }; name: NAME|CNAME; qualifiers: exp = { $$ = cons(cons(GUARD,$1),NIL); }| generator = { $$ = cons($1,NIL); }| qualifiers ';' generator = { $$ = cons($3,$1); }| qualifiers ';' exp = { $$ = cons(cons(GUARD,$3),$1); }; generator: e1 ',' generator = { /* fix syntax to disallow patlist on lhs of iterate generator */ if(hd[$3]==GENERATOR) { word e=tl[tl[$3]]; if(tag[e]==AP&&tag[hd[e]]==AP&& (hd[hd[e]]==ITERATE||hd[hd[e]]==ITERATE1)) syntax("ill-formed generator\n"); } $$ = cons(REPEAT,cons(genlhs($1),$3)); idsused=NIL; }| generator1; generator1: e1 LEFTARROW exp = { $$ = cons(GENERATOR,cons(genlhs($1),$3)); idsused=NIL; }| e1 LEFTARROW exp ',' exp DOTDOT = { word p = genlhs($1); idsused=NIL; $$ = cons(GENERATOR, cons(p,ap2(irrefutable(p)?ITERATE:ITERATE1, lambda(p,$5),$3))); }; defs: def| defs def; def: v act2 indent '=' here rhs outdent = { word l = $1, r = $6; word f = head(l); if(tag[f]==ID&&!isconstructor(f)) /* fnform defn */ while(tag[l]==AP)r=lambda(tl[l],r),l=hd[l]; r = label($5,r); /* to help locate type errors */ declare(l,r),lastname=l; }| spec = { word h=reverse(hd[$1]),hr=hd[tl[$1]],t=tl[tl[$1]]; while(h!=NIL&&!SYNERR)specify(hd[h],t,hr),h=tl[h]; $$ = cons(nill,NIL); }| ABSTYPE here typeforms indent WITH lspecs outdent = { extern word TABSTRS; extern char *dicp,*dicq; word x=reverse($6),ids=NIL,tids=NIL; while(x!=NIL&&!SYNERR) specify(hd[hd[x]],cons(tl[tl[hd[x]]],NIL),hd[tl[hd[x]]]), ids=cons(hd[hd[x]],ids),x=tl[x]; /* each id in specs has its id_type set to const(t,NIL) as a way of flagging that t is an abstract type */ x=reverse($3); while(x!=NIL&&!SYNERR) { word shfn; decltype(hd[x],abstract_t,undef_t,$2); tids=cons(head(hd[x]),tids); /* check for presence of showfunction */ (void)strcpy(dicp,"show"); (void)strcat(dicp,get_id(hd[tids])); dicq = dicp+strlen(dicp)+1; shfn=name(); if(member(ids,shfn)) t_showfn(hd[tids])=shfn; x=tl[x]; } TABSTRS = cons(cons(tids,ids),TABSTRS); $$ = cons(nill,NIL); }| typeform indent act1 here EQEQ type act2 outdent = { word x=redtvars(ap($1,$6)); decltype(hd[x],synonym_t,tl[x],$4); $$ = cons(nill,NIL); }| typeform indent act1 here COLON2EQ construction act2 outdent = { word rhs = $6, r_ids = $6, n=0; while(r_ids!=NIL)r_ids=tl[r_ids],n++; while(rhs!=NIL&&!SYNERR) { word h=hd[rhs],t=$1,stricts=NIL,i=0; while(tag[h]==AP) { if(tag[tl[h]]==AP&&hd[tl[h]]==strict_t) stricts=cons(i,stricts),tl[h]=tl[tl[h]]; t=ap2(arrow_t,tl[h],t),h=hd[h],i++; } if(tag[h]==ID) declconstr(h,--n,t); /* warning - type not yet in reduced form */ else { stricts=NIL; if(echoing)putchar('\n'); printf("syntax error: illegal construct \""); out_type(hd[rhs]); printf("\" on right of ::=\n"); acterror(); } /* can this still happen? check later */ if(stricts!=NIL) /* ! operators were present */ { word k = id_val(h); while(stricts!=NIL) k=ap2(MKSTRICT,i-hd[stricts],k), stricts=tl[stricts]; id_val(h)=k; /* overwrite id_val of original constructor */ } r_ids=cons(h,r_ids); rhs = tl[rhs]; } if(!SYNERR)decltype($1,algebraic_t,r_ids,$4); $$ = cons(nill,NIL); }| indent setexp EXPORT parts outdent = { inexplist=0; if(exports!=NIL) errs=$2, syntax("multiple %export statements are illegal\n"); else { if($4==NIL&&exportfiles==NIL&&embargoes!=NIL) exportfiles=cons(PLUS,NIL); exports=cons($2,$4); } /* cons(hereinfo,identifiers) */ $$ = cons(nill,NIL); }| FREE here '{' specs '}' = { if(freeids!=NIL) errs=$2, syntax("multiple %free statements are illegal\n"); else { word x=reverse($4); while(x!=NIL&&!SYNERR) { specify(hd[hd[x]],tl[tl[hd[x]]],hd[tl[hd[x]]]); freeids=cons(head(hd[hd[x]]),freeids); if(tl[tl[hd[x]]]==type_t) t_class(hd[freeids])=free_t; else id_val(hd[freeids])=FREE; /* conventional value */ x=tl[x]; } fil_share(hd[files])=0; /* parameterised scripts unshareable */ freeids=alfasort(freeids); for(x=freeids;x!=NIL;x=tl[x]) hd[x]=cons(hd[x],cons(datapair(get_id(hd[x]),0), id_type(hd[x]))); /* each element of freeids is of the form cons(id,cons(original_name,type)) */ } $$ = cons(nill,NIL); }| INCLUDE bindings modifiers outdent /* fiddle - 'indent' done by yylex() on reading fileid */ = { extern char *dicp; extern word CLASHES,BAD_DUMP; includees=cons(cons($1,cons($3,$2)),includees); /* $1 contains file+hereinfo */ $$ = cons(nill,NIL); }| here BNF { startbnf(); inbnf=1;} names outdent productions ENDIR /* fiddle - `indent' done by yylex() while processing directive */ = { word lhs=NIL,p=$6,subjects,body,startswith=NIL,leftrecs=NIL; ihlist=inbnf=0; nonterminals=UNION(nonterminals,$4); for(;p!=NIL;p=tl[p]) if(dval(hd[p])==UNDEF)nonterminals=add1(dlhs(hd[p]),nonterminals); else lhs=add1(dlhs(hd[p]),lhs); nonterminals=setdiff(nonterminals,lhs); if(nonterminals!=NIL) errs=$1, member($4,hd[nonterminals])/*||findnt(hd[nonterminals])*/, printf("%sfatal error in grammar, ",echoing?"\n":""), printf("undefined nonterminal%s: ", tl[nonterminals]==NIL?"":"s"), printlist("",nonterminals), acterror(); else { /* compute list of nonterminals admitting empty prodn */ eprodnts=NIL; L:for(p=$6;p!=NIL;p=tl[p]) if(!member(eprodnts,dlhs(hd[p]))&&eprod(dval(hd[p]))) { eprodnts=cons(dlhs(hd[p]),eprodnts); goto L; } /* now compute startswith reln between nonterminals (performing binomial transformation en route) and use to detect unremoved left recursion */ for(p=$6;p!=NIL;p=tl[p]) if(member(lhs=starts(dval(hd[p])),dlhs(hd[p]))) binom(dval(hd[p]),dlhs(hd[p])), startswith=cons(cons(dlhs(hd[p]),starts(dval(hd[p]))), startswith); else startswith=cons(cons(dlhs(hd[p]),lhs),startswith); startswith=tclos(sortrel(startswith)); for(;startswith!=NIL;startswith=tl[startswith]) if(member(tl[hd[startswith]],hd[hd[startswith]])) leftrecs=add1(hd[hd[startswith]],leftrecs); if(leftrecs!=NIL) errs=getloc(hd[leftrecs],$6), printf("%sfatal error in grammar, ",echoing?"\n":""), printlist("irremovable left recursion: ",leftrecs), acterror(); if($4==NIL) /* implied start symbol */ $4=cons(dlhs(hd[lastlink($6)]),NIL); fnts=1; /* fnts is flag indicating %bnf in use */ if(tl[$4]==NIL) /* only one start symbol */ subjects=getfname(hd[$4]), body=ap2(G_CLOSE,str_conv(get_id(hd[$4])),hd[$4]); else { body=subjects=Void; while($4!=NIL) subjects=pair(getfname(hd[$4]),subjects), body=pair( ap2(G_CLOSE,str_conv(get_id(hd[$4])),hd[$4]), body), $4=tl[$4]; } declare(subjects,label($1,block($6,body, 0))); }}; setexp: here = { $$=$1; inexplist=1; }; /* hack to fix lex analyser */ bindings: /* empty */ = { $$ = NIL; }| '{' bindingseq '}' = { $$ = $2; }; bindingseq: bindingseq binding = { $$ = cons($2,$1); }| binding = { $$ = cons($1,NIL); }; binding: NAME indent '=' exp outdent = { $$ = cons($1,$4); }| typeform indent act1 EQEQ type act2 outdent = { word x=redtvars(ap($1,$5)); word arity=0,h=hd[x]; while(tag[h]==AP)arity++,h=hd[h]; $$ = ap(h,make_typ(arity,0,synonym_t,tl[x])); }; modifiers: /* empty */ = { $$ = NIL; }| negmods = { word a,b,c=0; for(a=$1;a!=NIL;a=tl[a]) for(b=tl[a];b!=NIL;b=tl[b]) { if(hd[hd[a]]==hd[hd[b]])c=hd[hd[a]]; if(tl[hd[a]]==tl[hd[b]])c=tl[hd[a]]; if(c)break; } if(c)printf( "%ssyntax error: conflicting aliases (\"%s\")\n", echoing?"\n":"", get_id(c)), acterror(); }; negmods: negmods negmod = { $$ = cons($2,$1); }| negmod = { $$ = cons($1,NIL); }; negmod: NAME '/' NAME = { $$ = cons($1,$3); }| CNAME '/' CNAME = { $$ = cons($1,$3); }| '-' NAME = { $$ = cons(make_pn(UNDEF),$2); }/*| '-' CNAME */; /* no - cannot suppress constructors selectively */ here: /* empty */ = { extern word line_no; lasth = $$ = fileinfo(get_fil(current_file),line_no); /* (script,line_no) for diagnostics */ }; act1: /* empty */ = { tvarscope=1; }; act2: /* empty */ = { tvarscope=0; idsused= NIL; }; ldefs: ldef = { $$ = cons($1,NIL); dval($1) = tries(dlhs($1),cons(dval($1),NIL)); if(!SYNERR&&get_ids(dlhs($1))==NIL) errs=hd[hd[tl[dval($1)]]], syntax("illegal lhs for local definition\n"); }| ldefs ldef = { if(dlhs($2)==dlhs(hd[$1]) /*&&dval(hd[$1])!=UNDEF*/) { $$ = $1; if(!fallible(hd[tl[dval(hd[$1])]])) errs=hd[dval($2)], printf("%ssyntax error: \ unreachable case in defn of \"%s\"\n",echoing?"\n":"",get_id(dlhs($2))), acterror(); tl[dval(hd[$1])]=cons(dval($2),tl[dval(hd[$1])]); } else if(!SYNERR) { word ns=get_ids(dlhs($2)),hr=hd[dval($2)]; if(ns==NIL) errs=hr, syntax("illegal lhs for local definition\n"); $$ = cons($2,$1); dval($2)=tries(dlhs($2),cons(dval($2),NIL)); while(ns!=NIL&&!SYNERR) /* local nameclash check */ { nclashcheck(hd[ns],$1,hr); ns=tl[ns]; } /* potentially quadratic - fix later */ } }; ldef: spec = { errs=hd[tl[$1]]; syntax("`::' encountered in local defs\n"); $$ = cons(nill,NIL); }| typeform here EQEQ = { errs=$2; syntax("`==' encountered in local defs\n"); $$ = cons(nill,NIL); }| typeform here COLON2EQ = { errs=$2; syntax("`::=' encountered in local defs\n"); $$ = cons(nill,NIL); }| v act2 indent '=' here rhs outdent = { word l = $1, r = $6; word f = head(l); if(tag[f]==ID&&!isconstructor(f)) /* fnform defn */ while(tag[l]==AP)r=lambda(tl[l],r),l=hd[l]; r = label($5,r); /* to help locate type errors */ $$ = defn(l,undef_t,r); }; vlist: v = { $$ = cons($1,NIL); }| vlist ',' v /* left recursive so as not to eat YACC stack */ = { $$ = cons($3,$1); }; /* reverse order, NB */ v: v1 | v1 ':' v = { $$ = cons($1,$3); }; v1: v1 '+' CONST /* n+k pattern */ = { if(!isnat($3)) syntax("inappropriate use of \"+\" in pattern\n"); $$ = ap2(PLUS,$3,$1); }| '-' CONST = { /* if(tag[$2]==DOUBLE) $$ = cons(CONST,sto_dbl(-get_dbl($2))); else */ if(tag[$2]==INT) $$ = cons(CONST,bignegate($2)); else syntax("inappropriate use of \"-\" in pattern\n"); }| v2 INFIXNAME v1 = { $$ = ap2($2,$1,$3); }| v2 INFIXCNAME v1 = { $$ = ap2($2,$1,$3); }| v2; v2: v3 | v2 v3 = { $$ = ap(hd[$1]==CONST&&tag[tl[$1]]==ID?tl[$1]:$1,$2); }; /* repeated name apparatus may have wrapped CONST around leading id - not wanted */ v3: NAME = { if(sreds&&member(gvars,$1))syntax("illegal use of $num symbol\n"); /* cannot use grammar variable in a binding position */ if(memb(idsused,$1))$$ = cons(CONST,$1); /* picks up repeated names in a template */ else idsused= cons($1,idsused); } | CNAME | CONST = { if(tag[$1]==DOUBLE) syntax("use of floating point literal in pattern\n"); $$ = cons(CONST,$1); }| '[' ']' = { $$ = nill; }| '[' vlist ']' = { word x=$2,y=nill; while(x!=NIL)y = cons(hd[x],y), x = tl[x]; $$ = y; }| '(' ')' = { $$ = Void; }| '(' v ')' = { $$ = $2; }| '(' v ',' vlist ')' = { if(tl[$4]==NIL)$$=pair($2,hd[$4]); else { $$=pair(hd[tl[$4]],hd[$4]); $4=tl[tl[$4]]; while($4!=NIL)$$=tcons(hd[$4],$$),$4=tl[$4]; $$ = tcons($2,$$); } /* representation of the tuple (a1,...,an) is tcons(a1,tcons(a2,...pair(a(n-1),an))) */ }; type: type1 | type ARROW type = { $$ = ap2(arrow_t,$1,$3); }; type1: type2 INFIXNAME type1 = { $$ = ap2($2,$1,$3); }| type2; type2: /* type2 argtype /* too permissive - fix later */ /* = { $$ = ap($1,$2); }| */ tap| argtype; tap: NAME argtype = { $$ = ap($1,$2); }| tap argtype = { $$ = ap($1,$2); }; argtype: NAME = { $$ = transtypeid($1); }| /* necessary while prelude not meta_tchecked (for prelude)*/ typevar = { if(tvarscope&&!memb(idsused,$1)) printf("%ssyntax error: unbound type variable ",echoing?"\n":""), out_type($1),putchar('\n'),acterror(); $$ = $1; }| '(' typelist ')' = { $$ = $2; }| '[' type ']' /* at release one was `typelist' */ = { $$ = ap(list_t,$2); }| '[' type ',' typel ']' = { syntax( "tuple-type with missing parentheses (obsolete syntax)\n"); }; typelist: /* empty */ = { $$ = void_t; }| /* voidtype */ type | type ',' typel = { word x=$3,y=void_t; while(x!=NIL)y = ap2(comma_t,hd[x],y), x = tl[x]; $$ = ap2(comma_t,$1,y); }; typel: type = { $$ = cons($1,NIL); }| typel ',' type /* left recursive so as not to eat YACC stack */ = { $$ = cons($3,$1); }; parts: /* returned in reverse order */ parts NAME = { $$ = add1($2,$1); }| parts '-' NAME = { $$ = $1; embargoes=add1($3,embargoes); }| parts PATHNAME = { $$ = $1; }| /*the pathnames are placed on exportfiles in yylex*/ parts '+' = { $$ = $1; exportfiles=cons(PLUS,exportfiles); }| NAME = { $$ = add1($1,NIL); }| '-' NAME = { $$ = NIL; embargoes=add1($2,embargoes); }| PATHNAME = { $$ = NIL; }| '+' = { $$ = NIL; exportfiles=cons(PLUS,exportfiles); }; specs: /* returns a list of cons(id,cons(here,type)) in reverse order of appearance */ specs spec = { word x=$1,h=hd[$2],t=tl[$2]; while(h!=NIL)x=cons(cons(hd[h],t),x),h=tl[h]; $$ = x; }| spec = { word x=NIL,h=hd[$1],t=tl[$1]; while(h!=NIL)x=cons(cons(hd[h],t),x),h=tl[h]; $$ = x; }; spec: typeforms indent here COLONCOLON ttype outdent = { $$ = cons($1,cons($3,$5)); }; /* hack: `typeforms' includes `namelist' */ lspecs: /* returns a list of cons(id,cons(here,type)) in reverse order of appearance */ lspecs lspec = { word x=$1,h=hd[$2],t=tl[$2]; while(h!=NIL)x=cons(cons(hd[h],t),x),h=tl[h]; $$ = x; }| lspec = { word x=NIL,h=hd[$1],t=tl[$1]; while(h!=NIL)x=cons(cons(hd[h],t),x),h=tl[h]; $$ = x; }; lspec: namelist indent here {inbnf=0;} COLONCOLON type outdent = { $$ = cons($1,cons($3,$6)); }; namelist: NAME ',' namelist = { $$ = cons($1,$3); }| NAME = { $$ = cons($1,NIL); }; typeforms: typeforms ',' typeform act2 = { $$ = cons($3,$1); }| typeform act2 = { $$ = cons($1,NIL); }; typeform: CNAME typevars = { syntax("upper case identifier out of context\n"); }| NAME typevars /* warning if typevar is repeated */ = { $$ = $1; idsused=$2; while($2!=NIL) $$ = ap($$,hd[$2]),$2 = tl[$2]; }| typevar INFIXNAME typevar = { if(eqtvar($1,$3)) syntax("repeated type variable in typeform\n"); idsused=cons($1,cons($3,NIL)); $$ = ap2($2,$1,$3); }| typevar INFIXCNAME typevar = { syntax("upper case identifier cannot be used as typename\n"); }; ttype: type| TYPE = { $$ = type_t; }; typevar: '*' = { $$ = mktvar(1); }| TYPEVAR; typevars: /* empty */ = { $$ = NIL; }| typevar typevars = { if(memb($2,$1)) syntax("repeated type variable on lhs of type def\n"); $$ = cons($1,$2); }; construction: constructs = { extern word SGC; /* keeps track of sui-generis constructors */ if( tl[$1]==NIL && tag[hd[$1]]!=ID ) /* 2nd conjunct excludes singularity types */ SGC=cons(head(hd[$1]),SGC); }; constructs: construct = { $$ = cons($1,NIL); }| constructs '|' construct = { $$ = cons($3,$1); }; construct: field here INFIXCNAME field = { $$ = ap2($3,$1,$4); id_who($3)=$2; }| construct1; construct1: '(' construct ')' = { $$ = $2; }| construct1 field1 = { $$ = ap($1,$2); }| here CNAME = { $$ = $2; id_who($2)=$1; }; field: type| argtype '!' = { $$ = ap(strict_t,$1); }; field1: argtype '!' = { $$ = ap(strict_t,$1); }| argtype; names: /* used twice - for bnf list, and for inherited attr list */ /* empty */ = { $$ = NIL; }| names NAME = { if(member($1,$2)) printf("%ssyntax error: repeated identifier \"%s\" in %s list\n", echoing?"\n":"",get_id($2),inbnf?"bnf":"attribute"), acterror(); $$ = inbnf?add1($2,$1):cons($2,$1); }; productions: lspec = { word h=reverse(hd[$1]),hr=hd[tl[$1]],t=tl[tl[$1]]; inbnf=1; $$=NIL; while(h!=NIL&&!SYNERR) ntspecmap=cons(cons(hd[h],hr),ntspecmap), $$=add_prod(defn(hd[h],t,UNDEF),$$,hr), h=tl[h]; }| production = { $$ = cons($1,NIL); }| productions lspec = { word h=reverse(hd[$2]),hr=hd[tl[$2]],t=tl[tl[$2]]; inbnf=1; $$=$1; while(h!=NIL&&!SYNERR) ntspecmap=cons(cons(hd[h],hr),ntspecmap), $$=add_prod(defn(hd[h],t,UNDEF),$$,hr), h=tl[h]; }| productions production = { $$ = add_prod($2,$1,hd[dval($2)]); }; production: NAME params ':' indent grhs outdent /* found by experiment that indent must follow ':' here */ = { $$ = defn($1,undef_t,$5); }; params: /* places inherited attributes, if any, on ihlist */ /* empty */ = { ihlist=0; }| { inbnf=0; } '(' names ')' = { inbnf=1; if($3==NIL)syntax("unexpected token ')'\n"); ihlist=$3; } grhs: here phrase = { $$ = label($1,$2); }; phrase: error_term = { $$ = ap2(G_ERROR,G_ZERO,$1); }| phrase1 = { $$=hd[$1], $1=tl[$1]; while($1!=NIL) $$=label(hd[$1],$$),$1=tl[$1], $$=ap2(G_ALT,hd[$1],$$),$1=tl[$1]; }| phrase1 '|' error_term = { $$=hd[$1], $1=tl[$1]; while($1!=NIL) $$=label(hd[$1],$$),$1=tl[$1], $$=ap2(G_ALT,hd[$1],$$),$1=tl[$1]; $$ = ap2(G_ERROR,$$,$3); }; /* we right rotate G_ALT's to facilitate left factoring (see trans) */ phrase1: term = { $$=cons($1,NIL); }| phrase1 '|' here term = { $$ = cons($4,cons($3,$1)); }; term: count_factors = { word n=0,f=$1,rule=Void; /* default value of a production is () */ /* rule=mkgvar(sreds); /* formerly last symbol */ if(f!=NIL&&hd[f]==G_END)sreds++; if(ihlist)rule=ih_abstr(rule); while(n0?"unmatched { in grammar rule\n": "unmatched } in grammar rule\n"); for(sreds=0;f!=NIL;f=tl[f])sreds++; if(hd[$2]==G_END)sreds--; $$ = $2; }; factors: factor = { $$ = cons($1,NIL); }| factors factor = { if(hd[$1]==G_END) syntax("unexpected token after end\n"); $$ = cons($2,$1); }; factor: unit| '{' unit '}' = { $$ = ap(outdent_fn,ap2(indent_fn,getcol_fn(),$2)); }| '{' unit = { obrct++; $$ = ap2(indent_fn,getcol_fn(),$2); }| unit '}' = { if(--obrct<0)syntax("unmatched `}' in grammar rule\n"); $$ = ap(outdent_fn,$1); } ; unit: symbol| symbol '*' = { $$ = ap(G_STAR,$1); }| symbol '+' = { $$ = ap2(G_SEQ,$1,ap2(G_SEQ,ap(G_STAR,$1),ap(G_RULE,ap(C,P)))); }| symbol '?' = { $$ = ap(G_OPT,$1); }; symbol: NAME = { extern word NEW; nonterminals=newadd1($1,nonterminals); if(NEW)ntmap=cons(cons($1,lasth),ntmap); }| ENDSY = { $$ = G_END; }| CONST = { if(!isstring($1)) printf("%ssyntax error: illegal terminal ",echoing?"\n":""), out(stdout,$1),printf(" (should be string-const)\n"), acterror(); $$ = ap(G_SYMB,$1); }| '^' = { $$=G_STATE; }| {inbnf=0;} '[' exp {inbnf=1;} ']' = { $$ = ap(G_SUCHTHAT,$3); }| '-' = { $$ = G_ANY; }; %% /* end of Miranda rules */