aboutsummaryrefslogtreecommitdiff
path: root/parser
diff options
context:
space:
mode:
authorThomas Voss <mail@thomasvoss.com> 2023-09-09 23:03:57 +0200
committerThomas Voss <mail@thomasvoss.com> 2023-09-09 23:03:57 +0200
commit8fd0f17b7bcca4725046ca82d3193b9986fe1faa (patch)
treee0ab1680bba6bee3eb2cc653d88111b153fd396f /parser
parent96aef4c6a473e380b027a7b9db87cbbbcbae1cba (diff)
Add primitive support for doctypes and XML
Diffstat (limited to 'parser')
-rw-r--r--parser/parser.go65
1 files changed, 63 insertions, 2 deletions
diff --git a/parser/parser.go b/parser/parser.go
index 9e0a329..1101e65 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -12,9 +12,11 @@ import (
type nodeType uint
const (
- Normal nodeType = iota
+ DocType nodeType = iota
+ Normal
Tagless
Text
+ XmlDocType
)
type Attr struct {
@@ -31,7 +33,66 @@ type AstNode struct {
func ParseFile(file *os.File) (AstNode, error) {
r := reader{r: bufio.NewReader(file)}
- return r.parseNode()
+ return r.parseDocument()
+}
+
+func (reader *reader) parseDocument() (AstNode, error) {
+ document := AstNode{Type: Tagless}
+ if doctype, err, exists := reader.parseDocType(); err != nil {
+ return AstNode{}, err
+ } else if exists {
+ document.Children = append(document.Children, doctype)
+ }
+
+ if node, err := reader.parseNode(); err != nil {
+ return AstNode{}, err
+ } else {
+ document.Children = append(document.Children, node)
+ }
+
+ return document, nil
+}
+
+func (reader *reader) parseDocType() (AstNode, error, bool) {
+ doctype := AstNode{}
+
+ r, err := reader.readNonSpaceRune()
+ if err != nil {
+ return AstNode{}, err, false
+ }
+
+ switch r {
+ case '!':
+ doctype.Type = DocType
+ case '?':
+ doctype.Type = XmlDocType
+ default:
+ return AstNode{}, reader.unreadRune(), false
+ }
+
+ if attrs, err := reader.parseAttrs(); err != nil {
+ return AstNode{}, err, false
+ } else {
+ doctype.Attrs = attrs
+ }
+
+ // The above call to reader.parseAttrs() guarantees that we have the ā€˜{ā€™
+ // token.
+ if _, err := reader.readRune(); err != nil {
+ return AstNode{}, err, false
+ }
+
+ if r, err := reader.readNonSpaceRune(); err != nil {
+ return AstNode{}, err, false
+ } else if r != '}' {
+ return AstNode{}, invalidSyntax{
+ pos: reader.pos,
+ expected: "empty body (doctypes must have empty bodies)",
+ found: fmt.Sprintf("ā€˜%cā€™\n", r),
+ }, false
+ }
+
+ return doctype, nil, true
}
func (reader *reader) parseNode() (AstNode, error) {