summaryrefslogtreecommitdiffstats
path: root/parser.go
diff options
context:
space:
mode:
Diffstat (limited to 'parser.go')
-rw-r--r--parser.go152
1 files changed, 80 insertions, 72 deletions
diff --git a/parser.go b/parser.go
index 25830c8..b4a3ada 100644
--- a/parser.go
+++ b/parser.go
@@ -7,21 +7,11 @@ import (
"io"
"log"
"os"
+ "strconv"
"strings"
"unicode"
)
-type Entry struct {
- typ string
- key string
- fields map[string]string
- line int
-}
-
-func NewEntry(typ string) *Entry {
- return &Entry{typ: typ, fields: make(map[string]string)}
-}
-
const eof = rune(0)
type ParseError struct {
@@ -35,38 +25,47 @@ func (e ParseError) Error() string {
return fmt.Sprintf("%s:%d:%d error: %s", e.file, e.line, e.col, e.msg)
}
-type Parser struct {
+type parser struct {
*bufio.Reader
+ *Database
fname string
- strings map[string]string
- entries map[string]*Entry
lineno int
colno int
- preamble string
lastread rune
}
-func NewParser(f *os.File) *Parser {
+func NewParser(r io.Reader) *parser {
strings := map[string]string{
"jan": "January", "feb": "February", "mar": "March", "apr": "April",
"may": "May", "jun": "June", "jul": "July", "aug": "August",
"sep": "September", "oct": "October", "nov": "November",
"dec": "December",
}
- return &Parser{Reader: bufio.NewReader(f), strings: strings,
- entries: make(map[string]*Entry), lineno: 1, colno: 0,
- fname: f.Name()}
+ var name string
+ switch v := r.(type) {
+ case *os.File:
+ name = v.Name()
+ default:
+ name = ""
+ }
+ res := make(map[string]Value)
+ for key, s := range strings {
+ res[key] = []Literal{StringLiteral(s)}
+ }
+ db := &Database{Strings: res, Entries: make(map[string]Entry)}
+ return &parser{Reader: bufio.NewReader(r), lineno: 1, colno: 0,
+ fname: name, Database: db}
}
-func (p *Parser) NewError(msg string) error {
+func (p *parser) NewError(msg string) error {
return ParseError{msg: msg, line: p.lineno, col: p.colno, file: p.fname}
}
-func (p *Parser) Warning(msg string) {
+func (p *parser) Warning(msg string) {
log.Printf("%s:%d:%d warning: %s", p.fname, p.lineno, p.colno, msg)
}
-func (p *Parser) readUnsafe() rune {
+func (p *parser) readUnsafe() rune {
ch, _, _ := p.ReadRune()
if ch == '\n' {
p.lineno += 1
@@ -77,7 +76,7 @@ func (p *Parser) readUnsafe() rune {
return ch
}
-func (p *Parser) read() rune {
+func (p *parser) read() rune {
ch, _, err := p.ReadRune()
if err == io.EOF {
panic(err)
@@ -91,7 +90,7 @@ func (p *Parser) read() rune {
return ch
}
-func (p *Parser) peek() rune {
+func (p *parser) peek() rune {
ch, _, err := p.ReadRune()
p.UnreadRune()
if err == io.EOF {
@@ -100,7 +99,7 @@ func (p *Parser) peek() rune {
return ch
}
-func (p *Parser) unread() {
+func (p *parser) unread() {
p.colno -= 1
if p.lastread == '\n' {
p.lineno -= 1
@@ -108,7 +107,7 @@ func (p *Parser) unread() {
p.UnreadRune()
}
-func (p *Parser) readToken(s string) string {
+func (p *parser) readToken(s string) string {
var buf bytes.Buffer
var ch rune
@@ -126,7 +125,7 @@ func (p *Parser) readToken(s string) string {
const IllegalIdChars = "{}()=\",#%"
-func (p *Parser) readIdentifier() string {
+func (p *parser) readIdentifier() string {
var buf bytes.Buffer
var ch rune
if ch = p.read(); unicode.IsDigit(ch) {
@@ -152,7 +151,7 @@ func (p *Parser) readIdentifier() string {
return buf.String()
}
-func (p *Parser) eatSpace() {
+func (p *parser) eatSpace() {
var ch rune
for {
ch = p.read()
@@ -163,7 +162,7 @@ func (p *Parser) eatSpace() {
}
}
-func (p *Parser) readBraceLiteral() string {
+func (p *parser) readBraceLiteral() BraceLiteral {
var buf bytes.Buffer
ch := p.read()
if ch != '{' {
@@ -183,10 +182,10 @@ func (p *Parser) readBraceLiteral() string {
}
buf.WriteRune(ch)
}
- return buf.String()
+ return BraceLiteral(buf.String())
}
-func (p *Parser) readStringLiteral() string {
+func (p *parser) readStringLiteral() StringLiteral {
var buf bytes.Buffer
ch := p.read()
if ch != '"' {
@@ -208,10 +207,10 @@ func (p *Parser) readStringLiteral() string {
if blevel != 0 {
panic(p.NewError("Unbalanced '{'"))
}
- return buf.String()
+ return StringLiteral(buf.String())
}
-func (p *Parser) readNumber() string {
+func (p *parser) readNumber() NumberLiteral {
var buf bytes.Buffer
var ch rune
for {
@@ -223,10 +222,14 @@ func (p *Parser) readNumber() string {
buf.WriteRune(ch)
}
}
- return buf.String()
+ n, err := strconv.Atoi(buf.String())
+ if err != nil {
+ p.Warning(fmt.Sprintf("Couldn't parse number %q", buf.String()))
+ }
+ return NumberLiteral(n)
}
-func (p *Parser) readLiteral() string {
+func (p *parser) readLiteral() Literal {
if ch := p.peek(); ch == '{' {
return p.readBraceLiteral()
} else if ch == '"' {
@@ -234,39 +237,36 @@ func (p *Parser) readLiteral() string {
} else if unicode.IsDigit(ch) {
return p.readNumber()
} else {
- id := p.readIdentifier()
+ id := strings.ToLower(p.readIdentifier())
if id == "" {
panic(p.NewError("Expected an identifier"))
}
- if str, in := p.strings[strings.ToLower(id)]; in {
- return str
+ if v, in := p.Strings[id]; in {
+ return VariableLiteral{id, &v}
} else {
p.Warning(fmt.Sprintf("Unknown string %q", id))
- return ""
+ return VariableLiteral{id, &v}
}
}
}
-func (p *Parser) readValue() string {
- var buf bytes.Buffer
+func (p *parser) readValue() (res Value) {
var ch rune
- value := p.readLiteral()
- buf.WriteString(value)
+ res = append(res, p.readLiteral())
for {
p.eatSpace()
if ch = p.read(); ch == '#' {
p.eatSpace()
- value := p.readLiteral()
- buf.WriteString(value)
+ res = append(res, p.readLiteral())
} else {
p.unread()
break
}
}
- return buf.String()
+ return
}
-func (p *Parser) readIdValue() (string, string) {
+func (p *parser) readIdValue() (string, Value) {
p.eatSpace()
id := p.readIdentifier()
if id == "" {
@@ -282,7 +282,7 @@ func (p *Parser) readIdValue() (string, string) {
return id, value
}
-func (p *Parser) readOpen() rune {
+func (p *parser) readOpen() rune {
p.eatSpace()
if ch := p.read(); ch == '(' {
return ')'
@@ -294,11 +294,10 @@ func (p *Parser) readOpen() rune {
}
}
-func (p *Parser) readPreamble() {
+func (p *parser) readPreamble() {
close := p.readOpen()
p.eatSpace()
- val := p.readValue()
- p.preamble += val
+ p.Preamble = append(p.Preamble, p.readValue())
p.eatSpace()
if ch := p.read(); ch != close {
p.unread()
@@ -306,14 +305,16 @@ func (p *Parser) readPreamble() {
}
}
-func (p *Parser) readString() {
+func (p *parser) readString() {
close := p.readOpen()
p.eatSpace()
id, value := p.readIdValue()
- if _, in := p.strings[strings.ToLower(id)]; in {
+ id = strings.ToLower(id)
+ if _, in := p.Strings[id]; in {
p.Warning(fmt.Sprintf("String %q already defined, ignoring", id))
} else {
- p.strings[strings.ToLower(id)] = value
+ p.Strings[id] = value
+ p.SNames = append(p.SNames, id)
}
p.eatSpace()
if ch := p.read(); ch != close {
@@ -322,16 +323,19 @@ func (p *Parser) readString() {
}
}
-func (p *Parser) readEntry(entry *Entry) {
+func (p *parser) readEntry(t string) {
close := p.readOpen()
p.eatSpace()
+ var entry Entry
key := p.readToken("," + string(close))
- entry.key = key
+ entry.Key = key
+ entry.Type = t
+ entry.Fields = make(map[string]Value)
+ key = strings.ToLower(key)
- if _, in := p.entries[strings.ToLower(key)]; in {
+ if _, in := p.Entries[key]; in {
p.Warning(fmt.Sprintf("Entry %q already defined, ignoring", key))
- } else {
- p.entries[strings.ToLower(key)] = entry
+ return
}
for {
@@ -345,11 +349,13 @@ func (p *Parser) readEntry(entry *Entry) {
} else {
p.unread()
id, value := p.readIdValue()
- if _, in := entry.fields[strings.ToLower(id)]; in {
+ id = strings.ToLower(id)
+ if _, in := entry.Fields[id]; in {
p.Warning(fmt.Sprintf("Field %q already defined, ignoring",
id))
} else {
- entry.fields[strings.ToLower(id)] = value
+ entry.Fields[id] = value
+ entry.FNames = append(entry.FNames, id)
}
}
} else {
@@ -357,31 +363,31 @@ func (p *Parser) readEntry(entry *Entry) {
panic(p.NewError(fmt.Sprintf("Expected ',' or %q", close)))
}
}
+ p.Entries[key] = entry
+ p.EKeys = append(p.EKeys, key)
}
-func (p *Parser) readDeclaration() (err error) {
+func (p *parser) readDeclaration() (err error) {
defer errorHandler(&err, p)
- typ := p.readIdentifier()
- if typ == "" {
+ t := p.readIdentifier()
+ if t == "" {
err = p.NewError("Expected entry type")
}
- typ = strings.ToLower(typ)
- switch typ {
+ t = strings.ToLower(t)
+ switch t {
case "string":
p.readString()
case "preamble":
p.readPreamble()
case "comment":
default:
- entry := NewEntry(typ)
- entry.line = p.lineno
- p.readEntry(entry)
+ p.readEntry(t)
}
return
}
-func errorHandler(errp *error, p *Parser) {
+func errorHandler(errp *error, p *parser) {
if e := recover(); e != nil {
switch e.(type) {
case ParseError:
@@ -398,9 +404,11 @@ func errorHandler(errp *error, p *Parser) {
}
}
-func (p *Parser) Parse(strict bool) (err error) {
+func Parse(r io.Reader, strict bool) (db *Database, err error) {
+ p := NewParser(r)
var ch rune
+
for {
ch = p.readUnsafe()
switch ch {
@@ -415,8 +423,8 @@ func (p *Parser) Parse(strict bool) (err error) {
}
}
case eof:
+ db = p.Database
return
}
}
- return
}