From b8d7eabc023bf9db0150049d8e909086f6ad91bc Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 5 Nov 2024 15:18:32 -0500 Subject: Deprecate bit-width integer/num literals in favor of using type constructors --- ast.c | 4 +-- ast.h | 2 -- compile.c | 89 ++++++++++++++++++++++++----------------------- docs/integers.md | 62 +++++++++++++++++++++++++++------ docs/nums.md | 2 ++ environment.c | 17 ++++----- examples/base64/base64.tm | 14 ++++---- examples/http/http.tm | 4 +-- parse.c | 24 +++---------- repl.c | 15 ++------ stdlib/bytes.c | 2 +- stdlib/integers.c | 10 +++--- stdlib/nums.c | 2 +- test/bytes.tm | 8 ++--- test/datetime.tm | 4 +-- test/inline_c.tm | 2 +- test/integers.tm | 36 +++++++++---------- test/lang.tm | 2 +- test/nums.tm | 4 +-- test/optionals.tm | 6 ++-- test/paths.tm | 2 +- test/rng.tm | 14 ++++---- test/text.tm | 14 ++++---- typecheck.c | 37 +++++++------------- 24 files changed, 193 insertions(+), 183 deletions(-) diff --git a/ast.c b/ast.c index a17e64a2..d9ca65af 100644 --- a/ast.c +++ b/ast.c @@ -104,8 +104,8 @@ CORD ast_to_xml(ast_t *ast) T(Null, "%r", type_ast_to_xml(data.type)) T(Bool, "", data.b ? "yes" : "no") T(Var, "%s", data.name) - T(Int, "%s", data.bits, data.str) - T(Num, "%g", data.bits, data.n) + T(Int, "%s", data.str) + T(Num, "%g", data.n) T(TextLiteral, "%r", xml_escape(data.cord)) T(TextJoin, "%r", data.lang ? CORD_all(" lang=\"", data.lang, "\"") : CORD_EMPTY, ast_list_to_xml(data.children)) T(Declare, "%r", ast_to_xml(data.var), ast_to_xml(data.value)) diff --git a/ast.h b/ast.h index bae74e56..234fa9a2 100644 --- a/ast.h +++ b/ast.h @@ -164,11 +164,9 @@ struct ast_s { } Var; struct { const char *str; - enum { IBITS_UNSPECIFIED=0, IBITS_BYTE=7, IBITS8=8, IBITS16=16, IBITS32=32, IBITS64=64 } bits; } Int; struct { double n; - enum { NBITS_UNSPECIFIED=0, NBITS32=32, NBITS64=64 } bits; } Num; struct { CORD cord; diff --git a/compile.c b/compile.c index 5f274335..4c8e3785 100644 --- a/compile.c +++ b/compile.c @@ -1616,9 +1616,6 @@ env_t *with_enum_scope(env_t *env, type_t *t) CORD compile_int_to_type(env_t *env, ast_t *ast, type_t *target) { - if (target->tag == BigIntType) - return compile(env, ast); - if (ast->tag != Int) { CORD code = compile(env, ast); type_t *actual_type = get_type(env, ast); @@ -1627,41 +1624,54 @@ CORD compile_int_to_type(env_t *env, ast_t *ast, type_t *target) return code; } - OptionalInt_t int_val = Int$from_str(Match(ast, Int)->str); + if (target->tag == BigIntType) + return compile(env, ast); + + 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 CORD_asprintf("(Byte_t)(%s)", Match(ast, Int)->str); + return CORD_asprintf("(Byte_t)(%s)", 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 CORD_asprintf("N64(%s)", Match(ast, Int)->str); + return CORD_asprintf("N64(%s)", c_literal); } else { - return CORD_asprintf("N32(%s)", Match(ast, Int)->str); + return CORD_asprintf("N32(%s)", 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_MAX) <= 0 && mpz_cmp_si(i, INT64_MIN) >= 0) - return CORD_asprintf("I64(%s)", Match(ast, Int)->str); + return CORD_asprintf("I64(%s)", c_literal); break; case TYPE_IBITS32: if (mpz_cmp_si(i, INT32_MAX) <= 0 && mpz_cmp_si(i, INT32_MIN) >= 0) - return CORD_asprintf("I32(%s)", Match(ast, Int)->str); + return CORD_asprintf("I32(%s)", c_literal); break; case TYPE_IBITS16: if (mpz_cmp_si(i, INT16_MAX) <= 0 && mpz_cmp_si(i, INT16_MIN) >= 0) - return CORD_asprintf("I16(%s)", Match(ast, Int)->str); + return CORD_asprintf("I16(%s)", c_literal); break; case TYPE_IBITS8: if (mpz_cmp_si(i, INT8_MAX) <= 0 && mpz_cmp_si(i, INT8_MIN) >= 0) - return CORD_asprintf("I8(%s)", Match(ast, Int)->str); + return CORD_asprintf("I8(%s)", c_literal); break; default: break; } @@ -1934,6 +1944,22 @@ 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) { @@ -1960,9 +1986,6 @@ CORD compile(env_t *env, ast_t *ast) code_err(ast, "Failed to parse this integer"); mpz_t i; mpz_init_set_int(i, int_val); - - switch (Match(ast, Int)->bits) { - case IBITS_UNSPECIFIED: 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) { @@ -1970,37 +1993,9 @@ CORD compile(env_t *env, ast_t *ast) } else { return CORD_asprintf("Int$from_str(\"%s\")", str); } - case IBITS64: - if ((mpz_cmp_si(i, INT64_MAX) <= 0) && (mpz_cmp_si(i, INT64_MIN) >= 0)) - return CORD_asprintf("I64(%ldl)", mpz_get_si(i)); - code_err(ast, "This value cannot fit in a 64-bit integer"); - case IBITS32: - if ((mpz_cmp_si(i, INT32_MAX) <= 0) && (mpz_cmp_si(i, INT32_MIN) >= 0)) - return CORD_asprintf("I32(%ld)", mpz_get_si(i)); - code_err(ast, "This value cannot fit in a 32-bit integer"); - case IBITS16: - if ((mpz_cmp_si(i, INT16_MAX) <= 0) && (mpz_cmp_si(i, INT16_MIN) >= 0)) - return CORD_asprintf("I16(%ld)", mpz_get_si(i)); - code_err(ast, "This value cannot fit in a 16-bit integer"); - case IBITS8: - if ((mpz_cmp_si(i, INT8_MAX) <= 0) && (mpz_cmp_si(i, INT8_MIN) >= 0)) - return CORD_asprintf("I8(%ld)", mpz_get_si(i)); - code_err(ast, "This value cannot fit in a 8-bit integer"); - case IBITS_BYTE: - if ((mpz_cmp_si(i, UINT8_MAX) <= 0) && (mpz_cmp_si(i, 0) >= 0)) - return CORD_asprintf("Byte(%ld)", mpz_get_si(i)); - code_err(ast, "This value cannot fit in a byte"); - default: code_err(ast, "Not a valid integer bit width"); - } } case Num: { - switch (Match(ast, Num)->bits) { - case NBITS_UNSPECIFIED: case NBITS64: - return CORD_asprintf("N64(%.20g)", Match(ast, Num)->n); - case NBITS32: - return CORD_asprintf("N32(%.10g)", Match(ast, Num)->n); - default: code_err(ast, "This is not a valid number bit width"); - } + return CORD_asprintf("N64(%.20g)", Match(ast, Num)->n); } case Not: { ast_t *value = Match(ast, Not)->value; @@ -3032,6 +3027,14 @@ CORD compile(env_t *env, ast_t *ast) // 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"); type_t *actual = get_type(env, call->args->value); diff --git a/docs/integers.md b/docs/integers.md index 5b29d5a8..16728c44 100644 --- a/docs/integers.md +++ b/docs/integers.md @@ -8,16 +8,22 @@ Tomo has five types of integers: the GNU MP library. These integers are fast for small numbers and guaranteed to always be correct and never overflow. - `Int8`/`Int16`/`Int32`/`Int64`: Fixed-size integers that take up `N` bits. - These integers must be manually specified with their bits in square brackets - (e.g. `5[64]`) and are subject to overflowing. If an overflow occurs, a - runtime error will be raised. - -Conversion between integer types can be done by calling the target type as a -function: `Int32(x)`. For fixed-width types, the conversion function also -accepts a second parameter, `truncate`. If `truncate` is `no` (the default), -conversion will create a runtime error if the value is too large to fit in the -target type. If `truncate` is `yes`, then the resulting value will be a -truncated form of the input value. + These integers must be explicitly constructed using their type name (e.g. + `Int64(5)`) and are subject to overflowing on arithmetic operations. If an + overflow occurs, a runtime error will be raised. +- In cases where it is possible to infer that an integer literal should be + used as a fixed-size integer, the literal will be converted at compile time + to the appropriate fixed-size integer type and checked to ensure that it + can fit in the needed size. For example, if you declare a variable as + `x := Int64(0)` and later do `x + 1`, it's inferred that the `1` is a 64-bit + integer literal. + +Runtime conversion between integer types (casting) can be done explicitly by +calling the target type as a function: `Int32(x)`. For fixed-width types, the +conversion function also accepts a second parameter, `truncate`. If `truncate` +is `no` (the default), conversion will create a runtime error if the value is +too large to fit in the target type. If `truncate` is `yes`, then the resulting +value will be a truncated form of the input value. Integers support the standard math operations (`x+y`, `x-y`, `x*y`, `x/y`) as well as powers/exponentiation (`x^y`), modulus (`x mod y` and `x mod1 y`), and @@ -25,6 +31,42 @@ bitwise operations: `x and y`, `x or y`, `x xor y`, `x << y`, `x >> y`, `x >>> y` (unsigned right shift), and `x <<< y` (unsighted left shift). The operators `and`, `or`, and `xor` are _bitwise_, not logical operators. +# Integer Literals + +The simplest form of integer literal is a string of digits, which is inferred +to have type `Int` (unbounded size). + +```tomo +>>> 123456789012345678901234567890 += 123456789012345678901234567890 : Int +``` + +Underscores may also be used to visually break up the integer for readability: + +```tomo +a_million := 1_000_000 +``` + +Hexadecimal, octal, and binary integer literals are also supported: + +```tomo +hex := 0x123F +octal := 0o644 +binary := 0b10101 +``` + +For fixed-sized integers, use the type's name as a constructor: + +```tomo +my_int64 := Int64(12345) +my_int32 := Int32(12345) +my_int16 := Int32(12345) +my_int8 := Int32(123) +``` + +A compiler error will be raised if you attempt to construct a value that cannot +fit in the specified integer size (e.g. `Int8(99999)`). + # Integer Functions Each integer type has its own version of the following functions. Functions diff --git a/docs/nums.md b/docs/nums.md index 5f9b5285..63fae27f 100644 --- a/docs/nums.md +++ b/docs/nums.md @@ -10,6 +10,8 @@ are converted to radians at compile time (i.e. `180deg == Nums.PI`). Nums support the standard math operations (`x+y`, `x-y`, `x*y`, `x/y`) as well as powers/exponentiation (`x^y`) and modulus (`x mod y` and `x mod1 y`). +32-bit numbers can be constructed using the type name: `Num32(123.456)`. + # Num Functions Each Num type has its own version of the following functions. Functions can be diff --git a/environment.c b/environment.c index 47e30dba..6ae96291 100644 --- a/environment.c +++ b/environment.c @@ -48,7 +48,8 @@ env_t *new_compilation_unit(CORD libname) arg_t, .name="message", .type=Type(OptionalType, .type=Type(TextType)), .default_val=FakeAST(Null, .type=new(type_ast_t, .tag=VarTypeAST, .__data.VarTypeAST.name="Text")), .next=new(arg_t, .name="code", .type=Type(IntType, .bits=TYPE_IBITS32), - .default_val=FakeAST(Int, .bits=IBITS32, .str="1"))), .ret=Type(AbortType))}}, + .default_val=FakeAST(InlineCCode, .code="1", .type=Type(IntType, .bits=TYPE_IBITS32)))), + .ret=Type(AbortType))}}, {"fail", {.code="fail", .type=Type(FunctionType, .args=new(arg_t, .name="message", .type=Type(CStringType)), .ret=Type(AbortType))}}, {"sleep", {.code="sleep_num", .type=Type(FunctionType, .args=new(arg_t, .name="seconds", .type=Type(NumType, .bits=TYPE_NBITS64)), .ret=Type(VoidType))}}, {"now", {.code="DateTime$now", .type=Type(FunctionType, .args=NULL, .ret=Type(DateTimeType))}}, @@ -237,7 +238,7 @@ env_t *new_compilation_unit(CORD libname) #define F(name) {#name, #name"f", "func(n:Num32 -> Num32)"} #define F2(name) {#name, #name"f", "func(x,y:Num32 -> Num32)"} {"Num32", Type(NumType, .bits=TYPE_NBITS32), "Num32_t", "Num32$info", TypedArray(ns_entry_t, - {"near", "Num32$near", "func(x,y:Num32, ratio=1e-9f32, min_epsilon=1e-9f32 -> Bool)"}, + {"near", "Num32$near", "func(x,y:Num32, ratio=Num32(1e-9), min_epsilon=Num32(1e-9) -> Bool)"}, {"clamped", "Num32$clamped", "func(x,low,high:Num32 -> Num32)"}, {"format", "Num32$format", "func(n:Num32, precision=0 -> Text)"}, {"scientific", "Num32$scientific", "func(n:Num32, precision=0 -> Text)"}, @@ -301,12 +302,12 @@ env_t *new_compilation_unit(CORD libname) {"year", "DateTime$year", "func(dt:DateTime,timezone=!Text -> Int)"}, )}, {"Path", Type(TextType, .lang="Path", .env=namespace_env(env, "Path")), "Text_t", "Text$info", TypedArray(ns_entry_t, - {"append", "Path$append", "func(path:Path, text:Text, permissions=0o644[32])"}, - {"append_bytes", "Path$append_bytes", "func(path:Path, bytes:[Byte], permissions=0o644[32])"}, + {"append", "Path$append", "func(path:Path, text:Text, permissions=Int32(0o644))"}, + {"append_bytes", "Path$append_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644))"}, {"base_name", "Path$base_name", "func(path:Path -> Text)"}, {"by_line", "Path$by_line", "func(path:Path -> func(->Text?)?)"}, {"children", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, - {"create_directory", "Path$create_directory", "func(path:Path, permissions=0o755[32])"}, + {"create_directory", "Path$create_directory", "func(path:Path, permissions=Int32(0o755))"}, {"escape_int", "Int$value_as_text", "func(i:Int -> Path)"}, {"escape_path", "Path$escape_path", "func(path:Path -> Path)"}, {"escape_text", "Path$escape_text", "func(text:Text -> Path)"}, @@ -327,8 +328,8 @@ env_t *new_compilation_unit(CORD libname) {"resolved", "Path$resolved", "func(path:Path, relative_to=(./) -> Path)"}, {"subdirectories", "Path$children", "func(path:Path, include_hidden=no -> [Path])"}, {"unique_directory", "Path$unique_directory", "func(path:Path -> Path)"}, - {"write", "Path$write", "func(path:Path, text:Text, permissions=0o644[32])"}, - {"write_bytes", "Path$write_bytes", "func(path:Path, bytes:[Byte], permissions=0o644[32])"}, + {"write", "Path$write", "func(path:Path, text:Text, permissions=Int32(0o644))"}, + {"write_bytes", "Path$write_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644))"}, {"write_unique", "Path$write_unique", "func(path:Path, text:Text -> Path)"}, {"write_unique_bytes", "Path$write_unique_bytes", "func(path:Path, bytes:[Byte] -> Path)"}, @@ -357,7 +358,7 @@ env_t *new_compilation_unit(CORD libname) {"int8", "RNG$int8", "func(rng:RNG, min=Int8.min, max=Int8.max -> Int8)"}, {"new", "RNG$new", "func(seed=(/dev/urandom):read_bytes(40)! -> RNG)"}, {"num", "RNG$num", "func(rng:RNG, min=0.0, max=1.0 -> Num)"}, - {"num32", "RNG$num32", "func(rng:RNG, min=0.0_f32, max=1.0_f32 -> Num32)"}, + {"num32", "RNG$num32", "func(rng:RNG, min=Num32(0.0), max=Num32(1.0) -> Num32)"}, {"set_seed", "RNG$set_seed", "func(rng:RNG, seed:[Byte])"}, )}, {"Shell", Type(TextType, .lang="Shell", .env=namespace_env(env, "Shell")), "Shell_t", "Shell$info", TypedArray(ns_entry_t, diff --git a/examples/base64/base64.tm b/examples/base64/base64.tm index f22883cd..698c6bce 100644 --- a/examples/base64/base64.tm +++ b/examples/base64/base64.tm @@ -2,7 +2,7 @@ _enc := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/":utf8_bytes() -_EQUAL_BYTE := 0x3D[B] +_EQUAL_BYTE := Byte(0x3D) _dec := [ :Byte @@ -29,9 +29,9 @@ lang Base64: return Base64.from_bytes(text:utf8_bytes()) func from_bytes(bytes:[Byte] -> Base64?): - output := [0[B] for _ in bytes.length * 4 / 3 + 4] - src := 1[64] - dest := 1[64] + output := [Byte(0) for _ in bytes.length * 4 / 3 + 4] + src := Int64(1) + dest := Int64(1) while src + 2 <= bytes.length: chunk24 := ( (Int32(bytes[src]) <<< 16) or (Int32(bytes[src+1]) <<< 8) or Int32(bytes[src+2]) @@ -66,9 +66,9 @@ lang Base64: func decode_bytes(b64:Base64 -> [Byte]?): bytes := b64.text_content:utf8_bytes() - output := [0[B] for _ in bytes.length/4 * 3] - src := 1[64] - dest := 1[64] + output := [Byte(0) for _ in bytes.length/4 * 3] + src := Int64(1) + dest := Int64(1) while src + 3 <= bytes.length: chunk24 := ( (Int32(_dec[1+bytes[src]]) <<< 18) or diff --git a/examples/http/http.tm b/examples/http/http.tm index 934c8690..f64a5048 100644 --- a/examples/http/http.tm +++ b/examples/http/http.tm @@ -64,7 +64,7 @@ func _send(method:_Method, url:Text, data:Text?, headers=[:Text] -> HTTPResponse curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); } - code := 0[64] + code := Int64(0) inline C { CURLcode res = curl_easy_perform(curl); if (res != CURLE_OK) @@ -75,7 +75,7 @@ func _send(method:_Method, url:Text, data:Text?, headers=[:Text] -> HTTPResponse curl_slist_free_all(chunk); curl_easy_cleanup(curl); } - return HTTPResponse(code, "":join(chunks)) + return HTTPResponse(Int(code), "":join(chunks)) func get(url:Text, headers=[:Text] -> HTTPResponse): return _send(GET, url, !Text, headers) diff --git a/parse.c b/parse.c index 94f0cd0a..c63b23df 100644 --- a/parse.c +++ b/parse.c @@ -499,22 +499,13 @@ PARSER(parse_int) { if (match(&pos, "%")) { double n = strtod(str, NULL) / 100.; - return NewAST(ctx->file, start, pos, Num, .n=n, .bits=64); + return NewAST(ctx->file, start, pos, Num, .n=n); } else if (match(&pos, "deg")) { double n = strtod(str, NULL) * RADIANS_PER_DEGREE; - return NewAST(ctx->file, start, pos, Num, .n=n, .bits=64); + return NewAST(ctx->file, start, pos, Num, .n=n); } - auto bits = IBITS_UNSPECIFIED; - if (match(&pos, "[64]")) bits = IBITS64; - else if (match(&pos, "[32]")) bits = IBITS32; - else if (match(&pos, "[16]")) bits = IBITS16; - else if (match(&pos, "[8]")) bits = IBITS8; - else if (match(&pos, "[B]")) bits = IBITS_BYTE; - - // else if (match(&pos, ".") || match(&pos, "e")) return NULL; // looks like a float - - return NewAST(ctx->file, start, pos, Int, .str=str, .bits=bits); + return NewAST(ctx->file, start, pos, Int, .str=str); } PARSER(parse_datetime) { @@ -731,17 +722,12 @@ PARSER(parse_num) { if (negative) d *= -1; - auto bits = NBITS_UNSPECIFIED; - match(&pos, "_"); - if (match(&pos, "f64")) bits = NBITS64; - else if (match(&pos, "f32")) bits = NBITS32; - if (match(&pos, "%")) d /= 100.; else if (match(&pos, "deg")) d *= RADIANS_PER_DEGREE; - return NewAST(ctx->file, start, pos, Num, .n=d, .bits=bits); + return NewAST(ctx->file, start, pos, Num, .n=d); } static INLINE bool match_separator(const char **pos) { // Either comma or newline @@ -2253,7 +2239,7 @@ PARSER(parse_func_def) { if (match_word(&pos, "inline")) { is_inline = true; } else if (match_word(&pos, "cached")) { - if (!cache_ast) cache_ast = NewAST(ctx->file, pos, pos, Int, .str="-1", .bits=0); + if (!cache_ast) cache_ast = NewAST(ctx->file, pos, pos, Int, .str="-1"); } else if (match_word(&pos, "cache_size")) { whitespace(&pos); if (!match(&pos, "=")) diff --git a/repl.c b/repl.c index fca220c7..869512f1 100644 --- a/repl.c +++ b/repl.c @@ -353,23 +353,12 @@ void eval(env_t *env, ast_t *ast, void *dest) } case Int: { if (!dest) return; - switch (Match(ast, Int)->bits) { - case 0: *(Int_t*)dest = Int$from_text(Text$from_str(Match(ast, Int)->str)); break; - case 64: *(int64_t*)dest = Int64$from_text(Text$from_str(Match(ast, Int)->str)).i; break; - case 32: *(int32_t*)dest = Int32$from_text(Text$from_str(Match(ast, Int)->str)).i; break; - case 16: *(int16_t*)dest = Int16$from_text(Text$from_str(Match(ast, Int)->str)).i; break; - case 8: *(int8_t*)dest = Int8$from_text(Text$from_str(Match(ast, Int)->str)).i; break; - default: errx(1, "Invalid int bits: %ld", Match(ast, Int)->bits); - } + *(Int_t*)dest = Int$from_text(Text$from_str(Match(ast, Int)->str)); break; break; } case Num: { if (!dest) return; - switch (Match(ast, Num)->bits) { - case 0: case 64: *(double*)dest = Match(ast, Num)->n; break; - case 32: *(float*)dest = Match(ast, Num)->n; break; - default: errx(1, "Invalid num bits: %ld", Match(ast, Num)->bits); - } + *(double*)dest = Match(ast, Num)->n; break; break; } case TextLiteral: diff --git a/stdlib/bytes.c b/stdlib/bytes.c index 8d665790..f36a56ba 100644 --- a/stdlib/bytes.c +++ b/stdlib/bytes.c @@ -14,7 +14,7 @@ PUREFUNC public Text_t Byte$as_text(const Byte_t *b, bool colorize, const TypeIn { (void)type; if (!b) return Text("Byte"); - return Text$format(colorize ? "\x1b[35m%u[B]\x1b[m" : "%u[B]", *b); + return Text$format(colorize ? "\x1b[36mByte\x1b[m(\x1b[35m0x%02X\x1b[m)" : "Byte(0x%02X)", *b); } public const TypeInfo_t Byte$info = { diff --git a/stdlib/integers.c b/stdlib/integers.c index f6140da4..9bde1890 100644 --- a/stdlib/integers.c +++ b/stdlib/integers.c @@ -377,7 +377,7 @@ public const TypeInfo_t Int$info = { public Text_t KindOfInt ## $as_text(const c_type *i, bool colorize, const TypeInfo_t *type) { \ (void)type; \ if (!i) return Text(#KindOfInt); \ - return Text$format(colorize ? "\x1b[35m%" fmt "\x1b[m" : "%" fmt, *i); \ + return Text$format(colorize ? "\x1b[36m" #KindOfInt "\x1b[m(\x1b[35m" fmt "\x1b[m)" : #KindOfInt "(" fmt ")", *i); \ } \ public PUREFUNC int32_t KindOfInt ## $compare(const c_type *x, const c_type *y, const TypeInfo_t *type) { \ (void)type; \ @@ -431,10 +431,10 @@ public const TypeInfo_t Int$info = { .CustomInfo={.compare=(void*)KindOfInt##$compare, .as_text=(void*)KindOfInt##$as_text}, \ }; -DEFINE_INT_TYPE(int64_t, Int64, "ld[64]", INT64_MIN, INT64_MAX, __attribute__(())) -DEFINE_INT_TYPE(int32_t, Int32, "d[32]", INT32_MIN, INT32_MAX, CONSTFUNC) -DEFINE_INT_TYPE(int16_t, Int16, "d[16]", INT16_MIN, INT16_MAX, CONSTFUNC) -DEFINE_INT_TYPE(int8_t, Int8, "d[8]", INT8_MIN, INT8_MAX, CONSTFUNC) +DEFINE_INT_TYPE(int64_t, Int64, "%ld", INT64_MIN, INT64_MAX, __attribute__(())) +DEFINE_INT_TYPE(int32_t, Int32, "%d", INT32_MIN, INT32_MAX, CONSTFUNC) +DEFINE_INT_TYPE(int16_t, Int16, "%d", INT16_MIN, INT16_MAX, CONSTFUNC) +DEFINE_INT_TYPE(int8_t, Int8, "%d", INT8_MIN, INT8_MAX, CONSTFUNC) #undef DEFINE_INT_TYPE // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/nums.c b/stdlib/nums.c index b8de553e..30973fbc 100644 --- a/stdlib/nums.c +++ b/stdlib/nums.c @@ -93,7 +93,7 @@ public const TypeInfo_t Num$info = { public PUREFUNC Text_t Num32$as_text(const float *f, bool colorize, const TypeInfo_t *type) { (void)type; if (!f) return Text("Num32"); - return Text$format(colorize ? "\x1b[35m%.8g_f32\x1b[33;2m\x1b[m" : "%.8g_f32", (double)*f); + return Text$format(colorize ? "\x1b[36mNum32(\x1b[35m%.8g\x1b[33;2m\x1b[m)" : "Num32(%.8g)", (double)*f); } public PUREFUNC int32_t Num32$compare(const float *x, const float *y, const TypeInfo_t *type) { diff --git a/test/bytes.tm b/test/bytes.tm index 4db94930..be6d1cdd 100644 --- a/test/bytes.tm +++ b/test/bytes.tm @@ -1,8 +1,8 @@ func main(): !! Test bytes: - >> 100[B] - = 100[B] + >> Byte(100) + = Byte(0x64) - >> 0xFF[B] - = 255[B] + >> Byte(0xFF) + = Byte(0xFF) diff --git a/test/datetime.tm b/test/datetime.tm index 19ca74b0..64077c27 100644 --- a/test/datetime.tm +++ b/test/datetime.tm @@ -36,8 +36,8 @@ func main(): = "Tuesday" >> t:unix_timestamp() - = 1704221100[64] - >> t == DateTime.from_unix_timestamp(1704221100[64]) + = Int64(1704221100) + >> t == DateTime.from_unix_timestamp(1704221100) = yes >> t < t:after(minutes=1) diff --git a/test/inline_c.tm b/test/inline_c.tm index 876e78fd..3c0949cd 100644 --- a/test/inline_c.tm +++ b/test/inline_c.tm @@ -1,7 +1,7 @@ func main(): >> inline C:Int32 { int x = 1 + 2; x } - = 3[32] + = Int32(3) >> inline C { say(Text("Inline C code works!"), true); diff --git a/test/integers.tm b/test/integers.tm index ad9c40d7..323508da 100644 --- a/test/integers.tm +++ b/test/integers.tm @@ -11,21 +11,21 @@ func main(): >> 2 * 3 + 4 = 10 - >> 1[8] + 2[16] - = 3[16] + >> Int8(1) + Int16(2) + = Int16(3) >> 1 << 10 = 1024 !! Signed and unsigned bit shifting: - >> -2[64] << 1 - = -4[64] - >> -2[64] <<< 1 - = -4[64] - >> -2[64] >> 1 - = -1[64] - >> -2[64] >>> 1 - = 9223372036854775807[64] + >> Int64(-2) << 1 + = Int64(-4) + >> Int64(-2) <<< 1 + = Int64(-4) + >> Int64(-2) >> 1 + = Int64(-1) + >> Int64(-2) >>> 1 + = Int64(9223372036854775807) >> 3 and 2 = 2 @@ -42,7 +42,7 @@ func main(): >> nums = "1,2,3,4,5," - >> x := 123[64] + >> x := Int64(123) >> x:format(digits=5) = "00123" >> x:hex() @@ -51,16 +51,16 @@ func main(): = "0o173" >> Int64.min - = -9223372036854775808[64] + = Int64(-9223372036854775808) >> Int64.max - = 9223372036854775807[64] + = Int64(9223372036854775807) - >> 123[32]:hex() + >> Int32(123):hex() = "0x7B" - >> 123[16]:hex() + >> Int16(123):hex() = "0x7B" - >> 123[8]:hex() + >> Int8(123):hex() = "0x7B" >> Int(2.1) @@ -127,6 +127,6 @@ func main(): = 0 >> Int64(yes) - = 1[64] + = Int64(1) >> Int64(no) - = 0[64] + = Int64(0) diff --git a/test/lang.tm b/test/lang.tm index b30bcbea..509071a4 100644 --- a/test/lang.tm +++ b/test/lang.tm @@ -33,7 +33,7 @@ func main(): >> $HTML"$(1 + 2)" = $HTML"3" - >> $HTML"$(3[8])" + >> $HTML"$(Int8(3))" = $HTML"3" >> html:paragraph() diff --git a/test/nums.tm b/test/nums.tm index 32c55f3b..98bcd9cd 100644 --- a/test/nums.tm +++ b/test/nums.tm @@ -41,8 +41,8 @@ func main(): >> Num.INF:near(-Num.INF) = no - >> Num32.sqrt(16f32) - = 4_f32 + >> Num32.sqrt(16) + = Num32(4) >> 0.25:mix(10, 20) = 12.5 diff --git a/test/optionals.tm b/test/optionals.tm index 612ab76b..cf9284e1 100644 --- a/test/optionals.tm +++ b/test/optionals.tm @@ -21,7 +21,7 @@ func maybe_int(should_i:Bool->Int?): func maybe_int64(should_i:Bool->Int64?): if should_i: - return 123[64] + return Int64(123) else: return !Int64 @@ -113,12 +113,12 @@ func main(): !! ... !! Int64s: >> yep := maybe_int64(yes) - = 123[64]? + = Int64(123)? >> nope := maybe_int64(no) = !Int64 >> if yep: >> yep - = 123[64] + = Int64(123) else: fail("Falsey: $yep") >> if nope: fail("Truthy: $nope") diff --git a/test/paths.tm b/test/paths.tm index 4330e79f..6c1ee5f2 100644 --- a/test/paths.tm +++ b/test/paths.tm @@ -25,7 +25,7 @@ func main(): >> tmpfile:read() = "Hello world!"? >> tmpfile:read_bytes() - = [72[B], 101[B], 108[B], 108[B], 111[B], 32[B], 119[B], 111[B], 114[B], 108[B], 100[B], 33[B]]? + = [Byte(0x48), Byte(0x65), Byte(0x6C), Byte(0x6C), Byte(0x6F), Byte(0x20), Byte(0x77), Byte(0x6F), Byte(0x72), Byte(0x6C), Byte(0x64), Byte(0x21)]? : [Byte]? >> tmpdir:files():has(tmpfile) = yes diff --git a/test/rng.tm b/test/rng.tm index 3285cd0f..3f477d57 100644 --- a/test/rng.tm +++ b/test/rng.tm @@ -12,23 +12,23 @@ func main(): >> rng:int(1, 1000) = 921 >> rng:int64(1, 1000) - = 324[64] + = Int64(324) >> rng:int32(1, 1000) - = 586[32] + = Int32(586) >> rng:int16(1, 1000) - = 453[16] + = Int16(453) >> rng:int8(1, 100) - = 53[8] + = Int8(53) >> rng:byte() - = 220[B] + = Byte(0xDC) >> rng:bytes(10) - = [160[B], 90[B], 16[B], 63[B], 108[B], 209[B], 53[B], 194[B], 135[B], 140[B]] + = [Byte(0xA0), Byte(0x5A), Byte(0x10), Byte(0x3F), Byte(0x6C), Byte(0xD1), Byte(0x35), Byte(0xC2), Byte(0x87), Byte(0x8C)] >> rng:bool(p=0.8) = yes >> rng:num() = 0.03492503353647658 >> rng:num32(1, 1000) - = 761.05908_f32 + = Num32(761.05908) !! Random array methods: >> nums := [10*i for i in 10] diff --git a/test/text.tm b/test/text.tm index 73b9f952..6ecd211b 100644 --- a/test/text.tm +++ b/test/text.tm @@ -29,21 +29,21 @@ func main(): >> amelie:split() = ["A", "m", "é", "l", "i", "e"] : [Text] >> amelie:utf32_codepoints() - = [65[32], 109[32], 233[32], 108[32], 105[32], 101[32]] : [Int32] + = [Int32(65), Int32(109), Int32(233), Int32(108), Int32(105), Int32(101)] >> amelie:utf8_bytes() - = [65[B], 109[B], 195[B], 169[B], 108[B], 105[B], 101[B]] : [Byte] - >> Text.from_bytes([65[B], 109[B], 195[B], 169[B], 108[B], 105[B], 101[B]])! + = [Byte(0x41), Byte(0x6D), Byte(0xC3), Byte(0xA9), Byte(0x6C), Byte(0x69), Byte(0x65)] + >> Text.from_bytes([:Byte 0x41, 0x6D, 0xC3, 0xA9, 0x6C, 0x69, 0x65])! = "Amélie" - >> Text.from_bytes([255[B]]) + >> Text.from_bytes([Byte(0xFF)]) = !Text >> amelie2 := "Am$(\U65\U301)lie" >> amelie2:split() = ["A", "m", "é", "l", "i", "e"] : [Text] >> amelie2:utf32_codepoints() - = [65[32], 109[32], 233[32], 108[32], 105[32], 101[32]] : [Int32] + = [Int32(65), Int32(109), Int32(233), Int32(108), Int32(105), Int32(101)] >> amelie2:utf8_bytes() - = [65[B], 109[B], 195[B], 169[B], 108[B], 105[B], 101[B]] : [Byte] + = [Byte(0x41), Byte(0x6D), Byte(0xC3), Byte(0xA9), Byte(0x6C), Byte(0x69), Byte(0x65)] >> amelie:codepoint_names() = ["LATIN CAPITAL LETTER A", "LATIN SMALL LETTER M", "LATIN SMALL LETTER E WITH ACUTE", "LATIN SMALL LETTER L", "LATIN SMALL LETTER I", "LATIN SMALL LETTER E"] @@ -216,7 +216,7 @@ func main(): >> house:codepoint_names() = ["CJK Unified Ideographs-5BB6"] >> house:utf32_codepoints() - = [23478[32]] + = [Int32(23478)] >> "🐧":codepoint_names() = ["PENGUIN"] diff --git a/typecheck.c b/typecheck.c index 38b6f7fb..2dcfda42 100644 --- a/typecheck.c +++ b/typecheck.c @@ -491,26 +491,10 @@ type_t *get_type(env_t *env, ast_t *ast) return Type(BoolType); } case Int: { - auto i = Match(ast, Int); - switch (i->bits) { - case IBITS_UNSPECIFIED: return Type(BigIntType); - case IBITS_BYTE: return Type(ByteType); - case IBITS8: return Type(IntType, .bits=TYPE_IBITS8); - case IBITS16: return Type(IntType, .bits=TYPE_IBITS16); - case IBITS32: return Type(IntType, .bits=TYPE_IBITS32); - case IBITS64: return Type(IntType, .bits=TYPE_IBITS64); - default: errx(1, "Invalid integer bits"); - } + return Type(BigIntType); } case Num: { - auto n = Match(ast, Num); - switch (n->bits) { - case NBITS_UNSPECIFIED: case NBITS64: - return Type(NumType, .bits=TYPE_NBITS64); - case NBITS32: - return Type(NumType, .bits=TYPE_NBITS32); - default: errx(1, "Invalid num bits"); - } + return Type(NumType, .bits=TYPE_NBITS64); } case HeapAllocate: { type_t *pointed = get_type(env, Match(ast, HeapAllocate)->value); @@ -1314,12 +1298,9 @@ PUREFUNC bool is_constant(env_t *env, ast_t *ast) case Bool: case Num: case Null: return true; case Int: { auto info = Match(ast, Int); - if (info->bits == IBITS_UNSPECIFIED) { - Int_t int_val = Int$from_text(Text$from_str(info->str)); - if (int_val.small == 0) return false; // Failed to parse - return (Int$compare_value(int_val, I(BIGGEST_SMALL_INT)) <= 0); - } - return true; + Int_t int_val = Int$from_text(Text$from_str(info->str)); + if (int_val.small == 0) return false; // Failed to parse + return (Int$compare_value(int_val, I(BIGGEST_SMALL_INT)) <= 0); } case TextJoin: { auto text = Match(ast, TextJoin); @@ -1357,6 +1338,14 @@ PUREFUNC bool is_constant(env_t *env, ast_t *ast) if (call->fn->tag != Var) return false; binding_t *b = get_binding(env, Match(call->fn, Var)->name); if (b == NULL || b->type->tag != TypeInfoType) return false; + + type_t *t = Match(b->type, TypeInfoType)->type; + if (t->tag == IntType) { + return call->args->value->tag == Int; + } else if (t->tag == NumType) { + return call->args->value->tag == Num; + } + for (arg_ast_t *arg = call->args; arg; arg = arg->next) { if (!is_constant(env, arg->value)) return false; -- cgit v1.2.3