From f1338fb4b8a6fec0cc3d7cd1d166862687aa7196 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 12:19:57 +0100 Subject: [PATCH 01/27] Solve Advent of Code 2024 Day 2 --- packages_v5/example.candy | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index e804d9f15..e70552cec 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -731,6 +731,36 @@ fun identity[T](t: T) T { t } +# Advent of Code 2024 +# https://adventofcode.com/2024 + +fun day2Part1(input: Text) Int { + day2Wrapper(input, (line: List[Int]) { day2CheckLine(line) }) +} +fun day2Part2(input: Text) Int { + day2Wrapper(input, (line: List[Int]) { + listOf(line) + .concat(line.indexes().map((index: Int) { line.removeAt(index) })) + .any((line: List[Int]) { day2CheckLine(line) }) + }) +} +fun day2Wrapper(input: Text, checkLine: (List[Int]) Bool) Int { + input + .split("\n") + .map((line: Text) { line.split(" ").map((number: Text) { parseInt(number).unwrap() }) }) + .filter(checkLine) + .length() +} +fun day2CheckLine(line: List[Int]) Bool { + line + .pairs() + .all((pair: Pair[Int, Int]) { + let difference = pair.second.subtract(pair.first).absolute() + difference.isAtLeast(1).and(difference.isAtMost(3)) + }) + .and(line.isStrictlyAscending().or(line.isStrictlyDescending())) +} + fun main() Int { identity(1) identity("foo") From 19a8ba77f887466b12e41ee6bbead11779d79c3c Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 12:34:26 +0100 Subject: [PATCH 02/27] Find referenced IDs in switch cases --- compiler_v4/src/mono.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler_v4/src/mono.rs b/compiler_v4/src/mono.rs index c18373af3..6cf84afed 100644 --- a/compiler_v4/src/mono.rs +++ b/compiler_v4/src/mono.rs @@ -262,8 +262,13 @@ impl Body { referenced_ids.insert(*lambda); referenced_ids.extend(arguments.iter()); } - ExpressionKind::Switch { value, .. } => { + ExpressionKind::Switch { value, cases, .. } => { referenced_ids.insert(*value); + for case in cases.iter() { + referenced_ids.extend(case.value_id.iter()); + case.body + .collect_defined_and_referenced_ids(defined_ids, referenced_ids); + } } ExpressionKind::Lambda(Lambda { parameters, body }) => { defined_ids.extend(parameters.iter().map(|it| it.id)); From 52e887c622ca07ff97c325a4d89417be6c46bb0d Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 12:34:46 +0100 Subject: [PATCH 03/27] Fix referenced ID collection for ExpressionKind::CreateStruct --- compiler_v4/src/mono.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler_v4/src/mono.rs b/compiler_v4/src/mono.rs index 6cf84afed..050afe46a 100644 --- a/compiler_v4/src/mono.rs +++ b/compiler_v4/src/mono.rs @@ -241,7 +241,7 @@ impl Body { match &expression.kind { ExpressionKind::Int(_) | ExpressionKind::Text(_) => {} ExpressionKind::CreateStruct { fields, .. } => { - defined_ids.extend(fields.iter()); + referenced_ids.extend(fields.iter()); } ExpressionKind::StructAccess { struct_, .. } => { referenced_ids.insert(*struct_); From f8b1b0cbe242d4037e10a2c51097f6265df13741 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 14:18:14 +0100 Subject: [PATCH 04/27] Find type of lambda / switch case parameters --- compiler_v4/src/hir_to_mono.rs | 22 ++++++++++++++++++++- compiler_v4/src/mono.rs | 36 ++++++++++++++++++++++------------ compiler_v4/src/mono_to_c.rs | 15 ++------------ 3 files changed, 47 insertions(+), 26 deletions(-) diff --git a/compiler_v4/src/hir_to_mono.rs b/compiler_v4/src/hir_to_mono.rs index 7787e51ce..c1a4469a5 100644 --- a/compiler_v4/src/hir_to_mono.rs +++ b/compiler_v4/src/hir_to_mono.rs @@ -558,6 +558,14 @@ impl<'c, 'h> BodyBuilder<'c, 'h> { } => { let value = self.lower_id(*value); let enum_ = self.lower_type(enum_); + + let mono::TypeDeclaration::Enum { variants } = + &self.context.type_declarations[&enum_].as_ref().unwrap() + else { + unreachable!(); + }; + let variants = variants.clone(); + let cases = cases .iter() .map(|case| { @@ -566,7 +574,19 @@ impl<'c, 'h> BodyBuilder<'c, 'h> { .map(|hir_id| (hir_id, self.id_generator.generate())); mono::SwitchCase { variant: case.variant.clone(), - value_id: value_ids.map(|(_, mir_id)| mir_id), + value: value_ids.map(|(_, mir_id)| { + ( + mir_id, + variants + .iter() + .find(|it| it.name == case.variant) + .unwrap() + .value_type + .as_ref() + .unwrap() + .clone(), + ) + }), body: self .build_inner(|builder| { if let Some((hir_id, mir_id)) = value_ids { diff --git a/compiler_v4/src/mono.rs b/compiler_v4/src/mono.rs index 050afe46a..10af013d7 100644 --- a/compiler_v4/src/mono.rs +++ b/compiler_v4/src/mono.rs @@ -265,7 +265,9 @@ impl Body { ExpressionKind::Switch { value, cases, .. } => { referenced_ids.insert(*value); for case in cases.iter() { - referenced_ids.extend(case.value_id.iter()); + if let Some((value_id, _)) = &case.value { + referenced_ids.insert(*value_id); + } case.body .collect_defined_and_referenced_ids(defined_ids, referenced_ids); } @@ -278,17 +280,26 @@ impl Body { } } #[must_use] - pub fn find_expression(&self, id: Id) -> Option<&Expression> { + pub fn find_expression(&self, id: Id) -> Option<(&str, Option<&Expression>)> { self.expressions.iter().find_map(|(it_id, _, expression)| { if *it_id == id { - return Some(expression); + return Some((expression.type_.as_ref(), Some(expression))); } match &expression.kind { - ExpressionKind::Switch { cases, .. } => { - cases.iter().find_map(|it| it.body.find_expression(id)) - } - ExpressionKind::Lambda(Lambda { body, .. }) => body.find_expression(id), + ExpressionKind::Switch { cases, .. } => cases.iter().find_map(|it| { + if let Some((value_id, box value_type)) = &it.value + && *value_id == id + { + return Some((value_type, None)); + } + it.body.find_expression(id) + }), + ExpressionKind::Lambda(Lambda { parameters, body }) => parameters + .iter() + .find(|it| it.id == id) + .map(|it| (it.type_.as_ref(), None)) + .or_else(|| body.find_expression(id)), _ => None, } }) @@ -415,14 +426,14 @@ impl ToText for ExpressionKind { #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct SwitchCase { pub variant: Box, - pub value_id: Option, + pub value: Option<(Id, Box)>, pub body: Body, } impl ToText for SwitchCase { fn build_text(&self, builder: &mut TextBuilder) { builder.push(format!("{}", self.variant)); - if let Some(value_id) = self.value_id { - builder.push(format!("({value_id})")); + if let Some((value_id, value_type)) = &self.value { + builder.push(format!("({value_id}: {value_type})")); } builder.push(" => "); self.body.build_text(builder); @@ -455,8 +466,9 @@ impl Lambda { "Couldn't find expression {id} in declaration body {declaration_body:?}" ) }) - .type_ - .clone() + .0 + .to_string() + .into_boxed_str() }, |it| it.type_.clone(), ) diff --git a/compiler_v4/src/mono_to_c.rs b/compiler_v4/src/mono_to_c.rs index f02aee94f..8b89110c2 100644 --- a/compiler_v4/src/mono_to_c.rs +++ b/compiler_v4/src/mono_to_c.rs @@ -835,25 +835,14 @@ impl<'h> Context<'h> { enum_, cases, } => { - let TypeDeclaration::Enum { variants } = &self.mono.type_declarations[enum_] else { - unreachable!(); - }; - self.push(format!("{}* {id};\n", &expression.type_)); self.push(format!("switch ({value}->variant) {{")); for case in cases.iter() { self.push(format!("case {enum_}_{}:\n", case.variant)); - if let Some(value_id) = case.value_id { - let variant_type = variants - .iter() - .find(|variant| variant.name == case.variant) - .unwrap() - .value_type - .as_ref() - .unwrap(); + if let Some((value_id, value_type)) = &case.value { self.push(format!( - "{variant_type}* {value_id} = {value}->value.{};\n", + "{value_type}* {value_id} = {value}->value.{};\n", case.variant, )); } From 6c7f3d4b4fb84b8fde656650caca39806df10b71 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 14:39:15 +0100 Subject: [PATCH 05/27] =?UTF-8?q?Add=20coerceAtLeast(=E2=80=A6),=20coerceA?= =?UTF-8?q?tMost(=E2=80=A6),=20coerceInRange(=E2=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages_v5/example.candy | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index e70552cec..f8a0d63f4 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -51,6 +51,25 @@ fun isAtLeast[T: Compare](left: T, right: T) Bool { greater => true, } } +fun coerceAtLeast[T: Compare](value: T, minimum: T) T { + switch value.compareTo(minimum) { + less => minimum, + equal => value, + greater => value, + } +} +fun coerceAtMost[T: Compare](value: T, maximum: T) T { + switch value.compareTo(maximum) { + less => value, + equal => value, + greater => maximum, + } +} +fun coerceInRange[T: Compare](value: T, minimum: T, maximum: T) T { + needs(minimum.isAtMost(maximum)) + + value.coerceAtLeast(minimum).coerceAtMost(maximum) +} enum Ordering { less, equal, greater } impl Ordering: Compare { fun compareTo(self: Ordering, other: Ordering) Ordering { From f68ed4f8c6dee3d9d600ca3db3afa54a161d38c0 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 14:39:23 +0100 Subject: [PATCH 06/27] =?UTF-8?q?Add=20text.get(=E2=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages_v5/example.candy | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index f8a0d63f4..54e25a511 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -259,6 +259,12 @@ fun concat(self: Text, other: Text) Text { self.builtinTextConcat(other) } # TODO: Support ranges when we have them. +fun get(self: Text, index: Int) Maybe[Text] { + switch index.isNonNegative().and(index.isLessThan(self.length())) { + false => none[Text](), + true => some(self.getRange(index, index.add(1))), + } +} fun getRange(self: Text, startInclusive: Int, endExclusive: Int) Text { self.builtinTextGetRange(startInclusive, endExclusive) } From 5c4c5fca63762af880e4fa661c8239999c386088 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 14:39:29 +0100 Subject: [PATCH 07/27] Add text.lines() --- packages_v5/example.candy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index 54e25a511..388ef9e57 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -319,6 +319,9 @@ fun splitIfHelper(self: Text, predicate: (Text) Bool, currentStartOffset: Int, o }, } } +fun lines(self: Text) List[Text] { + self.split("\n") +} fun indexOf(self: Text, other: Text) Maybe[Int] { self.builtinTextIndexOf(other) From 8855f37c2d9757b3e7e71d123d3f302666c21ead Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 14:39:37 +0100 Subject: [PATCH 08/27] =?UTF-8?q?Add=20text.allIndexesOfOverlapping(?= =?UTF-8?q?=E2=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages_v5/example.candy | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index 388ef9e57..fd1cb1caf 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -326,6 +326,18 @@ fun lines(self: Text) List[Text] { fun indexOf(self: Text, other: Text) Maybe[Int] { self.builtinTextIndexOf(other) } +fun allIndexesOfOverlapping(self: Text, other: Text) List[Int] { + self.allIndexesOfOverlappingHelper(other, 0, listOf[Int]()) +} +fun allIndexesOfOverlappingHelper(self: Text, other: Text, offset: Int, result: List[Int]) List[Int] { + switch self.getRange(offset, self.length()).indexOf(other) { + none => result, + some(index) => { + let index = offset.add(index) + self.allIndexesOfOverlappingHelper(other, index.add(1), result.append(index)) + }, + } +} fun contains(self: Text, other: Text) Bool { self.indexOf(other).isSome() } From 108327ed74c6d094919fbf878a1637e845ced7c1 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 14:39:53 +0100 Subject: [PATCH 09/27] =?UTF-8?q?Add=20list.skip(=E2=80=A6),=20.skipLast(?= =?UTF-8?q?=E2=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages_v5/example.candy | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index fd1cb1caf..7acb889cf 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -471,6 +471,14 @@ fun getRange[T](list: List[T], startInclusive: Int, endExclusive: Int) List[T] { ), } } +fun skip[T](list: List[T], count: Int) List[T] { + needs(count.isNonNegative()) + getRange(list, count, list.length()) +} +fun skipLast[T](list: List[T], count: Int) List[T] { + needs(count.isNonNegative()) + getRange(list, 0, list.length().subtract(count)) +} fun indexes[T](list: List[T]) List[Int] { listGenerate(list.length(), (i: Int) { i }) } From e00ff5687b5aa6d74f3167f3887a0caba55dc058 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 14:40:01 +0100 Subject: [PATCH 10/27] =?UTF-8?q?Add=20range(=E2=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages_v5/example.candy | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index 7acb889cf..61641fbe3 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -479,8 +479,18 @@ fun skipLast[T](list: List[T], count: Int) List[T] { needs(count.isNonNegative()) getRange(list, 0, list.length().subtract(count)) } +fun range(length: Int) List[Int] { + needs(length.isNonNegative()) + + listGenerate(length, (i: Int) { i }) +} +fun range(startInclusive: Int, endExclusive: Int) List[Int] { + needs(startInclusive.isAtMost(endExclusive)) + + listGenerate(endExclusive.subtract(startInclusive), (i: Int) { startInclusive.add(i) }) +} fun indexes[T](list: List[T]) List[Int] { - listGenerate(list.length(), (i: Int) { i }) + list.length().range() } # TODO: .firstIndexWhere(…), .firstWhere(…), .firstIndexOf(…), .lastIndexWhere(…), .lastWhere(…), .lastIndexOf(…) From 2f7b3325f46c75f04bbe67d8d0ef0141984a20b4 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 14:40:13 +0100 Subject: [PATCH 11/27] =?UTF-8?q?Add=20list.mapIndexed(=E2=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages_v5/example.candy | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index 61641fbe3..ae310709f 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -547,8 +547,11 @@ fun isSortedBy[T](list: List[T], checkPair: (T, T) Bool) Bool { } fun map[T, R](list: List[T], transform: (T) R) List[R] { + list.mapIndexed((index: Int, item: T) { transform(item) }) +} +fun mapIndexed[T, R](list: List[T], transform: (Int, T) R) List[R] { list.fold(listOf[R](), (result: List[R], item: T) { - result.append(transform(item)) + result.append(transform(result.length(), item)) }) } fun filter[T](list: List[T], predicate: (T) Bool) List[T] { From 7eada84763f8088299945948528d257b17d872c8 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 14:40:17 +0100 Subject: [PATCH 12/27] Add list.sum() --- packages_v5/example.candy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index ae310709f..deb7c6971 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -519,6 +519,9 @@ fun reduceHelper[T](list: List[T], index: Int, current: T, combine: (T, T) T) T false => current, } } +fun sum(list: List[Int]) Int { + list.fold(0, (sum: Int, item: Int) { sum.add(item) }) +} fun all[T](list: List[T], predicate: (T) Bool) Bool { list.allHelper(predicate, 0) From 1795a63d0d2ed15147829c8c7ff80d7758488677 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 14:40:22 +0100 Subject: [PATCH 13/27] =?UTF-8?q?Add=20text.join(=E2=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages_v5/example.candy | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index deb7c6971..7cca72123 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -591,12 +591,15 @@ fun pairs[T](list: List[T]) List[Pair[T, T]] { # empty. list.windows(2).map((window: List[T]) { window.toPair().unwrap() }) } + +fun join[T: ToText](self: List[T], separator: Text) Text { + self.map((item: T) { item.toText() }) + .reduce((result: Text, item: Text) { "{result}{separator}{item}" }) + .unwrapOr("") +} impl[T: ToText] List[T]: ToText { fun toText(self: List[T]) Text { - let items = self.map((item: T) { item.toText() }) - .reduce((result: Text, item: Text) { "{result}, {item}" }) - .unwrapOr("") - "[{items}]" + "[{self.join(", ")}]" } } From 8134cb2ecb8783f38274f87c03662e1419e18b88 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 14:40:28 +0100 Subject: [PATCH 14/27] Add bool.toInt() --- packages_v5/example.candy | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index 7cca72123..f6cdc6bdf 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -646,6 +646,12 @@ fun xor(a: Bool, b: Bool) Bool { fun implies(a: Bool, b: Bool) Bool { not(a).or(b) } +fun toInt(self: Bool) Int { + switch self { + false => 0, + true => 1, + } +} impl Bool: Compare { fun compareTo(self: Bool, other: Bool) Ordering { switch self { From 28b3ca59b3d9742705ca3f8546d69ad98c2bb01a Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 14:42:17 +0100 Subject: [PATCH 15/27] Solve Advent of Code 2024 Day 4 --- packages_v5/example.candy | 64 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index f6cdc6bdf..aa2b68fe4 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -819,7 +819,7 @@ fun day2Part2(input: Text) Int { } fun day2Wrapper(input: Text, checkLine: (List[Int]) Bool) Int { input - .split("\n") + .lines() .map((line: Text) { line.split(" ").map((number: Text) { parseInt(number).unwrap() }) }) .filter(checkLine) .length() @@ -834,6 +834,68 @@ fun day2CheckLine(line: List[Int]) Bool { .and(line.isStrictlyAscending().or(line.isStrictlyDescending())) } +fun day4Part1(input: Text) Int { + let lines = input.lines() + let rows = lines.length() + let columns = lines.first().unwrap().length() + lines + .mapIndexed((row: Int, line: Text) { + columns.range() + .map((column: Int) { + let horizontalText = line.getRange(column, column.add(4).coerceAtMost(line.length())) + let verticalText = lines.getRange(row, row.add(4).coerceAtMost(lines.length())) + .map((line: Text) { line.get(column).unwrap() }) + .join("") + let diagonalRightDownText = switch row.isAtMost(rows.subtract(4)) + .and(column.isAtMost(columns.subtract(4))) { + true => range(4) + .map((i: Int) { lines.get(row.add(i)).unwrap().get(column.add(i)).unwrap() }) + .join(""), + false => "", + } + let diagonalLeftDownText = switch row.isAtLeast(3) + .and(column.isAtMost(columns.subtract(4))) { + true => range(4) + .map((i: Int) { lines.get(row.subtract(i)).unwrap().get(column.add(i)).unwrap() }) + .join(""), + false => "", + } + listOf(horizontalText, verticalText, diagonalRightDownText, diagonalLeftDownText) + .filter((text: Text) { text.equals("XMAS").or(text.equals("SAMX")) }) + .length() + }) + .sum() + }) + .sum() +} +fun day4Part2(input: Text) Int { + let lines = input.lines() + let rows = lines.length() + let columns = lines.first().unwrap().length() + range(1, rows.subtract(1)) + .map((row: Int) { + range(1, columns.subtract(1)) + .filter((column: Int) { + switch lines.get(row).unwrap().get(column).unwrap().equals("A") { + false => false, + true => { + let topLeft = lines.get(row.subtract(1)).unwrap().get(column.subtract(1)).unwrap() + let topRight = lines.get(row.subtract(1)).unwrap().get(column.add(1)).unwrap() + let bottomLeft = lines.get(row.add(1)).unwrap().get(column.subtract(1)).unwrap() + let bottomRight = lines.get(row.add(1)).unwrap().get(column.add(1)).unwrap() + let rightDown = topLeft.equals("M").and(bottomRight.equals("S")) + .or(topLeft.equals("S").and(bottomRight.equals("M"))) + let leftDown = topRight.equals("M").and(bottomLeft.equals("S")) + .or(topRight.equals("S").and(bottomLeft.equals("M"))) + rightDown.and(leftDown) + }, + } + }) + .length() + }) + .sum() +} + fun main() Int { identity(1) identity("foo") From 0758482f6357d3340b2e77c7b3c93409d0fc65b2 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 19:04:11 +0100 Subject: [PATCH 16/27] Malloc correct size in list functions --- compiler_v4/src/mono_to_c.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/compiler_v4/src/mono_to_c.rs b/compiler_v4/src/mono_to_c.rs index 8b89110c2..aa133f668 100644 --- a/compiler_v4/src/mono_to_c.rs +++ b/compiler_v4/src/mono_to_c.rs @@ -417,7 +417,7 @@ impl<'h> Context<'h> { {list_type}* result_pointer = malloc(sizeof({list_type})); result_pointer->length = {length}->value; - result_pointer->values = malloc({length}->value * sizeof({item_type})); + result_pointer->values = malloc({length}->value * sizeof({item_type}*)); for (uint64_t i = 0; i < {length}->value; i++) {{ result_pointer->values[i] = {item}; }} @@ -442,7 +442,7 @@ impl<'h> Context<'h> { {list_type}* result_pointer = malloc(sizeof({list_type})); result_pointer->length = {length}->value; - result_pointer->values = malloc({length}->value * sizeof({item_type})); + result_pointer->values = malloc({length}->value * sizeof({item_type}*)); for (uint64_t i = 0; i < {length}->value; i++) {{ Int* index = malloc(sizeof(Int)); index->value = i; @@ -483,8 +483,8 @@ impl<'h> Context<'h> { {list_type}* result_pointer = malloc(sizeof({list_type})); result_pointer->length = {list}->length + 1; - result_pointer->values = malloc(result_pointer->length * sizeof({item_type})); - memcpy(result_pointer->values, {list}->values, {index}->value * sizeof({item_type})); + result_pointer->values = malloc(result_pointer->length * sizeof({item_type}*)); + memcpy(result_pointer->values, {list}->values, {index}->value * sizeof({item_type}*)); result_pointer->values[{index}->value] = {item}; memcpy(result_pointer->values + {index}->value + 1, {list}->values + {index}->value, ({list}->length - {index}->value) * sizeof({item_type})); return result_pointer;", @@ -513,7 +513,7 @@ impl<'h> Context<'h> { "\ {list_type}* result_pointer = malloc(sizeof({list_type})); result_pointer->length = 1; - result_pointer->values = malloc(sizeof({item_type})); + result_pointer->values = malloc(sizeof({item_type}*)); result_pointer->values[0] = {item0}; return result_pointer;", item_type = substitutions["T"], @@ -524,7 +524,7 @@ impl<'h> Context<'h> { "\ {list_type}* result_pointer = malloc(sizeof({list_type})); result_pointer->length = 2; - result_pointer->values = malloc(2 * sizeof({item_type})); + result_pointer->values = malloc(2 * sizeof({item_type}*)); result_pointer->values[0] = {item0}; result_pointer->values[1] = {item1}; return result_pointer;", @@ -537,7 +537,7 @@ impl<'h> Context<'h> { "\ {list_type}* result_pointer = malloc(sizeof({list_type})); result_pointer->length = 3; - result_pointer->values = malloc(3 * sizeof({item_type})); + result_pointer->values = malloc(3 * sizeof({item_type}*)); result_pointer->values[0] = {item0}; result_pointer->values[1] = {item1}; result_pointer->values[2] = {item2}; @@ -552,7 +552,7 @@ impl<'h> Context<'h> { "\ {list_type}* result_pointer = malloc(sizeof({list_type})); result_pointer->length = 4; - result_pointer->values = malloc(4 * sizeof({item_type})); + result_pointer->values = malloc(4 * sizeof({item_type}*)); result_pointer->values[0] = {item0}; result_pointer->values[1] = {item1}; result_pointer->values[2] = {item2}; @@ -569,7 +569,7 @@ impl<'h> Context<'h> { "\ {list_type}* result_pointer = malloc(sizeof({list_type})); result_pointer->length = 5; - result_pointer->values = malloc(5 * sizeof({item_type})); + result_pointer->values = malloc(5 * sizeof({item_type}*)); result_pointer->values[0] = {item0}; result_pointer->values[1] = {item1}; result_pointer->values[2] = {item2}; @@ -599,9 +599,9 @@ impl<'h> Context<'h> { {list_type}* result_pointer = malloc(sizeof({list_type})); result_pointer->length = {list}->length - 1; - result_pointer->values = malloc(result_pointer->length * sizeof({item_type})); - memcpy(result_pointer->values, {list}->values, {index}->value * sizeof({item_type})); - memcpy(result_pointer->values + {index}->value, {list}->values + {index}->value + 1, ({list}->length - {index}->value - 1) * sizeof({item_type})); + result_pointer->values = malloc(result_pointer->length * sizeof({item_type}*)); + memcpy(result_pointer->values, {list}->values, {index}->value * sizeof({item_type}*)); + memcpy(result_pointer->values + {index}->value, {list}->values + {index}->value + 1, ({list}->length - {index}->value - 1) * sizeof({item_type}*)); return result_pointer;", item_type = substitutions["T"], list_type = function.return_type, @@ -623,8 +623,8 @@ impl<'h> Context<'h> { {list_type}* result_pointer = malloc(sizeof({list_type})); result_pointer->length = {list}->length; - result_pointer->values = malloc(result_pointer->length * sizeof({item_type})); - memcpy(result_pointer->values, {list}->values, {list}->length * sizeof({item_type})); + result_pointer->values = malloc(result_pointer->length * sizeof({item_type}*)); + memcpy(result_pointer->values, {list}->values, {list}->length * sizeof({item_type}*)); result_pointer->values[{index}->value] = {new_item}; return result_pointer;", item_type = substitutions["T"], From 11f3ce5fd31976fa0e41f56494420c27488da405 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 19:47:05 +0100 Subject: [PATCH 17/27] Remove old code --- packages_v5/example.candy | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index aa2b68fe4..277296309 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -1,11 +1,6 @@ struct Nothing {} enum Never {} -# struct List[T] {} -# impl[T: Equal] List[T]: Equal { -# # fun equals(self: Self, other: Self) Bool {} -# } - trait ToText { fun toText(self: Self) Text } From 6e944077bc9f6cf3b198eb4a4def6508828812fd Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 19:47:26 +0100 Subject: [PATCH 18/27] =?UTF-8?q?Add=20int.isInRange(=E2=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages_v5/example.candy | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index 277296309..da385563b 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -255,7 +255,7 @@ fun concat(self: Text, other: Text) Text { } # TODO: Support ranges when we have them. fun get(self: Text, index: Int) Maybe[Text] { - switch index.isNonNegative().and(index.isLessThan(self.length())) { + switch index.isInRange(0, self.length()) { false => none[Text](), true => some(self.getRange(index, index.add(1))), } @@ -264,7 +264,7 @@ fun getRange(self: Text, startInclusive: Int, endExclusive: Int) Text { self.builtinTextGetRange(startInclusive, endExclusive) } fun characterAt(self: Text, index: Int) Maybe[Text] { - switch index.isNonNegative().and(index.isLessThan(self.length())) { + switch index.isInRange(0, self.length()) { false => none[Text](), true => some(self.getRange(index, index.add(1))), } @@ -488,6 +488,12 @@ fun indexes[T](list: List[T]) List[Int] { list.length().range() } +fun isInRange[T: Compare](self: T, startInclusive: T, endExclusive: T) Bool { + needs(startInclusive.isAtMost(endExclusive)) + + self.isAtLeast(startInclusive).and(self.isLessThan(endExclusive)) +} + # TODO: .firstIndexWhere(…), .firstWhere(…), .firstIndexOf(…), .lastIndexWhere(…), .lastWhere(…), .lastIndexOf(…) fun print[T: ToText](t: T) { builtinPrint(t.toText()) From 7f62fe77095e157cd2dd5ec0095bee67873c454c Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 19:47:37 +0100 Subject: [PATCH 19/27] Add text.characters() --- packages_v5/example.candy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index da385563b..b923caa0c 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -269,6 +269,9 @@ fun characterAt(self: Text, index: Int) Maybe[Text] { true => some(self.getRange(index, index.add(1))), } } +fun characters(self: Text) List[Text] { + listGenerate(self.length(), (i: Int) { self.get(i).unwrap() }) +} fun startsWith(self: Text, prefix: Text) Bool { switch self.length().isAtLeast(prefix.length()) { From 9b706b0c6fde72a53d025f0bbc88fe72459af57a Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 19:47:49 +0100 Subject: [PATCH 20/27] =?UTF-8?q?Add=20list.foldIndexed(=E2=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages_v5/example.candy | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index b923caa0c..a8c93e579 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -503,12 +503,15 @@ fun print[T: ToText](t: T) { } fun fold[T, R](list: List[T], initial: R, combine: (R, T) R) R { - list.foldHelper(0, initial, combine) + list.foldIndexed(initial, (state: R, index: Int, item: T) { combine(state, item) }) } -fun foldHelper[T, R](list: List[T], index: Int, current: R, combine: (R, T) R) R { +fun foldIndexed[T, R](list: List[T], initial: R, combine: (R, Int, T) R) R { + list.foldIndexedHelper(0, initial, combine) +} +fun foldIndexedHelper[T, R](list: List[T], index: Int, state: R, combine: (R, Int, T) R) R { switch index.isLessThan(list.length()) { - true => list.foldHelper(index.add(1), combine(current, list.get(index).unwrap()), combine), - false => current, + true => list.foldIndexedHelper(index.add(1), combine(state, index, list.get(index).unwrap()), combine), + false => state, } } fun reduce[T](list: List[T], combine: (T, T) T) Maybe[T] { From d336d4be7952e158c97c6d0c98d1e5b5173a2a35 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 19:47:56 +0100 Subject: [PATCH 21/27] =?UTF-8?q?Add=20list.flatMap(=E2=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages_v5/example.candy | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index a8c93e579..606772f37 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -564,6 +564,11 @@ fun mapIndexed[T, R](list: List[T], transform: (Int, T) R) List[R] { result.append(transform(result.length(), item)) }) } +fun flatMap[T, R](list: List[T], transform: (T) List[R]) List[R] { + list.fold(listOf[R](), (result: List[R], item: T) { + result.concat(transform(item)) + }) +} fun filter[T](list: List[T], predicate: (T) Bool) List[T] { list.fold(listOf[T](), (result: List[T], item: T) { switch predicate(item) { From 37786628ebfc2af7b1d6c2c39a675df321f43d86 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 19:48:28 +0100 Subject: [PATCH 22/27] Add List2D --- packages_v5/example.candy | 64 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index 606772f37..04df84186 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -497,6 +497,61 @@ fun isInRange[T: Compare](self: T, startInclusive: T, endExclusive: T) Bool { self.isAtLeast(startInclusive).and(self.isLessThan(endExclusive)) } +struct List2D[T] { + width: Int, + height: Int, + items: List[T], +} +fun list2D[T](width: Int, height: Int, items: List[T]) List2D[T] { + needs(width.isNonNegative()) + needs(height.isNonNegative()) + needs(width.multiply(height).equals(items.length())) + + List2D[T](width, height, items) +} +fun list2DFilled[T](width: Int, height: Int, item: T) List2D[T] { + needs(width.isNonNegative()) + needs(height.isNonNegative()) + + list2D(width, height, listFilled(width.multiply(height), item)) +} +fun get[T](self: List2D[T], position: Pair[Int, Int]) Maybe[T] { + self.get(position.first, position.second) +} +fun get[T](self: List2D[T], x: Int, y: Int) Maybe[T] { + switch x.isInRange(0, self.width).and(y.isInRange(0, self.height)) { + false => none[T](), + true => some(self.items.get(self.rawIndex(x, y)).unwrap()), + } +} +fun replace[T](self: List2D[T], x: Int, y: Int, value: T) List2D[T] { + needs(x.isInRange(0, self.width)) + needs(y.isInRange(0, self.height)) + + let newItems = self.items.replace(self.rawIndex(x, y), value) + list2D(self.width, self.height, newItems) +} +fun indexesRowWise[T](self: List2D[T]) List[Pair[Int, Int]] { + range(self.height).flatMap((y: Int) { + range(self.width).map((x: Int) { Pair(x, y) }) + }) +} +fun rows[T](self: List2D[T]) List[List[T]] { + range(self.height).map((y: Int) { self.row(y).unwrap() }) +} +fun row[T](self: List2D[T], y: Int) Maybe[List[T]] { + switch y.isInRange(0, self.height) { + false => none[List[T]](), + true => some(self.items.getRange(self.rawIndex(0, y), self.rawIndex(0, y.add(1)))), + } +} +fun rawIndex[T](self: List2D[T], x: Int, y: Int) Int { + needs(x.isInRange(0, self.width)) + needs(y.isInRange(0, self.height)) + + y.multiply(self.width).add(x) +} + # TODO: .firstIndexWhere(…), .firstWhere(…), .firstIndexOf(…), .lastIndexWhere(…), .lastWhere(…), .lastIndexOf(…) fun print[T: ToText](t: T) { builtinPrint(t.toText()) @@ -625,6 +680,15 @@ fun toPair[T](list: List[T]) Maybe[Pair[T, T]] { false => none[Pair[T, T]](), } } +# TODO: Use this impl when it compiles +# impl[T0: Equal, T1: Equal] Pair[T0, T1]: Equal { +# fun equals(self: Pair[T0, T1], other: Pair[T0, T1]) Bool { +# self.first.equals(other.first).and(self.second.equals(other.second)) +# } +# } +fun equals[T: Equal](self: Pair[T, T], other: Pair[T, T]) Bool { + self.first.equals(other.first).and(self.second.equals(other.second)) +} struct MyStruct { name: Text, From 04b0a4629026db60021cc5c18318e7eaaf717268 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 19:48:59 +0100 Subject: [PATCH 23/27] Solve Advent of Code 2024 Day 6 Part 1 --- packages_v5/example.candy | 128 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index 04df84186..8838bb32f 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -972,6 +972,134 @@ fun day4Part2(input: Text) Int { .sum() } +fun day6Part1(input: Text) Int { + let lines = input.lines() + let width = lines.first().unwrap().length() + let height = lines.length() + + let state = lines.foldIndexed( + Day6Part1State( + list2DFilled(width, height, false), + list2DFilled(width, height, false), + Pair(0, 0), + Day6Part1Direction.up(), + ), + (state: Day6Part1State, y: Int, line: Text) { + line.characters() + .foldIndexed( + state, + (state: Day6Part1State, x: Int, cell: Text) { + Day6Part1State( + state.obstructions.replace(x, y, cell.equals("#")), + switch cell.equals("^") { + true => state.visited.replace(x, y, true), + false => state.visited, + }, + switch cell.equals("^") { + true => Pair(x, y), + false => state.position, + }, + state.direction, + ) + }, + ) + }, + ) + let state = state.moveToEnd() + # print(state) + state.visitedCellCount() +} +struct Day6Part1State { + obstructions: List2D[Bool], + visited: List2D[Bool], + position: Pair[Int, Int], + direction: Day6Part1Direction, +} +enum Day6Part1Direction { up, right, down, left } +fun rotateRight(self: Day6Part1Direction) Day6Part1Direction { + switch self { + up => Day6Part1Direction.right(), + right => Day6Part1Direction.down(), + down => Day6Part1Direction.left(), + left => Day6Part1Direction.up(), + } +} +fun moveToEnd(self: Day6Part1State) Day6Part1State { + switch self.move() { + none => self, + some(newState) => moveToEnd(newState), + } +} +fun move(self: Day6Part1State) Maybe[Day6Part1State] { + switch self.nextPosition() { + none => none[Day6Part1State](), + some(nextPosition) => switch self.obstructions.get(nextPosition).unwrap() { + true => some(Day6Part1State( + self.obstructions, + self.visited, + self.position, + self.direction.rotateRight(), + )), + false => some(Day6Part1State( + self.obstructions, + self.visited.replace(nextPosition.first, nextPosition.second, true), + nextPosition, + self.direction, + )), + }, + } +} +fun nextPosition(self: Day6Part1State) Maybe[Pair[Int, Int]] { + switch self.direction { + up => switch self.position.second.equals(0) { + true => none[Pair[Int, Int]](), + false => some(Pair(self.position.first, self.position.second.subtract(1))), + }, + right => switch self.position.first.equals(self.obstructions.width.subtract(1)) { + true => none[Pair[Int, Int]](), + false => some(Pair(self.position.first.add(1), self.position.second)), + }, + down => switch self.position.second.equals(self.obstructions.height.subtract(1)) { + true => none[Pair[Int, Int]](), + false => some(Pair(self.position.first, self.position.second.add(1))), + }, + left => switch self.position.first.equals(0) { + true => none[Pair[Int, Int]](), + false => some(Pair(self.position.first.subtract(1), self.position.second)), + }, + } +} +fun visitedCellCount(self: Day6Part1State) Int { + self.visited.items.filter((visited: Bool) { visited }).length() +} +impl Day6Part1State: ToText { + fun toText(self: Day6Part1State) Text { + self.obstructions.height.range() + .map((y: Int) { + self.obstructions.width.range() + .map((x: Int) { + switch self.position.equals(Pair(x, y)) { + true => switch self.direction { + up => "^", + right => ">", + down => "v", + left => "<", + }, + false => switch self.visited.get(x, y).unwrap() { + true => "X", + false => switch self.obstructions.get(x, y).unwrap() { + true => "#", + false => ".", + }, + }, + } + }) + .join("") + }) + .join("\n") + } +} + fun main() Int { identity(1) identity("foo") From 66beb20c977bf844ae2a5010093dfe6f01b2b686 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 19:49:15 +0100 Subject: [PATCH 24/27] Fix typo --- vscode_extension_v4/declarative/language-configuration.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vscode_extension_v4/declarative/language-configuration.json b/vscode_extension_v4/declarative/language-configuration.json index c3047f27a..be8471ffe 100644 --- a/vscode_extension_v4/declarative/language-configuration.json +++ b/vscode_extension_v4/declarative/language-configuration.json @@ -19,7 +19,7 @@ }, "onEnterRules": [ { - // Assignment start with optional comment but no actual content. + // Assignment starts with optional comment but no actual content. "beforeText": "^[^#]*=\\s*(?:#.*)?$", "action": { "indent": "indent" From 12c6c32dc8be5aa734167a0c936cfa430b0e6099 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 19:50:12 +0100 Subject: [PATCH 25/27] Print state before returning --- packages_v5/example.candy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index 8838bb32f..7c808e1ce 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -1006,7 +1006,7 @@ fun day6Part1(input: Text) Int { }, ) let state = state.moveToEnd() - # print(state) + print(state) state.visitedCellCount() } struct Day6Part1State { From 6bc83d0ec843ead4d88b370c91908108059b45ae Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 12 Dec 2024 19:50:56 +0100 Subject: [PATCH 26/27] Highlight self --- vscode_extension_v4/candy.tmGrammar.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/vscode_extension_v4/candy.tmGrammar.json b/vscode_extension_v4/candy.tmGrammar.json index 54fcfde2b..af6f0d042 100644 --- a/vscode_extension_v4/candy.tmGrammar.json +++ b/vscode_extension_v4/candy.tmGrammar.json @@ -41,7 +41,7 @@ }, { "name": "keyword.control", - "match": "([a-zA-Z_0-9]*)(?=(?:\\(.*\\))?\\s*=>)", + "match": "\\b([a-zA-Z_0-9]+)(?=(?:\\(.*\\))?\\s*=>)", "captures": { "1": { "name": "emphasis" @@ -69,6 +69,10 @@ } ] }, + { + "name": "variable.language", + "match": "\\bself\\b" + }, { "name": "constant.numeric", "match": "\\b[0-9]+\\b" From 96672afd1281338b90d7b3085d7426051304d04b Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Sat, 21 Dec 2024 09:54:27 +0100 Subject: [PATCH 27/27] =?UTF-8?q?Remove=20text.characterAt(=E2=80=A6)=20in?= =?UTF-8?q?=20favor=20of=20.get(=E2=80=A6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages_v5/example.candy | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages_v5/example.candy b/packages_v5/example.candy index 4827f1e2f..b7f5fabb0 100644 --- a/packages_v5/example.candy +++ b/packages_v5/example.candy @@ -263,12 +263,6 @@ fun get(self: Text, index: Int) Maybe[Text] { fun getRange(self: Text, startInclusive: Int, endExclusive: Int) Text { self.builtinTextGetRange(startInclusive, endExclusive) } -fun characterAt(self: Text, index: Int) Maybe[Text] { - switch index.isInRange(0, self.length()) { - false => none[Text](), - true => some(self.getRange(index, index.add(1))), - } -} fun characters(self: Text) List[Text] { listGenerate(self.length(), (i: Int) { self.get(i).unwrap() }) } @@ -305,7 +299,7 @@ fun splitIf(self: Text, predicate: (Text) Bool) List[Text] { self.splitIfHelper(predicate, 0, 0 listOf[Text]()) } fun splitIfHelper(self: Text, predicate: (Text) Bool, currentStartOffset: Int, offset: Int, result: List[Text]) List[Text] { - switch self.characterAt(offset) { + switch self.get(offset) { none => result.append(self.getRange(currentStartOffset, offset)), some(char) => switch predicate(char) { true => {