An attempt to implement parser combinators in Kotlin, inspired by Parsec.
space- any whitespaceupper- any uppercase letterlower- any lowercase letterletter- any letterdigit- any digit 0..9alphaNum- any letter or digituint/int- an integer (unsigned or signed)ulong/long- a long (unsigned or signed)ufloat/float- a floating-point number (decimal or e-notation, unsigned or signed)unumber/number- a floating-point or integer numbereof- end of inputskipSpaces- skip arbitrary number of spaces
oneOf(list)- any character in the provided listnoneOf(list)- character should not be in the provided listchar(ch)- character equal to the provided onestring(str)- string equal to the provided onesatisfy(pred)- character for which provided predicate returns true
List/Pair/Triple(parsers).chain()- inverts the structure and returns a parser ofList/Pair/Triplewith chained resultscount(number, parser)- repeatsparseras many times as statedchoice(list)- tries list of parsers in order, returning the first successchain(parsers)- executes a list of parsers and returns a list of their resultsbetween(open, close, parser)- runsopen, thenparserandclose, returning the result ofparsermany(parser)- repeatsparser0+ times until error occursmany1(parser)- repeatsparser1+ times until error occursskipMany(parser)- repeatsparser0+ times but does not return any resultskipMany1(parser)- repeatsparser1+ times but does not return any resultparser sepBy sep- repeatsparser0+ times, separated bysepparser sepBy1 sep- repeatsparser1+ times, separated bysepparser endBy sep- likesepBy, but input must also end withsepparser endBy1 sep- likesepBy1, but input must also end withsepjust(value)- always returnsvaluefail(message)- always fails withmessageoption(value, parser)- returnsvalueifparserfailsoptional(parser)- tries to runparserbut does not return any result (does not fail)lookahead(parser)- executesparserwithout consuming any inputpA and pB- returns both results of parserspAandpBas a pairpA andF pB- (and-flat) returns both results of parserspAandpBflattened to a list withoutUnitspA andL pB- (and-left) returns result ofpA(left parser) only ifpBsucceedspA andR pB- (and-right) returns result ofpB(right parser) only ifpAsucceedspA or pB- tries to runpAand if fails, returns result ofpBparser.map { ... }- apply function to the result ofparserparser.flatMap { ... }- use the successful result ofparserand return a new parserparser.recoverWith { ... }- use the error ofparserand return a new parser
parser % "Error message"- returns a parser with"Error message"for error
In order to parse a string with some parser, parse(parser, input) function should be used.
It returns a Result<T> which can be either a Success (contains the parsed value and rest of the input)
or an Error (contains error message).
val input = "ABCD1234"
val result = parse(letter, input)
when (result) {
is Error -> println(result.message)
is Success -> println(result.value)
}
// should print 'A'Another possibility is to use run(parser, input) that directly returns the value (if succeeded) or null (if error).
val input = "42A"
val result = run(int, input)
// should be 42val tail = oneOf('+', '-', '*', '/') andF number
val tails = many1(tail).map { it.flatten() }
val expr = number andF tails
val result = run(expr, "12+6.5")
// should be [12, '+', 6.5]In addition to the combinators and/andL/andR/or, as well as the modifier map,
there is a way to build parsers in declarative style.
buildParser creates a context which takes care of passing the rest of the input
from one parser to the other upon calling .ev(), all while tracking the return value (Success or Error) of each one.
In case of the latter, execution is stopped and the error is returned.
This parser would take two numbers and sum them together.
val sum = buildParser {
val a = int.ev()
char('+').ev()
val b = uint.ev()
a + b
}
val result = parse(sum, "3+4")
// should be Success(value=7, rest=)There is a possibility to create a parser for an expression that also evaluates it. It is only necessary to provide an operators table where all operators are listed and their functions defined. Operators table is a list of Operator lists ordered in descending precedence.
In this example we support incrementation, multiplication and addition/subtraction,
with ++ having the highest precedence.
val table: OperatorTable<Int> = listOf(
listOf( postfix("++") { it + 1 } ),
listOf( binary("*", Assoc.Left) { x, y -> x * y } ),
listOf(
binary("+", Assoc.Left) { x, y -> x + y },
binary("-", Assoc.Left) { x, y -> x - y }
)
)
val expr = buildExpressionParser(table, uint)
run(expr, "1+2*3") // should be 7
run(expr, "2++") // should be 3