package walk import ( "fmt" "math" "strings" "unicode/utf8" ) type valueIter struct { values []Value index int } func (iter *valueIter) Next() Edible { if iter.index >= len(iter.values) { return nil } iter.index += 1 return iter.values[iter.index - 1] } func NewValueIter(values []Value) StructureIter { return &valueIter { values: values, index: 0, } } type OutputList interface { outputList() } type StructureIter interface { Next() Edible } type Edible interface { edible() } type Atom interface { Edible atom() } type Scalar interface { Atom Value } type Structure interface { Value structure() Iter() StructureIter } type Value interface { Edible value() Debug() string } type Terminal interface { Atom terminal() } type ValueList []Value func (_ ValueList) outputList() {} type RuneList []StringRuneAtom func (_ RuneList) outputList() {} type NullScalar struct{} func (_ NullScalar) edible() {} func (_ NullScalar) atom() {} func (_ NullScalar) value() {} func (_ NullScalar) Debug() string { return "null" } type BoolScalar bool func (_ BoolScalar) edible() {} func (_ BoolScalar) atom() {} func (_ BoolScalar) value() {} func (b BoolScalar) Debug() string { if b { return "true" } return "false" } type NumberScalar float64 func (_ NumberScalar) edible() {} func (_ NumberScalar) atom() {} func (_ NumberScalar) value() {} func (n NumberScalar) Debug() string { return fmt.Sprintf("%v", float64(n)) } type StringStructure string func (_ StringStructure) edible() {} func (_ StringStructure) value() {} func (_ StringStructure) structure() {} func (s StringStructure) Iter() StructureIter { return &stringStructureIter { string: string(s), position: 0, } } func (s StringStructure) Debug() string { return fmt.Sprintf("%q", string(s)) } type stringStructureIter struct { string string position int } func (iter *stringStructureIter) Next() Edible { if iter.position == -1 { return nil } r, width := utf8.DecodeRuneInString(iter.string[iter.position:]) if width == 0 { iter.position = -1 return StringEndTerminal{} } iter.position += width return StringRuneAtom(r) } type StringBeginTerminal struct{} func (_ StringBeginTerminal) edible() {} func (_ StringBeginTerminal) atom() {} func (_ StringBeginTerminal) terminal() {} type StringEndTerminal struct{} func (_ StringEndTerminal) edible() {} func (_ StringEndTerminal) atom() {} func (_ StringEndTerminal) terminal() {} type StringRuneAtom rune func (_ StringRuneAtom) edible() {} func (_ StringRuneAtom) atom() {} type ArrayStructure []Value func (_ ArrayStructure) edible() {} func (_ ArrayStructure) value() {} func (_ ArrayStructure) structure() {} func (array ArrayStructure) Iter() StructureIter { return &arrayStructureIter { array: []Value(array), index: 0, } } func (array ArrayStructure) Debug() string { builder := strings.Builder{} builder.WriteRune('[') var sep string for _, element := range array { builder.WriteString(sep) builder.WriteString(fmt.Sprintf("%v", element)) sep = ", " } builder.WriteRune(']') return builder.String() } type arrayStructureIter struct { array []Value index int } func (iter *arrayStructureIter) Next() Edible { if iter.index > len(iter.array) { return nil } if iter.index == len(iter.array) { iter.index += 1 return ArrayEndTerminal{} } iter.index += 1 return iter.array[iter.index - 1] } type ArrayBeginTerminal struct{} func (_ ArrayBeginTerminal) edible() {} func (_ ArrayBeginTerminal) atom() {} func (_ ArrayBeginTerminal) terminal() {} type ArrayEndTerminal struct{} func (_ ArrayEndTerminal) edible() {} func (_ ArrayEndTerminal) atom() {} func (_ ArrayEndTerminal) terminal() {} type MapStructure map[string]Value func (_ MapStructure) edible() {} func (_ MapStructure) value() {} func (_ MapStructure) structure() {} func (m MapStructure) Debug() string { builder := strings.Builder{} builder.WriteRune('{') var sep string for key, value := range m { builder.WriteString(sep) builder.WriteString(fmt.Sprintf("%q", key)) builder.WriteString(": ") builder.WriteString(fmt.Sprintf("%q", value)) sep = ", " } builder.WriteRune('}') return builder.String() } type MapBeginTerminal struct{} func (_ MapBeginTerminal) edible() {} func (_ MapBeginTerminal) atom() {} func (_ MapBeginTerminal) terminal() {} type MapEndTerminal struct{} func (_ MapEndTerminal) edible() {} func (_ MapEndTerminal) atom() {} func (_ MapEndTerminal) terminal() {} // int or string type PathSegment interface {} type Path []PathSegment func (path Path) ToWalkValues() []ValueOLD { var values []ValueOLD for _, segment := range path { switch s := segment.(type) { case int: values = append(values, ValueNumber(s)) case string: values = append(values, ValueString(s)) default: panic("Invalid PathSegment") } } return values } func PathFromWalkValues(values []ValueOLD) Path { var segments []PathSegment for _, value := range values { switch v := value.(type) { case ValueNumber: segments = append(segments, int(math.Round(float64(v)))) case ValueString: segments = append(segments, string(v)) default: panic("Invalid value in path") } } return segments } type WalkItem struct { Value ValueList Path ValueList } func (item WalkItem) Debug() string { builder := strings.Builder{} var sep string for _, pathSegment := range item.Path { builder.WriteString(sep) builder.WriteString(fmt.Sprintf("%s", pathSegment.Debug())) sep = "." } builder.WriteString(": ") sep = "" for _, value := range item.Value { builder.WriteString(sep) builder.WriteString(fmt.Sprintf("%s", value.Debug())) sep = ", " } return builder.String() } func ConcatData(first []AtomOLD, second []AtomOLD) []AtomOLD { res := make([]AtomOLD, 0, len(first) + len(second)) res = append(res, first...) res = append(res, second...) return res } func Atomise(in []ValueOLD) (out []AtomOLD) { numAtoms := 0 for _, value := range in { switch v := value.(type) { case ValueTerminal, ValueNull, ValueBool, ValueNumber: numAtoms++ case ValueString: numAtoms += utf8.RuneCountInString(string(v)) + 2 default: panic("Invalid WalkValue") } } out = make([]AtomOLD, 0, numAtoms) for _, value := range in { out = value.Atomise(out) } return out } type CompoundError int const ( CompoundRuneOutsideString CompoundError = iota CompoundUnknownAtom CompoundMissingEnd CompoundInvalidStringAtom ) func (err CompoundError) Error() string { switch err { case CompoundRuneOutsideString: return "Compound Error: Rune Outside String" case CompoundUnknownAtom: return "Compound Error: Unknown Atom" case CompoundMissingEnd: return "Compound Error: Missing End" case CompoundInvalidStringAtom: return "Compound Error: Invalid String Atom" default: panic("Invalid CompoundError") } } type CompoundResult struct { value ValueOLD error error } func Compound(in []AtomOLD) (out []ValueOLD, error error) { numValues := 0 i := 0 inString := false for _, atom := range in { switch atom.Typ { case AtomNull, AtomBool, AtomNumber, AtomTerminal: if !inString { numValues++ } case AtomStringTerminal: if inString { numValues++ } inString = !inString } } i = 0 out = make([]ValueOLD, 0, numValues) for { if i >= len(in) { break } atom := in[i] i++ switch atom.Typ { case AtomNull: out = append(out, ValueNull{}) continue case AtomBool: out = append(out, ValueBool(atom.data != 0)) continue case AtomNumber: out = append(out, ValueNumber(math.Float64frombits(atom.data))) continue case AtomTerminal: out = append(out, ValueTerminal(atom.data)) continue case AtomStringRune: return nil, CompoundRuneOutsideString case AtomStringTerminal: default: return nil, CompoundUnknownAtom } // Handle string start var builder strings.Builder for { if i >= len(in) { return nil, CompoundMissingEnd } atom := in[i] i++ if atom.Typ == AtomStringTerminal { break } builder.WriteString(atom.String()) } out = append(out, ValueString(builder.String())) } return out, nil }