aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-02-13 15:03:22 -0500
committerBruce Hill <bruce@bruce-hill.com>2025-02-13 15:03:22 -0500
commit5be955904682300153af0abdfd6b711b9da2ac8f (patch)
treee616bc83904817fdefa43354fa815f13daf18739
parent8e0f1fa227be762cab19234abc106ba4c572077d (diff)
Deprecate Range datatype in favor of using iterator methods
-rw-r--r--Makefile2
-rw-r--r--compile.c64
-rw-r--r--docs/README.md1
-rw-r--r--docs/integers.md23
-rw-r--r--docs/ranges.md65
-rw-r--r--environment.c34
-rw-r--r--environment.h1
-rw-r--r--stdlib/README.md1
-rw-r--r--stdlib/datatypes.h4
-rw-r--r--stdlib/integers.c45
-rw-r--r--stdlib/integers.h14
-rw-r--r--stdlib/ranges.c74
-rw-r--r--stdlib/ranges.h10
-rw-r--r--stdlib/tomo.h1
-rw-r--r--test/ranges.tm31
15 files changed, 112 insertions, 258 deletions
diff --git a/Makefile b/Makefile
index a0194a90..a41312e5 100644
--- a/Makefile
+++ b/Makefile
@@ -32,7 +32,7 @@ CFLAGS_PLACEHOLDER="$$(echo -e '\033[2m<flags...>\033[m')"
LDLIBS=-lgc -lcord -lm -lunistring -lgmp -ldl
BUILTIN_OBJS=stdlib/siphash.o stdlib/arrays.o stdlib/bools.o stdlib/bytes.o stdlib/nums.o stdlib/integers.o \
stdlib/pointers.o stdlib/memory.o stdlib/text.o stdlib/threads.o stdlib/c_strings.o stdlib/tables.o \
- stdlib/types.o stdlib/util.o stdlib/files.o stdlib/ranges.o stdlib/shell.o stdlib/paths.o stdlib/rng.o \
+ stdlib/types.o stdlib/util.o stdlib/files.o stdlib/shell.o stdlib/paths.o stdlib/rng.o \
stdlib/optionals.o stdlib/patterns.o stdlib/metamethods.o stdlib/functiontype.o stdlib/stdlib.o \
stdlib/structs.o stdlib/enums.o stdlib/moments.o stdlib/mutexeddata.o
TESTS=$(patsubst %.tm,%.tm.testresult,$(wildcard test/*.tm))
diff --git a/compile.c b/compile.c
index 154a9e07..04ec9e00 100644
--- a/compile.c
+++ b/compile.c
@@ -500,7 +500,6 @@ PUREFUNC CORD compile_unsigned_type(type_t *t)
CORD compile_type(type_t *t)
{
if (t == THREAD_TYPE) return "Thread_t";
- else if (t == RANGE_TYPE) return "Range_t";
else if (t == RNG_TYPE) return "RNG_t";
else if (t == MATCH_TYPE) return "Match_t";
@@ -1541,26 +1540,58 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
naked_body = CORD_all(naked_body, "\n", loop_ctx.skip_label, ": continue;");
CORD stop = loop_ctx.stop_label ? CORD_all("\n", loop_ctx.stop_label, ":;") : CORD_EMPTY;
- type_t *iter_t = value_type(get_type(env, for_->iter));
- type_t *iter_value_t = value_type(iter_t);
+ // Special case for improving performance for numeric iteration:
+ if (for_->iter->tag == MethodCall && streq(Match(for_->iter, MethodCall)->name, "to") &&
+ get_type(env, Match(for_->iter, MethodCall)->self)->tag == BigIntType) {
+ arg_ast_t *args = Match(for_->iter, MethodCall)->args;
+ if (!args) code_err(for_->iter, "to() needs at least one argument");
+
+ CORD last = CORD_EMPTY, step = CORD_EMPTY, optional_step = CORD_EMPTY;
+ if (!args->name || streq(args->name, "last")) {
+ last = compile_to_type(env, args->value, INT_TYPE);
+ if (args->next) {
+ if (args->next->name && !streq(args->next->name, "step"))
+ code_err(args->next->value, "Invalid argument name: %s", args->next->name);
+ if (get_type(env, args->next->value)->tag == OptionalType)
+ optional_step = compile_to_type(env, args->next->value, Type(OptionalType, .type=INT_TYPE));
+ else
+ step = compile_to_type(env, args->next->value, INT_TYPE);
+ }
+ } else if (streq(args->name, "step")) {
+ if (get_type(env, args->value)->tag == OptionalType)
+ optional_step = compile_to_type(env, args->value, Type(OptionalType, .type=INT_TYPE));
+ else
+ step = compile_to_type(env, args->value, INT_TYPE);
+ if (args->next) {
+ if (args->next->name && !streq(args->next->name, "last"))
+ code_err(args->next->value, "Invalid argument name: %s", args->next->name);
+ last = compile_to_type(env, args->next->value, INT_TYPE);
+ }
+ }
+
+ if (!last)
+ code_err(for_->iter, "No `last` argument was given");
+
+ if (step && optional_step)
+ step = CORD_all("({ OptionalInt_t maybe_step = ", optional_step, "; maybe_step->small == 0 ? (Int$compare_value(last, first) >= 0 ? I_small(1) : I_small(-1)) : (Int_t)maybe_step; })");
+ else if (!step)
+ step = "Int$compare_value(last, first) >= 0 ? I_small(1) : I_small(-1)";
- if (iter_value_t == RANGE_TYPE) {
- CORD range = compile_to_pointer_depth(env, for_->iter, 0, false);
CORD value = for_->vars ? compile(body_scope, for_->vars->ast) : "i";
- if (for_->empty)
- code_err(ast, "Ranges are never empty, they always contain at least their starting element");
return CORD_all(
- "{\n"
- "const Range_t range = ", range, ";\n"
- "if (range.step.small == 0) fail(\"This range has a 'step' of zero and will loop infinitely!\");\n"
- "bool negative = (Int$compare_value(range.step, I(0)) < 0);\n"
- "for (Int_t ", value, " = range.first; ({ int32_t cmp = Int$compare_value(", value, ", range.last); negative ? cmp >= 0 : cmp <= 0;}) ; ", value, " = Int$plus(", value, ", range.step)) {\n"
+ "for (Int_t first = ", compile(env, Match(for_->iter, MethodCall)->self), ", ",
+ value, " = first, "
+ "last = ", last, ", "
+ "step = ", step, "; "
+ "Int$compare_value(", value, ", last) != Int$compare_value(step, I_small(0)); ", value, " = Int$plus(", value, ", step)) {\n"
"\t", naked_body,
- "\n}",
- stop,
- "\n}");
+ "}",
+ stop);
}
+ type_t *iter_t = value_type(get_type(env, for_->iter));
+ type_t *iter_value_t = value_type(iter_t);
+
switch (iter_value_t->tag) {
case ArrayType: {
type_t *item_t = Match(iter_value_t, ArrayType)->item_type;
@@ -3848,7 +3879,7 @@ CORD compile(env_t *env, ast_t *ast)
case StructType: {
for (arg_t *field = Match(value_t, StructType)->fields; field; field = field->next) {
if (streq(field->name, f->field)) {
- const char *prefix = (value_t == RANGE_TYPE || value_t == MATCH_TYPE) ? "" : "$";
+ const char *prefix = (value_t == MATCH_TYPE) ? "" : "$";
if (fielded_t->tag == PointerType) {
CORD fielded = compile_to_pointer_depth(env, f->fielded, 1, false);
return CORD_asprintf("(%r)->%s%s", fielded, prefix, f->field);
@@ -4086,7 +4117,6 @@ void compile_namespace(env_t *env, const char *ns_name, ast_t *block)
CORD compile_type_info(env_t *env, type_t *t)
{
if (t == THREAD_TYPE) return "&Thread$info";
- else if (t == RANGE_TYPE) return "&Range$info";
else if (t == MATCH_TYPE) return "&Match$info";
else if (t == RNG_TYPE) return "&RNG$info";
diff --git a/docs/README.md b/docs/README.md
index e5253963..01e261f3 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -24,7 +24,6 @@ Information about Tomo's built-in types can be found here:
- [Moment](moments.md)
- [Enums](enums.md)
- [Floating point numbers](nums.md)
-- [Integer Ranges](ranges.md)
- [Integers](integers.md)
- [Languages](langs.md)
- [Mutexed Data](mutexed.md)
diff --git a/docs/integers.md b/docs/integers.md
index b69ed2b9..b6600314 100644
--- a/docs/integers.md
+++ b/docs/integers.md
@@ -297,25 +297,34 @@ of the representable range or if the entire text can't be parsed as an integer,
### `to`
**Description:**
-Creates an inclusive range of integers between the specified start and end values.
+Returns an iterator function that iterates over the range of numbers specified.
+Iteration is assumed to be nonempty and
**Signature:**
```tomo
-func to(from: Int, to: Int -> Range)
+func to(first: Int, last: Int, step : Int? = none:Int -> func(->Int?))
```
**Parameters:**
-- `from`: The starting value of the range.
-- `to`: The ending value of the range.
+- `first`: The starting value of the range.
+- `last`: The ending value of the range.
+- `step`: An optional step size to use. If unspecified or `none`, the step will be inferred to be `+1` if `last >= first`, otherwise `-1`.
**Returns:**
-A range object representing all integers from `from` to `to` (inclusive).
+An iterator function that returns each integer in the given range (inclusive).
**Example:**
```tomo
->> 1:to(5)
-= Range(first=1, last=5, step=1)
+>> 2:to(5)
+= func(->Int?)
+>> [x for x in 2:to(5)]
+= [2, 3, 4, 5]
+>> [x for x in 5:to(2)]
+= [5, 4, 3, 2]
+
+>> [x for x in 2:to(5, step=2)]
+= [2, 4]
```
---
diff --git a/docs/ranges.md b/docs/ranges.md
deleted file mode 100644
index 799b0eb3..00000000
--- a/docs/ranges.md
+++ /dev/null
@@ -1,65 +0,0 @@
-# Ranges
-
-Ranges are Tomo's way to do iteration over numeric ranges. Ranges are typically
-created using the `Int.to()` method like so: `5:to(10)`. Ranges are
-*inclusive*.
-
-```tomo
->> [i for i in 3:to(5)]
-= [3, 4, 5]
-```
-
----
-
-## Range Methods
-
-### `reversed`
-
-**Description:**
-Returns a reversed copy of the range.
-
-**Signature:**
-```tomo
-func reversed(range: Range -> Range)
-```
-
-**Parameters:**
-
-- `range`: The range to be reversed.
-
-**Returns:**
-A new `Range` with the order of elements reversed.
-
-**Example:**
-```tomo
->> 1:to(5):reversed()
-= Range(first=5, last=1, step=-1)
-```
-
----
-
-### `by`
-
-**Description:**
-Creates a new range with a specified step value.
-
-**Signature:**
-```tomo
-func by(range: Range, step: Int -> Range)
-```
-
-**Parameters:**
-
-- `range`: The original range.
-- `step`: The step value to be used in the new range.
-
-**Returns:**
-A new `Range` that increments by the specified step value.
-
-**Example:**
-```tomo
->> 1:to(5):by(2)
-= Range(first=1, last=5, step=2)
-```
-
----
diff --git a/environment.c b/environment.c
index 5f3fe763..1427e9ec 100644
--- a/environment.c
+++ b/environment.c
@@ -13,7 +13,6 @@
type_t *TEXT_TYPE = NULL;
type_t *MATCH_TYPE = NULL;
-type_t *RANGE_TYPE = NULL;
type_t *RNG_TYPE = NULL;
public type_t *THREAD_TYPE = NULL;
@@ -68,15 +67,6 @@ env_t *new_compilation_unit(CORD libname)
} ns_entry_t;
{
- env_t *range_env = namespace_env(env, "Range");
- RANGE_TYPE = Type(
- StructType, .name="Range", .env=range_env,
- .fields=new(arg_t, .name="first", .type=INT_TYPE,
- .next=new(arg_t, .name="last", .type=INT_TYPE,
- .next=new(arg_t, .name="step", .type=INT_TYPE, .default_val=FakeAST(Int, .str="1")))));
- }
-
- {
env_t *match_env = namespace_env(env, "Match");
MATCH_TYPE = Type(
StructType, .name="Match", .env=match_env,
@@ -142,7 +132,7 @@ env_t *new_compilation_unit(CORD libname)
{"right_shifted", "Int$right_shifted", "func(x,y:Int -> Int)"},
{"sqrt", "Int$sqrt", "func(x:Int -> Int?)"},
{"times", "Int$times", "func(x,y:Int -> Int)"},
- {"to", "Int$to", "func(from:Int,to:Int -> Range)"},
+ {"to", "Int$to", "func(from:Int,to:Int,step=none:Int -> func(->Int?))"},
)},
{"Int64", Type(IntType, .bits=TYPE_IBITS64), "Int64_t", "Int64$info", TypedArray(ns_entry_t,
{"abs", "labs", "func(i:Int64 -> Int64)"},
@@ -158,7 +148,7 @@ env_t *new_compilation_unit(CORD libname)
{"modulo", "Int64$modulo", "func(x,y:Int64 -> Int64)"},
{"modulo1", "Int64$modulo1", "func(x,y:Int64 -> Int64)"},
{"octal", "Int64$octal", "func(i:Int64, digits=0, prefix=yes -> Text)"},
- {"to", "Int64$to", "func(from:Int64,to:Int64 -> Range)"},
+ {"to", "Int64$to", "func(from:Int64,to:Int64,step=none:Int64 -> func(->Int?))"},
{"unsigned_left_shifted", "Int64$unsigned_left_shifted", "func(x:Int64,y:Int64 -> Int64)"},
{"unsigned_right_shifted", "Int64$unsigned_right_shifted", "func(x:Int64,y:Int64 -> Int64)"},
{"wrapping_minus", "Int64$wrapping_minus", "func(x:Int64,y:Int64 -> Int64)"},
@@ -178,7 +168,7 @@ env_t *new_compilation_unit(CORD libname)
{"modulo", "Int32$modulo", "func(x,y:Int32 -> Int32)"},
{"modulo1", "Int32$modulo1", "func(x,y:Int32 -> Int32)"},
{"octal", "Int32$octal", "func(i:Int32, digits=0, prefix=yes -> Text)"},
- {"to", "Int32$to", "func(from:Int32,to:Int32 -> Range)"},
+ {"to", "Int32$to", "func(from:Int32,to:Int32,step=none:Int32 -> func(->Int?))"},
{"unsigned_left_shifted", "Int32$unsigned_left_shifted", "func(x:Int32,y:Int32 -> Int32)"},
{"unsigned_right_shifted", "Int32$unsigned_right_shifted", "func(x:Int32,y:Int32 -> Int32)"},
{"wrapping_minus", "Int32$wrapping_minus", "func(x:Int32,y:Int32 -> Int32)"},
@@ -198,7 +188,7 @@ env_t *new_compilation_unit(CORD libname)
{"modulo", "Int16$modulo", "func(x,y:Int16 -> Int16)"},
{"modulo1", "Int16$modulo1", "func(x,y:Int16 -> Int16)"},
{"octal", "Int16$octal", "func(i:Int16, digits=0, prefix=yes -> Text)"},
- {"to", "Int16$to", "func(from:Int16,to:Int16 -> Range)"},
+ {"to", "Int16$to", "func(from:Int16,to:Int16,step=none:Int16 -> func(->Int?))"},
{"unsigned_left_shifted", "Int16$unsigned_left_shifted", "func(x:Int16,y:Int16 -> Int16)"},
{"unsigned_right_shifted", "Int16$unsigned_right_shifted", "func(x:Int16,y:Int16 -> Int16)"},
{"wrapping_minus", "Int16$wrapping_minus", "func(x:Int16,y:Int16 -> Int16)"},
@@ -218,7 +208,7 @@ env_t *new_compilation_unit(CORD libname)
{"modulo", "Int8$modulo", "func(x,y:Int8 -> Int8)"},
{"modulo1", "Int8$modulo1", "func(x,y:Int8 -> Int8)"},
{"octal", "Int8$octal", "func(i:Int8, digits=0, prefix=yes -> Text)"},
- {"to", "Int8$to", "func(from:Int8,to:Int8 -> Range)"},
+ {"to", "Int8$to", "func(from:Int8,to:Int8,step=none:Int8 -> func(->Int?))"},
{"unsigned_left_shifted", "Int8$unsigned_left_shifted", "func(x:Int8,y:Int8 -> Int8)"},
{"unsigned_right_shifted", "Int8$unsigned_right_shifted", "func(x:Int8,y:Int8 -> Int8)"},
{"wrapping_minus", "Int8$wrapping_minus", "func(x:Int8,y:Int8 -> Int8)"},
@@ -285,10 +275,6 @@ env_t *new_compilation_unit(CORD libname)
#undef F_opt
#undef F
#undef C
- {"Range", RANGE_TYPE, "Range_t", "Range", TypedArray(ns_entry_t,
- {"reversed", "Range$reversed", "func(range:Range -> Range)"},
- {"by", "Range$by", "func(range:Range, step:Int -> Range)"},
- )},
{"Match", MATCH_TYPE, "Match_t", "Match", TypedArray(ns_entry_t,
// No methods
)},
@@ -547,16 +533,6 @@ env_t *for_scope(env_t *env, ast_t *ast)
type_t *iter_t = value_type(get_type(env, for_->iter));
env_t *scope = fresh_scope(env);
- if (iter_t == RANGE_TYPE) {
- if (for_->vars) {
- if (for_->vars->next)
- code_err(for_->vars->next->ast, "This is too many variables for this loop");
- const char *var = Match(for_->vars->ast, Var)->name;
- set_binding(scope, var, INT_TYPE, CORD_cat("_$", var));
- }
- return scope;
- }
-
switch (iter_t->tag) {
case ArrayType: {
type_t *item_t = Match(iter_t, ArrayType)->item_type;
diff --git a/environment.h b/environment.h
index 8f4eb1c1..287bb2b2 100644
--- a/environment.h
+++ b/environment.h
@@ -71,7 +71,6 @@ binding_t *get_namespace_binding(env_t *env, ast_t *self, const char *name);
#define code_err(ast, ...) compiler_err((ast)->file, (ast)->start, (ast)->end, __VA_ARGS__)
extern type_t *TEXT_TYPE;
extern type_t *MATCH_TYPE;
-extern type_t *RANGE_TYPE;
extern type_t *RNG_TYPE;
extern type_t *THREAD_TYPE;
diff --git a/stdlib/README.md b/stdlib/README.md
index 9916c064..c3814114 100644
--- a/stdlib/README.md
+++ b/stdlib/README.md
@@ -29,7 +29,6 @@ some common functionality.
- Paths: [header](stdlib/paths.h), [implementation](stdlib/paths.c)
- Patterns: [header](stdlib/patterns.h), [implementation](stdlib/patterns.c)
- Pointers: [header](stdlib/pointers.h), [implementation](stdlib/pointers.c)
-- Ranges: [header](stdlib/ranges.h), [implementation](stdlib/ranges.c)
- Shell: [header](stdlib/shell.h), [implementation](stdlib/shell.c)
- Tables: [header](stdlib/tables.h), [implementation](stdlib/tables.c)
- Text: [header](stdlib/text.h), [implementation](stdlib/text.c)
diff --git a/stdlib/datatypes.h b/stdlib/datatypes.h
index 0254060b..af37446a 100644
--- a/stdlib/datatypes.h
+++ b/stdlib/datatypes.h
@@ -62,10 +62,6 @@ typedef struct {
void *fn, *userdata;
} Closure_t;
-typedef struct Range_s {
- Int_t first, last, step;
-} Range_t;
-
enum text_type { TEXT_ASCII, TEXT_GRAPHEMES, TEXT_CONCAT };
typedef struct Text_s {
diff --git a/stdlib/integers.c b/stdlib/integers.c
index e5344370..858025fa 100644
--- a/stdlib/integers.c
+++ b/stdlib/integers.c
@@ -32,6 +32,11 @@ public Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t*) {
return text;
}
+static bool Int$is_none(const void *i, const TypeInfo_t*)
+{
+ return ((Int_t*)i)->small == 0;
+}
+
public PUREFUNC int32_t Int$compare_value(const Int_t x, const Int_t y) {
if (likely(x.small & y.small & 1))
return (x.small > y.small) - (x.small < y.small);
@@ -330,8 +335,31 @@ public OptionalInt_t Int$sqrt(Int_t i)
return Int$from_mpz(result);
}
-public PUREFUNC Range_t Int$to(Int_t from, Int_t to) {
- return (Range_t){from, to, Int$compare_value(to, from) >= 0 ? (Int_t){.small=(1<<2)|1} : (Int_t){.small=(-1>>2)|1}};
+typedef struct {
+ OptionalInt_t current, last;
+ Int_t step;
+} IntRange_t;
+
+static OptionalInt_t _next_int(IntRange_t *info)
+{
+ OptionalInt_t i = info->current;
+ if (!Int$is_none(&i, &Int$info)) {
+ Int_t next = Int$plus(i, info->step);
+ if (!Int$is_none(&info->last, &Int$info) && Int$compare_value(next, info->last) == Int$compare_value(info->step, I(0)))
+ next = NONE_INT;
+ info->current = next;
+ }
+ return i;
+}
+
+public PUREFUNC Closure_t Int$to(Int_t first, Int_t last, OptionalInt_t step) {
+ IntRange_t *range = GC_MALLOC(sizeof(IntRange_t));
+ range->current = first;
+ range->last = last;
+ range->step = Int$is_none(&step, &Int$info) ?
+ Int$compare_value(last, first) >= 0 ? (Int_t){.small=(1<<2)|1} : (Int_t){.small=(-1>>2)|1}
+ : step;
+ return (Closure_t){.fn=_next_int, .userdata=range};
}
public Int_t Int$from_str(const char *str) {
@@ -415,11 +443,6 @@ public Int_t Int$factorial(Int_t n)
return Int$from_mpz(ret);
}
-static bool Int$is_none(const void *i, const TypeInfo_t*)
-{
- return ((Int_t*)i)->small == 0;
-}
-
static void Int$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t*)
{
Int_t i = *(Int_t*)obj;
@@ -545,8 +568,12 @@ public void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t
} \
return bit_array; \
} \
- public to_attr Range_t KindOfInt ## $to(c_type from, c_type to) { \
- return (Range_t){Int64_to_Int(from), Int64_to_Int(to), to >= from ? I_small(1): I_small(-1)}; \
+ public to_attr Closure_t KindOfInt ## $to(c_type first, c_type last, Optional ## KindOfInt ## _t step) { \
+ IntRange_t *range = GC_MALLOC(sizeof(IntRange_t)); \
+ range->current = KindOfInt##_to_Int(first); \
+ range->last = KindOfInt##_to_Int(last); \
+ range->step = step.is_none ? (last >= first ? I_small(1) : I_small(-1)) : KindOfInt##_to_Int(step.i); \
+ return (Closure_t){.fn=_next_int, .userdata=range}; \
} \
public PUREFUNC Optional ## KindOfInt ## _t KindOfInt ## $parse(Text_t text) { \
OptionalInt_t full_int = Int$parse(text); \
diff --git a/stdlib/integers.h b/stdlib/integers.h
index 033a6873..fce243a9 100644
--- a/stdlib/integers.h
+++ b/stdlib/integers.h
@@ -22,7 +22,7 @@
#define I16(x) ((int16_t)x)
#define I8(x) ((int8_t)x)
-#define DEFINE_INT_TYPE(c_type, type_name, to_attr) \
+#define DEFINE_INT_TYPE(c_type, type_name) \
typedef struct { \
c_type i; \
bool is_none:1; \
@@ -34,7 +34,7 @@
Text_t type_name ## $hex(c_type i, Int_t digits, bool uppercase, bool prefix); \
Text_t type_name ## $octal(c_type i, Int_t digits, bool prefix); \
Array_t type_name ## $bits(c_type x); \
- to_attr Range_t type_name ## $to(c_type from, c_type to); \
+ Closure_t type_name ## $to(c_type first, c_type last, Optional ## type_name ## _t step); \
PUREFUNC Optional ## type_name ## _t type_name ## $parse(Text_t text); \
MACROLIKE PUREFUNC c_type type_name ## $clamped(c_type x, c_type min, c_type max) { \
return x < min ? min : (x > max ? max : x); \
@@ -68,10 +68,10 @@
return (c_type)((u##c_type)x >> y); \
}
-DEFINE_INT_TYPE(int64_t, Int64, __attribute__(()))
-DEFINE_INT_TYPE(int32_t, Int32, CONSTFUNC)
-DEFINE_INT_TYPE(int16_t, Int16, CONSTFUNC)
-DEFINE_INT_TYPE(int8_t, Int8, CONSTFUNC)
+DEFINE_INT_TYPE(int64_t, Int64)
+DEFINE_INT_TYPE(int32_t, Int32)
+DEFINE_INT_TYPE(int16_t, Int16)
+DEFINE_INT_TYPE(int8_t, Int8)
#undef DEFINE_INT_TYPE
#define NONE_INT64 ((OptionalInt64_t){.is_none=true})
@@ -101,7 +101,7 @@ PUREFUNC bool Int$equal_value(const Int_t x, const Int_t y);
Text_t Int$format(Int_t i, Int_t digits);
Text_t Int$hex(Int_t i, Int_t digits, bool uppercase, bool prefix);
Text_t Int$octal(Int_t i, Int_t digits, bool prefix);
-PUREFUNC Range_t Int$to(Int_t from, Int_t to);
+PUREFUNC Closure_t Int$to(Int_t first, Int_t last, OptionalInt_t step);
OptionalInt_t Int$from_str(const char *str);
OptionalInt_t Int$parse(Text_t text);
Int_t Int$abs(Int_t x);
diff --git a/stdlib/ranges.c b/stdlib/ranges.c
deleted file mode 100644
index dfb878ea..00000000
--- a/stdlib/ranges.c
+++ /dev/null
@@ -1,74 +0,0 @@
-// Functions that operate on numeric ranges
-
-#include <ctype.h>
-#include <err.h>
-#include <gc.h>
-#include <math.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/param.h>
-
-#include "integers.h"
-#include "text.h"
-#include "types.h"
-#include "util.h"
-
-
-PUREFUNC static int32_t Range$compare(const void *vx, const void *vy, const TypeInfo_t *)
-{
- if (vx == vy) return 0;
- Range_t *x = (Range_t*)vx, *y = (Range_t*)vy;
- int32_t diff = Int$compare(&x->first, &y->first, &Int$info);
- if (diff != 0) return diff;
- diff = Int$compare(&x->last, &y->last, &Int$info);
- if (diff != 0) return diff;
- return Int$compare(&x->step, &y->step, &Int$info);
-}
-
-PUREFUNC static bool Range$equal(const void *vx, const void *vy, const TypeInfo_t*)
-{
- if (vx == vy) return true;
- Range_t *x = (Range_t*)vx, *y = (Range_t*)vy;
- return Int$equal(&x->first, &y->first, &Int$info) && Int$equal(&x->last, &y->last, &Int$info) && Int$equal(&x->step, &y->step, &Int$info);
-}
-
-static Text_t Range$as_text(const void *obj, bool use_color, const TypeInfo_t *)
-{
- if (!obj) return Text("Range");
-
- Range_t *r = (Range_t*)obj;
- Text_t first = Int$as_text(&r->first, use_color, &Int$info);
- Text_t last = Int$as_text(&r->last, use_color, &Int$info);
- Text_t step = Int$as_text(&r->step, use_color, &Int$info);
- return Text$format(use_color ? "\x1b[0;1mRange\x1b[m(first=%k, last=%k, step=%k)"
- : "Range(first=%k, last=%k, step=%k)", &first, &last, &step);
-}
-
-PUREFUNC public Range_t Range$reversed(Range_t r)
-{
- return (Range_t){r.last, r.first, Int$negative(r.step)};
-}
-
-PUREFUNC public Range_t Range$by(Range_t r, Int_t step)
-{
- return (Range_t){r.first, r.last, Int$times(step, r.step)};
-}
-
-static bool Range$is_none(const void *obj, const TypeInfo_t*)
-{
- return ((Range_t*)obj)->step.small == 0x1;
-}
-
-public const TypeInfo_t Range$info = {
- .size=sizeof(Range_t),
- .align=__alignof(Range_t),
- .metamethods={
- .as_text=Range$as_text,
- .compare=Range$compare,
- .equal=Range$equal,
- .is_none=Range$is_none,
- },
-};
-
-// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/stdlib/ranges.h b/stdlib/ranges.h
deleted file mode 100644
index 1656bdaa..00000000
--- a/stdlib/ranges.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#pragma once
-
-// Ranges represent numeric ranges
-
-PUREFUNC Range_t Range$reversed(Range_t r);
-PUREFUNC Range_t Range$by(Range_t r, Int_t step);
-
-extern const TypeInfo_t Range$info;
-
-// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1
diff --git a/stdlib/tomo.h b/stdlib/tomo.h
index 7ed25412..d3887af4 100644
--- a/stdlib/tomo.h
+++ b/stdlib/tomo.h
@@ -24,7 +24,6 @@
#include "paths.h"
#include "patterns.h"
#include "pointers.h"
-#include "ranges.h"
#include "rng.h"
#include "shell.h"
#include "siphash.h"
diff --git a/test/ranges.tm b/test/ranges.tm
deleted file mode 100644
index 0ff67c1a..00000000
--- a/test/ranges.tm
+++ /dev/null
@@ -1,31 +0,0 @@
-
-func main():
- >> r := 5:to(10):by(2)
- = Range(first=5, last=10, step=2)
- >> r.first
- = 5
- >> r.last
- = 10
- >> r.step
- = 2
-
- >> Range(1, 5) == 1:to(5)
- = yes
-
- >> 1:to(5) == 5:to(1):reversed()
- = yes
-
- >> Range(1, 5) == Range(5, 10)
- = no
-
- >> [i for i in 3:to(5)]
- = [3, 4, 5]
-
- >> [i for i in 3:to(10):by(2)]
- = [3, 5, 7, 9]
-
- >> [i for i in 3:to(10):reversed():by(2)]
- = [10, 8, 6, 4]
-
- >> [i for i in (2:to(10):by(2)):by(2)]
- = [2, 6, 10]