2 * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
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.
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.
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;
43 #define STACK_HEIGHT 16
44 static struct value vstack[STACK_HEIGHT];
47 static struct value v_false = {.type = V_NUM, .v = {.num = 0}};
48 static struct value v_true = {.type = V_NUM, .v = {.num = 1}};
51 global_set(char *sym, struct op *op)
53 assert(op->type == OP_LITERAL);
61 op = xcalloc(1, sizeof(*op));
68 free_op(struct op *op)
70 /* TODO: probably more... */
75 op_assign(char *sym, struct op *expr)
79 op = newop(OP_ASSIGN);
80 op->v.assign.name = sym;
81 op->v.assign.expr = expr;
87 op_assert(struct op *expr)
91 op = newop(OP_ASSERT);
109 op_lit_str(char *str)
113 op = newop(OP_LITERAL);
114 op->v.literal.type = V_NUM;
115 op->v.literal.v.str = str;
121 op_lit_num(uint64_t n)
125 op = newop(OP_LITERAL);
126 op->v.literal.type = V_NUM;
127 op->v.literal.v.num = n;
133 op_cmp_eq(struct op *a, struct op *b)
137 op = newop(OP_CMP_EQ);
145 op_cast(struct op *expr, int totype)
150 op->v.cast.expr = expr;
151 op->v.cast.totype = totype;
157 pp_val(struct value *val)
161 printf("%s", val->v.str);
164 printf("\"%s\"", val->v.str);
170 printf("%"PRIu64, val->v.num);
173 printf("<unknown value>");
179 val_trueish(struct value *a)
181 return a->type == V_NUM && a->v.num;
185 val_isnum(struct value *a)
187 return a->type == V_NUM
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)
205 return !strcmp(a->v.str, b->v.str);
216 printf("%s = ", op->v.assign.name);
217 pp_op(op->v.assign.expr);
227 pp_val(&op->v.literal);
230 printf("%s", op->v.var);
233 pp_op(op->v.cast.expr);
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;
244 pp_op(op->v.cmp_eq.a);
246 pp_op(op->v.cmp_eq.b);
249 printf(" ???[%d] ", op->type);
254 pp_block(struct op *op)
266 popv(struct value *v)
269 errx(1, "can't pop the stack: underflow");
270 memcpy(v, &vstack[--stackh], sizeof(*v));
273 printf("popping "); pp_val(v); printf("\n");
278 pushv(struct value *v)
280 if (stackh == STACK_HEIGHT)
281 errx(1, "can't push the stack: overflow");
284 printf("pushing "); pp_val(v); printf("\n");
287 memcpy(&vstack[stackh++], v, sizeof(*v));
293 pushv(n ? &v_true : &v_false);
311 printf("TODO: assignment\n");
315 if ((ret = eval(op->v.assert)) != TEST_PASSED)
318 if (!val_trueish(&a)) {
319 printf("assertion failed: ");
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)
335 proc = op->v.funcall.proc;
336 if (proc->nativefn != NULL)
338 else if ((ret = eval(proc->body)) != TEST_PASSED)
343 pushv(&op->v.literal);
347 printf("TODO: load variable\n");
351 printf("TODO: cast value\n");
355 if ((ret = eval(op->v.cmp_eq.a)) != TEST_PASSED)
357 if ((ret = eval(op->v.cmp_eq.b)) != TEST_PASSED)
362 pushbool(val_eq(&a, &b));
371 return eval(op->next);
376 prepare_funcall(struct op *base)
378 if (curr_argv != NULL)
379 err(1, "can't funcall during funcall");
386 push_arg(struct op *op)
388 curr_argv->next = op;
394 op_funcall(struct proc *proc, struct op *base)
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;
409 add_builtin_proc(const char *name, int (*fn)(int))
413 proc = xcalloc(1, sizeof(*proc));
414 proc->name = xstrdup(name);
417 TAILQ_INSERT_HEAD(&procs, proc, entry);
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;
436 proc_setup_body(void)
438 struct op *next, *op = curr_proc->tmp_args.next;
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);
452 curr_proc->minargs = curr_argc;
458 TAILQ_INSERT_HEAD(&procs, curr_proc, entry);
464 block_push(struct op *op)
466 if (curr_block == NULL) {
467 curr_proc->body = op;
470 curr_block->next = op;
476 proc_by_name(const char *name)
480 TAILQ_FOREACH(p, &procs, entry) {
481 if (!strcmp(p->name, name))
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;
504 TAILQ_INSERT_HEAD(&tests, curr_test, entry);
509 builtin_dummy(int argc)
511 printf("dummy! yay!\n");
516 run_test(struct test *t)
519 puts("=====================");
520 pp_block(t->proc->body);
521 puts("=====================");
524 return eval(t->proc->body);
528 main(int argc, char **argv)
531 int i, passed = 0, failed = 0, skipped = 0;
533 log_init(1, LOG_DAEMON);
536 add_builtin_proc("dummy", builtin_dummy);
538 for (i = 1; i < argc; ++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);