Create an org heading builder
This commit is contained in:
45
docs/spec.org
Normal file
45
docs/spec.org
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#+TITLE: Spec
|
||||||
|
|
||||||
|
|
||||||
|
* Specs
|
||||||
|
|
||||||
|
[[https://nim-lang.org/docs/oids.html][std/oids]] :: ~genOid~
|
||||||
|
|
||||||
|
Takes bunch of builders
|
||||||
|
|
||||||
|
#+begin_src
|
||||||
|
DocumentBuilderResult(
|
||||||
|
id = (oid)
|
||||||
|
children: seq[OrgItem]
|
||||||
|
)
|
||||||
|
|
||||||
|
Heading = (
|
||||||
|
stars
|
||||||
|
)
|
||||||
|
|
||||||
|
OrgHeadingChild =
|
||||||
|
| Paragraph
|
||||||
|
|
||||||
|
OrgItem =
|
||||||
|
| OrgHeadingChild
|
||||||
|
| Heading
|
||||||
|
|
||||||
|
Heading(
|
||||||
|
id (oid)
|
||||||
|
parentId (oid)
|
||||||
|
children: seq[OrgHeadingChild]
|
||||||
|
)
|
||||||
|
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Headline Builder
|
||||||
|
Paragraph Builder
|
||||||
|
Newline builder
|
||||||
|
List
|
||||||
|
|
||||||
|
|
||||||
|
We need something that parses continously until a stop function is hit
|
||||||
|
|
||||||
|
for heading it is newline stars parsers for instance
|
||||||
|
|
||||||
|
We need a new tryParseBuild function that tries a bunch of builders
|
||||||
@@ -13,6 +13,7 @@ binDir = "./dst"
|
|||||||
requires "nim >= 1.4.4"
|
requires "nim >= 1.4.4"
|
||||||
requires "https://github.com/floscr/nimfp#master"
|
requires "https://github.com/floscr/nimfp#master"
|
||||||
requires "fusion"
|
requires "fusion"
|
||||||
|
requires "result"
|
||||||
# requires "print"
|
# requires "print"
|
||||||
# requires "zero_functional"
|
# requires "zero_functional"
|
||||||
# requires "cascade"
|
# requires "cascade"
|
||||||
|
|||||||
87
src/org/org_block_heading.nim
Normal file
87
src/org/org_block_heading.nim
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import std/sugar
|
||||||
|
import std/strformat
|
||||||
|
import std/collections/sequtils
|
||||||
|
import std/strutils
|
||||||
|
import results
|
||||||
|
import fusion/matching
|
||||||
|
|
||||||
|
import ../utils/fp
|
||||||
|
import ../parser/parser_internals
|
||||||
|
import ../parser/parser_types
|
||||||
|
import ../parser/utils
|
||||||
|
import ./org_properties_block
|
||||||
|
import ./org_types
|
||||||
|
import ./org_builder
|
||||||
|
|
||||||
|
{.experimental: "caseStmtMacros".}
|
||||||
|
|
||||||
|
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 parseHeadingText = @[
|
||||||
|
anyUntil(newline),
|
||||||
|
ignore(newline),
|
||||||
|
]
|
||||||
|
|
||||||
|
# func buildStars(token: seq[ParserToken]): seq[ParserToken] =
|
||||||
|
# # builder.kind = orgHeading
|
||||||
|
# # builder.level = token.tokenStringValue().len
|
||||||
|
# token
|
||||||
|
|
||||||
|
let buildStars = func(tokens: seq[ParserToken], org: OrgBlock): OrgBlock {.closure.}=
|
||||||
|
org.level = tokens[0].tokenStringValue().len
|
||||||
|
org
|
||||||
|
|
||||||
|
proc tryBuildHeading(builder: OrgBuilderResult): OrgBuilderResult =
|
||||||
|
builder
|
||||||
|
.applyParsersSeqToSingle(
|
||||||
|
OrgBlock(kind: orgHeading),
|
||||||
|
@[(parseHeadingStars, buildStars)]
|
||||||
|
)
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
echo initOrgBuilder("**** TODO Some stars")
|
||||||
|
.tryBuildHeading()
|
||||||
|
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
|
||||||
|
# Foo
|
||||||
|
# """),
|
||||||
|
# tree: newSeq[StringBuilderT](),
|
||||||
|
# )))
|
||||||
|
# .applyParsersSeq(@[
|
||||||
|
# (parseHeadingStars, stringConcat("Stars: ")),
|
||||||
|
|
||||||
|
# (@[optional(parseTodoKeyword)], stringConcat("TODO: ")),
|
||||||
|
# (@[optional(parseDoneKeyword)], stringConcat("DONE: ")),
|
||||||
|
|
||||||
|
# (parseHeadingText, stringConcat("Text: ")),
|
||||||
|
# (parseProperties, stringConcat("Properties: ", seperator = ", ")),
|
||||||
|
# # (@[optional(choice(parseProperties))], stringConcat("Properties: ")),
|
||||||
|
# ])
|
||||||
|
# .foldBuilder(
|
||||||
|
# err => &"Error Parsing: {err}",
|
||||||
|
# xs => "Parser Succesfull:\n" & xs.join("\n"),
|
||||||
|
# )
|
||||||
|
|
||||||
|
# echo sampleBuilder
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
import std/sugar
|
import std/sugar
|
||||||
import results
|
import results
|
||||||
|
import std/collections/sequtils
|
||||||
import ./org_types
|
import ./org_types
|
||||||
import ../parser/parser_types
|
import ../parser/parser_types
|
||||||
|
|
||||||
|
## Inline Blocks
|
||||||
type OrgInlineBuilderT* = OrgInlineBlock
|
type OrgInlineBuilderT* = OrgInlineBlock
|
||||||
type OrgInlineBuilder* = Builder[OrgInlineBuilderT]
|
type OrgInlineBuilder* = Builder[OrgInlineBuilderT]
|
||||||
type OrgInlineBuilderResult* = BuilderResult[OrgInlineBuilderT]
|
type OrgInlineBuilderResult* = BuilderResult[OrgInlineBuilderT]
|
||||||
@@ -28,3 +30,25 @@ func rawTextTokenizer*(kind: orgInlineBlockKind): string -> OrgInlineBuilderT =
|
|||||||
kind: kind,
|
kind: kind,
|
||||||
content: content,
|
content: content,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
## Blocks
|
||||||
|
type OrgBuilderT* = OrgBlock
|
||||||
|
type OrgBuilder* = Builder[OrgBuilderT]
|
||||||
|
type OrgBuilderResult* = BuilderResult[OrgBuilderT]
|
||||||
|
|
||||||
|
func initOrgBuilder*(content: string): OrgBuilderResult =
|
||||||
|
return OrgBuilderResult.ok(OrgBuilder((
|
||||||
|
parser: initParser(content),
|
||||||
|
tree: newSeq[OrgBuilderT](),
|
||||||
|
)))
|
||||||
|
|
||||||
|
# proc orgBuilderConcat*(typeInfo: OrgBuilderT, concatFn: (ParserToken, OrgBuilderT) -> OrgBuilderT):
|
||||||
|
# (seq[ParserToken], OrgBuilderT) -> OrgBuilderT =
|
||||||
|
# return proc(xs: seq[ParserToken], builder: OrgBuilderT): OrgBuilderT =
|
||||||
|
# return xs.foldl(concatFn(b, a), typeInfo)
|
||||||
|
|
||||||
|
|
||||||
|
proc orgBuilderApply*(concatFn: (ParserToken, OrgBuilderT) -> OrgBuilderT):
|
||||||
|
(seq[ParserToken], OrgBuilderT) -> OrgBuilderT =
|
||||||
|
return proc(tokens: seq[ParserToken], builder: OrgBuilderT): OrgBuilderT =
|
||||||
|
tokens.foldl(concatFn(b, a), builder)
|
||||||
|
|||||||
39
src/org/org_document.nim
Normal file
39
src/org/org_document.nim
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import std/sugar
|
||||||
|
import std/collections/sequtils
|
||||||
|
import std/strformat
|
||||||
|
import std/strutils
|
||||||
|
import results
|
||||||
|
import fusion/matching
|
||||||
|
import ./org_types
|
||||||
|
import ./org_builder
|
||||||
|
import ./org_text_link
|
||||||
|
import ../utils/fp
|
||||||
|
import ../parser/parser_internals
|
||||||
|
import ../parser/parser_types
|
||||||
|
import ../parser/builder_api
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
let test = initOrgBuilder("""
|
||||||
|
Some text to be ignored for now
|
||||||
|
|
||||||
|
* Stars 1
|
||||||
|
|
||||||
|
Ignore me
|
||||||
|
|
||||||
|
** Stars 1-1
|
||||||
|
|
||||||
|
*** Stars 1-1-1
|
||||||
|
|
||||||
|
** Stars 1-2
|
||||||
|
|
||||||
|
* Stars 2
|
||||||
|
|
||||||
|
""")
|
||||||
|
.flatMap((builder: OrgBuilder) => tryParseBuild(
|
||||||
|
builder = builder,
|
||||||
|
builderFns = orgStyledTextBuilders,
|
||||||
|
defaultBuilderFn = makeRawTokenOrEmpty,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
echo test
|
||||||
@@ -3,6 +3,7 @@ import std/sugar
|
|||||||
import std/strutils
|
import std/strutils
|
||||||
import fp/maybe
|
import fp/maybe
|
||||||
|
|
||||||
|
## Inline Block
|
||||||
type
|
type
|
||||||
orgInlineBlockKind* = enum
|
orgInlineBlockKind* = enum
|
||||||
orgRawText,
|
orgRawText,
|
||||||
@@ -76,3 +77,49 @@ proc `$`*(x: OrgInlineBlock): string =
|
|||||||
stringifySpecialFields(x) &
|
stringifySpecialFields(x) &
|
||||||
"""
|
"""
|
||||||
)"""
|
)"""
|
||||||
|
|
||||||
|
## Block
|
||||||
|
type
|
||||||
|
orgBlockKind* = enum
|
||||||
|
orgHeading
|
||||||
|
# orgParagraph
|
||||||
|
OrgBlock* = ref object
|
||||||
|
children*: seq[OrgInlineBlock]
|
||||||
|
|
||||||
|
case kind*: orgBlockKind
|
||||||
|
of orgHeading:
|
||||||
|
level*: int
|
||||||
|
|
||||||
|
func stringifySpecialFields(x: OrgBlock): string =
|
||||||
|
let specialFields = case x.kind:
|
||||||
|
of orgHeading:
|
||||||
|
&"""level: {x.level}"""
|
||||||
|
else: ""
|
||||||
|
|
||||||
|
specialFields
|
||||||
|
.just()
|
||||||
|
.notEmpty()
|
||||||
|
.map(x => x.indent(2))
|
||||||
|
.map(x => "\n" & x & "\n")
|
||||||
|
.getOrElse("")
|
||||||
|
|
||||||
|
proc `$`*(x: OrgBlock): string =
|
||||||
|
&"""OrgBlock(
|
||||||
|
kind: {x.kind}""" &
|
||||||
|
stringifySpecialFields(x) &
|
||||||
|
&"""
|
||||||
|
children: {x.children}
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
OrgDocument* = ref object
|
||||||
|
children*: seq[OrgBlock]
|
||||||
|
|
||||||
|
proc `$`*(x: OrgDocument): string =
|
||||||
|
&"""OrgDocument(
|
||||||
|
children: {x.children}
|
||||||
|
)"""
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
echo OrgBlock(kind: orgHeading)
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ when isMainModule:
|
|||||||
:PROP_NAME: Value
|
:PROP_NAME: Value
|
||||||
:PROP_NAME: Value
|
:PROP_NAME: Value
|
||||||
:PROPERTIES_END:
|
:PROPERTIES_END:
|
||||||
|
|
||||||
|
Foo
|
||||||
"""),
|
"""),
|
||||||
tree: newSeq[StringBuilderT](),
|
tree: newSeq[StringBuilderT](),
|
||||||
)))
|
)))
|
||||||
|
|||||||
@@ -7,6 +7,21 @@ import fusion/matching
|
|||||||
import ./parser_internals
|
import ./parser_internals
|
||||||
import ./parser_types
|
import ./parser_types
|
||||||
|
|
||||||
|
# proc tryParseBuildOrg*[T](
|
||||||
|
# builder: Builder[T],
|
||||||
|
# builderFns: seq[
|
||||||
|
# Builder[T] -> BuilderResult[T]
|
||||||
|
# ],
|
||||||
|
# stopAtParserFn = endOfStream
|
||||||
|
# ): BuilderResult[T] =
|
||||||
|
|
||||||
|
# # Mutating accumulators
|
||||||
|
# var builderAcc: BuilderResult[T] = BuilderResult.ok(builder)
|
||||||
|
|
||||||
|
# while builderAcc.isOk() and builderAcc.tryParser(stopAtParserFn).isErr()
|
||||||
|
# for fn in builderFns:
|
||||||
|
# fn
|
||||||
|
|
||||||
proc tryParseBuild*[T](
|
proc tryParseBuild*[T](
|
||||||
builder: Builder[T],
|
builder: Builder[T],
|
||||||
builderFns: seq[tuple[
|
builderFns: seq[tuple[
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ import std/strformat
|
|||||||
import std/collections/sequtils
|
import std/collections/sequtils
|
||||||
import std/sugar
|
import std/sugar
|
||||||
import results
|
import results
|
||||||
|
import fp/maybe
|
||||||
import fusion/matching
|
import fusion/matching
|
||||||
import ../utils/str
|
import ../utils/str
|
||||||
|
import ../utils/fp
|
||||||
|
|
||||||
{.experimental: "caseStmtMacros".}
|
{.experimental: "caseStmtMacros".}
|
||||||
|
|
||||||
@@ -46,6 +48,12 @@ type
|
|||||||
]
|
]
|
||||||
BuilderResult*[T] = Result[Builder[T], (Builder[T], string)]
|
BuilderResult*[T] = Result[Builder[T], (Builder[T], string)]
|
||||||
|
|
||||||
|
# SingleBuilder*[T] = tuple[
|
||||||
|
# parser: Parser,
|
||||||
|
# tree: T
|
||||||
|
# ]
|
||||||
|
# SingleBuilderResult*[T] = Result[SingleBuilder[T], (SingleBuilder[T], string)]
|
||||||
|
|
||||||
# -- Initalizers
|
# -- Initalizers
|
||||||
|
|
||||||
func initParserToken*(x: char): ParserToken = ParserToken(kind: parserTokenChar, charValue: x)
|
func initParserToken*(x: char): ParserToken = ParserToken(kind: parserTokenChar, charValue: x)
|
||||||
@@ -130,6 +138,71 @@ proc mapTree*[T](builder: BuilderResult[T], fn: seq[T] -> seq[T]): BuilderResult
|
|||||||
tree: fn(b[1]),
|
tree: fn(b[1]),
|
||||||
)))
|
)))
|
||||||
|
|
||||||
|
proc tryParser*[T](
|
||||||
|
builder: Builder[T],
|
||||||
|
parser: Parser -> ParserResult,
|
||||||
|
): BuilderResult[T] =
|
||||||
|
## Try out a `parser` on a `builder`
|
||||||
|
## When succesful return the original builder, otherwise return an error
|
||||||
|
ParserResult.ok(Parser(
|
||||||
|
state: builder[0].state,
|
||||||
|
tokens: @[]
|
||||||
|
))
|
||||||
|
.flatMap(parser)
|
||||||
|
.foldTokens(
|
||||||
|
(err: ParserError) => BuilderResult[T].err((builder, "Error")),
|
||||||
|
(newTokens: seq[ParserToken]) => BuilderResult[T].ok(builder),
|
||||||
|
)
|
||||||
|
|
||||||
|
proc tryParser*[T](
|
||||||
|
builder: BuilderResult[T],
|
||||||
|
parser: Parser -> ParserResult,
|
||||||
|
): BuilderResult[T] =
|
||||||
|
## Try out a `parser` on a `builder` result
|
||||||
|
## When succesful return the ok builder, otherwise return an error
|
||||||
|
builder.flatMap((x: Builder[T]) => tryParser(x, parser))
|
||||||
|
|
||||||
|
proc applyParsersToSingle*[T](
|
||||||
|
builder: Builder[T],
|
||||||
|
parsers: seq[Parser -> ParserResult],
|
||||||
|
tokenFoldFn: (seq[ParserToken], T) -> T,
|
||||||
|
initT: T,
|
||||||
|
): BuilderResult[T] =
|
||||||
|
# Apply the current parsing functions and convert to text tokens wrapped in ParserResult
|
||||||
|
let newParser = ParserResult.ok(Parser(
|
||||||
|
state: builder[0].state,
|
||||||
|
tokens: @[]
|
||||||
|
))
|
||||||
|
.parseSeq(parsers)
|
||||||
|
|
||||||
|
newParser
|
||||||
|
.foldTokens(
|
||||||
|
(err: ParserError) => BuilderResult[T].err((builder, "foo")),
|
||||||
|
(newTokens: seq[ParserToken]) => BuilderResult[T].ok(
|
||||||
|
builder.initBuilder(
|
||||||
|
newParser.unsafeGet(),
|
||||||
|
builder.tree
|
||||||
|
.last()
|
||||||
|
.orElse(just(initT))
|
||||||
|
.map((x: T) => @[tokenFoldFn(newTokens, x)])
|
||||||
|
.getOrElse(newSeq[T]())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
proc applyParsersSeqToSingle*[T](
|
||||||
|
builderResult: BuilderResult[T],
|
||||||
|
initT: T,
|
||||||
|
xs: seq[tuple[
|
||||||
|
parsers: seq[Parser -> ParserResult],
|
||||||
|
tokenFoldFn: (seq[ParserToken], T) -> T,
|
||||||
|
]],
|
||||||
|
): BuilderResult[T] =
|
||||||
|
xs.foldl(
|
||||||
|
a.flatMap((builder: Builder[T]) => applyParsersToSingle(builder, b[0], b[1], initT)),
|
||||||
|
builderResult
|
||||||
|
)
|
||||||
|
|
||||||
proc applyParsers*[T](
|
proc applyParsers*[T](
|
||||||
builder: Builder[T],
|
builder: Builder[T],
|
||||||
parsers: seq[Parser -> ParserResult],
|
parsers: seq[Parser -> ParserResult],
|
||||||
|
|||||||
@@ -2,6 +2,12 @@ import std/sugar
|
|||||||
import fp/maybe
|
import fp/maybe
|
||||||
import results
|
import results
|
||||||
|
|
||||||
|
func last*[T](xs: seq[T]): Maybe[T] =
|
||||||
|
if xs.len == 0:
|
||||||
|
nothing(T)
|
||||||
|
else:
|
||||||
|
just(xs[^1])
|
||||||
|
|
||||||
template isSome*(self: Result): bool = self.isOk()
|
template isSome*(self: Result): bool = self.isOk()
|
||||||
template isNone*(self: Result): bool = self.isErr()
|
template isNone*(self: Result): bool = self.isErr()
|
||||||
|
|
||||||
@@ -26,3 +32,6 @@ when isMainModule:
|
|||||||
echo @[
|
echo @[
|
||||||
(x: int) => (if x == 2: Just("foo") else: Nothing[string]()),
|
(x: int) => (if x == 2: Just("foo") else: Nothing[string]()),
|
||||||
].findMaybeFn(2)
|
].findMaybeFn(2)
|
||||||
|
|
||||||
|
assert last(@[1,2,3]) == just(3)
|
||||||
|
assert last[int](@[]) == nothing(int)
|
||||||
|
|||||||
Reference in New Issue
Block a user