rewriter.c (17644B)
1 // Copyright 2020, Brian Swetland <swetland@frotz.net> 2 // Licensed under the Apache License, Version 2.0. 3 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <stdarg.h> 7 #include <stdint.h> 8 #include <stdbool.h> 9 #include <strings.h> 10 #include <string.h> 11 12 #include <fcntl.h> 13 #include <unistd.h> 14 #include <sys/stat.h> 15 16 #define nil 0 17 18 void error(const char *fmt, ...); 19 20 typedef uint32_t u32; 21 typedef int32_t i32; 22 typedef uint8_t u8; 23 24 enum { FNMAXARGS = 8, }; 25 26 // token classes (tok & tcMASK) 27 enum { 28 tcRELOP = 0x08, tcADDOP = 0x10, tcMULOP = 0x18, 29 tcAEQOP = 0x20, tcMEQOP = 0x28, tcMASK = 0xF8, 30 }; 31 32 typedef enum { 33 // EndMarks, Braces, Brackets Parens 34 tEOF, tEOL, tOBRACE, tCBRACE, tOBRACK, tCBRACK, tOPAREN, tCPAREN, 35 // RelOps (do not reorder) 36 tEQ, tNE, tLT, tLE, tGT, tGE, tx0E, tx0F, 37 // AddOps (do not reorder) 38 tPLUS, tMINUS, tPIPE, tCARET, tx14, tx15, tx16, tx17, 39 // MulOps (do not reorder) 40 tSTAR, tSLASH, tPERCENT, tAMP, tANDNOT, tLEFT, tRIGHT, tx1F, 41 // AsnOps (do not reorder) 42 tADDEQ, tSUBEQ, tOREQ, tXOREQ, tx24, tx25, tx26, tx27, 43 tMULEQ, tDIVEQ, tMODEQ, tANDEQ, tANNEQ, tLSEQ, tRSEQ, t2F, 44 // Various, UnaryNot, LogicalOps, 45 tSEMI, tCOLON, tDOT, tCOMMA, tNOT, tAND, tOR, tBANG, 46 tASSIGN, tINC, tDEC, tHASH, tARROW, 47 // Keywords 48 tTYPEDEF, tSTRUCT, tVAR, tENUM, 49 tIF, tELSE, tWHILE, 50 tBREAK, tCONTINUE, tRETURN, 51 tFOR, tSWITCH, tCASE, 52 tTRUE, tFALSE, tNIL, 53 tIDN, tNUM, tSTR, tTYPE, 54 // used internal to the lexer but never returned 55 tSPC, tINV, tDQT, tSQT, tMSC, tTAB 56 } token_t; 57 58 char *tnames[] = { 59 "<EOF>", "<EOL>", "{", "}", "[", "]", "(", ")", 60 "==", "!=", "<", "<=", ">", ">=", "", "", 61 "+", "-", "|", "^", "", "", "", "", 62 "*", "/", "%", "&", "&~", "<<", ">>", "", 63 "+=", "-=", "|=", "^=", "", "", "", "", 64 "*=", "/=", "%=", "&=", "&~=", "<<=", ">>=", "", 65 ";", ":", ".", ",", "~", "&&", "||", "!", 66 "=", "++", "--", "#", "->", 67 "typedef", "struct", "var", "enum", 68 "if", "else", "while", 69 "break", "continue", "return", 70 "for", "switch", "case", 71 "true", "false", "nil", 72 "<ID>", "<NUM>", "<STR>", "<TYPE>", 73 "<SPC>", "<INV>", "<DQT>", "<SQT>", "<MSC>", "<TAB>" 74 }; 75 76 u8 lextab[256] = { 77 tEOF, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 78 tINV, tTAB, tEOL, tSPC, tINV, tSPC, tINV, tINV, 79 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 80 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 81 tSPC, tBANG, tDQT, tHASH, tMSC, tPERCENT, tAMP, tSQT, 82 tOPAREN, tCPAREN, tSTAR, tPLUS, tCOMMA, tMINUS, tDOT, tSLASH, 83 tNUM, tNUM, tNUM, tNUM, tNUM, tNUM, tNUM, tNUM, 84 tNUM, tNUM, tCOLON, tSEMI, tLT, tASSIGN, tGT, tMSC, 85 tMSC, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, 86 tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, 87 tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, 88 tIDN, tIDN, tIDN, tOBRACK, tMSC, tCBRACK, tCARET, tIDN, 89 tMSC, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, 90 tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, 91 tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, tIDN, 92 tIDN, tIDN, tIDN, tOBRACE, tPIPE, tCBRACE, tNOT, tINV, 93 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 94 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 95 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 96 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 97 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 98 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 99 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 100 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 101 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 102 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 103 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 104 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 105 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 106 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 107 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 108 tINV, tINV, tINV, tINV, tINV, tINV, tINV, tINV, 109 }; 110 111 typedef struct StringRec* String; 112 typedef struct StringRec StringRec; 113 114 115 struct StringRec { 116 String next; 117 u32 len; 118 u32 kind; 119 char text[0]; 120 }; 121 122 #define KindNone 0 123 #define KindType 1 124 #define KindKeyword 2 125 126 // ------------------------------------------------------------------ 127 128 struct CtxRec { 129 const char* filename; // filename of active source 130 int fd; 131 132 u8 iobuffer[1024]; // scanner file io buffer 133 u32 ionext; 134 u32 iolast; 135 136 u32 linenumber; // line number of most recent line 137 u32 lineoffset; // position of start of most recent line 138 u32 byteoffset; // position of the most recent character 139 u32 flags; 140 u32 cc; // scanner: next character 141 142 token_t tok; // most recent token 143 u32 num; // used for tNUM 144 char tmp[256]; // used for tIDN, tSTR; 145 String ident; // used for tIDN 146 147 String strtab; // TODO: hashtable 148 }; 149 150 struct CtxRec ctx; 151 152 String make_string(const char* text, u32 len, u32 kind) { 153 // OPT obviously this wants to be a hash table 154 String str = ctx.strtab; 155 while (str != nil) { 156 if ((str->len == len) && (memcmp(text, str->text, len) == 0)) { 157 if ((str->kind != kind) && (kind != tIDN)) { 158 error("string '%s' already kind %u\n", str->text, str->kind); 159 } 160 return str; 161 } 162 str = str->next; 163 } 164 165 str = malloc(sizeof(StringRec) + len + 1); 166 str->len = len; 167 str->kind = kind; 168 memcpy(str->text, text, len); 169 str->text[len] = 0; 170 str->next = ctx.strtab; 171 ctx.strtab = str; 172 173 return str; 174 } 175 176 void make_keyword(const char* text, u32 tok) { 177 make_string(text, strlen(text), tok); 178 } 179 180 void make_type(const char* text) { 181 make_string(text, strlen(text), tTYPE); 182 } 183 184 int is_type(String str) { 185 return str->kind == 0x1000; 186 } 187 188 void init_ctx() { 189 memset(&ctx, 0, sizeof(ctx)); 190 191 make_type("u8"); 192 make_type("u32"); 193 make_type("i32"); 194 make_type("void"); 195 make_type("str"); 196 make_type("strptr"); 197 make_type("bool"); 198 make_type("token_t"); 199 200 // pre-intern keywords 201 make_keyword("if", tIF); 202 //make_keyword("for", tFOR); 203 make_keyword("nil", tNIL); 204 make_keyword("else", tELSE); 205 make_keyword("enum", tENUM); 206 make_keyword("true", tTRUE); 207 make_keyword("false", tFALSE); 208 make_keyword("typedef", tTYPEDEF); 209 make_keyword("break", tBREAK); 210 make_keyword("while", tWHILE); 211 make_keyword("struct", tSTRUCT); 212 make_keyword("return", tRETURN); 213 make_keyword("continue", tCONTINUE); 214 } 215 216 void dump_file_line(const char* fn, u32 offset) { 217 int fd = open(fn, O_RDONLY); 218 if (fd < 0) { 219 return; 220 } 221 if (lseek(fd, offset, SEEK_SET) != offset) { 222 close(fd); 223 return; 224 } 225 char line[256]; 226 int r = read(fd, line, 255); 227 if (r > 0) { 228 line[r] = 0; 229 int n = 0; 230 while (n < r) { 231 if (line[n] == '\n') { 232 line[n] = 0; 233 break; 234 } 235 n++; 236 } 237 fprintf(stderr, "\n%s", line); 238 } 239 close(fd); 240 } 241 242 void error(const char *fmt, ...) { 243 va_list ap; 244 245 fprintf(stderr,"\n\n%s:%d: ", ctx.filename, ctx.linenumber); 246 va_start(ap, fmt); 247 vfprintf(stderr, fmt, ap); 248 va_end(ap); 249 if (ctx.linenumber > 0) { 250 dump_file_line(ctx.filename, ctx.lineoffset); 251 } 252 fprintf(stderr, "\n\n"); 253 exit(1); 254 } 255 256 void load(const char* filename) { 257 ctx.filename = filename; 258 ctx.linenumber = 0; 259 260 if (ctx.fd >= 0) { 261 close(ctx.fd); 262 } 263 ctx.fd = open(filename, O_RDONLY); 264 if (ctx.fd < 0) { 265 error("cannot open file '%s'", filename); 266 } 267 ctx.ionext = 0; 268 ctx.iolast = 0; 269 ctx.linenumber = 1; 270 ctx.lineoffset = 0; 271 ctx.byteoffset = 0; 272 } 273 274 int unhex(u32 ch) { 275 if ((ch >= '0') && (ch <= '9')) { 276 return ch - '0'; 277 } 278 if ((ch >= 'a') && (ch <= 'f')) { 279 return ch - 'a' + 10; 280 } 281 if ((ch >= 'A') && (ch <= 'F')) { 282 return ch - 'A' + 10; 283 } 284 return -1; 285 } 286 287 u32 scan() { 288 while (ctx.ionext == ctx.iolast) { 289 if (ctx.fd < 0) { 290 ctx.cc = 0; 291 return ctx.cc; 292 } 293 int r = read(ctx.fd, ctx.iobuffer, sizeof(ctx.iobuffer)); 294 if (r <= 0) { 295 ctx.fd = -1; 296 } else { 297 ctx.iolast = r; 298 ctx.ionext = 0; 299 } 300 } 301 ctx.cc = ctx.iobuffer[ctx.ionext]; 302 ctx.ionext++; 303 ctx.byteoffset++; 304 return ctx.cc; 305 } 306 307 u32 unescape(u32 n) { 308 if (n == 'n') { 309 return 10; 310 } else if (n == 'r') { 311 return 13; 312 } else if (n == 't') { 313 return 9; 314 } else if (n == '"') { 315 return '"'; 316 } else if (n == '\'') { 317 return '\''; 318 } else if (n == '\\') { 319 return '\\'; 320 } else if (n == 'x') { 321 int x0 = unhex(scan()); 322 int x1 = unhex(scan()); 323 if ((x0 < 0) || (x1 < 0)) { 324 error("invalid hex escape"); 325 } 326 return (x0 << 4) | x1; 327 } else { 328 error("invalid escape 0x%02x", n); 329 return 0; 330 } 331 } 332 333 token_t scan_string(u32 cc, u32 nc) { 334 u32 n = 0; 335 while (true) { 336 if (nc == '"') { 337 nc = scan(); 338 break; 339 } else if (nc == 0) { 340 error("unterminated string"); 341 } else if (nc == '\\') { 342 ctx.tmp[n] = unescape(scan()); 343 } else { 344 ctx.tmp[n] = nc; 345 } 346 nc = scan(); 347 n++; 348 if (n == 255) { 349 ctx.tmp[n] = 0; 350 error("constant string too large '%s'", ctx.tmp); 351 } 352 } 353 ctx.tmp[n] = 0; 354 return tSTR; 355 } 356 357 token_t scan_keyword(u32 len) { 358 ctx.tmp[len] = 0; 359 String idn = make_string(ctx.tmp, len, tIDN); 360 ctx.ident = idn; 361 362 return idn->kind; 363 } 364 365 token_t scan_number(u32 cc, u32 nc) { 366 u32 n = 1; 367 u32 val = cc - '0'; 368 369 if ((cc == '0') && (nc == 'b')) { // binary 370 nc = scan(); 371 while ((nc == '0') || (nc == '1')) { 372 val = (val << 1) | (nc - '0'); 373 nc = scan(); 374 n++; 375 if (n == 34) { 376 error("binary constant too large"); 377 } 378 } 379 } else if ((cc == '0') && (nc == 'x')) { // hex 380 nc = scan(); 381 while (true) { 382 int tmp = unhex(nc); 383 if (tmp == -1) { 384 break; 385 } 386 val = (val << 4) | tmp; 387 nc = scan(); 388 n++; 389 if (n == 10) { 390 error("hex constant too large"); 391 } 392 } 393 } else { // decimal 394 while (lextab[nc] == tNUM) { 395 u32 tmp = (val * 10) + (nc - '0'); 396 if (tmp <= val) { 397 error("decimal constant too large"); 398 } 399 val = tmp; 400 nc = scan(); 401 n++; 402 } 403 } 404 ctx.num = val; 405 return tNUM; 406 } 407 408 token_t scan_ident(u32 cc, u32 nc) { 409 ctx.tmp[0] = cc; 410 u32 n = 1; 411 412 while (true) { 413 u32 tok = lextab[nc]; 414 if ((tok == tIDN) || (tok == tNUM)) { 415 ctx.tmp[n] = nc; 416 n++; 417 if (n == 32) { error("identifier too large"); } 418 nc = scan(); 419 } else { 420 break; 421 } 422 } 423 return scan_keyword(n); 424 } 425 426 token_t _next(int ws) { 427 u8 nc = ctx.cc; 428 while (true) { 429 u8 cc = nc; 430 nc = scan(); 431 u32 tok = lextab[cc]; 432 if (tok == tNUM) { // 0..9 433 return scan_number(cc, nc); 434 } else if (tok == tIDN) { // _ A..Z a..z 435 return scan_ident(cc, nc); 436 } else if (tok == tDQT) { // " 437 return scan_string(cc, nc); 438 } else if (tok == tSQT) { // ' 439 ctx.num = nc; 440 if (nc == '\\') { 441 ctx.num = unescape(scan()); 442 } 443 nc = scan(); 444 if (nc != '\'') { 445 error("unterminated character constant"); 446 } 447 nc = scan(); 448 return tNUM; 449 } else if (tok == tPLUS) { 450 if (nc == '+') { tok = tINC; nc = scan(); } 451 } else if (tok == tMINUS) { 452 if (nc == '-') { tok = tDEC; nc = scan(); } 453 else if (nc == '>') { tok = tARROW; nc = scan(); } 454 } else if (tok == tAMP) { 455 if (nc == '&') { tok = tAND; nc = scan(); } 456 else if (nc == '~') { tok = tANDNOT; nc = scan(); } 457 } else if (tok == tPIPE) { 458 if (nc == '|') { tok = tOR; nc = scan(); } 459 } else if (tok == tGT) { 460 if (nc == '=') { tok = tGE; nc = scan(); } 461 else if (nc == '>') { tok = tRIGHT; nc = scan(); } 462 } else if (tok == tLT) { 463 if (nc == '=') { tok = tLE; nc = scan(); } 464 else if (nc == '<') { tok = tLEFT; nc = scan(); } 465 } else if (tok == tASSIGN) { 466 if (nc == '=') { tok = tEQ; nc = scan(); } 467 } else if (tok == tBANG) { 468 if (nc == '=') { tok = tNE; nc = scan(); } 469 } else if (tok == tSLASH) { 470 if (nc == '/') { 471 if (ws) printf("/"); 472 // comment -- consume until EOL or EOF 473 while ((nc != '\n') && (nc != 0)) { 474 if (ws) printf("%c", nc); 475 nc = scan(); 476 } 477 continue; 478 } 479 } else if (tok == tHASH) { 480 while ((nc != '\n') && (nc != 0)) { 481 nc = scan(); 482 } 483 continue; 484 } else if (tok == tEOL) { 485 ctx.linenumber++; 486 ctx.lineoffset = ctx.byteoffset; 487 //ctx.xref[ctx.pc / 4] = ctx.linenumber; 488 //if (ctx.flags & cfVisibleEOL) { 489 // return tEOL; 490 //} 491 if (ws) printf("\n"); 492 continue; 493 } else if (tok == tSPC) { 494 if (ws) printf(" "); 495 continue; 496 } else if (tok == tTAB) { 497 if (ws) printf("\t"); 498 continue; 499 } else if ((tok == tMSC) || (tok == tINV)) { 500 error("unknown character 0x%02x", cc); 501 } 502 503 // if we're an AddOp or MulOp, followed by an '=' 504 if (((tok & 0xF0) == 0x20) && (nc == '=')) { 505 nc = scan(); 506 // transform us to a XEQ operation 507 tok = tok + 0x10; 508 } 509 510 return tok; 511 } 512 } 513 514 token_t next() { 515 return (ctx.tok = _next(1)); 516 } 517 518 token_t nextq() { 519 return (ctx.tok = _next(0)); 520 } 521 522 void printstr() { 523 u32 n = 0; 524 printf("\""); 525 while (n < 256) { 526 u32 ch = ctx.tmp[n]; 527 if (ch == 0) { 528 break; 529 } else if ((ch < ' ') || (ch > '~')) { 530 printf("\\x%02x", ch); 531 } else if ((ch == '"') || (ch == '\\')) { 532 printf("\\%c", ch); 533 } else { 534 printf("%c", ch); 535 } 536 n++; 537 } 538 printf("\""); 539 } 540 541 void print() { 542 if (ctx.tok == tNUM) { 543 printf("%u ", ctx.num); 544 } else if (ctx.tok == tIDN) { 545 printf("@%s ", ctx.tmp); 546 } else if (ctx.tok == tTYPE) { 547 printf("@@%s ", ctx.tmp); 548 } else if (ctx.tok == tEOL) { 549 printf("\n"); 550 } else if (ctx.tok == tSTR) { 551 printstr(); 552 } else { 553 printf("%s ", tnames[ctx.tok]); 554 } 555 } 556 557 void emit() { 558 if (ctx.tok == tNUM) { 559 printf("%u", ctx.num); 560 } else if (ctx.tok == tIDN) { 561 printf("%s", ctx.tmp); 562 } else if (ctx.tok == tTYPE) { 563 printf("%s", ctx.tmp); 564 } else if (ctx.tok == tSTR) { 565 printstr(); 566 } else { 567 printf("%s", tnames[ctx.tok]); 568 } 569 } 570 571 void expected(const char* what) { 572 error("expected %s, found %s", what, tnames[ctx.tok]); 573 } 574 575 void expect(token_t tok) { 576 if (ctx.tok != tok) { 577 error("expected %s, found %s", tnames[tok], tnames[ctx.tok]); 578 } 579 } 580 581 void require(token_t tok) { 582 expect(tok); 583 emit(); 584 next(); 585 } 586 587 void requireq(token_t tok) { 588 expect(tok); 589 nextq(); 590 } 591 592 void parse_enum() { 593 printf("enum "); 594 require(tOBRACE); 595 while (ctx.tok != tCBRACE) { 596 emit(); 597 next(); 598 } 599 require(tCBRACE); 600 require(tSEMI); 601 } 602 603 void parse_expr() { 604 while (ctx.tok != tCPAREN) { 605 if (ctx.tok == tOPAREN) { 606 printf("("); 607 next(); 608 parse_expr(); 609 } else { 610 emit(); 611 next(); 612 } 613 } 614 require(tCPAREN); 615 printf(")"); 616 } 617 618 void parse_block() { 619 unsigned start = 1; 620 while (ctx.tok != tCBRACE) { 621 if (start) { 622 start = 0; 623 } 624 if (ctx.tok == tOPAREN) { 625 printf("("); 626 next(); 627 parse_expr(); 628 } else if (ctx.tok == tOBRACE) { 629 printf("{"); 630 next(); 631 parse_block(); 632 start = 1; 633 } else if (ctx.tok == tSEMI) { 634 printf(";"); 635 next(); 636 start = 1; 637 } else { 638 emit(); 639 next(); 640 } 641 } 642 require(tCBRACE); 643 } 644 645 void parse_func(String type, String name) { 646 printf("func %s(", name->text); 647 while (ctx.tok != tCPAREN) { 648 String pt = ctx.ident; 649 nextq(); 650 String pn = ctx.ident; 651 nextq(); 652 printf("%s %s", pn->text, pt->text); 653 if (ctx.tok == tCOMMA) { 654 printf(","); 655 next(); 656 } 657 } 658 require(tCPAREN); 659 if (ctx.tok == tSEMI) { 660 printf(" %s;", type->text); 661 next(); 662 return; 663 } 664 printf("%s ", type->text); 665 require(tOBRACE); 666 parse_block(); 667 } 668 669 void parse_array(String type, String name) { 670 u32 n = ctx.num; 671 if (ctx.tok == tCBRACK) { 672 next(); 673 printf("var %s []%s", name->text, type->text); 674 } else { 675 requireq(tNUM); 676 requireq(tCBRACK); 677 printf("var %s [%u]%s", name->text, n, type->text); 678 } 679 if (ctx.tok == tSEMI) { 680 printf(";"); 681 next(); 682 } else if (ctx.tok == tASSIGN) { 683 printf(" ="); 684 next(); 685 require(tOBRACE); 686 while (ctx.tok != tCBRACE) { 687 emit(); 688 next(); 689 } 690 require(tCBRACE); 691 require(tSEMI); 692 } else { 693 error("LOST"); 694 } 695 } 696 697 void parse_program() { 698 next(); 699 700 for (;;) { 701 if (ctx.tok == tENUM) { 702 nextq(); 703 parse_enum(); 704 } else if (ctx.tok == tTYPE) { 705 String type = ctx.ident; 706 requireq(tTYPE); 707 String ident = ctx.ident; 708 requireq(tIDN); 709 if (ctx.tok == tOBRACK) { // array 710 next(); 711 parse_array(type, ident); 712 } else if(ctx.tok == tOPAREN) { // func 713 next(); 714 parse_func(type, ident); 715 } else { // global var 716 printf("var %s %s", ident->text, type->text); 717 while (ctx.tok != tSEMI) { 718 emit(); 719 next(); 720 } 721 require(tSEMI); 722 } 723 } else if (ctx.tok == tTYPEDEF) { 724 nextq(); 725 requireq(tSTRUCT); 726 String t1 = ctx.ident; 727 nextq(); 728 if (ctx.tok == tSTAR) { 729 nextq(); 730 String t2 = ctx.ident; 731 next(); 732 t2->kind = tTYPE; 733 printf("type %s *%s", t2->text, t1->text); 734 } else { 735 next(); 736 t1->kind = tTYPE; 737 printf("type %s", t1->text); 738 } 739 require(tSEMI); 740 } else if (ctx.tok == tSTRUCT) { 741 nextq(); 742 String n = ctx.ident; 743 nextq(); 744 n->kind = tTYPE; 745 printf("type %s struct ", n->text); 746 require(tOBRACE); 747 while (ctx.tok != tCBRACE) { 748 emit(); 749 next(); 750 } 751 require(tCBRACE); 752 require(tSEMI); 753 } else if (ctx.tok == tEOF) { 754 return; 755 } else { 756 expected("top level entity"); 757 } 758 } 759 } 760 761 // ================================================================ 762 763 int main(int argc, char **argv) { 764 const char *outname = "out.c"; 765 const char *srcname = nil; 766 bool dump = false; 767 bool scan_only = false; 768 769 init_ctx(); 770 ctx.filename = "<commandline>"; 771 772 while (argc > 1) { 773 if (!strcmp(argv[1],"-o")) { 774 if (argc < 2) { 775 error("option -o requires argument"); 776 } 777 outname = argv[2]; 778 argc--; 779 argv++; 780 } else if (!strcmp(argv[1], "-p")) { 781 dump = true; 782 } else if (!strcmp(argv[1], "-s")) { 783 scan_only = true; 784 } else if (argv[1][0] == '-') { 785 error("unknown option: %s", argv[1]); 786 } else { 787 if (srcname != nil) { 788 error("multiple source files disallowed"); 789 } else { 790 srcname = argv[1]; 791 } 792 } 793 argc--; 794 argv++; 795 } 796 797 if (srcname == nil) { 798 error("no file specified"); 799 } 800 ctx.filename = srcname; 801 802 load(srcname); 803 ctx.linenumber = 1; 804 ctx.lineoffset = 0; 805 // prime the lexer 806 scan(); 807 808 if (scan_only) { 809 ctx.flags |= 1; 810 while (true) { 811 next(); 812 print(); 813 if (ctx.tok == tEOF) { 814 return 0; 815 } 816 } 817 } 818 819 parse_program(); 820 821 return 0; 822 }