Repository: MiSawa/xq (rust)

Issue:
Modify operator on overlapping paths
I noticed that `xq` is incompatible on the query with modify operator (`|=`) with overlapping paths.
```sh
 $ xq --version
xq 0.2.39-ccae20f320d451effd67014434fb13b2fc3a58b0

 $ xq -n '{foo:0} | (.foo,.foo,.foo) |= .+1'
{
  "foo": 1
}

 $ jq -n '{foo:0} | (.foo,.foo,.foo) |= .+1'
{
  "foo": 3
}
```

Pull request description:
Fix update-assignment operator on overlapping paths (fix #156)
This PR fixes the update-assignment operator `|=` to be more compatible with jq, fixing #156.

Patch:
diff --git a/src/compile/compiler.rs b/src/compile/compiler.rs
index 7d3701f..480cbe6 100644
--- a/src/compile/compiler.rs
+++ b/src/compile/compiler.rs
@@ -1411,7 +1411,7 @@ impl Compiler {
                     next: Address,
                 ) -> Result<Address> {
                     let slot = compiler.allocate_variable();
-                    let tmp_slot = compiler.allocate_variable();
+                    let path_slot = compiler.allocate_variable();
                     let del_slot = compiler.allocate_variable();
                     let label_slot = compiler.current_scope_mut().allocate_label();
 
@@ -1453,22 +1453,31 @@ impl Compiler {
                     );
                     let next = modification.compile(compiler, next)?;
                     let next = compiler.emitter.emit_fork(on_empty, next);
-                    // value, path, indexed value
+                    let next = compiler.emitter.emit_normal_op(
+                        ByteCode::Intrinsic1(NamedFn1 {
+                            name: "getpath",
+                            func: intrinsic::get_path,
+                        }),
+                        next,
+                    );
                     let next = compiler
                         .emitter
-                        .emit_normal_op(ByteCode::Load(tmp_slot), next);
-                    let next = compiler.emitter.emit_normal_op(ByteCode::Swap, next);
+                        .emit_normal_op(ByteCode::Load(path_slot), next);
                     let next = compiler.emitter.emit_normal_op(ByteCode::Load(slot), next);
                     let next = compiler
                         .emitter
-                        .emit_normal_op(ByteCode::ExitPathTracking, next);
+                        .emit_normal_op(ByteCode::Load(path_slot), next);
+                    let next = compiler.emitter.emit_fork(on_empty, next);
+                    let next = compiler.emitter.emit_normal_op(ByteCode::Load(slot), next);
                     let next = compiler
                         .emitter
-                        .emit_normal_op(ByteCode::Store(tmp_slot), next);
-                    let next = compiler.emitter.emit_normal_op(ByteCode::Dup, next);
+                        .emit_normal_op(ByteCode::ForkLabel(label_slot), next);
                     let next = compiler
                         .emitter
-                        .emit_normal_op(ByteCode::ForkLabel(label_slot), next);
+                        .emit_normal_op(ByteCode::Store(path_slot), next);
+                    let next = compiler
+                        .emitter
+                        .emit_normal_op(ByteCode::ExitPathTracking, next);
                     let next = compiler.compile_query(path_expression, next)?;
                     let next = compiler
                         .emitter


Test patch:
diff --git a/tests/hand_written/assignments.rs b/tests/hand_written/assignments.rs
index 484d4ec..9534774 100644
--- a/tests/hand_written/assignments.rs
+++ b/tests/hand_written/assignments.rs
@@ -120,3 +120,16 @@ test!(
     [1,2,null,null,5,3]
     "#
 );
+
+test!(
+    overlapping_update,
+    r#"
+    (.foo,.foo,.foo) |= . + 1
+    "#,
+    r#"
+    null
+    "#,
+    r#"
+    {"foo":3}
+    "#
+);