commit 83dfe3d818c4d92a62ea24e0a26482c2ab71da9f
parent 0a45c4708fa283f94989fe6324f148cfd16ce173
Author: weetat <wtenison@protonmail.com>
Date: Wed, 26 Feb 2025 19:01:00 +0000
Render fmd to HTML and show it on the home page
Add an interpreter that walks the AST and emits HTML. MakeDirectoryPkg
looks for a .fmd file in the data directory matching the page name and
passes the rendered HTML to the template.
Diffstat:
10 files changed, 196 insertions(+), 52 deletions(-)
diff --git a/src/filesystem/filesystem.go b/src/filesystem/filesystem.go
@@ -4,9 +4,11 @@ import (
"encoding/json"
"fmt"
"log"
+ "miki/src/fmd"
"os"
"path/filepath"
"regexp"
+ "strings"
)
var FilePatternHTML = regexp.MustCompile("^([a-zA-Z0-9.]+).html$")
@@ -25,9 +27,9 @@ type DirectorySignature struct {
}
type DirectoryPackage struct {
- Root string // The root directory of the resource.
- DependencyFiles []string // The set of valid resource files.
- DependencyData IDirectoryData // Any relevant resource data.
+ Root string // The root directory of the resource.
+ DependencyFiles []string // The set of valid resource files.
+ DependencyData interface{} // Any relevant resource data.
}
type IDirectoryData interface {
@@ -95,6 +97,30 @@ func MakeDirectoryPkg(resSig DirectorySignature) (DirectoryPackage, error) {
// Include any sub-directory file dependencies.
_resPkg.DependencyFiles = append(_resPkg.DependencyFiles, _resFileDirDeps...)
+ // Check for atricle entry.
+ // TODO: Move to secure and refined interface.
+ _dataFiles, _err := os.ReadDir(Dirs.DataDirectory)
+ if _err != nil {
+ _retErr := fmt.Errorf("MakeDirectoryPkg:%s", _err)
+ return DirectoryPackage{}, _retErr
+ }
+
+ for _, _df := range _dataFiles {
+ _rootFile := strings.Split(resSig.Root, "/")
+ _rootDataFile := _rootFile[len(_rootFile)-1] + ".fmd"
+
+ if _df.Name() == _rootDataFile {
+ log.Printf("INFO\tMakeDirectoryPkg:df:%s, rootDataFile:%s", _df.Name(), _rootDataFile)
+ var _DataContainer struct {
+ PageContent string
+ }
+ _DataContainer.PageContent = fmd.GetFMDToHTML(filepath.Join(Dirs.DataDirectory, _rootDataFile))
+
+ // log.Printf("INFO\tMakeDirectoryPkg:dataContainer.pageContent:%s", _DataContainer.PageContent)
+ _resPkg.DependencyData = _DataContainer
+ }
+ }
+
// log.Printf("INFO\tMakeDirectoryPkg:%v\n", _resPkg)
return _resPkg, nil
}
diff --git a/src/fmd/fmd.go b/src/fmd/fmd.go
@@ -1,2 +1,45 @@
package fmd
+import (
+ "bytes"
+ "log"
+ "os"
+)
+
+func RenderFMD(filePath string) {
+ _fileBytes, _err := os.ReadFile(filePath)
+ if _err != nil {
+ log.Println("_ERROR\tRenderFMD:ReadFile:", filePath, ":", _err)
+ }
+
+ _l := MakeLexer("RenderLexer", _fileBytes)
+
+ _tokens := _l.Exec()
+
+ _p := MakeParser(_tokens)
+
+ _ast := _p.Exec()
+
+ _outputBytes := bytes.NewBufferString(interpret(_ast))
+
+ if _err = os.WriteFile("temp.html", _outputBytes.Bytes(), 0666); _err != nil {
+ log.Println("_ERROR\tRenderFMD:os.WriteFile:", filePath, ":", _err)
+ }
+}
+
+func GetFMDToHTML(filePath string) string {
+ _fileBytes, _err := os.ReadFile(filePath)
+ if _err != nil {
+ log.Println("_ERROR\tRenderFMD:ReadFile:", filePath, ":", _err)
+ }
+
+ _l := MakeLexer("RenderLexer", _fileBytes)
+
+ _tokens := _l.Exec()
+
+ _p := MakeParser(_tokens)
+
+ _ast := _p.Exec()
+
+ return interpret(_ast)
+}
diff --git a/src/fmd/fmd_test.go b/src/fmd/fmd_test.go
@@ -1,26 +1,26 @@
package fmd
import (
- "os"
"testing"
)
func TestFMD(t *testing.T) {
_path := ("/home/weetat/MIKI/data/ents/home.fmd")
- t.Log("Path:", _path)
- _input, _err := os.ReadFile(_path)
- if _err != nil {
- t.Error("_ERROR\tcannot open file:", _err.Error())
- return
- }
+ RenderFMD(_path)
+ // t.Log("Path:", _path)
+ // _input, _err := os.ReadFile(_path)
+ // if _err != nil {
+ // t.Error("_ERROR\tcannot open file:", _err.Error())
+ // return
+ // }
- _l := MakeLexer("MyLexer", _input)
- _tokens := _l.Exec()
+ // _l := MakeLexer("MyLexer", _input)
+ // _tokens := _l.Exec()
- t.Log("Tokens:", _tokens)
+ // // t.Log("Tokens:", _tokens)
- _p := MakeParser(_tokens)
- _ast := _p.Exec()
+ // _p := MakeParser(_tokens)
+ // _ast := _p.Exec()
- t.Log("AST:", _ast)
+ // t.Log(interpret(_ast))
}
diff --git a/src/fmd/interpreter.go b/src/fmd/interpreter.go
@@ -0,0 +1,61 @@
+package fmd
+
+import (
+ "strings"
+)
+
+func interpret(n node) string {
+ var _res strings.Builder
+
+ switch n.nType {
+ case nodeFMD:
+ for _, _child := range n.children {
+ _res.WriteString(interpret(_child))
+ }
+ case nodeCommand:
+ switch n.value {
+ case "b":
+ return "<b>" + interpret(n.children[0]) + "</b>"
+ case "i":
+ return "<i>" + interpret(n.children[0]) + "</i>"
+ case "h1":
+ return "<h1>" + interpret(n.children[0]) + "</h1>"
+ case "h2":
+ return "<h2>" + interpret(n.children[0]) + "</h2>"
+ case "h3":
+ return "<h3>" + interpret(n.children[0]) + "</h3>"
+ case "h4":
+ return "<h4>" + interpret(n.children[0]) + "</h4>"
+ case "h5":
+ return "<h5>" + interpret(n.children[0]) + "</h5>"
+ case "h6":
+ return "<h6>" + interpret(n.children[0]) + "</h6>"
+ case "@":
+ return "<a>" + interpret(n.children[0]) + "</a>"
+ case "img":
+ {
+ _child := interpret(n.children[0])
+ _src := strings.Split(_child, "/")
+ _alt := _src[len(_src)-1]
+ return "<img src=\"" + _child + "\" alt=\"" + _alt + "\">" + "</img>"
+ }
+ case "col":
+ return "<div class=\"__mcol\">" + interpret(n.children[0]) + "</div>"
+ default:
+ return "<div>" + interpret(n.children[0]) + "</div>"
+ }
+ case nodeBlock:
+ _res.WriteString("<div>")
+ for _, _child := range n.children {
+ _res.WriteString(interpret(_child))
+ }
+ _res.WriteString("</div>")
+ case nodeNewline:
+ return n.value
+ case nodeSpace:
+ return n.value
+ case nodeLiteral:
+ return n.value
+ }
+ return _res.String()
+}
diff --git a/src/fmd/lexer.go b/src/fmd/lexer.go
@@ -2,7 +2,6 @@ package fmd
import (
"slices"
- "strings"
)
/* Reference source: Lexical Scanning in Go - Rob Pike,
@@ -11,15 +10,20 @@ https://www.youtube.com/watch?v=HxaD_trXwRE */
type tokenType int
const (
+ // WORDS
TokenLiteral tokenType = iota // 0. (letters, numbers, symbols)+
TokenKeyword // 1. (valid keywords)
- TokenSpace // 2. " "
- TokenNewline // 3. \n
- TokenTab // 4. \t
- TokenEscape // 5. \
- TokenColon // 6. :
- TokenLBrace // 7. {
- TokenRBrace // 8. }
+
+ // SPACING
+ TokenSpace // 2. " "
+ TokenNewline // 3. \n
+ TokenTab // 4. \t
+ TokenEscape // 5. \
+
+ // OPERATORS
+ TokenColon // 6. :
+ TokenLBrace // 7. {
+ TokenRBrace // 8. }
)
type token struct {
@@ -27,8 +31,9 @@ type token struct {
Value string
}
-// var opSymbols = []string{":", "{", "}", "\\", "\\n"}
-var opKeywords = []string{"b", "i", "h1", "h2", "h3", "h4", "h5", "h6", "@", "#"}
+var symbols = []string{" ", "\n", "\t", "\\", ":", "{", "}"}
+var opKeywords = []string{
+ "b", "i", "h1", "h2", "h3", "h4", "h5", "h6", "@", "#", "col", "img", "_"}
type lexer struct {
name string // for error output
@@ -55,6 +60,9 @@ func (l *lexer) Exec() []token {
case _currentSymbol == '\t':
l.tokens = append(l.tokens, token{TokenTab, "\t"})
l.pos++
+ case _currentSymbol == '\\':
+ l.tokens = append(l.tokens, token{TokenEscape, "\\"})
+ l.pos++
case _currentSymbol == ':':
l.tokens = append(l.tokens, token{TokenColon, ":"})
l.pos++
@@ -64,15 +72,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{TokenEscape, "\\"})
- l.pos++
default:
{ // NOTE: inlined check until good reason to abstract arrives.
var _tokenString string
_start := l.pos
- for l.pos < len(l.input) && !strings.Contains(":{} \\\n", string(l.input[l.pos])) {
+ for l.pos < len(l.input) && !slices.Contains(symbols, string(l.input[l.pos])) {
l.pos++
}
diff --git a/src/fmd/parser.go b/src/fmd/parser.go
@@ -2,7 +2,6 @@ package fmd
import (
"fmt"
- "log"
)
type nodeType string
@@ -13,6 +12,7 @@ const (
nodeCommand nodeType = "command"
nodeLiteral nodeType = "literal"
nodeSpace nodeType = "space"
+ nodeNewline nodeType = "newline"
)
type node struct {
@@ -32,6 +32,7 @@ func MakeParser(tokens []token) *parser {
func (p *parser) Exec() node {
_fmd := node{nType: nodeFMD}
+
for p.pos < len(p.tokens) {
if p.isNextCommand() { // keyword:?
_fmd.children = append(_fmd.children, p.parseCommand())
@@ -50,57 +51,47 @@ func (p *parser) parseCommand() node {
p.consume(TokenColon)
- log.Printf("Command:%v", _command)
// keyword:{block}
if p.peekNextN(TokenLBrace, 0) {
p.consume(TokenLBrace)
- // log.Printf("Command:branch:Block:%v", _command)
_command.children = []node{p.parseBlock()}
p.consume(TokenRBrace)
} else { // keyword:"literal"
- // log.Printf("Command:branch:Literal:%v", _command)
_command.children = []node{p.parseLiteral()}
}
- log.Printf("Command:completed:%v", _command)
return _command
}
func (p *parser) parseBlock() node {
_block := node{nType: nodeBlock}
- log.Printf("Block:%v", _block)
for p.pos < len(p.tokens) && p.current().Type != TokenRBrace {
+
if p.isNextCommand() { // keyword:?
- // log.Printf("Block:branch:Command:%v", _block)
_block.children = append(_block.children, p.parseCommand())
} else if p.peekNextN(TokenSpace, 0) { // " "
- // log.Printf("Block:space%v", _block)
- // _block.children = append(_block.children, node{nType: nodeSpace, value: " "})
+ _block.children = append(_block.children, node{nType: nodeSpace, value: " "})
p.pos++
} else if p.peekNextN(TokenNewline, 0) { // "\n"
- // log.Printf("Block:newline:%v", _block)
- // _block.children = append(_block.children, node{nType: nodeSpace, value: "\\n"})
+ _block.children = append(_block.children, node{nType: nodeNewline, value: "\n"})
p.pos++
} else if p.peekNextN(TokenTab, 0) { // "\t"
- // log.Printf("Block:tab:%v", _block)
- // _block.children = append(_block.children, node{nType: nodeSpace, value: "\\n"})
+ // _block.children = append(_block.children, node{nType: nodeSpace, value: "\\t"})
p.pos++
} else { // "literal"
- // log.Printf("Block:branch:Literal:%v", _block)
_block.children = append(_block.children, p.parseLiteral())
}
+
}
- log.Printf("Block:completed:%v", _block)
return _block
}
func (p *parser) parseLiteral() node {
_literal := node{nType: nodeLiteral, value: p.consume(TokenLiteral).Value}
- log.Printf("Literal:%v", _literal)
return _literal
}
@@ -113,14 +104,10 @@ func (p *parser) isNextCommand() bool {
func (p *parser) peekNextN(t tokenType, n int) bool {
_res := p.pos+n < len(p.tokens) && p.tokens[p.pos+n].Type == t
- if p.pos+n < len(p.tokens) {
- // log.Printf("\tPeek:inputToken:%v, parserToken:%v", t, p.tokens[p.pos+n])
- }
return _res
}
func (p *parser) consume(t tokenType) token {
- // log.Printf("\tconsume:%v", t)
if p.peekNextN(t, 0) {
token := p.tokens[p.pos]
p.pos++
diff --git a/src/fmd/temp.html b/src/fmd/temp.html
@@ -0,0 +1,12 @@
+<h1> <b>Welcome</b> To My <i>Wiki</i> </h1>
+
+This is the landing page for my wiki.
+
+<div class="__mcol">
+ <div>
+ <img src="data/logo.png" alt="logo.png"></img>
+ </div>
+ <div>
+ This is the content of the other column.
+ </div>
+</div>
+\ No newline at end of file
diff --git a/src/network/network.go b/src/network/network.go
@@ -41,6 +41,8 @@ func handlePageDefault(w http.ResponseWriter, r *http.Request) {
_rPkg.DependencyFiles = append(_rPkg.DependencyFiles, mfs.GlobalPkg.DependencyFiles...)
+ // log.Printf("INFO\thandleDefaultPage:rPkg:%v", _rPkg)
+
_tmpl, _err := template.ParseFiles(_rPkg.DependencyFiles...)
if _err != nil {
log.Printf("_ERROR\thandlePageDefault(\"/\"):%s\n", _err)
diff --git a/web/global/styles.css b/web/global/styles.css
@@ -345,6 +345,11 @@
font-size: 0.75em;
}
+ .__mcol {
+ display: flex;
+ flex-direction: column;
+ }
+
}
@layer modifier {
diff --git a/web/home/index.html b/web/home/index.html
@@ -18,11 +18,13 @@
<section id="subject_">
- <section id="_card" class="__container_bg-1">
+ <!-- <section id="_card" class="__container_bg-1">
- </section>
+ </section> -->
<section id="_body">
+
+ {{.PageContent}}
</section>