aboutsummaryrefslogtreecommitdiff
path: root/ast.c
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-02-04 15:23:59 -0500
committerBruce Hill <bruce@bruce-hill.com>2024-02-04 15:23:59 -0500
commit98f0c51119f9d42d733f44cb516b1c2bcd9061af (patch)
tree39ab4fa635f858b76b9a8bbf84701c2788d5f498 /ast.c
Initial commit
Diffstat (limited to 'ast.c')
-rw-r--r--ast.c179
1 files changed, 179 insertions, 0 deletions
diff --git a/ast.c b/ast.c
new file mode 100644
index 00000000..b62a058c
--- /dev/null
+++ b/ast.c
@@ -0,0 +1,179 @@
+// Some basic operations defined on AST nodes, mainly converting to
+// strings for debugging.
+#include <gc/cord.h>
+#include <stdarg.h>
+#include <printf.h>
+
+#include "ast.h"
+
+static const char *OP_NAMES[] = {
+ [BINOP_UNKNOWN]="unknown",
+ [UNOP_NOT]="not", [UNOP_NEGATIVE]="negative",
+ [BINOP_POWER]="^", [BINOP_MULT]="*", [BINOP_DIVIDE]="/",
+ [BINOP_MOD]="mod", [BINOP_MOD1]="mod1", [BINOP_PLUS]="+", [BINOP_MINUS]="minus",
+ [BINOP_CONCAT]="++", [BINOP_LSHIFT]="<<", [BINOP_RSHIFT]=">>", [BINOP_MIN]="min",
+ [BINOP_MAX]="max", [BINOP_EQ]="==", [BINOP_NE]="!=", [BINOP_LT]="<",
+ [BINOP_LE]="<=", [BINOP_GT]=">", [BINOP_GE]=">=", [BINOP_AND]="and", [BINOP_OR]="or", [BINOP_XOR]="xor",
+};
+
+static CORD ast_to_cord(ast_t *ast);
+static CORD ast_list_to_cord(ast_list_t *asts);
+static CORD type_ast_to_cord(type_ast_t *t);
+static CORD arg_list_to_cord(arg_list_t *args);
+static CORD tags_to_cord(tag_t *tags);
+
+#define TO_CORD(x) _Generic(x, \
+ ast_t*: ast_to_cord(x), \
+ ast_list_t*: ast_list_to_cord(x), \
+ type_ast_t*: type_ast_to_cord(x), \
+ arg_list_t*: arg_list_to_cord(x), \
+ tag_t*: tags_to_cord(x), \
+ const char *: x, \
+ int64_t: CORD_asprintf("%ld", x), \
+ unsigned short int: CORD_asprintf("%d", x), \
+ double: CORD_asprintf("%g", x), \
+ bool: CORD_asprintf("%s", x ? "yes" : "no"), \
+ unsigned char: CORD_asprintf("%s", x ? "yes" : "no"))
+
+CORD ast_list_to_cord(ast_list_t *asts)
+{
+ if (!asts)
+ return "\x1b[35mNULL\x1b[m";
+
+ CORD c = "[";
+ for (; asts; asts = asts->next) {
+ c = CORD_cat(c, ast_to_cord(asts->ast));
+ if (asts->next) c = CORD_cat(c, ", ");
+ }
+ c = CORD_cat(c, "]");
+ return c;
+}
+
+CORD arg_list_to_cord(arg_list_t *args) {
+ CORD c = "Args(";
+ for (; args; args = args->next) {
+ if (args->var && args->var->name)
+ c = CORD_cat(c, args->var->name);
+ if (args->type)
+ CORD_sprintf(&c, "%r:%s", c, type_ast_to_cord(args->type));
+ if (args->default_val)
+ CORD_sprintf(&c, "%r=%s", c, ast_to_cord(args->default_val));
+ if (args->next) c = CORD_cat(c, ", ");
+ }
+ c = CORD_cat(c, ")");
+ return c;
+}
+
+CORD tags_to_cord(tag_t *tags) {
+ CORD c = "Tags(";
+ for (; tags; tags = tags->next) {
+ if (tags->name)
+ c = CORD_cat(c, tags->name);
+ CORD_sprintf(&c, "%r:%s=%ld", c, type_ast_to_cord(tags->type), tags->value);
+ if (tags->next) c = CORD_cat(c, ", ");
+ }
+ c = CORD_cat(c, ")");
+ return c;
+}
+
+CORD ast_to_cord(ast_t *ast)
+{
+ if (!ast) return "\x1b[35mNULL\x1b[m";
+
+ switch (ast->tag) {
+#define T(type, ...) case type: { auto data = ast->__data.type; (void)data; return CORD_asprintf("\x1b[34;1m" #type "\x1b[m" __VA_ARGS__); }
+ T(Unknown, "Unknown")
+ T(Nil, "(%r)", type_ast_to_cord(data.type))
+ T(Bool, "(\x1b[35m%s\x1b[m)", data.b ? "yes" : "no")
+ T(Var, "(\x1b[36;1m%s\x1b[m)", data.var.name)
+ T(Int, "(\x1b[35m%ld\x1b[m, precision=%ld)", data.i, data.precision)
+ T(Num, "(\x1b[35m%ld\x1b[m, precision=%ld)", data.n, data.precision)
+ T(Char, "(\x1b[35m'%c'\x1b[m)", data.c)
+ T(StringLiteral, "\x1b[35m\"%s\"\x1b[m", data.str)
+ T(StringJoin, "(%r)", ast_list_to_cord(data.children))
+ T(Interp, "(%r)", ast_to_cord(data.value))
+ T(Declare, "(var=%s, value=%s)", ast_to_cord(data.var), ast_to_cord(data.value))
+ T(Assign, "(targets=%r, values=%r)", ast_list_to_cord(data.targets), ast_list_to_cord(data.values))
+ T(BinaryOp, "(%r, %s, %r)", ast_to_cord(data.lhs), OP_NAMES[data.op], ast_to_cord(data.rhs))
+ T(UpdateAssign, "(%r, %s, %r)", ast_to_cord(data.lhs), OP_NAMES[data.op], ast_to_cord(data.rhs))
+ T(UnaryOp, "(%s, %r)", OP_NAMES[data.op], ast_to_cord(data.value))
+ T(Min, "(%r, %r, key=%r)", ast_to_cord(data.lhs), ast_to_cord(data.rhs), ast_to_cord(data.key))
+ T(Max, "(%r, %r, key=%r)", ast_to_cord(data.lhs), ast_to_cord(data.rhs), ast_to_cord(data.key))
+ T(Array, "(%r, type=%r)", ast_list_to_cord(data.items), type_ast_to_cord(data.type))
+ T(Table, "(key_type=%r, value_type=%r, fallback=%r, default_value=%r, entries=%r)",
+ type_ast_to_cord(data.key_type), type_ast_to_cord(data.value_type),
+ ast_to_cord(data.fallback), ast_to_cord(data.default_value),
+ ast_list_to_cord(data.entries))
+ T(TableEntry, "(%r => %r)", ast_to_cord(data.key), ast_to_cord(data.value))
+ T(FunctionDef, "(name=%r, args=%r, ret=%r, body=%r)", ast_to_cord(data.name),
+ arg_list_to_cord(data.args), type_ast_to_cord(data.ret_type), ast_to_cord(data.body))
+ T(Lambda, "(args=%r, body=%r)", arg_list_to_cord(data.args), ast_to_cord(data.body))
+ T(FunctionCall, "(fn=%r, args=%r)", ast_to_cord(data.fn), ast_list_to_cord(data.args))
+ T(KeywordArg, "(%s=%r)", ast_to_cord(data.arg))
+ T(Block, "(%r)", ast_list_to_cord(data.statements))
+ T(For, "(index=%r, value=%r, iter=%r, body=%r)", ast_to_cord(data.index), ast_to_cord(data.value),
+ ast_to_cord(data.iter), ast_to_cord(data.body))
+ T(While, "(condition=%r, body=%r)", ast_to_cord(data.condition), ast_to_cord(data.body))
+ T(If, "(condition=%r, body=%r, else=%r)", ast_to_cord(data.condition), ast_to_cord(data.body), ast_to_cord(data.else_body))
+ T(Reduction, "(iter=%r, combination=%r, fallback=%r)", ast_to_cord(data.iter), ast_to_cord(data.combination), ast_to_cord(data.fallback))
+ T(Skip, "(%s)", data.target)
+ T(Stop, "(%s)", data.target)
+ T(Pass, "")
+ T(Return, "(%r)", ast_to_cord(data.value))
+ T(Extern, "(name=%s, type=%r)", data.name, type_ast_to_cord(data.type))
+ T(TypeDef, "(%s, type=%r, namespace=%r)", data.var.name, type_ast_to_cord(data.type), ast_to_cord(data.namespace))
+ T(Index, "(indexed=%r, index=%r)", ast_to_cord(data.indexed), ast_to_cord(data.index))
+ T(FieldAccess, "(fielded=%r, field=%s)", ast_to_cord(data.fielded), data.field)
+ T(DocTest, "(expr=%r, output=%s)", ast_to_cord(data.expr), data.output)
+ T(Use, "(%s)", data.path)
+ T(LinkerDirective, "(%s)", data.directive)
+#undef T
+ }
+ return NULL;
+}
+
+CORD type_ast_to_cord(type_ast_t *t)
+{
+ if (!t) return "\x1b[35mNULL\x1b[m";
+
+ switch (t->tag) {
+#define T(type, ...) case type: { auto data = t->__data.type; (void)data; return CORD_asprintf("\x1b[32;1m" #type "\x1b[m" __VA_ARGS__); }
+ T(TypeUnknown, "")
+ T(TypeVar, "(\x1b[36;1m%s\x1b[m)", data.var.name)
+ T(TypePointer, "(%r, is_optional=%d, is_stack=%d, is_readonly=%d)",
+ type_ast_to_cord(data.pointed), data.is_optional,
+ data.is_stack, data.is_readonly)
+ T(TypeStruct, "(%r)", arg_list_to_cord(data.fields))
+ T(TypeTaggedUnion, "(%r)", tags_to_cord(data.tags))
+ T(TypeArray, "(%r)", type_ast_to_cord(data.item))
+ T(TypeTable, "(%r => %r)", type_ast_to_cord(data.key), type_ast_to_cord(data.value))
+ T(TypeFunction, "(args=%r, ret=%r)", arg_list_to_cord(data.args), type_ast_to_cord(data.ret))
+#undef T
+ }
+ return NULL;
+}
+
+const char *ast_to_str(ast_t *ast) {
+ CORD c = ast_to_cord(ast);
+ return CORD_to_char_star(c);
+}
+
+const char *type_ast_to_str(type_ast_t *t) {
+ CORD c = type_ast_to_cord(t);
+ return CORD_to_char_star(c);
+}
+
+int printf_ast(FILE *stream, const struct printf_info *info, const void *const args[])
+{
+ ast_t *ast = *(ast_t**)(args[0]);
+ if (ast) {
+ if (info->alt)
+ return fprintf(stream, "%.*s", (int)(ast->end - ast->start), ast->start);
+ else
+ return fprintf(stream, "%s", ast_to_str(ast));
+ } else {
+ return fputs("(null)", stream);
+ }
+}
+
+// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0