Skip to content

Instantly share code, notes, and snippets.

@astarasikov
Created February 19, 2015 23:20
Show Gist options
  • Save astarasikov/32f02c4aac0c852e8204 to your computer and use it in GitHub Desktop.
Save astarasikov/32f02c4aac0c852e8204 to your computer and use it in GitHub Desktop.
some stupid expression parser, just a backup
#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