package bibtex import ( "bytes" "strconv" "strings" ) type Value interface { Marshal() string String() string } type BraceLiteral string type StringLiteral string type NumberLiteral int type VarLiteral struct { Name string Value *Value } type LiteralList []Value func (l BraceLiteral) String() string { return string(l) } func (l BraceLiteral) Marshal() string { return "{" + string(l) + "}" } func (l StringLiteral) String() string { return string(l) } func (l StringLiteral) Marshal() string { return "\"" + string(l) + "\"" } func (l NumberLiteral) Marshal() string { return strconv.Itoa(int(l)) } func (l NumberLiteral) String() string { return strconv.Itoa(int(l)) } func (l VarLiteral) String() string { return (*l.Value).String() } func (l VarLiteral) Marshal() string { return l.Name } func (l LiteralList) String() string { var buf bytes.Buffer for _, lit := range l { buf.WriteString(lit.String()) } return buf.String() } func (l LiteralList) Marshal() string { res := make([]string, len(l)) for i, lit := range l { res[i] = lit.Marshal() } return strings.Join(res, " # ") } func flatten(v Value) Value { switch t := v.(type) { case LiteralList: if len(t) == 1 { return flatten(t[0]) } else { return BraceLiteral(t.String()) } case VarLiteral: return flatten(*t.Value) default: return v } } type Entry struct { Type string `json:"type"` Key string `json:"key"` Fields map[string]Value `json:"fields"` FNames []string `json:"-"` } func (e Entry) Marshal() string { var buf bytes.Buffer temp := make([]string, len(e.FNames)) open, close := "{", "}" if strings.Contains(e.Key, "}") { open, close = "(", ")" } buf.WriteString("@" + e.Type + open + e.Key + ",") if len(e.FNames) == 0 { buf.WriteString(close) return buf.String() } else { buf.WriteString("\n\t") } for i, field := range e.FNames { temp[i] = field + " = " + e.Fields[field].Marshal() } buf.WriteString(strings.Join(temp, ",\n\t")) buf.WriteString("\n" + close) return buf.String() } type Database struct { SNames []string `json:"-"` Strings map[string]Value `json:"strings,omitempty"` Entries map[string]Entry `json:"entries"` Preamble Value `json:"preamble,omitempty"` EKeys []string `json:"-"` CrossRefs map[string]int `json:"crossrefs,omitempty"` } func (d *Database) UnTex() { for _, entry := range d.Entries { for field, value := range entry.Fields { switch v := value.(type) { case BraceLiteral: entry.Fields[field] = BraceLiteral(UnTex(v.String())) case StringLiteral: entry.Fields[field] = StringLiteral(UnTex(v.String())) case Names: v.untex() } } } } func (d *Database) Flatten() { for _, entry := range d.Entries { for field, value := range entry.Fields { entry.Fields[field] = flatten(value) } } } func (d *Database) SplitNames() { for _, entry := range d.Entries { for field, value := range entry.Fields { if field == "author" || field == "editor" { entry.Fields[field] = SplitNames(value.String()) } } } } func (d *Database) Resolve() { var key string var ref Entry for _, entry := range d.Entries { for field, value := range entry.Fields { if field != "crossref" { continue } key = strings.ToLower(value.String()) ref = d.Entries[key] for f, v := range ref.Fields { if _, in := entry.Fields[f]; !in { entry.Fields[f] = v entry.FNames = append(entry.FNames, f) } } } } }