comparison q/expr.c @ 14:388074ff9474

Add fixed point code
author Daniel O'Connor <darius@dons.net.au>
date Tue, 25 Feb 2025 13:28:29 +1030
parents
children
comparison
equal deleted inserted replaced
13:032acb7fbc04 14:388074ff9474
1 /* Project: Small Expression Evaluator for Q library
2 * Author: Richard James Howe
3 * License: The Unlicense
4 * See: <https://en.wikipedia.org/wiki/Shunting-yard_algorithm> */
5
6 #include <assert.h>
7 #include <ctype.h>
8 #include <limits.h>
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include "q.h"
14
15 #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
16 #define DEFAULT_STACK_SIZE (64)
17
18 enum { ASSOCIATE_NONE, ASSOCIATE_LEFT, ASSOCIATE_RIGHT, };
19 enum { LEX_NUMBER, LEX_OPERATOR, LEX_END, };
20
21 static void expr_delete(qexpr_t *e) {
22 if (!e)
23 return;
24 free(e->ops);
25 free(e->numbers);
26 for (size_t i = 0; i < e->vars_max; i++) {
27 free(e->vars[i]->name);
28 free(e->vars[i]);
29 }
30 free(e->vars);
31 free(e);
32 }
33
34 static qexpr_t *expr_new(size_t max) {
35 max = max ? max : 64;
36 qexpr_t *e = calloc(sizeof(*e), 1);
37 if (!e)
38 goto fail;
39 e->ops = calloc(sizeof(*e->ops), max);
40 e->numbers = calloc(sizeof(*(e->numbers)), max);
41 if (!(e->ops) || !(e->numbers))
42 goto fail;
43 e->ops_max = max;
44 e->numbers_max = max;
45 qexpr_init(e);
46 return e;
47 fail:
48 expr_delete(e);
49 return NULL;
50 }
51
52 static qvariable_t *variable_lookup(qexpr_t *e, const char *name) {
53 assert(e);
54 for (size_t i = 0; i < e->vars_max; i++) {
55 qvariable_t *v = e->vars[i];
56 if (!strcmp(v->name, name))
57 return v;
58 }
59 return NULL;
60 }
61
62 static char *estrdup(const char *s) {
63 assert(s);
64 const size_t l = strlen(s) + 1;
65 char *r = malloc(l);
66 return memcpy(r, s, l);
67 }
68
69 static int variable_name_is_valid(const char *n) {
70 assert(n);
71 if (!isalpha(*n) && !(*n == '_'))
72 return 0;
73 for (n++; *n; n++)
74 if (!isalnum(*n) && !(*n == '_'))
75 return 0;
76 return 1;
77 }
78
79 static qvariable_t *variable_add(qexpr_t *e, const char *name, q_t value) {
80 assert(e);
81 assert(name);
82 qvariable_t *v = variable_lookup(e, name), **vs = e->vars;
83 if (v) {
84 v->value = value;
85 return v;
86 }
87 if (!variable_name_is_valid(name))
88 return NULL;
89 char *s = estrdup(name);
90 vs = realloc(e->vars, (e->vars_max + 1) * sizeof(*vs));
91 v = calloc(1, sizeof(*v));
92 if (!vs || !v || !s)
93 goto fail;
94 v->name = s;
95 v->value = value;
96 vs[e->vars_max++] = v;
97 e->vars = vs;
98 return v;
99 fail:
100 free(v);
101 free(s);
102 free(vs);
103 if (vs != e->vars)
104 free(e->vars);
105 return NULL;
106 }
107
108 static inline int tests(FILE *out) {
109 assert(out);
110 int report = 0;
111 static const struct test {
112 int r;
113 q_t result;
114 const char *expr;
115 } tests[] = { // NB. Variables defined later.
116 { -1, QINT( 0), "" },
117 { -1, QINT( 0), "(" },
118 { -1, QINT( 0), ")" },
119 { -1, QINT( 0), "2**3" },
120 { 0, QINT( 0), "0" },
121 { 0, QINT( 2), "1+1" },
122 { 0, -QINT( 1), "-1" },
123 { 0, QINT( 1), "--1" },
124 { 0, QINT(14), "2+(3*4)" },
125 { 0, QINT(23), "a+(b*5)" },
126 { -1, QINT(14), "(2+(3* 4)" },
127 { -1, QINT(14), "2+(3*4)(" },
128 { 0, QINT(14), "2+3*4" },
129 { 0, QINT( 0), " 2==3 " },
130 { 0, QINT( 1), "2 ==2" },
131 { 0, QINT( 1), "2== (1+1)" },
132 //{ 0, QINT( 8), "2 pow 3" },
133 //{ -1, QINT( 0), "2pow3" },
134 { 0, QINT(20), "(2+3)*4" },
135 { 0, -QINT( 4), "(2+(-3))*4" },
136 { -1, QINT( 0), "1/0" },
137 { -1, QINT( 0), "1%0" },
138 { 0, QINT(50), "100/2" },
139 { 0, QINT( 2), "1--1", },
140 { 0, QINT( 0), "1---1", },
141 };
142
143 if (fputs("Running Built In Self Tests:\n", out) < 0)
144 report = -1;
145 const size_t length = sizeof (tests) / sizeof (tests[0]);
146 for (size_t i = 0; i < length; i++) {
147 qexpr_t *e = expr_new(64);
148 const struct test *test = &tests[i];
149 if (!e) {
150 (void)fprintf(out, "test failed (unable to allocate)\n");
151 report = -1;
152 goto end;
153 }
154
155 qvariable_t *v1 = variable_add(e, "a", QINT(3));
156 qvariable_t *v2 = variable_add(e, "b", QINT(4));
157 qvariable_t *v3 = variable_add(e, "c", -QINT(5));
158 if (!v1 || !v2 || !v3) {
159 (void)fprintf(out, "test failed (unable to assign variable)\n");
160 report = -1;
161 goto end;
162 }
163
164 const int r = qexpr(e, test->expr);
165 const q_t tos = e->numbers[0];
166 const int pass = (r == test->r) && (r != 0 || tos == test->result);
167 if (fprintf(out, "%s: r(%2d), eval(\"%s\") = %lg \n",
168 pass ? " ok" : " FAIL", r, test->expr, (double)tos) < 0)
169 report = -1;
170 if (!pass) {
171 (void)fprintf(out, "\tExpected: r(%2d), %lg\n",
172 test->r, (double)(test->result));
173 report = -1;
174 }
175 expr_delete(e);
176 }
177 end:
178 if (fprintf(out, "Tests Complete: %s\n", report == 0 ? "pass" : "FAIL") < 0)
179 report = -1;
180 return report;
181 }
182
183 static qexpr_t *expr_new_with_vars(size_t max) {
184 qexpr_t *e = expr_new(max);
185 if (!e) return NULL;
186 if (!variable_add(e, "whole", qinfo.whole)) goto fail;
187 if (!variable_add(e, "fractional", qinfo.fractional)) goto fail;
188 if (!variable_add(e, "bit", qinfo.bit)) goto fail;
189 if (!variable_add(e, "smallest", qinfo.min)) goto fail;
190 if (!variable_add(e, "biggest", qinfo.max)) goto fail;
191 if (!variable_add(e, "pi", qinfo.pi)) goto fail;
192 if (!variable_add(e, "e", qinfo.e)) goto fail;
193 if (!variable_add(e, "sqrt2", qinfo.sqrt2)) goto fail;
194 if (!variable_add(e, "sqrt3", qinfo.sqrt3)) goto fail;
195 if (!variable_add(e, "ln2", qinfo.ln2)) goto fail;
196 if (!variable_add(e, "ln10", qinfo.ln10)) goto fail;
197 return e;
198 fail:
199 expr_delete(e);
200 return NULL;
201 }
202
203 static int usage(FILE *out, const char *arg0) {
204 assert(out);
205 assert(arg0);
206 return fprintf(out, "usage: %s expr\n", arg0);
207 }
208
209 int main(int argc, char *argv[]) {
210
211 if (argc == 1) {
212 if (usage(stderr, argv[0]) < 0) { return 1; }
213 return tests(stderr);
214 }
215
216 if (argc < 2) {
217 (void)fprintf(stderr, "usage: %s expr\n", argv[0]);
218 return 1;
219 }
220
221 int r = 0;
222
223 for (int i = 1; i < argc; i++) {
224 qexpr_t *e = expr_new_with_vars(0);
225 if (!e) {
226 (void)fprintf(stderr, "allocate failed\n");
227 r = 1;
228 break;
229 }
230 if (qexpr(e, argv[i]) == 0) {
231 char n[64 + 1] = { 0, };
232 qsprint(e->numbers[0], n, sizeof n);
233 if (printf("%s\n", n) < 0)
234 r = 1;
235 r = 0;
236 } else {
237 (void)fprintf(stderr, "error: %s\n", e->error_string);
238 r = 1;
239 }
240 expr_delete(e);
241 }
242 return r;
243 }
244
245