Add a Range datatype with creation methods like 5:to(10) and

modification methods like `range:by(2)` or `range:reversed()`
This commit is contained in:
Bruce Hill 2024-08-05 14:40:28 -04:00
parent 8e2156ac48
commit c045c54309
10 changed files with 124 additions and 1 deletions

View File

@ -26,7 +26,7 @@ CFLAGS=$(CCONFIG) $(EXTRA) $(CWARN) $(G) $(O) $(OSFLAGS)
LDLIBS=-lgc -lcord -lm -lunistring -ldl
BUILTIN_OBJS=builtins/array.o builtins/bool.o builtins/nums.o builtins/functions.o builtins/integers.o \
builtins/pointer.o builtins/memory.o builtins/text.o builtins/where.o builtins/c_string.o builtins/table.o \
builtins/types.o builtins/util.o builtins/files.o
builtins/types.o builtins/util.o builtins/files.o builtins/range.o
TESTS=$(patsubst %.tm,%.tm.testresult,$(wildcard test/*.tm))
all: libtomo.so tomo

View File

@ -54,4 +54,8 @@ typedef struct {
void *fn, *userdata;
} closure_t;
typedef struct Range_s {
int64_t first, last, step;
} Range_t;
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -68,6 +68,9 @@
} \
return (c_type)((uint64_t)min + (r % range)); \
} \
public Range_t KindOfInt ## $to(c_type from, c_type to) { \
return (Range_t){from, to, to >= from ? 1 : -1}; \
} \
public c_type KindOfInt ## $from_text(CORD text, CORD *the_rest) { \
const char *str = CORD_to_const_char_star(text); \
long i; \

View File

@ -28,6 +28,7 @@
CORD type_name ## $octal(c_type i, int64_t digits, bool prefix); \
array_t type_name ## $bits(c_type x); \
c_type type_name ## $random(c_type min, c_type max); \
Range_t type_name ## $to(c_type from, c_type to); \
c_type type_name ## $from_text(CORD text, CORD *the_rest); \
extern const c_type type_name ## $min, type_name##$max; \
extern const TypeInfo $ ## type_name;

58
builtins/range.c Normal file
View File

@ -0,0 +1,58 @@
// Functions that operate on numeric ranges
#include <ctype.h>
#include <err.h>
#include <gc.h>
#include <gc/cord.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/param.h>
#include "types.h"
#include "util.h"
static int32_t Range$compare(const Range_t *x, const Range_t *y, const TypeInfo *type)
{
(void)type;
int32_t diff = (x->first > y->first) - (x->first < y->first);
if (diff != 0) return diff;
diff = (x->last > y->last) - (x->last < y->last);
if (diff != 0) return diff;
return (x->step > y->step) - (x->step < y->step);
}
static bool Range$equal(const Range_t *x, const Range_t *y, const TypeInfo *type)
{
(void)type;
return (x->first == y->first) && (x->last == y->last) && (x->step == y->step);
}
static CORD Range$as_text(const Range_t *r, bool use_color, const TypeInfo *type)
{
(void)type;
if (!r) return "Range";
return CORD_asprintf(use_color ? "\x1b[0;1mRange\x1b[m(first=\x1b[35m%ld\x1b[m, last=\x1b[35m%ld\x1b[m, step=\x1b[35m%ld\x1b[m)"
: "Range(first=%ld, last=%ld, step=%ld)", r->first, r->last, r->step);
}
public Range_t Range$reversed(Range_t r)
{
return (Range_t){r.last, r.first, -r.step};
}
public Range_t Range$by(Range_t r, int64_t step)
{
return (Range_t){r.first, r.last, step*r.step};
}
public const TypeInfo Range = {sizeof(Range_t), __alignof(Range_t), {.tag=CustomInfo, .CustomInfo={
.as_text=(void*)Range$as_text,
.compare=(void*)Range$compare,
.equal=(void*)Range$equal,
}}};
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

10
builtins/range.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
// Ranges represent numeric ranges
Range_t Range$reversed(Range_t r);
Range_t Range$by(Range_t r, int64_t step);
extern const TypeInfo Range;
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1

View File

@ -20,6 +20,7 @@
#include "memory.h"
#include "nums.h"
#include "pointer.h"
#include "range.h"
#include "table.h"
#include "text.h"
#include "types.h"

View File

@ -817,6 +817,22 @@ CORD compile_statement(env_t *env, ast_t *ast)
body = CORD_all(body, "\n", loop_ctx.skip_label, ": continue;");
CORD stop = loop_ctx.stop_label ? CORD_all("\n", loop_ctx.stop_label, ":;") : CORD_EMPTY;
if (iter_t == RANGE_TYPE) {
CORD value = for_->vars ? compile(env, for_->vars->ast) : "i";
CORD range = compile(env, for_->iter);
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 == 0) fail(\"This range has a 'step' of zero and will loop infinitely!\");\n"
"for (int64_t ", value, " = range.first; range.step > 0 ? ", value, " <= range.last : ", value, " >= range.last; ", value, " += range.step) {\n"
"\t", body,
"\n}",
stop,
"\n}");
}
switch (iter_t->tag) {
case ArrayType: {
type_t *item_t = Match(iter_t, ArrayType)->item_type;

View File

@ -10,6 +10,7 @@
#include "builtins/util.h"
type_t *TEXT_TYPE = NULL;
type_t *RANGE_TYPE = NULL;
env_t *new_compilation_unit(CORD *libname)
{
@ -58,6 +59,15 @@ env_t *new_compilation_unit(CORD *libname)
set_binding(where_env, "End", new(binding_t, .type=where, .code="Where$tagged$End"));
}
{
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, .i=1, .bits=64)))));
}
struct {
const char *name;
type_t *type;
@ -80,6 +90,7 @@ env_t *new_compilation_unit(CORD *libname)
{"abs", "labs", "func(i:Int)->Int"},
{"min", "Int$min", "Int"},
{"max", "Int$max", "Int"},
{"to", "Int$to", "func(from:Int,to:Int)->Range"},
)},
{"Int32", Type(IntType, .bits=32), "Int32_t", "$Int32", TypedArray(ns_entry_t,
{"format", "Int32$format", "func(i:Int32, digits=0)->Text"},
@ -91,6 +102,7 @@ env_t *new_compilation_unit(CORD *libname)
{"abs", "abs", "func(i:Int32)->Int32"},
{"min", "Int32$min", "Int32"},
{"max", "Int32$max", "Int32"},
{"to", "Int32$to", "func(from:Int32,to:Int32)->Range"},
)},
{"Int16", Type(IntType, .bits=16), "Int16_t", "$Int16", TypedArray(ns_entry_t,
{"format", "Int16$format", "func(i:Int16, digits=0)->Text"},
@ -102,6 +114,7 @@ env_t *new_compilation_unit(CORD *libname)
{"abs", "abs", "func(i:Int16)->Int16"},
{"min", "Int16$min", "Int16"},
{"max", "Int16$max", "Int16"},
{"to", "Int16$to", "func(from:Int16,to:Int16)->Range"},
)},
{"Int8", Type(IntType, .bits=8), "Int8_t", "$Int8", TypedArray(ns_entry_t,
{"format", "Int8$format", "func(i:Int8, digits=0)->Text"},
@ -113,6 +126,7 @@ env_t *new_compilation_unit(CORD *libname)
{"abs", "abs", "func(i:Int8)->Int8"},
{"min", "Int8$min", "Int8"},
{"max", "Int8$max", "Int8"},
{"to", "Int8$to", "func(from:Int8,to:Int8)->Range"},
)},
#define C(name) {#name, "M_"#name, "Num"}
#define F(name) {#name, #name, "func(n:Num)->Num"}
@ -174,6 +188,10 @@ env_t *new_compilation_unit(CORD *libname)
#undef F
#undef C
{"Where", where, "Where_t", "Where", {}},
{"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"},
)},
{"Text", TEXT_TYPE, "Text_t", "$Text", TypedArray(ns_entry_t,
// {"find", "Text$find", "func(text:Text, pattern:Text)->FindResult"},
{"as_c_string", "CORD_to_char_star", "func(text:Text)->CString"},
@ -286,6 +304,17 @@ env_t *for_scope(env_t *env, ast_t *ast)
auto for_ = Match(ast, For);
type_t *iter_t = 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, new(binding_t, .type=Type(IntType, .bits=64), .code=CORD_cat("$", var)));
}
return scope;
}
switch (iter_t->tag) {
case ArrayType: {
type_t *item_t = Match(iter_t, ArrayType)->item_type;

View File

@ -74,5 +74,6 @@ void set_binding(env_t *env, const char *name, binding_t *binding);
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 *RANGE_TYPE;
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0