From e920d306fb51c16fd473033b64aa5ba5c3e07409 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sun, 24 Aug 2025 15:56:38 -0400 Subject: Split into more files: promotion, sets, tables, pointers, functions. --- src/compile/enums.h | 1 + src/compile/functions.c | 96 ++++++++++++++++++++++++ src/compile/functions.h | 6 ++ src/compile/integers.c | 75 +++++++++++++++++++ src/compile/integers.h | 6 ++ src/compile/optionals.c | 85 +++++++++++++++++++++ src/compile/optionals.h | 7 ++ src/compile/pointers.c | 21 ++++++ src/compile/pointers.h | 3 + src/compile/promotion.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++++ src/compile/promotion.h | 7 ++ src/compile/sets.c | 51 +++++++++++++ src/compile/sets.h | 7 ++ src/compile/structs.h | 1 + src/compile/tables.c | 76 +++++++++++++++++++ src/compile/tables.h | 6 ++ 16 files changed, 643 insertions(+) create mode 100644 src/compile/functions.c create mode 100644 src/compile/functions.h create mode 100644 src/compile/integers.c create mode 100644 src/compile/integers.h create mode 100644 src/compile/optionals.c create mode 100644 src/compile/optionals.h create mode 100644 src/compile/promotion.c create mode 100644 src/compile/promotion.h create mode 100644 src/compile/sets.c create mode 100644 src/compile/sets.h create mode 100644 src/compile/tables.c create mode 100644 src/compile/tables.h (limited to 'src/compile') diff --git a/src/compile/enums.h b/src/compile/enums.h index c272086a..21235532 100644 --- a/src/compile/enums.h +++ b/src/compile/enums.h @@ -5,6 +5,7 @@ #include "../ast.h" #include "../environment.h" #include "../stdlib/datatypes.h" +#include "../types.h" Text_t compile_empty_enum(type_t *t); Text_t compile_enum_constructors(env_t *env, ast_t *ast); diff --git a/src/compile/functions.c b/src/compile/functions.c new file mode 100644 index 00000000..ac4e27ea --- /dev/null +++ b/src/compile/functions.c @@ -0,0 +1,96 @@ +#include "../ast.h" +#include "../compile.h" +#include "../environment.h" +#include "../stdlib/datatypes.h" +#include "../stdlib/integers.h" +#include "../stdlib/nums.h" +#include "../stdlib/tables.h" +#include "../stdlib/text.h" +#include "../stdlib/util.h" +#include "../typecheck.h" +#include "../types.h" +#include "integers.h" + +public +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); + *default_scope = *env; + 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: + if (spec_arg->name) { + for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) { + if (call_arg->name && streq(call_arg->name, spec_arg->name)) { + Text_t value; + if (spec_arg->type->tag == IntType && call_arg->value->tag == Int) { + 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 (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 { + env_t *arg_env = with_enum_scope(env, spec_arg->type); + value = compile_maybe_incref(arg_env, call_arg->value, spec_arg->type); + } + Table$str_set(&used_args, call_arg->name, call_arg); + if (code.length > 0) code = Texts(code, ", "); + code = Texts(code, value); + goto found_it; + } + } + } + // Find positional: + for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) { + if (call_arg->name) continue; + const char *pseudoname = String(i++); + if (!Table$str_get(used_args, pseudoname)) { + Text_t value; + if (spec_arg->type->tag == IntType && call_arg->value->tag == Int) { + 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 (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 { + env_t *arg_env = with_enum_scope(env, spec_arg->type); + value = compile_maybe_incref(arg_env, call_arg->value, spec_arg->type); + } + + Table$str_set(&used_args, pseudoname, call_arg); + if (code.length > 0) code = Texts(code, ", "); + code = Texts(code, value); + goto found_it; + } + } + + if (spec_arg->default_val) { + if (code.length > 0) code = Texts(code, ", "); + code = Texts(code, compile_maybe_incref(default_scope, spec_arg->default_val, get_arg_type(env, spec_arg))); + goto found_it; + } + + assert(spec_arg->name); + code_err(call_ast, "The required argument '", spec_arg->name, "' was not provided"); + found_it: + continue; + } + + int64_t i = 1; + for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) { + if (call_arg->name) { + if (!Table$str_get(used_args, call_arg->name)) + 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!"); + } + } + return code; +} diff --git a/src/compile/functions.h b/src/compile/functions.h new file mode 100644 index 00000000..1d2362a4 --- /dev/null +++ b/src/compile/functions.h @@ -0,0 +1,6 @@ + +#include "../ast.h" +#include "../environment.h" +#include "../stdlib/datatypes.h" + +Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t *call_args); diff --git a/src/compile/integers.c b/src/compile/integers.c new file mode 100644 index 00000000..97c9c61a --- /dev/null +++ b/src/compile/integers.c @@ -0,0 +1,75 @@ +#include + +#include "../ast.h" +#include "../compile.h" +#include "../environment.h" +#include "../stdlib/datatypes.h" +#include "../stdlib/integers.h" +#include "../stdlib/text.h" +#include "../typecheck.h" +#include "../types.h" +#include "promotion.h" + +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); + if (!promote(env, ast, &code, actual_type, target)) + code_err(ast, "I couldn't promote this ", type_to_str(actual_type), " to a ", type_to_str(target)); + return code; + } + + 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"); + + mpz_t i; + mpz_init_set_int(i, int_val); + + char *c_literal; + if (strncmp(literal, "0x", 2) == 0 || strncmp(literal, "0X", 2) == 0 || strncmp(literal, "0b", 2) == 0) { + gmp_asprintf(&c_literal, "0x%ZX", i); + } else if (strncmp(literal, "0o", 2) == 0) { + gmp_asprintf(&c_literal, "%#Zo", i); + } else { + gmp_asprintf(&c_literal, "%#Zd", i); + } + + if (target->tag == ByteType) { + 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) { + return Texts("N64(", c_literal, ")"); + } else { + return Texts("N32(", c_literal, ")"); + } + } else if (target->tag == IntType) { + 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)"); + break; + case TYPE_IBITS32: + 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, ")"); + break; + case TYPE_IBITS8: + if (mpz_cmp_si(i, INT8_MAX) <= 0 && mpz_cmp_si(i, INT8_MIN) >= 0) return Texts("I8(", c_literal, ")"); + break; + default: break; + } + code_err(ast, "This integer cannot fit in a ", target_bits, "-bit value"); + } else { + code_err(ast, "I don't know how to compile this to a ", type_to_str(target)); + } + return EMPTY_TEXT; +} diff --git a/src/compile/integers.h b/src/compile/integers.h new file mode 100644 index 00000000..f4479b3e --- /dev/null +++ b/src/compile/integers.h @@ -0,0 +1,6 @@ + +#include "../ast.h" +#include "../environment.h" +#include "../types.h" + +Text_t compile_int_to_type(env_t *env, ast_t *ast, type_t *target); diff --git a/src/compile/optionals.c b/src/compile/optionals.c new file mode 100644 index 00000000..4b360b31 --- /dev/null +++ b/src/compile/optionals.c @@ -0,0 +1,85 @@ +#include "../compile.h" +#include "../environment.h" +#include "../naming.h" +#include "../stdlib/datatypes.h" +#include "../stdlib/text.h" +#include "../stdlib/util.h" +#include "../types.h" + +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 StructType: + if (t == PATH_TYPE || t == PATH_TYPE_TYPE) return value; + return Texts(value, ".value"); + default: return value; + } +} + +public +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) { + switch (Match(t, IntType)->bits) { + case TYPE_IBITS8: return Texts("((OptionalInt8_t){.value=", code, "})"); + case TYPE_IBITS16: return Texts("((OptionalInt16_t){.value=", code, "})"); + case TYPE_IBITS32: return Texts("((OptionalInt32_t){.value=", code, "})"); + case TYPE_IBITS64: return Texts("((OptionalInt64_t){.value=", code, "})"); + default: errx(1, "Unsupported in type: %s", type_to_str(t)); + } + return 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; })"); + } else { + return code; + } +} + +public +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 == 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){})"); + + switch (t->tag) { + case BigIntType: return Text("NONE_INT"); + case IntType: { + switch (Match(t, IntType)->bits) { + case TYPE_IBITS8: return Text("NONE_INT8"); + case TYPE_IBITS16: return Text("NONE_INT16"); + case TYPE_IBITS32: return Text("NONE_INT32"); + case TYPE_IBITS64: return Text("NONE_INT64"); + default: errx(1, "Invalid integer bit size"); + } + break; + } + case BoolType: return Text("NONE_BOOL"); + case ByteType: return Text("NONE_BYTE"); + case ListType: return Text("NONE_LIST"); + case TableType: return Text("NONE_TABLE"); + case SetType: return Text("NONE_TABLE"); + case TextType: return Text("NONE_TEXT"); + case CStringType: return Text("NULL"); + 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 EnumType: { + env_t *enum_env = Match(t, EnumType)->env; + return Texts("((", compile_type(t), "){", namespace_name(enum_env, enum_env->namespace, Text("none")), "})"); + } + default: compiler_err(NULL, NULL, NULL, "none isn't implemented for this type: ", type_to_str(t)); + } + return EMPTY_TEXT; +} diff --git a/src/compile/optionals.h b/src/compile/optionals.h new file mode 100644 index 00000000..ec5d1954 --- /dev/null +++ b/src/compile/optionals.h @@ -0,0 +1,7 @@ +#include "../stdlib/datatypes.h" +#include "../types.h" + +Text_t check_none(type_t *t, Text_t value); +Text_t optional_into_nonnone(type_t *t, Text_t value); +Text_t promote_to_optional(type_t *t, Text_t code); +Text_t compile_none(type_t *t); diff --git a/src/compile/pointers.c b/src/compile/pointers.c index cb11844d..ee67b18d 100644 --- a/src/compile/pointers.c +++ b/src/compile/pointers.c @@ -10,7 +10,9 @@ #include "../environment.h" #include "../stdlib/text.h" #include "../typecheck.h" +#include "promotion.h" +public 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); @@ -46,3 +48,22 @@ Text_t compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bo return val; } + +public +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) { + case HeapAllocate: { + return Texts("heap(", compile_to_type(env, Match(ast, HeapAllocate)->value, pointed), ")"); + } + case StackReference: { + 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), ")"); + } + default: code_err(ast, "Not an allocation!"); + } + return EMPTY_TEXT; +} diff --git a/src/compile/pointers.h b/src/compile/pointers.h index 306c3019..49e73c1c 100644 --- a/src/compile/pointers.h +++ b/src/compile/pointers.h @@ -1,6 +1,9 @@ #include +#include "../ast.h" #include "../environment.h" #include "../stdlib/datatypes.h" +#include "../types.h" Text_t compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool needs_incref); +Text_t compile_typed_allocation(env_t *env, ast_t *ast, type_t *pointer_type); diff --git a/src/compile/promotion.c b/src/compile/promotion.c new file mode 100644 index 00000000..fbac5888 --- /dev/null +++ b/src/compile/promotion.c @@ -0,0 +1,195 @@ +#include "promotion.h" +#include "../ast.h" +#include "../compile.h" +#include "../environment.h" +#include "../stdlib/datatypes.h" +#include "../stdlib/text.h" +#include "../typecheck.h" +#include "../types.h" +#include "integers.h" +#include "lists.h" +#include "optionals.h" +#include "pointers.h" +#include "sets.h" +#include "tables.h" + +static Text_t quoted_str(const char *str) { return Text$quoted(Text$from_str(str), false, Text("\"")); } + +public +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 (needed->tag == ClosureType && actual->tag == FunctionType) { + *code = Texts("((Closure_t){", *code, ", NULL})"); + return true; + } + + // Empty promotion: + type_t *more_complete = most_complete_type(actual, needed); + if (more_complete) return true; + + // Optional promotion: + if (needed->tag == OptionalType && type_eq(actual, Match(needed, OptionalType)->type)) { + *code = promote_to_optional(actual, *code); + return true; + } + + // Optional -> Bool promotion + if (actual->tag == OptionalType && needed->tag == BoolType) { + *code = Texts("(!", check_none(actual, *code), ")"); + return true; + } + + // Lang to Text_t: + 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")), "; })"); + 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))); + if (constructor) { + DeclareMatch(fn, constructor->type, FunctionType); + if (fn->args->next == NULL) { + *code = Texts(constructor->code, "(", compile_arguments(env, ast, fn->args, args), ")"); + return true; + } + } + } + + if (needed->tag == EnumType) { + const char *tag = enum_single_value_tag(needed, actual); + 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; + *code = Texts(b->code, "(", *code, ")"); + return true; + } + + // Text_t to C String + if (actual->tag == TextType && type_eq(actual, TEXT_TYPE) && needed->tag == CStringType) { + *code = Texts("Text$as_c_string(", *code, ")"); + return true; + } + + // Automatic dereferencing: + 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; + + // Cross-promotion between tables with default values and without + if (needed->tag == TableType && actual->tag == TableType) return true; + + if (needed->tag == ClosureType && actual->tag == ClosureType) return true; + + if (needed->tag == FunctionType && actual->tag == FunctionType) { + *code = Texts("(", compile_type(needed), ")", *code); + return true; + } + + // Set -> List promotion: + if (needed->tag == ListType && actual->tag == SetType + && type_eq(Match(needed, ListType)->item_type, Match(actual, SetType)->item_type)) { + *code = Texts("(", *code, ").entries"); + return true; + } + + return false; +} + +public +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); + } else if (ast->tag == Num && t->tag == NumType) { + double n = Match(ast, Num)->n; + switch (Match(t, NumType)->bits) { + case TYPE_NBITS64: return Text$from_str(String(hex_double(n))); + case TYPE_NBITS32: return Text$from_str(String(hex_double(n), "f")); + 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"); + 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`"); + return compile_none(t); + } else if (t->tag == PointerType && (ast->tag == HeapAllocate || ast->tag == StackReference)) { + return compile_typed_allocation(env, ast, t); + } else if (t->tag == ListType && ast->tag == List) { + return compile_typed_list(env, ast, t); + } else if (t->tag == TableType && ast->tag == Table) { + return compile_typed_table(env, ast, t); + } else if (t->tag == SetType && ast->tag == Set) { + return compile_typed_set(env, ast, t); + } + + type_t *actual = get_type(env, ast); + + // Edge case: there are some situations where a method call needs to have + // the `self` value get compiled to a specific type that can't be fully + // inferred from the expression itself. We can infer what the specific type + // should be from what we know the specific type of the return value is, + // but it requires a bit of special logic. + // For example: + // x : [Int?] = [none].sorted() + // Here, we know that `[none]` is `[Int?]`, but we need to thread that + // information through the compiler using an `ExplicitlyTyped` node. + 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()`) + 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); + return compile_to_type(env, new_methodcall, t); + } + } + } + + // Promote values to views-of-values if needed: + if (t->tag == PointerType && Match(t, PointerType)->is_stack && actual->tag != PointerType) + return Texts("stack(", compile_to_type(env, ast, Match(t, PointerType)->pointed), ")"); + + if (!is_incomplete_type(actual)) { + Text_t code = compile(env, ast); + if (promote(env, ast, &code, actual, t)) return code; + } + + 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; + return Texts(constructor->code, "(", compile_arguments(env, ast, arg_spec, constructor_args), ")"); + } + + code_err(ast, "I expected a ", type_to_str(t), " here, but this is a ", type_to_str(actual)); +} diff --git a/src/compile/promotion.h b/src/compile/promotion.h new file mode 100644 index 00000000..2cfe0cbf --- /dev/null +++ b/src/compile/promotion.h @@ -0,0 +1,7 @@ + +#include "../ast.h" +#include "../environment.h" +#include "../types.h" + +bool promote(env_t *env, ast_t *ast, Text_t *code, type_t *actual, type_t *needed); +Text_t compile_to_type(env_t *env, ast_t *ast, type_t *t); diff --git a/src/compile/sets.c b/src/compile/sets.c new file mode 100644 index 00000000..a1471a5a --- /dev/null +++ b/src/compile/sets.c @@ -0,0 +1,51 @@ + +#include "../ast.h" +#include "../compile.h" +#include "../environment.h" +#include "../stdlib/datatypes.h" +#include "../stdlib/text.h" +#include "../types.h" +#include "promotion.h" + +static 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_typed_set(env_t *env, ast_t *ast, type_t *set_type) { + DeclareMatch(set, ast, Set); + 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; + } + + { // No comprehension: + 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)); + } + 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; +} +} diff --git a/src/compile/sets.h b/src/compile/sets.h new file mode 100644 index 00000000..bfbcf3b4 --- /dev/null +++ b/src/compile/sets.h @@ -0,0 +1,7 @@ + +#include "../ast.h" +#include "../environment.h" +#include "../stdlib/datatypes.h" +#include "../types.h" + +Text_t compile_typed_set(env_t *env, ast_t *ast, type_t *set_type); diff --git a/src/compile/structs.h b/src/compile/structs.h index 8185b561..42c95003 100644 --- a/src/compile/structs.h +++ b/src/compile/structs.h @@ -4,6 +4,7 @@ #include "../ast.h" #include "../environment.h" +#include "../types.h" Text_t compile_empty_struct(type_t *t); Text_t compile_struct_typeinfo(env_t *env, type_t *t, const char *name, arg_ast_t *fields, bool is_secret, diff --git a/src/compile/tables.c b/src/compile/tables.c new file mode 100644 index 00000000..0ddd310c --- /dev/null +++ b/src/compile/tables.c @@ -0,0 +1,76 @@ + +#include "../ast.h" +#include "../compile.h" +#include "../environment.h" +#include "../stdlib/datatypes.h" +#include "../stdlib/text.h" +#include "../types.h" +#include "promotion.h" + +static 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))); +} + +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), ")"); + return Texts(code, "})"); + } + + type_t *key_t = Match(table_type, TableType)->key_type; + type_t *value_t = Match(table_type, TableType)->value_type; + + if (value_t->tag == OptionalType) + 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; + } + + { // 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"); + + size_t n = 0; + for (ast_list_t *entry = table->entries; entry; entry = entry->next) + ++n; + code = Texts(code, ", ", String((int64_t)n)); + + 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), "}"); + } + 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)); + + Text_t code = Texts("({ Table_t ", comprehension_name, " = {"); + if (table->fallback) code = Texts(code, ".fallback=heap(", compile(env, table->fallback), "), "); + + 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; +} +} diff --git a/src/compile/tables.h b/src/compile/tables.h new file mode 100644 index 00000000..c8c19cb1 --- /dev/null +++ b/src/compile/tables.h @@ -0,0 +1,6 @@ +#include "../ast.h" +#include "../environment.h" +#include "../stdlib/datatypes.h" +#include "../types.h" + +Text_t compile_typed_table(env_t *env, ast_t *ast, type_t *table_type); -- cgit v1.2.3