The purpose of this code is to parse any expression as could appear in C that involves doubles or functions that involve only doubles.
I have written parsers before:
This is an upgraded version that uses more descriptive variable names and structures instead of parallel arrays. This version can also handle functions of varying arity. I was even planning on doing short string optimization here but had to skip that because reasons.
#include <math.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
typedef struct Symbol {
    unsigned arity, len;
    const char *name;
    union {
        double
            func0,
            (*func1)(double),
            (*func2)(double, double),
            (*func3)(double, double, double);
    };
} Symbol;
static int compar(const void *const restrict strp, const void *const restrict p) {
    const unsigned len = ((const Symbol *)p)->len;
    const char
        *const str = *(const char *const *)strp,
        *const name = ((const Symbol *)p)->name;
    const int cmp = memcmp(str, name, len);
    return cmp
        ? cmp
        : isalnum((unsigned char)str[len]) || str[len] == '_'
            ? str[len]-name[len]
            : (*(const char **)strp += len, 0);
}
#define SYMBOL(a, n)\
{\
    .arity = a,\
    .len = sizeof(#n)-1,\
    .name = #n,\
    .func##a = n\
}
static const Symbol symbols[] = {
    SYMBOL(0, M_1_PI),
    SYMBOL(0, M_2_PI),
    SYMBOL(0, M_2_SQRTPI),
    SYMBOL(0, M_E),
    SYMBOL(0, M_LN10),
    SYMBOL(0, M_LN2),
    SYMBOL(0, M_LOG10E),
    SYMBOL(0, M_LOG2E),
    SYMBOL(0, M_PI),
    SYMBOL(0, M_PI_2),
    SYMBOL(0, M_PI_4),
    SYMBOL(0, M_SQRT1_2),
    SYMBOL(0, M_SQRT2),
    SYMBOL(1, acos),
    SYMBOL(1, acosh),
    SYMBOL(1, asin),
    SYMBOL(1, asinh),
    SYMBOL(1, atan),
    SYMBOL(2, atan2),
    SYMBOL(1, atanh),
    SYMBOL(1, cbrt),
    SYMBOL(1, ceil),
    SYMBOL(1, cos),
    SYMBOL(1, cosh),
    SYMBOL(2, copysign),
    SYMBOL(1, erf),
    SYMBOL(1, erfc),
    SYMBOL(1, exp),
    SYMBOL(1, exp2),
    SYMBOL(1, expm1),
    SYMBOL(1, fabs),
    SYMBOL(2, fdim),
    SYMBOL(1, floor),
    SYMBOL(3, fma),
    SYMBOL(2, fmax),
    SYMBOL(2, fmin),
    SYMBOL(2, fmod),
    SYMBOL(2, hypot),
    SYMBOL(1, lgamma),
    SYMBOL(1, log),
    SYMBOL(1, log10),
    SYMBOL(1, log1p),
    SYMBOL(1, log2),
    SYMBOL(1, logb),
    SYMBOL(2, nextafter),
    SYMBOL(2, pow),
    SYMBOL(2, remainder),
    SYMBOL(1, round),
    SYMBOL(1, sin),
    SYMBOL(1, sinh),
    SYMBOL(1, sqrt),
    SYMBOL(1, tan),
    SYMBOL(1, tanh),
    SYMBOL(1, tgamma),
    SYMBOL(1, trunc)
};
static double term(const char **);
static double expr(const char **const str, const unsigned level) {
    *str += level != 0;
    for (double val = term(str);;) {
        while (isspace((unsigned char)**str))
            ++*str;
        switch (**str) {
            case '*':
                if (level > 2)
                    break;
                val *= expr(str, 2);
                continue;
            case '/':
                if (level > 2)
                    break;
                val /= expr(str, 2);
                continue;
            case '+':
                if (level > 1)
                    break;
                val += expr(str, 1);
                continue;
            case '-':
                if (level > 1)
                    break;
                val -= expr(str, 1);
                continue;
        }
        return val;
    }
}
static double term(const char **const str) {
    while (isspace((unsigned char)**str))
        ++*str;
    {
        char *end;
        const double val = strtod(*str, &end);
        if (*str != end) {
            *str = end;
            return val;
        }
    }
    if (isalpha((unsigned char)**str)) {
        const Symbol *const symbol = bsearch(str, symbols, sizeof(symbols)/sizeof(Symbol), sizeof(Symbol), compar);
        if (symbol) {
            if (!symbol->arity)
                return symbol->func0;
            while (isspace((unsigned char)**str))
                ++*str;
            if (*(*str)++ == '(') {
                double vals[symbol->arity];
                for (unsigned i = 0;;) {
                    vals[i] = expr(str, 0);
                    if (++i >= symbol->arity) {
                        if (*(*str)++ == ')') switch (symbol->arity) {
                            case 1:
                                return symbol->func1(vals[0]);
                            case 2:
                                return symbol->func2(vals[0], vals[1]);
                            case 3:
                                return symbol->func3(vals[0], vals[1], vals[2]);
                        }
                    } else if (*(*str)++ == ',')
                        continue;
                    break;
                }
            }
        }
    } else switch (*(*str)++) {
        case '+':
            return term(str);
        case '-':
            return -term(str);
        case '(':
        {
            const double val = expr(str, 0);
            if (*(*str)++ == ')')
                return val;
        }
    }
    return NAN;
}
#include <stdio.h>
int main(int argc, char **argv) {
    for (int arg = 1; arg < argc; ++arg)
        if (printf("%g\n", expr(&(const char *){argv[arg]}, 0)) < 0)
            return EXIT_FAILURE;
    return EXIT_SUCCESS;
}
