165 lines
4.7 KiB
Nim
165 lines
4.7 KiB
Nim
import std/sugar
|
|
import std/collections/sequtils
|
|
import std/strformat
|
|
import std/strutils
|
|
import results
|
|
import fusion/matching
|
|
import ./org_types
|
|
import ../utils/fp
|
|
import ../parser/parser_internals
|
|
import ../parser/parser_types
|
|
|
|
let parseBetweenDelimiter* = proc(start: (Parser -> ParserResult), stop: (Parser -> ParserResult)): (Parser -> ParserResult) {.closure.} =
|
|
ignore(start) +
|
|
anyUntil(stop + whitespace) +
|
|
ignore(start)
|
|
|
|
let parseBetweenIdenticalDelimiter* = proc(delimiterParser: (Parser -> ParserResult)): (Parser -> ParserResult) {.closure.} =
|
|
parseBetweenDelimiter(delimiterParser, delimiterParser)
|
|
|
|
let boldParser* = parseBetweenIdenticalDelimiter(ch('*'))
|
|
let italicParser* = parseBetweenIdenticalDelimiter(ch('/'))
|
|
let underlineParser* = parseBetweenIdenticalDelimiter(ch('_'))
|
|
let verbatimParser* = parseBetweenIdenticalDelimiter(ch('='))
|
|
let codeParser* = parseBetweenIdenticalDelimiter(ch('~'))
|
|
let strikeThroughParser* = parseBetweenIdenticalDelimiter(ch('+'))
|
|
|
|
type OrgBuilderT* = OrgElement
|
|
type OrgBuilder* = Builder[OrgBuilderT]
|
|
type OrgBuilderResult* = BuilderResult[OrgBuilderT]
|
|
|
|
proc makeRawToken*(content: string): OrgBuilderT =
|
|
OrgBuilderT(
|
|
kind: orgRawText,
|
|
content: content,
|
|
)
|
|
|
|
proc makeBoldToken*(content: string): OrgBuilderT =
|
|
OrgBuilderT(
|
|
kind: orgBoldText,
|
|
content: content,
|
|
)
|
|
|
|
proc makeItalicToken*(content: string): OrgBuilderT =
|
|
OrgBuilderT(
|
|
kind: orgItalicText,
|
|
content: content,
|
|
)
|
|
|
|
proc makeUnderlineToken*(content: string): OrgBuilderT =
|
|
OrgBuilderT(
|
|
kind: orgUnderlineText,
|
|
content: content,
|
|
)
|
|
|
|
proc makeVerbatimToken*(content: string): OrgBuilderT =
|
|
OrgBuilderT(
|
|
kind: orgVerbatimText,
|
|
content: content,
|
|
)
|
|
|
|
proc makeCodeToken*(content: string): OrgBuilderT =
|
|
OrgBuilderT(
|
|
kind: orgCodeText,
|
|
content: content,
|
|
)
|
|
|
|
proc makeStrikeThroughToken*(content: string): OrgBuilderT =
|
|
OrgBuilderT(
|
|
kind: orgStrikeThroughText,
|
|
content: content,
|
|
)
|
|
|
|
proc makeOrgToken*(orgTokenFn: string -> OrgBuilderT): (seq[ParserToken], seq[OrgBuilderT]) -> seq[OrgBuilderT] =
|
|
return proc(parserTokens: seq[ParserToken], builderTokens: seq[OrgBuilderT]): seq[OrgBuilderT] =
|
|
return builderTokens & parserTokens.foldl(a & b.tokenStringValue(), "").orgTokenFn()
|
|
|
|
proc parseText[T](
|
|
builder: Builder[T],
|
|
builderFns: seq[tuple[
|
|
parserFn: Parser -> ParserResult,
|
|
tokenFoldFn: (seq[ParserToken], seq[T]) -> seq[T],
|
|
]],
|
|
otherWiseFn: (seq[ParserToken]) -> seq[T],
|
|
stopFn = newline,
|
|
): BuilderResult[T] =
|
|
let (parser, tree) = builder
|
|
|
|
var parserAcc: ParserResult = ParserResult.ok(parser)
|
|
var builderAcc: Builder[T] = builder
|
|
|
|
while parserAcc.isOk() and parserAcc.flatMap(stopFn).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 it's tokens
|
|
# Otherwise leave the raw tokens
|
|
var found = false
|
|
for builderFn in builderFns:
|
|
let (parserFn, tokenFoldFn) = builderFn
|
|
let parseResult = emptyParser.flatMap(parserFn)
|
|
|
|
if parseResult.isOk():
|
|
let okParser = parseResult.unsafeGet()
|
|
|
|
let textTokens = parserAcc
|
|
.foldTokens(
|
|
onError = _ => newSeq[T](),
|
|
onSuccess = otherWiseFn,
|
|
)
|
|
|
|
found = true
|
|
parserAcc = parseResult.map(emptyTokens)
|
|
builderAcc = builder.initBuilder(
|
|
okParser,
|
|
tokenFoldFn(
|
|
okParser.tokens,
|
|
builderAcc[1] & textTokens,
|
|
),
|
|
)
|
|
break
|
|
|
|
if not found:
|
|
parserAcc = parserAcc.flatMap(anyCh)
|
|
|
|
let textTokens = parserAcc
|
|
.foldTokens(
|
|
onError = _ => newSeq[T](),
|
|
onSuccess = otherWiseFn,
|
|
)
|
|
|
|
BuilderResult[T].ok(builder.initBuilder(
|
|
builderAcc[0],
|
|
builderAcc[1] & textTokens,
|
|
))
|
|
|
|
proc makeRawTokenOrEmpty(xs: seq[ParserToken]): seq[OrgBuilderT] =
|
|
let str = xs.foldl(a & b.tokenStringValue(), "")
|
|
if str.len == 0: @[]
|
|
else: @[makeRawToken(str)]
|
|
|
|
when isMainModule:
|
|
|
|
let foo = OrgBuilderResult.ok(OrgBuilder((
|
|
parser: initParser("Regular *bold* /italic/ _underline_ =verbatim= ~code~ +strikethrough+"),
|
|
tree: newSeq[OrgBuilderT](),
|
|
)))
|
|
.flatMap((builder: OrgBuilder) => parseText(
|
|
builder = builder,
|
|
builderFns = @[
|
|
(boldParser, makeOrgToken(makeBoldToken)),
|
|
(italicParser, makeOrgToken(makeItalicToken)),
|
|
(underlineParser, makeOrgToken(makeUnderlineToken)),
|
|
(verbatimParser, makeOrgToken(makeVerbatimToken)),
|
|
(codeParser, makeOrgToken(makeCodeToken)),
|
|
(strikeThroughParser, makeOrgToken(makeStrikeThroughToken)),
|
|
],
|
|
otherWiseFn = makeRawTokenOrEmpty,
|
|
))
|
|
# .foldBuilder(
|
|
# err => "",
|
|
# xs => $xs
|
|
# )
|
|
|
|
echo foo
|