Skip to content

Commit a80771f

Browse files
committed
Implement PathCursor, add tests for various path types
1 parent f0fa9f3 commit a80771f

File tree

4 files changed

+216
-1
lines changed

4 files changed

+216
-1
lines changed

convex-core/src/main/java/convex/core/lang/RT.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,6 +1405,27 @@ public static <T extends ACell> T getIn(ACell coll, Object... keys) {
14051405
return (T) result;
14061406
}
14071407

1408+
/**
1409+
* Gets an element from a data structure using the given key path.
1410+
*
1411+
* @param coll Collection to query
1412+
* @param keys Key to look up in collection
1413+
* @return Value from collection with the specified key, or null if not found / invalid path
1414+
*/
1415+
@SuppressWarnings("unchecked")
1416+
public static <T extends ACell> T getIn(ACell coll, ACell... keys) {
1417+
ACell result=coll;
1418+
for (int i=0; i<keys.length; i++) {
1419+
if (result instanceof ADataStructure ds) {
1420+
ACell key=keys[i];
1421+
result=RT.get(ds,key);
1422+
} else {
1423+
return null;
1424+
}
1425+
}
1426+
return (T) result;
1427+
}
1428+
14081429
public static ACell assocIn(ACell a, ACell value, Object... keys) {
14091430
int n=keys.length;
14101431
ADataStructure<?>[] ass=new ADataStructure[n];
@@ -1429,6 +1450,31 @@ public static ACell assocIn(ACell a, ACell value, Object... keys) {
14291450
}
14301451
return value;
14311452
}
1453+
1454+
public static ACell assocIn(ACell a, ACell value, ACell... keys) {
1455+
int n=keys.length;
1456+
ADataStructure<?>[] ass=new ADataStructure[n];
1457+
ACell[] ks=new ACell[n];
1458+
ACell data=a;
1459+
for (int i = 0; i < n; i++) {
1460+
ADataStructure<?> struct = RT.ensureAssociative(data); // nil-> empty map
1461+
if (struct == null) throw new IllegalArgumentException("Not a data structure at depth: "+i+" found "+Utils.getClassName(data));
1462+
ass[i]=struct;
1463+
ACell k=keys[i];
1464+
ks[i]=k;
1465+
data=struct.get(k);
1466+
}
1467+
1468+
for (int i = n-1; i >=0; i--) {
1469+
ADataStructure<?> struct=ass[i];
1470+
ACell k=ks[i];
1471+
value=RT.assoc(struct, k, value);
1472+
if (value==null) {
1473+
throw new IllegalArgumentException("Invalid structure for assocIn at depth "+i);
1474+
}
1475+
}
1476+
return value;
1477+
}
14321478

