%{ /* compile: leg -o calc.c calc.leg * cc -o calc calc.c * * run: echo "2+3" | ./calc */ #include union u_t { int ival; char *sval; }; #define YYSTYPE union u_t YYSTYPE yylval; #define SYMBOL_TABLE_CHUNK 1024 typedef struct symbol_t { char *ident; bool defined; int value; } symbol_t; typedef struct table_t { symbol_t **array; int size; int capacity; } table_t; table_t table; typedef struct bsearch_t { int pos; bool found; } bsearch_t; char *ident_buf; bsearch_t binary_search(symbol_t *arr[], int l, int r, char *ident) { while (r >= l) { int mid = l + (r - l) / 2; int cmpres = strcmp(arr[mid]->ident, ident); if (cmpres > 0) { r = mid - 1; } else if (cmpres < 0) { l = mid + 1; } else { bsearch_t res = { mid, true }; return res; } } bsearch_t res = { l, false }; return res; } int insert(table_t *table, symbol_t *element, int pos) { if (pos < 0 || pos > table->size) { return -1; } if (table->size >= table->capacity) { table->array = realloc(table->array, sizeof(symbol_t *) * (table->capacity + SYMBOL_TABLE_CHUNK)); if (table->array == NULL) { printf("Error: running out of memory\n"); exit(1); } table->capacity += SYMBOL_TABLE_CHUNK; } for (int i = table->size; i > pos; i--) { table->array[i] = table->array[i-1]; } table->array[pos] = element; return ++(table->size); } symbol_t *intern(char *ident, bool create) { bsearch_t res = binary_search(table.array, 0, table.size - 1, ident); if (res.found) { return table.array[res.pos]; } if (create) { symbol_t *new_symbol = malloc(sizeof(symbol_t)); new_symbol->ident = strdup(ident); new_symbol->defined = false; insert(&table, new_symbol, res.pos); return new_symbol; } else { return NULL; } } symbol_t *update_value(symbol_t * s, int value) { s->value = value; s->defined = true; return s; } void init_table() { table.array = malloc(sizeof(symbol_t *) * SYMBOL_TABLE_CHUNK); if (table.array == NULL) { printf("Error: running out of memory\n"); exit(1); } table.size = 0; table.capacity = SYMBOL_TABLE_CHUNK; } %} start = e:exp { yylval = e } exp = - (a:assign { $$ = a } | s:sum { $$ = s } ) assign = l:IDENT { ident_buf = strdup(l.sval) } EQUAL n:sum { symbol_t *nsymb = intern(ident_buf, true); $$.ival = update_value(nsymb, n.ival)->value; free(ident_buf) } sum = PLUS* l:prod ( PLUS+ r:prod { l.ival += r.ival } | MINUS r:prod { l.ival -= r.ival } )* { $$.ival = l.ival } prod = l:neg ( MULTI r:neg { l.ival *= r.ival } | DIVIDE r:neg { l.ival /= r.ival } | MODULO r:neg { l.ival %= r.ival } )* { $$.ival = l.ival } neg = MINUS n:neg { $$.ival = -n.ival } | n:value { $$.ival = n.ival } value = n:NUMBER { $$.ival = n.ival } | l:IDENT { symbol_t *fsymb = intern(l.sval, false); $$.ival = (fsymb != NULL) ? (fsymb->defined == true) ? fsymb->value : 0 : 0 } - = [ \t]* NUMBER = < [0-9]+ > - { $$.ival = atoi(yytext) } PLUS = '+' - MINUS = '-' - MULTI = '*' - DIVIDE = '/' - MODULO = '%' - EQUAL = '=' - IDENT = < [a-zA-Z]+ > - { $$.sval = yytext } %% int main(int argc, char **argv) { init_table(); while (yyparse()) { printf("%d\n", yylval.ival); } return 0; }