Skip to content

Commit

Permalink
Merge pull request #925 from candy-lang/ranges
Browse files Browse the repository at this point in the history
  • Loading branch information
JonasWanke authored Apr 4, 2024
2 parents 178dcc9 + 448a5f7 commit f96314e
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 0 deletions.
104 changes: 104 additions & 0 deletions packages/Range/_.candy
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
bound := use ".bound"
[bool, equals, ifElse, int, iterator] = use "Core"
[print] = use "Builtins"

fromBounds start end :=
needs (bound.is start)
needs (bound.is end)
isEmpty = (start, end) %
(Unbounded, _) | (_, Unbounded) -> False
(Inclusive a, Inclusive b) -> a | int.isGreaterThan b
(Inclusive a, Exclusive b) -> a | int.isGreaterThanOrEqualTo b
(Exclusive a, Inclusive b) -> a | int.isGreaterThanOrEqualTo b
(Exclusive a, Exclusive b) -> b | int.subtract a | int.isLessThanOrEqualTo 1
ifElse isEmpty { Range Empty } { Range [start, end] }

is range := range %
Range range ->
range %
Empty -> True
[start, end] -> bound.is start | bool.and (bound.is end) | bool.lazyAnd {
fromBounds start end | equals (Range range)
}
_ -> False
_ -> False

from a :=
needs (int.is a)
fromBounds (Inclusive a) Unbounded
to a b :=
needs (int.is a)
needs (int.is b)
fromBounds (Inclusive a) (Exclusive b)
until a b :=
needs (int.is a)
needs (int.is b)
fromBounds (Inclusive a) (Inclusive b)

contains range value :=
needs (is range)
Range range = range
range %
Empty -> False
[start, end] ->
bool.and
start %
Unbounded -> True
Inclusive a -> value | int.isGreaterThanOrEqualTo a
Exclusive a -> value | int.isGreaterThan a
end %
Unbounded -> True
Inclusive a -> value | int.isLessThanOrEqualTo a
Exclusive a -> value | int.isLessThan a

intersection a b :=
needs (is a)
needs (is b)
Range a = a
Range b = b
(a, b) %
(Empty, _) | (_, Empty) -> Range Empty
(a, b) ->
fromBounds
a.start %
Unbounded -> b.start
Inclusive a -> b.start %
Unbounded -> Inclusive a
Inclusive b -> Inclusive (int.max a b)
Exclusive b -> if (a | int.isGreaterThanOrEqualTo b) { Inclusive a } { Exclusive b }
Exclusive a -> b.start %
Unbounded -> Exclusive a
Inclusive b -> if (b | int.isGreaterThan a) { Inclusive a } { Exclusive b }
Exclusive b -> Exclusive (int.max a b),
a.end %
Unbounded -> b.end
Inclusive a -> b.end %
Unbounded -> Inclusive a
Inclusive b -> Inclusive (int.min a b)
Exclusive b -> if (a | int.isGreaterThanOrEqualTo b) { Inclusive a } { Exclusive b }
Exclusive a -> b.end %
Unbounded -> Exclusive a
Inclusive b -> if (b | int.isGreaterThan a) { Inclusive a } { Exclusive b }
Exclusive b -> Exclusive (int.min a b),

iterate range :=
needs (is range)
Range range = range
range.start %
Unbounded -> needs False "The range needs to have a bounded start."
_ -> Nothing
first = range.start %
Inclusive a -> a
Exclusive a -> a | int.add 1
range.end %
Unbounded ->
iterator.generateWithState first { next -> More [Item: next, State: next | int.add 1] }
bound ->
end = bound %
Inclusive a -> a | int.add 1
Exclusive a -> a
iterator.generateWithState first { next ->
ifElse (next | int.isGreaterThanOrEqualTo end) { Empty } {
More [Item: next, State: next | int.add 1]
}
}
Empty file added packages/Range/_package.candy
Empty file.
6 changes: 6 additions & 0 deletions packages/Range/bound.candy
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[int] = use "Core"

is bound := bound %
Unbounded -> True
Inclusive a | Exclusive a -> int.is a
_ -> False
53 changes: 53 additions & 0 deletions packages/Range/example.candy
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
[checkEquals, iterator] = use "Core"
range = use "Range"
bound = range.bound

checkEquals (bound.is Unbounded) True
checkEquals (bound.is (Inclusive 2)) True
checkEquals (bound.is (Exclusive 3)) True
checkEquals (bound.is Foo) False

checkEquals (range.fromBounds Unbounded Unbounded) (Range [Start: Unbounded, End: Unbounded])
checkEquals (range.fromBounds (Inclusive 2) Unbounded) (Range [Start: Inclusive 2, End: Unbounded])
checkEquals (range.fromBounds (Exclusive 3) Unbounded) (Range [Start: Exclusive 3, End: Unbounded])
checkEquals (range.fromBounds Unbounded (Inclusive 1)) (Range [Start: Unbounded, End: Inclusive 1])
checkEquals (range.fromBounds Unbounded (Exclusive 1)) (Range [Start: Unbounded, End: Exclusive 1])
checkEquals
range.fromBounds (Inclusive 1) (Inclusive 1)
Range [Start: Inclusive 1, End: Inclusive 1]
checkEquals (range.fromBounds (Inclusive 1) (Inclusive 0)) (Range Empty)
checkEquals (range.fromBounds (Inclusive 1) (Exclusive 1)) (Range Empty)
checkEquals
range.fromBounds (Inclusive 1) (Exclusive 2)
Range [Start: Inclusive 1, End: Exclusive 2]
checkEquals (range.fromBounds (Exclusive 1) (Inclusive 1)) (Range Empty)
checkEquals (range.fromBounds (Exclusive 1) (Exclusive 1)) (Range Empty)
checkEquals
range.fromBounds (Exclusive 1) (Exclusive 3)
Range [Start: Exclusive 1, End: Exclusive 3]
checkEquals
range.fromBounds (Exclusive 1) (Inclusive 4)
Range [Start: Exclusive 1, End: Inclusive 4]

checkEquals (range.is (Range Empty)) True
checkEquals (range.is (Range [Start: Inclusive 1, End: Inclusive 2])) True

checkEquals (range.contains (Range [Start: Inclusive 1, End: Inclusive 2]) 2) True
checkEquals (range.contains (Range [Start: Inclusive 1, End: Exclusive 2]) 2) False
checkEquals (range.contains (Range [Start: Inclusive 1, End: Inclusive 2]) 3) False
checkEquals (range.contains (Range [Start: Inclusive 1, End: Inclusive 2]) 1) True
checkEquals (range.contains (Range [Start: Inclusive 2, End: Inclusive 2]) 1) False
checkEquals (range.contains (Range [Start: Exclusive 1, End: Inclusive 2]) 1) False

checkEquals
range.intersection
Range [Start: Inclusive 1, End: Inclusive 5]
Range [Start: Inclusive 3, End: Inclusive 8]
Range [Start: Inclusive 3, End: Inclusive 5]

main := { environment ->
a = 1 | range.until 10
environment.stdout a

1 | range.until 10 | range.iterate | iterator.forEach { a -> environment.stdout a }
}

0 comments on commit f96314e

Please sign in to comment.