aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compile.c64
-rw-r--r--src/environment.c1
-rw-r--r--src/stdlib/bytes.c25
-rw-r--r--src/stdlib/bytes.h9
-rw-r--r--src/stdlib/datatypes.h8
-rw-r--r--src/typecheck.c12
-rw-r--r--src/types.c2
7 files changed, 84 insertions, 37 deletions
diff --git a/src/compile.c b/src/compile.c
index 40d9ed4e..3b382111 100644
--- a/src/compile.c
+++ b/src/compile.c
@@ -947,7 +947,7 @@ CORD optional_into_nonnone(type_t *t, CORD value)
{
if (t->tag == OptionalType) t = Match(t, OptionalType)->type;
switch (t->tag) {
- case IntType:
+ case IntType: case ByteType:
return CORD_all(value, ".value");
case StructType:
if (t == PATH_TYPE || t == PATH_TYPE_TYPE)
@@ -1500,52 +1500,70 @@ static CORD _compile_statement(env_t *env, ast_t *ast)
// 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) {
+ is_int_type(get_type(env, Match(for_->iter, MethodCall)->self))) {
// TODO: support other integer types
arg_ast_t *args = Match(for_->iter, MethodCall)->args;
if (!args) code_err(for_->iter, "to() needs at least one argument");
+ type_t *int_type = get_type(env, Match(for_->iter, MethodCall)->self);
+ type_t *step_type = int_type->tag == ByteType ? Type(IntType, .bits=TYPE_IBITS8) : int_type;
+
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);
+ 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: ", 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));
+ optional_step = compile_to_type(env, args->next->value, Type(OptionalType, step_type));
else
- step = compile_to_type(env, args->next->value, INT_TYPE);
+ step = compile_to_type(env, args->next->value, step_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));
+ optional_step = compile_to_type(env, args->value, Type(OptionalType, step_type));
else
- step = compile_to_type(env, args->value, INT_TYPE);
+ step = compile_to_type(env, args->value, step_type);
if (args->next) {
if (args->next->name && !streq(args->next->name, "last"))
code_err(args->next->value, "Invalid argument name: ", args->next->name);
- last = compile_to_type(env, args->next->value, INT_TYPE);
+ 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)";
-
+ CORD type_code = compile_type(int_type);
CORD value = for_->vars ? compile(body_scope, for_->vars->ast) : "i";
- return CORD_all(
- "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,
- "}",
- stop);
+ if (int_type->tag == BigIntType) {
+ if (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)";
+ return CORD_all(
+ "for (", type_code, " 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,
+ "}",
+ stop);
+ } else {
+ if (optional_step)
+ step = CORD_all("({ ", compile_type(Type(OptionalType, step_type)), " maybe_step = ", optional_step, "; "
+ "maybe_step.is_none ? (", type_code, ")(last >= first ? 1 : -1) : maybe_step.value; })");
+ else if (!step)
+ step = CORD_all("(", type_code, ")(last >= first ? 1 : -1)");
+ return CORD_all(
+ "for (", type_code, " first = ", compile(env, Match(for_->iter, MethodCall)->self), ", ",
+ value, " = first, last = ", last, ", step = ", step, "; "
+ "step > 0 ? ", value, " <= last : ", value, " >= last; ",
+ value, " += step) {\n"
+ "\t", naked_body,
+ "}",
+ stop);
+ }
} else if (for_->iter->tag == MethodCall && streq(Match(for_->iter, MethodCall)->name, "onward") &&
get_type(env, Match(for_->iter, MethodCall)->self)->tag == BigIntType) {
// Special case for Int.onward()
@@ -1974,7 +1992,7 @@ CORD compile_to_pointer_depth(env_t *env, ast_t *ast, int64_t target_depth, bool
CORD compile_to_type(env_t *env, ast_t *ast, type_t *t)
{
- if (ast->tag == Int && is_numeric_type(t)) {
+ if (ast->tag == Int && is_numeric_type(non_optional(t))) {
return compile_int_to_type(env, ast, t);
} else if (ast->tag == Num && t->tag == NumType) {
double n = Match(ast, Num)->n;
diff --git a/src/environment.c b/src/environment.c
index 0d55be9c..927f2e87 100644
--- a/src/environment.c
+++ b/src/environment.c
@@ -88,6 +88,7 @@ env_t *global_env(void)
{"max", "Byte$max", "Byte"},
{"hex", "Byte$hex", "func(byte:Byte, uppercase=yes, prefix=no -> Text)"},
{"min", "Byte$min", "Byte"},
+ {"to", "Byte$to", "func(first:Byte,last:Byte,step:Int8?=none -> func(->Byte?))"},
)},
{"Int", Type(BigIntType), "Int_t", "Int$info", TypedList(ns_entry_t,
{"abs", "Int$abs", "func(x:Int -> Int)"},
diff --git a/src/stdlib/bytes.c b/src/stdlib/bytes.c
index 09e19c40..1203aa55 100644
--- a/src/stdlib/bytes.c
+++ b/src/stdlib/bytes.c
@@ -30,6 +30,31 @@ public Text_t Byte$hex(Byte_t byte, bool uppercase, bool prefix) {
return text;
}
+typedef struct {
+ OptionalByte_t current, last;
+ Int8_t step;
+} ByteRange_t;
+
+static OptionalByte_t _next_Byte(ByteRange_t *info) {
+ OptionalByte_t i = info->current;
+ if (!i.is_none) {
+ Byte_t next; bool overflow = __builtin_add_overflow(i.value, info->step, &next);
+ if (overflow || (!info->last.is_none && (info->step >= 0 ? next > info->last.value : next < info->last.value)))
+ info->current = (OptionalByte_t){.is_none=true};
+ else
+ info->current = (OptionalByte_t){.value=next};
+ }
+ return i;
+}
+
+public CONSTFUNC Closure_t Byte$to(Byte_t first, Byte_t last, OptionalInt8_t step) {
+ ByteRange_t *range = GC_MALLOC(sizeof(ByteRange_t));
+ range->current = (OptionalByte_t){.value=first};
+ range->last = (OptionalByte_t){.value=last};
+ range->step = step.is_none ? (last >= first ? 1 : -1) : step.value;
+ return (Closure_t){.fn=_next_Byte, .userdata=range};
+}
+
public PUREFUNC Byte_t Byte$from_int(Int_t i, bool truncate) {
if unlikely (truncate && Int$compare_value(i, I_small(0xFF)) > 0)
fail("This value is too large to convert to a byte without truncation: ", i);
diff --git a/src/stdlib/bytes.h b/src/stdlib/bytes.h
index ac1b61a3..f875e7cb 100644
--- a/src/stdlib/bytes.h
+++ b/src/stdlib/bytes.h
@@ -6,6 +6,7 @@
#include <stdint.h>
#include "datatypes.h"
+#include "integers.h"
#include "stdlib.h"
#include "types.h"
#include "util.h"
@@ -18,6 +19,7 @@ Byte_t Byte$from_int(Int_t i, bool truncate);
Byte_t Byte$from_int64(int64_t i, bool truncate);
Byte_t Byte$from_int32(int32_t i, bool truncate);
Byte_t Byte$from_int16(int16_t i, bool truncate);
+Closure_t Byte$to(Byte_t first, Byte_t last, OptionalInt8_t step);
MACROLIKE Byte_t Byte$from_int8(int8_t i) { return (Byte_t)i; }
MACROLIKE Byte_t Byte$from_bool(bool b) { return (Byte_t)b; }
@@ -28,11 +30,4 @@ extern const TypeInfo_t Byte$info;
Text_t Byte$hex(Byte_t byte, bool uppercase, bool prefix);
-typedef struct {
- Byte_t value;
- bool has_value:1;
-} OptionalByte_t;
-
-#define NONE_BYTE ((OptionalByte_t){.has_value=false})
-
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/src/stdlib/datatypes.h b/src/stdlib/datatypes.h
index 950c9457..77c09ddb 100644
--- a/src/stdlib/datatypes.h
+++ b/src/stdlib/datatypes.h
@@ -111,4 +111,12 @@ typedef struct {
#define OptionalText_t Text_t
#define OptionalClosure_t Closure_t
+typedef struct {
+ Byte_t value;
+ bool is_none:1;
+} OptionalByte_t;
+
+#define NONE_BYTE ((OptionalByte_t){.is_none=true})
+
+
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/src/typecheck.c b/src/typecheck.c
index ce8b4276..fedc7c96 100644
--- a/src/typecheck.c
+++ b/src/typecheck.c
@@ -1056,9 +1056,9 @@ type_t *get_type(env_t *env, ast_t *ast)
type_t *lhs_t = get_type(env, binop.lhs);
type_t *rhs_t = get_type(env, binop.rhs);
- if (binop.lhs->tag == Int && (is_int_type(rhs_t) || rhs_t->tag == ByteType))
+ if (binop.lhs->tag == Int && is_int_type(rhs_t))
return rhs_t;
- else if (binop.rhs->tag == Int && (is_int_type(lhs_t) || lhs_t->tag == ByteType))
+ else if (binop.rhs->tag == Int && is_int_type(lhs_t))
return lhs_t;
// `opt? or (x == y)` / `(x == y) or opt?` is a boolean conditional:
@@ -1102,9 +1102,9 @@ type_t *get_type(env_t *env, ast_t *ast)
type_t *lhs_t = get_type(env, binop.lhs);
type_t *rhs_t = get_type(env, binop.rhs);
- if (binop.lhs->tag == Int && (is_int_type(rhs_t) || rhs_t->tag == ByteType))
+ if (binop.lhs->tag == Int && is_int_type(rhs_t))
return rhs_t;
- else if (binop.rhs->tag == Int && (is_int_type(lhs_t) || lhs_t->tag == ByteType))
+ else if (binop.rhs->tag == Int && is_int_type(lhs_t))
return lhs_t;
// `and` between optionals/bools is a boolean expression like `if opt? and opt?:` or `if x > 0 and opt?:`
@@ -1136,9 +1136,9 @@ type_t *get_type(env_t *env, ast_t *ast)
type_t *lhs_t = get_type(env, binop.lhs);
type_t *rhs_t = get_type(env, binop.rhs);
- if (binop.lhs->tag == Int && (is_int_type(rhs_t) || rhs_t->tag == ByteType))
+ if (binop.lhs->tag == Int && is_int_type(rhs_t))
return rhs_t;
- else if (binop.rhs->tag == Int && (is_int_type(lhs_t) || lhs_t->tag == ByteType))
+ else if (binop.rhs->tag == Int && is_int_type(lhs_t))
return lhs_t;
// `xor` between optionals/bools is a boolean expression like `if opt? xor opt?:` or `if x > 0 xor opt?:`
diff --git a/src/types.c b/src/types.c
index 83c3e702..efd83477 100644
--- a/src/types.c
+++ b/src/types.c
@@ -420,7 +420,7 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed)
PUREFUNC bool is_int_type(type_t *t)
{
- return t->tag == IntType || t->tag == BigIntType;
+ return t->tag == IntType || t->tag == BigIntType || t->tag == ByteType;
}
PUREFUNC bool is_numeric_type(type_t *t)