<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/walk
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2023-07-19 11:57:59 +0100
committerCharlie Stanton <charlie@shtanton.xyz>2023-07-19 11:57:59 +0100
commit8cf10efe3b5a1bcc70bc6e5590ee63fd5eb00c5b (patch)
tree7a16883c17c2bdcc49b2f9d4f333dfc76c66248f /walk
parent3c34366bdd5d817a184d6b1c901d03a16b6faa4b (diff)
downloadstred-go-8cf10efe3b5a1bcc70bc6e5590ee63fd5eb00c5b.tar
Huge refactor to a more value based system, doing away with terminals. Also introduces unit testing
Diffstat (limited to 'walk')
-rw-r--r--walk/atom.go38
-rw-r--r--walk/value.go14
-rw-r--r--walk/walk.go259
-rw-r--r--walk/walk_test.go45
4 files changed, 317 insertions, 39 deletions
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")
+ }
+ }
+}