miki

Miki: personal wiki with the fmd markup format
git clone https://wtenison.com/repo/miki.git
Log | Files | Refs | README | LICENSE

commit d01d3ae2675406feaead139d3e77b1310827ca83
parent c2a06cf2427eeadb1914c8128f82a0b361492cc3
Author: weetat <wtenison@protonmail.com>
Date:   Fri, 28 Feb 2025 19:22:43 +0000

Track previous node in the fmd parser

Nodes now keep a pointer to the node before them. Space and newline
nodes are merged into a single spacing type, and the lexer emits tokens
for ( and ).

Diffstat:
Msrc/fmd/interpreter.go | 13++++++++-----
Msrc/fmd/lexer.go | 6++++++
Msrc/fmd/parser.go | 66+++++++++++++++++++++++++++++++++++++-----------------------------
3 files changed, 51 insertions(+), 34 deletions(-)

diff --git a/src/fmd/interpreter.go b/src/fmd/interpreter.go @@ -55,11 +55,14 @@ func interpret(n node) string { _res.WriteString(interpret(_child)) } _res.WriteString("</div>") - case nodeNewline: - return "<br>" - case nodeSpace: - return n.value - case nodeLiteral: + case nodeSpacing: + switch n.value { + case "\n": + return "<br>" + default: + return n.value + } + case nodeWord: if slices.Contains(invalidHTML, n.value) { return "" } diff --git a/src/fmd/lexer.go b/src/fmd/lexer.go @@ -82,6 +82,12 @@ func (l *lexer) Exec() []token { case _currentSymbol == '}': l.tokens = append(l.tokens, token{tokenRBrace, "}"}) l.pos++ + case _currentSymbol == '(': + l.tokens = append(l.tokens, token{tokenLParen, "("}) + l.pos++ + case _currentSymbol == ')': + l.tokens = append(l.tokens, token{tokenRParen, ")"}) + l.pos++ default: { // NOTE: inlined check until good reason to abstract arrives. var _tokenString string diff --git a/src/fmd/parser.go b/src/fmd/parser.go @@ -7,23 +7,26 @@ import ( type nodeType string const ( - nodeFMD nodeType = "fmd" - nodeBlock nodeType = "block" - nodeCommand nodeType = "command" - nodeLiteral nodeType = "literal" - nodeSpace nodeType = "space" - nodeNewline nodeType = "newline" + nodeFMD nodeType = "fmd" + + nodeBlock nodeType = "block" + nodeCommand nodeType = "command" + nodeWord nodeType = "literal" + nodeOperator nodeType = "operator" + nodeSpacing nodeType = "spacing" ) type node struct { nType nodeType value string children []node + prevNode *node } type parser struct { - tokens []token - pos int + tokens []token + pos int + prevNode *node } func MakeParser(tokens []token) *parser { @@ -31,7 +34,7 @@ func MakeParser(tokens []token) *parser { } func (p *parser) Exec() node { - _fmd := node{nType: nodeFMD} + _fmd := p.makeNode(nodeFMD, "_fmd_") for p.pos < len(p.tokens) { if p.isNextCommand() { // keyword:? @@ -44,10 +47,8 @@ func (p *parser) Exec() node { } func (p *parser) parseCommand() node { - _command := node{nType: nodeCommand} - - _keyword := p.consume(tokenKeyword) - _command.value = _keyword.Value + _keyword := p.consume(tokenKeyword).Value + _command := p.makeNode(nodeCommand, _keyword) p.consume(tokenColon) @@ -59,40 +60,41 @@ func (p *parser) parseCommand() node { p.consume(tokenRBrace) } else { // keyword:"literal" - _command.children = []node{p.parseLiteral()} + _command.children = []node{p.parseWord()} } return _command } func (p *parser) parseBlock() node { - _block := node{nType: nodeBlock} + _block := p.makeNode(nodeBlock, "") for p.pos < len(p.tokens) && p.current().tType != tokenRBrace { - if p.isNextCommand() { // keyword:? - _block.children = append(_block.children, p.parseCommand()) - } else if p.peekNextN(tokenSpace, 0) { // " " - _block.children = append(_block.children, node{nType: nodeSpace, value: " "}) + switch { + case p.peekNextN(tokenWord, 0): + _block.children = append(_block.children, p.parseWord()) + case p.peekNextN(tokenSpace, 0): + _block.children = append(_block.children, p.makeNode(nodeSpacing, " ")) p.pos++ - } else if p.peekNextN(tokenNewline, 0) { // "\n" - _block.children = append(_block.children, node{nType: nodeNewline, value: "\n"}) + case p.peekNextN(tokenTab, 0): + _block.children = append(_block.children, p.makeNode(nodeSpacing, "\t")) p.pos++ - } else if p.peekNextN(tokenTab, 0) { // "\t" - // _block.children = append(_block.children, node{nType: nodeSpace, value: "\\t"}) + case p.peekNextN(tokenNewline, 0): + _block.children = append(_block.children, p.makeNode(nodeSpacing, "\n")) p.pos++ - } else { // "literal" - _block.children = append(_block.children, p.parseLiteral()) + case p.isNextCommand(): + _block.children = append(_block.children, p.parseCommand()) } - } return _block } -func (p *parser) parseLiteral() node { - _literal := node{nType: nodeLiteral, value: p.consume(tokenWord).Value} - return _literal +func (p *parser) parseWord() node { + _wordVal := p.consume(tokenWord).Value + _word := p.makeNode(nodeWord, _wordVal) + return _word } func (p *parser) isNextCommand() bool { @@ -102,6 +104,12 @@ func (p *parser) isNextCommand() bool { p.peekNextN(tokenLBrace, 2) } +func (p *parser) makeNode(nType nodeType, value string) node { + _n := node{nType: nType, value: value, prevNode: p.prevNode} + p.prevNode = &_n + return _n +} + func (p *parser) peekNextN(t tokenType, n int) bool { _res := p.pos+n < len(p.tokens) && p.tokens[p.pos+n].tType == t return _res