execution_query_admission.bend

bendvy-ecs-core@0.1.0.3 · 148 lines · 32 definitions

1import Base
2import bendvy-ecs-core@0.1.0.3/execution_query_recipe.bend as Recipe
3import bendvy-ecs-core@0.1.0.3/execution_query_schema.bend as Schema
4import bendvy-ecs-core@0.1.0.3/execution_query_domain.bend as D
5import bendvy-ecs-core@0.1.0.3/execution_query_matches.bend as Match
6import bendvy-ecs-core@0.1.0.3/execution_query_identity.bend as Id
7import bendvy-ecs-core@0.1.0.3/execution_composer_layout.bend as L
8import bendvy-ecs-core@0.1.0.3/world.bend as W
9import bendvy-ecs-core@0.1.0.3/entity_key.bend as K
10import bendvy-ecs-core@0.1.0.3/indexed_lifecycle.bend as Indexed
11import bendvy-ecs-core@0.1.0.3/chunk_column.bend as Col
12
13type Granted<-S:Data,-WC:Type> is Data:
14  Granted{entry:Recipe.Entry,handles:List<&2,W.Handle<S>>}
15type Lowering<-S:Data,-WC:Type,-full:L.Layout> is Data:
16  Lowered{grants:List<&2,Granted<S,WC>>,book:Schema.Book<S,full>}
17  LoweringRefused{}
18def info_matches(route:L.Route,info:Schema.Info) -> Bool:
19  match info:
20    case Schema.Info{at,_,_}:Schema.same_route(route,at)
21def info_selected(info:Schema.Info,next:Maybe<&2,Schema.Info>,selected:Bool) -> Maybe<&2,Schema.Info>:
22  match selected:
23    case False{}:next
24    case True{}:Some{info}
25def info_at(+route:L.Route,info:List<&2,Schema.Info>) -> Maybe<&2,Schema.Info>:
26  match info:
27    case []:None{}
28    case +head <> rest:info_selected(head,info_at(route,rest),info_matches(route,head))
29def present(info:Maybe<&2,Schema.Info>,offset:U32,wanted:Bool) -> Bool:
30  match info:
31    case None{}:False{}
32    case Some{Schema.Info{_,_,domain}}:Bool.not(Bool.xor(D.contains(domain,offset),wanted))
33def bound(+info:List<&2,Schema.Info>,selection:Recipe.Bound,+offset:U32) -> Bool:
34  match selection:
35    case Recipe.AllLive{}:True{}
36    case Recipe.Changed{_}:True{}
37    case Recipe.With{route}:present(info_at(route,info),offset,True{})
38    case Recipe.Without{route}:present(info_at(route,info),offset,False{})
39    case Recipe.And{left,right}:Bool.and(bound(info,left,offset),bound(info,right,offset))
40    case Recipe.Or{left,right}:Bool.or(bound(info,left,offset),bound(info,right,offset))
41def selected_handle(~S:Data,handle:W.Handle<S>,rest:List<&2,W.Handle<S>>,selected:Bool) -> List<&2,W.Handle<S>>:
42  match selected:
43    case False{}:rest
44    case True{}:handle <> rest
45def candidate_offset(~S:Data,info:List<&2,Schema.Info>,selection:Recipe.Bound,handle:W.Handle<S>,rest:List<&2,W.Handle<S>>,offset:Maybe<&2,U32>) -> List<&2,W.Handle<S>>:
46  match offset:
47    case None{}:rest
48    case Some{offset}:selected_handle(~S,handle,rest,bound(info,selection,offset))
49def candidates(~S:Data,+info:List<&2,Schema.Info>,+selection:Recipe.Bound,handles:List<&2,W.Handle<S>>) -> List<&2,W.Handle<S>>:
50  match handles:
51    case []:[]
52    case +handle <> rest:candidate_offset(~S,info,selection,handle,candidates(~S,info,selection,rest),Id.physical_index(W.handle_index(~S,handle)))
53def capacity(info:Maybe<&2,Schema.Info>) -> U32:
54  match info:
55    case None{}:0
56    case Some{Schema.Info{_,capacity,_}}:capacity
57def physical_cons(offset:U32,rest:List<&2,U32>,fits:Bool) -> List<&2,U32>:
58  match fits:
59    case False{}:rest
60    case True{}:offset <> rest
61# Beyond-storage optional absence remains in Granted.handles. Only physical
62# owners that actually exist contribute physical-domain validation metadata.
63def physical_checked(capacity:U32,rest:List<&2,U32>,offset:Maybe<&2,U32>) -> List<&2,U32>:
64  match offset:
65    case None{}:rest
66    case Some{+offset}:physical_cons(offset,rest,U32.is_lt(offset,capacity))
67def physical(~S:Data,+capacity:U32,handles:List<&2,W.Handle<S>>) -> List<&2,U32>:
68  match handles:
69    case []:[]
70    case +handle <> rest:physical_checked(capacity,physical(~S,capacity,rest),Id.physical_index(W.handle_index(~S,handle)))
71def physical_recipe(kind:Recipe.QueryKind,domain:D.Domain) -> Match.Recipe:
72  match kind:
73    case Recipe.ReadRows{}:Match.Read{domain}
74    case Recipe.WriteRows{}:Match.Write{domain}
75def handles_valid(~S:Data,+namespace:U32,handles:List<&2,W.Handle<S>>) -> Bool:
76  match handles:
77    case []:True{}
78    case W.Handle{hn,key} <> rest:Bool.and(U32.is_eq(namespace,hn),Bool.and(Indexed.normal(K.index(key)),handles_valid(~S,namespace,rest)))
79def public_indexes(~S:Data,handles:List<&2,W.Handle<S>>) -> List<&2,U32>:
80  match handles:
81    case []:[]
82    case handle <> rest:W.handle_index(~S,handle) <> public_indexes(~S,rest)
83def recipe_valid(~S:Data,~WC:Type,~full:L.Layout,recipe:Recipe.Recipe<S,WC>) -> Bool:
84  match recipe:
85    case Recipe.Recipe{Recipe.Entry{route,_,selection,_}}:Bool.and(L.valid(full,route),Recipe.bound_valid(~full,selection))
86def recipes_valid(~S:Data,~WC:Type,~full:L.Layout,recipes:List<&2,Recipe.Recipe<S,WC>>) -> Bool:
87  match recipes:
88    case []:True{}
89    case recipe <> rest:Bool.and(recipe_valid(~S,~WC,~full,recipe),recipes_valid(~S,~WC,~full,rest))
90type LoweredEntries<-S:Data,-WC:Type> is Data:
91  LoweredEntries{grants:List<&2,Granted<S,WC>>,entries:List<&2,Schema.Entry>}
92def bound_entries(~S:Data,+info:List<&2,Schema.Info>,+handles:List<&2,W.Handle<S>>,selection:Recipe.Bound) -> List<&2,Schema.Entry>:
93  match selection:
94    case Recipe.AllLive{}:[]
95    case Recipe.Changed{+route}:[Schema.Entry{route,Match.Read{D.Domain{physical(~S,capacity(info_at(route,info)),handles)}}}]
96    case Recipe.With{+route}:[Schema.Entry{route,Match.Read{D.Domain{physical(~S,capacity(info_at(route,info)),handles)}}}]
97    case Recipe.Without{+route}:[Schema.Entry{route,Match.Read{D.Domain{physical(~S,capacity(info_at(route,info)),handles)}}}]
98    case Recipe.And{left,right}:List.append(&2,Schema.Entry,bound_entries(~S,info,handles,left),bound_entries(~S,info,handles,right))
99    case Recipe.Or{left,right}:List.append(&2,Schema.Entry,bound_entries(~S,info,handles,left),bound_entries(~S,info,handles,right))
100def lowered_cons(~S:Data,~WC:Type,+route:L.Route,+kind:Recipe.QueryKind,selection:Recipe.Bound,presence:Match.Presence,+handles:List<&2,W.Handle<S>>,capacity:U32,boundReads:List<&2,Schema.Entry>,rest:LoweredEntries<S,WC>) -> LoweredEntries<S,WC>:
101  match rest:
102    case LoweredEntries{grants,entries}:LoweredEntries{Granted{Recipe.Entry{route,kind,selection,presence},handles} <> grants,Schema.Entry{route,physical_recipe(kind,D.Domain{physical(~S,capacity,handles)})} <> List.append(&2,Schema.Entry,boundReads,entries)}
103def lower_entries(~S:Data,~WC:Type,recipes:List<&2,Recipe.Recipe<S,WC>>,+info:List<&2,Schema.Info>,+handles:List<&2,W.Handle<S>>) -> LoweredEntries<S,WC>:
104  match recipes:
105    case []:LoweredEntries{[],[]}
106    case Recipe.Recipe{Recipe.Entry{+route,kind,+selection,presence}} <> rest:lowered_cons(~S,~WC,route,kind,selection,presence,candidates(~S,info,selection,handles),capacity(info_at(route,info)),bound_entries(~S,info,handles,selection),lower_entries(~S,~WC,rest,info,handles))
107def lowered(~S:Data,~WC:Type,~full:L.Layout,result:LoweredEntries<S,WC>) -> Lowering<S,WC,full>:
108  match result:
109    case LoweredEntries{grants,entries}:Lowered{grants,Schema.Book{entries}}
110def lower_checked(~S:Data,~WC:Type,~full:L.Layout,recipes:List<&2,Recipe.Recipe<S,WC>>,info:List<&2,Schema.Info>,handles:List<&2,W.Handle<S>>,valid:Bool) -> Lowering<S,WC,full>:
111  match valid:
112    case False{}:LoweringRefused{}
113    case True{}:lowered(~S,~WC,~full,lower_entries(~S,~WC,recipes,info,handles))
114def lower(~S:Data,~WC:Type,~full:L.Layout,~same:{WC == L.columns(~S,full) : Type},+recipes:List<&2,Recipe.Recipe<S,WC>>,+info:List<&2,Schema.Info>,namespace:U32,+handles:List<&2,W.Handle<S>>) -> Lowering<S,WC,full>:
115  lower_checked(~S,~WC,~full,recipes,info,handles,Bool.and(handles_valid(~S,namespace,handles),Bool.and(Id.unique(~S,handles),Bool.and(Match.inventory_valid(D.validate(U32.add(Col.maximum_capacity(),1),D.Domain{public_indexes(~S,handles)})),recipes_valid(~S,~WC,~full,recipes)))))
116
117def same_kind(left:Recipe.QueryKind,right:Recipe.QueryKind) -> Bool:
118  match left right:
119    case Recipe.ReadRows{} Recipe.ReadRows{}:True{}
120    case Recipe.WriteRows{} Recipe.WriteRows{}:True{}
121    case _ _:False{}
122def same_presence(left:Match.Presence,right:Match.Presence) -> Bool:
123  match left right:
124    case Match.Optional{} Match.Optional{}:True{}
125    case Match.Required{} Match.Required{}:True{}
126    case Match.With{} Match.With{}:True{}
127    case Match.Without{} Match.Without{}:True{}
128    case _ _:False{}
129def same_bound(left:Recipe.Bound,right:Recipe.Bound) -> Bool:
130  match left right:
131    case Recipe.AllLive{} Recipe.AllLive{}:True{}
132    case Recipe.Changed{l} Recipe.Changed{r}:Schema.same_route(l,r)
133    case Recipe.With{l} Recipe.With{r}:Schema.same_route(l,r)
134    case Recipe.Without{l} Recipe.Without{r}:Schema.same_route(l,r)
135    case Recipe.And{ll,lr} Recipe.And{rl,rr}:Bool.and(same_bound(ll,rl),same_bound(lr,rr))
136    case Recipe.Or{ll,lr} Recipe.Or{rl,rr}:Bool.and(same_bound(ll,rl),same_bound(lr,rr))
137    case _ _:False{}
138def same_entry(left:Recipe.Entry,right:Recipe.Entry) -> Bool:
139  match left right:
140    case Recipe.Entry{lr,lk,lb,lp} Recipe.Entry{rr,rk,rb,rp}:Bool.and(Schema.same_route(lr,rr),Bool.and(same_kind(lk,rk),Bool.and(same_bound(lb,rb),same_presence(lp,rp))))
141def grant_selected(~S:Data,handles:List<&2,W.Handle<S>>,next:Maybe<&2,List<&2,W.Handle<S>>>,selected:Bool) -> Maybe<&2,List<&2,W.Handle<S>>>:
142  match selected:
143    case False{}:next
144    case True{}:Some{handles}
145def grant_handles(~S:Data,~WC:Type,+entry:Recipe.Entry,grants:List<&2,Granted<S,WC>>) -> Maybe<&2,List<&2,W.Handle<S>>>:
146  match grants:
147    case []:None{}
148    case Granted{at,handles} <> rest:grant_selected(~S,handles,grant_handles(~S,~WC,entry,rest),same_entry(entry,at))