-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
83 additions
and
56 deletions.
There are no files selected for viewing
47 changes: 18 additions & 29 deletions
47
tribune-core/src/main/kotlin/com/sksamuel/tribune/core/collections/lists.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,34 @@ | ||
package com.sksamuel.tribune.core.collections | ||
|
||
import arrow.core.leftNel | ||
import arrow.core.sequence | ||
import arrow.core.flatten | ||
import arrow.core.left | ||
import arrow.core.right | ||
import arrow.core.toNonEmptyListOrNull | ||
import com.sksamuel.tribune.core.Parser | ||
import com.sksamuel.tribune.core.map | ||
|
||
/** | ||
* Lifts an existing [Parser] to support lists of the input types supported by | ||
* the underlying parser. | ||
* | ||
* In other words, given a parser from I to A, returns a parser from List<I> to List<A>. | ||
* In other words, given a parser from I to A, returns a parser from Collection<I> to List<A>. | ||
* | ||
* @return a parser that accepts lists | ||
* @return a [Parser] that produces sets | ||
*/ | ||
fun <I, A, E> Parser<I, A, E>.asList(): Parser<Collection<I>, List<A>, E> { | ||
fun <I, O, E> Parser<I, O, E>.asList(): Parser<Collection<I>, List<O>, E> { | ||
val self = this | ||
return Parser { input -> | ||
input.map { this@asList.parse(it) }.sequence() | ||
val results = input.map { self.parse(it) } | ||
val lefts: List<E> = results.mapNotNull { it.leftOrNull() }.flatten() | ||
val rights: List<O> = results.mapNotNull { it.getOrNull() } | ||
if (lefts.isNotEmpty()) | ||
lefts.toNonEmptyListOrNull()?.left() ?: error("unknown") | ||
else | ||
rights.right() | ||
} | ||
} | ||
|
||
fun <I, A, E> Parser.Companion.list(elementParser: Parser<I, A, E>): Parser<Collection<I>, List<A>, E> = | ||
fun <I, O, E> Parser.Companion.list(elementParser: Parser<I, O, E>): Parser<Collection<I>, List<O>, E> = | ||
elementParser.asList() | ||
|
||
/** | ||
* Lifts an existing [Parser] to support lists of the input types supported by | ||
* the underlying parser. This version of repeated supports upper and lower bounds | ||
* on the list size. | ||
* | ||
* In other words, given a parser, this will return a parser that handles lists of the inputs. | ||
* | ||
* @param min the minimum number of elements in the list | ||
* @param max the maximum number of elements in the list | ||
* | ||
* @return a parser that accepts lists | ||
*/ | ||
fun <I, A, E> Parser<I, A, E>.asList( | ||
min: Int = 0, | ||
max: Int = Int.MAX_VALUE, | ||
ifInvalidSize: (Int) -> E | ||
): Parser<List<I>, List<A>, E> { | ||
return Parser { input -> | ||
if ((min..max).contains(input.size)) input.map { this@asList.parse(it) }.sequence() | ||
else ifInvalidSize(input.size).leftNel() | ||
} | ||
} | ||
fun <I, O, E> Parser<I, List<O?>, E>.filterNulls(): Parser<I, List<O>, E> = map { it.filterNotNull() } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 16 additions & 8 deletions
24
tribune-core/src/test/kotlin/com/sksamuel/tribune/core/collections/ListTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,33 @@ | ||
package com.sksamuel.tribune.core.collections | ||
|
||
import arrow.core.leftNel | ||
import arrow.core.right | ||
import com.sksamuel.tribune.core.Foo | ||
import com.sksamuel.tribune.core.Parser | ||
import com.sksamuel.tribune.core.map | ||
import com.sksamuel.tribune.core.strings.minlen | ||
import io.kotest.core.spec.style.FunSpec | ||
import io.kotest.matchers.shouldBe | ||
|
||
class ListTest : FunSpec() { | ||
init { | ||
|
||
data class ParsedString(val str: String) | ||
|
||
test("asList") { | ||
val ps: Parser<List<String>, List<Foo>, Nothing> = Parser<String>().map { Foo(it) }.asList() | ||
ps.parse(listOf("a", "b")) shouldBe listOf(Foo("a"), Foo("b")).right() | ||
val p = Parser<String>().map { ParsedString(it) } | ||
val plist: Parser<Collection<String>, List<ParsedString>, Nothing> = p.asList() | ||
plist.parse(listOf("a", "b")).getOrNull() shouldBe listOf(ParsedString("a"), ParsedString("b")) | ||
} | ||
|
||
test("asList should accumulate errors") { | ||
val p = Parser<String>().minlen(2) { "whack $it" }.map { ParsedString(it) } | ||
val plist: Parser<Collection<String>, List<ParsedString>, String> = p.asList() | ||
plist.parse(listOf("a", "b")).leftOrNull() shouldBe listOf("whack a", "whack b") | ||
} | ||
|
||
test("asList with min length") { | ||
val ps = Parser<String>().map { Foo(it) }.asList(min = 2) { "Must have at least two elements" } | ||
ps.parse(listOf("a", "b")) shouldBe listOf(Foo("a"), Foo("b")).right() | ||
ps.parse(listOf("a")) shouldBe "Must have at least two elements".leftNel() | ||
test("filterNulls") { | ||
val p = Parser<String>().map { if (it == "a") null else ParsedString(it) } | ||
val plist: Parser<Collection<String>, List<ParsedString>, String> = p.asList().filterNulls() | ||
plist.parse(listOf("a", "b")).getOrNull() shouldBe listOf(ParsedString("b")) | ||
} | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
tribune-core/src/test/kotlin/com/sksamuel/tribune/core/collections/SetTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.sksamuel.tribune.core.collections | ||
|
||
import com.sksamuel.tribune.core.Parser | ||
import com.sksamuel.tribune.core.map | ||
import com.sksamuel.tribune.core.strings.minlen | ||
import io.kotest.core.spec.style.FunSpec | ||
import io.kotest.matchers.shouldBe | ||
|
||
class SetTest : FunSpec() { | ||
init { | ||
|
||
data class ParsedString(val str: String) | ||
|
||
test("asSet") { | ||
val p = Parser<String>().map { ParsedString(it) } | ||
val pset: Parser<Collection<String>, Set<ParsedString>, Nothing> = p.asSet() | ||
pset.parse(listOf("a", "b")).getOrNull() shouldBe setOf(ParsedString("a"), ParsedString("b")) | ||
} | ||
|
||
test("asSet should accumulate errors") { | ||
val p = Parser<String>().minlen(2) { "whack $it" }.map { ParsedString(it) } | ||
val pset: Parser<Collection<String>, Set<ParsedString>, String> = p.asSet() | ||
pset.parse(listOf("a", "b")).leftOrNull() shouldBe listOf("whack a", "whack b") | ||
} | ||
|
||
test("filterNulls") { | ||
val p = Parser<String>().map { if (it == "a") null else ParsedString(it) } | ||
val pset: Parser<Collection<String>, Set<ParsedString>, String> = p.asSet().filterNulls() | ||
pset.parse(listOf("a", "b")).getOrNull() shouldBe setOf(ParsedString("b")) | ||
} | ||
} | ||
} |