Pretty print errors
This commit is contained in:
56
src/test.nim
56
src/test.nim
@@ -11,11 +11,6 @@ import print
|
|||||||
{.experimental: "caseStmtMacros".}
|
{.experimental: "caseStmtMacros".}
|
||||||
|
|
||||||
type
|
type
|
||||||
ParseError = tuple
|
|
||||||
unexpected: string
|
|
||||||
expected: seq[string]
|
|
||||||
state: ParserState
|
|
||||||
message: string
|
|
||||||
|
|
||||||
ParserState* = ref object
|
ParserState* = ref object
|
||||||
stream: string
|
stream: string
|
||||||
@@ -27,13 +22,25 @@ type
|
|||||||
Parser* = ref object
|
Parser* = ref object
|
||||||
state: ParserState
|
state: ParserState
|
||||||
tokens: seq[Token]
|
tokens: seq[Token]
|
||||||
ParserResult* = Result[Parser, (Parser, string)]
|
|
||||||
|
ParseErrorKind = enum
|
||||||
|
charMismatchErr
|
||||||
|
endOfStringErr
|
||||||
|
ParserError = ref object
|
||||||
|
kind: ParseErrorKind
|
||||||
|
unexpected: char
|
||||||
|
expected: char
|
||||||
|
index: int
|
||||||
|
parser: Parser
|
||||||
|
ParserResult* = Result[Parser, ParserError]
|
||||||
|
|
||||||
# Builder[T]* = ref object
|
# Builder[T]* = ref object
|
||||||
# parser: ParserResult,
|
# parser: ParserResult,
|
||||||
# tree: seq[T],
|
# tree: seq[T],
|
||||||
# BuilderResult* = Result[Builder, (Builder, string)]
|
# BuilderResult* = Result[Builder, (Builder, string)]
|
||||||
|
|
||||||
|
# func prettyPrintError*(err: ParserErrror,)
|
||||||
|
|
||||||
func getOrElse*[T, E](self: Result[T, E], otherwise: T): T =
|
func getOrElse*[T, E](self: Result[T, E], otherwise: T): T =
|
||||||
if self.isOk():
|
if self.isOk():
|
||||||
self.v
|
self.v
|
||||||
@@ -63,6 +70,18 @@ proc `$`*(x: Parser): string =
|
|||||||
tokens: {indentKey($x.tokens, 2)},
|
tokens: {indentKey($x.tokens, 2)},
|
||||||
)"""
|
)"""
|
||||||
|
|
||||||
|
proc `$`*(x: ParserError): string =
|
||||||
|
case x:
|
||||||
|
of charMismatchErr(expected: @expected, parser: @parser, index: @index, unexpected: @unexpected):
|
||||||
|
# TODO: Only works for single line right now
|
||||||
|
let original = parser.state.stream
|
||||||
|
let errSpace = " ".repeat(index - 1)
|
||||||
|
|
||||||
|
&"""Parsing Error (Character Mismatch Error):
|
||||||
|
{original}
|
||||||
|
{errSpace}^ Wanted '{unexpected}' but got '{expected}'"""
|
||||||
|
else: "ParseError"
|
||||||
|
|
||||||
proc initParser(str: string): ParserResult =
|
proc initParser(str: string): ParserResult =
|
||||||
Parser(
|
Parser(
|
||||||
state: ParserState(
|
state: ParserState(
|
||||||
@@ -79,10 +98,14 @@ func ch(c1: char): (Parser -> ParserResult) {.inline.} =
|
|||||||
let newIndex = state.position + 1
|
let newIndex = state.position + 1
|
||||||
|
|
||||||
if newIndex > (state.stream.len - 1):
|
if newIndex > (state.stream.len - 1):
|
||||||
return err((parser, &"Expected {c1} at {newIndex}, got End of string"))
|
return err(ParserError(
|
||||||
|
kind: endOfStringErr,
|
||||||
|
expected: c1,
|
||||||
|
index: newIndex,
|
||||||
|
parser: parser,
|
||||||
|
))
|
||||||
else:
|
else:
|
||||||
let c2 = state.stream[newIndex]
|
let c2 = state.stream[newIndex]
|
||||||
|
|
||||||
if c1 == c2:
|
if c1 == c2:
|
||||||
return Parser(
|
return Parser(
|
||||||
state: ParserState(
|
state: ParserState(
|
||||||
@@ -93,7 +116,13 @@ func ch(c1: char): (Parser -> ParserResult) {.inline.} =
|
|||||||
tokens: parser.tokens & Token(value: c2)
|
tokens: parser.tokens & Token(value: c2)
|
||||||
).ok()
|
).ok()
|
||||||
else:
|
else:
|
||||||
return err((parser, &"Expected {c2} at {newIndex}, got {c1}"))
|
return err(ParserError(
|
||||||
|
kind: charMismatchErr,
|
||||||
|
unexpected: c2,
|
||||||
|
expected: c1,
|
||||||
|
index: newIndex,
|
||||||
|
parser: parser,
|
||||||
|
))
|
||||||
|
|
||||||
func ignore(parserFn: Parser -> ParserResult): (Parser -> ParserResult) {.inline.} =
|
func ignore(parserFn: Parser -> ParserResult): (Parser -> ParserResult) {.inline.} =
|
||||||
## Parse characters but throw success tokens away
|
## Parse characters but throw success tokens away
|
||||||
@@ -126,14 +155,14 @@ func str(s: string): (Parser -> ParserResult) {.inline.} =
|
|||||||
|
|
||||||
proc foldTokens[T](
|
proc foldTokens[T](
|
||||||
parserResult: ParserResult,
|
parserResult: ParserResult,
|
||||||
onError: T,
|
onError: ParserError -> T,
|
||||||
onSuccess: seq[Token] -> T,
|
onSuccess: seq[Token] -> T,
|
||||||
): T =
|
): T =
|
||||||
if parserResult.isOk():
|
if parserResult.isOk():
|
||||||
onSuccess(parserResult.unsafeGet().tokens)
|
onSuccess(parserResult.unsafeGet().tokens)
|
||||||
else:
|
else:
|
||||||
onError
|
let err = parserResult.error()
|
||||||
# onError(parserResult.error)
|
onError(err)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
let fooParser = initParser("FOO_BAR")
|
let fooParser = initParser("FOO_BAR")
|
||||||
@@ -141,11 +170,12 @@ when isMainModule:
|
|||||||
str("FOO"),
|
str("FOO"),
|
||||||
ignore(ch('_')),
|
ignore(ch('_')),
|
||||||
ch('B'),
|
ch('B'),
|
||||||
|
ch('B'),
|
||||||
ch('A'),
|
ch('A'),
|
||||||
ch('R'),
|
ch('R'),
|
||||||
])
|
])
|
||||||
.foldTokens(
|
.foldTokens(
|
||||||
"Error",
|
err => $err,
|
||||||
xs => xs.foldl(a & b.value, "")
|
xs => xs.foldl(a & b.value, "")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user