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}