diff options
-rw-r--r-- | formatter/formatter.go | 167 | ||||
-rw-r--r-- | main.go | 2 | ||||
-rw-r--r-- | parser/parser.go | 12 |
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{ - '"': """, - '&': "&", - '<': "<", -} - -func PrintHtml(ast parser.AstNode) { - if ast.Type == parser.Text { - fmt.Print(ast.Text) - return +var ( + attrValueEscapes = map[rune]string{ + '"': """, + '&': "&", + '<': "<", } - - 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 { + '"': """, + '&': "&", + '<': "<", + '>': ">", + '\'': "'", } +) - 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) } } @@ -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’ |