pax_global_header00006660000000000000000000000064134035701460014515gustar00rootroot0000000000000052 comment=7302245f8dafb33f4ecf9861fd81ceb4d7b193c8 kexpr-nim-0.0.2/000077500000000000000000000000001340357014600134265ustar00rootroot00000000000000kexpr-nim-0.0.2/.gitignore000066400000000000000000000000231340357014600154110ustar00rootroot00000000000000nimcache *.o kexpr kexpr-nim-0.0.2/LICENSE000066400000000000000000000020571340357014600144370ustar00rootroot00000000000000MIT License Copyright (c) 2017 Brent Pedersen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. kexpr-nim-0.0.2/Makefile000066400000000000000000000001401340357014600150610ustar00rootroot00000000000000CC=$(CC) kexpr: kexpr.nim cp kexpr-c.h nimcache/ nim c --passL:"-lm" kexpr.nim all: kexpr kexpr-nim-0.0.2/README.md000066400000000000000000000014621340357014600147100ustar00rootroot00000000000000# kexpr-nim nim wrapper for Heng Li's kexpr math expression library [![badge](https://img.shields.io/badge/docs-latest-blue.svg)](https://brentp.github.io/kexpr-nim/) ```Nim var e = expression("5*6+x > 20") e["x"] = 10 echo e.int e["x"] = 20 echo e.int64 assert e.error() == 0 e = expression("(sample1 > 20 & sample2 > 10 & sample3 < 40") # missing paren assert e.error() != 0 e.clear() e = expression("(sample1 > 20) & (sample2 > 10) & (sample3 < 40)") e["sample1"] = 21; e["sample2"] = 65; e["sample3"] = 20 echo e.int # 1 e["sample1"] = 0 echo e.int # 0 assert e.error() == 0 echo e.float # 0.0 ``` ## installation If you have nimble installed you can do: ``` nimble install kexpr ``` [![nimble](https://raw.githubusercontent.com/yglukhov/nimble-tag/master/nimble.png)](https://github.com/nim-lang/nimble) kexpr-nim-0.0.2/docs/000077500000000000000000000000001340357014600143565ustar00rootroot00000000000000kexpr-nim-0.0.2/docs/.nojekyll000066400000000000000000000000001340357014600161740ustar00rootroot00000000000000kexpr-nim-0.0.2/docs/index.html000066400000000000000000002046471340357014600163700ustar00rootroot00000000000000 kexpr

kexpr

kexpr evaluates math and boolean expressions

Types

kexpr_t = kexpr_s
Expr = ref object
  ke*: ptr kexpr_t
  err: cint
Expr is a math expression

Consts

KEE_UNQU = 0x00000001
KEE_UNLP = 0x00000002
KEE_UNRP = 0x00000004
KEE_UNOP = 0x00000008
KEE_FUNC = 0x00000010
KEE_ARG = 0x00000020
KEE_NUM = 0x00000040
KEE_UNFUNC = 0x00000040
KEE_UNVAR = 0x00000080
KEV_REAL = 1
KEV_INT = 2
KEV_STR = 3

Procs

proc ke_parse(s: cstring; err: ptr cint): ptr kexpr_t {...}{.importc: "ke_parse", cdecl.}
parse an expression and return errors in $err
proc ke_destroy(ke: ptr kexpr_t) {...}{.importc: "ke_destroy", cdecl.}
free memory allocated during parsing
proc ke_set_int(ke: ptr kexpr_t; `var`: cstring; x: int64): cint {...}{.importc: "ke_set_int",
    discardable, cdecl.}
set a variable to integer value and return the occurrence of the variable
proc ke_set_real(ke: ptr kexpr_t; `var`: cstring; x: cdouble): cint {...}{.
    importc: "ke_set_real", discardable, cdecl.}
set a variable to real value and return the occurrence of the variable
proc ke_set_str(ke: ptr kexpr_t; `var`: cstring; x: cstring): cint {...}{.
    importc: "ke_set_str", discardable, cdecl.}
set a variable to string value and return the occurrence of the variable
proc ke_set_real_func1(ke: ptr kexpr_t; name: cstring;
                      `func`: proc (a2: cdouble): cdouble): cint {...}{.
    importc: "ke_set_real_func1", cdecl.}
proc ke_set_real_func2(ke: ptr kexpr_t; name: cstring;
                      `func`: proc (a2: cdouble; a3: cdouble): cdouble): cint {...}{.
    importc: "ke_set_real_func2", cdecl.}
set a user-defined function
proc ke_set_default_func(ke: ptr kexpr_t): cint {...}{.importc: "ke_set_default_func", cdecl.}
set default math functions
proc ke_unset(e: ptr kexpr_t) {...}{.importc: "ke_unset", cdecl.}
mark all variable as unset
proc ke_eval(ke: ptr kexpr_t; i: ptr int64; r: ptr cdouble; s: cstringArray;
            ret_type: ptr cint): cint {...}{.importc: "ke_eval", cdecl.}
