-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from alllex/and-tuples
And tuples
- Loading branch information
Showing
15 changed files
with
922 additions
and
272 deletions.
There are no files selected for viewing
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
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
41 changes: 41 additions & 0 deletions
41
src/commonMain/kotlin/me/alllex/parsus/parser/TupleParser.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,41 @@ | ||
package me.alllex.parsus.parser | ||
|
||
|
||
internal class TupleParser<out T>( | ||
internal val parsers: List<Parser<Any?>>, | ||
internal val transform: (List<Any?>) -> T, | ||
) : ParserImpl<T>( | ||
null, | ||
firstTokens = parsers.first().firstTokens | ||
) { | ||
|
||
val arity: Int get() = parsers.count { it !is SkipParser<*> } | ||
|
||
override suspend fun ParsingScope.parse(): T { | ||
val items = buildList { | ||
for (parser in parsers) { | ||
if (parser is SkipParser<*>) { | ||
parser() | ||
} else { | ||
val item = parser() | ||
add(item) | ||
} | ||
} | ||
} | ||
|
||
return transform(items) | ||
} | ||
|
||
override fun toString(): String { | ||
return "TupleParser(parsers=$parsers)" | ||
} | ||
|
||
} | ||
|
||
internal fun Parser<*>.tryUnwrapTupleParsers(): List<Parser<Any?>> = | ||
if (this is TupleParser<*>) parsers else listOf(this) | ||
|
||
internal fun <T> retuple(parser1: Parser<Any?>, parser2: Parser<Any?>, transform: (List<Any?>) -> T): TupleParser<T> { | ||
val newParsers = parser1.tryUnwrapTupleParsers() + parser2 | ||
return TupleParser(newParsers, transform) | ||
} |
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
99 changes: 99 additions & 0 deletions
99
src/commonMain/kotlin/me/alllex/parsus/parser/skipCombinators.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,99 @@ | ||
package me.alllex.parsus.parser | ||
|
||
|
||
/** | ||
* Creates a new parser that applies the given [parser], but ignores the result and returns `Unit`. | ||
* | ||
* ```kotlin | ||
* object : Grammar<String> { | ||
* val title by regexToken("Mrs?\\.?\\s+") | ||
* val surname by regexToken("\\w+") | ||
* override val root by ignored(title) and surname | ||
* } | ||
* ``` | ||
*/ | ||
fun ignored(parser: Parser<*>): Parser<Unit> = SkipParser.of(parser) | ||
|
||
/** | ||
* Creates a new parser that applies the given [parser], but ignores the result and returns `Unit`. | ||
* | ||
* ```kotlin | ||
* object : Grammar<String> { | ||
* val title by regexToken("Mrs?\\.?\\s+") | ||
* val surname by regexToken("\\w+") | ||
* override val root by ignored(title) and surname | ||
* } | ||
* ``` | ||
*/ | ||
operator fun Parser<*>.unaryMinus(): Parser<Unit> = ignored(this) | ||
|
||
/** | ||
* Creates a new parser that tries to apply the given [parser] | ||
* and fallbacks to returning null in case of failure. | ||
* | ||
* ```kotlin | ||
* object : Grammar<Tuple2<String?, String>> { | ||
* val title by regexToken("Mrs?\\.?") | ||
* val ws by regexToken("\\s+") | ||
* val surname by regexToken("\\w+") | ||
* override val root by maybe(title) * -maybe(ws) * surname | ||
*/ | ||
fun <T : Any> maybe(parser: Parser<T>): Parser<T?> = optional(parser) | ||
|
||
/** | ||
* Creates a new parser that tries to apply the given [parser] | ||
* and fallbacks to returning null in case of failure. | ||
* | ||
* ```kotlin | ||
* object : Grammar<Tuple2<String?, String>> { | ||
* val title by regexToken("Mrs?\\.?\\s+") | ||
* val surname by regexToken("\\w+") | ||
* override val root by optional(title) * surname | ||
*/ | ||
fun <T : Any> optional(parser: Parser<T>): Parser<T?> = parser { | ||
poll(parser) | ||
} | ||
|
||
/** | ||
* Runs the [parser] and returns its result or null in case of failure. | ||
*/ | ||
suspend fun <R : Any> ParsingScope.tryOrNull(parser: Parser<R>): R? = tryParse(parser).getOrElse { null } | ||
|
||
/** | ||
* Runs the [parser] and returns its result or null in case of failure. | ||
*/ | ||
suspend fun <R : Any> ParsingScope.poll(parser: Parser<R>): R? = tryOrNull(parser) | ||
|
||
/** | ||
* Executes given parser, ignoring the result. | ||
*/ | ||
suspend fun ParsingScope.skip(p: Parser<*>): IgnoredValue { | ||
p() // execute parser, but ignore the result | ||
return IgnoredValue | ||
} | ||
|
||
|
||
/** | ||
* A wrapping parser that is used as a marker to denote parsed value that is ignored, e.g. by [TupleParser]. | ||
*/ | ||
@Suppress("UNCHECKED_CAST") | ||
internal class SkipParser<out T> private constructor( | ||
private val parser: Parser<T> | ||
) : ParserImpl<Unit>(firstTokens = parser.firstTokens) { | ||
|
||
@Suppress("UNUSED_VARIABLE") | ||
override suspend fun ParsingScope.parse() { | ||
val ignored = parser() | ||
} | ||
|
||
override fun toString(): String { | ||
return "SkipParser(parser=$parser)" | ||
} | ||
|
||
companion object { | ||
fun <T> of(parser: Parser<T>): Parser<Unit> = when (parser) { | ||
is SkipParser<*> -> parser as SkipParser<Unit> | ||
else -> SkipParser(parser) | ||
} | ||
} | ||
} |
Oops, something went wrong.