summaryrefslogtreecommitdiffhomepage
path: root/ast.c
diff options
context:
space:
mode:
Diffstat (limited to 'ast.c')
-rw-r--r--ast.c251
1 files changed, 251 insertions, 0 deletions
diff --git a/ast.c b/ast.c
new file mode 100644
index 0000000..ab4f873
--- /dev/null
+++ b/ast.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2020 Jo-Philipp Wich <jo@mein.io>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ast.h"
+#include "lexer.h"
+#include "parser.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <math.h>
+#include <libubox/utils.h>
+
+struct ut_opcode *
+ut_new_op(struct ut_state *s, int type, struct json_object *val, ...)
+{
+ struct ut_opcode *newop, *child;
+ int n_op = 0;
+ va_list ap;
+
+ newop = calloc(1, sizeof(*newop));
+
+ if (!newop) {
+ fprintf(stderr, "Out of memory\n");
+ exit(127);
+ }
+
+ newop->off = s->off;
+ newop->type = type;
+ newop->val = val;
+
+ va_start(ap, val);
+
+ while ((child = va_arg(ap, void *)) != (void *)1)
+ if (n_op < sizeof(newop->operand) / sizeof(newop->operand[0]))
+ newop->operand[n_op++] = child;
+
+ va_end(ap);
+
+ newop->next = s->pool;
+ s->pool = newop;
+
+ return newop;
+}
+
+struct ut_opcode *
+ut_wrap_op(struct ut_opcode *parent, ...)
+{
+ struct ut_opcode *child;
+ int n_op = 0;
+ va_list ap;
+
+ va_start(ap, parent);
+
+ while ((child = va_arg(ap, void *)) != (void *)1)
+ if (n_op < sizeof(parent->operand) / sizeof(parent->operand[0]))
+ parent->operand[n_op++] = child;
+
+ va_end(ap);
+
+ return parent;
+}
+
+struct ut_opcode *
+ut_append_op(struct ut_opcode *a, struct ut_opcode *b)
+{
+ struct ut_opcode *tail = a;
+
+ while (tail->sibling)
+ tail = tail->sibling;
+
+ tail->sibling = b;
+
+ return a;
+}
+
+static int
+double_rounded_to_string(struct json_object *v, struct printbuf *pb, int level, int flags)
+{
+ double d = json_object_get_double(v);
+
+ if (isnan(d))
+ return sprintbuf(pb, level ? "\"NaN\"" : "NaN");
+
+ if (d == INFINITY)
+ return sprintbuf(pb, level ? "1e309" : "Infinity");
+
+ if (d == -INFINITY)
+ return sprintbuf(pb, level ? "-1e309" : "-Infinity");
+
+ return sprintbuf(pb, "%g", d);
+}
+
+struct json_object *
+json_object_new_double_rounded(double v)
+{
+ struct json_object *d = json_object_new_double(v);
+
+ json_object_set_serializer(d, double_rounded_to_string, NULL, NULL);
+
+ return d;
+}
+
+static int
+null_obj_to_string(struct json_object *v, struct printbuf *pb, int level, int flags)
+{
+ return sprintbuf(pb, "null");
+}
+
+struct json_object *
+json_object_new_null_obj(void) {
+ struct json_object *d = json_object_new_boolean(false);
+
+ json_object_set_serializer(d, null_obj_to_string, NULL, NULL);
+
+ return d;
+}
+
+static int
+func_to_string(struct json_object *v, struct printbuf *pb, int level, int flags)
+{
+ struct ut_opcode *op = json_object_get_userdata(v);
+ struct ut_opcode *args = op ? op->operand[1] : NULL;
+ struct json_object *name = (op && op->operand[0]) ? op->operand[0]->val : NULL;
+
+ sprintbuf(pb, "%sfunction%s%s(",
+ level ? "\"" : "",
+ name ? " " : "",
+ name ? json_object_get_string(name) : "");
+
+ while (args) {
+ sprintbuf(pb, "%s%s",
+ (args != op->operand[1]) ? ", " : "",
+ json_object_get_string(args->val));
+
+ args = args->sibling;
+ }
+
+ return sprintbuf(pb, ") { ... }%s",
+ level ? "\"" : "");
+}
+
+struct ut_opcode *
+ut_new_func(struct ut_state *s, struct ut_opcode *name, struct ut_opcode *args, struct ut_opcode *body)
+{
+ struct ut_opcode *op = ut_new_op(s, T_FUNC, json_object_new_boolean(0), name, args, body, (void *)1);
+
+ json_object_set_serializer(op->val, func_to_string, op, NULL);
+
+ return op;
+}
+
+static void
+ut_reset(struct ut_state *s)
+{
+ s->semicolon_emitted = false;
+ s->start_tag_seen = false;
+ s->blocktype = UT_BLOCK_NONE;
+ s->off = 0;
+
+ if (s->error.code == UT_ERROR_EXCEPTION)
+ json_object_put(s->error.info.exception);
+
+ memset(&s->error, 0, sizeof(s->error));
+}
+
+void
+ut_free(struct ut_state *s)
+{
+ struct ut_opcode *op, *tmp;
+
+ if (s) {
+ while (s->stack.off > 0)
+ json_object_put(s->stack.scope[--s->stack.off]);
+
+ free(s->stack.scope);
+
+ for (op = s->pool; op;) {
+ tmp = op->next;
+
+ if (op->val != (void *)1)
+ json_object_put(op->val);
+
+ free(op);
+ op = tmp;
+ }
+
+ ut_reset(s);
+ }
+
+ free(s);
+}
+
+enum ut_error_type
+ut_parse(struct ut_state *s, const char *expr)
+{
+ int len = strlen(expr);
+ const char *ptr = expr;
+ struct ut_opcode *op;
+ void *pParser;
+ int mlen = 0;
+
+ if (!s)
+ return UT_ERROR_OUT_OF_MEMORY;
+
+ ut_reset(s);
+
+ pParser = ParseAlloc(malloc);
+
+ if (!pParser)
+ return UT_ERROR_OUT_OF_MEMORY;
+
+ while (len > 0) {
+ op = ut_get_token(s, ptr, &mlen);
+
+ if (mlen < 0) {
+ s->error.code = -mlen;
+ goto out;
+ }
+
+ if (op)
+ Parse(pParser, op->type, op, s);
+
+ if (s->error.code)
+ goto out;
+
+ len -= mlen;
+ ptr += mlen;
+ }
+
+ Parse(pParser, 0, NULL, s);
+
+out:
+ ParseFree(pParser, free);
+
+ return s->error.code;
+}