summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThibaut Horel <thibaut.horel@gmail.com>2016-02-21 22:16:43 -0500
committerThibaut Horel <thibaut.horel@gmail.com>2016-02-21 22:16:43 -0500
commit86cddf333ee9a80a4ea0a68814face6a32ced48b (patch)
tree5be4e3ea46bce6171740b9438bae819c7d0bf24a
parentac7a1a413b9566e0b052248502206eb3d6f8941c (diff)
downloadbibtex-86cddf333ee9a80a4ea0a68814face6a32ced48b.tar.gz
Use panic/defer all the way through to simplify error bubbling
-rw-r--r--parser.go192
1 files changed, 77 insertions, 115 deletions
diff --git a/parser.go b/parser.go
index 35999e1..70903fa 100644
--- a/parser.go
+++ b/parser.go
@@ -31,7 +31,7 @@ type ParseError struct {
file string
}
-func (e *ParseError) Error() string {
+func (e ParseError) Error() string {
return fmt.Sprintf("%s:%d:%d error: %s", e.file, e.line, e.col, e.msg)
}
@@ -59,7 +59,7 @@ func NewParser(f *os.File) *Parser {
}
func (p *Parser) NewError(msg string) error {
- return &ParseError{msg: msg, line: p.lineno, col: p.colno, file: p.fname}
+ return ParseError{msg: msg, line: p.lineno, col: p.colno, file: p.fname}
}
func (p *Parser) Warning(msg string) {
@@ -163,12 +163,12 @@ func (p *Parser) eatSpace() {
}
}
-func (p *Parser) readBraceLiteral() (string, error) {
+func (p *Parser) readBraceLiteral() string {
var buf bytes.Buffer
ch := p.read()
if ch != '{' {
p.unread()
- return "", p.NewError("Expected '{'")
+ panic(p.NewError("Expected '{'"))
}
blevel := 1
for {
@@ -183,15 +183,15 @@ func (p *Parser) readBraceLiteral() (string, error) {
}
buf.WriteRune(ch)
}
- return buf.String(), nil
+ return buf.String()
}
-func (p *Parser) readStringLiteral() (string, error) {
+func (p *Parser) readStringLiteral() string {
var buf bytes.Buffer
ch := p.read()
if ch != '"' {
p.unread()
- return "", p.NewError("Expected '\"'")
+ panic(p.NewError("Expected '\"'"))
}
blevel := 0
for blevel >= 0 {
@@ -206,12 +206,12 @@ func (p *Parser) readStringLiteral() (string, error) {
buf.WriteRune(ch)
}
if blevel != 0 {
- return "", p.NewError("Unbalanced '{'")
+ panic(p.NewError("Unbalanced '{'"))
}
- return buf.String(), nil
+ return buf.String()
}
-func (p *Parser) readNumber() (string, error) {
+func (p *Parser) readNumber() string {
var buf bytes.Buffer
var ch rune
for {
@@ -223,10 +223,10 @@ func (p *Parser) readNumber() (string, error) {
buf.WriteRune(ch)
}
}
- return buf.String(), nil
+ return buf.String()
}
-func (p *Parser) readLiteral() (string, error) {
+func (p *Parser) readLiteral() string {
if ch := p.peek(); ch == '{' {
return p.readBraceLiteral()
} else if ch == '"' {
@@ -236,102 +236,80 @@ func (p *Parser) readLiteral() (string, error) {
} else {
id := p.readIdentifier()
if id == "" {
- return "", p.NewError("Expected an identifier")
+ panic(p.NewError("Expected an identifier"))
}
if str, in := p.strings[strings.ToLower(id)]; in {
- return str, nil
+ return str
} else {
p.Warning(fmt.Sprintf("Unknown string %q", id))
- return "", nil
+ return ""
}
}
}
-func (p *Parser) readValue() (string, error) {
+func (p *Parser) readValue() string {
var buf bytes.Buffer
var ch rune
- value, err := p.readLiteral()
- if err != nil {
- return "", err
- }
+ value := p.readLiteral()
buf.WriteString(value)
for {
p.eatSpace()
if ch = p.read(); ch == '#' {
p.eatSpace()
- value, err := p.readLiteral()
- if err != nil {
- return "", err
- }
+ value := p.readLiteral()
buf.WriteString(value)
} else {
p.unread()
break
}
}
- return buf.String(), nil
+ return buf.String()
}
-func (p *Parser) readIdValue() (string, string, error) {
+func (p *Parser) readIdValue() (string, string) {
p.eatSpace()
id := p.readIdentifier()
if id == "" {
- return "", "", p.NewError("Expected an identifier")
+ panic(p.NewError("Expected an identifier"))
}
p.eatSpace()
if ch := p.read(); ch != '=' {
p.unread()
- return "", "", p.NewError("Expected '='")
+ panic(p.NewError("Expected '='"))
}
p.eatSpace()
- value, err := p.readValue()
- if err != nil {
- return "", "", err
- }
- return id, value, nil
+ value := p.readValue()
+ return id, value
}
-func (p *Parser) readOpen() (rune, error) {
+func (p *Parser) readOpen() rune {
p.eatSpace()
if ch := p.read(); ch == '(' {
- return ')', nil
+ return ')'
} else if ch == '{' {
- return '}', nil
+ return '}'
} else {
p.unread()
- return ch, p.NewError("Expected '(' or '{'")
+ panic(p.NewError("Expected '(' or '{'"))
}
}
-func (p *Parser) readPreamble() error {
- close, err := p.readOpen()
- if err != nil {
- return err
- }
+func (p *Parser) readPreamble() {
+ close := p.readOpen()
p.eatSpace()
- val, err := p.readValue()
- if err != nil {
- return err
- }
+ val := p.readValue()
p.preamble += val
p.eatSpace()
if ch := p.read(); ch != close {
p.unread()
- return p.NewError(fmt.Sprintf("Expected %q", close))
+ panic(p.NewError(fmt.Sprintf("Expected %q", close)))
}
- return nil
}
-func (p *Parser) readString() error {
- close, err := p.readOpen()
- if err != nil {
- return err
- }
+func (p *Parser) readString() {
+ close := p.readOpen()
p.eatSpace()
- id, value, err := p.readIdValue()
- if err != nil {
- return err
- }
+ id, value := p.readIdValue()
if _, in := p.strings[strings.ToLower(id)]; in {
p.Warning(fmt.Sprintf("String %q already defined, ignoring", id))
} else {
@@ -340,16 +318,12 @@ func (p *Parser) readString() error {
p.eatSpace()
if ch := p.read(); ch != close {
p.unread()
- return p.NewError(fmt.Sprintf("Expected %q", close))
+ panic(p.NewError(fmt.Sprintf("Expected %q", close)))
}
- return nil
}
-func (p *Parser) readEntry(entry *Entry) error {
- close, err := p.readOpen()
- if err != nil {
- return err
- }
+func (p *Parser) readEntry(entry *Entry) {
+ close := p.readOpen()
p.eatSpace()
key := p.readToken("," + string(close))
entry.key = key
@@ -363,17 +337,14 @@ func (p *Parser) readEntry(entry *Entry) error {
for {
p.eatSpace()
if ch := p.read(); ch == close {
- return nil
+ break
} else if ch == ',' {
p.eatSpace()
if ch := p.read(); ch == close {
- return nil
+ break
} else {
p.unread()
- id, value, err := p.readIdValue()
- if err != nil {
- return err
- }
+ id, value := p.readIdValue()
if _, in := entry.fields[strings.ToLower(id)]; in {
p.Warning(fmt.Sprintf("Field %q already defined, ignoring",
id))
@@ -383,74 +354,65 @@ func (p *Parser) readEntry(entry *Entry) error {
}
} else {
p.unread()
- return p.NewError(fmt.Sprintf("Expected ',' or %q", close))
+ panic(p.NewError(fmt.Sprintf("Expected ',' or %q", close)))
}
}
- return nil
}
-func (p *Parser) readDeclaration() error {
+func (p *Parser) readDeclaration() {
typ := p.readIdentifier()
if typ == "" {
- return p.NewError("Expected entry type")
+ panic(p.NewError("Expected entry type"))
}
typ = strings.ToLower(typ)
- var err error
switch typ {
case "string":
- err = p.readString()
+ p.readString()
case "preamble":
- err = p.readPreamble()
+ p.readPreamble()
case "comment":
default:
entry := NewEntry(typ)
entry.line = p.lineno
- err = p.readEntry(entry)
+ p.readEntry(entry)
}
- return err
}
-func (p *Parser) Parse(strict bool) error {
-
- defer func() {
- if r := recover(); r != nil {
- p.Warning("Reached end of file while parsing.")
- }
- }()
-
- for ch := p.readUnsafe(); ch != eof; ch = p.readUnsafe() {
- if ch == '@' {
- p.eatSpace()
- err := p.readDeclaration()
- if err != nil {
- switch err.(type) {
- case *ParseError:
- if strict {
- return err
- } else {
- log.Println(err.Error())
- }
- default:
- return err
- }
+func errorHandler(errp *error, strict bool, p *Parser) {
+ if e := recover(); e != nil {
+ switch e.(type) {
+ case ParseError:
+ if strict {
+ *errp = e.(ParseError)
+ } else {
+ log.Println(e.(ParseError).Error())
}
+ case error:
+ if e == io.EOF {
+ p.Warning("Reached end of file while parsing.")
+ *errp = e.(error)
+ } else {
+ panic(e)
+ }
+ default:
+ panic(e)
}
}
- return nil
}
-func main() {
- log.SetFlags(0)
- log.SetOutput(os.Stderr)
- p := NewParser(os.Stdin)
- err := p.Parse(false)
- if err != nil {
- log.Fatal(err)
- }
- for key, entry := range p.entries {
- fmt.Println(key, entry.typ)
- for name, value := range entry.fields {
- fmt.Println("\t", name, value)
+func (p *Parser) Parse(strict bool) (err error) {
+ defer errorHandler(&err, strict, p)
+
+ var ch rune
+ for {
+ ch = p.readUnsafe()
+ switch ch {
+ case '@':
+ p.eatSpace()
+ p.readDeclaration()
+ case eof:
+ return
}
}
+ return
}