Files
org-parser/src/parser/builder_api.nim
Florian Schroedl bec4953a97 Commentary
2022-05-05 20:17:22 +02:00

78 lines
2.3 KiB
Nim

import std/sugar
import std/collections/sequtils
import std/strformat
import std/strutils
import results
import fusion/matching
import ./parser_internals
import ./parser_types
proc tryParseBuild*[T](
builder: Builder[T],
builderFns: seq[tuple[
parserFn: Parser -> ParserResult,
builderFn: seq[ParserToken] -> seq[T],
]],
defaultBuilderFn: seq[ParserToken] -> seq[T],
stopAtParserFn = newline,
concatFn = concat[T],
): BuilderResult[T] =
## Parse text in `builder` by checking the `builderFns` list for a sucessful `parserFn`.
## The `ok` `parserFn` result will be merged into the `Builder[T].tree` by using the `concatFn`.
## Otherwise continue taking any character until the `stopAtParserFn` condition is found.
## Any non-matching tokens will be converted using the `defaultBuilderFn`.
let (parser, tree) = builder
# Mutating accumulators
var parserAcc: ParserResult = ParserResult.ok(parser)
var builderAcc: Builder[T] = builder
while parserAcc.isOk() and parserAcc.flatMap(stopAtParserFn).isErr():
# Empty the parser tokens as we want to seperate them for the next parser in the sequence
let emptyParser = parserAcc.map(emptyTokens)
# Find the first matching parser and convert its tokens
var found = false
for fn in builderFns:
let (parserFn, builderFn) = fn
let parseResult = emptyParser.flatMap(parserFn)
if parseResult.isOk():
let okParser = parseResult.unsafeGet()
# Convert all previous unmatched tokens via the `defaultBuilderTokens`
let defaultBuilderTokens = parserAcc
.foldTokens(
onError = _ => newSeq[T](),
onSuccess = defaultBuilderFn,
)
found = true
parserAcc = parseResult.map(emptyTokens)
builderAcc = builder.initBuilder(
okParser,
concatFn(
builderAcc.tree,
defaultBuilderTokens,
builderFn(okParser.tokens),
)
)
break
if not found:
parserAcc = parserAcc.flatMap(anyCh)
let defaultBuilderTokens = parserAcc
.foldTokens(
onError = _ => newSeq[T](),
onSuccess = defaultBuilderFn,
)
BuilderResult[T].ok(builder.initBuilder(
builderAcc.parser,
concatFn(
builderAcc.tree,
defaultBuilderTokens,
),
))