Build text parser

This commit is contained in:
Florian Schroedl
2022-01-20 17:00:00 +01:00
parent 647c5cdf00
commit 7c16ece752
8 changed files with 126 additions and 5 deletions

17
src/org/org_builder.nim Normal file
View File

@@ -0,0 +1,17 @@
import ./org_types
import ../parser/parser_types
type OrgBuilderT* = seq[OrgElement]
type StringBuilder* = Builder[OrgBuilderT]
type OrgBuilderResult* = BuilderResult[OrgBuilderT]
proc concat*(typeInfo: OrgBuilderT): (seq[ParserToken], seq[OrgBuilderT]) -> seq[OrgBuilderT] =
return proc(xs: seq[ParserToken], ys: seq[OrgBuilderT]): seq[OrgBuilderT] =
return ys & xs.foldl(a & b.tokenStringValue() & seperator, typeInfo)
proc initStringBuilder*(str: string): StringBuilderResult =
StringBuilderResult
.ok(StringBuilder((
parser: initParser(str),
tree: newSeq[OrgBuilderT](),
)))

View File

@@ -1,12 +1,16 @@
import std/sugar
import std/collections/sequtils
import results
import fusion/matching
import ./org_types
import ../utils/fp
import ../parser/parser_internals
import ../parser/parser_types
let parseBetweenDelimiter* = proc(delimiterParser: (Parser -> ParserResult)): (Parser -> ParserResult) {.closure.} =
ignore(delimiterParser) + anyUntil(delimiterParser + whitespace) + ignore(delimiterParser)
ignore(delimiterParser) +
anyUntil(delimiterParser + whitespace) +
ignore(delimiterParser)
let boldParser* = parseBetweenDelimiter(ch('*'))
let italicParser* = parseBetweenDelimiter(ch('/'))
@@ -15,5 +19,71 @@ let verbatimParser* = parseBetweenDelimiter(ch('='))
let codeParser* = parseBetweenDelimiter(ch('~'))
let strikeThroughParser* = parseBetweenDelimiter(ch('+'))
echo initParser("""_foo bar *_
""").underlinedParser()
# until parser != delimiterparser
# takeChar and save to current parser
# else:
# saveTokens to new text block
# saveBlock delimiterparser
# let delimiterParser* = anyUntil(choice(@[
# boldParser,
# italicParser,
# underlinedParser,
# verbatimParser,
# codeParser,
# strikeThroughParser,
# ]), newline)
type OrgBuilderT* = OrgElement
type OrgBuilder* = Builder[OrgBuilderT]
type OrgBuilderResult* = BuilderResult[OrgBuilderT]
proc makeBoldTokens*(content: string): OrgBuilderT =
OrgBuilderT(
kind: orgBoldText,
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 textParser[T](
builder: Builder[T],
builderFns: seq[tuple[
parserFn: Parser -> ParserResult,
tokenFoldFn: (seq[ParserToken], seq[T]) -> seq[T],
]],
stopFn = newline,
): BuilderResult[T] =
let (parser, tree) = builder
var parserAcc: ParserResult = parser.ok()
var builderAcc: Builder = builder
while parser.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()
found = true
parserAcc = parseResult
builderAcc = builder.initBuilder(
okParser,
tokenFoldFn(okParser.tokens, builderAcc[1]),
)
break
if not found:
parserAcc = parserAcc.flatMap(anyCh)

13
src/org/org_types.nim Normal file
View File

@@ -0,0 +1,13 @@
type
orgElementKind* = enum
orgRawText,
orgText,
orgBoldText,
OrgElement* = ref object
children*: seq[OrgElement]
content*: string
case kind*: orgElementKind
of orgRawText: discard
of orgText: discard
of orgBoldText: discard

View File

@@ -78,6 +78,8 @@ func ch*(expectedChar: char): (Parser -> ParserResult) {.inline.} =
parser: parser,
))
let anyCh* = ch(AllChars)
func str*(s: string): (Parser -> ParserResult) {.inline.} =
return func(parser: Parser): ParserResult =
var p = parser.ok()
@@ -115,8 +117,8 @@ func manyUntil*(acceptFn: Parser -> ParserResult, stopFn: Parser -> ParserResult
res = res.flatMap(acceptFn)
return res
func anyUntil*(stopFn: Parser -> ParserResult): (Parser -> ParserResult) {.inline.} =
manyUntil(ch(AllChars), stopFn)
proc anyUntil*(stopFn: Parser -> ParserResult): (Parser -> ParserResult) {.inline.} =
manyUntil(anyCh, stopFn)
func choice*(parsers: seq[Parser -> ParserResult]): (Parser -> ParserResult) {.inline.} =
return proc(parser: Parser): ParserResult =

View File

@@ -155,6 +155,7 @@ proc applyParsersSeq*[T](
]]): BuilderResult[T] =
xs.foldl(a.flatMap((x: Builder[T]) => x.applyParsers(b[0], b[1])), builder)
proc foldBuilder*[T, T2](
builderResult: BuilderResult[T],
onError: string -> T2,

View File

@@ -19,3 +19,13 @@ proc initStringBuilder*(str: string): StringBuilderResult =
parser: initParser(str),
tree: newSeq[StringBuilderT](),
)))
proc fold*[T, E, T2](
self: Result[T, E],
onError: E -> T2,
onSuccess: T -> T2,
): T2 =
if self.isOk():
onSuccess(self.unsafeGet())
else:
onError(self.error())

View File

View File

@@ -0,0 +1,8 @@
import unittest
import parser/parser_internals
import parser/parser_types
suite "parser/parser_internals":
test "whitespace":
echo whitespace(initParser(" ")) == ParserResult.ok(Parser())