From 147e0f0269440fce15d6b88a8a90627f3a3b2df2 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Wed, 5 Mar 2025 18:20:54 -0500 Subject: [PATCH] Overhaul of constructors, making it more consistent and correct. Also changed T(), T, T_t, T_s type names to T(), T$$info, T$$type, T$$struct for unambiguity --- Makefile | 4 +- compile.c | 191 ++++++++++++------------------------ enums.c | 14 +-- environment.c | 129 ++++++++++++++++++++---- examples/vectors/vectors.tm | 11 ++- repl.c | 28 +++--- stdlib/arrays.c | 17 ++-- stdlib/arrays.h | 2 +- stdlib/bools.h | 7 +- stdlib/bytes.c | 23 +++++ stdlib/bytes.h | 18 ++-- stdlib/datatypes.h | 10 ++ stdlib/integers.c | 27 +++-- stdlib/integers.h | 152 +++++++++++++--------------- stdlib/moments.c | 17 ++-- stdlib/nums.c | 8 +- stdlib/nums.h | 81 +++++++++++++-- stdlib/optionals.c | 1 + stdlib/paths.c | 2 +- stdlib/patterns.c | 6 +- stdlib/rng.c | 2 +- stdlib/stdlib.c | 1 + stdlib/text.c | 10 +- structs.c | 15 +-- test/integers.tm | 2 +- test/minmax.tm | 2 +- test/rng.tm | 2 +- tomo.c | 8 +- typecheck.c | 108 +++++++++++--------- typecheck.h | 2 + 30 files changed, 524 insertions(+), 376 deletions(-) diff --git a/Makefile b/Makefile index 7037226..a208c76 100644 --- a/Makefile +++ b/Makefile @@ -66,14 +66,14 @@ test: $(TESTS) @echo -e '\x1b[32;7m ALL TESTS PASSED! \x1b[m' clean: - rm -f tomo *.o stdlib/*.o libtomo.so test/*.tm.{c,h,o,testresult} examples/**/*.tm.{c,h,o} + rm -f tomo *.o stdlib/*.o libtomo.so test/*.tm.{c,h,o,testresult} examples/**/*.tm.{c,h,o} examples/*.tm.{c,h,o} %: %.md pandoc --lua-filter=.pandoc/bold-code.lua -s $< -t man -o $@ examples: - ./tomo examples/learnxiny.tm ./tomo -IL examples/vectors examples/base64 examples/log examples/ini examples/game examples/http examples/threads examples/tomodeps examples/tomo-install examples/wrap + ./tomo examples/learnxiny.tm install: tomo libtomo.so tomo.1 mkdir -p -m 755 "$(PREFIX)/man/man1" "$(PREFIX)/bin" "$(PREFIX)/include/tomo" "$(PREFIX)/lib" "$(PREFIX)/share/tomo/modules" diff --git a/compile.c b/compile.c index 884bcbd..aa8f645 100644 --- a/compile.c +++ b/compile.c @@ -13,6 +13,7 @@ #include "enums.h" #include "environment.h" #include "stdlib/integers.h" +#include "stdlib/nums.h" #include "stdlib/patterns.h" #include "stdlib/text.h" #include "stdlib/util.h" @@ -102,22 +103,20 @@ static bool promote(env_t *env, ast_t *ast, CORD *code, type_t *actual, type_t * return true; } - if (actual->tag == IntType && needed->tag == BigIntType) { - *code = CORD_all("I(", *code, ")"); - 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=FakeAST(InlineCCode, .code=*code, .type=actual)); + binding_t *constructor = NULL; + if ((constructor=get_constructor(env, needed, args, needed)) + || (constructor=get_constructor(env, actual, args, needed))) { + auto fn = Match(constructor->type, FunctionType); + if (fn->args->next == NULL) { + *code = CORD_all(constructor->code, "(", compile_arguments(env, ast, fn->args, args), ")"); + return true; + } + } } - if ((actual->tag == IntType || actual->tag == BigIntType) && needed->tag == NumType) { - *code = CORD_all(type_to_cord(actual), "_to_", type_to_cord(needed), "(", *code, ")"); - return true; - } - - if (actual->tag == NumType && needed->tag == IntType) - return false; - - if (actual->tag == IntType || actual->tag == NumType) - 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); @@ -527,7 +526,7 @@ CORD compile_type(type_t *t) else if (streq(text->lang, "Shell")) return "Shell_t"; else - return CORD_all(namespace_prefix(text->env, text->env->namespace->parent), text->lang, "_t"); + return CORD_all(namespace_prefix(text->env, text->env->namespace->parent), text->lang, "$$type"); } case ArrayType: return "Array_t"; case SetType: return "Table_t"; @@ -547,11 +546,11 @@ CORD compile_type(type_t *t) case PointerType: return CORD_cat(compile_type(Match(t, PointerType)->pointed), "*"); case StructType: { auto s = Match(t, StructType); - return CORD_all("struct ", namespace_prefix(s->env, s->env->namespace->parent), s->name, "_s"); + return CORD_all("struct ", namespace_prefix(s->env, s->env->namespace->parent), s->name, "$$struct"); } case EnumType: { auto e = Match(t, EnumType); - return CORD_all(namespace_prefix(e->env, e->env->namespace->parent), e->name, "_t"); + return CORD_all(namespace_prefix(e->env, e->env->namespace->parent), e->name, "$$type"); } case OptionalType: { type_t *nonnull = Match(t, OptionalType)->type; @@ -570,7 +569,7 @@ CORD compile_type(type_t *t) if (nonnull == MATCH_TYPE) return "OptionalMatch_t"; auto s = Match(nonnull, StructType); - return CORD_all(namespace_prefix(s->env, s->env->namespace->parent), "$Optional", s->name, "_t"); + return CORD_all(namespace_prefix(s->env, s->env->namespace->parent), "$Optional", s->name, "$$type"); } default: compiler_err(NULL, NULL, NULL, "Optional types are not supported for: %T", t); @@ -1848,10 +1847,18 @@ CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool CORD compile_to_type(env_t *env, ast_t *ast, type_t *t) { - if (ast->tag == Int && is_numeric_type(t)) + if (ast->tag == Int && is_numeric_type(t)) { return compile_int_to_type(env, ast, t); - if (ast->tag == None && Match(ast, None)->type == NULL) + } else if (ast->tag == Num && t->tag == NumType) { + double n = Match(ast, Num)->n; + switch (Match(t, NumType)->bits) { + case TYPE_NBITS64: return CORD_asprintf("N64(%.20g)", n); + case TYPE_NBITS32: return CORD_asprintf("N32(%.10g)", n); + default: code_err(ast, "This is not a valid number bit width"); + } + } else if (ast->tag == None && Match(ast, None)->type == NULL) { return compile_none(t); + } type_t *actual = get_type(env, ast); @@ -1886,7 +1893,7 @@ CORD compile_int_to_type(env_t *env, ast_t *ast, type_t *target) CORD code = compile(env, ast); type_t *actual_type = get_type(env, ast); if (!promote(env, ast, &code, actual_type, target)) - code = CORD_all(type_to_cord(actual_type), "_to_", type_to_cord(target), "(", code, ", no)"); + code_err(ast, "I couldn't promote this %T to a %T", actual_type, target); return code; } @@ -1968,9 +1975,10 @@ CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t 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"); - double n = Int_to_Num(int_val); - value = CORD_asprintf(Match(spec_arg->type, NumType)->bits == TYPE_NBITS64 - ? "N64(%.20g)" : "N32(%.10g)", n); + if (Match(spec_arg->type, NumType)->bits == TYPE_NBITS64) + value = CORD_asprintf("N64(%.20g)", Num$from_int(int_val, false)); + else + value = CORD_asprintf("N32(%.10g)", (double)Num32$from_int(int_val, false)); } else { env_t *arg_env = with_enum_scope(env, spec_arg->type); value = compile_maybe_incref(arg_env, call_arg->value, spec_arg->type); @@ -1994,9 +2002,10 @@ CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t 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"); - double n = Int_to_Num(int_val); - value = CORD_asprintf(Match(spec_arg->type, NumType)->bits == TYPE_NBITS64 - ? "N64(%.20g)" : "N32(%.10g)", n); + if (Match(spec_arg->type, NumType)->bits == TYPE_NBITS64) + value = CORD_asprintf("N64(%.20g)", Num$from_int(int_val, false)); + else + value = CORD_asprintf("N32(%.10g)", (double)Num32$from_int(int_val, false)); } else { env_t *arg_env = with_enum_scope(env, spec_arg->type); value = compile_maybe_incref(arg_env, call_arg->value, spec_arg->type); @@ -2226,22 +2235,6 @@ 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)); } -static CORD compile_num_to_type(ast_t *ast, type_t *type) -{ - double n = Match(ast, Num)->n; - - if (type->tag != NumType) - code_err(ast, "I can't compile a number literal to a %T", type); - - switch (Match(type, NumType)->bits) { - case TYPE_NBITS64: - return CORD_asprintf("N64(%.20g)", n); - case TYPE_NBITS32: - return CORD_asprintf("N32(%.10g)", n); - default: code_err(ast, "This is not a valid number bit width"); - } -} - CORD compile(env_t *env, ast_t *ast) { switch (ast->tag) { @@ -2273,7 +2266,7 @@ CORD compile(env_t *env, ast_t *ast) if (mpz_cmpabs_ui(i, BIGGEST_SMALL_INT) <= 0) { return CORD_asprintf("I_small(%s)", str); } else if (mpz_cmp_si(i, INT64_MAX) <= 0 && mpz_cmp_si(i, INT64_MIN) >= 0) { - return CORD_asprintf("Int64_to_Int(%s)", str); + return CORD_asprintf("Int$from_int64(%s)", str); } else { return CORD_asprintf("Int$from_str(\"%s\")", str); } @@ -3012,7 +3005,6 @@ CORD compile(env_t *env, ast_t *ast) if (entry->b->type->tag == ModuleType) continue; binding_t *b = get_binding(env, entry->name); - if (!b) printf("Couldn't find: %s\n", entry->name); assert(b); CORD binding_code = b->code; if (entry->b->type->tag == ArrayType) @@ -3371,6 +3363,12 @@ CORD 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: + 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, t); if (constructor) { arg_t *arg_spec = Match(constructor->type, FunctionType)->args; @@ -3378,65 +3376,7 @@ CORD compile(env_t *env, ast_t *ast) } type_t *actual = call->args ? get_type(env, call->args->value) : NULL; - if (t->tag == StructType) { - // Struct constructor: - fn_t = Type(FunctionType, .args=Match(t, StructType)->fields, .ret=t); - return CORD_all("((", compile_type(t), "){", compile_arguments(env, ast, Match(fn_t, FunctionType)->args, call->args), "})"); - } else if (is_numeric_type(t) && call->args && call->args->value->tag == Int) { - if (call->args->next) - code_err(call->args->next->value, "This is too many arguments to an integer literal constructor"); - return compile_int_to_type(env, call->args->value, t); - } else if (t->tag == NumType && call->args && call->args->value->tag == Num) { - if (call->args->next) - code_err(call->args->next->value, "This is too many arguments to a number literal constructor"); - return compile_num_to_type(call->args->value, t); - } else if (t->tag == NumType || t->tag == BigIntType) { - if (!call->args) code_err(ast, "This constructor needs a value"); - if (!call->args) - code_err(ast, "This constructor requires an argument!"); - - if (type_eq(actual, t)) { - if (call->args->next) - code_err(ast, "This is too many arguments!"); - return compile(env, call->args->value); - } - - arg_t *args = new(arg_t, .name="i", .type=actual); // No truncation argument - CORD arg_code = compile_arguments(env, ast, args, call->args); - if (is_numeric_type(actual)) { - return CORD_all(type_to_cord(actual), "_to_", type_to_cord(t), "(", arg_code, ")"); - } else if (actual->tag == BoolType) { - if (t->tag == NumType) { - return CORD_all("((", compile_type(t), ")(", arg_code, "))"); - } else { - return CORD_all("I((int)(", arg_code, "))"); - } - } else { - code_err(ast, "You cannot convert a %T to a %T this way.", actual, t); - } - } else if (t->tag == IntType || t->tag == ByteType) { - if (!call->args) - code_err(ast, "This constructor requires an argument!"); - - if (type_eq(actual, t)) { - if (call->args->next) - code_err(ast, "This is too many arguments!"); - return compile(env, call->args->value); - } - - if (is_numeric_type(actual)) { - arg_t *args = new(arg_t, .name="i", .type=actual, .next=new(arg_t, .name="truncate", .type=Type(BoolType), - .default_val=FakeAST(Bool, false))); - CORD arg_code = compile_arguments(env, ast, args, call->args); - return CORD_all(type_to_cord(actual), "_to_", type_to_cord(t), "(", arg_code, ")"); - } else if (actual->tag == BoolType) { - arg_t *args = new(arg_t, .name="i", .type=actual); - CORD arg_code = compile_arguments(env, ast, args, call->args); - return CORD_all("((", compile_type(t),")(", arg_code, "))"); - } else { - code_err(ast, "You cannot convert a %T to a %T this way.", actual, t); - } - } else if (t->tag == TextType) { + if (t->tag == TextType) { if (!call->args) code_err(ast, "This constructor needs a value"); const char *lang = Match(t, TextType)->lang; if (lang) @@ -3458,11 +3398,6 @@ CORD compile(env_t *env, ast_t *ast) else if (call->args->value->tag == TextJoin && Match(call->args->value, TextJoin)->children->next == NULL) return compile_string_literal(Match(Match(call->args->value, TextJoin)->children->ast, TextLiteral)->cord); return CORD_all("Text$as_c_string(", expr_as_text(env, compile(env, call->args->value), actual, "no"), ")"); - } else if (t->tag == MomentType) { - // Moment constructor: - binding_t *new_binding = get_binding(Match(fn_t, TypeInfoType)->env, "new"); - CORD arg_code = compile_arguments(env, ast, Match(new_binding->type, FunctionType)->args, call->args); - return CORD_all(new_binding->code, "(", arg_code, ")"); } else { code_err(call->fn, "This is not a type that has a constructor"); } @@ -3726,7 +3661,7 @@ CORD compile(env_t *env, ast_t *ast) CORD text = compile_to_pointer_depth(env, f->fielded, 0, false); return CORD_all("((Text_t)", text, ")"); } else if (streq(f->field, "length")) { - return CORD_all("Int64_to_Int((", compile_to_pointer_depth(env, f->fielded, 0, false), ").length)"); + return CORD_all("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").length)"); } code_err(ast, "There is no '%s' field on %T values", f->field, value_t); } @@ -3763,19 +3698,19 @@ CORD compile(env_t *env, ast_t *ast) } case ArrayType: { if (streq(f->field, "length")) - return CORD_all("Int64_to_Int((", compile_to_pointer_depth(env, f->fielded, 0, false), ").length)"); + return CORD_all("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").length)"); code_err(ast, "There is no %s field on arrays", f->field); } case SetType: { if (streq(f->field, "items")) return CORD_all("ARRAY_COPY((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries)"); else if (streq(f->field, "length")) - return CORD_all("Int64_to_Int((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries.length)"); + return CORD_all("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries.length)"); code_err(ast, "There is no '%s' field on sets", f->field); } case TableType: { if (streq(f->field, "length")) { - return CORD_all("Int64_to_Int((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries.length)"); + return CORD_all("Int$from_int64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries.length)"); } else if (streq(f->field, "keys")) { return CORD_all("ARRAY_COPY((", compile_to_pointer_depth(env, f->fielded, 0, false), ").entries)"); } else if (streq(f->field, "values")) { @@ -3835,12 +3770,14 @@ CORD compile(env_t *env, ast_t *ast) type_t *item_type = Match(container_t, ArrayType)->item_type; CORD arr = compile_to_pointer_depth(env, indexing->indexed, 0, false); file_t *f = indexing->index->file; + CORD index_code = indexing->index->tag == Int + ? compile_int_to_type(env, indexing->index, Type(IntType, .bits=TYPE_IBITS64)) + : (index_t->tag == BigIntType ? CORD_all("Int64$from_int(", compile(env, indexing->index), ", no)") + : CORD_all("(Int64_t)(", compile(env, indexing->index), ")")); if (indexing->unchecked) - return CORD_all("Array_get_unchecked(", compile_type(item_type), ", ", arr, ", ", - compile_int_to_type(env, indexing->index, Type(IntType, .bits=TYPE_IBITS64)), ")"); + return CORD_all("Array_get_unchecked(", compile_type(item_type), ", ", arr, ", ", index_code, ")"); else - return CORD_all("Array_get(", compile_type(item_type), ", ", arr, ", ", - compile_int_to_type(env, indexing->index, Type(IntType, .bits=TYPE_IBITS64)), ", ", + return CORD_all("Array_get(", compile_type(item_type), ", ", arr, ", ", index_code, ", ", CORD_asprintf("%ld", (int64_t)(indexing->index->start - f->text)), ", ", CORD_asprintf("%ld", (int64_t)(indexing->index->end - f->text)), ")"); @@ -3912,15 +3849,15 @@ CORD compile_type_info(env_t *env, type_t *t) return "&Shell$info"; else if (streq(text->lang, "Path")) return "&Path$info"; - return CORD_all("(&", namespace_prefix(text->env, text->env->namespace->parent), text->lang, ")"); + return CORD_all("(&", namespace_prefix(text->env, text->env->namespace->parent), text->lang, "$$info)"); } case StructType: { auto s = Match(t, StructType); - return CORD_all("(&", namespace_prefix(s->env, s->env->namespace->parent), s->name, ")"); + return CORD_all("(&", namespace_prefix(s->env, s->env->namespace->parent), s->name, "$$info)"); } case EnumType: { auto e = Match(t, EnumType); - return CORD_all("(&", namespace_prefix(e->env, e->env->namespace->parent), e->name, ")"); + return CORD_all("(&", namespace_prefix(e->env, e->env->namespace->parent), e->name, "$$info)"); } case ArrayType: { type_t *item_t = Match(t, ArrayType)->item_type; @@ -4295,7 +4232,7 @@ CORD compile_top_level_code(env_t *env, ast_t *ast) } case LangDef: { auto def = Match(ast, LangDef); - CORD code = CORD_asprintf("public const TypeInfo_t %r%s = {%zu, %zu, .metamethods=Text$metamethods, .tag=TextInfo, .TextInfo={%r}};\n", + CORD code = CORD_asprintf("public const TypeInfo_t %r%s$$info = {%zu, %zu, .metamethods=Text$metamethods, .tag=TextInfo, .TextInfo={%r}};\n", namespace_prefix(env, env->namespace), def->name, sizeof(Text_t), __alignof__(Text_t), CORD_quoted(def->name)); env_t *ns_env = namespace_env(env, def->name); @@ -4429,9 +4366,9 @@ CORD compile_statement_type_header(env_t *env, ast_t *ast) return CORD_all( // Constructor macro: "#define ", namespace_prefix(env, env->namespace), def->name, - "(text) ((", namespace_prefix(env, env->namespace), def->name, "_t){.length=sizeof(text)-1, .tag=TEXT_ASCII, .ascii=\"\" text})\n" + "(text) ((", namespace_prefix(env, env->namespace), def->name, "$$type){.length=sizeof(text)-1, .tag=TEXT_ASCII, .ascii=\"\" text})\n" "#define ", namespace_prefix(env, env->namespace), def->name, - "s(...) ((", namespace_prefix(env, env->namespace), def->name, "_t)Texts(__VA_ARGS__))\n" + "s(...) ((", namespace_prefix(env, env->namespace), def->name, "$$type)Texts(__VA_ARGS__))\n" "extern const TypeInfo_t ", full_name, ";\n" ); } @@ -4539,19 +4476,19 @@ static void _make_typedefs(compile_typedef_info_t *info, ast_t *ast) if (ast->tag == StructDef) { auto def = Match(ast, StructDef); CORD full_name = CORD_cat(namespace_prefix(info->env, info->env->namespace), def->name); - *info->header = CORD_all(*info->header, "typedef struct ", full_name, "_s ", full_name, "_t;\n"); + *info->header = CORD_all(*info->header, "typedef struct ", full_name, "$$struct ", full_name, "$$type;\n"); } else if (ast->tag == EnumDef) { auto def = Match(ast, EnumDef); CORD full_name = CORD_cat(namespace_prefix(info->env, info->env->namespace), def->name); - *info->header = CORD_all(*info->header, "typedef struct ", full_name, "_s ", full_name, "_t;\n"); + *info->header = CORD_all(*info->header, "typedef struct ", full_name, "$$struct ", full_name, "$$type;\n"); for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { if (!tag->fields) continue; - *info->header = CORD_all(*info->header, "typedef struct ", full_name, "$", tag->name, "_s ", full_name, "$", tag->name, "_t;\n"); + *info->header = CORD_all(*info->header, "typedef struct ", full_name, "$", tag->name, "$$struct ", full_name, "$", tag->name, "$$type;\n"); } } else if (ast->tag == LangDef) { auto def = Match(ast, LangDef); - *info->header = CORD_all(*info->header, "typedef Text_t ", namespace_prefix(info->env, info->env->namespace), def->name, "_t;\n"); + *info->header = CORD_all(*info->header, "typedef Text_t ", namespace_prefix(info->env, info->env->namespace), def->name, "$$type;\n"); } } diff --git a/enums.c b/enums.c index b91a99e..f913af8 100644 --- a/enums.c +++ b/enums.c @@ -34,7 +34,7 @@ CORD compile_enum_typeinfo(env_t *env, ast_t *ast) type_t *t = Table$str_get(*env->types, def->name); const char *metamethods = is_packed_data(t) ? "PackedDataEnum$metamethods" : "Enum$metamethods"; - CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {%zu, %zu, .metamethods=%s, {.tag=EnumInfo, .EnumInfo={.name=\"%s\", " + CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r$$info = {%zu, %zu, .metamethods=%s, {.tag=EnumInfo, .EnumInfo={.name=\"%s\", " ".num_tags=%d, .tags=(NamedType_t[]){", full_name, type_size(t), type_align(t), metamethods, def->name, num_tags); @@ -66,8 +66,8 @@ CORD compile_enum_constructors(env_t *env, ast_t *ast) if (field->next) arg_sig = CORD_cat(arg_sig, ", "); } if (arg_sig == CORD_EMPTY) arg_sig = "void"; - CORD constructor_impl = CORD_all("public inline ", full_name, "_t ", full_name, "$tagged$", tag->name, "(", arg_sig, ") { return (", - full_name, "_t){.tag=", full_name, "$tag$", tag->name, ", .$", tag->name, "={"); + CORD constructor_impl = CORD_all("public inline ", full_name, "$$type ", full_name, "$tagged$", tag->name, "(", arg_sig, ") { return (", + full_name, "$$type){.tag=", full_name, "$tag$", tag->name, ", .$", tag->name, "={"); for (arg_ast_t *field = tag->fields; field; field = field->next) { constructor_impl = CORD_all(constructor_impl, "$", field->name); if (field->next) constructor_impl = CORD_cat(constructor_impl, ", "); @@ -83,7 +83,7 @@ CORD compile_enum_header(env_t *env, ast_t *ast) auto def = Match(ast, EnumDef); CORD full_name = CORD_all(namespace_prefix(env, env->namespace), def->name); CORD all_defs = CORD_EMPTY; - CORD enum_def = CORD_all("struct ", full_name, "_s {\n" + CORD enum_def = CORD_all("struct ", full_name, "$$struct {\n" "\tenum { ", full_name, "$null=0, "); bool has_any_tags_with_fields = false; @@ -100,14 +100,14 @@ CORD compile_enum_header(env_t *env, ast_t *ast) if (!tag->fields) continue; CORD field_def = compile_struct_header(env, WrapAST(ast, StructDef, .name=CORD_to_const_char_star(CORD_all(def->name, "$", tag->name)), .fields=tag->fields)); all_defs = CORD_all(all_defs, field_def); - enum_def = CORD_all(enum_def, full_name, "$", tag->name, "_t $", tag->name, ";\n"); + enum_def = CORD_all(enum_def, full_name, "$", tag->name, "$$type $", tag->name, ";\n"); } enum_def = CORD_all(enum_def, "};\n"); } enum_def = CORD_all(enum_def, "};\n"); all_defs = CORD_all(all_defs, enum_def); - all_defs = CORD_all(all_defs, "extern const TypeInfo_t ", full_name, ";\n"); + all_defs = CORD_all(all_defs, "extern const TypeInfo_t ", full_name, "$$info;\n"); for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { if (!tag->fields) continue; @@ -118,7 +118,7 @@ CORD compile_enum_header(env_t *env, ast_t *ast) if (field->next) arg_sig = CORD_all(arg_sig, ", "); } if (arg_sig == CORD_EMPTY) arg_sig = "void"; - CORD constructor_def = CORD_all(full_name, "_t ", full_name, "$tagged$", tag->name, "(", arg_sig, ");\n"); + CORD constructor_def = CORD_all(full_name, "$$type ", full_name, "$tagged$", tag->name, "(", arg_sig, ");\n"); all_defs = CORD_all(all_defs, constructor_def); } return all_defs; diff --git a/environment.c b/environment.c index 3b4bb62..aaf6dfe 100644 --- a/environment.c +++ b/environment.c @@ -469,27 +469,114 @@ env_t *new_compilation_unit(CORD libname) } // Conversion constructors: -#define ADD_CONSTRUCTOR(ns_env, identifier, typestr) Array$insert(&ns_env->namespace->constructors, ((binding_t[1]){{.code=identifier, .type=Match(parse_type_string(ns_env, typestr), ClosureType)->fn}}), I(0), sizeof(binding_t)) - { - env_t *ns_env = namespace_env(env, "Pattern"); - ADD_CONSTRUCTOR(ns_env, "Pattern$escape_text", "func(text:Text -> Pattern)"); - ADD_CONSTRUCTOR(ns_env, "Int$value_as_text", "func(i:Int -> Pattern)"); - } - { - env_t *ns_env = namespace_env(env, "Path"); - ADD_CONSTRUCTOR(ns_env, "Path$escape_text", "func(text:Text -> Path)"); - ADD_CONSTRUCTOR(ns_env, "Path$escape_path", "func(path:Path -> Path)"); - ADD_CONSTRUCTOR(ns_env, "Int$value_as_text", "func(i:Int -> Path)"); - } - { - env_t *ns_env = namespace_env(env, "Shell"); - ADD_CONSTRUCTOR(ns_env, "Shell$escape_text", "func(text:Text -> Shell)"); - ADD_CONSTRUCTOR(ns_env, "Shell$escape_text", "func(path:Path -> Shell)"); - ADD_CONSTRUCTOR(ns_env, "Shell$escape_text_array", "func(texts:[Text] -> Shell)"); - ADD_CONSTRUCTOR(ns_env, "Shell$escape_text_array", "func(paths:[Path] -> Shell)"); - ADD_CONSTRUCTOR(ns_env, "Int$value_as_text", "func(i:Int -> Shell)"); - } -#undef ADD_CONSTRUCTOR +#define ADD_CONSTRUCTORS(type_name, ...) do {\ + env_t *ns_env = namespace_env(env, type_name); \ + struct { const char *c_name, *type_str; } constructor_infos[] = {__VA_ARGS__}; \ + for (size_t i = 0; i < sizeof(constructor_infos)/sizeof(constructor_infos[0]); i++) { \ + type_t *t = parse_type_string(ns_env, constructor_infos[i].type_str); \ + Array$insert(&ns_env->namespace->constructors, \ + ((binding_t[1]){{.code=constructor_infos[i].c_name, \ + .type=Match(t, ClosureType)->fn}}), I(0), sizeof(binding_t)); \ + } \ +} while (0) + + ADD_CONSTRUCTORS("Bool", + {"Bool$from_byte", "func(b:Byte -> Bool)"}, + {"Bool$from_int8", "func(i:Int8 -> Bool)"}, + {"Bool$from_int16", "func(i:Int16 -> Bool)"}, + {"Bool$from_int32", "func(i:Int32 -> Bool)"}, + {"Bool$from_int64", "func(i:Int64 -> Bool)"}, + {"Bool$from_int", "func(i:Int -> Bool)"}); + ADD_CONSTRUCTORS("Byte", + {"Byte$from_bool", "func(b:Bool -> Byte)"}, + {"Byte$from_int8", "func(i:Int8 -> Byte)"}, + {"Byte$from_int16", "func(i:Int16, truncate=no -> Byte)"}, + {"Byte$from_int32", "func(i:Int32, truncate=no -> Byte)"}, + {"Byte$from_int64", "func(i:Int64, truncate=no -> Byte)"}, + {"Byte$from_int", "func(i:Int, truncate=no -> Byte)"}); + ADD_CONSTRUCTORS("Int", + {"Int$from_bool", "func(b:Bool -> Int)"}, + {"Int$from_byte", "func(b:Byte -> Int)"}, + {"Int$from_int8", "func(i:Int8 -> Int)"}, + {"Int$from_int16", "func(i:Int16 -> Int)"}, + {"Int$from_int32", "func(i:Int32 -> Int)"}, + {"Int$from_int64", "func(i:Int64 -> Int)"}, + {"Int$from_num", "func(n:Num, truncate=no -> Int)"}, + {"Int$from_num32", "func(n:Num32, truncate=no -> Int)"}); + ADD_CONSTRUCTORS("Int64", + {"Int64$from_bool", "func(b:Bool -> Int64)"}, + {"Int64$from_byte", "func(b:Byte -> Int64)"}, + {"Int64$from_int8", "func(i:Int8 -> Int64)"}, + {"Int64$from_int16", "func(i:Int16 -> Int64)"}, + {"Int64$from_int32", "func(i:Int32 -> Int64)"}, + {"Int64$from_int", "func(i:Int, truncate=no -> Int64)"}, + {"Int64$from_num", "func(n:Num, truncate=no -> Int64)"}, + {"Int64$from_num32", "func(n:Num32, truncate=no -> Int64)"}); + ADD_CONSTRUCTORS("Int32", + {"Int32$from_bool", "func(b:Bool -> Int32)"}, + {"Int32$from_byte", "func(b:Byte -> Int32)"}, + {"Int32$from_int8", "func(i:Int8 -> Int32)"}, + {"Int32$from_int16", "func(i:Int16 -> Int32)"}, + {"Int32$from_int64", "func(i:Int64, truncate=no -> Int32)"}, + {"Int32$from_int", "func(i:Int, truncate=no -> Int32)"}, + {"Int32$from_num", "func(n:Num, truncate=no -> Int32)"}, + {"Int32$from_num32", "func(n:Num32, truncate=no -> Int32)"}); + ADD_CONSTRUCTORS("Int16", + {"Int16$from_bool", "func(b:Bool -> Int16)"}, + {"Int16$from_byte", "func(b:Byte -> Int16)"}, + {"Int16$from_int8", "func(i:Int8 -> Int16)"}, + {"Int16$from_int32", "func(i:Int32, truncate=no -> Int16)"}, + {"Int16$from_int64", "func(i:Int64, truncate=no -> Int16)"}, + {"Int16$from_int", "func(i:Int, truncate=no -> Int16)"}, + {"Int16$from_num", "func(n:Num, truncate=no -> Int16)"}, + {"Int16$from_num32", "func(n:Num32, truncate=no -> Int16)"}); + ADD_CONSTRUCTORS("Int8", + {"Int8$from_bool", "func(b:Bool -> Int8)"}, + {"Int8$from_byte", "func(b:Byte -> Int8)"}, + {"Int8$from_int16", "func(i:Int16, truncate=no -> Int8)"}, + {"Int8$from_int32", "func(i:Int32, truncate=no -> Int8)"}, + {"Int8$from_int64", "func(i:Int64, truncate=no -> Int8)"}, + {"Int8$from_int", "func(i:Int, truncate=no -> Int8)"}, + {"Int8$from_num", "func(n:Num, truncate=no -> Int8)"}, + {"Int8$from_num32", "func(n:Num32, truncate=no -> Int8)"}); + ADD_CONSTRUCTORS("Num", + {"Num$from_bool", "func(b:Bool -> Num)"}, + {"Num$from_byte", "func(b:Byte -> Num)"}, + {"Num$from_int8", "func(i:Int8 -> Num)"}, + {"Num$from_int16", "func(i:Int16 -> Num)"}, + {"Num$from_int32", "func(i:Int32 -> Num)"}, + {"Num$from_int64", "func(i:Int64, truncate=no -> Num)"}, + {"Num$from_int", "func(i:Int, truncate=no -> Num)"}, + {"Num$from_num32", "func(n:Num32 -> Num)"}); + ADD_CONSTRUCTORS("Num32", + {"Num32$from_bool", "func(b:Bool -> Num32)"}, + {"Num32$from_byte", "func(b:Byte -> Num32)"}, + {"Num32$from_int8", "func(i:Int8 -> Num32)"}, + {"Num32$from_int16", "func(i:Int16 -> Num32)"}, + {"Num32$from_int32", "func(i:Int32, truncate=no -> Num32)"}, + {"Num32$from_int64", "func(i:Int64, truncate=no -> Num32)"}, + {"Num32$from_int", "func(i:Int, truncate=no -> Num32)"}, + {"Num32$from_num", "func(n:Num -> Num32)"}); + ADD_CONSTRUCTORS("Pattern", + {"Pattern$escape_text", "func(text:Text -> Pattern)"}, + {"Int$value_as_text", "func(i:Int -> Pattern)"}); + ADD_CONSTRUCTORS("Path", + {"Path$escape_text", "func(text:Text -> Path)"}, + {"Path$escape_path", "func(path:Path -> Path)"}, + {"Int$value_as_text", "func(i:Int -> Path)"}); + ADD_CONSTRUCTORS("Shell", + {"Shell$escape_text", "func(text:Text -> Shell)"}, + {"Shell$escape_text", "func(path:Path -> Shell)"}, + {"Shell$escape_text_array", "func(texts:[Text] -> Shell)"}, + {"Shell$escape_text_array", "func(paths:[Path] -> Shell)"}, + {"Int$value_as_text", "func(i:Int -> Shell)"}); + ADD_CONSTRUCTORS("Moment", + {"Moment$now", "func(-> Moment)"}, + {"Moment$new", "func(year,month,day:Int,hour,minute=0,second=0.0,timezone=none:Text -> Moment)"}, + {"Moment$from_unix_timestamp", "func(timestamp:Int64 -> Moment)"}); + ADD_CONSTRUCTORS("RNG", {"RNG$new", "func(-> RNG)"}); + ADD_CONSTRUCTORS("Thread", {"Thread$new", "func(fn:func() -> Thread)"}); +#undef ADD_CONSTRUCTORS set_binding(namespace_env(env, "Shell"), "without_escaping", Type(FunctionType, .args=new(arg_t, .name="text", .type=TEXT_TYPE), diff --git a/examples/vectors/vectors.tm b/examples/vectors/vectors.tm index b2533be..c07880a 100644 --- a/examples/vectors/vectors.tm +++ b/examples/vectors/vectors.tm @@ -83,11 +83,13 @@ struct IVec2(x,y:Int): func divided_by(v:IVec2, divisor:Int->IVec2; inline): return IVec2(v.x/divisor, v.y/divisor) func length(v:IVec2->Num; inline): - return Num.sqrt(v.x*v.x + v.y*v.y) + x := Num(v.x) + y := Num(v.y) + return Num.sqrt(x*x + y*y) func dist(a,b:IVec2->Num; inline): return a:minus(b):length() func angle(v:IVec2->Num; inline): - return Num.atan2(v.y, v.x) + return Num.atan2(Num(v.y), Num(v.x)) struct IVec3(x,y,z:Int): ZERO := IVec3(0, 0, 0) @@ -106,7 +108,10 @@ struct IVec3(x,y,z:Int): func divided_by(v:IVec3, divisor:Int->IVec3; inline): return IVec3(v.x/divisor, v.y/divisor, v.z/divisor) func length(v:IVec3->Num; inline): - return Num.sqrt(v.x*v.x + v.y*v.y + v.z*v.z) + x := Num(v.x) + y := Num(v.y) + z := Num(v.z) + return Num.sqrt(x*x + y*y + z*z) func dist(a,b:IVec3->Num; inline): return a:minus(b):length() diff --git a/repl.c b/repl.c index 3b2e1af..474532e 100644 --- a/repl.c +++ b/repl.c @@ -177,10 +177,10 @@ static Int_t ast_to_int(env_t *env, ast_t *ast) number_t num; eval(env, ast, &num); switch (Match(t, IntType)->bits) { - case TYPE_IBITS64: return Int64_to_Int((int64_t)num.i64); - case TYPE_IBITS32: return Int32_to_Int(num.i32); - case TYPE_IBITS16: return Int16_to_Int(num.i16); - case TYPE_IBITS8: return Int8_to_Int(num.i8); + case TYPE_IBITS64: return Int$from_int64((int64_t)num.i64); + case TYPE_IBITS32: return Int$from_int32(num.i32); + case TYPE_IBITS16: return Int$from_int16(num.i16); + case TYPE_IBITS8: return Int$from_int8(num.i8); default: errx(1, "Invalid int bits"); } } @@ -196,12 +196,12 @@ static double ast_to_num(env_t *env, ast_t *ast) number_t num; eval(env, ast, &num); if (t->tag == BigIntType) - return Int_to_Num(num.integer); + return Num$from_int(num.integer, false); switch (Match(t, IntType)->bits) { - case TYPE_IBITS64: return (double)num.i64; - case TYPE_IBITS32: return (double)num.i32; - case TYPE_IBITS16: return (double)num.i16; - case TYPE_IBITS8: return (double)num.i8; + case TYPE_IBITS64: return Num$from_int64(num.i64, false); + case TYPE_IBITS32: return Num$from_int32(num.i32); + case TYPE_IBITS16: return Num$from_int16(num.i16); + case TYPE_IBITS8: return Num$from_int8(num.i8); default: errx(1, "Invalid int bits"); } } @@ -401,10 +401,10 @@ void eval(env_t *env, ast_t *ast, void *dest) return; \ } \ switch (Match(t, IntType)->bits) { \ - case 64: *(int64_t*)dest = Int_to_Int64(result, false); return; \ - case 32: *(int32_t*)dest = Int_to_Int32(result, false); return; \ - case 16: *(int16_t*)dest = Int_to_Int16(result, false); return; \ - case 8: *(int8_t*)dest = Int_to_Int8(result, false); return; \ + case 64: *(int64_t*)dest = Int64$from_int(result, false); return; \ + case 32: *(int32_t*)dest = Int32$from_int(result, false); return; \ + case 16: *(int16_t*)dest = Int16$from_int(result, false); return; \ + case 8: *(int8_t*)dest = Int8$from_int(result, false); return; \ default: errx(1, "Invalid int bits"); \ } \ break; \ @@ -467,7 +467,7 @@ void eval(env_t *env, ast_t *ast, void *dest) case ArrayType: { Array_t arr; eval(env, index->indexed, &arr); - int64_t raw_index = Int_to_Int64(ast_to_int(env, index->index), false); + int64_t raw_index = Int64$from_int(ast_to_int(env, index->index), false); int64_t index_int = raw_index; if (index_int < 1) index_int = arr.length + index_int + 1; if (index_int < 1 || index_int > arr.length) diff --git a/stdlib/arrays.c b/stdlib/arrays.c index 1f4b825..41d4674 100644 --- a/stdlib/arrays.c +++ b/stdlib/arrays.c @@ -7,6 +7,7 @@ #include "arrays.h" #include "integers.h" +#include "math.h" #include "metamethods.h" #include "optionals.h" #include "rng.h" @@ -51,7 +52,7 @@ public void Array$compact(Array_t *arr, int64_t padded_item_size) public void Array$insert(Array_t *arr, const void *item, Int_t int_index, int64_t padded_item_size) { - int64_t index = Int_to_Int64(int_index, false); + int64_t index = Int64$from_int(int_index, false); if (index <= 0) index = arr->length + index + 1; if (index < 1) index = 1; @@ -90,7 +91,7 @@ public void Array$insert(Array_t *arr, const void *item, Int_t int_index, int64_ public void Array$insert_all(Array_t *arr, Array_t to_insert, Int_t int_index, int64_t padded_item_size) { - int64_t index = Int_to_Int64(int_index, false); + int64_t index = Int64$from_int(int_index, false); if (to_insert.length == 0) return; @@ -163,10 +164,10 @@ public void Array$insert_all(Array_t *arr, Array_t to_insert, Int_t int_index, i public void Array$remove_at(Array_t *arr, Int_t int_index, Int_t int_count, int64_t padded_item_size) { - int64_t index = Int_to_Int64(int_index, false); + int64_t index = Int64$from_int(int_index, false); if (index < 1) index = arr->length + index + 1; - int64_t count = Int_to_Int64(int_count, false); + int64_t count = Int64$from_int(int_count, false); if (index < 1 || index > (int64_t)arr->length || count < 1) return; if (count > arr->length - index + 1) @@ -297,7 +298,7 @@ public Table_t Array$counts(Array_t arr, const TypeInfo_t *type) public Array_t Array$sample(Array_t arr, Int_t int_n, Array_t weights, RNG_t rng, int64_t padded_item_size) { - int64_t n = Int_to_Int64(int_n, false); + int64_t n = Int64$from_int(int_n, false); if (n < 0) fail("Cannot select a negative number of values"); @@ -399,7 +400,7 @@ public Array_t Array$to(Array_t array, Int_t last) public Array_t Array$by(Array_t array, Int_t int_stride, int64_t padded_item_size) { - int64_t stride = Int_to_Int64(int_stride, false); + int64_t stride = Int64$from_int(int_stride, false); // In the unlikely event that the stride value would be too large to fit in // a 15-bit integer, fall back to creating a copy of the array: if (unlikely(array.stride*stride < ARRAY_MIN_STRIDE || array.stride*stride > ARRAY_MAX_STRIDE)) { @@ -434,11 +435,11 @@ public Array_t Array$by(Array_t array, Int_t int_stride, int64_t padded_item_siz public Array_t Array$slice(Array_t array, Int_t int_first, Int_t int_last) { - int64_t first = Int_to_Int64(int_first, false); + int64_t first = Int64$from_int(int_first, false); if (first < 0) first = array.length + first + 1; - int64_t last = Int_to_Int64(int_last, false); + int64_t last = Int64$from_int(int_last, false); if (last < 0) last = array.length + last + 1; diff --git a/stdlib/arrays.h b/stdlib/arrays.h index 53681dc..0e75f0e 100644 --- a/stdlib/arrays.h +++ b/stdlib/arrays.h @@ -69,7 +69,7 @@ void Array$remove_item(Array_t *arr, void *item, Int_t max_removals, const TypeI #define Array$pop(arr_expr, index_expr, item_type, nonnone_var, nonnone_expr, none_expr, padded_item_size) ({ \ Array_t *arr = arr_expr; \ Int_t index = index_expr; \ - int64_t index64 = Int_to_Int64(index, false); \ + int64_t index64 = Int64$from_int(index, false); \ int64_t off = index64 + (index64 < 0) * (arr->length + 1) - 1; \ (off >= 0 && off < arr->length) ? ({ \ item_type nonnone_var = *(item_type*)(arr->data + off*arr->stride); \ diff --git a/stdlib/bools.h b/stdlib/bools.h index c1b344b..6d0300d 100644 --- a/stdlib/bools.h +++ b/stdlib/bools.h @@ -9,12 +9,17 @@ #include "optionals.h" #include "util.h" -#define Bool_t bool #define yes (Bool_t)true #define no (Bool_t)false PUREFUNC Text_t Bool$as_text(const void *b, bool colorize, const TypeInfo_t *type); OptionalBool_t Bool$parse(Text_t text); +MACROLIKE Bool_t Bool$from_int(Int_t i) { return (i.small != 0); } +MACROLIKE Bool_t Bool$from_int64(Int64_t i) { return (i != 0); } +MACROLIKE Bool_t Bool$from_int32(Int32_t i) { return (i != 0); } +MACROLIKE Bool_t Bool$from_int16(Int16_t i) { return (i != 0); } +MACROLIKE Bool_t Bool$from_int8(Int8_t i) { return (i != 0); } +MACROLIKE Bool_t Bool$from_byte(uint8_t b) { return (b != 0); } extern const TypeInfo_t Bool$info; diff --git a/stdlib/bytes.c b/stdlib/bytes.c index 1e889f6..b24a721 100644 --- a/stdlib/bytes.c +++ b/stdlib/bytes.c @@ -30,6 +30,29 @@ public Text_t Byte$hex(Byte_t byte, bool uppercase, bool prefix) { return text; } +public PUREFUNC Byte_t Byte$from_int(Int_t i, bool truncate) { + if unlikely (truncate && Int$compare_value(i, I_small(0xFF)) > 0) + fail("This value is too large to convert to a byte without truncation: %k", (Text_t[1]){Int$value_as_text(i)}); + else if unlikely (truncate && Int$compare_value(i, I_small(0)) < 0) + fail("Negative values can't be converted to bytes: %k", (Text_t[1]){Int$value_as_text(i)}); + return (i.small != 0); +} +public PUREFUNC Byte_t Byte$from_int64(Int64_t i, bool truncate) { + if unlikely (truncate && i != (Int64_t)(Byte_t)i) + fail("This value can't be converted to a byte without truncation: %ld", i); + return (Byte_t)i; +} +public PUREFUNC Byte_t Byte$from_int32(Int32_t i, bool truncate) { + if unlikely (truncate && i != (Int32_t)(Byte_t)i) + fail("This value can't be converted to a byte without truncation: %d", i); + return (Byte_t)i; +} +public PUREFUNC Byte_t Byte$from_int16(Int16_t i, bool truncate) { + if unlikely (truncate && i != (Int16_t)(Byte_t)i) + fail("This value can't be converted to a byte without truncation: %d", i); + return (Byte_t)i; +} + public const TypeInfo_t Byte$info = { .size=sizeof(Byte_t), .align=__alignof__(Byte_t), diff --git a/stdlib/bytes.h b/stdlib/bytes.h index 9bd4e1c..ac1b61a 100644 --- a/stdlib/bytes.h +++ b/stdlib/bytes.h @@ -5,23 +5,21 @@ #include #include +#include "datatypes.h" +#include "stdlib.h" #include "types.h" #include "util.h" -#define Byte_t uint8_t #define Byte(b) ((Byte_t)(b)) PUREFUNC Text_t Byte$as_text(const void *b, bool colorize, const TypeInfo_t *type); -#define Byte_to_Int64(b, _) ((Int64_t)(b)) -#define Byte_to_Int32(b, _) ((Int32_t)(b)) -#define Byte_to_Int16(b, _) ((Int16_t)(b)) -#define Byte_to_Int8(b, _) ((Int8_t)(b)) - -#define Int64_to_Byte(b, _) ((Byte_t)(b)) -#define Int32_to_Byte(b, _) ((Byte_t)(b)) -#define Int16_to_Byte(b, _) ((Byte_t)(b)) -#define Int8_to_Byte(b, _) ((Byte_t)(b)) +Byte_t Byte$from_int(Int_t i, bool truncate); +Byte_t Byte$from_int64(int64_t i, bool truncate); +Byte_t Byte$from_int32(int32_t i, bool truncate); +Byte_t Byte$from_int16(int16_t i, bool truncate); +MACROLIKE Byte_t Byte$from_int8(int8_t i) { return (Byte_t)i; } +MACROLIKE Byte_t Byte$from_bool(bool b) { return (Byte_t)b; } extern const Byte_t Byte$min; extern const Byte_t Byte$max; diff --git a/stdlib/datatypes.h b/stdlib/datatypes.h index af37446..e131239 100644 --- a/stdlib/datatypes.h +++ b/stdlib/datatypes.h @@ -19,6 +19,16 @@ #define ARRAY_MAX_DATA_REFCOUNT MAX_FOR_N_BITS(ARRAY_REFCOUNT_BITS) #define ARRAY_MAX_FREE_ENTRIES MAX_FOR_N_BITS(ARRAY_FREE_BITS) +#define Num_t double +#define Num32_t float + +#define Int64_t int64_t +#define Int32_t int32_t +#define Int16_t int16_t +#define Int8_t int8_t +#define Byte_t uint8_t +#define Bool_t bool + typedef union { int64_t small; mpz_t *big; diff --git a/stdlib/integers.c b/stdlib/integers.c index c60f237..dbacda9 100644 --- a/stdlib/integers.c +++ b/stdlib/integers.c @@ -74,7 +74,7 @@ public PUREFUNC uint64_t Int$hash(const void *vx, const TypeInfo_t*) { } public Text_t Int$format(Int_t i, Int_t digits_int) { - int64_t digits = Int_to_Int64(digits_int, false); + int64_t digits = Int64$from_int(digits_int, false); if (likely(i.small & 1)) { return Text$format("%0.*ld", digits, (i.small)>>2); } else { @@ -97,7 +97,7 @@ public Text_t Int$hex(Int_t i, Int_t digits_int, bool uppercase, bool prefix) { if (Int$is_negative(i)) return Text$concat(Text("-"), Int$hex(Int$negative(i), digits_int, uppercase, prefix)); - int64_t digits = Int_to_Int64(digits_int, false); + int64_t digits = Int64$from_int(digits_int, false); if (likely(i.small & 1)) { const char *hex_fmt = uppercase ? (prefix ? "0x%0.*lX" : "%0.*lX") : (prefix ? "0x%0.*lx" : "%0.*lx"); return Text$format(hex_fmt, digits, (i.small)>>2); @@ -124,7 +124,7 @@ public Text_t Int$octal(Int_t i, Int_t digits_int, bool prefix) { if (Int$is_negative(i)) return Text$concat(Text("-"), Int$octal(Int$negative(i), digits_int, prefix)); - int64_t digits = Int_to_Int64(digits_int, false); + int64_t digits = Int64$from_int(digits_int, false); if (likely(i.small & 1)) { const char *octal_fmt = prefix ? "0o%0.*lo" : "%0.*lo"; return Text$format(octal_fmt, digits, (i.small)>>2); @@ -221,7 +221,7 @@ public Int_t Int$slow_modulo1(Int_t x, Int_t modulus) public Int_t Int$slow_left_shifted(Int_t x, Int_t y) { - mp_bitcnt_t bits = (mp_bitcnt_t)Int_to_Int64(y, false); + mp_bitcnt_t bits = (mp_bitcnt_t)Int64$from_int(y, false); mpz_t result; mpz_init_set_int(result, x); mpz_mul_2exp(result, result, bits); @@ -230,7 +230,7 @@ public Int_t Int$slow_left_shifted(Int_t x, Int_t y) public Int_t Int$slow_right_shifted(Int_t x, Int_t y) { - mp_bitcnt_t bits = (mp_bitcnt_t)Int_to_Int64(y, false); + mp_bitcnt_t bits = (mp_bitcnt_t)Int64$from_int(y, false); mpz_t result; mpz_init_set_int(result, x); mpz_tdiv_q_2exp(result, result, bits); @@ -300,7 +300,7 @@ public Int_t Int$abs(Int_t x) public Int_t Int$power(Int_t base, Int_t exponent) { - int64_t exp = Int_to_Int64(exponent, false); + int64_t exp = Int64$from_int(exponent, false); if (unlikely(exp < 0)) fail("Cannot take a negative power of an integer!"); mpz_t result; @@ -397,7 +397,7 @@ public bool Int$is_prime(Int_t x, Int_t reps) mpz_init_set_int(p, x); if (unlikely(Int$compare_value(reps, I(9999)) > 0)) fail("Number of prime-test repetitions should not be above 9999"); - int reps_int = Int_to_Int32(reps, false); + int reps_int = Int32$from_int(reps, false); return (mpz_probab_prime_p(p, reps_int) != 0); } @@ -426,7 +426,7 @@ public Int_t Int$choose(Int_t n, Int_t k) mpz_t ret; mpz_init(ret); - int64_t k_i64 = Int_to_Int64(k, false); + int64_t k_i64 = Int64$from_int(k, false); if unlikely (k_i64 < 0) fail("Negative inputs are not supported for choose()"); @@ -444,7 +444,7 @@ public Int_t Int$factorial(Int_t n) { mpz_t ret; mpz_init(ret); - int64_t n_i64 = Int_to_Int64(n, false); + int64_t n_i64 = Int64$from_int(n, false); if unlikely (n_i64 < 0) fail("Factorials are not defined for negative numbers"); mpz_fac_ui(ret, (unsigned long)n_i64); @@ -556,15 +556,14 @@ public void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t return *(c_type*)x == *(c_type*)y; \ } \ public Text_t KindOfInt ## $format(c_type i, Int_t digits_int) { \ - Int_t as_int = KindOfInt##_to_Int(i); \ - return Int$format(as_int, digits_int); \ + return Text$format("%0*ld", Int32$from_int(digits_int, false), i); \ } \ public Text_t KindOfInt ## $hex(c_type i, Int_t digits_int, bool uppercase, bool prefix) { \ - Int_t as_int = KindOfInt##_to_Int(i); \ + Int_t as_int = Int$from_int64((int64_t)i); \ return Int$hex(as_int, digits_int, uppercase, prefix); \ } \ public Text_t KindOfInt ## $octal(c_type i, Int_t digits_int, bool prefix) { \ - Int_t as_int = KindOfInt##_to_Int(i); \ + Int_t as_int = Int$from_int64((int64_t)i); \ return Int$octal(as_int, digits_int, prefix); \ } \ public Array_t KindOfInt ## $bits(c_type x) { \ @@ -615,7 +614,7 @@ public void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t if (Int$compare_value(full_int, I(max_val)) > 0) { \ return (Optional ## KindOfInt ## _t){.is_none=true}; \ } \ - return (Optional ## KindOfInt ## _t){.i=Int_to_ ## KindOfInt(full_int, true)}; \ + return (Optional ## KindOfInt ## _t){.i=KindOfInt##$from_int(full_int, true)}; \ } \ public CONSTFUNC c_type KindOfInt ## $gcd(c_type x, c_type y) { \ if (x == 0 || y == 0) return 0; \ diff --git a/stdlib/integers.h b/stdlib/integers.h index 7bed455..c78cabf 100644 --- a/stdlib/integers.h +++ b/stdlib/integers.h @@ -8,15 +8,10 @@ #include #include "datatypes.h" -#include "nums.h" #include "stdlib.h" #include "types.h" #include "util.h" -#define Int64_t int64_t -#define Int32_t int32_t -#define Int16_t int16_t -#define Int8_t int8_t #define I64(x) ((int64_t)x) #define I32(x) ((int32_t)x) #define I16(x) ((int16_t)x) @@ -40,6 +35,8 @@ MACROLIKE PUREFUNC c_type type_name ## $clamped(c_type x, c_type min, c_type max) { \ return x < min ? min : (x > max ? max : x); \ } \ + MACROLIKE CONSTFUNC c_type type_name ## $from_byte(Byte_t b) { return (c_type)b; } \ + MACROLIKE CONSTFUNC c_type type_name ## $from_bool(Bool_t b) { return (c_type)b; } \ CONSTFUNC c_type type_name ## $gcd(c_type x, c_type y); \ extern const c_type type_name ## $min, type_name##$max; \ extern const TypeInfo_t type_name ## $info; \ @@ -126,8 +123,8 @@ OptionalInt_t Int$sqrt(Int_t i); else mpz_init_set(mpz, *(i).big); \ } while (0) -#define I(i) ((i) >= SMALLEST_SMALL_INT && (i) <= BIGGEST_SMALL_INT ? ((Int_t){.small=(int64_t)((uint64_t)(i)<<2)|1}) : Int64_to_Int(i)) #define I_small(i) ((Int_t){.small=(int64_t)((uint64_t)(i)<<2)|1}) +#define I(i) _Generic(i, int8_t: I_small(i), int16_t: I_small(i), default: Int$from_int64(i)) #define I_is_zero(i) ((i).small == 1) Int_t Int$slow_plus(Int_t x, Int_t y); @@ -273,113 +270,104 @@ MACROLIKE PUREFUNC bool Int$is_negative(Int_t x) { return Int$compare_value(x, I_small(0)) < 0; } -// Conversion functions: +// Constructors/conversion functions: -MACROLIKE Int_t Int64_to_Int(int64_t i) -{ +// Int constructors: +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +MACROLIKE PUREFUNC Int_t Int$from_num(double n, bool truncate) { + mpz_t result; + mpz_init_set_d(result, n); + if unlikely (!truncate && mpz_get_d(result) != n) + fail("Could not convert to an integer without truncation: %g", n); + return Int$from_mpz(result); +} +#pragma GCC diagnostic pop +MACROLIKE PUREFUNC Int_t Int$from_num32(float n, bool truncate) { return Int$from_num((double)n, truncate); } +MACROLIKE Int_t Int$from_int64(int64_t i) { if likely (i >= SMALLEST_SMALL_INT && i <= BIGGEST_SMALL_INT) return (Int_t){.small=(i<<2)|1}; mpz_t result; mpz_init_set_si(result, i); return Int$from_mpz(result); } +MACROLIKE CONSTFUNC Int_t Int$from_int32(Int32_t i) { return Int$from_int64((Int32_t)i); } +MACROLIKE CONSTFUNC Int_t Int$from_int16(Int16_t i) { return I_small(i); } +MACROLIKE CONSTFUNC Int_t Int$from_int8(Int8_t i) { return I_small(i); } +MACROLIKE CONSTFUNC Int_t Int$from_byte(Byte_t b) { return I_small(b); } +MACROLIKE CONSTFUNC Int_t Int$from_bool(Bool_t b) { return I_small(b); } -#define Int32_to_Int(i, ...) Int64_to_Int(i) -#define Int16_to_Int(i, ...) Int64_to_Int(i) -#define Int8_to_Int(i, ...) Int64_to_Int(i) -#define Int32_to_Int64(i, ...) (Int64_t)(i) -#define Int16_to_Int64(i, ...) (Int64_t)(i) -#define Int8_to_Int64(i, ...) (Int64_t)(i) -#define Int16_to_Int32(i, ...) (Int32_t)(i) -#define Int8_to_Int32(i, ...) (Int32_t)(i) -#define Int8_to_Int16(i, ...) (Int16_t)(i) - -MACROLIKE PUREFUNC Int64_t Int_to_Int64(Int_t i, bool truncate) { +// Int64 constructors +MACROLIKE PUREFUNC Int64_t Int64$from_int(Int_t i, bool truncate) { if likely (i.small & 1) return (int64_t)(i.small >> 2); if (!truncate && unlikely(!mpz_fits_slong_p(*i.big))) - fail("Integer is too big to fit in a 64-bit integer!"); + fail("Integer is too big to fit in a 64-bit integer: %k", (Text_t[1]){Int$value_as_text(i)}); return mpz_get_si(*i.big); } +MACROLIKE CONSTFUNC Int64_t Int64$from_int32(Int32_t i) { return (Int64_t)i; } +MACROLIKE CONSTFUNC Int64_t Int64$from_int16(Int16_t i) { return (Int64_t)i; } +MACROLIKE CONSTFUNC Int64_t Int64$from_int8(Int8_t i) { return (Int64_t)i; } -MACROLIKE PUREFUNC Int32_t Int_to_Int32(Int_t i, bool truncate) { - int64_t i64 = Int_to_Int64(i, truncate); +// Int32 constructors +MACROLIKE PUREFUNC Int32_t Int32$from_int(Int_t i, bool truncate) { + int64_t i64 = Int64$from_int(i, truncate); int32_t i32 = (int32_t)i64; if (!truncate && unlikely(i64 != i32)) - fail("Integer is too big to fit in a 32-bit integer!"); + fail("Integer is too big to fit in a 32-bit integer: %k", (Text_t[1]){Int$value_as_text(i)}); return i32; } +MACROLIKE PUREFUNC Int32_t Int32$from_int64(Int64_t i, bool truncate) { + if (!truncate && unlikely(i != (Int64_t)(Int32_t)i)) + fail("Integer is too big to fit in a 32-bit integer: %ld", i); + return (Int32_t)i; +} +MACROLIKE CONSTFUNC Int32_t Int32$from_int16(Int16_t i) { return (Int32_t)i; } +MACROLIKE CONSTFUNC Int32_t Int32$from_int8(Int8_t i) { return (Int32_t)i; } -MACROLIKE PUREFUNC Int16_t Int_to_Int16(Int_t i, bool truncate) { - int64_t i64 = Int_to_Int64(i, truncate); +// Int16 constructors +MACROLIKE PUREFUNC Int16_t Int16$from_int(Int_t i, bool truncate) { + int64_t i64 = Int64$from_int(i, truncate); int16_t i16 = (int16_t)i64; if (!truncate && unlikely(i64 != i16)) fail("Integer is too big to fit in a 16-bit integer!"); return i16; } -MACROLIKE PUREFUNC Int8_t Int_to_Int8(Int_t i, bool truncate) { - int64_t i64 = Int_to_Int64(i, truncate); +MACROLIKE PUREFUNC Int16_t Int16$from_int64(Int64_t i, bool truncate) { + if (!truncate && unlikely(i != (Int64_t)(Int16_t)i)) + fail("Integer is too big to fit in a 16-bit integer: %ld", i); + return (Int16_t)i; +} +MACROLIKE PUREFUNC Int16_t Int16$from_int32(Int32_t i, bool truncate) { + if (!truncate && unlikely(i != (Int32_t)(Int16_t)i)) + fail("Integer is too big to fit in a 16-bit integer: %ld", i); + return (Int16_t)i; +} +MACROLIKE CONSTFUNC Int16_t Int16$from_int8(Int8_t i) { return (Int16_t)i; } + +// Int8 constructors +MACROLIKE PUREFUNC Int8_t Int8$from_int(Int_t i, bool truncate) { + int64_t i64 = Int64$from_int(i, truncate); int8_t i8 = (int8_t)i64; if (!truncate && unlikely(i64 != i8)) fail("Integer is too big to fit in an 8-bit integer!"); return i8; } - -MACROLIKE PUREFUNC Int_t Num_to_Int(double n) -{ - mpz_t result; - mpz_init_set_d(result, n); - return Int$from_mpz(result); +MACROLIKE PUREFUNC Int8_t Int8$from_int64(Int64_t i, bool truncate) { + if (!truncate && unlikely(i != (Int64_t)(Int8_t)i)) + fail("Integer is too big to fit in a 8-bit integer: %ld", i); + return (Int8_t)i; } - -MACROLIKE PUREFUNC double Int_to_Num(Int_t i) -{ - if likely (i.small & 1) - return (double)(i.small >> 2); - - return mpz_get_d(*i.big); +MACROLIKE PUREFUNC Int8_t Int8$from_int32(Int32_t i, bool truncate) { + if (!truncate && unlikely(i != (Int32_t)(Int8_t)i)) + fail("Integer is too big to fit in a 8-bit integer: %ld", i); + return (Int8_t)i; +} +MACROLIKE PUREFUNC Int8_t Int8$from_int16(Int16_t i, bool truncate) { + if (!truncate && unlikely(i != (Int16_t)(Int8_t)i)) + fail("Integer is too big to fit in a 8-bit integer: %ld", i); + return (Int8_t)i; } - -#define Int_to_Num32(i) (Num32_t)Int_to_Num(i) - -#define CONVERSION_FUNC(hi, lo) \ - MACROLIKE PUREFUNC int##lo##_t Int##hi##_to_Int##lo(int##hi##_t i, bool truncate) { \ - if (!truncate && unlikely(i != (int##lo##_t)i)) \ - fail("Cannot truncate the Int" #hi " %ld to an Int" #lo, (int64_t)i); \ - return (int##lo##_t)i; \ - } - -CONVERSION_FUNC(64, 32) -CONVERSION_FUNC(64, 16) -CONVERSION_FUNC(64, 8) -CONVERSION_FUNC(32, 16) -CONVERSION_FUNC(32, 8) -CONVERSION_FUNC(16, 8) -#undef CONVERSION_FUNC - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wfloat-equal" -#define CONVERSION_FUNC(num, int_type) \ - MACROLIKE PUREFUNC int_type##_t num##_to_##int_type(num##_t n, bool truncate) { \ - num##_t rounded = (num##_t)round((double)n); \ - if (!truncate && unlikely((num##_t)(int_type##_t)rounded != rounded)) \ - fail("Cannot truncate the " #num " %g to an " #int_type, (double)rounded); \ - return (int_type##_t)rounded; \ - } \ - MACROLIKE PUREFUNC num##_t int_type##_to_##num(int_type##_t n) { \ - return (num##_t)n; \ - } - -CONVERSION_FUNC(Num, Int64) -CONVERSION_FUNC(Num, Int32) -CONVERSION_FUNC(Num, Int16) -CONVERSION_FUNC(Num, Int8) -CONVERSION_FUNC(Num32, Int64) -CONVERSION_FUNC(Num32, Int32) -CONVERSION_FUNC(Num32, Int16) -CONVERSION_FUNC(Num32, Int8) -#pragma GCC diagnostic pop -#undef CONVERSION_FUNC // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/moments.c b/stdlib/moments.c index e1f5960..1fadd2e 100644 --- a/stdlib/moments.c +++ b/stdlib/moments.c @@ -8,6 +8,7 @@ #include #include "datatypes.h" +#include "math.h" #include "moments.h" #include "optionals.h" #include "patterns.h" @@ -60,11 +61,11 @@ public Moment_t Moment$now(void) public Moment_t Moment$new(Int_t year, Int_t month, Int_t day, Int_t hour, Int_t minute, double second, OptionalText_t timezone) { struct tm info = { - .tm_min=Int_to_Int32(minute, false), - .tm_hour=Int_to_Int32(hour, false), - .tm_mday=Int_to_Int32(day, false), - .tm_mon=Int_to_Int32(month, false) - 1, - .tm_year=Int_to_Int32(year, false) - 1900, + .tm_min=Int32$from_int(minute, false), + .tm_hour=Int32$from_int(hour, false), + .tm_mday=Int32$from_int(day, false), + .tm_mon=Int32$from_int(month, false) - 1, + .tm_year=Int32$from_int(year, false) - 1900, .tm_isdst=-1, }; @@ -81,9 +82,9 @@ public Moment_t Moment$after(Moment_t moment, double seconds, double minutes, do struct tm info = {}; WITH_TIMEZONE(timezone, localtime_r(&moment.tv_sec, &info)); - info.tm_mday += Int_to_Int32(days, false) + 7*Int_to_Int32(weeks, false); - info.tm_mon += Int_to_Int32(months, false); - info.tm_year += Int_to_Int32(years, false); + info.tm_mday += Int32$from_int(days, false) + 7*Int32$from_int(weeks, false); + info.tm_mon += Int32$from_int(months, false); + info.tm_year += Int32$from_int(years, false); time_t t = mktime(&info); return (Moment_t){ diff --git a/stdlib/nums.c b/stdlib/nums.c index 47eb685..b227ccb 100644 --- a/stdlib/nums.c +++ b/stdlib/nums.c @@ -50,11 +50,11 @@ public CONSTFUNC bool Num$near(double a, double b, double ratio, double absolute } public Text_t Num$format(double f, Int_t precision) { - return Text$format("%.*f", (int)Int_to_Int64(precision, false), f); + return Text$format("%.*f", (int)Int64$from_int(precision, false), f); } public Text_t Num$scientific(double f, Int_t precision) { - return Text$format("%.*e", (int)Int_to_Int64(precision, false), f); + return Text$format("%.*e", (int)Int64$from_int(precision, false), f); } public CONSTFUNC double Num$mod(double num, double modulus) { @@ -125,11 +125,11 @@ public CONSTFUNC bool Num32$near(float a, float b, float ratio, float absolute) } public Text_t Num32$format(float f, Int_t precision) { - return Text$format("%.*f", (int)Int_to_Int64(precision, false), (double)f); + return Text$format("%.*f", (int)Int64$from_int(precision, false), (double)f); } public Text_t Num32$scientific(float f, Int_t precision) { - return Text$format("%.*e", (int)Int_to_Int64(precision, false), (double)f); + return Text$format("%.*e", (int)Int64$from_int(precision, false), (double)f); } public CONSTFUNC float Num32$mod(float num, float modulus) { diff --git a/stdlib/nums.h b/stdlib/nums.h index f000ca8..0b4cdc1 100644 --- a/stdlib/nums.h +++ b/stdlib/nums.h @@ -6,15 +6,16 @@ #include #include +#include "datatypes.h" +#include "integers.h" +#include "stdlib.h" #include "types.h" #include "util.h" -#define Num_t double -#define Num32_t float #define OptionalNum_t double #define OptionalNum32_t float -#define N32(n) ((float)n) -#define N64(n) ((double)n) +#define N32(n) ((float)(n)) +#define N64(n) ((double)(n)) Text_t Num$as_text(const void *f, bool colorize, const TypeInfo_t *type); PUREFUNC int32_t Num$compare(const void *x, const void *y, const TypeInfo_t *type); @@ -32,6 +33,38 @@ OptionalNum_t Num$parse(Text_t text); MACROLIKE CONSTFUNC double Num$clamped(double x, double low, double high) { return (x <= low) ? low : (x >= high ? high : x); } +MACROLIKE CONSTFUNC double Num$from_num32(Num32_t n) { return (double)n; } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +MACROLIKE CONSTFUNC double Num$from_int(Int_t i, bool truncate) { + if likely (i.small & 0x1) { + double ret = (double)(i.small >> 2); + if unlikely (!truncate && (int64_t)ret != (i.small >> 2)) + fail("Could not convert integer to 64-bit floating point without losing precision: %ld", i.small >> 2); + return ret; + } else { + double ret = mpz_get_d(*i.big); + if (!truncate) { + mpz_t roundtrip; + mpz_init_set_d(roundtrip, ret); + if unlikely (mpz_cmp(*i.big, roundtrip) != 0) + fail("Could not convert integer to 64-bit floating point without losing precision: %k", (Text_t[1]){Int$value_as_text(i)}); + } + return ret; + } +} +#pragma GCC diagnostic pop +MACROLIKE CONSTFUNC double Num$from_int64(Int64_t i, bool truncate) { + double n = (double)i; + if unlikely (!truncate && (Int64_t)n != i) + fail("Could not convert integer to 64-bit floating point without losing precision: %ld", i); + return n; +} +MACROLIKE CONSTFUNC double Num$from_int32(Int32_t i) { return (double)i; } +MACROLIKE CONSTFUNC double Num$from_int16(Int16_t i) { return (double)i; } +MACROLIKE CONSTFUNC double Num$from_int8(Int8_t i) { return (double)i; } +MACROLIKE CONSTFUNC double Num$from_byte(Byte_t i) { return (double)i; } + extern const TypeInfo_t Num$info; Text_t Num32$as_text(const void *f, bool colorize, const TypeInfo_t *type); @@ -50,9 +83,43 @@ float Num32$nan(Text_t tag); MACROLIKE CONSTFUNC float Num32$clamped(float x, float low, float high) { return (x <= low) ? low : (x >= high ? high : x); } +MACROLIKE CONSTFUNC float Num32$from_num(Num_t n) { return (float)n; } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +MACROLIKE CONSTFUNC float Num32$from_int(Int_t i, bool truncate) { + if likely (i.small & 0x1) { + float ret = (float)(i.small >> 2); + if unlikely (!truncate && (int64_t)ret != (i.small >> 2)) + fail("Could not convert integer to 32-bit floating point without losing precision: %ld", i.small >> 2); + return ret; + } else { + float ret = (float)mpz_get_d(*i.big); + if (!truncate) { + mpz_t roundtrip; + mpz_init_set_d(roundtrip, ret); + if unlikely (mpz_cmp(*i.big, roundtrip) != 0) + fail("Could not convert integer to 32-bit floating point without losing precision: %k", (Text_t[1]){Int$value_as_text(i)}); + } + return ret; + } +} +#pragma GCC diagnostic pop +MACROLIKE CONSTFUNC float Num32$from_int64(Int64_t i, bool truncate) { + float n = (float)i; + if unlikely (!truncate && (Int64_t)n != i) + fail("Could not convert integer to 32-bit floating point without losing precision: %ld", i); + return n; +} +MACROLIKE CONSTFUNC float Num32$from_int32(Int32_t i, bool truncate) { + float n = (float)i; + if unlikely (!truncate && (Int32_t)n != i) + fail("Could not convert integer to 32-bit floating point without losing precision: %d", i); + return n; +} +MACROLIKE CONSTFUNC float Num32$from_int16(Int16_t i) { return (float)i; } +MACROLIKE CONSTFUNC float Num32$from_int8(Int8_t i) { return (float)i; } +MACROLIKE CONSTFUNC float Num32$from_byte(Byte_t i) { return (float)i; } + extern const TypeInfo_t Num32$info; -#define Num_to_Num32(n) ((Num32_t)(n)) -#define Num32_to_Num(n) ((Num_t)(n)) - // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/optionals.c b/stdlib/optionals.c index 990ca13..d91ebff 100644 --- a/stdlib/optionals.c +++ b/stdlib/optionals.c @@ -8,6 +8,7 @@ #include "integers.h" #include "metamethods.h" #include "moments.h" +#include "nums.h" #include "patterns.h" #include "text.h" #include "threads.h" diff --git a/stdlib/paths.c b/stdlib/paths.c index 98bcab5..a42dc5a 100644 --- a/stdlib/paths.c +++ b/stdlib/paths.c @@ -277,7 +277,7 @@ public OptionalArray_t Path$read_bytes(Path_t path, OptionalInt_t count) if (fstat(fd, &sb) != 0) return NONE_ARRAY; - int64_t const target_count = count.small ? Int_to_Int64(count, false) : INT64_MAX; + int64_t const target_count = count.small ? Int64$from_int(count, false) : INT64_MAX; if (target_count < 0) fail("Cannot read a negative number of bytes!"); diff --git a/stdlib/patterns.c b/stdlib/patterns.c index ad6267f..7d6ccd3 100644 --- a/stdlib/patterns.c +++ b/stdlib/patterns.c @@ -807,7 +807,7 @@ static int64_t _find(Text_t text, Pattern_t pattern, int64_t first, int64_t last public OptionalMatch_t Text$find(Text_t text, Pattern_t pattern, Int_t from_index) { - int64_t first = Int_to_Int64(from_index, false); + int64_t first = Int64$from_int(from_index, false); if (first == 0) fail("Invalid index: 0"); if (first < 0) first = text.length + first + 1; if (first > text.length || first < 1) @@ -874,7 +874,7 @@ public Array_t Text$find_all(Text_t text, Pattern_t pattern) OptionalMatch_t m = Text$find(text, pattern, I(i)); if (!m.index.small) break; - i = Int_to_Int64(m.index, false) + m.text.length; + i = Int64$from_int(m.index, false) + m.text.length; Array$insert(&matches, &m, I_small(0), sizeof(Match_t)); } return matches; @@ -888,7 +888,7 @@ typedef struct { static OptionalMatch_t next_match(match_iter_state_t *state) { - if (Int_to_Int64(state->i, false) > state->state.stack[0].text.length) + if (Int64$from_int(state->i, false) > state->state.stack[0].text.length) return NONE_MATCH; OptionalMatch_t m = Text$find(state->state.stack[0].text, state->pattern, state->i); diff --git a/stdlib/rng.c b/stdlib/rng.c index 8e98aa8..07b2f36 100644 --- a/stdlib/rng.c +++ b/stdlib/rng.c @@ -251,7 +251,7 @@ public Byte_t RNG$byte(RNG_t rng) public Array_t RNG$bytes(RNG_t rng, Int_t count) { - int64_t n = Int_to_Int64(count, false); + int64_t n = Int64$from_int(count, false); Byte_t *r = GC_MALLOC_ATOMIC(sizeof(Byte_t[n])); random_bytes(rng, r, sizeof(Byte_t[n])); return (Array_t){.data=r, .length=n, .stride=1, .atomic=1}; diff --git a/stdlib/stdlib.c b/stdlib/stdlib.c index 93b0d48..efee2a0 100644 --- a/stdlib/stdlib.c +++ b/stdlib/stdlib.c @@ -17,6 +17,7 @@ #include "integers.h" #include "optionals.h" #include "metamethods.h" +#include "nums.h" #include "patterns.h" #include "paths.h" #include "rng.h" diff --git a/stdlib/text.c b/stdlib/text.c index 3cb5de0..4ee2160 100644 --- a/stdlib/text.c +++ b/stdlib/text.c @@ -505,7 +505,7 @@ public Text_t Text$repeat(Text_t text, Int_t count) if (Int$compare_value(result_len, I(1l<<40)) > 0) fail("Text repeating would produce too big of an result!"); - int64_t count64 = Int_to_Int64(count, false); + int64_t count64 = Int64$from_int(count, false); Text_t ret = text; for (int64_t c = 1; c < count64; c++) ret = concat2(ret, text); @@ -514,8 +514,8 @@ public Text_t Text$repeat(Text_t text, Int_t count) public Text_t Text$slice(Text_t text, Int_t first_int, Int_t last_int) { - int64_t first = Int_to_Int64(first_int, false); - int64_t last = Int_to_Int64(last_int, false); + int64_t first = Int64$from_int(first_int, false); + int64_t last = Int64$from_int(last_int, false); if (first == 0) fail("Invalid index: 0"); if (last == 0) return EMPTY_TEXT; @@ -604,14 +604,14 @@ public Text_t Text$reversed(Text_t text) public PUREFUNC Text_t Text$cluster(Text_t text, Int_t index_int) { - int64_t index = Int_to_Int64(index_int, false); + int64_t index = Int64$from_int(index_int, false); if (index == 0) fail("Invalid index: 0"); if (index < 0) index = text.length + index + 1; if (index > text.length || index < 1) fail("Invalid index: %ld is beyond the length of the text (length = %ld)", - Int_to_Int64(index_int, false), text.length); + Int64$from_int(index_int, false), text.length); while (text.tag == TEXT_CONCAT) { if (index <= text.left->length) diff --git a/structs.c b/structs.c index f436114..ed0a0f7 100644 --- a/structs.c +++ b/structs.c @@ -27,7 +27,7 @@ CORD compile_struct_typeinfo(env_t *env, ast_t *ast) short_name = strrchr(short_name, '$') + 1; const char *metamethods = is_packed_data(t) ? "PackedData$metamethods" : "Struct$metamethods"; - CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {.size=%zu, .align=%zu, .metamethods=%s, " + CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r$$info = {.size=%zu, .align=%zu, .metamethods=%s, " ".tag=StructInfo, .StructInfo.name=\"%s\"%s, " ".StructInfo.num_fields=%ld", full_name, type_size(t), type_align(t), metamethods, short_name, def->secret ? ", .StructInfo.is_secret=true" : "", @@ -55,23 +55,26 @@ CORD compile_struct_header(env_t *env, ast_t *ast) CORD type_code = compile_type(field_t); fields = CORD_all(fields, type_code, " $", field->name, field_t->tag == BoolType ? ":1" : CORD_EMPTY, ";\n"); } - CORD struct_code = CORD_all("struct ", full_name, "_s {\n"); + CORD struct_code = CORD_all("struct ", full_name, "$$struct {\n"); struct_code = CORD_all(struct_code, "};\n"); type_t *t = Table$str_get(*env->types, def->name); return CORD_all( - "struct ", full_name, "_s {\n", + "struct ", full_name, "$$struct {\n", fields, "};\n", "typedef struct {\n", "union {\n", - full_name, "_t value;\n" + full_name, "$$type value;\n" "struct {\n" "char _padding[", heap_strf("%zu", unpadded_struct_size(t)), "];\n", "Bool_t is_none:1;\n" "};\n" "};\n" - "} ", namespace_prefix(env, env->namespace), "$Optional", def->name, "_t;\n" - "extern const TypeInfo_t ", full_name, ";\n"); + "} ", namespace_prefix(env, env->namespace), "$Optional", def->name, "$$type;\n" + // Constructor macro: + "#define ", namespace_prefix(env, env->namespace), def->name, + "(...) ((", namespace_prefix(env, env->namespace), def->name, "$$type){__VA_ARGS__})\n" + "extern const TypeInfo_t ", full_name, "$$info;\n"); } // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/test/integers.tm b/test/integers.tm index bf15380..e6b2ba9 100644 --- a/test/integers.tm +++ b/test/integers.tm @@ -63,7 +63,7 @@ func main(): >> Int8(123):hex() = "0x7B" - >> Int(2.1) + >> Int(2.1, truncate=yes) = 2 : Int do: diff --git a/test/minmax.tm b/test/minmax.tm index 3d11f8e..f28aec9 100644 --- a/test/minmax.tm +++ b/test/minmax.tm @@ -1,7 +1,7 @@ struct Foo(x:Int, y:Int): func len(f:Foo->Num): - return Num.sqrt(f.x*f.x + f.y*f.y)! + return Num.sqrt(Num(f.x*f.x + f.y*f.y))! func main(): >> 3 _min_ 5 diff --git a/test/rng.tm b/test/rng.tm index 6c38caa..fe3d9f7 100644 --- a/test/rng.tm +++ b/test/rng.tm @@ -36,5 +36,5 @@ func main(): = [30, 50, 100, 20, 90, 10, 80, 40, 70, 60] >> nums:random(rng=rng) = 70 - >> nums:sample(10, weights=[1.0/i for i in nums.length], rng=rng) + >> nums:sample(10, weights=[1.0/Num(i) for i in nums.length], rng=rng) = [10, 20, 10, 10, 30, 70, 10, 40, 60, 80] diff --git a/tomo.c b/tomo.c index 0fd3d2c..81be5e6 100644 --- a/tomo.c +++ b/tomo.c @@ -230,19 +230,19 @@ static void _make_typedefs_for_library(libheader_info_t *info, ast_t *ast) if (ast->tag == StructDef) { auto def = Match(ast, StructDef); CORD full_name = CORD_cat(namespace_prefix(info->env, info->env->namespace), def->name); - CORD_put(CORD_all("typedef struct ", full_name, "_s ", full_name, "_t;\n"), info->output); + CORD_put(CORD_all("typedef struct ", full_name, "$$struct ", full_name, "$$type;\n"), info->output); } else if (ast->tag == EnumDef) { auto def = Match(ast, EnumDef); CORD full_name = CORD_cat(namespace_prefix(info->env, info->env->namespace), def->name); - CORD_put(CORD_all("typedef struct ", full_name, "_s ", full_name, "_t;\n"), info->output); + CORD_put(CORD_all("typedef struct ", full_name, "$$struct ", full_name, "$$type;\n"), info->output); for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { if (!tag->fields) continue; - CORD_put(CORD_all("typedef struct ", full_name, "$", tag->name, "_s ", full_name, "$", tag->name, "_t;\n"), info->output); + CORD_put(CORD_all("typedef struct ", full_name, "$", tag->name, "$$struct ", full_name, "$", tag->name, "$$type;\n"), info->output); } } else if (ast->tag == LangDef) { auto def = Match(ast, LangDef); - CORD_put(CORD_all("typedef Text_t ", namespace_prefix(info->env, info->env->namespace), def->name, "_t;\n"), info->output); + CORD_put(CORD_all("typedef Text_t ", namespace_prefix(info->env, info->env->namespace), def->name, "$$type;\n"), info->output); } } diff --git a/typecheck.c b/typecheck.c index 9134317..99a9a78 100644 --- a/typecheck.c +++ b/typecheck.c @@ -13,6 +13,7 @@ #include "environment.h" #include "parse.h" #include "stdlib/patterns.h" +#include "stdlib/tables.h" #include "stdlib/text.h" #include "stdlib/util.h" #include "typecheck.h" @@ -233,7 +234,7 @@ void prebind_statement(env_t *env, ast_t *statement) type_t *type = Type(StructType, .name=def->name, .opaque=true, .env=ns_env); // placeholder Table$str_set(env->types, def->name, type); set_binding(env, def->name, Type(TypeInfoType, .name=def->name, .type=type, .env=ns_env), - CORD_all(namespace_prefix(env, env->namespace), def->name)); + CORD_all(namespace_prefix(env, env->namespace), def->name, "$$info")); for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) prebind_statement(ns_env, stmt->ast); break; @@ -247,7 +248,7 @@ void prebind_statement(env_t *env, ast_t *statement) type_t *type = Type(EnumType, .name=def->name, .opaque=true, .env=ns_env); // placeholder Table$str_set(env->types, def->name, type); set_binding(env, def->name, Type(TypeInfoType, .name=def->name, .type=type, .env=ns_env), - CORD_all(namespace_prefix(env, env->namespace), def->name)); + CORD_all(namespace_prefix(env, env->namespace), def->name, "$$info")); for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) prebind_statement(ns_env, stmt->ast); break; @@ -261,7 +262,7 @@ void prebind_statement(env_t *env, ast_t *statement) type_t *type = Type(TextType, .lang=def->name, .env=ns_env); Table$str_set(env->types, def->name, type); set_binding(env, def->name, Type(TypeInfoType, .name=def->name, .type=type, .env=ns_env), - CORD_all(namespace_prefix(env, env->namespace), def->name)); + CORD_all(namespace_prefix(env, env->namespace), def->name, "$$info")); for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) prebind_statement(ns_env, stmt->ast); break; @@ -338,6 +339,12 @@ void bind_statement(env_t *env, ast_t *statement) REVERSE_LIST(fields); type->__data.StructType.fields = fields; // populate placeholder type->__data.StructType.opaque = false; + + // Default constructor: + type_t *constructor_t = Type(FunctionType, .args=fields, .ret=type); + Array$insert(&ns_env->namespace->constructors, + new(binding_t, .code=CORD_all(namespace_prefix(env, env->namespace), def->name), .type=constructor_t), + I(0), sizeof(binding_t)); for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) { bind_statement(ns_env, stmt->ast); @@ -381,7 +388,7 @@ void bind_statement(env_t *env, ast_t *statement) type_t *constructor_t = Type(FunctionType, .args=Match(tag->type, StructType)->fields, .ret=type); set_binding(ns_env, tag->name, constructor_t, CORD_all(namespace_prefix(env, env->namespace), def->name, "$tagged$", tag->name)); } else { // Empty singleton value: - CORD code = CORD_all("(", namespace_prefix(env, env->namespace), def->name, "_t){", namespace_prefix(env, env->namespace), def->name, "$tag$", tag->name, "}"); + CORD code = CORD_all("((", namespace_prefix(env, env->namespace), def->name, "$$type){", namespace_prefix(env, env->namespace), def->name, "$tag$", tag->name, "})"); set_binding(ns_env, tag->name, type, code); } Table$str_set(env->types, heap_strf("%s$%s", def->name, tag->name), tag->type); @@ -400,7 +407,7 @@ void bind_statement(env_t *env, ast_t *statement) set_binding(ns_env, "without_escaping", Type(FunctionType, .args=new(arg_t, .name="text", .type=TEXT_TYPE), .ret=type), - CORD_all("(", namespace_prefix(env, env->namespace), def->name, "_t)")); + CORD_all("(", namespace_prefix(env, env->namespace), def->name, "$$type)")); for (ast_list_t *stmt = def->namespace ? Match(def->namespace, Block)->statements : NULL; stmt; stmt = stmt->next) bind_statement(ns_env, stmt->ast); @@ -1369,56 +1376,69 @@ type_t *get_arg_type(env_t *env, arg_t *arg) return get_type(env, arg->default_val); } -bool is_valid_call(env_t *env, arg_t *spec_args, arg_ast_t *call_args, bool promotion_allowed) +Table_t *get_arg_bindings(env_t *env, arg_t *spec_args, arg_ast_t *call_args, bool promotion_allowed) { Table_t used_args = {}; - for (arg_t *spec_arg = spec_args; spec_arg; spec_arg = spec_arg->next) { - type_t *spec_type = get_arg_type(env, spec_arg); - 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)) { - type_t *call_type = get_arg_ast_type(env, call_arg); - if (!(type_eq(call_type, spec_type) || (promotion_allowed && can_promote(call_type, spec_type)))) - return false; - Table$str_set(&used_args, call_arg->name, call_arg); - goto found_it; - } - } + + // Populate keyword args: + for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) { + if (!call_arg->name) continue; + + type_t *call_type = get_arg_ast_type(env, call_arg); + for (arg_t *spec_arg = spec_args; spec_arg; spec_arg = spec_arg->next) { + if (!streq(call_arg->name, spec_arg->name)) continue; + type_t *spec_type = get_arg_type(env, spec_arg); + if (!(type_eq(call_type, spec_type) || (promotion_allowed && can_promote(call_type, spec_type)) + || (!promotion_allowed && call_arg->value->tag == Int && is_numeric_type(spec_type)) + || (!promotion_allowed && call_arg->value->tag == Num && spec_type->tag == NumType))) + return NULL; + Table$str_set(&used_args, call_arg->name, call_arg); + goto next_call_arg; } - // 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 = heap_strf("%ld", i++); - if (!Table$str_get(used_args, pseudoname)) { - type_t *call_type = get_arg_ast_type(env, call_arg); - if (!(type_eq(call_type, spec_type) || (promotion_allowed && can_promote(call_type, spec_type)))) - return false; - Table$str_set(&used_args, pseudoname, call_arg); - goto found_it; - } + return NULL; + next_call_arg:; + } + + arg_ast_t *unused_args = call_args; + for (arg_t *spec_arg = spec_args; spec_arg; spec_arg = spec_arg->next) { + arg_ast_t *keyworded = Table$str_get(used_args, spec_arg->name); + if (keyworded) continue; + + type_t *spec_type = get_arg_type(env, spec_arg); + for (; unused_args; unused_args = unused_args->next) { + if (unused_args->name) continue; // Already handled the keyword args + type_t *call_type = get_arg_ast_type(env, unused_args); + if (!(type_eq(call_type, spec_type) || (promotion_allowed && can_promote(call_type, spec_type)) + || (!promotion_allowed && unused_args->value->tag == Int && is_numeric_type(spec_type)) + || (!promotion_allowed && unused_args->value->tag == Num && spec_type->tag == NumType))) + return NULL; // Positional arg trying to fill in + Table$str_set(&used_args, spec_arg->name, unused_args); + unused_args = unused_args->next; + goto found_it; } if (spec_arg->default_val) goto found_it; - return false; + return NULL; 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)) - return false; - } else { - const char *pseudoname = heap_strf("%ld", i++); - if (!Table$str_get(used_args, pseudoname)) - return false; - } - } - return true; + while (unused_args && unused_args->name) + unused_args = unused_args->next; + + if (unused_args != NULL) + return NULL; + + Table_t *ret = new(Table_t); + *ret = used_args; + return ret; +} + +bool is_valid_call(env_t *env, arg_t *spec_args, arg_ast_t *call_args, bool promotion_allowed) +{ + Table_t *arg_bindings = get_arg_bindings(env, spec_args, call_args, promotion_allowed); + return (arg_bindings != NULL); } PUREFUNC bool can_be_mutated(env_t *env, ast_t *ast) diff --git a/typecheck.h b/typecheck.h index b3f1dd6..1175e5f 100644 --- a/typecheck.h +++ b/typecheck.h @@ -9,6 +9,7 @@ #include "ast.h" #include "environment.h" +#include "stdlib/datatypes.h" #include "types.h" type_t *parse_type_ast(env_t *env, type_ast_t *ast); @@ -25,6 +26,7 @@ PUREFUNC bool can_be_mutated(env_t *env, ast_t *ast); type_t *parse_type_string(env_t *env, const char *str); type_t *get_method_type(env_t *env, ast_t *self, const char *name); PUREFUNC bool is_constant(env_t *env, ast_t *ast); +Table_t *get_arg_bindings(env_t *env, arg_t *spec_args, arg_ast_t *call_args, bool promotion_allowed); bool is_valid_call(env_t *env, arg_t *spec_args, arg_ast_t *call_args, bool promotion_allowed); // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0