From 8cf10efe3b5a1bcc70bc6e5590ee63fd5eb00c5b Mon Sep 17 00:00:00 2001 From: Charlie Stanton Date: Wed, 19 Jul 2023 11:57:59 +0100 Subject: Huge refactor to a more value based system, doing away with terminals. Also introduces unit testing --- walk/atom.go | 38 ++++---- walk/value.go | 14 +-- walk/walk.go | 259 +++++++++++++++++++++++++++++++++++++++++++++++++++--- walk/walk_test.go | 45 ++++++++++ 4 files changed, 317 insertions(+), 39 deletions(-) create mode 100644 walk/walk_test.go (limited to 'walk') diff --git a/walk/atom.go b/walk/atom.go index 299c39d..471f030 100644 --- a/walk/atom.go +++ b/walk/atom.go @@ -14,78 +14,78 @@ const ( AtomStringTerminal AtomStringRune ) -type Atom struct { +type AtomOLD struct { Typ AtomType data uint64 } -func NewAtomNull() Atom { - return Atom { +func NewAtomNull() AtomOLD { + return AtomOLD { Typ: AtomNull, data: 0, } } -func NewAtomBool(v bool) Atom { +func NewAtomBool(v bool) AtomOLD { if v { - return Atom { + return AtomOLD { Typ: AtomBool, data: 1, } } else { - return Atom { + return AtomOLD { Typ: AtomBool, data: 0, } } } -func (v Atom) Bool() bool { +func (v AtomOLD) 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 { +func NewAtomNumber(v float64) AtomOLD { + return AtomOLD { Typ: AtomNumber, data: math.Float64bits(v), } } -func (v Atom) Number() float64 { +func (v AtomOLD) 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 { +func NewAtomTerminal(v ValueTerminal) AtomOLD { + return AtomOLD { Typ: AtomTerminal, data: uint64(v), } } -func (v Atom) Terminal() ValueTerminal { +func (v AtomOLD) Terminal() ValueTerminal { if v.Typ != AtomTerminal { panic("Tried to use non-terminal as terminal") } return ValueTerminal(v.data) } -func NewAtomStringTerminal() Atom { - return Atom { +func NewAtomStringTerminal() AtomOLD { + return AtomOLD { Typ: AtomStringTerminal, data: 0, } } -func NewAtomStringRune(v rune) Atom { - return Atom { +func NewAtomStringRune(v rune) AtomOLD { + return AtomOLD { Typ: AtomStringRune, data: uint64(v), } } -func (v Atom) StringRune() rune { +func (v AtomOLD) StringRune() rune { if v.Typ != AtomStringRune { panic("Tried to use non-stringrune as stringrune") } return rune(v.data) } -func (v Atom) String() string { +func (v AtomOLD) String() string { switch v.Typ { case AtomNull: return "null" diff --git a/walk/value.go b/walk/value.go index 2e2c3c9..4459d89 100644 --- a/walk/value.go +++ b/walk/value.go @@ -11,7 +11,7 @@ const ( MapBegin MapEnd ) -func (value ValueTerminal) Atomise(in []Atom) []Atom { +func (value ValueTerminal) Atomise(in []AtomOLD) []AtomOLD { return append(in, NewAtomTerminal(value)) } func (value ValueTerminal) String() string { @@ -30,7 +30,7 @@ func (value ValueTerminal) String() string { } type ValueNull struct {} -func (value ValueNull) Atomise(in []Atom) []Atom { +func (value ValueNull) Atomise(in []AtomOLD) []AtomOLD { return append(in, NewAtomNull()) } func (value ValueNull) String() string { @@ -38,7 +38,7 @@ func (value ValueNull) String() string { } type ValueBool bool -func (value ValueBool) Atomise(in []Atom) []Atom { +func (value ValueBool) Atomise(in []AtomOLD) []AtomOLD { return append(in, NewAtomBool(bool(value))) } func (value ValueBool) String() string { @@ -50,7 +50,7 @@ func (value ValueBool) String() string { } type ValueNumber float64 -func (value ValueNumber) Atomise(in []Atom) []Atom { +func (value ValueNumber) Atomise(in []AtomOLD) []AtomOLD { return append(in, NewAtomNumber(float64(value))) } func (value ValueNumber) String() string { @@ -59,7 +59,7 @@ func (value ValueNumber) String() string { } type ValueString string -func (value ValueString) Atomise(in []Atom) []Atom { +func (value ValueString) Atomise(in []AtomOLD) []AtomOLD { in = append(in, NewAtomStringTerminal()) for _, char := range value { in = append(in, NewAtomStringRune(char)) @@ -71,8 +71,8 @@ func (value ValueString) String() string { return fmt.Sprintf("\"%s\"", string(value)) } -type Value interface { +type ValueOLD interface { // Append this values atoms to the input - Atomise(in []Atom) []Atom + Atomise(in []AtomOLD) []AtomOLD String() string } diff --git a/walk/walk.go b/walk/walk.go index 1073c67..65fac6e 100644 --- a/walk/walk.go +++ b/walk/walk.go @@ -1,17 +1,232 @@ package walk import ( - "strings" + "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() []Value { - var values []Value +func (path Path) ToWalkValues() []ValueOLD { + var values []ValueOLD for _, segment := range path { switch s := segment.(type) { case int: @@ -25,7 +240,7 @@ func (path Path) ToWalkValues() []Value { return values } -func PathFromWalkValues(values []Value) Path { +func PathFromWalkValues(values []ValueOLD) Path { var segments []PathSegment for _, value := range values { switch v := value.(type) { @@ -41,18 +256,36 @@ func PathFromWalkValues(values []Value) Path { } type WalkItem struct { - Value []Atom - Path []Atom + 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 []Atom, second []Atom) []Atom { - res := make([]Atom, 0, len(first) + len(second)) +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 []Value) (out []Atom) { +func Atomise(in []ValueOLD) (out []AtomOLD) { numAtoms := 0 for _, value := range in { switch v := value.(type) { @@ -64,7 +297,7 @@ func Atomise(in []Value) (out []Atom) { panic("Invalid WalkValue") } } - out = make([]Atom, 0, numAtoms) + out = make([]AtomOLD, 0, numAtoms) for _, value := range in { out = value.Atomise(out) } @@ -96,11 +329,11 @@ func (err CompoundError) Error() string { } type CompoundResult struct { - value Value + value ValueOLD error error } -func Compound(in []Atom) (out []Value, error error) { +func Compound(in []AtomOLD) (out []ValueOLD, error error) { numValues := 0 i := 0 inString := false @@ -118,7 +351,7 @@ func Compound(in []Atom) (out []Value, error error) { } } i = 0 - out = make([]Value, 0, numValues) + out = make([]ValueOLD, 0, numValues) for { if i >= len(in) { break diff --git a/walk/walk_test.go b/walk/walk_test.go new file mode 100644 index 0000000..c05da02 --- /dev/null +++ b/walk/walk_test.go @@ -0,0 +1,45 @@ +package walk + +import ( + "testing" + "log" +) + +func TestValueIter(t *testing.T) { + values := ValueList{ + NumberScalar(1), + NumberScalar(2), + NumberScalar(3), + } + + valuesCopy := ValueList{} + + iter := NewValueIter(values) + + for { + edible := iter.Next() + if edible == nil { + break + } + + log.Println(edible) + + value, isValue := edible.(Value) + + if !isValue { + t.Fatalf("Iterator produced a non-value") + } + + valuesCopy = append(valuesCopy, value) + } + + if len(values) != len(valuesCopy) { + t.Fatalf("iter gave the wrong number of values") + } + + for i, value := range values { + if value != valuesCopy[i] { + t.Fatalf("iter produced an incorrect value") + } + } +} -- cgit v1.2.3