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 "https://github.com/floscr/nimfp#master"
|
||||
requires "fusion"
|
||||
requires "result"
|
||||
# requires "print"
|
||||
# requires "zero_functional"
|
||||
# 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 results
|
||||
import std/collections/sequtils
|
||||
import ./org_types
|
||||
import ../parser/parser_types
|
||||
|
||||
## Inline Blocks
|
||||
type OrgInlineBuilderT* = OrgInlineBlock
|
||||
type OrgInlineBuilder* = Builder[OrgInlineBuilderT]
|
||||
type OrgInlineBuilderResult* = BuilderResult[OrgInlineBuilderT]
|
||||
@@ -28,3 +30,25 @@ func rawTextTokenizer*(kind: orgInlineBlockKind): string -> OrgInlineBuilderT =
|
||||
kind: kind,
|
||||
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 fp/maybe
|
||||
|
||||
## Inline Block
|
||||
type
|
||||
orgInlineBlockKind* = enum
|
||||
orgRawText,
|
||||
@@ -76,3 +77,49 @@ proc `$`*(x: OrgInlineBlock): string =
|
||||
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
|
||||
:PROPERTIES_END:
|
||||
|
||||
Foo
|
||||
"""),
|
||||
tree: newSeq[StringBuilderT](),
|
||||
)))
|
||||
|
||||
@@ -7,6 +7,21 @@ import fusion/matching
|
||||
import ./parser_internals
|
||||
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](
|
||||
builder: Builder[T],
|
||||
builderFns: seq[tuple[
|
||||
|
||||
@@ -3,8 +3,10 @@ import std/strformat
|
||||
import std/collections/sequtils
|
||||
import std/sugar
|
||||
import results
|
||||
import fp/maybe
|
||||
import fusion/matching
|
||||
import ../utils/str
|
||||
import ../utils/fp
|
||||
|
||||
{.experimental: "caseStmtMacros".}
|
||||
|
||||
@@ -46,6 +48,12 @@ type
|
||||
]
|
||||
BuilderResult*[T] = Result[Builder[T], (Builder[T], string)]
|
||||
|
||||
# SingleBuilder*[T] = tuple[
|
||||
# parser: Parser,
|
||||
# tree: T
|
||||
# ]
|
||||
# SingleBuilderResult*[T] = Result[SingleBuilder[T], (SingleBuilder[T], string)]
|
||||
|
||||
# -- Initalizers
|
||||
|
||||
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]),
|
||||
)))
|
||||
|
||||
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](
|
||||
builder: Builder[T],
|
||||
parsers: seq[Parser -> ParserResult],
|
||||
|
||||
@@ -2,6 +2,12 @@ import std/sugar
|
||||
import fp/maybe
|
||||
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 isNone*(self: Result): bool = self.isErr()
|
||||
|
||||
@@ -26,3 +32,6 @@ when isMainModule:
|
||||
echo @[
|
||||
(x: int) => (if x == 2: Just("foo") else: Nothing[string]()),
|
||||
].findMaybeFn(2)
|
||||
|
||||
assert last(@[1,2,3]) == just(3)
|
||||
assert last[int](@[]) == nothing(int)
|
||||
|
||||
Reference in New Issue
Block a user