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
62
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))