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:
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