Build text parser
This commit is contained in:
17
src/org/org_builder.nim
Normal file
17
src/org/org_builder.nim
Normal 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](),
|
||||||
|
)))
|
||||||
@@ -1,12 +1,16 @@
|
|||||||
import std/sugar
|
import std/sugar
|
||||||
|
import std/collections/sequtils
|
||||||
import results
|
import results
|
||||||
import fusion/matching
|
import fusion/matching
|
||||||
|
import ./org_types
|
||||||
import ../utils/fp
|
import ../utils/fp
|
||||||
import ../parser/parser_internals
|
import ../parser/parser_internals
|
||||||
import ../parser/parser_types
|
import ../parser/parser_types
|
||||||
|
|
||||||
let parseBetweenDelimiter* = proc(delimiterParser: (Parser -> ParserResult)): (Parser -> ParserResult) {.closure.} =
|
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 boldParser* = parseBetweenDelimiter(ch('*'))
|
||||||
let italicParser* = parseBetweenDelimiter(ch('/'))
|
let italicParser* = parseBetweenDelimiter(ch('/'))
|
||||||
@@ -15,5 +19,71 @@ let verbatimParser* = parseBetweenDelimiter(ch('='))
|
|||||||
let codeParser* = parseBetweenDelimiter(ch('~'))
|
let codeParser* = parseBetweenDelimiter(ch('~'))
|
||||||
let strikeThroughParser* = 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
13
src/org/org_types.nim
Normal 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
|
||||||
@@ -78,6 +78,8 @@ func ch*(expectedChar: char): (Parser -> ParserResult) {.inline.} =
|
|||||||
parser: parser,
|
parser: parser,
|
||||||
))
|
))
|
||||||
|
|
||||||
|
let anyCh* = ch(AllChars)
|
||||||
|
|
||||||
func str*(s: string): (Parser -> ParserResult) {.inline.} =
|
func str*(s: string): (Parser -> ParserResult) {.inline.} =
|
||||||
return func(parser: Parser): ParserResult =
|
return func(parser: Parser): ParserResult =
|
||||||
var p = parser.ok()
|
var p = parser.ok()
|
||||||
@@ -115,8 +117,8 @@ func manyUntil*(acceptFn: Parser -> ParserResult, stopFn: Parser -> ParserResult
|
|||||||
res = res.flatMap(acceptFn)
|
res = res.flatMap(acceptFn)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
func anyUntil*(stopFn: Parser -> ParserResult): (Parser -> ParserResult) {.inline.} =
|
proc anyUntil*(stopFn: Parser -> ParserResult): (Parser -> ParserResult) {.inline.} =
|
||||||
manyUntil(ch(AllChars), stopFn)
|
manyUntil(anyCh, stopFn)
|
||||||
|
|
||||||
func choice*(parsers: seq[Parser -> ParserResult]): (Parser -> ParserResult) {.inline.} =
|
func choice*(parsers: seq[Parser -> ParserResult]): (Parser -> ParserResult) {.inline.} =
|
||||||
return proc(parser: Parser): ParserResult =
|
return proc(parser: Parser): ParserResult =
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ proc applyParsersSeq*[T](
|
|||||||
]]): BuilderResult[T] =
|
]]): BuilderResult[T] =
|
||||||
xs.foldl(a.flatMap((x: Builder[T]) => x.applyParsers(b[0], b[1])), builder)
|
xs.foldl(a.flatMap((x: Builder[T]) => x.applyParsers(b[0], b[1])), builder)
|
||||||
|
|
||||||
|
|
||||||
proc foldBuilder*[T, T2](
|
proc foldBuilder*[T, T2](
|
||||||
builderResult: BuilderResult[T],
|
builderResult: BuilderResult[T],
|
||||||
onError: string -> T2,
|
onError: string -> T2,
|
||||||
|
|||||||
@@ -19,3 +19,13 @@ proc initStringBuilder*(str: string): StringBuilderResult =
|
|||||||
parser: initParser(str),
|
parser: initParser(str),
|
||||||
tree: newSeq[StringBuilderT](),
|
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())
|
||||||
|
|||||||
0
tests/parser/parser_internals.nim
Normal file
0
tests/parser/parser_internals.nim
Normal file
8
tests/parser/test_parser_internals.nim
Normal file
8
tests/parser/test_parser_internals.nim
Normal 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())
|
||||||
Reference in New Issue
Block a user