diff options
author | Charlie Stanton <charlie@shtanton.xyz> | 2024-03-30 15:20:07 +0000 |
---|---|---|
committer | Charlie Stanton <charlie@shtanton.xyz> | 2024-03-30 15:20:07 +0000 |
commit | 7a9f00b9bd39173494ea734b899a9f099dafb306 (patch) | |
tree | a169451bcba8c424ade1ff5cdac29eef6818d8d8 /subex | |
parent | 9d82785f46949151b783d83648b39ce9ba40c615 (diff) | |
download | stred-go-7a9f00b9bd39173494ea734b899a9f099dafb306.tar |
Add array value destructure
Diffstat (limited to 'subex')
-rw-r--r-- | subex/main.go | 60 | ||||
-rw-r--r-- | subex/main_test.go | 58 | ||||
-rw-r--r-- | subex/parse.go | 21 | ||||
-rw-r--r-- | subex/subexast.go | 40 | ||||
-rw-r--r-- | subex/subexstate.go | 27 |
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, }} } |