diff --git a/Makefile b/Makefile index f642547..97801d2 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/builtins/datatypes.h b/builtins/datatypes.h index f1aa574..12c7cbc 100644 --- a/builtins/datatypes.h +++ b/builtins/datatypes.h @@ -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 diff --git a/builtins/integers.c b/builtins/integers.c index d50c657..db1a73e 100644 --- a/builtins/integers.c +++ b/builtins/integers.c @@ -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; \ diff --git a/builtins/integers.h b/builtins/integers.h index 2c638a4..4ab3049 100644 --- a/builtins/integers.h +++ b/builtins/integers.h @@ -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; diff --git a/builtins/range.c b/builtins/range.c new file mode 100644 index 0000000..ced2a26 --- /dev/null +++ b/builtins/range.c @@ -0,0 +1,58 @@ +// Functions that operate on numeric ranges + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/builtins/range.h b/builtins/range.h new file mode 100644 index 0000000..9f3b25a --- /dev/null +++ b/builtins/range.h @@ -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 diff --git a/builtins/tomo.h b/builtins/tomo.h index 482c8d2..595d631 100644 --- a/builtins/tomo.h +++ b/builtins/tomo.h @@ -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" diff --git a/compile.c b/compile.c index 9f91161..a141302 100644 --- a/compile.c +++ b/compile.c @@ -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; diff --git a/environment.c b/environment.c index 3058066..69ad9b6 100644 --- a/environment.c +++ b/environment.c @@ -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; diff --git a/environment.h b/environment.h index 80723b0..b72d481 100644 --- a/environment.h +++ b/environment.h @@ -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