Rev 2 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
%{/** R : A Computer Langage for Statistical Data Analysis* Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, write to the Free Software* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#include "Defn.h"extern SEXP listAppend(SEXP,SEXP);void pushCmt();void popCmt();static int eatln;extern SEXP R_CommentSxp;#define YYSTYPE SEXP#ifdef YYBYACC#define YYRETURN(x) { return(x); }#else#define YYRETURN(x) { free((void*)yys); free((void*)yyv); return(x); }#endif%}%token STR_CONST NUM_CONST NULL_CONST SYMBOL FUNCTION LEX_ERROR%token LBB ERROR%token LEFT_ASSIGN RIGHT_ASSIGN%token FOR IN IF ELSE WHILE NEXT BREAK REPEAT%token GT GE LT LE EQ NE AND OR%left '?'%left LOW WHILE FOR REPEAT%right IF%left ELSE%right LEFT_ASSIGN%left RIGHT_ASSIGN%nonassoc '~' TILDE%left OR%left AND%left UNOT NOT%nonassoc GT GE LT LE EQ NE%left '+' '-'%left '*' '/' '%'%left SPECIAL%left ':'%left UMINUS UPLUS%right '^'%left '$'%nonassoc '(' '[' LBB%%prog: { newline = 0; }| prog '\n' { R_CurrentExpr = NULL; return 2; }| prog expr '\n' { R_CurrentExpr = $2; UNPROTECT(1); YYRETURN(3); }| prog expr ';' { R_CurrentExpr = $2; UNPROTECT(1); YYRETURN(4); }| prog error { YYABORT; };expr: NUM_CONST { $$ = $1; }| STR_CONST { $$ = $1; }| NULL_CONST { $$ = $1; }| SYMBOL { $$ = $1; }| '{' exprlist '}' { UNPROTECT(1); TYPEOF($2) = LANGSXP; CAR($2) = $1; $$ = $2; PROTECT($$); eatln = 0; }| '(' expr ')' { UNPROTECT(1); $$ = lang2($1, $2); PROTECT($$); }| '-' expr %prec UMINUS { UNPROTECT(1); $$ = lang2($1, $2); PROTECT($$); }| '+' expr %prec UMINUS { UNPROTECT(1); $$ = lang2($1, $2); PROTECT($$); }| '!' expr %prec UNOT { UNPROTECT(1); $$ = lang2($1, $2); PROTECT($$); }| '~' expr %prec TILDE { UNPROTECT(1); $$ = lang2($1, $2); PROTECT($$); }| '?' expr { UNPROTECT(1); $$ = lang2($1, $2); PROTECT($$); }| expr ':' expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr '+' expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr '-' expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr '*' expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr '/' expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr '^' expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr SPECIAL expr { UNPROTECT(3); $$ = lang3($2, $1, $3); PROTECT($$); }| expr '%' expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr '~' expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr LT expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr LE expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr EQ expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr NE expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr GE expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr GT expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr AND expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr OR expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr LEFT_ASSIGN expr { UNPROTECT(2); $$ = lang3($2, $1, $3); PROTECT($$); }| expr RIGHT_ASSIGN expr { UNPROTECT(2); $$ = lang3($2, $3, $1); PROTECT($$); }| FUNCTION '(' formlist ')' gobble expr %prec LOW{ addcomment($6); UNPROTECT(2); $$ = lang3($1, CDR($3), $6); PROTECT($$); popCmt();}| expr '(' sublist ')' { if(isString($1)) $1=install(CHAR(STRING($1)[0])); UNPROTECT(2); if(length(CDR($3)) == 1 && CADR($3) == R_MissingArg )$$ = lang1($1);else$$ = LCONS($1, CDR($3));PROTECT($$); }| IF ifcond expr { UNPROTECT(2); $$ = lang3($1, $2, $3); PROTECT($$); }| IF ifcond expr ELSE expr { UNPROTECT(3); $$ = lang4($1, $2, $3, $5); PROTECT($$); }| FOR forcond expr %prec FOR { UNPROTECT(2); $$ = lang4($1, CAR($2), CDR($2), $3); PROTECT($$); }| WHILE cond expr { UNPROTECT(2); $$ = lang3($1, $2, $3); PROTECT($$); }| REPEAT expr { UNPROTECT(1); $$ = lang2($1, $2); PROTECT($$); }| expr LBB sublist ']' ']' { UNPROTECT(2); $$ = LCONS($2, LCONS($1, CDR($3))); PROTECT($$); }| expr '[' sublist ']' { UNPROTECT(2); $$ = LCONS($2, LCONS($1, CDR($3))); PROTECT($$); }| expr '$' SYMBOL { $$ = lang3($2, $1, $3); UNPROTECT(2); PROTECT($$); }| expr '$' STR_CONST { $$ = lang3($2, $1, $3); UNPROTECT(2); PROTECT($$); }| NEXT { $$ = lang1($1); PROTECT($$); }| BREAK { $$ = lang1($1); PROTECT($$); };cond: '(' expr ')' { $$ = $2; eatln = 1; };ifcond: '(' expr ')' { $$ = $2; ifpush(); eatln = 1; };forcond: '(' SYMBOL IN expr ')' { UNPROTECT(2); $$ = LCONS($2,$4); PROTECT($$); eatln=1;};exprlist: { $$ = newlist(); PROTECT($$); }| expr { addcomment($1); UNPROTECT(1); $$ = growlist(newlist(), $1); PROTECT($$);}| exprlist ';' expr { addcomment($3); UNPROTECT(2); $$ = growlist($1, $3); PROTECT($$);}| exprlist ';' { $$ = $1; addcomment(CAR($$));}| exprlist '\n' expr { addcomment($3); UNPROTECT(2); $$ = growlist($1, $3); PROTECT($$);}| exprlist '\n' { $$ = $1;};sublist: sub { UNPROTECT(1); $$ = firstarg(CAR($1),CADR($1)); PROTECT($$); }| sublist gobble ',' sub { UNPROTECT(2); $$ = nextarg($1, CAR($4), CADR($4)); PROTECT($$); };sub: { $$ = lang2(R_MissingArg,R_NilValue); PROTECT($$); }| expr { UNPROTECT(1); $$ = tagarg($1, R_NilValue); PROTECT($$); }| SYMBOL '=' expr { UNPROTECT(2); $$ = tagarg($3, $1); PROTECT($$); }| SYMBOL '=' { UNPROTECT(1); $$ = tagarg(R_MissingArg, $1); PROTECT($$); }| STR_CONST '=' expr { UNPROTECT(2); $$ = tagarg($3, $1); PROTECT($$); }| STR_CONST '=' { UNPROTECT(1); $$ = tagarg(R_MissingArg, $1); PROTECT($$); }| NULL_CONST '=' expr { UNPROTECT(2); $$ = tagarg($3, install("NULL")); PROTECT($$); }| NULL_CONST '=' { UNPROTECT(1); $$ = tagarg(R_MissingArg, install("NULL")); PROTECT($$); };formlist: { $$ = R_NilValue; PROTECT($$); }| SYMBOL { UNPROTECT(1); $$ = firstarg(R_MissingArg, $1); PROTECT($$); }| SYMBOL '=' expr { UNPROTECT(2); $$ = firstarg($3, $1); PROTECT($$); }| formlist ',' SYMBOL { UNPROTECT(2); check_formals($1,$3); $$ = nextarg($1, R_MissingArg, $3); PROTECT($$); }| formlist ',' SYMBOL '=' expr { UNPROTECT(3); check_formals($1,$3); $$ = nextarg($1, $5, $3); PROTECT($$); };gobble: {eatln = 1;};%%SEXP tagarg(SEXP arg, SEXP tag){switch (TYPEOF(tag)) {case NILSXP:case SYMSXP:case STRSXP:return lang2(arg, tag);default:error("incorrect tag type\n");}}/* Lists are created and grown using a special dotted pair. *//* The CAR of the list points to the last cons-cell in the list *//* and the CDR points to the first. The list can be extracted *//* from the pair by taking its CDR, while the CAR gives fast access *//* to the end of the list. *//* Create a stretchy-list dotted pair */SEXP newlist(void){SEXP s = CONS(R_NilValue, R_NilValue);CAR(s) = s;return s;}/* Add a new element at the end of a stretchy list */SEXP growlist(SEXP l, SEXP s){SEXP tmp;PROTECT(l);tmp = CONS(s, R_NilValue);UNPROTECT(1);SETCDR(CAR(l), tmp);CAR(l) = tmp;return l;}/* Comment Handling *//* R_CommentSxp is of the same form as an expression list, *//* each time a new { is encountered a new element is placed *//* in the R_CommentSxp and when a } is encountered it is *//* removed. */extern void ResetComment(){R_CommentSxp = CONS(R_NilValue, R_NilValue);}void pushCmt(){R_CommentSxp = CONS(R_NilValue, R_CommentSxp);}void popCmt(){R_CommentSxp = CDR(R_CommentSxp);}int isComment(SEXP l){if (isList(l) && isString(CAR(l)) && !strncmp(CHAR(STRING(CAR(l))[0]), "#", 1))return 1;elsereturn 0;}void addcomment(SEXP l){SEXP tcmt, cmt;int i, ncmt;tcmt = CAR(R_CommentSxp);/* Return if there are no comments */if (tcmt == R_NilValue || l == R_NilValue)return;/* Attach the comments as a comment attribute */ncmt = length(tcmt);cmt = allocVector(STRSXP, ncmt);for(i=0 ; i<ncmt ; i++) {STRING(cmt)[i] = CAR(tcmt);tcmt = CDR(tcmt);}PROTECT(cmt);setAttrib(l, R_CommentSymbol, cmt);UNPROTECT(1);/* Reset the comment accumulator */CAR(R_CommentSxp) = R_NilValue;}SEXP firstarg(SEXP s, SEXP tag){SEXP tmp;PROTECT(s);PROTECT(tag);tmp = newlist();tmp = growlist(tmp, s);TAG(CAR(tmp)) = tag;UNPROTECT(2);return tmp;}SEXP nextarg(SEXP l, SEXP s, SEXP tag){PROTECT(tag);l = growlist(l, s);TAG(CAR(l)) = tag;UNPROTECT(1);return l;}SEXP mkString(char *);SEXP mkInteger(char *);SEXP mkFloat(char *);SEXP mkComplex(char *);SEXP mkNA(void);SEXP mkTrue(void);SEXP mkFalse(void);/*// Basic File IO://// This code is here because at this particular instant it// seems closely related to cget(), which appears below.*/int R_fgetc(FILE *fp){int c = fgetc(fp);return feof(fp) ? R_EOF : c;}static char *buf; /* The input stream buffer */static char *bufp; /* Pointer within current buffer */static int cnt = 0; /* Pointer to character count */static char buf1[MAXELTSIZE]; /* File or text buffer */static char buf0[MAXELTSIZE]; /* Console buffer */static char *bufp0; /* Pointer within the console buffer */static int cnt0 = 0; /* Characters in the console buffer */static current_input;/*// Set the input stream for the parser// input = 0 initialize// input = 1 console// input = 2 file// input = 3 text*/void R_SetInput(int input){switch (input) {case 0: /* Initialization / Reset */cnt = cnt0 = 0;buf = buf0;bufp = buf0;break;case 1: /* Restore console values */cnt = cnt0;buf = buf0;bufp = bufp0;R_Console = 1;break;case 2: /* Text or file input */case 3:if(R_Console == 1) {cnt0 = cnt;bufp0 = bufp;}cnt = 0;buf = buf1;bufp = buf1;R_Console = 0;break;}current_input = input;}/*// Fetch a single character from the current input stream.// The stream has been set by a call to R_SetInput().*/int cget(){if (--cnt < 0) {switch(current_input) {case 1:if (ReadKBD(buf, MAXELTSIZE) == 0) {ClearerrConsole();return R_EOF;}break;case 2:if (fgets(buf, MAXELTSIZE, R_Inputfile) == NULL) {ResetConsole();return R_EOF;}break;case 3:if (R_ParseCnt < LENGTH(R_ParseText)) {strcpy(buf, CHAR(STRING(R_ParseText)[(R_ParseCnt)]));strcat(buf, "\n");}else return R_EOF;break;}R_ParseCnt++;bufp = buf;cnt = strlen(buf);cnt--;}return *bufp++;}/*// Push n characters back onto the input stream.// This is only called when the characters are// currently in the input buffer so pushing back// beyond the start of the buffer is impossible.*/void uncget(int n){cnt += n;bufp -= n;}/*// Lexical Analyzer://// Basic lexical analysis is performed by the following// routines. Input is read a line at a time, and, if the// program is in batch mode, each input line is echoed to// standard output after it is read.//// The function yylex() scans the input, breaking it into// tokens which are then passed to the parser. The lexical// analyser maintains a symbol table (in a very messy fashion).//// The fact that if statements need to parse differently// depending on whether the statement is being interpreted or// part of the body of a function causes the need for ifpop// and ifpush. When an if statement is encountered an 'i' is// pushed on a stack (provided there are parentheses active).// At later points this 'i' needs to be popped off of the if// stack.*/static int newline = 0;static int reset = 1;#ifndef DEBUG_LEXstatic#endifchar *parenp, parenstack[50];static void ifpush(void){if (*parenp == '{' || *parenp == '[' || *parenp == '(' || *parenp == 'i')*++parenp = 'i';}static void ifpop(void){if (*parenp == 'i')parenp--;}static int typeofnext(void){int k, c;c = cget();if (isdigit(c)) k = 1;else if (isalpha(c) || c == '.')k = 2;elsek = 3;uncget(1);return k;}static int nextchar(int expect){int c = cget();if (c == expect)return 1;elseuncget(1);return 0;}/* Special Symbols *//* Syntactic Keywords + Symbolic Constants */struct {char *name;int token;} keywords[] = {{ "NULL", NULL_CONST },{ "NA", NUM_CONST },{ "TRUE", NUM_CONST },{ "FALSE", NUM_CONST },{ "GLOBAL.ENV", NUM_CONST },{ "function", FUNCTION },{ "while", WHILE },{ "repeat", REPEAT },{ "for", FOR },{ "if", IF },{ "in", IN },{ "else", ELSE },{ "next", NEXT },{ "break", BREAK },{ "...", SYMBOL },{ 0, 0 }};/* klookup has side effects, it sets yylval */int klookup(s)char *s;{int i;for (i = 0; keywords[i].name; i++) {if (strcmp(keywords[i].name, s) == 0) {switch (keywords[i].token) {case NULL_CONST:PROTECT(yylval = R_NilValue);eatln = 0;break;case NUM_CONST:switch(i) {case 1:PROTECT(yylval = mkNA());break;case 2:PROTECT(yylval = mkTrue());break;case 3:PROTECT(yylval = mkFalse());break;case 4:PROTECT(yylval = R_GlobalEnv);}eatln = 0;break;case FUNCTION:case WHILE:case REPEAT:case FOR:case IF:eatln = 1;yylval = install(s);break;case IN:eatln = 1;break;case ELSE:ifpop();eatln = 1;break;case NEXT:case BREAK:eatln = 0;yylval = install(s);break;case SYMBOL:PROTECT(yylval = install(s));eatln = 0;break;}return keywords[i].token;}}return 0;}static void prompt(){if (R_ParseText == R_NilValue && R_Console == 1)yyprompt(CHAR(STRING(GetOption(install("continue"), R_NilValue))[0]));}SEXP mkString(char *s){SEXP t;PROTECT(t = allocVector(STRSXP, 1));STRING(t)[0] = mkChar(s);UNPROTECT(1);return t;}SEXP mkFloat(char *s){SEXP t = allocVector(REALSXP, 1);REAL(t)[0] = atof(s);return t;}SEXP mkComplex(char *s){SEXP t = allocVector(CPLXSXP, 1);COMPLEX(t)[0].r = 0;COMPLEX(t)[0].i = atof(s);return t;}SEXP mkNA(void){SEXP t = allocVector(LGLSXP, 1);LOGICAL(t)[0] = NA_LOGICAL;return t;}SEXP mkTrue(void){SEXP s = allocVector(LGLSXP, 1);LOGICAL(s)[0] = 1;return s;}SEXP mkFalse(void){SEXP s = allocVector(LGLSXP, 1);LOGICAL(s)[0] = 0;return s;}void yyinit(void){newline = 0;reset = 1;}int yywrap(){return feof(R_Inputfile);}#ifdef HAVE_LIBREADLINEextern char R_prompt_buf[512];#endifvoid yyprompt(char *format, ...){va_list(ap);va_start(ap, format);#ifdef HAVE_LIBREADLINEvsprintf(R_prompt_buf, format, ap);#elseREvprintf(format, ap);#endifva_end(ap);fflush(stdout);RBusy(0);}void yyerror(char *s){int i;R_CommentSxp = R_NilValue;REprintf("%s", buf);for (i = 1; i < bufp - buf; i++) {REprintf(" ");}REprintf("^\n");if (R_Console == 0) {fclose(R_Inputfile);ResetConsole();}else {FlushConsole();REprintf("Error: %s\n", s);}newline = 0;reset = 1;cnt = 0;}void check_formals(SEXP formlist, SEXP new){int i;while( formlist != R_NilValue ) {if(TAG(formlist) == new ) {REprintf("%s", buf);for (i = 2; i < bufp - buf; i++)REprintf(" ");REprintf("^\n");newline = 0;reset = 1;cnt = 0;error("Repeated formal argument.\n");}formlist=CDR(formlist);}}int yylex(){SEXP f;int c, quote, kw;char *p, yytext[MAXELTSIZE];if (newline) {newline = 0;prompt();}again:if (reset) {parenp = parenstack;*parenp = ' ';reset = 0;eatln = 0;ResetComment();}while ((c = cget()) == ' ' || c == '\t' || c == '');if (c == '#') {p = yytext;*p++ = c;while ((c = cget()) != '\n' && c != R_EOF)*p++ = c;*p = '\0';if(R_CommentSxp != R_NilValue) {f = mkChar(yytext);f = CONS(f, R_NilValue);CAR(R_CommentSxp) = listAppend(CAR(R_CommentSxp), f);}}if (c == R_EOF) {return EOF;}/* This code deals with context sensitivity to *//* newlines. The main problem is finding out *//* whether a newline is followed by an ELSE clause. *//* This is only of importance if we are inside one *//* of "(", "[", or "{". */if (c == '\n') {if (eatln || *parenp == '[' || *parenp == '(') {prompt();goto again;}if (*parenp == 'i') {prompt();while ((c = cget()) == ' ' || c == '\t');if (c == R_EOF) {error("unexpected end-of-file in parse\n");}if (c == '#') {p = yytext;*p++ = c;while ((c = cget()) != '\n' && c != R_EOF)*p++ = c;*p = '\0';if(R_CommentSxp != R_NilValue) {f = mkChar(yytext);f = CONS(f, R_NilValue);CAR(R_CommentSxp) = listAppend(CAR(R_CommentSxp), f);}}if (c == '\n') {prompt();uncget(1);goto again;}if (c == '}' || c == ')' || c == ']' ) {while (*parenp == 'i')ifpop();parenp--;return c;}if (c == ',') {ifpop();return c;}uncget(1);if (!strncmp(bufp, "else", 4) && !isalnum(bufp[4]) && bufp[4] != '.') {eatln = 1;bufp += 4;cnt -= 4;ifpop();return ELSE;}ifpop();}else newline = 1;return '\n';}/* These are needed because both ";" and "," can *//* end an "if" clause without a newline. Ifpop *//* only does its thing in the right context. */if (c == ';' || c == ',') {ifpop();return c;}/* Either digits or symbols can start with a "." *//* so we need to decide which it is and jump to *//* the correct spot. */if (c == '.') {kw = typeofnext();if (kw >= 2) goto symbol;}/* literal numbers */if (c == '.' || isdigit(c)) {int seendot = (c == '.');int seenexp = 0;p = yytext;*p++ = c;while (isdigit(c = cget()) || c == '.' || c == 'e' || c == 'E') {if (c == 'E' || c == 'e') {if (seenexp)break;seenexp = 1;seendot = 1;*p++ = c;c = cget();if (!isdigit(c) && c != '+' && c != '-')break;}if (c == '.') {if (seendot)break;seendot = 1;}*p++ = c;}*p = '\0';if(c == 'i') {PROTECT(yylval = mkComplex(yytext));}else {PROTECT(yylval = mkFloat(yytext));uncget(1);}eatln = 0;return NUM_CONST;}/* literal strings */if (c == '\"' || c == '\'') {quote = c;p = yytext;while ((c = cget()) != R_EOF && c != quote) {if (c == '\n') {uncget(1);return ERROR;}if (c == '\\') {c = cget();switch (c) {case 'a':c = '\a';break;case 'b':c = '\b';break;case 'f':c = '\f';break;case 'n':c = '\n';break;case 'r':c = '\r';break;case 't':c = '\t';break;case 'v':c = '\v';break;case '\\':c = '\\';break;}}*p++ = c;}*p = '\0';PROTECT(yylval = mkString(yytext));eatln = 0;return STR_CONST;}/* special functions */if (c == '%') {p = yytext;*p++ = c;while ((c = cget()) != R_EOF && c != '%') {if (c == '\n') {uncget(1);return ERROR;}*p++ = c;}if (c == '%')*p++ = c;*p++ = '\0';PROTECT(yylval = install(yytext));eatln=1;return SPECIAL;}/* functions, constants and variables *//* gag, barf, but the punters want it */if (c == '_') {eatln = 1;yylval = install("<-");return LEFT_ASSIGN;}symbol:if (c == '.' || isalpha(c)) {p = yytext;do {*p++ = c;} while ((c = cget()) != R_EOF && (isalnum(c) || c == '.'));uncget(1);*p = '\0';if ((kw = klookup(yytext))) {if(kw == FUNCTION) pushCmt();return kw;}PROTECT(yylval = install(yytext));eatln = 0;return SYMBOL;}/* compound tokens */switch (c) {case '<':eatln = 1;if (nextchar('=')) {yylval = install("<=");return LE;}if (nextchar('-')) {yylval = install("<-");return LEFT_ASSIGN;}if (nextchar('<'))if (nextchar('-')) {yylval = install("<<-");return LEFT_ASSIGN;}elsereturn ERROR;yylval = install("<");return LT;case '-':eatln = 1;if (nextchar('>'))if (nextchar('>')) {yylval = install("<<-");return RIGHT_ASSIGN;}else {yylval = install("<-");return RIGHT_ASSIGN;}yylval = install("-");return '-';case '>':eatln = 1;if (nextchar('=')) {yylval = install(">=");return GE;}yylval = install(">");return GT;case '!':eatln = 1;if (nextchar('=')) {yylval = install("!=");return NE;}yylval = install("!");return '!';case '=':eatln = 1;if (nextchar('=')) {yylval = install("==");return EQ;}return '=';case ':':eatln = 1;if (nextchar('=')) {yylval = install(":=");return LEFT_ASSIGN;}yylval = install(":");return ':';case '&':eatln = 1;if (nextchar('&')) {yylval = install("&&");return AND;}yylval = install("&");return AND;case '|':eatln = 1;if (nextchar('|')) {yylval = install("||");return OR;}yylval = install("|");return OR;case '{':*++parenp = c;yylval = install("{");pushCmt();return c;case '}':ifpop();if(*parenp == '{')popCmt();parenp--;return c;case '(':*++parenp = c;yylval = install("(");return c;case ')':eatln = 0;ifpop();parenp--;return c;case '[':*++parenp = c;if (nextchar('[')) {*++parenp = c;yylval = install("[[");return LBB;}yylval = install("[");return c;case ']':ifpop();eatln = 0;parenp--;return c;case '?':eatln = 1;strcpy(yytext, "help");yylval = install(yytext);return c;case '*':eatln=1;if (nextchar('*'))c='^';yytext[0] = c;yytext[1] = '\0';yylval = install(yytext);return c;case '+':case '/':case '^':case '~':case '$':eatln = 1;yytext[0] = c;yytext[1] = '\0';yylval = install(yytext);return c;default:return c;}}