aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-09-01 12:51:19 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-09-01 12:51:19 -0400
commit639d5ddfca562e5b3645955551be244b5e8ca9c6 (patch)
tree2a2fba7b2d442b5cf7e9dd2e1f876e4f799a6bdf
parent0a7062e2d711b5ac7fb71e873f293a8f0d0e8bc6 (diff)
Make text indexing optional
-rw-r--r--examples/colorful/colorful.tm2
-rw-r--r--lib/json/json.tm4
-rw-r--r--src/compile/indexing.c11
-rw-r--r--src/stdlib/text.c5
-rw-r--r--src/stdlib/text.h12
-rw-r--r--src/typecheck.c2
-rw-r--r--test/text.tm3
7 files changed, 31 insertions, 8 deletions
diff --git a/examples/colorful/colorful.tm b/examples/colorful/colorful.tm
index fed873fd..bef77dd3 100644
--- a/examples/colorful/colorful.tm
+++ b/examples/colorful/colorful.tm
@@ -53,7 +53,7 @@ enum _Color(Default, Bright(color:Int16), Color8Bit(color:Int16), Color24Bit(col
hex := text.from(2)
return none unless hex.length == 3 or hex.length == 6
if hex.length == 3
- hex = hex[1]++hex[1]++hex[2]++hex[2]++hex[3]++hex[3]
+ hex = hex[1]!++hex[1]!++hex[2]!++hex[2]!++hex[3]!++hex[3]!
n := Int32.parse("0x" ++ hex) or return none
return Color24Bit(n)
else if text.matches_pattern($Pat/{1-3 digit}/)
diff --git a/lib/json/json.tm b/lib/json/json.tm
index ab9b95dd..1ac19426 100644
--- a/lib/json/json.tm
+++ b/lib/json/json.tm
@@ -65,8 +65,8 @@ enum JSON(
string := ""
pos := 2
escapes := {"n"="\n", "t"="\t", "r"="\r", '"'='"', "\\"="\\", "/"="/", "b"="\b", "f"="\f"}
- while pos <= text.length
- c := text[pos]
+ repeat
+ c := text[pos] or stop
if c == '"'
if remainder
remainder[] = text.from(pos + 1)
diff --git a/src/compile/indexing.c b/src/compile/indexing.c
index cca06184..9d3501c9 100644
--- a/src/compile/indexing.c
+++ b/src/compile/indexing.c
@@ -73,8 +73,15 @@ Text_t compile_indexing(env_t *env, ast_t *ast, bool checked) {
compile_none(table_type->value_type), ", ", compile_type_info(container_t), ")");
}
} else if (container_t->tag == TextType) {
- return Texts("Text$cluster(", compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ",
- compile_to_type(env, indexing->index, Type(BigIntType)), ")");
+ if (checked) {
+ int64_t start = (int64_t)(ast->start - ast->file->text), end = (int64_t)(ast->end - ast->file->text);
+ return Texts("Text$cluster_checked(", compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ",
+ compile_to_type(env, indexing->index, Type(BigIntType)), ", ", String(start), ", ",
+ String(end), ")");
+ } else {
+ return Texts("Text$cluster(", compile_to_pointer_depth(env, indexing->indexed, 0, false), ", ",
+ compile_to_type(env, indexing->index, Type(BigIntType)), ")");
+ }
} else {
code_err(ast, "Indexing is not supported for type: ", type_to_str(container_t));
}
diff --git a/src/stdlib/text.c b/src/stdlib/text.c
index ed4023a4..f4533be5 100644
--- a/src/stdlib/text.c
+++ b/src/stdlib/text.c
@@ -725,7 +725,10 @@ Text_t Text$reversed(Text_t text) {
}
public
-PUREFUNC Text_t Text$cluster(Text_t text, Int_t index) { return Text$slice(text, index, index); }
+PUREFUNC OptionalText_t Text$cluster(Text_t text, Int_t index) {
+ Text_t slice = Text$slice(text, index, index);
+ return slice.length <= 0 ? NONE_TEXT : slice;
+}
static Text_t Text$from_components(List_t graphemes, Table_t unique_clusters) {
size_t blob_size = (sizeof(int32_t[unique_clusters.entries.length]) + sizeof(uint8_t[graphemes.length]));
diff --git a/src/stdlib/text.h b/src/stdlib/text.h
index 5fa95675..c08e5267 100644
--- a/src/stdlib/text.h
+++ b/src/stdlib/text.h
@@ -41,7 +41,17 @@ Text_t Text$slice(Text_t text, Int_t first_int, Int_t last_int);
Text_t Text$from(Text_t text, Int_t first);
Text_t Text$to(Text_t text, Int_t last);
Text_t Text$reversed(Text_t text);
-Text_t Text$cluster(Text_t text, Int_t index_int);
+OptionalText_t Text$cluster(Text_t text, Int_t index_int);
+#define Text$cluster_checked(text_expr, index_expr, start, end) \
+ ({ \
+ const Text_t text = text_expr; \
+ Int_t index = index_expr; \
+ OptionalText_t cluster = Text$cluster(text, index); \
+ if (unlikely(cluster.length < 0)) \
+ fail_source(__SOURCE_FILE__, start, end, "Invalid text index: ", index, " (text has length ", \
+ (int64_t)text.length, ")\n"); \
+ cluster; \
+ })
OptionalText_t Text$from_str(const char *str);
OptionalText_t Text$from_strn(const char *str, size_t len);
PUREFUNC uint64_t Text$hash(const void *text, const TypeInfo_t *);
diff --git a/src/typecheck.c b/src/typecheck.c
index eba12a6e..2af2408e 100644
--- a/src/typecheck.c
+++ b/src/typecheck.c
@@ -897,7 +897,7 @@ type_t *get_type(env_t *env, ast_t *ast) {
if (table_type->default_value) return table_type->value_type;
return Type(OptionalType, table_type->value_type);
} else if (value_t->tag == TextType) {
- return value_t;
+ return Type(OptionalType, value_t);
} else {
code_err(ast, "I don't know how to index ", type_to_str(indexed_t), " values");
}
diff --git a/test/text.tm b/test/text.tm
index ff55555d..a32db2ce 100644
--- a/test/text.tm
+++ b/test/text.tm
@@ -34,6 +34,9 @@ func main()
>> str[9]
= "é"
+ >> str[99]
+ = none
+
>> "\{UE9}"
= "é"