package walk import ( "math" "fmt" ) type AtomType int64 const ( AtomNull AtomType = iota AtomBool AtomNumber AtomTerminal AtomStringTerminal AtomStringRune ) type Atom struct { Typ AtomType data uint64 } func NewAtomNull() Atom { return Atom { Typ: AtomNull, data: 0, } } func NewAtomBool(v bool) Atom { if v { return Atom { Typ: AtomBool, data: 1, } } else { return Atom { Typ: AtomBool, data: 0, } } } func (v Atom) Bool() bool { if v.Typ != AtomBool { panic("Tried to use non-bool as bool") } return v.data == 1 } func NewAtomNumber(v float64) Atom { return Atom { Typ: AtomNumber, data: math.Float64bits(v), } } func (v Atom) Number() float64 { if v.Typ != AtomNumber { panic("Tried to use non-number as number") } return math.Float64frombits(v.data) } func NewAtomTerminal(v ValueTerminal) Atom { return Atom { Typ: AtomTerminal, data: uint64(v), } } func (v Atom) Terminal() ValueTerminal { if v.Typ != AtomTerminal { panic("Tried to use non-terminal as terminal") } return ValueTerminal(v.data) } func NewAtomStringTerminal() Atom { return Atom { Typ: AtomStringTerminal, data: 0, } } func NewAtomStringRune(v rune) Atom { return Atom { Typ: AtomStringRune, data: uint64(v), } } func (v Atom) StringRune() rune { if v.Typ != AtomStringRune { panic("Tried to use non-stringrune as stringrune") } return rune(v.data) } func (v Atom) String() string { switch v.Typ { case AtomNull: return "null" case AtomBool: if v.data == 0 { return "false" } return "true" case AtomNumber: return fmt.Sprintf("%v", math.Float64frombits(v.data)) case AtomTerminal: switch ValueTerminal(v.data) { case MapBegin: return "{" case MapEnd: return "}" case ArrayBegin: return "[" case ArrayEnd: return "]" default: panic("Invalid terminal atom") } case AtomStringTerminal: return "\"" case AtomStringRune: return string(rune(v.data)) default: panic("Invalid atom type") } }