diff options
| author | Charlie Stanton <charlie@shtanton.xyz> | 2023-07-19 11:57:59 +0100 | 
|---|---|---|
| committer | Charlie Stanton <charlie@shtanton.xyz> | 2023-07-19 11:57:59 +0100 | 
| commit | 8cf10efe3b5a1bcc70bc6e5590ee63fd5eb00c5b (patch) | |
| tree | 7a16883c17c2bdcc49b2f9d4f333dfc76c66248f /walk | |
| parent | 3c34366bdd5d817a184d6b1c901d03a16b6faa4b (diff) | |
| download | stred-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.go | 38 | ||||
| -rw-r--r-- | walk/value.go | 14 | ||||
| -rw-r--r-- | walk/walk.go | 259 | ||||
| -rw-r--r-- | walk/walk_test.go | 45 | 
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") +		} +	} +} | 
