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, ), ))