Restore tryTokenize function
This commit is contained in:
113
src_v2/parser/builder_api.nim
Normal file
113
src_v2/parser/builder_api.nim
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import std/[
|
||||||
|
collections/sequtils,
|
||||||
|
options,
|
||||||
|
strformat,
|
||||||
|
strutils,
|
||||||
|
sugar,
|
||||||
|
]
|
||||||
|
import fp/[
|
||||||
|
resultM
|
||||||
|
]
|
||||||
|
import ./builder_types
|
||||||
|
import ./parser_types
|
||||||
|
import ./parser_api
|
||||||
|
|
||||||
|
# -- Builder API
|
||||||
|
|
||||||
|
proc tryTokenize*[T](
|
||||||
|
builder: Builder[T],
|
||||||
|
builderFns: seq[tuple[
|
||||||
|
parserFn: parserFnT,
|
||||||
|
tokenizerFn: seq[ParserToken] -> seq[T],
|
||||||
|
]],
|
||||||
|
defaultTokenizerFn: seq[ParserToken] -> seq[T],
|
||||||
|
stopAtParserFn = newlineOrEol,
|
||||||
|
concatTokensFn = concat[T],
|
||||||
|
): BuilderResult[T] =
|
||||||
|
## Try to tokenize text in `builder` by checking `builderFns` seq for a sucessful `parserFn`.
|
||||||
|
## When a `parserFn.ok` is found, tokenize the text in the `Parser` using the current tokenizerFn
|
||||||
|
## and merge the result into `builder.tree` using the `concatTokensFn`.
|
||||||
|
## When no parser matches, tokenize with the `defaultTokenizerFn` until `stopAtParserFn is matched.
|
||||||
|
|
||||||
|
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 a parser and convert its tokens with the `tokenizerFn`
|
||||||
|
var found = false
|
||||||
|
for fn in builderFns:
|
||||||
|
let (parserFn, tokenizerFn) = fn
|
||||||
|
|
||||||
|
let parseResult = emptyParser.flatMap(parserFn)
|
||||||
|
if parseResult.isOk():
|
||||||
|
found = true
|
||||||
|
|
||||||
|
# Convert all previous unmatched tokens via the `defaultTokenizerFn`
|
||||||
|
let defaultBuilderTokens = parserAcc
|
||||||
|
.foldTokens(
|
||||||
|
onErrorFn = _ => newSeq[T](),
|
||||||
|
onSuccessFn = defaultTokenizerFn,
|
||||||
|
)
|
||||||
|
|
||||||
|
let okParser = parseResult.unsafeGet()
|
||||||
|
parserAcc = parseResult.map(emptyTokens)
|
||||||
|
builderAcc = initBuilder(
|
||||||
|
okParser,
|
||||||
|
concatTokensFn(
|
||||||
|
builderAcc.tree,
|
||||||
|
defaultBuilderTokens,
|
||||||
|
tokenizerFn(okParser.tokens),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
parserAcc = parserAcc.flatMap(anyCh)
|
||||||
|
|
||||||
|
let defaultBuilderTokens = parserAcc
|
||||||
|
.foldTokens(
|
||||||
|
onErrorFn = _ => newSeq[T](),
|
||||||
|
onSuccessFn = defaultTokenizerFn,
|
||||||
|
)
|
||||||
|
|
||||||
|
BuilderResult[T].ok(initBuilder(
|
||||||
|
builderAcc.parser,
|
||||||
|
concatTokensFn(
|
||||||
|
builderAcc.tree,
|
||||||
|
defaultBuilderTokens,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
type TestStringBuilderT* = string
|
||||||
|
type TestStringBuilder* = Builder[TestStringBuilderT]
|
||||||
|
type TestStringBuilderResult* = BuilderResult[TestStringBuilderT]
|
||||||
|
|
||||||
|
block testApi:
|
||||||
|
let testParensParser = anyBetween(str(" ("), str(") "))
|
||||||
|
|
||||||
|
func testDefaultTokenizer(tokens: seq[ParserToken]): seq[string] {.closure.} =
|
||||||
|
@[tokens.toString()]
|
||||||
|
func testParensTokenizer(tokens: seq[ParserToken]): seq[string] {.closure.} =
|
||||||
|
@[&"Parens({tokens.toString()})"]
|
||||||
|
|
||||||
|
let testBuilder = initBuilder(
|
||||||
|
initParser("""sentence (with parens) and more
|
||||||
|
And ignore this part"""),
|
||||||
|
newSeq[TestStringBuilderT]()
|
||||||
|
)
|
||||||
|
.tryTokenize(
|
||||||
|
builderFns = @[(
|
||||||
|
testParensParser,
|
||||||
|
testParensTokenizer
|
||||||
|
)],
|
||||||
|
defaultTokenizerFn = testDefaultTokenizer,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert testBuilder.isOk() == true
|
||||||
|
assert testBuilder.unsafeGet().tree == @["sentence", "Parens(with parens)", "and more"]
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
import parser_types
|
import parser_types
|
||||||
import parser_api
|
import parser_api
|
||||||
import builder_types
|
import builder_types
|
||||||
|
import builder_api
|
||||||
|
|
||||||
export parser_types
|
export parser_types
|
||||||
export parser_api
|
export parser_api
|
||||||
export builder_types
|
export builder_types
|
||||||
|
export builder_api
|
||||||
|
|||||||
Reference in New Issue
Block a user