import std/options import std/strutils import std/strformat import std/collections/sequtils import std/sugar import std/collections/tables import results import fusion/matching import fp/maybe import ./parser_types import ../utils/str {.experimental: "caseStmtMacros".} # -- Parsing Functions func ch*(expectedChars: set[char]): (Parser -> ParserResult) {.inline.} = return func(parser: Parser): ParserResult = let state = parser.state let newIndex = state.position + 1 if newIndex > (state.stream.len - 1): return err(ParserError( kind: endOfStringErr, expected: &"{expectedChars.prettyExpectedSet()}", index: newIndex, parser: parser, )) else: let foundChar = state.stream[newIndex] if foundChar in expectedChars: return Parser( state: ParserState( stream: state.stream, position: newIndex, lastPosition: parser.state.position, ), tokens: parser.tokens & initParserToken(foundChar) ).ok() else: return err(ParserError( kind: charMismatchErr, unexpected: &"{foundChar}", expected: &"{expectedChars.prettyExpectedSet()}", index: newIndex, parser: parser, )) func ch*(expectedChar: char): (Parser -> ParserResult) {.inline.} = return func(parser: Parser): ParserResult = let state = parser.state let newIndex = state.position + 1 if newIndex > (state.stream.len - 1): return err(ParserError( kind: endOfStringErr, expected: &"{expectedChar}", index: newIndex, parser: parser, )) else: let foundChar = state.stream[newIndex] if expectedChar == foundChar: return Parser( state: ParserState( stream: state.stream, position: newIndex, lastPosition: parser.state.position, ), tokens: parser.tokens & initParserToken(foundChar) ).ok() else: return err(ParserError( kind: charMismatchErr, unexpected: &"{foundChar}", expected: &"{expectedChar}", index: newIndex, parser: parser, )) let anyCh* = ch(AllChars) func str*(s: string): (Parser -> ParserResult) {.inline.} = return func(parser: Parser): ParserResult = var p = parser.ok() for c in s.items: p = p.flatMap(ch(c)) return p # -- Parsing API func optional*(parserFn: Parser -> ParserResult): (Parser -> ParserResult) {.inline.} = ## Parse characters and ignore failure return proc(parser: Parser): ParserResult = let newParser = parserFn(parser) if newParser.isOk(): newParser else: parser.ok() func ignore*(parserFn: Parser -> ParserResult): (Parser -> ParserResult) {.inline.} = ## Parse characters but throw success tokens away return proc(parser: Parser): ParserResult = return parserFn(parser) .map((x: Parser) => Parser( state: x.state, tokens: parser.tokens, )) func manyUntilPerformant*(acceptFn: Parser -> ParserResult, stopFn: Parser -> ParserResult): (Parser -> ParserResult) {.inline.} = ## Parse characters but throw success tokens away return proc(parser: Parser): ParserResult = let startPosition = parser.state.position var res: ParserResult = parser.ok() while res.isOk() and res.flatMap(stopFn).isErr(): res = res.flatMap(acceptFn) return res.map((p: Parser) => Parser( state: ParserState( stream: p.state.stream, position: p.state.position, lastPosition: p.state.lastPosition, ), tokens: @[ ParserToken( kind: parserTokenString, stringValue: p.state.stream[(startPosition - 1)..p.state.position], ) ] )) proc anyUntilPerformant*(stopFn: Parser -> ParserResult): (Parser -> ParserResult) {.inline.} = manyUntilPerformant(anyCh, stopFn) func manyUntil*(acceptFn: Parser -> ParserResult, stopFn: Parser -> ParserResult): (Parser -> ParserResult) {.inline.} = ## Parse characters but throw success tokens away return proc(parser: Parser): ParserResult = var res: ParserResult = parser.ok() while res.isOk() and res.flatMap(stopFn).isErr(): res = res.flatMap(acceptFn) return res 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 = var errors: seq[ParserResult] = newSeq[ParserResult]() var found = Nothing[ParserResult]() for fn in parsers: let fnResult: ParserResult = fn(parser) if fnResult.isOk(): found = fnResult.just break else: errors = errors & fnResult return found .fold( proc(): ParserResult = let prettyErrors = errors.map((x: ParserResult) => x.error().expected) err(ParserError( kind: choiceMismatchErr, index: parser.state.position + 1, expected: &"Choice ({prettyErrors})", unexpected: errors[0].error().unexpected, parser: parser, )), proc(x: ParserResult): ParserResult = x, ) proc `+`*(parserFnA: Parser -> ParserResult, parserFnB: Parser -> ParserResult): (Parser -> ParserResult) {.inline.} = ## Parse characters and ignore failure return proc(parser: Parser): ParserResult = parserFnA(parser).flatMap(parserFnB) proc parseSeq*(parser: ParserResult, xs: seq[Parser -> ParserResult]): ParserResult = xs.foldl(a.flatMap(b), parser) # -- Parsing Aliases proc endOfStream*(parser: Parser): ParserResult = let index = parser.state.position + 1 if index == parser.state.stream.len: ok(parser) else: err(ParserError( kind: endOfStringErr, expected: &"EndOfString", index: index, parser: parser, )) let newlineParser = choice(@[ ch(NewLines), endOfStream, ]) proc newline*(parser: Parser): ParserResult = newlineParser(parser) .mapErr((x: ParserError) => x.setErrorExpectedField("Newline")) let whitespaceParser = choice(@[ ch(Whitespace), newlineParser, ]) proc whitespace*(parser: Parser): ParserResult = whitespaceParser(parser) .mapErr((x: ParserError) => x.setErrorExpectedField("Whitespace")) # -- Parsing Helpers let parseBetweenDelimiter* = proc(start: (Parser -> ParserResult), stop: (Parser -> ParserResult)): (Parser -> ParserResult) {.closure.} = ignore(start) + anyUntil(stop + whitespace) + ignore(start) let parseBetweenPair* = proc(delimiterParser: (Parser -> ParserResult)): (Parser -> ParserResult) {.closure.} = parseBetweenDelimiter(delimiterParser, delimiterParser)