// TODO(rsc):
// Test error handling.
-// Expose parser line number in errors.
import (
"bufio"
)
// A SyntaxError represents a syntax error in the XML input stream.
-type SyntaxError string
+type SyntaxError struct {
+ Msg string
+ Line int
+}
-func (e SyntaxError) String() string { return "XML syntax error: " + string(e) }
+func (e *SyntaxError) String() string {
+ return "XML syntax error on line " + strconv.Itoa(e.Line) + ": " + e.Msg
+}
// A Name represents an XML name (Local) annotated
// with a name space identifier (Space).
s.ok = ok
}
+// Creates a SyntaxError with the current line number.
+func (p *Parser) syntaxError(msg string) os.Error {
+ return &SyntaxError{Msg: msg, Line: p.line}
+}
+
// Record that we are ending an element with the given name.
// The name must match the record at the top of the stack,
// which must be a pushElement record.
name := t.Name
switch {
case s == nil || s.kind != stkStart:
- p.err = SyntaxError("unexpected end element </" + name.Local + ">")
+ p.err = p.syntaxError("unexpected end element </" + name.Local + ">")
return false
case s.name.Local != name.Local:
if !p.Strict {
t.Name = s.name
return true
}
- p.err = SyntaxError("element <" + s.name.Local + "> closed by </" + name.Local + ">")
+ p.err = p.syntaxError("element <" + s.name.Local + "> closed by </" + name.Local + ">")
return false
case s.name.Space != name.Space:
- p.err = SyntaxError("element <" + s.name.Local + "> in space " + s.name.Space +
+ p.err = p.syntaxError("element <" + s.name.Local + "> in space " + s.name.Space +
"closed by </" + name.Local + "> in space " + name.Space)
return false
}
var name Name
if name, ok = p.nsname(); !ok {
if p.err == nil {
- p.err = SyntaxError("expected element name after </")
+ p.err = p.syntaxError("expected element name after </")
}
return nil, p.err
}
return nil, p.err
}
if b != '>' {
- p.err = SyntaxError("invalid characters between </" + name.Local + " and >")
+ p.err = p.syntaxError("invalid characters between </" + name.Local + " and >")
return nil, p.err
}
return EndElement{name}, nil
var target string
if target, ok = p.name(); !ok {
if p.err == nil {
- p.err = SyntaxError("expected target name after <?")
+ p.err = p.syntaxError("expected target name after <?")
}
return nil, p.err
}
return nil, p.err
}
if b != '-' {
- p.err = SyntaxError("invalid sequence <!- not part of <!--")
+ p.err = p.syntaxError("invalid sequence <!- not part of <!--")
return nil, p.err
}
// Look for terminator.
return nil, p.err
}
if b != "CDATA["[i] {
- p.err = SyntaxError("invalid <![ sequence")
+ p.err = p.syntaxError("invalid <![ sequence")
return nil, p.err
}
}
)
if name, ok = p.nsname(); !ok {
if p.err == nil {
- p.err = SyntaxError("expected element name after <")
+ p.err = p.syntaxError("expected element name after <")
}
return nil, p.err
}
return nil, p.err
}
if b != '>' {
- p.err = SyntaxError("expected /> in element")
+ p.err = p.syntaxError("expected /> in element")
return nil, p.err
}
break
a := &attr[n]
if a.Name, ok = p.nsname(); !ok {
if p.err == nil {
- p.err = SyntaxError("expected attribute name in element")
+ p.err = p.syntaxError("expected attribute name in element")
}
return nil, p.err
}
return nil, p.err
}
if b != '=' {
- p.err = SyntaxError("attribute name without = in element")
+ p.err = p.syntaxError("attribute name without = in element")
return nil, p.err
}
p.space()
}
// Handle unquoted attribute values for strict parsers
if p.Strict {
- p.err = SyntaxError("unquoted or missing attribute value in element")
+ p.err = p.syntaxError("unquoted or missing attribute value in element")
return nil
}
// Handle unquoted attribute values for unstrict parsers
func (p *Parser) mustgetc() (b byte, ok bool) {
if b, ok = p.getc(); !ok {
if p.err == os.EOF {
- p.err = SyntaxError("unexpected EOF")
+ p.err = p.syntaxError("unexpected EOF")
}
}
return
trunc = 2
break Input
}
- p.err = SyntaxError("unescaped ]]> not in CDATA section")
+ p.err = p.syntaxError("unescaped ]]> not in CDATA section")
return nil
}
// Stop reading text if we see a <.
if b == '<' && !cdata {
if quote >= 0 {
- p.err = SyntaxError("unescaped < inside quoted string")
+ p.err = p.syntaxError("unescaped < inside quoted string")
return nil
}
p.ungetc('<')
p.tmp[i], p.err = p.r.ReadByte()
if p.err != nil {
if p.err == os.EOF {
- p.err = SyntaxError("unexpected EOF")
+ p.err = p.syntaxError("unexpected EOF")
}
return nil
}
p.buf.Write(p.tmp[0:i])
continue Input
}
- p.err = SyntaxError("character entity expression &" + s + "... too long")
+ p.err = p.syntaxError("character entity expression &" + s + "... too long")
return nil
}
var haveText bool
p.buf.Write(p.tmp[0:i])
continue Input
}
- p.err = SyntaxError("invalid character entity &" + s + ";")
+ p.err = p.syntaxError("invalid character entity &" + s + ";")
return nil
}
p.buf.Write([]byte(text))
s = p.buf.String()
for i, c := range s {
if !unicode.Is(first, c) && (i == 0 || !unicode.Is(second, c)) {
- p.err = SyntaxError("invalid XML name: " + s)
+ p.err = p.syntaxError("invalid XML name: " + s)
return "", false
}
}
var err os.Error
for _, err = p.Token(); err == nil; _, err = p.Token() {
}
- if _, ok := err.(SyntaxError); !ok {
+ if _, ok := err.(*SyntaxError); !ok {
t.Fatalf(`xmlInput "%s": expected SyntaxError not received`, xmlInput[i])
}
}
p := NewParser(StringReader(data))
p.Strict = false
token, err := p.Token()
- if _, ok := err.(SyntaxError); ok {
+ if _, ok := err.(*SyntaxError); ok {
t.Errorf("Unexpected error: %v", err)
}
if token.(StartElement).Name.Local != "tag" {
t.Error("CopyToken(CharData) uses same buffer.")
}
}
+
+func TestSyntaxErrorLineNum(t *testing.T) {
+ testInput := "<P>Foo<P>\n\n<P>Bar</>\n"
+ p := NewParser(StringReader(testInput))
+ var err os.Error
+ for _, err = p.Token(); err == nil; _, err = p.Token() {
+ }
+ synerr, ok := err.(*SyntaxError)
+ if !ok {
+ t.Error("Expected SyntaxError.")
+ }
+ if synerr.Line != 3 {
+ t.Error("SyntaxError didn't have correct line number.")
+ }
+}