|
@@ -0,0 +1,1393 @@
|
|
|
+/**
|
|
|
+ * The MIT License (MIT)
|
|
|
+ *
|
|
|
+ * Copyright (c) 2016 Serge Zaitsev
|
|
|
+ * Copyright (c) 2022 Daniel-Constantin Mierla
|
|
|
+ *
|
|
|
+ * 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.
|
|
|
+ */
|
|
|
+
|
|
|
+#ifndef _SNEXPR_H_
|
|
|
+#define _SNEXPR_H_
|
|
|
+
|
|
|
+#ifdef __cplusplus
|
|
|
+extern "C" {
|
|
|
+#endif
|
|
|
+
|
|
|
+#include <ctype.h> /* for isspace */
|
|
|
+#include <limits.h>
|
|
|
+#include <math.h> /* for pow */
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+#define SNEXPR_TOP (1 << 0)
|
|
|
+#define SNEXPR_TOPEN (1 << 1)
|
|
|
+#define SNEXPR_TCLOSE (1 << 2)
|
|
|
+#define SNEXPR_TNUMBER (1 << 3)
|
|
|
+#define SNEXPR_TSTRING (1 << 4)
|
|
|
+#define SNEXPR_TWORD (1 << 5)
|
|
|
+#define SNEXPR_TDEFAULT (SNEXPR_TOPEN | SNEXPR_TNUMBER | SNEXPR_TSTRING | SNEXPR_TWORD)
|
|
|
+
|
|
|
+#define SNEXPR_UNARY (1 << 16)
|
|
|
+#define SNEXPR_COMMA (1 << 17)
|
|
|
+#define SNEXPR_EXPALLOC (1 << 18)
|
|
|
+#define SNEXPR_VALALLOC (1 << 19)
|
|
|
+#define SNEXPR_VALASSIGN (1 << 20)
|
|
|
+
|
|
|
+
|
|
|
+/*
|
|
|
+ * Simple expandable vector implementation
|
|
|
+ */
|
|
|
+static int sne_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);
|
|
|
+ if(ptr == NULL) {
|
|
|
+ return -1; /* allocation failed */
|
|
|
+ }
|
|
|
+ *buf = (char *)ptr;
|
|
|
+ *cap = n;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#define sne_vec(T) \
|
|
|
+ struct \
|
|
|
+ { \
|
|
|
+ T *buf; \
|
|
|
+ int len; \
|
|
|
+ int cap; \
|
|
|
+ }
|
|
|
+#define sne_vec_init() \
|
|
|
+ { \
|
|
|
+ NULL, 0, 0 \
|
|
|
+ }
|
|
|
+#define sne_vec_len(v) ((v)->len)
|
|
|
+#define sne_vec_unpack(v) \
|
|
|
+ (char **)&(v)->buf, &(v)->len, &(v)->cap, sizeof(*(v)->buf)
|
|
|
+#define sne_vec_push(v, val) \
|
|
|
+ sne_vec_expand(sne_vec_unpack(v)) ? -1 : ((v)->buf[(v)->len++] = (val), 0)
|
|
|
+#define sne_vec_nth(v, i) (v)->buf[i]
|
|
|
+#define sne_vec_peek(v) (v)->buf[(v)->len - 1]
|
|
|
+#define sne_vec_pop(v) (v)->buf[--(v)->len]
|
|
|
+#define sne_vec_free(v) (free((v)->buf), (v)->buf = NULL, (v)->len = (v)->cap = 0)
|
|
|
+#define sne_vec_foreach(v, var, iter) \
|
|
|
+ if((v)->len > 0) \
|
|
|
+ for((iter) = 0; (iter) < (v)->len && (((var) = (v)->buf[(iter)]), 1); \
|
|
|
+ ++(iter))
|
|
|
+
|
|
|
+/*
|
|
|
+ * Expression data types
|
|
|
+ */
|
|
|
+struct snexpr;
|
|
|
+struct snexpr_func;
|
|
|
+struct snexpr_var;
|
|
|
+
|
|
|
+enum snexpr_type
|
|
|
+{
|
|
|
+ SNE_OP_UNKNOWN,
|
|
|
+ SNE_OP_UNARY_MINUS,
|
|
|
+ SNE_OP_UNARY_LOGICAL_NOT,
|
|
|
+ SNE_OP_UNARY_BITWISE_NOT,
|
|
|
+
|
|
|
+ SNE_OP_POWER,
|
|
|
+ SNE_OP_DIVIDE,
|
|
|
+ SNE_OP_MULTIPLY,
|
|
|
+ SNE_OP_REMAINDER,
|
|
|
+
|
|
|
+ SNE_OP_PLUS,
|
|
|
+ SNE_OP_MINUS,
|
|
|
+
|
|
|
+ SNE_OP_SHL,
|
|
|
+ SNE_OP_SHR,
|
|
|
+
|
|
|
+ SNE_OP_LT,
|
|
|
+ SNE_OP_LE,
|
|
|
+ SNE_OP_GT,
|
|
|
+ SNE_OP_GE,
|
|
|
+ SNE_OP_EQ,
|
|
|
+ SNE_OP_NE,
|
|
|
+
|
|
|
+ SNE_OP_BITWISE_AND,
|
|
|
+ SNE_OP_BITWISE_OR,
|
|
|
+ SNE_OP_BITWISE_XOR,
|
|
|
+
|
|
|
+ SNE_OP_LOGICAL_AND,
|
|
|
+ SNE_OP_LOGICAL_OR,
|
|
|
+
|
|
|
+ SNE_OP_ASSIGN,
|
|
|
+ SNE_OP_COMMA,
|
|
|
+
|
|
|
+ SNE_OP_CONSTNUM,
|
|
|
+ SNE_OP_CONSTSTZ,
|
|
|
+ SNE_OP_VAR,
|
|
|
+ SNE_OP_FUNC,
|
|
|
+};
|
|
|
+
|
|
|
+static int prec[] = {0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 5, 5, 5, 5, 5, 6, 7,
|
|
|
+ 8, 9, 10, 11, 12, 0, 0, 0, 0};
|
|
|
+
|
|
|
+typedef sne_vec(struct snexpr) sne_vec_expr_t;
|
|
|
+typedef void (*snexprfn_cleanup_t)(struct snexpr_func *f, void *context);
|
|
|
+typedef struct snexpr* (*snexprfn_t)(struct snexpr_func *f, sne_vec_expr_t *args, void *context);
|
|
|
+
|
|
|
+typedef struct snexpr* (*snexternval_cbf_t)(char *vname);
|
|
|
+
|
|
|
+static snexternval_cbf_t _snexternval_cbf = NULL;
|
|
|
+
|
|
|
+struct snexpr
|
|
|
+{
|
|
|
+ enum snexpr_type type;
|
|
|
+ unsigned int eflags;
|
|
|
+ union
|
|
|
+ {
|
|
|
+ struct
|
|
|
+ {
|
|
|
+ float nval;
|
|
|
+ } num;
|
|
|
+ struct
|
|
|
+ {
|
|
|
+ char *sval;
|
|
|
+ } stz;
|
|
|
+ struct
|
|
|
+ {
|
|
|
+ struct snexpr_var *vref;
|
|
|
+ } var;
|
|
|
+ struct
|
|
|
+ {
|
|
|
+ sne_vec_expr_t args;
|
|
|
+ } op;
|
|
|
+ struct
|
|
|
+ {
|
|
|
+ struct snexpr_func *f;
|
|
|
+ sne_vec_expr_t args;
|
|
|
+ void *context;
|
|
|
+ } func;
|
|
|
+ } param;
|
|
|
+};
|
|
|
+
|
|
|
+#define snexpr_init() \
|
|
|
+ { \
|
|
|
+ .type = (enum snexpr_type)0, \
|
|
|
+ .eflags = 0u \
|
|
|
+ }
|
|
|
+
|
|
|
+struct snexpr_string
|
|
|
+{
|
|
|
+ const char *s;
|
|
|
+ int n;
|
|
|
+};
|
|
|
+struct snexpr_arg
|
|
|
+{
|
|
|
+ int oslen;
|
|
|
+ int eslen;
|
|
|
+ sne_vec_expr_t args;
|
|
|
+};
|
|
|
+
|
|
|
+typedef sne_vec(struct snexpr_string) sne_vec_str_t;
|
|
|
+typedef sne_vec(struct snexpr_arg) sne_vec_arg_t;
|
|
|
+
|
|
|
+static int snexpr_is_unary(enum snexpr_type op)
|
|
|
+{
|
|
|
+ return op == SNE_OP_UNARY_MINUS || op == SNE_OP_UNARY_LOGICAL_NOT
|
|
|
+ || op == SNE_OP_UNARY_BITWISE_NOT;
|
|
|
+}
|
|
|
+
|
|
|
+static int snexpr_is_binary(enum snexpr_type op)
|
|
|
+{
|
|
|
+ return !snexpr_is_unary(op) && op != SNE_OP_CONSTNUM && op != SNE_OP_CONSTSTZ
|
|
|
+ && op != SNE_OP_VAR && op != SNE_OP_FUNC && op != SNE_OP_UNKNOWN;
|
|
|
+}
|
|
|
+
|
|
|
+static int snexpr_prec(enum snexpr_type a, enum snexpr_type b)
|
|
|
+{
|
|
|
+ int left = snexpr_is_binary(a) && a != SNE_OP_ASSIGN && a != SNE_OP_POWER
|
|
|
+ && a != SNE_OP_COMMA;
|
|
|
+ return (left && prec[a] >= prec[b]) || (prec[a] > prec[b]);
|
|
|
+}
|
|
|
+
|
|
|
+#define isfirstvarchr(c) \
|
|
|
+ (((unsigned char)c >= '@' && c != '^' && c != '|') || c == '$')
|
|
|
+#define isvarchr(c) \
|
|
|
+ (((unsigned char)c >= '@' && c != '^' && c != '|') || c == '$' || c == '#' \
|
|
|
+ || (c >= '0' && c <= '9'))
|
|
|
+
|
|
|
+static struct
|
|
|
+{
|
|
|
+ const char *s;
|
|
|
+ const enum snexpr_type op;
|
|
|
+} OPS[] = {
|
|
|
+ {"-u", SNE_OP_UNARY_MINUS},
|
|
|
+ {"!u", SNE_OP_UNARY_LOGICAL_NOT},
|
|
|
+ {"^u", SNE_OP_UNARY_BITWISE_NOT},
|
|
|
+ {"**", SNE_OP_POWER},
|
|
|
+ {"*", SNE_OP_MULTIPLY},
|
|
|
+ {"/", SNE_OP_DIVIDE},
|
|
|
+ {"%", SNE_OP_REMAINDER},
|
|
|
+ {"+", SNE_OP_PLUS},
|
|
|
+ {"-", SNE_OP_MINUS},
|
|
|
+ {"<<", SNE_OP_SHL},
|
|
|
+ {">>", SNE_OP_SHR},
|
|
|
+ {"<", SNE_OP_LT},
|
|
|
+ {"<=", SNE_OP_LE},
|
|
|
+ {">", SNE_OP_GT},
|
|
|
+ {">=", SNE_OP_GE},
|
|
|
+ {"==", SNE_OP_EQ},
|
|
|
+ {"!=", SNE_OP_NE},
|
|
|
+ {"&", SNE_OP_BITWISE_AND},
|
|
|
+ {"|", SNE_OP_BITWISE_OR},
|
|
|
+ {"^", SNE_OP_BITWISE_XOR},
|
|
|
+ {"&&", SNE_OP_LOGICAL_AND},
|
|
|
+ {"||", SNE_OP_LOGICAL_OR},
|
|
|
+ {"=", SNE_OP_ASSIGN},
|
|
|
+ {",", SNE_OP_COMMA},
|
|
|
+
|
|
|
+ /* These are used by lexer and must be ignored by parser, so we put
|
|
|
+ them at the end */
|
|
|
+ {"-", SNE_OP_UNARY_MINUS},
|
|
|
+ {"!", SNE_OP_UNARY_LOGICAL_NOT},
|
|
|
+ {"^", SNE_OP_UNARY_BITWISE_NOT},
|
|
|
+};
|
|
|
+
|
|
|
+static enum snexpr_type snexpr_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
|
|
|
+ && (unary == -1 || snexpr_is_unary(OPS[i].op) == unary)) {
|
|
|
+ return OPS[i].op;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return SNE_OP_UNKNOWN;
|
|
|
+}
|
|
|
+
|
|
|
+static float snexpr_parse_number(const char *s, size_t len)
|
|
|
+{
|
|
|
+ float num = 0;
|
|
|
+ unsigned int frac = 0;
|
|
|
+ unsigned int digits = 0;
|
|
|
+ for(unsigned int i = 0; i < len; i++) {
|
|
|
+ if(s[i] == '.' && frac == 0) {
|
|
|
+ frac++;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if(isdigit(s[i])) {
|
|
|
+ digits++;
|
|
|
+ if(frac > 0) {
|
|
|
+ frac++;
|
|
|
+ }
|
|
|
+ num = num * 10 + (s[i] - '0');
|
|
|
+ } else {
|
|
|
+ return NAN;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ while(frac > 1) {
|
|
|
+ num = num / 10;
|
|
|
+ frac--;
|
|
|
+ }
|
|
|
+ return (digits > 0 ? num : NAN);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Functions
|
|
|
+ */
|
|
|
+struct snexpr_func
|
|
|
+{
|
|
|
+ const char *name;
|
|
|
+ snexprfn_t f;
|
|
|
+ snexprfn_cleanup_t cleanup;
|
|
|
+ size_t ctxsz;
|
|
|
+};
|
|
|
+
|
|
|
+static struct snexpr_func *snexpr_func_find(
|
|
|
+ struct snexpr_func *funcs, const char *s, size_t len)
|
|
|
+{
|
|
|
+ for(struct snexpr_func *f = funcs; f->name; f++) {
|
|
|
+ if(strlen(f->name) == len && strncmp(f->name, s, len) == 0) {
|
|
|
+ return f;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Variables
|
|
|
+ */
|
|
|
+struct snexpr_var
|
|
|
+{
|
|
|
+ unsigned int evflags;
|
|
|
+ char *name;
|
|
|
+ union
|
|
|
+ {
|
|
|
+ float nval;
|
|
|
+ char *sval;
|
|
|
+ } v;
|
|
|
+ struct snexpr_var *next;
|
|
|
+};
|
|
|
+
|
|
|
+struct snexpr_var_list
|
|
|
+{
|
|
|
+ struct snexpr_var *head;
|
|
|
+};
|
|
|
+
|
|
|
+static struct snexpr_var *snexpr_var_find(
|
|
|
+ struct snexpr_var_list *vars, const char *s, size_t len)
|
|
|
+{
|
|
|
+ struct snexpr_var *v = NULL;
|
|
|
+ if(len == 0 || !isfirstvarchr(*s)) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ for(v = vars->head; v; v = v->next) {
|
|
|
+ if(strlen(v->name) == len && strncmp(v->name, s, len) == 0) {
|
|
|
+ return v;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ v = (struct snexpr_var *)calloc(1, sizeof(struct snexpr_var) + len + 1);
|
|
|
+ if(v == NULL) {
|
|
|
+ return NULL; /* allocation failed */
|
|
|
+ }
|
|
|
+ memset(v, 0, sizeof(struct snexpr_var) + len + 1);
|
|
|
+ v->next = vars->head;
|
|
|
+ v->name = (char *)v + sizeof(struct snexpr_var);
|
|
|
+ strncpy(v->name, s, len);
|
|
|
+ v->name[len] = '\0';
|
|
|
+ vars->head = v;
|
|
|
+ return v;
|
|
|
+}
|
|
|
+
|
|
|
+static int to_int(float x)
|
|
|
+{
|
|
|
+ if(isnan(x)) {
|
|
|
+ return 0;
|
|
|
+ } else if(isinf(x) != 0) {
|
|
|
+ return INT_MAX * isinf(x);
|
|
|
+ } else {
|
|
|
+ return (int)x;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static struct snexpr *snexpr_convert_num(float value, unsigned int ctype)
|
|
|
+{
|
|
|
+ struct snexpr *e = (struct snexpr *)malloc(sizeof(struct snexpr));
|
|
|
+ if(e == NULL) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ memset(e, 0, sizeof(struct snexpr));
|
|
|
+
|
|
|
+ if(ctype == SNE_OP_CONSTSTZ) {
|
|
|
+ e->eflags |= SNEXPR_EXPALLOC | SNEXPR_VALALLOC;
|
|
|
+ e->type = SNE_OP_CONSTSTZ;
|
|
|
+ asprintf(&e->param.stz.sval, "%g", value);
|
|
|
+ return e;
|
|
|
+ }
|
|
|
+
|
|
|
+ e->eflags |= SNEXPR_EXPALLOC;
|
|
|
+ e->type = SNE_OP_CONSTNUM;
|
|
|
+ e->param.num.nval = value;
|
|
|
+ return e;
|
|
|
+}
|
|
|
+
|
|
|
+static struct snexpr *snexpr_convert_stzl(char *value, size_t len, unsigned int ctype)
|
|
|
+{
|
|
|
+ struct snexpr *e = NULL;
|
|
|
+ if(value==NULL) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ e = (struct snexpr *)malloc(sizeof(struct snexpr));
|
|
|
+ if(e == NULL) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ memset(e, 0, sizeof(struct snexpr));
|
|
|
+
|
|
|
+ if(ctype == SNE_OP_CONSTNUM) {
|
|
|
+ e->eflags |= SNEXPR_EXPALLOC;
|
|
|
+ e->type = SNE_OP_CONSTNUM;
|
|
|
+ e->param.num.nval = snexpr_parse_number(value, len);
|
|
|
+ return e;
|
|
|
+ }
|
|
|
+
|
|
|
+ e->param.stz.sval = (char *)malloc(len + 1);
|
|
|
+ if(e->param.stz.sval == NULL) {
|
|
|
+ free(e);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ e->eflags |= SNEXPR_EXPALLOC | SNEXPR_VALALLOC;
|
|
|
+ e->type = SNE_OP_CONSTSTZ;
|
|
|
+ memcpy(e->param.stz.sval, value, len);
|
|
|
+ e->param.stz.sval[len] = '\0';
|
|
|
+ return e;
|
|
|
+}
|
|
|
+
|
|
|
+static struct snexpr *snexpr_convert_stz(char *value, unsigned int ctype)
|
|
|
+{
|
|
|
+ if(value==NULL) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return snexpr_convert_stzl(value, strlen(value), ctype);
|
|
|
+}
|
|
|
+
|
|
|
+static struct snexpr *snexpr_concat_strz(char *value0, char *value1)
|
|
|
+{
|
|
|
+ struct snexpr *e = (struct snexpr *)malloc(sizeof(struct snexpr));
|
|
|
+ if(e == NULL) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ memset(e, 0, sizeof(struct snexpr));
|
|
|
+
|
|
|
+ e->param.stz.sval = (char *)malloc(strlen(value0) + strlen(value1) + 1);
|
|
|
+ if(e->param.stz.sval == NULL) {
|
|
|
+ free(e);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ e->eflags |= SNEXPR_EXPALLOC | SNEXPR_VALALLOC;
|
|
|
+ e->type = SNE_OP_CONSTSTZ;
|
|
|
+ strcpy(e->param.stz.sval, value0);
|
|
|
+ strcat(e->param.stz.sval, value1);
|
|
|
+ return e;
|
|
|
+}
|
|
|
+
|
|
|
+static void snexpr_result_free(struct snexpr *e)
|
|
|
+{
|
|
|
+ if(e == NULL) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if((e->eflags & SNEXPR_VALALLOC) && (e->type == SNE_OP_CONSTSTZ)
|
|
|
+ && (e->param.stz.sval != NULL)) {
|
|
|
+ free(e->param.stz.sval);
|
|
|
+ }
|
|
|
+ if(!(e->eflags & SNEXPR_EXPALLOC)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ free(e);
|
|
|
+}
|
|
|
+
|
|
|
+#define snexpr_eval_check_val(val, vtype) do { \
|
|
|
+ if(val==NULL || val->type != vtype) { \
|
|
|
+ goto error; \
|
|
|
+ } \
|
|
|
+ } while(0)
|
|
|
+
|
|
|
+#define snexpr_eval_check_null(val, vtype) do { \
|
|
|
+ if(val==NULL) { \
|
|
|
+ goto error; \
|
|
|
+ } \
|
|
|
+ } while(0)
|
|
|
+
|
|
|
+#define snexpr_eval_cmp(_CMPOP_) do { \
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]); \
|
|
|
+ snexpr_eval_check_null(rv0, SNE_OP_CONSTNUM); \
|
|
|
+ rv1 = snexpr_eval(&e->param.op.args.buf[1]); \
|
|
|
+ snexpr_eval_check_null(rv1, SNE_OP_CONSTNUM); \
|
|
|
+ if(rv0->type == SNE_OP_CONSTSTZ) { \
|
|
|
+ /* string comparison */ \
|
|
|
+ if(rv1->type == SNE_OP_CONSTNUM) { \
|
|
|
+ tv = snexpr_convert_num(rv1->param.num.nval, SNE_OP_CONSTSTZ); \
|
|
|
+ snexpr_result_free(rv1); \
|
|
|
+ rv1 = tv; \
|
|
|
+ snexpr_eval_check_val(rv1, SNE_OP_CONSTSTZ); \
|
|
|
+ } \
|
|
|
+ lv = snexpr_concat_strz(rv0->param.stz.sval, rv1->param.stz.sval); \
|
|
|
+ if(strcmp(rv0->param.stz.sval, rv1->param.stz.sval) _CMPOP_ 0) { \
|
|
|
+ lv = snexpr_convert_num(1, SNE_OP_CONSTNUM); \
|
|
|
+ } else { \
|
|
|
+ lv = snexpr_convert_num(0, SNE_OP_CONSTNUM); \
|
|
|
+ } \
|
|
|
+ } else { \
|
|
|
+ /* number comparison */ \
|
|
|
+ if(rv1->type == SNE_OP_CONSTSTZ) { \
|
|
|
+ tv = snexpr_convert_stz(rv1->param.stz.sval, SNE_OP_CONSTNUM); \
|
|
|
+ snexpr_result_free(rv1); \
|
|
|
+ rv1 = tv; \
|
|
|
+ snexpr_eval_check_val(rv1, SNE_OP_CONSTNUM); \
|
|
|
+ } \
|
|
|
+ lv = snexpr_convert_num( \
|
|
|
+ rv0->param.num.nval _CMPOP_ rv1->param.num.nval, SNE_OP_CONSTNUM); \
|
|
|
+ } \
|
|
|
+ } while(0)
|
|
|
+
|
|
|
+static struct snexpr *snexpr_eval(struct snexpr *e)
|
|
|
+{
|
|
|
+ float n;
|
|
|
+ struct snexpr *lv = NULL;
|
|
|
+ struct snexpr *rv0 = NULL;
|
|
|
+ struct snexpr *rv1 = NULL;
|
|
|
+ struct snexpr *tv = NULL;
|
|
|
+
|
|
|
+ switch(e->type) {
|
|
|
+ case SNE_OP_UNARY_MINUS:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ snexpr_eval_check_val(rv0, SNE_OP_CONSTNUM);
|
|
|
+ lv = snexpr_convert_num(-(rv0->param.num.nval), SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_UNARY_LOGICAL_NOT:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ snexpr_eval_check_val(rv0, SNE_OP_CONSTNUM);
|
|
|
+ lv = snexpr_convert_num(!(rv0->param.num.nval), SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_UNARY_BITWISE_NOT:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ snexpr_eval_check_val(rv0, SNE_OP_CONSTNUM);
|
|
|
+ lv = snexpr_convert_num(~(to_int(rv0->param.num.nval)), SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_POWER:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ snexpr_eval_check_val(rv0, SNE_OP_CONSTNUM);
|
|
|
+ rv1 = snexpr_eval(&e->param.op.args.buf[1]);
|
|
|
+ snexpr_eval_check_val(rv1, SNE_OP_CONSTNUM);
|
|
|
+ lv = snexpr_convert_num(
|
|
|
+ powf(rv0->param.num.nval, rv1->param.num.nval),
|
|
|
+ SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_MULTIPLY:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ snexpr_eval_check_val(rv0, SNE_OP_CONSTNUM);
|
|
|
+ rv1 = snexpr_eval(&e->param.op.args.buf[1]);
|
|
|
+ snexpr_eval_check_val(rv1, SNE_OP_CONSTNUM);
|
|
|
+ lv = snexpr_convert_num(
|
|
|
+ rv0->param.num.nval * rv1->param.num.nval, SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_DIVIDE:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ snexpr_eval_check_val(rv0, SNE_OP_CONSTNUM);
|
|
|
+ rv1 = snexpr_eval(&e->param.op.args.buf[1]);
|
|
|
+ snexpr_eval_check_val(rv1, SNE_OP_CONSTNUM);
|
|
|
+ if(rv1->param.num.nval == 0) {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+ lv = snexpr_convert_num(
|
|
|
+ rv0->param.num.nval / rv1->param.num.nval, SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_REMAINDER:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ snexpr_eval_check_val(rv0, SNE_OP_CONSTNUM);
|
|
|
+ rv1 = snexpr_eval(&e->param.op.args.buf[1]);
|
|
|
+ snexpr_eval_check_val(rv1, SNE_OP_CONSTNUM);
|
|
|
+ lv = snexpr_convert_num(
|
|
|
+ fmodf(rv0->param.num.nval, rv1->param.num.nval),
|
|
|
+ SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_PLUS:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ snexpr_eval_check_null(rv0, SNE_OP_CONSTNUM);
|
|
|
+ rv1 = snexpr_eval(&e->param.op.args.buf[1]);
|
|
|
+ snexpr_eval_check_null(rv1, SNE_OP_CONSTNUM);
|
|
|
+ if(rv0->type == SNE_OP_CONSTSTZ) {
|
|
|
+ /* string concatenation */
|
|
|
+ if(rv1->type == SNE_OP_CONSTNUM) {
|
|
|
+ tv = snexpr_convert_num(rv1->param.num.nval, SNE_OP_CONSTSTZ);
|
|
|
+ snexpr_result_free(rv1);
|
|
|
+ rv1 = tv;
|
|
|
+ snexpr_eval_check_val(rv1, SNE_OP_CONSTSTZ);
|
|
|
+ }
|
|
|
+ lv = snexpr_concat_strz(rv0->param.stz.sval, rv1->param.stz.sval);
|
|
|
+ } else {
|
|
|
+ /* add */
|
|
|
+ if(rv1->type == SNE_OP_CONSTSTZ) {
|
|
|
+ tv = snexpr_convert_stz(rv1->param.stz.sval, SNE_OP_CONSTNUM);
|
|
|
+ snexpr_result_free(rv1);
|
|
|
+ rv1 = tv;
|
|
|
+ snexpr_eval_check_val(rv1, SNE_OP_CONSTNUM);
|
|
|
+ }
|
|
|
+ lv = snexpr_convert_num(
|
|
|
+ rv0->param.num.nval + rv1->param.num.nval, SNE_OP_CONSTNUM);
|
|
|
+ }
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_MINUS:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ snexpr_eval_check_val(rv0, SNE_OP_CONSTNUM);
|
|
|
+ rv1 = snexpr_eval(&e->param.op.args.buf[1]);
|
|
|
+ snexpr_eval_check_val(rv1, SNE_OP_CONSTNUM);
|
|
|
+ lv = snexpr_convert_num(
|
|
|
+ rv0->param.num.nval - rv1->param.num.nval, SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_SHL:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ snexpr_eval_check_val(rv0, SNE_OP_CONSTNUM);
|
|
|
+ rv1 = snexpr_eval(&e->param.op.args.buf[1]);
|
|
|
+ snexpr_eval_check_val(rv1, SNE_OP_CONSTNUM);
|
|
|
+ lv = snexpr_convert_num(
|
|
|
+ to_int(rv0->param.num.nval) << to_int(rv1->param.num.nval),
|
|
|
+ SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_SHR:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ snexpr_eval_check_val(rv0, SNE_OP_CONSTNUM);
|
|
|
+ rv1 = snexpr_eval(&e->param.op.args.buf[1]);
|
|
|
+ snexpr_eval_check_val(rv1, SNE_OP_CONSTNUM);
|
|
|
+ lv = snexpr_convert_num(
|
|
|
+ to_int(rv0->param.num.nval) >> to_int(rv1->param.num.nval),
|
|
|
+ SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_LT:
|
|
|
+ snexpr_eval_cmp(<);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_LE:
|
|
|
+ snexpr_eval_cmp(<=);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_GT:
|
|
|
+ snexpr_eval_cmp(>);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_GE:
|
|
|
+ snexpr_eval_cmp(>=);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_EQ:
|
|
|
+ snexpr_eval_cmp(==);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_NE:
|
|
|
+ snexpr_eval_cmp(!=);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_BITWISE_AND:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ snexpr_eval_check_val(rv0, SNE_OP_CONSTNUM);
|
|
|
+ rv1 = snexpr_eval(&e->param.op.args.buf[1]);
|
|
|
+ snexpr_eval_check_val(rv1, SNE_OP_CONSTNUM);
|
|
|
+ lv = snexpr_convert_num(
|
|
|
+ to_int(rv0->param.num.nval) & to_int(rv1->param.num.nval),
|
|
|
+ SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_BITWISE_OR:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ snexpr_eval_check_val(rv0, SNE_OP_CONSTNUM);
|
|
|
+ rv1 = snexpr_eval(&e->param.op.args.buf[1]);
|
|
|
+ snexpr_eval_check_val(rv1, SNE_OP_CONSTNUM);
|
|
|
+ lv = snexpr_convert_num(
|
|
|
+ to_int(rv0->param.num.nval) | to_int(rv1->param.num.nval),
|
|
|
+ SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_BITWISE_XOR:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ snexpr_eval_check_val(rv0, SNE_OP_CONSTNUM);
|
|
|
+ rv1 = snexpr_eval(&e->param.op.args.buf[1]);
|
|
|
+ snexpr_eval_check_val(rv1, SNE_OP_CONSTNUM);
|
|
|
+ lv = snexpr_convert_num(
|
|
|
+ to_int(rv0->param.num.nval) ^ to_int(rv1->param.num.nval),
|
|
|
+ SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_LOGICAL_AND:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ n = rv0->param.num.nval;
|
|
|
+ if(n != 0) {
|
|
|
+ rv1 = snexpr_eval(&e->param.op.args.buf[1]);
|
|
|
+ n = rv1->param.num.nval;
|
|
|
+ if(n != 0) {
|
|
|
+ lv = snexpr_convert_num(n, SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ lv = snexpr_convert_num(0, SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_LOGICAL_OR:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ n = rv0->param.num.nval;
|
|
|
+ if(n != 0 && !isnan(n)) {
|
|
|
+ lv = snexpr_convert_num(n, SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ } else {
|
|
|
+ rv1 = snexpr_eval(&e->param.op.args.buf[1]);
|
|
|
+ n = rv1->param.num.nval;
|
|
|
+ if(n != 0) {
|
|
|
+ lv = snexpr_convert_num(n, SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ lv = snexpr_convert_num(0, SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_ASSIGN:
|
|
|
+ rv1 = snexpr_eval(&e->param.op.args.buf[1]);
|
|
|
+ snexpr_eval_check_null(rv1, SNE_OP_CONSTNUM);
|
|
|
+ if(sne_vec_nth(&e->param.op.args, 0).type == SNE_OP_VAR) {
|
|
|
+ if(e->param.op.args.buf[0].param.var.vref->evflags & SNEXPR_VALALLOC) {
|
|
|
+ if(e->param.op.args.buf[0].param.var.vref->v.sval!=NULL) {
|
|
|
+ free(e->param.op.args.buf[0].param.var.vref->v.sval);
|
|
|
+ e->param.op.args.buf[0].param.var.vref->v.sval = NULL;
|
|
|
+ }
|
|
|
+ e->param.op.args.buf[0].param.var.vref->evflags &= ~(SNEXPR_TSTRING|SNEXPR_VALALLOC);
|
|
|
+ }
|
|
|
+ if(rv1->type == SNE_OP_CONSTSTZ) {
|
|
|
+ e->param.op.args.buf[0].param.var.vref->v.sval = strdup(rv1->param.stz.sval);
|
|
|
+ e->param.op.args.buf[0].param.var.vref->evflags |= SNEXPR_VALASSIGN|SNEXPR_TSTRING|SNEXPR_VALALLOC;
|
|
|
+ lv = snexpr_convert_stz(rv1->param.stz.sval, SNE_OP_CONSTSTZ);
|
|
|
+ } else {
|
|
|
+ n = rv1->param.num.nval;
|
|
|
+ e->param.op.args.buf[0].param.var.vref->v.nval = n;
|
|
|
+ e->param.op.args.buf[0].param.var.vref->evflags |= SNEXPR_VALASSIGN;
|
|
|
+ lv = snexpr_convert_num(n, SNE_OP_CONSTNUM);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_COMMA:
|
|
|
+ rv0 = snexpr_eval(&e->param.op.args.buf[0]);
|
|
|
+ rv1 = snexpr_eval(&e->param.op.args.buf[1]);
|
|
|
+ if(rv1->type == SNE_OP_CONSTSTZ) {
|
|
|
+ lv = snexpr_convert_stz(rv1->param.stz.sval, SNE_OP_CONSTSTZ);
|
|
|
+ } else {
|
|
|
+ lv = snexpr_convert_num(rv1->param.num.nval, SNE_OP_CONSTNUM);
|
|
|
+ }
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_CONSTNUM:
|
|
|
+ lv = snexpr_convert_num(e->param.num.nval, SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_CONSTSTZ:
|
|
|
+ lv = snexpr_convert_stz(e->param.stz.sval, SNE_OP_CONSTSTZ);
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_VAR:
|
|
|
+ if((_snexternval_cbf == NULL)
|
|
|
+ || (e->param.var.vref->evflags & SNEXPR_VALASSIGN)) {
|
|
|
+ if(e->param.var.vref->evflags & SNEXPR_TSTRING) {
|
|
|
+ lv = snexpr_convert_stz(e->param.var.vref->v.sval, SNE_OP_CONSTSTZ);
|
|
|
+ } else {
|
|
|
+ snexpr_convert_num(e->param.var.vref->v.nval, SNE_OP_CONSTNUM);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ lv = _snexternval_cbf(e->param.var.vref->name);
|
|
|
+ }
|
|
|
+ goto done;
|
|
|
+ case SNE_OP_FUNC:
|
|
|
+ rv0 = e->param.func.f->f(e->param.func.f, &e->param.func.args,
|
|
|
+ e->param.func.context);
|
|
|
+ if(rv0->type == SNE_OP_CONSTSTZ) {
|
|
|
+ lv = snexpr_convert_stz(rv0->param.stz.sval, SNE_OP_CONSTSTZ);
|
|
|
+ } else {
|
|
|
+ lv = snexpr_convert_num(rv0->param.num.nval, SNE_OP_CONSTNUM);
|
|
|
+ }
|
|
|
+ goto done;
|
|
|
+ default:
|
|
|
+ lv = snexpr_convert_num(NAN, SNE_OP_CONSTNUM);
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+
|
|
|
+done:
|
|
|
+ if(rv0 != NULL) {
|
|
|
+ snexpr_result_free(rv0);
|
|
|
+ }
|
|
|
+ if(rv1 != NULL) {
|
|
|
+ snexpr_result_free(rv1);
|
|
|
+ }
|
|
|
+ return lv;
|
|
|
+
|
|
|
+error:
|
|
|
+ if(rv0 != NULL) {
|
|
|
+ snexpr_result_free(rv0);
|
|
|
+ }
|
|
|
+ if(rv1 != NULL) {
|
|
|
+ snexpr_result_free(rv1);
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+static int snexpr_next_token(const char *s, size_t len, int *flags)
|
|
|
+{
|
|
|
+ unsigned int i = 0;
|
|
|
+ char b;
|
|
|
+ if(len == 0) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ char c = s[0];
|
|
|
+ if(c == '#') {
|
|
|
+ for(; i < len && s[i] != '\n'; i++)
|
|
|
+ ;
|
|
|
+ return i;
|
|
|
+ } else if(c == '\n') {
|
|
|
+ for(; i < len && isspace(s[i]); i++)
|
|
|
+ ;
|
|
|
+ if(*flags & SNEXPR_TOP) {
|
|
|
+ if(i == len || s[i] == ')') {
|
|
|
+ *flags = *flags & (~SNEXPR_COMMA);
|
|
|
+ } else {
|
|
|
+ *flags = SNEXPR_TNUMBER | SNEXPR_TSTRING | SNEXPR_TWORD | SNEXPR_TOPEN
|
|
|
+ | SNEXPR_COMMA;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return i;
|
|
|
+ } else if(isspace(c)) {
|
|
|
+ while(i < len && isspace(s[i]) && s[i] != '\n') {
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+ return i;
|
|
|
+ } else if(isdigit(c)) {
|
|
|
+ if((*flags & SNEXPR_TNUMBER) == 0) {
|
|
|
+ return -1; // unexpected number
|
|
|
+ }
|
|
|
+ *flags = SNEXPR_TOP | SNEXPR_TCLOSE;
|
|
|
+ while((c == '.' || isdigit(c)) && i < len) {
|
|
|
+ i++;
|
|
|
+ c = s[i];
|
|
|
+ }
|
|
|
+ return i;
|
|
|
+ } else if(c == '"' || c == '\'') {
|
|
|
+ if((*flags & SNEXPR_TSTRING) == 0) {
|
|
|
+ return -1; // unexpected string
|
|
|
+ }
|
|
|
+ if(i == len - 1) {
|
|
|
+ return -1; // invalud start of string
|
|
|
+ }
|
|
|
+ *flags = SNEXPR_TOP | SNEXPR_TCLOSE;
|
|
|
+ b = c;
|
|
|
+ i++;
|
|
|
+ c = s[i];
|
|
|
+ while(c != b && i < len) {
|
|
|
+ i++;
|
|
|
+ c = s[i];
|
|
|
+ }
|
|
|
+ return i + 1;
|
|
|
+ } else if(isfirstvarchr(c)) {
|
|
|
+ if((*flags & SNEXPR_TWORD) == 0) {
|
|
|
+ return -2; // unexpected word
|
|
|
+ }
|
|
|
+ *flags = SNEXPR_TOP | SNEXPR_TOPEN | SNEXPR_TCLOSE;
|
|
|
+ while((isvarchr(c)) && i < len) {
|
|
|
+ i++;
|
|
|
+ c = s[i];
|
|
|
+ }
|
|
|
+ return i;
|
|
|
+ } else if(c == '(' || c == ')') {
|
|
|
+ if(c == '(' && (*flags & SNEXPR_TOPEN) != 0) {
|
|
|
+ *flags = SNEXPR_TNUMBER | SNEXPR_TSTRING | SNEXPR_TWORD | SNEXPR_TOPEN
|
|
|
+ | SNEXPR_TCLOSE;
|
|
|
+ } else if(c == ')' && (*flags & SNEXPR_TCLOSE) != 0) {
|
|
|
+ *flags = SNEXPR_TOP | SNEXPR_TCLOSE;
|
|
|
+ } else {
|
|
|
+ return -3; // unexpected parenthesis
|
|
|
+ }
|
|
|
+ return 1;
|
|
|
+ } else {
|
|
|
+ if((*flags & SNEXPR_TOP) == 0) {
|
|
|
+ if(snexpr_op(&c, 1, 1) == SNE_OP_UNKNOWN) {
|
|
|
+ return -4; // missing expected operand
|
|
|
+ }
|
|
|
+ *flags = SNEXPR_TNUMBER | SNEXPR_TSTRING | SNEXPR_TWORD | SNEXPR_TOPEN
|
|
|
+ | SNEXPR_UNARY;
|
|
|
+ return 1;
|
|
|
+ } else {
|
|
|
+ int found = 0;
|
|
|
+ while(!isvarchr(c) && !isspace(c) && c != '(' && c != ')'
|
|
|
+ && i < len) {
|
|
|
+ if(snexpr_op(s, i + 1, 0) != SNE_OP_UNKNOWN) {
|
|
|
+ found = 1;
|
|
|
+ } else if(found) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ i++;
|
|
|
+ c = s[i];
|
|
|
+ }
|
|
|
+ if(!found) {
|
|
|
+ return -5; // unknown operator
|
|
|
+ }
|
|
|
+ *flags = SNEXPR_TNUMBER | SNEXPR_TSTRING | SNEXPR_TWORD | SNEXPR_TOPEN;
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#define SNEXPR_PAREN_ALLOWED 0
|
|
|
+#define SNEXPR_PAREN_EXPECTED 1
|
|
|
+#define SNEXPR_PAREN_FORBIDDEN 2
|
|
|
+
|
|
|
+static int snexpr_bind(const char *s, size_t len, sne_vec_expr_t *es)
|
|
|
+{
|
|
|
+ enum snexpr_type op = snexpr_op(s, len, -1);
|
|
|
+ if(op == SNE_OP_UNKNOWN) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(snexpr_is_unary(op)) {
|
|
|
+ if(sne_vec_len(es) < 1) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ struct snexpr arg = sne_vec_pop(es);
|
|
|
+ struct snexpr unary = snexpr_init();
|
|
|
+ unary.type = op;
|
|
|
+ sne_vec_push(&unary.param.op.args, arg);
|
|
|
+ sne_vec_push(es, unary);
|
|
|
+ } else {
|
|
|
+ if(sne_vec_len(es) < 2) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ struct snexpr b = sne_vec_pop(es);
|
|
|
+ struct snexpr a = sne_vec_pop(es);
|
|
|
+ struct snexpr binary = snexpr_init();
|
|
|
+ binary.type = op;
|
|
|
+ if(op == SNE_OP_ASSIGN && a.type != SNE_OP_VAR) {
|
|
|
+ return -1; /* Bad assignment */
|
|
|
+ }
|
|
|
+ sne_vec_push(&binary.param.op.args, a);
|
|
|
+ sne_vec_push(&binary.param.op.args, b);
|
|
|
+ sne_vec_push(es, binary);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static struct snexpr snexpr_constnum(float value)
|
|
|
+{
|
|
|
+ struct snexpr e = snexpr_init();
|
|
|
+ e.type = SNE_OP_CONSTNUM;
|
|
|
+ e.param.num.nval = value;
|
|
|
+ return e;
|
|
|
+}
|
|
|
+
|
|
|
+static struct snexpr snexpr_varref(struct snexpr_var *v)
|
|
|
+{
|
|
|
+ struct snexpr e = snexpr_init();
|
|
|
+ e.type = SNE_OP_VAR;
|
|
|
+ e.param.var.vref = v;
|
|
|
+ return e;
|
|
|
+}
|
|
|
+
|
|
|
+static struct snexpr snexpr_conststr(const char *value, int len)
|
|
|
+{
|
|
|
+ struct snexpr e = snexpr_init();
|
|
|
+ if(len < 2) {
|
|
|
+ len = 0;
|
|
|
+ } else {
|
|
|
+ /* skip the quotes */
|
|
|
+ len -= 2;
|
|
|
+ }
|
|
|
+ e.type = SNE_OP_CONSTSTZ;
|
|
|
+ e.param.stz.sval = malloc(len + 1);
|
|
|
+ if(e.param.stz.sval) {
|
|
|
+ if(len > 0) {
|
|
|
+ /* do not copy the quotes */
|
|
|
+ memcpy(e.param.stz.sval, value + 1, len);
|
|
|
+ e.param.stz.sval[len] = '\0';
|
|
|
+ } else {
|
|
|
+ e.param.stz.sval[0] = '\0';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return e;
|
|
|
+}
|
|
|
+
|
|
|
+static struct snexpr snexpr_binary(
|
|
|
+ enum snexpr_type type, struct snexpr a, struct snexpr b)
|
|
|
+{
|
|
|
+ struct snexpr e = snexpr_init();
|
|
|
+ e.type = type;
|
|
|
+ sne_vec_push(&e.param.op.args, a);
|
|
|
+ sne_vec_push(&e.param.op.args, b);
|
|
|
+ return e;
|
|
|
+}
|
|
|
+
|
|
|
+static inline void snexpr_copy(struct snexpr *dst, struct snexpr *src)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ struct snexpr arg;
|
|
|
+ dst->type = src->type;
|
|
|
+ if(src->type == SNE_OP_FUNC) {
|
|
|
+ dst->param.func.f = src->param.func.f;
|
|
|
+ sne_vec_foreach(&src->param.func.args, arg, i)
|
|
|
+ {
|
|
|
+ struct snexpr tmp = snexpr_init();
|
|
|
+ snexpr_copy(&tmp, &arg);
|
|
|
+ sne_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);
|
|
|
+ }
|
|
|
+ } else if(src->type == SNE_OP_CONSTNUM) {
|
|
|
+ dst->param.num.nval = src->param.num.nval;
|
|
|
+ } else if(src->type == SNE_OP_VAR) {
|
|
|
+ dst->param.var.vref = src->param.var.vref;
|
|
|
+ } else {
|
|
|
+ sne_vec_foreach(&src->param.op.args, arg, i)
|
|
|
+ {
|
|
|
+ struct snexpr tmp = snexpr_init();
|
|
|
+ snexpr_copy(&tmp, &arg);
|
|
|
+ sne_vec_push(&dst->param.op.args, tmp);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void snexpr_destroy_args(struct snexpr *e);
|
|
|
+
|
|
|
+static struct snexpr *snexpr_create(const char *s, size_t len,
|
|
|
+ struct snexpr_var_list *vars, struct snexpr_func *funcs,
|
|
|
+ snexternval_cbf_t evcbf)
|
|
|
+{
|
|
|
+ float num;
|
|
|
+ struct snexpr_var *v;
|
|
|
+ const char *id = NULL;
|
|
|
+ size_t idn = 0;
|
|
|
+
|
|
|
+ struct snexpr *result = NULL;
|
|
|
+
|
|
|
+ _snexternval_cbf = evcbf;
|
|
|
+
|
|
|
+ sne_vec_expr_t es = sne_vec_init();
|
|
|
+ sne_vec_str_t os = sne_vec_init();
|
|
|
+ sne_vec_arg_t as = sne_vec_init();
|
|
|
+
|
|
|
+ struct macro
|
|
|
+ {
|
|
|
+ char *name;
|
|
|
+ sne_vec_expr_t body;
|
|
|
+ };
|
|
|
+ sne_vec(struct macro) macros = sne_vec_init();
|
|
|
+
|
|
|
+ int flags = SNEXPR_TDEFAULT;
|
|
|
+ int paren = SNEXPR_PAREN_ALLOWED;
|
|
|
+ for(;;) {
|
|
|
+ int n = snexpr_next_token(s, len, &flags);
|
|
|
+ if(n == 0) {
|
|
|
+ break;
|
|
|
+ } else if(n < 0) {
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+ const char *tok = s;
|
|
|
+ s = s + n;
|
|
|
+ len = len - n;
|
|
|
+ if(*tok == '#') {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if(flags & SNEXPR_UNARY) {
|
|
|
+ if(n == 1) {
|
|
|
+ switch(*tok) {
|
|
|
+ case '-':
|
|
|
+ tok = "-u";
|
|
|
+ break;
|
|
|
+ case '^':
|
|
|
+ tok = "^u";
|
|
|
+ break;
|
|
|
+ case '!':
|
|
|
+ tok = "!u";
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+ n = 2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(*tok == '\n' && (flags & SNEXPR_COMMA)) {
|
|
|
+ flags = flags & (~SNEXPR_COMMA);
|
|
|
+ n = 1;
|
|
|
+ tok = ",";
|
|
|
+ }
|
|
|
+ if(isspace(*tok)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ int paren_next = SNEXPR_PAREN_ALLOWED;
|
|
|
+
|
|
|
+ if(idn > 0) {
|
|
|
+ if(n == 1 && *tok == '(') {
|
|
|
+ int i;
|
|
|
+ int has_macro = 0;
|
|
|
+ struct macro m;
|
|
|
+ sne_vec_foreach(¯os, m, i)
|
|
|
+ {
|
|
|
+ if(strlen(m.name) == idn && strncmp(m.name, id, idn) == 0) {
|
|
|
+ has_macro = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if((idn == 1 && id[0] == '$') || has_macro
|
|
|
+ || snexpr_func_find(funcs, id, idn) != NULL) {
|
|
|
+ struct snexpr_string str = {id, (int)idn};
|
|
|
+ sne_vec_push(&os, str);
|
|
|
+ paren = SNEXPR_PAREN_EXPECTED;
|
|
|
+ } else {
|
|
|
+ goto cleanup; /* invalid function name */
|
|
|
+ }
|
|
|
+ } else if((v = snexpr_var_find(vars, id, idn)) != NULL) {
|
|
|
+ sne_vec_push(&es, snexpr_varref(v));
|
|
|
+ paren = SNEXPR_PAREN_FORBIDDEN;
|
|
|
+ }
|
|
|
+ id = NULL;
|
|
|
+ idn = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(n == 1 && *tok == '(') {
|
|
|
+ if(paren == SNEXPR_PAREN_EXPECTED) {
|
|
|
+ struct snexpr_string str = {"{", 1};
|
|
|
+ sne_vec_push(&os, str);
|
|
|
+ struct snexpr_arg arg = {sne_vec_len(&os), sne_vec_len(&es), sne_vec_init()};
|
|
|
+ sne_vec_push(&as, arg);
|
|
|
+ } else if(paren == SNEXPR_PAREN_ALLOWED) {
|
|
|
+ struct snexpr_string str = {"(", 1};
|
|
|
+ sne_vec_push(&os, str);
|
|
|
+ } else {
|
|
|
+ goto cleanup; // Bad call
|
|
|
+ }
|
|
|
+ } else if(paren == SNEXPR_PAREN_EXPECTED) {
|
|
|
+ goto cleanup; // Bad call
|
|
|
+ } else if(n == 1 && *tok == ')') {
|
|
|
+ int minlen = (sne_vec_len(&as) > 0 ? sne_vec_peek(&as).oslen : 0);
|
|
|
+ while(sne_vec_len(&os) > minlen && *sne_vec_peek(&os).s != '('
|
|
|
+ && *sne_vec_peek(&os).s != '{') {
|
|
|
+ struct snexpr_string str = sne_vec_pop(&os);
|
|
|
+ if(snexpr_bind(str.s, str.n, &es) == -1) {
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(sne_vec_len(&os) == 0) {
|
|
|
+ goto cleanup; // Bad parens
|
|
|
+ }
|
|
|
+ struct snexpr_string str = sne_vec_pop(&os);
|
|
|
+ if(str.n == 1 && *str.s == '{') {
|
|
|
+ str = sne_vec_pop(&os);
|
|
|
+ struct snexpr_arg arg = sne_vec_pop(&as);
|
|
|
+ if(sne_vec_len(&es) > arg.eslen) {
|
|
|
+ sne_vec_push(&arg.args, sne_vec_pop(&es));
|
|
|
+ }
|
|
|
+ if(str.n == 1 && str.s[0] == '$') {
|
|
|
+ if(sne_vec_len(&arg.args) < 1) {
|
|
|
+ sne_vec_free(&arg.args);
|
|
|
+ goto cleanup; /* too few arguments for $() function */
|
|
|
+ }
|
|
|
+ struct snexpr *u = &sne_vec_nth(&arg.args, 0);
|
|
|
+ if(u->type != SNE_OP_VAR) {
|
|
|
+ sne_vec_free(&arg.args);
|
|
|
+ goto cleanup; /* first argument is not a variable */
|
|
|
+ }
|
|
|
+ for(struct snexpr_var *v = vars->head; v; v = v->next) {
|
|
|
+ if(v == u->param.var.vref) {
|
|
|
+ struct macro m = {v->name, arg.args};
|
|
|
+ sne_vec_push(¯os, m);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sne_vec_push(&es, snexpr_constnum(0));
|
|
|
+ } else {
|
|
|
+ int i = 0;
|
|
|
+ int found = -1;
|
|
|
+ struct macro m;
|
|
|
+ sne_vec_foreach(¯os, m, i)
|
|
|
+ {
|
|
|
+ if(strlen(m.name) == (size_t)str.n
|
|
|
+ && strncmp(m.name, str.s, str.n) == 0) {
|
|
|
+ found = i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if(found != -1) {
|
|
|
+ m = sne_vec_nth(¯os, found);
|
|
|
+ struct snexpr root = snexpr_constnum(0);
|
|
|
+ struct snexpr *p = &root;
|
|
|
+ /* Assign macro parameters */
|
|
|
+ for(int j = 0; j < sne_vec_len(&arg.args); j++) {
|
|
|
+ char varname[4];
|
|
|
+ snprintf(varname, sizeof(varname) - 1, "$%d",
|
|
|
+ (j + 1));
|
|
|
+ struct snexpr_var *v =
|
|
|
+ snexpr_var_find(vars, varname, strlen(varname));
|
|
|
+ struct snexpr ev = snexpr_varref(v);
|
|
|
+ struct snexpr assign = snexpr_binary(
|
|
|
+ SNE_OP_ASSIGN, ev, sne_vec_nth(&arg.args, j));
|
|
|
+ *p = snexpr_binary(
|
|
|
+ SNE_OP_COMMA, assign, snexpr_constnum(0));
|
|
|
+ p = &sne_vec_nth(&p->param.op.args, 1);
|
|
|
+ }
|
|
|
+ /* Expand macro body */
|
|
|
+ for(int j = 1; j < sne_vec_len(&m.body); j++) {
|
|
|
+ if(j < sne_vec_len(&m.body) - 1) {
|
|
|
+ *p = snexpr_binary(SNE_OP_COMMA, snexpr_constnum(0),
|
|
|
+ snexpr_constnum(0));
|
|
|
+ snexpr_copy(&sne_vec_nth(&p->param.op.args, 0),
|
|
|
+ &sne_vec_nth(&m.body, j));
|
|
|
+ } else {
|
|
|
+ snexpr_copy(p, &sne_vec_nth(&m.body, j));
|
|
|
+ }
|
|
|
+ p = &sne_vec_nth(&p->param.op.args, 1);
|
|
|
+ }
|
|
|
+ sne_vec_push(&es, root);
|
|
|
+ sne_vec_free(&arg.args);
|
|
|
+ } else {
|
|
|
+ struct snexpr_func *f = snexpr_func_find(funcs, str.s, str.n);
|
|
|
+ struct snexpr bound_func = snexpr_init();
|
|
|
+ bound_func.type = SNE_OP_FUNC;
|
|
|
+ bound_func.param.func.f = f;
|
|
|
+ bound_func.param.func.args = arg.args;
|
|
|
+ if(f->ctxsz > 0) {
|
|
|
+ void *p = calloc(1, f->ctxsz);
|
|
|
+ if(p == NULL) {
|
|
|
+ goto cleanup; /* allocation failed */
|
|
|
+ }
|
|
|
+ bound_func.param.func.context = p;
|
|
|
+ }
|
|
|
+ sne_vec_push(&es, bound_func);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ paren_next = SNEXPR_PAREN_FORBIDDEN;
|
|
|
+ } else if(!isnan(num = snexpr_parse_number(tok, n))) {
|
|
|
+ sne_vec_push(&es, snexpr_constnum(num));
|
|
|
+ paren_next = SNEXPR_PAREN_FORBIDDEN;
|
|
|
+ } else if(*tok == '"' || *tok == '\'') {
|
|
|
+ sne_vec_push(&es, snexpr_conststr(tok, n));
|
|
|
+ paren_next = SNEXPR_PAREN_FORBIDDEN;
|
|
|
+ } else if(snexpr_op(tok, n, -1) != SNE_OP_UNKNOWN) {
|
|
|
+ enum snexpr_type op = snexpr_op(tok, n, -1);
|
|
|
+ struct snexpr_string o2 = {NULL, 0};
|
|
|
+ if(sne_vec_len(&os) > 0) {
|
|
|
+ o2 = sne_vec_peek(&os);
|
|
|
+ }
|
|
|
+ for(;;) {
|
|
|
+ if(n == 1 && *tok == ',' && sne_vec_len(&os) > 0) {
|
|
|
+ struct snexpr_string str = sne_vec_peek(&os);
|
|
|
+ if(str.n == 1 && *str.s == '{') {
|
|
|
+ struct snexpr e = sne_vec_pop(&es);
|
|
|
+ sne_vec_push(&sne_vec_peek(&as).args, e);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ enum snexpr_type type2 = snexpr_op(o2.s, o2.n, -1);
|
|
|
+ if(!(type2 != SNE_OP_UNKNOWN && snexpr_prec(op, type2))) {
|
|
|
+ struct snexpr_string str = {tok, n};
|
|
|
+ sne_vec_push(&os, str);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(snexpr_bind(o2.s, o2.n, &es) == -1) {
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+ (void)sne_vec_pop(&os);
|
|
|
+ if(sne_vec_len(&os) > 0) {
|
|
|
+ o2 = sne_vec_peek(&os);
|
|
|
+ } else {
|
|
|
+ o2.n = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if(n > 0 && !isdigit(*tok)) {
|
|
|
+ /* Valid identifier, a variable or a function */
|
|
|
+ id = tok;
|
|
|
+ idn = n;
|
|
|
+ } else {
|
|
|
+ goto cleanup; // Bad variable name, e.g. '2.3.4' or '4ever'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ paren = paren_next;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(idn > 0) {
|
|
|
+ sne_vec_push(&es, snexpr_varref(snexpr_var_find(vars, id, idn)));
|
|
|
+ }
|
|
|
+
|
|
|
+ while(sne_vec_len(&os) > 0) {
|
|
|
+ struct snexpr_string rest = sne_vec_pop(&os);
|
|
|
+ if(rest.n == 1 && (*rest.s == '(' || *rest.s == ')')) {
|
|
|
+ goto cleanup; // Bad paren
|
|
|
+ }
|
|
|
+ if(snexpr_bind(rest.s, rest.n, &es) == -1) {
|
|
|
+ goto cleanup;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ result = (struct snexpr *)calloc(1, sizeof(struct snexpr));
|
|
|
+ if(result != NULL) {
|
|
|
+ if(sne_vec_len(&es) == 0) {
|
|
|
+ result->type = SNE_OP_CONSTNUM;
|
|
|
+ } else {
|
|
|
+ *result = sne_vec_pop(&es);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ int i, j;
|
|
|
+ struct macro m;
|
|
|
+ struct snexpr e;
|
|
|
+ struct snexpr_arg a;
|
|
|
+cleanup:
|
|
|
+ sne_vec_foreach(¯os, m, i)
|
|
|
+ {
|
|
|
+ struct snexpr e;
|
|
|
+ sne_vec_foreach(&m.body, e, j)
|
|
|
+ {
|
|
|
+ snexpr_destroy_args(&e);
|
|
|
+ }
|
|
|
+ sne_vec_free(&m.body);
|
|
|
+ }
|
|
|
+ sne_vec_free(¯os);
|
|
|
+
|
|
|
+ sne_vec_foreach(&es, e, i)
|
|
|
+ {
|
|
|
+ snexpr_destroy_args(&e);
|
|
|
+ }
|
|
|
+ sne_vec_free(&es);
|
|
|
+
|
|
|
+ sne_vec_foreach(&as, a, i)
|
|
|
+ {
|
|
|
+ sne_vec_foreach(&a.args, e, j)
|
|
|
+ {
|
|
|
+ snexpr_destroy_args(&e);
|
|
|
+ }
|
|
|
+ sne_vec_free(&a.args);
|
|
|
+ }
|
|
|
+ sne_vec_free(&as);
|
|
|
+
|
|
|
+ /*sne_vec_foreach(&os, o, i) {sne_vec_free(&m.body);}*/
|
|
|
+ sne_vec_free(&os);
|
|
|
+ if(result==NULL) {
|
|
|
+ _snexternval_cbf = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+static void snexpr_destroy_args(struct snexpr *e)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ struct snexpr arg;
|
|
|
+ if(e->type == SNE_OP_FUNC) {
|
|
|
+ sne_vec_foreach(&e->param.func.args, arg, i)
|
|
|
+ {
|
|
|
+ snexpr_destroy_args(&arg);
|
|
|
+ }
|
|
|
+ sne_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);
|
|
|
+ }
|
|
|
+ free(e->param.func.context);
|
|
|
+ }
|
|
|
+ } else if(e->type != SNE_OP_CONSTNUM && e->type != SNE_OP_CONSTSTZ
|
|
|
+ && e->type != SNE_OP_VAR) {
|
|
|
+ sne_vec_foreach(&e->param.op.args, arg, i)
|
|
|
+ {
|
|
|
+ snexpr_destroy_args(&arg);
|
|
|
+ }
|
|
|
+ sne_vec_free(&e->param.op.args);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void snexpr_destroy(struct snexpr *e, struct snexpr_var_list *vars)
|
|
|
+{
|
|
|
+ _snexternval_cbf = NULL;
|
|
|
+
|
|
|
+ if(e != NULL) {
|
|
|
+ snexpr_destroy_args(e);
|
|
|
+ free(e);
|
|
|
+ }
|
|
|
+ if(vars != NULL) {
|
|
|
+ for(struct snexpr_var *v = vars->head; v;) {
|
|
|
+ struct snexpr_var *next = v->next;
|
|
|
+ if(v->evflags & SNEXPR_VALALLOC) {
|
|
|
+ free(v->v.sval);
|
|
|
+ }
|
|
|
+ free(v);
|
|
|
+ v = next;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef __cplusplus
|
|
|
+} /* extern "C" */
|
|
|
+#endif
|
|
|
+
|
|
|
+#endif /* _SNEXPR_H_ */
|