aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Voss <mail@thomasvoss.com> 2023-09-10 19:38:25 +0200
committerThomas Voss <mail@thomasvoss.com> 2023-09-10 19:38:25 +0200
commit3521bff39c39ffc35c6b0cb65cac51591d51c8b9 (patch)
tree8d37fe112505ce13bdc74bf6e923d498969b8575
parent5cf8e6e4ea355dbbdfaca5998088704a43e0ceab (diff)
Rework some of the formatting- and parsing code
-rw-r--r--formatter/formatter.go167
-rw-r--r--main.go2
-rw-r--r--parser/parser.go12
3 files changed, 95 insertions, 86 deletions
diff --git a/formatter/formatter.go b/formatter/formatter.go
index 738581d..6524747 100644
--- a/formatter/formatter.go
+++ b/formatter/formatter.go
@@ -2,113 +2,122 @@ package formatter
import (
"fmt"
+ "slices"
"unicode"
"git.thomasvoss.com/gsp/parser"
)
-var xml = false
-
-var stringEscapes = map[rune]string{
- '"': "&quot;",
- '&': "&amp;",
- '<': "&lt;",
-}
-
-func PrintHtml(ast parser.AstNode) {
- if ast.Type == parser.Text {
- fmt.Print(ast.Text)
- return
+var (
+ attrValueEscapes = map[rune]string{
+ '"': "&quot;",
+ '&': "&amp;",
+ '<': "&lt;",
}
-
- if ast.Type == parser.DocType || ast.Type == parser.XmlDocType {
- if ast.Type == parser.DocType {
- fmt.Print("<!DOCTYPE")
- } else {
- xml = true
- fmt.Print("<?xml")
- }
-
- for _, a := range ast.Attrs {
- printAttr(a)
- }
-
- if ast.Type == parser.XmlDocType {
- fmt.Print("?")
- }
- fmt.Print(">")
+ stringEscapes = map[rune]string {
+ '"': "&quot;",
+ '&': "&amp;",
+ '<': "&lt;",
+ '>': "&gt;",
+ '\'': "&apos;",
}
+)
- if ast.Type == parser.Normal {
+func PrintAst(ast parser.AstNode) {
+ switch ast.Type {
+ case parser.Text:
+ printText(ast.Text)
+ case parser.DocType:
+ printDocType(ast)
+ case parser.Normal:
fmt.Printf("<%s", ast.Text)
+ printAttrs(ast.Attrs)
- // Classes are grouped together with ‘class="…"’, so we need
- // special handling.
- classes := []string{}
- notClasses := []parser.Attr{}
-
- for _, a := range ast.Attrs {
- if a.Key == "class" {
- classes = append(classes, a.Value)
+ if len(ast.Children) == 0 {
+ if parser.Xml {
+ fmt.Print("/>")
} else {
- notClasses = append(notClasses, a)
+ fmt.Print(">")
}
- }
-
- if len(classes) > 0 {
- fmt.Printf(" class=\"%s", classes[0])
- for _, c := range classes[1:] {
- fmt.Printf(" %s", c)
- }
- fmt.Print("\"")
- }
-
- for _, a := range notClasses {
- printAttr(a)
- }
-
- if xml && len(ast.Children) == 0 {
- fmt.Print("/>")
} else {
fmt.Print(">")
+ printChildren(ast.Children)
+ fmt.Printf("</%s>", ast.Text)
}
+ case parser.Tagless:
+ printChildren(ast.Children)
}
+}
- if len(ast.Children) == 0 {
- return
- }
-
- for i, n := range ast.Children {
- if n.Type == parser.Text {
- if i == 0 {
- n.Text = trimLeftSpaces(n.Text)
+func printAttrs(attrs []parser.Attr) {
+ classes := attrs
+ classes = slices.DeleteFunc(classes, func (a parser.Attr) bool {
+ return a.Key != "class"
+ })
+ attrs = slices.DeleteFunc(attrs, func (a parser.Attr) bool {
+ return a.Key == "class"
+ })
+
+ if len(classes) > 0 {
+ fmt.Print(" class=\"")
+ for i, a := range classes {
+ fmt.Print(a.Value)
+ if i != len(classes) - 1 {
+ fmt.Print(" ")
+ } else {
+ fmt.Print("\"")
}
+ }
+ }
- if i == len(ast.Children)-1 {
- n.Text = trimRightSpaces(n.Text)
+ for _, a := range attrs {
+ fmt.Printf(" %s", a.Key)
+ if a.Value != "" {
+ fmt.Print("=\"")
+ for _, r := range a.Value {
+ if v, ok := attrValueEscapes[r]; ok {
+ fmt.Print(v)
+ } else {
+ fmt.Printf("%c", r)
+ }
}
+ fmt.Print("\"")
}
+ }
+}
- PrintHtml(n)
+func printDocType(node parser.AstNode) {
+ if parser.Xml {
+ fmt.Print("<?xml")
+ printAttrs(node.Attrs)
+ fmt.Print("?>")
+ } else {
+ fmt.Print("<!DOCTYPE")
+ printAttrs(node.Attrs)
+ fmt.Print(">")
}
+}
- if ast.Type == parser.Normal {
- fmt.Printf("</%s>", ast.Text)
+func printText(s string) {
+ for _, r := range s {
+ if v, ok := stringEscapes[r]; ok {
+ fmt.Print(v)
+ } else {
+ fmt.Printf("%c", r)
+ }
}
}
-func printAttr(a parser.Attr) {
- fmt.Printf(" %s", a.Key)
- if a.Value != "" {
- fmt.Print("=\"")
- for _, r := range a.Value {
- if v, ok := stringEscapes[r]; ok {
- fmt.Print(v)
- } else {
- fmt.Printf("%c", r)
- }
+func printChildren(nodes []parser.AstNode) {
+ for i, n := range nodes {
+ if i == 0 && n.Type == parser.Text {
+ n.Text = trimLeftSpaces(n.Text)
}
- fmt.Print("\"")
+ if i == len(nodes) - 1 && n.Type == parser.Text {
+ n.Text = trimRightSpaces(n.Text)
+ }
+
+ PrintAst(n)
}
}
diff --git a/main.go b/main.go
index 1c94efa..8351b78 100644
--- a/main.go
+++ b/main.go
@@ -37,7 +37,7 @@ func process(filename string) {
die(err)
}
- formatter.PrintHtml(ast)
+ formatter.PrintAst(ast)
fmt.Print("\n")
}
diff --git a/parser/parser.go b/parser/parser.go
index 1101e65..946cc97 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -16,9 +16,10 @@ const (
Normal
Tagless
Text
- XmlDocType
)
+var Xml = false
+
type Attr struct {
Key string
Value string
@@ -62,10 +63,11 @@ func (reader *reader) parseDocType() (AstNode, error, bool) {
}
switch r {
+ case '?':
+ Xml = true
+ fallthrough
case '!':
doctype.Type = DocType
- case '?':
- doctype.Type = XmlDocType
default:
return AstNode{}, reader.unreadRune(), false
}
@@ -262,9 +264,7 @@ loop:
switch r {
case '{':
break loop
- case '.':
- fallthrough
- case '#':
+ case '.', '#':
sym := r
// Skip ‘sym’