Blob


1 /*
2 * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include "compat.h"
19 #include <assert.h>
20 #include <inttypes.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <syslog.h>
26 #include "utils.h"
27 #include "script.h"
28 #include "log.h"
30 #define DEBUG 0
32 static struct procs procs = TAILQ_HEAD_INITIALIZER(procs);
33 static struct tests tests = TAILQ_HEAD_INITIALIZER(tests);
35 static struct proc *curr_proc;
36 static struct test *curr_test;
38 static struct op *curr_block;
40 static struct op *curr_argv;
41 static int curr_argc;
43 #define STACK_HEIGHT 16
44 static struct value vstack[STACK_HEIGHT];
45 static int stackh;
47 static struct value v_false = {.type = V_NUM, .v = {.num = 0}};
48 static struct value v_true = {.type = V_NUM, .v = {.num = 1}};
50 void
51 global_set(char *sym, struct op *op)
52 {
53 assert(op->type == OP_LITERAL);
54 }
56 struct op *
57 newop(int type)
58 {
59 struct op *op;
61 op = xcalloc(1, sizeof(*op));
62 op->type = type;
64 return op;
65 }
67 void
68 free_op(struct op *op)
69 {
70 /* TODO: probably more... */
71 free(op);
72 }
74 struct op *
75 op_assign(char *sym, struct op *expr)
76 {
77 struct op *op;
79 op = newop(OP_ASSIGN);
80 op->v.assign.name = sym;
81 op->v.assign.expr = expr;
83 return op;
84 }
86 struct op *
87 op_assert(struct op *expr)
88 {
89 struct op *op;
91 op = newop(OP_ASSERT);
92 op->v.assert = expr;
94 return op;
95 }
97 struct op *
98 op_var(char *sym)
99 {
100 struct op *op;
102 op = newop(OP_VAR);
103 op->v.var = sym;
105 return op;
108 struct op *
109 op_lit_str(char *str)
111 struct op *op;
113 op = newop(OP_LITERAL);
114 op->v.literal.type = V_NUM;
115 op->v.literal.v.str = str;
117 return op;
120 struct op *
121 op_lit_num(uint64_t n)
123 struct op *op;
125 op = newop(OP_LITERAL);
126 op->v.literal.type = V_NUM;
127 op->v.literal.v.num = n;
129 return op;
132 struct op *
133 op_cmp_eq(struct op *a, struct op *b)
135 struct op *op;
137 op = newop(OP_CMP_EQ);
138 op->v.cmp_eq.a = a;
139 op->v.cmp_eq.b = b;
141 return op;
144 struct op *
145 op_cast(struct op *expr, int totype)
147 struct op *op;
149 op = newop(OP_CAST);
150 op->v.cast.expr = expr;
151 op->v.cast.totype = totype;
153 return op;
156 void
157 pp_val(struct value *val)
159 switch (val->type) {
160 case V_SYM:
161 printf("%s", val->v.str);
162 break;
163 case V_STR:
164 printf("\"%s\"", val->v.str);
165 break;
166 case V_NUM:
167 case V_U8:
168 case V_U16:
169 case V_U32:
170 printf("%"PRIu64, val->v.num);
171 break;
172 default:
173 printf("<unknown value>");
174 break;
178 int
179 val_trueish(struct value *a)
181 return a->type == V_NUM && a->v.num;
184 static inline int
185 val_isnum(struct value *a)
187 return a->type == V_NUM
188 || a->type == V_U8
189 || a->type == V_U16
190 || a->type == V_U32;
193 int
194 val_eq(struct value *a, struct value *b)
196 if (val_isnum(a) && val_isnum(b))
197 return a->v.num == b->v.num;
199 if (a->type != b->type)
200 return 0;
202 switch (a->type) {
203 case V_STR:
204 case V_SYM:
205 return !strcmp(a->v.str, b->v.str);
208 return 0;
211 void
212 pp_op(struct op *op)
214 switch (op->type) {
215 case OP_ASSIGN:
216 printf("%s = ", op->v.assign.name);
217 pp_op(op->v.assign.expr);
218 break;
219 case OP_ASSERT:
220 printf("assert ");
221 pp_op(op->v.assert);
222 break;
223 case OP_FUNCALL:
224 printf("funcall()");
225 break;
226 case OP_LITERAL:
227 pp_val(&op->v.literal);
228 break;
229 case OP_VAR:
230 printf("%s", op->v.var);
231 break;
232 case OP_CAST:
233 pp_op(op->v.cast.expr);
234 printf(":");
235 switch (op->v.cast.totype) {
236 case V_U8: printf("u8"); break;
237 case V_U16: printf("u16"); break;
238 case V_U32: printf("u32"); break;
239 case V_STR: printf("str"); break;
240 default: printf("???"); break;
242 break;
243 case OP_CMP_EQ:
244 pp_op(op->v.cmp_eq.a);
245 printf(" == ");
246 pp_op(op->v.cmp_eq.b);
247 break;
248 default:
249 printf(" ???[%d] ", op->type);
253 void
254 pp_block(struct op *op)
256 while (op != NULL) {
257 printf("> ");
258 pp_op(op);
259 printf("\n");
261 op = op->next;
265 static inline void
266 popv(struct value *v)
268 if (stackh == 0)
269 errx(1, "can't pop the stack: underflow");
270 memcpy(v, &vstack[--stackh], sizeof(*v));
272 #if DEBUG
273 printf("popping "); pp_val(v); printf("\n");
274 #endif
277 static inline void
278 pushv(struct value *v)
280 if (stackh == STACK_HEIGHT)
281 errx(1, "can't push the stack: overflow");
283 #if DEBUG
284 printf("pushing "); pp_val(v); printf("\n");
285 #endif
287 memcpy(&vstack[stackh++], v, sizeof(*v));
290 static inline void
291 pushbool(int n)
293 pushv(n ? &v_true : &v_false);
296 int
297 eval(struct op *op)
299 struct value a, b;
300 struct proc *proc;
301 struct op *t;
302 int i, ret;
304 #if DEBUG
305 pp_op(op);
306 printf("\n");
307 #endif
309 switch (op->type) {
310 case OP_ASSIGN:
311 printf("TODO: assignment\n");
312 break;
314 case OP_ASSERT:
315 if ((ret = eval(op->v.assert)) != TEST_PASSED)
316 return ret;
317 popv(&a);
318 if (!val_trueish(&a)) {
319 printf("assertion failed: ");
320 pp_op(op->v.assert);
321 printf("\n");
322 return TEST_FAILED;
324 break;
326 case OP_FUNCALL:
327 /* TODO: arity check! */
329 for (i = 0; i < op->v.funcall.argc; ++i) {
330 t = &op->v.funcall.argv[i];
331 if ((ret = eval(t)) != TEST_PASSED)
332 return ret;
335 proc = op->v.funcall.proc;
336 if (proc->nativefn != NULL)
337 proc->nativefn(i);
338 else if ((ret = eval(proc->body)) != TEST_PASSED)
339 return ret;
340 break;
342 case OP_LITERAL:
343 pushv(&op->v.literal);
344 break;
346 case OP_VAR:
347 printf("TODO: load variable\n");
348 break;
350 case OP_CAST:
351 printf("TODO: cast value\n");
352 break;
354 case OP_CMP_EQ:
355 if ((ret = eval(op->v.cmp_eq.a)) != TEST_PASSED)
356 return ret;
357 if ((ret = eval(op->v.cmp_eq.b)) != TEST_PASSED)
358 return ret;
360 popv(&b);
361 popv(&a);
362 pushbool(val_eq(&a, &b));
364 break;
366 default:
367 abort();
370 if (op->next)
371 return eval(op->next);
372 return TEST_PASSED;
375 void
376 prepare_funcall(struct op *base)
378 if (curr_argv != NULL)
379 err(1, "can't funcall during funcall");
381 curr_argv = base;
382 curr_argc = 0;
385 void
386 push_arg(struct op *op)
388 curr_argv->next = op;
389 curr_argv = op;
390 curr_argc++;
393 struct op *
394 op_funcall(struct proc *proc, struct op *base)
396 struct op *op;
398 op = newop(OP_FUNCALL);
399 op->v.funcall.proc = proc;
400 op->v.funcall.argv = base->next;
401 op->v.funcall.argc = curr_argc;
403 curr_argv = NULL;
405 return op;
408 void
409 add_builtin_proc(const char *name, int (*fn)(int))
411 struct proc *proc;
413 proc = xcalloc(1, sizeof(*proc));
414 proc->name = xstrdup(name);
415 proc->nativefn = fn;
417 TAILQ_INSERT_HEAD(&procs, proc, entry);
420 void
421 prepare_proc(char *name)
423 if (curr_proc != NULL)
424 err(1, "can't recursively create a proc!");
426 curr_proc = xcalloc(1, sizeof(*curr_proc));
427 curr_proc->name = name;
429 curr_argv = &curr_proc->tmp_args;
431 curr_argv = NULL;
432 curr_argc = 0;
435 void
436 proc_setup_body(void)
438 struct op *next, *op = curr_proc->tmp_args.next;
439 int i;
441 i = 0;
442 while (op != NULL) {
443 if (op->type != OP_VAR)
444 errx(1, "invalid argument in proc definition: "
445 "got type %d but want OP_VAR", op->type);
446 assert(i < curr_argc && curr_argc < MAXWELEM);
447 curr_proc->args[i] = xstrdup(op->v.var);
448 next = op->next;
449 free_op(op);
452 curr_proc->minargs = curr_argc;
455 void
456 proc_done(void)
458 TAILQ_INSERT_HEAD(&procs, curr_proc, entry);
460 curr_proc = NULL;
463 void
464 block_push(struct op *op)
466 if (curr_block == NULL) {
467 curr_proc->body = op;
468 curr_block = op;
469 } else {
470 curr_block->next = op;
471 curr_block = op;
475 struct proc *
476 proc_by_name(const char *name)
478 struct proc *p;
480 TAILQ_FOREACH(p, &procs, entry) {
481 if (!strcmp(p->name, name))
482 return p;
485 return NULL;
488 void
489 prepare_test(char *name, char *dir)
491 assert(curr_test == NULL);
493 prepare_proc(xstrdup("<test-internal>"));
495 curr_test = xcalloc(1, sizeof(*curr_test));
496 curr_test->name = name;
497 curr_test->dir = dir;
498 curr_test->proc = curr_proc;
501 void
502 test_done(void)
504 TAILQ_INSERT_HEAD(&tests, curr_test, entry);
505 curr_test = NULL;
508 static int
509 builtin_dummy(int argc)
511 printf("dummy! yay!\n");
512 return 0;
515 static int
516 run_test(struct test *t)
518 #if DEBUG
519 puts("=====================");
520 pp_block(t->proc->body);
521 puts("=====================");
522 #endif
524 return eval(t->proc->body);
527 int
528 main(int argc, char **argv)
530 struct test *t;
531 int i, passed = 0, failed = 0, skipped = 0;
533 log_init(1, LOG_DAEMON);
534 log_setverbose(1);
536 add_builtin_proc("dummy", builtin_dummy);
538 for (i = 1; i < argc; ++i)
539 loadfile(argv[i]);
541 TAILQ_FOREACH(t, &tests, entry) {
542 switch (run_test(t)) {
543 case TEST_PASSED: passed++; break;
544 case TEST_FAILED: failed++; break;
545 case TEST_SKIPPED: skipped++; break;
549 printf("passed = %d\n", passed);
550 printf("failed = %d\n", failed);
551 printf("skipped = %d\n", skipped);
553 return failed != 0;