Skip to content

Instantly share code, notes, and snippets.

@rw-r-r-0644
Created November 28, 2021 19:09
Show Gist options
  • Save rw-r-r-0644/481c91e412873d38b12992c66a6f488c to your computer and use it in GitHub Desktop.
Save rw-r-r-0644/481c91e412873d38b12992c66a6f488c to your computer and use it in GitHub Desktop.
Math expression parser thing from 2/8/2020, working state unknown
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
typedef enum statement_type statement_type_t;
typedef enum operators_op0 operators_op0_t;
typedef enum operators_op1 operators_op1_t;
typedef enum operators_op2 operators_op2_t;
typedef struct statement statement_t;
void statementslistfree(statement_t *statements);
int parsenum(const char *str, statement_t *out, int *parsed);
int parseop(const char *str, statement_t *out, int *parsed);
int parsepri(const char *str, statement_t *out, int *parsed);
statement_t *statementparse(const char *str, int *parsed);
statement_t *statementslistparse(const char *str, int size);
int ismathop(statement_t *st);
void statementslistcreateimplied(statement_t *statements);
double handleop0(statement_t *lo, statement_t *op, statement_t *ro);
double handleop1(statement_t *lo, statement_t *op, statement_t *ro);
double handleop2(statement_t *lo, statement_t *op, statement_t *ro);
void solveops(statement_t *statements, statement_type_t type);
double solve(statement_t *statements);
double mathexpr(const char *str);
enum statement_type
{
TYPE_PRI,
TYPE_NUM,
TYPE_OP0,
TYPE_OP1,
TYPE_OP2,
};
enum operators_op0
{
OP0_ADD,
OP0_SUB,
};
enum operators_op1
{
OP1_MUL,
OP1_DIV,
};
enum operators_op2
{
OP2_EXP,
};
struct statement
{
statement_type_t type;
union
{
statement_t *pri;
operators_op0_t op0;
operators_op1_t op1;
operators_op2_t op2;
double num;
} data;
statement_t *next;
};
void printspc(int count)
{
while(count-- > 0)
putchar(' ');
}
void statementslistprintrec(statement_t *statements, int rec)
{
for (statement_t *st = statements; st; st = st->next)
{
printspc(rec);
switch(st->type)
{
case TYPE_PRI:
{
printf("[TYPE_PRI]\n");
statementslistprintrec(st->data.pri, rec + 3);
continue;
}
case TYPE_NUM:
{
printf("[TYPE_NUM] %f\n", st->data.num);
continue;
}
case TYPE_OP0:
{
printf("[TYPE_OP0] %s\n", (st->data.op0 == OP0_ADD) ? "+" : "-");
continue;
}
case TYPE_OP1:
{
printf("[TYPE_OP1] %s\n", (st->data.op1 == OP1_MUL) ? "*" : "/");
continue;
}
case TYPE_OP2:
{
printf("[TYPE_OP2] %s\n", "^");
continue;
}
default:
{
printf("[????????]\n");
continue;
}
}
}
}
void statementslistprint(statement_t *statements)
{
statementslistprintrec(statements, 0);
}
void statementslistfree(statement_t *statements)
{
statement_t *st = statements, *pv;
while(st)
{
pv = st;
st = st->next;
free(pv);
}
}
/* parse math operators */
int parseop(const char *str, statement_t *out, int *parsed)
{
*parsed = 1;
switch(*str)
{
case '+':
{
out->type = TYPE_OP0;
out->data.op0 = OP0_ADD;
return 1;
}
case '-':
{
out->type = TYPE_OP0;
out->data.op0 = OP0_SUB;
return 1;
}
case '*':
{
out->type = TYPE_OP1;
out->data.op1 = OP1_MUL;
return 1;
}
case '/':
{
out->type = TYPE_OP1;
out->data.op1 = OP1_DIV;
return 1;
}
case '^':
{
out->type = TYPE_OP2;
out->data.op2 = OP2_EXP;
return 1;
}
}
return 0;
}
/* parse numbers */
int parsenum(const char *str, statement_t *out, int *parsed)
{
double num;
if (sscanf(str, "%lf %n", &num, parsed) != 1)
return 0;
out->type = TYPE_NUM;
out->data.num = num;
return 1;
}
/* parse parenthesis */
int parsepri(const char *str, statement_t *out, int *parsed)
{
int size, count;
if (*str != '(')
return 0;
for (size = 1, count = 1; count && str[size]; size++)
{
if (str[size] == '(')
count++;
if (str[size] == ')')
count--;
}
out->type = TYPE_PRI;
out->data.pri = statementslistparse(str + 1, size - 1 - !count);
*parsed = size;
return 1;
}
statement_t *statementparse(const char *str, int *parsed)
{
statement_t *st;
/* skip whitespaces */
if (*str == ' ')
{
*parsed = 1;
return NULL;
}
/* parse statement */
st = malloc(sizeof(statement_t));
if (parseop(str, st, parsed))
return st;
if (parsenum(str, st, parsed))
return st;
if (parsepri(str, st, parsed))
return st;
free(st);
*parsed = 1;
return NULL;
}
statement_t *statementslistparse(const char *str, int size)
{
statement_t *beg = NULL, *pv = NULL, *st = NULL;
int i = 0, parsed = 0;
beg = malloc(sizeof(statement_t));
beg->type = TYPE_NUM;
beg->data.num = 0.0;
pv = malloc(sizeof(statement_t));
pv->type = TYPE_OP0;
pv->data.op0 = OP0_ADD;
beg->next = pv;
for (i = 0; (i < size) && str[i]; i += parsed)
{
st = statementparse(&str[i], &parsed);
if (!st)
continue;
pv->next = st;
pv = st;
}
pv->next = NULL;
return beg;
}
int ismathop(statement_t *st)
{
return ((st->type == TYPE_OP0) ||
(st->type == TYPE_OP1) ||
(st->type == TYPE_OP2));
}
/* add implied multiplications for numbers immediatly before and after parenthesis */
int handleimpliedmul(statement_t *st0, statement_t *st1)
{
statement_t *new;
if (!st0 || !st1)
return 0;
if (ismathop(st0) || ismathop(st1))
return 0;
if ((st0->type != TYPE_PRI) && (st1->type != TYPE_PRI))
return 0;
new = malloc(sizeof(statement_t));
new->type = TYPE_OP1;
new->data.op1 = OP1_MUL;
st0->next = new;
new->next = st1;
return 1;
}
/* handle minus used as number negate, rather than operator, without parenthesis */
int handleimpliedneg(statement_t *st0, statement_t *st1, statement_t *st2)
{
if (!st0 || !st1 || !st2)
return 0;
if (!ismathop(st0))
return 0;
if ((st1->type != TYPE_OP0) || (st1->data.op0 != OP0_SUB))
return 0;
if (st2->type != TYPE_NUM)
return 0;
st0->next = st2;
st2->data.num = -st2->data.num;
free(st1);
return 1;
}
void statementslistcreateimplied(statement_t *statements)
{
statement_t *st, *pv, *new;
statement_t *st0, *st1, *st2, *next;
double num;
for (st = statements; st; st = st->next)
{
st0 = st;
st1 = st0 ? st0->next : NULL;
st2 = st1 ? st1->next : NULL;
if (st->type == TYPE_PRI)
statementslistcreateimplied(st->data.pri);
if (handleimpliedmul(st0, st1))
continue;
if (handleimpliedneg(st0, st1, st2))
continue;
}
}
/*
* math operations handling
*/
double handleop0(statement_t *lo, statement_t *op, statement_t *ro)
{
double num = 0.0;
switch (op->data.op0) {
case OP0_ADD:
{
num = lo->data.num + ro->data.num;
break;
}
case OP0_SUB:
{
num = lo->data.num - ro->data.num;
break;
}
}
return num;
}
double handleop1(statement_t *lo, statement_t *op, statement_t *ro)
{
double num = 0.0;
switch (op->data.op1)
{
case OP1_MUL:
{
num = lo->data.num * ro->data.num;
break;
}
case OP1_DIV:
{
num = lo->data.num / ro->data.num;
break;
}
}
return num;
}
double handleop2(statement_t *lo, statement_t *op, statement_t *ro)
{
double num = 0.0;
switch (op->data.op2)
{
case OP2_EXP:
{
num = pow(lo->data.num, ro->data.num);
break;
}
}
return num;
}
/*
* handle numeric operations by replacing the
* [left number] -> [operator] -> [right number]
* set with their [resulting number]
*/
void solveops(statement_t *st, statement_type_t type)
{
statement_t *st0, *st1, *st2;
double num;
while(st)
{
st0 = st;
st1 = st0 ? st0->next : NULL;
st2 = st1 ? st1->next : NULL;
if ((!st0 || (st0->type != TYPE_NUM)) ||
(!st1 || (st1->type != type)) ||
(!st2 || (st2->type != TYPE_NUM)))
{
st = st->next;
continue;
}
switch (type)
{
case TYPE_OP2:
{
num = handleop2(st0, st1, st2);
break;
}
case TYPE_OP1:
{
num = handleop1(st0, st1, st2);
break;
}
case TYPE_OP0:
{
num = handleop0(st0, st1, st2);
break;
}
};
st0->type = TYPE_NUM;
st0->data.num = num;
st0->next = st2->next;
free(st1);
free(st2);
}
}
/*
* computes the solution to a list of statements
* and returns their result
*
* statements are handled in the following order:
* 1) parenthesis
* 2) exponentiation
* 3) multiplication, division
* 4) addition, subtration
*
* this function is recursively called to handle parenthesis
*/
double solve(statement_t *statements)
{
statement_t *st;
double num;
/*
* recursively solve prioritized operation lists and
* replace them with the numeric value of the result
*/
for (st = statements; st; st = st->next)
{
if (st->type == TYPE_PRI)
{
num = solve(st->data.pri);
statementslistfree(st->data.pri);
st->type = TYPE_NUM;
st->data.num = num;
}
}
/*
* solve maths operations in the described order
*/
solveops(statements, TYPE_OP2);
solveops(statements, TYPE_OP1);
solveops(statements, TYPE_OP0);
/*
* the list should now contain a single statement,
* the numeric value of the final result
*/
if (statements->type != TYPE_NUM)
{
printf("something went wrong\n");
exit(0);
}
return statements->data.num;
}
double mathexpr(const char *str)
{
statement_t *statements;
double result;
statements = statementslistparse(str, strlen(str) + 1);
statementslistcreateimplied(statements);
result = solve(statements);
statementslistfree(statements);
return result;
}
int main(int argc, char *argv[])
{
double result = 0.0;
result = mathexpr(argv[1]);
printf("result: %g %f\n", result, result);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment