hoc.y 9.6 KB

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