<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2024-04-07 16:04:23 +0100
committerCharlie Stanton <charlie@shtanton.xyz>2024-04-07 16:04:23 +0100
commit7162ae8c641314846f0b565d7614ac8d71dbd628 (patch)
treefba1b545e6d20dac7f958bedf83afc61fcbbc256
parent658900fcae610caace83a112ac0ee865108ebc92 (diff)
downloadstred-go-7162ae8c641314846f0b565d7614ac8d71dbd628.tar
Add merge command
-rw-r--r--main/command.go34
-rw-r--r--main/main_test.go5
-rw-r--r--main/parse.go2
-rw-r--r--subex/parse.go21
-rw-r--r--walk/walk.go85
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")
+ }
+}