diff --git a/Makefile b/Makefile index 80deb90..4b183a6 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 df7136f..bb151aa 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 dfa0659..d431a98 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,18 +69,12 @@ 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; @@ -108,7 +82,7 @@ struct type_ast_s { 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; - } TypeDef; + } StructDef; + struct { + const char *name; + tag_ast_t *tags; + ast_t *namespace; + } EnumDef; struct { ast_t *indexed, *index; bool unchecked; diff --git a/compile.c b/compile.c index f7bfbd1..7193b5a 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"); + 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); + } + 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); } - code = CORD_cat(code, "};\n"); - break; - } - default: errx(1, "Typedef not implemented"); + 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 51a8740..4036071 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 f3b8d88..7b56d0a 100644 --- a/nextlang.c +++ b/nextlang.c @@ -2,15 +2,23 @@ #include #include #include +#include #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 9a457d1..2e3fa51 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 0000000..aa97750 --- /dev/null +++ b/types.c @@ -0,0 +1,557 @@ +// Logic for handling type_t types +#include +#include +#include +#include + +#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 0000000..001167a --- /dev/null +++ b/types.h @@ -0,0 +1,123 @@ +#pragma once +#include +#include +#include +#include + +#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 416c0b3..90ac8b3 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