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, "");
}
/* te_free should always be called after te_compile. */
/* te_free is safe to call on null. */
te_free(n);
return 0;
@@ -235,9 +235,6 @@ In addition, the following C math functions are also supported:
- 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
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, "");
}
/* te_free should always be called after te_compile. */
/* te_free is safe to call on null. */
te_free(n);
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;
for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
const char *expr = cases[i].expr;
@@ -136,7 +155,7 @@ void test1() {
}
void test2() {
void test_syntax() {
int i;
for (i = 0; i < sizeof(errors) / sizeof(test_case); ++i) {
const char *expr = errors[i].expr;
@@ -150,7 +169,7 @@ void test2() {
void test3() {
void test_variables() {
double x, y;
te_variable lookup[] = {{"x", &x}, {"y", &y}};
@@ -185,6 +204,7 @@ void test3() {
}
#define cross_check(a, b) do {\
expr = te_compile((a), &lookup, 1, &err);\
lfequal(te_eval(expr), (b));\
@@ -192,7 +212,7 @@ void test3() {
te_free(expr);\
}while(0)
void test4() {
void test_functions() {
double 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[])
{
lrun("Results", test1);
lrun("Syntax", test2);
lrun("Bind", test3);
lrun("Functions", test4);
lrun("Results", test_results);
lrun("Syntax", test_syntax);
lrun("Variables", test_variables);
lrun("Functions", test_functions);
lrun("NaNs", test_nans);
lresults();
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) {
if (!n) return;
if (n->left) te_free(n->left);
if (n->right) te_free(n->right);
free(n);
@@ -232,7 +233,7 @@ static te_expr *base(state *s) {
default:
ret = new_expr(0, 0);
s->type = TOK_ERROR;
ret->value = 1.0/0.0;
ret->value = 0.0/0.0;
break;
}
@@ -358,24 +359,30 @@ te_expr *te_compile(const char *expression, const te_variable *lookup, int looku
next_token(&s);
te_expr *root = expr(&s);
if (s.type != TOK_END) {
if (error) *error = (s.next - s.start);
if (*error == 0) *error = 1;
te_free(root);
if (error) {
*error = (s.next - s.start);
if (*error == 0) *error = 1;
}
return 0;
} else {
optimize(root);
if (error) *error = 0;
return root;
}
return root;
}
double te_interp(const char *expression, int *error) {
te_expr *n = te_compile(expression, 0, 0, error);
double ret = te_eval(n);
free(n);
double ret;
if (n) {
ret = te_eval(n);
free(n);
} else {
ret = 0.0/0.0;
}
return ret;
}

View File

@@ -48,29 +48,23 @@ typedef struct {
} 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. */
double te_interp(const char *expression, int *error);
/* 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);
/* Evaluates the expression. */
/* Returns NaN on error. */
double te_eval(const te_expr *n);
/* Prints debugging information on the syntax tree. */
void te_print(const te_expr *n);
/* Frees the expression. */
/* This is safe to call on NULL pointers. */
void te_free(te_expr *n);