Changed error handling a bit. te_interp returns NaN for bad expressions.

This commit is contained in:
Lewis Van Winkle
2016-01-23 12:09:38 -06:00
parent b7af2e3751
commit 1ee637afef
5 changed files with 63 additions and 31 deletions

View File

@@ -86,7 +86,7 @@ line. It also does error checking and binds the variables *x* and *y*.
printf("\t%*s^\nError near here", err-1, ""); printf("\t%*s^\nError near here", err-1, "");
} }
/* te_free should always be called after te_compile. */ /* te_free is safe to call on null. */
te_free(n); te_free(n);
return 0; return 0;
@@ -235,9 +235,6 @@ In addition, the following C math functions are also supported:
- All functions/types start with the letters *te*. - All functions/types start with the letters *te*.
- Remember to always call *te_free* on the result of *te_compile*, even if
there is an error.
- If there is an error, you can usually still evaluate the first part of the - If there is an error, you can usually still evaluate the first part of the
expression. This may or may not be useful to you. expression. This may or may not be useful to you.

View File

@@ -32,7 +32,7 @@ int main(int argc, char *argv[])
printf("\t%*s^\nError near here", err-1, ""); printf("\t%*s^\nError near here", err-1, "");
} }
/* te_free should always be called after te_compile. */ /* te_free is safe to call on null. */
te_free(n); te_free(n);
return 0; return 0;

50
test.c
View File

@@ -122,7 +122,26 @@ test_case errors[] = {
}; };
void test1() { const char *nans[] = {
"1+",
"1)",
"(1",
"1**1",
"1*2(+4",
"1*2(1+4",
"a+5",
"A+5",
"Aa+5",
"1^^5",
"1**5",
"sin(cos5",
"5+5error",
"5+5+error",
};
void test_results() {
int i; int i;
for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) { for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
const char *expr = cases[i].expr; const char *expr = cases[i].expr;
@@ -136,7 +155,7 @@ void test1() {
} }
void test2() { void test_syntax() {
int i; int i;
for (i = 0; i < sizeof(errors) / sizeof(test_case); ++i) { for (i = 0; i < sizeof(errors) / sizeof(test_case); ++i) {
const char *expr = errors[i].expr; const char *expr = errors[i].expr;
@@ -150,7 +169,7 @@ void test2() {
void test3() { void test_variables() {
double x, y; double x, y;
te_variable lookup[] = {{"x", &x}, {"y", &y}}; te_variable lookup[] = {{"x", &x}, {"y", &y}};
@@ -185,6 +204,7 @@ void test3() {
} }
#define cross_check(a, b) do {\ #define cross_check(a, b) do {\
expr = te_compile((a), &lookup, 1, &err);\ expr = te_compile((a), &lookup, 1, &err);\
lfequal(te_eval(expr), (b));\ lfequal(te_eval(expr), (b));\
@@ -192,7 +212,7 @@ void test3() {
te_free(expr);\ te_free(expr);\
}while(0) }while(0)
void test4() { void test_functions() {
double x; double x;
te_variable lookup = {"x", &x}; te_variable lookup = {"x", &x};
@@ -221,12 +241,26 @@ void test4() {
} }
void test_nans() {
int i;
for (i = 0; i < sizeof(nans) / sizeof(const char*); ++i) {
const char *expr = nans[i];
int err;
const double r = te_interp(expr, &err);
lok(err);
lok(r != r);
}
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
lrun("Results", test1); lrun("Results", test_results);
lrun("Syntax", test2); lrun("Syntax", test_syntax);
lrun("Bind", test3); lrun("Variables", test_variables);
lrun("Functions", test4); lrun("Functions", test_functions);
lrun("NaNs", test_nans);
lresults(); lresults();
return lfails != 0; return lfails != 0;

View File

@@ -56,6 +56,7 @@ static te_expr *new_expr(te_expr *l, te_expr *r) {
void te_free(te_expr *n) { void te_free(te_expr *n) {
if (!n) return;
if (n->left) te_free(n->left); if (n->left) te_free(n->left);
if (n->right) te_free(n->right); if (n->right) te_free(n->right);
free(n); free(n);
@@ -232,7 +233,7 @@ static te_expr *base(state *s) {
default: default:
ret = new_expr(0, 0); ret = new_expr(0, 0);
s->type = TOK_ERROR; s->type = TOK_ERROR;
ret->value = 1.0/0.0; ret->value = 0.0/0.0;
break; break;
} }
@@ -358,24 +359,30 @@ te_expr *te_compile(const char *expression, const te_variable *lookup, int looku
next_token(&s); next_token(&s);
te_expr *root = expr(&s); te_expr *root = expr(&s);
if (s.type != TOK_END) { if (s.type != TOK_END) {
if (error) *error = (s.next - s.start); te_free(root);
if (*error == 0) *error = 1; if (error) {
*error = (s.next - s.start);
if (*error == 0) *error = 1;
}
return 0;
} else { } else {
optimize(root); optimize(root);
if (error) *error = 0; if (error) *error = 0;
return root;
} }
return root;
} }
double te_interp(const char *expression, int *error) { double te_interp(const char *expression, int *error) {
te_expr *n = te_compile(expression, 0, 0, error); te_expr *n = te_compile(expression, 0, 0, error);
double ret = te_eval(n); double ret;
free(n); if (n) {
ret = te_eval(n);
free(n);
} else {
ret = 0.0/0.0;
}
return ret; return ret;
} }

View File

@@ -48,29 +48,23 @@ typedef struct {
} te_variable; } te_variable;
/* Note on error handling:
* If the parser encounters an error, it will still return
* an expression up to that point (which may be worthless or useful.
* If the error pointer parameter is passed in and not null, the
* parser will set it to roughly the index of the error in the
* input expression. If there is no error, the parse sets
* the error pointer to 0.
*/
/* Parses the input expression, evaluates it, and frees it. */ /* Parses the input expression, evaluates it, and frees it. */
double te_interp(const char *expression, int *error); double te_interp(const char *expression, int *error);
/* Parses the input expression and binds variables. */ /* Parses the input expression and binds variables. */
/* Returns NULL on error. */
te_expr *te_compile(const char *expression, const te_variable *lookup, int lookup_len, int *error); te_expr *te_compile(const char *expression, const te_variable *lookup, int lookup_len, int *error);
/* Evaluates the expression. */ /* Evaluates the expression. */
/* Returns NaN on error. */
double te_eval(const te_expr *n); double te_eval(const te_expr *n);
/* Prints debugging information on the syntax tree. */ /* Prints debugging information on the syntax tree. */
void te_print(const te_expr *n); void te_print(const te_expr *n);
/* Frees the expression. */ /* Frees the expression. */
/* This is safe to call on NULL pointers. */
void te_free(te_expr *n); void te_free(te_expr *n);