| ... | ... | @@ -8,6 +8,8 @@ |
| 8 | 8 | #include "config.h" |
| 9 | 9 | #include "util.hpp" |
| 10 | 10 | #include "list.hpp" |
| 11 | #include "buffer.hpp" |
| 12 | |
| 11 | 13 | #include <stdio.h> |
| 12 | 14 | #include <string.h> |
| 13 | 15 | #include <stdlib.h> |
| ... | ... | @@ -19,25 +21,6 @@ |
| 19 | 21 | #include <sys/stat.h> |
| 20 | 22 | #include <unistd.h> |
| 21 | 23 | |
| 22 | | struct Buf { |
| 23 | | int len; |
| 24 | | char ptr[0]; |
| 25 | | }; |
| 26 | | |
| 27 | | static Buf *alloc_buf(int size) { |
| 28 | | Buf *buf = (Buf *)allocate_nonzero<char>(sizeof(Buf) + size + 1); |
| 29 | | buf->len = size; |
| 30 | | buf->ptr[buf->len] = 0; |
| 31 | | return buf; |
| 32 | | } |
| 33 | | |
| 34 | | /* |
| 35 | | static void fprint_buf(FILE *f, Buf *buf) { |
| 36 | | if (fwrite(buf->ptr, 1, buf->len, f)) |
| 37 | | zig_panic("error writing: %s", strerror(errno)); |
| 38 | | } |
| 39 | | */ |
| 40 | | |
| 41 | 24 | static int usage(char *arg0) { |
| 42 | 25 | fprintf(stderr, "Usage: %s --output outfile code.zig\n" |
| 43 | 26 | "Other options:\n" |
| ... | ... | @@ -46,7 +29,7 @@ static int usage(char *arg0) { |
| 46 | 29 | return EXIT_FAILURE; |
| 47 | 30 | } |
| 48 | 31 | |
| 49 | | static struct Buf *fetch_file(FILE *f) { |
| 32 | static Buf *fetch_file(FILE *f) { |
| 50 | 33 | int fd = fileno(f); |
| 51 | 34 | struct stat st; |
| 52 | 35 | if (fstat(fd, &st)) |
| ... | ... | @@ -56,9 +39,9 @@ static struct Buf *fetch_file(FILE *f) { |
| 56 | 39 | zig_panic("file too big"); |
| 57 | 40 | int size = (int)big_size; |
| 58 | 41 | |
| 59 | | Buf *buf = alloc_buf(size); |
| 60 | | size_t amt_read = fread(buf->ptr, 1, buf->len, f); |
| 61 | | if (amt_read != (size_t)buf->len) |
| 42 | Buf *buf = buf_alloc_fixed(size); |
| 43 | size_t amt_read = fread(buf_ptr(buf), 1, buf_len(buf), f); |
| 44 | if (amt_read != (size_t)buf_len(buf)) |
| 62 | 45 | zig_panic("error reading: %s", strerror(errno)); |
| 63 | 46 | |
| 64 | 47 | return buf; |
| ... | ... | @@ -138,6 +121,12 @@ static struct Buf *fetch_file(FILE *f) { |
| 138 | 121 | case 'Y': \ |
| 139 | 122 | case 'Z' |
| 140 | 123 | |
| 124 | #define SYMBOL_CHAR \ |
| 125 | ALPHA: \ |
| 126 | case DIGIT: \ |
| 127 | case '_' |
| 128 | |
| 129 | |
| 141 | 130 | enum TokenId { |
| 142 | 131 | TokenIdDirective, |
| 143 | 132 | TokenIdSymbol, |
| ... | ... | @@ -157,6 +146,8 @@ struct Token { |
| 157 | 146 | TokenId id; |
| 158 | 147 | int start_pos; |
| 159 | 148 | int end_pos; |
| 149 | int start_line; |
| 150 | int start_column; |
| 160 | 151 | }; |
| 161 | 152 | |
| 162 | 153 | enum TokenizeState { |
| ... | ... | @@ -178,10 +169,21 @@ struct Tokenize { |
| 178 | 169 | |
| 179 | 170 | __attribute__ ((format (printf, 2, 3))) |
| 180 | 171 | static void tokenize_error(Tokenize *t, const char *format, ...) { |
| 172 | int line; |
| 173 | int column; |
| 174 | if (t->cur_tok) { |
| 175 | line = t->cur_tok->start_line + 1; |
| 176 | column = t->cur_tok->start_column + 1; |
| 177 | } else { |
| 178 | line = t->line + 1; |
| 179 | column = t->column + 1; |
| 180 | } |
| 181 | |
| 181 | 182 | va_list ap; |
| 182 | 183 | va_start(ap, format); |
| 183 | | fprintf(stderr, "Error. Line %d, column %d: ", t->line + 1, t->column + 1); |
| 184 | fprintf(stderr, "Error: Line %d, column %d: ", line, column); |
| 184 | 185 | vfprintf(stderr, format, ap); |
| 186 | fprintf(stderr, "\n"); |
| 185 | 187 | va_end(ap); |
| 186 | 188 | exit(EXIT_FAILURE); |
| 187 | 189 | } |
| ... | ... | @@ -190,6 +192,8 @@ static void begin_token(Tokenize *t, TokenId id) { |
| 190 | 192 | assert(!t->cur_tok); |
| 191 | 193 | t->tokens->add_one(); |
| 192 | 194 | Token *token = &t->tokens->last(); |
| 195 | token->start_line = t->line; |
| 196 | token->start_column = t->column; |
| 193 | 197 | token->id = id; |
| 194 | 198 | token->start_pos = t->pos; |
| 195 | 199 | t->cur_tok = token; |
| ... | ... | @@ -205,11 +209,24 @@ static void put_back(Tokenize *t, int count) { |
| 205 | 209 | t->pos -= count; |
| 206 | 210 | } |
| 207 | 211 | |
| 212 | static void end_directive(Tokenize *t) { |
| 213 | assert(t->cur_tok); |
| 214 | t->cur_tok->end_pos = t->pos; |
| 215 | t->cur_tok = nullptr; |
| 216 | t->state = TokenizeStateStart; |
| 217 | } |
| 218 | |
| 219 | static void end_symbol(Tokenize *t) { |
| 220 | put_back(t, 1); |
| 221 | end_token(t); |
| 222 | t->state = TokenizeStateStart; |
| 223 | } |
| 224 | |
| 208 | 225 | static ZigList<Token> *tokenize(Buf *buf) { |
| 209 | 226 | Tokenize t = {0}; |
| 210 | 227 | t.tokens = allocate<ZigList<Token>>(1); |
| 211 | | for (t.pos = 0; t.pos < buf->len; t.pos += 1) { |
| 212 | | uint8_t c = buf->ptr[t.pos]; |
| 228 | for (t.pos = 0; t.pos < buf_len(buf); t.pos += 1) { |
| 229 | uint8_t c = buf_ptr(buf)[t.pos]; |
| 213 | 230 | switch (t.state) { |
| 214 | 231 | case TokenizeStateStart: |
| 215 | 232 | switch (c) { |
| ... | ... | @@ -232,7 +249,7 @@ static ZigList<Token> *tokenize(Buf *buf) { |
| 232 | 249 | end_token(&t); |
| 233 | 250 | break; |
| 234 | 251 | case ')': |
| 235 | | begin_token(&t, TokenIdLParen); |
| 252 | begin_token(&t, TokenIdRParen); |
| 236 | 253 | end_token(&t); |
| 237 | 254 | break; |
| 238 | 255 | case ',': |
| ... | ... | @@ -269,22 +286,15 @@ static ZigList<Token> *tokenize(Buf *buf) { |
| 269 | 286 | break; |
| 270 | 287 | case TokenizeStateDirective: |
| 271 | 288 | if (c == '\n') { |
| 272 | | assert(t.cur_tok); |
| 273 | | t.cur_tok->end_pos = t.pos; |
| 274 | | t.cur_tok = nullptr; |
| 275 | | t.state = TokenizeStateStart; |
| 289 | end_directive(&t); |
| 276 | 290 | } |
| 277 | 291 | break; |
| 278 | 292 | case TokenizeStateSymbol: |
| 279 | 293 | switch (c) { |
| 280 | | case ALPHA: |
| 281 | | case DIGIT: |
| 282 | | case '_': |
| 294 | case SYMBOL_CHAR: |
| 283 | 295 | break; |
| 284 | 296 | default: |
| 285 | | put_back(&t, 1); |
| 286 | | end_token(&t); |
| 287 | | t.state = TokenizeStateStart; |
| 297 | end_symbol(&t); |
| 288 | 298 | break; |
| 289 | 299 | } |
| 290 | 300 | break; |
| ... | ... | @@ -303,9 +313,7 @@ static ZigList<Token> *tokenize(Buf *buf) { |
| 303 | 313 | case DIGIT: |
| 304 | 314 | break; |
| 305 | 315 | default: |
| 306 | | put_back(&t, 1); |
| 307 | | end_token(&t); |
| 308 | | t.state = TokenizeStateStart; |
| 316 | end_symbol(&t); |
| 309 | 317 | break; |
| 310 | 318 | } |
| 311 | 319 | break; |
| ... | ... | @@ -317,6 +325,24 @@ static ZigList<Token> *tokenize(Buf *buf) { |
| 317 | 325 | t.column += 1; |
| 318 | 326 | } |
| 319 | 327 | } |
| 328 | // EOF |
| 329 | switch (t.state) { |
| 330 | case TokenizeStateStart: |
| 331 | break; |
| 332 | case TokenizeStateDirective: |
| 333 | end_directive(&t); |
| 334 | break; |
| 335 | case TokenizeStateSymbol: |
| 336 | end_symbol(&t); |
| 337 | break; |
| 338 | case TokenizeStateString: |
| 339 | tokenize_error(&t, "unterminated string"); |
| 340 | break; |
| 341 | case TokenizeStateNumber: |
| 342 | end_symbol(&t); |
| 343 | break; |
| 344 | } |
| 345 | assert(!t.cur_tok); |
| 320 | 346 | return t.tokens; |
| 321 | 347 | } |
| 322 | 348 | |
| ... | ... | @@ -342,11 +368,118 @@ static void print_tokens(Buf *buf, ZigList<Token> *tokens) { |
| 342 | 368 | for (int i = 0; i < tokens->length; i += 1) { |
| 343 | 369 | Token *token = &tokens->at(i); |
| 344 | 370 | printf("%s ", token_name(token)); |
| 345 | | fwrite(buf->ptr + token->start_pos, 1, token->end_pos - token->start_pos, stdout); |
| 371 | fwrite(buf_ptr(buf) + token->start_pos, 1, token->end_pos - token->start_pos, stdout); |
| 346 | 372 | printf("\n"); |
| 347 | 373 | } |
| 348 | 374 | } |
| 349 | 375 | |
| 376 | struct Preprocess { |
| 377 | Buf *out_buf; |
| 378 | Buf *in_buf; |
| 379 | Token *token; |
| 380 | }; |
| 381 | |
| 382 | __attribute__ ((format (printf, 2, 3))) |
| 383 | static void preprocess_error(Preprocess *p, const char *format, ...) { |
| 384 | va_list ap; |
| 385 | va_start(ap, format); |
| 386 | fprintf(stderr, "Error: Line %d, column %d: ", p->token->start_line + 1, p->token->start_column + 1); |
| 387 | vfprintf(stderr, format, ap); |
| 388 | fprintf(stderr, "\n"); |
| 389 | va_end(ap); |
| 390 | exit(EXIT_FAILURE); |
| 391 | } |
| 392 | |
| 393 | enum IncludeState { |
| 394 | IncludeStateStart, |
| 395 | IncludeStateQuote, |
| 396 | }; |
| 397 | |
| 398 | static void render_include(Preprocess *p, Buf *include_path, char unquote_char) { |
| 399 | fprintf(stderr, "render_include \"%s\" '%c'\n", buf_ptr(include_path), unquote_char); |
| 400 | } |
| 401 | |
| 402 | static void parse_and_render_include(Preprocess *p, Buf *directive_buf, int pos) { |
| 403 | int state = IncludeStateStart; |
| 404 | char unquote_char; |
| 405 | int quote_start_pos; |
| 406 | for (; pos < buf_len(directive_buf); pos += 1) { |
| 407 | uint8_t c = buf_ptr(directive_buf)[pos]; |
| 408 | switch (state) { |
| 409 | case IncludeStateStart: |
| 410 | switch (c) { |
| 411 | case WHITESPACE: |
| 412 | break; |
| 413 | case '<': |
| 414 | case '"': |
| 415 | state = IncludeStateQuote; |
| 416 | quote_start_pos = pos; |
| 417 | unquote_char = (c == '<') ? '>' : '"'; |
| 418 | break; |
| 419 | |
| 420 | } |
| 421 | break; |
| 422 | case IncludeStateQuote: |
| 423 | if (c == unquote_char) { |
| 424 | Buf *include_path = buf_slice(directive_buf, quote_start_pos + 1, pos); |
| 425 | render_include(p, include_path, unquote_char); |
| 426 | return; |
| 427 | } |
| 428 | break; |
| 429 | } |
| 430 | } |
| 431 | preprocess_error(p, "include directive missing path"); |
| 432 | } |
| 433 | |
| 434 | static void render_directive(Preprocess *p, Buf *directive_buf) { |
| 435 | for (int pos = 1; pos < buf_len(directive_buf); pos += 1) { |
| 436 | uint8_t c = buf_ptr(directive_buf)[pos]; |
| 437 | switch (c) { |
| 438 | case SYMBOL_CHAR: |
| 439 | break; |
| 440 | default: |
| 441 | pos -= 1; |
| 442 | Buf *directive_name = buf_from_mem(buf_ptr(directive_buf) + 1, pos); |
| 443 | if (strcmp(buf_ptr(directive_name), "include") == 0) { |
| 444 | parse_and_render_include(p, directive_buf, pos); |
| 445 | } else { |
| 446 | preprocess_error(p, "invalid directive: \"%s\"", buf_ptr(directive_name)); |
| 447 | } |
| 448 | return; |
| 449 | } |
| 450 | } |
| 451 | } |
| 452 | |
| 453 | static void render_token(Preprocess *p) { |
| 454 | Buf *token_buf = buf_slice(p->in_buf, p->token->start_pos, p->token->end_pos); |
| 455 | switch (p->token->id) { |
| 456 | case TokenIdDirective: |
| 457 | render_directive(p, token_buf); |
| 458 | break; |
| 459 | default: |
| 460 | buf_append_buf(p->out_buf, token_buf); |
| 461 | if (p->token->id == TokenIdSemicolon || |
| 462 | p->token->id == TokenIdLBrace || |
| 463 | p->token->id == TokenIdRBrace) |
| 464 | { |
| 465 | buf_append_str(p->out_buf, "\n", -1); |
| 466 | } else { |
| 467 | buf_append_str(p->out_buf, " ", -1); |
| 468 | } |
| 469 | } |
| 470 | } |
| 471 | |
| 472 | static Buf *preprocess(Buf *in_buf, ZigList<Token> *tokens) { |
| 473 | Preprocess p = {0}; |
| 474 | p.out_buf = buf_alloc(); |
| 475 | p.in_buf = in_buf; |
| 476 | for (int i = 0; i < tokens->length; i += 1) { |
| 477 | p.token = &tokens->at(i); |
| 478 | render_token(&p); |
| 479 | } |
| 480 | return p.out_buf; |
| 481 | } |
| 482 | |
| 350 | 483 | int main(int argc, char **argv) { |
| 351 | 484 | char *arg0 = argv[0]; |
| 352 | 485 | char *in_file = NULL; |
| ... | ... | @@ -386,14 +519,19 @@ int main(int argc, char **argv) { |
| 386 | 519 | zig_panic("unable to open %s for reading: %s\n", in_file, strerror(errno)); |
| 387 | 520 | } |
| 388 | 521 | |
| 389 | | struct Buf *in_data = fetch_file(in_f); |
| 522 | Buf *in_data = fetch_file(in_f); |
| 390 | 523 | |
| 391 | | fprintf(stderr, "%s\n", in_data->ptr); |
| 524 | fprintf(stderr, "Original source:\n%s\n", buf_ptr(in_data)); |
| 392 | 525 | |
| 393 | 526 | ZigList<Token> *tokens = tokenize(in_data); |
| 394 | 527 | |
| 528 | fprintf(stderr, "\nTokens:\n"); |
| 395 | 529 | print_tokens(in_data, tokens); |
| 396 | 530 | |
| 531 | Buf *preprocessed_source = preprocess(in_data, tokens); |
| 532 | |
| 533 | fprintf(stderr, "\nPreprocessed source:\n%s\n", buf_ptr(preprocessed_source)); |
| 534 | |
| 397 | 535 | |
| 398 | 536 | return EXIT_SUCCESS; |
| 399 | 537 | } |