<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2024-03-30 15:20:07 +0000
committerCharlie Stanton <charlie@shtanton.xyz>2024-03-30 15:20:07 +0000
commit7a9f00b9bd39173494ea734b899a9f099dafb306 (patch)
treea169451bcba8c424ade1ff5cdac29eef6818d8d8
parent9d82785f46949151b783d83648b39ce9ba40c615 (diff)
downloadstred-go-7a9f00b9bd39173494ea734b899a9f099dafb306.tar
Add array value destructure
-rw-r--r--subex/main.go60
-rw-r--r--subex/main_test.go58
-rw-r--r--subex/parse.go21
-rw-r--r--subex/subexast.go40
-rw-r--r--subex/subexstate.go27
5 files changed, 171 insertions, 35 deletions
diff --git a/subex/main.go b/subex/main.go
index f8d9093..982b585 100644
--- a/subex/main.go
+++ b/subex/main.go
@@ -150,7 +150,8 @@ type auxiliaryState struct {
outputStack OutputStack
// How deeply nested the current execution is inside of the overall value
// i.e. starts at zero, is incremented to one when entering an array
- nesting int
+ nestingLen int
+ nestingValue bool
}
func (aux auxiliaryState) cloneStore() auxiliaryState {
@@ -204,16 +205,6 @@ func (aux auxiliaryState) topAppendRune(runes []rune) auxiliaryState {
return aux
}
-func (aux auxiliaryState) incNest() auxiliaryState {
- aux.nesting++
- return aux
-}
-
-func (aux auxiliaryState) decNest() auxiliaryState {
- aux.nesting--
- return aux
-}
-
type SubexBranch struct {
state SubexState
aux auxiliaryState
@@ -237,7 +228,7 @@ func (pair SubexEatBranch) accepting() []OutputStack {
func equalStates(left SubexEatBranch, right SubexEatBranch) bool {
// Only care about if they are the same pointer
- return left.state == right.state && left.aux.nesting == right.aux.nesting
+ return left.state == right.state && left.aux.nestingLen == right.aux.nestingLen && left.aux.nestingValue == right.aux.nestingValue
}
// If two branches have the same state, only the first has a chance of being successful
@@ -257,12 +248,15 @@ outer:
return states[:uniqueStates]
}
-func addStates(curStates []SubexEatBranch, newStates []SubexBranch) []SubexEatBranch {
+func addStates(curStates []SubexEatBranch, newStates []SubexBranch, nesting []bool) []SubexEatBranch {
for _, state := range newStates {
switch s := state.state.(type) {
case SubexEpsilonState:
- curStates = addStates(curStates, s.epsilon(state.aux))
+ curStates = addStates(curStates, s.epsilon(state.aux), nesting)
case SubexEatState:
+ if state.aux.nestingLen < len(nesting) && state.aux.nestingLen > 0 {
+ state.aux.nestingValue = nesting[state.aux.nestingLen - 1]
+ }
curStates = append(curStates, SubexEatBranch{
state: s,
aux: state.aux,
@@ -272,13 +266,18 @@ func addStates(curStates []SubexEatBranch, newStates []SubexBranch) []SubexEatBr
return curStates
}
-func processInput(states []SubexEatBranch, input walk.Edible, nesting int) []SubexEatBranch {
+func processInput(states []SubexEatBranch, input walk.Edible, nesting []bool) []SubexEatBranch {
newStates := make([]SubexEatBranch, 0, 2)
for _, state := range states {
- if state.aux.nesting == nesting {
- newStates = addStates(newStates, state.eat(input))
- } else if state.aux.nesting < nesting {
+ if state.aux.nestingLen > len(nesting) {
+ continue
+ }
+
+ if (state.aux.nestingLen == len(nesting) &&
+ (len(nesting) == 0 || state.aux.nestingValue || nesting[len(nesting) - 1])) {
+ newStates = addStates(newStates, state.eat(input), nesting)
+ } else {
newStates = append(newStates, state)
}
}
@@ -286,21 +285,21 @@ func processInput(states []SubexEatBranch, input walk.Edible, nesting int) []Sub
switch input := input.(type) {
case walk.StringValue:
for _, r := range input {
- newStates = processInput(newStates, walk.RuneEdible(r), nesting+1)
+ newStates = processInput(newStates, walk.RuneEdible(r), append(nesting, true))
}
- newStates = processInput(newStates, walk.StringEnd, nesting+1)
+ newStates = processInput(newStates, walk.StringEnd, append(nesting, true))
case walk.ArrayValue:
for _, el := range input {
- newStates = processInput(newStates, walk.NumberValue(el.Index), nesting+1)
- newStates = processInput(newStates, el.Value, nesting+1)
+ newStates = processInput(newStates, walk.NumberValue(el.Index), append(nesting, false))
+ newStates = processInput(newStates, el.Value, append(nesting, true))
}
- newStates = processInput(newStates, walk.ArrayEnd, nesting+1)
+ newStates = processInput(newStates, walk.ArrayEnd, append(nesting, true))
case walk.MapValue:
for _, el := range input {
- newStates = processInput(newStates, walk.StringValue(el.Key), nesting+1)
- newStates = processInput(newStates, el.Value, nesting+1)
+ newStates = processInput(newStates, walk.StringValue(el.Key), append(nesting, false))
+ newStates = processInput(newStates, el.Value, append(nesting, true))
}
- newStates = processInput(newStates, walk.MapEnd, nesting+1)
+ newStates = processInput(newStates, walk.MapEnd, append(nesting, true))
}
newStates = pruneStates(newStates)
@@ -321,20 +320,21 @@ func RunTransducer(transducer Transducer, input []walk.Value) (output []walk.Val
values: make([][]walk.Value, transducer.storeSize.values),
runes: make([][]rune, transducer.storeSize.runes),
},
- nesting: 0,
+ nestingLen: 0,
+ nestingValue: true,
},
- }})
+ }}, nil)
for _, value := range input {
if len(states) == 0 {
break
}
- states = processInput(states, value, 0)
+ states = processInput(states, value, nil)
}
for _, state := range states {
- if state.aux.nesting > 0 {
+ if state.aux.nestingLen > 0 {
continue
}
acceptingStacks := state.accepting()
diff --git a/subex/main_test.go b/subex/main_test.go
index d7424b3..673b807 100644
--- a/subex/main_test.go
+++ b/subex/main_test.go
@@ -235,6 +235,64 @@ func TestSubexMain(t *testing.T) {
walk.StringValue("abcdefghijklm"),
},
},
+ {
+ subex: ":(.)-",
+ input: []walk.Value {
+ walk.ArrayValue {
+ {
+ Index: 0,
+ Value: walk.NullValue{},
+ },
+ },
+ },
+ expected: []walk.Value {
+ walk.NullValue{},
+ },
+ },
+ {
+ subex: ":(.{-0}+)-",
+ input: []walk.Value {
+ walk.ArrayValue {
+ {
+ Index: 0,
+ Value: walk.NumberValue(4),
+ },
+ {
+ Index: 1,
+ Value: walk.NumberValue(-123),
+ },
+ {
+ Index: 2,
+ Value: walk.NumberValue(124),
+ },
+ },
+ },
+ expected: []walk.Value {
+ walk.NumberValue(5),
+ },
+ },
+ {
+ subex: "~(-(.)~{-0}):",
+ input: []walk.Value {
+ walk.StringValue("abc"),
+ },
+ expected: []walk.Value {
+ walk.ArrayValue {
+ {
+ Index: 0,
+ Value: walk.StringValue("a"),
+ },
+ {
+ Index: 0,
+ Value: walk.StringValue("b"),
+ },
+ {
+ Index: 0,
+ Value: walk.StringValue("c"),
+ },
+ },
+ },
+ },
}
for i, test := range tests {
diff --git a/subex/parse.go b/subex/parse.go
index f1565f5..98821fd 100644
--- a/subex/parse.go
+++ b/subex/parse.go
@@ -34,13 +34,18 @@ const (
NoneStructure Structure = iota
StringStructure
ArrayStructure
+ ArrayValuesStructure
)
-func (s Structure) innerType() Type {
+func (s Structure) String() string {
switch s {
+ case NoneStructure:
+ return "-"
case StringStructure:
- return RuneType
+ return "~"
case ArrayStructure:
- return ValueType
+ return "@"
+ case ArrayValuesStructure:
+ return ":"
default:
panic("Invalid structure")
}
@@ -321,6 +326,9 @@ func parseDestructure(l RuneReader, destructure Structure, inType Type) (lhs Sub
case ArrayStructure:
innerInType = ValueType
expectedInType = ValueType
+ case ArrayValuesStructure:
+ innerInType = ValueType
+ expectedInType = ValueType
default:
panic("Invalid structure")
}
@@ -345,6 +353,9 @@ func parseDestructure(l RuneReader, destructure Structure, inType Type) (lhs Sub
case '@':
structure = ArrayStructure
expectedInnerOutType = ValueType
+ case ':':
+ structure = ArrayValuesStructure
+ expectedInnerOutType = ValueType
default:
panic("Missing matching destructure")
}
@@ -358,6 +369,8 @@ func parseDestructure(l RuneReader, destructure Structure, inType Type) (lhs Sub
outType = ValueType
case ArrayStructure:
outType = ValueType
+ case ArrayValuesStructure:
+ outType = ValueType
}
lhs = SubexASTDestructure {
@@ -385,6 +398,8 @@ func parseSubex(l RuneReader, minPower int, inType Type) (lhs SubexAST, outType
lhs, outType = parseDestructure(l, StringStructure, inType)
case '@':
lhs, outType = parseDestructure(l, ArrayStructure, inType)
+ case ':':
+ lhs, outType = parseDestructure(l, ArrayValuesStructure, inType)
// TODO
// case '[':
// rangeParts := parseRangeSubex(l)
diff --git a/subex/subexast.go b/subex/subexast.go
index 7070baf..a2c3675 100644
--- a/subex/subexast.go
+++ b/subex/subexast.go
@@ -484,6 +484,13 @@ func (ast SubexASTDestructure) compileWith(next SubexState, slotMap *SlotMap, in
construct = &SubexConstructArrayState {
next: next,
}
+ case ArrayValuesStructure:
+ innerOutType = ValueType
+ construct = &SubexConstructArrayValuesState {
+ next: next,
+ }
+ default:
+ panic("Invalid ast structure")
}
var innerInType Type
@@ -508,6 +515,16 @@ func (ast SubexASTDestructure) compileWith(next SubexState, slotMap *SlotMap, in
next: construct,
},
}
+ case ArrayValuesStructure:
+ innerInType = ValueType
+ destructFooter = &SubexDiscardTerminalState {
+ terminal: walk.ArrayEnd,
+ next: &SubexDecrementNestState {
+ next: construct,
+ },
+ }
+ default:
+ panic("Invalid ast destructure")
}
inner := ast.Content.compileWith(
@@ -529,6 +546,12 @@ func (ast SubexASTDestructure) compileWith(next SubexState, slotMap *SlotMap, in
beginConstruct = &SubexCaptureBeginState {
next: inner,
}
+ case ArrayValuesStructure:
+ beginConstruct = &SubexCaptureBeginState {
+ next: inner,
+ }
+ default:
+ panic("Invalid ast structure")
}
switch ast.Destructure {
@@ -540,6 +563,7 @@ func (ast SubexASTDestructure) compileWith(next SubexState, slotMap *SlotMap, in
filter: anyStringFilter{},
next: &SubexDiscardState {
next: &SubexIncrementNestState {
+ keys: true,
next: beginConstruct,
},
},
@@ -551,6 +575,19 @@ func (ast SubexASTDestructure) compileWith(next SubexState, slotMap *SlotMap, in
filter: anyArrayFilter{},
next: &SubexDiscardState {
next: &SubexIncrementNestState {
+ keys: true,
+ next: beginConstruct,
+ },
+ },
+ },
+ }
+ case ArrayValuesStructure:
+ return &SubexCaptureBeginState {
+ next: &SubexCopyState {
+ filter: anyArrayFilter{},
+ next: &SubexDiscardState {
+ next: &SubexIncrementNestState {
+ keys: false,
next: beginConstruct,
},
},
@@ -560,3 +597,6 @@ func (ast SubexASTDestructure) compileWith(next SubexState, slotMap *SlotMap, in
panic("Invalid destructure in ast")
}
}
+func (ast SubexASTDestructure) String() string {
+ return fmt.Sprintf("%v(%v)%v", ast.Destructure, ast.Content, ast.Structure)
+}
diff --git a/subex/subexstate.go b/subex/subexstate.go
index 4de8ae2..45b5d00 100644
--- a/subex/subexstate.go
+++ b/subex/subexstate.go
@@ -355,6 +355,24 @@ func (state SubexConstructArrayState) epsilon(aux auxiliaryState) []SubexBranch
}}
}
+type SubexConstructArrayValuesState struct {
+ next SubexState
+}
+func (state SubexConstructArrayValuesState) epsilon(aux auxiliaryState) []SubexBranch {
+ values, aux := aux.popOutput()
+ var array walk.ArrayValue
+ for _, v := range values {
+ array = append(array, walk.ArrayElement {
+ Index: 0,
+ Value: v,
+ })
+ }
+ return []SubexBranch {{
+ state: state.next,
+ aux: aux.topAppend([]walk.Value {array}),
+ }}
+}
+
type SubexConstructStringState struct {
next SubexState
}
@@ -377,12 +395,15 @@ func (state SubexConstructStringState) String() string {
}
type SubexIncrementNestState struct {
+ keys bool
next SubexState
}
func (state SubexIncrementNestState) epsilon(aux auxiliaryState) []SubexBranch {
+ aux.nestingLen += 1
+ aux.nestingValue = state.keys
return []SubexBranch {{
state: state.next,
- aux: aux.incNest(),
+ aux: aux,
}}
}
func (state SubexIncrementNestState) String() string {
@@ -393,8 +414,10 @@ type SubexDecrementNestState struct {
next SubexState
}
func (state SubexDecrementNestState) epsilon(aux auxiliaryState) []SubexBranch {
+ aux.nestingLen -= 1
+ // aux.nestingValue will be set in addStates
return []SubexBranch {{
state: state.next,
- aux: aux.decNest(),
+ aux: aux,
}}
}