hoc.y - 9base - revived minimalist port of Plan 9 userland to Unix
 (HTM) git clone git://git.suckless.org/9base
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       hoc.y (9754B)
       ---
            1 %{
            2 #include <u.h>
            3 #include <libc.h>
            4 #include <bio.h>
            5 #include <ctype.h>
            6 #include "hoc.h"
            7 #define        code2(c1,c2)        code(c1); code(c2)
            8 #define        code3(c1,c2,c3)        code(c1); code(c2); code(c3)
            9 %}
           10 %union {
           11         Symbol        *sym;        /* symbol table pointer */
           12         Inst        *inst;        /* machine instruction */
           13         int        narg;        /* number of arguments */
           14         Formal        *formals;        /* list of formal parameters */
           15 }
           16 %token        <sym>        NUMBER STRING PRINT VAR BLTIN UNDEF WHILE FOR IF ELSE
           17 %token        <sym>        FUNCTION PROCEDURE RETURN FUNC PROC READ
           18 %type        <formals>        formals
           19 %type        <inst>        expr stmt asgn prlist stmtlist
           20 %type        <inst>        cond while for if begin end 
           21 %type        <sym>        procname
           22 %type        <narg>        arglist
           23 %right        '=' ADDEQ SUBEQ MULEQ DIVEQ MODEQ
           24 %left        OR
           25 %left        AND
           26 %left        GT GE LT LE EQ NE
           27 %left        '+' '-'
           28 %left        '*' '/' '%'
           29 %left        UNARYMINUS NOT INC DEC
           30 %right        '^'
           31 %%
           32 list:          /* nothing */
           33         | list '\n'
           34         | list defn '\n'
           35         | list asgn '\n'  { code2(xpop, STOP); return 1; }
           36         | list stmt '\n'  { code(STOP); return 1; } 
           37         | list expr '\n'  { code2(printtop, STOP); return 1; }
           38         | list error '\n' { yyerrok; }
           39         ;
           40 asgn:          VAR '=' expr { code3(varpush,(Inst)$1,assign); $$=$3; }
           41         | VAR ADDEQ expr        { code3(varpush,(Inst)$1,addeq); $$=$3; }
           42         | VAR SUBEQ expr        { code3(varpush,(Inst)$1,subeq); $$=$3; }
           43         | VAR MULEQ expr        { code3(varpush,(Inst)$1,muleq); $$=$3; }
           44         | VAR DIVEQ expr        { code3(varpush,(Inst)$1,diveq); $$=$3; }
           45         | VAR MODEQ expr        { code3(varpush,(Inst)$1,modeq); $$=$3; }
           46         ;
           47 stmt:          expr        { code(xpop); }
           48         | RETURN { defnonly("return"); code(procret); }
           49         | RETURN expr
           50                 { defnonly("return"); $$=$2; code(funcret); }
           51         | PROCEDURE begin '(' arglist ')'
           52                 { $$ = $2; code3(call, (Inst)$1, (Inst)(uintptr)$4); }
           53         | PRINT prlist        { $$ = $2; }
           54         | while '(' cond ')' stmt end {
           55                 ($1)[1] = (Inst)$5;        /* body of loop */
           56                 ($1)[2] = (Inst)$6; }        /* end, if cond fails */
           57         | for '(' cond ';' cond ';' cond ')' stmt end {
           58                 ($1)[1] = (Inst)$5;        /* condition */
           59                 ($1)[2] = (Inst)$7;        /* post loop */
           60                 ($1)[3] = (Inst)$9;        /* body of loop */
           61                 ($1)[4] = (Inst)$10; }        /* end, if cond fails */
           62         | if '(' cond ')' stmt end {        /* else-less if */
           63                 ($1)[1] = (Inst)$5;        /* thenpart */
           64                 ($1)[3] = (Inst)$6; }        /* end, if cond fails */
           65         | if '(' cond ')' stmt end ELSE stmt end {        /* if with else */
           66                 ($1)[1] = (Inst)$5;        /* thenpart */
           67                 ($1)[2] = (Inst)$8;        /* elsepart */
           68                 ($1)[3] = (Inst)$9; }        /* end, if cond fails */
           69         | '{' stmtlist '}'        { $$ = $2; }
           70         ;
           71 cond:           expr         { code(STOP); }
           72         ;
           73 while:          WHILE        { $$ = code3(whilecode,STOP,STOP); }
           74         ;
           75 for:          FOR        { $$ = code(forcode); code3(STOP,STOP,STOP); code(STOP); }
           76         ;
           77 if:          IF        { $$ = code(ifcode); code3(STOP,STOP,STOP); }
           78         ;
           79 begin:          /* nothing */                { $$ = progp; }
           80         ;
           81 end:          /* nothing */                { code(STOP); $$ = progp; }
           82         ;
           83 stmtlist: /* nothing */                { $$ = progp; }
           84         | stmtlist '\n'
           85         | stmtlist stmt
           86         ;
           87 expr:          NUMBER { $$ = code2(constpush, (Inst)$1); }
           88         | VAR         { $$ = code3(varpush, (Inst)$1, eval); }
           89         | asgn
           90         | FUNCTION begin '(' arglist ')'
           91                 { $$ = $2; code3(call,(Inst)$1,(Inst)(uintptr)$4); }
           92         | READ '(' VAR ')' { $$ = code2(varread, (Inst)$3); }
           93         | BLTIN '(' expr ')' { $$=$3; code2(bltin, (Inst)$1->u.ptr); }
           94         | '(' expr ')'        { $$ = $2; }
           95         | expr '+' expr        { code(add); }
           96         | expr '-' expr        { code(sub); }
           97         | expr '*' expr        { code(mul); }
           98         | expr '/' expr        { code(div); }
           99         | expr '%' expr        { code(mod); }
          100         | expr '^' expr        { code (power); }
          101         | '-' expr   %prec UNARYMINUS   { $$=$2; code(negate); }
          102         | expr GT expr        { code(gt); }
          103         | expr GE expr        { code(ge); }
          104         | expr LT expr        { code(lt); }
          105         | expr LE expr        { code(le); }
          106         | expr EQ expr        { code(eq); }
          107         | expr NE expr        { code(ne); }
          108         | expr AND expr        { code(and); }
          109         | expr OR expr        { code(or); }
          110         | NOT expr        { $$ = $2; code(not); }
          111         | INC VAR        { $$ = code2(preinc,(Inst)$2); }
          112         | DEC VAR        { $$ = code2(predec,(Inst)$2); }
          113         | VAR INC        { $$ = code2(postinc,(Inst)$1); }
          114         | VAR DEC        { $$ = code2(postdec,(Inst)$1); }
          115         ;
          116 prlist:          expr                        { code(prexpr); }
          117         | STRING                { $$ = code2(prstr, (Inst)$1); }
          118         | prlist ',' expr        { code(prexpr); }
          119         | prlist ',' STRING        { code2(prstr, (Inst)$3); }
          120         ;
          121 defn:          FUNC procname { $2->type=FUNCTION; indef=1; }
          122             '(' formals ')' stmt { code(procret); define($2, $5); indef=0; }
          123         | PROC procname { $2->type=PROCEDURE; indef=1; }
          124             '(' formals ')' stmt { code(procret); define($2, $5); indef=0; }
          125         ;
          126 formals:        { $$ = 0; }
          127         | VAR                        { $$ = formallist($1, 0); }
          128         | VAR ',' formals        { $$ = formallist($1, $3); }
          129         ;
          130 procname: VAR
          131         | FUNCTION
          132         | PROCEDURE
          133         ;
          134 arglist:  /* nothing */         { $$ = 0; }
          135         | expr                        { $$ = 1; }
          136         | arglist ',' expr        { $$ = $1 + 1; }
          137         ;
          138 %%
          139         /* end of grammar */
          140 char        *progname;
          141 int        lineno = 1;
          142 jmp_buf        begin;
          143 int        indef;
          144 char        *infile;        /* input file name */
          145 Biobuf        *bin;                /* input file descriptor */
          146 Biobuf        binbuf;
          147 char        **gargv;        /* global argument list */
          148 int        gargc;
          149 
          150 int c = '\n';        /* global for use by warning() */
          151 
          152 int        backslash(int), follow(int, int, int);
          153 void        defnonly(char*), run(void);
          154 void        warning(char*, char*);
          155 
          156 int
          157 yylex(void)                /* hoc6 */
          158 {
          159         while ((c=Bgetc(bin)) == ' ' || c == '\t')
          160                 ;
          161         if (c < 0)
          162                 return 0;
          163         if (c == '\\') {
          164                 c = Bgetc(bin);
          165                 if (c == '\n') {
          166                         lineno++;
          167                         return yylex();
          168                 }
          169         }
          170         if (c == '#') {                /* comment */
          171                 while ((c=Bgetc(bin)) != '\n' && c >= 0)
          172                         ;
          173                 if (c == '\n')
          174                         lineno++;
          175                 return c;
          176         }
          177         if (c == '.' || isdigit(c)) {        /* number */
          178                 double d;
          179                 Bungetc(bin);
          180                 Bgetd(bin, &d);
          181                 yylval.sym = install("", NUMBER, d);
          182                 return NUMBER;
          183         }
          184         if (isalpha(c) || c == '_') {
          185                 Symbol *s;
          186                 char sbuf[100], *p = sbuf;
          187                 do {
          188                         if (p >= sbuf + sizeof(sbuf) - 1) {
          189                                 *p = '\0';
          190                                 execerror("name too long", sbuf);
          191                         }
          192                         *p++ = c;
          193                 } while ((c=Bgetc(bin)) >= 0 && (isalnum(c) || c == '_'));
          194                 Bungetc(bin);
          195                 *p = '\0';
          196                 if ((s=lookup(sbuf)) == 0)
          197                         s = install(sbuf, UNDEF, 0.0);
          198                 yylval.sym = s;
          199                 return s->type == UNDEF ? VAR : s->type;
          200         }
          201         if (c == '"') {        /* quoted string */
          202                 char sbuf[100], *p;
          203                 for (p = sbuf; (c=Bgetc(bin)) != '"'; p++) {
          204                         if (c == '\n' || c == Beof)
          205                                 execerror("missing quote", "");
          206                         if (p >= sbuf + sizeof(sbuf) - 1) {
          207                                 *p = '\0';
          208                                 execerror("string too long", sbuf);
          209                         }
          210                         *p = backslash(c);
          211                 }
          212                 *p = 0;
          213                 yylval.sym = (Symbol *)emalloc(strlen(sbuf)+1);
          214                 strcpy((char*)yylval.sym, sbuf);
          215                 return STRING;
          216         }
          217         switch (c) {
          218         case '+':        return follow('+', INC, '+') == INC ? INC : follow('=', ADDEQ, '+');
          219         case '-':        return follow('-', DEC, '-') == DEC ? DEC : follow('=', SUBEQ, '-');
          220         case '*':        return follow('=', MULEQ, '*');
          221         case '/':        return follow('=', DIVEQ, '/');
          222         case '%':        return follow('=', MODEQ, '%');
          223         case '>':        return follow('=', GE, GT);
          224         case '<':        return follow('=', LE, LT);
          225         case '=':        return follow('=', EQ, '=');
          226         case '!':        return follow('=', NE, NOT);
          227         case '|':        return follow('|', OR, '|');
          228         case '&':        return follow('&', AND, '&');
          229         case '\n':        lineno++; return '\n';
          230         default:        return c;
          231         }
          232 }
          233 
          234 int
          235 backslash(int c)        /* get next char with \'s interpreted */
          236 {
          237         static char transtab[] = "b\bf\fn\nr\rt\t";
          238         if (c != '\\')
          239                 return c;
          240         c = Bgetc(bin);
          241         if (islower(c) && strchr(transtab, c))
          242                 return strchr(transtab, c)[1];
          243         return c;
          244 }
          245 
          246 int
          247 follow(int expect, int ifyes, int ifno)        /* look ahead for >=, etc. */
          248 {
          249         int c = Bgetc(bin);
          250 
          251         if (c == expect)
          252                 return ifyes;
          253         Bungetc(bin);
          254         return ifno;
          255 }
          256 
          257 void
          258 yyerror(char* s)        /* report compile-time error */
          259 {
          260 /*rob
          261         warning(s, (char *)0);
          262         longjmp(begin, 0);
          263 rob*/
          264         execerror(s, (char *)0);
          265 }
          266 
          267 void
          268 execerror(char* s, char* t)        /* recover from run-time error */
          269 {
          270         warning(s, t);
          271         Bseek(bin, 0L, 2);                /* flush rest of file */
          272         restoreall();
          273         longjmp(begin, 0);
          274 }
          275 
          276 void
          277 fpecatch(void)        /* catch floating point exceptions */
          278 {
          279         execerror("floating point exception", (char *) 0);
          280 }
          281 
          282 void
          283 intcatch(void)        /* catch interrupts */
          284 {
          285         execerror("interrupt", 0);
          286 }
          287 
          288 void
          289 run(void)        /* execute until EOF */
          290 {
          291         setjmp(begin);
          292         for (initcode(); yyparse(); initcode())
          293                 execute(progbase);
          294 }
          295 
          296 void
          297 main(int argc, char* argv[])        /* hoc6 */
          298 {
          299         static int first = 1;
          300 #ifdef YYDEBUG
          301         extern int yydebug;
          302         yydebug=3;
          303 #endif
          304         progname = argv[0];
          305         init();
          306         if (argc == 1) {        /* fake an argument list */
          307                 static char *stdinonly[] = { "-" };
          308 
          309                 gargv = stdinonly;
          310                 gargc = 1;
          311         } else if (first) {        /* for interrupts */
          312                 first = 0;
          313                 gargv = argv+1;
          314                 gargc = argc-1;
          315         }
          316         Binit(&binbuf, 0, OREAD);
          317         bin = &binbuf;
          318         while (moreinput())
          319                 run();
          320         exits(0);
          321 }
          322 
          323 int
          324 moreinput(void)
          325 {
          326         char *expr;
          327         static char buf[64];
          328         int fd;
          329         static Biobuf b;
          330 
          331         if (gargc-- <= 0)
          332                 return 0;
          333         if (bin && bin != &binbuf)
          334                 Bterm(bin);
          335         infile = *gargv++;
          336         lineno = 1;
          337         if (strcmp(infile, "-") == 0) {
          338                 bin = &binbuf;
          339                 infile = 0;
          340                 return 1;
          341         }
          342         if(strncmp(infile, "-e", 2) == 0) {
          343                 if(infile[2]==0){
          344                         if(gargc == 0){
          345                                 fprint(2, "%s: no argument for -e\n", progname);
          346                                 return 0;
          347                         }
          348                         gargc--;
          349                         expr = *gargv++;
          350                 }else
          351                         expr = infile+2;
          352                 sprint(buf, "/tmp/hocXXXXXXX");
          353                 fd = mkstemp(buf);
          354                 remove(buf);
          355 /*
          356                 infile = mktemp(buf);
          357                 fd = create(infile, ORDWR|ORCLOSE, 0600);
          358                 if(fd < 0){
          359                         fprint(2, "%s: can't create temp. file: %r\n", progname);
          360                         return 0;
          361                 }
          362 */
          363                 fprint(fd, "%s\n", expr);
          364                 /* leave fd around; file will be removed on exit */
          365                 /* the following looks weird but is required for unix version */
          366                 bin = &b;
          367                 seek(fd, 0, 0);
          368                 Binit(bin, fd, OREAD);
          369         } else {
          370                 bin=Bopen(infile, OREAD);
          371                 if (bin == 0) {
          372                         fprint(2, "%s: can't open %s\n", progname, infile);
          373                         return moreinput();
          374                 }
          375         }
          376         return 1;
          377 }
          378 
          379 void
          380 warning(char* s, char* t)        /* print warning message */
          381 {
          382         fprint(2, "%s: %s", progname, s);
          383         if (t)
          384                 fprint(2, " %s", t);
          385         if (infile)
          386                 fprint(2, " in %s", infile);
          387         fprint(2, " near line %d\n", lineno);
          388         while (c != '\n' && c != Beof)
          389                 if((c = Bgetc(bin)) == '\n')        /* flush rest of input line */
          390                         lineno++;
          391 }
          392 
          393 void
          394 defnonly(char *s)        /* warn if illegal definition */
          395 {
          396         if (!indef)
          397                 execerror(s, "used outside definition");
          398 }