14331479
/**
14341480
* Gets an element from a data structure using the given key. Returns the

convex-core/src/main/java/convex/lattice/ACursor.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
import convex.core.data.ACell;
77
import convex.core.lang.RT;
88

9+
/**
10+
* A Lattice cursor is a mutable pointer into a CVM data structure.
11+
*
12+
* Methods are modelled after java.util.concurrent.atomic.AtomicReference for consistency and logic
13+
*
14+
* @param <V>
15+
*/
916
public abstract class ACursor<V extends ACell> {
1017

1118
/**
@@ -49,6 +56,10 @@ public void set(Object o) {
4956

5057
public abstract V updateAndGet(UnaryOperator<V> updateFunction);
5158

59+
public void update(UnaryOperator<V> updateFunction) {
60+
getAndUpdate(updateFunction);
61+
}
62+
5263
public abstract V getAndAccumulate(V x, BinaryOperator<V> accumulatorFunction);
5364

5465
public abstract V accumulateAndGet(V x, BinaryOperator<V> accumulatorFunction);
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package convex.lattice;
2+
3+
import java.util.function.BinaryOperator;
4+
import java.util.function.UnaryOperator;
5+
6+
import convex.core.data.ACell;
7+
import convex.core.lang.RT;
8+
import convex.core.util.Utils;
9+
10+
public class PathCursor<V extends ACell> extends ACursor<V> {
11+
12+
ACursor<ACell> base;
13+
ACell[] path;
14+
15+
@SuppressWarnings({ "unchecked", "rawtypes" })
16+
public PathCursor(ACursor<?> base, ACell... path) {
17+
this.base=(ACursor)base;
18+
this.path=path;
19+
}
20+
21+
@SuppressWarnings("unchecked")
22+
@Override
23+
public V get() {
24+
return (V) RT.getIn(base.get(), path);
25+
}
26+
27+
@Override
28+
public void set(V newValue) {
29+
base.getAndUpdate(bv->{
30+
return RT.assocIn(bv,newValue,path);
31+
});
32+
}
33+
34+
@SuppressWarnings("unchecked")
35+
@Override
36+
public V getAndSet(V newValue) {
37+
ACell oldBase=base.getAndUpdate(bv->{
38+
return RT.assocIn(bv,newValue,path);
39+
});
40+
return (V) RT.getIn(oldBase, path);
41+
}
42+
43+
@SuppressWarnings("unchecked")
44+
@Override
45+
public V getAndUpdate(UnaryOperator<V> updateFunction) {
46+
ACell oldBase=base.getAndUpdate(bv->{
47+
V oldValue=RT.getIn(bv, path);
48+
ACell newValue=updateFunction.apply(oldValue);
49+
return RT.assocIn(bv,newValue,path);
50+
});
51+
return (V) RT.getIn(oldBase, path);
52+
}
53+
54+
@SuppressWarnings("unchecked")
55+
@Override
56+
public V updateAndGet(UnaryOperator<V> updateFunction) {
57+
ACell[] nv=new ACell[1];
58+
base.updateAndGet(bv->{
59+
V oldValue=RT.getIn(bv, path);
60+
V newValue=updateFunction.apply(oldValue);
61+
nv[0]=newValue;
62+
return RT.assocIn(bv,newValue,(ACell[])path);
63+
});
64+
return (V)nv[0];
65+
}
66+
67+
@SuppressWarnings("unchecked")
68+
@Override
69+
public V getAndAccumulate(V x, BinaryOperator<V> accumulatorFunction) {
70+
ACell oldBase=base.getAndUpdate(bv->{
71+
V oldValue=RT.getIn(bv, path);
72+
ACell newValue=accumulatorFunction.apply(x,oldValue);
73+
return RT.assocIn(bv,newValue,path);
74+
});
75+
return (V) RT.getIn(oldBase, path);
76+
}
77+
78+
@SuppressWarnings("unchecked")
79+
@Override
80+
public V accumulateAndGet(V x, BinaryOperator<V> accumulatorFunction) {
81+
ACell[] nv=new ACell[1];
82+
base.updateAndGet(bv->{
83+
V oldValue=RT.getIn(bv, path);
84+
V newValue=accumulatorFunction.apply(x,oldValue);
85+
nv[0]=newValue;
86+
return RT.assocIn(bv,newValue,(ACell[])path);
87+
});
88+
return (V)nv[0];
89+
}
90+
91+
@Override
92+
public boolean compareAndSet(V expected, V newValue) {
93+
boolean[] updated=new boolean[1];
94+
base.update(bv->{
95+
V oldValue=RT.getIn(bv, path);
96+
if(Utils.equals(expected,oldValue)) {
97+
updated[0]=true;
98+
return RT.assocIn(bv,newValue,(ACell[])path);
99+
} else {
100+
return bv;
101+
}
102+
});
103+
return updated[0];
104+
}
105+
106+
107+
108+
109+
110+
111+
}

convex-core/src/test/java/convex/lattice/CursorTest.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,59 @@
88

99
import org.junit.jupiter.api.Test;
1010

11+
import convex.core.cvm.Keywords;
12+
import convex.core.cvm.Symbols;
13+
import convex.core.data.AIndex;
14+
import convex.core.data.AMap;
15+
import convex.core.data.AVector;
16+
import convex.core.data.Index;
17+
import convex.core.data.Keyword;
18+
import convex.core.data.Maps;
19+
import convex.core.data.Vectors;
1120
import convex.core.data.prim.AInteger;
1221
import convex.core.data.prim.CVMLong;
1322

1423
public class CursorTest {
1524

1625
@Test public void testRoot() {
1726
Root<AInteger> root=new Root<>();
18-
CVMLong TWO=CVMLong.create(2);
27+
doIntCursorTest(root);
28+
}
29+
30+
@Test public void testPathCursor() {
31+
Root<AInteger> root=new Root<>();
32+
PathCursor<AInteger> pc=new PathCursor<AInteger>(root,Symbols.FOO);
33+
doIntCursorTest(pc);
34+
}
35+
36+
@Test public void testVectorPathCursor() {
37+
Root<AInteger> root=new Root<>();
38+
PathCursor<AVector<AInteger>> vpc=new PathCursor<>(root,Symbols.BAR);
39+
vpc.set(Vectors.of(567,null,78));
40+
PathCursor<AInteger> pc=new PathCursor<>(vpc,CVMLong.ONE);
41+
doIntCursorTest(pc);
42+
}
43+
44+
@Test public void testIndexPathCursor() {
45+
Root<AInteger> root=new Root<>();
46+
PathCursor<AIndex<Keyword,AInteger>> vpc=new PathCursor<>(root,Keywords.FOO);
47+
vpc.set(Index.of(Keywords.FOO,null,Keywords.BAR,3));
48+
PathCursor<AInteger> pc=new PathCursor<>(vpc,Keywords.FOO);
49+
doIntCursorTest(pc);
50+
}
51+
52+
@Test public void testMapPathCursor() {
53+
Root<AInteger> root=new Root<>();
54+
PathCursor<AMap<Keyword,AInteger>> vpc=new PathCursor<>(root,Keywords.FOO);
55+
vpc.set(Maps.of(Keywords.FOO,null,Keywords.BAR,3));
56+
PathCursor<AInteger> pc=new PathCursor<>(vpc,Keywords.FOO);
57+
doIntCursorTest(pc);
58+
}
59+
60+
private void doIntCursorTest(ACursor<AInteger> root) {
61+
assertEquals("nil",root.toString());
62+
63+
CVMLong TWO=CVMLong.create(2); // just another value for testing
1964

2065
root.set(1); // value is now 1
2166
assertCVMEquals(1,root.get());
@@ -33,5 +78,7 @@ public class CursorTest {
3378
assertCVMEquals(4,root.getAndAccumulate(CVMLong.ONE,(a,b)->a.add(b))); // value is now 5;
3479
assertCVMEquals(7,root.accumulateAndGet(TWO,(a,b)->a.add(b))); // value is now 7;
3580

81+
assertEquals("7",root.toString());
3682
}
83+
3784
}

0 commit comments

Comments
 (0)