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,  	}}  } | 
