diff options
| author | Charlie Stanton <charlie@shtanton.xyz> | 2024-04-07 16:04:23 +0100 | 
|---|---|---|
| committer | Charlie Stanton <charlie@shtanton.xyz> | 2024-04-07 16:04:23 +0100 | 
| commit | 7162ae8c641314846f0b565d7614ac8d71dbd628 (patch) | |
| tree | fba1b545e6d20dac7f958bedf83afc61fcbbc256 | |
| parent | 658900fcae610caace83a112ac0ee865108ebc92 (diff) | |
| download | stred-go-7162ae8c641314846f0b565d7614ac8d71dbd628.tar | |
Add merge command
| -rw-r--r-- | main/command.go | 34 | ||||
| -rw-r--r-- | main/main_test.go | 5 | ||||
| -rw-r--r-- | main/parse.go | 2 | ||||
| -rw-r--r-- | subex/parse.go | 21 | ||||
| -rw-r--r-- | walk/walk.go | 85 | 
5 files changed, 144 insertions, 3 deletions
| diff --git a/main/command.go b/main/command.go index 3f22821..6d75974 100644 --- a/main/command.go +++ b/main/command.go @@ -44,13 +44,47 @@ func (cmd AppendNextCommand) exec(state *ProgramState) {  	if err != nil {  		panic("Missing next value")  	} + +	state.prevStart = nextItem.PrevStart +	state.start = nextItem.Start +	state.end = nextItem.End +	state.nextEnd = nextItem.NextEnd +  	state.value = append(state.value, nextItem.Value) +  	state.pc++  }  func (cmd AppendNextCommand) String() string {  	return "N"  } +type MergeCommand struct {} +func (cmd MergeCommand) exec(state *ProgramState) { +	nextItem, err := state.in.Read() +	if err != nil { +		panic("Missing next value") +	} + +	state.prevStart = nextItem.PrevStart +	state.start = nextItem.Start +	state.end = nextItem.End +	state.nextEnd = nextItem.NextEnd + +	if len(state.value) == 0 { +		state.value = []walk.Value {nextItem.Value} +	} else { +		state.value = append( +			state.value[:len(state.value) - 1], +			walk.Merge(state.value[len(state.value) - 1], nextItem.Value)... +		) +	} + +	state.pc++ +} +func (cmd MergeCommand) String() string { +	return "m" +} +  type DeleteValueCommand struct {}  func (cmd DeleteValueCommand) exec(state *ProgramState) {  	state.value = nil diff --git a/main/main_test.go b/main/main_test.go index 7aa90aa..74f179b 100644 --- a/main/main_test.go +++ b/main/main_test.go @@ -64,6 +64,11 @@ func TestMain(t *testing.T) {  			input: miscInput,  			expected: `["Charlie Johnson","Tom Johnson","Charlie Chaplin","John Johnson"]`,  		}, +		{ +			program: "s/#(\"people\" @(. #(\"first_name\" .)#)@)#/{ ms/#(\"people\" @(. (#(\"first_name\" \".{-0}$a\" \"last_name\" \".{-0}$b\")#$_) `#(\"name\" \"$a $b\")#`)@)#/ }", +			input: miscInput, +			expected: `{"something":{"nested":"Here is my test value"},"array":["Hello","world","these","are","values"],"people":[{"name":"Charlie Johnson","age":22},{"name":"Tom Johnson","age":18},{"name":"Charlie Chaplin","age":122},{"name":"John Johnson","age":48}]}`, +		},  	}  	for i, test := range tests { diff --git a/main/parse.go b/main/parse.go index 3e0e80b..3c24e6c 100644 --- a/main/parse.go +++ b/main/parse.go @@ -69,6 +69,8 @@ func (p *parser) parseBasicCommand(commands []Command, commandChar rune) []Comma  			return append(commands, NextCommand{})  		case 'N':  			return append(commands, AppendNextCommand{}) +		case 'm': +			return append(commands, MergeCommand{})  		case 's':  			ast := p.parseSubex()  			subex := subex.CompileTransducer(ast) diff --git a/subex/parse.go b/subex/parse.go index 619c1c3..d825f75 100644 --- a/subex/parse.go +++ b/subex/parse.go @@ -183,7 +183,7 @@ func parseRepeatRange(l RuneReader) (output []ConvexRange) {  	return output  } -func parseValueReplacement(l RuneReader) (output SubexAST) { +func parseValueReplacement(l RuneReader, end rune) (output SubexAST) {  	output = SubexASTEmpty{}  	// TODO escaping  	// TODO add arrays, maps and strings @@ -193,7 +193,7 @@ func parseValueReplacement(l RuneReader) (output SubexAST) {  		case eof:  			panic("Missing closing `")  		case ' ': -		case '`': +		case end:  			break loop  		case '$':  			slot := l.Next() @@ -207,6 +207,21 @@ func parseValueReplacement(l RuneReader) (output SubexAST) {  				},  			}  		// TODO: destructures +		case '#': +			if !accept(l, "(") { +				panic("Missing ( after #") +			} +			output = SubexASTConcat { +				First: output, +				Second: SubexASTDestructure { +					Destructure: NoneStructure, +					Structure: MapStructure, +					Content: parseValueReplacement(l, ')'), +				}, +			} +			if !accept(l, "#") { +				panic("Missing # after )") +			}  		case '"':  			output = SubexASTConcat {  				First: output, @@ -501,7 +516,7 @@ func parseSubex(l RuneReader, minPower int, inType Type) (lhs SubexAST, outType  			lhs = SubexASTCopyNumber{}  		case '`':  			outType = inType -			lhs = parseValueReplacement(l) +			lhs = parseValueReplacement(l, '`')  		case ' ':  			if inType == RuneType {  				outType = RuneType diff --git a/walk/walk.go b/walk/walk.go index 3fba62f..e66feff 100644 --- a/walk/walk.go +++ b/walk/walk.go @@ -148,3 +148,88 @@ func (item WalkItem) Debug() string {  	builder.WriteString(item.Value.Debug())  	return builder.String()  } + +func Merge(first Value, second Value) []Value { +	switch first := first.(type) { +	case NullValue: +		return []Value {first, second} +	case BoolValue: +		return []Value {first, second} +	case NumberValue: +		return []Value {first, second} +	case StringValue: +		return []Value {first, second} +	case ArrayValue: +		secondArr, isArr := second.(ArrayValue) +		if !isArr { +			return []Value {first, second} +		} + +		if len(first) == 0 { +			return []Value {second} +		} + +		if len(secondArr) == 0 { +			return []Value {first} +		} + +		var res ArrayValue +		for _, el := range first[:len(first) - 1] { +			res = append(res, el) +		} +		midFirst := first[len(first) - 1] +		midSecond := secondArr[0] +		if midFirst.Index == midSecond.Index { +			for _, el := range Merge(midFirst.Value, midSecond.Value) { +				res = append(res, ArrayElement { +					Index: midFirst.Index, +					Value: el, +				}) +			} +		} else { +			res = append(res, midFirst, midSecond) +		} +		for _, el := range secondArr[1:] { +			res = append(res, el) +		} + +		return []Value {res} +	case MapValue: +		secondMap, isMap := second.(MapValue) +		if !isMap { +			return []Value {first, second} +		} + +		if len(first) == 0 { +			return []Value {second} +		} + +		if len(secondMap) == 0 { +			return []Value {first} +		} + +		var res MapValue +		for _, el := range first[:len(first) - 1] { +			res = append(res, el) +		} +		midFirst := first[len(first) - 1] +		midSecond := secondMap[0] +		if midFirst.Key == midSecond.Key { +			for _, el := range Merge(midFirst.Value, midSecond.Value) { +				res = append(res, MapElement { +					Key: midFirst.Key, +					Value: el, +				}) +			} +		} else { +			res = append(res, midFirst, midSecond) +		} +		for _, el := range secondMap[1:] { +			res = append(res, el) +		} + +		return []Value {res} +	default: +		panic("first is invalid value type") +	} +} | 