evaluate expression; return error code; final value is returned via pointers
proc ke_eval_int(ke: ptr kexpr_t; err: ptr cint): int64 {...}{.importc: "ke_eval_int", cdecl.}
proc ke_eval_real(ke: ptr kexpr_t; err: ptr cint): cdouble {...}{.importc: "ke_eval_real",
    cdecl.}
proc ke_print(ke: ptr kexpr_t) {...}{.importc: "ke_print", cdecl.}
print the expression in Reverse Polish notation (RPN)
proc expression(s: string): Expr {...}{.raises: [], tags: [].}
initalize an expression
proc error(e: Expr): int {...}{.inline, raises: [], tags: [].}
check the error value of the expression. non-zero values are errors.
proc clear(e: Expr) {...}{.inline, raises: [], tags: [].}
clear the error state and empty the expression.
proc `[]=`(e: Expr; k: string; val: int or int32 or int64 or int8 or uint or uint8 or uint16 or
    uint32) {...}{.inline.}
proc `[]=`(e: Expr; k: string; val: float or float32 or float64) {...}{.inline.}
proc `[]=`(e: Expr; k: string; val: string) {...}{.inline, raises: [], tags: [].}

Converters

converter toInt(e: Expr): int {...}{.inline, raises: [], tags: [].}
evaluate the epression and interpret the result as an int.
converter toInt64(e: Expr): int64 {...}{.inline, raises: [], tags: [].}
evaluate the epression and interpret the result as an int64.
converter toBool(e: Expr): bool {...}{.inline, raises: [], tags: [].}
evaluate the epression and interpret the result as a bool
converter toFloat(e: Expr): float {...}{.inline, raises: [], tags: [].}
evaluate the expression and interpret the result as a float.
converter toFloat64(e: Expr): float64 {...}{.inline, raises: [], tags: [].}
evaluate the expression and interpret the result as a float64.
kexpr-nim-0.0.2/kexpr.nim000066400000000000000000000100121340357014600152560ustar00rootroot00000000000000## kexpr evaluates math and boolean expressions {.compile: "kexpr/kexpr-c.c".} import tables type kexpr_s {.bycopy.} = object kexpr_t* = kexpr_s const KEE_UNQU* = 0x00000001 KEE_UNLP* = 0x00000002 KEE_UNRP* = 0x00000004 KEE_UNOP* = 0x00000008 KEE_FUNC* = 0x00000010 KEE_ARG* = 0x00000020 KEE_NUM* = 0x00000040 const KEE_UNFUNC* = 0x00000040 KEE_UNVAR* = 0x00000080 const KEV_REAL* = 1 KEV_INT* = 2 KEV_STR* = 3 proc ke_parse*(s: cstring; err: ptr cint): ptr kexpr_t {.importc:"ke_parse",cdecl.} ## parse an expression and return errors in $err proc ke_destroy*(ke: ptr kexpr_t) {.importc:"ke_destroy",cdecl.} ## free memory allocated during parsing proc ke_set_int*(ke: ptr kexpr_t; `var`: cstring; x: int64): cint {.importc:"ke_set_int", discardable, cdecl.} ## set a variable to integer value and return the occurrence of the variable proc ke_set_real*(ke: ptr kexpr_t; `var`: cstring; x: cdouble): cint {.importc:"ke_set_real", discardable, cdecl.} ## set a variable to real value and return the occurrence of the variable proc ke_set_str*(ke: ptr kexpr_t; `var`: cstring; x: cstring): cint {.importc:"ke_set_str", discardable, cdecl.} ## set a variable to string value and return the occurrence of the variable proc ke_set_real_func1*(ke: ptr kexpr_t; name: cstring; `func`: proc (a2: cdouble): cdouble): cint {.importc:"ke_set_real_func1", cdecl.} proc ke_set_real_func2*(ke: ptr kexpr_t; name: cstring; `func`: proc (a2: cdouble; a3: cdouble): cdouble): cint {.importc:"ke_set_real_func2", cdecl.} ## set a user-defined function proc ke_set_default_func*(ke: ptr kexpr_t): cint {.importc:"ke_set_default_func", cdecl.} ## set default math functions proc ke_unset*(e: ptr kexpr_t) {.importc:"ke_unset", cdecl.} ## mark all variable as unset proc ke_eval*(ke: ptr kexpr_t; i: ptr int64; r: ptr cdouble; s: cstringArray; ret_type: ptr cint): cint {.importc:"ke_eval", cdecl.} ## evaluate expression; return error code; final value is returned via pointers proc ke_eval_int*(ke: ptr kexpr_t; err: ptr cint): int64 {.importc:"ke_eval_int", cdecl.} proc ke_eval_real*(ke: ptr kexpr_t; err: ptr cint): cdouble {.importc:"ke_eval_real", cdecl.} proc ke_print*(ke: ptr kexpr_t) {.importc:"ke_print", cdecl.} ## print the expression in Reverse Polish notation (RPN) type Expr* = ref object ## Expr is a math expression ke*: ptr kexpr_t err: cint proc finalize_expr(e: Expr) = ke_destroy(e.ke) proc expression*(s: string): Expr = ## initalize an expression var e: Expr new(e, finalize_expr) e.ke = ke_parse(s, e.err.addr) return e proc error*(e:Expr): int {.inline.} = ## check the error value of the expression. non-zero values are errors. return int(e.err) proc clear*(e:Expr) {.inline.} = ## clear the error state and empty the expression. e.err = 0 if e.ke != nil: ke_unset(e.ke) proc `[]=`*(e:Expr, k:string, val:int or int32 or int64 or int8 or uint or uint8 or uint16 or uint32) {.inline.} = ke_set_int(e.ke, k, cint(val)) proc `[]=`*(e:Expr, k:string, val:float or float32 or float64) {.inline.} = ke_set_real(e.ke, k, cdouble(val)) proc `[]=`*(e:Expr, k:string, val:string) {.inline.} = ke_set_str(e.ke, k, val) converter toInt*(e: Expr): int {.inline.} = ## evaluate the epression and interpret the result as an int. return int(ke_eval_int(e.ke, e.err.addr)) converter toInt64*(e: Expr): int64 {.inline.} = ## evaluate the epression and interpret the result as an int64. return int64(ke_eval_int(e.ke, e.err.addr)) converter toBool*(e: Expr): bool {.inline.} = ## evaluate the epression and interpret the result as a bool return abs(ke_eval_real(e.ke, e.err.addr)) > 1e-8 converter toFloat*(e: Expr): float {.inline.} = ## evaluate the expression and interpret the result as a float. return float(ke_eval_real(e.ke, e.err.addr)) converter toFloat64*(e: Expr): float64 {.inline.} = ## evaluate the expression and interpret the result as a float64. return float64(ke_eval_real(e.ke, e.err.addr)) kexpr-nim-0.0.2/kexpr.nimble000066400000000000000000000006351340357014600157530ustar00rootroot00000000000000# Package version = "0.0.2" description = "math expression evaluator library" author = "Brent Pedersen" license = "MIT" skipFiles = @["tests.nim"] #installFiles = @["src/kexpr.nim", "src/kexpr-c.c", "src/kexpr-c.h"] requires "nim >= 0.17.0" task test, "tests": exec "nim c -r tests.nim" task docs, "make the docs": exec "nim doc kexpr.nim" exec "mv kexpr.html docs/index.html" kexpr-nim-0.0.2/kexpr/000077500000000000000000000000001340357014600145575ustar00rootroot00000000000000kexpr-nim-0.0.2/kexpr/kexpr-c.c000066400000000000000000000364631340357014600163100ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "kexpr-c.h" /*************** * Definitions * ***************/ #define KEO_NULL 0 #define KEO_POS 1 #define KEO_NEG 2 #define KEO_BNOT 3 #define KEO_LNOT 4 #define KEO_POW 5 #define KEO_MUL 6 #define KEO_DIV 7 #define KEO_IDIV 8 #define KEO_MOD 9 #define KEO_ADD 10 #define KEO_SUB 11 #define KEO_LSH 12 #define KEO_RSH 13 #define KEO_LT 14 #define KEO_LE 15 #define KEO_GT 16 #define KEO_GE 17 #define KEO_EQ 18 #define KEO_NE 19 #define KEO_BAND 20 #define KEO_BXOR 21 #define KEO_BOR 22 #define KEO_LAND 23 #define KEO_LOR 24 #define KET_NULL 0 #define KET_VAL 1 #define KET_OP 2 #define KET_FUNC 3 #define KEF_NULL 0 #define KEF_REAL 1 struct ke1_s; typedef struct ke1_s { uint32_t ttype:16, vtype:10, assigned:1, user_func:5; // ttype: token type; vtype: value type int32_t op:8, n_args:24; // op: operator, n_args: number of arguments char *name; // variable name or function name union { void (*builtin)(struct ke1_s *a, struct ke1_s *b); // execution function double (*real_func1)(double); double (*real_func2)(double, double); } f; double r; int64_t i; char *s; } ke1_t; static int ke_op[25] = { 0, 1<<1|1, 1<<1|1, 1<<1|1, 1<<1|1, // unary operators 2<<1|1, // pow() 3<<1, 3<<1, 3<<1, 3<<1, // * / // % 4<<1, 4<<1, // + and - 5<<1, 5<<1, // << and >> 6<<1, 6<<1, 6<<1, 6<<1, // < > <= >= 7<<1, 7<<1, // == != 8<<1, // & 9<<1, // ^ 10<<1,// | 11<<1,// && 12<<1 // || }; static const char *ke_opstr[] = { "", "+(1)", "-(1)", "~", "!", "**", "*", "/", "//", "%", "+", "-", "<<", ">>", "<", "<=", ">", ">=", "==", "!=", "&", "^", "|", "&&", "||" }; struct kexpr_s { int n; ke1_t *e; }; /********************** * Operator functions * **********************/ #define KE_GEN_CMP(_type, _op) \ static void ke_op_##_type(ke1_t *p, ke1_t *q) { \ if (p->vtype == KEV_STR && q->vtype == KEV_STR) p->i = (strcmp(p->s, q->s) _op 0); \ else p->i = p->vtype == KEV_REAL || q->vtype == KEV_REAL? (p->r _op q->r) : (p->i _op q->i); \ p->r = (double)p->i; \ p->vtype = KEV_INT; \ } KE_GEN_CMP(KEO_LT, <) KE_GEN_CMP(KEO_LE, <=) KE_GEN_CMP(KEO_GT, >) KE_GEN_CMP(KEO_GE, >=) KE_GEN_CMP(KEO_EQ, ==) KE_GEN_CMP(KEO_NE, !=) #define KE_GEN_BIN_INT(_type, _op) \ static void ke_op_##_type(ke1_t *p, ke1_t *q) { \ p->i _op q->i; p->r = (double)p->i; \ p->vtype = KEV_INT; \ } KE_GEN_BIN_INT(KEO_BAND, &=) KE_GEN_BIN_INT(KEO_BOR, |=) KE_GEN_BIN_INT(KEO_BXOR, ^=) KE_GEN_BIN_INT(KEO_LSH, <<=) KE_GEN_BIN_INT(KEO_RSH, >>=) KE_GEN_BIN_INT(KEO_MOD, %=) KE_GEN_BIN_INT(KEO_IDIV, /=) #define KE_GEN_BIN_BOTH(_type, _op) \ static void ke_op_##_type(ke1_t *p, ke1_t *q) { \ p->i _op q->i; p->r _op q->r; \ p->vtype = p->vtype == KEV_REAL || q->vtype == KEV_REAL? KEV_REAL : KEV_INT; \ } KE_GEN_BIN_BOTH(KEO_ADD, +=) KE_GEN_BIN_BOTH(KEO_SUB, -=) KE_GEN_BIN_BOTH(KEO_MUL, *=) static void ke_op_KEO_DIV(ke1_t *p, ke1_t *q) { p->r /= q->r, p->i = (int64_t)(p->r + .5); p->vtype = KEV_REAL; } static void ke_op_KEO_LAND(ke1_t *p, ke1_t *q) { p->i = (p->i && q->i); p->r = p->i; p->vtype = KEV_INT; } static void ke_op_KEO_LOR(ke1_t *p, ke1_t *q) { p->i = (p->i || q->i); p->r = p->i; p->vtype = KEV_INT; } static void ke_op_KEO_POW(ke1_t *p, ke1_t *q) { p->r = pow(p->r, q->r), p->i = (int64_t)(p->r + .5); p->vtype = p->vtype == KEV_REAL || q->vtype == KEV_REAL? KEV_REAL : KEV_INT; } static void ke_op_KEO_BNOT(ke1_t *p, ke1_t *q) { p->i = ~p->i; p->r = (double)p->i; p->vtype = KEV_INT; } static void ke_op_KEO_LNOT(ke1_t *p, ke1_t *q) { p->i = !p->i; p->r = (double)p->i; p->vtype = KEV_INT; } static void ke_op_KEO_POS(ke1_t *p, ke1_t *q) { } // do nothing static void ke_op_KEO_NEG(ke1_t *p, ke1_t *q) { p->i = -p->i, p->r = -p->r; } static void ke_func1_abs(ke1_t *p, ke1_t *q) { if (p->vtype == KEV_INT) p->i = abs(p->i), p->r = (double)p->i; else p->r = fabs(p->r), p->i = (int64_t)(p->r + .5); } /********** * Parser * **********/ static inline char *mystrndup(const char *src, int n) { char *dst; dst = (char*)calloc(n + 1, 1); strncpy(dst, src, n); return dst; } // parse a token except "(", ")" and "," static ke1_t ke_read_token(char *p, char **r, int *err, int last_is_val) // it doesn't parse parentheses { char *q = p; ke1_t e; memset(&e, 0, sizeof(ke1_t)); if (isalpha(*p) || *p == '_') { // a variable or a function for (; *p && (*p == '_' || isalnum(*p)); ++p); if (*p == '(') e.ttype = KET_FUNC, e.n_args = 1; else e.ttype = KET_VAL, e.vtype = KEV_REAL; e.name = mystrndup(q, p - q); e.i = 0, e.r = 0.; *r = p; } else if (isdigit(*p) || *p == '.') { // a number long x; double y; char *pp; e.ttype = KET_VAL; y = strtod(q, &p); x = strtol(q, &pp, 0); // FIXME: check int/double parsing errors if (q == p && q == pp) { // parse error *err |= KEE_NUM; } else if (p > pp) { // has "." or "[eE]"; then it is a real number e.vtype = KEV_REAL; e.i = (int64_t)(y + .5), e.r = y; *r = p; } else { e.vtype = KEV_INT; e.i = x, e.r = y; *r = pp; } } else if (*p == '"' || *p == '\'') { // a string value int c = *p; for (++p; *p && *p != c; ++p) if (*p == '\\') ++p; // escaping if (*p == c) { e.ttype = KET_VAL, e.vtype = KEV_STR; e.s = mystrndup(q + 1, p - q - 1); *r = p + 1; } else *err |= KEE_UNQU, *r = p; } else { // an operator e.ttype = KET_OP; if (*p == '*' && p[1] == '*') e.op = KEO_POW, e.f.builtin = ke_op_KEO_POW, e.n_args = 2, *r = q + 2; else if (*p == '*') e.op = KEO_MUL, e.f.builtin = ke_op_KEO_MUL, e.n_args = 2, *r = q + 1; // FIXME: NOT working for unary operators else if (*p == '/' && p[1] == '/') e.op = KEO_IDIV, e.f.builtin = ke_op_KEO_IDIV, e.n_args = 2, *r = q + 2; else if (*p == '/') e.op = KEO_DIV, e.f.builtin = ke_op_KEO_DIV, e.n_args = 2, *r = q + 1; else if (*p == '%') e.op = KEO_MOD, e.f.builtin = ke_op_KEO_MOD, e.n_args = 2, *r = q + 1; else if (*p == '+') { if (last_is_val) e.op = KEO_ADD, e.f.builtin = ke_op_KEO_ADD, e.n_args = 2; else e.op = KEO_POS, e.f.builtin = ke_op_KEO_POS, e.n_args = 1; *r = q + 1; } else if (*p == '-') { if (last_is_val) e.op = KEO_SUB, e.f.builtin = ke_op_KEO_SUB, e.n_args = 2; else e.op = KEO_NEG, e.f.builtin = ke_op_KEO_NEG, e.n_args = 1; *r = q + 1; } else if (*p == '=' && p[1] == '=') e.op = KEO_EQ, e.f.builtin = ke_op_KEO_EQ, e.n_args = 2, *r = q + 2; else if (*p == '!' && p[1] == '=') e.op = KEO_NE, e.f.builtin = ke_op_KEO_NE, e.n_args = 2, *r = q + 2; else if (*p == '<' && p[1] == '>') e.op = KEO_NE, e.f.builtin = ke_op_KEO_NE, e.n_args = 2, *r = q + 2; else if (*p == '>' && p[1] == '=') e.op = KEO_GE, e.f.builtin = ke_op_KEO_GE, e.n_args = 2, *r = q + 2; else if (*p == '<' && p[1] == '=') e.op = KEO_LE, e.f.builtin = ke_op_KEO_LE, e.n_args = 2, *r = q + 2; else if (*p == '>' && p[1] == '>') e.op = KEO_RSH, e.f.builtin = ke_op_KEO_RSH, e.n_args = 2, *r = q + 2; else if (*p == '<' && p[1] == '<') e.op = KEO_LSH, e.f.builtin = ke_op_KEO_LSH, e.n_args = 2, *r = q + 2; else if (*p == '>') e.op = KEO_GT, e.f.builtin = ke_op_KEO_GT, e.n_args = 2, *r = q + 1; else if (*p == '<') e.op = KEO_LT, e.f.builtin = ke_op_KEO_LT, e.n_args = 2, *r = q + 1; else if (*p == '|' && p[1] == '|') e.op = KEO_LOR, e.f.builtin = ke_op_KEO_LOR, e.n_args = 2, *r = q + 2; else if (*p == '&' && p[1] == '&') e.op = KEO_LAND, e.f.builtin = ke_op_KEO_LAND, e.n_args = 2, *r = q + 2; else if (*p == '|') e.op = KEO_BOR, e.f.builtin = ke_op_KEO_BOR, e.n_args = 2, *r = q + 1; else if (*p == '&') e.op = KEO_BAND, e.f.builtin = ke_op_KEO_BAND, e.n_args = 2, *r = q + 1; else if (*p == '^') e.op = KEO_BXOR, e.f.builtin = ke_op_KEO_BXOR, e.n_args = 2, *r = q + 1; else if (*p == '~') e.op = KEO_BNOT, e.f.builtin = ke_op_KEO_BNOT, e.n_args = 1, *r = q + 1; else if (*p == '!') e.op = KEO_LNOT, e.f.builtin = ke_op_KEO_LNOT, e.n_args = 1, *r = q + 1; else e.ttype = KET_NULL, *err |= KEE_UNOP; } return e; } static inline ke1_t *push_back(ke1_t **a, int *n, int *m) { if (*n == *m) { int old_m = *m; *m = *m? *m<<1 : 8; *a = (ke1_t*)realloc(*a, *m * sizeof(ke1_t)); memset(*a + old_m, 0, (*m - old_m) * sizeof(ke1_t)); } return &(*a)[(*n)++]; } static ke1_t *ke_parse_core(const char *_s, int *_n, int *err) { char *s, *p, *q; int n_out, m_out, n_op, m_op, last_is_val = 0; ke1_t *out, *op, *t, *u; *err = 0; *_n = 0; s = strdup(_s); // make a copy for (p = q = s; *p; ++p) // squeeze out spaces if (!isspace(*p)) *q++ = *p; *q++ = 0; out = op = 0; n_out = m_out = n_op = m_op = 0; p = s; while (*p) { if (*p == '(') { t = push_back(&op, &n_op, &m_op); // push to the operator stack t->op = -1, t->ttype = KET_NULL; // ->op < 0 for a left parenthsis ++p; } else if (*p == ')') { while (n_op > 0 && op[n_op-1].op >= 0) { // move operators to the output until we see a left parenthesis u = push_back(&out, &n_out, &m_out); *u = op[--n_op]; } if (n_op == 0) { // error: extra right parenthesis *err |= KEE_UNRP; break; } else --n_op; // pop out '(' if (n_op > 0 && op[n_op-1].ttype == KET_FUNC) { // the top of the operator stack is a function u = push_back(&out, &n_out, &m_out); // move it to the output *u = op[--n_op]; if (u->n_args == 1 && strcmp(u->name, "abs") == 0) u->f.builtin = ke_func1_abs; } ++p; } else if (*p == ',') { // function arguments separator while (n_op > 0 && op[n_op-1].op >= 0) { u = push_back(&out, &n_out, &m_out); *u = op[--n_op]; } if (n_op < 2 || op[n_op-2].ttype != KET_FUNC) { // we should at least see a function and a left parenthesis *err |= KEE_FUNC; break; } ++op[n_op-2].n_args; ++p; } else { // output-able token ke1_t v; v = ke_read_token(p, &p, err, last_is_val); if (*err) break; if (v.ttype == KET_VAL) { u = push_back(&out, &n_out, &m_out); *u = v; last_is_val = 1; } else if (v.ttype == KET_FUNC) { t = push_back(&op, &n_op, &m_op); *t = v; last_is_val = 0; } else if (v.ttype == KET_OP) { int oi = ke_op[v.op]; while (n_op > 0 && op[n_op-1].ttype == KET_OP) { int pre = ke_op[op[n_op-1].op]>>1; if (((oi&1) && oi>>1 <= pre) || (!(oi&1) && oi>>1 < pre)) break; u = push_back(&out, &n_out, &m_out); *u = op[--n_op]; } t = push_back(&op, &n_op, &m_op); *t = v; last_is_val = 0; } } } if (*err == 0) { while (n_op > 0 && op[n_op-1].op >= 0) { u = push_back(&out, &n_out, &m_out); *u = op[--n_op]; } if (n_op > 0) *err |= KEE_UNLP; } if (*err == 0) { // then check if the number of args is correct int i, n; for (i = n = 0; i < n_out; ++i) { ke1_t *e = &out[i]; if (e->ttype == KET_VAL) ++n; else n -= e->n_args - 1; } if (n != 1) *err |= KEE_ARG; } free(op); free(s); if (*err) { free(out); return 0; } *_n = n_out; return out; } kexpr_t *ke_parse(const char *_s, int *err) { int n; ke1_t *e; kexpr_t *ke; e = ke_parse_core(_s, &n, err); if (*err) return 0; ke = (kexpr_t*)calloc(1, sizeof(kexpr_t)); ke->n = n, ke->e = e; return ke; } int ke_eval(const kexpr_t *ke, int64_t *_i, double *_r, const char **_p, int *ret_type) { ke1_t *stack, *p, *q; int i, top = 0, err = 0; *_i = 0, *_r = 0., *ret_type = 0; for (i = 0; i < ke->n; ++i) { ke1_t *e = &ke->e[i]; if ((e->ttype == KET_OP || e->ttype == KET_FUNC) && e->f.builtin == 0) err |= KEE_UNFUNC; else if (e->ttype == KET_VAL && e->name && e->assigned == 0) err |= KEE_UNVAR; } stack = (ke1_t*)malloc(ke->n * sizeof(ke1_t)); for (i = 0; i < ke->n; ++i) { ke1_t *e = &ke->e[i]; if (e->ttype == KET_OP || e->ttype == KET_FUNC) { if (e->n_args == 2 && e->f.builtin) { q = &stack[--top], p = &stack[top-1]; if (e->user_func) { if (e->user_func == KEF_REAL) p->r = e->f.real_func2(p->r, q->r), p->i = (int64_t)(p->r + .5), p->vtype = KEV_REAL; } else e->f.builtin(p, q); } else if (e->n_args == 1 && e->f.builtin) { p = &stack[top-1]; if (e->user_func) { if (e->user_func == KEF_REAL) p->r = e->f.real_func1(p->r), p->i = (int64_t)(p->r + .5), p->vtype = KEV_REAL; } else e->f.builtin(&stack[top-1], 0); } else top -= e->n_args - 1; } else stack[top++] = *e; } *ret_type = stack->vtype; *_i = stack->i, *_r = stack->r, *_p = stack->s; free(stack); return err; } int64_t ke_eval_int(const kexpr_t *ke, int *err) { int int_ret; int64_t i; double r; const char *s; *err = ke_eval(ke, &i, &r, &s, &int_ret); return i; } double ke_eval_real(const kexpr_t *ke, int *err) { int int_ret; int64_t i; double r; const char *s; *err = ke_eval(ke, &i, &r, &s, &int_ret); return r; } void ke_destroy(kexpr_t *ke) { int i; if (ke == 0) return; for (i = 0; i < ke->n; ++i) { free(ke->e[i].name); free(ke->e[i].s); } free(ke->e); free(ke); } int ke_set_int(kexpr_t *ke, const char *var, int64_t y) { int i, n = 0; double yy = (double)y; for (i = 0; i < ke->n; ++i) { ke1_t *e = &ke->e[i]; if (e->ttype == KET_VAL && e->name && strcmp(e->name, var) == 0) e->i = y, e->r = yy, e->vtype = KEV_INT, e->assigned = 1, ++n; } return n; } int ke_set_real(kexpr_t *ke, const char *var, double x) { int i, n = 0; int64_t xx = (int64_t)(x + .5); for (i = 0; i < ke->n; ++i) { ke1_t *e = &ke->e[i]; if (e->ttype == KET_VAL && e->name && strcmp(e->name, var) == 0) e->r = x, e->i = xx, e->vtype = KEV_REAL, e->assigned = 1, ++n; } return n; } int ke_set_str(kexpr_t *ke, const char *var, const char *x) { int i, n = 0; for (i = 0; i < ke->n; ++i) { ke1_t *e = &ke->e[i]; if (e->ttype == KET_VAL && e->name && strcmp(e->name, var) == 0) { if (e->vtype == KEV_STR) free(e->s); e->s = strdup(x); e->i = 0, e->r = 0., e->assigned = 1; e->vtype = KEV_STR; ++n; } } return n; } int ke_set_real_func1(kexpr_t *ke, const char *name, double (*func)(double)) { int i, n = 0; for (i = 0; i < ke->n; ++i) { ke1_t *e = &ke->e[i]; if (e->ttype == KET_FUNC && e->n_args == 1 && strcmp(e->name, name) == 0) e->f.real_func1 = func, e->user_func = KEF_REAL, ++n; } return n; } int ke_set_real_func2(kexpr_t *ke, const char *name, double (*func)(double, double)) { int i, n = 0; for (i = 0; i < ke->n; ++i) { ke1_t *e = &ke->e[i]; if (e->ttype == KET_FUNC && e->n_args == 2 && strcmp(e->name, name) == 0) e->f.real_func2 = func, e->user_func = KEF_REAL, ++n; } return n; } int ke_set_default_func(kexpr_t *ke) { int n = 0; n += ke_set_real_func1(ke, "exp", exp); n += ke_set_real_func1(ke, "log", log); n += ke_set_real_func1(ke, "log10", log10); n += ke_set_real_func1(ke, "sqrt", sqrt); n += ke_set_real_func1(ke, "sin", sin); n += ke_set_real_func1(ke, "cos", cos); n += ke_set_real_func1(ke, "tan", tan); n += ke_set_real_func2(ke, "pow", pow); return n; } void ke_unset(kexpr_t *ke) { int i; for (i = 0; i < ke->n; ++i) { ke1_t *e = &ke->e[i]; if (e->ttype == KET_VAL && e->name) e->assigned = 0; } } void ke_print(const kexpr_t *ke) { int i; if (ke == 0) return; for (i = 0; i < ke->n; ++i) { const ke1_t *u = &ke->e[i]; if (i) putchar(' '); if (u->ttype == KET_VAL) { if (u->name) printf("%s", u->name); else if (u->vtype == KEV_REAL) printf("%g", u->r); else if (u->vtype == KEV_INT) printf("%lld", (long long)u->i); else if (u->vtype == KEV_STR) printf("\"%s\"", u->s); } else if (u->ttype == KET_OP) { printf("%s", ke_opstr[u->op]); } else if (u->ttype == KET_FUNC) { printf("%s(%d)", u->name, u->n_args); } } putchar('\n'); } kexpr-nim-0.0.2/kexpr/kexpr-c.h000066400000000000000000000037251340357014600163100ustar00rootroot00000000000000#ifndef KEXPR_H #define KEXPR_H #include struct kexpr_s; typedef struct kexpr_s kexpr_t; // Parse errors #define KEE_UNQU 0x01 // unmatched quotation marks #define KEE_UNLP 0x02 // unmatched left parentheses #define KEE_UNRP 0x04 // unmatched right parentheses #define KEE_UNOP 0x08 // unknown operators #define KEE_FUNC 0x10 // wrong function syntax #define KEE_ARG 0x20 #define KEE_NUM 0x40 // fail to parse a number // Evaluation errors #define KEE_UNFUNC 0x40 // undefined function #define KEE_UNVAR 0x80 // unassigned variable // Return type #define KEV_REAL 1 #define KEV_INT 2 #define KEV_STR 3 #ifdef __cplusplus extern "C" { #endif // parse an expression and return errors in $err kexpr_t *ke_parse(const char *_s, int *err); // free memory allocated during parsing void ke_destroy(kexpr_t *ke); // set a variable to integer value and return the occurrence of the variable int ke_set_int(kexpr_t *ke, const char *var, int64_t x); // set a variable to real value and return the occurrence of the variable int ke_set_real(kexpr_t *ke, const char *var, double x); // set a variable to string value and return the occurrence of the variable int ke_set_str(kexpr_t *ke, const char *var, const char *x); // set a user-defined function int ke_set_real_func1(kexpr_t *ke, const char *name, double (*func)(double)); int ke_set_real_func2(kexpr_t *ke, const char *name, double (*func)(double, double)); // set default math functions int ke_set_default_func(kexpr_t *ke); // mark all variable as unset void ke_unset(kexpr_t *e); // evaluate expression; return error code; final value is returned via pointers int ke_eval(const kexpr_t *ke, int64_t *_i, double *_r, const char **_s, int *ret_type); int64_t ke_eval_int(const kexpr_t *ke, int *err); double ke_eval_real(const kexpr_t *ke, int *err); // print the expression in Reverse Polish notation (RPN) void ke_print(const kexpr_t *ke); #ifdef __cplusplus } #endif #endif kexpr-nim-0.0.2/tests.nim000066400000000000000000000025551340357014600153040ustar00rootroot00000000000000import ./kexpr, unittest, math # these are taken from: https://github.com/Yardanico/nim-mathexpr # which is a great library to use if you need pure nim. const TestCases = [ ("1", 1.0), ("1 ", 1.0), ("2+1", 2+1.0), ("(((2+(1))))", 2 + 1.0), ("3+2", 3+2.0), ("3+2+4", 3+2+4.0), ("(3+2)+4", 3+2+4.0), ("3+(2+4)", 3+2+4.0), ("(3+2+4)", 3+2+4.0), ("3*2*4", 3*2*4.0), ("(3*2)*4", 3*2*4.0), ("3*(2*4)", 3*2*4.0), ("(3*2*4)", 3*2*4.0), ("3-2-4", 3-2-4.0), ("(3-2)-4", (3-2)-4.0), ("3-(2-4)", 3-(2-4.0)), ("(3-2-4)", 3-2-4.0), ("(3.0*2.0/4.0)", 3.0*2.0/4.0), ("log10(1000)", 3.0), ("10 > 20", 0.0), ("(10 > 20)", 0.0), ("20 > 10", 1.0) ] proc `~=`(a, b: float): bool = ## Checks if difference between two floats is less than 0.0001 abs(a - b) < 1e-2 suite "Eval test cases": for data in TestCases: let (expr, expected) = data var e = expression(expr) echo ke_set_default_func(e.ke) check e.error() == 0 echo expr, " got:", e.float, " expected:", expected, " ", e.float ~= expected check e.float() ~= expected e.clear() var e = expression("(2 > 1)") check e.bool() e = expression("0.4") check e.bool() e = expression("0.0") check (not e.bool()) test "expression strings": e = expression("sval > aa22") e["sval"] = "asdf" #echo e.float