diff --git a/manifest b/manifest index 32f1c93969..450cd009b7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Work\saround\sa\sbug\sin\sMSVC++.\s\sTicket\s#1513.\s(CVS\s2763) -D 2005-11-05T15:11:23 +C About\s0.5KiB\sof\sadditional\scompression\sin\sthe\sparser\stables.\s(CVS\s2764) +D 2005-11-06T04:06:59 F Makefile.in 12784cdce5ffc8dfb707300c34e4f1eb3b8a14f1 F Makefile.linux-gcc aee18d8a05546dcf1888bd4547e442008a49a092 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -59,7 +59,7 @@ F src/os_win.c fbccc85e7011174068c27d54256746321a1f0059 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b F src/pager.c ca23cdff9e67a8e826e4796c60f06db6aab69b72 F src/pager.h e7b41ce8e7b5f629d456708b7ad9a8c8ede37140 -F src/parse.y 1d5afb972ed5ef3ec8712a2b5953b48f086091da +F src/parse.y 6322f4b2ef60b041232215663535bc83c4dcf8b7 F src/pragma.c b40189967155a522433b8470f363192a927ba22c F src/prepare.c fc098db25d2a121affb08686cf04833fd50452d4 F src/printf.c 3ea3a17d25d7ac498efc18007c70371a42c968f8 @@ -249,7 +249,7 @@ F test/view.test ce0f0ad39fa4a3572acffcf1e634850ee151aae0 F test/where.test 752728413eab42e5854690d84c7319cdf7edc515 F test/where2.test 503e2e2b6abe14c5c10222e72d08ef84c1bf1ffb F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b -F tool/lemon.c 0cedabac11734ec2a40286552de4af01cc859f6e +F tool/lemon.c 26d271a753ef87fe1e6194f53c594ab5e6783d85 F tool/lempar.c 424df14a48736bb961ed47acf30c26d66ed85a62 F tool/memleak.awk 4e7690a51bf3ed757e611273d43fe3f65b510133 F tool/memleak2.awk 9cc20c8e8f3c675efac71ea0721ee6874a1566e8 @@ -317,7 +317,7 @@ F www/tclsqlite.tcl ddcf912ea48695603c8ed7efb29f0812ef8d1b49 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P e66289b52f56c8242aa264a9365c834cd820e988 -R 9199262fb53b49fec9604daa08dac2c4 +P 6331860e7754be6e0d2a484d66427947c0781dd6 +R 89df35f460d2e4c3a14757990021ac40 U drh -Z b5fb95f8a7e272d62cd13eee39061a7a +Z dd438883ac1053c2a2f713e5ad3d2f5b diff --git a/manifest.uuid b/manifest.uuid index 9a5248b802..f1b934d518 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6331860e7754be6e0d2a484d66427947c0781dd6 \ No newline at end of file +f39974ebd81f274dc4cf6cf94e6e87ee7b4a0814 \ No newline at end of file diff --git a/src/parse.y b/src/parse.y index b7a7e4518a..d565c78aa7 100644 --- a/src/parse.y +++ b/src/parse.y @@ -14,7 +14,7 @@ ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** -** @(#) $Id: parse.y,v 1.182 2005/11/03 02:03:13 drh Exp $ +** @(#) $Id: parse.y,v 1.183 2005/11/06 04:06:59 drh Exp $ */ // All token codes are small integers with #defines that begin with "TK_" @@ -179,7 +179,7 @@ id(A) ::= ID(X). {A = X;} %ifdef SQLITE_OMIT_COMPOUND_SELECT EXCEPT INTERSECT UNION %endif - REINDEX RENAME CTIME_KW ALTER + REINDEX RENAME CTIME_KW . // Define operator precedence early so that this is the first occurance @@ -208,8 +208,7 @@ id(A) ::= ID(X). {A = X;} // And "ids" is an identifer-or-string. // %type ids {Token} -ids(A) ::= ID(X). {A = X;} -ids(A) ::= STRING(X). {A = X;} +ids(A) ::= ID|STRING(X). {A = X;} // The name of a column or table can be any of the following: // @@ -381,10 +380,9 @@ select(A) ::= select(X) multiselect_op(Y) oneselect(Z). { A = Z; } %type multiselect_op {int} -multiselect_op(A) ::= UNION(OP). {A = @OP;} -multiselect_op(A) ::= UNION ALL. {A = TK_ALL;} -multiselect_op(A) ::= INTERSECT(OP). {A = @OP;} -multiselect_op(A) ::= EXCEPT(OP). {A = @OP;} +multiselect_op(A) ::= UNION(OP). {A = @OP;} +multiselect_op(A) ::= UNION ALL. {A = TK_ALL;} +multiselect_op(A) ::= EXCEPT|INTERSECT(OP). {A = @OP;} %endif // SQLITE_OMIT_COMPOUND_SELECT oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y) groupby_opt(P) having_opt(Q) orderby_opt(Z) limit_opt(L). { @@ -501,8 +499,7 @@ fullname(A) ::= nm(X) dbnm(Y). {A = sqlite3SrcListAppend(0,&X,&Y);} %type joinop {int} %type joinop2 {int} -joinop(X) ::= COMMA. { X = JT_INNER; } -joinop(X) ::= JOIN. { X = JT_INNER; } +joinop(X) ::= COMMA|JOIN. { X = JT_INNER; } joinop(X) ::= JOIN_KW(A) JOIN. { X = sqlite3JoinType(pParse,&A,0,0); } joinop(X) ::= JOIN_KW(A) nm(B) JOIN. { X = sqlite3JoinType(pParse,&A,&B,0); } joinop(X) ::= JOIN_KW(A) nm(B) nm(C) JOIN. @@ -645,10 +642,8 @@ expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). { Expr *temp4 = sqlite3Expr(TK_DOT, temp2, temp3, 0); A = sqlite3Expr(TK_DOT, temp1, temp4, 0); } -term(A) ::= INTEGER(X). {A = sqlite3Expr(@X, 0, 0, &X);} -term(A) ::= FLOAT(X). {A = sqlite3Expr(@X, 0, 0, &X);} +term(A) ::= INTEGER|FLOAT|BLOB(X). {A = sqlite3Expr(@X, 0, 0, &X);} term(A) ::= STRING(X). {A = sqlite3Expr(@X, 0, 0, &X);} -term(A) ::= BLOB(X). {A = sqlite3Expr(@X, 0, 0, &X);} expr(A) ::= REGISTER(X). {A = sqlite3RegisterExpr(pParse, &X);} expr(A) ::= VARIABLE(X). { Token *pToken = &X; @@ -678,24 +673,15 @@ term(A) ::= CTIME_KW(OP). { A = sqlite3ExprFunction(0,&OP); if( A ) A->op = TK_CONST_FUNC; } -expr(A) ::= expr(X) AND(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) OR(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) LT(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) GT(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) LE(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) GE(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) NE(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) EQ(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) BITAND(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) BITOR(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) LSHIFT(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) RSHIFT(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) PLUS(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) MINUS(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) STAR(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) SLASH(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) REM(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} -expr(A) ::= expr(X) CONCAT(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) AND(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) OR(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) LT|GT|GE|LE(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) EQ|NE(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) BITAND|BITOR|LSHIFT|RSHIFT(OP) expr(Y). + {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) PLUS|MINUS(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) STAR|SLASH|REM(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} +expr(A) ::= expr(X) CONCAT(OP) expr(Y). {A = sqlite3Expr(@OP, X, Y, 0);} %type likeop {struct LikeOp} likeop(A) ::= LIKE_KW(X). {A.operator = X; A.not = 0;} likeop(A) ::= NOT LIKE_KW(X). {A.operator = X; A.not = 1;} @@ -713,18 +699,14 @@ expr(A) ::= expr(X) likeop(OP) expr(Y) escape(E). [LIKE_KW] { sqlite3ExprSpan(A, &X->span, &Y->span); } -expr(A) ::= expr(X) ISNULL(E). { - A = sqlite3Expr(TK_ISNULL, X, 0, 0); +expr(A) ::= expr(X) ISNULL|NOTNULL(E). { + A = sqlite3Expr(@E, X, 0, 0); sqlite3ExprSpan(A,&X->span,&E); } expr(A) ::= expr(X) IS NULL(E). { A = sqlite3Expr(TK_ISNULL, X, 0, 0); sqlite3ExprSpan(A,&X->span,&E); } -expr(A) ::= expr(X) NOTNULL(E). { - A = sqlite3Expr(TK_NOTNULL, X, 0, 0); - sqlite3ExprSpan(A,&X->span,&E); -} expr(A) ::= expr(X) NOT NULL(E). { A = sqlite3Expr(TK_NOTNULL, X, 0, 0); sqlite3ExprSpan(A,&X->span,&E); @@ -733,11 +715,7 @@ expr(A) ::= expr(X) IS NOT NULL(E). { A = sqlite3Expr(TK_NOTNULL, X, 0, 0); sqlite3ExprSpan(A,&X->span,&E); } -expr(A) ::= NOT(B) expr(X). { - A = sqlite3Expr(@B, X, 0, 0); - sqlite3ExprSpan(A,&B,&X->span); -} -expr(A) ::= BITNOT(B) expr(X). { +expr(A) ::= NOT|BITNOT(B) expr(X). { A = sqlite3Expr(@B, X, 0, 0); sqlite3ExprSpan(A,&B,&X->span); } @@ -920,8 +898,7 @@ cmd ::= PRAGMA nm(X) dbnm(Z). {sqlite3Pragma(pParse,&X,&Z,0,0);} %endif // SQLITE_OMIT_PRAGMA plus_num(A) ::= plus_opt number(X). {A = X;} minus_num(A) ::= MINUS number(X). {A = X;} -number(A) ::= INTEGER(X). {A = X;} -number(A) ::= FLOAT(X). {A = X;} +number(A) ::= INTEGER|FLOAT(X). {A = X;} plus_opt ::= PLUS. plus_opt ::= . @@ -951,8 +928,7 @@ trigger_time(A) ::= . { A = TK_BEFORE; } %type trigger_event {struct TrigEvent} %destructor trigger_event {sqlite3IdListDelete($$.b);} -trigger_event(A) ::= DELETE(OP). {A.a = @OP; A.b = 0;} -trigger_event(A) ::= INSERT(OP). {A.a = @OP; A.b = 0;} +trigger_event(A) ::= DELETE|INSERT(OP). {A.a = @OP; A.b = 0;} trigger_event(A) ::= UPDATE(OP). {A.a = @OP; A.b = 0;} trigger_event(A) ::= UPDATE OF inscollist(X). {A.a = TK_UPDATE; A.b = X;} diff --git a/tool/lemon.c b/tool/lemon.c index 6cfd2c0bff..b5a877c0d2 100644 --- a/tool/lemon.c +++ b/tool/lemon.c @@ -120,7 +120,8 @@ struct symbol { int index; /* Index number for this symbol */ enum { TERMINAL, - NONTERMINAL + NONTERMINAL, + MULTITERMINAL } type; /* Symbols are all either TERMINALS or NTs */ struct rule *rule; /* Linked list of rules of this (if an NT) */ struct symbol *fallback; /* fallback token in case this token doesn't parse */ @@ -141,6 +142,9 @@ struct symbol { int dtnum; /* The data type number. In the parser, the value ** stack is a union. The .yy%d element of this ** union is the correct data type for this object */ + /* The following fields are used by MULTITERMINALs only */ + int nsubsym; /* Number of constituent symbols in the MULTI */ + struct symbol **subsym; /* Array of constituent symbols */ }; /* Each production rule in the grammar is stored in the following @@ -585,11 +589,18 @@ struct lemon *xp; struct rule *rp; for(rp=xp->rule; rp; rp=rp->next){ if( rp->precsym==0 ){ - int i; - for(i=0; inrhs; i++){ - if( rp->rhs[i]->prec>=0 ){ + int i, j; + for(i=0; inrhs && rp->precsym==0; i++){ + struct symbol *sp = rp->rhs[i]; + if( sp->type==MULTITERMINAL ){ + for(j=0; jnsubsym; j++){ + if( sp->subsym[j]->prec>=0 ){ + rp->precsym = sp->subsym[j]; + break; + } + } + }else if( sp->prec>=0 ){ rp->precsym = rp->rhs[i]; - break; } } } @@ -605,7 +616,7 @@ struct lemon *xp; void FindFirstSets(lemp) struct lemon *lemp; { - int i; + int i, j; struct rule *rp; int progress; @@ -622,7 +633,8 @@ struct lemon *lemp; for(rp=lemp->rule; rp; rp=rp->next){ if( rp->lhs->lambda ) continue; for(i=0; inrhs; i++){ - if( rp->rhs[i]->lambda==B_FALSE ) break; + struct symbol *sp = rp->rhs[i]; + if( sp->type!=TERMINAL || sp->lambda==B_FALSE ) break; } if( i==rp->nrhs ){ rp->lhs->lambda = B_TRUE; @@ -642,6 +654,11 @@ struct lemon *lemp; if( s2->type==TERMINAL ){ progress += SetAdd(s1->firstset,s2->index); break; + }else if( s2->type==MULTITERMINAL ){ + for(j=0; jnsubsym; j++){ + progress += SetAdd(s1->firstset,s2->subsym[j]->index); + } + break; }else if( s1==s2 ){ if( s1->lambda==B_FALSE ) break; }else{ @@ -688,7 +705,7 @@ symbol instead.",lemp->start,lemp->rule->lhs->name); for(rp=lemp->rule; rp; rp=rp->next){ int i; for(i=0; inrhs; i++){ - if( rp->rhs[i]==sp ){ + if( rp->rhs[i]==sp ){ /* FIX ME: Deal with multiterminals */ ErrorMsg(lemp->filename,0, "The start symbol \"%s\" occurs on the \ right-hand side of a rule. This will result in a parser which \ @@ -760,6 +777,24 @@ struct lemon *lemp; return stp; } +/* +** Return true if two symbols are the same. +*/ +int same_symbol(a,b) +struct symbol *a; +struct symbol *b; +{ + int i; + if( a==b ) return 1; + if( a->type!=MULTITERMINAL ) return 0; + if( b->type!=MULTITERMINAL ) return 0; + if( a->nsubsym!=b->nsubsym ) return 0; + for(i=0; insubsym; i++){ + if( a->subsym[i]!=b->subsym[i] ) return 0; + } + return 1; +} + /* Construct all successor states to the given state. A "successor" ** state is any state which can be reached by a shift action. */ @@ -792,7 +827,7 @@ struct state *stp; /* The state from which successors are computed */ if( bcfp->status==COMPLETE ) continue; /* Already used */ if( bcfp->dot>=bcfp->rp->nrhs ) continue; /* Can't shift this one */ bsp = bcfp->rp->rhs[bcfp->dot]; /* Get symbol after dot */ - if( bsp!=sp ) continue; /* Must be same as for "cfp" */ + if( !same_symbol(bsp,sp) ) continue; /* Must be same as for "cfp" */ bcfp->status = COMPLETE; /* Mark this config as used */ new = Configlist_addbasis(bcfp->rp,bcfp->dot+1); Plink_add(&new->bplp,bcfp); @@ -804,7 +839,14 @@ struct state *stp; /* The state from which successors are computed */ /* The state "newstp" is reached from the state "stp" by a shift action ** on the symbol "sp" */ - Action_add(&stp->ap,SHIFT,sp,(char *)newstp); + if( sp->type==MULTITERMINAL ){ + int i; + for(i=0; insubsym; i++){ + Action_add(&stp->ap,SHIFT,sp->subsym[i],(char*)newstp); + } + }else{ + Action_add(&stp->ap,SHIFT,sp,(char *)newstp); + } } } @@ -1167,6 +1209,12 @@ struct lemon *lemp; if( xsp->type==TERMINAL ){ SetAdd(newcfp->fws,xsp->index); break; + }else if( xsp->type==MULTITERMINAL ){ + int k; + for(k=0; knsubsym; k++){ + SetAdd(newcfp->fws, xsp->subsym[k]->index); + } + break; }else{ SetUnion(newcfp->fws,xsp->firstset); if( xsp->lambda==B_FALSE ) break; @@ -2091,7 +2139,7 @@ to follow the previous rule."); }else if( isalpha(x[0]) ){ if( psp->nrhs>=MAXRHS ){ ErrorMsg(psp->filename,psp->tokenlineno, - "Too many symbol on RHS or rule beginning at \"%s\".", + "Too many symbols on RHS or rule beginning at \"%s\".", x); psp->errorcnt++; psp->state = RESYNC_AFTER_RULE_ERROR; @@ -2100,6 +2148,27 @@ to follow the previous rule."); psp->alias[psp->nrhs] = 0; psp->nrhs++; } + }else if( (x[0]=='|' || x[0]=='/') && psp->nrhs>0 ){ + struct symbol *msp = psp->rhs[psp->nrhs-1]; + if( msp->type!=MULTITERMINAL ){ + struct symbol *origsp = msp; + msp = malloc(sizeof(*msp)); + memset(msp, 0, sizeof(*msp)); + msp->type = MULTITERMINAL; + msp->nsubsym = 1; + msp->subsym = malloc(sizeof(struct symbol*)); + msp->subsym[0] = origsp; + msp->name = origsp->name; + psp->rhs[psp->nrhs-1] = msp; + } + msp->nsubsym++; + msp->subsym = realloc(msp->subsym, sizeof(struct symbol*)*msp->nsubsym); + msp->subsym[msp->nsubsym-1] = Symbol_new(&x[1]); + if( islower(x[1]) || islower(msp->subsym[0]->name[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Cannot form a compound containing a non-terminal"); + psp->errorcnt++; + } }else if( x[0]=='(' && psp->nrhs>0 ){ psp->state = RHS_ALIAS_1; }else{ @@ -2487,6 +2556,10 @@ struct lemon *gp; }else if( c==':' && cp[1]==':' && cp[2]=='=' ){ /* The operator "::=" */ cp += 3; nextcp = cp; + }else if( (c=='/' || c=='|') && isalpha(cp[1]) ){ + cp += 2; + while( (c = *cp)!=0 && (isalnum(c) || c=='_') ) cp++; + nextcp = cp; }else{ /* All other (one character) operators */ cp++; nextcp = cp; @@ -2646,15 +2719,21 @@ struct lemon *lemp; } for(rp=lemp->rule; rp; rp=rp->next){ printf("%s",rp->lhs->name); -/* if( rp->lhsalias ) printf("(%s)",rp->lhsalias); */ + /* if( rp->lhsalias ) printf("(%s)",rp->lhsalias); */ printf(" ::="); for(i=0; inrhs; i++){ - printf(" %s",rp->rhs[i]->name); -/* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */ + sp = rp->rhs[i]; + printf(" %s", sp->name); + if( sp->type==MULTITERMINAL ){ + for(j=1; jnsubsym; j++){ + printf("|%s", sp->subsym[j]->name); + } + } + /* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */ } printf("."); if( rp->precsym ) printf(" [%s]",rp->precsym->name); -/* if( rp->code ) printf("\n %s",rp->code); */ + /* if( rp->code ) printf("\n %s",rp->code); */ printf("\n"); } } @@ -2664,18 +2743,25 @@ FILE *fp; struct config *cfp; { struct rule *rp; - int i; + struct symbol *sp; + int i, j; rp = cfp->rp; fprintf(fp,"%s ::=",rp->lhs->name); for(i=0; i<=rp->nrhs; i++){ if( i==cfp->dot ) fprintf(fp," *"); if( i==rp->nrhs ) break; - fprintf(fp," %s",rp->rhs[i]->name); + sp = rp->rhs[i]; + fprintf(fp," %s", sp->name); + if( sp->type==MULTITERMINAL ){ + for(j=1; jnsubsym; j++){ + fprintf(fp,"|%s",sp->subsym[j]->name); + } + } } } /* #define TEST */ -#ifdef TEST +#if 0 /* Print a set */ PRIVATE void SetPrint(out,set,lemp) FILE *out; @@ -2769,7 +2855,7 @@ struct lemon *lemp; } ConfigPrint(fp,cfp); fprintf(fp,"\n"); -#ifdef TEST +#if 0 SetPrint(fp,cfp->fws,lemp); PlinkPrint(fp,cfp->fplp,"To "); PlinkPrint(fp,cfp->bplp,"From"); @@ -3113,8 +3199,14 @@ PRIVATE void translate_code(struct lemon *lemp, struct rule *rp){ ** the token number of X, not the value of X */ append_str("yymsp[%d].major",-1,i-rp->nrhs+1,0); }else{ - append_str("yymsp[%d].minor.yy%d",0, - i-rp->nrhs+1,rp->rhs[i]->dtnum); + struct symbol *sp = rp->rhs[i]; + int dtnum; + if( sp->type==MULTITERMINAL ){ + dtnum = sp->subsym[0]->dtnum; + }else{ + dtnum = sp->dtnum; + } + append_str("yymsp[%d].minor.yy%d",0,i-rp->nrhs+1, dtnum); } cp = xp; used[i] = 1; @@ -3636,7 +3728,16 @@ int mhflag; /* Output in makeheaders format if true */ for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){ assert( rp->index==i ); fprintf(out," /* %3d */ \"%s ::=", i, rp->lhs->name); - for(j=0; jnrhs; j++) fprintf(out," %s",rp->rhs[j]->name); + for(j=0; jnrhs; j++){ + struct symbol *sp = rp->rhs[j]; + fprintf(out," %s", sp->name); + if( sp->type==MULTITERMINAL ){ + int k; + for(k=1; knsubsym; k++){ + fprintf(out,"|%s",sp->subsym[k]->name); + } + } + } fprintf(out,"\",\n"); lineno++; } tplt_xfer(lemp->name,in,out,&lineno);