aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-11-05 15:18:32 -0500
committerBruce Hill <bruce@bruce-hill.com>2024-11-05 15:18:32 -0500
commitb8d7eabc023bf9db0150049d8e909086f6ad91bc (patch)
tree5b678c7949638b3f07095affc04611f0422ef8c7
parent2fa26e6af3ec1599396d9260ef44b0d035b1f686 (diff)
Deprecate bit-width integer/num literals in favor of using type
constructors
-rw-r--r--ast.c4
-rw-r--r--ast.h2
-rw-r--r--compile.c89
-rw-r--r--docs/integers.md62
-rw-r--r--docs/nums.md2
-rw-r--r--environment.c17
-rw-r--r--examples/base64/base64.tm14
-rw-r--r--examples/http/http.tm4
-rw-r--r--parse.c24
-rw-r--r--repl.c15
-rw-r--r--stdlib/bytes.c2
-rw-r--r--stdlib/integers.c10
-rw-r--r--stdlib/nums.c2
-rw-r--r--test/bytes.tm8
-rw-r--r--test/datetime.tm4
-rw-r--r--test/inline_c.tm2
-rw-r--r--test/integers.tm36
-rw-r--r--test/lang.tm2
-rw-r--r--test/nums.tm4
-rw-r--r--test/optionals.tm6
-rw-r--r--test/paths.tm2
-rw-r--r--test/rng.tm14
-rw-r--r--test/text.tm14
-rw-r--r--typecheck.c37
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, "<Null>%r</Null>", type_ast_to_xml(data.type))
T(Bool, "<Bool value=\"%s\" />", data.b ? "yes" : "no")
T(Var, "<Var>%s</Var>", data.name)
- T(Int, "<Int bits=\"%d\">%s</Int>", data.bits, data.str)
- T(Num, "<Num bits=\"%d\">%g</Num>", data.bits, data.n)
+ T(Int, "<Int>%s</Int>", data.str)
+ T(Num, "<Num>%g</Num>", data.n)
T(TextLiteral, "%r", xml_escape(data.cord))
T(TextJoin, "<Text%r>%r</Text>", data.lang ? CORD_all(" lang=\"", data.lang, "\"") : CORD_EMPTY, ast_list_to_xml(data.children))
T(Declare, "<Declare var=\"%r\">%r</Declare>", 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;