diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2025-08-23 19:28:08 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2025-08-23 19:28:08 -0400 |
| commit | fcda36561d668f43bac91ea31cd55cbbd605d330 (patch) | |
| tree | eb74c0b17df584af0fd8154422ad924e04c96cc2 /src/compile.c | |
| parent | 414b0c7472c87c5a013029aefef49e2dbc41e700 (diff) | |
Autoformat everything with clang-format
Diffstat (limited to 'src/compile.c')
| -rw-r--r-- | src/compile.c | 2824 |
1 files changed, 1419 insertions, 1405 deletions
diff --git a/src/compile.c b/src/compile.c index a747b870..438e6cce 100644 --- a/src/compile.c +++ b/src/compile.c @@ -1,14 +1,14 @@ // Compilation logic #include <ctype.h> -#include <glob.h> #include <gc.h> +#include <glob.h> #include <gmp.h> #include <stdio.h> #include <uninorm.h> #include "ast.h" -#include "config.h" #include "compile.h" +#include "config.h" #include "enums.h" #include "environment.h" #include "modules.h" @@ -22,7 +22,7 @@ #include "structs.h" #include "typecheck.h" -typedef ast_t* (*comprehension_body_t)(ast_t*, ast_t*); +typedef ast_t *(*comprehension_body_t)(ast_t *, ast_t *); static Text_t compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool needs_incref); static Text_t compile_text(env_t *env, ast_t *ast, Text_t color); @@ -47,16 +47,11 @@ static ast_t *add_to_table_comprehension(ast_t *entry, ast_t *subject); static ast_t *add_to_set_comprehension(ast_t *item, ast_t *subject); static Text_t compile_lvalue(env_t *env, ast_t *ast); -static Text_t quoted_str(const char *str) { - return Text$quoted(Text$from_str(str), false, Text("\"")); -} +static Text_t quoted_str(const char *str) { return Text$quoted(Text$from_str(str), false, Text("\"")); } -static inline Text_t quoted_text(Text_t text) { - return Text$quoted(text, false, Text("\"")); -} +static inline Text_t quoted_text(Text_t text) { return Text$quoted(text, false, Text("\"")); } -Text_t promote_to_optional(type_t *t, Text_t code) -{ +Text_t promote_to_optional(type_t *t, Text_t code) { if (t == PATH_TYPE || t == PATH_TYPE_TYPE) { return code; } else if (t->tag == IntType) { @@ -71,27 +66,23 @@ Text_t promote_to_optional(type_t *t, Text_t code) } else if (t->tag == ByteType) { return Texts("((OptionalByte_t){.value=", code, "})"); } else if (t->tag == StructType) { - return Texts("({ ", compile_type(Type(OptionalType, .type=t)), " nonnull = {.value=", code, "}; nonnull.is_none = false; nonnull; })"); + return Texts("({ ", compile_type(Type(OptionalType, .type = t)), " nonnull = {.value=", code, + "}; nonnull.is_none = false; nonnull; })"); } else { return code; } } -static Text_t with_source_info(env_t *env, ast_t *ast, Text_t code) -{ - if (code.length == 0 || !ast || !ast->file || !env->do_source_mapping) - return code; +static Text_t with_source_info(env_t *env, ast_t *ast, Text_t code) { + if (code.length == 0 || !ast || !ast->file || !env->do_source_mapping) return code; int64_t line = get_line_number(ast->file, ast->start); return Texts("\n#line ", String(line), "\n", code); } -static bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t *needed) -{ - if (type_eq(actual, needed)) - return true; +static bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t *needed) { + if (type_eq(actual, needed)) return true; - if (!can_promote(actual, needed)) - return false; + if (!can_promote(actual, needed)) return false; if (needed->tag == ClosureType && actual->tag == FunctionType) { *code = Texts("((Closure_t){", *code, ", NULL})"); @@ -100,8 +91,7 @@ static bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t // Empty promotion: type_t *more_complete = most_complete_type(actual, needed); - if (more_complete) - return true; + if (more_complete) return true; // Optional promotion: if (needed->tag == OptionalType && type_eq(actual, Match(needed, OptionalType)->type)) { @@ -116,28 +106,25 @@ static bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t } // Lang to Text_t: - if (actual->tag == TextType && needed->tag == TextType && streq(Match(needed, TextType)->lang, "Text")) - return true; + if (actual->tag == TextType && needed->tag == TextType && streq(Match(needed, TextType)->lang, "Text")) return true; // Automatic optional checking for nums: if (needed->tag == NumType && actual->tag == OptionalType && Match(actual, OptionalType)->type->tag == NumType) { int64_t line = get_line_number(ast->file, ast->start); - *code = Texts("({ ", compile_declaration(actual, Text("opt")), " = ", *code, "; ", - "if unlikely (", check_none(actual, Text("opt")), ")\n", - "#line ", String(line), "\n", - "fail_source(", quoted_str(ast->file->filename), ", ", - String((int64_t)(ast->start - ast->file->text)), ", ", - String((int64_t)(ast->end - ast->file->text)), ", ", - "\"This was expected to be a value, but it's none\");\n", - optional_into_nonnone(actual, Text("opt")), "; })"); + *code = Texts("({ ", compile_declaration(actual, Text("opt")), " = ", *code, "; ", "if unlikely (", + check_none(actual, Text("opt")), ")\n", "#line ", String(line), "\n", "fail_source(", + quoted_str(ast->file->filename), ", ", String((int64_t)(ast->start - ast->file->text)), ", ", + String((int64_t)(ast->end - ast->file->text)), ", ", + "\"This was expected to be a value, but it's none\");\n", + optional_into_nonnone(actual, Text("opt")), "; })"); return true; } // Numeric promotions/demotions if ((is_numeric_type(actual) || actual->tag == BoolType) && (is_numeric_type(needed) || needed->tag == BoolType)) { - arg_ast_t *args = new(arg_ast_t, .value=LiteralCode(*code, .type=actual)); - binding_t *constructor = get_constructor(env, needed, args, - env->current_type != NULL && type_eq(env->current_type, value_type(needed))); + arg_ast_t *args = new (arg_ast_t, .value = LiteralCode(*code, .type = actual)); + binding_t *constructor = get_constructor( + env, needed, args, env->current_type != NULL && type_eq(env->current_type, value_type(needed))); if (constructor) { DeclareMatch(fn, constructor->type, FunctionType); if (fn->args->next == NULL) { @@ -152,8 +139,7 @@ static bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t binding_t *b = get_binding(Match(needed, EnumType)->env, tag); assert(b && b->type->tag == FunctionType); // Single-value enum constructor: - if (!promote(env, ast, code, actual, Match(b->type, FunctionType)->args->type)) - return false; + if (!promote(env, ast, code, actual, Match(b->type, FunctionType)->args->type)) return false; *code = Texts(b->code, "(", *code, ")"); return true; } @@ -165,22 +151,18 @@ static bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t } // Automatic dereferencing: - if (actual->tag == PointerType - && can_promote(Match(actual, PointerType)->pointed, needed)) { + if (actual->tag == PointerType && can_promote(Match(actual, PointerType)->pointed, needed)) { *code = Texts("*(", *code, ")"); return promote(env, ast, code, Match(actual, PointerType)->pointed, needed); } // Stack ref promotion: - if (actual->tag == PointerType && needed->tag == PointerType) - return true; + if (actual->tag == PointerType && needed->tag == PointerType) return true; // Cross-promotion between tables with default values and without - if (needed->tag == TableType && actual->tag == TableType) - return true; + if (needed->tag == TableType && actual->tag == TableType) return true; - if (needed->tag == ClosureType && actual->tag == ClosureType) - return true; + if (needed->tag == ClosureType && actual->tag == ClosureType) return true; if (needed->tag == FunctionType && actual->tag == FunctionType) { *code = Texts("(", compile_type(needed), ")", *code); @@ -197,29 +179,24 @@ static bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t return false; } -Text_t compile_maybe_incref(env_t *env, ast_t *ast, type_t *t) -{ +Text_t compile_maybe_incref(env_t *env, ast_t *ast, type_t *t) { if (is_idempotent(ast) && can_be_mutated(env, ast)) { - if (t->tag == ListType) - return Texts("LIST_COPY(", compile_to_type(env, ast, t), ")"); + if (t->tag == ListType) return Texts("LIST_COPY(", compile_to_type(env, ast, t), ")"); else if (t->tag == TableType || t->tag == SetType) return Texts("TABLE_COPY(", compile_to_type(env, ast, t), ")"); } return compile_to_type(env, ast, t); } -static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t *env, ast_t *ast) -{ - if (ast == NULL) - return; +static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t *env, ast_t *ast) { + if (ast == NULL) return; switch (ast->tag) { case Var: { binding_t *b = get_binding(enclosing_scope, Match(ast, Var)->name); if (b) { binding_t *shadow = get_binding(env, Match(ast, Var)->name); - if (!shadow || shadow == b) - Table$str_set(closed_vars, Match(ast, Var)->name, b); + if (!shadow || shadow == b) Table$str_set(closed_vars, Match(ast, Var)->name, b); } break; } @@ -247,7 +224,10 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t add_closed_vars(closed_vars, enclosing_scope, env, binop.rhs); break; } - case Not: case Negative: case HeapAllocate: case StackReference: { + case Not: + case Negative: + case HeapAllocate: + case StackReference: { // UNSAFE: ast_t *value = ast->__data.Not.value; // END UNSAFE @@ -291,16 +271,15 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t case Comprehension: { DeclareMatch(comp, ast, Comprehension); if (comp->expr->tag == Comprehension) { // Nested comprehension - ast_t *body = comp->filter ? WrapAST(ast, If, .condition=comp->filter, .body=comp->expr) : comp->expr; - ast_t *loop = WrapAST(ast, For, .vars=comp->vars, .iter=comp->iter, .body=body); + ast_t *body = comp->filter ? WrapAST(ast, If, .condition = comp->filter, .body = comp->expr) : comp->expr; + ast_t *loop = WrapAST(ast, For, .vars = comp->vars, .iter = comp->iter, .body = body); return add_closed_vars(closed_vars, enclosing_scope, env, loop); } // List/Set/Table comprehension: ast_t *body = comp->expr; - if (comp->filter) - body = WrapAST(comp->expr, If, .condition=comp->filter, .body=body); - ast_t *loop = WrapAST(ast, For, .vars=comp->vars, .iter=comp->iter, .body=body); + if (comp->filter) body = WrapAST(comp->expr, If, .condition = comp->filter, .body = body); + ast_t *loop = WrapAST(ast, For, .vars = comp->vars, .iter = comp->iter, .body = body); add_closed_vars(closed_vars, enclosing_scope, env, loop); break; } @@ -356,8 +335,7 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t ast_t *var = Match(condition, Declare)->var; type_t *cond_t = get_type(truthy_scope, var); if (cond_t->tag == OptionalType) { - set_binding(truthy_scope, Match(var, Var)->name, - Match(cond_t, OptionalType)->type, EMPTY_TEXT); + set_binding(truthy_scope, Match(var, Var)->name, Match(cond_t, OptionalType)->type, EMPTY_TEXT); } add_closed_vars(closed_vars, enclosing_scope, truthy_scope, if_->body); add_closed_vars(closed_vars, enclosing_scope, env, if_->else_body); @@ -367,8 +345,7 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t type_t *cond_t = get_type(env, condition); if (condition->tag == Var && cond_t->tag == OptionalType) { truthy_scope = fresh_scope(env); - set_binding(truthy_scope, Match(condition, Var)->name, - Match(cond_t, OptionalType)->type, EMPTY_TEXT); + set_binding(truthy_scope, Match(condition, Var)->name, Match(cond_t, OptionalType)->type, EMPTY_TEXT); } add_closed_vars(closed_vars, enclosing_scope, truthy_scope, if_->body); add_closed_vars(closed_vars, enclosing_scope, env, if_->else_body); @@ -386,20 +363,17 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t add_closed_vars(closed_vars, enclosing_scope, env, clause->body); } - if (when->else_body) - add_closed_vars(closed_vars, enclosing_scope, env, when->else_body); + if (when->else_body) add_closed_vars(closed_vars, enclosing_scope, env, when->else_body); return; } DeclareMatch(enum_t, subject_t, EnumType); for (when_clause_t *clause = when->clauses; clause; clause = clause->next) { const char *clause_tag_name; - if (clause->pattern->tag == Var) - clause_tag_name = Match(clause->pattern, Var)->name; + if (clause->pattern->tag == Var) clause_tag_name = Match(clause->pattern, Var)->name; else if (clause->pattern->tag == FunctionCall && Match(clause->pattern, FunctionCall)->fn->tag == Var) clause_tag_name = Match(Match(clause->pattern, FunctionCall)->fn, Var)->name; - else - code_err(clause->pattern, "This is not a valid pattern for a ", type_to_str(subject_t), " enum"); + else code_err(clause->pattern, "This is not a valid pattern for a ", type_to_str(subject_t), " enum"); type_t *tag_type = NULL; for (tag_t *tag = enum_t->tags; tag; tag = tag->next) { @@ -412,8 +386,7 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t env_t *scope = when_clause_scope(env, subject_t, clause); add_closed_vars(closed_vars, enclosing_scope, scope, clause->body); } - if (when->else_body) - add_closed_vars(closed_vars, enclosing_scope, env, when->else_body); + if (when->else_body) add_closed_vars(closed_vars, enclosing_scope, env, when->else_body); break; } case Repeat: { @@ -424,7 +397,8 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t DeclareMatch(reduction, ast, Reduction); static int64_t next_id = 1; ast_t *item = FakeAST(Var, String("$it", next_id++)); - ast_t *loop = FakeAST(For, .vars=new(ast_list_t, .ast=item), .iter=reduction->iter, .body=FakeAST(Pass)); + ast_t *loop = + FakeAST(For, .vars = new (ast_list_t, .ast = item), .iter = reduction->iter, .body = FakeAST(Pass)); env_t *scope = for_scope(env, loop); add_closed_vars(closed_vars, enclosing_scope, scope, reduction->key ? reduction->key : item); break; @@ -472,16 +446,20 @@ static void add_closed_vars(Table_t *closed_vars, env_t *enclosing_scope, env_t add_closed_vars(closed_vars, enclosing_scope, env, Match(ast, ExplicitlyTyped)->ast); break; } - case Use: case FunctionDef: case ConvertDef: case StructDef: case EnumDef: case LangDef: case Extend: { + case Use: + case FunctionDef: + case ConvertDef: + case StructDef: + case EnumDef: + case LangDef: + case Extend: { errx(1, "Definitions should not be reachable in a closure."); } - default: - break; + default: break; } } -static Table_t get_closed_vars(env_t *env, arg_ast_t *args, ast_t *block) -{ +static Table_t get_closed_vars(env_t *env, arg_ast_t *args, ast_t *block) { env_t *body_scope = fresh_scope(env); for (arg_ast_t *arg = args; arg; arg = arg->next) { type_t *arg_type = get_arg_ast_type(env, arg); @@ -493,8 +471,7 @@ static Table_t get_closed_vars(env_t *env, arg_ast_t *args, ast_t *block) return closed_vars; } -Text_t compile_declaration(type_t *t, Text_t name) -{ +Text_t compile_declaration(type_t *t, Text_t name) { if (t->tag == FunctionType) { DeclareMatch(fn, t, FunctionType); Text_t code = Texts(compile_type(fn->ret), " (*", name, ")("); @@ -511,10 +488,8 @@ Text_t compile_declaration(type_t *t, Text_t name) } } -static Text_t compile_update_assignment(env_t *env, ast_t *ast) -{ - if (!is_update_assignment(ast)) - code_err(ast, "This is not an update assignment"); +static Text_t compile_update_assignment(env_t *env, ast_t *ast) { + if (!is_update_assignment(ast)) code_err(ast, "This is not an update assignment"); binary_operands_t update = UPDATE_OPERANDS(ast); @@ -557,35 +532,34 @@ static Text_t compile_update_assignment(env_t *env, ast_t *ast) } case AndUpdate: { if (lhs_t->tag == BoolType) - update_assignment = Texts("if (", lhs, ") ", lhs, " = ", compile_to_type(env, update.rhs, Type(BoolType)), ";"); + update_assignment = + Texts("if (", lhs, ") ", lhs, " = ", compile_to_type(env, update.rhs, Type(BoolType)), ";"); break; } case OrUpdate: { if (lhs_t->tag == BoolType) - update_assignment = Texts("if (!", lhs, ") ", lhs, " = ", compile_to_type(env, update.rhs, Type(BoolType)), ";"); + update_assignment = + Texts("if (!", lhs, ") ", lhs, " = ", compile_to_type(env, update.rhs, Type(BoolType)), ";"); break; } default: break; } if (update_assignment.length == 0) { - ast_t *binop = new(ast_t); + ast_t *binop = new (ast_t); *binop = *ast; binop->tag = binop_tag(binop->tag); - if (needs_idemotency_fix) - binop->__data.Plus.lhs = LiteralCode(Text("*lhs"), .type=lhs_t); + if (needs_idemotency_fix) binop->__data.Plus.lhs = LiteralCode(Text("*lhs"), .type = lhs_t); update_assignment = Texts(lhs, " = ", compile_to_type(env, binop, lhs_t), ";"); } - + if (needs_idemotency_fix) - return Texts("{ ", compile_declaration(Type(PointerType, .pointed=lhs_t), Text("lhs")), " = &", compile_lvalue(env, update.lhs), "; ", - update_assignment, "; }"); - else - return update_assignment; + return Texts("{ ", compile_declaration(Type(PointerType, .pointed = lhs_t), Text("lhs")), " = &", + compile_lvalue(env, update.lhs), "; ", update_assignment, "; }"); + else return update_assignment; } -static Text_t compile_binary_op(env_t *env, ast_t *ast) -{ +static Text_t compile_binary_op(env_t *env, ast_t *ast) { binary_operands_t binop = BINARY_OPERANDS(ast); type_t *lhs_t = get_type(env, binop.lhs); type_t *rhs_t = get_type(env, binop.rhs); @@ -594,7 +568,7 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) binding_t *b = get_metamethod_binding(env, ast->tag, binop.lhs, binop.rhs, overall_t); if (!b) b = get_metamethod_binding(env, ast->tag, binop.rhs, binop.lhs, overall_t); if (b) { - arg_ast_t *args = new(arg_ast_t, .value=binop.lhs, .next=new(arg_ast_t, .value=binop.rhs)); + arg_ast_t *args = new (arg_ast_t, .value = binop.lhs, .next = new (arg_ast_t, .value = binop.rhs)); DeclareMatch(fn, b->type, FunctionType); return Texts(b->code, "(", compile_arguments(env, ast, fn->args, args), ")"); } @@ -604,7 +578,7 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); if (type_eq(fn->ret, rhs_t)) { - arg_ast_t *args = new(arg_ast_t, .value=binop.rhs, .next=new(arg_ast_t, .value=binop.lhs)); + arg_ast_t *args = new (arg_ast_t, .value = binop.rhs, .next = new (arg_ast_t, .value = binop.lhs)); if (is_valid_call(env, fn->args, args, true)) return Texts(b->code, "(", compile_arguments(env, ast, fn->args, args), ")"); } @@ -614,7 +588,7 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); if (type_eq(fn->ret, lhs_t)) { - arg_ast_t *args = new(arg_ast_t, .value=binop.lhs, .next=new(arg_ast_t, .value=binop.rhs)); + arg_ast_t *args = new (arg_ast_t, .value = binop.lhs, .next = new (arg_ast_t, .value = binop.rhs)); if (is_valid_call(env, fn->args, args, true)) return Texts(b->code, "(", compile_arguments(env, ast, fn->args, args), ")"); } @@ -624,7 +598,7 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); if (type_eq(fn->ret, lhs_t)) { - arg_ast_t *args = new(arg_ast_t, .value=binop.lhs, .next=new(arg_ast_t, .value=binop.rhs)); + arg_ast_t *args = new (arg_ast_t, .value = binop.lhs, .next = new (arg_ast_t, .value = binop.rhs)); if (is_valid_call(env, fn->args, args, true)) return Texts(b->code, "(", compile_arguments(env, ast, fn->args, args), ")"); } @@ -634,7 +608,7 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); if (type_eq(fn->ret, lhs_t)) { - arg_ast_t *args = new(arg_ast_t, .value=binop.lhs, .next=new(arg_ast_t, .value=binop.rhs)); + arg_ast_t *args = new (arg_ast_t, .value = binop.lhs, .next = new (arg_ast_t, .value = binop.rhs)); if (is_valid_call(env, fn->args, args, true)) return Texts(b->code, "(", compile_arguments(env, ast, fn->args, args), ")"); } @@ -643,29 +617,31 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) if (ast->tag == Or && lhs_t->tag == OptionalType) { if (rhs_t->tag == AbortType || rhs_t->tag == ReturnType) { - return Texts("({ ", compile_declaration(lhs_t, Text("lhs")), " = ", compile(env, binop.lhs), "; ", - "if (", check_none(lhs_t, Text("lhs")), ") ", compile_statement(env, binop.rhs), " ", - optional_into_nonnone(lhs_t, Text("lhs")), "; })"); + return Texts("({ ", compile_declaration(lhs_t, Text("lhs")), " = ", compile(env, binop.lhs), "; ", "if (", + check_none(lhs_t, Text("lhs")), ") ", compile_statement(env, binop.rhs), " ", + optional_into_nonnone(lhs_t, Text("lhs")), "; })"); } if (is_incomplete_type(rhs_t)) { type_t *complete = most_complete_type(rhs_t, Match(lhs_t, OptionalType)->type); if (complete == NULL) - code_err(binop.rhs, "I don't know how to convert a ", type_to_str(rhs_t), " to a ", type_to_str(Match(lhs_t, OptionalType)->type)); + code_err(binop.rhs, "I don't know how to convert a ", type_to_str(rhs_t), " to a ", + type_to_str(Match(lhs_t, OptionalType)->type)); rhs_t = complete; } if (rhs_t->tag == OptionalType && type_eq(lhs_t, rhs_t)) { return Texts("({ ", compile_declaration(lhs_t, Text("lhs")), " = ", compile(env, binop.lhs), "; ", - check_none(lhs_t, Text("lhs")), " ? ", compile(env, binop.rhs), " : lhs; })"); + check_none(lhs_t, Text("lhs")), " ? ", compile(env, binop.rhs), " : lhs; })"); } else if (rhs_t->tag != OptionalType && type_eq(Match(lhs_t, OptionalType)->type, rhs_t)) { return Texts("({ ", compile_declaration(lhs_t, Text("lhs")), " = ", compile(env, binop.lhs), "; ", - check_none(lhs_t, Text("lhs")), " ? ", compile(env, binop.rhs), " : ", - optional_into_nonnone(lhs_t, Text("lhs")), "; })"); + check_none(lhs_t, Text("lhs")), " ? ", compile(env, binop.rhs), " : ", + optional_into_nonnone(lhs_t, Text("lhs")), "; })"); } else if (rhs_t->tag == BoolType) { return Texts("((!", check_none(lhs_t, compile(env, binop.lhs)), ") || ", compile(env, binop.rhs), ")"); } else { - code_err(ast, "I don't know how to do an 'or' operation between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, "I don't know how to do an 'or' operation between ", type_to_str(lhs_t), " and ", + type_to_str(rhs_t)); } } @@ -678,70 +654,98 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) code_err(ast, "Exponentiation is only supported for Num types, not ", type_to_str(overall_t)); if (overall_t->tag == NumType && Match(overall_t, NumType)->bits == TYPE_NBITS32) return Texts("powf(", lhs, ", ", rhs, ")"); - else - return Texts("pow(", lhs, ", ", rhs, ")"); + else return Texts("pow(", lhs, ", ", rhs, ")"); } case Multiply: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", lhs, " * ", rhs, ")"); } case Divide: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", lhs, " / ", rhs, ")"); } case Mod: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", lhs, " % ", rhs, ")"); } case Mod1: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("((((", lhs, ")-1) % (", rhs, ")) + 1)"); } case Plus: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", lhs, " + ", rhs, ")"); } case Minus: { if (overall_t->tag == SetType) return Texts("Table$without(", lhs, ", ", rhs, ", ", compile_type_info(overall_t), ")"); if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", lhs, " - ", rhs, ")"); } case LeftShift: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", lhs, " << ", rhs, ")"); } case RightShift: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", lhs, " >> ", rhs, ")"); } case UnsignedLeftShift: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", compile_type(overall_t), ")((", compile_unsigned_type(lhs_t), ")", lhs, " << ", rhs, ")"); } case UnsignedRightShift: { if (overall_t->tag != IntType && overall_t->tag != NumType && overall_t->tag != ByteType) - code_err(ast, "Math operations are only supported for values of the same numeric type, not ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + code_err(ast, + "Math operations are only supported for values of the same " + "numeric type, not ", + type_to_str(lhs_t), " and ", type_to_str(rhs_t)); return Texts("(", compile_type(overall_t), ")((", compile_unsigned_type(lhs_t), ")", lhs, " >> ", rhs, ")"); } case And: { - if (overall_t->tag == BoolType) - return Texts("(", lhs, " && ", rhs, ")"); - else if (overall_t->tag == IntType || overall_t->tag == ByteType) - return Texts("(", lhs, " & ", rhs, ")"); + if (overall_t->tag == BoolType) return Texts("(", lhs, " && ", rhs, ")"); + else if (overall_t->tag == IntType || overall_t->tag == ByteType) return Texts("(", lhs, " & ", rhs, ")"); else if (overall_t->tag == SetType) return Texts("Table$overlap(", lhs, ", ", rhs, ", ", compile_type_info(overall_t), ")"); else - code_err(ast, "The 'and' operator isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), " values"); + code_err(ast, "The 'and' operator isn't supported between ", type_to_str(lhs_t), " and ", + type_to_str(rhs_t), " values"); } case Compare: { return Texts("generic_compare(stack(", lhs, "), stack(", rhs, "), ", compile_type_info(overall_t), ")"); @@ -754,7 +758,8 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) } else if (overall_t->tag == SetType) { return Texts("Table$with(", lhs, ", ", rhs, ", ", compile_type_info(overall_t), ")"); } else { - code_err(ast, "The 'or' operator isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), " values"); + code_err(ast, "The 'or' operator isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), + " values"); } } case Xor: { @@ -764,20 +769,22 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) else if (overall_t->tag == SetType) return Texts("Table$xor(", lhs, ", ", rhs, ", ", compile_type_info(overall_t), ")"); else - code_err(ast, "The 'xor' operator isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), " values"); + code_err(ast, "The 'xor' operator isn't supported between ", type_to_str(lhs_t), " and ", + type_to_str(rhs_t), " values"); } case Concat: { - if (overall_t == PATH_TYPE) - return Texts("Path$concat(", lhs, ", ", rhs, ")"); + if (overall_t == PATH_TYPE) return Texts("Path$concat(", lhs, ", ", rhs, ")"); switch (overall_t->tag) { case TextType: { return Texts("Text$concat(", lhs, ", ", rhs, ")"); } case ListType: { - return Texts("List$concat(", lhs, ", ", rhs, ", sizeof(", compile_type(Match(overall_t, ListType)->item_type), "))"); + return Texts("List$concat(", lhs, ", ", rhs, ", sizeof(", + compile_type(Match(overall_t, ListType)->item_type), "))"); } default: - code_err(ast, "Concatenation isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), " values"); + code_err(ast, "Concatenation isn't supported between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t), + " values"); } } default: errx(1, "Not a valid binary operation: %s", ast_to_sexp_str(ast)); @@ -785,10 +792,8 @@ static Text_t compile_binary_op(env_t *env, ast_t *ast) return EMPTY_TEXT; } -PUREFUNC Text_t compile_unsigned_type(type_t *t) -{ - if (t->tag != IntType) - errx(1, "Not an int type, so unsigned doesn't make sense!"); +PUREFUNC Text_t compile_unsigned_type(type_t *t) { + if (t->tag != IntType) errx(1, "Not an int type, so unsigned doesn't make sense!"); switch (Match(t, IntType)->bits) { case TYPE_IBITS8: return Text("uint8_t"); case TYPE_IBITS16: return Text("uint16_t"); @@ -799,8 +804,7 @@ PUREFUNC Text_t compile_unsigned_type(type_t *t) return EMPTY_TEXT; } -Text_t compile_type(type_t *t) -{ +Text_t compile_type(type_t *t) { if (t == PATH_TYPE) return Text("Path_t"); else if (t == PATH_TYPE_TYPE) return Text("PathType_t"); @@ -814,13 +818,13 @@ Text_t compile_type(type_t *t) case CStringType: return Text("const char*"); case BigIntType: return Text("Int_t"); case IntType: return Texts("Int", String(Match(t, IntType)->bits), "_t"); - case NumType: return Match(t, NumType)->bits == TYPE_NBITS64 ? Text("Num_t") : Texts("Num", String(Match(t, NumType)->bits), "_t"); + case NumType: + return Match(t, NumType)->bits == TYPE_NBITS64 ? Text("Num_t") + : Texts("Num", String(Match(t, NumType)->bits), "_t"); case TextType: { DeclareMatch(text, t, TextType); - if (!text->lang || streq(text->lang, "Text")) - return Text("Text_t"); - else - return namespace_name(text->env, text->env->namespace, Text("$type")); + if (!text->lang || streq(text->lang, "Text")) return Text("Text_t"); + else return namespace_name(text->env, text->env->namespace, Text("$type")); } case ListType: return Text("List_t"); case SetType: return Text("Table_t"); @@ -832,8 +836,7 @@ Text_t compile_type(type_t *t) code = Texts(code, compile_type(arg->type)); if (arg->next) code = Texts(code, ", "); } - if (!fn->args) - code = Texts(code, "void"); + if (!fn->args) code = Texts(code, "void"); return Texts(code, ")"); } case ClosureType: return Text("Closure_t"); @@ -850,24 +853,27 @@ Text_t compile_type(type_t *t) case OptionalType: { type_t *nonnull = Match(t, OptionalType)->type; switch (nonnull->tag) { - case CStringType: case FunctionType: case ClosureType: - case PointerType: case EnumType: - return compile_type(nonnull); - case TextType: - return Match(nonnull, TextType)->lang ? compile_type(nonnull) : Text("OptionalText_t"); - case IntType: case BigIntType: case NumType: case BoolType: case ByteType: - case ListType: case TableType: case SetType: - return Texts("Optional", compile_type(nonnull)); + case CStringType: + case FunctionType: + case ClosureType: + case PointerType: + case EnumType: return compile_type(nonnull); + case TextType: return Match(nonnull, TextType)->lang ? compile_type(nonnull) : Text("OptionalText_t"); + case IntType: + case BigIntType: + case NumType: + case BoolType: + case ByteType: + case ListType: + case TableType: + case SetType: return Texts("Optional", compile_type(nonnull)); case StructType: { - if (nonnull == PATH_TYPE) - return Text("OptionalPath_t"); - if (nonnull == PATH_TYPE_TYPE) - return Text("OptionalPathType_t"); + if (nonnull == PATH_TYPE) return Text("OptionalPath_t"); + if (nonnull == PATH_TYPE_TYPE) return Text("OptionalPathType_t"); DeclareMatch(s, nonnull, StructType); return namespace_name(s->env, s->env->namespace->parent, Texts("$Optional", s->name, "$$type")); } - default: - compiler_err(NULL, NULL, NULL, "Optional types are not supported for: ", type_to_str(t)); + default: compiler_err(NULL, NULL, NULL, "Optional types are not supported for: ", type_to_str(t)); } } case TypeInfoType: return Text("TypeInfo_t"); @@ -876,18 +882,19 @@ Text_t compile_type(type_t *t) return EMPTY_TEXT; } -Text_t compile_lvalue(env_t *env, ast_t *ast) -{ +Text_t compile_lvalue(env_t *env, ast_t *ast) { if (!can_be_mutated(env, ast)) { if (ast->tag == Index) { ast_t *subject = Match(ast, Index)->indexed; - code_err(subject, "This is an immutable value, you can't mutate its contents"); + code_err(subject, "This is an immutable value, you can't mutate " + "its contents"); } else if (ast->tag == FieldAccess) { ast_t *subject = Match(ast, FieldAccess)->fielded; type_t *t = get_type(env, subject); code_err(subject, "This is an immutable ", type_to_str(t), " value, you can't assign to its fields"); } else { - code_err(ast, "This is a value of type ", type_to_str(get_type(env, ast)), " and can't be used as an assignment target"); + code_err(ast, "This is a value of type ", type_to_str(get_type(env, ast)), + " and can't be used as an assignment target"); } } @@ -895,47 +902,44 @@ Text_t compile_lvalue(env_t *env, ast_t *ast) DeclareMatch(index, ast, Index); type_t *container_t = get_type(env, index->indexed); if (container_t->tag == OptionalType) - code_err(index->indexed, "This value might be none, so it can't be safely used as an assignment target"); + code_err(index->indexed, "This value might be none, so it can't be " + "safely used as an assignment target"); - if (!index->index && container_t->tag == PointerType) - return compile(env, ast); + if (!index->index && container_t->tag == PointerType) return compile(env, ast); container_t = value_type(container_t); type_t *index_t = get_type(env, index->index); if (container_t->tag == ListType) { Text_t target_code = compile_to_pointer_depth(env, index->indexed, 1, false); type_t *item_type = Match(container_t, ListType)->item_type; - Text_t index_code = index->index->tag == Int - ? compile_int_to_type(env, index->index, Type(IntType, .bits=TYPE_IBITS64)) - : (index_t->tag == BigIntType ? Texts("Int64$from_int(", compile(env, index->index), ", no)") - : Texts("(Int64_t)(", compile(env, index->index), ")")); + Text_t index_code = + index->index->tag == Int + ? compile_int_to_type(env, index->index, Type(IntType, .bits = TYPE_IBITS64)) + : (index_t->tag == BigIntType ? Texts("Int64$from_int(", compile(env, index->index), ", no)") + : Texts("(Int64_t)(", compile(env, index->index), ")")); if (index->unchecked) { - return Texts("List_lvalue_unchecked(", compile_type(item_type), ", ", target_code, ", ", - index_code, ")"); + return Texts("List_lvalue_unchecked(", compile_type(item_type), ", ", target_code, ", ", index_code, + ")"); } else { - return Texts("List_lvalue(", compile_type(item_type), ", ", target_code, ", ", - index_code, - ", ", String((int)(ast->start - ast->file->text)), - ", ", String((int)(ast->end - ast->file->text)), ")"); + return Texts("List_lvalue(", compile_type(item_type), ", ", target_code, ", ", index_code, ", ", + String((int)(ast->start - ast->file->text)), ", ", + String((int)(ast->end - ast->file->text)), ")"); } } else if (container_t->tag == TableType) { DeclareMatch(table_type, container_t, TableType); if (table_type->default_value) { type_t *value_type = get_type(env, table_type->default_value); - return Texts("*Table$get_or_setdefault(", - compile_to_pointer_depth(env, index->indexed, 1, false), ", ", - compile_type(table_type->key_type), ", ", - compile_type(value_type), ", ", - compile_to_type(env, index->index, table_type->key_type), ", ", - compile_to_type(env, table_type->default_value, table_type->value_type), ", ", - compile_type_info(container_t), ")"); + return Texts("*Table$get_or_setdefault(", compile_to_pointer_depth(env, index->indexed, 1, false), ", ", + compile_type(table_type->key_type), ", ", compile_type(value_type), ", ", + compile_to_type(env, index->index, table_type->key_type), ", ", + compile_to_type(env, table_type->default_value, table_type->value_type), ", ", + compile_type_info(container_t), ")"); } - if (index->unchecked) - code_err(ast, "Table indexes cannot be unchecked"); + if (index->unchecked) code_err(ast, "Table indexes cannot be unchecked"); return Texts("*(", compile_type(Type(PointerType, table_type->value_type)), ")Table$reserve(", - compile_to_pointer_depth(env, index->indexed, 1, false), ", ", - compile_to_type(env, index->index, Type(PointerType, table_type->key_type, .is_stack=true)), ", NULL,", - compile_type_info(container_t), ")"); + compile_to_pointer_depth(env, index->indexed, 1, false), ", ", + compile_to_type(env, index->index, Type(PointerType, table_type->key_type, .is_stack = true)), + ", NULL,", compile_type_info(container_t), ")"); } else { code_err(ast, "I don't know how to assign to this target"); } @@ -947,15 +951,12 @@ Text_t compile_lvalue(env_t *env, ast_t *ast) return EMPTY_TEXT; } -static Text_t compile_assignment(env_t *env, ast_t *target, Text_t value) -{ +static Text_t compile_assignment(env_t *env, ast_t *target, Text_t value) { return Texts(compile_lvalue(env, target), " = ", value); } -static Text_t compile_inline_block(env_t *env, ast_t *ast) -{ - if (ast->tag != Block) - return compile_statement(env, ast); +static Text_t compile_inline_block(env_t *env, ast_t *ast) { + if (ast->tag != Block) return compile_statement(env, ast); Text_t code = EMPTY_TEXT; ast_list_t *stmts = Match(ast, Block)->statements; @@ -973,60 +974,44 @@ static Text_t compile_inline_block(env_t *env, ast_t *ast) return code; } -Text_t optional_into_nonnone(type_t *t, Text_t value) -{ +Text_t optional_into_nonnone(type_t *t, Text_t value) { if (t->tag == OptionalType) t = Match(t, OptionalType)->type; switch (t->tag) { - case IntType: case ByteType: - return Texts(value, ".value"); + case IntType: + case ByteType: return Texts(value, ".value"); case StructType: - if (t == PATH_TYPE || t == PATH_TYPE_TYPE) - return value; + if (t == PATH_TYPE || t == PATH_TYPE_TYPE) return value; return Texts(value, ".value"); - default: - return value; + default: return value; } } -Text_t check_none(type_t *t, Text_t value) -{ +Text_t check_none(type_t *t, Text_t value) { t = Match(t, OptionalType)->type; // NOTE: these use statement expressions ({...;}) because some compilers // complain about excessive parens around equality comparisons if (t->tag == PointerType || t->tag == FunctionType || t->tag == CStringType) return Texts("({", value, " == NULL;})"); - else if (t == PATH_TYPE) - return Texts("({(", value, ").type.$tag == PATH_NONE;})"); - else if (t == PATH_TYPE_TYPE) - return Texts("({(", value, ").$tag == PATH_NONE;})"); - else if (t->tag == BigIntType) - return Texts("({(", value, ").small == 0;})"); - else if (t->tag == ClosureType) - return Texts("({(", value, ").fn == NULL;})"); + else if (t == PATH_TYPE) return Texts("({(", value, ").type.$tag == PATH_NONE;})"); + else if (t == PATH_TYPE_TYPE) return Texts("({(", value, ").$tag == PATH_NONE;})"); + else if (t->tag == BigIntType) return Texts("({(", value, ").small == 0;})"); + else if (t->tag == ClosureType) return Texts("({(", value, ").fn == NULL;})"); else if (t->tag == NumType) return Texts(Match(t, NumType)->bits == TYPE_NBITS64 ? "Num$isnan(" : "Num32$isnan(", value, ")"); - else if (t->tag == ListType) - return Texts("({(", value, ").length < 0;})"); - else if (t->tag == TableType || t->tag == SetType) - return Texts("({(", value, ").entries.length < 0;})"); - else if (t->tag == BoolType) - return Texts("({(", value, ") == NONE_BOOL;})"); - else if (t->tag == TextType) - return Texts("({(", value, ").length < 0;})"); - else if (t->tag == IntType || t->tag == ByteType || t->tag == StructType) - return Texts("(", value, ").is_none"); + else if (t->tag == ListType) return Texts("({(", value, ").length < 0;})"); + else if (t->tag == TableType || t->tag == SetType) return Texts("({(", value, ").entries.length < 0;})"); + else if (t->tag == BoolType) return Texts("({(", value, ") == NONE_BOOL;})"); + else if (t->tag == TextType) return Texts("({(", value, ").length < 0;})"); + else if (t->tag == IntType || t->tag == ByteType || t->tag == StructType) return Texts("(", value, ").is_none"); else if (t->tag == EnumType) { - if (enum_has_fields(t)) - return Texts("({(", value, ").$tag == 0;})"); - else - return Texts("((", value, ") == 0)"); + if (enum_has_fields(t)) return Texts("({(", value, ").$tag == 0;})"); + else return Texts("((", value, ") == 0)"); } print_err("Optional check not implemented for: ", type_to_str(t)); return EMPTY_TEXT; } -static Text_t compile_condition(env_t *env, ast_t *ast) -{ +static Text_t compile_condition(env_t *env, ast_t *ast) { type_t *t = get_type(env, ast); if (t->tag == BoolType) { return compile(env, ast); @@ -1039,15 +1024,15 @@ static Text_t compile_condition(env_t *env, ast_t *ast) } else if (t->tag == OptionalType) { return Texts("!", check_none(t, compile(env, ast))); } else if (t->tag == PointerType) { - code_err(ast, "This pointer will always be non-none, so it should not be used in a conditional."); + code_err(ast, "This pointer will always be non-none, so it should not be " + "used in a conditional."); } else { code_err(ast, type_to_str(t), " values cannot be used for conditionals"); } return EMPTY_TEXT; } -static Text_t _compile_statement(env_t *env, ast_t *ast) -{ +static Text_t _compile_statement(env_t *env, ast_t *ast) { switch (ast->tag) { case When: { // Typecheck to verify exhaustiveness: @@ -1061,21 +1046,20 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) Text_t prefix = EMPTY_TEXT, suffix = EMPTY_TEXT; ast_t *subject = when->subject; if (!is_idempotent(when->subject)) { - prefix = Texts("{\n", compile_declaration(subject_t, Text("_when_subject")), " = ", compile(env, subject), ";\n"); + prefix = Texts("{\n", compile_declaration(subject_t, Text("_when_subject")), " = ", + compile(env, subject), ";\n"); suffix = Text("}\n"); - subject = LiteralCode(Text("_when_subject"), .type=subject_t); + subject = LiteralCode(Text("_when_subject"), .type = subject_t); } Text_t code = EMPTY_TEXT; for (when_clause_t *clause = when->clauses; clause; clause = clause->next) { - ast_t *comparison = WrapAST(clause->pattern, Equals, .lhs=subject, .rhs=clause->pattern); + ast_t *comparison = WrapAST(clause->pattern, Equals, .lhs = subject, .rhs = clause->pattern); (void)get_type(env, comparison); - if (code.length > 0) - code = Texts(code, "else "); + if (code.length > 0) code = Texts(code, "else "); code = Texts(code, "if (", compile(env, comparison), ")", compile_statement(env, clause->body)); } - if (when->else_body) - code = Texts(code, "else ", compile_statement(env, when->else_body)); + if (when->else_body) code = Texts(code, "else ", compile_statement(env, when->else_body)); code = Texts(prefix, code, suffix); return code; } @@ -1085,17 +1069,17 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) Text_t code; if (enum_has_fields(subject_t)) code = Texts("WHEN(", compile_type(subject_t), ", ", compile(env, when->subject), ", _when_subject, {\n"); - else - code = Texts("switch(", compile(env, when->subject), ") {\n"); + else code = Texts("switch(", compile(env, when->subject), ") {\n"); for (when_clause_t *clause = when->clauses; clause; clause = clause->next) { if (clause->pattern->tag == Var) { const char *clause_tag_name = Match(clause->pattern, Var)->name; type_t *clause_type = clause->body ? get_type(env, clause->body) : Type(VoidType); - code = Texts(code, "case ", namespace_name(enum_t->env, enum_t->env->namespace, Texts("tag$", clause_tag_name)), ": {\n", - compile_inline_block(env, clause->body), - (clause_type->tag == ReturnType || clause_type->tag == AbortType) ? EMPTY_TEXT : Text("break;\n"), - "}\n"); + code = Texts( + code, "case ", namespace_name(enum_t->env, enum_t->env->namespace, Texts("tag$", clause_tag_name)), + ": {\n", compile_inline_block(env, clause->body), + (clause_type->tag == ReturnType || clause_type->tag == AbortType) ? EMPTY_TEXT : Text("break;\n"), + "}\n"); continue; } @@ -1103,7 +1087,8 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) code_err(clause->pattern, "This is not a valid pattern for a ", type_to_str(subject_t), " enum type"); const char *clause_tag_name = Match(Match(clause->pattern, FunctionCall)->fn, Var)->name; - code = Texts(code, "case ", namespace_name(enum_t->env, enum_t->env->namespace, Texts("tag$", clause_tag_name)), ": {\n"); + code = Texts(code, "case ", + namespace_name(enum_t->env, enum_t->env->namespace, Texts("tag$", clause_tag_name)), ": {\n"); type_t *tag_type = NULL; for (tag_t *tag = enum_t->tags; tag; tag = tag->next) { if (streq(tag->name, clause_tag_name)) { @@ -1117,12 +1102,12 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) DeclareMatch(tag_struct, tag_type, StructType); arg_ast_t *args = Match(clause->pattern, FunctionCall)->args; if (args && !args->next && tag_struct->fields && tag_struct->fields->next) { - if (args->value->tag != Var) - code_err(args->value, "This is not a valid variable to bind to"); + if (args->value->tag != Var) code_err(args->value, "This is not a valid variable to bind to"); const char *var_name = Match(args->value, Var)->name; if (!streq(var_name, "_")) { Text_t var = Texts("_$", var_name); - code = Texts(code, compile_declaration(tag_type, var), " = _when_subject.", valid_c_name(clause_tag_name), ";\n"); + code = Texts(code, compile_declaration(tag_type, var), " = _when_subject.", + valid_c_name(clause_tag_name), ";\n"); scope = fresh_scope(scope); set_binding(scope, Match(args->value, Var)->name, tag_type, EMPTY_TEXT); } @@ -1131,11 +1116,10 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) arg_t *field = tag_struct->fields; for (arg_ast_t *arg = args; arg || field; arg = arg->next) { if (!arg) - code_err(ast, "The field ", type_to_str(subject_t), ".", clause_tag_name, ".", field->name, " wasn't accounted for"); - if (!field) - code_err(arg->value, "This is one more field than ", type_to_str(subject_t), " has"); - if (arg->name) - code_err(arg->value, "Named arguments are not currently supported"); + code_err(ast, "The field ", type_to_str(subject_t), ".", clause_tag_name, ".", field->name, + " wasn't accounted for"); + if (!field) code_err(arg->value, "This is one more field than ", type_to_str(subject_t), " has"); + if (arg->name) code_err(arg->value, "Named arguments are not currently supported"); const char *var_name = Match(arg->value, Var)->name; if (!streq(var_name, "_")) { @@ -1151,8 +1135,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) ast_list_t *statements = Match(clause->body, Block)->statements; if (!statements || (statements->ast->tag == Pass && !statements->next)) code = Texts(code, "break;\n}\n"); - else - code = Texts(code, compile_inline_block(scope, clause->body), "\nbreak;\n}\n"); + else code = Texts(code, compile_inline_block(scope, clause->body), "\nbreak;\n}\n"); } else { code = Texts(code, compile_statement(scope, clause->body), "\nbreak;\n}\n"); } @@ -1162,8 +1145,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) ast_list_t *statements = Match(when->else_body, Block)->statements; if (!statements || (statements->ast->tag == Pass && !statements->next)) code = Texts(code, "default: break;"); - else - code = Texts(code, "default: {\n", compile_inline_block(env, when->else_body), "\nbreak;\n}\n"); + else code = Texts(code, "default: {\n", compile_inline_block(env, when->else_body), "\nbreak;\n}\n"); } else { code = Texts(code, "default: {\n", compile_statement(env, when->else_body), "\nbreak;\n}\n"); } @@ -1176,8 +1158,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) case DocTest: { DeclareMatch(test, ast, DocTest); type_t *expr_t = get_type(env, test->expr); - if (!expr_t) - code_err(test->expr, "I couldn't figure out the type of this expression"); + if (!expr_t) code_err(test->expr, "I couldn't figure out the type of this expression"); Text_t setup = EMPTY_TEXT; Text_t test_code; @@ -1199,28 +1180,37 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) && value_type(get_type(env, Match(assign->targets->ast, Index)->indexed))->tag == TableType) lhs_t = Match(lhs_t, OptionalType)->type; if (has_stack_memory(lhs_t)) - code_err(test->expr, "Stack references cannot be assigned to variables because the variable's scope may outlive the scope of the stack memory."); + code_err(test->expr, "Stack references cannot be assigned " + "to variables because the " + "variable's scope may outlive the " + "scope of the stack memory."); env_t *val_scope = with_enum_scope(env, lhs_t); Text_t value = compile_to_type(val_scope, assign->values->ast, lhs_t); test_code = Texts("(", compile_assignment(env, assign->targets->ast, value), ")"); expr_t = lhs_t; } else { - // Multi-assign or assignment to potentially non-idempotent targets + // Multi-assign or assignment to potentially non-idempotent + // targets if (test->expected && assign->targets->next) - code_err(ast, "Sorry, but doctesting with '=' is not supported for multi-assignments"); + code_err(ast, "Sorry, but doctesting with '=' is not " + "supported for " + "multi-assignments"); test_code = Text("({ // Assignment\n"); int64_t i = 1; - for (ast_list_t *target = assign->targets, *value = assign->values; target && value; target = target->next, value = value->next) { + for (ast_list_t *target = assign->targets, *value = assign->values; target && value; + target = target->next, value = value->next) { type_t *lhs_t = get_type(env, target->ast); if (target->ast->tag == Index && lhs_t->tag == OptionalType && value_type(get_type(env, Match(target->ast, Index)->indexed))->tag == TableType) lhs_t = Match(lhs_t, OptionalType)->type; if (has_stack_memory(lhs_t)) - code_err(ast, "Stack references cannot be assigned to variables because the variable's scope may outlive the scope of the stack memory."); - if (target == assign->targets) - expr_t = lhs_t; + code_err(ast, "Stack references cannot be assigned to " + "variables because the " + "variable's scope may outlive the scope " + "of the stack memory."); + if (target == assign->targets) expr_t = lhs_t; env_t *val_scope = with_enum_scope(env, lhs_t); Text_t val_code = compile_to_type(val_scope, value->ast, lhs_t); test_code = Texts(test_code, compile_type(lhs_t), " $", String(i), " = ", val_code, ";\n"); @@ -1240,15 +1230,16 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) if (update.lhs->tag == Index) { type_t *indexed = value_type(get_type(env, Match(update.lhs, Index)->indexed)); if (indexed->tag == TableType && Match(indexed, TableType)->default_value == NULL) - code_err(update.lhs, "Update assignments are not currently supported for tables"); + code_err(update.lhs, "Update assignments are not currently " + "supported for tables"); } - ast_t *update_var = new(ast_t); + ast_t *update_var = new (ast_t); *update_var = *test->expr; - update_var->__data.PlusUpdate.lhs = LiteralCode(Text("(*expr)"), .type=lhs_t); // UNSAFE - test_code = Texts("({", - compile_declaration(Type(PointerType, lhs_t), Text("expr")), " = &(", compile_lvalue(env, update.lhs), "); ", - compile_statement(env, update_var), "; *expr; })"); + update_var->__data.PlusUpdate.lhs = LiteralCode(Text("(*expr)"), .type = lhs_t); // UNSAFE + test_code = + Texts("({", compile_declaration(Type(PointerType, lhs_t), Text("expr")), " = &(", + compile_lvalue(env, update.lhs), "); ", compile_statement(env, update_var), "; *expr; })"); expr_t = lhs_t; } else if (expr_t->tag == VoidType || expr_t->tag == AbortType || expr_t->tag == ReturnType) { test_code = Texts("({", compile_statement(env, test->expr), " NULL;})"); @@ -1256,27 +1247,19 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) test_code = compile(env, test->expr); } if (test->expected) { - return Texts( - setup, - "test(", compile_type(expr_t), ", ", test_code, ", ", - compile_to_type(env, test->expected, expr_t), ", ", - compile_type_info(expr_t), ", ", - String((int64_t)(test->expr->start - test->expr->file->text)), ", ", - String((int64_t)(test->expr->end - test->expr->file->text)), ");"); + return Texts(setup, "test(", compile_type(expr_t), ", ", test_code, ", ", + compile_to_type(env, test->expected, expr_t), ", ", compile_type_info(expr_t), ", ", + String((int64_t)(test->expr->start - test->expr->file->text)), ", ", + String((int64_t)(test->expr->end - test->expr->file->text)), ");"); } else { if (expr_t->tag == VoidType || expr_t->tag == AbortType) { - return Texts( - setup, - "inspect_void(", test_code, ", ", compile_type_info(expr_t), ", ", - String((int64_t)(test->expr->start - test->expr->file->text)), ", ", - String((int64_t)(test->expr->end - test->expr->file->text)), ");"); + return Texts(setup, "inspect_void(", test_code, ", ", compile_type_info(expr_t), ", ", + String((int64_t)(test->expr->start - test->expr->file->text)), ", ", + String((int64_t)(test->expr->end - test->expr->file->text)), ");"); } - return Texts( - setup, - "inspect(", compile_type(expr_t), ", ", test_code, ", ", - compile_type_info(expr_t), ", ", - String((int64_t)(test->expr->start - test->expr->file->text)), ", ", - String((int64_t)(test->expr->end - test->expr->file->text)), ");"); + return Texts(setup, "inspect(", compile_type(expr_t), ", ", test_code, ", ", compile_type_info(expr_t), + ", ", String((int64_t)(test->expr->start - test->expr->file->text)), ", ", + String((int64_t)(test->expr->end - test->expr->file->text)), ");"); } } case Assert: { @@ -1286,67 +1269,69 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) switch (expr->tag) { case And: { DeclareMatch(and_, ast, And); - return Texts( - compile_statement(env, WrapAST(ast, Assert, .expr=and_->lhs, .message=message)), - compile_statement(env, WrapAST(ast, Assert, .expr=and_->rhs, .message=message))); + return Texts(compile_statement(env, WrapAST(ast, Assert, .expr = and_->lhs, .message = message)), + compile_statement(env, WrapAST(ast, Assert, .expr = and_->rhs, .message = message))); } case Equals: failure = "!="; goto assert_comparison; case NotEquals: failure = "=="; goto assert_comparison; case LessThan: failure = ">="; goto assert_comparison; case LessThanOrEquals: failure = ">"; goto assert_comparison; case GreaterThan: failure = "<="; goto assert_comparison; - case GreaterThanOrEquals: failure = "<"; goto assert_comparison; { - assert_comparison:; - binary_operands_t cmp = BINARY_OPERANDS(expr); - type_t *lhs_t = get_type(env, cmp.lhs); - type_t *rhs_t = get_type(env, cmp.rhs); - type_t *operand_t; - if (cmp.lhs->tag == Int && is_numeric_type(rhs_t)) { - operand_t = rhs_t; - } else if (cmp.rhs->tag == Int && is_numeric_type(lhs_t)) { - operand_t = lhs_t; - } else if (can_compile_to_type(env, cmp.rhs, lhs_t)) { - operand_t = lhs_t; - } else if (can_compile_to_type(env, cmp.lhs, rhs_t)) { - operand_t = rhs_t; - } else { - code_err(ast, "I can't do comparisons between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); - } - - ast_t *lhs_var = FakeAST(InlineCCode, .chunks=new(ast_list_t, .ast=FakeAST(TextLiteral, Text("_lhs"))), .type=operand_t); - ast_t *rhs_var = FakeAST(InlineCCode, .chunks=new(ast_list_t, .ast=FakeAST(TextLiteral, Text("_rhs"))), .type=operand_t); - ast_t *var_comparison = new(ast_t, .file=expr->file, .start=expr->start, .end=expr->end, .tag=expr->tag, - .__data.Equals={.lhs=lhs_var, .rhs=rhs_var}); - int64_t line = get_line_number(ast->file, ast->start); - return Texts("{ // assertion\n", - compile_declaration(operand_t, Text("_lhs")), " = ", compile_to_type(env, cmp.lhs, operand_t), ";\n", - "\n#line ", String(line), "\n", - compile_declaration(operand_t, Text("_rhs")), " = ", compile_to_type(env, cmp.rhs, operand_t), ";\n", - "\n#line ", String(line), "\n", - "if (!(", compile_condition(env, var_comparison), "))\n", - "#line ", String(line), "\n", - Texts( - "fail_source(", quoted_str(ast->file->filename), ", ", - String((int64_t)(expr->start - expr->file->text)), ", ", - String((int64_t)(expr->end - expr->file->text)), ", ", - message ? Texts("Text$as_c_string(", compile_to_type(env, message, Type(TextType)), ")") - : Text("\"This assertion failed!\""), ", ", - "\" (\", ", expr_as_text(Text("_lhs"), operand_t, Text("no")), ", " - "\" ", failure, " \", ", expr_as_text(Text("_rhs"), operand_t, Text("no")), ", \")\");\n"), - "}\n"); + case GreaterThanOrEquals: + failure = "<"; + goto assert_comparison; + { + assert_comparison:; + binary_operands_t cmp = BINARY_OPERANDS(expr); + type_t *lhs_t = get_type(env, cmp.lhs); + type_t *rhs_t = get_type(env, cmp.rhs); + type_t *operand_t; + if (cmp.lhs->tag == Int && is_numeric_type(rhs_t)) { + operand_t = rhs_t; + } else if (cmp.rhs->tag == Int && is_numeric_type(lhs_t)) { + operand_t = lhs_t; + } else if (can_compile_to_type(env, cmp.rhs, lhs_t)) { + operand_t = lhs_t; + } else if (can_compile_to_type(env, cmp.lhs, rhs_t)) { + operand_t = rhs_t; + } else { + code_err(ast, "I can't do comparisons between ", type_to_str(lhs_t), " and ", type_to_str(rhs_t)); + } - } + ast_t *lhs_var = + FakeAST(InlineCCode, .chunks = new (ast_list_t, .ast = FakeAST(TextLiteral, Text("_lhs"))), + .type = operand_t); + ast_t *rhs_var = + FakeAST(InlineCCode, .chunks = new (ast_list_t, .ast = FakeAST(TextLiteral, Text("_rhs"))), + .type = operand_t); + ast_t *var_comparison = new (ast_t, .file = expr->file, .start = expr->start, .end = expr->end, + .tag = expr->tag, .__data.Equals = {.lhs = lhs_var, .rhs = rhs_var}); + int64_t line = get_line_number(ast->file, ast->start); + return Texts("{ // assertion\n", compile_declaration(operand_t, Text("_lhs")), " = ", + compile_to_type(env, cmp.lhs, operand_t), ";\n", "\n#line ", String(line), "\n", + compile_declaration(operand_t, Text("_rhs")), " = ", + compile_to_type(env, cmp.rhs, operand_t), ";\n", "\n#line ", String(line), "\n", "if (!(", + compile_condition(env, var_comparison), "))\n", "#line ", String(line), "\n", + Texts("fail_source(", quoted_str(ast->file->filename), ", ", + String((int64_t)(expr->start - expr->file->text)), ", ", + String((int64_t)(expr->end - expr->file->text)), ", ", + message + ? Texts("Text$as_c_string(", compile_to_type(env, message, Type(TextType)), ")") + : Text("\"This assertion failed!\""), + ", ", "\" (\", ", expr_as_text(Text("_lhs"), operand_t, Text("no")), + ", " + "\" ", + failure, " \", ", expr_as_text(Text("_rhs"), operand_t, Text("no")), ", \")\");\n"), + "}\n"); + } default: { int64_t line = get_line_number(ast->file, ast->start); - return Texts( - "if (!(", compile_condition(env, expr), "))\n", - "#line ", String(line), "\n", - "fail_source(", quoted_str(ast->file->filename), ", ", - String((int64_t)(expr->start - expr->file->text)), ", ", - String((int64_t)(expr->end - expr->file->text)), ", ", - message ? Texts("Text$as_c_string(", compile_to_type(env, message, Type(TextType)), ")") - : Text("\"This assertion failed!\""), - ");\n"); + return Texts("if (!(", compile_condition(env, expr), "))\n", "#line ", String(line), "\n", "fail_source(", + quoted_str(ast->file->filename), ", ", String((int64_t)(expr->start - expr->file->text)), ", ", + String((int64_t)(expr->end - expr->file->text)), ", ", + message ? Texts("Text$as_c_string(", compile_to_type(env, message, Type(TextType)), ")") + : Text("\"This assertion failed!\""), + ");\n"); } } } @@ -1354,10 +1339,8 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) DeclareMatch(decl, ast, Declare); const char *name = Match(decl->var, Var)->name; if (streq(name, "_")) { // Explicit discard - if (decl->value) - return Texts("(void)", compile(env, decl->value), ";"); - else - return EMPTY_TEXT; + if (decl->value) return Texts("(void)", compile(env, decl->value), ";"); + else return EMPTY_TEXT; } else { type_t *t = decl->type ? parse_type_ast(env, decl->type) : get_type(env, decl->value); if (t->tag == FunctionType) t = Type(ClosureType, t); @@ -1377,7 +1360,10 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) && value_type(get_type(env, Match(assign->targets->ast, Index)->indexed))->tag == TableType) lhs_t = Match(lhs_t, OptionalType)->type; if (has_stack_memory(lhs_t)) - code_err(ast, "Stack references cannot be assigned to variables because the variable's scope may outlive the scope of the stack memory."); + code_err(ast, "Stack references cannot be assigned to " + "variables because the " + "variable's scope may outlive the scope of the " + "stack memory."); env_t *val_env = with_enum_scope(env, lhs_t); Text_t val = compile_to_type(val_env, assign->values->ast, lhs_t); return Texts(compile_assignment(env, assign->targets->ast, val), ";\n"); @@ -1385,13 +1371,17 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) Text_t code = Text("{ // Assignment\n"); int64_t i = 1; - for (ast_list_t *value = assign->values, *target = assign->targets; value && target; value = value->next, target = target->next) { + for (ast_list_t *value = assign->values, *target = assign->targets; value && target; + value = value->next, target = target->next) { type_t *lhs_t = get_type(env, target->ast); if (target->ast->tag == Index && lhs_t->tag == OptionalType && value_type(get_type(env, Match(target->ast, Index)->indexed))->tag == TableType) lhs_t = Match(lhs_t, OptionalType)->type; if (has_stack_memory(lhs_t)) - code_err(ast, "Stack references cannot be assigned to variables because the variable's scope may outlive the scope of the stack memory."); + code_err(ast, "Stack references cannot be assigned to " + "variables because the " + "variable's scope may outlive the scope of the " + "stack memory."); env_t *val_env = with_enum_scope(env, lhs_t); Text_t val = compile_to_type(val_env, value->ast, lhs_t); code = Texts(code, compile_type(lhs_t), " $", String(i), " = ", val, ";\n"); @@ -1439,11 +1429,24 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) return Texts(compile_lvalue(env, update->lhs), " %= ", compile_to_type(env, update->rhs, lhs_t), ";"); return compile_update_assignment(env, ast); } - case PowerUpdate: case Mod1Update: case ConcatUpdate: case LeftShiftUpdate: case UnsignedLeftShiftUpdate: - case RightShiftUpdate: case UnsignedRightShiftUpdate: case AndUpdate: case OrUpdate: case XorUpdate: { + case PowerUpdate: + case Mod1Update: + case ConcatUpdate: + case LeftShiftUpdate: + case UnsignedLeftShiftUpdate: + case RightShiftUpdate: + case UnsignedRightShiftUpdate: + case AndUpdate: + case OrUpdate: + case XorUpdate: { return compile_update_assignment(env, ast); } - case StructDef: case EnumDef: case LangDef: case Extend: case FunctionDef: case ConvertDef: { + case StructDef: + case EnumDef: + case LangDef: + case Extend: + case FunctionDef: + case ConvertDef: { return EMPTY_TEXT; } case Skip: { @@ -1460,20 +1463,16 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) ++skip_label_count; } Text_t code = EMPTY_TEXT; - for (deferral_t *deferred = env->deferred; deferred && deferred != ctx->deferred; deferred = deferred->next) + for (deferral_t *deferred = env->deferred; deferred && deferred != ctx->deferred; + deferred = deferred->next) code = Texts(code, compile_statement(deferred->defer_env, deferred->block)); - if (code.length > 0) - return Texts("{\n", code, "goto ", ctx->skip_label, ";\n}\n"); - else - return Texts("goto ", ctx->skip_label, ";"); + if (code.length > 0) return Texts("{\n", code, "goto ", ctx->skip_label, ";\n}\n"); + else return Texts("goto ", ctx->skip_label, ";"); } } - if (env->loop_ctx) - code_err(ast, "This is not inside any loop"); - else if (target) - code_err(ast, "No loop target named '", target, "' was found"); - else - return Text("continue;"); + if (env->loop_ctx) code_err(ast, "This is not inside any loop"); + else if (target) code_err(ast, "No loop target named '", target, "' was found"); + else return Text("continue;"); } case Stop: { const char *target = Match(ast, Stop)->target; @@ -1489,20 +1488,16 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) ++stop_label_count; } Text_t code = EMPTY_TEXT; - for (deferral_t *deferred = env->deferred; deferred && deferred != ctx->deferred; deferred = deferred->next) + for (deferral_t *deferred = env->deferred; deferred && deferred != ctx->deferred; + deferred = deferred->next) code = Texts(code, compile_statement(deferred->defer_env, deferred->block)); - if (code.length > 0) - return Texts("{\n", code, "goto ", ctx->stop_label, ";\n}\n"); - else - return Texts("goto ", ctx->stop_label, ";"); + if (code.length > 0) return Texts("{\n", code, "goto ", ctx->stop_label, ";\n}\n"); + else return Texts("goto ", ctx->stop_label, ";"); } } - if (env->loop_ctx) - code_err(ast, "This is not inside any loop"); - else if (target) - code_err(ast, "No loop target named '", target, "' was found"); - else - return Text("break;"); + if (env->loop_ctx) code_err(ast, "This is not inside any loop"); + else if (target) code_err(ast, "No loop target named '", target, "' was found"); + else return Text("break;"); } case Pass: return Text(";"); case Defer: { @@ -1513,20 +1508,21 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) env_t *defer_env = fresh_scope(env); Text_t code = EMPTY_TEXT; for (int64_t i = 0; i < closed_vars.entries.length; i++) { - struct { const char *name; binding_t *b; } *entry = closed_vars.entries.data + closed_vars.entries.stride*i; - if (entry->b->type->tag == ModuleType) - continue; + struct { + const char *name; + binding_t *b; + } *entry = closed_vars.entries.data + closed_vars.entries.stride * i; + if (entry->b->type->tag == ModuleType) continue; if (Text$starts_with(entry->b->code, Text("userdata->"), NULL)) { Table$str_set(defer_env->locals, entry->name, entry->b); } else { Text_t defer_name = Texts("defer$", String(++defer_id), "$", entry->name); defer_id += 1; - code = Texts( - code, compile_declaration(entry->b->type, defer_name), " = ", entry->b->code, ";\n"); + code = Texts(code, compile_declaration(entry->b->type, defer_name), " = ", entry->b->code, ";\n"); set_binding(defer_env, entry->name, entry->b->type, defer_name); } } - env->deferred = new(deferral_t, .defer_env=defer_env, .block=body, .next=env->deferred); + env->deferred = new (deferral_t, .defer_env = defer_env, .block = body, .next = env->deferred); return code; } case Return: { @@ -1540,7 +1536,8 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) if (ret) { if (env->fn_ret->tag == VoidType || env->fn_ret->tag == AbortType) - code_err(ast, "This function is not supposed to return any values, according to its type signature"); + code_err(ast, "This function is not supposed to return any values, " + "according to its type signature"); env = with_enum_scope(env, env->fn_ret); Text_t value = compile_to_type(env, ret, env->fn_ret); @@ -1560,34 +1557,31 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) DeclareMatch(while_, ast, While); env_t *scope = fresh_scope(env); loop_ctx_t loop_ctx = (loop_ctx_t){ - .loop_name="while", - .deferred=scope->deferred, - .next=env->loop_ctx, + .loop_name = "while", + .deferred = scope->deferred, + .next = env->loop_ctx, }; scope->loop_ctx = &loop_ctx; Text_t body = compile_statement(scope, while_->body); - if (loop_ctx.skip_label.length > 0) - body = Texts(body, "\n", loop_ctx.skip_label, ": continue;"); - Text_t loop = Texts("while (", while_->condition ? compile(scope, while_->condition) : Text("yes"), ") {\n\t", body, "\n}"); - if (loop_ctx.stop_label.length > 0) - loop = Texts(loop, "\n", loop_ctx.stop_label, ":;"); + if (loop_ctx.skip_label.length > 0) body = Texts(body, "\n", loop_ctx.skip_label, ": continue;"); + Text_t loop = Texts("while (", while_->condition ? compile(scope, while_->condition) : Text("yes"), ") {\n\t", + body, "\n}"); + if (loop_ctx.stop_label.length > 0) loop = Texts(loop, "\n", loop_ctx.stop_label, ":;"); return loop; } case Repeat: { ast_t *body = Match(ast, Repeat)->body; env_t *scope = fresh_scope(env); loop_ctx_t loop_ctx = (loop_ctx_t){ - .loop_name="repeat", - .deferred=scope->deferred, - .next=env->loop_ctx, + .loop_name = "repeat", + .deferred = scope->deferred, + .next = env->loop_ctx, }; scope->loop_ctx = &loop_ctx; Text_t body_code = compile_statement(scope, body); - if (loop_ctx.skip_label.length > 0) - body_code = Texts(body_code, "\n", loop_ctx.skip_label, ": continue;"); + if (loop_ctx.skip_label.length > 0) body_code = Texts(body_code, "\n", loop_ctx.skip_label, ": continue;"); Text_t loop = Texts("for (;;) {\n\t", body_code, "\n}"); - if (loop_ctx.stop_label.length > 0) - loop = Texts(loop, "\n", loop_ctx.stop_label, ":;"); + if (loop_ctx.stop_label.length > 0) loop = Texts(loop, "\n", loop_ctx.stop_label, ":;"); return loop; } case For: { @@ -1595,50 +1589,48 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) // If we're iterating over a comprehension, that's actually just doing // one loop, we don't need to compile the comprehension as a list - // comprehension. This is a common case for reducers like `(+: i*2 for i in 5)` - // or `(and) x.is_good() for x in xs` + // comprehension. This is a common case for reducers like `(+: i*2 for i + // in 5)` or `(and) x.is_good() for x in xs` if (for_->iter->tag == Comprehension) { DeclareMatch(comp, for_->iter, Comprehension); ast_t *body = for_->body; if (for_->vars) { - if (for_->vars->next) - code_err(for_->vars->next->ast, "This is too many variables for iteration"); + if (for_->vars->next) code_err(for_->vars->next->ast, "This is too many variables for iteration"); body = WrapAST( ast, Block, - .statements=new(ast_list_t, .ast=WrapAST(ast, Declare, .var=for_->vars->ast, .value=comp->expr), - .next=body->tag == Block ? Match(body, Block)->statements : new(ast_list_t, .ast=body))); + .statements = new ( + ast_list_t, .ast = WrapAST(ast, Declare, .var = for_->vars->ast, .value = comp->expr), + .next = body->tag == Block ? Match(body, Block)->statements : new (ast_list_t, .ast = body))); } - if (comp->filter) - body = WrapAST(for_->body, If, .condition=comp->filter, .body=body); - ast_t *loop = WrapAST(ast, For, .vars=comp->vars, .iter=comp->iter, .body=body); + if (comp->filter) body = WrapAST(for_->body, If, .condition = comp->filter, .body = body); + ast_t *loop = WrapAST(ast, For, .vars = comp->vars, .iter = comp->iter, .body = body); return compile_statement(env, loop); } env_t *body_scope = for_scope(env, ast); loop_ctx_t loop_ctx = (loop_ctx_t){ - .loop_name="for", - .loop_vars=for_->vars, - .deferred=body_scope->deferred, - .next=body_scope->loop_ctx, + .loop_name = "for", + .loop_vars = for_->vars, + .deferred = body_scope->deferred, + .next = body_scope->loop_ctx, }; body_scope->loop_ctx = &loop_ctx; // Naked means no enclosing braces: Text_t naked_body = compile_inline_block(body_scope, for_->body); - if (loop_ctx.skip_label.length > 0) - naked_body = Texts(naked_body, "\n", loop_ctx.skip_label, ": continue;"); + if (loop_ctx.skip_label.length > 0) naked_body = Texts(naked_body, "\n", loop_ctx.skip_label, ": continue;"); Text_t stop = loop_ctx.stop_label.length > 0 ? Texts("\n", loop_ctx.stop_label, ":;") : EMPTY_TEXT; // Special case for improving performance for numeric iteration: - if (for_->iter->tag == MethodCall && streq(Match(for_->iter, MethodCall)->name, "to") && - is_int_type(get_type(env, Match(for_->iter, MethodCall)->self))) { + if (for_->iter->tag == MethodCall && streq(Match(for_->iter, MethodCall)->name, "to") + && is_int_type(get_type(env, Match(for_->iter, MethodCall)->self))) { // TODO: support other integer types arg_ast_t *args = Match(for_->iter, MethodCall)->args; if (!args) code_err(for_->iter, "to() needs at least one argument"); type_t *int_type = get_type(env, Match(for_->iter, MethodCall)->self); - type_t *step_type = int_type->tag == ByteType ? Type(IntType, .bits=TYPE_IBITS8) : int_type; + type_t *step_type = int_type->tag == ByteType ? Type(IntType, .bits = TYPE_IBITS8) : int_type; Text_t last = EMPTY_TEXT, step = EMPTY_TEXT, optional_step = EMPTY_TEXT; if (!args->name || streq(args->name, "last")) { @@ -1648,14 +1640,12 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) code_err(args->next->value, "Invalid argument name: ", args->next->name); if (get_type(env, args->next->value)->tag == OptionalType) optional_step = compile_to_type(env, args->next->value, Type(OptionalType, step_type)); - else - step = compile_to_type(env, args->next->value, step_type); + else step = compile_to_type(env, args->next->value, step_type); } } else if (streq(args->name, "step")) { if (get_type(env, args->value)->tag == OptionalType) optional_step = compile_to_type(env, args->value, Type(OptionalType, step_type)); - else - step = compile_to_type(env, args->value, step_type); + else step = compile_to_type(env, args->value, step_type); if (args->next) { if (args->next->name && !streq(args->next->name, "last")) code_err(args->next->value, "Invalid argument name: ", args->next->name); @@ -1663,52 +1653,57 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) } } - if (last.length == 0) - code_err(for_->iter, "No `last` argument was given"); - + if (last.length == 0) code_err(for_->iter, "No `last` argument was given"); + Text_t type_code = compile_type(int_type); Text_t value = for_->vars ? compile(body_scope, for_->vars->ast) : Text("i"); if (int_type->tag == BigIntType) { if (optional_step.length > 0) - step = Texts("({ OptionalInt_t maybe_step = ", optional_step, "; maybe_step->small == 0 ? (Int$compare_value(last, first) >= 0 ? I_small(1) : I_small(-1)) : (Int_t)maybe_step; })"); + step = Texts("({ OptionalInt_t maybe_step = ", optional_step, + "; maybe_step->small == 0 ? " + "(Int$compare_value(last, first) >= 0 " + "? I_small(1) : I_small(-1)) : (Int_t)maybe_step; " + "})"); else if (step.length == 0) - step = Text("Int$compare_value(last, first) >= 0 ? I_small(1) : I_small(-1)"); - return Texts( - "for (", type_code, " first = ", compile(env, Match(for_->iter, MethodCall)->self), ", ", - value, " = first, last = ", last, ", step = ", step, "; " - "Int$compare_value(", value, ", last) != Int$compare_value(step, I_small(0)); ", - value, " = Int$plus(", value, ", step)) {\n" - "\t", naked_body, - "}", - stop); + step = Text("Int$compare_value(last, first) >= 0 ? " + "I_small(1) : I_small(-1)"); + return Texts("for (", type_code, " first = ", compile(env, Match(for_->iter, MethodCall)->self), ", ", + value, " = first, last = ", last, ", step = ", step, + "; " + "Int$compare_value(", + value, ", last) != Int$compare_value(step, I_small(0)); ", value, " = Int$plus(", value, + ", step)) {\n" + "\t", + naked_body, "}", stop); } else { if (optional_step.length > 0) - step = Texts("({ ", compile_type(Type(OptionalType, step_type)), " maybe_step = ", optional_step, "; " - "maybe_step.is_none ? (", type_code, ")(last >= first ? 1 : -1) : maybe_step.value; })"); - else if (step.length == 0) - step = Texts("(", type_code, ")(last >= first ? 1 : -1)"); - return Texts( - "for (", type_code, " first = ", compile(env, Match(for_->iter, MethodCall)->self), ", ", - value, " = first, last = ", last, ", step = ", step, "; " - "step > 0 ? ", value, " <= last : ", value, " >= last; ", - value, " += step) {\n" - "\t", naked_body, - "}", - stop); + step = Texts("({ ", compile_type(Type(OptionalType, step_type)), " maybe_step = ", optional_step, + "; " + "maybe_step.is_none ? (", + type_code, ")(last >= first ? 1 : -1) : maybe_step.value; })"); + else if (step.length == 0) step = Texts("(", type_code, ")(last >= first ? 1 : -1)"); + return Texts("for (", type_code, " first = ", compile(env, Match(for_->iter, MethodCall)->self), ", ", + value, " = first, last = ", last, ", step = ", step, + "; " + "step > 0 ? ", + value, " <= last : ", value, " >= last; ", value, + " += step) {\n" + "\t", + naked_body, "}", stop); } - } else if (for_->iter->tag == MethodCall && streq(Match(for_->iter, MethodCall)->name, "onward") && - get_type(env, Match(for_->iter, MethodCall)->self)->tag == BigIntType) { + } else if (for_->iter->tag == MethodCall && streq(Match(for_->iter, MethodCall)->name, "onward") + && get_type(env, Match(for_->iter, MethodCall)->self)->tag == BigIntType) { // Special case for Int.onward() arg_ast_t *args = Match(for_->iter, MethodCall)->args; - arg_t *arg_spec = new(arg_t, .name="step", .type=INT_TYPE, .default_val=FakeAST(Int, .str="1"), .next=NULL); + arg_t *arg_spec = + new (arg_t, .name = "step", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "1"), .next = NULL); Text_t step = compile_arguments(env, for_->iter, arg_spec, args); Text_t value = for_->vars ? compile(body_scope, for_->vars->ast) : Text("i"); - return Texts( - "for (Int_t ", value, " = ", compile(env, Match(for_->iter, MethodCall)->self), ", ", - "step = ", step, "; ; ", value, " = Int$plus(", value, ", step)) {\n" - "\t", naked_body, - "}", - stop); + return Texts("for (Int_t ", value, " = ", compile(env, Match(for_->iter, MethodCall)->self), ", ", + "step = ", step, "; ; ", value, " = Int$plus(", value, + ", step)) {\n" + "\t", + naked_body, "}", stop); } type_t *iter_t = get_type(env, for_->iter); @@ -1734,14 +1729,11 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) Text_t loop = EMPTY_TEXT; loop = Texts(loop, "for (int64_t i = 1; i <= iterating.length; ++i)"); - if (index.length > 0) - naked_body = Texts("Int_t ", index, " = I(i);\n", naked_body); + if (index.length > 0) naked_body = Texts("Int_t ", index, " = I(i);\n", naked_body); if (value.length > 0) { - loop = Texts(loop, "{\n", - compile_declaration(item_t, value), - " = *(", compile_type(item_t), "*)(iterating.data + (i-1)*iterating.stride);\n", - naked_body, "\n}"); + loop = Texts(loop, "{\n", compile_declaration(item_t, value), " = *(", compile_type(item_t), + "*)(iterating.data + (i-1)*iterating.stride);\n", naked_body, "\n}"); } else { loop = Texts(loop, "{\n", naked_body, "\n}"); } @@ -1751,38 +1743,37 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) if (iter_t->tag == PointerType) { loop = Texts("{\n" - "List_t *ptr = ", compile_to_pointer_depth(env, for_->iter, 1, false), ";\n" - "\nLIST_INCREF(*ptr);\n" - "List_t iterating = *ptr;\n", - loop, - stop, - "\nLIST_DECREF(*ptr);\n" - "}\n"); + "List_t *ptr = ", + compile_to_pointer_depth(env, for_->iter, 1, false), + ";\n" + "\nLIST_INCREF(*ptr);\n" + "List_t iterating = *ptr;\n", + loop, stop, + "\nLIST_DECREF(*ptr);\n" + "}\n"); } else { loop = Texts("{\n" - "List_t iterating = ", compile_to_pointer_depth(env, for_->iter, 0, false), ";\n", - loop, - stop, - "}\n"); + "List_t iterating = ", + compile_to_pointer_depth(env, for_->iter, 0, false), ";\n", loop, stop, "}\n"); } return loop; } - case SetType: case TableType: { + case SetType: + case TableType: { Text_t loop = Text("for (int64_t i = 0; i < iterating.length; ++i) {\n"); if (for_->vars) { if (iter_value_t->tag == SetType) { - if (for_->vars->next) - code_err(for_->vars->next->ast, "This is too many variables for this loop"); + if (for_->vars->next) code_err(for_->vars->next->ast, "This is too many variables for this loop"); Text_t item = compile(body_scope, for_->vars->ast); type_t *item_type = Match(iter_value_t, SetType)->item_type; loop = Texts(loop, compile_declaration(item_type, item), " = *(", compile_type(item_type), "*)(", - "iterating.data + i*iterating.stride);\n"); + "iterating.data + i*iterating.stride);\n"); } else { Text_t key = compile(body_scope, for_->vars->ast); type_t *key_t = Match(iter_value_t, TableType)->key_type; loop = Texts(loop, compile_declaration(key_t, key), " = *(", compile_type(key_t), "*)(", - "iterating.data + i*iterating.stride);\n"); + "iterating.data + i*iterating.stride);\n"); if (for_->vars->next) { if (for_->vars->next->next) @@ -1790,9 +1781,10 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) type_t *value_t = Match(iter_value_t, TableType)->value_type; Text_t value = compile(body_scope, for_->vars->next->ast); - Text_t value_offset = Texts("offsetof(struct { ", compile_declaration(key_t, Text("k")), "; ", compile_declaration(value_t, Text("v")), "; }, v)"); + Text_t value_offset = Texts("offsetof(struct { ", compile_declaration(key_t, Text("k")), "; ", + compile_declaration(value_t, Text("v")), "; }, v)"); loop = Texts(loop, compile_declaration(value_t, value), " = *(", compile_type(value_t), "*)(", - "iterating.data + i*iterating.stride + ", value_offset, ");\n"); + "iterating.data + i*iterating.stride + ", value_offset, ");\n"); } } } @@ -1804,20 +1796,16 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) } if (iter_t->tag == PointerType) { - loop = Texts( - "{\n", - "Table_t *t = ", compile_to_pointer_depth(env, for_->iter, 1, false), ";\n" - "LIST_INCREF(t->entries);\n" - "List_t iterating = t->entries;\n", - loop, - "LIST_DECREF(t->entries);\n" - "}\n"); + loop = Texts("{\n", "Table_t *t = ", compile_to_pointer_depth(env, for_->iter, 1, false), + ";\n" + "LIST_INCREF(t->entries);\n" + "List_t iterating = t->entries;\n", + loop, + "LIST_DECREF(t->entries);\n" + "}\n"); } else { - loop = Texts( - "{\n", - "List_t iterating = (", compile_to_pointer_depth(env, for_->iter, 0, false), ").entries;\n", - loop, - "}\n"); + loop = Texts("{\n", "List_t iterating = (", compile_to_pointer_depth(env, for_->iter, 0, false), + ").entries;\n", loop, "}\n"); } return loop; } @@ -1826,52 +1814,50 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) if (for_->iter->tag == Int) { const char *str = Match(for_->iter, Int)->str; Int_t int_val = Int$from_str(str); - if (int_val.small == 0) - code_err(for_->iter, "Failed to parse this integer"); + if (int_val.small == 0) code_err(for_->iter, "Failed to parse this integer"); mpz_t i; mpz_init_set_int(i, int_val); - if (mpz_cmpabs_ui(i, BIGGEST_SMALL_INT) <= 0) - n = Text$from_str(mpz_get_str(NULL, 10, i)); - else - goto big_n; - + if (mpz_cmpabs_ui(i, BIGGEST_SMALL_INT) <= 0) n = Text$from_str(mpz_get_str(NULL, 10, i)); + else goto big_n; if (for_->empty && mpz_cmp_si(i, 0) <= 0) { return compile_statement(env, for_->empty); } else { - return Texts( - "for (int64_t i = 1; i <= ", n, "; ++i) {\n", - for_->vars ? Texts("\tInt_t ", compile(body_scope, for_->vars->ast), " = I_small(i);\n") : EMPTY_TEXT, - "\t", naked_body, - "}\n", - stop, "\n"); + return Texts("for (int64_t i = 1; i <= ", n, "; ++i) {\n", + for_->vars + ? Texts("\tInt_t ", compile(body_scope, for_->vars->ast), " = I_small(i);\n") + : EMPTY_TEXT, + "\t", naked_body, "}\n", stop, "\n"); } } - big_n: + big_n: n = compile_to_pointer_depth(env, for_->iter, 0, false); Text_t i = for_->vars ? compile(body_scope, for_->vars->ast) : Text("i"); Text_t n_var = for_->vars ? Texts("max", i) : Text("n"); if (for_->empty) { - return Texts( - "{\n" - "Int_t ", n_var, " = ", n, ";\n" - "if (Int$compare_value(", n_var, ", I(0)) > 0) {\n" - "for (Int_t ", i, " = I(1); Int$compare_value(", i, ", ", n_var, ") <= 0; ", i, " = Int$plus(", i, ", I(1))) {\n", - "\t", naked_body, - "}\n" - "} else ", compile_statement(env, for_->empty), - stop, "\n" - "}\n"); + return Texts("{\n" + "Int_t ", + n_var, " = ", n, + ";\n" + "if (Int$compare_value(", + n_var, + ", I(0)) > 0) {\n" + "for (Int_t ", + i, " = I(1); Int$compare_value(", i, ", ", n_var, ") <= 0; ", i, " = Int$plus(", i, + ", I(1))) {\n", "\t", naked_body, + "}\n" + "} else ", + compile_statement(env, for_->empty), stop, + "\n" + "}\n"); } else { - return Texts( - "for (Int_t ", i, " = I(1), ", n_var, " = ", n, "; Int$compare_value(", i, ", ", n_var, ") <= 0; ", i, " = Int$plus(", i, ", I(1))) {\n", - "\t", naked_body, - "}\n", - stop, "\n"); + return Texts("for (Int_t ", i, " = I(1), ", n_var, " = ", n, "; Int$compare_value(", i, ", ", n_var, + ") <= 0; ", i, " = Int$plus(", i, ", I(1))) {\n", "\t", naked_body, "}\n", stop, "\n"); } } - case FunctionType: case ClosureType: { + case FunctionType: + case ClosureType: { // Iterator function: Text_t code = Text("{\n"); @@ -1879,53 +1865,63 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) if (is_idempotent(for_->iter)) { next_fn = compile_to_pointer_depth(env, for_->iter, 0, false); } else { - code = Texts(code, compile_declaration(iter_value_t, Text("next")), " = ", compile_to_pointer_depth(env, for_->iter, 0, false), ";\n"); + code = Texts(code, compile_declaration(iter_value_t, Text("next")), " = ", + compile_to_pointer_depth(env, for_->iter, 0, false), ";\n"); next_fn = Text("next"); } - __typeof(iter_value_t->__data.FunctionType) *fn = iter_value_t->tag == ClosureType ? Match(Match(iter_value_t, ClosureType)->fn, FunctionType) : Match(iter_value_t, FunctionType); + __typeof(iter_value_t->__data.FunctionType) *fn = + iter_value_t->tag == ClosureType ? Match(Match(iter_value_t, ClosureType)->fn, FunctionType) + : Match(iter_value_t, FunctionType); Text_t get_next; if (iter_value_t->tag == ClosureType) { type_t *fn_t = Match(iter_value_t, ClosureType)->fn; arg_t *closure_fn_args = NULL; for (arg_t *arg = Match(fn_t, FunctionType)->args; arg; arg = arg->next) - closure_fn_args = new(arg_t, .name=arg->name, .type=arg->type, .default_val=arg->default_val, .next=closure_fn_args); - closure_fn_args = new(arg_t, .name="userdata", .type=Type(PointerType, .pointed=Type(MemoryType)), .next=closure_fn_args); + closure_fn_args = new (arg_t, .name = arg->name, .type = arg->type, .default_val = arg->default_val, + .next = closure_fn_args); + closure_fn_args = new (arg_t, .name = "userdata", + .type = Type(PointerType, .pointed = Type(MemoryType)), .next = closure_fn_args); REVERSE_LIST(closure_fn_args); - Text_t fn_type_code = compile_type(Type(FunctionType, .args=closure_fn_args, .ret=Match(fn_t, FunctionType)->ret)); + Text_t fn_type_code = + compile_type(Type(FunctionType, .args = closure_fn_args, .ret = Match(fn_t, FunctionType)->ret)); get_next = Texts("((", fn_type_code, ")", next_fn, ".fn)(", next_fn, ".userdata)"); } else { get_next = Texts(next_fn, "()"); } if (fn->ret->tag == OptionalType) { - // Use an optional variable `cur` for each iteration step, which will be checked for none + // Use an optional variable `cur` for each iteration step, which + // will be checked for none code = Texts(code, compile_declaration(fn->ret, Text("cur")), ";\n"); get_next = Texts("(cur=", get_next, ", !", check_none(fn->ret, Text("cur")), ")"); if (for_->vars) { - naked_body = Texts( - compile_declaration(Match(fn->ret, OptionalType)->type, Texts("_$", Match(for_->vars->ast, Var)->name)), - " = ", optional_into_nonnone(fn->ret, Text("cur")), ";\n", - naked_body); + naked_body = Texts(compile_declaration(Match(fn->ret, OptionalType)->type, + Texts("_$", Match(for_->vars->ast, Var)->name)), + " = ", optional_into_nonnone(fn->ret, Text("cur")), ";\n", naked_body); } if (for_->empty) { - code = Texts(code, "if (", get_next, ") {\n" - "\tdo{\n\t\t", naked_body, "\t} while(", get_next, ");\n" - "} else {\n\t", compile_statement(env, for_->empty), "}", stop, "\n}\n"); + code = Texts(code, "if (", get_next, + ") {\n" + "\tdo{\n\t\t", + naked_body, "\t} while(", get_next, + ");\n" + "} else {\n\t", + compile_statement(env, for_->empty), "}", stop, "\n}\n"); } else { code = Texts(code, "while(", get_next, ") {\n\t", naked_body, "}\n", stop, "\n}\n"); } } else { if (for_->vars) { - naked_body = Texts( - compile_declaration(fn->ret, Texts("_$", Match(for_->vars->ast, Var)->name)), - " = ", get_next, ";\n", naked_body); + naked_body = Texts(compile_declaration(fn->ret, Texts("_$", Match(for_->vars->ast, Var)->name)), + " = ", get_next, ";\n", naked_body); } else { naked_body = Texts(get_next, ";\n", naked_body); } if (for_->empty) - code_err(for_->empty, "This iteration loop will always have values, so this block will never run"); + code_err(for_->empty, "This iteration loop will always have values, " + "so this block will never run"); code = Texts(code, "for (;;) {\n\t", naked_body, "}\n", stop, "\n}\n"); } @@ -1938,8 +1934,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) DeclareMatch(if_, ast, If); ast_t *condition = if_->condition; if (condition->tag == Declare) { - if (Match(condition, Declare)->value == NULL) - code_err(condition, "This declaration must have a value"); + if (Match(condition, Declare)->value == NULL) code_err(condition, "This declaration must have a value"); env_t *truthy_scope = fresh_scope(env); Text_t code = Texts("IF_DECLARE(", compile_statement(truthy_scope, condition), ", "); bind_statement(truthy_scope, condition); @@ -1947,13 +1942,11 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) code = Texts(code, compile_condition(truthy_scope, var), ", "); type_t *cond_t = get_type(truthy_scope, var); if (cond_t->tag == OptionalType) { - set_binding(truthy_scope, Match(var, Var)->name, - Match(cond_t, OptionalType)->type, + set_binding(truthy_scope, Match(var, Var)->name, Match(cond_t, OptionalType)->type, optional_into_nonnone(cond_t, compile(truthy_scope, var))); } code = Texts(code, compile_statement(truthy_scope, if_->body), ")"); - if (if_->else_body) - code = Texts(code, "\nelse ", compile_statement(env, if_->else_body)); + if (if_->else_body) code = Texts(code, "\nelse ", compile_statement(env, if_->else_body)); return code; } else { Text_t code = Texts("if (", compile_condition(env, condition), ")"); @@ -1961,13 +1954,11 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) type_t *cond_t = get_type(env, condition); if (condition->tag == Var && cond_t->tag == OptionalType) { truthy_scope = fresh_scope(env); - set_binding(truthy_scope, Match(condition, Var)->name, - Match(cond_t, OptionalType)->type, + set_binding(truthy_scope, Match(condition, Var)->name, Match(cond_t, OptionalType)->type, optional_into_nonnone(cond_t, compile(truthy_scope, condition))); } code = Texts(code, compile_statement(truthy_scope, if_->body)); - if (if_->else_body) - code = Texts(code, "\nelse ", compile_statement(env, if_->else_body)); + if (if_->else_body) code = Texts(code, "\nelse ", compile_statement(env, if_->else_body)); return code; } } @@ -1975,21 +1966,19 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) return Texts("{\n", compile_inline_block(env, ast), "}\n"); } case Comprehension: { - if (!env->comprehension_action) - code_err(ast, "I don't know what to do with this comprehension!"); + if (!env->comprehension_action) code_err(ast, "I don't know what to do with this comprehension!"); DeclareMatch(comp, ast, Comprehension); if (comp->expr->tag == Comprehension) { // Nested comprehension - ast_t *body = comp->filter ? WrapAST(ast, If, .condition=comp->filter, .body=comp->expr) : comp->expr; - ast_t *loop = WrapAST(ast, For, .vars=comp->vars, .iter=comp->iter, .body=body); + ast_t *body = comp->filter ? WrapAST(ast, If, .condition = comp->filter, .body = comp->expr) : comp->expr; + ast_t *loop = WrapAST(ast, For, .vars = comp->vars, .iter = comp->iter, .body = body); return compile_statement(env, loop); } // List/Set/Table comprehension: - comprehension_body_t get_body = (void*)env->comprehension_action->fn; + comprehension_body_t get_body = (void *)env->comprehension_action->fn; ast_t *body = get_body(comp->expr, env->comprehension_action->userdata); - if (comp->filter) - body = WrapAST(comp->expr, If, .condition=comp->filter, .body=body); - ast_t *loop = WrapAST(ast, For, .vars=comp->vars, .iter=comp->iter, .body=body); + if (comp->filter) body = WrapAST(comp->expr, If, .condition = comp->filter, .body = body); + ast_t *loop = WrapAST(ast, For, .vars = comp->vars, .iter = comp->iter, .body = body); return compile_statement(env, loop); } case Extern: return EMPTY_TEXT; @@ -2017,9 +2006,10 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) module_info_t mod = get_module_info(ast); glob_t tm_files; const char *folder = mod.version ? String(mod.name, "_", mod.version) : mod.name; - if (glob(String(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, &tm_files) != 0) { - if (!try_install_module(mod)) - code_err(ast, "Could not find library"); + if (glob(String(TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, + NULL, &tm_files) + != 0) { + if (!try_install_module(mod)) code_err(ast, "Could not find library"); } Text_t initialization = EMPTY_TEXT; @@ -2027,8 +2017,7 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) for (size_t i = 0; i < tm_files.gl_pathc; i++) { const char *filename = tm_files.gl_pathv[i]; initialization = Texts( - initialization, - with_source_info(env, ast, Texts("$initialize", get_id_suffix(filename), "();\n"))); + initialization, with_source_info(env, ast, Texts("$initialize", get_id_suffix(filename), "();\n"))); } globfree(&tm_files); return initialization; @@ -2037,7 +2026,8 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) } } default: - // print("Is discardable: ", ast_to_sexp_str(ast), " ==> ", is_discardable(env, ast)); + // print("Is discardable: ", ast_to_sexp_str(ast), " ==> ", + // is_discardable(env, ast)); if (!is_discardable(env, ast)) code_err(ast, "The ", type_to_str(get_type(env, ast)), " result of this statement cannot be discarded"); return Texts("(void)", compile(env, ast), ";"); @@ -2049,15 +2039,18 @@ Text_t compile_statement(env_t *env, ast_t *ast) { return with_source_info(env, ast, stmt); } -Text_t expr_as_text(Text_t expr, type_t *t, Text_t color) -{ +Text_t expr_as_text(Text_t expr, type_t *t, Text_t color) { switch (t->tag) { case MemoryType: return Texts("Memory$as_text(stack(", expr, "), ", color, ", &Memory$info)"); case BoolType: - // NOTE: this cannot use stack(), since bools may actually be bit fields: - return Texts("Bool$as_text((Bool_t[1]){", expr, "}, ", color, ", &Bool$info)"); + // NOTE: this cannot use stack(), since bools may actually be bit + // fields: + return Texts("Bool$as_text((Bool_t[1]){", expr, "}, ", color, ", &Bool$info)"); case CStringType: return Texts("CString$as_text(stack(", expr, "), ", color, ", &CString$info)"); - case BigIntType: case IntType: case ByteType: case NumType: { + case BigIntType: + case IntType: + case ByteType: + case NumType: { Text_t name = type_to_text(t); return Texts(name, "$as_text(stack(", expr, "), ", color, ", &", name, "$info)"); } @@ -2065,25 +2058,24 @@ Text_t expr_as_text(Text_t expr, type_t *t, Text_t color) case ListType: return Texts("List$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); case SetType: return Texts("Table$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); case TableType: return Texts("Table$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); - case FunctionType: case ClosureType: return Texts("Func$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); + case FunctionType: + case ClosureType: return Texts("Func$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); case PointerType: return Texts("Pointer$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); case OptionalType: return Texts("Optional$as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); - case StructType: case EnumType: - return Texts("generic_as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); + case StructType: + case EnumType: return Texts("generic_as_text(stack(", expr, "), ", color, ", ", compile_type_info(t), ")"); default: compiler_err(NULL, NULL, NULL, "Stringifying is not supported for ", type_to_str(t)); } return EMPTY_TEXT; } -Text_t compile_text(env_t *env, ast_t *ast, Text_t color) -{ +Text_t compile_text(env_t *env, ast_t *ast, Text_t color) { type_t *t = get_type(env, ast); Text_t expr = compile(env, ast); return expr_as_text(expr, t, color); } -Text_t compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool needs_incref) -{ +Text_t compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool needs_incref) { Text_t val = compile(env, ast); type_t *t = get_type(env, ast); int64_t depth = 0; @@ -2092,16 +2084,13 @@ Text_t compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bo // Passing a literal value won't trigger an incref, because it's ephemeral, // e.g. [10, 20].reversed() - if (t->tag != PointerType && needs_incref && !can_be_mutated(env, ast)) - needs_incref = false; + if (t->tag != PointerType && needs_incref && !can_be_mutated(env, ast)) needs_incref = false; while (depth != target_depth) { if (depth < target_depth) { - if (ast->tag == Var && target_depth == 1) - val = Texts("(&", val, ")"); - else - code_err(ast, "This should be a pointer, not ", type_to_str(get_type(env, ast))); - t = Type(PointerType, .pointed=t, .is_stack=true); + if (ast->tag == Var && target_depth == 1) val = Texts("(&", val, ")"); + else code_err(ast, "This should be a pointer, not ", type_to_str(get_type(env, ast))); + t = Type(PointerType, .pointed = t, .is_stack = true); ++depth; } else { DeclareMatch(ptr, t, PointerType); @@ -2116,16 +2105,13 @@ Text_t compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bo t = ptr->pointed; } - if (needs_incref && t->tag == ListType) - val = Texts("LIST_COPY(", val, ")"); - else if (needs_incref && (t->tag == TableType || t->tag == SetType)) - val = Texts("TABLE_COPY(", val, ")"); + if (needs_incref && t->tag == ListType) val = Texts("LIST_COPY(", val, ")"); + else if (needs_incref && (t->tag == TableType || t->tag == SetType)) val = Texts("TABLE_COPY(", val, ")"); return val; } -Text_t compile_to_type(env_t *env, ast_t *ast, type_t *t) -{ +Text_t compile_to_type(env_t *env, ast_t *ast, type_t *t) { assert(!is_incomplete_type(t)); if (ast->tag == Int && is_numeric_type(non_optional(t))) { return compile_int_to_type(env, ast, t); @@ -2137,10 +2123,11 @@ Text_t compile_to_type(env_t *env, ast_t *ast, type_t *t) default: code_err(ast, "This is not a valid number bit width"); } } else if (ast->tag == None) { - if (t->tag != OptionalType) - code_err(ast, "This is not supposed to be an optional type"); + if (t->tag != OptionalType) code_err(ast, "This is not supposed to be an optional type"); else if (Match(t, OptionalType)->type == NULL) - code_err(ast, "I don't know what kind of `none` this is supposed to be!\nPlease tell me by declaring a variable like `foo : Type = none`"); + code_err(ast, "I don't know what kind of `none` this is supposed to " + "be!\nPlease " + "tell me by declaring a variable like `foo : Type = none`"); return compile_none(t); } else if (t->tag == PointerType && (ast->tag == HeapAllocate || ast->tag == StackReference)) { return compile_typed_allocation(env, ast, t); @@ -2166,15 +2153,17 @@ Text_t compile_to_type(env_t *env, ast_t *ast, type_t *t) if (ast->tag == MethodCall) { DeclareMatch(methodcall, ast, MethodCall); type_t *self_type = get_type(env, methodcall->self); - // Currently, this is only implemented for cases where you have the return type - // and the self type equal to each other, because that's the main case I care - // about with list and set methods (e.g. `List.sorted()`) + // Currently, this is only implemented for cases where you have the + // return type and the self type equal to each other, because that's the + // main case I care about with list and set methods (e.g. + // `List.sorted()`) if (is_incomplete_type(self_type) && type_eq(self_type, actual)) { type_t *completed_self = most_complete_type(self_type, t); if (completed_self) { - ast_t *explicit_self = WrapAST(methodcall->self, ExplicitlyTyped, .ast=methodcall->self, .type=completed_self); - ast_t *new_methodcall = WrapAST(ast, MethodCall, .self=explicit_self, - .name=methodcall->name, .args=methodcall->args); + ast_t *explicit_self = + WrapAST(methodcall->self, ExplicitlyTyped, .ast = methodcall->self, .type = completed_self); + ast_t *new_methodcall = + WrapAST(ast, MethodCall, .self = explicit_self, .name = methodcall->name, .args = methodcall->args); return compile_to_type(env, new_methodcall, t); } } @@ -2186,11 +2175,10 @@ Text_t compile_to_type(env_t *env, ast_t *ast, type_t *t) if (!is_incomplete_type(actual)) { Text_t code = compile(env, ast); - if (promote(env, ast, &code, actual, t)) - return code; + if (promote(env, ast, &code, actual, t)) return code; } - arg_ast_t *constructor_args = new(arg_ast_t, .value=ast); + arg_ast_t *constructor_args = new (arg_ast_t, .value = ast); binding_t *constructor = get_constructor(env, t, constructor_args, true); if (constructor) { arg_t *arg_spec = Match(constructor->type, FunctionType)->args; @@ -2200,25 +2188,21 @@ Text_t compile_to_type(env_t *env, ast_t *ast, type_t *t) code_err(ast, "I expected a ", type_to_str(t), " here, but this is a ", type_to_str(actual)); } -Text_t compile_typed_list(env_t *env, ast_t *ast, type_t *list_type) -{ +Text_t compile_typed_list(env_t *env, ast_t *ast, type_t *list_type) { DeclareMatch(list, ast, List); - if (!list->items) - return Text("(List_t){.length=0}"); + if (!list->items) return Text("(List_t){.length=0}"); type_t *item_type = Match(list_type, ListType)->item_type; int64_t n = 0; for (ast_list_t *item = list->items; item; item = item->next) { ++n; - if (item->ast->tag == Comprehension) - goto list_comprehension; + if (item->ast->tag == Comprehension) goto list_comprehension; } { env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : env; - if (is_incomplete_type(item_type)) - code_err(ast, "This list's type can't be inferred!"); + if (is_incomplete_type(item_type)) code_err(ast, "This list's type can't be inferred!"); Text_t code = Texts("TypedListN(", compile_type(item_type), ", ", String(n)); for (ast_list_t *item = list->items; item; item = item->next) { code = Texts(code, ", ", compile_to_type(scope, item->ast, item_type)); @@ -2226,48 +2210,39 @@ Text_t compile_typed_list(env_t *env, ast_t *ast, type_t *list_type) return Texts(code, ")"); } - list_comprehension: - { - env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env); - static int64_t comp_num = 1; - const char *comprehension_name = String("list$", comp_num++); - ast_t *comprehension_var = LiteralCode(Texts("&", comprehension_name), - .type=Type(PointerType, .pointed=list_type, .is_stack=true)); - Closure_t comp_action = {.fn=add_to_list_comprehension, .userdata=comprehension_var}; - scope->comprehension_action = &comp_action; - Text_t code = Texts("({ List_t ", comprehension_name, " = {};"); - // set_binding(scope, comprehension_name, list_type, comprehension_name); - for (ast_list_t *item = list->items; item; item = item->next) { - if (item->ast->tag == Comprehension) - code = Texts(code, "\n", compile_statement(scope, item->ast)); - else - code = Texts(code, compile_statement(env, add_to_list_comprehension(item->ast, comprehension_var))); - } - code = Texts(code, " ", comprehension_name, "; })"); - return code; +list_comprehension: { + env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env); + static int64_t comp_num = 1; + const char *comprehension_name = String("list$", comp_num++); + ast_t *comprehension_var = + LiteralCode(Texts("&", comprehension_name), .type = Type(PointerType, .pointed = list_type, .is_stack = true)); + Closure_t comp_action = {.fn = add_to_list_comprehension, .userdata = comprehension_var}; + scope->comprehension_action = &comp_action; + Text_t code = Texts("({ List_t ", comprehension_name, " = {};"); + // set_binding(scope, comprehension_name, list_type, comprehension_name); + for (ast_list_t *item = list->items; item; item = item->next) { + if (item->ast->tag == Comprehension) code = Texts(code, "\n", compile_statement(scope, item->ast)); + else code = Texts(code, compile_statement(env, add_to_list_comprehension(item->ast, comprehension_var))); } + code = Texts(code, " ", comprehension_name, "; })"); + return code; +} } -Text_t compile_typed_set(env_t *env, ast_t *ast, type_t *set_type) -{ +Text_t compile_typed_set(env_t *env, ast_t *ast, type_t *set_type) { DeclareMatch(set, ast, Set); - if (!set->items) - return Text("((Table_t){})"); + if (!set->items) return Text("((Table_t){})"); type_t *item_type = Match(set_type, SetType)->item_type; int64_t n = 0; for (ast_list_t *item = set->items; item; item = item->next) { ++n; - if (item->ast->tag == Comprehension) - goto set_comprehension; + if (item->ast->tag == Comprehension) goto set_comprehension; } - + { // No comprehension: - Text_t code = Texts("Set(", - compile_type(item_type), ", ", - compile_type_info(item_type), ", ", - String(n)); + Text_t code = Texts("Set(", compile_type(item_type), ", ", compile_type_info(item_type), ", ", String(n)); env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : env; for (ast_list_t *item = set->items; item; item = item->next) { code = Texts(code, ", ", compile_to_type(scope, item->ast, item_type)); @@ -2275,34 +2250,29 @@ Text_t compile_typed_set(env_t *env, ast_t *ast, type_t *set_type) return Texts(code, ")"); } - set_comprehension: - { - static int64_t comp_num = 1; - env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env); - const char *comprehension_name = String("set$", comp_num++); - ast_t *comprehension_var = LiteralCode(Texts("&", comprehension_name), - .type=Type(PointerType, .pointed=set_type, .is_stack=true)); - Text_t code = Texts("({ Table_t ", comprehension_name, " = {};"); - Closure_t comp_action = {.fn=add_to_set_comprehension, .userdata=comprehension_var}; - scope->comprehension_action = &comp_action; - for (ast_list_t *item = set->items; item; item = item->next) { - if (item->ast->tag == Comprehension) - code = Texts(code, "\n", compile_statement(scope, item->ast)); - else - code = Texts(code, compile_statement(env, add_to_set_comprehension(item->ast, comprehension_var))); - } - code = Texts(code, " ", comprehension_name, "; })"); - return code; +set_comprehension: { + static int64_t comp_num = 1; + env_t *scope = item_type->tag == EnumType ? with_enum_scope(env, item_type) : fresh_scope(env); + const char *comprehension_name = String("set$", comp_num++); + ast_t *comprehension_var = + LiteralCode(Texts("&", comprehension_name), .type = Type(PointerType, .pointed = set_type, .is_stack = true)); + Text_t code = Texts("({ Table_t ", comprehension_name, " = {};"); + Closure_t comp_action = {.fn = add_to_set_comprehension, .userdata = comprehension_var}; + scope->comprehension_action = &comp_action; + for (ast_list_t *item = set->items; item; item = item->next) { + if (item->ast->tag == Comprehension) code = Texts(code, "\n", compile_statement(scope, item->ast)); + else code = Texts(code, compile_statement(env, add_to_set_comprehension(item->ast, comprehension_var))); } + code = Texts(code, " ", comprehension_name, "; })"); + return code; +} } -Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type) -{ +Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type) { DeclareMatch(table, ast, Table); if (!table->entries) { Text_t code = Text("((Table_t){"); - if (table->fallback) - code = Texts(code, ".fallback=heap(", compile(env, table->fallback),")"); + if (table->fallback) code = Texts(code, ".fallback=heap(", compile(env, table->fallback), ")"); return Texts(code, "})"); } @@ -2313,22 +2283,16 @@ Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type) code_err(ast, "Tables whose values are optional (", type_to_str(value_t), ") are not currently supported."); for (ast_list_t *entry = table->entries; entry; entry = entry->next) { - if (entry->ast->tag == Comprehension) - goto table_comprehension; + if (entry->ast->tag == Comprehension) goto table_comprehension; } { // No comprehension: env_t *key_scope = key_t->tag == EnumType ? with_enum_scope(env, key_t) : env; env_t *value_scope = value_t->tag == EnumType ? with_enum_scope(env, value_t) : env; - Text_t code = Texts("Table(", - compile_type(key_t), ", ", - compile_type(value_t), ", ", - compile_type_info(key_t), ", ", - compile_type_info(value_t)); - if (table->fallback) - code = Texts(code, ", /*fallback:*/ heap(", compile(env, table->fallback), ")"); - else - code = Texts(code, ", /*fallback:*/ NULL"); + Text_t code = Texts("Table(", compile_type(key_t), ", ", compile_type(value_t), ", ", compile_type_info(key_t), + ", ", compile_type_info(value_t)); + if (table->fallback) code = Texts(code, ", /*fallback:*/ heap(", compile(env, table->fallback), ")"); + else code = Texts(code, ", /*fallback:*/ NULL"); size_t n = 0; for (ast_list_t *entry = table->entries; entry; entry = entry->next) @@ -2338,40 +2302,35 @@ Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type) for (ast_list_t *entry = table->entries; entry; entry = entry->next) { DeclareMatch(e, entry->ast, TableEntry); code = Texts(code, ",\n\t{", compile_to_type(key_scope, e->key, key_t), ", ", - compile_to_type(value_scope, e->value, value_t), "}"); + compile_to_type(value_scope, e->value, value_t), "}"); } return Texts(code, ")"); } - table_comprehension: - { - static int64_t comp_num = 1; - env_t *scope = fresh_scope(env); - const char *comprehension_name = String("table$", comp_num++); - ast_t *comprehension_var = LiteralCode(Texts("&", comprehension_name), - .type=Type(PointerType, .pointed=table_type, .is_stack=true)); +table_comprehension: { + static int64_t comp_num = 1; + env_t *scope = fresh_scope(env); + const char *comprehension_name = String("table$", comp_num++); + ast_t *comprehension_var = + LiteralCode(Texts("&", comprehension_name), .type = Type(PointerType, .pointed = table_type, .is_stack = true)); - Text_t code = Texts("({ Table_t ", comprehension_name, " = {"); - if (table->fallback) - code = Texts(code, ".fallback=heap(", compile(env, table->fallback), "), "); + Text_t code = Texts("({ Table_t ", comprehension_name, " = {"); + if (table->fallback) code = Texts(code, ".fallback=heap(", compile(env, table->fallback), "), "); - code = Texts(code, "};"); + code = Texts(code, "};"); - Closure_t comp_action = {.fn=add_to_table_comprehension, .userdata=comprehension_var}; - scope->comprehension_action = &comp_action; - for (ast_list_t *entry = table->entries; entry; entry = entry->next) { - if (entry->ast->tag == Comprehension) - code = Texts(code, "\n", compile_statement(scope, entry->ast)); - else - code = Texts(code, compile_statement(env, add_to_table_comprehension(entry->ast, comprehension_var))); - } - code = Texts(code, " ", comprehension_name, "; })"); - return code; + Closure_t comp_action = {.fn = add_to_table_comprehension, .userdata = comprehension_var}; + scope->comprehension_action = &comp_action; + for (ast_list_t *entry = table->entries; entry; entry = entry->next) { + if (entry->ast->tag == Comprehension) code = Texts(code, "\n", compile_statement(scope, entry->ast)); + else code = Texts(code, compile_statement(env, add_to_table_comprehension(entry->ast, comprehension_var))); } + code = Texts(code, " ", comprehension_name, "; })"); + return code; +} } -Text_t compile_typed_allocation(env_t *env, ast_t *ast, type_t *pointer_type) -{ +Text_t compile_typed_allocation(env_t *env, ast_t *ast, type_t *pointer_type) { // TODO: for constructors, do new(T, ...) instead of heap((T){...}) type_t *pointed = Match(pointer_type, PointerType)->pointed; switch (ast->tag) { @@ -2382,16 +2341,14 @@ Text_t compile_typed_allocation(env_t *env, ast_t *ast, type_t *pointer_type) ast_t *subject = Match(ast, StackReference)->value; if (can_be_mutated(env, subject) && type_eq(pointed, get_type(env, subject))) return Texts("(&", compile_lvalue(env, subject), ")"); - else - return Texts("stack(", compile_to_type(env, subject, pointed), ")"); + else return Texts("stack(", compile_to_type(env, subject, pointed), ")"); } default: code_err(ast, "Not an allocation!"); } return EMPTY_TEXT; } -Text_t compile_int_to_type(env_t *env, ast_t *ast, type_t *target) -{ +Text_t compile_int_to_type(env_t *env, ast_t *ast, type_t *target) { if (ast->tag != Int) { Text_t code = compile(env, ast); type_t *actual_type = get_type(env, ast); @@ -2400,16 +2357,14 @@ Text_t compile_int_to_type(env_t *env, ast_t *ast, type_t *target) return code; } - if (target->tag == BigIntType) - return compile(env, ast); + if (target->tag == BigIntType) return compile(env, ast); if (target->tag == OptionalType && Match(target, OptionalType)->type) return compile_int_to_type(env, ast, Match(target, OptionalType)->type); const char *literal = Match(ast, Int)->str; OptionalInt_t int_val = Int$from_str(literal); - if (int_val.small == 0) - code_err(ast, "Failed to parse this integer"); + if (int_val.small == 0) code_err(ast, "Failed to parse this integer"); mpz_t i; mpz_init_set_int(i, int_val); @@ -2424,8 +2379,7 @@ Text_t compile_int_to_type(env_t *env, ast_t *ast, type_t *target) } if (target->tag == ByteType) { - if (mpz_cmp_si(i, UINT8_MAX) <= 0 && mpz_cmp_si(i, 0) >= 0) - return Texts("(Byte_t)(", c_literal, ")"); + if (mpz_cmp_si(i, UINT8_MAX) <= 0 && mpz_cmp_si(i, 0) >= 0) return Texts("(Byte_t)(", c_literal, ")"); code_err(ast, "This integer cannot fit in a byte"); } else if (target->tag == NumType) { if (Match(target, NumType)->bits == TYPE_NBITS64) { @@ -2437,22 +2391,17 @@ Text_t compile_int_to_type(env_t *env, ast_t *ast, type_t *target) int64_t target_bits = (int64_t)Match(target, IntType)->bits; switch (target_bits) { case TYPE_IBITS64: - if (mpz_cmp_si(i, INT64_MIN) == 0) - return Text("I64(INT64_MIN)"); - if (mpz_cmp_si(i, INT64_MAX) <= 0 && mpz_cmp_si(i, INT64_MIN) >= 0) - return Texts("I64(", c_literal, "L)"); + if (mpz_cmp_si(i, INT64_MIN) == 0) return Text("I64(INT64_MIN)"); + if (mpz_cmp_si(i, INT64_MAX) <= 0 && mpz_cmp_si(i, INT64_MIN) >= 0) return Texts("I64(", c_literal, "L)"); break; case TYPE_IBITS32: - if (mpz_cmp_si(i, INT32_MAX) <= 0 && mpz_cmp_si(i, INT32_MIN) >= 0) - return Texts("I32(", c_literal, ")"); + if (mpz_cmp_si(i, INT32_MAX) <= 0 && mpz_cmp_si(i, INT32_MIN) >= 0) return Texts("I32(", c_literal, ")"); break; case TYPE_IBITS16: - if (mpz_cmp_si(i, INT16_MAX) <= 0 && mpz_cmp_si(i, INT16_MIN) >= 0) - return Texts("I16(", c_literal, ")"); + if (mpz_cmp_si(i, INT16_MAX) <= 0 && mpz_cmp_si(i, INT16_MIN) >= 0) return Texts("I16(", c_literal, ")"); break; case TYPE_IBITS8: - if (mpz_cmp_si(i, INT8_MAX) <= 0 && mpz_cmp_si(i, INT8_MIN) >= 0) - return Texts("I8(", c_literal, ")"); + if (mpz_cmp_si(i, INT8_MAX) <= 0 && mpz_cmp_si(i, INT8_MIN) >= 0) return Texts("I8(", c_literal, ")"); break; default: break; } @@ -2463,13 +2412,12 @@ Text_t compile_int_to_type(env_t *env, ast_t *ast, type_t *target) return EMPTY_TEXT; } -Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t *call_args) -{ +Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t *call_args) { Table_t used_args = {}; Text_t code = EMPTY_TEXT; - env_t *default_scope = new(env_t); + env_t *default_scope = new (env_t); *default_scope = *env; - default_scope->locals = new(Table_t, .fallback=env->namespace_bindings ? env->namespace_bindings : env->globals); + default_scope->locals = new (Table_t, .fallback = env->namespace_bindings ? env->namespace_bindings : env->globals); for (arg_t *spec_arg = spec_args; spec_arg; spec_arg = spec_arg->next) { int64_t i = 1; // Find keyword: @@ -2481,12 +2429,10 @@ Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_ value = compile_int_to_type(env, call_arg->value, spec_arg->type); } else if (spec_arg->type->tag == NumType && call_arg->value->tag == Int) { OptionalInt_t int_val = Int$from_str(Match(call_arg->value, Int)->str); - if (int_val.small == 0) - code_err(call_arg->value, "Failed to parse this integer"); + if (int_val.small == 0) code_err(call_arg->value, "Failed to parse this integer"); if (Match(spec_arg->type, NumType)->bits == TYPE_NBITS64) value = Text$from_str(String(hex_double(Num$from_int(int_val, false)))); - else - value = Text$from_str(String(hex_double((double)Num32$from_int(int_val, false)), "f")); + else value = Text$from_str(String(hex_double((double)Num32$from_int(int_val, false)), "f")); } else { env_t *arg_env = with_enum_scope(env, spec_arg->type); value = compile_maybe_incref(arg_env, call_arg->value, spec_arg->type); @@ -2508,12 +2454,10 @@ Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_ value = compile_int_to_type(env, call_arg->value, spec_arg->type); } else if (spec_arg->type->tag == NumType && call_arg->value->tag == Int) { OptionalInt_t int_val = Int$from_str(Match(call_arg->value, Int)->str); - if (int_val.small == 0) - code_err(call_arg->value, "Failed to parse this integer"); + if (int_val.small == 0) code_err(call_arg->value, "Failed to parse this integer"); if (Match(spec_arg->type, NumType)->bits == TYPE_NBITS64) value = Text$from_str(String(hex_double(Num$from_int(int_val, false)))); - else - value = Text$from_str(String(hex_double((double)Num32$from_int(int_val, false)), "f")); + else value = Text$from_str(String(hex_double((double)Num32$from_int(int_val, false)), "f")); } else { env_t *arg_env = with_enum_scope(env, spec_arg->type); value = compile_maybe_incref(arg_env, call_arg->value, spec_arg->type); @@ -2534,7 +2478,8 @@ Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_ assert(spec_arg->name); code_err(call_ast, "The required argument '", spec_arg->name, "' was not provided"); - found_it: continue; + found_it: + continue; } int64_t i = 1; @@ -2544,15 +2489,13 @@ Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_ code_err(call_arg->value, "There is no argument with the name '", call_arg->name, "'"); } else { const char *pseudoname = String(i++); - if (!Table$str_get(used_args, pseudoname)) - code_err(call_arg->value, "This is one argument too many!"); + if (!Table$str_get(used_args, pseudoname)) code_err(call_arg->value, "This is one argument too many!"); } } return code; } -Text_t compile_text_literal(Text_t literal) -{ +Text_t compile_text_literal(Text_t literal) { Text_t code = Text("\""); const char *utf8 = Text$as_c_string(literal); for (const char *p = utf8; *p; p++) { @@ -2569,8 +2512,8 @@ Text_t compile_text_literal(Text_t literal) if (isprint(*p)) { code = Texts(code, Text$from_strn(p, 1)); } else { - uint8_t byte = *(uint8_t*)p; - code = Texts(code, "\\x", String(hex(byte, .no_prefix=true, .uppercase=true, .digits=2)), "\"\""); + uint8_t byte = *(uint8_t *)p; + code = Texts(code, "\\x", String(hex(byte, .no_prefix = true, .uppercase = true, .digits = 2)), "\"\""); } break; } @@ -2579,27 +2522,21 @@ Text_t compile_text_literal(Text_t literal) return Texts(code, "\""); } -PUREFUNC static bool string_literal_is_all_ascii(Text_t literal) -{ +PUREFUNC static bool string_literal_is_all_ascii(Text_t literal) { TextIter_t state = NEW_TEXT_ITER_STATE(literal); for (int64_t i = 0; i < literal.length; i++) { int32_t g = Text$get_grapheme_fast(&state, i); - if (g < 0 || g > 127 || !isascii(g)) - return false; + if (g < 0 || g > 127 || !isascii(g)) return false; } return true; } -Text_t compile_none(type_t *t) -{ - if (t == NULL) - compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type"); +Text_t compile_none(type_t *t) { + if (t == NULL) compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type"); - if (t->tag == OptionalType) - t = Match(t, OptionalType)->type; + if (t->tag == OptionalType) t = Match(t, OptionalType)->type; - if (t == NULL) - compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type"); + if (t == NULL) compiler_err(NULL, NULL, NULL, "I can't compile a `none` value with no type"); if (t == PATH_TYPE) return Text("NONE_PATH"); else if (t == PATH_TYPE_TYPE) return Text("((OptionalPathType_t){})"); @@ -2626,7 +2563,7 @@ Text_t compile_none(type_t *t) case PointerType: return Texts("((", compile_type(t), ")NULL)"); case ClosureType: return Text("NONE_CLOSURE"); case NumType: return Text("nan(\"none\")"); - case StructType: return Texts("((", compile_type(Type(OptionalType, .type=t)), "){.is_none=true})"); + case StructType: return Texts("((", compile_type(Type(OptionalType, .type = t)), "){.is_none=true})"); case EnumType: { env_t *enum_env = Match(t, EnumType)->env; return Texts("((", compile_type(t), "){", namespace_name(enum_env, enum_env->namespace, Text("none")), "})"); @@ -2636,13 +2573,10 @@ Text_t compile_none(type_t *t) return EMPTY_TEXT; } -Text_t compile_empty(type_t *t) -{ - if (t == NULL) - compiler_err(NULL, NULL, NULL, "I can't compile a value with no type"); +Text_t compile_empty(type_t *t) { + if (t == NULL) compiler_err(NULL, NULL, NULL, "I can't compile a value with no type"); - if (t->tag == OptionalType) - return compile_none(t); + if (t->tag == OptionalType) return compile_none(t); if (t == PATH_TYPE) return Text("NONE_PATH"); else if (t == PATH_TYPE_TYPE) return Text("((OptionalPathType_t){})"); @@ -2662,13 +2596,15 @@ Text_t compile_empty(type_t *t) case ByteType: return Text("((Byte_t)0)"); case BoolType: return Text("((Bool_t)no)"); case ListType: return Text("((List_t){})"); - case TableType: case SetType: return Text("((Table_t){})"); + case TableType: + case SetType: return Text("((Table_t){})"); case TextType: return Text("Text(\"\")"); case CStringType: return Text("\"\""); case PointerType: { DeclareMatch(ptr, t, PointerType); Text_t empty_pointed = compile_empty(ptr->pointed); - return empty_pointed.length == 0 ? EMPTY_TEXT : Texts(ptr->is_stack ? Text("stack(") : Text("heap("), empty_pointed, ")"); + return empty_pointed.length == 0 ? EMPTY_TEXT + : Texts(ptr->is_stack ? Text("stack(") : Text("heap("), empty_pointed, ")"); } case NumType: { return Match(t, NumType)->bits == TYPE_NBITS32 ? Text("N32(0.0f)") : Text("N64(0.0)"); @@ -2677,15 +2613,12 @@ Text_t compile_empty(type_t *t) DeclareMatch(struct_, t, StructType); Text_t code = Texts("((", compile_type(t), "){"); for (arg_t *field = struct_->fields; field; field = field->next) { - Text_t empty_field = field->default_val - ? compile(struct_->env, field->default_val) - : compile_empty(field->type); - if (empty_field.length == 0) - return EMPTY_TEXT; + Text_t empty_field = + field->default_val ? compile(struct_->env, field->default_val) : compile_empty(field->type); + if (empty_field.length == 0) return EMPTY_TEXT; code = Texts(code, empty_field); - if (field->next) - code = Texts(code, ", "); + if (field->next) code = Texts(code, ", "); } return Texts(code, "})"); } @@ -2695,19 +2628,17 @@ Text_t compile_empty(type_t *t) assert(tag); assert(tag->type); if (Match(tag->type, StructType)->fields) - return Texts("((", compile_type(t), "){.$tag=", String(tag->tag_value), ", .", tag->name, "=", compile_empty(tag->type), "})"); - else if (enum_has_fields(t)) - return Texts("((", compile_type(t), "){.$tag=", String(tag->tag_value), "})"); - else - return Texts("((", compile_type(t), ")", String(tag->tag_value), ")"); + return Texts("((", compile_type(t), "){.$tag=", String(tag->tag_value), ", .", tag->name, "=", + compile_empty(tag->type), "})"); + else if (enum_has_fields(t)) return Texts("((", compile_type(t), "){.$tag=", String(tag->tag_value), "})"); + else return Texts("((", compile_type(t), ")", String(tag->tag_value), ")"); } default: return EMPTY_TEXT; } return EMPTY_TEXT; } -static Text_t compile_declared_value(env_t *env, ast_t *declare_ast) -{ +static Text_t compile_declared_value(env_t *env, ast_t *declare_ast) { DeclareMatch(decl, declare_ast, Declare); type_t *t = decl->type ? parse_type_ast(env, decl->type) : get_type(env, decl->value); @@ -2724,30 +2655,27 @@ static Text_t compile_declared_value(env_t *env, ast_t *declare_ast) } else { Text_t val_code = compile_empty(t); if (val_code.length == 0) - code_err(declare_ast, "This type (", type_to_str(t), ") cannot be uninitialized. You must provide a value."); + code_err(declare_ast, "This type (", type_to_str(t), + ") cannot be uninitialized. You must provide a value."); return val_code; } } -ast_t *add_to_table_comprehension(ast_t *entry, ast_t *subject) -{ +ast_t *add_to_table_comprehension(ast_t *entry, ast_t *subject) { DeclareMatch(e, entry, TableEntry); - return WrapAST(entry, MethodCall, .name="set", .self=subject, - .args=new(arg_ast_t, .value=e->key, .next=new(arg_ast_t, .value=e->value))); + return WrapAST(entry, MethodCall, .name = "set", .self = subject, + .args = new (arg_ast_t, .value = e->key, .next = new (arg_ast_t, .value = e->value))); } -ast_t *add_to_list_comprehension(ast_t *item, ast_t *subject) -{ - return WrapAST(item, MethodCall, .name="insert", .self=subject, .args=new(arg_ast_t, .value=item)); +ast_t *add_to_list_comprehension(ast_t *item, ast_t *subject) { + return WrapAST(item, MethodCall, .name = "insert", .self = subject, .args = new (arg_ast_t, .value = item)); } -ast_t *add_to_set_comprehension(ast_t *item, ast_t *subject) -{ - return WrapAST(item, MethodCall, .name="add", .self=subject, .args=new(arg_ast_t, .value=item)); +ast_t *add_to_set_comprehension(ast_t *item, ast_t *subject) { + return WrapAST(item, MethodCall, .name = "add", .self = subject, .args = new (arg_ast_t, .value = item)); } -Text_t compile(env_t *env, ast_t *ast) -{ +Text_t compile(env_t *env, ast_t *ast) { switch (ast->tag) { case None: { code_err(ast, "I can't figure out what this `none`'s type is!"); @@ -2755,16 +2683,14 @@ Text_t compile(env_t *env, ast_t *ast) case Bool: return Match(ast, Bool)->b ? Text("yes") : Text("no"); case Var: { binding_t *b = get_binding(env, Match(ast, Var)->name); - if (b) - return b->code.length > 0 ? b->code : Texts("_$", Match(ast, Var)->name); + if (b) return b->code.length > 0 ? b->code : Texts("_$", Match(ast, Var)->name); // return Texts("_$", Match(ast, Var)->name); code_err(ast, "I don't know of any variable by this name"); } case Int: { const char *str = Match(ast, Int)->str; OptionalInt_t int_val = Int$from_str(str); - if (int_val.small == 0) - code_err(ast, "Failed to parse this integer"); + if (int_val.small == 0) code_err(ast, "Failed to parse this integer"); mpz_t i; mpz_init_set_int(i, int_val); if (mpz_cmpabs_ui(i, BIGGEST_SMALL_INT) <= 0) { @@ -2786,21 +2712,16 @@ Text_t compile(env_t *env, ast_t *ast) if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); if (fn->args && can_compile_to_type(env, value, get_arg_type(env, fn->args))) - return Texts(b->code, "(", compile_arguments(env, ast, fn->args, new(arg_ast_t, .value=value)), ")"); + return Texts(b->code, "(", compile_arguments(env, ast, fn->args, new (arg_ast_t, .value = value)), ")"); } - if (t->tag == BoolType) - return Texts("!(", compile(env, value), ")"); - else if (t->tag == IntType || t->tag == ByteType) - return Texts("~(", compile(env, value), ")"); - else if (t->tag == ListType) - return Texts("((", compile(env, value), ").length == 0)"); + if (t->tag == BoolType) return Texts("!(", compile(env, value), ")"); + else if (t->tag == IntType || t->tag == ByteType) return Texts("~(", compile(env, value), ")"); + else if (t->tag == ListType) return Texts("((", compile(env, value), ").length == 0)"); else if (t->tag == SetType || t->tag == TableType) return Texts("((", compile(env, value), ").entries.length == 0)"); - else if (t->tag == TextType) - return Texts("(", compile(env, value), ".length == 0)"); - else if (t->tag == OptionalType) - return check_none(t, compile(env, value)); + else if (t->tag == TextType) return Texts("(", compile(env, value), ".length == 0)"); + else if (t->tag == OptionalType) return check_none(t, compile(env, value)); code_err(ast, "I don't know how to negate values of type ", type_to_str(t)); } @@ -2811,16 +2732,15 @@ Text_t compile(env_t *env, ast_t *ast) if (b && b->type->tag == FunctionType) { DeclareMatch(fn, b->type, FunctionType); if (fn->args && can_compile_to_type(env, value, get_arg_type(env, fn->args))) - return Texts(b->code, "(", compile_arguments(env, ast, fn->args, new(arg_ast_t, .value=value)), ")"); + return Texts(b->code, "(", compile_arguments(env, ast, fn->args, new (arg_ast_t, .value = value)), ")"); } - if (t->tag == IntType || t->tag == NumType) - return Texts("-(", compile(env, value), ")"); + if (t->tag == IntType || t->tag == NumType) return Texts("-(", compile(env, value), ")"); code_err(ast, "I don't know how to get the negative value of type ", type_to_str(t)); - } - case HeapAllocate: case StackReference: { + case HeapAllocate: + case StackReference: { return compile_typed_allocation(env, ast, get_type(env, ast)); } case Optional: { @@ -2833,20 +2753,32 @@ Text_t compile(env_t *env, ast_t *ast) type_t *t = get_type(env, value); Text_t value_code = compile(env, value); int64_t line = get_line_number(ast->file, ast->start); - return Texts("({ ", compile_declaration(t, Text("opt")), " = ", value_code, "; ", - "if unlikely (", check_none(t, Text("opt")), ")\n", - "#line ", String(line), "\n", - "fail_source(", quoted_str(ast->file->filename), ", ", - String((int64_t)(value->start - value->file->text)), ", ", - String((int64_t)(value->end - value->file->text)), ", ", - "\"This was expected to be a value, but it's none\");\n", - optional_into_nonnone(t, Text("opt")), "; })"); - } - case Power: case Multiply: case Divide: case Mod: case Mod1: case Plus: case Minus: case Concat: - case LeftShift: case UnsignedLeftShift: case RightShift: case UnsignedRightShift: case And: case Or: case Xor: { + return Texts("({ ", compile_declaration(t, Text("opt")), " = ", value_code, "; ", "if unlikely (", + check_none(t, Text("opt")), ")\n", "#line ", String(line), "\n", "fail_source(", + quoted_str(ast->file->filename), ", ", String((int64_t)(value->start - value->file->text)), ", ", + String((int64_t)(value->end - value->file->text)), ", ", + "\"This was expected to be a value, but it's none\");\n", optional_into_nonnone(t, Text("opt")), + "; })"); + } + case Power: + case Multiply: + case Divide: + case Mod: + case Mod1: + case Plus: + case Minus: + case Concat: + case LeftShift: + case UnsignedLeftShift: + case RightShift: + case UnsignedRightShift: + case And: + case Or: + case Xor: { return compile_binary_op(env, ast); } - case Equals: case NotEquals: { + case Equals: + case NotEquals: { binary_operands_t binop = BINARY_OPERANDS(ast); type_t *lhs_t = get_type(env, binop.lhs); @@ -2871,14 +2803,22 @@ Text_t compile(env_t *env, ast_t *ast) switch (operand_t->tag) { case BigIntType: return Texts(ast->tag == Equals ? EMPTY_TEXT : Text("!"), "Int$equal_value(", lhs, ", ", rhs, ")"); - case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType: - return Texts("(", lhs, ast->tag == Equals ? " == " : " != ", rhs, ")"); + case BoolType: + case ByteType: + case IntType: + case NumType: + case PointerType: + case FunctionType: return Texts("(", lhs, ast->tag == Equals ? " == " : " != ", rhs, ")"); default: - return Texts(ast->tag == Equals ? EMPTY_TEXT : Text("!"), - "generic_equal(stack(", lhs, "), stack(", rhs, "), ", compile_type_info(operand_t), ")"); + return Texts(ast->tag == Equals ? EMPTY_TEXT : Text("!"), "generic_equal(stack(", lhs, "), stack(", rhs, + "), ", compile_type_info(operand_t), ")"); } } - case LessThan: case LessThanOrEquals: case GreaterThan: case GreaterThanOrEquals: case Compare: { + case LessThan: + case LessThanOrEquals: + case GreaterThan: + case GreaterThanOrEquals: + case Compare: { binary_operands_t cmp = BINARY_OPERANDS(ast); type_t *lhs_t = get_type(env, cmp.lhs); @@ -2900,49 +2840,47 @@ Text_t compile(env_t *env, ast_t *ast) Text_t rhs = compile_to_type(env, cmp.rhs, operand_t); if (ast->tag == Compare) - return Texts("generic_compare(stack(", lhs, "), stack(", rhs, "), ", - compile_type_info(operand_t), ")"); + return Texts("generic_compare(stack(", lhs, "), stack(", rhs, "), ", compile_type_info(operand_t), ")"); const char *op = binop_operator(ast->tag); switch (operand_t->tag) { - case BigIntType: - return Texts("(Int$compare_value(", lhs, ", ", rhs, ") ", op, " 0)"); - case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType: - return Texts("(", lhs, " ", op, " ", rhs, ")"); + case BigIntType: return Texts("(Int$compare_value(", lhs, ", ", rhs, ") ", op, " 0)"); + case BoolType: + case ByteType: + case IntType: + case NumType: + case PointerType: + case FunctionType: return Texts("(", lhs, " ", op, " ", rhs, ")"); default: - return Texts("(generic_compare(stack(", lhs, "), stack(", rhs, "), ", - compile_type_info(operand_t), ") ", op, " 0)"); + return Texts("(generic_compare(stack(", lhs, "), stack(", rhs, "), ", compile_type_info(operand_t), ") ", + op, " 0)"); } } case TextLiteral: { - Text_t literal = Match(ast, TextLiteral)->text; - if (literal.length == 0) - return Text("EMPTY_TEXT"); + Text_t literal = Match(ast, TextLiteral)->text; + if (literal.length == 0) return Text("EMPTY_TEXT"); - if (string_literal_is_all_ascii(literal)) - return Texts("Text(", compile_text_literal(literal), ")"); - else - return Texts("Text$from_str(", compile_text_literal(literal), ")"); + if (string_literal_is_all_ascii(literal)) return Texts("Text(", compile_text_literal(literal), ")"); + else return Texts("Text$from_str(", compile_text_literal(literal), ")"); } case TextJoin: { const char *lang = Match(ast, TextJoin)->lang; Text_t colorize = Match(ast, TextJoin)->colorize ? Text("yes") : Text("no"); type_t *text_t = lang ? Table$str_get(*env->types, lang) : TEXT_TYPE; - if (!text_t || text_t->tag != TextType) - code_err(ast, quoted(lang), " is not a valid text language name"); + if (!text_t || text_t->tag != TextType) code_err(ast, quoted(lang), " is not a valid text language name"); Text_t lang_constructor; - if (!lang || streq(lang, "Text")) - lang_constructor = Text("Text"); + if (!lang || streq(lang, "Text")) lang_constructor = Text("Text"); else - lang_constructor = namespace_name(Match(text_t, TextType)->env, Match(text_t, TextType)->env->namespace->parent, Text$from_str(lang)); + lang_constructor = namespace_name(Match(text_t, TextType)->env, + Match(text_t, TextType)->env->namespace->parent, Text$from_str(lang)); ast_list_t *chunks = Match(ast, TextJoin)->children; if (!chunks) { return Texts(lang_constructor, "(\"\")"); } else if (!chunks->next && chunks->ast->tag == TextLiteral) { - Text_t literal = Match(chunks->ast, TextLiteral)->text; + Text_t literal = Match(chunks->ast, TextLiteral)->text; if (string_literal_is_all_ascii(literal)) return Texts(lang_constructor, "(", compile_text_literal(literal), ")"); return Texts("((", compile_type(text_t), ")", compile(env, chunks->ast), ")"); @@ -2954,28 +2892,26 @@ Text_t compile(env_t *env, ast_t *ast) if (chunk->ast->tag == TextLiteral || type_eq(chunk_t, text_t)) { chunk_code = compile(env, chunk->ast); } else { - binding_t *constructor = get_constructor(env, text_t, new(arg_ast_t, .value=chunk->ast), - env->current_type != NULL && type_eq(env->current_type, text_t)); + binding_t *constructor = + get_constructor(env, text_t, new (arg_ast_t, .value = chunk->ast), + env->current_type != NULL && type_eq(env->current_type, text_t)); if (constructor) { arg_t *arg_spec = Match(constructor->type, FunctionType)->args; - arg_ast_t *args = new(arg_ast_t, .value=chunk->ast); + arg_ast_t *args = new (arg_ast_t, .value = chunk->ast); chunk_code = Texts(constructor->code, "(", compile_arguments(env, ast, arg_spec, args), ")"); } else if (type_eq(text_t, TEXT_TYPE)) { - if (chunk_t->tag == TextType) - chunk_code = compile(env, chunk->ast); - else - chunk_code = compile_text(env, chunk->ast, colorize); + if (chunk_t->tag == TextType) chunk_code = compile(env, chunk->ast); + else chunk_code = compile_text(env, chunk->ast, colorize); } else { - code_err(chunk->ast, "I don't know how to convert ", type_to_str(chunk_t), " to ", type_to_str(text_t)); + code_err(chunk->ast, "I don't know how to convert ", type_to_str(chunk_t), " to ", + type_to_str(text_t)); } } code = Texts(code, chunk_code); if (chunk->next) code = Texts(code, ", "); } - if (chunks->next) - return Texts(lang_constructor, "s(", code, ")"); - else - return code; + if (chunks->next) return Texts(lang_constructor, "s(", code, ")"); + else return code; } } case Path: { @@ -2983,8 +2919,7 @@ Text_t compile(env_t *env, ast_t *ast) } case Block: { ast_list_t *stmts = Match(ast, Block)->statements; - if (stmts && !stmts->next) - return compile(env, stmts->ast); + if (stmts && !stmts->next) return compile(env, stmts->ast); Text_t code = Text("({\n"); deferral_t *prev_deferred = env->deferred; @@ -2996,7 +2931,8 @@ Text_t compile(env_t *env, ast_t *ast) code = Texts(code, compile_statement(env, stmt->ast), "\n"); } else { // TODO: put defer after evaluating block expression - for (deferral_t *deferred = env->deferred; deferred && deferred != prev_deferred; deferred = deferred->next) { + for (deferral_t *deferred = env->deferred; deferred && deferred != prev_deferred; + deferred = deferred->next) { code = Texts(code, compile_statement(deferred->defer_env, deferred->block)); } code = Texts(code, compile(env, stmt->ast), ";\n"); @@ -3006,7 +2942,8 @@ Text_t compile(env_t *env, ast_t *ast) return Texts(code, "})"); } - case Min: case Max: { + case Min: + case Max: { type_t *t = get_type(env, ast); ast_t *key = ast->tag == Min ? Match(ast, Min)->key : Match(ast, Max)->key; ast_t *lhs = ast->tag == Min ? Match(ast, Min)->lhs : Match(ast, Max)->lhs; @@ -3024,23 +2961,23 @@ Text_t compile(env_t *env, ast_t *ast) type_t *key_t = get_type(expr_env, key); Text_t comparison; if (key_t->tag == BigIntType) - comparison = Texts("(Int$compare_value(", lhs_key, ", ", rhs_key, ")", (ast->tag == Min ? "<=" : ">="), "0)"); - else if (key_t->tag == IntType || key_t->tag == NumType || key_t->tag == BoolType || key_t->tag == PointerType || key_t->tag == ByteType) + comparison = + Texts("(Int$compare_value(", lhs_key, ", ", rhs_key, ")", (ast->tag == Min ? "<=" : ">="), "0)"); + else if (key_t->tag == IntType || key_t->tag == NumType || key_t->tag == BoolType || key_t->tag == PointerType + || key_t->tag == ByteType) comparison = Texts("((", lhs_key, ")", (ast->tag == Min ? "<=" : ">="), "(", rhs_key, "))"); else - comparison = Texts("generic_compare(stack(", lhs_key, "), stack(", rhs_key, "), ", compile_type_info(key_t), ")", - (ast->tag == Min ? "<=" : ">="), "0"); + comparison = Texts("generic_compare(stack(", lhs_key, "), stack(", rhs_key, "), ", compile_type_info(key_t), + ")", (ast->tag == Min ? "<=" : ">="), "0"); - return Texts( - "({\n", - compile_type(t), " ternary$lhs = ", compile(env, lhs), ", ternary$rhs = ", compile(env, rhs), ";\n", - comparison, " ? ternary$lhs : ternary$rhs;\n" - "})"); + return Texts("({\n", compile_type(t), " ternary$lhs = ", compile(env, lhs), + ", ternary$rhs = ", compile(env, rhs), ";\n", comparison, + " ? ternary$lhs : ternary$rhs;\n" + "})"); } case List: { DeclareMatch(list, ast, List); - if (!list->items) - return Text("(List_t){.length=0}"); + if (!list->items) return Text("(List_t){.length=0}"); type_t *list_type = get_type(env, ast); return compile_typed_list(env, ast, list_type); @@ -3049,8 +2986,7 @@ Text_t compile(env_t *env, ast_t *ast) DeclareMatch(table, ast, Table); if (!table->entries) { Text_t code = Text("((Table_t){"); - if (table->fallback) - code = Texts(code, ".fallback=heap(", compile(env, table->fallback),")"); + if (table->fallback) code = Texts(code, ".fallback=heap(", compile(env, table->fallback), ")"); return Texts(code, "})"); } @@ -3059,8 +2995,7 @@ Text_t compile(env_t *env, ast_t *ast) } case Set: { DeclareMatch(set, ast, Set); - if (!set->items) - return Text("((Table_t){})"); + if (!set->items) return Text("((Table_t){})"); type_t *set_type = get_type(env, ast); return compile_typed_set(env, ast, set_type); @@ -3069,10 +3004,8 @@ Text_t compile(env_t *env, ast_t *ast) ast_t *base = Match(ast, Comprehension)->expr; while (base->tag == Comprehension) base = Match(ast, Comprehension)->expr; - if (base->tag == TableEntry) - return compile(env, WrapAST(ast, Table, .entries=new(ast_list_t, .ast=ast))); - else - return compile(env, WrapAST(ast, List, .items=new(ast_list_t, .ast=ast))); + if (base->tag == TableEntry) return compile(env, WrapAST(ast, Table, .entries = new (ast_list_t, .ast = ast))); + else return compile(env, WrapAST(ast, List, .items = new (ast_list_t, .ast = ast))); } case Lambda: { DeclareMatch(lambda, ast, Lambda); @@ -3086,13 +3019,11 @@ Text_t compile(env_t *env, ast_t *ast) } type_t *ret_t = get_type(body_scope, lambda->body); - if (ret_t->tag == ReturnType) - ret_t = Match(ret_t, ReturnType)->ret; + if (ret_t->tag == ReturnType) ret_t = Match(ret_t, ReturnType)->ret; if (lambda->ret_type) { type_t *declared = parse_type_ast(env, lambda->ret_type); - if (can_promote(ret_t, declared)) - ret_t = declared; + if (can_promote(ret_t, declared)) ret_t = declared; else code_err(ast, "This function was declared to return a value of type ", type_to_str(declared), ", but actually returns a value of type ", type_to_str(ret_t)); @@ -3104,12 +3035,15 @@ Text_t compile(env_t *env, ast_t *ast) if (Table$length(closed_vars) > 0) { // Create a typedef for the lambda's closure userdata Text_t def = Text("typedef struct {"); for (int64_t i = 0; i < closed_vars.entries.length; i++) { - struct { const char *name; binding_t *b; } *entry = closed_vars.entries.data + closed_vars.entries.stride*i; + struct { + const char *name; + binding_t *b; + } *entry = closed_vars.entries.data + closed_vars.entries.stride * i; if (has_stack_memory(entry->b->type)) code_err(ast, "This function is holding onto a reference to ", type_to_str(entry->b->type), - " stack memory in the variable `", entry->name, "`, but the function may outlive the stack memory"); - if (entry->b->type->tag == ModuleType) - continue; + " stack memory in the variable `", entry->name, + "`, but the function may outlive the stack memory"); + if (entry->b->type->tag == ModuleType) continue; set_binding(body_scope, entry->name, entry->b->type, Texts("userdata->", entry->name)); def = Texts(def, compile_declaration(entry->b->type, Text$from_str(entry->name)), "; "); } @@ -3130,18 +3064,18 @@ Text_t compile(env_t *env, ast_t *ast) } else { userdata = Texts("new(", name, "$userdata_t"); for (int64_t i = 0; i < closed_vars.entries.length; i++) { - struct { const char *name; binding_t *b; } *entry = closed_vars.entries.data + closed_vars.entries.stride*i; - if (entry->b->type->tag == ModuleType) - continue; + struct { + const char *name; + binding_t *b; + } *entry = closed_vars.entries.data + closed_vars.entries.stride * i; + if (entry->b->type->tag == ModuleType) continue; binding_t *b = get_binding(env, entry->name); assert(b); Text_t binding_code = b->code; - if (entry->b->type->tag == ListType) - userdata = Texts(userdata, ", LIST_COPY(", binding_code, ")"); + if (entry->b->type->tag == ListType) userdata = Texts(userdata, ", LIST_COPY(", binding_code, ")"); else if (entry->b->type->tag == TableType || entry->b->type->tag == SetType) userdata = Texts(userdata, ", TABLE_COPY(", binding_code, ")"); - else - userdata = Texts(userdata, ", ", binding_code); + else userdata = Texts(userdata, ", ", binding_code); } userdata = Texts(userdata, ")"); code = Texts(code, name, "$userdata_t *userdata)"); @@ -3149,10 +3083,10 @@ Text_t compile(env_t *env, ast_t *ast) Text_t body = EMPTY_TEXT; for (ast_list_t *stmt = Match(lambda->body, Block)->statements; stmt; stmt = stmt->next) { - if (stmt->next || ret_t->tag == VoidType || ret_t->tag == AbortType || get_type(body_scope, stmt->ast)->tag == ReturnType) + if (stmt->next || ret_t->tag == VoidType || ret_t->tag == AbortType + || get_type(body_scope, stmt->ast)->tag == ReturnType) body = Texts(body, compile_statement(body_scope, stmt->ast), "\n"); - else - body = Texts(body, compile_statement(body_scope, FakeAST(Return, stmt->ast)), "\n"); + else body = Texts(body, compile_statement(body_scope, FakeAST(Return, stmt->ast)), "\n"); bind_statement(body_scope, stmt->ast); } if ((ret_t->tag == VoidType || ret_t->tag == AbortType) && body_scope->deferred) @@ -3166,10 +3100,9 @@ Text_t compile(env_t *env, ast_t *ast) type_t *self_t = get_type(env, call->self); if (streq(call->name, "serialized")) { - if (call->args) - code_err(ast, ".serialized() doesn't take any arguments"); + if (call->args) code_err(ast, ".serialized() doesn't take any arguments"); return Texts("generic_serialize((", compile_declaration(self_t, Text("[1]")), "){", - compile(env, call->self), "}, ", compile_type_info(self_t), ")"); + compile(env, call->self), "}, ", compile_type_info(self_t), ")"); } int64_t pointer_depth = 0; @@ -3178,23 +3111,29 @@ Text_t compile(env_t *env, ast_t *ast) pointer_depth += 1; if (self_value_t->tag == TypeInfoType || self_value_t->tag == ModuleType) { - return compile(env, WrapAST(ast, FunctionCall, .fn=WrapAST(call->self, FieldAccess, .fielded=call->self, .field=call->name), - .args=call->args)); + return compile(env, + WrapAST(ast, FunctionCall, + .fn = WrapAST(call->self, FieldAccess, .fielded = call->self, .field = call->name), + .args = call->args)); } type_t *field_type = get_field_type(self_value_t, call->name); - if (field_type && field_type->tag == ClosureType) - field_type = Match(field_type, ClosureType)->fn; + if (field_type && field_type->tag == ClosureType) field_type = Match(field_type, ClosureType)->fn; if (field_type && field_type->tag == FunctionType) - return compile(env, WrapAST(ast, FunctionCall, .fn=WrapAST(call->self, FieldAccess, .fielded=call->self, .field=call->name), - .args=call->args)); + return compile(env, + WrapAST(ast, FunctionCall, + .fn = WrapAST(call->self, FieldAccess, .fielded = call->self, .field = call->name), + .args = call->args)); Text_t self = compile(env, call->self); -#define EXPECT_POINTER(article, name) do { \ - if (pointer_depth < 1) code_err(call->self, "I expected "article" "name" pointer here, not "article" "name" value"); \ - else if (pointer_depth > 1) code_err(call->self, "I expected "article" "name" pointer here, not a nested "name" pointer"); \ -} while (0) +#define EXPECT_POINTER(article, name) \ + do { \ + if (pointer_depth < 1) \ + code_err(call->self, "I expected " article " " name " pointer here, not " article " " name " value"); \ + else if (pointer_depth > 1) \ + code_err(call->self, "I expected " article " " name " pointer here, not a nested " name " pointer"); \ + } while (0) switch (self_value_t->tag) { case ListType: { type_t *item_t = Match(self_value_t, ListType)->item_type; @@ -3202,116 +3141,141 @@ Text_t compile(env_t *env, ast_t *ast) if (streq(call->name, "insert")) { EXPECT_POINTER("a", "list"); - arg_t *arg_spec = new(arg_t, .name="item", .type=item_t, - .next=new(arg_t, .name="at", .type=INT_TYPE, .default_val=FakeAST(Int, .str="0"))); + arg_t *arg_spec = + new (arg_t, .name = "item", .type = item_t, + .next = new (arg_t, .name = "at", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "0"))); return Texts("List$insert_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - padded_item_size, ")"); + padded_item_size, ")"); } else if (streq(call->name, "insert_all")) { EXPECT_POINTER("a", "list"); - arg_t *arg_spec = new(arg_t, .name="items", .type=self_value_t, - .next=new(arg_t, .name="at", .type=INT_TYPE, .default_val=FakeAST(Int, .str="0"))); + arg_t *arg_spec = + new (arg_t, .name = "items", .type = self_value_t, + .next = new (arg_t, .name = "at", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "0"))); return Texts("List$insert_all(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - padded_item_size, ")"); + padded_item_size, ")"); } else if (streq(call->name, "remove_at")) { EXPECT_POINTER("a", "list"); - arg_t *arg_spec = new(arg_t, .name="index", .type=INT_TYPE, .default_val=FakeAST(Int, .str="-1"), - .next=new(arg_t, .name="count", .type=INT_TYPE, .default_val=FakeAST(Int, .str="1"))); + arg_t *arg_spec = new ( + arg_t, .name = "index", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "-1"), + .next = new (arg_t, .name = "count", .type = INT_TYPE, .default_val = FakeAST(Int, .str = "1"))); return Texts("List$remove_at(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - padded_item_size, ")"); + padded_item_size, ")"); } else if (streq(call->name, "remove_item")) { EXPECT_POINTER("a", "list"); - arg_t *arg_spec = new(arg_t, .name="item", .type=item_t, - .next=new(arg_t, .name="max_count", .type=INT_TYPE, .default_val=FakeAST(Int, .str="-1"))); - return Texts("List$remove_item_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new (arg_t, .name = "item", .type = item_t, + .next = new (arg_t, .name = "max_count", .type = INT_TYPE, + .default_val = FakeAST(Int, .str = "-1"))); + return Texts("List$remove_item_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), + ", ", compile_type_info(self_value_t), ")"); } else if (streq(call->name, "has")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="item", .type=item_t); + arg_t *arg_spec = new (arg_t, .name = "item", .type = item_t); return Texts("List$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - compile_type_info(self_value_t), ")"); + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "sample")) { type_t *random_num_type = parse_type_string(env, "func(->Num)?"); self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="count", .type=INT_TYPE, - .next=new(arg_t, .name="weights", .type=Type(ListType, .item_type=Type(NumType, .bits=TYPE_NBITS64)), - .default_val=FakeAST(None), - .next=new(arg_t, .name="random", .type=random_num_type, .default_val=FakeAST(None)))); + arg_t *arg_spec = + new (arg_t, .name = "count", .type = INT_TYPE, + .next = new (arg_t, .name = "weights", + .type = Type(ListType, .item_type = Type(NumType, .bits = TYPE_NBITS64)), + .default_val = FakeAST(None), + .next = new (arg_t, .name = "random", .type = random_num_type, + .default_val = FakeAST(None)))); return Texts("List$sample(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - padded_item_size, ")"); + padded_item_size, ")"); } else if (streq(call->name, "shuffle")) { type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?"); EXPECT_POINTER("a", "list"); - arg_t *arg_spec = new(arg_t, .name="random", .type=random_int64_type, .default_val=FakeAST(None)); - return Texts("List$shuffle(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); + arg_t *arg_spec = + new (arg_t, .name = "random", .type = random_int64_type, .default_val = FakeAST(None)); + return Texts("List$shuffle(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + padded_item_size, ")"); } else if (streq(call->name, "shuffled")) { type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?"); self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="random", .type=random_int64_type, .default_val=FakeAST(None)); - return Texts("List$shuffled(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); + arg_t *arg_spec = + new (arg_t, .name = "random", .type = random_int64_type, .default_val = FakeAST(None)); + return Texts("List$shuffled(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + padded_item_size, ")"); } else if (streq(call->name, "random")) { type_t *random_int64_type = parse_type_string(env, "func(min,max:Int64->Int64)?"); self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="random", .type=random_int64_type, .default_val=FakeAST(None)); - return Texts("List$random_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", compile_type(item_t), ")"); + arg_t *arg_spec = + new (arg_t, .name = "random", .type = random_int64_type, .default_val = FakeAST(None)); + return Texts("List$random_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + compile_type(item_t), ")"); } else if (streq(call->name, "sort") || streq(call->name, "sorted")) { - if (streq(call->name, "sort")) - EXPECT_POINTER("a", "list"); - else - self = compile_to_pointer_depth(env, call->self, 0, false); + if (streq(call->name, "sort")) EXPECT_POINTER("a", "list"); + else self = compile_to_pointer_depth(env, call->self, 0, false); Text_t comparison; if (call->args) { - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); - type_t *fn_t = NewFunctionType(Type(IntType, .bits=TYPE_IBITS32), {.name="x", .type=item_ptr}, {.name="y", .type=item_ptr}); - arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t)); + type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true); + type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr}, + {.name = "y", .type = item_ptr}); + arg_t *arg_spec = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t)); comparison = compile_arguments(env, ast, arg_spec, call->args); } else { - comparison = Texts("((Closure_t){.fn=generic_compare, .userdata=(void*)", compile_type_info(item_t), "})"); + comparison = Texts("((Closure_t){.fn=generic_compare, " + ".userdata=(void*)", + compile_type_info(item_t), "})"); } return Texts("List$", call->name, "(", self, ", ", comparison, ", ", padded_item_size, ")"); } else if (streq(call->name, "heapify")) { EXPECT_POINTER("a", "list"); Text_t comparison; if (call->args) { - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); - type_t *fn_t = NewFunctionType(Type(IntType, .bits=TYPE_IBITS32), {.name="x", .type=item_ptr}, {.name="y", .type=item_ptr}); - arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t)); + type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true); + type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr}, + {.name = "y", .type = item_ptr}); + arg_t *arg_spec = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t)); comparison = compile_arguments(env, ast, arg_spec, call->args); } else { - comparison = Texts("((Closure_t){.fn=generic_compare, .userdata=(void*)", compile_type_info(item_t), "})"); + comparison = Texts("((Closure_t){.fn=generic_compare, " + ".userdata=(void*)", + compile_type_info(item_t), "})"); } return Texts("List$heapify(", self, ", ", comparison, ", ", padded_item_size, ")"); } else if (streq(call->name, "heap_push")) { EXPECT_POINTER("a", "list"); - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); - type_t *fn_t = NewFunctionType(Type(IntType, .bits=TYPE_IBITS32), {.name="x", .type=item_ptr}, {.name="y", .type=item_ptr}); - ast_t *default_cmp = LiteralCode(Texts("((Closure_t){.fn=generic_compare, .userdata=(void*)", - compile_type_info(item_t), "})"), - .type=Type(ClosureType, .fn=fn_t)); - arg_t *arg_spec = new(arg_t, .name="item", .type=item_t, - .next=new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t), .default_val=default_cmp)); + type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true); + type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr}, + {.name = "y", .type = item_ptr}); + ast_t *default_cmp = LiteralCode(Texts("((Closure_t){.fn=generic_compare, " + ".userdata=(void*)", + compile_type_info(item_t), "})"), + .type = Type(ClosureType, .fn = fn_t)); + arg_t *arg_spec = new (arg_t, .name = "item", .type = item_t, + .next = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t), + .default_val = default_cmp)); Text_t arg_code = compile_arguments(env, ast, arg_spec, call->args); return Texts("List$heap_push_value(", self, ", ", arg_code, ", ", padded_item_size, ")"); } else if (streq(call->name, "heap_pop")) { EXPECT_POINTER("a", "list"); - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); - type_t *fn_t = NewFunctionType(Type(IntType, .bits=TYPE_IBITS32), {.name="x", .type=item_ptr}, {.name="y", .type=item_ptr}); - ast_t *default_cmp = LiteralCode(Texts("((Closure_t){.fn=generic_compare, .userdata=(void*)", - compile_type_info(item_t), "})"), - .type=Type(ClosureType, .fn=fn_t)); - arg_t *arg_spec = new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t), .default_val=default_cmp); + type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true); + type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr}, + {.name = "y", .type = item_ptr}); + ast_t *default_cmp = LiteralCode(Texts("((Closure_t){.fn=generic_compare, " + ".userdata=(void*)", + compile_type_info(item_t), "})"), + .type = Type(ClosureType, .fn = fn_t)); + arg_t *arg_spec = + new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t), .default_val = default_cmp); Text_t arg_code = compile_arguments(env, ast, arg_spec, call->args); return Texts("List$heap_pop_value(", self, ", ", arg_code, ", ", compile_type(item_t), ", _, ", - promote_to_optional(item_t, Text("_")), ", ", compile_none(item_t), ")"); + promote_to_optional(item_t, Text("_")), ", ", compile_none(item_t), ")"); } else if (streq(call->name, "binary_search")) { self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL); - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); - type_t *fn_t = NewFunctionType(Type(IntType, .bits=TYPE_IBITS32), {.name="x", .type=item_ptr}, {.name="y", .type=item_ptr}); - ast_t *default_cmp = LiteralCode( - Texts("((Closure_t){.fn=generic_compare, .userdata=(void*)", - compile_type_info(item_t), "})"), - .type=Type(ClosureType, .fn=fn_t)); - arg_t *arg_spec = new(arg_t, .name="target", .type=item_t, - .next=new(arg_t, .name="by", .type=Type(ClosureType, .fn=fn_t), .default_val=default_cmp)); + type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true); + type_t *fn_t = NewFunctionType(Type(IntType, .bits = TYPE_IBITS32), {.name = "x", .type = item_ptr}, + {.name = "y", .type = item_ptr}); + ast_t *default_cmp = LiteralCode(Texts("((Closure_t){.fn=generic_compare, " + ".userdata=(void*)", + compile_type_info(item_t), "})"), + .type = Type(ClosureType, .fn = fn_t)); + arg_t *arg_spec = new (arg_t, .name = "target", .type = item_t, + .next = new (arg_t, .name = "by", .type = Type(ClosureType, .fn = fn_t), + .default_val = default_cmp)); Text_t arg_code = compile_arguments(env, ast, arg_spec, call->args); return Texts("List$binary_search_value(", self, ", ", arg_code, ")"); } else if (streq(call->name, "clear")) { @@ -3320,32 +3284,34 @@ Text_t compile(env_t *env, ast_t *ast) return Texts("List$clear(", self, ")"); } else if (streq(call->name, "find")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="item", .type=item_t); - return Texts("List$find_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), - ", ", compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new (arg_t, .name = "item", .type = item_t); + return Texts("List$find_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "where")) { self = compile_to_pointer_depth(env, call->self, 0, call->args != NULL); - type_t *item_ptr = Type(PointerType, .pointed=item_t, .is_stack=true); - type_t *predicate_type = Type( - ClosureType, .fn=NewFunctionType(Type(BoolType), {.name="item", .type=item_ptr})); - arg_t *arg_spec = new(arg_t, .name="predicate", .type=predicate_type); + type_t *item_ptr = Type(PointerType, .pointed = item_t, .is_stack = true); + type_t *predicate_type = + Type(ClosureType, .fn = NewFunctionType(Type(BoolType), {.name = "item", .type = item_ptr})); + arg_t *arg_spec = new (arg_t, .name = "predicate", .type = predicate_type); return Texts("List$first(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); } else if (streq(call->name, "from")) { self = compile_to_pointer_depth(env, call->self, 0, true); - arg_t *arg_spec = new(arg_t, .name="first", .type=INT_TYPE); + arg_t *arg_spec = new (arg_t, .name = "first", .type = INT_TYPE); return Texts("List$from(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); } else if (streq(call->name, "to")) { self = compile_to_pointer_depth(env, call->self, 0, true); - arg_t *arg_spec = new(arg_t, .name="last", .type=INT_TYPE); + arg_t *arg_spec = new (arg_t, .name = "last", .type = INT_TYPE); return Texts("List$to(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); } else if (streq(call->name, "slice")) { self = compile_to_pointer_depth(env, call->self, 0, true); - arg_t *arg_spec = new(arg_t, .name="first", .type=INT_TYPE, .next=new(arg_t, .name="last", .type=INT_TYPE)); + arg_t *arg_spec = new (arg_t, .name = "first", .type = INT_TYPE, + .next = new (arg_t, .name = "last", .type = INT_TYPE)); return Texts("List$slice(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); } else if (streq(call->name, "by")) { self = compile_to_pointer_depth(env, call->self, 0, true); - arg_t *arg_spec = new(arg_t, .name="stride", .type=INT_TYPE); - return Texts("List$by(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", padded_item_size, ")"); + arg_t *arg_spec = new (arg_t, .name = "stride", .type = INT_TYPE); + return Texts("List$by(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + padded_item_size, ")"); } else if (streq(call->name, "reversed")) { self = compile_to_pointer_depth(env, call->self, 0, true); (void)compile_arguments(env, ast, NULL, call->args); @@ -3356,7 +3322,7 @@ Text_t compile(env_t *env, ast_t *ast) return Texts("Table$from_entries(", self, ", Set$info(", compile_type_info(item_t), "))"); } else if (streq(call->name, "pop")) { EXPECT_POINTER("a", "list"); - arg_t *arg_spec = new(arg_t, .name="index", .type=INT_TYPE, .default_val=FakeAST(Int, "-1")); + arg_t *arg_spec = new (arg_t, .name = "index", .type = INT_TYPE, .default_val = FakeAST(Int, "-1")); Text_t index = compile_arguments(env, ast, arg_spec, call->args); return Texts("List$pop(", self, ", ", index, ", ", compile_type(item_t), ", _, ", promote_to_optional(item_t, Text("_")), ", ", compile_none(item_t), ")"); @@ -3370,103 +3336,105 @@ Text_t compile(env_t *env, ast_t *ast) DeclareMatch(set, self_value_t, SetType); if (streq(call->name, "has")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="key", .type=set->item_type); + arg_t *arg_spec = new (arg_t, .name = "key", .type = set->item_type); return Texts("Table$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - compile_type_info(self_value_t), ")"); + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "add")) { EXPECT_POINTER("a", "set"); - arg_t *arg_spec = new(arg_t, .name="item", .type=set->item_type); - return Texts("Table$set_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", NULL, ", - compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new (arg_t, .name = "item", .type = set->item_type); + return Texts("Table$set_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), + ", NULL, ", compile_type_info(self_value_t), ")"); } else if (streq(call->name, "add_all")) { EXPECT_POINTER("a", "set"); - arg_t *arg_spec = new(arg_t, .name="items", .type=Type(ListType, .item_type=Match(self_value_t, SetType)->item_type)); + arg_t *arg_spec = new (arg_t, .name = "items", + .type = Type(ListType, .item_type = Match(self_value_t, SetType)->item_type)); return Texts("({ Table_t *set = ", self, "; ", - "List_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ", - "for (int64_t i = 0; i < to_add.length; i++)\n" - "Table$set(set, to_add.data + i*to_add.stride, NULL, ", compile_type_info(self_value_t), ");\n", - "(void)0; })"); + "List_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ", + "for (int64_t i = 0; i < to_add.length; i++)\n" + "Table$set(set, to_add.data + i*to_add.stride, NULL, ", + compile_type_info(self_value_t), ");\n", "(void)0; })"); } else if (streq(call->name, "remove")) { EXPECT_POINTER("a", "set"); - arg_t *arg_spec = new(arg_t, .name="item", .type=set->item_type); + arg_t *arg_spec = new (arg_t, .name = "item", .type = set->item_type); return Texts("Table$remove_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - compile_type_info(self_value_t), ")"); + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "remove_all")) { EXPECT_POINTER("a", "set"); - arg_t *arg_spec = new(arg_t, .name="items", .type=Type(ListType, .item_type=Match(self_value_t, SetType)->item_type)); + arg_t *arg_spec = new (arg_t, .name = "items", + .type = Type(ListType, .item_type = Match(self_value_t, SetType)->item_type)); return Texts("({ Table_t *set = ", self, "; ", - "List_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ", - "for (int64_t i = 0; i < to_add.length; i++)\n" - "Table$remove(set, to_add.data + i*to_add.stride, ", compile_type_info(self_value_t), ");\n", - "(void)0; })"); + "List_t to_add = ", compile_arguments(env, ast, arg_spec, call->args), "; ", + "for (int64_t i = 0; i < to_add.length; i++)\n" + "Table$remove(set, to_add.data + i*to_add.stride, ", + compile_type_info(self_value_t), ");\n", "(void)0; })"); } else if (streq(call->name, "clear")) { EXPECT_POINTER("a", "set"); (void)compile_arguments(env, ast, NULL, call->args); return Texts("Table$clear(", self, ")"); } else if (streq(call->name, "with")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t); - return Texts("Table$with(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), - ", ", compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new (arg_t, .name = "other", .type = self_value_t); + return Texts("Table$with(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "overlap")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t); - return Texts("Table$overlap(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), - ", ", compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new (arg_t, .name = "other", .type = self_value_t); + return Texts("Table$overlap(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "without")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t); - return Texts("Table$without(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), - ", ", compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new (arg_t, .name = "other", .type = self_value_t); + return Texts("Table$without(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "is_subset_of")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t, - .next=new(arg_t, .name="strict", .type=Type(BoolType), .default_val=FakeAST(Bool, false))); - return Texts("Table$is_subset_of(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), - ", ", compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new ( + arg_t, .name = "other", .type = self_value_t, + .next = new (arg_t, .name = "strict", .type = Type(BoolType), .default_val = FakeAST(Bool, false))); + return Texts("Table$is_subset_of(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "is_superset_of")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="other", .type=self_value_t, - .next=new(arg_t, .name="strict", .type=Type(BoolType), .default_val=FakeAST(Bool, false))); + arg_t *arg_spec = new ( + arg_t, .name = "other", .type = self_value_t, + .next = new (arg_t, .name = "strict", .type = Type(BoolType), .default_val = FakeAST(Bool, false))); return Texts("Table$is_superset_of(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), - ", ", compile_type_info(self_value_t), ")"); + ", ", compile_type_info(self_value_t), ")"); } else code_err(ast, "There is no '", call->name, "' method for tables"); } case TableType: { DeclareMatch(table, self_value_t, TableType); if (streq(call->name, "get")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type); - return Texts( - "Table$get_optional(", self, ", ", compile_type(table->key_type), ", ", - compile_type(table->value_type), ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - "_, ", optional_into_nonnone(table->value_type, Text("(*_)")), ", ", compile_none(table->value_type), ", ", - compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type); + return Texts("Table$get_optional(", self, ", ", compile_type(table->key_type), ", ", + compile_type(table->value_type), ", ", compile_arguments(env, ast, arg_spec, call->args), + ", ", "_, ", optional_into_nonnone(table->value_type, Text("(*_)")), ", ", + compile_none(table->value_type), ", ", compile_type_info(self_value_t), ")"); } else if (streq(call->name, "get_or_set")) { self = compile_to_pointer_depth(env, call->self, 1, false); - arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type, - .next=new(arg_t, .name="default", .type=table->value_type, .default_val=table->default_value)); - return Texts("*Table$get_or_setdefault(", - self, ", ", compile_type(table->key_type), ", ", - compile_type(table->value_type), ", ", - compile_arguments(env, ast, arg_spec, call->args), ", ", - compile_type_info(self_value_t), ")"); + arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type, + .next = new (arg_t, .name = "default", .type = table->value_type, + .default_val = table->default_value)); + return Texts("*Table$get_or_setdefault(", self, ", ", compile_type(table->key_type), ", ", + compile_type(table->value_type), ", ", compile_arguments(env, ast, arg_spec, call->args), + ", ", compile_type_info(self_value_t), ")"); } else if (streq(call->name, "has")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type); + arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type); return Texts("Table$has_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - compile_type_info(self_value_t), ")"); + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "set")) { EXPECT_POINTER("a", "table"); - arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type, - .next=new(arg_t, .name="value", .type=table->value_type)); + arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type, + .next = new (arg_t, .name = "value", .type = table->value_type)); return Texts("Table$set_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - compile_type_info(self_value_t), ")"); + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "remove")) { EXPECT_POINTER("a", "table"); - arg_t *arg_spec = new(arg_t, .name="key", .type=table->key_type); + arg_t *arg_spec = new (arg_t, .name = "key", .type = table->key_type); return Texts("Table$remove_value(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ", ", - compile_type_info(self_value_t), ")"); + compile_type_info(self_value_t), ")"); } else if (streq(call->name, "clear")) { EXPECT_POINTER("a", "table"); (void)compile_arguments(env, ast, NULL, call->args); @@ -3477,14 +3445,15 @@ Text_t compile(env_t *env, ast_t *ast) return Texts("Table$sorted(", self, ", ", compile_type_info(self_value_t), ")"); } else if (streq(call->name, "with_fallback")) { self = compile_to_pointer_depth(env, call->self, 0, false); - arg_t *arg_spec = new(arg_t, .name="fallback", .type=Type(OptionalType, self_value_t)); - return Texts("Table$with_fallback(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), ")"); + arg_t *arg_spec = new (arg_t, .name = "fallback", .type = Type(OptionalType, self_value_t)); + return Texts("Table$with_fallback(", self, ", ", compile_arguments(env, ast, arg_spec, call->args), + ")"); } else code_err(ast, "There is no '", call->name, "' method for tables"); } default: { DeclareMatch(methodcall, ast, MethodCall); type_t *fn_t = get_method_type(env, methodcall->self, methodcall->name); - arg_ast_t *args = new(arg_ast_t, .value=methodcall->self, .next=methodcall->args); + arg_ast_t *args = new (arg_ast_t, .value = methodcall->self, .next = methodcall->args); binding_t *b = get_namespace_binding(env, methodcall->self, methodcall->name); if (!b) code_err(ast, "No such method"); return Texts(b->code, "(", compile_arguments(env, ast, Match(fn_t, FunctionType)->args, args), ")"); @@ -3501,14 +3470,15 @@ Text_t compile(env_t *env, ast_t *ast) } else if (fn_t->tag == TypeInfoType) { type_t *t = Match(fn_t, TypeInfoType)->type; - // Literal constructors for numeric types like `Byte(123)` should not go through any conversion, just a cast: + // Literal constructors for numeric types like `Byte(123)` should + // not go through any conversion, just a cast: if (is_numeric_type(t) && call->args && !call->args->next && call->args->value->tag == Int) return compile_to_type(env, call->args->value, t); else if (t->tag == NumType && call->args && !call->args->next && call->args->value->tag == Num) return compile_to_type(env, call->args->value, t); - binding_t *constructor = get_constructor(env, t, call->args, - env->current_type != NULL && type_eq(env->current_type, t)); + binding_t *constructor = + get_constructor(env, t, call->args, env->current_type != NULL && type_eq(env->current_type, t)); if (constructor) { arg_t *arg_spec = Match(constructor->type, FunctionType)->args; return Texts(constructor->code, "(", compile_arguments(env, ast, arg_spec, call->args), ")"); @@ -3518,49 +3488,61 @@ Text_t compile(env_t *env, ast_t *ast) if (t->tag == TextType) { if (!call->args) code_err(ast, "This constructor needs a value"); if (!type_eq(t, TEXT_TYPE)) - code_err(call->fn, "I don't have a constructor defined for these arguments"); + code_err(call->fn, "I don't have a constructor defined for " + "these arguments"); // Text constructor: - if (!call->args || call->args->next) - code_err(call->fn, "This constructor takes exactly 1 argument"); - if (type_eq(actual, t)) - return compile(env, call->args->value); + if (!call->args || call->args->next) code_err(call->fn, "This constructor takes exactly 1 argument"); + if (type_eq(actual, t)) return compile(env, call->args->value); return expr_as_text(compile(env, call->args->value), actual, Text("no")); } else if (t->tag == CStringType) { // C String constructor: - if (!call->args || call->args->next) - code_err(call->fn, "This constructor takes exactly 1 argument"); + if (!call->args || call->args->next) code_err(call->fn, "This constructor takes exactly 1 argument"); if (call->args->value->tag == TextLiteral) return compile_text_literal(Match(call->args->value, TextLiteral)->text); else if (call->args->value->tag == TextJoin && Match(call->args->value, TextJoin)->children == NULL) return Text("\"\""); - else if (call->args->value->tag == TextJoin && Match(call->args->value, TextJoin)->children->next == NULL) - return compile_text_literal(Match(Match(call->args->value, TextJoin)->children->ast, TextLiteral)->text); - return Texts("Text$as_c_string(", expr_as_text(compile(env, call->args->value), actual, Text("no")), ")"); + else if (call->args->value->tag == TextJoin + && Match(call->args->value, TextJoin)->children->next == NULL) + return compile_text_literal( + Match(Match(call->args->value, TextJoin)->children->ast, TextLiteral)->text); + return Texts("Text$as_c_string(", expr_as_text(compile(env, call->args->value), actual, Text("no")), + ")"); } else if (t->tag == StructType) { DeclareMatch(struct_, t, StructType); if (!struct_->opaque && is_valid_call(env, struct_->fields, call->args, true)) { if (env->current_type == NULL || !type_eq(env->current_type, t)) { for (arg_t *field = struct_->fields; field; field = field->next) { if (field->name[0] == '_') - code_err(ast, "This struct can't be initialized directly because it has private fields (starting with underscore).\n" - "Use a `convert` or `func` to instantiate the type instead."); + code_err(ast, "This struct can't be " + "initialized directly because it " + "has private fields (starting " + "with underscore).\n" + "Use a `convert` or `func` to " + "instantiate the type " + "instead."); } } - return Texts("((", compile_type(t), "){", - compile_arguments(env, ast, struct_->fields, call->args), "})"); + return Texts("((", compile_type(t), "){", compile_arguments(env, ast, struct_->fields, call->args), + "})"); } } - code_err(ast, "I could not find a constructor matching these arguments for ", type_to_str(t)); + code_err(ast, + "I could not find a constructor matching these arguments " + "for ", + type_to_str(t)); } else if (fn_t->tag == ClosureType) { fn_t = Match(fn_t, ClosureType)->fn; arg_t *type_args = Match(fn_t, FunctionType)->args; arg_t *closure_fn_args = NULL; for (arg_t *arg = Match(fn_t, FunctionType)->args; arg; arg = arg->next) - closure_fn_args = new(arg_t, .name=arg->name, .type=arg->type, .default_val=arg->default_val, .next=closure_fn_args); - closure_fn_args = new(arg_t, .name="userdata", .type=Type(PointerType, .pointed=Type(MemoryType)), .next=closure_fn_args); + closure_fn_args = new (arg_t, .name = arg->name, .type = arg->type, .default_val = arg->default_val, + .next = closure_fn_args); + closure_fn_args = new (arg_t, .name = "userdata", .type = Type(PointerType, .pointed = Type(MemoryType)), + .next = closure_fn_args); REVERSE_LIST(closure_fn_args); - Text_t fn_type_code = compile_type(Type(FunctionType, .args=closure_fn_args, .ret=Match(fn_t, FunctionType)->ret)); + Text_t fn_type_code = + compile_type(Type(FunctionType, .args = closure_fn_args, .ret = Match(fn_t, FunctionType)->ret)); Text_t closure = compile(env, call->fn); Text_t arg_code = compile_arguments(env, ast, type_args, call->args); @@ -3568,8 +3550,8 @@ Text_t compile(env_t *env, ast_t *ast) if (call->fn->tag == Var) { return Texts("((", fn_type_code, ")", closure, ".fn)(", arg_code, closure, ".userdata)"); } else { - return Texts("({ Closure_t closure = ", closure, "; ((", fn_type_code, ")closure.fn)(", - arg_code, "closure.userdata); })"); + return Texts("({ Closure_t closure = ", closure, "; ((", fn_type_code, ")closure.fn)(", arg_code, + "closure.userdata); })"); } } else { code_err(call->fn, "This is not a function, it's a ", type_to_str(fn_t)); @@ -3581,27 +3563,30 @@ Text_t compile(env_t *env, ast_t *ast) if (!type_eq(value_type, Type(ListType, Type(ByteType)))) code_err(value, "This value should be a list of bytes, not a ", type_to_str(value_type)); type_t *t = parse_type_ast(env, Match(ast, Deserialize)->type); - return Texts("({ ", compile_declaration(t, Text("deserialized")), ";\n" - "generic_deserialize(", compile(env, value), ", &deserialized, ", compile_type_info(t), ");\n" - "deserialized; })"); + return Texts("({ ", compile_declaration(t, Text("deserialized")), + ";\n" + "generic_deserialize(", + compile(env, value), ", &deserialized, ", compile_type_info(t), + ");\n" + "deserialized; })"); } case ExplicitlyTyped: { return compile_to_type(env, Match(ast, ExplicitlyTyped)->ast, get_type(env, ast)); } case When: { DeclareMatch(original, ast, When); - ast_t *when_var = WrapAST(ast, Var, .name="when"); + ast_t *when_var = WrapAST(ast, Var, .name = "when"); when_clause_t *new_clauses = NULL; type_t *subject_t = get_type(env, original->subject); for (when_clause_t *clause = original->clauses; clause; clause = clause->next) { type_t *clause_type = get_clause_type(env, subject_t, clause); if (clause_type->tag == AbortType || clause_type->tag == ReturnType) { - new_clauses = new(when_clause_t, .pattern=clause->pattern, .body=clause->body, .next=new_clauses); + new_clauses = + new (when_clause_t, .pattern = clause->pattern, .body = clause->body, .next = new_clauses); } else { - ast_t *assign = WrapAST(clause->body, Assign, - .targets=new(ast_list_t, .ast=when_var), - .values=new(ast_list_t, .ast=clause->body)); - new_clauses = new(when_clause_t, .pattern=clause->pattern, .body=assign, .next=new_clauses); + ast_t *assign = WrapAST(clause->body, Assign, .targets = new (ast_list_t, .ast = when_var), + .values = new (ast_list_t, .ast = clause->body)); + new_clauses = new (when_clause_t, .pattern = clause->pattern, .body = assign, .next = new_clauses); } } REVERSE_LIST(new_clauses); @@ -3609,19 +3594,18 @@ Text_t compile(env_t *env, ast_t *ast) if (else_body) { type_t *clause_type = get_type(env, else_body); if (clause_type->tag != AbortType && clause_type->tag != ReturnType) { - else_body = WrapAST(else_body, Assign, - .targets=new(ast_list_t, .ast=when_var), - .values=new(ast_list_t, .ast=else_body)); + else_body = WrapAST(else_body, Assign, .targets = new (ast_list_t, .ast = when_var), + .values = new (ast_list_t, .ast = else_body)); } } type_t *t = get_type(env, ast); env_t *when_env = fresh_scope(env); set_binding(when_env, "when", t, Text("when")); - return Texts( - "({ ", compile_declaration(t, Text("when")), ";\n", - compile_statement(when_env, WrapAST(ast, When, .subject=original->subject, .clauses=new_clauses, .else_body=else_body)), - "when; })"); + return Texts("({ ", compile_declaration(t, Text("when")), ";\n", + compile_statement(when_env, WrapAST(ast, When, .subject = original->subject, + .clauses = new_clauses, .else_body = else_body)), + "when; })"); } case If: { DeclareMatch(if_, ast, If); @@ -3632,32 +3616,31 @@ Text_t compile(env_t *env, ast_t *ast) Text_t condition_code; if (condition->tag == Declare) { DeclareMatch(decl, condition, Declare); - if (decl->value == NULL) - code_err(condition, "This declaration must have a value"); - type_t *condition_type = - decl->type ? parse_type_ast(env, decl->type) - : get_type(env, Match(condition, Declare)->value); + if (decl->value == NULL) code_err(condition, "This declaration must have a value"); + type_t *condition_type = + decl->type ? parse_type_ast(env, decl->type) : get_type(env, Match(condition, Declare)->value); if (condition_type->tag != OptionalType) - code_err(condition, "This `if var := ...:` declaration should be an optional type, not ", type_to_str(condition_type)); + code_err(condition, + "This `if var := ...:` declaration should be an " + "optional " + "type, not ", + type_to_str(condition_type)); - if (is_incomplete_type(condition_type)) - code_err(condition, "This type is incomplete!"); + if (is_incomplete_type(condition_type)) code_err(condition, "This type is incomplete!"); decl_code = compile_statement(env, condition); ast_t *var = Match(condition, Declare)->var; truthy_scope = fresh_scope(env); bind_statement(truthy_scope, condition); condition_code = compile_condition(truthy_scope, var); - set_binding(truthy_scope, Match(var, Var)->name, - Match(condition_type, OptionalType)->type, + set_binding(truthy_scope, Match(var, Var)->name, Match(condition_type, OptionalType)->type, optional_into_nonnone(condition_type, compile(truthy_scope, var))); } else if (condition->tag == Var) { type_t *condition_type = get_type(env, condition); condition_code = compile_condition(env, condition); if (condition_type->tag == OptionalType) { truthy_scope = fresh_scope(env); - set_binding(truthy_scope, Match(condition, Var)->name, - Match(condition_type, OptionalType)->type, + set_binding(truthy_scope, Match(condition, Var)->name, Match(condition_type, OptionalType)->type, optional_into_nonnone(condition_type, compile(truthy_scope, condition))); } } else { @@ -3668,16 +3651,17 @@ Text_t compile(env_t *env, ast_t *ast) type_t *false_type = get_type(falsey_scope, if_->else_body); if (true_type->tag == AbortType || true_type->tag == ReturnType) return Texts("({ ", decl_code, "if (", condition_code, ") ", compile_statement(truthy_scope, if_->body), - "\n", compile(falsey_scope, if_->else_body), "; })"); + "\n", compile(falsey_scope, if_->else_body), "; })"); else if (false_type->tag == AbortType || false_type->tag == ReturnType) - return Texts("({ ", decl_code, "if (!(", condition_code, ")) ", compile_statement(falsey_scope, if_->else_body), - "\n", compile(truthy_scope, if_->body), "; })"); + return Texts("({ ", decl_code, "if (!(", condition_code, ")) ", + compile_statement(falsey_scope, if_->else_body), "\n", compile(truthy_scope, if_->body), + "; })"); else if (decl_code.length > 0) return Texts("({ ", decl_code, "(", condition_code, ") ? ", compile(truthy_scope, if_->body), " : ", - compile(falsey_scope, if_->else_body), ";})"); + compile(falsey_scope, if_->else_body), ";})"); else - return Texts("((", condition_code, ") ? ", - compile(truthy_scope, if_->body), " : ", compile(falsey_scope, if_->else_body), ")"); + return Texts("((", condition_code, ") ? ", compile(truthy_scope, if_->body), " : ", + compile(falsey_scope, if_->else_body), ")"); } case Reduction: { DeclareMatch(reduction, ast, Reduction); @@ -3685,14 +3669,16 @@ Text_t compile(env_t *env, ast_t *ast) type_t *iter_t = get_type(env, reduction->iter); type_t *item_t = get_iterated_type(iter_t); - if (!item_t) code_err(reduction->iter, "I couldn't figure out how to iterate over this type: ", type_to_str(iter_t)); + if (!item_t) + code_err(reduction->iter, "I couldn't figure out how to iterate over this type: ", type_to_str(iter_t)); static int64_t next_id = 1; ast_t *item = FakeAST(Var, String("$it", next_id++)); ast_t *body = LiteralCode(Text("{}")); // placeholder - ast_t *loop = FakeAST(For, .vars=new(ast_list_t, .ast=item), .iter=reduction->iter, .body=body); + ast_t *loop = FakeAST(For, .vars = new (ast_list_t, .ast = item), .iter = reduction->iter, .body = body); env_t *body_scope = for_scope(env, loop); - if (op == Equals || op == NotEquals || op == LessThan || op == LessThanOrEquals || op == GreaterThan || op == GreaterThanOrEquals) { + if (op == Equals || op == NotEquals || op == LessThan || op == LessThanOrEquals || op == GreaterThan + || op == GreaterThanOrEquals) { // Chained comparisons like ==, <, etc. type_t *item_value_type = item_t; ast_t *item_value = item; @@ -3702,36 +3688,34 @@ Text_t compile(env_t *env, ast_t *ast) item_value_type = get_type(body_scope, reduction->key); } - Text_t code = Texts( - "({ // Reduction:\n", - compile_declaration(item_value_type, Text("prev")), ";\n" - "OptionalBool_t result = NONE_BOOL;\n" - ); - - ast_t *comparison = new(ast_t, .file=ast->file, .start=ast->start, .end=ast->end, - .tag=op, .__data.Plus.lhs=LiteralCode(Text("prev"), .type=item_value_type), .__data.Plus.rhs=item_value); - body->__data.InlineCCode.chunks = new(ast_list_t, .ast=FakeAST(TextLiteral, Texts( - "if (result == NONE_BOOL) {\n" - " prev = ", compile(body_scope, item_value), ";\n" - " result = yes;\n" - "} else {\n" - " if (", compile(body_scope, comparison), ") {\n", - " prev = ", compile(body_scope, item_value), ";\n", - " } else {\n" - " result = no;\n", - " break;\n", - " }\n", - "}\n"))); + Text_t code = Texts("({ // Reduction:\n", compile_declaration(item_value_type, Text("prev")), + ";\n" + "OptionalBool_t result = NONE_BOOL;\n"); + + ast_t *comparison = new (ast_t, .file = ast->file, .start = ast->start, .end = ast->end, .tag = op, + .__data.Plus.lhs = LiteralCode(Text("prev"), .type = item_value_type), + .__data.Plus.rhs = item_value); + body->__data.InlineCCode.chunks = new ( + ast_list_t, .ast = FakeAST(TextLiteral, Texts("if (result == NONE_BOOL) {\n" + " prev = ", + compile(body_scope, item_value), + ";\n" + " result = yes;\n" + "} else {\n" + " if (", + compile(body_scope, comparison), ") {\n", + " prev = ", compile(body_scope, item_value), ";\n", + " } else {\n" + " result = no;\n", + " break;\n", " }\n", "}\n"))); code = Texts(code, compile_statement(env, loop), "\nresult;})"); return code; } else if (op == Min || op == Max) { // Min/max: Text_t superlative = op == Min ? Text("min") : Text("max"); - Text_t code = Texts( - "({ // Reduction:\n", - compile_declaration(item_t, superlative), ";\n" - "Bool_t has_value = no;\n" - ); + Text_t code = Texts("({ // Reduction:\n", compile_declaration(item_t, superlative), + ";\n" + "Bool_t has_value = no;\n"); Text_t item_code = compile(body_scope, item); ast_e cmp_op = op == Min ? LessThan : GreaterThan; @@ -3742,31 +3726,39 @@ Text_t compile(env_t *env, ast_t *ast) Text_t superlative_key = op == Min ? Text("min_key") : Text("max_key"); code = Texts(code, compile_declaration(key_type, superlative_key), ";\n"); - ast_t *comparison = new(ast_t, .file=ast->file, .start=ast->start, .end=ast->end, - .tag=cmp_op, .__data.Plus.lhs=LiteralCode(Text("key"), .type=key_type), - .__data.Plus.rhs=LiteralCode(superlative_key, .type=key_type)); - - body->__data.InlineCCode.chunks = new(ast_list_t, .ast=FakeAST(TextLiteral, Texts( - compile_declaration(key_type, Text("key")), " = ", compile(key_scope, reduction->key), ";\n", - "if (!has_value || ", compile(body_scope, comparison), ") {\n" - " ", superlative, " = ", compile(body_scope, item), ";\n" - " ", superlative_key, " = key;\n" - " has_value = yes;\n" - "}\n"))); + ast_t *comparison = new (ast_t, .file = ast->file, .start = ast->start, .end = ast->end, .tag = cmp_op, + .__data.Plus.lhs = LiteralCode(Text("key"), .type = key_type), + .__data.Plus.rhs = LiteralCode(superlative_key, .type = key_type)); + + body->__data.InlineCCode.chunks = new ( + ast_list_t, .ast = FakeAST(TextLiteral, Texts(compile_declaration(key_type, Text("key")), " = ", + compile(key_scope, reduction->key), ";\n", + "if (!has_value || ", compile(body_scope, comparison), + ") {\n" + " ", + superlative, " = ", compile(body_scope, item), + ";\n" + " ", + superlative_key, + " = key;\n" + " has_value = yes;\n" + "}\n"))); } else { - ast_t *comparison = new(ast_t, .file=ast->file, .start=ast->start, .end=ast->end, - .tag=cmp_op, .__data.Plus.lhs=item, - .__data.Plus.rhs=LiteralCode(superlative, .type=item_t)); - body->__data.InlineCCode.chunks = new(ast_list_t, .ast=FakeAST(TextLiteral, Texts( - "if (!has_value || ", compile(body_scope, comparison), ") {\n" - " ", superlative, " = ", compile(body_scope, item), ";\n" - " has_value = yes;\n" - "}\n"))); + ast_t *comparison = + new (ast_t, .file = ast->file, .start = ast->start, .end = ast->end, .tag = cmp_op, + .__data.Plus.lhs = item, .__data.Plus.rhs = LiteralCode(superlative, .type = item_t)); + body->__data.InlineCCode.chunks = new ( + ast_list_t, .ast = FakeAST(TextLiteral, Texts("if (!has_value || ", compile(body_scope, comparison), + ") {\n" + " ", + superlative, " = ", compile(body_scope, item), + ";\n" + " has_value = yes;\n" + "}\n"))); } - code = Texts(code, compile_statement(env, loop), "\nhas_value ? ", promote_to_optional(item_t, superlative), - " : ", compile_none(item_t), ";})"); + " : ", compile_none(item_t), ";})"); return code; } else { // Accumulator-style reductions like +, ++, *, etc. @@ -3777,43 +3769,44 @@ Text_t compile(env_t *env, ast_t *ast) item_value = reduction->key; } - Text_t code = Texts( - "({ // Reduction:\n", - compile_declaration(reduction_type, Text("reduction")), ";\n" - "Bool_t has_value = no;\n" - ); + Text_t code = Texts("({ // Reduction:\n", compile_declaration(reduction_type, Text("reduction")), + ";\n" + "Bool_t has_value = no;\n"); - // For the special case of (or)/(and), we need to early out if we can: + // For the special case of (or)/(and), we need to early out if we + // can: Text_t early_out = EMPTY_TEXT; if (op == Compare) { if (reduction_type->tag != IntType || Match(reduction_type, IntType)->bits != TYPE_IBITS32) - code_err(ast, "<> reductions are only supported for Int32 values"); + code_err(ast, "<> reductions are only supported for Int32 " + "values"); } else if (op == And) { - if (reduction_type->tag == BoolType) - early_out = Text("if (!reduction) break;"); + if (reduction_type->tag == BoolType) early_out = Text("if (!reduction) break;"); else if (reduction_type->tag == OptionalType) early_out = Texts("if (", check_none(reduction_type, Text("reduction")), ") break;"); } else if (op == Or) { - if (reduction_type->tag == BoolType) - early_out = Text("if (reduction) break;"); + if (reduction_type->tag == BoolType) early_out = Text("if (reduction) break;"); else if (reduction_type->tag == OptionalType) early_out = Texts("if (!", check_none(reduction_type, Text("reduction")), ") break;"); } - ast_t *combination = new(ast_t, .file=ast->file, .start=ast->start, .end=ast->end, - .tag=op, .__data.Plus.lhs=LiteralCode(Text("reduction"), .type=reduction_type), - .__data.Plus.rhs=item_value); - body->__data.InlineCCode.chunks = new(ast_list_t, .ast=FakeAST(TextLiteral, Texts( - "if (!has_value) {\n" - " reduction = ", compile(body_scope, item_value), ";\n" - " has_value = yes;\n" - "} else {\n" - " reduction = ", compile(body_scope, combination), ";\n", - early_out, - "}\n"))); - - code = Texts(code, compile_statement(env, loop), "\nhas_value ? ", promote_to_optional(reduction_type, Text("reduction")), - " : ", compile_none(reduction_type), ";})"); + ast_t *combination = new (ast_t, .file = ast->file, .start = ast->start, .end = ast->end, .tag = op, + .__data.Plus.lhs = LiteralCode(Text("reduction"), .type = reduction_type), + .__data.Plus.rhs = item_value); + body->__data.InlineCCode.chunks = + new (ast_list_t, + .ast = FakeAST(TextLiteral, Texts("if (!has_value) {\n" + " reduction = ", + compile(body_scope, item_value), + ";\n" + " has_value = yes;\n" + "} else {\n" + " reduction = ", + compile(body_scope, combination), ";\n", early_out, "}\n"))); + + code = Texts(code, compile_statement(env, loop), "\nhas_value ? ", + promote_to_optional(reduction_type, Text("reduction")), " : ", compile_none(reduction_type), + ";})"); return code; } } @@ -3826,7 +3819,9 @@ Text_t compile(env_t *env, ast_t *ast) DeclareMatch(info, value_t, TypeInfoType); if (f->field[0] == '_') { if (!type_eq(env->current_type, info->type)) - code_err(ast, "Fields that start with underscores are not accessible on types outside of the type definition."); + code_err(ast, "Fields that start with underscores are not " + "accessible " + "on types outside of the type definition."); } binding_t *b = get_binding(info->env, f->field); if (!b) code_err(ast, "I couldn't find the field '", f->field, "' on this type"); @@ -3834,7 +3829,7 @@ Text_t compile(env_t *env, ast_t *ast) return b->code; } case TextType: { - const char *lang = Match(value_t, TextType)->lang; + const char *lang = Match(value_t, TextType)->lang; if (lang && streq(f->field, "text")) { Text_t text = compile_to_pointer_depth(env, f->fielded, 0, false); return Texts("((Text_t)", text, ")"); @@ -3885,24 +3880,31 @@ Text_t compile(env_t *env, ast_t *ast) if (streq(f->field, "items")) return Texts("LIST_COPY((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries)"); else if (streq(f->field, "length")) - return Texts("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries.length)"); + return Texts("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), + ").entries.length)"); code_err(ast, "There is no '", f->field, "' field on sets"); } case TableType: { if (streq(f->field, "length")) { - return Texts("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries.length)"); + return Texts("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), + ").entries.length)"); } else if (streq(f->field, "keys")) { return Texts("LIST_COPY((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries)"); } else if (streq(f->field, "values")) { DeclareMatch(table, value_t, TableType); - Text_t offset = Texts("offsetof(struct { ", compile_declaration(table->key_type, Text("k")), "; ", compile_declaration(table->value_type, Text("v")), "; }, v)"); - return Texts("({ List_t *entries = &(", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries;\n" - "LIST_INCREF(*entries);\n" - "List_t values = *entries;\n" - "values.data += ", offset, ";\n" - "values; })"); + Text_t offset = Texts("offsetof(struct { ", compile_declaration(table->key_type, Text("k")), "; ", + compile_declaration(table->value_type, Text("v")), "; }, v)"); + return Texts("({ List_t *entries = &(", compile_to_pointer_depth(env, f->fielded, 0, false), + ").entries;\n" + "LIST_INCREF(*entries);\n" + "List_t values = *entries;\n" + "values.data += ", + offset, + ";\n" + "values; })"); } else if (streq(f->field, "fallback")) { - return Texts("({ Table_t *_fallback = (", compile_to_pointer_depth(env, f->fielded, 0, false), ").fallback; _fallback ? *_fallback : NONE_TABLE; })"); + return Texts("({ Table_t *_fallback = (", compile_to_pointer_depth(env, f->fielded, 0, false), + ").fallback; _fallback ? *_fallback : NONE_TABLE; })"); } code_err(ast, "There is no '", f->field, "' field on tables"); } @@ -3911,8 +3913,7 @@ Text_t compile(env_t *env, ast_t *ast) env_t *module_env = Table$str_get(*env->imports, name); return compile(module_env, WrapAST(ast, Var, f->field)); } - default: - code_err(ast, "Field accesses are not supported on ", type_to_str(fielded_t), " values"); + default: code_err(ast, "Field accesses are not supported on ", type_to_str(fielded_t), " values"); } } case Index: { @@ -3920,7 +3921,9 @@ Text_t compile(env_t *env, ast_t *ast) type_t *indexed_type = get_type(env, indexing->indexed); if (!indexing->index) { if (indexed_type->tag != PointerType) - code_err(ast, "Only pointers can use the '[]' operator to dereference the entire value."); + code_err(ast, "Only pointers can use the '[]' operator to " + "dereference " + "the entire value."); DeclareMatch(ptr, indexed_type, PointerType); if (ptr->pointed->tag == ListType) { return Texts("*({ List_t *list = ", compile(env, indexing->indexed), "; LIST_INCREF(*list); list; })"); @@ -3939,77 +3942,90 @@ Text_t compile(env_t *env, ast_t *ast) type_t *item_type = Match(container_t, ListType)->item_type; Text_t list = compile_to_pointer_depth(env, indexing->indexed, 0, false); file_t *f = indexing->index->file; - Text_t index_code = indexing->index->tag == Int - ? compile_int_to_type(env, indexing->index, Type(IntType, .bits=TYPE_IBITS64)) - : (index_t->tag == BigIntType ? Texts("Int64$from_int(", compile(env, indexing->index), ", no)") - : Texts("(Int64_t)(", compile(env, indexing->index), ")")); + Text_t index_code = + indexing->index->tag == Int + ? compile_int_to_type(env, indexing->index, Type(IntType, .bits = TYPE_IBITS64)) + : (index_t->tag == BigIntType ? Texts("Int64$from_int(", compile(env, indexing->index), ", no)") + : Texts("(Int64_t)(", compile(env, indexing->index), ")")); if (indexing->unchecked) return Texts("List_get_unchecked(", compile_type(item_type), ", ", list, ", ", index_code, ")"); else return Texts("List_get(", compile_type(item_type), ", ", list, ", ", index_code, ", ", - String((int64_t)(indexing->index->start - f->text)), ", ", - String((int64_t)(indexing->index->end - f->text)), - ")"); + String((int64_t)(indexing->index->start - f->text)), ", ", + String((int64_t)(indexing->index->end - f->text)), ")"); } else if (container_t->tag == TableType) { DeclareMatch(table_type, container_t, TableType); - if (indexing->unchecked) - code_err(ast, "Table indexes cannot be unchecked"); + if (indexing->unchecked) code_err(ast, "Table indexes cannot be unchecked"); if (table_type->default_value) { - return Texts("Table$get_or_default(", - compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ", - compile_type(table_type->key_type), ", ", - compile_type(table_type->value_type), ", ", - compile(env, indexing->index), ", ", - compile_to_type(env, table_type->default_value, table_type->value_type), ", ", - compile_type_info(container_t), ")"); + return Texts("Table$get_or_default(", compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ", + compile_type(table_type->key_type), ", ", compile_type(table_type->value_type), ", ", + compile(env, indexing->index), ", ", + compile_to_type(env, table_type->default_value, table_type->value_type), ", ", + compile_type_info(container_t), ")"); } else { - return Texts("Table$get_optional(", - compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ", - compile_type(table_type->key_type), ", ", - compile_type(table_type->value_type), ", ", - compile(env, indexing->index), ", " - "_, ", promote_to_optional(table_type->value_type, Text("(*_)")), ", ", - compile_none(table_type->value_type), ", ", - compile_type_info(container_t), ")"); + return Texts("Table$get_optional(", compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ", + compile_type(table_type->key_type), ", ", compile_type(table_type->value_type), ", ", + compile(env, indexing->index), + ", " + "_, ", + promote_to_optional(table_type->value_type, Text("(*_)")), ", ", + compile_none(table_type->value_type), ", ", compile_type_info(container_t), ")"); } } else if (container_t->tag == TextType) { - return Texts("Text$cluster(", compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ", compile_to_type(env, indexing->index, Type(BigIntType)), ")"); + return Texts("Text$cluster(", compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ", + compile_to_type(env, indexing->index, Type(BigIntType)), ")"); } else { code_err(ast, "Indexing is not supported for type: ", type_to_str(container_t)); } } case InlineCCode: { type_t *t = get_type(env, ast); - if (t->tag == VoidType) - return Texts("{\n", compile_statement(env, ast), "\n}"); - else - return compile_statement(env, ast); + if (t->tag == VoidType) return Texts("{\n", compile_statement(env, ast), "\n}"); + else return compile_statement(env, ast); } case Use: code_err(ast, "Compiling 'use' as expression!"); case Defer: code_err(ast, "Compiling 'defer' as expression!"); case Extern: code_err(ast, "Externs are not supported as expressions"); case TableEntry: code_err(ast, "Table entries should not be compiled directly"); - case Declare: case Assign: case UPDATE_CASES: case For: case While: case Repeat: case StructDef: case LangDef: case Extend: - case EnumDef: case FunctionDef: case ConvertDef: case Skip: case Stop: case Pass: case Return: case DocTest: case Assert: - code_err(ast, "This is not a valid expression"); - default: case Unknown: code_err(ast, "Unknown AST: ", ast_to_sexp_str(ast)); + case Declare: + case Assign: + case UPDATE_CASES: + case For: + case While: + case Repeat: + case StructDef: + case LangDef: + case Extend: + case EnumDef: + case FunctionDef: + case ConvertDef: + case Skip: + case Stop: + case Pass: + case Return: + case DocTest: + case Assert: code_err(ast, "This is not a valid expression"); + default: + case Unknown: code_err(ast, "Unknown AST: ", ast_to_sexp_str(ast)); } return EMPTY_TEXT; } -Text_t compile_type_info(type_t *t) -{ +Text_t compile_type_info(type_t *t) { if (t == NULL) compiler_err(NULL, NULL, NULL, "Attempt to compile a NULL type"); if (t == PATH_TYPE) return Text("&Path$info"); else if (t == PATH_TYPE_TYPE) return Text("&PathType$info"); switch (t->tag) { - case BoolType: case ByteType: case IntType: case BigIntType: case NumType: case CStringType: - return Texts("&", type_to_text(t), "$info"); + case BoolType: + case ByteType: + case IntType: + case BigIntType: + case NumType: + case CStringType: return Texts("&", type_to_text(t), "$info"); case TextType: { DeclareMatch(text, t, TextType); - if (!text->lang || streq(text->lang, "Text")) - return Text("&Text$info"); + if (!text->lang || streq(text->lang, "Text")) return Text("&Text$info"); return Texts("(&", namespace_name(text->env, text->env->namespace, Text("$info")), ")"); } case StructType: { @@ -4047,20 +4063,18 @@ Text_t compile_type_info(type_t *t) } case OptionalType: { type_t *non_optional = Match(t, OptionalType)->type; - return Texts("Optional$info(sizeof(", compile_type(non_optional), - "), __alignof__(", compile_type(non_optional), "), ", compile_type_info(non_optional), ")"); + return Texts("Optional$info(sizeof(", compile_type(non_optional), "), __alignof__(", compile_type(non_optional), + "), ", compile_type_info(non_optional), ")"); } case TypeInfoType: return Texts("Type$info(", quoted_text(type_to_text(Match(t, TypeInfoType)->type)), ")"); case MemoryType: return Text("&Memory$info"); case VoidType: return Text("&Void$info"); - default: - compiler_err(NULL, 0, 0, "I couldn't convert to a type info: ", type_to_str(t)); + default: compiler_err(NULL, 0, 0, "I couldn't convert to a type info: ", type_to_str(t)); } return EMPTY_TEXT; } -static Text_t get_flag_options(type_t *t, const char *separator) -{ +static Text_t get_flag_options(type_t *t, const char *separator) { if (t->tag == BoolType) { return Text("yes|no"); } else if (t->tag == EnumType) { @@ -4077,8 +4091,7 @@ static Text_t get_flag_options(type_t *t, const char *separator) } } -Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const char *version) -{ +Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const char *version) { DeclareMatch(fn_info, fn_type, FunctionType); env_t *main_env = fresh_scope(env); @@ -4106,39 +4119,32 @@ Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const c if (strlen(arg->name) == 1) { if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType)) usage = Texts(usage, "[-", flag, "]"); - else - usage = Texts(usage, "[-", flag, " ", get_flag_options(t, "|"), "]"); + else usage = Texts(usage, "[-", flag, " ", get_flag_options(t, "|"), "]"); } else { if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType)) usage = Texts(usage, "[--", flag, "]"); - else if (t->tag == ListType) - usage = Texts(usage, "[--", flag, " ", get_flag_options(t, "|"), "]"); - else - usage = Texts(usage, "[--", flag, "=", get_flag_options(t, "|"), "]"); + else if (t->tag == ListType) usage = Texts(usage, "[--", flag, " ", get_flag_options(t, "|"), "]"); + else usage = Texts(usage, "[--", flag, "=", get_flag_options(t, "|"), "]"); } } else { - if (t->tag == BoolType) - usage = Texts(usage, "<--", flag, "|--no-", flag, ">"); - else if (t->tag == EnumType) - usage = Texts(usage, get_flag_options(t, "|")); - else if (t->tag == ListType) - usage = Texts(usage, "[", flag, "...]"); - else - usage = Texts(usage, "<", flag, ">"); + if (t->tag == BoolType) usage = Texts(usage, "<--", flag, "|--no-", flag, ">"); + else if (t->tag == EnumType) usage = Texts(usage, get_flag_options(t, "|")); + else if (t->tag == ListType) usage = Texts(usage, "[", flag, "...]"); + else usage = Texts(usage, "<", flag, ">"); } } - code = Texts(code, "Text_t usage = Texts(Text(\"Usage: \"), Text$from_str(argv[0])", - usage.length == 0 ? EMPTY_TEXT : Texts(", Text(", quoted_text(usage), ")"), ");\n"); + code = Texts(code, + "Text_t usage = Texts(Text(\"Usage: \"), " + "Text$from_str(argv[0])", + usage.length == 0 ? EMPTY_TEXT : Texts(", Text(", quoted_text(usage), ")"), ");\n"); } - for (arg_t *arg = fn_info->args; arg; arg = arg->next) { - type_t *opt_type = arg->type->tag == OptionalType ? arg->type : Type(OptionalType, .type=arg->type); + type_t *opt_type = arg->type->tag == OptionalType ? arg->type : Type(OptionalType, .type = arg->type); code = Texts(code, compile_declaration(opt_type, Texts("_$", arg->name))); if (arg->default_val) { Text_t default_val = compile(env, arg->default_val); - if (arg->type->tag != OptionalType) - default_val = promote_to_optional(arg->type, default_val); + if (arg->type->tag != OptionalType) default_val = promote_to_optional(arg->type, default_val); code = Texts(code, " = ", default_val); } else { code = Texts(code, " = ", compile_none(arg->type)); @@ -4150,17 +4156,15 @@ Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const c code = Texts(code, "tomo_parse_args(argc, argv, ", usage_code, ", ", help_code, ", ", version_code); for (arg_t *arg = fn_info->args; arg; arg = arg->next) { code = Texts(code, ",\n{", quoted_text(Text$replace(Text$from_str(arg->name), Text("_"), Text("-"))), ", ", - (arg->default_val || arg->type->tag == OptionalType) ? "false" : "true", ", ", - compile_type_info(arg->type), - ", &", Texts("_$", arg->name), "}"); + (arg->default_val || arg->type->tag == OptionalType) ? "false" : "true", ", ", + compile_type_info(arg->type), ", &", Texts("_$", arg->name), "}"); } code = Texts(code, ");\n"); code = Texts(code, fn_name, "("); for (arg_t *arg = fn_info->args; arg; arg = arg->next) { Text_t arg_code = Texts("_$", arg->name); - if (arg->type->tag != OptionalType) - arg_code = optional_into_nonnone(arg->type, arg_code); + if (arg->type->tag != OptionalType) arg_code = optional_into_nonnone(arg->type, arg_code); code = Texts(code, arg_code); if (arg->next) code = Texts(code, ", "); @@ -4169,8 +4173,7 @@ Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const c return code; } -Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *staticdefs) -{ +Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *staticdefs) { bool is_private = false; const char *function_name; arg_ast_t *args; @@ -4193,7 +4196,10 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static ret_t = convertdef->ret_type ? parse_type_ast(env, convertdef->ret_type) : Type(VoidType); function_name = get_type_name(ret_t); if (!function_name) - code_err(ast, "Conversions are only supported for text, struct, and enum types, not ", type_to_str(ret_t)); + code_err(ast, + "Conversions are only supported for text, struct, and enum " + "types, not ", + type_to_str(ret_t)); body = convertdef->body; cache = convertdef->cache; is_inline = convertdef->is_inline; @@ -4212,21 +4218,17 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static arg_signature = Texts(arg_signature, ")"); Text_t ret_type_code = compile_type(ret_t); - if (ret_t->tag == AbortType) - ret_type_code = Texts("__attribute__((noreturn)) _Noreturn ", ret_type_code); + if (ret_t->tag == AbortType) ret_type_code = Texts("__attribute__((noreturn)) _Noreturn ", ret_type_code); - if (is_private) - *staticdefs = Texts(*staticdefs, "static ", ret_type_code, " ", name_code, arg_signature, ";\n"); + if (is_private) *staticdefs = Texts(*staticdefs, "static ", ret_type_code, " ", name_code, arg_signature, ";\n"); Text_t code; if (cache) { code = Texts("static ", ret_type_code, " ", name_code, "$uncached", arg_signature); } else { code = Texts(ret_type_code, " ", name_code, arg_signature); - if (is_inline) - code = Texts("INLINE ", code); - if (!is_private) - code = Texts("public ", code); + if (is_inline) code = Texts("INLINE ", code); + if (!is_private) code = Texts("public ", code); } env_t *body_scope = fresh_scope(env); @@ -4245,72 +4247,92 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static type_t *body_type = get_type(body_scope, body); if (ret_t->tag == AbortType) { - if (body_type->tag != AbortType) - code_err(ast, "This function can reach the end without aborting!"); + if (body_type->tag != AbortType) code_err(ast, "This function can reach the end without aborting!"); } else if (ret_t->tag == VoidType) { if (body_type->tag == AbortType) - code_err(ast, "This function will always abort before it reaches the end, but it's declared as having a Void return. It should be declared as an Abort return instead."); + code_err(ast, "This function will always abort before it reaches the " + "end, but it's declared as having a Void return. It should " + "be declared as an Abort return instead."); } else { if (body_type->tag != ReturnType && body_type->tag != AbortType) - code_err(ast, "This function looks like it can reach the end without returning a ", type_to_str(ret_t), " value! \n " - "If this is not the case, please add a call to `fail(\"Unreachable\")` at the end of the function to help the compiler out."); + code_err(ast, + "This function looks like it can reach the end without " + "returning a ", + type_to_str(ret_t), + " value! \n " + "If this is not the case, please add a call to " + "`fail(\"Unreachable\")` at the end of the function to " + "help the " + "compiler out."); } Text_t body_code = Texts("{\n", compile_inline_block(body_scope, body), "}\n"); Text_t definition = with_source_info(env, ast, Texts(code, " ", body_code, "\n")); if (cache && args == NULL) { // no-args cache just uses a static var - Text_t wrapper = Texts( - is_private ? EMPTY_TEXT : Text("public "), ret_type_code, " ", name_code, "(void) {\n" - "static ", compile_declaration(ret_t, Text("cached_result")), ";\n", - "static bool initialized = false;\n", - "if (!initialized) {\n" - "\tcached_result = ", name_code, "$uncached();\n", - "\tinitialized = true;\n", - "}\n", - "return cached_result;\n" - "}\n"); + Text_t wrapper = + Texts(is_private ? EMPTY_TEXT : Text("public "), ret_type_code, " ", name_code, + "(void) {\n" + "static ", + compile_declaration(ret_t, Text("cached_result")), ";\n", "static bool initialized = false;\n", + "if (!initialized) {\n" + "\tcached_result = ", + name_code, "$uncached();\n", "\tinitialized = true;\n", "}\n", + "return cached_result;\n" + "}\n"); definition = Texts(definition, wrapper); } else if (cache && cache->tag == Int) { assert(args); OptionalInt64_t cache_size = Int64$parse(Text$from_str(Match(cache, Int)->str), NULL); Text_t pop_code = EMPTY_TEXT; if (cache->tag == Int && !cache_size.is_none && cache_size.value > 0) { - // FIXME: this currently just deletes the first entry, but this should be more like a - // least-recently-used cache eviction policy or least-frequently-used + // FIXME: this currently just deletes the first entry, but this + // should be more like a least-recently-used cache eviction policy + // or least-frequently-used pop_code = Texts("if (cache.entries.length > ", String(cache_size.value), - ") Table$remove(&cache, cache.entries.data + cache.entries.stride*0, table_type);\n"); + ") Table$remove(&cache, cache.entries.data + " + "cache.entries.stride*0, table_type);\n"); } if (!args->next) { // Single-argument functions have simplified caching logic type_t *arg_type = get_arg_ast_type(env, args); - Text_t wrapper = Texts( - is_private ? EMPTY_TEXT : Text("public "), ret_type_code, " ", name_code, arg_signature, "{\n" - "static Table_t cache = {};\n", - "const TypeInfo_t *table_type = Table$info(", compile_type_info(arg_type), ", ", compile_type_info(ret_t), ");\n", - compile_declaration(Type(PointerType, .pointed=ret_t), Text("cached")), " = Table$get_raw(cache, &_$", args->name, ", table_type);\n" - "if (cached) return *cached;\n", - compile_declaration(ret_t, Text("ret")), " = ", name_code, "$uncached(_$", args->name, ");\n", - pop_code, - "Table$set(&cache, &_$", args->name, ", &ret, table_type);\n" - "return ret;\n" - "}\n"); + Text_t wrapper = + Texts(is_private ? EMPTY_TEXT : Text("public "), ret_type_code, " ", name_code, arg_signature, + "{\n" + "static Table_t cache = {};\n", + "const TypeInfo_t *table_type = Table$info(", compile_type_info(arg_type), ", ", + compile_type_info(ret_t), ");\n", + compile_declaration(Type(PointerType, .pointed = ret_t), Text("cached")), + " = Table$get_raw(cache, &_$", args->name, + ", table_type);\n" + "if (cached) return *cached;\n", + compile_declaration(ret_t, Text("ret")), " = ", name_code, "$uncached(_$", args->name, ");\n", + pop_code, "Table$set(&cache, &_$", args->name, + ", &ret, table_type);\n" + "return ret;\n" + "}\n"); definition = Texts(definition, wrapper); } else { - // Multi-argument functions use a custom struct type (only defined internally) as a cache key: + // Multi-argument functions use a custom struct type (only defined + // internally) as a cache key: arg_t *fields = NULL; for (arg_ast_t *arg = args; arg; arg = arg->next) - fields = new(arg_t, .name=arg->name, .type=get_arg_ast_type(env, arg), .next=fields); + fields = new (arg_t, .name = arg->name, .type = get_arg_ast_type(env, arg), .next = fields); REVERSE_LIST(fields); - type_t *t = Type(StructType, .name=String("func$", get_line_number(ast->file, ast->start), "$args"), .fields=fields, .env=env); + type_t *t = Type(StructType, .name = String("func$", get_line_number(ast->file, ast->start), "$args"), + .fields = fields, .env = env); int64_t num_fields = used_names.entries.length; const char *metamethods = is_packed_data(t) ? "PackedData$metamethods" : "Struct$metamethods"; - Text_t args_typeinfo = Texts("((TypeInfo_t[1]){{.size=sizeof(args), .align=__alignof__(args), .metamethods=", metamethods, - ", .tag=StructInfo, .StructInfo.name=\"FunctionArguments\", " - ".StructInfo.num_fields=", String(num_fields), - ", .StructInfo.fields=(NamedType_t[", String(num_fields), "]){"); + Text_t args_typeinfo = + Texts("((TypeInfo_t[1]){{.size=sizeof(args), " + ".align=__alignof__(args), .metamethods=", + metamethods, + ", .tag=StructInfo, " + ".StructInfo.name=\"FunctionArguments\", " + ".StructInfo.num_fields=", + String(num_fields), ", .StructInfo.fields=(NamedType_t[", String(num_fields), "]){"); Text_t args_type = Text("struct { "); for (arg_t *f = fields; f; f = f->next) { args_typeinfo = Texts(args_typeinfo, "{\"", f->name, "\", ", compile_type_info(f->type), "}"); @@ -4325,14 +4347,17 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static all_args = Texts(all_args, "_$", arg->name, arg->next ? Text(", ") : EMPTY_TEXT); Text_t wrapper = Texts( - is_private ? EMPTY_TEXT : Text("public "), ret_type_code, " ", name_code, arg_signature, "{\n" + is_private ? EMPTY_TEXT : Text("public "), ret_type_code, " ", name_code, arg_signature, + "{\n" "static Table_t cache = {};\n", - args_type, " args = {", all_args, "};\n" - "const TypeInfo_t *table_type = Table$info(", args_typeinfo, ", ", compile_type_info(ret_t), ");\n", - compile_declaration(Type(PointerType, .pointed=ret_t), Text("cached")), " = Table$get_raw(cache, &args, table_type);\n" + args_type, " args = {", all_args, + "};\n" + "const TypeInfo_t *table_type = Table$info(", + args_typeinfo, ", ", compile_type_info(ret_t), ");\n", + compile_declaration(Type(PointerType, .pointed = ret_t), Text("cached")), + " = Table$get_raw(cache, &args, table_type);\n" "if (cached) return *cached;\n", - compile_declaration(ret_t, Text("ret")), " = ", name_code, "$uncached(", all_args, ");\n", - pop_code, + compile_declaration(ret_t, Text("ret")), " = ", name_code, "$uncached(", all_args, ");\n", pop_code, "Table$set(&cache, &args, &ret, table_type);\n" "return ret;\n" "}\n"); @@ -4348,22 +4373,22 @@ Text_t compile_function(env_t *env, Text_t name_code, ast_t *ast, Text_t *static text = Texts(text, type_to_text(get_arg_ast_type(env, arg))); if (arg->next) text = Texts(text, ", "); } - if (ret_t && ret_t->tag != VoidType) - text = Texts(text, "->", type_to_text(ret_t)); + if (ret_t && ret_t->tag != VoidType) text = Texts(text, "->", type_to_text(ret_t)); text = Texts(text, ")"); return definition; } -Text_t compile_top_level_code(env_t *env, ast_t *ast) -{ +Text_t compile_top_level_code(env_t *env, ast_t *ast) { if (!ast) return EMPTY_TEXT; switch (ast->tag) { case Use: { // DeclareMatch(use, ast, Use); // if (use->what == USE_C_CODE) { - // Path_t path = Path$relative_to(Path$from_str(use->path), Path(".build")); - // return Texts("#include \"", Path$as_c_string(path), "\"\n"); + // Path_t path = Path$relative_to(Path$from_str(use->path), + // Path(".build")); return Texts("#include \"", + // Path$as_c_string(path), + // "\"\n"); // } return EMPTY_TEXT; } @@ -4377,31 +4402,32 @@ Text_t compile_top_level_code(env_t *env, ast_t *ast) bool is_private = decl_name[0] == '_'; if ((decl->value && is_constant(env, decl->value)) || (!decl->value && !has_heap_memory(t))) { set_binding(env, decl_name, t, full_name); - return Texts( - is_private ? "static " : "public ", - compile_declaration(t, full_name), " = ", val_code, ";\n"); + return Texts(is_private ? "static " : "public ", compile_declaration(t, full_name), " = ", val_code, ";\n"); } else { Text_t init_var = namespace_name(env, env->namespace, Texts(decl_name, "$$initialized")); Text_t checked_access = Texts("check_initialized(", full_name, ", ", init_var, ", \"", decl_name, "\")"); set_binding(env, decl_name, t, checked_access); Text_t initialized_name = namespace_name(env, env->namespace, Texts(decl_name, "$$initialized")); - return Texts( - "static bool ", initialized_name, " = false;\n", - is_private ? "static " : "public ", - compile_declaration(t, full_name), ";\n"); + return Texts("static bool ", initialized_name, " = false;\n", is_private ? "static " : "public ", + compile_declaration(t, full_name), ";\n"); } } case FunctionDef: { - Text_t name_code = namespace_name(env, env->namespace, Text$from_str(Match(Match(ast, FunctionDef)->name, Var)->name)); + Text_t name_code = + namespace_name(env, env->namespace, Text$from_str(Match(Match(ast, FunctionDef)->name, Var)->name)); return compile_function(env, name_code, ast, &env->code->staticdefs); } case ConvertDef: { type_t *type = get_function_def_type(env, ast); const char *name = get_type_name(Match(type, FunctionType)->ret); if (!name) - code_err(ast, "Conversions are only supported for text, struct, and enum types, not ", type_to_str(Match(type, FunctionType)->ret)); - Text_t name_code = namespace_name(env, env->namespace, Texts(name, "$", String(get_line_number(ast->file, ast->start)))); + code_err(ast, + "Conversions are only supported for text, struct, and enum " + "types, not ", + type_to_str(Match(type, FunctionType)->ret)); + Text_t name_code = + namespace_name(env, env->namespace, Texts(name, "$", String(get_line_number(ast->file, ast->start)))); return compile_function(env, name_code, ast, &env->code->staticdefs); } case StructDef: { @@ -4421,9 +4447,10 @@ Text_t compile_top_level_code(env_t *env, ast_t *ast) } case LangDef: { DeclareMatch(def, ast, LangDef); - Text_t code = Texts("public const TypeInfo_t ", namespace_name(env, env->namespace, Texts(def->name, "$$info")), - " = {", String((int64_t)sizeof(Text_t)), ", ", String((int64_t)__alignof__(Text_t)), - ", .metamethods=Text$metamethods, .tag=TextInfo, .TextInfo={", quoted_str(def->name), "}};\n"); + Text_t code = + Texts("public const TypeInfo_t ", namespace_name(env, env->namespace, Texts(def->name, "$$info")), " = {", + String((int64_t)sizeof(Text_t)), ", ", String((int64_t)__alignof__(Text_t)), + ", .metamethods=Text$metamethods, .tag=TextInfo, .TextInfo={", quoted_str(def->name), "}};\n"); env_t *ns_env = namespace_env(env, def->name); return Texts(code, def->namespace ? compile_top_level_code(ns_env, def->namespace) : EMPTY_TEXT); } @@ -4433,10 +4460,10 @@ Text_t compile_top_level_code(env_t *env, ast_t *ast) if (!b || b->type->tag != TypeInfoType) code_err(ast, "'", extend->name, "' is not the name of any type I recognize."); env_t *ns_env = Match(b->type, TypeInfoType)->env; - env_t *extended = new(env_t); + env_t *extended = new (env_t); *extended = *ns_env; - extended->locals = new(Table_t, .fallback=env->locals); - extended->namespace_bindings = new(Table_t, .fallback=env->namespace_bindings); + extended->locals = new (Table_t, .fallback = env->locals); + extended->namespace_bindings = new (Table_t, .fallback = env->namespace_bindings); extended->id_suffix = env->id_suffix; return compile_top_level_code(extended, extend->body); } @@ -4452,8 +4479,7 @@ Text_t compile_top_level_code(env_t *env, ast_t *ast) } } -static void initialize_vars_and_statics(env_t *env, ast_t *ast) -{ +static void initialize_vars_and_statics(env_t *env, ast_t *ast) { if (!ast) return; for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) { @@ -4469,13 +4495,10 @@ static void initialize_vars_and_statics(env_t *env, ast_t *ast) Text_t val_code = compile_declared_value(env, stmt->ast); if ((decl->value && !is_constant(env, decl->value)) || (!decl->value && has_heap_memory(t))) { Text_t initialized_name = namespace_name(env, env->namespace, Texts(decl_name, "$$initialized")); - env->code->variable_initializers = Texts( - env->code->variable_initializers, - with_source_info( - env, stmt->ast, - Texts( - full_name, " = ", val_code, ",\n", - initialized_name, " = true;\n"))); + env->code->variable_initializers = + Texts(env->code->variable_initializers, + with_source_info(env, stmt->ast, + Texts(full_name, " = ", val_code, ",\n", initialized_name, " = true;\n"))); } } else if (stmt->ast->tag == StructDef) { initialize_vars_and_statics(namespace_env(env, Match(stmt->ast, StructDef)->name), @@ -4498,8 +4521,7 @@ static void initialize_vars_and_statics(env_t *env, ast_t *ast) } } -Text_t compile_file(env_t *env, ast_t *ast) -{ +Text_t compile_file(env_t *env, ast_t *ast) { Text_t top_level_code = compile_top_level_code(env, ast); Text_t includes = EMPTY_TEXT; Text_t use_imports = EMPTY_TEXT; @@ -4520,27 +4542,18 @@ Text_t compile_file(env_t *env, ast_t *ast) initialize_vars_and_statics(env, ast); const char *name = file_base_name(ast->file->filename); - return Texts( - env->do_source_mapping ? Texts("#line 1 ", quoted_str(ast->file->filename), "\n") : EMPTY_TEXT, - "#define __SOURCE_FILE__ ", quoted_str(ast->file->filename), "\n", - "#include <tomo_"TOMO_VERSION"/tomo.h>\n" - "#include \"", name, ".tm.h\"\n\n", - includes, - env->code->local_typedefs, "\n", - env->code->lambdas, "\n", - env->code->staticdefs, "\n", - top_level_code, - "public void ", namespace_name(env, env->namespace, Text("$initialize")), "(void) {\n", - "static bool initialized = false;\n", - "if (initialized) return;\n", - "initialized = true;\n", - use_imports, - env->code->variable_initializers, - "}\n"); + return Texts(env->do_source_mapping ? Texts("#line 1 ", quoted_str(ast->file->filename), "\n") : EMPTY_TEXT, + "#define __SOURCE_FILE__ ", quoted_str(ast->file->filename), "\n", + "#include <tomo_" TOMO_VERSION "/tomo.h>\n" + "#include \"", + name, ".tm.h\"\n\n", includes, env->code->local_typedefs, "\n", env->code->lambdas, "\n", + env->code->staticdefs, "\n", top_level_code, "public void ", + namespace_name(env, env->namespace, Text("$initialize")), "(void) {\n", + "static bool initialized = false;\n", "if (initialized) return;\n", "initialized = true;\n", + use_imports, env->code->variable_initializers, "}\n"); } -Text_t compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast) -{ +Text_t compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast) { switch (ast->tag) { case Use: { DeclareMatch(use, ast, Use); @@ -4552,9 +4565,10 @@ Text_t compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast) module_info_t mod = get_module_info(ast); glob_t tm_files; const char *folder = mod.version ? String(mod.name, "_", mod.version) : mod.name; - if (glob(String(TOMO_PREFIX"/share/tomo_"TOMO_VERSION"/installed/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, NULL, &tm_files) != 0) { - if (!try_install_module(mod)) - code_err(ast, "Could not find library"); + if (glob(String(TOMO_PREFIX "/share/tomo_" TOMO_VERSION "/installed/", folder, "/[!._0-9]*.tm"), GLOB_TILDE, + NULL, &tm_files) + != 0) { + if (!try_install_module(mod)) code_err(ast, "Could not find library"); } Text_t includes = EMPTY_TEXT; @@ -4581,8 +4595,7 @@ Text_t compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast) Path_t used_path = Path$resolved(Path$from_str(use->path), source_dir); return Texts("#include \"", Path$as_c_string(Path$relative_to(used_path, build_dir)), "\"\n"); } - default: - return EMPTY_TEXT; + default: return EMPTY_TEXT; } } case StructDef: { @@ -4595,23 +4608,25 @@ Text_t compile_statement_type_header(env_t *env, Path_t header_path, ast_t *ast) DeclareMatch(def, ast, LangDef); return Texts( // Constructor macro: - "#define ", namespace_name(env, env->namespace, Text$from_str(def->name)), - "(text) ((", namespace_name(env, env->namespace, Texts(def->name, "$$type")), "){.length=sizeof(text)-1, .tag=TEXT_ASCII, .ascii=\"\" text})\n" - "#define ", namespace_name(env, env->namespace, Text$from_str(def->name)), - "s(...) ((", namespace_name(env, env->namespace, Texts(def->name, "$$type")), ")Texts(__VA_ARGS__))\n" - "extern const TypeInfo_t ", namespace_name(env, env->namespace, Texts(def->name, Text("$$info"))), ";\n" - ); + "#define ", namespace_name(env, env->namespace, Text$from_str(def->name)), "(text) ((", + namespace_name(env, env->namespace, Texts(def->name, "$$type")), + "){.length=sizeof(text)-1, .tag=TEXT_ASCII, .ascii=\"\" " + "text})\n" + "#define ", + namespace_name(env, env->namespace, Text$from_str(def->name)), "s(...) ((", + namespace_name(env, env->namespace, Texts(def->name, "$$type")), + ")Texts(__VA_ARGS__))\n" + "extern const TypeInfo_t ", + namespace_name(env, env->namespace, Texts(def->name, Text("$$info"))), ";\n"); } case Extend: { return EMPTY_TEXT; } - default: - return EMPTY_TEXT; + default: return EMPTY_TEXT; } } -Text_t compile_statement_namespace_header(env_t *env, Path_t header_path, ast_t *ast) -{ +Text_t compile_statement_namespace_header(env_t *env, Path_t header_path, ast_t *ast) { env_t *ns_env = NULL; ast_t *block = NULL; switch (ast->tag) { @@ -4625,10 +4640,10 @@ Text_t compile_statement_namespace_header(env_t *env, Path_t header_path, ast_t DeclareMatch(extend, ast, Extend); ns_env = namespace_env(env, extend->name); - env_t *extended = new(env_t); + env_t *extended = new (env_t); *extended = *ns_env; - extended->locals = new(Table_t, .fallback=env->locals); - extended->namespace_bindings = new(Table_t, .fallback=env->namespace_bindings); + extended->locals = new (Table_t, .fallback = env->locals); + extended->namespace_bindings = new (Table_t, .fallback = env->namespace_bindings); extended->id_suffix = env->id_suffix; ns_env = extended; @@ -4669,19 +4684,16 @@ Text_t compile_statement_namespace_header(env_t *env, Path_t header_path, ast_t DeclareMatch(decl, ast, Declare); const char *decl_name = Match(decl->var, Var)->name; bool is_private = (decl_name[0] == '_'); - if (is_private) - return EMPTY_TEXT; + if (is_private) return EMPTY_TEXT; type_t *t = decl->type ? parse_type_ast(env, decl->type) : get_type(env, decl->value); - if (t->tag == FunctionType) - t = Type(ClosureType, t); + if (t->tag == FunctionType) t = Type(ClosureType, t); assert(t->tag != ModuleType); if (t->tag == AbortType || t->tag == VoidType || t->tag == ReturnType) code_err(ast, "You can't declare a variable with a ", type_to_str(t), " value"); - return Texts( - decl->value ? compile_statement_type_header(env, header_path, decl->value) : EMPTY_TEXT, - "extern ", compile_declaration(t, namespace_name(env, env->namespace, Text$from_str(decl_name))), ";\n"); + return Texts(decl->value ? compile_statement_type_header(env, header_path, decl->value) : EMPTY_TEXT, "extern ", + compile_declaration(t, namespace_name(env, env->namespace, Text$from_str(decl_name))), ";\n"); } case FunctionDef: { DeclareMatch(fndef, ast, FunctionDef); @@ -4698,8 +4710,7 @@ Text_t compile_statement_namespace_header(env_t *env, Path_t header_path, ast_t type_t *ret_t = fndef->ret_type ? parse_type_ast(env, fndef->ret_type) : Type(VoidType); Text_t ret_type_code = compile_type(ret_t); - if (ret_t->tag == AbortType) - ret_type_code = Texts("__attribute__((noreturn)) _Noreturn ", ret_type_code); + if (ret_t->tag == AbortType) ret_type_code = Texts("__attribute__((noreturn)) _Noreturn ", ret_type_code); Text_t name = namespace_name(env, env->namespace, Text$from_str(decl_name)); if (env->namespace && env->namespace->parent && env->namespace->name && streq(decl_name, env->namespace->name)) name = namespace_name(env, env->namespace, Text$from_str(String(get_line_number(ast->file, ast->start)))); @@ -4720,8 +4731,12 @@ Text_t compile_statement_namespace_header(env_t *env, Path_t header_path, ast_t Text_t ret_type_code = compile_type(ret_t); Text_t name = Text$from_str(get_type_name(ret_t)); if (name.length == 0) - code_err(ast, "Conversions are only supported for text, struct, and enum types, not ", type_to_str(ret_t)); - Text_t name_code = namespace_name(env, env->namespace, Texts(name, "$", String(get_line_number(ast->file, ast->start)))); + code_err(ast, + "Conversions are only supported for text, struct, and enum " + "types, not ", + type_to_str(ret_t)); + Text_t name_code = + namespace_name(env, env->namespace, Texts(name, "$", String(get_line_number(ast->file, ast->start)))); return Texts(ret_type_code, " ", name_code, arg_signature, ";\n"); } default: return EMPTY_TEXT; @@ -4740,8 +4755,7 @@ typedef struct { Path_t header_path; } compile_typedef_info_t; -static void _make_typedefs(compile_typedef_info_t *info, ast_t *ast) -{ +static void _make_typedefs(compile_typedef_info_t *info, ast_t *ast) { if (ast->tag == StructDef) { DeclareMatch(def, ast, StructDef); if (def->external) return; @@ -4762,8 +4776,10 @@ static void _make_typedefs(compile_typedef_info_t *info, ast_t *ast) for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { if (!tag->fields) continue; - Text_t tag_struct = namespace_name(info->env, info->env->namespace, Texts(def->name, "$", tag->name, "$$struct")); - Text_t tag_type = namespace_name(info->env, info->env->namespace, Texts(def->name, "$", tag->name, "$$type")); + Text_t tag_struct = + namespace_name(info->env, info->env->namespace, Texts(def->name, "$", tag->name, "$$struct")); + Text_t tag_type = + namespace_name(info->env, info->env->namespace, Texts(def->name, "$", tag->name, "$$type")); *info->header = Texts(*info->header, "typedef struct ", tag_struct, " ", tag_type, ";\n"); } } else { @@ -4773,27 +4789,25 @@ static void _make_typedefs(compile_typedef_info_t *info, ast_t *ast) } } else if (ast->tag == LangDef) { DeclareMatch(def, ast, LangDef); - *info->header = Texts(*info->header, "typedef Text_t ", namespace_name(info->env, info->env->namespace, Texts(def->name, "$$type")), ";\n"); + *info->header = Texts(*info->header, "typedef Text_t ", + namespace_name(info->env, info->env->namespace, Texts(def->name, "$$type")), ";\n"); } } -static void _define_types_and_funcs(compile_typedef_info_t *info, ast_t *ast) -{ - *info->header = Texts(*info->header, - compile_statement_type_header(info->env, info->header_path, ast), - compile_statement_namespace_header(info->env, info->header_path, ast)); +static void _define_types_and_funcs(compile_typedef_info_t *info, ast_t *ast) { + *info->header = Texts(*info->header, compile_statement_type_header(info->env, info->header_path, ast), + compile_statement_namespace_header(info->env, info->header_path, ast)); } -Text_t compile_file_header(env_t *env, Path_t header_path, ast_t *ast) -{ - Text_t header = Texts( - "#pragma once\n", - env->do_source_mapping ? Texts("#line 1 ", quoted_str(ast->file->filename), "\n") : EMPTY_TEXT, - "#include <tomo_"TOMO_VERSION"/tomo.h>\n"); +Text_t compile_file_header(env_t *env, Path_t header_path, ast_t *ast) { + Text_t header = + Texts("#pragma once\n", + env->do_source_mapping ? Texts("#line 1 ", quoted_str(ast->file->filename), "\n") : EMPTY_TEXT, + "#include <tomo_" TOMO_VERSION "/tomo.h>\n"); - compile_typedef_info_t info = {.env=env, .header=&header, .header_path=header_path}; - visit_topologically(Match(ast, Block)->statements, (Closure_t){.fn=(void*)_make_typedefs, &info}); - visit_topologically(Match(ast, Block)->statements, (Closure_t){.fn=(void*)_define_types_and_funcs, &info}); + compile_typedef_info_t info = {.env = env, .header = &header, .header_path = header_path}; + visit_topologically(Match(ast, Block)->statements, (Closure_t){.fn = (void *)_make_typedefs, &info}); + visit_topologically(Match(ast, Block)->statements, (Closure_t){.fn = (void *)_define_types_and_funcs, &info}); header = Texts(header, "void ", namespace_name(env, env->namespace, Text("$initialize")), "(void);\n"); return header; |
