78 lines
2.3 KiB
Nim
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,
|
|
),
|
|
))
|