From 21a623414913697044d51202f13c5f78f9f1aac7 Mon Sep 17 00:00:00 2001 From: Florian Schroedl Date: Thu, 20 Jan 2022 17:00:00 +0100 Subject: [PATCH] Extract properties block parser to file --- src/org/org_parser_properties_block.nim | 59 +++++++++++++ src/org_parser.nim | 109 ++++++------------------ src/parser/parser.nim | 26 +++++- src/parser/utils.nim | 21 +++++ src/utils/fp.nim | 4 + 5 files changed, 131 insertions(+), 88 deletions(-) create mode 100644 src/org/org_parser_properties_block.nim create mode 100644 src/parser/utils.nim diff --git a/src/org/org_parser_properties_block.nim b/src/org/org_parser_properties_block.nim new file mode 100644 index 0000000..0c11469 --- /dev/null +++ b/src/org/org_parser_properties_block.nim @@ -0,0 +1,59 @@ +import std/sugar +import std/strformat +import std/collections/sequtils +import std/strutils +import results +import fusion/matching + +import ../utils/fp +import ../parser/parser +import ../parser/utils + +{.experimental: "caseStmtMacros".} + + +let propertiesKeyParser* = ignore(ch(':')) + anyUntil(choice(@[str(": "), newline])) +let propertiesValueParser* = ignore(str(": ")) + anyUntil(newline) + ignore(newline) + +let propertiesParser* = proc(parser: Parser): ParserResult {.closure.} = + let keyTokenParser = parser + .propertiesKeyParser() + .flatMap(flattenParserTokens) + + let valueTokenParser = keyTokenParser + .map(emptyTokens) + .flatMap(propertiesValueParser) + .flatMap(flattenParserTokens) + + case (keyTokenParser, valueTokenParser): + of (Some(@key), Some(@value)): + ok(Parser( + state: value.state, + tokens: @[ + key.tokens[0], + value.tokens[0], + ] + )) + else: valueTokenParser + +let propertiesStartParser* = str(":PROPERTIES_START:") + newline +let propertiesEndParser* = str(":PROPERTIES_END:") + newline + +let parseProperties* = @[ + ignore(propertiesStartParser), + manyUntil( + propertiesParser, + propertiesEndParser, + ), + ignore(propertiesEndParser), +] + +when isMainModule: + let example = initParserResult(""":PROPERTIES: +:PROP_NAME: Value +:PROP_NAME: Value +:PROP_NAME: Value +:PROP_NAME: Value +:PROPERTIES_END:""").parseSeq(parseProperties) + + echo example diff --git a/src/org_parser.nim b/src/org_parser.nim index 76f0640..1324982 100644 --- a/src/org_parser.nim +++ b/src/org_parser.nim @@ -3,104 +3,43 @@ import std/strformat import std/collections/sequtils import std/strutils import parser/parser +import parser/utils import results import utils/fp import fusion/matching {.experimental: "caseStmtMacros".} -type StringBuilderT = string -type StringBuilder = Builder[StringBuilderT] -type StringBuilderResult = BuilderResult[StringBuilderT] -proc stringConcat(typeInfo: StringBuilderT): - (seq[ParserToken], seq[StringBuilderT]) -> seq[StringBuilderT] = +let parseHeadingStars = @[ + manyUntil(ch('*'), ch(' ')), + ignore(ch(' ')) +] - return proc(xs: seq[ParserToken], ys: seq[StringBuilderT]): seq[StringBuilderT] = - return ys & xs.foldl(a & b.tokenStringValue(), typeInfo) +proc createTodoKeywordParser(xs: seq[string]): (Parser -> ParserResult) = + choice(xs.map((x: string) => str(x) + ignore(ch(' ')))) +let todoKeywords = @["TODO"] +let doneKeywords = @["DONE"] -proc initStringBuilder(str: string): StringBuilderResult = - StringBuilderResult - .ok(StringBuilder(( - parser: initParser(str), - tree: newSeq[StringBuilderT](), - ))) +let parseTodoKeyword = todoKeywords.createTodoKeywordParser() +let parseDoneKeyword = doneKeywords.createTodoKeywordParser() + +let parseHeadingText = @[ + anyUntil(newline), + ignore(newline), +] when isMainModule: - let parseHeadingStars = @[ - manyUntil(ch('*'), ch(' ')), - ignore(ch(' ')) - ] - - proc createTodoKeywordParser(xs: seq[string]): (Parser -> ParserResult) = - choice(xs.map((x: string) => str(x) + ignore(ch(' ')))) - - let todoKeywords = @["TODO"] - let doneKeywords = @["DONE"] - - let parseTodoKeyword = todoKeywords.createTodoKeywordParser() - let parseDoneKeyword = doneKeywords.createTodoKeywordParser() - - let propertiesKeyParser = ignore(ch(':')) + anyUntil(choice(@[str(": "), newline])) - let propertiesValueParser = ignore(str(": ")) + anyUntil(newline) + ignore(newline) - let propertiesParser = proc(parser: Parser): ParserResult {.closure.} = - let keyTokenParser = parser - .propertiesKeyParser() - .flatMap(flattenParserTokens) - - - let valueTokenParser = keyTokenParser - .map(emptyTokens) - .flatMap(propertiesValueParser) - .flatMap(flattenParserTokens) - - case (keyTokenParser, valueTokenParser): - of (Some(@key), Some(@value)): - ok(Parser( - state: value.state, - tokens: @[ - key.tokens[0], - value.tokens[0], - ] - )) - else: valueTokenParser - - let propertiesStartParser = str(":PROPERTIES_START:") + newline - let propertiesEndParser = str(":PROPERTIES_END:") + newline - - let parseProperties = @[ - ignore(propertiesStartParser), - manyUntil( - propertiesParser, - propertiesEndParser, - ), - ignore(propertiesEndParser), - ] - - - echo initParserResult(""":PROPERTIES: -:PROP_NAME: Value -:PROP_NAME: Value -:PROP_NAME: Value -:PROP_NAME: Value -:PROPERTIES_END: -""").parseSeq(parseProperties) - - let parseHeadingText = @[ - anyUntil(newline), - ignore(newline), - ] - let sampleBuilder = StringBuilderResult .ok(StringBuilder(( parser: initParser("""**** TODO Some stars -:PROPERTIES: -:PROP_NAME: Value -:PROP_NAME: Value -:PROP_NAME: Value -:PROP_NAME: Value -:PROPERTIES_END: -"""), + :PROPERTIES: + :PROP_NAME: Value + :PROP_NAME: Value + :PROP_NAME: Value + :PROP_NAME: Value + :PROPERTIES_END: + """), tree: newSeq[StringBuilderT](), ))) .applyParsersSeq(@[ @@ -118,4 +57,4 @@ when isMainModule: xs => "Parser Succesfull:\n" & xs.join("\n"), ) - # echo sampleBuilder + echo sampleBuilder diff --git a/src/parser/parser.nim b/src/parser/parser.nim index ddccbfb..9cabec2 100644 --- a/src/parser/parser.nim +++ b/src/parser/parser.nim @@ -33,8 +33,8 @@ type # keyValuePairValue*: parserTokenKeyValuePairValueT Parser* = ref object - state: ParserState - tokens: seq[ParserToken] + state*: ParserState + tokens*: seq[ParserToken] ParseErrorKind = enum choiceMismatchErr @@ -238,6 +238,26 @@ func ignore*(parserFn: Parser -> ParserResult): (Parser -> ParserResult) {.inlin tokens: parser.tokens, )) + +func flattenParserTokens*(parser: Parser): ParserResult = + return ParserResult.ok( + Parser( + state: parser.state, + tokens: @[ + ParserToken( + kind: parserTokenString, + stringValue: parser.tokens.foldl(a & b.tokenStringValue(), "") + ) + ] + ) + ) + +func emptyTokens*(parser: Parser): Parser = + Parser( + state: parser.state, + tokens: newSeq[ParserToken](), + ) + func optional*(parserFn: Parser -> ParserResult): (Parser -> ParserResult) {.inline.} = ## Parse characters and ignore failure return proc(parser: Parser): ParserResult = @@ -293,8 +313,8 @@ proc `+`*(parserFnA: Parser -> ParserResult, parserFnB: Parser -> ParserResult): let newlineParser = choice(@[ + ch(NewLines), endOfStream, - ch(NewLines) ]) proc newline*(parser: Parser): ParserResult = newlineParser(parser) diff --git a/src/parser/utils.nim b/src/parser/utils.nim new file mode 100644 index 0000000..803a760 --- /dev/null +++ b/src/parser/utils.nim @@ -0,0 +1,21 @@ +import std/sugar +import std/collections/sequtils +import ./parser +import results + +type StringBuilderT* = string +type StringBuilder* = Builder[StringBuilderT] +type StringBuilderResult* = BuilderResult[StringBuilderT] + +proc stringConcat*(typeInfo: StringBuilderT): + (seq[ParserToken], seq[StringBuilderT]) -> seq[StringBuilderT] = + + return proc(xs: seq[ParserToken], ys: seq[StringBuilderT]): seq[StringBuilderT] = + return ys & xs.foldl(a & b.tokenStringValue(), typeInfo) + +proc initStringBuilder*(str: string): StringBuilderResult = + StringBuilderResult + .ok(StringBuilder(( + parser: initParser(str), + tree: newSeq[StringBuilderT](), + ))) diff --git a/src/utils/fp.nim b/src/utils/fp.nim index c66f995..9c1e9c1 100644 --- a/src/utils/fp.nim +++ b/src/utils/fp.nim @@ -1,5 +1,9 @@ import std/sugar import fp/maybe +import results + +template isSome*(self: Result): bool = self.isOk() +template isNone*(self: Result): bool = self.isErr() proc findMaybe*[T](xs: seq[T], fn: T -> bool): Maybe[T] = for x in xs: