import std/[ collections/sequtils, strformat, strutils, sugar, ] import fp/[ option, resultM, ] import fusion/matching {.experimental: "caseStmtMacros".} import ./org_types import ./org_builder_api import ../parser/parser import ../utils/fp # -- Parsers let headingStarsParser* = following(@[ ignore(optional(newline)), manyUntil(ch('*'), ch(' ')), ignore(space) ]) proc createTodoKeywordParser(xs: seq[string]): parserFnT = choice(xs.map((x: string) => str(x) + ignore(space))) let todoKeywords = @["TODO"] let doneKeywords = @["DONE"] let parseTodoKeyword = todoKeywords.createTodoKeywordParser() let parseDoneKeyword = doneKeywords.createTodoKeywordParser() # let parseContentText = @[ # anyUntil(choice(@[endOfStream, str("\n")])), # ] # let parseHeadlineNewline = @[ # choice(@[ # ch(NewLines), # endOfStream, # ]) # ] # let parseHeadingText = @[ # anyUntil(newline), # ] # -- Tokenizers let buildStars = func(tokens: seq[ParserToken], org: OrgBlock): OrgBlock {.closure.}= org.level = tokens.len org # let buildTodo = func(tokens: seq[ParserToken], org: OrgBlock): OrgBlock {.closure.}= # org.todo = tokens.tokensToString().some() # org # let buildHeadlineContent = func(tokens: seq[ParserToken], org: OrgBlock): OrgBlock {.closure.}= # let headlineString = tokens.tokensToString() # let tokens = tryBuildInline(headlineString) # .fold( # x => newSeq[OrgInlineBuilderT](), # (x: OrgInlineBuilder) => x.tree, # ) # org.headlineContent = tokens # org # let buildHeadlineChildren = func(tokens: seq[ParserToken], org: OrgBlock): OrgBlock {.closure.}= # # let headlineString = tokens.tokensToString() # org # let buildHeadlineNewline = func(tokens: seq[ParserToken], org: OrgBlock): OrgBlock {.closure.}= # org # proc tryBuildHeading(builder: OrgBuilderResult): OrgBuilderResult = # # echo builder # builder # .applyParsersSeqToSingle( # OrgBlock(kind: orgHeading), # @[ # (parseHeadingStars, buildStars, false), # (@[optional(parseTodoKeyword)], buildTodo, true), # (@[optional(parseDoneKeyword)], buildTodo, true), # (parseHeadingText, buildHeadlineContent, false), # # (parseContentText, buildHeadlineChildren, true), # (parseHeadlineNewline, buildHeadlineNewline, false), # ] # ) # let headingParser = choice(@[ # newline + ch('*'), # endOfStream, # ]) # let contentEndParser = newline # let buildNewline = func(tokens: seq[ParserToken], org: OrgBlock): OrgBlock {.closure.} = # OrgBlock( # kind: orgNewline # ) # proc concatOrgBlock(xs: seq[OrgBlock], ys: seq[OrgBlock]): seq[OrgBlock] = xs & ys # let parseBlockText = @[ # anyUntil(choice(@[endOfStream, str("\n")])), # ] # let contentParsersSeq: seq[tuple[ # parsers: seq[Parser -> ParserResult], # tokenFoldFn: (seq[ParserToken], OrgBlock) -> OrgBlock, # ignoreEmpty: bool, # # concatFn: (seq[OrgBlock], seq[OrgBlock]) -> seq[OrgBlock], # ]] = @[ # (parseBlockText, buildNewline, true), # (contentEndParser, buildNewline, true), # ] # proc parseHeadlineChildren(builder: OrgBuilder): OrgBuilderResult = # var headingBlock = builder.tree[0] # # echo "Builder " & $builder # # var builderAcc: OrgBuilderResult = OrgBuilderResult.ok(builder) # var builderAcc: OrgBuilderResult = OrgBuilderResult.ok(OrgBuilder( # ( # parser: builder.parser, # tree: @[], # ) # )) # echo "builderAcc before parse content ", builderAcc # # echo builderAcc.tryParser(headingParser) # while builderAcc.isOk() and builderAcc.tryParser(headingParser).isErr(): # builderAcc = builderAcc # .applyParsersSeqToSeq( # contentParsersSeq # ) # echo "builderAcc after parse in while loop ", builderAcc # # echo builderAcc # let content = builderAcc.fold( # (err) => 0, # newSeq[OrgBlock](), # (builder: OrgBuilder) => len builder.tree, # ) # # echo "builderAcc" & $builderAcc # echo "content" & $content # # headingBlock.content = content # let res = builderAcc.fold( # (err) => OrgBuilderResult.err((builder, "Could not parse content")), # (builder: OrgBuilder) => OrgBuilderResult.ok(( # parser: builder.parser.emptyTokens(), # tree: @[headingBlock], # )) # ) # res # proc makeOrg*(x: string): OrgBuilderResult = # var acc = initOrgBuilder(x) # while acc.isOk() and acc.tryParser(endOfStream).isErr(): # let unsafeAcc = acc.unsafeGet() # let item = acc # .tryBuildHeading() # .flatMap( # (x: OrgBuilder) => parseHeadlineChildren(x) # ) # .flatMap( # (x: OrgBuilder) => OrgBuilderResult.ok(OrgBuilder(( # parser: x.parser, # tree: unsafeAcc.tree & x.tree, # ))) # ) # acc = item # acc # proc foldOrg*(x: OrgBuilderResult): string = # x # .fold( # (err) => "Error" & $err, # (builder: OrgBuilder) => $builder.tree, # ) # # echo acc.unsafeGet().tree[^1] # when isMainModule: # let test1 = """* TODO Level 1""" # let test2 = """* TODO Level 1 # Foo # ** DONE Level 2 # Some more content # """ # let test3 = """* TODO Level 1 # Foo # """ # let acc = makeOrg(test3).foldOrg() # discard acc