From c19dd096c38cdaf7b9d3f642d1e31d44ac471531 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Thu, 20 Jan 2022 17:00:00 +0100 Subject: [PATCH] Document and move tryParseBuild --- src/org/org_text_delimiter.nim | 5 ++- src/org/org_types.nim | 27 +++++++++++-- src/parser/builder_api.nim | 72 ++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 src/parser/builder_api.nim diff --git a/src/org/org_text_delimiter.nim b/src/org/org_text_delimiter.nim index 43d07b2..63c7309 100644 --- a/src/org/org_text_delimiter.nim +++ b/src/org/org_text_delimiter.nim @@ -8,6 +8,7 @@ import ./org_types import ../utils/fp import ../parser/parser_internals import ../parser/parser_types +import ../parser/builder_api let parseBetweenDelimiter* = proc(start: (Parser -> ParserResult), stop: (Parser -> ParserResult)): (Parser -> ParserResult) {.closure.} = ignore(start) + @@ -74,7 +75,7 @@ proc makeOrgToken*(orgTokenFn: string -> OrgBuilderT): (seq[ParserToken], seq[Or return proc(parserTokens: seq[ParserToken], builderTokens: seq[OrgBuilderT]): seq[OrgBuilderT] = return builderTokens & parserTokens.foldl(a & b.tokenStringValue(), "").orgTokenFn() -proc parseText[T]( +proc tryParseBuild[T]( builder: Builder[T], builderFns: seq[tuple[ parserFn: Parser -> ParserResult, @@ -149,7 +150,7 @@ when isMainModule: parser: initParser("Regular *bold* /italic/ _underline_ =verbatim= ~code~ +strikethrough+"), tree: newSeq[OrgBuilderT](), ))) - .flatMap((builder: OrgBuilder) => parseText( + .flatMap((builder: OrgBuilder) => tryParseBuild( builder = builder, builderFns = @[ (boldParser, makeOrgToken(makeBoldToken)), diff --git a/src/org/org_types.nim b/src/org/org_types.nim index 19e8715..a0c5ac6 100644 --- a/src/org/org_types.nim +++ b/src/org/org_types.nim @@ -4,7 +4,14 @@ type orgElementKind* = enum orgRawText, orgText, + + # Formating orgBoldText, + orgItalicText, + orgUnderlineText, + orgVerbatimText, + orgCodeText, + orgStrikeThroughText, OrgElement* = ref object children*: seq[OrgElement] content*: string @@ -12,13 +19,27 @@ type case kind*: orgElementKind of orgRawText: discard of orgText: discard + + # Formating of orgBoldText: discard + of orgItalicText: discard + of orgUnderlineText: discard + of orgVerbatimText: discard + of orgCodeText: discard + of orgStrikeThroughText: discard proc `$`*(x: orgElementKind): string = case x: - of orgRawText: "Text (Raw)" - of orgText: "Text" - of orgBoldText: "Text (Bold)" + of orgRawText: "Text (Raw)" + of orgText: "Text" + + # Formating + of orgBoldText: "Text (Bold)" + of orgItalicText: "Text (Italic)" + of orgUnderlineText: "Text (Underline)" + of orgVerbatimText: "Text (Verbatim)" + of orgCodeText: "Text (Code)" + of orgStrikeThroughText: "Text (StrikeThrough)" proc `$`*(x: OrgElement): string = &"""OrgElement( diff --git a/src/parser/builder_api.nim b/src/parser/builder_api.nim new file mode 100644 index 0000000..69c0582 --- /dev/null +++ b/src/parser/builder_api.nim @@ -0,0 +1,72 @@ +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, + concatFn: (seq[ParserToken], seq[T]) -> seq[T], + ]], + defaultBuilderFn: (seq[ParserToken]) -> seq[T], + stopAtParserFn = newline, +): BuilderResult[T] = + ## Parse remaining text in `builder` by going checking in 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 builderFn in builderFns: + let (parserFn, concatFn) = builderFn + + 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( + okParser.tokens, + builderAcc[1] & defaultBuilderTokens, + ), + ) + break + + if not found: + parserAcc = parserAcc.flatMap(anyCh) + + let defaultBuilderTokens = parserAcc + .foldTokens( + onError = _ => newSeq[T](), + onSuccess = defaultBuilderFn, + ) + + BuilderResult[T].ok(builder.initBuilder( + builderAcc[0], + builderAcc[1] & defaultBuilderTokens, + ))