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 opstacks blocks = TAILQ_HEAD_INITIALIZER(blocks);
36 static struct opstacks args = TAILQ_HEAD_INITIALIZER(args);
38 #define STACK_HEIGHT 16
39 static struct value vstack[STACK_HEIGHT];
40 static int stackh;
42 static struct value v_false = {.type = V_NUM, .v = {.num = 0}};
43 static struct value v_true = {.type = V_NUM, .v = {.num = 1}};
45 static inline struct opstack *
46 pushstack(struct opstacks *stack)
47 {
48 struct opstack *ops;
50 ops = xcalloc(1, sizeof(*ops));
51 TAILQ_INSERT_HEAD(stack, ops, entry);
52 return ops;
53 }
55 static inline struct opstack *
56 peek(struct opstacks *stack)
57 {
58 if (TAILQ_EMPTY(stack))
59 errx(1, "%s: args underflow", __func__);
61 return TAILQ_FIRST(stack);
62 }
64 static inline struct op *
65 finalize(struct opstacks *stack, int *argc)
66 {
67 struct opstack *ops;
68 struct op *op;
70 if (TAILQ_EMPTY(stack))
71 errx(1, "%s: args underflow", __func__);
73 ops = peek(stack);
74 TAILQ_REMOVE(&args, ops, entry);
75 op = ops->base.next;
77 if (argc != NULL)
78 *argc = ops->counter;
80 free(ops);
81 return op;
82 }
84 static inline void
85 push(struct opstacks *stack, struct op *op)
86 {
87 struct opstack *ops;
89 ops = peek(stack);
90 if (ops->last == NULL) {
91 ops->base.next = op;
92 ops->last = op;
93 } else {
94 ops->last->next = op;
95 ops->last = op;
96 }
98 ops->counter++;
99 }
101 void
102 global_set(char *sym, struct op *op)
104 assert(op->type == OP_LITERAL);
107 struct op *
108 newop(int type)
110 struct op *op;
112 op = xcalloc(1, sizeof(*op));
113 op->type = type;
115 return op;
118 void
119 free_op(struct op *op)
121 /* TODO: probably more... */
122 free(op);
125 struct op *
126 op_assign(char *sym, struct op *expr)
128 struct op *op;
130 op = newop(OP_ASSIGN);
131 op->v.assign.name = sym;
132 op->v.assign.expr = expr;
134 return op;
137 struct op *
138 op_assert(struct op *expr)
140 struct op *op;
142 op = newop(OP_ASSERT);
143 op->v.assert = expr;
145 return op;
148 struct op *
149 op_var(char *sym)
151 struct op *op;
153 op = newop(OP_VAR);
154 op->v.var = sym;
156 return op;
159 struct op *
160 op_lit_str(char *str)
162 struct op *op;
164 op = newop(OP_LITERAL);
165 op->v.literal.type = V_NUM;
166 op->v.literal.v.str = str;
168 return op;
171 struct op *
172 op_lit_num(uint64_t n)
174 struct op *op;
176 op = newop(OP_LITERAL);
177 op->v.literal.type = V_NUM;
178 op->v.literal.v.num = n;
180 return op;
183 struct op *
184 op_cmp_eq(struct op *a, struct op *b)
186 struct op *op;
188 op = newop(OP_CMP_EQ);
189 op->v.cmp_eq.a = a;
190 op->v.cmp_eq.b = b;
192 return op;
195 struct op *
196 op_cast(struct op *expr, int totype)
198 struct op *op;
200 op = newop(OP_CAST);
201 op->v.cast.expr = expr;
202 op->v.cast.totype = totype;
204 return op;
207 void
208 ppf_val(FILE *f, struct value *val)
210 switch (val->type) {
211 case V_SYM:
212 fprintf(f, "%s", val->v.str);
213 break;
214 case V_STR:
215 fprintf(f, "\"%s\"", val->v.str);
216 break;
217 case V_NUM:
218 case V_U8:
219 case V_U16:
220 case V_U32:
221 fprintf(f, "%"PRIu64, val->v.num);
222 break;
223 default:
224 fprintf(f, "<unknown value>");
225 break;
229 void
230 pp_val(struct value *val)
232 ppf_val(stdout, val);
235 int
236 val_trueish(struct value *a)
238 return a->type == V_NUM && a->v.num;
241 static inline int
242 val_isnum(struct value *a)
244 return a->type == V_NUM
245 || a->type == V_U8
246 || a->type == V_U16
247 || a->type == V_U32;
250 int
251 val_eq(struct value *a, struct value *b)
253 if (val_isnum(a) && val_isnum(b))
254 return a->v.num == b->v.num;
256 if (a->type != b->type)
257 return 0;
259 switch (a->type) {
260 case V_STR:
261 case V_SYM:
262 return !strcmp(a->v.str, b->v.str);
265 return 0;
268 static inline const char *
269 pp_totype(int totype)
271 /*
272 * Not all of these are valid cast type thought, including
273 * every possibility only to aid debugging.
274 */
275 switch (totype) {
276 case V_STR: return "str";
277 case V_SYM: return "sym";
278 case V_NUM: return "num";
279 case V_QID: return "qid";
280 case V_U8: return "u8";
281 case V_U16: return "u16";
282 case V_U32: return "u32";
283 default: return "unknown";
287 int
288 val_cast(struct value *a, int totype)
290 uint64_t v;
292 #define NUMCAST(v, totype, max) do { \
293 if (v >= max) { \
294 fprintf(stderr, "Can't cast %"PRIu64 \
295 " to %s\n", v, pp_totype(totype)); \
296 return EVAL_ERR; \
297 } \
298 a->type = totype; \
299 return EVAL_OK; \
300 } while (0)
302 if (!val_isnum(a)) {
303 fprintf(stderr, "Can't cast ");
304 ppf_val(stderr, a);
305 fprintf(stderr, " to type %s\n", pp_totype(totype));
306 return EVAL_ERR;
309 v = a->v.num;
310 switch (totype) {
311 case V_U8: NUMCAST(v, totype, UINT8_MAX);
312 case V_U16: NUMCAST(v, totype, UINT16_MAX);
313 case V_U32: NUMCAST(v, totype, UINT32_MAX);
314 default:
315 fprintf(stderr, "Can't cast %"PRIu64" to %s\n",
316 v, pp_totype(totype));
317 return EVAL_ERR;
320 #undef NUMCAST
323 void
324 pp_op(struct op *op)
326 switch (op->type) {
327 case OP_ASSIGN:
328 printf("%s = ", op->v.assign.name);
329 pp_op(op->v.assign.expr);
330 break;
331 case OP_ASSERT:
332 printf("assert ");
333 pp_op(op->v.assert);
334 break;
335 case OP_FUNCALL:
336 printf("funcall()");
337 break;
338 case OP_LITERAL:
339 pp_val(&op->v.literal);
340 break;
341 case OP_VAR:
342 printf("%s", op->v.var);
343 break;
344 case OP_CAST:
345 pp_op(op->v.cast.expr);
346 printf(":");
347 switch (op->v.cast.totype) {
348 case V_U8: printf("u8"); break;
349 case V_U16: printf("u16"); break;
350 case V_U32: printf("u32"); break;
351 case V_STR: printf("str"); break;
352 default: printf("???"); break;
354 break;
355 case OP_CMP_EQ:
356 pp_op(op->v.cmp_eq.a);
357 printf(" == ");
358 pp_op(op->v.cmp_eq.b);
359 break;
360 default:
361 printf(" ???[%d] ", op->type);
365 void
366 pp_block(struct op *op)
368 while (op != NULL) {
369 printf("> ");
370 pp_op(op);
371 printf("\n");
373 op = op->next;
377 static inline void
378 popv(struct value *v)
380 if (stackh == 0)
381 errx(1, "can't pop the stack: underflow");
382 memcpy(v, &vstack[--stackh], sizeof(*v));
384 #if DEBUG
385 printf("popping "); pp_val(v); printf("\n");
386 #endif
389 static inline void
390 pushv(struct value *v)
392 if (stackh == STACK_HEIGHT)
393 errx(1, "can't push the stack: overflow");
395 #if DEBUG
396 printf("pushing "); pp_val(v); printf("\n");
397 #endif
399 memcpy(&vstack[stackh++], v, sizeof(*v));
402 static inline void
403 pushbool(int n)
405 pushv(n ? &v_true : &v_false);
408 int
409 eval(struct op *op)
411 struct value a, b;
412 struct proc *proc;
413 struct op *t;
414 int i, ret;
416 #if DEBUG
417 pp_op(op);
418 printf("\n");
419 #endif
421 switch (op->type) {
422 case OP_ASSIGN:
423 printf("TODO: assignment\n");
424 break;
426 case OP_ASSERT:
427 if ((ret = eval(op->v.assert)) != EVAL_OK)
428 return ret;
429 popv(&a);
430 if (!val_trueish(&a)) {
431 printf("assertion failed: ");
432 pp_op(op->v.assert);
433 printf("\n");
434 return EVAL_ERR;
436 break;
438 case OP_FUNCALL:
439 /* TODO: arity check! */
441 for (i = 0; i < op->v.funcall.argc; ++i) {
442 t = &op->v.funcall.argv[i];
443 if ((ret = eval(t)) != EVAL_OK)
444 return ret;
447 proc = op->v.funcall.proc;
448 if (proc->nativefn != NULL)
449 proc->nativefn(i);
450 else if ((ret = eval(proc->body)) != EVAL_OK)
451 return ret;
452 break;
454 case OP_LITERAL:
455 pushv(&op->v.literal);
456 break;
458 case OP_VAR:
459 printf("TODO: load variable\n");
460 break;
462 case OP_CAST:
463 if ((ret = eval(op->v.cast.expr)) != EVAL_OK)
464 return ret;
465 popv(&a);
466 if ((ret = val_cast(&a, op->v.cast.totype)) != EVAL_OK)
467 return ret;
468 pushv(&a);
469 break;
471 case OP_CMP_EQ:
472 if ((ret = eval(op->v.cmp_eq.a)) != EVAL_OK)
473 return ret;
474 if ((ret = eval(op->v.cmp_eq.b)) != EVAL_OK)
475 return ret;
477 popv(&b);
478 popv(&a);
479 pushbool(val_eq(&a, &b));
481 break;
483 default:
484 fprintf(stderr, "invalid op, aborting.\n");
485 abort();
488 if (op->next)
489 return eval(op->next);
490 return EVAL_OK;
493 void
494 prepare_funcall(void)
496 pushstack(&args);
499 void
500 push_arg(struct op *op)
502 push(&args, op);
505 struct op *
506 op_funcall(struct proc *proc)
508 struct op *op, *argv;
509 int argc;
511 argv = finalize(&args, &argc);
513 op = newop(OP_FUNCALL);
514 op->v.funcall.proc = proc;
515 op->v.funcall.argv = argv;
516 op->v.funcall.argc = argc;
518 return op;
521 void
522 add_builtin_proc(const char *name, int (*fn)(int))
524 struct proc *proc;
526 proc = xcalloc(1, sizeof(*proc));
527 proc->name = xstrdup(name);
528 proc->nativefn = fn;
530 TAILQ_INSERT_HEAD(&procs, proc, entry);
533 void
534 prepare_proc(void)
536 pushstack(&args);
539 int
540 proc_setup_body(void)
542 struct opstack *argv;
543 struct op *op;
544 int i;
546 argv = peek(&args);
547 for (i = 0, op = argv->base.next; op != NULL; i++) {
548 /*
549 * TODO: should free the whole list but.., we're gonna
550 * exit real soon(tm)!
551 */
552 if (op->type != OP_VAR)
553 return 0;
555 op = op->next;
558 assert(i == argv->counter);
559 pushstack(&blocks);
560 return 1;
563 void
564 proc_done(char *name)
566 struct proc *proc;
567 struct op *op, *next, *argv, *body;
568 int i, argc;
570 argv = finalize(&args, &argc);
571 body = finalize(&blocks, NULL);
573 proc = xcalloc(1, sizeof(*proc));
574 proc->name = name;
575 proc->minargs = argc;
577 for (i = 0, op = argv; op != NULL; ++i) {
578 proc->args[i] = xstrdup(op->v.var);
580 next = op->next;
581 free_op(op);
582 op = next;
584 assert(i == argc);
586 proc->body = body;
588 TAILQ_INSERT_HEAD(&procs, proc, entry);
591 void
592 block_push(struct op *op)
594 push(&blocks, op);
597 struct proc *
598 proc_by_name(const char *name)
600 struct proc *p;
602 TAILQ_FOREACH(p, &procs, entry) {
603 if (!strcmp(p->name, name))
604 return p;
607 return NULL;
610 void
611 prepare_test(void)
613 pushstack(&blocks);
616 void
617 test_done(char *name, char *dir)
619 struct test *test;
621 test = xcalloc(1, sizeof(*test));
622 test->name = name;
623 test->dir = dir;
624 test->body = finalize(&blocks, NULL);
626 if (TAILQ_EMPTY(&tests))
627 TAILQ_INSERT_HEAD(&tests, test, entry);
628 else
629 TAILQ_INSERT_TAIL(&tests, test, entry);
632 static int
633 builtin_dummy(int argc)
635 printf("dummy! yay!\n");
636 return EVAL_OK;
639 static int
640 run_test(struct test *t)
642 #if DEBUG
643 puts("=====================");
644 pp_block(t->body);
645 puts("=====================");
646 #endif
648 return eval(t->body);
651 int
652 main(int argc, char **argv)
654 struct test *t;
655 int i, passed = 0, failed = 0, skipped = 0;
657 log_init(1, LOG_DAEMON);
658 log_setverbose(1);
660 add_builtin_proc("dummy", builtin_dummy);
662 for (i = 1; i < argc; ++i)
663 loadfile(argv[i]);
665 i = 0;
666 TAILQ_FOREACH(t, &tests, entry) {
667 printf("===> running test \"%s\"... ", t->name);
668 fflush(stdout);
670 switch (run_test(t)) {
671 case EVAL_OK:
672 printf("ok!\n");
673 passed++;
674 break;
675 case EVAL_ERR:
676 failed++;
677 /* we've already printed the failure */
678 printf("\n");
679 break;
680 case EVAL_SKIP:
681 printf("skipped!\n");
682 skipped++;
683 break;
686 i++;
689 printf("passed %d/%d\n", passed, i);
690 printf("failed %d\n", failed);
691 printf("skipped %d\n", skipped);
693 return failed != 0;