summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bin/fspec/dump.c881
-rw-r--r--src/compiler/compiler.lm298
-rw-r--r--src/compiler/expr.lm410
-rw-r--r--src/compiler/types.lm55
-rw-r--r--src/fspec/bcode.c199
-rw-r--r--src/fspec/bcode.h92
-rw-r--r--src/fspec/lexer.h25
-rw-r--r--src/fspec/memory.h8
-rw-r--r--src/fspec/private/bcode-types.h16
-rw-r--r--src/fspec/ragel/lexer-expr.h20
-rw-r--r--src/fspec/ragel/lexer-expr.rl118
-rw-r--r--src/fspec/ragel/lexer-stack.h42
-rw-r--r--src/fspec/ragel/lexer-stack.rl153
-rw-r--r--src/fspec/ragel/lexer.rl180
-rw-r--r--src/fspec/ragel/validator.rl96
-rw-r--r--src/fspec/validator.h19
-rw-r--r--src/util/membuf.c31
-rw-r--r--src/util/membuf.h14
18 files changed, 763 insertions, 1894 deletions
diff --git a/src/bin/fspec/dump.c b/src/bin/fspec/dump.c
deleted file mode 100644
index 8ca53b2..0000000
--- a/src/bin/fspec/dump.c
+++ /dev/null
@@ -1,881 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <limits.h>
-#include <string.h>
-#include <assert.h>
-#include <err.h>
-
-#include <iconv.h>
-#include <errno.h>
-#include <locale.h>
-#include <langinfo.h>
-#include <squash.h>
-
-#include <fspec/bcode.h>
-#include <fspec/lexer.h>
-#include <fspec/validator.h>
-#include "fspec/ragel/lexer-expr.h"
-#include "util/membuf.h"
-
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
-
-#if 0
-
-static size_t
-to_hex(const uint8_t *buf, const size_t buf_sz, char *out, const size_t out_sz, const bool reverse)
-{
- assert(out);
- const char nibble[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
- const uint8_t nbs = sizeof(nibble) - 1;
-
- size_t w = 0, last_non_zero = w;
- for (size_t i = 0; i < buf_sz && out_sz > 2 && w < out_sz - 2; ++i) {
- for (uint8_t c = 0; c < CHAR_BIT / 8 && w < out_sz; ++c) {
- const size_t idx = (reverse ? (buf_sz - 1) - i : i);
- const uint8_t hi = (buf[idx] >> (4 * (c + 1))) & nbs;
- const uint8_t lo = (buf[idx] >> (8 * c)) & nbs;
-
- if (w || hi || lo) {
- out[w++] = nibble[hi];
- out[w++] = nibble[lo];
- last_non_zero = (hi || lo ? w : last_non_zero);
- }
- }
- }
-
- if (!w) {
- out[w++] = nibble[0];
- out[w++] = nibble[0];
- } else {
- w = last_non_zero;
- }
-
- assert(w < out_sz);
- out[w] = 0;
- return w;
-}
-
-static void
-print_dec(const uint8_t *buf, const size_t size, const bool is_signed)
-{
- char hex[2 * sizeof(fspec_num) + 1];
- to_hex(buf, size, hex, sizeof(hex), true);
-
- static_assert(sizeof(fspec_num) <= sizeof(uint64_t), "fspec_num is larger than uint64_t");
-
- if (is_signed) {
- printf("%ld", (int64_t)strtoll(hex, NULL, 16));
- } else {
- printf("%lu", (uint64_t)strtoull(hex, NULL, 16));
- }
-}
-
-static void
-print_udec(const uint8_t *buf, const size_t size)
-{
- print_dec(buf, size, false);
-}
-
-static void
-print_sdec(const uint8_t *buf, const size_t size)
-{
- print_dec(buf, size, true);
-}
-
-static void
-print_hex(const uint8_t *buf, const size_t size)
-{
- char hex[2 * sizeof(fspec_num) + 1];
- to_hex(buf, size, hex, sizeof(hex), true);
- printf("0x%s", hex);
-}
-
-static void
-print_array(const uint8_t *buf, const size_t size, const size_t nmemb, void (*fun)(const uint8_t *buf, const size_t size))
-{
- const int indent = 4;
- if (nmemb > 8) {
- printf("{\n%*s", indent, "");
- } else if (nmemb > 1) {
- printf("{ ");
- }
-
- for (size_t n = 0; n < nmemb; ++n) {
- fun(buf + n * size, size);
- printf("%s", (nmemb > 1 && n + 1 < nmemb ? ", " : ""));
-
- if (n + 1 < nmemb && !((n + 1) % 8))
- printf("\n%*s", indent, "");
- }
-
- printf("%s\n", (nmemb > 8 ? "\n}" : (nmemb > 1 ? " }" : "")));
-}
-
-static void
-print_str(const char *buf, const size_t size, const size_t nmemb)
-{
- const bool has_nl = memchr(buf, '\n', size * nmemb);
- if (has_nl)
- puts("```");
-
- for (size_t n = 0; n < size * nmemb && buf[n] != 0; ++n)
- printf("%c", buf[n]);
-
- puts((has_nl ? "```" : ""));
-}
-
-struct code {
- const enum fspec_op *start, *end, *data;
-};
-
-static void
-dump_ops(const struct code *code)
-{
- for (const enum fspec_op *op = code->start; op; op = fspec_op_next(op, code->end, false)) {
- printf("%*s- ", (*op == FSPEC_OP_ARG ? 2 : 0), "");
- switch (*op) {
- case FSPEC_OP_HEADER:
- printf("header\n");
- break;
-
- case FSPEC_OP_DECLARATION:
- printf("declaration\n");
- break;
-
- case FSPEC_OP_READ:
- printf("read\n");
- break;
-
- case FSPEC_OP_GOTO:
- printf("goto\n");
- break;
-
- case FSPEC_OP_FILTER:
- printf("filter\n");
- break;
-
- case FSPEC_OP_VISUAL:
- printf("visual\n");
- break;
-
- case FSPEC_OP_ARG:
- {
- const enum fspec_arg *arg = (void*)(op + 1);
- printf("arg ");
- switch (*arg) {
- case FSPEC_ARG_STR:
- printf("str %s\n", fspec_arg_get_cstr(arg, code->data));
- break;
-
- case FSPEC_ARG_VAR:
- printf("var %" PRI_FSPEC_NUM "\n", fspec_arg_get_num(arg));
- break;
-
- case FSPEC_ARG_NUM:
- printf("num %" PRI_FSPEC_NUM "\n", fspec_arg_get_num(arg));
- break;
-
- case FSPEC_ARG_OFF:
- printf("off %" PRI_FSPEC_NUM "\n", fspec_arg_get_num(arg));
- break;
-
- case FSPEC_ARG_DAT:
- printf("dat %" PRI_FSPEC_NUM "\n", fspec_arg_get_num(arg));
- break;
-
- case FSPEC_ARG_EOF:
- printf("eof\n");
- break;
-
- case FSPEC_ARG_LAST:
- break;
- }
- }
- break;
-
- case FSPEC_OP_LAST:
- break;
- }
- }
-}
-
-static const enum fspec_op*
-get_last_struct(const struct code *code)
-{
- const enum fspec_op *last = NULL;
- for (const enum fspec_op *op = code->start; op; op = fspec_op_next(op, code->end, true)) {
- const enum fspec_arg *arg;
- if (*op == FSPEC_OP_DECLARATION &&
- (arg = fspec_op_get_arg(op, code->end, 1, 1<<FSPEC_ARG_NUM)) &&
- fspec_arg_get_num(arg) == FSPEC_DECLARATION_STRUCT) {
- last = op;
- }
- }
- return last;
-}
-
-struct dynbuf {
- void *data;
- size_t len, written;
-};
-
-static inline void
-dynbuf_resize(struct dynbuf *buf, const size_t size)
-{
- assert(buf);
- if (!(buf->data = realloc(buf->data, size)))
- err(EXIT_FAILURE, "realloc(%zu)", size);
-
- buf->len = size;
-}
-
-static inline void
-dynbuf_resize_if_needed(struct dynbuf *buf, const size_t size)
-{
- if (buf->len >= size)
- return;
-
- dynbuf_resize(buf, size);
-}
-
-static inline void
-dynbuf_grow_if_needed(struct dynbuf *buf, const size_t nmemb)
-{
- assert(buf);
- if (buf->len >= nmemb && buf->written <= buf->len - nmemb)
- return;
-
- dynbuf_resize(buf, buf->written + nmemb);
-}
-
-static inline void
-dynbuf_append(struct dynbuf *buf, const void *data, const size_t data_sz)
-{
- dynbuf_grow_if_needed(buf, data_sz);
- memcpy((char*)buf->data + buf->written, data, data_sz);
- buf->written += data_sz;
- assert(buf->written <= buf->len);
-}
-
-static inline void
-dynbuf_reset(struct dynbuf *buf)
-{
- assert(buf);
- buf->written = 0;
-}
-
-static inline void
-dynbuf_release(struct dynbuf *buf)
-{
- assert(buf);
- free(buf->data);
- *buf = (struct dynbuf){0};
-}
-
-static void
-display(const void *buf, const size_t size, const size_t nmemb, const bool is_signed, const enum fspec_visual visual)
-{
- switch (visual) {
- case FSPEC_VISUAL_NUL:
- puts("...");
- break;
-
- case FSPEC_VISUAL_STR:
- print_str(buf, size, nmemb);
- break;
-
- case FSPEC_VISUAL_HEX:
- print_array(buf, size, nmemb, print_hex);
- break;
-
- case FSPEC_VISUAL_DEC:
- print_array(buf, size, nmemb, (is_signed ? print_sdec : print_udec));
- break;
-
- case FSPEC_VISUAL_LAST:
- break;
- }
-}
-
-struct decl {
- struct dynbuf buf;
- const char *name;
- const void *start, *end;
- size_t nmemb;
- uint8_t size;
- enum fspec_visual visual;
- enum fspec_declaration declaration;
-};
-
-static void
-decl_display(const struct decl *decl)
-{
- assert(decl);
- assert(decl->size * decl->nmemb <= decl->buf.len);
- printf("%s: ", decl->name);
- display(decl->buf.data, decl->size, decl->nmemb, false, decl->visual);
-}
-
-static fspec_num
-decl_get_num(const struct decl *decl)
-{
- assert(decl);
- assert(decl->nmemb == 1);
- assert(decl->size * decl->nmemb <= decl->buf.len);
- char hex[2 * sizeof(fspec_num) + 1];
- to_hex(decl->buf.data, decl->size, hex, sizeof(hex), true);
- static_assert(sizeof(fspec_num) <= sizeof(uint64_t), "fspec_num is larger than uint64_t");
- return (fspec_num)strtoull(hex, NULL, 16);
-}
-
-static const char*
-decl_get_cstr(const struct decl *decl)
-{
- assert(decl);
- return decl->buf.data;
-}
-
-struct context {
- struct code code;
- struct decl *decl;
- fspec_num decl_count;
-};
-
-static fspec_num
-var_get_num(const struct context *context, const enum fspec_arg *arg)
-{
- assert(context && arg);
- return decl_get_num(&context->decl[fspec_arg_get_num(arg)]);
-}
-
-static const char*
-var_get_cstr(const struct context *context, const enum fspec_arg *arg)
-{
- assert(context && arg);
- return decl_get_cstr(&context->decl[fspec_arg_get_num(arg)]);
-}
-
-enum type {
- TYPE_NUM,
- TYPE_STR,
-};
-
-static enum type
-var_get_type(const struct context *context, const enum fspec_arg *arg)
-{
- assert(context && arg);
- const struct decl *decl = &context->decl[fspec_arg_get_num(arg)];
- switch (decl->visual) {
- case FSPEC_VISUAL_DEC:
- case FSPEC_VISUAL_HEX:
- case FSPEC_VISUAL_NUL:
- return TYPE_NUM;
-
- case FSPEC_VISUAL_STR:
- return TYPE_STR;
-
- case FSPEC_VISUAL_LAST:
- break;
- }
- return ~0;
-}
-
-static void
-filter_decompress(const struct context *context, struct decl *decl)
-{
- assert(decl);
-
- const enum fspec_arg *arg;
- if (!(arg = fspec_op_get_arg(context->code.start, context->code.end, 2, 1<<FSPEC_ARG_STR)))
- errx(EXIT_FAILURE, "missing compression");
-
- SquashCodec *codec;
- const char *algo = fspec_arg_get_cstr(arg, context->code.data);
- if (!(codec = squash_get_codec(algo)))
- errx(EXIT_FAILURE, "unknown compression '%s'", algo);
-
- SquashOptions *opts;
- if (!(opts = squash_options_new(codec, NULL)))
- errx(EXIT_FAILURE, "squash_options_new");
-
- size_t dsize = squash_codec_get_uncompressed_size(codec, decl->buf.len, decl->buf.data);
- dsize = (dsize ? dsize : decl->buf.len * 2);
-
- {
- const enum fspec_arg *var = arg;
- if ((arg = fspec_arg_next(arg, context->code.end, 1, 1<<FSPEC_ARG_NUM | 1<<FSPEC_ARG_VAR))) {
- var = arg;
-
- switch (*var) {
- case FSPEC_ARG_NUM:
- dsize = fspec_arg_get_num(arg);
- break;
-
- case FSPEC_ARG_VAR:
- dsize = var_get_num(context, arg);
- break;
-
- default:
- break;
- }
- }
-
- for (; (var = fspec_arg_next(var, context->code.end, 1, 1<<FSPEC_ARG_STR));) {
- const char *key = fspec_arg_get_cstr(var, context->code.data);
- if (!(var = fspec_arg_next(var, context->code.end, 1, ~0)))
- errx(EXIT_FAILURE, "expected argument for key '%s'", key);
-
- switch (*var) {
- case FSPEC_ARG_STR:
- squash_options_set_string(opts, key, fspec_arg_get_cstr(var, context->code.data));
- break;
-
- case FSPEC_ARG_NUM:
- squash_options_set_int(opts, key, fspec_arg_get_num(var));
- break;
-
- case FSPEC_ARG_VAR:
- if (var_get_type(context, var) == TYPE_STR) {
- squash_options_set_string(opts, key, var_get_cstr(context, var));
- } else {
- squash_options_set_int(opts, key, var_get_num(context, var));
- }
- break;
-
- default:
- break;
- }
- }
- }
-
- // what a horrible api
- squash_object_ref(opts);
-
- SquashStatus r;
- struct dynbuf buf = {0};
- dynbuf_resize(&buf, dsize);
- while ((r = squash_codec_decompress_with_options(codec, &buf.len, buf.data, decl->buf.len, decl->buf.data, opts)) == SQUASH_BUFFER_FULL)
- dynbuf_resize(&buf, dsize *= 2);
-
- dynbuf_resize_if_needed(&buf, (buf.written = buf.len));
- squash_object_unref(opts);
-
- if (r != SQUASH_OK)
- errx(EXIT_FAILURE, "squash_codec_decompress(%zu, %zu) = %d: %s", dsize, decl->buf.len, r, squash_status_to_string(r));
-
- dynbuf_release(&decl->buf);
- decl->buf = buf;
- decl->nmemb = buf.len / decl->size;
-}
-
-static void
-filter_decode(const struct context *context, struct decl *decl)
-{
- assert(decl);
-
- const enum fspec_arg *arg;
- if (!(arg = fspec_op_get_arg(context->code.start, context->code.end, 2, 1<<FSPEC_ARG_STR)))
- errx(EXIT_FAILURE, "missing encoding");
-
- const char *encoding = fspec_arg_get_cstr(arg, context->code.data);
-
- static const char *sys_encoding;
- if (!sys_encoding) {
- setlocale(LC_ALL, "");
- sys_encoding = nl_langinfo(CODESET);
- }
-
- iconv_t iv;
- if ((iv = iconv_open(sys_encoding, encoding)) == (iconv_t)-1)
- err(EXIT_FAILURE, "iconv_open(%s, %s)", sys_encoding, encoding);
-
- struct dynbuf buf = {0};
- const uint8_t *in = decl->buf.data;
- size_t in_left = decl->buf.written;
- do {
- char enc[1024], *out = enc;
- size_t out_left = sizeof(enc);
-
- errno = 0;
- if (iconv(iv, (char**)&in, &in_left, &out, &out_left) == (size_t)-1 && errno != E2BIG)
- err(EXIT_FAILURE, "iconv(%s, %s)", sys_encoding, encoding);
-
- dynbuf_append(&buf, enc, sizeof(enc) - out_left);
- } while (in_left > 0);
-
- iconv_close(iv);
-
- dynbuf_release(&decl->buf);
- decl->buf = buf;
- decl->nmemb = buf.len / decl->size;
-}
-
-static void
-call(const struct context *context, FILE *f)
-{
- assert(context && f);
-
- struct decl *decl = NULL;
- for (const enum fspec_op *op = context->code.start; op; op = fspec_op_next(op, context->code.end, true)) {
- if (decl && op == decl->end) {
- decl_display(decl);
- decl = NULL;
- }
-
- switch (*op) {
- case FSPEC_OP_DECLARATION:
- {
- const enum fspec_arg *arg;
- arg = fspec_op_get_arg(op, context->code.end, 2, 1<<FSPEC_ARG_NUM);
- decl = &context->decl[fspec_arg_get_num(arg)];
- dynbuf_reset(&decl->buf);
- }
- break;
-
- case FSPEC_OP_READ:
- {
- assert(decl);
- const enum fspec_arg *arg = fspec_op_get_arg(op, context->code.end, 1, 1<<FSPEC_ARG_NUM);
- static_assert(CHAR_BIT == 8, "doesn't work otherwere right now");
- decl->size = fspec_arg_get_num(arg) / 8;
- decl->nmemb = 0;
-
- for (const enum fspec_arg *var = arg; (var = fspec_arg_next(var, context->code.end, 1, ~0));) {
- switch (*var) {
- case FSPEC_ARG_NUM:
- case FSPEC_ARG_VAR:
- {
- const fspec_num v = (*var == FSPEC_ARG_NUM ? fspec_arg_get_num(var) : var_get_num(context, var));
- if (v == 0) {
- goto noop;
- } else if (v > 1) {
- const size_t nmemb = (decl->nmemb ? decl->nmemb : 1) * v;
- dynbuf_grow_if_needed(&decl->buf, decl->size * nmemb);
- const size_t read = fread((char*)decl->buf.data + decl->buf.written, decl->size, nmemb, f);
- decl->buf.written += decl->size * read;
- decl->nmemb += read;
- }
- }
- break;
-
- case FSPEC_ARG_STR:
- break;
-
- case FSPEC_ARG_EOF:
- {
- const size_t nmemb = (decl->nmemb ? decl->nmemb : 1);
- size_t read = 0, r = nmemb;
- while (r == nmemb) {
- dynbuf_grow_if_needed(&decl->buf, decl->size * nmemb);
- read += (r = fread((char*)decl->buf.data + decl->buf.written, decl->size, nmemb, f));
- decl->buf.written += decl->size * r;
- };
- decl->nmemb += read;
- }
- break;
-
- default:
- break;
- }
- }
-noop:
-
- if (!fspec_arg_next(arg, context->code.end, 1, ~0)) {
- dynbuf_grow_if_needed(&decl->buf, decl->size * 1);
- const size_t read = fread((char*)decl->buf.data + decl->buf.written, decl->size, 1, f);
- decl->buf.written += decl->size * read;
- decl->nmemb = read;
- }
-
- assert(decl->nmemb != 0);
- }
- break;
-
- case FSPEC_OP_GOTO:
- {
- decl = NULL;
- const enum fspec_arg *arg = fspec_op_get_arg(op, context->code.end, 1, 1<<FSPEC_ARG_VAR);
- const struct decl *d = &context->decl[fspec_arg_get_num(arg)];
- struct context c = *context;
- c.code.start = d->start;
- c.code.end = d->end;
-
- for (const enum fspec_arg *var = arg; (var = fspec_arg_next(var, context->code.end, 1, ~0));) {
- switch (*var) {
- case FSPEC_ARG_NUM:
- case FSPEC_ARG_VAR:
- {
- const fspec_num v = (*var == FSPEC_ARG_NUM ? fspec_arg_get_num(var) : var_get_num(context, var));
- for (fspec_num i = 0; i < v; ++i)
- call(&c, f);
- }
- break;
-
- // XXX: How to handle STR with stdin?
- // With fseek would be easy.
- case FSPEC_ARG_STR:
- break;
-
- case FSPEC_ARG_EOF:
- while (!feof(f))
- call(&c, f);
- break;
-
- default:
- break;
- }
- }
-
- if (!fspec_arg_next(arg, context->code.end, 1, ~0))
- call(&c, f);
- }
- break;
-
- case FSPEC_OP_FILTER:
- {
- assert(decl);
- const enum fspec_arg *arg = fspec_op_get_arg(op, context->code.end, 1, 1<<FSPEC_ARG_STR);
-
- const struct {
- const char *name;
- void (*fun)(const struct context*, struct decl*);
- } map[] = {
- { .name = "encoding", .fun = filter_decode },
- { .name = "compression", .fun = filter_decompress },
- };
-
- const char *filter = fspec_arg_get_cstr(arg, context->code.data);
- for (size_t i = 0; i < ARRAY_SIZE(map); ++i) {
- if (!strcmp(filter, map[i].name)) {
- struct context c = *context;
- c.code.start = op;
- map[i].fun(&c, decl);
- break;
- }
-
- if (i == ARRAY_SIZE(map) - 1)
- warnx("unknown filter '%s'", filter);
- }
- }
- break;
-
- case FSPEC_OP_VISUAL:
- {
- assert(decl);
- const enum fspec_arg *arg = fspec_op_get_arg(op, context->code.end, 1, 1<<FSPEC_ARG_NUM);
- decl->visual = fspec_arg_get_num(arg);
- }
- break;
-
- case FSPEC_OP_ARG:
- case FSPEC_OP_HEADER:
- case FSPEC_OP_LAST:
- break;
- }
- }
-
- if (decl && context->code.end == decl->end)
- decl_display(decl);
-}
-
-static void
-setup(const struct context *context)
-{
- assert(context);
-
- for (const enum fspec_op *op = context->code.start; op; op = fspec_op_next(op, context->code.end, true)) {
- switch (*op) {
- case FSPEC_OP_DECLARATION:
- {
- const enum fspec_arg *arg[4];
- arg[0] = fspec_op_get_arg(op, context->code.end, 1, 1<<FSPEC_ARG_NUM);
- arg[1] = fspec_arg_next(arg[0], context->code.end, 1, 1<<FSPEC_ARG_NUM);
- arg[2] = fspec_arg_next(arg[1], context->code.end, 1, 1<<FSPEC_ARG_OFF);
- arg[3] = fspec_arg_next(arg[2], context->code.end, 1, 1<<FSPEC_ARG_STR);
- const fspec_num id = fspec_arg_get_num(arg[1]);
- struct decl *decl = &context->decl[id];
- decl->declaration = fspec_arg_get_num(arg[0]);
- decl->name = fspec_arg_get_cstr(arg[3], context->code.data);
- decl->visual = FSPEC_VISUAL_DEC;
- decl->start = op;
- decl->end = (char*)op + fspec_arg_get_num(arg[2]);
- assert(!decl->buf.data);
- }
- break;
-
- default:
- break;
- }
- }
-}
-
-static void
-execute(const struct fspec_mem *mem)
-{
- assert(mem);
-
- struct context context = {
- .code.start = mem->data,
- .code.end = (void*)((char*)mem->data + mem->len),
- .code.data = mem->data
- };
-
- printf("output: %zu bytes\n", mem->len);
- dump_ops(&context.code);
-
- const enum fspec_arg *arg = fspec_op_get_arg(context.code.data, context.code.end, 2, 1<<FSPEC_ARG_NUM);
- context.decl_count = fspec_arg_get_num(arg);
-
- if (!(context.decl = calloc(context.decl_count, sizeof(*context.decl))))
- err(EXIT_FAILURE, "calloc(%zu, %zu)", context.decl_count, sizeof(*context.decl));
-
- setup(&context);
-
- puts("\nexecution:");
- context.code.start = get_last_struct(&context.code);
- assert(context.code.start);
- call(&context, stdin);
-
- for (fspec_num i = 0; i < context.decl_count; ++i)
- dynbuf_release(&context.decl[i].buf);
-
- free(context.decl);
-}
-#endif
-
-static FILE*
-fopen_or_die(const char *path, const char *mode)
-{
- assert(path && mode);
-
- FILE *f;
- if (!(f = fopen(path, mode)))
- err(EXIT_FAILURE, "fopen(%s, %s)", path, mode);
-
- return f;
-}
-
-#define container_of(ptr, type, member) ((type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member)))
-
-struct expr {
- struct fspec_expr expr;
- struct membuf output;
- FILE *file;
-};
-
-static size_t
-fspec_expr_write(struct fspec_expr *expr, const void *output, const size_t size, const size_t nmemb)
-{
- assert(expr && output);
- // struct expr *l = container_of(expr, struct expr, expr);
- (void)expr, (void)size, (void)nmemb;
- return nmemb;
-}
-
-static size_t
-fspec_expr_read(struct fspec_expr *expr, void *input, const size_t size, const size_t nmemb)
-{
- assert(expr && input);
- struct expr *l = container_of(expr, struct expr, expr);
- return fread(input, size, nmemb, l->file);
-}
-
-struct lexer {
- struct fspec_lexer lexer;
- struct membuf output;
- FILE *file;
-};
-
-static size_t
-fspec_lexer_write(struct fspec_lexer *lexer, const enum fspec_lexer_section section, const void *output, const size_t size, const size_t nmemb)
-{
- assert(lexer && output);
- // struct lexer *l = container_of(lexer, struct lexer, lexer);
- (void)lexer, (void)section, (void)size, (void)nmemb;
- return nmemb;
-}
-
-static size_t
-fspec_lexer_read(struct fspec_lexer *lexer, void *input, const size_t size, const size_t nmemb)
-{
- assert(lexer && input);
- struct lexer *l = container_of(lexer, struct lexer, lexer);
- return fread(input, size, nmemb, l->file);
-}
-
-#if 0
-static size_t
-fspec_validator_read(struct fspec_validator *validator, void *ptr, const size_t size, const size_t nmemb)
-{
- assert(validator && ptr);
- assert(ptr == validator->mem.input.data);
- const size_t read = validator->mem.input.len / size;
- assert((validator->mem.input.len && read == nmemb) || (!validator->mem.input.len && !read));
- validator->mem.input.len -= read * size;
- assert(validator->mem.input.len == 0);
- return read;
-}
-#endif
-
-int
-main(int argc, const char *argv[])
-{
- if (argc < 2)
- errx(EXIT_FAILURE, "usage: %s file.spec < data", argv[0]);
-
- char output[4096];
-
- {
- char input[4096];
- struct expr l = {
- .expr = {
- .ops.read = fspec_expr_read,
- .ops.write = fspec_expr_write,
- .mem.input = { .data = input, .len = sizeof(input) },
- },
- .file = fopen_or_die(argv[1], "rb"),
- .output.mem = { .data = output, .len = sizeof(output) },
- };
-
- if (!fspec_expr_parse(&l.expr, argv[1]))
- exit(EXIT_FAILURE);
-
- fclose(l.file);
- // bcode = l.expr.mem.output;
- }
-
- {
- char input[4096];
- struct lexer l = {
- .lexer = {
- .ops.read = fspec_lexer_read,
- .ops.write = fspec_lexer_write,
- .mem.input = { .data = input, .len = sizeof(input) },
- },
- .file = fopen_or_die(argv[1], "rb"),
- .output.mem = { .data = output, .len = sizeof(output) },
- };
-
- if (!fspec_lexer_parse(&l.lexer, argv[1]))
- exit(EXIT_FAILURE);
-
- fclose(l.file);
- // bcode = l.lexer.mem.output;
- }
-
-#if 0
-
- {
- struct fspec_validator validator = {
- .ops.read = fspec_validator_read,
- .mem.input = bcode,
- };
-
- if (!fspec_validator_parse(&validator, argv[1]))
- exit(EXIT_FAILURE);
- }
-
- execute(&bcode);
-#endif
- return EXIT_SUCCESS;
-}
diff --git a/src/compiler/compiler.lm b/src/compiler/compiler.lm
new file mode 100644
index 0000000..59b0ee3
--- /dev/null
+++ b/src/compiler/compiler.lm
@@ -0,0 +1,298 @@
+include 'expr.lm'
+
+context fspec
+ token WS / space /
+
+ context primitive
+ token TYPE_SIGN / [us] /
+ token TYPE_BITS / [1-9][0-9]* /
+ int strtoull(a:str, b:int) = c_strtoull
+
+ def type
+ signed:bool
+ bits:int
+ [TYPE_SIGN TYPE_BITS] {
+ lhs.signed = ($r1 == 's')
+ lhs.bits = strtoull($r2, 10)
+ }
+ end
+
+ context container
+ context enum
+ lex
+ ignore / space+ /
+ literal `= `, `{ `}
+ end
+
+ literal `enum
+
+ int
+ const_int_expr(expr:collapser::collapsed) {
+ if (!expr || !expr.result.value || !expr.result.value.number) reject
+ return expr.result.value.number.value
+ }
+
+ def item
+ value:int
+ [name:name::type `= expr::enum::type `, item] { lhs.value = const_int_expr(r3.collapsed) }
+ | [name:name::type `= expr::enum::type] { lhs.value = const_int_expr(r3.collapsed) }
+ | [name:name::type `, item] { lhs.value = 0 } # TODO: count
+ | [name:name::type] { lhs.value = 0 } # TODO: count
+
+ def type
+ name:str
+ items:item+
+ [type:`enum WS+ name::type? `{ item+ `}] { if (name::type in r3) lhs.name = $(name::type in r3) lhs.items = r5 }
+ end
+
+ context strukt # <- struct is taken :(
+ lex
+ ignore / space+ /
+ literal `{ `}
+ end
+
+ literal `struct
+
+ def item
+ [data:declaration::type]
+
+ def type
+ name:str
+ items:item+
+ [type:`struct WS+ name::type? `{ item+ `}] { if (name::type in r3) lhs.name = $(name::type in r3) lhs.items = r5 }
+ end
+
+ context select
+ lex
+ ignore / space+ /
+ literal `( `) `{ `} `*
+ end
+
+ literal `select
+
+ def item
+ [expr:expr::paren::type `) data:declaration::type]
+ | [expr:`* `) data:declaration::type]
+
+ def type
+ name:str
+ items:item+ # BUG: marking item+ with items: in the match below causes weird behaviour
+ [type:`select `( expr::paren::type `) `{ item+ `}] { lhs.items = r6 }
+ end
+
+ def type
+ [data:enum::type] | [data:strukt::type] | [data:select::type]
+ end
+
+ context declaration
+ lex
+ ignore / space+ /
+ literal `; `| `[ `]
+ end
+
+ literal `enum `struct
+ token VISUAL / 'nul' | 'dec' | 'hex' | 'str' /
+
+ def visual
+ [WS+ name:VISUAL]
+
+ def filter
+ [`| function:reference::function::type]
+
+ def length
+ [`[ expr:expr::bracket::type `]]
+
+ def extra
+ length:collapser::collapsed
+ [length* filter:filter* visual:visual?] {
+ f:str = ''
+ for l:length in repeat(r1) {
+ if (f != '')
+ f = f + '*'
+
+ if (l.expr.collapsed.result.value) {
+ f = f + '(' + $l.expr.collapsed.result.value + ')'
+ } else {
+ f = f + '(' + $l.expr.collapsed + ')'
+ }
+ }
+ lhs.length = collapser::collapsestr(f)
+ }
+
+ def type
+ # enum name <primitive> name <extra>;
+ [cref:`enum WS+ parent:name::type WS+ primitive:primitive::type WS+ name:name::type extra:extra `;] commit
+ # struct name name <extra>;
+ | [cref:`struct WS+ parent:name::type WS+ name:name::type extra:extra `;] commit
+ # <primitive> name <extra>;
+ | [primitive:primitive::type WS+ name:name::type extra:extra `;] commit
+ # select ((thing)) { ... } <extra>; INVALID
+ | [container::select::type extra `;] commit { reject }
+ # select ((thing)) { ... } <primitive> name <extra>; INVALID
+ | [container::select::type primitive::type WS+ name::type extra `;] commit { reject }
+ # struct (optional) { ... } <primitive> name <extra>; INVALID
+ | [container::strukt::type primitive::type WS+ name::type extra `;] commit { reject }
+ # enum (optional) { ... } <primitive> name <extra>;
+ | [container:container::type primitive:primitive::type WS+ name:name::type extra:extra `;] commit
+ # select ((expr)) { ... } name <extra>;
+ # struct (optional) { ... } name <extra>;
+ | [container:container::type name:name::type extra:extra `;]
+ # (enum|struct) name { ... };
+ | [container:container::type `;]
+ end
+
+ def source
+ [items:declaration::type*] commit
+end
+
+parse source:fspec::source[stdin]
+
+if (!source) {
+ print(error)
+ exit(1)
+}
+
+struct scope
+ names:map<str, map<str, any>>
+end
+
+global g_scopes:list<scope> = new list<scope>()
+
+void
+push_scope() {
+ s:scope = new scope()
+ s->names = new map<str, map<str, any>>()
+ g_scopes->push_head(s)
+}
+
+void
+pop_scope()
+{
+ g_scopes->pop_head()
+}
+
+any
+lookup_no_error(type:str, name:str) {
+ for s:scope in g_scopes {
+ cmap:map<str, any> = s->names->find(type)
+ if (cmap) {
+ var:any = cmap->find(name)
+ if (var)
+ return var
+ }
+ }
+ return nil
+}
+
+any
+insert(type:str, name:str, var:any)
+{
+ if (!name)
+ return var # <anon>
+
+ if (type != 'variable' && lookup_no_error(type, name)) {
+ print('`', type, ' ', name, '` is already declared as a `', type, '` in current scope!\n')
+ exit(1)
+ }
+
+ cmap:map<str, any> = g_scopes->top->names->find(type)
+
+ if (!cmap) {
+ cmap = new map<str, any>()
+ } else if (cmap->find(name)) {
+ print('`', type, ' ', name, '` is already declared as a `', type, '` in current scope!\n')
+ exit(1)
+ }
+
+ cmap->insert(name, var)
+ g_scopes->top->names->insert(type, cmap)
+ return var
+}
+
+any
+lookup(type:str, name:str)
+{
+ r:any = lookup_no_error(type, name)
+ if (!r) {
+ print('`', type, ' ', name, '` is not declared in this or outer scope!\n')
+ exit(1)
+ }
+ return r
+}
+
+str
+container_name_str(s:ref<str>) { if (!s) return '<anon>' return s }
+
+str
+signed_str(s:ref<bool>) { if (s) return 'signed' return 'unsigned' }
+
+void
+print_declaration(d:fspec::declaration::type)
+{
+ insert('variable', $d.name, d)
+ print('variable `', $d.name, '` is ')
+
+ c:fspec::container::type
+ if (d.cref) c = lookup($d.cref, $d.parent) else c = d.container
+
+ if (c)
+ print('`', c.data.type, ' ', container_name_str(c.data.name), '` ')
+
+ if (d.primitive)
+ print(d.primitive.bits, ' bits and ', signed_str(d.primitive.signed))
+
+ print('\n')
+
+ if (d.extra) {
+ if (d.extra.length) {
+ if (!d.extra.length.result.value || d.extra.length.result.value.reference) {
+ print(' it has a variable length that needs to be computed with formula `', $d.extra.length, '`\n')
+ } else {
+ if (d.extra.length.result.value.number) {
+ print(' it has a constant length of `', $d.extra.length.result.value, '`\n')
+ } else if (d.extra.length.result.value.string) {
+ print(' its length will increase until pattern `', $d.extra.length.result.value.string.raw, '` has been read from stream\n')
+ }
+ }
+ }
+
+ for f:fspec::declaration::filter in repeat(d.extra.filter)
+ print(' it needs to be filtered with `', $f.function, '`\n')
+
+ for v:fspec::declaration::visual in child(d.extra.visual)
+ print(' and it should be visualized as `', $v.name, '`\n')
+ }
+}
+
+void
+walk(s:fspec::container::type)
+{
+ insert($s.data.type, s.data.name, s)
+ if ($s.data.type == 'enum') {
+ for i:fspec::container::enum::item in repeat(s.data.items)
+ insert('variable', $i.name, i)
+ } else if ($s.data.type == 'struct') {
+ push_scope()
+ for d:fspec::container::strukt::item in repeat(s.data.items) {
+ if (d.data.container)
+ walk(d.data.container)
+ if (d.data.name)
+ print_declaration(d.data)
+ }
+ pop_scope()
+ } else if ($s.data.type == 'select') {
+ push_scope()
+ for d:fspec::container::select::item in repeat(s.data.items) {
+ if (d.data.container)
+ walk(d.data.container)
+ if (d.data.name)
+ print_declaration(d.data)
+ }
+ pop_scope()
+ }
+}
+
+push_scope()
+for s:fspec::declaration::type in repeat(source.items)
+ walk(s.container)
+pop_scope()
diff --git a/src/compiler/expr.lm b/src/compiler/expr.lm
new file mode 100644
index 0000000..d615358
--- /dev/null
+++ b/src/compiler/expr.lm
@@ -0,0 +1,410 @@
+include 'types.lm'
+
+global RTYPE_UNKNOWN:int = 0
+global RTYPE_NUMBER:int = 1
+global RTYPE_STRING:int = 2
+
+context expr
+ context enum
+ token EXPR / (any - [,}])+ /
+
+ def type
+ collapsed:collapser::collapsed
+ [EXPR] {
+ lhs.collapsed = collapser::collapsestr($r1)
+ if (!lhs.collapsed) reject
+ }
+ end
+
+ context paren
+ literal `( `)
+ token EXPR / (any - [()])+ /
+
+ def syntax
+ [EXPR] | [`( syntax `)]
+
+ def type
+ collapsed:collapser::collapsed
+ [syntax] {
+ lhs.collapsed = collapser::collapsestr($r1)
+ if (!lhs.collapsed) reject
+ }
+ end
+
+ context bracket
+ literal `[ `]
+ token EXPR / (any - '[' - ']')+ /
+
+ def syntax
+ [EXPR] | [`[ syntax `]]
+
+ def type
+ collapsed:collapser::collapsed
+ [syntax] {
+ lhs.collapsed = collapser::collapsestr($r1)
+ if (!lhs.collapsed) reject
+ }
+ end
+
+ context arg
+ literal `( `)
+ token EXPR / (any - [(),])+ /
+
+ def syntax
+ [EXPR] | [`( syntax `)]
+
+ def type
+ collapsed:collapser::collapsed
+ [syntax] {
+ lhs.collapsed = collapser::collapsestr($r1)
+ if (!lhs.collapsed) reject
+ }
+ end
+end
+
+context reference
+ context function
+ literal `( `) `,
+
+ def arg
+ [expr::arg::type `, arg] | [expr::arg::type]
+
+ def type
+ [name:name::type `( args:arg* `)]
+ end
+
+ context variable
+ def type
+ [name:name::type]
+ end
+
+ def type
+ [function::type]
+ | [variable::type]
+end
+
+context collapser
+ # BUG: lists seem to not really work well here
+ # implement simple native stack
+ int op_stack_new() = c_op_stack_new
+ int op_stack_free(stack:int) = c_op_stack_free
+ str op_stack_top(stack:int) = c_op_stack_top
+ bool op_stack_push(stack:int, op:str) = c_op_stack_push
+ str op_stack_pop(stack:int) = c_op_stack_pop
+
+ stack:int
+ values:str
+ next_is_unary:bool
+
+ token WS / space /
+ literal `+ `-
+ literal `( `) `+# `-# `! `~ `* `/ `% `#+ `#- `<< `>> `< `> `<= `>= `== `!= `& `^ `| `&& `|| `? `:
+ literal `. `[ `]
+ literal `sizeof
+
+ def unary_unambi
+ [`!] | [`~]
+
+ def binary_unambi
+ [`.] | [`*] | [`/] | [`%] | [`<<] | [`>>] | [`<] | [`>] | [`<=] | [`>=] | [`==] | [`!=] | [`&] | [`^] | [`|] | [`&&] | [`||]
+
+ def ternary
+ [`:]
+
+ context reducer
+ int modulo(a:int, b:int) = c_modulo
+ int bitnot(a:int) = c_bitnot
+ int bitand(a:int, b:int) = c_bitand
+ int bitor(a:int, b:int) = c_bitor
+ int bitxor(a:int, b:int) = c_bitxor
+ int shiftl(a:int, b:int) = c_shiftl
+ int shiftr(a:int, b:int) = c_shiftr
+ int subscript(a:str, b:int) = c_subscript
+
+ def builtin
+ value:value
+ [`sizeof `( string::type `)] { lhs.value = parse value[$r3.length] }
+
+ def value
+ rtype:int
+ [builtin:builtin] { lhs = r1.value }
+ | [number:number::type] { lhs.rtype = RTYPE_NUMBER }
+ | [string:string::type] { lhs.rtype = RTYPE_STRING }
+ | [reference:reference::type]
+
+ def unary
+ [`+#] | [`-#] | [unary_unambi]
+
+ def binary
+ [`#+] | [`#-] | [binary_unambi]
+
+ def anynary
+ [unary] | [binary] | [ternary]
+
+ def numop
+ value:value
+ [number::type WS `-#] { lhs.value = parse value[$(r1.value - (r1.value * 2))] }
+ | [number::type WS `+#] { lhs.value = parse value[$r1.value] }
+ | [number::type WS `!] { r:int = 0 if (r1.value == 0) r = 1 lhs.value = parse value[$r] }
+ | [number::type WS `~] { lhs.value = parse value[$bitnot(r1.value)] }
+ | [number::type WS number::type WS `*] { lhs.value = parse value[$(r1.value * r3.value)] }
+ | [number::type WS number::type WS `/] { lhs.value = parse value[$(r1.value / r3.value)] }
+ | [number::type WS number::type WS `#+] { lhs.value = parse value[$(r1.value + r3.value)] }
+ | [number::type WS number::type WS `#-] { lhs.value = parse value[$(r1.value - r3.value)] }
+ | [number::type WS number::type WS `<<] { lhs.value = parse value[$shiftl(r1.value, r3.value)] }
+ | [number::type WS number::type WS `>>] { lhs.value = parse value[$shiftr(r1.value, r3.value)] }
+ | [number::type WS number::type WS `<] { r:int = 0 if (r1.value < r3.value) r = 1 lhs.value = parse value[$r] }
+ | [number::type WS number::type WS `>] { r:int = 0 if (r1.value > r3.value) r = 1 lhs.value = parse value[$r] }
+ | [number::type WS number::type WS `<=] { r:int = 0 if (r1.value <= r3.value) r = 1 lhs.value = parse value[$r] }
+ | [number::type WS number::type WS `>=] { r:int = 0 if (r1.value >= r3.value) r = 1 lhs.value = parse value[$r] }
+ | [number::type WS number::type WS `==] { r:int = 0 if (r1.value == r3.value) r = 1 lhs.value = parse value[$r] }
+ | [number::type WS number::type WS `!=] { r:int = 0 if (r1.value != r3.value) r = 1 lhs.value = parse value[$r] }
+ | [number::type WS number::type WS `&] { lhs.value = parse value[$bitand(r1.value, r3.value)] }
+ | [number::type WS number::type WS `^] { lhs.value = parse value[$bitxor(r1.value, r3.value)] }
+ | [number::type WS number::type WS `|] { lhs.value = parse value[$bitor(r1.value, r3.value)] }
+ | [number::type WS number::type WS `&&] { r:int = 0 if (r1.value && r3.value) r = 1 lhs.value = parse value[$r] }
+ | [number::type WS number::type WS `||] { r:int = 0 if (r1.value || r3.value) r = 1 lhs.value = parse value[$r] }
+ | [number::type WS number::type WS number::type WS `:] { if (r1.value) lhs.value = parse value[$r3] else lhs.value = parse value[$r5] }
+ | [number::type WS value WS `]] commit { reject }
+
+ # strings can only be operated with `!= and `== against other strings
+ def stringop
+ value:value
+ [string::type WS string::type WS `==] commit { r:int = 0 if (r1.raw == r3.raw) r = 1 lhs.value = parse value[$r] }
+ | [string::type WS string::type WS `!=] commit { r:int = 0 if (r1.raw != r3.raw) r = 1 lhs.value = parse value[$r] }
+ | [number::type WS string::type WS string::type WS `:] { if (r1.value) lhs.value = parse value[$r3] else lhs.value = parse value[$r5] }
+ | [string::type WS unary] commit { reject } # <unary> str
+ | [string::type WS number::type WS binary] commit { reject } # str <binary> num
+ | [number::type WS string::type WS binary] commit { reject } # num <binary> str
+ | [string::type WS string::type WS binary] { reject } # str <math> str
+ | [value WS number::type WS string::type WS ternary] commit { reject } # (v ? num : str)
+ | [value WS string::type WS number::type WS ternary] commit { reject } # (v ? str : num)
+ | [string::type WS value WS value WS ternary] commit { reject } # (str ? v : v)
+ | [string::type WS number::type WS `]] {
+ if (r1.length <= r3.value) {
+ print('subscript out of bounds\n')
+ reject
+ } else {
+ lhs.value = parse value[$subscript($r1.raw, r3.value)]
+ }
+ }
+
+ def valueop
+ rtype:int
+ [value WS value WS `]] { lhs.rtype = RTYPE_NUMBER }
+ | [value WS unary] { lhs.rtype = RTYPE_NUMBER }
+ | [value WS value WS binary] { lhs.rtype = RTYPE_NUMBER }
+ | [value WS value WS value WS ternary] { if (r3.rtype != r5.rtype) reject lhs.rtype = r1.rtype }
+
+ def operation
+ rtype:int
+ [numop] { lhs = parse operation[$r1.value] }
+ | [stringop] { lhs = parse operation[$r1.value] }
+ | [valueop] { lhs.rtype = r1.rtype }
+ | [value] { lhs.rtype = r1.rtype }
+ | [value WS] { lhs.rtype = r1.rtype }
+ | [operation WS] { lhs.rtype = r1.rtype }
+ | [operation anynary] { lhs.rtype = r1.rtype }
+
+ def collapsed
+ value:value
+ [operation+] commit {
+ # we check return type of every operation to make sure we don't operate on different types
+ rtype:int = RTYPE_UNKNOWN
+ for i:operation in repeat(r1) {
+ if (i.rtype != RTYPE_UNKNOWN && rtype != RTYPE_UNKNOWN && i.rtype != rtype)
+ reject
+ rtype = i.rtype
+ }
+ lhs.value = parse value[$lhs]
+ }
+ end
+
+ def operator
+ precedence:int
+ rassoc:bool
+ open:str
+ close:str
+ args:int
+ [`[] { lhs.precedence = 0 lhs.rassoc = false lhs.args = 0 lhs.open = ']' }
+ | [`]] { lhs.precedence = 0 lhs.rassoc = false lhs.args = 2 lhs.close = '[' }
+ | [`(] { lhs.precedence = 0 lhs.rassoc = false lhs.args = 0 lhs.open = ')' }
+ | [`)] { lhs.precedence = 0 lhs.rassoc = false lhs.args = 0 lhs.close = '(' }
+ | [`.] { lhs.precedence = 0 lhs.rassoc = false lhs.args = 2 }
+ | [`+#] { lhs.precedence = 1 lhs.rassoc = true lhs.args = 1 }
+ | [`-#] { lhs.precedence = 1 lhs.rassoc = true lhs.args = 1 }
+ | [`!] { lhs.precedence = 1 lhs.rassoc = true lhs.args = 1 }
+ | [`~] { lhs.precedence = 1 lhs.rassoc = true lhs.args = 1 }
+ | [`*] { lhs.precedence = 2 lhs.rassoc = false lhs.args = 2 }
+ | [`/] { lhs.precedence = 2 lhs.rassoc = false lhs.args = 2 }
+ | [`%] { lhs.precedence = 2 lhs.rassoc = false lhs.args = 2 }
+ | [`#+] { lhs.precedence = 3 lhs.rassoc = false lhs.args = 2 }
+ | [`#-] { lhs.precedence = 3 lhs.rassoc = false lhs.args = 2 }
+ | [`<<] { lhs.precedence = 4 lhs.rassoc = false lhs.args = 2 }
+ | [`>>] { lhs.precedence = 4 lhs.rassoc = false lhs.args = 2 }
+ | [`<] { lhs.precedence = 5 lhs.rassoc = false lhs.args = 2 }
+ | [`>] { lhs.precedence = 5 lhs.rassoc = false lhs.args = 2 }
+ | [`<=] { lhs.precedence = 5 lhs.rassoc = false lhs.args = 2 }
+ | [`>=] { lhs.precedence = 5 lhs.rassoc = false lhs.args = 2 }
+ | [`==] { lhs.precedence = 6 lhs.rassoc = false lhs.args = 2 }
+ | [`!=] { lhs.precedence = 6 lhs.rassoc = false lhs.args = 2 }
+ | [`&] { lhs.precedence = 7 lhs.rassoc = false lhs.args = 2 }
+ | [`^] { lhs.precedence = 8 lhs.rassoc = false lhs.args = 2 }
+ | [`|] { lhs.precedence = 9 lhs.rassoc = false lhs.args = 2 }
+ | [`&&] { lhs.precedence = 10 lhs.rassoc = false lhs.args = 2 }
+ | [`||] { lhs.precedence = 11 lhs.rassoc = false lhs.args = 2 }
+ | [`?] { lhs.precedence = 12 lhs.rassoc = true lhs.args = 0 lhs.open = ':' }
+ | [`:] { lhs.precedence = 12 lhs.rassoc = true lhs.args = 3 }
+
+ void
+ operate(op:operator)
+ {
+ if (!op.args)
+ return 0
+
+ s:str = values + $op
+ # print('collapse: ', s, ' -> ')
+ r:reducer::collapsed = parse reducer::collapsed[s]
+
+ if (!r) {
+ reject
+ } else {
+ # print(^r, '\n')
+ values = $r + ' '
+ }
+ }
+
+ void
+ flush_all()
+ {
+ while (op_stack_top(stack))
+ operate(parse operator[op_stack_pop(stack)])
+ }
+
+ void
+ flush_until(name:str)
+ {
+ while (op_stack_top(stack) && op_stack_top(stack) != name)
+ operate(parse operator[op_stack_pop(stack)])
+ }
+
+ void
+ flush_ordered(name:str)
+ {
+ op:operator = parse operator[name]
+
+ top:operator
+ if (op_stack_top(stack)) top = parse operator[op_stack_top(stack)]
+ while (top && (top.precedence < op.precedence || (top.precedence == op.precedence && !top.rassoc)) && !top.open) {
+ operate(parse operator[op_stack_pop(stack)])
+ if (op_stack_top(stack)) top = parse operator[op_stack_top(stack)] else top = nil
+ }
+
+ if (op.close)
+ flush_until(op.close)
+
+ next_is_unary = !op.close
+ }
+
+ void
+ stack_op(name:str)
+ {
+ flush_ordered(name)
+ # print('push op: ', name, '\n')
+ op_stack_push(stack, name)
+ }
+
+ void
+ stack_value(value:str)
+ {
+ # print('push value: ', value, '\n')
+ values = values + value + ' '
+ next_is_unary = false
+ }
+
+ def value
+ [reducer::builtin] | [number::unsigned::type] | [string::type] | [reference::type]
+
+ def ambiguous
+ [`+] | [`-]
+
+ def unambiguous
+ [unary_unambi] | [binary_unambi]
+
+ def binary
+ [ambiguous] | [binary_unambi]
+
+ def otherops
+ op:str
+ [ambiguous] { if (next_is_unary) lhs.op = $r1 + '#' else lhs.op = '#' + $r1 }
+ | [unambiguous] { lhs.op = $r1 }
+
+ def lsquare
+ [`[] { stack_op($lhs) }
+
+ def rsquare
+ [`]] { stack_op($lhs) }
+
+ def lparen
+ [`(] { stack_op($lhs) }
+
+ def rparen
+ [`)] { stack_op($lhs) }
+
+ def question
+ [`?] { stack_op($lhs) }
+
+ def colon
+ [`:] { stack_op($lhs) }
+
+ def constant
+ [number::unsigned::type] | [string::type]
+
+ def tok#en
+ [value WS+ value] commit { reject }
+ | [binary WS* binary] commit { reject }
+ | [constant WS* `(] commit { reject }
+ | [`) WS* value] commit { reject }
+ | [`] WS* value] commit { reject }
+ | [lparen tok+ rparen] commit
+ | [lsquare WS* rsquare] commit
+ | [tok+ question tok+ colon tok+] commit
+ | [otherops] { stack_op(r1.op) }
+ | [value] { stack_value($r1) }
+ | [WS] { lhs = nil }
+
+ def collapsed
+ result:reducer::collapsed
+ [tok*] commit { flush_all() lhs.result = parse reducer::collapsed[values] if (!lhs.result) reject }
+
+ collapsed
+ collapse(s:stream)
+ {
+ c:collapser = new collapser()
+ c->stack = op_stack_new()
+ c->values = ''
+ c->next_is_unary = true
+ parse r:collapsed(c)[s]
+ op_stack_free(c->stack)
+ return r
+ }
+
+ collapsed
+ collapsestr(s:str)
+ {
+ c:collapser = new collapser()
+ c->stack = op_stack_new()
+ c->values = ''
+ c->next_is_unary = true
+ parse r:collapsed(c)[s]
+ op_stack_free(c->stack)
+ return r
+ }
+end
+
+# r:collapser::collapsed = collapser::collapse(stdin)
+# if (r) {
+# print($r.result, '\n')
+# } else {
+# print('invalid expression\n')
+# }
diff --git a/src/compiler/types.lm b/src/compiler/types.lm
new file mode 100644
index 0000000..34a9026
--- /dev/null
+++ b/src/compiler/types.lm
@@ -0,0 +1,55 @@
+context number
+ context unsigned
+ literal `true `false
+ token OCT / '0'[0-7]+ /
+ token DEC / [0-9]+ /
+ token HEX / '0x' xdigit+ /
+ int strtoull(a:str, b:int) = c_strtoull
+
+ def type
+ value:int
+ [`false] { lhs.value = 0 }
+ | [`true] { lhs.value = 1 }
+ | [OCT] { lhs.value = strtoull($r1, 8) }
+ | [DEC] { lhs.value = strtoull($r1, 10) }
+ | [HEX] { lhs.value = strtoull($r1, 16) }
+ end
+
+ lex
+ ignore / space+ /
+ literal `+ `-
+ end
+
+ def type
+ value:int
+ [unsigned::type] { lhs.value = r1.value }
+ | [`- type] { lhs.value = r2.value - (r2.value * 2) }
+ | [`+ type] { lhs.value = r2.value }
+end
+
+context string
+ rl ESC / '\\' /
+ token ESC_CHR / ESC [abfnrtv\\'"e] /
+ token ESC_HEX / ESC 'x' xdigit{2} /
+ token ESC_OCT / ESC [0-7]{1,3} /
+ token CHAR / ^cntrl - ['"] - ESC /
+ literal `' `"
+
+ def raw
+ [ESC_CHR] # TODO: how to output raw bytes?
+ | [ESC_HEX] # TODO: how to output raw bytes?
+ | [ESC_OCT] # TODO: how to output raw bytes?
+ | [CHAR]
+
+ def type
+ length:int
+ [`' raw:raw* `'] { i:int = 0 for s:raw in r2 i = i + 1 lhs.length = i }
+ | [`" raw:raw* `"] { i:int = 0 for s:raw in r2 i = i + 1 lhs.length = i }
+end
+
+context name
+ token NAME / [a-zA-Z_][a-zA-Z_0-9]* /
+
+ def type
+ [NAME]
+end
diff --git a/src/fspec/bcode.c b/src/fspec/bcode.c
deleted file mode 100644
index 70e4b08..0000000
--- a/src/fspec/bcode.c
+++ /dev/null
@@ -1,199 +0,0 @@
-#include <fspec/bcode.h>
-#include <fspec/memory.h>
-#include "private/bcode-types.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-#include <err.h>
-
-static_assert(sizeof(fspec_off) <= sizeof(((struct fspec_mem*)0)->len), "fspec_off should not be larger than what fspec_mem can represent");
-static_assert(sizeof(enum fspec_op) == sizeof(uint8_t), "enum fspec_op is expected to have size of uint8_t");
-static_assert(sizeof(struct fspec_bcode) == sizeof(enum fspec_op), "struct fspec_bcode is expected to have size of enum fspec_op");
-static_assert(sizeof(FSPEC_OP_LAST) <= 8, "op codes need more than 3 bits to be represented");
-
-#if 0
-uint8_t
-fspec_op_get_num_args(const struct fspec_op_code *code)
-{
- return code->op >> 2;
-}
-
-static fspec_off
-arg_data_len(const enum fspec_arg *arg)
-{
- assert(arg);
-
- switch (*arg) {
- case FSPEC_ARG_NUM:
- return sizeof(fspec_num);
-
- case FSPEC_ARG_VAR:
- return sizeof(fspec_var);
-
- case FSPEC_ARG_STR:
- case FSPEC_ARG_OFF:
- return sizeof(fspec_off);
-
- case FSPEC_ARG_DAT:
- {
- struct fspec_mem mem;
- fspec_arg_get_mem(arg, NULL, &mem);
- return sizeof(fspec_off) + mem.len;
- }
-
- case FSPEC_ARG_EOF:
- break;
-
- case FSPEC_ARG_LAST:
- errx(EXIT_FAILURE, "%s: unexpected argument type %u", __func__, *arg);
- break;
- }
-
- return 0;
-}
-
-static fspec_off
-arg_len(const enum fspec_arg *arg)
-{
- return sizeof(*arg) + arg_data_len(arg);
-}
-
-void
-fspec_arg_get_mem(const enum fspec_arg *arg, const void *data, struct fspec_mem *out_mem)
-{
- assert(arg && out_mem);
-
- switch (*arg) {
- case FSPEC_ARG_STR:
- {
- assert(data);
- fspec_off off;
- fspec_strsz len;
- memcpy(&off, (char*)arg + sizeof(*arg), sizeof(off));
- memcpy(&len, (char*)data + off, sizeof(len));
- out_mem->data = (char*)data + off + sizeof(len);
- out_mem->len = len;
- }
- break;
-
- case FSPEC_ARG_DAT:
- {
- fspec_off len;
- memcpy(&len, (char*)arg + sizeof(*arg), sizeof(len));
- out_mem->data = (char*)arg + sizeof(*arg) + sizeof(len);
- out_mem->len = len;
- }
- break;
-
- case FSPEC_ARG_VAR:
- case FSPEC_ARG_NUM:
- case FSPEC_ARG_OFF:
- out_mem->data = (char*)arg + sizeof(*arg);
- out_mem->len = arg_data_len(arg);
- break;
-
- case FSPEC_ARG_EOF:
- *out_mem = (struct fspec_mem){0};
- break;
-
- case FSPEC_ARG_LAST:
- errx(EXIT_FAILURE, "%s: unexpected argument type %u", __func__, *arg);
- break;
- }
-}
-
-fspec_num
-fspec_arg_get_num(const enum fspec_arg *arg)
-{
- assert(arg && *arg < FSPEC_ARG_LAST);
- fspec_num v;
- switch (*arg) {
- case FSPEC_ARG_NUM:
- memcpy(&v, arg + sizeof(*arg), sizeof(v));
- break;
-
- case FSPEC_ARG_VAR:
- {
- fspec_var var;
- memcpy(&var, arg + sizeof(*arg), sizeof(var));
- v = var;
- }
- break;
-
- case FSPEC_ARG_DAT:
- case FSPEC_ARG_OFF:
- {
- fspec_off off;
- memcpy(&off, arg + sizeof(*arg), sizeof(off));
- v = off;
- }
- break;
-
- case FSPEC_ARG_STR:
- case FSPEC_ARG_EOF:
- case FSPEC_ARG_LAST:
- errx(EXIT_FAILURE, "%s: unexpected argument type %u", __func__, *arg);
- break;
- }
- return v;
-}
-
-const char*
-fspec_arg_get_cstr(const enum fspec_arg *arg, const void *data)
-{
- assert(arg && *arg == FSPEC_ARG_STR);
- struct fspec_mem mem;
- fspec_arg_get_mem(arg, data, &mem);
- return (const char*)mem.data;
-}
-
-const enum fspec_arg*
-fspec_op_get_arg(const enum fspec_op *start, const void *end, const uint8_t nth, const uint32_t expect)
-{
- uint8_t i = 0;
- const enum fspec_arg *arg = NULL;
- for (const enum fspec_op *op = fspec_op_next(start, end, false); op && i < nth; op = fspec_op_next(op, end, false)) {
- if (*op != FSPEC_OP_ARG)
- return NULL;
-
- arg = (void*)(op + 1);
- assert(*arg >= 0 && *arg < FSPEC_ARG_LAST);
- ++i;
- }
-
- if (arg && !(expect & (1<<*arg)))
- errx(EXIT_FAILURE, "got unexpected argument of type %u", *arg);
-
- return arg;
-}
-
-const enum fspec_arg*
-fspec_arg_next(const enum fspec_arg *arg, const void *end, const uint8_t nth, const uint32_t expect)
-{
- return fspec_op_get_arg((void*)(arg - 1), end, nth, expect);
-}
-
-const enum fspec_op*
-fspec_op_next(const enum fspec_op *start, const void *end, const bool skip_args)
-{
- assert(start && end);
- fspec_off off = sizeof(*start);
- if ((void*)start < end && *start == FSPEC_OP_ARG)
- off += arg_len((void*)(start + 1));
-
- for (const enum fspec_op *op = start + off; (void*)start < end && (void*)op < end; ++op) {
- if (*op >= FSPEC_OP_LAST)
- errx(EXIT_FAILURE, "got unexected opcode %u", *op);
-
- if (skip_args && *op == FSPEC_OP_ARG) {
- op += arg_len((void*)(op + 1));
- continue;
- }
-
- return op;
- }
-
- return NULL;
-}
-#endif
diff --git a/src/fspec/bcode.h b/src/fspec/bcode.h
deleted file mode 100644
index 3d216af..0000000
--- a/src/fspec/bcode.h
+++ /dev/null
@@ -1,92 +0,0 @@
-#pragma once
-
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdbool.h>
-
-/** maximum range of numbers */
-#define PRI_FSPEC_NUM PRIu64
-typedef uint64_t fspec_num;
-
-enum fspec_visual {
- FSPEC_VISUAL_NUL,
- FSPEC_VISUAL_DEC,
- FSPEC_VISUAL_HEX,
- FSPEC_VISUAL_STR,
- FSPEC_VISUAL_LAST,
-} __attribute__((packed));
-
-enum fspec_type {
- FSPEC_TYPE_CODE,
- FSPEC_TYPE_CALL,
- FSPEC_TYPE_U8,
- FSPEC_TYPE_S8,
- FSPEC_TYPE_U16,
- FSPEC_TYPE_S16,
- FSPEC_TYPE_U32,
- FSPEC_TYPE_S32,
- FSPEC_TYPE_U64,
- FSPEC_TYPE_S64,
- FSPEC_TYPE_LAST,
-} __attribute__((packed));
-
-enum fspec_storage {
- FSPEC_STORAGE_DATA,
- FSPEC_STORAGE_LOCAL,
- FSPEC_STORAGE_LAST,
-} __attribute__((packed));
-
-enum fspec_builtin {
- FSPEC_BUILTIN_ADD,
- FSPEC_BUILTIN_SUB,
- FSPEC_BUILTIN_MUL,
- FSPEC_BUILTIN_DIV,
- FSPEC_BUILTIN_MOD,
- FSPEC_BUILTIN_BIT_AND,
- FSPEC_BUILTIN_BIT_OR,
- FSPEC_BUILTIN_BIT_XOR,
- FSPEC_BUILTIN_BIT_LEFT,
- FSPEC_BUILTIN_BIT_RIGHT,
- FSPEC_BUILTIN_DECLARE,
- FSPEC_BUILTIN_READ,
- FSPEC_BUILTIN_FILTER,
- FSPEC_BUILTIN_VISUAL,
- FSPEC_BUILTIN_LAST,
-} __attribute__((packed));
-
-enum fspec_op {
- FSPEC_OP_BUILTIN,
- FSPEC_OP_PUSH,
- FSPEC_OP_POP,
- FSPEC_OP_VAR,
- FSPEC_OP_LAST,
-} __attribute__((packed));
-
-struct fspec_bcode {
- char op, data[];
-} __attribute__((packed));
-
-#if 0
-('fspc')(version)
-OP_BUILTIN (declare) OP_PUSH OP_VAR8 (storage) OP_VAR8 (type) OP_VAR [name] OP_POP
-OP_BUILTIN (filter)
-OP_FUN FUN_ASSIGN VAR0 VAR [data]
-OP_FUN FUN_READ
-#endif
-
-#if 0
-uint8_t
-fspec_op_get_num_args(const struct fspec_bcode *code);
-
-const struct fspec_bcode*
-fspec_op_next(const struct fspec_bcode *code, const void *end, const bool skip_args);
-
-const struct fspec_bcode*
-fspec_op_get_arg(const struct fspec_bcode *code, const void *end, const uint8_t nth, const uint32_t expect);
-
-const struct fspec_arg*
-fspec_arg_next(const struct fspec_bcode *code, const void *end, const uint8_t nth, const uint32_t expect);
-
-fspec_num
-fspec_ref_get_num(const struct fspec_bcode *code);
-#endif
diff --git a/src/fspec/lexer.h b/src/fspec/lexer.h
deleted file mode 100644
index ef6e059..0000000
--- a/src/fspec/lexer.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-#include <fspec/memory.h>
-
-#include <stdbool.h>
-
-enum fspec_lexer_section {
- FSPEC_SECTION_DATA,
- FSPEC_SECTION_CODE,
-};
-
-struct fspec_lexer;
-struct fspec_lexer {
- struct {
- size_t (*read)(struct fspec_lexer *lexer, void *input, const size_t size, const size_t nmemb);
- size_t (*write)(struct fspec_lexer *lexer, const enum fspec_lexer_section section, const void *output, const size_t size, const size_t nmemb);
- } ops;
-
- struct {
- struct fspec_mem input;
- } mem;
-};
-
-bool
-fspec_lexer_parse(struct fspec_lexer *lexer, const char *name);
diff --git a/src/fspec/memory.h b/src/fspec/memory.h
deleted file mode 100644
index 768415a..0000000
--- a/src/fspec/memory.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma once
-
-#include <stddef.h>
-
-struct fspec_mem {
- void *data;
- size_t len;
-};
diff --git a/src/fspec/private/bcode-types.h b/src/fspec/private/bcode-types.h
deleted file mode 100644
index 8c9ce74..0000000
--- a/src/fspec/private/bcode-types.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-
-#include <inttypes.h>
-#include <stdint.h>
-
-/** maximum size of string literals */
-#define PRI_FSPEC_STRSZ PRIu8
-typedef uint8_t fspec_strsz;
-
-/** maximum range of variable ids */
-#define PRI_FSPEC_VAR PRIu16
-typedef uint16_t fspec_var;
-
-/** maximum range of bytecode offsets */
-#define PRI_FSPEC_OFF PRIu32
-typedef uint32_t fspec_off;
diff --git a/src/fspec/ragel/lexer-expr.h b/src/fspec/ragel/lexer-expr.h
deleted file mode 100644
index 904736d..0000000
--- a/src/fspec/ragel/lexer-expr.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#pragma once
-
-#include <fspec/memory.h>
-
-#include <stdbool.h>
-
-struct fspec_expr;
-struct fspec_expr {
- struct {
- size_t (*read)(struct fspec_expr *lexer, void *input, const size_t size, const size_t nmemb);
- size_t (*write)(struct fspec_expr *lexer, const void *output, const size_t size, const size_t nmemb);
- } ops;
-
- struct {
- struct fspec_mem input;
- } mem;
-};
-
-bool
-fspec_expr_parse(struct fspec_expr *lexer, const char *name);
diff --git a/src/fspec/ragel/lexer-expr.rl b/src/fspec/ragel/lexer-expr.rl
deleted file mode 100644
index 0f1f83d..0000000
--- a/src/fspec/ragel/lexer-expr.rl
+++ /dev/null
@@ -1,118 +0,0 @@
-#include "lexer-expr.h"
-#include "lexer-stack.h"
-#include "util/ragel/ragel.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <assert.h>
-#include <err.h>
-
-static uint8_t
-precedence(char op)
-{
- switch (op) {
- case '^': return 4;
- case '*': return 3;
- case '/': return 3;
- case '+': return 2;
- case '-': return 2;
- }
- errx(EXIT_FAILURE, "unknown operator %c for precedence", op);
- return 0;
-}
-
-static size_t
-pop(char cur, char *mstack, size_t open)
-{
- static char cvar = 'a';
-
- // 1 + 2 + 4 + 3 * 2 / 2 * 2 * 2 - 2 * 2 + 5;
- while (open >= 3) {
- const char last_op = mstack[open - 2];
- const uint8_t last_prio = precedence(last_op);
- const uint8_t new_prio = precedence(cur);
-
- if (last_prio <= new_prio)
- break;
-
- printf("%c = ", cvar);
- for (size_t i = open - 3; i < open; ++i)
- printf("%c ", mstack[i]);
- puts(";");
- open -= 3;
-
- mstack[open++] = cvar;
- ++cvar;
- }
-
- return open;
-}
-
-%%{
- machine fspec_expr;
- include fspec_stack "lexer-stack.rl";
- variable p ragel.p;
- variable pe ragel.pe;
- variable eof ragel.eof;
- write data noerror nofinal;
-
- action op {
- open = pop(fc, mstack, open);
- mstack[open++] = fc;
- }
-
- logical_operators = '&&' | '||' | '==' | '<' | '>' | '<=' | '>=';
- calc_operators = '-' | '+' | '/' | '*' | '%';
- bitwise_operators = '&' | '|' | '^' | '<<' | '>>';
-
- main := |*
- calc_operators => op;
- stack_num => { mstack[open++] = fc;};
- '(' => { };
- ')' => { };
- ' ';
- ';' => {
- printf("v = ");
- for (size_t i = 0; i < open; ++i)
- printf("%c ", mstack[i]);
- puts(";");
- };
- *|;
-}%%
-
-
-bool
-fspec_expr_parse(struct fspec_expr *expr, const char *name)
-{
- int cs, act;
- const char *ts, *te;
- (void)ts;
-
- size_t open = 0;
- char mstack[25];
-
- %% write init;
-
- (void)fspec_expr_en_main;
- assert(expr);
- assert(expr->ops.read);
- assert(expr->ops.write);
- assert(expr->mem.input.data && expr->mem.input.len);
- assert(expr->mem.input.len <= (size_t)~0 && "input storage size exceeds size_t range");
-
- char var[256];
- struct stack stack = { .var.buf.mem = { .data = var, .len = sizeof(var) } };
- struct ragel ragel = { .name = name, .lineno = 1 };
-
- // static const fspec_num version = 0;
-
- struct fspec_mem input = expr->mem.input;
- for (bool eof = false; !ragel.error && !eof;) {
- const size_t bytes = expr->ops.read(expr, input.data, 1, input.len);
- const struct ragel_mem rl = { .data = input.data, .end = (char*)input.data + bytes };
- ragel_feed_input(&ragel, (eof = (bytes < input.len)), &rl);
- %% write exec;
- }
-
- return !ragel.error;
-}
diff --git a/src/fspec/ragel/lexer-stack.h b/src/fspec/ragel/lexer-stack.h
deleted file mode 100644
index eebf055..0000000
--- a/src/fspec/ragel/lexer-stack.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#pragma once
-
-#include "util/membuf.h"
-
-#include <stdint.h>
-
-struct varbuf {
- struct membuf buf;
- size_t offset;
-};
-
-void
-varbuf_begin(struct varbuf *var);
-
-void
-varbuf_reset(struct varbuf *var);
-
-void
-varbuf_remove_last(struct varbuf *var);
-
-struct stack {
- struct varbuf var;
-
- union {
- struct fspec_mem str;
- uint64_t num;
- };
-
- enum stack_type {
- STACK_STR,
- STACK_NUM,
- } type;
-};
-
-void
-stack_num(struct stack *stack, const uint8_t base);
-
-const struct fspec_mem*
-stack_get_str(const struct stack *stack);
-
-uint64_t
-stack_get_num(const struct stack *stack);
diff --git a/src/fspec/ragel/lexer-stack.rl b/src/fspec/ragel/lexer-stack.rl
deleted file mode 100644
index 940f820..0000000
--- a/src/fspec/ragel/lexer-stack.rl
+++ /dev/null
@@ -1,153 +0,0 @@
-#include "lexer-stack.h"
-
-#include <stdlib.h>
-#include <assert.h>
-#include <err.h>
-
-void
-varbuf_begin(struct varbuf *var)
-{
- assert(var);
- var->offset = var->buf.written;
- assert(var->offset <= var->buf.mem.len);
-}
-
-void
-varbuf_reset(struct varbuf *var)
-{
- assert(var);
- var->offset = var->buf.written = 0;
-}
-
-void
-varbuf_remove_last(struct varbuf *var)
-{
- assert(var);
- assert(var->buf.written >= var->offset);
- const size_t size = var->buf.written - var->offset;
- assert(var->buf.written >= size);
- var->buf.written -= size;
- assert(var->buf.written <= var->buf.mem.len);
-}
-
-static void
-stack_check_type(const struct stack *stack, const enum stack_type type)
-{
- assert(stack);
-
- if (stack->type == type)
- return;
-
- const char *got = (type == STACK_STR ? "str" : "num"), *expected = (stack->type == STACK_STR ? "str" : "num");
- errx(EXIT_FAILURE, "tried to get '%s' from stack, but the last pushed type was '%s'", got, expected);
-}
-
-void
-stack_num(struct stack *stack, const uint8_t base)
-{
- assert(stack);
- membuf_terminate(&stack->var.buf, (char[]){ 0 }, 1);
- const char *str = (char*)stack->var.buf.mem.data + stack->var.offset;
- stack->type = STACK_NUM;
- stack->num = strtoll(str, NULL, base);
- varbuf_remove_last(&stack->var);
-}
-
-const struct fspec_mem*
-stack_get_str(const struct stack *stack)
-{
- stack_check_type(stack, STACK_STR);
- return &stack->str;
-}
-
-uint64_t
-stack_get_num(const struct stack *stack)
-{
- stack_check_type(stack, STACK_NUM);
- return stack->num;
-}
-
-%%{
- machine fspec_stack;
-
- action stack_oct {
- stack_num(&stack, 8);
- }
-
- action stack_hex {
- stack_num(&stack, 16);
- }
-
- action stack_dec {
- stack_num(&stack, 10);
- }
-
- action stack_str {
- membuf_terminate(&stack.var.buf, (char[]){ 0 }, 1);
- stack.type = STACK_STR;
- stack.str = stack.var.buf.mem;
- stack.str.len = stack.var.buf.written;
- }
-
- action store_esc_num {
- const fspec_num v = stack_get_num(&stack);
- assert(v <= 255);
- membuf_append(&stack.var.buf, (uint8_t[]){ v }, sizeof(uint8_t));
- }
-
- action store_esc {
- const struct { const char e, v; } map[] = {
- { .e = 'a', .v = '\a' },
- { .e = 'b', .v = '\b' },
- { .e = 'f', .v = '\f' },
- { .e = 'n', .v = '\n' },
- { .e = 'r', .v = '\r' },
- { .e = 't', .v = '\t' },
- { .e = 'v', .v = '\v' },
- { .e = '\\', .v = '\\' },
- { .e = '\'', .v = '\'' },
- { .e = '\"', .v = '"' },
- { .e = 'e', .v = 0x1B },
- };
-
- for (size_t i = 0; i < sizeof(map) / sizeof(map[0]); ++i) {
- if (fc != map[i].e)
- continue;
-
- membuf_append(&stack.var.buf, &map[i].v, sizeof(map[i].v));
- break;
- }
- }
-
- action store {
- membuf_append(&stack.var.buf, fpc, 1);
- }
-
- action begin_num {
- varbuf_begin(&stack.var);
- }
-
- action begin_str {
- varbuf_reset(&stack.var);
- }
-
- # Semantic
- quote = ['"];
- esc = [abfnrtv\\'"e];
- esc_chr = '\\';
- esc_hex = 'x' <: xdigit{2};
- hex = '0x' <: xdigit{1,};
- oct = [0-7]{1,3};
- dec = [\-+]? <: (([1-9] <: digit*) | '0');
- name = ((alpha | '_') <: (alnum | '_')*);
-
- # Stack
- stack_name = name >begin_str $store %stack_str;
- stack_hex = hex >begin_num $store %stack_hex;
- stack_dec = dec >begin_num $store %stack_dec;
- stack_oct = oct >begin_num $store %stack_oct;
- stack_esc_hex = esc_hex >begin_num <>*store %stack_hex;
- stack_esc = esc_chr <: ((stack_esc_hex | stack_oct) %store_esc_num | esc %~store_esc);
- stack_str = quote <: ((stack_esc? <: print? $store) - zlen)* >begin_str %stack_str :>> quote;
- stack_num = stack_dec | stack_hex;
-}%%
diff --git a/src/fspec/ragel/lexer.rl b/src/fspec/ragel/lexer.rl
deleted file mode 100644
index 8354bc0..0000000
--- a/src/fspec/ragel/lexer.rl
+++ /dev/null
@@ -1,180 +0,0 @@
-#include <fspec/lexer.h>
-#include <fspec/bcode.h>
-#include "lexer-stack.h"
-#include "util/ragel/ragel.h"
-#include "fspec/private/bcode-types.h"
-
-#include <assert.h>
-
-%%{
- machine fspec_lexer;
- include fspec_stack "lexer-stack.rl";
- variable p ragel.p;
- variable pe ragel.pe;
- variable eof ragel.eof;
- write data noerror nofinal;
-
- action arg_eof {
- // codebuf_append_arg(&state.out, FSPEC_ARG_EOF, NULL);
- }
-
- action arg_num {
- // codebuf_append_arg(&state.out, FSPEC_ARG_NUM, (fspec_num[]){ stack_get_num(&state.stack) });
- }
-
- action arg_str {
- // const struct fspec_mem *str = stack_get_str(&state.stack);
- // codebuf_append_arg_cstr(&state.out, str->data, str->len);
- }
-
- action arg_var {
- // state_append_arg_var(&state, true, stack_get_str(&state.stack));
- }
-
- action filter {
- // codebuf_append_op(&state.out, FSPEC_OP_FILTER);
- }
-
- action goto {
- // codebuf_append_op(&state.out, FSPEC_OP_GOTO);
- // state_append_arg_var(&state, false, stack_get_str(&state.stack));
- }
-
- action vnul {
- // codebuf_append_op(&state.out, FSPEC_OP_VISUAL);
- // codebuf_append_arg(&state.out, FSPEC_ARG_NUM, (fspec_num[]){ FSPEC_VISUAL_NUL });
- }
-
- action vdec {
- // codebuf_append_op(&state.out, FSPEC_OP_VISUAL);
- // codebuf_append_arg(&state.out, FSPEC_ARG_NUM, (fspec_num[]){ FSPEC_VISUAL_DEC });
- }
-
- action vhex {
- // codebuf_append_op(&state.out, FSPEC_OP_VISUAL);
- // codebuf_append_arg(&state.out, FSPEC_ARG_NUM, (fspec_num[]){ FSPEC_VISUAL_HEX });
- }
-
- action vstr {
- // codebuf_append_op(&state.out, FSPEC_OP_VISUAL);
- // codebuf_append_arg(&state.out, FSPEC_ARG_NUM, (fspec_num[]){ FSPEC_VISUAL_STR });
- }
-
- action r8 {
- // codebuf_append_op(&state.out, FSPEC_OP_READ);
- // codebuf_append_arg(&state.out, FSPEC_ARG_NUM, (fspec_num[]){ 8 });
- }
-
- action r16 {
- // codebuf_append_op(&state.out, FSPEC_OP_READ);
- // codebuf_append_arg(&state.out, FSPEC_ARG_NUM, (fspec_num[]){ 16 });
- }
-
- action r32 {
- // codebuf_append_op(&state.out, FSPEC_OP_READ);
- // codebuf_append_arg(&state.out, FSPEC_ARG_NUM, (fspec_num[]){ 32 });
- }
-
- action r64 {
- // codebuf_append_op(&state.out, FSPEC_OP_READ);
- // codebuf_append_arg(&state.out, FSPEC_ARG_NUM, (fspec_num[]){ 64 });
- }
-
- action enum_member_end {
- }
-
- action enum_member_start {
- }
-
- action enum_end {
- }
-
- action enum_start {
- }
-
- action struct_member_end {
- // state_finish_declaration(&state, FSPEC_DECLARATION_MEMBER);
- }
-
- action struct_member_start {
- // state_append_declaration(&state, FSPEC_DECLARATION_MEMBER, stack_get_str(&state.stack));
- }
-
- action struct_end {
- // state_finish_declaration(&state, FSPEC_DECLARATION_STRUCT);
- }
-
- action struct_start {
- // state_append_declaration(&state, FSPEC_DECLARATION_STRUCT, stack_get_str(&state.stack));
- }
-
- action type_err {
- ragel_throw_error(&ragel, "unknown type name");
- }
-
- action visual_err {
- ragel_throw_error(&ragel, "unknown visualization");
- }
-
- action syntax_err {
- ragel_throw_error(&ragel, "malformed input (machine failed here or in next expression)");
- }
-
- action line {
- ragel_advance_line(&ragel);
- }
-
- # Semantic
- newline = '\n';
- valid = ^cntrl;
- comment = '//' <: valid* :>> newline;
- type = ('u8' | 's8') %r8 | ('u16' | 's16') %r16 | ('u32' | 's32') %r32 | ('u64' | 's64') %r64;
- visual = 'nul' %vnul | 'dec' %vdec | 'hex' %vhex | 'str' %vstr;
-
- # Catchers
- catch_const_expr = stack_num %arg_num;
- catch_struct = 'struct ' <: stack_name;
- catch_enum = 'enum ' <: stack_name;
- catch_type = (catch_struct %goto | type) $!type_err;
- catch_args = stack_num %arg_num | stack_str %arg_str | stack_name %arg_var;
- catch_array = '[' <: (catch_args | '$' %arg_eof) :>> ']';
- catch_filter = ' | ' %filter <: stack_name %arg_str :>> ('(' <: catch_args? <: (', ' <: catch_args)* :>> ')')?;
- catch_visual = ' ' <: visual $!visual_err;
-
- # Abstract
- struct_member = stack_name %struct_member_start :>> ': ' <: (catch_type <: catch_array* catch_filter* catch_visual?) :>> ';' %struct_member_end;
- struct = catch_struct %struct_start :>> ' {' <: (space | comment | struct_member)* :>> '};' %struct_end;
- enum_member = stack_name %enum_member_start :>> (': ' <: catch_const_expr)? :>> ';' %enum_member_end;
- enum = catch_enum %enum_start :>> ' {' <: (space | comment | enum_member)* :>> '};' %enum_end;
- line = valid* :>> newline %line;
- main := ((space | comment | enum | struct)* & line*) $!syntax_err;
-}%%
-
-bool
-fspec_lexer_parse(struct fspec_lexer *lexer, const char *name)
-{
- int cs;
- %% write init;
-
- (void)fspec_lexer_en_main;
- assert(lexer);
- assert(lexer->ops.read);
- assert(lexer->mem.input.data && lexer->mem.input.len);
- assert(lexer->mem.input.len <= (size_t)~0 && "input storage size exceeds size_t range");
-
- char var[256];
- struct stack stack = { .var.buf.mem = { .data = var, .len = sizeof(var) } };
- struct ragel ragel = { .name = name, .lineno = 1 };
-
- // static const fspec_num version = 0;
-
- struct fspec_mem input = lexer->mem.input;
- for (bool eof = false; !ragel.error && !eof;) {
- const size_t bytes = lexer->ops.read(lexer, input.data, 1, input.len);
- const struct ragel_mem rl = { .data = input.data, .end = (char*)input.data + bytes };
- ragel_feed_input(&ragel, (eof = (bytes < input.len)), &rl);
- %% write exec;
- }
-
- return !ragel.error;
-}
diff --git a/src/fspec/ragel/validator.rl b/src/fspec/ragel/validator.rl
deleted file mode 100644
index 90ead21..0000000
--- a/src/fspec/ragel/validator.rl
+++ /dev/null
@@ -1,96 +0,0 @@
-#include <fspec/bcode.h>
-#include <fspec/validator.h>
-#include "util/ragel/ragel.h"
-#include "fspec/private/bcode-types.h"
-
-#include <assert.h>
-
-struct stack {
- union {
- fspec_num num;
- fspec_off off;
- fspec_var var;
- fspec_strsz strsz;
- unsigned char b[sizeof(fspec_num)];
- } u;
- uint8_t i; // writing index for u.b
-};
-
-struct state {
- struct ragel ragel;
- struct stack stack;
-};
-
-%%{
- machine fspec_validator;
- variable p state.ragel.p;
- variable pe state.ragel.pe;
- variable eof state.ragel.eof;
- write data noerror nofinal;
-
-# BLT_HEADER = 0;
-# BLT_ADD = 1;
-# BLT_SUB = 2;
-# BLT_MUL = 3;
-# BLT_DIV = 4;
-# BLT_MOD = 5;
-# BLT_BIT_AND = 6;
-# BLT_BIT_OR = 7;
-# BLT_BIT_XOR = 8;
-# BLT_BIT_LEFT = 9;
-# BLT_BIT_RIGHT = 10;
-# BLT_DECLARE = 11;
-# BLT_READ = 12;
-# BLT_GOTO = 13;
-# BLT_FILTER = 14;
-# BLT_VISUAL = 15;
-#
-# builtins = BLT_HEADER |
-# BLT_ADD | BLT_SUB | BLT_MUL | BLT_DIV | BLT_MOD |
-# BLT_BIT_AND | BLT_BIT_OR | BLT_BIT_XOR | BLT_BIT_LEFT | BLT_BIT_RIGHT
-# BLT_DECLARE | BLT_READ | BLT_GOTO | BLT_FILTER | BLT_VISUAL;
-#
-# OP_ARG = 0;
-# OP_REF = 1;
-# OP_BLT = 2 OP_ARG builtins;
-# OP_FUN = 3;
-#
-# arg_ops = OP_REF | OP_FUN | OP_BUILTIN OP_FUN
-#
-# BLT_DECLARE = OP_BUILTIN 10 OP_ARG 2 OP_REF OP_REF;
-# BLT_READ = OP_BUILTIN 11 OP_ARG 1..255 OP_REF (arg_ops)*;
-#
-# pattern = ((BLT_READ | BLT_GOTO) BLT_FILTER* BLT_VISUAL?)* $!pattern_error;
-# main := (BLT_HEADER <: BLT_DECLARE* <: pattern) %check_decls $advance $!syntax_error;
- main := any*;
-}%%
-
-bool
-fspec_validator_parse(struct fspec_validator *validator, const char *name)
-{
- int cs;
- %% write init;
-
- (void)fspec_validator_en_main;
- assert(validator);
- assert(validator->ops.read);
- assert(validator->mem.input.data && validator->mem.input.len);
- assert(validator->mem.input.len <= (size_t)~0 && "input storage size exceeds size_t range");
-
- struct state state = {
- .ragel.name = name,
- .ragel.lineno = 1,
- };
-
- static_assert(sizeof(state.stack.u) == sizeof(state.stack.u.b), "bytes doesn't represent the largest member in union");
-
- struct fspec_mem input = validator->mem.input;
- for (bool eof = false; !state.ragel.error && !eof;) {
- const size_t bytes = validator->ops.read(validator, input.data, 1, input.len);
- const struct ragel_mem rl = { .data = input.data, .end = (char*)input.data + bytes, .binary = true };
- ragel_feed_input(&state.ragel, (eof = (bytes < input.len)), &rl);
- %% write exec;
- }
-
- return !state.ragel.error;
-}
diff --git a/src/fspec/validator.h b/src/fspec/validator.h
deleted file mode 100644
index a20e98a..0000000
--- a/src/fspec/validator.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include <fspec/memory.h>
-
-#include <stdbool.h>
-
-struct fspec_validator;
-struct fspec_validator {
- struct {
- size_t (*read)(struct fspec_validator *validator, void *ptr, const size_t size, const size_t nmemb);
- } ops;
-
- struct {
- struct fspec_mem input;
- } mem;
-};
-
-bool
-fspec_validator_parse(struct fspec_validator *validator, const char *name);
diff --git a/src/util/membuf.c b/src/util/membuf.c
deleted file mode 100644
index 0602679..0000000
--- a/src/util/membuf.c
+++ /dev/null
@@ -1,31 +0,0 @@
-#include "membuf.h"
-
-#include <stdlib.h>
-#include <assert.h>
-#include <memory.h>
-#include <err.h>
-
-static void
-membuf_bounds_check(const struct membuf *buf, const size_t nmemb)
-{
- assert(buf);
-
- if (buf->mem.len < nmemb || buf->written > buf->mem.len - nmemb)
- errx(EXIT_FAILURE, "%s: %zu bytes exceeds the maximum storage size of %zu bytes", __func__, buf->written + nmemb, buf->mem.len);
-}
-
-void
-membuf_terminate(struct membuf *buf, const void *data, const size_t data_sz)
-{
- assert(data || !data_sz);
- membuf_bounds_check(buf, data_sz);
- memcpy((char*)buf->mem.data + buf->written, data, data_sz);
-}
-
-void
-membuf_append(struct membuf *buf, const void *data, const size_t data_sz)
-{
- membuf_terminate(buf, data, data_sz);
- buf->written += data_sz;
- assert(buf->written <= buf->mem.len);
-}
diff --git a/src/util/membuf.h b/src/util/membuf.h
deleted file mode 100644
index 86d8dde..0000000
--- a/src/util/membuf.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#pragma once
-
-#include <fspec/memory.h>
-
-struct membuf {
- struct fspec_mem mem;
- size_t written;
-};
-
-void
-membuf_terminate(struct membuf *buf, const void *data, const size_t data_sz);
-
-void
-membuf_append(struct membuf *buf, const void *data, const size_t data_sz);