diff --git a/README.md b/README.md index cf8a9a9..8556abe 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# htnoml +# HTnoML -Experimental alternative to HTML +HTML-like DSL that that can be converted into HTML. ![Test](https://github.com/radulucut/htnoml/actions/workflows/test.yml/badge.svg) -### Example +### Example of HTnoML syntax ```htnoml {:html @@ -20,3 +20,30 @@ Experimental alternative to HTML } } ``` + +### Usage + +```go +package main + +import ( + "fmt" + "github.com/radulucut/htnoml" +) + +func main() { + f, err := os.Open("example.htnoml") + if err != nil { + panic(err) + } + defer f.Close() + + p, err := NewParser(f) + if err != nil { + panic(err) + } + + w := new(bytes.Buffer) + p.ToHTML(w) // writes the HTML to the buffer +} +``` diff --git a/go.mod b/go.mod index d50277f..cf97aac 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module github.com/radulucut/htnoml go 1.20 -require github.com/google/go-cmp v0.6.0 // indirect +require github.com/google/go-cmp v0.6.0 diff --git a/parser.go b/parser.go index 22b4472..1d27468 100644 --- a/parser.go +++ b/parser.go @@ -56,39 +56,50 @@ func NewParser(reader io.ReadSeeker) (*Parser, error) { }, nil } -func (p *Parser) ToHTML() string { +func (p *Parser) ToHTML(writer io.Writer) { if p.node == nil { p.parse() } - return p.nodeToHTML(p.node, true) + p.nodeToHTML(writer, p.node, true) } -func (p *Parser) nodeToHTML(node *Node, isRoot bool) string { +func (p *Parser) nodeToHTML(w io.Writer, node *Node, isRoot bool) { if node.Text != nil { - return string(p.buf[node.Text.Start:node.Text.End]) + w.Write(p.buf[node.Text.Start:node.Text.End]) + return } - children := "" - for _, child := range node.Children { - children += p.nodeToHTML(&child, false) - } - if isRoot { - return children - } - attributes := "" - for _, attr := range node.Attributes { - attributes += " " + string(p.buf[attr.Name.Start:attr.Name.End]) - if attr.Value != nil { - attributes += "=" + "\"" + string(p.buf[attr.Value.Start:attr.Value.End]) + "\"" + var tag []byte + if !isRoot { + w.Write([]byte{'<'}) + if node.ElementType == nil { + tag = []byte{'d', 'i', 'v'} + } else { + tag = p.buf[node.ElementType.Start:node.ElementType.End] } + w.Write(tag) + for _, attr := range node.Attributes { + w.Write([]byte{' '}) + w.Write(p.buf[attr.Name.Start:attr.Name.End]) + if attr.Value != nil { + w.Write([]byte{'=', '"'}) + w.Write(p.buf[attr.Value.Start:attr.Value.End]) + w.Write([]byte{'"'}) + } + } + if len(node.Children) == 0 && isVoidElement(string(tag)) { + w.Write([]byte{' ', '/', '>'}) + return + } + w.Write([]byte{'>'}) } - if node.ElementType == nil { - return "" + children + "" + for _, child := range node.Children { + p.nodeToHTML(w, &child, false) } - tag := string(p.buf[node.ElementType.Start:node.ElementType.End]) - if children == "" && isVoidElement(tag) { - return "<" + tag + attributes + " />" + if !isRoot { + w.Write([]byte{'<', '/'}) + w.Write(tag) + w.Write([]byte{'>'}) } - return "<" + tag + attributes + ">" + children + "" } func (p *Parser) parse() { diff --git a/parser_test.go b/parser_test.go index f7dc119..6bb7da3 100644 --- a/parser_test.go +++ b/parser_test.go @@ -1,6 +1,7 @@ package htnoml import ( + "bytes" "os" "testing" @@ -19,12 +20,13 @@ func TestParser(t *testing.T) { if err != nil { t.Fatal(err) } - html := p.ToHTML() + w := new(bytes.Buffer) + p.ToHTML(w) expected, err := os.ReadFile("fixtures/basic.html") if err != nil { t.Fatal(err) } - if diff := cmp.Diff(string(expected), html); diff != "" { + if diff := cmp.Diff(string(expected), w.String()); diff != "" { t.Errorf("ToHTML() mismatch (-want +got):\n%s", diff) } })