hoc.y 9.4 KB

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