relation_graph.bend
bendvy-ecs-core@0.1.0.1 · 130 lines · 0 definitions
1import Base
2import entity_key.bend as K
3import relation_types.bend as T
4
5# Experimental maintained inverse implementation. Does not call graph-spec.
6def key(~S: Data,descriptor: T.Descriptor<S>) -> U32:
7 match descriptor:
8 case T.Descriptor{key,_,_,_}: key
9def name(~S: Data,descriptor: T.Descriptor<S>) -> String:
10 match descriptor:
11 case T.Descriptor{_,name,_,_}: name
12def contains(xs: List<&2,K.Key>,+id: K.Key) -> Bool:
13 match xs:
14 case []: False{}
15 case x <> rest: Bool.or(K.equal(x,id),contains(rest,id))
16def removed_id(remove: Bool,id: K.Key,rest: List<&2,K.Key>) -> List<&2,K.Key>:
17 match remove:
18 case True{}: rest
19 case False{}: id <> rest
20def remove_id(xs: List<&2,K.Key>,+id: K.Key) -> List<&2,K.Key>:
21 match xs:
22 case []: []
23 case +x <> rest: removed_id(K.equal(x,id),x,remove_id(rest,id))
24def chosen_target(found: Bool,target: K.Key,next: Unit -> Maybe<&2,K.Key>) -> Maybe<&2,K.Key>:
25 match found:
26 case True{}: Some{target}
27 case False{}: next(Unit{})
28def target_edges(~S: Data,edges: List<&2,T.Edge<S>>,+descriptor: T.Descriptor<S>,+source: K.Key) -> Maybe<&2,K.Key>:
29 match edges:
30 case []: None{}
31 case T.Edge{other,id,target} <> rest: chosen_target(Bool.and(U32.is_eq(key(~S,other),key(~S,descriptor)),K.equal(id,source)),target,_ => target_edges(~S,rest,descriptor,source))
32def target(~S: Data,graph: T.Graph<S>,descriptor: T.Descriptor<S>,source: K.Key) -> Maybe<&2,K.Key>:
33 match graph:
34 case T.Graph{edges,_}: target_edges(~S,edges,descriptor,source)
35def chosen_inverse(found: Bool,sources: List<&2,K.Key>,next: Unit -> List<&2,K.Key>) -> List<&2,K.Key>:
36 match found:
37 case True{}: sources
38 case False{}: next(Unit{})
39def inverse_entries(~S: Data,entries: List<&2,T.Inverse<S>>,+descriptor: T.Descriptor<S>,+target: K.Key) -> List<&2,K.Key>:
40 match entries:
41 case []: []
42 case T.Inverse{other,id,sources} <> rest: chosen_inverse(Bool.and(U32.is_eq(key(~S,other),key(~S,descriptor)),K.equal(id,target)),sources,_ => inverse_entries(~S,rest,descriptor,target))
43def inverse(~S: Data,graph: T.Graph<S>,descriptor: T.Descriptor<S>,target: K.Key) -> List<&2,K.Key>:
44 match graph:
45 case T.Graph{_,entries}: inverse_entries(~S,entries,descriptor,target)
46def edge_removed(~S: Data,remove: Bool,edge: T.Edge<S>,rest: List<&2,T.Edge<S>>) -> List<&2,T.Edge<S>>:
47 match remove:
48 case True{}: rest
49 case False{}: edge <> rest
50def remove_edge_fields(~S: Data,edge: T.Edge<S>,+descriptor: T.Descriptor<S>,source: K.Key,next: Unit -> List<&2,T.Edge<S>>) -> List<&2,T.Edge<S>>:
51 match edge:
52 case T.Edge{+other,+id,+target}: edge_removed(~S,Bool.and(U32.is_eq(key(~S,other),key(~S,descriptor)),K.equal(id,source)),T.Edge{other,id,target},next(Unit{}))
53def remove_edge_keep(~S:Data,edge:T.Edge<S>,+descriptor:T.Descriptor<S>,source:K.Key,acc:List<&2,T.Edge<S>>) -> List<&2,T.Edge<S>>:
54 match edge:
55 case T.Edge{+other,+id,+target}: edge_removed(~S,Bool.and(U32.is_eq(key(~S,other),key(~S,descriptor)),K.equal(id,source)),T.Edge{other,id,target},acc)
56def remove_edge_acc(~S:Data,edges:List<&2,T.Edge<S>>,+descriptor:T.Descriptor<S>,+source:K.Key,acc:List<&2,T.Edge<S>>) -> List<&2,T.Edge<S>>:
57 match edges:
58 case []: List.reverse(&2,T.Edge<S>,acc)
59 case +edge <> rest: remove_edge_acc(~S,rest,descriptor,source,remove_edge_keep(~S,edge,descriptor,source,acc))
60def remove_edge(~S: Data,edges: List<&2,T.Edge<S>>,descriptor: T.Descriptor<S>,source: K.Key) -> List<&2,T.Edge<S>>:
61 remove_edge_acc(~S,edges,descriptor,source,[])
62def nonempty_inverse(~S: Data,descriptor: T.Descriptor<S>,target: K.Key,sources: List<&2,K.Key>,rest: List<&2,T.Inverse<S>>) -> List<&2,T.Inverse<S>>:
63 match sources:
64 case []: rest
65 case head <> tail: T.Inverse{descriptor,target,head <> tail} <> rest
66def inverse_remove_one(~S: Data,matched: Bool,descriptor: T.Descriptor<S>,target: K.Key,sources: List<&2,K.Key>,source: K.Key,rest: List<&2,T.Inverse<S>>) -> List<&2,T.Inverse<S>>:
67 match matched:
68 case True{}: nonempty_inverse(~S,descriptor,target,remove_id(sources,source),rest)
69 case False{}: T.Inverse{descriptor,target,sources} <> rest
70def inverse_remove_acc(~S:Data,entries:List<&2,T.Inverse<S>>,+descriptor:T.Descriptor<S>,+target:K.Key,+source:K.Key,acc:List<&2,T.Inverse<S>>) -> List<&2,T.Inverse<S>>:
71 match entries:
72 case []: List.reverse(&2,T.Inverse<S>,acc)
73 case T.Inverse{+other,+id,sources} <> rest: inverse_remove_acc(~S,rest,descriptor,target,source,inverse_remove_one(~S,Bool.and(U32.is_eq(key(~S,other),key(~S,descriptor)),K.equal(id,target)),other,id,sources,source,acc))
74def inverse_remove(~S: Data,entries: List<&2,T.Inverse<S>>,descriptor: T.Descriptor<S>,target: K.Key,source: K.Key) -> List<&2,T.Inverse<S>>:
75 inverse_remove_acc(~S,entries,descriptor,target,source,[])
76def remove_previous(~S: Data,previous: Maybe<&2,K.Key>,entries: List<&2,T.Inverse<S>>,descriptor: T.Descriptor<S>,source: K.Key) -> List<&2,T.Inverse<S>>:
77 match previous:
78 case None{}: entries
79 case Some{target}: inverse_remove(~S,entries,descriptor,target,source)
80def appended_source(present: Bool,sources: List<&2,K.Key>,source: K.Key) -> List<&2,K.Key>:
81 match present:
82 case True{}: sources
83 case False{}: List.append(&2,K.Key,sources,[source])
84def inverse_add_found(~S: Data,found: Bool,other: T.Descriptor<S>,id: K.Key,+sources: List<&2,K.Key>,+descriptor: T.Descriptor<S>,target: K.Key,+source: K.Key,rest: List<&2,T.Inverse<S>>,next: Unit -> List<&2,T.Inverse<S>>) -> List<&2,T.Inverse<S>>:
85 match found:
86 case True{}: T.Inverse{other,id,appended_source(contains(sources,source),sources,source)} <> rest
87 case False{}: T.Inverse{other,id,sources} <> next(Unit{})
88def inverse_add(~S: Data,entries: List<&2,T.Inverse<S>>,+descriptor: T.Descriptor<S>,+target: K.Key,+source: K.Key) -> List<&2,T.Inverse<S>>:
89 match entries:
90 case []: [T.Inverse{descriptor,target,[source]}]
91 case T.Inverse{+other,+id,+sources} <> +rest: inverse_add_found(~S,Bool.and(U32.is_eq(key(~S,other),key(~S,descriptor)),K.equal(id,target)),other,id,sources,descriptor,target,source,rest,_ => inverse_add(~S,rest,descriptor,target,source))
92def unrelate(~S: Data,graph: T.Graph<S>,+descriptor: T.Descriptor<S>,+source: K.Key) -> T.Graph<S>:
93 match graph:
94 case T.Graph{+edges,inverses}: T.Graph{remove_edge(~S,edges,descriptor,source),remove_previous(~S,target_edges(~S,edges,descriptor,source),inverses,descriptor,source)}
95def next_cycle(~S: Data,current: Maybe<&2,K.Key>,next: K.Key -> Bool) -> Bool:
96 match current:
97 case None{}: False{}
98 case Some{id}: next(id)
99def cycle_loop(~S: Data,fuel: Nat,+graph: T.Graph<S>,+descriptor: T.Descriptor<S>,+source: K.Key,+current: K.Key) -> Bool:
100 match fuel:
101 case 0n: K.equal(source,current)
102 case 1n+rest: Bool.or(K.equal(source,current),next_cycle(~S,target(~S,graph,descriptor,current),id => cycle_loop(~S,rest,graph,descriptor,source,id)))
103def cycle(~S: Data,+graph: T.Graph<S>,descriptor: T.Descriptor<S>,source: K.Key,target: K.Key) -> Bool:
104 match graph descriptor:
105 case T.Graph{edges,inverses} T.Descriptor{k,n,inv,T.Ordinary{}}: False{}
106 case T.Graph{+edges,+inverses} T.Descriptor{+k,+n,+inv,T.Hierarchy{}}: cycle_loop(~S,List.length(&2,T.Edge<S>,edges),T.Graph{edges,inverses},T.Descriptor{k,n,inv,T.Hierarchy{}},source,target)
107def validation(sourceLive: Bool,targetLive: Bool,self: Bool,cyclic: Bool,source: K.Key,target: K.Key,name: String) -> Maybe<&2,T.Error>:
108 match sourceLive targetLive self cyclic:
109 case False{} _ _ _: Some{T.MissingEntity{source}}
110 case True{} False{} _ _: Some{T.MissingTargetEntity{source,target,name}}
111 case True{} True{} True{} _: Some{T.SelfRelationNotAllowed{source,name}}
112 case True{} True{} False{} True{}: Some{T.HierarchyCycle{source,target,name}}
113 case True{} True{} False{} False{}: None{}
114def already(previous: Maybe<&2,K.Key>,target: K.Key) -> Bool:
115 match previous:
116 case None{}: False{}
117 case Some{id}: K.equal(id,target)
118def updated(~S: Data,same: Bool,+edges: List<&2,T.Edge<S>>,inverses: List<&2,T.Inverse<S>>,+descriptor: T.Descriptor<S>,+source: K.Key,+target: K.Key) -> T.Graph<S>:
119 match same:
120 case True{}: T.Graph{edges,inverses}
121 case False{}: T.Graph{List.append(&2,T.Edge<S>,remove_edge(~S,edges,descriptor,source),[T.Edge{descriptor,source,target}]),inverse_add(~S,remove_previous(~S,target_edges(~S,edges,descriptor,source),inverses,descriptor,source),descriptor,target,source)}
122def checked(~S: Data,error: Maybe<&2,T.Error>,graph: T.Graph<S>,+descriptor: T.Descriptor<S>,+source: K.Key,+target: K.Key) -> T.CoreResult<S>:
123 match error graph:
124 case Some{error} graph: T.CoreResult{graph,Some{error}}
125 case None{} T.Graph{+edges,inverses}: T.CoreResult{updated(~S,already(target_edges(~S,edges,descriptor,source),target),edges,inverses,descriptor,source,target),None{}}
126def relate(~S: Data,+graph: T.Graph<S>,+descriptor: T.Descriptor<S>,+source: K.Key,+target: K.Key,sourceLive: Bool,targetLive: Bool) -> T.CoreResult<S>:
127 checked(~S,validation(sourceLive,targetLive,K.equal(source,target),cycle(~S,graph,descriptor,source,target),source,target,name(~S,descriptor)),graph,descriptor,source,target)
128def project(~S: Data,graph: T.Graph<S>) -> T.SpecGraph<S>:
129 match graph:
130 case T.Graph{edges,_}: T.SpecGraph{edges}