Document and test parser type internals

This commit is contained in:
Florian Schroedl
2022-08-26 20:05:54 +02:00
parent a64d9d7d9a
commit 4679cda441
2 changed files with 67 additions and 31 deletions

View File

@@ -189,7 +189,7 @@ proc anyUntil*(stopFn: parserFnT): parserFnT {.inline.} =
manyUntil(anyCh, stopFn) manyUntil(anyCh, stopFn)
proc choice*(parserFns: seq[parserFnT]): parserFnT {.inline} = proc choice*(parserFns: seq[parserFnT]): parserFnT {.inline} =
## Creates parser function that checks any of the `parserFns`. ## creates parser function that checks any of the `parserFns`.
## Needs one match for a `ParserResult.ok`. ## Needs one match for a `ParserResult.ok`.
return proc(parser: Parser): ParserResult {.closure.} = return proc(parser: Parser): ParserResult {.closure.} =
var errors: seq[ParserResult] = newSeq[ParserResult]() var errors: seq[ParserResult] = newSeq[ParserResult]()

View File

@@ -1,5 +1,4 @@
import std/[ import std/[
options,
strutils, strutils,
strformat, strformat,
collections/sequtils, collections/sequtils,
@@ -47,8 +46,12 @@ type
# -- Initalizers # -- Initalizers
func initParserToken*(x: char): ParserToken = ParserToken(kind: parserTokenChar, charValue: x) func initParserToken*(x: char): ParserToken =
func initParserToken*(x: string): ParserToken = ParserToken(kind: parserTokenString, stringValue: x) ## Initialize `ParserToken` as `parserTokenChar` with value `x`.
ParserToken(kind: parserTokenChar, charValue: x)
func initParserToken*(x: string): ParserToken =
## Initialize `ParserToken` as `parserTokenString` with value `x`.
ParserToken(kind: parserTokenString, stringValue: x)
func initParser*( func initParser*(
stream: string, stream: string,
@@ -56,6 +59,7 @@ func initParser*(
position = -1, position = -1,
lastPosition = 0, lastPosition = 0,
): Parser = ): Parser =
## Initialize `Parser` with `stream`.
Parser( Parser(
state: ParserState( state: ParserState(
stream: stream, stream: stream,
@@ -71,6 +75,7 @@ func initParserResult*(
position = -1, position = -1,
lastPosition = 0, lastPosition = 0,
): ParserResult = ): ParserResult =
## Initialize `ParserResult.ok` with `stream`.
ParserResult.ok(initParser( ParserResult.ok(initParser(
stream, stream,
tokens, tokens,
@@ -78,10 +83,19 @@ func initParserResult*(
lastPosition, lastPosition,
)) ))
# -- Equalizers
func `==`*(a: ParserToken, b: ParserToken): bool =
## Compare two `ParserToken` objects.
case ((a.kind, b.kind)):
of (parserTokenChar, parserTokenChar): a.charValue == b.charValue
of (parserTokenString, parserTokenString): a.stringValue == b.stringValue
else: false
# -- Getters # -- Getters
func toString*(x: ParserToken): string = func toString*(x: ParserToken): string =
## Get the Token value `x` as a string. ## Get the `ParserToken` value `x` as a string.
case x.kind: case x.kind:
of parserTokenChar: of parserTokenChar:
$x.charValue $x.charValue
@@ -89,11 +103,13 @@ func toString*(x: ParserToken): string =
x.stringValue x.stringValue
func toString*(tokens: seq[ParserToken]): string = func toString*(tokens: seq[ParserToken]): string =
## Get the seq `ParserToken` value `x` as a string.
tokens.foldl(a & b.toString(), "") tokens.foldl(a & b.toString(), "")
# -- Modifiers # -- Modifiers
func flattenTokens*(parser: Parser): Parser = func flattenTokens*(parser: Parser): Parser =
## Flatten tokens in `parser` as a string `ParserToken`.
let token = initParserToken(parser.tokens.foldl(a & b.toString(), "")) let token = initParserToken(parser.tokens.foldl(a & b.toString(), ""))
Parser( Parser(
state: parser.state, state: parser.state,
@@ -101,6 +117,7 @@ func flattenTokens*(parser: Parser): Parser =
) )
func emptyTokens*(parser: Parser): Parser = func emptyTokens*(parser: Parser): Parser =
## Empty `parser` tokens.
Parser( Parser(
state: parser.state, state: parser.state,
tokens: newSeq[ParserToken](), tokens: newSeq[ParserToken](),
@@ -108,14 +125,15 @@ func emptyTokens*(parser: Parser): Parser =
func foldTokens*[T]( func foldTokens*[T](
parserResult: ParserResult, parserResult: ParserResult,
onError: ParserError -> T, onErrorFn: ParserError -> T,
onSuccess: seq[ParserToken] -> T, onSuccessFn: seq[ParserToken] -> T,
): T = ): T =
## Fold over `tokens` inside `ParserResult` with `onSuccessFn` or `onErrorFn`.
if parserResult.isOk(): if parserResult.isOk():
onSuccess(parserResult.unsafeGet().tokens) onSuccessFn(parserResult.unsafeGet().tokens)
else: else:
let err = parserResult.error() let err = parserResult.error()
onError(err) onErrorFn(err)
func tokensToString*(parserResult: ParserResult, fallback = ""): string = func tokensToString*(parserResult: ParserResult, fallback = ""): string =
parserResult.foldTokens( parserResult.foldTokens(
@@ -264,30 +282,48 @@ func `$`*(x: ParserError): string =
else: "ParseError" else: "ParseError"
when isMainModule: when isMainModule:
let test1 = """ABC block testEqualizers:
assert initParserToken("a") == initParserToken("a")
assert initParserToken('a') == initParserToken('a')
assert initParserToken("a") != initParserToken('a')
D block testGetters:
# toString
assert initParserToken("a").toString() == "a"
assert initParserToken('a').toString() == "a"
EFG""" block testModifiers:
# echo test1.highlightStreamPosition2(test1.find("B")) let testTokensSeq = @[initParserToken("a"), initParserToken('b'), initParserToken("c")]
# echo "==============" let testExpectedStr = "abc"
# echo test1.highlightStreamPosition2(test1.find("C")) let testTokensSeqParser = initParser(
# echo "==============" stream = testExpectedStr,
# echo test1.highlightStreamPosition2(test1.find("D")) tokens = testTokensSeq,
# echo "==============" position = testTokensSeq.len - 1,
# echo test1.highlightStreamPosition2(test1.find("\n")) )
# echo "==============" let testTokensSeqParserResult = ParserResult.ok(testTokensSeqParser)
# echo test1.highlightStreamPosition2(test1.find("\n", test1.find("\n") + 1)) let testTokensSeqParserResultErr = ParserResult.err(ParserError(
kind: charMismatchErr,
unexpected: "a",
expected: "b",
index: 0,
parser: testTokensSeqParser,
))
# flattenTokens
assert testTokensSeqParser.flattenTokens().tokens[0] == initParserToken("abc")
# echo "\n1\n".rfind('\n', 0, 2) # emptyTokens
assert testTokensSeqParser.emptyTokens().tokens.len == 0
# block highlightStreamPosition: # foldTokens
# let s = "abc" assert testTokensSeqParserResult.foldTokens(
# # Out of bounds err => "Failure",
# assert s.highlightStreamPosition(-1) == s xs => xs.toString(),
# assert s.highlightStreamPosition(s.len) == s ) == testExpectedStr
# # Regular highlighting assert testTokensSeqParserResultErr.foldTokens(
# assert s.highlightStreamPosition(0) == LEFT_HIGHLIGHT_CHAR & "a" & RIGHT_HIGHLIGHT_CHAR & "bc" err => "Failure",
# assert s.highlightStreamPosition(1) == "a" & LEFT_HIGHLIGHT_CHAR & "b" & RIGHT_HIGHLIGHT_CHAR & "c" xs => xs.toString(),
# assert s.highlightStreamPosition(2) == "ab" & LEFT_HIGHLIGHT_CHAR & "c" & RIGHT_HIGHLIGHT_CHAR ) == "Failure"
# tokensToString
assert testTokensSeqParserResult.tokensToString() == testExpectedStr