aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-02-08 00:52:18 -0500
committerBruce Hill <bruce@bruce-hill.com>2024-02-08 00:52:18 -0500
commit930c09f46d1e249fc889e8f1179046a48c1eaf32 (patch)
tree4900307e726dcbc643b12341a9bb7168edebdff5
parentee0f45e2959484d390c30a8a1430a0f040f56631 (diff)
More features and progress
-rw-r--r--Makefile2
-rw-r--r--ast.c27
-rw-r--r--ast.h66
-rw-r--r--compile.c47
-rw-r--r--files.c1
-rw-r--r--nextlang.c16
-rw-r--r--parse.c106
-rw-r--r--types.c557
-rw-r--r--types.h123
-rw-r--r--util.h25
10 files changed, 839 insertions, 131 deletions
diff --git a/Makefile b/Makefile
index 80deb903..4b183a68 100644
--- a/Makefile
+++ b/Makefile
@@ -28,7 +28,7 @@ BUILTIN_OBJS=builtins/array.o builtins/bool.o builtins/builtins.o builtins/char.
all: nextlang
-nextlang: nextlang.c parse.o files.o util.o ast.o compile.o SipHash/halfsiphash.o $(BUILTIN_OBJS)
+nextlang: nextlang.c parse.o files.o util.o ast.o compile.o types.o SipHash/halfsiphash.o $(BUILTIN_OBJS)
SipHash/halfsiphash.c:
git submodule update --init --recursive
diff --git a/ast.c b/ast.c
index df7136f1..bb151aa6 100644
--- a/ast.c
+++ b/ast.c
@@ -20,15 +20,15 @@ static const char *OP_NAMES[] = {
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);
+static CORD arg_list_to_cord(arg_ast_t *args);
+static CORD tags_to_cord(tag_ast_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), \
+ arg_ast_list_t*: arg_list_to_cord(x), \
+ tag_ast_t*: tags_to_cord(x), \
const char *: x, \
int64_t: CORD_asprintf("%ld", x), \
unsigned short int: CORD_asprintf("%d", x), \
@@ -50,11 +50,11 @@ CORD ast_list_to_cord(ast_list_t *asts)
return c;
}
-CORD arg_list_to_cord(arg_list_t *args) {
+CORD arg_list_to_cord(arg_ast_t *args) {
CORD c = "Args(";
for (; args; args = args->next) {
- if (args->var.name)
- c = CORD_cat(c, args->var.name);
+ if (args->name)
+ c = CORD_cat(c, args->name);
if (args->type)
CORD_sprintf(&c, "%r:%s", c, type_ast_to_cord(args->type));
if (args->default_val)
@@ -65,12 +65,12 @@ CORD arg_list_to_cord(arg_list_t *args) {
return c;
}
-CORD tags_to_cord(tag_t *tags) {
+CORD tags_to_cord(tag_ast_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);
+ CORD_sprintf(&c, "%r(%r)=%ld", c, arg_list_to_cord(tags->fields), tags->value);
if (tags->next) c = CORD_cat(c, ", ");
}
c = CORD_cat(c, ")");
@@ -86,7 +86,7 @@ CORD ast_to_cord(ast_t *ast)
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(Var, "(\x1b[36;1m%s\x1b[m)", data.name)
T(Int, "(\x1b[35m%ld\x1b[m, precision=\x1b[35m%ld\x1b[m)", data.i, data.precision)
T(Num, "(\x1b[35m%ld\x1b[m, precision=\x1b[35m%ld\x1b[m)", data.n, data.precision)
T(Char, "(\x1b[35m'%c'\x1b[m)", data.c)
@@ -122,7 +122,8 @@ CORD ast_to_cord(ast_t *ast)
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(StructDef, "(%s, fields=%r, namespace=%r)", data.name, arg_list_to_cord(data.fields), ast_to_cord(data.namespace))
+ T(EnumDef, "(%s, tags=%r, namespace=%r)", data.name, tags_to_cord(data.tags), 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)
@@ -140,12 +141,10 @@ CORD type_ast_to_cord(type_ast_t *t)
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(UnknownTypeAST, "")
- T(VarTypeAST, "(\x1b[36;1m%s\x1b[m)", data.var.name)
+ T(VarTypeAST, "(\x1b[36;1m%s\x1b[m)", data.name)
T(PointerTypeAST, "(%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(StructTypeAST, "(%r)", arg_list_to_cord(data.fields))
- T(TaggedUnionTypeAST, "(%r)", tags_to_cord(data.tags))
T(ArrayTypeAST, "(%r)", type_ast_to_cord(data.item))
T(TableTypeAST, "(%r => %r)", type_ast_to_cord(data.key), type_ast_to_cord(data.value))
T(FunctionTypeAST, "(args=%r, ret=%r)", arg_list_to_cord(data.args), type_ast_to_cord(data.ret))
diff --git a/ast.h b/ast.h
index dfa0659b..d431a988 100644
--- a/ast.h
+++ b/ast.h
@@ -19,35 +19,17 @@ struct binding_s;
typedef struct type_ast_s type_ast_t;
typedef struct ast_s ast_t;
-typedef struct {
- const char *name;
- struct binding_s *binding;
-} var_t;
-
typedef struct ast_list_s {
ast_t *ast;
struct ast_list_s *next;
} ast_list_t;
-typedef struct arg_list_s {
- var_t var;
+typedef struct arg_ast_s {
+ const char *name;
type_ast_t *type;
ast_t *default_val;
- struct arg_list_s *next;
-} arg_list_t;
-
-#define REVERSE_LIST(list) do { \
- __typeof(list) _prev = NULL; \
- __typeof(list) _next = NULL; \
- auto _current = list; \
- while (_current != NULL) { \
- _next = _current->next; \
- _current->next = _prev; \
- _prev = _current; \
- _current = _next; \
- } \
- list = _prev; \
-} while(0)
+ struct arg_ast_s *next;
+} arg_ast_t;
typedef enum {
UNOP_UNKNOWN,
@@ -68,19 +50,17 @@ typedef enum {
UnknownTypeAST,
VarTypeAST,
PointerTypeAST,
- StructTypeAST,
- TaggedUnionTypeAST,
ArrayTypeAST,
TableTypeAST,
FunctionTypeAST,
} type_ast_e;
-typedef struct tag_s {
+typedef struct tag_ast_s {
const char *name;
- struct type_ast_s *type;
+ arg_ast_t *fields;
int64_t value;
- struct tag_s *next;
-} tag_t;
+ struct tag_ast_s *next;
+} tag_ast_t;
struct type_ast_s {
type_ast_e tag;
@@ -89,26 +69,20 @@ struct type_ast_s {
union {
struct {} UnknownTypeAST;
struct {
- var_t var;
+ const char *name;
} VarTypeAST;
struct {
type_ast_t *pointed;
bool is_optional:1, is_stack:1, is_readonly:1;
} PointerTypeAST;
struct {
- arg_list_t *fields;
- } StructTypeAST;
- struct {
- tag_t *tags;
- } TaggedUnionTypeAST;
- struct {
type_ast_t *item;
} ArrayTypeAST;
struct {
type_ast_t *key, *value;
} TableTypeAST;
struct {
- arg_list_t *args;
+ arg_ast_t *args;
type_ast_t *ret;
} FunctionTypeAST;
} __data;
@@ -131,7 +105,8 @@ typedef enum {
Skip, Stop, Pass,
Return,
Extern,
- TypeDef,
+ StructDef,
+ EnumDef,
Index, FieldAccess,
DocTest,
Use,
@@ -151,7 +126,7 @@ struct ast_s {
bool b;
} Bool;
struct {
- var_t var;
+ const char *name;
} Var;
struct {
int64_t i;
@@ -207,14 +182,14 @@ struct ast_s {
} TableEntry;
struct {
ast_t *name;
- arg_list_t *args;
+ arg_ast_t *args;
type_ast_t *ret_type;
ast_t *body;
ast_t *cache;
bool is_inline;
} FunctionDef;
struct {
- arg_list_t *args;
+ arg_ast_t *args;
ast_t *body;
} Lambda;
struct {
@@ -254,10 +229,15 @@ struct ast_s {
bool address;
} Extern;
struct {
- var_t var;
- type_ast_t *type;
+ const char *name;
+ arg_ast_t *fields;
+ ast_t *namespace;
+ } StructDef;
+ struct {
+ const char *name;
+ tag_ast_t *tags;
ast_t *namespace;
- } TypeDef;
+ } EnumDef;
struct {
ast_t *indexed, *index;
bool unchecked;
diff --git a/compile.c b/compile.c
index f7bfbd1b..7193b5ae 100644
--- a/compile.c
+++ b/compile.c
@@ -11,7 +11,7 @@
CORD compile_type(type_ast_t *t)
{
switch (t->tag) {
- case VarTypeAST: return CORD_cat(Match(t, VarTypeAST)->var.name, "_t");
+ case VarTypeAST: return CORD_cat(Match(t, VarTypeAST)->name, "_t");
default: errx(1, "Not implemented");
}
}
@@ -19,7 +19,8 @@ CORD compile_type(type_ast_t *t)
static inline CORD compile_statement(ast_t *ast)
{
switch (ast->tag) {
- case If: case For: case While: case FunctionDef: case Return: case TypeDef: case Declare: case Assign: case UpdateAssign:
+ case If: case For: case While: case FunctionDef: case Return: case StructDef: case EnumDef:
+ case Declare: case Assign: case UpdateAssign:
return compile(ast);
default:
return CORD_asprintf("(void)%r;", compile(ast));
@@ -31,7 +32,7 @@ CORD compile(ast_t *ast)
switch (ast->tag) {
case Nil: return CORD_asprintf("(%r)NULL", compile_type(Match(ast, Nil)->type));
case Bool: return Match(ast, Bool)->b ? "true" : "false";
- case Var: return Match(ast, Var)->var.name;
+ case Var: return Match(ast, Var)->name;
case Int: return CORD_asprintf("((Int%ld_t)%ld)", Match(ast, Int)->precision, Match(ast, Int)->i);
case Num: return CORD_asprintf(Match(ast, Num)->precision == 64 ? "%g" : "%gf", Match(ast, Num)->n);
case Char: return CORD_asprintf("'\\x%02X'", (int)Match(ast, Char)->c);
@@ -174,8 +175,8 @@ CORD compile(ast_t *ast)
case FunctionDef: {
auto fndef = Match(ast, FunctionDef);
CORD code = CORD_asprintf("%r %r(", fndef->ret_type ? compile_type(fndef->ret_type) : "void", compile(fndef->name));
- for (arg_list_t *arg = fndef->args; arg; arg = arg->next) {
- CORD_sprintf(&code, "%r%r %s", code, compile_type(arg->type), arg->var.name);
+ for (arg_ast_t *arg = fndef->args; arg; arg = arg->next) {
+ CORD_sprintf(&code, "%r%r %s", code, compile_type(arg->type), arg->name);
if (arg->next) code = CORD_cat(code, ", ");
}
code = CORD_cat(code, ") ");
@@ -235,24 +236,30 @@ CORD compile(ast_t *ast)
return ret ? CORD_asprintf("return %r;", compile(ret)) : "return;";
}
// Extern,
- case TypeDef: {
- auto def = Match(ast, TypeDef);
- CORD code;
- switch (def->type->tag) {
- case VarTypeAST: {
- CORD_sprintf(&code, "typedef %r %s_t;\n", compile_type(def->type), def->var.name);
- break;
+ case StructDef: {
+ auto def = Match(ast, StructDef);
+ CORD code = CORD_asprintf("typedef struct %s_s %s_t;\nstruct %s_s {\n", def->name, def->name, def->name);
+ for (arg_ast_t *field = def->fields; field; field = field->next) {
+ CORD_sprintf(&code, "%r%r %s;\n", code, compile_type(field->type), field->name);
}
- case StructTypeAST: {
- CORD_sprintf(&code, "typedef struct %s_s %s_t;\nstruct %s_s {\n", def->var.name, def->var.name, def->var.name);
- for (arg_list_t *field = Match(def->type, StructTypeAST)->fields; field; field = field->next) {
- CORD_sprintf(&code, "%r%r %s;\n", code, compile_type(field->type), field->var.name);
- }
- code = CORD_cat(code, "};\n");
- break;
+ code = CORD_cat(code, "};\n");
+ return code;
+ }
+ case EnumDef: {
+ auto def = Match(ast, EnumDef);
+ CORD code = CORD_asprintf("typedef struct %s_s %s_t;\nstruct %s_s {\nenum {", def->name, def->name, def->name);
+ for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
+ CORD_sprintf(&code, "%r%s__%s = %ld, ", code, def->name, tag->name, tag->value);
}
- default: errx(1, "Typedef not implemented");
+ code = CORD_cat(code, "} tag;\nunion {\n");
+ for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
+ code = CORD_cat(code, "struct {\n");
+ for (arg_ast_t *field = tag->fields; field; field = field->next) {
+ CORD_sprintf(&code, "%r%r %s;\n", code, compile_type(field->type), field->name);
+ }
+ CORD_sprintf(&code, "%r} %s;\n", code, tag->name);
}
+ code = CORD_cat(code, "} __data;\n};\n");
return code;
}
// Index, FieldAccess,
diff --git a/files.c b/files.c
index 51a8740c..40360719 100644
--- a/files.c
+++ b/files.c
@@ -91,6 +91,7 @@ static sss_file_t *_load_file(const char* filename, FILE *file)
memcpy(copy, file_buf, file_size);
copy[file_size] = '\0';
ret->text = copy;
+ ret->len = file_size;
fclose(mem);
free(file_buf);
diff --git a/nextlang.c b/nextlang.c
index f3b8d88b..7b56d0a6 100644
--- a/nextlang.c
+++ b/nextlang.c
@@ -2,15 +2,23 @@
#include <stdlib.h>
#include <gc.h>
#include <gc/cord.h>
+#include <printf.h>
#include "ast.h"
#include "parse.h"
#include "compile.h"
+#include "types.h"
int main(int argc, char *argv[])
{
if (argc < 2) return 1;
+ // register_printf_modifier(L"p");
+ if (register_printf_specifier('T', printf_type, printf_pointer_size))
+ errx(1, "Couldn't set printf specifier");
+ if (register_printf_specifier('W', printf_ast, printf_pointer_size))
+ errx(1, "Couldn't set printf specifier");
+
const char *autofmt = getenv("AUTOFMT");
if (!autofmt) autofmt = "indent -kr -nut | bat --file-name=out.c";
@@ -34,7 +42,7 @@ int main(int argc, char *argv[])
// Predeclare types:
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
switch (stmt->ast->tag) {
- case TypeDef: {
+ case StructDef: case EnumDef: {
code = CORD_cat(code, compile(stmt->ast));
break;
}
@@ -48,8 +56,8 @@ int main(int argc, char *argv[])
case FunctionDef: {
auto fndef = Match(stmt->ast, FunctionDef);
CORD_sprintf(&code, "%rstatic %r %r(", code, fndef->ret_type ? compile_type(fndef->ret_type) : "void", compile(fndef->name));
- for (arg_list_t *arg = fndef->args; arg; arg = arg->next) {
- CORD_sprintf(&code, "%r%r %s", code, compile_type(arg->type), arg->var.name);
+ for (arg_ast_t *arg = fndef->args; arg; arg = arg->next) {
+ CORD_sprintf(&code, "%r%r %s", code, compile_type(arg->type), arg->name);
if (arg->next) code = CORD_cat(code, ", ");
}
code = CORD_cat(code, ");\n");
@@ -77,7 +85,7 @@ int main(int argc, char *argv[])
"GC_INIT();\n\n");
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
switch (stmt->ast->tag) {
- case FunctionDef: case TypeDef: break;
+ case FunctionDef: case StructDef: case EnumDef: break;
default: {
code = CORD_cat(code, compile(stmt->ast));
code = CORD_cat(code, ";\n");
diff --git a/parse.c b/parse.c
index 9a457d10..2e3fa51c 100644
--- a/parse.c
+++ b/parse.c
@@ -14,6 +14,7 @@
#include "ast.h"
#include "util.h"
+
typedef struct {
sss_file_t *file;
jmp_buf *on_err;
@@ -64,7 +65,7 @@ static inline binop_e match_binary_operator(const char **pos);
static ast_t *parse_fncall_suffix(parse_ctx_t *ctx, ast_t *fn, bool is_extern);
static ast_t *parse_field_suffix(parse_ctx_t *ctx, ast_t *lhs);
static ast_t *parse_index_suffix(parse_ctx_t *ctx, ast_t *lhs);
-static arg_list_t *parse_args(parse_ctx_t *ctx, const char **pos, bool allow_unnamed);
+static arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos, bool allow_unnamed);
static PARSER(parse_for);
static PARSER(parse_while);
static PARSER(parse_if);
@@ -77,7 +78,8 @@ static PARSER(parse_statement);
static PARSER(parse_block);
static PARSER(parse_opt_indented_block);
static PARSER(parse_var);
-static PARSER(parse_type_def);
+static PARSER(parse_enum_def);
+static PARSER(parse_struct_def);
static PARSER(parse_func_def);
static PARSER(parse_extern);
static PARSER(parse_declaration);
@@ -87,7 +89,6 @@ static PARSER(parse_linker);
static PARSER(parse_namespace);
static type_ast_t *parse_type(parse_ctx_t *ctx, const char *pos);
-static type_ast_t *parse_enum_type(parse_ctx_t *ctx, const char *pos);
//
// Print a parse error and exit (or use the on_err longjmp)
@@ -438,23 +439,12 @@ type_ast_t *parse_table_type(parse_ctx_t *ctx, const char *pos) {
return NewTypeAST(ctx->file, start, pos, TableTypeAST, .key=key_type, .value=value_type);
}
-type_ast_t *parse_struct_type(parse_ctx_t *ctx, const char *pos) {
- const char *start = pos;
- if (!match(&pos, "struct")) return NULL;
- spaces(&pos);
- if (!match(&pos, "(")) return NULL;
- arg_list_t *args = parse_args(ctx, &pos, false);
- whitespace(&pos);
- expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this struct type");
- return NewTypeAST(ctx->file, start, pos, StructTypeAST, .fields=args);
-}
-
type_ast_t *parse_func_type(parse_ctx_t *ctx, const char *pos) {
const char *start = pos;
if (!match_word(&pos, "func")) return NULL;
spaces(&pos);
if (!match(&pos, "(")) return NULL;
- arg_list_t *args = parse_args(ctx, &pos, true);
+ arg_ast_t *args = parse_args(ctx, &pos, true);
expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this function type");
spaces(&pos);
if (!match(&pos, "->")) return NULL;
@@ -504,18 +494,16 @@ type_ast_t *parse_type_name(parse_ctx_t *ctx, const char *pos) {
id = heap_strf("%s.%s", id, next_id);
pos = next;
}
- return NewTypeAST(ctx->file, start, pos, VarTypeAST, .var.name=id);
+ return NewTypeAST(ctx->file, start, pos, VarTypeAST, .name=id);
}
type_ast_t *parse_type(parse_ctx_t *ctx, const char *pos) {
const char *start = pos;
type_ast_t *type = NULL;
bool success = (false
- || (type=parse_enum_type(ctx, pos))
|| (type=parse_pointer_type(ctx, pos))
|| (type=parse_array_type(ctx, pos))
|| (type=parse_table_type(ctx, pos))
- || (type=parse_struct_type(ctx, pos))
|| (type=parse_type_name(ctx, pos))
|| (type=parse_func_type(ctx, pos))
);
@@ -707,8 +695,8 @@ PARSER(parse_reduction) {
if (op == BINOP_UNKNOWN) return NULL;
ast_t *combination;
- ast_t *lhs = NewAST(ctx->file, pos, pos, Var, .var.name="lhs.0");
- ast_t *rhs = NewAST(ctx->file, pos, pos, Var, .var.name="rhs.0");
+ ast_t *lhs = NewAST(ctx->file, pos, pos, Var, .name="lhs.0");
+ ast_t *rhs = NewAST(ctx->file, pos, pos, Var, .name="rhs.0");
if (op == BINOP_MIN || op == BINOP_MAX) {
for (bool progress = true; progress; ) {
ast_t *new_term;
@@ -1070,7 +1058,7 @@ PARSER(parse_lambda) {
spaces(&pos);
if (!match(&pos, "("))
return NULL;
- arg_list_t *args = parse_args(ctx, &pos, false);
+ arg_ast_t *args = parse_args(ctx, &pos, false);
spaces(&pos);
expect_closing(ctx, &pos, ")", "I was expecting a ')' to finish this anonymous function's arguments");
ast_t *body = optional(ctx, &pos, parse_opt_indented_block);
@@ -1089,7 +1077,7 @@ PARSER(parse_var) {
const char *start = pos;
const char* name = get_id(&pos);
if (!name) return NULL;
- return NewAST(ctx->file, start, pos, Var, .var.name=name);
+ return NewAST(ctx->file, start, pos, Var, .name=name);
}
PARSER(parse_term_no_suffix) {
@@ -1188,7 +1176,7 @@ ast_t *parse_fncall_suffix(parse_ctx_t *ctx, ast_t *fn, bool is_extern) {
if (match(&pos, ":"))
extern_return_type = expect(ctx, start, &pos, parse_type, "I couldn't parse the return type of this external function call");
else
- extern_return_type = NewTypeAST(ctx->file, pos, pos, VarTypeAST, .var.name="Void");
+ extern_return_type = NewTypeAST(ctx->file, pos, pos, VarTypeAST, .name="Void");
}
REVERSE_LIST(args);
return NewAST(ctx->file, start, pos, FunctionCall, .fn=fn, .args=args, .extern_return_type=extern_return_type);
@@ -1238,7 +1226,7 @@ static ast_t *parse_infix_expr(parse_ctx_t *ctx, const char *pos, int min_tightn
ast_t *key = NULL;
if (op == BINOP_MIN || op == BINOP_MAX) {
- key = NewAST(ctx->file, pos, pos, Var, .var.name=op == BINOP_MIN ? "_min_" : "_max_");
+ key = NewAST(ctx->file, pos, pos, Var, .name=op == BINOP_MIN ? "_min_" : "_max_");
for (bool progress = true; progress; ) {
ast_t *new_term;
progress = (false
@@ -1415,7 +1403,8 @@ PARSER(parse_namespace) {
whitespace(&next);
if (sss_get_indent(ctx->file, next) != indent) break;
ast_t *stmt;
- if ((stmt=optional(ctx, &pos, parse_type_def))
+ if ((stmt=optional(ctx, &pos, parse_struct_def))
+ ||(stmt=optional(ctx, &pos, parse_enum_def))
||(stmt=optional(ctx, &pos, parse_linker))
||(stmt=optional(ctx, &pos, parse_statement)))
{
@@ -1432,19 +1421,24 @@ PARSER(parse_namespace) {
return NewAST(ctx->file, start, pos, Block, .statements=statements);
}
-PARSER(parse_type_def) {
- // type Foo := Type... \n body...
+PARSER(parse_struct_def) {
+ // struct Foo(...) \n body
const char *start = pos;
- if (!match_word(&pos, "type")) return NULL;
+ if (!match_word(&pos, "struct")) return NULL;
int64_t starting_indent = sss_get_indent(ctx->file, pos);
+ spaces(&pos);
const char *name = get_id(&pos);
- if (!name) return NULL;
+ if (!name) parser_err(ctx, start, pos, "I expected a name for this struct");
spaces(&pos);
- if (!match(&pos, ":=")) return NULL;
- type_ast_t *type_ast = expect(ctx, start, &pos, parse_type, "I expected a type after this ':='");
+ if (!match(&pos, "("))
+ parser_err(ctx, pos, pos, "I expected a '(' and a list of fields here");
+
+ arg_ast_t *fields = parse_args(ctx, &pos, false);
+
+ expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this struct");
const char *ns_pos = pos;
whitespace(&ns_pos);
@@ -1456,18 +1450,22 @@ PARSER(parse_type_def) {
}
if (!namespace)
namespace = NewAST(ctx->file, pos, pos, Block, .statements=NULL);
- return NewAST(ctx->file, start, pos, TypeDef, .var.name=name, .type=type_ast, .namespace=namespace);
+ return NewAST(ctx->file, start, pos, StructDef, .name=name, .fields=fields, .namespace=namespace);
}
-type_ast_t *parse_enum_type(parse_ctx_t *ctx, const char *pos) {
- // tagged union: enum Foo := a|b(x:Int,y:Int)=5|...
+ast_t *parse_enum_def(parse_ctx_t *ctx, const char *pos) {
+ // tagged union: enum Foo(a|b(x:Int,y:Int)=5|...) \n namespace
const char *start = pos;
-
if (!match_word(&pos, "enum")) return NULL;
+ int64_t starting_indent = sss_get_indent(ctx->file, pos);
+ spaces(&pos);
+ const char *name = get_id(&pos);
+ if (!name)
+ parser_err(ctx, start, pos, "I expected a name for this enum");
spaces(&pos);
if (!match(&pos, "(")) return NULL;
- tag_t *tags = NULL;
+ tag_ast_t *tags = NULL;
int64_t next_value = 0;
whitespace(&pos);
@@ -1479,7 +1477,7 @@ type_ast_t *parse_enum_type(parse_ctx_t *ctx, const char *pos) {
if (!tag_name) break;
spaces(&pos);
- arg_list_t *fields;
+ arg_ast_t *fields;
if (match(&pos, "(")) {
whitespace(&pos);
fields = parse_args(ctx, &pos, false);
@@ -1496,17 +1494,16 @@ type_ast_t *parse_enum_type(parse_ctx_t *ctx, const char *pos) {
}
// Check for duplicate values:
- for (tag_t *t = tags; t; t = t->next) {
+ for (tag_ast_t *t = tags; t; t = t->next) {
if (t->value == next_value)
parser_err(ctx, tag_start, pos, "This tag value (%ld) is a duplicate of an earlier tag value", next_value);
}
- type_ast_t *type = NewTypeAST(ctx->file, tag_start, pos, StructTypeAST, .fields=fields);
- tags = new(tag_t, .name=tag_name, .value=next_value, .type=type, .next=tags);
+ tags = new(tag_ast_t, .name=tag_name, .value=next_value, .fields=fields, .next=tags);
const char *next_pos = pos;
whitespace(&next_pos);
- if (!match(&next_pos, "|"))
+ if (!match(&next_pos, ","))
break;
whitespace(&next_pos);
pos = next_pos;
@@ -1518,12 +1515,23 @@ type_ast_t *parse_enum_type(parse_ctx_t *ctx, const char *pos) {
REVERSE_LIST(tags);
- return NewTypeAST(ctx->file, start, pos, TaggedUnionTypeAST, .tags=tags);
+ const char *ns_pos = pos;
+ whitespace(&ns_pos);
+ int64_t ns_indent = sss_get_indent(ctx->file, ns_pos);
+ ast_t *namespace = NULL;
+ if (ns_indent > starting_indent) {
+ pos = ns_pos;
+ namespace = optional(ctx, &pos, parse_namespace);
+ }
+ if (!namespace)
+ namespace = NewAST(ctx->file, pos, pos, Block, .statements=NULL);
+
+ return NewAST(ctx->file, start, pos, EnumDef, .name=name, .tags=tags, .namespace=namespace);
}
-arg_list_t *parse_args(parse_ctx_t *ctx, const char **pos, bool allow_unnamed)
+arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos, bool allow_unnamed)
{
- arg_list_t *args = NULL;
+ arg_ast_t *args = NULL;
for (;;) {
const char *batch_start = *pos;
ast_t *default_val = NULL;
@@ -1570,7 +1578,7 @@ arg_list_t *parse_args(parse_ctx_t *ctx, const char **pos, bool allow_unnamed)
REVERSE_LIST(names);
for (; names; names = names->next)
- args = new(arg_list_t, .var.name=names->name, .type=type, .default_val=default_val, .next=args);
+ args = new(arg_ast_t, .name=names->name, .type=type, .default_val=default_val, .next=args);
whitespace(pos);
match(pos, ",");
}
@@ -1590,7 +1598,7 @@ PARSER(parse_func_def) {
if (!match(&pos, "(")) return NULL;
- arg_list_t *args = parse_args(ctx, &pos, false);
+ arg_ast_t *args = parse_args(ctx, &pos, false);
whitespace(&pos);
bool is_inline = false;
ast_t *cache_ast = NULL;
@@ -1630,7 +1638,7 @@ PARSER(parse_extern) {
spaces(&pos);
// extern function call:
if (match(&pos, "(")) {
- return parse_fncall_suffix(ctx, NewAST(ctx->file, start, pos-1, Var, .var.name=name), EXTERN_FUNCTION);
+ return parse_fncall_suffix(ctx, NewAST(ctx->file, start, pos-1, Var, .name=name), EXTERN_FUNCTION);
}
if (!match(&pos, ":"))
parser_err(ctx, start, pos, "I couldn't get a type for this extern");
@@ -1712,8 +1720,8 @@ ast_t *parse_file(sss_file_t *file, jmp_buf *on_err) {
ast_t *ast = parse_namespace(&ctx, pos);
pos = ast->end;
whitespace(&pos);
- if (strlen(pos) > 0) {
- parser_err(&ctx, pos, pos + strlen(pos), "I couldn't parse this part of the file");
+ if (pos < file->text + file->len) {
+ parser_err(&ctx, pos, pos + strlen(pos), "I couldn't parse this part of the file %zu");
}
return ast;
}
diff --git a/types.c b/types.c
new file mode 100644
index 00000000..aa977507
--- /dev/null
+++ b/types.c
@@ -0,0 +1,557 @@
+// Logic for handling type_t types
+#include <gc/cord.h>
+#include <stdint.h>
+#include <limits.h>
+#include <math.h>
+
+#include "builtins/table.h"
+#include "types.h"
+#include "util.h"
+
+static CORD type_to_cord(type_t *t) {
+ switch (t->tag) {
+ case UnknownType: return "???";
+ case AbortType: return "Abort";
+ case VoidType: return "Void";
+ case MemoryType: return "Memory";
+ case BoolType: return "Bool";
+ case CharType: return "Char";
+ case IntType: return CORD_asprintf("Int%ld", Match(t, IntType)->bits);
+ case NumType: return CORD_asprintf("Num%ld", Match(t, NumType)->bits);
+ case ArrayType: {
+ auto array = Match(t, ArrayType);
+ return CORD_asprintf("[%r]", type_to_cord(array->item_type));
+ }
+ case TableType: {
+ auto table = Match(t, TableType);
+ return CORD_asprintf("{%r=>%r}", type_to_cord(table->key_type), type_to_cord(table->value_type));
+ }
+ case FunctionType: {
+ CORD c = "func(";
+ auto fn = Match(t, FunctionType);
+ for (arg_t *arg = fn->args; arg; arg = arg->next) {
+ c = CORD_cat(c, type_to_cord(arg->type));
+ if (arg->next) c = CORD_cat(c, ", ");
+ }
+ c = CORD_cat(c, ")->");
+ c = CORD_cat(c, type_to_cord(fn->ret));
+ return c;
+ }
+ case StructType: {
+ auto struct_ = Match(t, StructType);
+ CORD c = "struct(";
+ int64_t i = 1;
+ for (arg_t *field = struct_->fields; field; field = field->next) {
+ const char *fname = field->name ? field->name : heap_strf("_%lu", i);
+ ++i;
+ if (fname && !streq(fname, heap_strf("_%lu", i+1)))
+ c = CORD_cat(CORD_cat(c, fname), ":");
+ else
+ c = CORD_cat(c, ":");
+
+ c = CORD_cat(c, type_to_cord(field->type));
+
+ if (field->next) c = CORD_cat(c, ", ");
+ }
+ c = CORD_cat(c, ")");
+ return c;
+ }
+ case PointerType: {
+ auto ptr = Match(t, PointerType);
+ CORD sigil = ptr->is_stack ? "&" : (ptr->is_optional ? "?" : "@");
+ if (ptr->is_readonly) sigil = CORD_cat(sigil, "(readonly)");
+ return CORD_cat(sigil, type_to_cord(ptr->pointed));
+ }
+ case EnumType: {
+ auto tagged = Match(t, EnumType);
+
+ CORD c = "enum(";
+ int64_t next_tag = 0;
+ for (tag_t *tag = tagged->tags; tag; tag = tag->next) {
+ // name, tag_value, type
+ c = CORD_cat(c, tag->name);
+ if (tag->type) {
+ c = CORD_cat(c, "(");
+ auto struct_ = Match(tag->type, StructType);
+ int64_t i = 1;
+ for (arg_t *field = struct_->fields; field; field = field->next) {
+ if (field->name && !streq(field->name, heap_strf("_%lu", i)))
+ c = CORD_cat(CORD_cat(c, field->name), ":");
+
+ CORD fstr = type_to_cord(field->type);
+ c = CORD_cat(c, fstr);
+ if (field->next) c = CORD_cat(c, ",");
+ ++i;
+ }
+ c = CORD_cat(c, ")");
+ }
+
+ if (tag->tag_value != next_tag) {
+ CORD_sprintf(&c, "%r=%ld", c, tag->tag_value);
+ next_tag = tag->tag_value + 1;
+ } else {
+ ++next_tag;
+ }
+
+ if (tag->next)
+ c = CORD_cat(c, "|");
+ }
+ c = CORD_cat(c, ")");
+ return c;
+ }
+ case VariantType: {
+ return Match(t, VariantType)->name;
+ }
+ case PlaceholderType: {
+ return Match(t, PlaceholderType)->name;
+ }
+ case TypeInfoType: {
+ return "TypeInfo";
+ }
+ default: {
+ return CORD_asprintf("Unknown type: %d", t->tag);
+ }
+ }
+}
+
+int printf_pointer_size(const struct printf_info *info, size_t n, int argtypes[n], int sizes[n])
+{
+ if (n < 1) return -1;
+ (void)info;
+ argtypes[0] = PA_POINTER;
+ sizes[0] = sizeof(void*);
+ return 1;
+}
+
+int printf_type(FILE *stream, const struct printf_info *info, const void *const args[])
+{
+ (void)info;
+ type_t *t = *(type_t**)args[0];
+ if (!t) return fputs("(null)", stream);
+ return CORD_put(type_to_cord(t), stream);
+}
+
+const char *type_to_string(type_t *t) {
+ return CORD_to_const_char_star(type_to_cord(t));
+}
+
+bool type_eq(type_t *a, type_t *b)
+{
+ if (a == b) return true;
+ if (a->tag != b->tag) return false;
+ if (a->tag == PlaceholderType) return a == b;
+ return streq(type_to_string(a), type_to_string(b));
+}
+
+bool type_is_a(type_t *t, type_t *req)
+{
+ if (type_eq(t, req)) return true;
+ if (t->tag == PointerType && req->tag == PointerType) {
+ auto t_ptr = Match(t, PointerType);
+ auto req_ptr = Match(req, PointerType);
+ if (type_eq(t_ptr->pointed, req_ptr->pointed))
+ return (!t_ptr->is_stack && !t_ptr->is_optional && req_ptr->is_stack)
+ || (!t_ptr->is_stack && req_ptr->is_optional);
+ }
+ return false;
+}
+
+static type_t *non_optional(type_t *t)
+{
+ if (t->tag != PointerType) return t;
+ auto ptr = Match(t, PointerType);
+ return ptr->is_optional ? Type(PointerType, .is_optional=false, .pointed=ptr->pointed) : t;
+}
+
+type_t *value_type(type_t *t)
+{
+ while (t->tag == PointerType)
+ t = Match(t, PointerType)->pointed;
+ return t;
+}
+
+type_t *base_value_type(type_t *t)
+{
+ for (;;) {
+ if (t->tag == PointerType)
+ t = Match(t, PointerType)->pointed;
+ else if (t->tag == VariantType)
+ t = Match(t, VariantType)->variant_of;
+ else break;
+ }
+ return t;
+}
+
+type_t *type_or_type(type_t *a, type_t *b)
+{
+ if (!a) return b;
+ if (!b) return a;
+ if (type_is_a(b, a)) return a;
+ if (type_is_a(a, b)) return b;
+ if (a->tag == AbortType) return non_optional(b);
+ if (b->tag == AbortType) return non_optional(a);
+ if ((a->tag == IntType || a->tag == NumType) && (b->tag == IntType || b->tag == NumType)) {
+ switch (compare_precision(a, b)) {
+ case NUM_PRECISION_EQUAL: case NUM_PRECISION_MORE: return a;
+ case NUM_PRECISION_LESS: return b;
+ case NUM_PRECISION_INCOMPARABLE: {
+ if (a->tag == IntType && b->tag == IntType && Match(a, IntType)->bits < 64)
+ return Type(IntType, .bits=Match(a, IntType)->bits * 2);
+ return NULL;
+ }
+ }
+ return NULL;
+ }
+ return NULL;
+}
+
+bool is_integral(type_t *t)
+{
+ t = base_variant(t);
+ return t->tag == IntType || t->tag == CharType;
+}
+
+bool is_floating_point(type_t *t)
+{
+ t = base_variant(t);
+ return t->tag == NumType;
+}
+
+bool is_numeric(type_t *t)
+{
+ t = base_variant(t);
+ return t->tag == IntType || t->tag == NumType;
+}
+
+static inline double type_min_magnitude(type_t *t)
+{
+ switch (t->tag) {
+ case BoolType: return (double)false;
+ case CharType: return (double)CHAR_MIN;
+ case IntType: {
+ switch (Match(t, IntType)->bits) {
+ case 8: return (double)INT8_MIN;
+ case 16: return (double)INT16_MIN;
+ case 32: return (double)INT32_MIN;
+ case 64: return (double)INT64_MIN;
+ default: return NAN;
+ }
+ }
+ case NumType: return -1./0.;
+ case VariantType: return type_min_magnitude(Match(t, VariantType)->variant_of);
+ default: return NAN;
+ }
+}
+
+static inline double type_max_magnitude(type_t *t)
+{
+ switch (t->tag) {
+ case BoolType: return (double)true;
+ case CharType: return (double)CHAR_MAX;
+ case IntType: {
+ switch (Match(t, IntType)->bits) {
+ case 8: return (double)INT8_MAX;
+ case 16: return (double)INT16_MAX;
+ case 32: return (double)INT32_MAX;
+ case 64: return (double)INT64_MAX;
+ default: return NAN;
+ }
+ }
+ case NumType: return 1./0.;
+ case VariantType: return type_max_magnitude(Match(t, VariantType)->variant_of);
+ default: return NAN;
+ }
+}
+
+precision_cmp_e compare_precision(type_t *a, type_t *b)
+{
+ double a_min = type_min_magnitude(a),
+ b_min = type_min_magnitude(b),
+ a_max = type_max_magnitude(a),
+ b_max = type_max_magnitude(b);
+
+ if (isnan(a_min) || isnan(b_min) || isnan(a_max) || isnan(b_max))
+ return NUM_PRECISION_INCOMPARABLE;
+ else if (a_min == b_min && a_max == b_max) return NUM_PRECISION_EQUAL;
+ else if (a_min <= b_min && b_max <= a_max) return NUM_PRECISION_MORE;
+ else if (b_min <= a_min && a_max <= b_max) return NUM_PRECISION_LESS;
+ else return NUM_PRECISION_INCOMPARABLE;
+}
+
+bool is_orderable(type_t *t)
+{
+ switch (t->tag) {
+ case ArrayType: return is_orderable(Match(t, ArrayType)->item_type);
+ case PointerType: case FunctionType: case TableType: return false;
+ case StructType: {
+ for (arg_t *field = Match(t, StructType)->fields; field; field = field->next) {
+ if (!is_orderable(field->type))
+ return false;
+ }
+ return true;
+ }
+ case EnumType: {
+ for (tag_t *tag = Match(t, EnumType)->tags; tag; tag = tag->next) {
+ if (tag->type && !is_orderable(tag->type))
+ return false;
+ }
+ return true;
+ }
+ default: return true;
+ }
+}
+
+bool has_heap_memory(type_t *t)
+{
+ switch (t->tag) {
+ case ArrayType: return true;
+ case TableType: return true;
+ case PointerType: return true;
+ case StructType: {
+ for (arg_t *field = Match(t, StructType)->fields; field; field = field->next) {
+ if (has_heap_memory(field->type))
+ return true;
+ }
+ return false;
+ }
+ case EnumType: {
+ for (tag_t *tag = Match(t, EnumType)->tags; tag; tag = tag->next) {
+ if (tag->type && has_heap_memory(tag->type))
+ return true;
+ }
+ return false;
+ }
+ default: return false;
+ }
+}
+
+bool has_stack_memory(type_t *t)
+{
+ switch (t->tag) {
+ case PointerType: return Match(t, PointerType)->is_stack;
+ default: return false;
+ }
+}
+
+bool can_promote(type_t *actual, type_t *needed)
+{
+ // No promotion necessary:
+ if (type_eq(actual, needed))
+ return true;
+
+ if (is_numeric(actual) && is_numeric(needed)) {
+ auto cmp = compare_precision(actual, needed);
+ return cmp == NUM_PRECISION_EQUAL || cmp == NUM_PRECISION_LESS;
+ }
+
+ // Automatic dereferencing:
+ if (actual->tag == PointerType && !Match(actual, PointerType)->is_optional
+ && can_promote(Match(actual, PointerType)->pointed, needed))
+ return true;
+
+ // Optional promotion:
+ if (needed->tag == PointerType && actual->tag == PointerType) {
+ auto needed_ptr = Match(needed, PointerType);
+ auto actual_ptr = Match(actual, PointerType);
+ if (needed_ptr->pointed->tag != MemoryType && !type_eq(needed_ptr->pointed, actual_ptr->pointed))
+ // Can't use @Foo for a function that wants @Baz
+ // But you *can* use @Foo for a function that wants @Memory
+ return false;
+ else if (actual_ptr->is_stack && !needed_ptr->is_stack)
+ // Can't use &x for a function that wants a @Foo or ?Foo
+ return false;
+ else if (actual_ptr->is_optional && !needed_ptr->is_optional)
+ // Can't use !Foo for a function that wants @Foo
+ return false;
+ else if (actual_ptr->is_readonly && !needed_ptr->is_readonly)
+ // Can't use pointer to readonly data when we need a pointer that can write to the data
+ return false;
+ else
+ return true;
+ }
+
+ // Function promotion:
+ if (needed->tag == FunctionType && actual->tag == FunctionType) {
+ auto needed_fn = Match(needed, FunctionType);
+ auto actual_fn = Match(actual, FunctionType);
+ for (arg_t *needed_arg = needed_fn->args, *actual_arg = actual_fn->args;
+ needed_arg || actual_arg;
+ needed_arg = needed_arg->next, actual_arg = actual_arg->next) {
+
+ if (!needed_arg || !actual_arg)
+ return false;
+
+ if (!type_eq(needed_arg->type, actual_arg->type))
+ return false;
+ }
+ return true;
+ }
+
+ // If we have a DSL, it should be possible to use it as a Str
+ if (is_variant_of(actual, needed))
+ return true;
+
+ if (actual->tag == StructType && base_variant(needed)->tag == StructType) {
+ auto actual_struct = Match(actual, StructType);
+ auto needed_struct = Match(base_variant(needed), StructType);
+ // TODO: allow promoting with uninitialized or extraneous values?
+ for (arg_t *needed_field = needed_struct->fields, *actual_field = actual_struct->fields;
+ needed_field || actual_field;
+ needed_field = needed_field->next, actual_field = actual_field->next) {
+
+ if (!needed_field || !actual_field)
+ return false;
+
+ // TODO: check field names??
+ if (!can_promote(actual_field->type, needed_field->type))
+ return false;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool can_leave_uninitialized(type_t *t)
+{
+ switch (t->tag) {
+ case PointerType: return Match(t, PointerType)->is_optional;
+ case ArrayType: case IntType: case NumType: case CharType: case BoolType:
+ return true;
+ case StructType: {
+ for (arg_t *field = Match(t, StructType)->fields; field; field = field->next) {
+ if (!can_leave_uninitialized(field->type))
+ return false;
+ }
+ return true;
+ }
+ case EnumType: {
+ for (tag_t *tag = Match(t, EnumType)->tags; tag; tag = tag->next) {
+ if (tag->type && !can_leave_uninitialized(tag->type))
+ return false;
+ }
+ return true;
+ }
+ default: return false;
+ }
+}
+
+static bool _can_have_cycles(type_t *t, table_t *seen)
+{
+ switch (t->tag) {
+ case ArrayType: return _can_have_cycles(Match(t, ArrayType)->item_type, seen);
+ case TableType: {
+ auto table = Match(t, TableType);
+ return _can_have_cycles(table->key_type, seen) || _can_have_cycles(table->value_type, seen);
+ }
+ case StructType: {
+ for (arg_t *field = Match(t, StructType)->fields; field; field = field->next) {
+ if (_can_have_cycles(field->type, seen))
+ return true;
+ }
+ return false;
+ }
+ case PointerType: return _can_have_cycles(Match(t, PointerType)->pointed, seen);
+ case EnumType: {
+ for (tag_t *tag = Match(t, EnumType)->tags; tag; tag = tag->next) {
+ if (tag->type && _can_have_cycles(tag->type, seen))
+ return true;
+ }
+ return false;
+ }
+ case VariantType: {
+ const char *name = Match(t, VariantType)->name;
+ if (name && Table_str_get(seen, name))
+ return true;
+ Table_str_set(seen, name, t);
+ return _can_have_cycles(Match(t, VariantType)->variant_of, seen);
+ }
+ default: return false;
+ }
+}
+
+bool can_have_cycles(type_t *t)
+{
+ table_t seen = {0};
+ return _can_have_cycles(t, &seen);
+}
+
+type_t *table_entry_type(type_t *table_type)
+{
+ static table_t cache = {0};
+ arg_t *fields = new(
+ arg_t, .name="key",
+ .type=Match(table_type, TableType)->key_type);
+ fields->next = new(
+ arg_t, .name="value",
+ .type=Match(table_type, TableType)->value_type);
+ type_t *t = Type(StructType, .fields=fields);
+ type_t *cached = Table_str_get(&cache, type_to_string(t));
+ if (cached) {
+ return cached;
+ } else {
+ Table_str_set(&cache, type_to_string(t), t);
+ return t;
+ }
+}
+
+type_t *base_variant(type_t *t)
+{
+ while (t->tag == VariantType)
+ t = Match(t, VariantType)->variant_of;
+ return t;
+}
+
+bool is_variant_of(type_t *t, type_t *base)
+{
+ for (; t->tag == VariantType; t = Match(t, VariantType)->variant_of) {
+ if (type_eq(Match(t, VariantType)->variant_of, base))
+ return true;
+ }
+ return false;
+}
+
+type_t *replace_type(type_t *t, type_t *target, type_t *replacement)
+{
+ if (type_eq(t, target))
+ return replacement;
+
+#define COPY(t) memcpy(GC_MALLOC(sizeof(type_t)), (t), sizeof(type_t))
+#define REPLACED_MEMBER(t, tag, member) ({ t = memcpy(GC_MALLOC(sizeof(type_t)), (t), sizeof(type_t)); Match((struct type_s*)(t), tag)->member = replace_type(Match((t), tag)->member, target, replacement); t; })
+ switch (t->tag) {
+ case ArrayType: return REPLACED_MEMBER(t, ArrayType, item_type);
+ case TableType: {
+ t = REPLACED_MEMBER(t, TableType, key_type);
+ t = REPLACED_MEMBER(t, TableType, value_type);
+ return t;
+ }
+ case FunctionType: {
+ auto fn = Match(t, FunctionType);
+ t = REPLACED_MEMBER(t, FunctionType, ret);
+ arg_t *args = LIST_MAP(fn->args, old_arg, .type=replace_type(old_arg->type, target, replacement));
+ Match((struct type_s*)t, FunctionType)->args = args;
+ return t;
+ }
+ case StructType: {
+ auto struct_ = Match(t, StructType);
+ arg_t *fields = LIST_MAP(struct_->fields, field, .type=replace_type(field->type, target, replacement));
+ t = COPY(t);
+ Match((struct type_s*)t, StructType)->fields = fields;
+ return t;
+ }
+ case PointerType: return REPLACED_MEMBER(t, PointerType, pointed);
+ case EnumType: {
+ auto tagged = Match(t, EnumType);
+ tag_t *tags = LIST_MAP(tagged->tags, tag, .type=replace_type(tag->type, target, replacement));
+ t = COPY(t);
+ Match((struct type_s*)t, EnumType)->tags = tags;
+ return t;
+ }
+ case VariantType: return REPLACED_MEMBER(t, VariantType, variant_of);
+ default: return t;
+ }
+#undef COPY
+#undef REPLACED_MEMBER
+}
+
+// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/types.h b/types.h
new file mode 100644
index 00000000..001167ab
--- /dev/null
+++ b/types.h
@@ -0,0 +1,123 @@
+#pragma once
+#include <libgccjit.h>
+#include <printf.h>
+#include <stdlib.h>
+#include <libgccjit.h>
+
+#include "ast.h"
+#include "builtins/array.h"
+
+typedef struct type_s type_t;
+
+typedef struct arg_s {
+ const char *name;
+ type_t *type;
+ ast_t *default_val;
+ struct arg_s *next;
+} arg_t;
+
+typedef struct tag_s {
+ const char *name;
+ int64_t tag_value;
+ type_t *type;
+ struct tag_s *next;
+} tag_t;
+
+typedef struct type_list_s {
+ type_t *type;
+ struct type_list_s *next;
+} type_list_t;
+
+struct type_s {
+ enum {
+ UnknownType,
+ AbortType,
+ VoidType,
+ MemoryType,
+ BoolType,
+ CharType,
+ IntType,
+ NumType,
+ ArrayType,
+ TableType,
+ FunctionType,
+ PointerType,
+ StructType,
+ EnumType,
+ VariantType,
+ TypeInfoType,
+ PlaceholderType,
+ } tag;
+
+ union {
+ struct {
+ } UnknownType, AbortType, VoidType, MemoryType, BoolType, CharType;
+ struct {
+ int64_t bits;
+ } IntType;
+ struct {
+ int64_t bits;
+ } NumType;
+ struct {
+ type_t *item_type;
+ } ArrayType;
+ struct {
+ type_t *key_type, *value_type;
+ } TableType;
+ struct {
+ arg_t *args;
+ type_t *ret;
+ } FunctionType;
+ struct {
+ type_t *pointed;
+ bool is_optional:1, is_stack:1, is_readonly:1;
+ } PointerType;
+ struct {
+ arg_t *fields;
+ } StructType;
+ struct {
+ tag_t *tags;
+ } EnumType;
+ struct {
+ const char *name, *filename;
+ type_t *variant_of;
+ type_t *namespace_type;
+ } VariantType;
+ struct {} TypeInfoType;
+ struct {
+ const char *filename, *name;
+ } PlaceholderType;
+ } __data;
+};
+
+#define Type(typetag, ...) new(type_t, .tag=typetag, .__data.typetag={__VA_ARGS__})
+#define INT_TYPE Type(IntType, .bits=64)
+#define NUM_TYPE Type(NumType, .bits=64)
+
+int printf_pointer_size(const struct printf_info *info, size_t n, int argtypes[n], int size[n]);
+int printf_type(FILE *stream, const struct printf_info *info, const void *const args[]);
+const char* type_to_string_concise(type_t *t);
+const char* type_to_typeof_string(type_t *t);
+const char* type_to_string(type_t *t);
+bool type_eq(type_t *a, type_t *b);
+bool type_is_a(type_t *t, type_t *req);
+type_t *type_or_type(type_t *a, type_t *b);
+type_t *value_type(type_t *a);
+bool is_integral(type_t *t);
+bool is_floating_point(type_t *t);
+bool is_numeric(type_t *t);
+typedef enum {NUM_PRECISION_EQUAL, NUM_PRECISION_LESS, NUM_PRECISION_MORE, NUM_PRECISION_INCOMPARABLE} precision_cmp_e;
+precision_cmp_e compare_precision(type_t *a, type_t *b);
+bool is_orderable(type_t *t);
+bool has_heap_memory(type_t *t);
+bool has_stack_memory(type_t *t);
+bool can_promote(type_t *actual, type_t *needed);
+bool can_leave_uninitialized(type_t *t);
+bool can_have_cycles(type_t *t);
+type_t *table_entry_type(type_t *table_t);
+type_t *base_variant(type_t *t);
+bool is_variant_of(type_t *t, type_t *base);
+type_t *base_value_type(type_t *t);
+type_t *replace_type(type_t *t, type_t *target, type_t *replacement);
+
+// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/util.h b/util.h
index 416c0b3e..90ac8b3d 100644
--- a/util.h
+++ b/util.h
@@ -26,4 +26,29 @@ char *heap_str(const char *str);
char *heap_strf(const char *fmt, ...);
CORD CORD_asprintf(const char *fmt, ...);
+#define REVERSE_LIST(list) do { \
+ __typeof(list) _prev = NULL; \
+ __typeof(list) _next = NULL; \
+ auto _current = list; \
+ while (_current != NULL) { \
+ _next = _current->next; \
+ _current->next = _prev; \
+ _prev = _current; \
+ _current = _next; \
+ } \
+ list = _prev; \
+} while(0)
+
+#define LIST_MAP(src, var, ...) ({\
+ __typeof(src) __mapped = NULL; \
+ __typeof(src) *__next = &__mapped; \
+ for (__typeof(src) var = src; var; var = var->next) { \
+ *__next = GC_MALLOC(sizeof(__typeof(*(src)))); \
+ **__next = *var; \
+ **__next = (__typeof(*(src))){__VA_ARGS__}; \
+ __next = &((*__next)->next); \
+ } \
+ __mapped; })
+
+
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0