From 488d0b173bdcce1a7de85f8eefdafe5bbd9fb276 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Thu, 20 Jan 2022 17:00:00 +0100 Subject: [PATCH] Seperate types & utils to files --- src/org/org_properties_block.nim | 1 + src/org_parser.nim | 6 +- src/parser/parser.nim | 144 +------------------------------ src/parser/parser_types.nim | 128 +++++++++++++++++++++++++++ src/parser/utils.nim | 2 +- src/utils/str.nim | 28 ++++++ 6 files changed, 163 insertions(+), 146 deletions(-) create mode 100644 src/parser/parser_types.nim diff --git a/src/org/org_properties_block.nim b/src/org/org_properties_block.nim index 1fe5dce..aa5cb27 100644 --- a/src/org/org_properties_block.nim +++ b/src/org/org_properties_block.nim @@ -2,6 +2,7 @@ import results import fusion/matching import ../utils/fp import ../parser/parser +import ../parser/parser_types {.experimental: "caseStmtMacros".} diff --git a/src/org_parser.nim b/src/org_parser.nim index 28adc16..ce527db 100644 --- a/src/org_parser.nim +++ b/src/org_parser.nim @@ -2,11 +2,13 @@ import std/sugar import std/strformat import std/collections/sequtils import std/strutils -import parser/parser -import parser/utils import results import utils/fp import fusion/matching + +import ./parser/parser +import ./parser/parser_types +import ./parser/utils import ./org/org_properties_block {.experimental: "caseStmtMacros".} diff --git a/src/parser/parser.nim b/src/parser/parser.nim index 76b0728..18862c1 100644 --- a/src/parser/parser.nim +++ b/src/parser/parser.nim @@ -7,153 +7,11 @@ import std/collections/tables import results import fusion/matching import fp/maybe +import ./parser_types import ../utils/str {.experimental: "caseStmtMacros".} -type - ParserState* = ref object - stream: string - position, lastPosition: int - - parserTokenCharValueT* = char - parserTokenStringValueT* = string - parserTokenKeyValuePairValueT* = tuple[k: string, v: string] - - ParserTokenKind* = enum - parserTokenChar - parserTokenString - # parserTokenKeyValuePair - ParserToken* = ref object - case kind*: ParserTokenKind - of parserTokenChar: - charValue*: parserTokenCharValueT - of parserTokenString: - stringValue*: parserTokenStringValueT - # of parserTokenKeyValuePair: - # keyValuePairValue*: parserTokenKeyValuePairValueT - - Parser* = ref object - state*: ParserState - tokens*: seq[ParserToken] - - ParseErrorKind = enum - choiceMismatchErr - charMismatchErr - endOfStringErr - ParserError = ref object - kind: ParseErrorKind - unexpected: string - expected: string - index: int - parser: Parser - ParserResult* = Result[Parser, ParserError] - - Builder*[T] = tuple[ - parser: Parser, - tree: seq[T] - ] - BuilderResult*[T] = Result[Builder[T], (Builder[T], string)] - -proc indentKey(x: string, count: int): string = - var y = x.indent(count) - y.delete(0..count - 1) - y - -proc tokenStringValue*(x: ParserToken): string = - case x.kind: - of parserTokenChar: - $x.charValue - of parserTokenString: - x.stringValue - -proc `$`*(x: ParserToken): string = - &"""ParserToken( - value: {tokenStringValue(x)}, -)""" - -proc `$`*(x: ParserState): string = - &"""ParserState( - stream: "{x.stream}", - position: {x.position}, - lastPosition: {x.lastPosition}, -)""" - -proc `$`*(x: Parser): string = - &"""Parser( - state: {indentKey($x.state, 2)}, - tokens: {indentKey($x.tokens, 2)}, -)""" - -proc prettyExpectedSet(x: set[char]): string = - case x: - of AllChars: - "AllChars {'\x00'..'\xFF'}" - of Digits: - "Digits {'0'..'9'}" - of HexDigits: - "HexDigits {'0'..'9', 'A'..'F', 'a'..'f'}" - of Letters: - "Letters {'A'..'Z', 'a'..'z'}" - of Newlines: - "Newlines {'\r', '\n'}" - of Whitespace: - "Whitespace {' ', '\t', '\v', '\r', '\n', '\f'}" - else: - $x - -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 - .deleteAfterNewline(parser.state.position) - let errSpace = " ".repeat(max(0, index)) - - - $index & $parser & - &"""Parsing Error (Character Mismatch Error): -{original} -{errSpace}^ Expected '{expected}' but got '{unexpected}'""" - of choiceMismatchErr(expected: @expected, parser: @parser, index: @index, unexpected: @unexpected): - let original = parser.state.stream - .deleteAfterNewline(parser.state.position) - let errSpace = " ".repeat(max(0, index)) - - $index & $parser & - &"""Parsing Error (Character Mismatch Error): -{original} -{errSpace}^ Expected '{expected}' but got '{unexpected}'""" - - of endOfStringErr(parser: @parser, index: @index): - let original = parser.state.stream - .deleteAfterNewline(parser.state.position) - let errSpace = " ".repeat(max(0, index)) - - $index & $parser & - &"""Parsing Error (EndOfString Expected): -{original} -{errSpace}^ Expected 'EndOfString' at {index} but got {original.len - 1}""" - - else: "ParseError" - -func initParserToken(x: char): ParserToken = ParserToken(kind: parserTokenChar, charValue: x) -func initParserToken(x: string): ParserToken = ParserToken(kind: parserTokenString, stringValue: x) -# func initParserToken(x: parserTokenKeyValuePairValueT): ParserToken = ParserToken(kind: parserTokenKeyValuePair, keyValuePairValue: x) - -proc initParser*(str: string): Parser = - Parser( - state: ParserState( - stream: str, - position: -1, - lastPosition: 0, - ), - tokens: newSeq[ParserToken](), - ) - -proc initParserResult*(str: string): ParserResult = - ParserResult.ok(initParser(str)) - func ch*(expectedChars: set[char]): (Parser -> ParserResult) {.inline.} = return func(parser: Parser): ParserResult = let state = parser.state diff --git a/src/parser/parser_types.nim b/src/parser/parser_types.nim new file mode 100644 index 0000000..63ac265 --- /dev/null +++ b/src/parser/parser_types.nim @@ -0,0 +1,128 @@ +import std/strutils +import std/strformat +import results +import fusion/matching +import ../utils/str + +{.experimental: "caseStmtMacros".} + +type + ParserState* = ref object + stream*: string + position*, lastPosition*: int + + parserTokenCharValueT* = char + parserTokenStringValueT* = string + ParserTokenKind* = enum + parserTokenChar + parserTokenString + ParserToken* = ref object + case kind*: ParserTokenKind + of parserTokenChar: + charValue*: parserTokenCharValueT + of parserTokenString: + stringValue*: parserTokenStringValueT + + Parser* = ref object + state*: ParserState + tokens*: seq[ParserToken] + ParseErrorKind* = enum + choiceMismatchErr + charMismatchErr + endOfStringErr + ParserError* = ref object + kind*: ParseErrorKind + unexpected*: string + expected*: string + index*: int + parser*: Parser + ParserResult* = Result[Parser, ParserError] + + Builder*[T] = tuple[ + parser: Parser, + tree: seq[T] + ] + BuilderResult*[T] = Result[Builder[T], (Builder[T], string)] + +# -- Initalizers + +func initParserToken*(x: char): ParserToken = ParserToken(kind: parserTokenChar, charValue: x) +func initParserToken*(x: string): ParserToken = ParserToken(kind: parserTokenString, stringValue: x) + +proc initParser*(str: string): Parser = + Parser( + state: ParserState( + stream: str, + position: -1, + lastPosition: 0, + ), + tokens: newSeq[ParserToken](), + ) + +proc initParserResult*(str: string): ParserResult = + ParserResult.ok(initParser(str)) + +# -- Getters + +proc tokenStringValue*(x: ParserToken): string = + ## Get the Token value `x` as a string. + case x.kind: + of parserTokenChar: + $x.charValue + of parserTokenString: + x.stringValue + +# -- Stringifiers + +proc `$`*(x: ParserToken): string = + &"""ParserToken( + value: {tokenStringValue(x)}, +)""" + +proc `$`*(x: ParserState): string = + &"""ParserState( + stream: "{x.stream}", + position: {x.position}, + lastPosition: {x.lastPosition}, +)""" + +proc `$`*(x: Parser): string = + &"""Parser( + state: {indentAfterNewline($x.state, 2)}, + tokens: {indentAfterNewline($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 + .deleteAfterNewline(parser.state.position) + let errSpace = " ".repeat(max(0, index)) + + + $index & $parser & + &"""Parsing Error (Character Mismatch Error): +{original} +{errSpace}^ Expected '{expected}' but got '{unexpected}'""" + of choiceMismatchErr(expected: @expected, parser: @parser, index: @index, unexpected: @unexpected): + let original = parser.state.stream + .deleteAfterNewline(parser.state.position) + let errSpace = " ".repeat(max(0, index)) + + $index & $parser & + &"""Parsing Error (Character Mismatch Error): +{original} +{errSpace}^ Expected '{expected}' but got '{unexpected}'""" + + of endOfStringErr(parser: @parser, index: @index): + let original = parser.state.stream + .deleteAfterNewline(parser.state.position) + let errSpace = " ".repeat(max(0, index)) + + $index & $parser & + &"""Parsing Error (EndOfString Expected): +{original} +{errSpace}^ Expected 'EndOfString' at {index} but got {original.len - 1}""" + + else: "ParseError" diff --git a/src/parser/utils.nim b/src/parser/utils.nim index fdca8e9..3964f62 100644 --- a/src/parser/utils.nim +++ b/src/parser/utils.nim @@ -1,7 +1,7 @@ import std/sugar import std/collections/sequtils -import ./parser import results +import ./parser_types type StringBuilderT* = string type StringBuilder* = Builder[StringBuilderT] diff --git a/src/utils/str.nim b/src/utils/str.nim index 1a9176a..794778e 100644 --- a/src/utils/str.nim +++ b/src/utils/str.nim @@ -1,5 +1,8 @@ import std/strutils import std/math +import fusion/matching + +{.experimental: "caseStmtMacros".} proc safeDelete*(str: string, slice: Slice[int]): string = ## Deletes the items `str[slice]`, ignoring elements out of range. @@ -25,3 +28,28 @@ func findAndDelete*(str: string, chars: set[char], start = 0, last = str.len - 1 func deleteAfterNewline*(str: string, start = 0): string = ## Delete string after next Newline from `start`. findAndDelete(str, Newlines, start) + +proc indentAfterNewline*(str: string, count: int): string = + ## Indent lines following the first line of `str` by `count`. + ## Useful for indenting nested keys of stringify functions. + var strDup = str.indent(count) + strDup.delete(0..count - 1) + strDup + +proc prettyExpectedSet*(x: set[char]): string = + ## Pretty print value for a set `x` of characters + case x: + of AllChars: + "AllChars {'\x00'..'\xFF'}" + of Digits: + "Digits {'0'..'9'}" + of HexDigits: + "HexDigits {'0'..'9', 'A'..'F', 'a'..'f'}" + of Letters: + "Letters {'A'..'Z', 'a'..'z'}" + of Newlines: + "Newlines {'\r', '\n'}" + of Whitespace: + "Whitespace {' ', '\t', '\v', '\r', '\n', '\f'}" + else: + $x