package walk import ( "fmt" "strings" ) type PathSegment interface {} type OutputList interface { outputList() } type OutputValueList []Value func (_ OutputValueList) outputList() {} type OutputRuneList []rune func (_ OutputRuneList) outputList() {} // Can be passed to .eat on a state type Edible interface { edible() } type RuneEdible rune func (_ RuneEdible) edible() {} type Terminal int const ( ArrayEnd Terminal = iota MapEnd StringEnd ) func (_ Terminal) edible() {} // Simple value // Has a literal type Scalar interface { Value scalar() } type Value interface { Edible value() Debug() string } type NullValue struct{} func (_ NullValue) value() {} func (_ NullValue) Debug() string { return "null" } func (_ NullValue) edible() {} func (_ NullValue) scalar() {} type BoolValue bool func (_ BoolValue) value() {} func (b BoolValue) Debug() string { if b { return "true" } return "false" } func (_ BoolValue) edible() {} func (_ BoolValue) scalar() {} type NumberValue float64 func (_ NumberValue) value() {} func (n NumberValue) Debug() string { return fmt.Sprintf("%v", float64(n)) } func (_ NumberValue) edible() {} func (_ NumberValue) scalar() {} type StringValue string func (_ StringValue) value() {} func (s StringValue) Debug() string { return fmt.Sprintf("%q", string(s)) } func (_ StringValue) edible() {} type ArrayElement struct { Index int Value Value } type ArrayValue []ArrayElement func (_ ArrayValue) value() {} func (array ArrayValue) Debug() string { builder := strings.Builder{} builder.WriteRune('[') var sep string for _, element := range array { builder.WriteString(sep) builder.WriteString(fmt.Sprintf("%v", element.Index)) builder.WriteString(": ") builder.WriteString(element.Value.Debug()) sep = ", " } builder.WriteRune(']') return builder.String() } func (_ ArrayValue) edible() {} type MapElement struct { Key string Value Value } type MapValue []MapElement func (_ MapValue) value() {} func (m MapValue) Debug() string { builder := strings.Builder{} builder.WriteRune('{') var sep string for _, element := range m { builder.WriteString(sep) builder.WriteString(fmt.Sprintf("%q", element.Key)) builder.WriteString(": ") builder.WriteString(element.Value.Debug()) sep = ", " } builder.WriteRune('}') return builder.String() } func (_ MapValue) edible() {} type WalkItem struct { Value Value Start, PrevStart, End, NextEnd bool } func (item WalkItem) Debug() string { builder := strings.Builder{} if item.Start { builder.WriteRune('a') } if item.PrevStart { builder.WriteRune('A') } if item.NextEnd { builder.WriteRune('Z') } if item.End { builder.WriteRune('z') } builder.WriteString(item.Value.Debug()) return builder.String() } func Merge(first Value, second Value) []Value { switch first := first.(type) { case NullValue: return []Value {first, second} case BoolValue: return []Value {first, second} case NumberValue: return []Value {first, second} case StringValue: return []Value {first, second} case ArrayValue: secondArr, isArr := second.(ArrayValue) if !isArr { return []Value {first, second} } if len(first) == 0 { return []Value {second} } if len(secondArr) == 0 { return []Value {first} } var res ArrayValue for _, el := range first[:len(first) - 1] { res = append(res, el) } midFirst := first[len(first) - 1] midSecond := secondArr[0] if midFirst.Index == midSecond.Index { for _, el := range Merge(midFirst.Value, midSecond.Value) { res = append(res, ArrayElement { Index: midFirst.Index, Value: el, }) } } else { res = append(res, midFirst, midSecond) } for _, el := range secondArr[1:] { res = append(res, el) } return []Value {res} case MapValue: secondMap, isMap := second.(MapValue) if !isMap { return []Value {first, second} } if len(first) == 0 { return []Value {second} } if len(secondMap) == 0 { return []Value {first} } var res MapValue for _, el := range first[:len(first) - 1] { res = append(res, el) } midFirst := first[len(first) - 1] midSecond := secondMap[0] if midFirst.Key == midSecond.Key { for _, el := range Merge(midFirst.Value, midSecond.Value) { res = append(res, MapElement { Key: midFirst.Key, Value: el, }) } } else { res = append(res, midFirst, midSecond) } for _, el := range secondMap[1:] { res = append(res, el) } return []Value {res} default: panic("first is invalid value type") } }