Created
February 19, 2015 23:20
-
-
Save astarasikov/32f02c4aac0c852e8204 to your computer and use it in GitHub Desktop.
some stupid expression parser, just a backup
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <assert.h> | |
#include <ctype.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <math.h> | |
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) | |
enum token_type { | |
TOK_INVALID = 0, | |
TOK_NUMBER, | |
TOK_PLUS, | |
TOK_MINUS, | |
TOK_MULT, | |
TOK_DIV, | |
TOK_SYMBOL, | |
TOK_LPAREN, | |
TOK_RPAREN, | |
TOK_COUNT, | |
}; | |
enum { | |
SYM_LIMIT = 64, | |
EXPR_LIMIT = 128, | |
}; | |
struct expr { | |
enum token_type type; | |
union { | |
char s_val[SYM_LIMIT + 1]; | |
float f_val; | |
} val; | |
}; | |
static void dump_expr(struct expr *e) | |
{ | |
#define TNAME(name) [TOK_ ## name] = #name | |
const char *names[TOK_COUNT] = { | |
TNAME(INVALID), | |
TNAME(NUMBER), | |
TNAME(PLUS), | |
TNAME(MINUS), | |
TNAME(MULT), | |
TNAME(DIV), | |
TNAME(SYMBOL), | |
TNAME(LPAREN), | |
TNAME(RPAREN), | |
}; | |
#undef TNAME | |
if (e->type == TOK_SYMBOL) { | |
printf("Expr: symbol '%s'\n", e->val.s_val); | |
} | |
else if (e->type == TOK_NUMBER) { | |
printf("Expr: type='%s' val='%f'\n", names[e->type % TOK_COUNT], e->val.f_val); | |
} | |
else { | |
printf("Expr: type='%s'\n", names[e->type % TOK_COUNT]); | |
} | |
} | |
static const char* eat_space(const char *str) | |
{ | |
while (str && (*str == ' ')) { | |
++str; | |
} | |
return str; | |
} | |
static const char* next_token(const char *str, struct expr *e) | |
{ | |
float val = 0; | |
switch (*str) { | |
case '0' ... '9': | |
case '.': | |
sscanf(str, "%f", &val); | |
while ((*str >= '0' && *str <= '9') || (*str == '.')) { | |
++str; | |
} | |
e->type = TOK_NUMBER; | |
e->val.f_val = val; | |
break; | |
case '+': | |
++str; | |
e->type = TOK_PLUS; | |
break; | |
case '-': | |
++str; | |
e->type = TOK_MINUS; | |
break; | |
case '*': | |
++str; | |
e->type = TOK_MULT; | |
break; | |
case '/': | |
++str; | |
e->type = TOK_DIV; | |
break; | |
case '(': | |
++str; | |
e->type = TOK_LPAREN; | |
break; | |
case ')': | |
++str; | |
e->type = TOK_RPAREN; | |
break; | |
case 'a' ... 'z': | |
case 'A' ... 'Z': | |
{ | |
int i = 0; | |
do { | |
if (i >= SYM_LIMIT) { | |
puts("symbol name too long"); | |
exit(-1); | |
} | |
if (!isalpha(tolower(*str))) { | |
break; | |
} | |
e->val.s_val[i++] = *str++; | |
} while (i < SYM_LIMIT); | |
e->val.s_val[i] = 0; | |
e->type = TOK_SYMBOL; | |
} | |
break; | |
} | |
return str; | |
} | |
#ifndef M_PI | |
#define M_PI acos(-1.0) | |
#endif | |
static const struct { | |
const char *name; | |
const float val; | |
} parser_const_symtab[] = { | |
{ | |
.name = "PI", | |
.val = M_PI, | |
}, | |
{ | |
.name = "yolo", | |
.val = 4010, | |
} | |
}; | |
static struct expr lookup_symbol(struct expr *expr) | |
{ | |
struct expr retval = {}; | |
size_t idx = 0; | |
for (idx = 0; idx < ARRAY_SIZE(parser_const_symtab); idx++) { | |
if (!strcmp(expr->val.s_val, parser_const_symtab[idx].name)) { | |
retval.type = TOK_NUMBER; | |
retval.val.f_val = parser_const_symtab[idx].val; | |
break; | |
} | |
} | |
return retval; | |
} | |
static struct expr eval(struct expr *input, size_t count) | |
{ | |
struct expr stack[EXPR_LIMIT] = {}; | |
size_t stack_depth = 0; | |
size_t idx; | |
for (idx = 0; idx < count && input[idx].type != TOK_INVALID; idx++) { | |
printf(">>> expr %lu ", idx); | |
dump_expr(input + idx); | |
enum token_type cur_type = input[idx].type; | |
switch (cur_type) { | |
case TOK_NUMBER: | |
stack[stack_depth++] = input[idx]; | |
break; | |
case TOK_SYMBOL: | |
stack[stack_depth++] = lookup_symbol(input + idx); | |
break; | |
case TOK_PLUS: | |
case TOK_MINUS: | |
case TOK_MULT: | |
case TOK_DIV: | |
stack[stack_depth++] = eval(input + idx + 1, count - idx - 2); | |
printf("Binary operation: popping operands\n"); | |
dump_expr(&stack[stack_depth - 1]); | |
dump_expr(&stack[stack_depth - 2]); | |
assert(stack_depth >= 2); | |
assert(stack_depth < EXPR_LIMIT); | |
assert(stack[stack_depth - 1].type == TOK_NUMBER); | |
assert(stack[stack_depth - 2].type == TOK_NUMBER); | |
stack[stack_depth].type = TOK_NUMBER; | |
switch(cur_type) { | |
case TOK_MULT: | |
stack[stack_depth - 2].val.f_val = | |
stack[stack_depth - 2].val.f_val * | |
stack[stack_depth - 1].val.f_val; | |
break; | |
case TOK_DIV: | |
stack[stack_depth - 2].val.f_val = | |
stack[stack_depth - 2].val.f_val / | |
stack[stack_depth - 1].val.f_val; | |
break; | |
case TOK_PLUS: | |
stack[stack_depth - 2].val.f_val = | |
stack[stack_depth - 2].val.f_val + | |
stack[stack_depth - 1].val.f_val; | |
break; | |
case TOK_MINUS: | |
stack[stack_depth - 2].val.f_val = | |
stack[stack_depth - 2].val.f_val - | |
stack[stack_depth - 1].val.f_val; | |
break; | |
default: | |
// not reached | |
printf("unhandled token type\n"); | |
exit(-1); | |
break; | |
} | |
--stack_depth; | |
break; | |
case TOK_LPAREN: | |
stack[stack_depth++] = eval(input + idx + 1, count - idx - 2); | |
break; | |
case TOK_RPAREN: | |
//assert(stack_depth); | |
//--stack_depth; | |
return stack[0]; | |
break; | |
default: | |
printf("Unhandled token type\n"); | |
exit(-1); | |
break; | |
} | |
} | |
return stack[0]; | |
} | |
static void parse(const char *str) | |
{ | |
size_t expr_count = 0, idx = 0; | |
struct expr tree[EXPR_LIMIT] = {}; | |
printf("===\nParse: <<%s>>\n", str); | |
while ((expr_count < EXPR_LIMIT) && *str && (str = eat_space(str))) { | |
str = next_token(str, tree + expr_count); | |
++expr_count; | |
} | |
for (idx = 0; idx < expr_count; idx++) { | |
dump_expr(tree + idx); | |
} | |
printf("===\nevaluating\n===\n"); | |
struct expr result = eval(tree, EXPR_LIMIT); | |
printf("===\nresult\n"); | |
dump_expr(&result); | |
} | |
int main() | |
{ | |
parse("1 + 2"); | |
parse("1 + 2 * 3"); | |
parse("1 + (2 * 3)"); | |
parse("1 + 2 * ( 10 * 50)"); | |
parse("1 + 2 * (100)"); | |
parse("1 + 2 / 3"); | |
parse("1 + 2 / (4 * ( 10 * 50))"); | |
parse("1 + 2 / (4 * ( 10 * PI))"); | |
parse("1 + 2 / (4 * ( 10 * PI * yolo))"); | |
//parse("1 + 2 / (4 * sin( 10 * 50))"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment