diff options
Diffstat (limited to 'ast.c')
-rw-r--r-- | ast.c | 251 |
1 files changed, 251 insertions, 0 deletions
@@ -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; +} |