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