From 7d000bc3fc5c55476ea87cef68f1aad221e5b57d Mon Sep 17 00:00:00 2001 From: silvioprog Date: Mon, 27 Apr 2020 09:39:00 -0300 Subject: [PATCH 01/32] added type expr_num_t allowing to replace the expr number type (Fix #2) --- expr.h | 29 ++++++++++++++++++----------- expr_test.c | 18 +++++++++--------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/expr.h b/expr.h index 7484c5c..8dc13d7 100644 --- a/expr.h +++ b/expr.h @@ -12,6 +12,13 @@ extern "C" { #include #include +/* + * Expression number type + */ +#ifndef expr_num_t +#define expr_num_t double +#endif /* expr_num_t */ + /* * Simple expandable vector implementation */ @@ -100,16 +107,16 @@ static int prec[] = {0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 5, typedef vec(struct expr) vec_expr_t; typedef void (*exprfn_cleanup_t)(struct expr_func *f, void *context); -typedef float (*exprfn_t)(struct expr_func *f, vec_expr_t *args, void *context); +typedef expr_num_t (*exprfn_t)(struct expr_func *f, vec_expr_t *args, void *context); struct expr { enum expr_type type; union { struct { - float value; + expr_num_t value; } num; struct { - float *value; + expr_num_t *value; } var; struct { vec_expr_t args; @@ -206,8 +213,8 @@ static enum expr_type expr_op(const char *s, size_t len, int unary) { return OP_UNKNOWN; } -static float expr_parse_number(const char *s, size_t len) { - float num = 0; +static expr_num_t expr_parse_number(const char *s, size_t len) { + expr_num_t num = 0; unsigned int frac = 0; unsigned int digits = 0; for (unsigned int i = 0; i < len; i++) { @@ -256,7 +263,7 @@ static struct expr_func *expr_func(struct expr_func *funcs, const char *s, * Variables */ struct expr_var { - float value; + expr_num_t value; struct expr_var *next; char name[]; }; @@ -288,7 +295,7 @@ static struct expr_var *expr_var(struct expr_var_list *vars, const char *s, return v; } -static int to_int(float x) { +static int to_int(expr_num_t x) { if (isnan(x)) { return 0; } else if (isinf(x) != 0) { @@ -298,8 +305,8 @@ static int to_int(float x) { } } -static float expr_eval(struct expr *e) { - float n; +static expr_num_t expr_eval(struct expr *e) { + expr_num_t n; switch (e->type) { case OP_UNARY_MINUS: return -(expr_eval(&e->param.op.args.buf[0])); @@ -528,7 +535,7 @@ static int expr_bind(const char *s, size_t len, vec_expr_t *es) { return 0; } -static struct expr expr_const(float value) { +static struct expr expr_const(expr_num_t value) { struct expr e = expr_init(); e.type = OP_CONST; e.param.num.value = value; @@ -583,7 +590,7 @@ static void expr_destroy_args(struct expr *e); static struct expr *expr_create(const char *s, size_t len, struct expr_var_list *vars, struct expr_func *funcs) { - float num; + expr_num_t num; struct expr_var *v; const char *id = NULL; size_t idn = 0; diff --git a/expr_test.c b/expr_test.c index b82e7d5..fcee537 100644 --- a/expr_test.c +++ b/expr_test.c @@ -114,7 +114,7 @@ static void user_func_nop_cleanup(struct expr_func *f, void *c) { struct nop_context *nop = (struct nop_context *)c; free(nop->p); } -static float user_func_nop(struct expr_func *f, vec_expr_t *args, void *c) { +static expr_num_t user_func_nop(struct expr_func *f, vec_expr_t *args, void *c) { (void)args; struct nop_context *nop = (struct nop_context *)c; if (f->ctxsz == 0) { @@ -127,20 +127,20 @@ static float user_func_nop(struct expr_func *f, vec_expr_t *args, void *c) { return 0; } -static float user_func_add(struct expr_func *f, vec_expr_t *args, void *c) { +static expr_num_t user_func_add(struct expr_func *f, vec_expr_t *args, void *c) { (void)f, (void)c; - float a = expr_eval(&vec_nth(args, 0)); - float b = expr_eval(&vec_nth(args, 1)); + expr_num_t a = expr_eval(&vec_nth(args, 0)); + expr_num_t b = expr_eval(&vec_nth(args, 1)); return a + b; } -static float user_func_next(struct expr_func *f, vec_expr_t *args, void *c) { +static expr_num_t user_func_next(struct expr_func *f, vec_expr_t *args, void *c) { (void)f, (void)c; - float a = expr_eval(&vec_nth(args, 0)); + expr_num_t a = expr_eval(&vec_nth(args, 0)); return a + 1; } -static float user_func_print(struct expr_func *f, vec_expr_t *args, void *c) { +static expr_num_t user_func_print(struct expr_func *f, vec_expr_t *args, void *c) { (void)f, (void)c; int i; struct expr e; @@ -158,7 +158,7 @@ static struct expr_func user_funcs[] = { {NULL, NULL, NULL, 0}, }; -static void test_expr(char *s, float expected) { +static void test_expr(char *s, expr_num_t expected) { struct expr_var_list vars = {0}; struct expr *e = expr_create(s, strlen(s), &vars, user_funcs); if (e == NULL) { @@ -166,7 +166,7 @@ static void test_expr(char *s, float expected) { status = 1; return; } - float result = expr_eval(e); + expr_num_t result = expr_eval(e); char *p = (char *)malloc(strlen(s) + 1); strncpy(p, s, strlen(s) + 1); From 2034c850c5b0e0b570dc5929a39dfc28875ada7a Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 01:48:48 -0300 Subject: [PATCH 02/32] implemented error handling support (Fix #4) --- expr.h | 58 +++++++++++++++++++++++++++----- expr_test.c | 97 ++++++++++++++++++++++++++++++++++------------------- 2 files changed, 112 insertions(+), 43 deletions(-) diff --git a/expr.h b/expr.h index 8dc13d7..5cdc6e5 100644 --- a/expr.h +++ b/expr.h @@ -416,6 +416,21 @@ static expr_num_t expr_eval(struct expr *e) { #define EXPR_UNARY (1 << 5) #define EXPR_COMMA (1 << 6) +#define EXPR_ERR_UNKNOWN (0) +#define EXPR_ERR_UNEXPECTED_NUMBER (-1) +#define EXPR_ERR_UNEXPECTED_WORD (-2) +#define EXPR_ERR_UNEXPECTED_PARENS (-3) +#define EXPR_ERR_MISS_EXPECTED_OPERAND (-4) +#define EXPR_ERR_UNKNOWN_OPERATOR (-5) +#define EXPR_ERR_INVALID_FUNC_NAME (-6) +#define EXPR_ERR_BAD_CALL (-7) +#define EXPR_ERR_BAD_PARENS (-8) +#define EXPR_ERR_TOO_FEW_FUNC_ARGS (-9) +#define EXPR_ERR_FIRST_ARG_IS_NOT_VAR (-10) +#define EXPR_ERR_ALLOCATION_FAILED (-11) +#define EXPR_ERR_BAD_VARIABLE_NAME (-12) +#define EXPR_ERR_BAD_ASSIGNMENT (-13) + static int expr_next_token(const char *s, size_t len, int *flags) { unsigned int i = 0; if (len == 0) { @@ -444,7 +459,7 @@ static int expr_next_token(const char *s, size_t len, int *flags) { return i; } else if (isdigit(c)) { if ((*flags & EXPR_TNUMBER) == 0) { - return -1; // unexpected number + return EXPR_ERR_UNEXPECTED_NUMBER; // unexpected number } *flags = EXPR_TOP | EXPR_TCLOSE; while ((c == '.' || isdigit(c)) && i < len) { @@ -454,7 +469,7 @@ static int expr_next_token(const char *s, size_t len, int *flags) { return i; } else if (isfirstvarchr(c)) { if ((*flags & EXPR_TWORD) == 0) { - return -2; // unexpected word + return EXPR_ERR_UNEXPECTED_WORD; // unexpected word } *flags = EXPR_TOP | EXPR_TOPEN | EXPR_TCLOSE; while ((isvarchr(c)) && i < len) { @@ -468,13 +483,13 @@ static int expr_next_token(const char *s, size_t len, int *flags) { } else if (c == ')' && (*flags & EXPR_TCLOSE) != 0) { *flags = EXPR_TOP | EXPR_TCLOSE; } else { - return -3; // unexpected parenthesis + return EXPR_ERR_UNEXPECTED_PARENS; // unexpected parenthesis } return 1; } else { if ((*flags & EXPR_TOP) == 0) { if (expr_op(&c, 1, 1) == OP_UNKNOWN) { - return -4; // missing expected operand + return EXPR_ERR_MISS_EXPECTED_OPERAND; // missing expected operand } *flags = EXPR_TNUMBER | EXPR_TWORD | EXPR_TOPEN | EXPR_UNARY; return 1; @@ -490,7 +505,7 @@ static int expr_next_token(const char *s, size_t len, int *flags) { c = s[i]; } if (!found) { - return -5; // unknown operator + return EXPR_ERR_UNKNOWN_OPERATOR; // unknown operator } *flags = EXPR_TNUMBER | EXPR_TWORD | EXPR_TOPEN; return i; @@ -587,9 +602,10 @@ static inline void expr_copy(struct expr *dst, struct expr *src) { static void expr_destroy_args(struct expr *e); -static struct expr *expr_create(const char *s, size_t len, - struct expr_var_list *vars, - struct expr_func *funcs) { +static struct expr *expr_create2(const char *s, size_t len, + struct expr_var_list *vars, + struct expr_func *funcs, + int *near, int *error) { expr_num_t num; struct expr_var *v; const char *id = NULL; @@ -609,13 +625,17 @@ static struct expr *expr_create(const char *s, size_t len, int flags = EXPR_TDEFAULT; int paren = EXPR_PAREN_ALLOWED; + *near = 0; + *error = EXPR_ERR_UNKNOWN; for (;;) { int n = expr_next_token(s, len, &flags); if (n == 0) { break; } else if (n < 0) { + *error = n; goto cleanup; } + *near += n; const char *tok = s; s = s + n; len = len - n; @@ -667,6 +687,7 @@ static struct expr *expr_create(const char *s, size_t len, vec_push(&os, str); paren = EXPR_PAREN_EXPECTED; } else { + *error = EXPR_ERR_INVALID_FUNC_NAME; goto cleanup; /* invalid function name */ } } else if ((v = expr_var(vars, id, idn)) != NULL) { @@ -687,9 +708,11 @@ static struct expr *expr_create(const char *s, size_t len, struct expr_string str = {"(", 1}; vec_push(&os, str); } else { + *error = EXPR_ERR_BAD_CALL; goto cleanup; // Bad call } } else if (paren == EXPR_PAREN_EXPECTED) { + *error = EXPR_ERR_BAD_CALL; goto cleanup; // Bad call } else if (n == 1 && *tok == ')') { int minlen = (vec_len(&as) > 0 ? vec_peek(&as).oslen : 0); @@ -701,6 +724,7 @@ static struct expr *expr_create(const char *s, size_t len, } } if (vec_len(&os) == 0) { + *error = EXPR_ERR_BAD_PARENS; goto cleanup; // Bad parens } struct expr_string str = vec_pop(&os); @@ -713,11 +737,13 @@ static struct expr *expr_create(const char *s, size_t len, if (str.n == 1 && str.s[0] == '$') { if (vec_len(&arg.args) < 1) { vec_free(&arg.args); + *error = EXPR_ERR_TOO_FEW_FUNC_ARGS; goto cleanup; /* too few arguments for $() function */ } struct expr *u = &vec_nth(&arg.args, 0); if (u->type != OP_VAR) { vec_free(&arg.args); + *error = EXPR_ERR_FIRST_ARG_IS_NOT_VAR; goto cleanup; /* first argument is not a variable */ } for (struct expr_var *v = vars->head; v; v = v->next) { @@ -774,6 +800,7 @@ static struct expr *expr_create(const char *s, size_t len, if (f->ctxsz > 0) { void *p = calloc(1, f->ctxsz); if (p == NULL) { + *error = EXPR_ERR_ALLOCATION_FAILED; goto cleanup; /* allocation failed */ } bound_func.param.func.context = p; @@ -824,6 +851,7 @@ static struct expr *expr_create(const char *s, size_t len, id = tok; idn = n; } else { + *error = EXPR_ERR_BAD_VARIABLE_NAME; goto cleanup; // Bad variable name, e.g. '2.3.4' or '4ever' } } @@ -837,9 +865,11 @@ static struct expr *expr_create(const char *s, size_t len, while (vec_len(&os) > 0) { struct expr_string rest = vec_pop(&os); if (rest.n == 1 && (*rest.s == '(' || *rest.s == ')')) { + *error = EXPR_ERR_BAD_PARENS; goto cleanup; // Bad paren } if (expr_bind(rest.s, rest.n, &es) == -1) { + *error = (*rest.s == '=') ? EXPR_ERR_BAD_ASSIGNMENT : EXPR_ERR_BAD_PARENS; goto cleanup; } } @@ -876,9 +906,21 @@ static struct expr *expr_create(const char *s, size_t len, /*vec_foreach(&os, o, i) {vec_free(&m.body);}*/ vec_free(&os); + + if (*near == 0) { + *near = 1; + } return result; } +static struct expr *expr_create(const char *s, size_t len, + struct expr_var_list *vars, + struct expr_func *funcs) { + int near; + int error; + return expr_create2(s, len, vars, funcs, &near, &error); +} + static void expr_destroy_args(struct expr *e) { int i; struct expr arg; diff --git a/expr_test.c b/expr_test.c index fcee537..f1c9ea8 100644 --- a/expr_test.c +++ b/expr_test.c @@ -187,13 +187,38 @@ static void test_expr(char *s, expr_num_t expected) { free(p); } -static void test_expr_error(char *s) { +static void test_expr_error(char *s, int near, int error) { + const char *ERRORS[] = { + "EXPR_ERR_UNKNOWN", + "EXPR_ERR_UNEXPECTED_NUMBER", + "EXPR_ERR_UNEXPECTED_WORD", + "EXPR_ERR_UNEXPECTED_PARENS", + "EXPR_ERR_MISS_EXPECTED_OPERAND", + "EXPR_ERR_UNKNOWN_OPERATOR", + "EXPR_ERR_INVALID_FUNC_NAME", + "EXPR_ERR_BAD_CALL", + "EXPR_ERR_BAD_PARENS", + "EXPR_ERR_TOO_FEW_FUNC_ARGS", + "EXPR_ERR_FIRST_ARG_IS_NOT_VAR", + "EXPR_ERR_ALLOCATION_FAILED", + "EXPR_ERR_BAD_VARIABLE_NAME", + "EXPR_ERR_BAD_ASSIGNMENT" + }; struct expr_var_list vars = {0}; - struct expr *e = expr_create(s, strlen(s), &vars, user_funcs); + int n, f; + struct expr *e = expr_create2(s, strlen(s), &vars, user_funcs, &n, &f); if (e != NULL) { printf("FAIL: %s should return error\n", s); status = 1; } + if (n != near) { + printf("FAIL: %s should return error near to %d, but returned at %d\n", s, near, n); + status = 1; + } + if (f != error) { + printf("FAIL: %s should return error %s, but returned %s\n", s, ERRORS[-error], ERRORS[-f]); + status = 1; + } expr_destroy(e, &vars); } @@ -365,39 +390,41 @@ static void test_benchmark(const char *s) { } static void test_bad_syntax() { - test_expr_error("("); - test_expr_error(")"); - test_expr_error("()3"); - test_expr_error("()x"); - test_expr_error("0^+1"); - test_expr_error("()\\"); - test_expr_error("()."); - test_expr_error("4ever"); - test_expr_error("(2+3"); - test_expr_error("(-2"); - test_expr_error("*2"); - test_expr_error("nop="); - test_expr_error("nop("); - test_expr_error("unknownfunc()"); - test_expr_error("$(recurse, recurse()), recurse()"); - test_expr_error("),"); - test_expr_error("+("); - test_expr_error("2=3"); - test_expr_error("2.3.4"); - test_expr_error("1()"); - test_expr_error("x()"); - test_expr_error(","); - test_expr_error("1,,2"); - test_expr_error("nop(,x)"); - test_expr_error("nop(x=)>1"); - test_expr_error("1 x"); - test_expr_error("1++"); - test_expr_error("foo((x))"); - test_expr_error("nop(x))"); - test_expr_error("nop((x)"); - test_expr_error("$($())"); - test_expr_error("$(1)"); - test_expr_error("$()"); + test_expr_error("(", 1, EXPR_ERR_BAD_PARENS); + test_expr_error(")", 1, EXPR_ERR_UNEXPECTED_PARENS); + test_expr_error("()3", 2, EXPR_ERR_UNEXPECTED_NUMBER); + test_expr_error("()x", 2, EXPR_ERR_UNEXPECTED_WORD); + test_expr_error("0^+1", 2, EXPR_ERR_MISS_EXPECTED_OPERAND); + test_expr_error("()\\", 2, EXPR_ERR_UNEXPECTED_WORD); + test_expr_error("().", 2, EXPR_ERR_UNKNOWN_OPERATOR); + test_expr_error("4ever", 1, EXPR_ERR_UNEXPECTED_WORD); + test_expr_error("(2+3", 4, EXPR_ERR_BAD_PARENS); + test_expr_error("(-2", 3, EXPR_ERR_BAD_PARENS); + test_expr_error("*2", 1, EXPR_ERR_MISS_EXPECTED_OPERAND); + test_expr_error("nop=", 4, EXPR_ERR_BAD_ASSIGNMENT); + test_expr_error("nop(", 4, EXPR_ERR_BAD_PARENS); + test_expr_error("unknownfunc()", 12, EXPR_ERR_INVALID_FUNC_NAME); + test_expr_error("$(recurse, recurse()), recurse()", 19, EXPR_ERR_INVALID_FUNC_NAME); + test_expr_error("),", 1, EXPR_ERR_UNEXPECTED_PARENS); + test_expr_error("+(", 1, EXPR_ERR_MISS_EXPECTED_OPERAND); + test_expr_error("2=3", 3, EXPR_ERR_BAD_ASSIGNMENT); + test_expr_error("2.3.4", 5, EXPR_ERR_BAD_VARIABLE_NAME); + test_expr_error("1()", 1, EXPR_ERR_UNEXPECTED_PARENS); + test_expr_error("x()", 2, EXPR_ERR_INVALID_FUNC_NAME); + test_expr_error(",", 1, EXPR_ERR_MISS_EXPECTED_OPERAND); + test_expr_error("1,,2", 2, EXPR_ERR_MISS_EXPECTED_OPERAND); + test_expr_error("nop(,x)", 4, EXPR_ERR_MISS_EXPECTED_OPERAND); + test_expr_error("nop(x=)>1", 6, EXPR_ERR_UNEXPECTED_PARENS); + test_expr_error("1 x", 2, EXPR_ERR_UNEXPECTED_WORD); + test_expr_error("1++", 2, EXPR_ERR_MISS_EXPECTED_OPERAND); + test_expr_error("foo((x))", 4, EXPR_ERR_INVALID_FUNC_NAME); + test_expr_error("nop(x))", 7, EXPR_ERR_BAD_PARENS); + test_expr_error("nop((x)", 7, EXPR_ERR_BAD_PARENS); + test_expr_error("$($())", 5, EXPR_ERR_TOO_FEW_FUNC_ARGS); + test_expr_error("$(1)", 4, EXPR_ERR_FIRST_ARG_IS_NOT_VAR); + test_expr_error("$()", 3, EXPR_ERR_TOO_FEW_FUNC_ARGS); + test_expr_error("n=", 2, EXPR_ERR_BAD_ASSIGNMENT); + test_expr_error("a+10/((1+x)-b)-((5-(8/2))", 25, EXPR_ERR_BAD_PARENS); } int main() { From 1b9ada62635500cfb2b685e17b9a67f19b70bdae Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 09:30:44 -0300 Subject: [PATCH 03/32] added macros to customize functions pow() and fmod() --- expr.h | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/expr.h b/expr.h index 5cdc6e5..32dec13 100644 --- a/expr.h +++ b/expr.h @@ -19,6 +19,16 @@ extern "C" { #define expr_num_t double #endif /* expr_num_t */ +/* + * Math + */ +#ifndef expr_pow +#define expr_pow(x, y) pow((x), (y)) +#endif /* expr_powf */ +#ifndef expr_fmod +#define expr_fmod(x, y) fmod((x), (y)) +#endif /* expr_fmodf */ + /* * Simple expandable vector implementation */ @@ -315,8 +325,8 @@ static expr_num_t expr_eval(struct expr *e) { case OP_UNARY_BITWISE_NOT: return ~(to_int(expr_eval(&e->param.op.args.buf[0]))); case OP_POWER: - return powf(expr_eval(&e->param.op.args.buf[0]), - expr_eval(&e->param.op.args.buf[1])); + return expr_pow(expr_eval(&e->param.op.args.buf[0]), + expr_eval(&e->param.op.args.buf[1])); case OP_MULTIPLY: return expr_eval(&e->param.op.args.buf[0]) * expr_eval(&e->param.op.args.buf[1]); @@ -324,8 +334,8 @@ static expr_num_t expr_eval(struct expr *e) { return expr_eval(&e->param.op.args.buf[0]) / expr_eval(&e->param.op.args.buf[1]); case OP_REMAINDER: - return fmodf(expr_eval(&e->param.op.args.buf[0]), - expr_eval(&e->param.op.args.buf[1])); + return expr_fmod(expr_eval(&e->param.op.args.buf[0]), + expr_eval(&e->param.op.args.buf[1])); case OP_PLUS: return expr_eval(&e->param.op.args.buf[0]) + expr_eval(&e->param.op.args.buf[1]); From b78a34dc5b61bd8f69df1a07548859e39262ec31 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 09:42:19 -0300 Subject: [PATCH 04/32] added file .gitattributes --- .gitattributes | 1 + .gitignore | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.gitignore b/.gitignore index 648721f..9d4032c 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,6 @@ expr_test # Makefile local config config.mk + +# Build directory +build/ From 671819bc97a13426e945520849e954253e928fc9 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 09:43:15 -0300 Subject: [PATCH 05/32] added file .editorconfig --- .editorconfig | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0f17867 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true From ba7abacf010a0e44759e09cb5eeb7dae78013591 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 09:51:11 -0300 Subject: [PATCH 06/32] added file .clang-format --- .clang-format | 12 ++ expr.h | 313 +++++++++++++++++++++++---------------------- expr_debug.h | 346 +++++++++++++++++++++++++------------------------- expr_jit.dasc | 4 +- expr_test.c | 99 ++++++++------- 5 files changed, 403 insertions(+), 371 deletions(-) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..f8253ca --- /dev/null +++ b/.clang-format @@ -0,0 +1,12 @@ +--- +AlignTrailingComments: false +AllowShortFunctionsOnASingleLine: None +BreakBeforeTernaryOperators: false +ColumnLimit: 80 +ContinuationIndentWidth: 2 +IndentCaseLabels: true +IndentWrappedFunctionNames: true +ReflowComments: false +SortIncludes: false +SpaceAfterCStyleCast: true +... diff --git a/expr.h b/expr.h index 32dec13..5080dde 100644 --- a/expr.h +++ b/expr.h @@ -40,7 +40,7 @@ static int vec_expand(char **buf, int *length, int *cap, int memsz) { if (ptr == NULL) { return -1; /* allocation failed */ } - *buf = (char *)ptr; + *buf = (char *) ptr; *cap = n; } return 0; @@ -55,7 +55,7 @@ static int vec_expand(char **buf, int *length, int *cap, int memsz) { { NULL, 0, 0 } #define vec_len(v) ((v)->len) #define vec_unpack(v) \ - (char **)&(v)->buf, &(v)->len, &(v)->cap, sizeof(*(v)->buf) + (char **) &(v)->buf, &(v)->len, &(v)->cap, sizeof(*(v)->buf) #define vec_push(v, val) \ vec_expand(vec_unpack(v)) ? -1 : ((v)->buf[(v)->len++] = (val), 0) #define vec_nth(v, i) (v)->buf[i] @@ -117,7 +117,8 @@ static int prec[] = {0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 5, typedef vec(struct expr) vec_expr_t; typedef void (*exprfn_cleanup_t)(struct expr_func *f, void *context); -typedef expr_num_t (*exprfn_t)(struct expr_func *f, vec_expr_t *args, void *context); +typedef expr_num_t (*exprfn_t)(struct expr_func *f, vec_expr_t *args, + void *context); struct expr { enum expr_type type; @@ -140,7 +141,7 @@ struct expr { }; #define expr_init() \ - { .type = (enum expr_type)0 } + { .type = (enum expr_type) 0 } struct expr_string { const char *s; @@ -167,50 +168,50 @@ static int expr_is_binary(enum expr_type op) { static int expr_prec(enum expr_type a, enum expr_type b) { int left = - expr_is_binary(a) && a != OP_ASSIGN && a != OP_POWER && a != OP_COMMA; + expr_is_binary(a) && a != OP_ASSIGN && a != OP_POWER && a != OP_COMMA; return (left && prec[a] >= prec[b]) || (prec[a] > prec[b]); } #define isfirstvarchr(c) \ - (((unsigned char)c >= '@' && c != '^' && c != '|') || c == '$') + (((unsigned char) c >= '@' && c != '^' && c != '|') || c == '$') #define isvarchr(c) \ - (((unsigned char)c >= '@' && c != '^' && c != '|') || c == '$' || \ + (((unsigned char) c >= '@' && c != '^' && c != '|') || c == '$' || \ c == '#' || (c >= '0' && c <= '9')) static struct { const char *s; const enum expr_type op; } OPS[] = { - {"-u", OP_UNARY_MINUS}, - {"!u", OP_UNARY_LOGICAL_NOT}, - {"^u", OP_UNARY_BITWISE_NOT}, - {"**", OP_POWER}, - {"*", OP_MULTIPLY}, - {"/", OP_DIVIDE}, - {"%", OP_REMAINDER}, - {"+", OP_PLUS}, - {"-", OP_MINUS}, - {"<<", OP_SHL}, - {">>", OP_SHR}, - {"<", OP_LT}, - {"<=", OP_LE}, - {">", OP_GT}, - {">=", OP_GE}, - {"==", OP_EQ}, - {"!=", OP_NE}, - {"&", OP_BITWISE_AND}, - {"|", OP_BITWISE_OR}, - {"^", OP_BITWISE_XOR}, - {"&&", OP_LOGICAL_AND}, - {"||", OP_LOGICAL_OR}, - {"=", OP_ASSIGN}, - {",", OP_COMMA}, - - /* These are used by lexer and must be ignored by parser, so we put + {"-u", OP_UNARY_MINUS}, + {"!u", OP_UNARY_LOGICAL_NOT}, + {"^u", OP_UNARY_BITWISE_NOT}, + {"**", OP_POWER}, + {"*", OP_MULTIPLY}, + {"/", OP_DIVIDE}, + {"%", OP_REMAINDER}, + {"+", OP_PLUS}, + {"-", OP_MINUS}, + {"<<", OP_SHL}, + {">>", OP_SHR}, + {"<", OP_LT}, + {"<=", OP_LE}, + {">", OP_GT}, + {">=", OP_GE}, + {"==", OP_EQ}, + {"!=", OP_NE}, + {"&", OP_BITWISE_AND}, + {"|", OP_BITWISE_OR}, + {"^", OP_BITWISE_XOR}, + {"&&", OP_LOGICAL_AND}, + {"||", OP_LOGICAL_OR}, + {"=", OP_ASSIGN}, + {",", OP_COMMA}, + + /* These are used by lexer and must be ignored by parser, so we put them at the end */ - {"-", OP_UNARY_MINUS}, - {"!", OP_UNARY_LOGICAL_NOT}, - {"^", OP_UNARY_BITWISE_NOT}, + {"-", OP_UNARY_MINUS}, + {"!", OP_UNARY_LOGICAL_NOT}, + {"^", OP_UNARY_BITWISE_NOT}, }; static enum expr_type expr_op(const char *s, size_t len, int unary) { @@ -293,7 +294,7 @@ static struct expr_var *expr_var(struct expr_var_list *vars, const char *s, return v; } } - v = (struct expr_var *)calloc(1, sizeof(struct expr_var) + len + 1); + v = (struct expr_var *) calloc(1, sizeof(struct expr_var) + len + 1); if (v == NULL) { return NULL; /* allocation failed */ } @@ -311,108 +312,108 @@ static int to_int(expr_num_t x) { } else if (isinf(x) != 0) { return INT_MAX * isinf(x); } else { - return (int)x; + return (int) x; } } static expr_num_t expr_eval(struct expr *e) { expr_num_t n; switch (e->type) { - case OP_UNARY_MINUS: - return -(expr_eval(&e->param.op.args.buf[0])); - case OP_UNARY_LOGICAL_NOT: - return !(expr_eval(&e->param.op.args.buf[0])); - case OP_UNARY_BITWISE_NOT: - return ~(to_int(expr_eval(&e->param.op.args.buf[0]))); - case OP_POWER: - return expr_pow(expr_eval(&e->param.op.args.buf[0]), - expr_eval(&e->param.op.args.buf[1])); - case OP_MULTIPLY: - return expr_eval(&e->param.op.args.buf[0]) * - expr_eval(&e->param.op.args.buf[1]); - case OP_DIVIDE: - return expr_eval(&e->param.op.args.buf[0]) / - expr_eval(&e->param.op.args.buf[1]); - case OP_REMAINDER: - return expr_fmod(expr_eval(&e->param.op.args.buf[0]), - expr_eval(&e->param.op.args.buf[1])); - case OP_PLUS: - return expr_eval(&e->param.op.args.buf[0]) + - expr_eval(&e->param.op.args.buf[1]); - case OP_MINUS: - return expr_eval(&e->param.op.args.buf[0]) - - expr_eval(&e->param.op.args.buf[1]); - case OP_SHL: - return to_int(expr_eval(&e->param.op.args.buf[0])) - << to_int(expr_eval(&e->param.op.args.buf[1])); - case OP_SHR: - return to_int(expr_eval(&e->param.op.args.buf[0])) >> - to_int(expr_eval(&e->param.op.args.buf[1])); - case OP_LT: - return expr_eval(&e->param.op.args.buf[0]) < - expr_eval(&e->param.op.args.buf[1]); - case OP_LE: - return expr_eval(&e->param.op.args.buf[0]) <= - expr_eval(&e->param.op.args.buf[1]); - case OP_GT: - return expr_eval(&e->param.op.args.buf[0]) > - expr_eval(&e->param.op.args.buf[1]); - case OP_GE: - return expr_eval(&e->param.op.args.buf[0]) >= - expr_eval(&e->param.op.args.buf[1]); - case OP_EQ: - return expr_eval(&e->param.op.args.buf[0]) == - expr_eval(&e->param.op.args.buf[1]); - case OP_NE: - return expr_eval(&e->param.op.args.buf[0]) != - expr_eval(&e->param.op.args.buf[1]); - case OP_BITWISE_AND: - return to_int(expr_eval(&e->param.op.args.buf[0])) & - to_int(expr_eval(&e->param.op.args.buf[1])); - case OP_BITWISE_OR: - return to_int(expr_eval(&e->param.op.args.buf[0])) | - to_int(expr_eval(&e->param.op.args.buf[1])); - case OP_BITWISE_XOR: - return to_int(expr_eval(&e->param.op.args.buf[0])) ^ - to_int(expr_eval(&e->param.op.args.buf[1])); - case OP_LOGICAL_AND: - n = expr_eval(&e->param.op.args.buf[0]); - if (n != 0) { - n = expr_eval(&e->param.op.args.buf[1]); + case OP_UNARY_MINUS: + return -(expr_eval(&e->param.op.args.buf[0])); + case OP_UNARY_LOGICAL_NOT: + return !(expr_eval(&e->param.op.args.buf[0])); + case OP_UNARY_BITWISE_NOT: + return ~(to_int(expr_eval(&e->param.op.args.buf[0]))); + case OP_POWER: + return expr_pow(expr_eval(&e->param.op.args.buf[0]), + expr_eval(&e->param.op.args.buf[1])); + case OP_MULTIPLY: + return expr_eval(&e->param.op.args.buf[0]) * + expr_eval(&e->param.op.args.buf[1]); + case OP_DIVIDE: + return expr_eval(&e->param.op.args.buf[0]) / + expr_eval(&e->param.op.args.buf[1]); + case OP_REMAINDER: + return expr_fmod(expr_eval(&e->param.op.args.buf[0]), + expr_eval(&e->param.op.args.buf[1])); + case OP_PLUS: + return expr_eval(&e->param.op.args.buf[0]) + + expr_eval(&e->param.op.args.buf[1]); + case OP_MINUS: + return expr_eval(&e->param.op.args.buf[0]) - + expr_eval(&e->param.op.args.buf[1]); + case OP_SHL: + return to_int(expr_eval(&e->param.op.args.buf[0])) + << to_int(expr_eval(&e->param.op.args.buf[1])); + case OP_SHR: + return to_int(expr_eval(&e->param.op.args.buf[0])) >> + to_int(expr_eval(&e->param.op.args.buf[1])); + case OP_LT: + return expr_eval(&e->param.op.args.buf[0]) < + expr_eval(&e->param.op.args.buf[1]); + case OP_LE: + return expr_eval(&e->param.op.args.buf[0]) <= + expr_eval(&e->param.op.args.buf[1]); + case OP_GT: + return expr_eval(&e->param.op.args.buf[0]) > + expr_eval(&e->param.op.args.buf[1]); + case OP_GE: + return expr_eval(&e->param.op.args.buf[0]) >= + expr_eval(&e->param.op.args.buf[1]); + case OP_EQ: + return expr_eval(&e->param.op.args.buf[0]) == + expr_eval(&e->param.op.args.buf[1]); + case OP_NE: + return expr_eval(&e->param.op.args.buf[0]) != + expr_eval(&e->param.op.args.buf[1]); + case OP_BITWISE_AND: + return to_int(expr_eval(&e->param.op.args.buf[0])) & + to_int(expr_eval(&e->param.op.args.buf[1])); + case OP_BITWISE_OR: + return to_int(expr_eval(&e->param.op.args.buf[0])) | + to_int(expr_eval(&e->param.op.args.buf[1])); + case OP_BITWISE_XOR: + return to_int(expr_eval(&e->param.op.args.buf[0])) ^ + to_int(expr_eval(&e->param.op.args.buf[1])); + case OP_LOGICAL_AND: + n = expr_eval(&e->param.op.args.buf[0]); if (n != 0) { + n = expr_eval(&e->param.op.args.buf[1]); + if (n != 0) { + return n; + } + } + return 0; + case OP_LOGICAL_OR: + n = expr_eval(&e->param.op.args.buf[0]); + if (n != 0 && !isnan(n)) { return n; + } else { + n = expr_eval(&e->param.op.args.buf[1]); + if (n != 0) { + return n; + } } - } - return 0; - case OP_LOGICAL_OR: - n = expr_eval(&e->param.op.args.buf[0]); - if (n != 0 && !isnan(n)) { - return n; - } else { + return 0; + case OP_ASSIGN: n = expr_eval(&e->param.op.args.buf[1]); - if (n != 0) { - return n; + if (vec_nth(&e->param.op.args, 0).type == OP_VAR) { + *e->param.op.args.buf[0].param.var.value = n; } - } - return 0; - case OP_ASSIGN: - n = expr_eval(&e->param.op.args.buf[1]); - if (vec_nth(&e->param.op.args, 0).type == OP_VAR) { - *e->param.op.args.buf[0].param.var.value = n; - } - return n; - case OP_COMMA: - expr_eval(&e->param.op.args.buf[0]); - return expr_eval(&e->param.op.args.buf[1]); - case OP_CONST: - return e->param.num.value; - case OP_VAR: - return *e->param.var.value; - case OP_FUNC: - return e->param.func.f->f(e->param.func.f, &e->param.func.args, - e->param.func.context); - default: - return NAN; + return n; + case OP_COMMA: + expr_eval(&e->param.op.args.buf[0]); + return expr_eval(&e->param.op.args.buf[1]); + case OP_CONST: + return e->param.num.value; + case OP_VAR: + return *e->param.var.value; + case OP_FUNC: + return e->param.func.f->f(e->param.func.f, &e->param.func.args, + e->param.func.context); + default: + return NAN; } } @@ -614,8 +615,8 @@ static void expr_destroy_args(struct expr *e); static struct expr *expr_create2(const char *s, size_t len, struct expr_var_list *vars, - struct expr_func *funcs, - int *near, int *error) { + struct expr_func *funcs, int *near, + int *error) { expr_num_t num; struct expr_var *v; const char *id = NULL; @@ -655,17 +656,17 @@ static struct expr *expr_create2(const char *s, size_t len, if (flags & EXPR_UNARY) { if (n == 1) { switch (*tok) { - case '-': - tok = "-u"; - break; - case '^': - tok = "^u"; - break; - case '!': - tok = "!u"; - break; - default: - goto cleanup; + case '-': + tok = "-u"; + break; + case '^': + tok = "^u"; + break; + case '!': + tok = "!u"; + break; + default: + goto cleanup; } n = 2; } @@ -693,7 +694,7 @@ static struct expr *expr_create2(const char *s, size_t len, } if ((idn == 1 && id[0] == '$') || has_macro || expr_func(funcs, id, idn) != NULL) { - struct expr_string str = {id, (int)idn}; + struct expr_string str = {id, (int) idn}; vec_push(&os, str); paren = EXPR_PAREN_EXPECTED; } else { @@ -769,7 +770,7 @@ static struct expr *expr_create2(const char *s, size_t len, int found = -1; struct macro m; vec_foreach(¯os, m, i) { - if (strlen(m.name) == (size_t)str.n && + if (strlen(m.name) == (size_t) str.n && strncmp(m.name, str.s, str.n) == 0) { found = i; } @@ -785,7 +786,7 @@ static struct expr *expr_create2(const char *s, size_t len, struct expr_var *v = expr_var(vars, varname, strlen(varname)); struct expr ev = expr_varref(v); struct expr assign = - expr_binary(OP_ASSIGN, ev, vec_nth(&arg.args, j)); + expr_binary(OP_ASSIGN, ev, vec_nth(&arg.args, j)); *p = expr_binary(OP_COMMA, assign, expr_const(0)); p = &vec_nth(&p->param.op.args, 1); } @@ -848,7 +849,7 @@ static struct expr *expr_create2(const char *s, size_t len, if (expr_bind(o2.s, o2.n, &es) == -1) { goto cleanup; } - (void)vec_pop(&os); + (void) vec_pop(&os); if (vec_len(&os) > 0) { o2 = vec_peek(&os); } else { @@ -884,7 +885,7 @@ static struct expr *expr_create2(const char *s, size_t len, } } - result = (struct expr *)calloc(1, sizeof(struct expr)); + result = (struct expr *) calloc(1, sizeof(struct expr)); if (result != NULL) { if (vec_len(&es) == 0) { result->type = OP_CONST; @@ -900,16 +901,22 @@ static struct expr *expr_create2(const char *s, size_t len, cleanup: vec_foreach(¯os, m, i) { struct expr e; - vec_foreach(&m.body, e, j) { expr_destroy_args(&e); } + vec_foreach(&m.body, e, j) { + expr_destroy_args(&e); + } vec_free(&m.body); } vec_free(¯os); - vec_foreach(&es, e, i) { expr_destroy_args(&e); } + vec_foreach(&es, e, i) { + expr_destroy_args(&e); + } vec_free(&es); vec_foreach(&as, a, i) { - vec_foreach(&a.args, e, j) { expr_destroy_args(&e); } + vec_foreach(&a.args, e, j) { + expr_destroy_args(&e); + } vec_free(&a.args); } vec_free(&as); @@ -935,7 +942,9 @@ static void expr_destroy_args(struct expr *e) { int i; struct expr arg; if (e->type == OP_FUNC) { - vec_foreach(&e->param.func.args, arg, i) { expr_destroy_args(&arg); } + vec_foreach(&e->param.func.args, arg, i) { + expr_destroy_args(&arg); + } vec_free(&e->param.func.args); if (e->param.func.context != NULL) { if (e->param.func.f->cleanup != NULL) { @@ -944,7 +953,9 @@ static void expr_destroy_args(struct expr *e) { free(e->param.func.context); } } else if (e->type != OP_CONST && e->type != OP_VAR) { - vec_foreach(&e->param.op.args, arg, i) { expr_destroy_args(&arg); } + vec_foreach(&e->param.op.args, arg, i) { + expr_destroy_args(&arg); + } vec_free(&e->param.op.args); } } diff --git a/expr_debug.h b/expr_debug.h index f4d013a..f51ece7 100644 --- a/expr_debug.h +++ b/expr_debug.h @@ -5,179 +5,179 @@ static void expr_print(struct expr *e) { switch (e->type) { - case OP_UNKNOWN: - break; - case OP_UNARY_MINUS: - printf("-("); - expr_print(&e->param.op.args.buf[0]); - printf(")"); - break; - case OP_UNARY_LOGICAL_NOT: - printf("!("); - expr_print(&e->param.op.args.buf[0]); - printf(")"); - break; - case OP_UNARY_BITWISE_NOT: - printf("^("); - expr_print(&e->param.op.args.buf[0]); - printf(")"); - break; - case OP_POWER: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf("**"); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_MULTIPLY: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf("*"); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_DIVIDE: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf("/"); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_REMAINDER: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf("%%"); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_PLUS: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf("+"); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_MINUS: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf("-"); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_SHL: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf("<<"); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_SHR: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf(">>"); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_LT: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf("<"); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_LE: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf("<="); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_GT: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf(">"); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_GE: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf(">="); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_EQ: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf("=="); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_NE: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf("!="); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_BITWISE_AND: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf("&"); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_BITWISE_OR: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf("|"); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_BITWISE_XOR: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf("^"); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_LOGICAL_AND: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf("&&"); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_LOGICAL_OR: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf("||"); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_ASSIGN: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf(":="); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_COMMA: - printf("("); - expr_print(&e->param.op.args.buf[0]); - printf(","); - expr_print(&e->param.op.args.buf[1]); - printf(")"); - break; - case OP_CONST: - printf("%.2f", e->param.num.value); - break; - case OP_VAR: - printf("[%.2f@%p]", *e->param.var.value, (void *)e->param.var.value); - break; - case OP_FUNC: - printf("func(todo)"); - break; + case OP_UNKNOWN: + break; + case OP_UNARY_MINUS: + printf("-("); + expr_print(&e->param.op.args.buf[0]); + printf(")"); + break; + case OP_UNARY_LOGICAL_NOT: + printf("!("); + expr_print(&e->param.op.args.buf[0]); + printf(")"); + break; + case OP_UNARY_BITWISE_NOT: + printf("^("); + expr_print(&e->param.op.args.buf[0]); + printf(")"); + break; + case OP_POWER: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf("**"); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_MULTIPLY: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf("*"); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_DIVIDE: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf("/"); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_REMAINDER: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf("%%"); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_PLUS: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf("+"); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_MINUS: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf("-"); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_SHL: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf("<<"); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_SHR: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf(">>"); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_LT: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf("<"); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_LE: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf("<="); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_GT: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf(">"); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_GE: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf(">="); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_EQ: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf("=="); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_NE: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf("!="); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_BITWISE_AND: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf("&"); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_BITWISE_OR: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf("|"); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_BITWISE_XOR: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf("^"); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_LOGICAL_AND: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf("&&"); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_LOGICAL_OR: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf("||"); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_ASSIGN: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf(":="); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_COMMA: + printf("("); + expr_print(&e->param.op.args.buf[0]); + printf(","); + expr_print(&e->param.op.args.buf[1]); + printf(")"); + break; + case OP_CONST: + printf("%.2f", e->param.num.value); + break; + case OP_VAR: + printf("[%.2f@%p]", *e->param.var.value, (void *) e->param.var.value); + break; + case OP_FUNC: + printf("func(todo)"); + break; } } diff --git a/expr_jit.dasc b/expr_jit.dasc index 3099569..8bb130b 100644 --- a/expr_jit.dasc +++ b/expr_jit.dasc @@ -106,7 +106,7 @@ static int expr_compile_dynasm(struct expr *e, dasm_State **Dst) { |.endmacro |.macro MOVIMM, reg, value - | push value + | push value | movss reg, dword [rsp] | add rsp, aword*1 |.endmacro @@ -162,7 +162,7 @@ static int expr_compile_dynasm(struct expr *e, dasm_State **Dst) { expr_compile_dynasm(&e->param.op.args.buf[0], Dst); | PUSH_XMM0 expr_compile_dynasm(&e->param.op.args.buf[1], Dst); - | POP_XMM0 + | POP_XMM0 | mulss xmm0, xmm1 break; case OP_DIVIDE: diff --git a/expr_test.c b/expr_test.c index f1c9ea8..bf201d2 100644 --- a/expr_test.c +++ b/expr_test.c @@ -34,7 +34,9 @@ static void test_vector() { assert(vec_len(&strings) == 3); int i; char *el; - vec_foreach(&strings, el, i) { printf("%s %d\n", el, i); } + vec_foreach(&strings, el, i) { + printf("%s %d\n", el, i); + } vec_free(&strings); } @@ -91,12 +93,12 @@ static int assert_tokens(char *s, char **expected) { static void test_tokizer() { char **TESTS[] = { - (char *[]){"", NULL}, - (char *[]){"1", "1", NULL}, - (char *[]){"1+11", "1", "+", "11", NULL}, - (char *[]){"1*11", "1", "*", "11", NULL}, - (char *[]){"1**11", "1", "**", "11", NULL}, - (char *[]){"1**-11", "1", "**", "-", "11", NULL}, + (char *[]){"", NULL}, + (char *[]){"1", "1", NULL}, + (char *[]){"1+11", "1", "+", "11", NULL}, + (char *[]){"1*11", "1", "*", "11", NULL}, + (char *[]){"1**11", "1", "**", "11", NULL}, + (char *[]){"1**-11", "1", "**", "-", "11", NULL}, }; for (unsigned int i = 0; i < sizeof(TESTS) / sizeof(TESTS[0]); i++) { assert_tokens(TESTS[i][0], TESTS[i] + 1); @@ -110,13 +112,14 @@ struct nop_context { void *p; }; static void user_func_nop_cleanup(struct expr_func *f, void *c) { - (void)f; - struct nop_context *nop = (struct nop_context *)c; + (void) f; + struct nop_context *nop = (struct nop_context *) c; free(nop->p); } -static expr_num_t user_func_nop(struct expr_func *f, vec_expr_t *args, void *c) { - (void)args; - struct nop_context *nop = (struct nop_context *)c; +static expr_num_t user_func_nop(struct expr_func *f, vec_expr_t *args, + void *c) { + (void) args; + struct nop_context *nop = (struct nop_context *) c; if (f->ctxsz == 0) { free(nop->p); return 0; @@ -127,35 +130,40 @@ static expr_num_t user_func_nop(struct expr_func *f, vec_expr_t *args, void *c) return 0; } -static expr_num_t user_func_add(struct expr_func *f, vec_expr_t *args, void *c) { - (void)f, (void)c; +static expr_num_t user_func_add(struct expr_func *f, vec_expr_t *args, + void *c) { + (void) f, (void) c; expr_num_t a = expr_eval(&vec_nth(args, 0)); expr_num_t b = expr_eval(&vec_nth(args, 1)); return a + b; } -static expr_num_t user_func_next(struct expr_func *f, vec_expr_t *args, void *c) { - (void)f, (void)c; +static expr_num_t user_func_next(struct expr_func *f, vec_expr_t *args, + void *c) { + (void) f, (void) c; expr_num_t a = expr_eval(&vec_nth(args, 0)); return a + 1; } -static expr_num_t user_func_print(struct expr_func *f, vec_expr_t *args, void *c) { - (void)f, (void)c; +static expr_num_t user_func_print(struct expr_func *f, vec_expr_t *args, + void *c) { + (void) f, (void) c; int i; struct expr e; fprintf(stderr, ">> "); - vec_foreach(args, e, i) { fprintf(stderr, "%f ", expr_eval(&e)); } + vec_foreach(args, e, i) { + fprintf(stderr, "%f ", expr_eval(&e)); + } fprintf(stderr, "\n"); return 0; } static struct expr_func user_funcs[] = { - {"nop", user_func_nop, user_func_nop_cleanup, sizeof(struct nop_context)}, - {"add", user_func_add, NULL, 0}, - {"next", user_func_next, NULL, 0}, - {"print", user_func_print, NULL, 0}, - {NULL, NULL, NULL, 0}, + {"nop", user_func_nop, user_func_nop_cleanup, sizeof(struct nop_context)}, + {"add", user_func_add, NULL, 0}, + {"next", user_func_next, NULL, 0}, + {"print", user_func_print, NULL, 0}, + {NULL, NULL, NULL, 0}, }; static void test_expr(char *s, expr_num_t expected) { @@ -168,7 +176,7 @@ static void test_expr(char *s, expr_num_t expected) { } expr_num_t result = expr_eval(e); - char *p = (char *)malloc(strlen(s) + 1); + char *p = (char *) malloc(strlen(s) + 1); strncpy(p, s, strlen(s) + 1); for (char *it = p; *it; it++) { if (*it == '\n') { @@ -188,22 +196,20 @@ static void test_expr(char *s, expr_num_t expected) { } static void test_expr_error(char *s, int near, int error) { - const char *ERRORS[] = { - "EXPR_ERR_UNKNOWN", - "EXPR_ERR_UNEXPECTED_NUMBER", - "EXPR_ERR_UNEXPECTED_WORD", - "EXPR_ERR_UNEXPECTED_PARENS", - "EXPR_ERR_MISS_EXPECTED_OPERAND", - "EXPR_ERR_UNKNOWN_OPERATOR", - "EXPR_ERR_INVALID_FUNC_NAME", - "EXPR_ERR_BAD_CALL", - "EXPR_ERR_BAD_PARENS", - "EXPR_ERR_TOO_FEW_FUNC_ARGS", - "EXPR_ERR_FIRST_ARG_IS_NOT_VAR", - "EXPR_ERR_ALLOCATION_FAILED", - "EXPR_ERR_BAD_VARIABLE_NAME", - "EXPR_ERR_BAD_ASSIGNMENT" - }; + const char *ERRORS[] = {"EXPR_ERR_UNKNOWN", + "EXPR_ERR_UNEXPECTED_NUMBER", + "EXPR_ERR_UNEXPECTED_WORD", + "EXPR_ERR_UNEXPECTED_PARENS", + "EXPR_ERR_MISS_EXPECTED_OPERAND", + "EXPR_ERR_UNKNOWN_OPERATOR", + "EXPR_ERR_INVALID_FUNC_NAME", + "EXPR_ERR_BAD_CALL", + "EXPR_ERR_BAD_PARENS", + "EXPR_ERR_TOO_FEW_FUNC_ARGS", + "EXPR_ERR_FIRST_ARG_IS_NOT_VAR", + "EXPR_ERR_ALLOCATION_FAILED", + "EXPR_ERR_BAD_VARIABLE_NAME", + "EXPR_ERR_BAD_ASSIGNMENT"}; struct expr_var_list vars = {0}; int n, f; struct expr *e = expr_create2(s, strlen(s), &vars, user_funcs, &n, &f); @@ -212,11 +218,13 @@ static void test_expr_error(char *s, int near, int error) { status = 1; } if (n != near) { - printf("FAIL: %s should return error near to %d, but returned at %d\n", s, near, n); + printf("FAIL: %s should return error near to %d, but returned at %d\n", s, + near, n); status = 1; } if (f != error) { - printf("FAIL: %s should return error %s, but returned %s\n", s, ERRORS[-error], ERRORS[-f]); + printf("FAIL: %s should return error %s, but returned %s\n", s, + ERRORS[-error], ERRORS[-f]); status = 1; } expr_destroy(e, &vars); @@ -386,7 +394,7 @@ static void test_benchmark(const char *s) { double end = t.tv_sec + t.tv_usec * 1e-6; expr_destroy(e, &vars); double ns = 1000000000 * (end - start) / N; - printf("BENCH %40s:\t%f ns/op (%dM op/sec)\n", s, ns, (int)(1000 / ns)); + printf("BENCH %40s:\t%f ns/op (%dM op/sec)\n", s, ns, (int) (1000 / ns)); } static void test_bad_syntax() { @@ -404,7 +412,8 @@ static void test_bad_syntax() { test_expr_error("nop=", 4, EXPR_ERR_BAD_ASSIGNMENT); test_expr_error("nop(", 4, EXPR_ERR_BAD_PARENS); test_expr_error("unknownfunc()", 12, EXPR_ERR_INVALID_FUNC_NAME); - test_expr_error("$(recurse, recurse()), recurse()", 19, EXPR_ERR_INVALID_FUNC_NAME); + test_expr_error("$(recurse, recurse()), recurse()", 19, + EXPR_ERR_INVALID_FUNC_NAME); test_expr_error("),", 1, EXPR_ERR_UNEXPECTED_PARENS); test_expr_error("+(", 1, EXPR_ERR_MISS_EXPECTED_OPERAND); test_expr_error("2=3", 3, EXPR_ERR_BAD_ASSIGNMENT); From 7640bb6f663e5f281fd063ca93c372c846a23f7c Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 10:22:26 -0300 Subject: [PATCH 07/32] added macros allowing to customize functions for memory management and string handling --- expr.h | 72 ++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/expr.h b/expr.h index 5080dde..77506f6 100644 --- a/expr.h +++ b/expr.h @@ -19,14 +19,43 @@ extern "C" { #define expr_num_t double #endif /* expr_num_t */ +/* + * Memory management + */ +#ifndef expr_alloc +#define expr_alloc(sz) calloc(1, (sz)) +#endif /* expr_alloc */ +#ifndef expr_realloc +#define expr_realloc realloc +#endif /* expr_realloc */ +#ifndef expr_free +#define expr_free free +#endif /* expr_free */ + +/* + * String handling + */ +#ifndef expr_strlen +#define expr_strlen strlen +#endif /* expr_strlen */ +#ifndef expr_strncmp +#define expr_strncmp strncmp +#endif /* expr_strncmp */ +#ifndef expr_strncpy +#define expr_strncpy strncpy +#endif /* expr_strncpy */ +#ifndef expr_snprintf +#define expr_snprintf snprintf +#endif /* expr_snprintf */ + /* * Math */ #ifndef expr_pow -#define expr_pow(x, y) pow((x), (y)) +#define expr_pow pow #endif /* expr_powf */ #ifndef expr_fmod -#define expr_fmod(x, y) fmod((x), (y)) +#define expr_fmod fmod #endif /* expr_fmodf */ /* @@ -36,7 +65,7 @@ static int vec_expand(char **buf, int *length, int *cap, int memsz) { if (*length + 1 > *cap) { void *ptr; int n = (*cap == 0) ? 1 : *cap << 1; - ptr = realloc(*buf, n * memsz); + ptr = expr_realloc(*buf, n * memsz); if (ptr == NULL) { return -1; /* allocation failed */ } @@ -61,7 +90,8 @@ static int vec_expand(char **buf, int *length, int *cap, int memsz) { #define vec_nth(v, i) (v)->buf[i] #define vec_peek(v) (v)->buf[(v)->len - 1] #define vec_pop(v) (v)->buf[--(v)->len] -#define vec_free(v) (free((v)->buf), (v)->buf = NULL, (v)->len = (v)->cap = 0) +#define vec_free(v) \ + (expr_free((v)->buf), (v)->buf = NULL, (v)->len = (v)->cap = 0) #define vec_foreach(v, var, iter) \ if ((v)->len > 0) \ for ((iter) = 0; (iter) < (v)->len && (((var) = (v)->buf[(iter)]), 1); \ @@ -216,7 +246,7 @@ static struct { static enum expr_type expr_op(const char *s, size_t len, int unary) { for (unsigned int i = 0; i < sizeof(OPS) / sizeof(OPS[0]); i++) { - if (strlen(OPS[i].s) == len && strncmp(OPS[i].s, s, len) == 0 && + if (expr_strlen(OPS[i].s) == len && expr_strncmp(OPS[i].s, s, len) == 0 && (unary == -1 || expr_is_unary(OPS[i].op) == unary)) { return OPS[i].op; } @@ -263,7 +293,7 @@ struct expr_func { static struct expr_func *expr_func(struct expr_func *funcs, const char *s, size_t len) { for (struct expr_func *f = funcs; f->name; f++) { - if (strlen(f->name) == len && strncmp(f->name, s, len) == 0) { + if (expr_strlen(f->name) == len && expr_strncmp(f->name, s, len) == 0) { return f; } } @@ -290,17 +320,17 @@ static struct expr_var *expr_var(struct expr_var_list *vars, const char *s, return NULL; } for (v = vars->head; v; v = v->next) { - if (strlen(v->name) == len && strncmp(v->name, s, len) == 0) { + if (expr_strlen(v->name) == len && expr_strncmp(v->name, s, len) == 0) { return v; } } - v = (struct expr_var *) calloc(1, sizeof(struct expr_var) + len + 1); + v = expr_alloc(sizeof(struct expr_var) + len + 1); if (v == NULL) { return NULL; /* allocation failed */ } v->next = vars->head; v->value = 0; - strncpy(v->name, s, len); + expr_strncpy(v->name, s, len); v->name[len] = '\0'; vars->head = v; return v; @@ -596,7 +626,7 @@ static inline void expr_copy(struct expr *dst, struct expr *src) { vec_push(&dst->param.func.args, tmp); } if (src->param.func.f->ctxsz > 0) { - dst->param.func.context = calloc(1, src->param.func.f->ctxsz); + dst->param.func.context = expr_alloc(src->param.func.f->ctxsz); } } else if (src->type == OP_CONST) { dst->param.num.value = src->param.num.value; @@ -687,7 +717,8 @@ static struct expr *expr_create2(const char *s, size_t len, int has_macro = 0; struct macro m; vec_foreach(¯os, m, i) { - if (strlen(m.name) == idn && strncmp(m.name, id, idn) == 0) { + if (expr_strlen(m.name) == idn && + expr_strncmp(m.name, id, idn) == 0) { has_macro = 1; break; } @@ -770,8 +801,8 @@ static struct expr *expr_create2(const char *s, size_t len, int found = -1; struct macro m; vec_foreach(¯os, m, i) { - if (strlen(m.name) == (size_t) str.n && - strncmp(m.name, str.s, str.n) == 0) { + if (expr_strlen(m.name) == (size_t) str.n && + expr_strncmp(m.name, str.s, str.n) == 0) { found = i; } } @@ -782,8 +813,9 @@ static struct expr *expr_create2(const char *s, size_t len, /* Assign macro parameters */ for (int j = 0; j < vec_len(&arg.args); j++) { char varname[4]; - snprintf(varname, sizeof(varname) - 1, "$%d", (j + 1)); - struct expr_var *v = expr_var(vars, varname, strlen(varname)); + expr_snprintf(varname, sizeof(varname) - 1, "$%d", (j + 1)); + struct expr_var *v = + expr_var(vars, varname, expr_strlen(varname)); struct expr ev = expr_varref(v); struct expr assign = expr_binary(OP_ASSIGN, ev, vec_nth(&arg.args, j)); @@ -809,7 +841,7 @@ static struct expr *expr_create2(const char *s, size_t len, bound_func.param.func.f = f; bound_func.param.func.args = arg.args; if (f->ctxsz > 0) { - void *p = calloc(1, f->ctxsz); + void *p = expr_alloc(f->ctxsz); if (p == NULL) { *error = EXPR_ERR_ALLOCATION_FAILED; goto cleanup; /* allocation failed */ @@ -885,7 +917,7 @@ static struct expr *expr_create2(const char *s, size_t len, } } - result = (struct expr *) calloc(1, sizeof(struct expr)); + result = expr_alloc(sizeof(struct expr)); if (result != NULL) { if (vec_len(&es) == 0) { result->type = OP_CONST; @@ -950,7 +982,7 @@ static void expr_destroy_args(struct expr *e) { if (e->param.func.f->cleanup != NULL) { e->param.func.f->cleanup(e->param.func.f, e->param.func.context); } - free(e->param.func.context); + expr_free(e->param.func.context); } } else if (e->type != OP_CONST && e->type != OP_VAR) { vec_foreach(&e->param.op.args, arg, i) { @@ -963,12 +995,12 @@ static void expr_destroy_args(struct expr *e) { static void expr_destroy(struct expr *e, struct expr_var_list *vars) { if (e != NULL) { expr_destroy_args(e); - free(e); + expr_free(e); } if (vars != NULL) { for (struct expr_var *v = vars->head; v;) { struct expr_var *next = v->next; - free(v); + expr_free(v); v = next; } } From 4917e27a1cc17c418be87f2191cf83d6b3d54194 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 10:43:39 -0300 Subject: [PATCH 08/32] added function expr_calc() --- expr.h | 14 ++++++++++++++ expr_test.c | 19 +++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/expr.h b/expr.h index 77506f6..608975c 100644 --- a/expr.h +++ b/expr.h @@ -1006,6 +1006,20 @@ static void expr_destroy(struct expr *e, struct expr_var_list *vars) { } } +static expr_num_t expr_calc(const char *s) { + struct expr_var_list vars = {0}; + struct expr_func funcs[] = {{NULL, NULL, NULL, 0}}; + struct expr *e; + expr_num_t r; + e = expr_create(s, expr_strlen(s), &vars, funcs); + if (e == NULL) { + return NAN; + } + r = expr_eval(e); + expr_destroy(e, &vars); + return r; +} + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/expr_test.c b/expr_test.c index bf201d2..df38cff 100644 --- a/expr_test.c +++ b/expr_test.c @@ -91,7 +91,7 @@ static int assert_tokens(char *s, char **expected) { } } -static void test_tokizer() { +static void test_tokenizer() { char **TESTS[] = { (char *[]){"", NULL}, (char *[]){"1", "1", NULL}, @@ -436,11 +436,24 @@ static void test_bad_syntax() { test_expr_error("a+10/((1+x)-b)-((5-(8/2))", 25, EXPR_ERR_BAD_PARENS); } +static void test_calc() { + const char *p = "2+3"; + expr_num_t result = expr_calc(p); + expr_num_t expected = 5; + if ((isnan(result) && !isnan(expected)) || + fabs(result - expected) > 0.00001f) { + printf("FAIL: %s: %f != %f\n", p, result, expected); + status = 1; + } else { + printf("OK: %s == %f\n", p, expected); + } +} + int main() { test_vector(); test_vars(); - test_tokizer(); + test_tokenizer(); test_empty(); test_const(); @@ -459,6 +472,8 @@ int main() { test_bad_syntax(); + test_calc(); + test_benchmark("5"); test_benchmark("5+5+5+5+5+5+5+5+5+5"); test_benchmark("5*5*5*5*5*5*5*5*5*5"); From adaaf22132403a5b67228a72d08fc6c339969e87 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 11:51:08 -0300 Subject: [PATCH 09/32] fixed makefile for "make all" --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 7a2e8e5..66b8740 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,9 @@ LDFLAGS ?= -lm -O0 -g TESTBIN := expr_test all: - @echo make test - run tests - @echo make llvm-cov - report test coverage using LLVM (set LLVM_VER if needed) - @echo make gcov - report test coverage (set GCC_VER if needed) + @echo make test - run tests + @echo make llvm-cov - report test coverage using LLVM \(set LLVM_VER if needed\) + @echo make gcov - report test coverage \(set GCC_VER if needed\) test: $(TESTBIN) ./$(TESTBIN) From 5728919b64d27ad94b7a6544128854cb724d16b7 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 12:10:40 -0300 Subject: [PATCH 10/32] added support for cmake and three examples --- CMakeLists.txt | 25 +++++++++++++++++++++++++ example1.c | 6 ++++++ example2.c | 9 +++++++++ example3.c | 28 ++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 example1.c create mode 100644 example2.c create mode 100644 example3.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..eecbf90 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.0.2) + +project(expr C) + +set(CMAKE_C_STANDARD 99) + +option(EXPR_PICKY_COMPILER "Enable picky compiler options" ON) + +if(EXPR_PICKY_COMPILER) + set(CMAKE_C_FLAGS + "${CMAKE_C_FLAGS} -Wall -Werror -Wextra -Wno-unused-function -Wpedantic -Wdeclaration-after-statement -Wstrict-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline" + ) +endif() + +add_executable(example1 example1.c) +target_link_libraries(example1 m) + +add_executable(example2 example2.c) +target_link_libraries(example2 m) + +add_executable(example3 example3.c) +target_link_libraries(example3 m) + +add_executable(test expr_test.c) +target_link_libraries(test m) diff --git a/example1.c b/example1.c new file mode 100644 index 0000000..d13133e --- /dev/null +++ b/example1.c @@ -0,0 +1,6 @@ +#include "expr.h" + +int main(void) { + printf("result: %f\n", expr_calc("2 + 3")); + return 0; +} diff --git a/example2.c b/example2.c new file mode 100644 index 0000000..7d45d3b --- /dev/null +++ b/example2.c @@ -0,0 +1,9 @@ +#include "expr.h" + +int main(void) { + printf("macro definition: %f\n", expr_calc("$(mysum, $1 + $2), mysum(2, 3)")); + printf("variable assignment: %f\n", expr_calc("a=2, b=3, a+b")); + printf("all together: %f\n", + expr_calc("$(mysum, $1 + $2), a=2, b=3, mysum(a, b)")); + return 0; +} diff --git a/example3.c b/example3.c new file mode 100644 index 0000000..0485051 --- /dev/null +++ b/example3.c @@ -0,0 +1,28 @@ +#include "expr.h" + +// Custom function that returns the sum of its two arguments +static expr_num_t sum(struct expr_func *f, vec_expr_t *args, void *c) { + (void) f; + (void) c; + expr_num_t a = expr_eval(&vec_nth(args, 0)); + expr_num_t b = expr_eval(&vec_nth(args, 1)); + return a + b; +} + +static struct expr_func user_funcs[] = { + {"sum", sum, NULL, 0}, + {NULL, NULL, NULL, 0}, +}; + +int main(void) { + const char *s = "x = 5, sum(2, x)"; + struct expr_var_list vars = {0}; + struct expr *e = expr_create(s, strlen(s), &vars, user_funcs); + if (e == NULL) { + printf("Syntax error"); + return 1; + } + printf("result: %f\n", expr_eval(e)); + expr_destroy(e, &vars); + return 0; +} From 10637db254a6c76eaeffb0bb895841d915918f40 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 12:49:46 -0300 Subject: [PATCH 11/32] moved files to their own directories --- .gitignore | 3 +++ CMakeLists.txt | 8 +++++--- Makefile | 5 +++-- example1.c => examples/example1.c | 0 example2.c => examples/example2.c | 0 example3.c => examples/example3.c | 0 expr.h => include/expr.h | 0 expr_debug.h => include/expr_debug.h | 0 8 files changed, 11 insertions(+), 5 deletions(-) rename example1.c => examples/example1.c (100%) rename example2.c => examples/example2.c (100%) rename example3.c => examples/example3.c (100%) rename expr.h => include/expr.h (100%) rename expr_debug.h => include/expr_debug.h (100%) diff --git a/.gitignore b/.gitignore index 9d4032c..d202c2a 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,6 @@ config.mk # Build directory build/ + +# VSCode directory +.vscode diff --git a/CMakeLists.txt b/CMakeLists.txt index eecbf90..8e0853a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,13 +12,15 @@ if(EXPR_PICKY_COMPILER) ) endif() -add_executable(example1 example1.c) +include_directories(include) + +add_executable(example1 examples/example1.c) target_link_libraries(example1 m) -add_executable(example2 example2.c) +add_executable(example2 examples/example2.c) target_link_libraries(example2 m) -add_executable(example3 example3.c) +add_executable(example3 examples/example3.c) target_link_libraries(example3 m) add_executable(test expr_test.c) diff --git a/Makefile b/Makefile index 66b8740..40e043a 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ -CFLAGS ?= -std=c99 -g -O0 -pedantic -Wall -Wextra +CFLAGS ?= -std=c99 -g -O0 -pedantic -Wall -Wextra -Iinclude LDFLAGS ?= -lm -O0 -g +INC_DIR := include TESTBIN := expr_test all: @@ -14,7 +15,7 @@ test: $(TESTBIN) $(TESTBIN): expr_test.o $(CC) $^ $(LDFLAGS) -o $@ -expr_test.o: expr_test.c expr.h expr_debug.h +expr_test.o: expr_test.c $(INC_DIR)/expr.h $(INC_DIR)/expr_debug.h llvm-cov: CC := clang$(LLVM_VER) llvm-cov: CFLAGS += -fprofile-instr-generate -fcoverage-mapping diff --git a/example1.c b/examples/example1.c similarity index 100% rename from example1.c rename to examples/example1.c diff --git a/example2.c b/examples/example2.c similarity index 100% rename from example2.c rename to examples/example2.c diff --git a/example3.c b/examples/example3.c similarity index 100% rename from example3.c rename to examples/example3.c diff --git a/expr.h b/include/expr.h similarity index 100% rename from expr.h rename to include/expr.h diff --git a/expr_debug.h b/include/expr_debug.h similarity index 100% rename from expr_debug.h rename to include/expr_debug.h From 92aaa47b10fb93557fb7de617abd794c4c0de4b7 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 13:54:22 -0300 Subject: [PATCH 12/32] enabled picky compiler options --- .gitignore | 2 ++ CMakeLists.txt | 2 +- Makefile | 2 +- expr_test.c | 36 ++++++++++++++++++------------------ 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index d202c2a..77eda33 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,8 @@ expr_test *.gcda *.gcno *.gcov +*.profraw +*.profdata # Debug files *.dSYM/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e0853a..e5b3918 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ option(EXPR_PICKY_COMPILER "Enable picky compiler options" ON) if(EXPR_PICKY_COMPILER) set(CMAKE_C_FLAGS - "${CMAKE_C_FLAGS} -Wall -Werror -Wextra -Wno-unused-function -Wpedantic -Wdeclaration-after-statement -Wstrict-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline" + "${CMAKE_C_FLAGS} -Wall -Werror -Wextra -Wno-unused-function -Wpedantic -Wstrict-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline" ) endif() diff --git a/Makefile b/Makefile index 40e043a..8f82f7a 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -CFLAGS ?= -std=c99 -g -O0 -pedantic -Wall -Wextra -Iinclude +CFLAGS ?= -std=c99 -g -O0 -pedantic -Wall -Werror -Wextra -Wno-unused-function -Wpedantic -Wstrict-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Iinclude LDFLAGS ?= -lm -O0 -g INC_DIR := include diff --git a/expr_test.c b/expr_test.c index df38cff..e5197ba 100644 --- a/expr_test.c +++ b/expr_test.c @@ -17,7 +17,7 @@ int status = 0; typedef vec(int) test_vec_int_t; typedef vec(char *) test_vec_str_t; -static void test_vector() { +static void test_vector(void) { test_vec_int_t ints = vec_init(); test_vec_str_t strings = vec_init(); @@ -43,7 +43,7 @@ static void test_vector() { /* * VARIABLES VECTOR TEST */ -static void test_vars() { +static void test_vars(void) { struct expr_var_list vars = {0}; struct expr_var *a = expr_var(&vars, "a", 1); @@ -91,7 +91,7 @@ static int assert_tokens(char *s, char **expected) { } } -static void test_tokenizer() { +static void test_tokenizer(void) { char **TESTS[] = { (char *[]){"", NULL}, (char *[]){"1", "1", NULL}, @@ -230,20 +230,20 @@ static void test_expr_error(char *s, int near, int error) { expr_destroy(e, &vars); } -static void test_empty() { +static void test_empty(void) { test_expr("", 0); test_expr(" ", 0); test_expr(" \t \n ", 0); } -static void test_const() { +static void test_const(void) { test_expr("1", 1); test_expr(" 1 ", 1); test_expr("12", 12); test_expr("12.3", 12.3); } -static void test_unary() { +static void test_unary(void) { test_expr("-1", -1); test_expr("--1", -(-1)); test_expr("!0 ", !0); @@ -251,7 +251,7 @@ static void test_unary() { test_expr("^3", ~3); } -static void test_binary() { +static void test_binary(void) { test_expr("1+2", 1 + 2); test_expr("10-2", 10 - 2); test_expr("2*3", 2 * 3); @@ -295,7 +295,7 @@ static void test_binary() { test_expr("2**2**3", 256); /* 2^(2^3), not (2^2)^3 */ } -static void test_logical() { +static void test_logical(void) { test_expr("2&&3", 3); test_expr("0&&3", 0); test_expr("3&&0", 0); @@ -310,7 +310,7 @@ static void test_logical() { test_expr("(3%0)||1", 1); } -static void test_parens() { +static void test_parens(void) { test_expr("(1+2)*3", (1 + 2) * 3); test_expr("(1)", 1); test_expr("(2.4)", 2.4); @@ -319,12 +319,12 @@ static void test_parens() { test_expr("(((3)))*(1+(2))", 9); } -static void test_assign() { +static void test_assign(void) { test_expr("x=5", 5); test_expr("x=y=3", 3); } -static void test_comma() { +static void test_comma(void) { test_expr("2,3,4", 4); test_expr("2+3,4*5", 4 * 5); test_expr("x=5, x", 5); @@ -333,7 +333,7 @@ static void test_comma() { test_expr("x=5, x = x+1", 6); } -static void test_funcs() { +static void test_funcs(void) { test_expr("add(1,2) + next(3)", 7); test_expr("add(1,next(2))", 4); test_expr("add(1,1+1) + add(2*2+1,2)", 10); @@ -347,12 +347,12 @@ static void test_funcs() { test_expr("$(triw, ($1 * 256) & 255), triw(0.1)+triw(0.7)+triw(0.2)", 255); } -static void test_name_collision() { +static void test_name_collision(void) { test_expr("next=5", 5); test_expr("next=2,next(5)+next", 8); } -static void test_fancy_variable_names() { +static void test_fancy_variable_names(void) { test_expr("one=1", 1); test_expr("один=1", 1); test_expr("six=6, seven=7, six*seven", 42); @@ -363,7 +363,7 @@ static void test_fancy_variable_names() { test_expr("x#4=12, x#3=3, x#4+x#3", 15); } -static void test_auto_comma() { +static void test_auto_comma(void) { test_expr("a=3\na+2\n", 5); test_expr("a=3\n\n\na+2\n", 5); test_expr("\n\na=\n3\n\n\na+2\n", 5); @@ -397,7 +397,7 @@ static void test_benchmark(const char *s) { printf("BENCH %40s:\t%f ns/op (%dM op/sec)\n", s, ns, (int) (1000 / ns)); } -static void test_bad_syntax() { +static void test_bad_syntax(void) { test_expr_error("(", 1, EXPR_ERR_BAD_PARENS); test_expr_error(")", 1, EXPR_ERR_UNEXPECTED_PARENS); test_expr_error("()3", 2, EXPR_ERR_UNEXPECTED_NUMBER); @@ -436,7 +436,7 @@ static void test_bad_syntax() { test_expr_error("a+10/((1+x)-b)-((5-(8/2))", 25, EXPR_ERR_BAD_PARENS); } -static void test_calc() { +static void test_calc(void) { const char *p = "2+3"; expr_num_t result = expr_calc(p); expr_num_t expected = 5; @@ -449,7 +449,7 @@ static void test_calc() { } } -int main() { +int main(void) { test_vector(); test_vars(); From 04081df0584c56829fdac9a4d394e225a1b1c2e2 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 15:53:06 -0300 Subject: [PATCH 13/32] updated README file [ci skip] --- README.md | 108 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index d2967aa..207838b 100644 --- a/README.md +++ b/README.md @@ -7,81 +7,88 @@ input and returns floating-point number as a result. ## Features -* Supports arithmetic, bitwise and logical operators -* Supports variables -* Can be extended with custom functions -* Simple evaluation takes ~50 nanoseconds on an average PC -* Low memory usage makes it suitable for embedded systems -* Pure C99 with no external dependencies -* Good test coverage -* Easy to understand (~600 LOC in a single header file) +- Supports arithmetic, bitwise and logical operators +- Supports variables +- Supports macros +- Can be extended with custom functions +- Error handling with error kind and position +- Simple evaluation takes ~50 nanoseconds on an average PC +- Low memory usage makes it suitable for embedded systems +- Pure C99 with no external dependencies +- Good test coverage +- Easy to understand (~600 LOC in a single header file) ## Example ```c #include "expr.h" -// Custom function that returns the sum of its two arguments -static float add(struct expr_func *f, vec_expr_t *args, void *c) { - float a = expr_eval(&vec_nth(args, 0)); - float b = expr_eval(&vec_nth(args, 1)); - return a + b; +int main(void) { + printf("result: %f\n", expr_calc("2 + 3")); + return 0; } +``` -static struct expr_func user_funcs[] = { - {"add", add, NULL, 0}, - {NULL, NULL, NULL, 0}, -}; - -int main() { - const char *s = "x = 5, add(2, x)"; - struct expr_var_list vars = {0}; - struct expr *e = expr_create(s, strlen(s), &vars, user_funcs); - if (e == NULL) { - printf("Syntax error"); - return 1; - } +Output: `result: 5.000000` - float result = expr_eval(e); - printf("result: %f\n", result); +## API - expr_destroy(e, &vars); - return 0; -} +```c +static struct expr *expr_create2(const char *s, size_t len, + struct expr_var_list *vars, + struct expr_func *funcs, int *near, + int *error); + +static struct expr *expr_create(const char *s, size_t len, + struct expr_var_list *vars, + struct expr_func *funcs); ``` -Output: `result: 7.000000` +Returns compiled expression from the given string. If expression uses +variables - they are bound to `vars`, so you can modify values before evaluation +or check the results after the evaluation. The `near` and `error` arguments are +used for error handling. -## API +```c +static void expr_destroy(struct expr *e, struct expr_var_list *vars); +``` -`struct expr *expr_create(const char *s, size_t len, struct expr_var_list -*vars, struct expr_func *funcs)` - returns compiled expression from the given -string. If expression uses variables - they are bound to `vars`, so you can -modify values before evaluation or check the results after the evaluation. +Cleans up. Parameters can be `NULL` (e.g. if you want to clean up expression, +but reuse variables for another expression). -`float expr_eval(struct expr *e)` - evaluates compiled expression. +```c +static expr_num_t expr_eval(struct expr *e); +``` + +Evaluates compiled expression. -`void expr_destroy(struct expr *e, struct expr_var_list *vars)` - cleans up -memory. Parameters can be NULL (e.g. if you want to clean up expression, but -reuse variables for another expression). +```c +static struct expr_var *expr_var(struct expr_var_list *vars, const char *s, + size_t len); +``` -`struct expr_var *expr_var(struct expr_var *vars, const char *s, size_t len)` - -returns/creates variable of the given name in the given list. This can be used +Returns/creates variable of the given name in the given list. This can be used to get variable references to get/set them manually. +```c +static expr_num_t expr_calc(const char *s); +``` + +Takes an expression and immediately returns the result of it. If there is a parse error, `expr_calc()` returns `NAN`. + ## Supported operators -* Arithmetics: `+`, `-`, `*`, `/`, `%` (remainder), `**` (power) -* Bitwise: `<<`, `>>`, `&`, `|`, `^` (xor or unary bitwise negation) -* Logical: `<`, `>`, `==`, `!=`, `<=`, `>=`, `&&`, `||`, `!` (unary not) -* Other: `=` (assignment, e.g. `x=y=5`), `,` (separates expressions or function parameters) +- Arithmetics: `+`, `-`, `*`, `/`, `%` (remainder), `**` (power) +- Bitwise: `<<`, `>>`, `&`, `|`, `^` (xor or unary bitwise negation) +- Logical: `<`, `>`, `==`, `!=`, `<=`, `>=`, `&&`, `||`, `!` (unary not) +- Other: `=` (assignment, e.g. `x=y=5`), `,` (separates expressions or function parameters) Only the following functions from libc are used to reduce the footprint and make it easier to use: -* calloc, realloc and free - memory management -* isnan, isinf, fmodf, powf - math operations -* strlen, strncmp, strncpy, strtof - tokenizing and parsing +- `calloc()`, `realloc()` and `free()` - memory management (all replaceable via macro) +- `isnan()`, `isinf()`, `fmod()`, `pow()` - math operations (`fmod()` and `pow()` replaceable via macro) +- `strlen()`, `strncmp()`, `strncpy()` and `snprintf()` - tokenizing and parsing (all replaceable via macro) ## Running tests @@ -98,4 +105,3 @@ explicitly, e.g. `make llvm-cov LLVM_VER=-3.8` or `make gcov GCC_VER=-5`. Code is distributed under MIT license, feel free to use it in your proprietary projects as well. - From 8ab5484bc1218f88773878cbabedb6723fea49c0 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 16:05:21 -0300 Subject: [PATCH 14/32] added file .cmake-format --- .cmake-format | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .cmake-format diff --git a/.cmake-format b/.cmake-format new file mode 100644 index 0000000..54f4495 --- /dev/null +++ b/.cmake-format @@ -0,0 +1,10 @@ +# How wide to allow formatted cmake files. +line_width = 80 +# How many spaces to tab for indent. +tab_size = 2 +# What style line endings to use in the output. +line_ending = 'unix' +# Enable comment markup parsing and reflow. +enable_markup = False +# Specify the encoding of the output file. +output_encoding = 'utf-8' From 38bdda00bd55ee0f4ee6be3390ba4c817931e1e8 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 16:06:19 -0300 Subject: [PATCH 15/32] added target and instructions for PVS-Studio analysis --- CMakeLists.txt | 23 ++ README.md | 23 ++ cmake/PVS-Studio.cmake | 552 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 598 insertions(+) create mode 100644 cmake/PVS-Studio.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index e5b3918..5551986 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,12 @@ set(CMAKE_C_STANDARD 99) option(EXPR_PICKY_COMPILER "Enable picky compiler options" ON) +option(EXPR_PVS_STUDIO "Enable PVS-Studio analysis" OFF) + +#TODO: coverage + +set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) + if(EXPR_PICKY_COMPILER) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wextra -Wno-unused-function -Wpedantic -Wstrict-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline" @@ -25,3 +31,20 @@ target_link_libraries(example3 m) add_executable(test expr_test.c) target_link_libraries(test m) + +if(EXPR_PVS_STUDIO) + include(PVS-Studio) + if(NOT CMAKE_EXPORT_COMPILE_COMMANDS) + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + endif() + pvs_studio_add_target( + TARGET + pvs_studio_analysis + ALL + FORMAT + fullhtml + ANALYZE + test + LOG + pvs_studio_fullhtml) +endif() diff --git a/README.md b/README.md index 207838b..99128a4 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,29 @@ depending on whether you use GCC or LLVM/Clang. Since people may have different compiler versions, one may specify a version explicitly, e.g. `make llvm-cov LLVM_VER=-3.8` or `make gcov GCC_VER=-5`. +## Building with CMake + +To build all the examples, tests and benchmarks using [CMake](https://cmake.org), do: + +```bash +mkdir build && cd build/ +cmake .. +make +``` + +## Running PVS Studio analysis + +Download and install the PVS's command line tool [`how-to-use-pvs-studio-free`](https://github.com/viva64/how-to-use-pvs-studio-free) according its site instructions. After that, in the library root directory, perform the following commands: + +```bash +how-to-use-pvs-studio-free -c 2 -m . +mkdir build && cd build/ +cmake -DEXPR_PVS_STUDIO=ON .. +make pvs_studio_analysis +``` + +The full PVS report will be generated in the `build/pvs_studio_fullhtml` directory. + ## License Code is distributed under MIT license, feel free to use it in your proprietary diff --git a/cmake/PVS-Studio.cmake b/cmake/PVS-Studio.cmake new file mode 100644 index 0000000..66e4cfb --- /dev/null +++ b/cmake/PVS-Studio.cmake @@ -0,0 +1,552 @@ +# 2006-2008 (c) Viva64.com Team +# 2008-2018 (c) OOO "Program Verification Systems" +# +# Version 12 + +cmake_minimum_required(VERSION 2.8.12) +cmake_policy(SET CMP0054 NEW) + +if (PVS_STUDIO_AS_SCRIPT) + # This code runs at build time. + # It executes pvs-studio-analyzer and propagates its return value. + + set(in_cl_params FALSE) + set(additional_args) + + foreach (arg ${PVS_STUDIO_COMMAND}) + if (NOT in_cl_params) + if ("${arg}" STREQUAL "--cl-params") + set(in_cl_params TRUE) + endif () + else () + # A workaround for macOS frameworks (e.g. QtWidgets.framework) + # You can test this workaround on this project: https://github.com/easyaspi314/MidiEditor/tree/gba + if (APPLE AND "${arg}" MATCHES "^-I(.*)\\.framework$") + STRING(REGEX REPLACE "^-I(.*)\\.framework$" "\\1.framework" framework "${arg}") + if (IS_ABSOLUTE "${framework}") + get_filename_component(framework "${framework}" DIRECTORY) + list(APPEND additional_args "-iframework") + list(APPEND additional_args "${framework}") + endif () + endif () + endif () + endforeach () + + execute_process(COMMAND ${PVS_STUDIO_COMMAND} ${additional_args} + ERROR_VARIABLE error + RESULT_VARIABLE result) + + set(stderr_type "") + + if (result) + set(stderr_type FATAL_ERROR) + endif () + + if (result OR error) + message(${stderr_type} "${error}") + endif () + + return() +endif () + +if(__PVS_STUDIO_INCLUDED) + return() +endif() +set(__PVS_STUDIO_INCLUDED TRUE) + +set(PVS_STUDIO_SCRIPT "${CMAKE_CURRENT_LIST_FILE}") + +function (pvs_studio_log TEXT) + if (PVS_STUDIO_DEBUG) + message("PVS-Studio: ${TEXT}") + endif () +endfunction () + +function (pvs_studio_relative_path VAR ROOT FILEPATH) + set("${VAR}" "${FILEPATH}" PARENT_SCOPE) + if ("${FILEPATH}" MATCHES "^/.*$" OR "${FILEPATH}" MATCHES "^.:/.*$") + file(RELATIVE_PATH RPATH "${ROOT}" "${FILEPATH}") + if (NOT "${RPATH}" MATCHES "^\\.\\..*$") + set("${VAR}" "${RPATH}" PARENT_SCOPE) + endif () + endif () +endfunction () + +function (pvs_studio_join_path VAR DIR1 DIR2) + if ("${DIR2}" MATCHES "^(/|~|.:/).*$" OR "${DIR1}" STREQUAL "") + set("${VAR}" "${DIR2}" PARENT_SCOPE) + else () + set("${VAR}" "${DIR1}/${DIR2}" PARENT_SCOPE) + endif () +endfunction () + +macro (pvs_studio_append_flags_from_property CXX C DIR PREFIX) + if (NOT "${PROPERTY}" STREQUAL "NOTFOUND" AND NOT "${PROPERTY}" STREQUAL "PROPERTY-NOTFOUND") + foreach (PROP ${PROPERTY}) + pvs_studio_join_path(PROP "${DIR}" "${PROP}") + + if (APPLE AND "${PREFIX}" STREQUAL "-I" AND IS_ABSOLUTE "${PROP}" AND "${PROP}" MATCHES "\\.framework$") + get_filename_component(FRAMEWORK "${PROP}" DIRECTORY) + list(APPEND "${CXX}" "-iframework") + list(APPEND "${CXX}" "${FRAMEWORK}") + list(APPEND "${C}" "-iframework") + list(APPEND "${C}" "${FRAMEWORK}") + pvs_studio_log("framework: ${FRAMEWORK}") + elseif (NOT "${PROP}" STREQUAL "") + list(APPEND "${CXX}" "${PREFIX}${PROP}") + list(APPEND "${C}" "${PREFIX}${PROP}") + endif() + endforeach () + endif () +endmacro () + +macro (pvs_studio_append_standard_flag FLAGS STANDARD) + if ("${STANDARD}" MATCHES "^(99|11|14|17)$") + if ("${PVS_STUDIO_PREPROCESSOR}" MATCHES "gcc|clang") + list(APPEND "${FLAGS}" "-std=c++${STANDARD}") + endif () + endif () +endmacro () + +function (pvs_studio_set_directory_flags DIRECTORY CXX C) + set(CXX_FLAGS "${${CXX}}") + set(C_FLAGS "${${C}}") + + get_directory_property(PROPERTY DIRECTORY "${DIRECTORY}" INCLUDE_DIRECTORIES) + pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "${DIRECTORY}" "-I") + + get_directory_property(PROPERTY DIRECTORY "${DIRECTORY}" COMPILE_DEFINITIONS) + pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "" "-D") + + set("${CXX}" "${CXX_FLAGS}" PARENT_SCOPE) + set("${C}" "${C_FLAGS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_set_target_flags TARGET CXX C) + set(CXX_FLAGS "${${CXX}}") + set(C_FLAGS "${${C}}") + + set(prop_incdirs "$") + list(APPEND CXX_FLAGS "$<$:-I$-I>>") + list(APPEND C_FLAGS "$<$:-I$-I>>") + + set(prop_compdefs "$") + list(APPEND CXX_FLAGS "$<$:-D$-D>>") + list(APPEND C_FLAGS "$<$:-D$-D>>") + + set("${CXX}" "${CXX_FLAGS}" PARENT_SCOPE) + set("${C}" "${C_FLAGS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_set_source_file_flags SOURCE) + set(LANGUAGE "") + + string(TOLOWER "${SOURCE}" SOURCE_LOWER) + if ("${LANGUAGE}" STREQUAL "" AND "${SOURCE_LOWER}" MATCHES "^.*\\.(c|cpp|cc|cx|cxx|cp|c\\+\\+)$") + if ("${SOURCE}" MATCHES "^.*\\.c$") + set(LANGUAGE C) + else () + set(LANGUAGE CXX) + endif () + endif () + + if ("${LANGUAGE}" STREQUAL "C") + set(CL_PARAMS ${PVS_STUDIO_C_FLAGS} ${PVS_STUDIO_TARGET_C_FLAGS} -DPVS_STUDIO) + elseif ("${LANGUAGE}" STREQUAL "CXX") + set(CL_PARAMS ${PVS_STUDIO_CXX_FLAGS} ${PVS_STUDIO_TARGET_CXX_FLAGS} -DPVS_STUDIO) + endif () + + set(PVS_STUDIO_LANGUAGE "${LANGUAGE}" PARENT_SCOPE) + set(PVS_STUDIO_CL_PARAMS "${CL_PARAMS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_analyze_file SOURCE SOURCE_DIR BINARY_DIR) + set(PLOGS ${PVS_STUDIO_PLOGS}) + pvs_studio_set_source_file_flags("${SOURCE}") + + get_filename_component(SOURCE "${SOURCE}" REALPATH) + + get_source_file_property(PROPERTY "${SOURCE}" HEADER_FILE_ONLY) + if (PROPERTY) + return() + endif () + + pvs_studio_relative_path(SOURCE_RELATIVE "${SOURCE_DIR}" "${SOURCE}") + pvs_studio_join_path(SOURCE "${SOURCE_DIR}" "${SOURCE}") + + set(LOG "${BINARY_DIR}/PVS-Studio/${SOURCE_RELATIVE}.plog") + get_filename_component(LOG "${LOG}" REALPATH) + get_filename_component(PARENT_DIR "${LOG}" DIRECTORY) + + if (EXISTS "${SOURCE}" AND NOT TARGET "${LOG}" AND NOT "${PVS_STUDIO_LANGUAGE}" STREQUAL "") + # A workaround to support implicit dependencies for ninja generators. + set(depPvsArg) + set(depCommandArg) + if (CMAKE_VERSION VERSION_GREATER 3.6 AND "${CMAKE_GENERATOR}" STREQUAL "Ninja") + pvs_studio_relative_path(relLog "${CMAKE_BINARY_DIR}" "${LOG}") + set(depPvsArg --dep-file "${LOG}.d" --dep-file-target "${relLog}") + set(depCommandArg DEPFILE "${LOG}.d") + endif () + + # https://public.kitware.com/Bug/print_bug_page.php?bug_id=14353 + # https://public.kitware.com/Bug/file/5436/expand_command.cmake + # + # It is a workaround to expand generator expressions. + set(cmdline "${PVS_STUDIO_BIN}" analyze + --output-file "${LOG}" + --source-file "${SOURCE}" + ${depPvsArg} + ${PVS_STUDIO_ARGS} + --cl-params "${PVS_STUDIO_CL_PARAMS}" "${SOURCE}") + + string(REPLACE ";" "$" cmdline "${cmdline}") + set(pvscmd "${CMAKE_COMMAND}" + -D PVS_STUDIO_AS_SCRIPT=TRUE + -D "PVS_STUDIO_COMMAND=${cmdline}" + -P "${PVS_STUDIO_SCRIPT}" + ) + + add_custom_command(OUTPUT "${LOG}" + COMMAND "${CMAKE_COMMAND}" -E make_directory "${PARENT_DIR}" + COMMAND "${CMAKE_COMMAND}" -E remove_directory "${LOG}" + COMMAND ${pvscmd} + WORKING_DIRECTORY "${BINARY_DIR}" + DEPENDS "${SOURCE}" "${PVS_STUDIO_CONFIG}" "${PVS_STUDIO_SUPPRESS_BASE}" + IMPLICIT_DEPENDS "${PVS_STUDIO_LANGUAGE}" "${SOURCE}" + ${depCommandArg} + VERBATIM + COMMENT "Analyzing ${PVS_STUDIO_LANGUAGE} file ${SOURCE_RELATIVE}") + list(APPEND PLOGS "${LOG}") + endif () + set(PVS_STUDIO_PLOGS "${PLOGS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_analyze_target TARGET DIR) + set(PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}") + set(PVS_STUDIO_TARGET_CXX_FLAGS "") + set(PVS_STUDIO_TARGET_C_FLAGS "") + + get_target_property(PROPERTY "${TARGET}" SOURCES) + pvs_studio_relative_path(BINARY_DIR "${CMAKE_SOURCE_DIR}" "${DIR}") + if ("${BINARY_DIR}" MATCHES "^/.*$") + pvs_studio_join_path(BINARY_DIR "${CMAKE_BINARY_DIR}" "PVS-Studio/__${BINARY_DIR}") + else () + pvs_studio_join_path(BINARY_DIR "${CMAKE_BINARY_DIR}" "${BINARY_DIR}") + endif () + + file(MAKE_DIRECTORY "${BINARY_DIR}") + + pvs_studio_set_directory_flags("${DIR}" PVS_STUDIO_TARGET_CXX_FLAGS PVS_STUDIO_TARGET_C_FLAGS) + pvs_studio_set_target_flags("${TARGET}" PVS_STUDIO_TARGET_CXX_FLAGS PVS_STUDIO_TARGET_C_FLAGS) + + if (NOT "${PROPERTY}" STREQUAL "NOTFOUND" AND NOT "${PROPERTY}" STREQUAL "PROPERTY-NOTFOUND") + foreach (SOURCE ${PROPERTY}) + pvs_studio_join_path(SOURCE "${DIR}" "${SOURCE}") + pvs_studio_analyze_file("${SOURCE}" "${DIR}" "${BINARY_DIR}") + endforeach () + endif () + + set(PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}" PARENT_SCOPE) +endfunction () + +set(PVS_STUDIO_RECURSIVE_TARGETS) +set(PVS_STUDIO_RECURSIVE_TARGETS_NEW) + +macro(pvs_studio_get_recursive_targets TARGET) + get_target_property(libs "${TARGET}" LINK_LIBRARIES) + foreach (lib IN LISTS libs) + list(FIND PVS_STUDIO_RECURSIVE_TARGETS "${lib}" index) + if (TARGET "${lib}" AND "${index}" STREQUAL -1) + get_target_property(target_type "${lib}" TYPE) + if (NOT "${target_type}" STREQUAL "INTERFACE_LIBRARY") + list(APPEND PVS_STUDIO_RECURSIVE_TARGETS "${lib}") + list(APPEND PVS_STUDIO_RECURSIVE_TARGETS_NEW "${lib}") + pvs_studio_get_recursive_targets("${lib}") + endif () + endif () + endforeach () +endmacro() + +option(PVS_STUDIO_DISABLE OFF "Disable PVS-Studio targets") +option(PVS_STUDIO_DEBUG OFF "Add debug info") + +# pvs_studio_add_target +# Target options: +# ALL add PVS-Studio target to default build (default: off) +# TARGET target name of analysis target (default: pvs) +# ANALYZE targets... targets to analyze +# RECURSIVE analyze target's dependencies (requires CMake 3.5+) +# COMPILE_COMMANDS use compile_commands.json instead of targets (specified by the 'ANALYZE' option) to determine files for analysis +# (set CMAKE_EXPORT_COMPILE_COMMANDS, available only for Makefile and Ninja generators) +# +# Output options: +# OUTPUT prints report to stdout +# LOG path path to report (default: ${CMAKE_CURRENT_BINARY_DIR}/PVS-Studio.log) +# FORMAT format format of report +# MODE mode analyzers/levels filter (default: GA:1,2) +# HIDE_HELP do not print help message +# +# Analyzer options: +# PLATFORM name linux32/linux64 (default: linux64) +# PREPROCESSOR name preprocessor type: gcc/clang (default: auto detected) +# LICENSE path path to PVS-Studio.lic (default: ~/.config/PVS-Studio/PVS-Studio.lic) +# CONFIG path path to PVS-Studio.cfg +# CFG_TEXT text embedded PVS-Studio.cfg +# SUPPRESS_BASE path to suppress base file +# KEEP_COMBINED_PLOG do not delete combined plog file *.pvs.raw for further processing with plog-converter +# +# Misc options: +# DEPENDS targets.. additional target dependencies +# SOURCES path... list of source files to analyze +# BIN path path to pvs-studio-analyzer (Unix) or CompilerCommandsAnalyzer.exe (Windows) +# CONVERTER path path to plog-converter (Unix) or HtmlGenerator.exe (Windows) +# C_FLAGS flags... additional C_FLAGS +# CXX_FLAGS flags... additional CXX_FLAGS +# ARGS args... additional pvs-studio-analyzer/CompilerCommandsAnalyzer.exe flags +function (pvs_studio_add_target) + macro (default VAR VALUE) + if ("${${VAR}}" STREQUAL "") + set("${VAR}" "${VALUE}") + endif () + endmacro () + + set(PVS_STUDIO_SUPPORTED_PREPROCESSORS "gcc|clang|visualcpp") + if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + set(DEFAULT_PREPROCESSOR "clang") + elseif (MSVC) + set(DEFAULT_PREPROCESSOR "visualcpp") + else () + set(DEFAULT_PREPROCESSOR "gcc") + endif () + + set(OPTIONAL OUTPUT ALL RECURSIVE HIDE_HELP KEEP_COMBINED_PLOG COMPILE_COMMANDS) + set(SINGLE LICENSE CONFIG TARGET LOG FORMAT BIN CONVERTER PLATFORM PREPROCESSOR CFG_TEXT SUPPRESS_BASE) + set(MULTI SOURCES C_FLAGS CXX_FLAGS ARGS DEPENDS ANALYZE MODE) + cmake_parse_arguments(PVS_STUDIO "${OPTIONAL}" "${SINGLE}" "${MULTI}" ${ARGN}) + + if ("${PVS_STUDIO_CONFIG}" STREQUAL "" OR NOT "${PVS_STUDIO_CFG_TEXT}" STREQUAL "") + set(PVS_STUDIO_EMPTY_CONFIG ON) + else () + set(PVS_STUDIO_EMPTY_CONFIG OFF) + endif () + + default(PVS_STUDIO_CFG_TEXT "analysis-mode=31") + default(PVS_STUDIO_CONFIG "${CMAKE_BINARY_DIR}/PVS-Studio.cfg") + default(PVS_STUDIO_C_FLAGS "") + default(PVS_STUDIO_CXX_FLAGS "") + default(PVS_STUDIO_TARGET "pvs") + default(PVS_STUDIO_LOG "PVS-Studio.log") + + set(PATHS) + if (WIN32) + set(ROOT "PROGRAMFILES(X86)") + set(ROOT "$ENV{${ROOT}}/PVS-Studio") + string(REPLACE \\ / ROOT "${ROOT}") + + if (EXISTS "${ROOT}") + set(PATHS "${ROOT}") + endif () + + default(PVS_STUDIO_BIN "CompilerCommandsAnalyzer.exe") + default(PVS_STUDIO_CONVERTER "HtmlGenerator.exe") + else () + default(PVS_STUDIO_BIN "pvs-studio-analyzer") + default(PVS_STUDIO_CONVERTER "plog-converter") + endif () + + find_program(PVS_STUDIO_BIN_PATH "${PVS_STUDIO_BIN}" ${PATHS}) + set(PVS_STUDIO_BIN "${PVS_STUDIO_BIN_PATH}") + + if (NOT EXISTS "${PVS_STUDIO_BIN}") + message(FATAL_ERROR "pvs-studio-analyzer is not found") + endif () + + find_program(PVS_STUDIO_CONVERTER_PATH "${PVS_STUDIO_CONVERTER}" ${PATHS}) + set(PVS_STUDIO_CONVERTER "${PVS_STUDIO_CONVERTER_PATH}") + + if (NOT EXISTS "${PVS_STUDIO_CONVERTER}") + message(FATAL_ERROR "plog-converter is not found") + endif () + + default(PVS_STUDIO_MODE "GA:1,2") + default(PVS_STUDIO_PREPROCESSOR "${DEFAULT_PREPROCESSOR}") + if (WIN32) + default(PVS_STUDIO_PLATFORM "x64") + else () + default(PVS_STUDIO_PLATFORM "linux64") + endif () + + string(REPLACE ";" "+" PVS_STUDIO_MODE "${PVS_STUDIO_MODE}") + + if (PVS_STUDIO_EMPTY_CONFIG) + set(PVS_STUDIO_CONFIG_COMMAND "${CMAKE_COMMAND}" -E echo "${PVS_STUDIO_CFG_TEXT}" > "${PVS_STUDIO_CONFIG}") + else () + set(PVS_STUDIO_CONFIG_COMMAND "${CMAKE_COMMAND}" -E touch "${PVS_STUDIO_CONFIG}") + endif () + + add_custom_command(OUTPUT "${PVS_STUDIO_CONFIG}" + COMMAND ${PVS_STUDIO_CONFIG_COMMAND} + WORKING_DIRECTORY "${BINARY_DIR}" + COMMENT "Generating PVS-Studio.cfg") + + if (NOT "${PVS_STUDIO_PREPROCESSOR}" MATCHES "^${PVS_STUDIO_SUPPORTED_PREPROCESSORS}$") + message(FATAL_ERROR "Preprocessor ${PVS_STUDIO_PREPROCESSOR} isn't supported. Available options: ${PVS_STUDIO_SUPPORTED_PREPROCESSORS}.") + endif () + + pvs_studio_append_standard_flag(PVS_STUDIO_CXX_FLAGS "${CMAKE_CXX_STANDARD}") + pvs_studio_set_directory_flags("${CMAKE_CURRENT_SOURCE_DIR}" PVS_STUDIO_CXX_FLAGS PVS_STUDIO_C_FLAGS) + + if (NOT "${PVS_STUDIO_LICENSE}" STREQUAL "") + pvs_studio_join_path(PVS_STUDIO_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}" "${PVS_STUDIO_LICENSE}") + list(APPEND PVS_STUDIO_ARGS --lic-file "${PVS_STUDIO_LICENSE}") + endif () + + list(APPEND PVS_STUDIO_ARGS --cfg "${PVS_STUDIO_CONFIG}" + --platform "${PVS_STUDIO_PLATFORM}" + --preprocessor "${PVS_STUDIO_PREPROCESSOR}") + + if (NOT "${PVS_STUDIO_SUPPRESS_BASE}" STREQUAL "") + pvs_studio_join_path(PVS_STUDIO_SUPPRESS_BASE "${CMAKE_CURRENT_SOURCE_DIR}" "${PVS_STUDIO_SUPPRESS_BASE}") + list(APPEND PVS_STUDIO_ARGS --suppress-file "${PVS_STUDIO_SUPPRESS_BASE}") + endif () + + if (NOT "${CMAKE_CXX_COMPILER}" STREQUAL "") + list(APPEND PVS_STUDIO_ARGS --cxx "${CMAKE_CXX_COMPILER}") + endif () + + if (NOT "${CMAKE_C_COMPILER}" STREQUAL "") + list(APPEND PVS_STUDIO_ARGS --cc "${CMAKE_C_COMPILER}") + endif () + + set(PVS_STUDIO_PLOGS "") + + set(PVS_STUDIO_RECURSIVE_TARGETS_NEW) + if (${PVS_STUDIO_RECURSIVE}) + foreach (TARGET IN LISTS PVS_STUDIO_ANALYZE) + list(APPEND PVS_STUDIO_RECURSIVE_TARGETS_NEW "${TARGET}") + pvs_studio_get_recursive_targets("${TARGET}") + endforeach () + endif () + + set(inc_path) + + foreach (TARGET ${PVS_STUDIO_ANALYZE}) + set(DIR "${CMAKE_CURRENT_SOURCE_DIR}") + string(FIND "${TARGET}" ":" DELIM) + if ("${DELIM}" GREATER "-1") + math(EXPR DELIMI "${DELIM}+1") + string(SUBSTRING "${TARGET}" "${DELIMI}" "-1" DIR) + string(SUBSTRING "${TARGET}" "0" "${DELIM}" TARGET) + pvs_studio_join_path(DIR "${CMAKE_CURRENT_SOURCE_DIR}" "${DIR}") + else () + get_target_property(TARGET_SOURCE_DIR "${TARGET}" SOURCE_DIR) + if (EXISTS "${TARGET_SOURCE_DIR}") + set(DIR "${TARGET_SOURCE_DIR}") + endif () + endif () + pvs_studio_analyze_target("${TARGET}" "${DIR}") + list(APPEND PVS_STUDIO_DEPENDS "${TARGET}") + + if ("${inc_path}" STREQUAL "") + set(inc_path "$") + else () + set(inc_path "${inc_path}$$") + endif () + endforeach () + + foreach (TARGET ${PVS_STUDIO_RECURSIVE_TARGETS_NEW}) + set(DIR "${CMAKE_CURRENT_SOURCE_DIR}") + get_target_property(TARGET_SOURCE_DIR "${TARGET}" SOURCE_DIR) + if (EXISTS "${TARGET_SOURCE_DIR}") + set(DIR "${TARGET_SOURCE_DIR}") + endif () + pvs_studio_analyze_target("${TARGET}" "${DIR}") + list(APPEND PVS_STUDIO_DEPENDS "${TARGET}") + endforeach () + + set(PVS_STUDIO_TARGET_CXX_FLAGS "") + set(PVS_STUDIO_TARGET_C_FLAGS "") + foreach (SOURCE ${PVS_STUDIO_SOURCES}) + pvs_studio_analyze_file("${SOURCE}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}") + endforeach () + + if (PVS_STUDIO_COMPILE_COMMANDS) + set(COMPILE_COMMANDS_LOG "${PVS_STUDIO_LOG}.pvs.analyzer.raw") + if (NOT CMAKE_EXPORT_COMPILE_COMMANDS) + message(FATAL_ERROR "You should set CMAKE_EXPORT_COMPILE_COMMANDS to TRUE") + endif () + add_custom_command( + OUTPUT "${COMPILE_COMMANDS_LOG}" + COMMAND "${PVS_STUDIO_BIN}" analyze -i + --output-file "${COMPILE_COMMANDS_LOG}.always" + ${PVS_STUDIO_ARGS} + COMMENT "Analyzing with PVS-Studio" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + DEPENDS "${PVS_STUDIO_CONFIG}" "${PVS_STUDIO_SUPPRESS_BASE}" + ) + list(APPEND PVS_STUDIO_PLOGS_LOGS "${COMPILE_COMMANDS_LOG}.always") + list(APPEND PVS_STUDIO_PLOGS_DEPENDENCIES "${COMPILE_COMMANDS_LOG}") + endif () + + pvs_studio_relative_path(LOG_RELATIVE "${CMAKE_BINARY_DIR}" "${PVS_STUDIO_LOG}") + if (PVS_STUDIO_PLOGS OR PVS_STUDIO_COMPILE_COMMANDS) + if (WIN32) + string(REPLACE / \\ PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}") + endif () + if (WIN32) + set(COMMANDS COMMAND type ${PVS_STUDIO_PLOGS} ${PVS_STUDIO_PLOGS_LOGS} > "${PVS_STUDIO_LOG}" 2>nul) + else () + set(COMMANDS COMMAND cat ${PVS_STUDIO_PLOGS} ${PVS_STUDIO_PLOGS_LOGS} > "${PVS_STUDIO_LOG}") + endif () + set(COMMENT "Generating ${LOG_RELATIVE}") + if (NOT "${PVS_STUDIO_FORMAT}" STREQUAL "" OR PVS_STUDIO_OUTPUT) + if ("${PVS_STUDIO_FORMAT}" STREQUAL "") + set(PVS_STUDIO_FORMAT "errorfile") + endif () + list(APPEND COMMANDS + COMMAND "${CMAKE_COMMAND}" -E remove -f "${PVS_STUDIO_LOG}.pvs.raw" + COMMAND "${CMAKE_COMMAND}" -E rename "${PVS_STUDIO_LOG}" "${PVS_STUDIO_LOG}.pvs.raw" + COMMAND "${PVS_STUDIO_CONVERTER}" -t "${PVS_STUDIO_FORMAT}" "${PVS_STUDIO_LOG}.pvs.raw" -o "${PVS_STUDIO_LOG}" -a "${PVS_STUDIO_MODE}" + ) + if(NOT PVS_STUDIO_KEEP_COMBINED_PLOG) + list(APPEND COMMANDS COMMAND "${CMAKE_COMMAND}" -E remove -f "${PVS_STUDIO_LOG}.pvs.raw") + endif() + endif () + else () + set(COMMANDS COMMAND "${CMAKE_COMMAND}" -E touch "${PVS_STUDIO_LOG}") + set(COMMENT "Generating ${LOG_RELATIVE}: no sources found") + endif () + + if (WIN32) + string(REPLACE / \\ PVS_STUDIO_LOG "${PVS_STUDIO_LOG}") + endif () + + add_custom_command(OUTPUT "${PVS_STUDIO_LOG}" + ${COMMANDS} + COMMENT "${COMMENT}" + DEPENDS ${PVS_STUDIO_PLOGS} ${PVS_STUDIO_PLOGS_DEPENDENCIES} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") + + if (PVS_STUDIO_ALL) + set(ALL "ALL") + else () + set(ALL "") + endif () + + if (PVS_STUDIO_OUTPUT) + if (PVS_STUDIO_HIDE_HELP AND NOT WIN32) + set(COMMANDS COMMAND grep -v " error: Help:" ${PVS_STUDIO_LOG} 1>&2 || exit 0) + elseif (WIN32) + set(COMMANDS COMMAND type "${PVS_STUDIO_LOG}" 1>&2) + else () + set(COMMANDS COMMAND cat "${PVS_STUDIO_LOG}" 1>&2) + endif() + else () + set(COMMANDS "") + endif () + + add_custom_target("${PVS_STUDIO_TARGET}" ${ALL} ${COMMANDS} WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" DEPENDS ${PVS_STUDIO_DEPENDS} "${PVS_STUDIO_LOG}") + + # A workaround to add implicit dependencies of source files from include directories + set_target_properties("${PVS_STUDIO_TARGET}" PROPERTIES INCLUDE_DIRECTORIES "${inc_path}") +endfunction () From 941408899e6f93431f1ffde1a12da3f40e480b58 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 16:17:53 -0300 Subject: [PATCH 16/32] fixed broken build on MSVC --- Makefile | 4 ++-- include/expr.h | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8f82f7a..d68773a 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,8 @@ TESTBIN := expr_test all: @echo make test - run tests - @echo make llvm-cov - report test coverage using LLVM \(set LLVM_VER if needed\) - @echo make gcov - report test coverage \(set GCC_VER if needed\) + @echo make llvm-cov - report test coverage using LLVM "(set LLVM_VER if needed)" + @echo make gcov - report test coverage "(set GCC_VER if needed)" test: $(TESTBIN) ./$(TESTBIN) diff --git a/include/expr.h b/include/expr.h index 608975c..85a6d14 100644 --- a/include/expr.h +++ b/include/expr.h @@ -12,6 +12,33 @@ extern "C" { #include #include +#ifdef _MSC_VER +#include + +#ifndef NAN +static unsigned long __nan[2] = {0xffffffff, 0x7fffffff}; +#define NAN (*(double *) __nan) +#endif /* NAN */ + +#ifndef INFINITY +static unsigned long __inf[2] = {0x0, 0x7ff00000}; +#define INFINITY (*(double *) __inf) +#endif /* INFINITY */ + +#define isnan(_x) _isnan(_x) +#define isinf(_x) (!_finite(_x)) + +#define snprintf _snprintf +#else /* _MSC_VER */ +#ifndef NAN +#define NAN (0.0 / 0.0) +#endif /* NAN */ + +#ifndef INFINITY +#define INFINITY (1.0 / 0.0) +#endif /* INFINITY */ +#endif /* _MSC_VER */ + /* * Expression number type */ From a9c3f59c3481a5b77dc44541128296364ac3c660 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 17:43:37 -0300 Subject: [PATCH 17/32] fixed MSVC alerts --- include/expr.h | 58 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/include/expr.h b/include/expr.h index 85a6d14..82b9347 100644 --- a/include/expr.h +++ b/include/expr.h @@ -197,8 +197,13 @@ struct expr { } param; }; +#ifdef _MSC_VER +#define expr_init() \ + { (enum expr_type) 0 } +#else #define expr_init() \ { .type = (enum expr_type) 0 } +#endif struct expr_string { const char *s; @@ -272,7 +277,8 @@ static struct { }; static enum expr_type expr_op(const char *s, size_t len, int unary) { - for (unsigned int i = 0; i < sizeof(OPS) / sizeof(OPS[0]); i++) { + unsigned int i; + for (i = 0; i < sizeof(OPS) / sizeof(OPS[0]); i++) { if (expr_strlen(OPS[i].s) == len && expr_strncmp(OPS[i].s, s, len) == 0 && (unary == -1 || expr_is_unary(OPS[i].op) == unary)) { return OPS[i].op; @@ -285,7 +291,8 @@ static expr_num_t expr_parse_number(const char *s, size_t len) { expr_num_t num = 0; unsigned int frac = 0; unsigned int digits = 0; - for (unsigned int i = 0; i < len; i++) { + unsigned int i; + for (i = 0; i < len; i++) { if (s[i] == '.' && frac == 0) { frac++; continue; @@ -319,7 +326,8 @@ struct expr_func { static struct expr_func *expr_func(struct expr_func *funcs, const char *s, size_t len) { - for (struct expr_func *f = funcs; f->name; f++) { + struct expr_func *f; + for (f = funcs; f->name; f++) { if (expr_strlen(f->name) == len && expr_strncmp(f->name, s, len) == 0) { return f; } @@ -501,10 +509,11 @@ static expr_num_t expr_eval(struct expr *e) { static int expr_next_token(const char *s, size_t len, int *flags) { unsigned int i = 0; + char c; if (len == 0) { return 0; } - char c = s[0]; + c = s[0]; if (c == '#') { for (; i < len && s[i] != '\n'; i++) ; @@ -595,25 +604,29 @@ static int expr_bind(const char *s, size_t len, vec_expr_t *es) { if (vec_len(es) < 1) { return -1; } - struct expr arg = vec_pop(es); - struct expr unary = expr_init(); - unary.type = op; - vec_push(&unary.param.op.args, arg); - vec_push(es, unary); + { + struct expr arg = vec_pop(es); + struct expr unary = expr_init(); + unary.type = op; + vec_push(&unary.param.op.args, arg); + vec_push(es, unary); + } } else { if (vec_len(es) < 2) { return -1; } - struct expr b = vec_pop(es); - struct expr a = vec_pop(es); - struct expr binary = expr_init(); - binary.type = op; - if (op == OP_ASSIGN && a.type != OP_VAR) { - return -1; /* Bad assignment */ + { + struct expr b = vec_pop(es); + struct expr a = vec_pop(es); + struct expr binary = expr_init(); + binary.type = op; + if (op == OP_ASSIGN && a.type != OP_VAR) { + return -1; /* Bad assignment */ + } + vec_push(&binary.param.op.args, a); + vec_push(&binary.param.op.args, b); + vec_push(es, binary); } - vec_push(&binary.param.op.args, a); - vec_push(&binary.param.op.args, b); - vec_push(es, binary); } return 0; } @@ -641,7 +654,13 @@ static struct expr expr_binary(enum expr_type type, struct expr a, return e; } +#ifdef _MSC_VER +#define inline __inline +#endif static inline void expr_copy(struct expr *dst, struct expr *src) { +#ifdef _MSC_VER +#undef inline +#endif int i; struct expr arg; dst->type = src->type; @@ -1025,7 +1044,8 @@ static void expr_destroy(struct expr *e, struct expr_var_list *vars) { expr_free(e); } if (vars != NULL) { - for (struct expr_var *v = vars->head; v;) { + struct expr_var *v; + for (v = vars->head; v;) { struct expr_var *next = v->next; expr_free(v); v = next; From 38ce72300706d5eff1a9e1b6cada619ab66e3949 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 18:27:15 -0300 Subject: [PATCH 18/32] fixed MSVC alerts --- include/expr.h | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/include/expr.h b/include/expr.h index 82b9347..434f73b 100644 --- a/include/expr.h +++ b/include/expr.h @@ -716,6 +716,8 @@ static struct expr *expr_create2(const char *s, size_t len, *error = EXPR_ERR_UNKNOWN; for (;;) { int n = expr_next_token(s, len, &flags); + const char *tok; + int paren_next; if (n == 0) { break; } else if (n < 0) { @@ -723,7 +725,7 @@ static struct expr *expr_create2(const char *s, size_t len, goto cleanup; } *near += n; - const char *tok = s; + tok = s; s = s + n; len = len - n; if (*tok == '#') { @@ -755,7 +757,7 @@ static struct expr *expr_create2(const char *s, size_t len, if (isspace(*tok)) { continue; } - int paren_next = EXPR_PAREN_ALLOWED; + paren_next = EXPR_PAREN_ALLOWED; if (idn > 0) { if (n == 1 && *tok == '(') { @@ -790,8 +792,10 @@ static struct expr *expr_create2(const char *s, size_t len, if (paren == EXPR_PAREN_EXPECTED) { struct expr_string str = {"{", 1}; vec_push(&os, str); - struct expr_arg arg = {vec_len(&os), vec_len(&es), vec_init()}; - vec_push(&as, arg); + { + struct expr_arg arg = {vec_len(&os), vec_len(&es), vec_init()}; + vec_push(&as, arg); + } } else if (paren == EXPR_PAREN_ALLOWED) { struct expr_string str = {"(", 1}; vec_push(&os, str); @@ -909,6 +913,7 @@ static struct expr *expr_create2(const char *s, size_t len, o2 = vec_peek(&os); } for (;;) { + enum expr_type type2; if (n == 1 && *tok == ',' && vec_len(&os) > 0) { struct expr_string str = vec_peek(&os); if (str.n == 1 && *str.s == '{') { @@ -917,7 +922,7 @@ static struct expr *expr_create2(const char *s, size_t len, break; } } - enum expr_type type2 = expr_op(o2.s, o2.n, -1); + type2 = expr_op(o2.s, o2.n, -1); if (!(type2 != OP_UNKNOWN && expr_prec(op, type2))) { struct expr_string str = {tok, n}; vec_push(&os, str); @@ -972,11 +977,11 @@ static struct expr *expr_create2(const char *s, size_t len, } } +cleanup : { int i, j; struct macro m; struct expr e; struct expr_arg a; -cleanup: vec_foreach(¯os, m, i) { struct expr e; vec_foreach(&m.body, e, j) { @@ -997,6 +1002,7 @@ static struct expr *expr_create2(const char *s, size_t len, } vec_free(&a.args); } +} vec_free(&as); /*vec_foreach(&os, o, i) {vec_free(&m.body);}*/ From ac421807f92f37b987afb3d39d768483e8326b86 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 18:47:31 -0300 Subject: [PATCH 19/32] scoping local block to avoid MSVC alerts --- include/expr.h | 147 +++++++++++++++++++++++++------------------------ 1 file changed, 75 insertions(+), 72 deletions(-) diff --git a/include/expr.h b/include/expr.h index 434f73b..e151ce4 100644 --- a/include/expr.h +++ b/include/expr.h @@ -819,86 +819,89 @@ static struct expr *expr_create2(const char *s, size_t len, *error = EXPR_ERR_BAD_PARENS; goto cleanup; // Bad parens } - struct expr_string str = vec_pop(&os); - if (str.n == 1 && *str.s == '{') { - str = vec_pop(&os); - struct expr_arg arg = vec_pop(&as); - if (vec_len(&es) > arg.eslen) { - vec_push(&arg.args, vec_pop(&es)); - } - if (str.n == 1 && str.s[0] == '$') { - if (vec_len(&arg.args) < 1) { - vec_free(&arg.args); - *error = EXPR_ERR_TOO_FEW_FUNC_ARGS; - goto cleanup; /* too few arguments for $() function */ - } - struct expr *u = &vec_nth(&arg.args, 0); - if (u->type != OP_VAR) { - vec_free(&arg.args); - *error = EXPR_ERR_FIRST_ARG_IS_NOT_VAR; - goto cleanup; /* first argument is not a variable */ + { + struct expr_string str = vec_pop(&os); + if (str.n == 1 && *str.s == '{') { + str = vec_pop(&os); + struct expr_arg arg = vec_pop(&as); + if (vec_len(&es) > arg.eslen) { + vec_push(&arg.args, vec_pop(&es)); } - for (struct expr_var *v = vars->head; v; v = v->next) { - if (&v->value == u->param.var.value) { - struct macro m = {v->name, arg.args}; - vec_push(¯os, m); - break; + if (str.n == 1 && str.s[0] == '$') { + if (vec_len(&arg.args) < 1) { + vec_free(&arg.args); + *error = EXPR_ERR_TOO_FEW_FUNC_ARGS; + goto cleanup; /* too few arguments for $() function */ } - } - vec_push(&es, expr_const(0)); - } else { - int i = 0; - int found = -1; - struct macro m; - vec_foreach(¯os, m, i) { - if (expr_strlen(m.name) == (size_t) str.n && - expr_strncmp(m.name, str.s, str.n) == 0) { - found = i; + struct expr *u = &vec_nth(&arg.args, 0); + if (u->type != OP_VAR) { + vec_free(&arg.args); + *error = EXPR_ERR_FIRST_ARG_IS_NOT_VAR; + goto cleanup; /* first argument is not a variable */ } - } - if (found != -1) { - m = vec_nth(¯os, found); - struct expr root = expr_const(0); - struct expr *p = &root; - /* Assign macro parameters */ - for (int j = 0; j < vec_len(&arg.args); j++) { - char varname[4]; - expr_snprintf(varname, sizeof(varname) - 1, "$%d", (j + 1)); - struct expr_var *v = - expr_var(vars, varname, expr_strlen(varname)); - struct expr ev = expr_varref(v); - struct expr assign = - expr_binary(OP_ASSIGN, ev, vec_nth(&arg.args, j)); - *p = expr_binary(OP_COMMA, assign, expr_const(0)); - p = &vec_nth(&p->param.op.args, 1); - } - /* Expand macro body */ - for (int j = 1; j < vec_len(&m.body); j++) { - if (j < vec_len(&m.body) - 1) { - *p = expr_binary(OP_COMMA, expr_const(0), expr_const(0)); - expr_copy(&vec_nth(&p->param.op.args, 0), &vec_nth(&m.body, j)); - } else { - expr_copy(p, &vec_nth(&m.body, j)); + for (struct expr_var *v = vars->head; v; v = v->next) { + if (&v->value == u->param.var.value) { + struct macro m = {v->name, arg.args}; + vec_push(¯os, m); + break; } - p = &vec_nth(&p->param.op.args, 1); } - vec_push(&es, root); - vec_free(&arg.args); + vec_push(&es, expr_const(0)); } else { - struct expr_func *f = expr_func(funcs, str.s, str.n); - struct expr bound_func = expr_init(); - bound_func.type = OP_FUNC; - bound_func.param.func.f = f; - bound_func.param.func.args = arg.args; - if (f->ctxsz > 0) { - void *p = expr_alloc(f->ctxsz); - if (p == NULL) { - *error = EXPR_ERR_ALLOCATION_FAILED; - goto cleanup; /* allocation failed */ + int i = 0; + int found = -1; + struct macro m; + vec_foreach(¯os, m, i) { + if (expr_strlen(m.name) == (size_t) str.n && + expr_strncmp(m.name, str.s, str.n) == 0) { + found = i; + } + } + if (found != -1) { + m = vec_nth(¯os, found); + struct expr root = expr_const(0); + struct expr *p = &root; + /* Assign macro parameters */ + for (int j = 0; j < vec_len(&arg.args); j++) { + char varname[4]; + expr_snprintf(varname, sizeof(varname) - 1, "$%d", (j + 1)); + struct expr_var *v = + expr_var(vars, varname, expr_strlen(varname)); + struct expr ev = expr_varref(v); + struct expr assign = + expr_binary(OP_ASSIGN, ev, vec_nth(&arg.args, j)); + *p = expr_binary(OP_COMMA, assign, expr_const(0)); + p = &vec_nth(&p->param.op.args, 1); + } + /* Expand macro body */ + for (int j = 1; j < vec_len(&m.body); j++) { + if (j < vec_len(&m.body) - 1) { + *p = expr_binary(OP_COMMA, expr_const(0), expr_const(0)); + expr_copy(&vec_nth(&p->param.op.args, 0), + &vec_nth(&m.body, j)); + } else { + expr_copy(p, &vec_nth(&m.body, j)); + } + p = &vec_nth(&p->param.op.args, 1); + } + vec_push(&es, root); + vec_free(&arg.args); + } else { + struct expr_func *f = expr_func(funcs, str.s, str.n); + struct expr bound_func = expr_init(); + bound_func.type = OP_FUNC; + bound_func.param.func.f = f; + bound_func.param.func.args = arg.args; + if (f->ctxsz > 0) { + void *p = expr_alloc(f->ctxsz); + if (p == NULL) { + *error = EXPR_ERR_ALLOCATION_FAILED; + goto cleanup; /* allocation failed */ + } + bound_func.param.func.context = p; } - bound_func.param.func.context = p; + vec_push(&es, bound_func); } - vec_push(&es, bound_func); } } } From 180823422a1d93db14d57a28153840c56ee958b3 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 19:00:28 -0300 Subject: [PATCH 20/32] fixed MSVC alerts --- include/expr.h | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/include/expr.h b/include/expr.h index e151ce4..51fbcaf 100644 --- a/include/expr.h +++ b/include/expr.h @@ -858,23 +858,26 @@ static struct expr *expr_create2(const char *s, size_t len, } } if (found != -1) { + int j; m = vec_nth(¯os, found); struct expr root = expr_const(0); struct expr *p = &root; /* Assign macro parameters */ - for (int j = 0; j < vec_len(&arg.args); j++) { + for (j = 0; j < vec_len(&arg.args); j++) { char varname[4]; expr_snprintf(varname, sizeof(varname) - 1, "$%d", (j + 1)); - struct expr_var *v = - expr_var(vars, varname, expr_strlen(varname)); - struct expr ev = expr_varref(v); - struct expr assign = - expr_binary(OP_ASSIGN, ev, vec_nth(&arg.args, j)); - *p = expr_binary(OP_COMMA, assign, expr_const(0)); + { + struct expr_var *v = + expr_var(vars, varname, expr_strlen(varname)); + struct expr ev = expr_varref(v); + struct expr assign = + expr_binary(OP_ASSIGN, ev, vec_nth(&arg.args, j)); + *p = expr_binary(OP_COMMA, assign, expr_const(0)); + } p = &vec_nth(&p->param.op.args, 1); } /* Expand macro body */ - for (int j = 1; j < vec_len(&m.body); j++) { + for (j = 1; j < vec_len(&m.body); j++) { if (j < vec_len(&m.body) - 1) { *p = expr_binary(OP_COMMA, expr_const(0), expr_const(0)); expr_copy(&vec_nth(&p->param.op.args, 0), From a2bd9e65e49fda54ea3e0c58d726261d12bf210e Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 20:04:27 -0300 Subject: [PATCH 21/32] enabled strict options of the compiler --- CMakeLists.txt | 2 +- Makefile | 2 +- examples/example3.c | 3 +- expr_test.c | 151 +++++++++++++++++++++++++++----------------- include/expr.h | 20 ++++-- 5 files changed, 110 insertions(+), 68 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5551986..f7add8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) if(EXPR_PICKY_COMPILER) set(CMAKE_C_FLAGS - "${CMAKE_C_FLAGS} -Wall -Werror -Wextra -Wno-unused-function -Wpedantic -Wstrict-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline" + "${CMAKE_C_FLAGS} -Wall -Werror -Wextra -Wno-unused-function -Wpedantic -Wdeclaration-after-statement -Wstrict-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline" ) endif() diff --git a/Makefile b/Makefile index d68773a..21cb805 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -CFLAGS ?= -std=c99 -g -O0 -pedantic -Wall -Werror -Wextra -Wno-unused-function -Wpedantic -Wstrict-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Iinclude +CFLAGS ?= -std=c99 -g -O0 -pedantic -Wall -Werror -Wextra -Wno-unused-function -Wpedantic -Wdeclaration-after-statement -Wstrict-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Iinclude LDFLAGS ?= -lm -O0 -g INC_DIR := include diff --git a/examples/example3.c b/examples/example3.c index 0485051..b1eabf7 100644 --- a/examples/example3.c +++ b/examples/example3.c @@ -2,10 +2,9 @@ // Custom function that returns the sum of its two arguments static expr_num_t sum(struct expr_func *f, vec_expr_t *args, void *c) { - (void) f; - (void) c; expr_num_t a = expr_eval(&vec_nth(args, 0)); expr_num_t b = expr_eval(&vec_nth(args, 1)); + (void) f, (void) c; return a + b; } diff --git a/expr_test.c b/expr_test.c index e5197ba..e94ecd9 100644 --- a/expr_test.c +++ b/expr_test.c @@ -14,6 +14,7 @@ int status = 0; /* * VECTOR TESTS */ + typedef vec(int) test_vec_int_t; typedef vec(char *) test_vec_str_t; @@ -32,10 +33,12 @@ static void test_vector(void) { vec_push(&strings, "world"); vec_push(&strings, "foo"); assert(vec_len(&strings) == 3); - int i; - char *el; - vec_foreach(&strings, el, i) { - printf("%s %d\n", el, i); + { + int i; + char *el; + vec_foreach(&strings, el, i) { + printf("%s %d\n", el, i); + } } vec_free(&strings); } @@ -43,6 +46,7 @@ static void test_vector(void) { /* * VARIABLES VECTOR TEST */ + static void test_vars(void) { struct expr_var_list vars = {0}; @@ -52,15 +56,18 @@ static void test_vars(void) { expr_var(&vars, "b", 1); expr_var(&vars, "ab", 2); - struct expr_var *again = expr_var(&vars, "a", 1); - assert(again == a); - assert(again->value == 4); + { + struct expr_var *again = expr_var(&vars, "a", 1); + assert(again == a); + assert(again->value == 4); + } expr_destroy(NULL, &vars); } /* * LEXER TESTS */ + static int assert_tokens(char *s, char **expected) { int len = strlen(s); int flags = EXPR_TDEFAULT; @@ -92,6 +99,16 @@ static int assert_tokens(char *s, char **expected) { } static void test_tokenizer(void) { + unsigned int i; +#ifdef _MSC_VER + char *T1[] = {"", NULL}; + char *T2[] = {"1", "1", NULL}; + char *T3[] = {"1+11", "1", "+", "11", NULL}; + char *T4[] = {"1*11", "1", "*", "11", NULL}; + char *T5[] = {"1**11", "1", "**", "11", NULL}; + char *T6[] = {"1**-11", "1", "**", "-", "11", NULL}; + char **TESTS[] = {T1, T2, T3, T4, T5, T6}; +#else char **TESTS[] = { (char *[]){"", NULL}, (char *[]){"1", "1", NULL}, @@ -100,7 +117,8 @@ static void test_tokenizer(void) { (char *[]){"1**11", "1", "**", "11", NULL}, (char *[]){"1**-11", "1", "**", "-", "11", NULL}, }; - for (unsigned int i = 0; i < sizeof(TESTS) / sizeof(TESTS[0]); i++) { +#endif + for (i = 0; i < sizeof(TESTS) / sizeof(TESTS[0]); i++) { assert_tokens(TESTS[i][0], TESTS[i] + 1); } } @@ -111,15 +129,17 @@ static void test_tokenizer(void) { struct nop_context { void *p; }; + static void user_func_nop_cleanup(struct expr_func *f, void *c) { - (void) f; struct nop_context *nop = (struct nop_context *) c; + (void) f; free(nop->p); } + static expr_num_t user_func_nop(struct expr_func *f, vec_expr_t *args, void *c) { - (void) args; struct nop_context *nop = (struct nop_context *) c; + (void) args; if (f->ctxsz == 0) { free(nop->p); return 0; @@ -132,24 +152,24 @@ static expr_num_t user_func_nop(struct expr_func *f, vec_expr_t *args, static expr_num_t user_func_add(struct expr_func *f, vec_expr_t *args, void *c) { - (void) f, (void) c; expr_num_t a = expr_eval(&vec_nth(args, 0)); expr_num_t b = expr_eval(&vec_nth(args, 1)); + (void) f, (void) c; return a + b; } static expr_num_t user_func_next(struct expr_func *f, vec_expr_t *args, void *c) { - (void) f, (void) c; expr_num_t a = expr_eval(&vec_nth(args, 0)); + (void) f, (void) c; return a + 1; } static expr_num_t user_func_print(struct expr_func *f, vec_expr_t *args, void *c) { - (void) f, (void) c; int i; struct expr e; + (void) f, (void) c; fprintf(stderr, ">> "); vec_foreach(args, e, i) { fprintf(stderr, "%f ", expr_eval(&e)); @@ -174,42 +194,47 @@ static void test_expr(char *s, expr_num_t expected) { status = 1; return; } - expr_num_t result = expr_eval(e); - - char *p = (char *) malloc(strlen(s) + 1); - strncpy(p, s, strlen(s) + 1); - for (char *it = p; *it; it++) { - if (*it == '\n') { - *it = '\\'; + { + expr_num_t result = expr_eval(e); + + char *p = (char *) malloc(strlen(s) + 1); + char *it; + strncpy(p, s, strlen(s) + 1); + for (it = p; *it; it++) { + if (*it == '\n') { + *it = '\\'; + } } - } - if ((isnan(result) && !isnan(expected)) || - fabs(result - expected) > 0.00001f) { - printf("FAIL: %s: %f != %f\n", p, result, expected); - status = 1; - } else { - printf("OK: %s == %f\n", p, expected); + if ((isnan(result) && !isnan(expected)) || + fabs(result - expected) > 0.00001f) { + printf("FAIL: %s: %f != %f\n", p, result, expected); + status = 1; + } else { + printf("OK: %s == %f\n", p, expected); + } + expr_destroy(e, &vars); + free(p); } - expr_destroy(e, &vars); - free(p); } static void test_expr_error(char *s, int near, int error) { - const char *ERRORS[] = {"EXPR_ERR_UNKNOWN", - "EXPR_ERR_UNEXPECTED_NUMBER", - "EXPR_ERR_UNEXPECTED_WORD", - "EXPR_ERR_UNEXPECTED_PARENS", - "EXPR_ERR_MISS_EXPECTED_OPERAND", - "EXPR_ERR_UNKNOWN_OPERATOR", - "EXPR_ERR_INVALID_FUNC_NAME", - "EXPR_ERR_BAD_CALL", - "EXPR_ERR_BAD_PARENS", - "EXPR_ERR_TOO_FEW_FUNC_ARGS", - "EXPR_ERR_FIRST_ARG_IS_NOT_VAR", - "EXPR_ERR_ALLOCATION_FAILED", - "EXPR_ERR_BAD_VARIABLE_NAME", - "EXPR_ERR_BAD_ASSIGNMENT"}; + const char *ERRORS[] = { + "EXPR_ERR_UNKNOWN", + "EXPR_ERR_UNEXPECTED_NUMBER", + "EXPR_ERR_UNEXPECTED_WORD", + "EXPR_ERR_UNEXPECTED_PARENS", + "EXPR_ERR_MISS_EXPECTED_OPERAND", + "EXPR_ERR_UNKNOWN_OPERATOR", + "EXPR_ERR_INVALID_FUNC_NAME", + "EXPR_ERR_BAD_CALL", + "EXPR_ERR_BAD_PARENS", + "EXPR_ERR_TOO_FEW_FUNC_ARGS", + "EXPR_ERR_FIRST_ARG_IS_NOT_VAR", + "EXPR_ERR_ALLOCATION_FAILED", + "EXPR_ERR_BAD_VARIABLE_NAME", + "EXPR_ERR_BAD_ASSIGNMENT", + }; struct expr_var_list vars = {0}; int n, f; struct expr *e = expr_create2(s, strlen(s), &vars, user_funcs, &n, &f); @@ -354,12 +379,16 @@ static void test_name_collision(void) { static void test_fancy_variable_names(void) { test_expr("one=1", 1); +#ifndef _MSC_VER test_expr("один=1", 1); +#endif test_expr("six=6, seven=7, six*seven", 42); +#ifndef _MSC_VER test_expr("шість=6, сім=7, шість*сім", 42); test_expr("六=6, 七=7, 六*七", 42); test_expr("ταῦ=1.618, 3*ταῦ", 3 * 1.618); test_expr("$(ταῦ, 1.618), 3*ταῦ()", 3 * 1.618); +#endif test_expr("x#4=12, x#3=3, x#4+x#3", 15); } @@ -378,23 +407,27 @@ static void test_auto_comma(void) { static void test_benchmark(const char *s) { struct timeval t; gettimeofday(&t, NULL); - double start = t.tv_sec + t.tv_usec * 1e-6; - struct expr_var_list vars = {0}; - struct expr *e = expr_create(s, strlen(s), &vars, user_funcs); - if (e == NULL) { - printf("FAIL: %s can't be compiled\n", s); - status = 1; - return; - } - long N = 1000000L; - for (long i = 0; i < N; i++) { - expr_eval(e); + { + double start = t.tv_sec + t.tv_usec * 1e-6; + double end, ns; + long N, i; + struct expr_var_list vars = {0}; + struct expr *e = expr_create(s, strlen(s), &vars, user_funcs); + if (e == NULL) { + printf("FAIL: %s can't be compiled\n", s); + status = 1; + return; + } + N = 1000000L; + for (i = 0; i < N; i++) { + expr_eval(e); + } + gettimeofday(&t, NULL); + end = t.tv_sec + t.tv_usec * 1e-6; + expr_destroy(e, &vars); + ns = 1000000000 * (end - start) / N; + printf("BENCH %40s:\t%f ns/op (%dM op/sec)\n", s, ns, (int) (1000 / ns)); } - gettimeofday(&t, NULL); - double end = t.tv_sec + t.tv_usec * 1e-6; - expr_destroy(e, &vars); - double ns = 1000000000 * (end - start) / N; - printf("BENCH %40s:\t%f ns/op (%dM op/sec)\n", s, ns, (int) (1000 / ns)); } static void test_bad_syntax(void) { diff --git a/include/expr.h b/include/expr.h index 51fbcaf..452115a 100644 --- a/include/expr.h +++ b/include/expr.h @@ -822,26 +822,34 @@ static struct expr *expr_create2(const char *s, size_t len, { struct expr_string str = vec_pop(&os); if (str.n == 1 && *str.s == '{') { + struct expr_arg arg; str = vec_pop(&os); - struct expr_arg arg = vec_pop(&as); + arg = vec_pop(&as); if (vec_len(&es) > arg.eslen) { vec_push(&arg.args, vec_pop(&es)); } if (str.n == 1 && str.s[0] == '$') { + struct expr *u; + struct expr_var *v; if (vec_len(&arg.args) < 1) { vec_free(&arg.args); *error = EXPR_ERR_TOO_FEW_FUNC_ARGS; goto cleanup; /* too few arguments for $() function */ } - struct expr *u = &vec_nth(&arg.args, 0); + u = &vec_nth(&arg.args, 0); if (u->type != OP_VAR) { vec_free(&arg.args); *error = EXPR_ERR_FIRST_ARG_IS_NOT_VAR; goto cleanup; /* first argument is not a variable */ } - for (struct expr_var *v = vars->head; v; v = v->next) { + for (v = vars->head; v; v = v->next) { if (&v->value == u->param.var.value) { +#ifdef _MSC_VER + struct macro m = {v->name}; + m.body = arg.args; +#else struct macro m = {v->name, arg.args}; +#endif vec_push(¯os, m); break; } @@ -858,10 +866,12 @@ static struct expr *expr_create2(const char *s, size_t len, } } if (found != -1) { + struct expr root; + struct expr *p; int j; m = vec_nth(¯os, found); - struct expr root = expr_const(0); - struct expr *p = &root; + root = expr_const(0); + p = &root; /* Assign macro parameters */ for (j = 0; j < vec_len(&arg.args); j++) { char varname[4]; From 0dc337412696eed5e4c985e25432f6363923a54e Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 22:32:12 -0300 Subject: [PATCH 22/32] use memcpy() instead of strncpy() for portability --- CMakeLists.txt | 24 ++++++++++++------- README.md | 4 ++-- expr_test.c | 32 +++++++++++++++++++++---- include/expr.h | 65 ++++++++++++-------------------------------------- 4 files changed, 61 insertions(+), 64 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f7add8a..9c9fe88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,31 +6,39 @@ set(CMAKE_C_STANDARD 99) option(EXPR_PICKY_COMPILER "Enable picky compiler options" ON) -option(EXPR_PVS_STUDIO "Enable PVS-Studio analysis" OFF) +if(NOT MSVC) + option(EXPR_PVS_STUDIO "Enable PVS-Studio analysis" OFF) +endif() #TODO: coverage set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) if(EXPR_PICKY_COMPILER) - set(CMAKE_C_FLAGS - "${CMAKE_C_FLAGS} -Wall -Werror -Wextra -Wno-unused-function -Wpedantic -Wdeclaration-after-statement -Wstrict-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline" - ) + if(NOT MSVC) + set(CMAKE_C_FLAGS + "${CMAKE_C_FLAGS} -Wall -Werror -Wno-unused-function -Wpedantic -Wdeclaration-after-statement -Wstrict-prototypes -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline" + ) + endif() endif() include_directories(include) +if(NOT MSVC) + list(APPEND _libs m) +endif() + add_executable(example1 examples/example1.c) -target_link_libraries(example1 m) +target_link_libraries(example1 ${_libs}) add_executable(example2 examples/example2.c) -target_link_libraries(example2 m) +target_link_libraries(example2 ${_libs}) add_executable(example3 examples/example3.c) -target_link_libraries(example3 m) +target_link_libraries(example3 ${_libs}) add_executable(test expr_test.c) -target_link_libraries(test m) +target_link_libraries(test ${_libs}) if(EXPR_PVS_STUDIO) include(PVS-Studio) diff --git a/README.md b/README.md index 99128a4..ee33595 100644 --- a/README.md +++ b/README.md @@ -86,9 +86,9 @@ Takes an expression and immediately returns the result of it. If there is a pars Only the following functions from libc are used to reduce the footprint and make it easier to use: -- `calloc()`, `realloc()` and `free()` - memory management (all replaceable via macro) +- `calloc()`, `realloc()`, `free()` and `memcpy()` - memory management (all replaceable via macro) - `isnan()`, `isinf()`, `fmod()`, `pow()` - math operations (`fmod()` and `pow()` replaceable via macro) -- `strlen()`, `strncmp()`, `strncpy()` and `snprintf()` - tokenizing and parsing (all replaceable via macro) +- `strlen()`, `strncmp()` and `snprintf()` - tokenizing and parsing (all replaceable via macro) ## Running tests diff --git a/expr_test.c b/expr_test.c index e94ecd9..b252eae 100644 --- a/expr_test.c +++ b/expr_test.c @@ -7,7 +7,31 @@ #include #include +#ifdef _MSC_VER +#include +#else /* _MSC_VER */ #include +#endif /* _MSC_VER */ + +#ifdef _MSC_VER +int gettimeofday(struct timeval *tv, struct timezone *tz) { + static LONGLONG birthunixhnsec = 116444736000000000; /* in units of 100 ns */ + FILETIME systemtime; + GetSystemTimeAsFileTime(&systemtime); + ULARGE_INTEGER utime; + utime.LowPart = systemtime.dwLowDateTime; + utime.HighPart = systemtime.dwHighDateTime; + ULARGE_INTEGER birthunix; + birthunix.LowPart = (DWORD) birthunixhnsec; + birthunix.HighPart = birthunixhnsec >> 32; + LONGLONG usecs; + usecs = (LONGLONG)((utime.QuadPart - birthunix.QuadPart) / 10); + tv->tv_sec = (long) (usecs / 1000000); + tv->tv_usec = (long) (usecs % 1000000); + return 0; +} + +#endif /* _MSC_VER */ int status = 0; @@ -69,7 +93,7 @@ static void test_vars(void) { */ static int assert_tokens(char *s, char **expected) { - int len = strlen(s); + size_t len = strlen(s); int flags = EXPR_TDEFAULT; char *test = s; for (;;) { @@ -218,7 +242,7 @@ static void test_expr(char *s, expr_num_t expected) { } } -static void test_expr_error(char *s, int near, int error) { +static void test_expr_error(char *s, int pos, int error) { const char *ERRORS[] = { "EXPR_ERR_UNKNOWN", "EXPR_ERR_UNEXPECTED_NUMBER", @@ -242,9 +266,9 @@ static void test_expr_error(char *s, int near, int error) { printf("FAIL: %s should return error\n", s); status = 1; } - if (n != near) { + if (n != pos) { printf("FAIL: %s should return error near to %d, but returned at %d\n", s, - near, n); + pos, n); status = 1; } if (f != error) { diff --git a/include/expr.h b/include/expr.h index 452115a..c832d7a 100644 --- a/include/expr.h +++ b/include/expr.h @@ -12,24 +12,6 @@ extern "C" { #include #include -#ifdef _MSC_VER -#include - -#ifndef NAN -static unsigned long __nan[2] = {0xffffffff, 0x7fffffff}; -#define NAN (*(double *) __nan) -#endif /* NAN */ - -#ifndef INFINITY -static unsigned long __inf[2] = {0x0, 0x7ff00000}; -#define INFINITY (*(double *) __inf) -#endif /* INFINITY */ - -#define isnan(_x) _isnan(_x) -#define isinf(_x) (!_finite(_x)) - -#define snprintf _snprintf -#else /* _MSC_VER */ #ifndef NAN #define NAN (0.0 / 0.0) #endif /* NAN */ @@ -37,7 +19,6 @@ static unsigned long __inf[2] = {0x0, 0x7ff00000}; #ifndef INFINITY #define INFINITY (1.0 / 0.0) #endif /* INFINITY */ -#endif /* _MSC_VER */ /* * Expression number type @@ -58,6 +39,9 @@ static unsigned long __inf[2] = {0x0, 0x7ff00000}; #ifndef expr_free #define expr_free free #endif /* expr_free */ +#ifndef expr_memcpy +#define expr_memcpy memcpy +#endif /* expr_memcpy */ /* * String handling @@ -68,9 +52,6 @@ static unsigned long __inf[2] = {0x0, 0x7ff00000}; #ifndef expr_strncmp #define expr_strncmp strncmp #endif /* expr_strncmp */ -#ifndef expr_strncpy -#define expr_strncpy strncpy -#endif /* expr_strncpy */ #ifndef expr_snprintf #define expr_snprintf snprintf #endif /* expr_snprintf */ @@ -197,13 +178,8 @@ struct expr { } param; }; -#ifdef _MSC_VER -#define expr_init() \ - { (enum expr_type) 0 } -#else #define expr_init() \ { .type = (enum expr_type) 0 } -#endif struct expr_string { const char *s; @@ -365,7 +341,7 @@ static struct expr_var *expr_var(struct expr_var_list *vars, const char *s, } v->next = vars->head; v->value = 0; - expr_strncpy(v->name, s, len); + expr_memcpy(v->name, s, len); v->name[len] = '\0'; vars->head = v; return v; @@ -536,7 +512,7 @@ static int expr_next_token(const char *s, size_t len, int *flags) { return i; } else if (isdigit(c)) { if ((*flags & EXPR_TNUMBER) == 0) { - return EXPR_ERR_UNEXPECTED_NUMBER; // unexpected number + return EXPR_ERR_UNEXPECTED_NUMBER; /* unexpected number */ } *flags = EXPR_TOP | EXPR_TCLOSE; while ((c == '.' || isdigit(c)) && i < len) { @@ -546,7 +522,7 @@ static int expr_next_token(const char *s, size_t len, int *flags) { return i; } else if (isfirstvarchr(c)) { if ((*flags & EXPR_TWORD) == 0) { - return EXPR_ERR_UNEXPECTED_WORD; // unexpected word + return EXPR_ERR_UNEXPECTED_WORD; /* unexpected word */ } *flags = EXPR_TOP | EXPR_TOPEN | EXPR_TCLOSE; while ((isvarchr(c)) && i < len) { @@ -560,13 +536,13 @@ static int expr_next_token(const char *s, size_t len, int *flags) { } else if (c == ')' && (*flags & EXPR_TCLOSE) != 0) { *flags = EXPR_TOP | EXPR_TCLOSE; } else { - return EXPR_ERR_UNEXPECTED_PARENS; // unexpected parenthesis + return EXPR_ERR_UNEXPECTED_PARENS; /* unexpected parenthesis */ } return 1; } else { if ((*flags & EXPR_TOP) == 0) { if (expr_op(&c, 1, 1) == OP_UNKNOWN) { - return EXPR_ERR_MISS_EXPECTED_OPERAND; // missing expected operand + return EXPR_ERR_MISS_EXPECTED_OPERAND; /* missing expected operand */ } *flags = EXPR_TNUMBER | EXPR_TWORD | EXPR_TOPEN | EXPR_UNARY; return 1; @@ -582,7 +558,7 @@ static int expr_next_token(const char *s, size_t len, int *flags) { c = s[i]; } if (!found) { - return EXPR_ERR_UNKNOWN_OPERATOR; // unknown operator + return EXPR_ERR_UNKNOWN_OPERATOR; /* unknown operator */ } *flags = EXPR_TNUMBER | EXPR_TWORD | EXPR_TOPEN; return i; @@ -654,13 +630,7 @@ static struct expr expr_binary(enum expr_type type, struct expr a, return e; } -#ifdef _MSC_VER -#define inline __inline -#endif static inline void expr_copy(struct expr *dst, struct expr *src) { -#ifdef _MSC_VER -#undef inline -#endif int i; struct expr arg; dst->type = src->type; @@ -801,11 +771,11 @@ static struct expr *expr_create2(const char *s, size_t len, vec_push(&os, str); } else { *error = EXPR_ERR_BAD_CALL; - goto cleanup; // Bad call + goto cleanup; /* bad call */ } } else if (paren == EXPR_PAREN_EXPECTED) { *error = EXPR_ERR_BAD_CALL; - goto cleanup; // Bad call + goto cleanup; /* bad call */ } else if (n == 1 && *tok == ')') { int minlen = (vec_len(&as) > 0 ? vec_peek(&as).oslen : 0); while (vec_len(&os) > minlen && *vec_peek(&os).s != '(' && @@ -817,7 +787,7 @@ static struct expr *expr_create2(const char *s, size_t len, } if (vec_len(&os) == 0) { *error = EXPR_ERR_BAD_PARENS; - goto cleanup; // Bad parens + goto cleanup; /* bad parens */ } { struct expr_string str = vec_pop(&os); @@ -844,12 +814,7 @@ static struct expr *expr_create2(const char *s, size_t len, } for (v = vars->head; v; v = v->next) { if (&v->value == u->param.var.value) { -#ifdef _MSC_VER - struct macro m = {v->name}; - m.body = arg.args; -#else struct macro m = {v->name, arg.args}; -#endif vec_push(¯os, m); break; } @@ -962,7 +927,7 @@ static struct expr *expr_create2(const char *s, size_t len, idn = n; } else { *error = EXPR_ERR_BAD_VARIABLE_NAME; - goto cleanup; // Bad variable name, e.g. '2.3.4' or '4ever' + goto cleanup; /* bad variable name, e.g. '2.3.4' or '4ever' */ } } paren = paren_next; @@ -976,7 +941,7 @@ static struct expr *expr_create2(const char *s, size_t len, struct expr_string rest = vec_pop(&os); if (rest.n == 1 && (*rest.s == '(' || *rest.s == ')')) { *error = EXPR_ERR_BAD_PARENS; - goto cleanup; // Bad paren + goto cleanup; /* bad paren */ } if (expr_bind(rest.s, rest.n, &es) == -1) { *error = (*rest.s == '=') ? EXPR_ERR_BAD_ASSIGNMENT : EXPR_ERR_BAD_PARENS; @@ -984,7 +949,7 @@ static struct expr *expr_create2(const char *s, size_t len, } } - result = expr_alloc(sizeof(struct expr)); + result = (struct expr *) expr_alloc(sizeof(struct expr)); if (result != NULL) { if (vec_len(&es) == 0) { result->type = OP_CONST; From 4420c62ea56bea0d15080a2701a738c2bf0eecf8 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 28 Apr 2020 23:35:16 -0300 Subject: [PATCH 23/32] fixed PVS-Studio alerts --- CMakeLists.txt | 3 +++ expr_test.c | 5 ++++- include/expr.h | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c9fe88..41eb98c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,9 @@ if(EXPR_PVS_STUDIO) FORMAT fullhtml ANALYZE + example1 + example2 + example3 test LOG pvs_studio_fullhtml) diff --git a/expr_test.c b/expr_test.c index b252eae..bfce3a1 100644 --- a/expr_test.c +++ b/expr_test.c @@ -221,8 +221,11 @@ static void test_expr(char *s, expr_num_t expected) { { expr_num_t result = expr_eval(e); - char *p = (char *) malloc(strlen(s) + 1); char *it; + char *p = (char *) malloc(strlen(s) + 1); + if (p == NULL) { + return; + } strncpy(p, s, strlen(s) + 1); for (it = p; *it; it++) { if (*it == '\n') { diff --git a/include/expr.h b/include/expr.h index c832d7a..4cf9b82 100644 --- a/include/expr.h +++ b/include/expr.h @@ -921,7 +921,7 @@ static struct expr *expr_create2(const char *s, size_t len, } } } else { - if (n > 0 && !isdigit(*tok)) { + if (/*n > 0 &&*/ !isdigit(*tok)) { /* Valid identifier, a variable or a function */ id = tok; idn = n; From e9b86a6daa5ab976da87d93ac37a48a029e3c931 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Wed, 29 Apr 2020 09:35:15 -0300 Subject: [PATCH 24/32] removed unused ifdef from tokenizer test --- expr_test.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/expr_test.c b/expr_test.c index bfce3a1..7b0934f 100644 --- a/expr_test.c +++ b/expr_test.c @@ -14,6 +14,7 @@ #endif /* _MSC_VER */ #ifdef _MSC_VER + int gettimeofday(struct timeval *tv, struct timezone *tz) { static LONGLONG birthunixhnsec = 116444736000000000; /* in units of 100 ns */ FILETIME systemtime; @@ -124,15 +125,6 @@ static int assert_tokens(char *s, char **expected) { static void test_tokenizer(void) { unsigned int i; -#ifdef _MSC_VER - char *T1[] = {"", NULL}; - char *T2[] = {"1", "1", NULL}; - char *T3[] = {"1+11", "1", "+", "11", NULL}; - char *T4[] = {"1*11", "1", "*", "11", NULL}; - char *T5[] = {"1**11", "1", "**", "11", NULL}; - char *T6[] = {"1**-11", "1", "**", "-", "11", NULL}; - char **TESTS[] = {T1, T2, T3, T4, T5, T6}; -#else char **TESTS[] = { (char *[]){"", NULL}, (char *[]){"1", "1", NULL}, @@ -141,7 +133,6 @@ static void test_tokenizer(void) { (char *[]){"1**11", "1", "**", "11", NULL}, (char *[]){"1**-11", "1", "**", "-", "11", NULL}, }; -#endif for (i = 0; i < sizeof(TESTS) / sizeof(TESTS[0]); i++) { assert_tokens(TESTS[i][0], TESTS[i] + 1); } From 541395055a5dd55906805ab19b9ae264721099c7 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Wed, 29 Apr 2020 20:42:46 -0300 Subject: [PATCH 25/32] muted "unused function" warning and avoided to use strncpy() for more portability --- expr_test.c | 6 +++++- include/expr.h | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/expr_test.c b/expr_test.c index 7b0934f..441792a 100644 --- a/expr_test.c +++ b/expr_test.c @@ -212,12 +212,16 @@ static void test_expr(char *s, expr_num_t expected) { { expr_num_t result = expr_eval(e); + size_t l; char *it; char *p = (char *) malloc(strlen(s) + 1); if (p == NULL) { return; } - strncpy(p, s, strlen(s) + 1); + l = strlen(s); + memcpy(p, s, l + 1); + p[l] = '\0'; + for (it = p; *it; it++) { if (*it == '\n') { *it = '\\'; diff --git a/include/expr.h b/include/expr.h index 4cf9b82..6e9204a 100644 --- a/include/expr.h +++ b/include/expr.h @@ -20,6 +20,12 @@ extern "C" { #define INFINITY (1.0 / 0.0) #endif /* INFINITY */ +#ifdef __GNUC__ +#define EXPR_UNUSED __attribute__((used)) +#else /* __GNUC__ */ +#define EXPR_UNUSED +#endif /* __GNUC__ */ + /* * Expression number type */ @@ -1040,7 +1046,7 @@ static void expr_destroy(struct expr *e, struct expr_var_list *vars) { } } -static expr_num_t expr_calc(const char *s) { +EXPR_UNUSED static expr_num_t expr_calc(const char *s) { struct expr_var_list vars = {0}; struct expr_func funcs[] = {{NULL, NULL, NULL, 0}}; struct expr *e; From 63249362783d2910bdc98686a582ca4229fb3fa7 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Fri, 1 May 2020 16:55:20 -0300 Subject: [PATCH 26/32] added new function "expr_calc2()" --- include/expr.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/expr.h b/include/expr.h index 6e9204a..64b3f77 100644 --- a/include/expr.h +++ b/include/expr.h @@ -1046,12 +1046,12 @@ static void expr_destroy(struct expr *e, struct expr_var_list *vars) { } } -EXPR_UNUSED static expr_num_t expr_calc(const char *s) { +EXPR_UNUSED static expr_num_t expr_calc2(const char *s, size_t len) { struct expr_var_list vars = {0}; struct expr_func funcs[] = {{NULL, NULL, NULL, 0}}; struct expr *e; expr_num_t r; - e = expr_create(s, expr_strlen(s), &vars, funcs); + e = expr_create(s, len, &vars, funcs); if (e == NULL) { return NAN; } @@ -1060,6 +1060,10 @@ EXPR_UNUSED static expr_num_t expr_calc(const char *s) { return r; } +EXPR_UNUSED static expr_num_t expr_calc(const char *s) { + return expr_calc2(s, expr_strlen(s)); +} + #ifdef __cplusplus } /* extern "C" */ #endif From a20f68dacbfb572bcfe1ffdc5938c6547d16681d Mon Sep 17 00:00:00 2001 From: silvioprog Date: Sat, 2 May 2020 10:00:26 -0300 Subject: [PATCH 27/32] added error handling to "expr_calc()" --- examples/example1.c | 3 ++- examples/example2.c | 9 ++++++--- examples/example3.c | 3 ++- expr_test.c | 3 ++- include/expr.h | 11 +++++++---- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/examples/example1.c b/examples/example1.c index d13133e..80c6a10 100644 --- a/examples/example1.c +++ b/examples/example1.c @@ -1,6 +1,7 @@ #include "expr.h" int main(void) { - printf("result: %f\n", expr_calc("2 + 3")); + int near, error; + printf("result: %f\n", expr_calc("2 + 3", &near, &error)); return 0; } diff --git a/examples/example2.c b/examples/example2.c index 7d45d3b..4085ad7 100644 --- a/examples/example2.c +++ b/examples/example2.c @@ -1,9 +1,12 @@ #include "expr.h" int main(void) { - printf("macro definition: %f\n", expr_calc("$(mysum, $1 + $2), mysum(2, 3)")); - printf("variable assignment: %f\n", expr_calc("a=2, b=3, a+b")); + int near, error; + printf("macro definition: %f\n", + expr_calc("$(mysum, $1 + $2), mysum(2, 3)", &near, &error)); + printf("variable assignment: %f\n", + expr_calc("a=2, b=3, a+b", &near, &error)); printf("all together: %f\n", - expr_calc("$(mysum, $1 + $2), a=2, b=3, mysum(a, b)")); + expr_calc("$(mysum, $1 + $2), a=2, b=3, mysum(a, b)", &near, &error)); return 0; } diff --git a/examples/example3.c b/examples/example3.c index b1eabf7..af5b8f9 100644 --- a/examples/example3.c +++ b/examples/example3.c @@ -14,9 +14,10 @@ static struct expr_func user_funcs[] = { }; int main(void) { + int near, error; const char *s = "x = 5, sum(2, x)"; struct expr_var_list vars = {0}; - struct expr *e = expr_create(s, strlen(s), &vars, user_funcs); + struct expr *e = expr_create2(s, strlen(s), &vars, user_funcs, &near, &error); if (e == NULL) { printf("Syntax error"); return 1; diff --git a/expr_test.c b/expr_test.c index 441792a..ccfefd2 100644 --- a/expr_test.c +++ b/expr_test.c @@ -492,8 +492,9 @@ static void test_bad_syntax(void) { } static void test_calc(void) { + int near, error; const char *p = "2+3"; - expr_num_t result = expr_calc(p); + expr_num_t result = expr_calc(p, &near, &error); expr_num_t expected = 5; if ((isnan(result) && !isnan(expected)) || fabs(result - expected) > 0.00001f) { diff --git a/include/expr.h b/include/expr.h index 64b3f77..a5993e6 100644 --- a/include/expr.h +++ b/include/expr.h @@ -1046,22 +1046,25 @@ static void expr_destroy(struct expr *e, struct expr_var_list *vars) { } } -EXPR_UNUSED static expr_num_t expr_calc2(const char *s, size_t len) { +EXPR_UNUSED static expr_num_t expr_calc2(const char *s, size_t len, int *near, + int *error) { struct expr_var_list vars = {0}; struct expr_func funcs[] = {{NULL, NULL, NULL, 0}}; struct expr *e; expr_num_t r; - e = expr_create(s, len, &vars, funcs); + e = expr_create2(s, len, &vars, funcs, near, error); if (e == NULL) { return NAN; } + *near = 0; + *error = 0; r = expr_eval(e); expr_destroy(e, &vars); return r; } -EXPR_UNUSED static expr_num_t expr_calc(const char *s) { - return expr_calc2(s, expr_strlen(s)); +EXPR_UNUSED static expr_num_t expr_calc(const char *s, int *near, int *error) { + return expr_calc2(s, expr_strlen(s), near, error); } #ifdef __cplusplus From ebf05d8ef1329def83f09fbfa07c79c9ab61f30b Mon Sep 17 00:00:00 2001 From: silvioprog Date: Sat, 2 May 2020 10:12:06 -0300 Subject: [PATCH 28/32] avoid error "unused function" --- include/expr.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/expr.h b/include/expr.h index a5993e6..21a1852 100644 --- a/include/expr.h +++ b/include/expr.h @@ -1001,9 +1001,9 @@ cleanup : { return result; } -static struct expr *expr_create(const char *s, size_t len, - struct expr_var_list *vars, - struct expr_func *funcs) { +EXPR_UNUSED static struct expr *expr_create(const char *s, size_t len, + struct expr_var_list *vars, + struct expr_func *funcs) { int near; int error; return expr_create2(s, len, vars, funcs, &near, &error); From f3064c7e88c83a1e4bbc0873925cde2aa794518b Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 5 May 2020 00:59:07 -0300 Subject: [PATCH 29/32] improved the function context allowing to send any value as closure --- expr_test.c | 22 ++++++++-------------- include/expr.h | 21 +++++---------------- 2 files changed, 13 insertions(+), 30 deletions(-) mode change 100644 => 100755 expr_test.c diff --git a/expr_test.c b/expr_test.c old mode 100644 new mode 100755 index ccfefd2..3406905 --- a/expr_test.c +++ b/expr_test.c @@ -145,24 +145,19 @@ struct nop_context { void *p; }; +static int fake_context = 123; + static void user_func_nop_cleanup(struct expr_func *f, void *c) { - struct nop_context *nop = (struct nop_context *) c; (void) f; - free(nop->p); + if (c != &fake_context) + printf("FAIL: user_func_nop_cleanup()\n"); } static expr_num_t user_func_nop(struct expr_func *f, vec_expr_t *args, void *c) { - struct nop_context *nop = (struct nop_context *) c; + (void) f; (void) args; - if (f->ctxsz == 0) { - free(nop->p); - return 0; - } - if (nop->p == NULL) { - nop->p = malloc(10000); - } - return 0; + return (c == &fake_context) ? 1 : 0; } static expr_num_t user_func_add(struct expr_func *f, vec_expr_t *args, @@ -194,7 +189,7 @@ static expr_num_t user_func_print(struct expr_func *f, vec_expr_t *args, } static struct expr_func user_funcs[] = { - {"nop", user_func_nop, user_func_nop_cleanup, sizeof(struct nop_context)}, + {"nop", user_func_nop, user_func_nop_cleanup, &fake_context}, {"add", user_func_add, NULL, 0}, {"next", user_func_next, NULL, 0}, {"print", user_func_print, NULL, 0}, @@ -253,7 +248,6 @@ static void test_expr_error(char *s, int pos, int error) { "EXPR_ERR_BAD_PARENS", "EXPR_ERR_TOO_FEW_FUNC_ARGS", "EXPR_ERR_FIRST_ARG_IS_NOT_VAR", - "EXPR_ERR_ALLOCATION_FAILED", "EXPR_ERR_BAD_VARIABLE_NAME", "EXPR_ERR_BAD_ASSIGNMENT", }; @@ -384,7 +378,7 @@ static void test_funcs(void) { test_expr("add(1,2) + next(3)", 7); test_expr("add(1,next(2))", 4); test_expr("add(1,1+1) + add(2*2+1,2)", 10); - test_expr("nop()", 0); + test_expr("nop()", 1); test_expr("x=2,add(1, next(x))", 4); test_expr("$(zero), zero()", 0); test_expr("$(zero), zero(1, 2, 3)", 0); diff --git a/include/expr.h b/include/expr.h index 21a1852..ed32d6f 100644 --- a/include/expr.h +++ b/include/expr.h @@ -303,7 +303,7 @@ struct expr_func { const char *name; exprfn_t f; exprfn_cleanup_t cleanup; - size_t ctxsz; + void *context; }; static struct expr_func *expr_func(struct expr_func *funcs, const char *s, @@ -485,9 +485,8 @@ static expr_num_t expr_eval(struct expr *e) { #define EXPR_ERR_BAD_PARENS (-8) #define EXPR_ERR_TOO_FEW_FUNC_ARGS (-9) #define EXPR_ERR_FIRST_ARG_IS_NOT_VAR (-10) -#define EXPR_ERR_ALLOCATION_FAILED (-11) -#define EXPR_ERR_BAD_VARIABLE_NAME (-12) -#define EXPR_ERR_BAD_ASSIGNMENT (-13) +#define EXPR_ERR_BAD_VARIABLE_NAME (-11) +#define EXPR_ERR_BAD_ASSIGNMENT (-12) static int expr_next_token(const char *s, size_t len, int *flags) { unsigned int i = 0; @@ -647,9 +646,7 @@ static inline void expr_copy(struct expr *dst, struct expr *src) { expr_copy(&tmp, &arg); vec_push(&dst->param.func.args, tmp); } - if (src->param.func.f->ctxsz > 0) { - dst->param.func.context = expr_alloc(src->param.func.f->ctxsz); - } + dst->param.func.context = src->param.func.f->context; } else if (src->type == OP_CONST) { dst->param.num.value = src->param.num.value; } else if (src->type == OP_VAR) { @@ -876,14 +873,7 @@ static struct expr *expr_create2(const char *s, size_t len, bound_func.type = OP_FUNC; bound_func.param.func.f = f; bound_func.param.func.args = arg.args; - if (f->ctxsz > 0) { - void *p = expr_alloc(f->ctxsz); - if (p == NULL) { - *error = EXPR_ERR_ALLOCATION_FAILED; - goto cleanup; /* allocation failed */ - } - bound_func.param.func.context = p; - } + bound_func.param.func.context = f->context; vec_push(&es, bound_func); } } @@ -1021,7 +1011,6 @@ static void expr_destroy_args(struct expr *e) { if (e->param.func.f->cleanup != NULL) { e->param.func.f->cleanup(e->param.func.f, e->param.func.context); } - expr_free(e->param.func.context); } } else if (e->type != OP_CONST && e->type != OP_VAR) { vec_foreach(&e->param.op.args, arg, i) { From db26344081e00b2eb94dd0a594cb2c2004501fd8 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Tue, 5 May 2020 01:05:38 -0300 Subject: [PATCH 30/32] called function cleanup even if context is null --- include/expr.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/expr.h b/include/expr.h index ed32d6f..974dae7 100644 --- a/include/expr.h +++ b/include/expr.h @@ -1007,10 +1007,8 @@ static void expr_destroy_args(struct expr *e) { expr_destroy_args(&arg); } vec_free(&e->param.func.args); - if (e->param.func.context != NULL) { - if (e->param.func.f->cleanup != NULL) { - e->param.func.f->cleanup(e->param.func.f, e->param.func.context); - } + if (e->param.func.f->cleanup != NULL) { + e->param.func.f->cleanup(e->param.func.f, e->param.func.context); } } else if (e->type != OP_CONST && e->type != OP_VAR) { vec_foreach(&e->param.op.args, arg, i) { From 106a62fdf07b43ea7890d2ce327ee3afb96827a2 Mon Sep 17 00:00:00 2001 From: silvioprog Date: Fri, 8 May 2020 23:53:27 -0300 Subject: [PATCH 31/32] Avoided SIGSEGV if evaluate expression without functions. --- include/expr.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/expr.h b/include/expr.h index 974dae7..eaa0fb6 100644 --- a/include/expr.h +++ b/include/expr.h @@ -309,9 +309,11 @@ struct expr_func { static struct expr_func *expr_func(struct expr_func *funcs, const char *s, size_t len) { struct expr_func *f; - for (f = funcs; f->name; f++) { - if (expr_strlen(f->name) == len && expr_strncmp(f->name, s, len) == 0) { - return f; + if (funcs != NULL) { + for (f = funcs; f->name; f++) { + if (expr_strlen(f->name) == len && expr_strncmp(f->name, s, len) == 0) { + return f; + } } } return NULL; From 083827b3b157617c1621a6be351979c5fbee958d Mon Sep 17 00:00:00 2001 From: silvioprog Date: Thu, 4 Jun 2020 23:46:42 -0300 Subject: [PATCH 32/32] Fixed build on GCC 10.1.1. --- include/expr.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/expr.h b/include/expr.h index eaa0fb6..9b40d08 100644 --- a/include/expr.h +++ b/include/expr.h @@ -637,7 +637,7 @@ static struct expr expr_binary(enum expr_type type, struct expr a, return e; } -static inline void expr_copy(struct expr *dst, struct expr *src) { +static void expr_copy(struct expr *dst, struct expr *src) { int i; struct expr arg; dst->type = src->type; @@ -844,7 +844,7 @@ static struct expr *expr_create2(const char *s, size_t len, p = &root; /* Assign macro parameters */ for (j = 0; j < vec_len(&arg.args); j++) { - char varname[4]; + char varname[13]; expr_snprintf(varname, sizeof(varname) - 1, "$%d", (j + 1)); { struct expr_var *v =