diff options
-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") + } +} |