|
| 1 | +// Lexer is a classical example of usecase for coroutines. |
| 2 | +// This is a *very* simple and basic lexer that |
| 3 | +// can lex single digit integers, + and -. |
| 4 | +// The example would be better if we could return values |
| 5 | +// when we yield (kind of like a generator). But it is what it is. |
| 6 | + |
| 7 | +#include <stdio.h> |
| 8 | +#include <assert.h> |
| 9 | +#include <stdbool.h> |
| 10 | + |
| 11 | +#include <coroutine.h> |
| 12 | + |
| 13 | +typedef enum { |
| 14 | + TK_INT, |
| 15 | + TK_OP, |
| 16 | + TK_EOF |
| 17 | +} TokenKind; |
| 18 | + |
| 19 | +typedef union { |
| 20 | + char tk_op; |
| 21 | + int tk_int; |
| 22 | +} TokenValue; |
| 23 | + |
| 24 | +TokenKind token_kind = TK_EOF; |
| 25 | +TokenValue token_value = {0}; |
| 26 | + |
| 27 | +void lex(void* input_void) { |
| 28 | + if (input_void == NULL) return; |
| 29 | + |
| 30 | + const char* input = input_void; |
| 31 | + |
| 32 | + while(true) { |
| 33 | + switch(*input) { |
| 34 | + // Numba |
| 35 | + case '0': case '1': case '2': case '3': case '4': |
| 36 | + case '5': case '6': case '7': case '8': case '9': { |
| 37 | + token_kind = TK_INT; |
| 38 | + token_value.tk_int = *input - '0'; |
| 39 | + } break; |
| 40 | + |
| 41 | + // Operators |
| 42 | + case '+': case '-': { |
| 43 | + token_kind = TK_OP; |
| 44 | + token_value.tk_op = *input; |
| 45 | + } break; |
| 46 | + |
| 47 | + default: { |
| 48 | + token_kind = TK_EOF; |
| 49 | + return; |
| 50 | + } |
| 51 | + } |
| 52 | + input++; |
| 53 | + |
| 54 | + // For every token we consume, we yield control back to the caller (a parser, I guess). |
| 55 | + coroutine_yield(); |
| 56 | + } |
| 57 | +} |
| 58 | + |
| 59 | +int main(int argc, char* argv[]){ |
| 60 | + if (argc != 2) { |
| 61 | + printf("Usage: %s <input-text>\n", argv[0]); |
| 62 | + return 1; |
| 63 | + } |
| 64 | + |
| 65 | + coroutine_init(); |
| 66 | + { |
| 67 | + coroutine_go(lex, argv[1]); |
| 68 | + |
| 69 | + // Consume those tokens |
| 70 | + bool quit = false; |
| 71 | + while(!quit && coroutine_alive() > 1){ |
| 72 | + // Yield control to the lexer. |
| 73 | + // It will lex and yield control back to here. |
| 74 | + coroutine_yield(); |
| 75 | + switch(token_kind){ |
| 76 | + case TK_INT: { printf("TK_INT: %d\n", token_value.tk_int); } break; |
| 77 | + case TK_OP: { printf("TK_OP: %c\n", token_value.tk_op); } break; |
| 78 | + default: { printf("Done!\n"); quit = true; } break; |
| 79 | + } |
| 80 | + } |
| 81 | + } |
| 82 | + coroutine_finish(); |
| 83 | + |
| 84 | + return 0; |
| 85 | +} |
0 commit comments