diff options
Diffstat (limited to 'parser.go')
| -rw-r--r-- | parser.go | 152 |
1 files changed, 80 insertions, 72 deletions
@@ -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 } |
