1use super::sites::split_keyed_listener_event;
2use super::*;
3use crate::ast::component::{ComponentDef, Trigger};
4use crate::ast::stmt::{BinaryOpKind, Expr, Literal, Stmt, TypeExpr};
5use crate::ast::view::BoundInput;
6use crate::lgx::html::EventModifier;
7use logicaffeine_base::Symbol;
8
9#[derive(Clone)]
17pub enum ListenerSite {
18 Action {
25 component: Symbol,
26 action: Symbol,
27 argc: usize,
28 key: Option<Symbol>,
29 carries_value: bool,
33 once: Option<Symbol>,
37 },
38 Bind {
40 component: Symbol,
41 field: Symbol,
42 checked: bool,
43 },
44 DefaultToggle {
49 component: Symbol,
50 field: Symbol,
51 radio: bool,
52 },
53 CallbackProp {
59 component: Symbol,
60 prop: Symbol,
61 argc: usize,
62 key: Option<Symbol>,
63 once: Option<Symbol>,
64 },
65 ScrollNear { component: Symbol, action: Symbol },
69}
70
71impl ListenerSite {
72 pub(crate) fn once_local(&self) -> Option<Symbol> {
74 match self {
75 ListenerSite::Action { once, .. } | ListenerSite::CallbackProp { once, .. } => *once,
76 ListenerSite::Bind { .. }
77 | ListenerSite::DefaultToggle { .. }
78 | ListenerSite::ScrollNear { .. } => None,
79 }
80 }
81
82 pub(crate) fn waits_for(&self) -> Option<Symbol> {
84 match self {
85 ListenerSite::Action { key, .. } | ListenerSite::CallbackProp { key, .. } => *key,
86 ListenerSite::Bind { .. }
87 | ListenerSite::DefaultToggle { .. }
88 | ListenerSite::ScrollNear { .. } => None,
89 }
90 }
91}
92
93pub const DROP_TARGET_CANCELS: &[&str] = &["dragenter", "dragover", "drop"];
97
98pub(crate) fn action_tag(
102 all: &[ComponentDef<'_>],
103 component: Symbol,
104 action: Symbol,
105) -> Option<i64> {
106 all.iter()
107 .flat_map(|c| c.actions.iter().map(move |a| (c.name, a.name)))
108 .position(|(c, a)| c == component && a == action)
109 .map(|i| i as i64)
110}
111
112pub(crate) fn callback_candidates<'a>(
116 all: &[ComponentDef<'a>],
117 owners: &[Symbol],
118 signature: &[&'a TypeExpr<'a>],
119) -> Vec<(i64, Symbol, Symbol)> {
120 let mut out = Vec::new();
121 let mut tag = 0i64;
122 for c in all {
123 for a in &c.actions {
124 let fits = owners.contains(&c.name)
125 && a.params.len() == signature.len()
126 && a.params
127 .iter()
128 .zip(signature.iter())
129 .all(|((_, p), s)| param_shapes_match(p, s));
130 if fits {
131 out.push((tag, c.name, a.name));
132 }
133 tag += 1;
134 }
135 }
136 out
137}
138
139impl<'a, 'ctx, 'int> super::super::Parser<'a, 'ctx, 'int> {
140 pub(crate) fn callback_prop_registration(
146 &mut self,
147 value: &'a Expr<'a>,
148 owner: &ComponentDef<'a>,
149 child: Symbol,
150 prop: Symbol,
151 child_key: &'a Expr<'a>,
152 all: &[ComponentDef<'a>],
153 ) -> Vec<Stmt<'a>> {
154 let Some(child_def) = all.iter().find(|d| d.name == child) else {
155 return Vec::new();
156 };
157 let Some((_, ty)) = child_def.props.iter().find(|(p, _)| *p == prop).copied() else {
158 return Vec::new();
159 };
160 if !is_callback_type(ty, self.interner) {
161 return Vec::new();
162 }
163 let (tag_expr, key_expr) = match callback_source(value, owner, self.interner) {
164 Some(CallbackSource::Action(action)) => {
165 let Some(tag) = action_tag(all, owner.name, action.name) else {
166 return Vec::new();
167 };
168 (self.te_int(tag), self.key_expr())
169 }
170 Some(CallbackSource::Forwarded(own)) => {
171 let oname = self.interner.resolve(owner.name).to_string();
172 let pname = self.interner.resolve(own).to_string();
173 (
174 self.callback_identity_read(&oname, &pname, true),
175 self.callback_identity_read(&oname, &pname, false),
176 )
177 }
178 None => return Vec::new(),
179 };
180 let component = self.interner.resolve(child).to_string();
181 let p = self.interner.resolve(prop).to_string();
182 let tag_map = self.interner.intern(&callback_tag_binding(&component, &p));
183 let key_map = self.interner.intern(&callback_key_binding(&component, &p));
184 vec![
185 Stmt::SetIndex {
186 collection: self.te_id(tag_map),
187 index: child_key,
188 value: tag_expr,
189 },
190 Stmt::SetIndex {
191 collection: self.te_id(key_map),
192 index: child_key,
193 value: key_expr,
194 },
195 ]
196 }
197
198 fn callback_identity_read(
201 &mut self,
202 component: &str,
203 prop: &str,
204 want_tag: bool,
205 ) -> &'a Expr<'a> {
206 let name = if want_tag {
207 callback_tag_binding(component, prop)
208 } else {
209 callback_key_binding(component, prop)
210 };
211 let global = self.interner.intern(&name);
212 let key = self.key_expr();
213 self.ctx.alloc_imperative_expr(Expr::Index {
214 collection: self.te_id(global),
215 index: key,
216 })
217 }
218
219 pub(crate) fn callback_prop_declarations(
227 &mut self,
228 def: &ComponentDef<'a>,
229 all: &[ComponentDef<'a>],
230 ) -> Vec<Stmt<'a>> {
231 let mut out = Vec::new();
232 let component = self.interner.resolve(def.name).to_string();
233 let map_of = self.interner.intern("mapOf");
234 let root = self.interner.intern("");
235 for (prop, _) in callback_props(def, self.interner) {
236 let p = self.interner.resolve(prop).to_string();
237 let tag_var = self.interner.intern(&callback_tag_binding(&component, &p));
238 let tag_value = self.te_call(map_of, vec![self.te_text(root), self.te_int(0)]);
239 out.push(Stmt::Let {
240 var: tag_var,
241 ty: None,
242 value: tag_value,
243 mutable: true,
244 });
245 let key_var = self.interner.intern(&callback_key_binding(&component, &p));
246 let key_value = self.te_call(map_of, vec![self.te_text(root), self.te_text(root)]);
247 out.push(Stmt::Let {
248 var: key_var,
249 ty: None,
250 value: key_value,
251 mutable: true,
252 });
253 }
254 for (var, ty) in self.callback_arg_maps(def, all) {
255 let seed = self.zero_of_type(ty);
256 let value = self.te_call(map_of, vec![self.te_int(0), seed]);
257 out.push(Stmt::Let {
258 var,
259 ty: None,
260 value,
261 mutable: true,
262 });
263 }
264 out
265 }
266
267 pub(crate) fn callback_prop_identity_map_names(
270 &mut self,
271 def: &ComponentDef<'a>,
272 ) -> Vec<Symbol> {
273 let mut out = Vec::new();
274 let component = self.interner.resolve(def.name).to_string();
275 for (prop, _) in callback_props(def, self.interner) {
276 let p = self.interner.resolve(prop).to_string();
277 out.push(self.interner.intern(&callback_tag_binding(&component, &p)));
278 out.push(self.interner.intern(&callback_key_binding(&component, &p)));
279 }
280 out
281 }
282
283 pub(crate) fn callback_arg_maps(
288 &mut self,
289 def: &ComponentDef<'a>,
290 all: &[ComponentDef<'a>],
291 ) -> Vec<(Symbol, &'a TypeExpr<'a>)> {
292 let mut out = Vec::new();
293 let component = self.interner.resolve(def.name).to_string();
294 for prop in callback_prop_targets(def) {
295 let Some(signature) = callback_signature(all, def.name, prop, self.interner) else {
296 continue;
297 };
298 let p = self.interner.resolve(prop).to_string();
299 for (i, ty) in signature.into_iter().enumerate() {
300 out.push((
301 self.interner
302 .intern(&callback_arg_binding(&component, &p, i)),
303 ty,
304 ));
305 }
306 }
307 out
308 }
309
310 pub(crate) fn callback_trampoline(
320 &mut self,
321 def: &ComponentDef<'a>,
322 prop: Symbol,
323 all: &[ComponentDef<'a>],
324 owners: &[Symbol],
325 ) -> Stmt<'a> {
326 let component = self.interner.resolve(def.name).to_string();
327 let p = self.interner.resolve(prop).to_string();
328 let signature = callback_signature(all, def.name, prop, self.interner).unwrap_or_default();
329 let key = self.interner.intern(instance_key_param());
330 let tag_map = self.interner.intern(&callback_tag_binding(&component, &p));
331 let key_map = self.interner.intern(&callback_key_binding(&component, &p));
332 let known = self.ctx.alloc_imperative_expr(Expr::Contains {
333 collection: self.te_id(tag_map),
334 value: self.te_id(key),
335 });
336 let unknown = self.ctx.alloc_imperative_expr(Expr::Not { operand: known });
337 let mut body = vec![Stmt::If {
338 cond: unknown,
339 then_block: self.alloc_block(vec![Stmt::Return { value: None }]),
340 else_block: None,
341 }];
342 let tag_local = self.interner.intern("__lgx_cbtag");
343 let tag_read = self.ctx.alloc_imperative_expr(Expr::Index {
344 collection: self.te_id(tag_map),
345 index: self.te_id(key),
346 });
347 body.push(Stmt::Let {
348 var: tag_local,
349 ty: None,
350 value: tag_read,
351 mutable: false,
352 });
353 let owner_local = self.interner.intern("__lgx_cbkey");
354 let owner_read = self.ctx.alloc_imperative_expr(Expr::Index {
355 collection: self.te_id(key_map),
356 index: self.te_id(key),
357 });
358 body.push(Stmt::Let {
359 var: owner_local,
360 ty: None,
361 value: owner_read,
362 mutable: false,
363 });
364 let mut params = vec![(key, self.text_type())];
365 let mut call_args = vec![self.te_id(owner_local)];
366 for (i, ty) in signature.iter().enumerate() {
367 let name = self.interner.intern(&format!("__lgx_cb{i}"));
368 params.push((name, *ty));
369 call_args.push(self.te_id(name));
370 }
371 let mut chain: Vec<Stmt<'a>> = Vec::new();
372 for (tag, owner_c, owner_a) in callback_candidates(all, owners, &signature)
373 .into_iter()
374 .rev()
375 {
376 let oc = self.interner.resolve(owner_c).to_string();
377 let oa = self.interner.resolve(owner_a).to_string();
378 let act = self.interner.intern(&action_fn_name(&oc, &oa));
379 let cond = self.te_bin(BinaryOpKind::Eq, self.te_id(tag_local), self.te_int(tag));
380 let run = self.alloc_block(vec![Stmt::Call {
381 function: act,
382 args: call_args.clone(),
383 }]);
384 let otherwise = if chain.is_empty() {
385 None
386 } else {
387 Some(self.alloc_block(chain))
388 };
389 chain = vec![Stmt::If {
390 cond,
391 then_block: run,
392 else_block: otherwise,
393 }];
394 }
395 body.extend(chain);
396 let name = self
397 .interner
398 .intern(&callback_trampoline_fn_name(&component, &p));
399 Stmt::FunctionDef {
400 name,
401 generics: Vec::new(),
402 params,
403 body: self.alloc_block(body),
404 return_type: None,
405 is_native: false,
406 native_path: None,
407 is_exported: false,
408 export_target: None,
409 opt_flags: crate::optimization::OptimizationConfig::all_on(),
410 }
411 }
412
413 pub(crate) fn callback_invoker(
417 &mut self,
418 def: &ComponentDef<'a>,
419 prop: Symbol,
420 ) -> &'a Expr<'a> {
421 let all = self.lgx_all_components.clone();
422 let signature = callback_signature(&all, def.name, prop, self.interner).unwrap_or_default();
423 let component = self.interner.resolve(def.name).to_string();
424 let p = self.interner.resolve(prop).to_string();
425 let trampoline = self
426 .interner
427 .intern(&callback_trampoline_fn_name(&component, &p));
428 let mut params = Vec::new();
429 let mut args = vec![self.key_expr()];
430 for (i, ty) in signature.into_iter().enumerate() {
431 let name = self.interner.intern(&format!("__lgx_cb{i}"));
432 params.push((name, ty));
433 args.push(self.te_id(name));
434 }
435 let body = self.alloc_block(vec![Stmt::Call {
436 function: trampoline,
437 args,
438 }]);
439 self.ctx.alloc_imperative_expr(Expr::Closure {
440 params,
441 body: crate::ast::stmt::ClosureBody::Block(body),
442 return_type: None,
443 })
444 }
445
446 pub(crate) fn lower_lgx_dispatcher(&mut self, all: &[ComponentDef<'a>]) -> Option<Stmt<'a>> {
473 if self.lgx_components.is_empty() {
476 return None;
477 }
478 if all
479 .iter()
480 .any(|d| self.lgx_components.contains(&d.name) && has_any_listener_site(d))
481 {
482 let forget = self.forget_listener_fn(all);
483 self.deferred_functions.push(forget);
484 }
485 let commit = self.lower_lgx_commit();
489 self.deferred_functions.push(commit);
490 if self.instances_can_leave() {
491 self.declare_frag_registry();
492 let own = self.lower_lgx_own_frag();
493 self.deferred_functions.push(own);
494 let gone = self.lower_lgx_gone();
495 self.deferred_functions.push(gone);
496 }
497 if !self.drawn_directives().is_empty() {
498 self.declare_directive_queues();
499 let drain = self.lower_lgx_drain_directives();
500 self.deferred_functions.push(drain);
501 }
502 let selector = self.interner.intern("__lgx_sel");
503 let event = self.interner.intern("__lgx_ev");
504 let node = self.interner.intern("__lgx_node");
505 let lookup = self.interner.intern("__lgx_lk");
506 let id = self.interner.intern("__lgx_id");
507 let key = self.interner.intern(instance_key_param());
508 let mut body: Vec<Stmt<'a>> = Vec::new();
509
510 if uses_outbox(all) {
511 body.push(self.outbox_flush_arm(event));
512 }
513
514 let val = self.lgx_value_param();
534 let mut topics: Vec<(String, Vec<(Symbol, usize)>)> = Vec::new();
535 for d in all.iter().filter(|d| keeps_instance_registry(d)) {
536 for (index, handler) in d.events.iter().enumerate() {
537 let Trigger::Message { topic, .. } = &handler.trigger else {
538 continue;
539 };
540 let Expr::Literal(Literal::Text(t)) = topic else {
541 continue;
542 };
543 let name = self.interner.resolve(*t).to_string();
544 match topics.iter_mut().find(|(n, _)| *n == name) {
545 Some((_, subs)) => subs.push((d.name, index)),
546 None => topics.push((name, vec![(d.name, index)])),
547 }
548 }
549 }
550 for (topic, subs) in topics {
551 let ev_name = self.interner.intern(&format!("message:{topic}"));
552 let is_it = self.te_bin(BinaryOpKind::Eq, self.te_id(event), self.te_text(ev_name));
553 let mut fan: Vec<Stmt<'a>> = Vec::new();
554 for (component, index) in subs {
555 let name = self.interner.resolve(component).to_string();
556 let keys = self.interner.intern(&keys_binding(&name));
557 let seen = self.interner.intern(&seen_binding(&name));
558 let k = self.interner.intern("__lgx_mk");
559 let stamp = self.ctx.alloc_imperative_expr(Expr::Index {
560 collection: self.te_id(seen),
561 index: self.te_id(k),
562 });
563 let live = self.te_bin(BinaryOpKind::NotEq, stamp, self.te_int(-1));
564 let act = self
565 .interner
566 .intern(&action_fn_name(&name, &format!("__e{index}")));
567 let call = Stmt::Call {
568 function: act,
569 args: vec![self.te_id(k), self.te_id(val)],
570 };
571 let then_block = self.alloc_block(vec![call]);
572 let inner = self.alloc_block(vec![Stmt::If {
573 cond: live,
574 then_block,
575 else_block: None,
576 }]);
577 fan.push(Stmt::Repeat {
578 pattern: crate::ast::stmt::Pattern::Identifier(k),
579 iterable: self.te_id(keys),
580 body: inner,
581 });
582 }
583 fan.push(Stmt::Return {
584 value: Some(self.te_bool(true)),
585 });
586 body.push(Stmt::If {
587 cond: is_it,
588 then_block: self.alloc_block(fan),
589 else_block: None,
590 });
591 }
592
593 let query = self.interner.intern("querySelector");
598 let dispatch_node = self.interner.intern(dispatch_node_fn_name());
599 let found = self.te_call(query, vec![self.te_id(selector)]);
600 let carried = self.lgx_value_param();
601 let handed = self.te_call(
602 dispatch_node,
603 vec![found, self.te_id(event), self.te_id(carried)],
604 );
605 body.push(Stmt::Return {
606 value: Some(handed),
607 });
608 let selector_body = std::mem::take(&mut body);
609 let code = self.interner.intern("__lgx_code");
620 let keyed = self.interner.intern("__lgx_keyed");
621 body.push(Stmt::Let {
622 var: code,
623 ty: None,
624 value: self.te_int(-1),
625 mutable: true,
626 });
627 body.push(Stmt::Let {
628 var: keyed,
629 ty: None,
630 value: self.te_int(-1),
631 mutable: true,
632 });
633 let value = self.lgx_value_param();
634 let lower = self.interner.intern("lower");
635 for (i, entry) in self.lgx_listener_events.clone().iter().enumerate() {
636 let entry = self.interner.resolve(*entry).to_string();
637 let (cond, target) = match split_keyed_listener_event(&entry) {
638 Some((name, want)) => {
639 let name = self.interner.intern(name);
640 let want = self.interner.intern(want);
641 let is_event =
642 self.te_bin(BinaryOpKind::Eq, self.te_id(event), self.te_text(name));
643 let pressed = self.te_call(lower, vec![self.te_id(value)]);
644 let is_key = self.te_bin(BinaryOpKind::Eq, pressed, self.te_text(want));
645 (self.te_bin(BinaryOpKind::And, is_event, is_key), keyed)
646 }
647 None => {
648 let name = self.interner.intern(&entry);
649 (
650 self.te_bin(BinaryOpKind::Eq, self.te_id(event), self.te_text(name)),
651 code,
652 )
653 }
654 };
655 let set = Stmt::Set {
656 target,
657 value: self.te_int(i as i64),
658 };
659 body.push(Stmt::If {
660 cond,
661 then_block: self.alloc_block(vec![set]),
662 else_block: None,
663 });
664 }
665 let answer = self.interner.intern(answer_fn_name());
666 let answered = self.interner.intern("__lgx_answered");
667 body.push(Stmt::Let {
668 var: answered,
669 ty: None,
670 value: self.te_bool(false),
671 mutable: true,
672 });
673 if !self.lgx_listener_events.is_empty() {
689 let space = self.interner.intern(listener_space_binding());
690 let entries = self.te_int(self.lgx_listener_events.len() as i64);
691 self.deferred_functions.insert(
692 0,
693 Stmt::Let {
694 var: space,
695 ty: None,
696 value: entries,
697 mutable: false,
698 },
699 );
700 let more = self.interner.intern(listener_spill_binding());
701 let queue = self.interner.intern("__lgx_queue");
702 let int_name = self.interner.intern("Int");
703 let mut queued: Vec<Stmt<'a>> = Vec::new();
704 queued.push(Stmt::Let {
705 var: queue,
706 ty: None,
707 value: self.new_seq_expr(int_name),
708 mutable: true,
709 });
710 let anchor = self.interner.intern("__lgx_anchor");
711 let vnode = self.vd_expr("vdNodeOfWire", vec![self.te_id(node)]);
712 let site = self.vd_expr("vdSiteOf", vec![vnode]);
713 body.push(Stmt::Let {
714 var: anchor,
715 ty: None,
716 value: site,
717 mutable: false,
718 });
719 let plain_slot = self.interner.intern("__lgx_cs");
720 let scaled = self.te_bin(
721 BinaryOpKind::Multiply,
722 self.te_id(anchor),
723 self.te_id(space),
724 );
725 let composed = self.te_bin(BinaryOpKind::Add, scaled, self.te_id(code));
726 body.push(Stmt::Let {
727 var: plain_slot,
728 ty: None,
729 value: composed,
730 mutable: false,
731 });
732 for (which, slot, spilled) in [
733 (keyed, "__lgx_ks", "__lgx_ksp"),
734 (code, "__lgx_cs", "__lgx_csp"),
735 ] {
736 let slot = self.interner.intern(slot);
737 let spilled = self.interner.intern(spilled);
738 let scaled = self.te_bin(
739 BinaryOpKind::Multiply,
740 self.te_id(anchor),
741 self.te_id(space),
742 );
743 let composed = self.te_bin(BinaryOpKind::Add, scaled, self.te_id(which));
744 let has_more = self.ctx.alloc_imperative_expr(Expr::Contains {
745 collection: self.te_id(more),
746 value: self.te_id(slot),
747 });
748 let after = self.ctx.alloc_imperative_expr(Expr::Index {
749 collection: self.te_id(more),
750 index: self.te_id(slot),
751 });
752 let each_after = Stmt::Repeat {
753 pattern: crate::ast::stmt::Pattern::Identifier(spilled),
754 iterable: after,
755 body: self.alloc_block(vec![Stmt::Push {
756 value: self.te_id(spilled),
757 collection: self.te_id(queue),
758 }]),
759 };
760 let mut enqueue = Vec::new();
761 if slot != plain_slot {
762 enqueue.push(Stmt::Let {
763 var: slot,
764 ty: None,
765 value: composed,
766 mutable: false,
767 });
768 }
769 enqueue.push(Stmt::Push {
770 value: self.te_id(slot),
771 collection: self.te_id(queue),
772 });
773 enqueue.push(Stmt::If {
774 cond: has_more,
775 then_block: self.alloc_block(vec![each_after]),
776 else_block: None,
777 });
778 let registered = self.te_bin(BinaryOpKind::GtEq, self.te_id(which), self.te_int(0));
779 queued.push(Stmt::If {
780 cond: registered,
781 then_block: self.alloc_block(enqueue),
782 else_block: None,
783 });
784 }
785 let each = self.interner.intern("__lgx_each");
786 let held = self.interner.intern(listener_held_binding());
787 let left = self.interner.intern(listener_left_binding());
788 let holds = self.ctx.alloc_imperative_expr(Expr::Index {
789 collection: self.te_id(held),
790 index: self.te_id(each),
791 });
792 let already = self.ctx.alloc_imperative_expr(Expr::Contains {
793 collection: self.te_id(held),
794 value: self.te_id(each),
795 });
796 let one_more = self.te_bin(BinaryOpKind::Add, holds, self.te_int(1));
797 let hold = Stmt::If {
798 cond: already,
799 then_block: self.alloc_block(vec![Stmt::SetIndex {
800 collection: self.te_id(held),
801 index: self.te_id(each),
802 value: one_more,
803 }]),
804 else_block: Some(self.alloc_block(vec![Stmt::SetIndex {
805 collection: self.te_id(held),
806 index: self.te_id(each),
807 value: self.te_int(1),
808 }])),
809 };
810 queued.push(Stmt::Repeat {
811 pattern: crate::ast::stmt::Pattern::Identifier(each),
812 iterable: self.te_id(queue),
813 body: self.alloc_block(vec![hold]),
814 });
815 let asked = self.te_call(
816 answer,
817 vec![self.te_id(each), self.te_id(value), self.te_id(node)],
818 );
819 let either = self.te_bin(BinaryOpKind::Or, asked, self.te_id(answered));
820 queued.push(Stmt::Repeat {
821 pattern: crate::ast::stmt::Pattern::Identifier(each),
822 iterable: self.te_id(queue),
823 body: self.alloc_block(vec![Stmt::Set {
824 target: answered,
825 value: either,
826 }]),
827 });
828 let forget = self.interner.intern(forget_listener_fn_name());
830 let remaining = self.interner.intern("__lgx_holds");
831 let holds = self.ctx.alloc_imperative_expr(Expr::Index {
832 collection: self.te_id(held),
833 index: self.te_id(each),
834 });
835 let fewer = self.te_bin(BinaryOpKind::Subtract, holds, self.te_int(1));
836 let was_left = self.ctx.alloc_imperative_expr(Expr::Contains {
837 collection: self.te_id(left),
838 value: self.te_id(each),
839 });
840 let last = self.te_bin(BinaryOpKind::Eq, self.te_id(remaining), self.te_int(0));
841 let release = vec![
842 Stmt::Let {
843 var: remaining,
844 ty: None,
845 value: fewer,
846 mutable: false,
847 },
848 Stmt::If {
849 cond: last,
850 then_block: self.alloc_block(vec![
851 Stmt::Remove {
852 value: self.te_id(each),
853 collection: self.te_id(held),
854 },
855 Stmt::If {
856 cond: was_left,
857 then_block: self.alloc_block(vec![
858 Stmt::Remove {
859 value: self.te_id(each),
860 collection: self.te_id(left),
861 },
862 Stmt::Call {
863 function: forget,
864 args: vec![self.te_id(each)],
865 },
866 ]),
867 else_block: None,
868 },
869 ]),
870 else_block: Some(self.alloc_block(vec![Stmt::SetIndex {
871 collection: self.te_id(held),
872 index: self.te_id(each),
873 value: self.te_id(remaining),
874 }])),
875 },
876 ];
877 queued.push(Stmt::Repeat {
878 pattern: crate::ast::stmt::Pattern::Identifier(each),
879 iterable: self.te_id(queue),
880 body: self.alloc_block(release),
881 });
882 let unkeyed = self.te_bin(BinaryOpKind::Lt, self.te_id(keyed), self.te_int(0));
887 let spilled_here = self.ctx.alloc_imperative_expr(Expr::Contains {
888 collection: self.te_id(more),
889 value: self.te_id(plain_slot),
890 });
891 let alone = self.ctx.alloc_imperative_expr(Expr::Not {
892 operand: spilled_here,
893 });
894 let single = self.te_bin(BinaryOpKind::And, unkeyed, alone);
895 let asked = self.te_call(
896 answer,
897 vec![self.te_id(plain_slot), self.te_id(value), self.te_id(node)],
898 );
899 let registered = self.te_bin(BinaryOpKind::GtEq, self.te_id(code), self.te_int(0));
900 let direct = Stmt::If {
901 cond: registered,
902 then_block: self.alloc_block(vec![Stmt::Set {
903 target: answered,
904 value: asked,
905 }]),
906 else_block: None,
907 };
908 body.push(Stmt::If {
909 cond: single,
910 then_block: self.alloc_block(vec![direct]),
911 else_block: Some(self.alloc_block(queued)),
912 });
913 }
914 body.push(Stmt::Return {
915 value: Some(self.te_id(answered)),
916 });
917
918 let mut arms: Vec<Stmt<'a>> = Vec::new();
921 let fns = self.interner.intern(listener_fn_binding());
925 let known = self.ctx.alloc_imperative_expr(Expr::Contains {
926 collection: self.te_id(fns),
927 value: self.te_id(lookup),
928 });
929 let unknown = self.ctx.alloc_imperative_expr(Expr::Not { operand: known });
930 arms.push(Stmt::If {
931 cond: unknown,
932 then_block: self.alloc_block(vec![Stmt::Return {
933 value: Some(self.te_bool(false)),
934 }]),
935 else_block: None,
936 });
937 let read_id = self.ctx.alloc_imperative_expr(Expr::Index {
938 collection: self.te_id(fns),
939 index: self.te_id(lookup),
940 });
941 arms.push(Stmt::Let {
942 var: id,
943 ty: None,
944 value: read_id,
945 mutable: false,
946 });
947 let keys = self.interner.intern(listener_key_binding());
948 let read_key = self.ctx.alloc_imperative_expr(Expr::Index {
949 collection: self.te_id(keys),
950 index: self.te_id(lookup),
951 });
952 arms.push(Stmt::Let {
955 var: key,
956 ty: None,
957 value: read_key,
958 mutable: false,
959 });
960 for (which, site) in self.lgx_listeners.clone().iter().enumerate() {
961 let arm = self.dispatch_site_arm(which, site);
962 arms.push(arm);
963 }
964 arms.push(Stmt::Return {
965 value: Some(self.te_bool(false)),
966 });
967
968 let text_ty: &'a TypeExpr<'a> = self
969 .ctx
970 .alloc_type_expr(TypeExpr::Primitive(self.interner.intern("Text")));
971 let int_ty: &'a TypeExpr<'a> = self
972 .ctx
973 .alloc_type_expr(TypeExpr::Primitive(self.interner.intern("Int")));
974 let bool_ty: &'a TypeExpr<'a> = self
975 .ctx
976 .alloc_type_expr(TypeExpr::Primitive(self.interner.intern("Bool")));
977 let node_half = Stmt::FunctionDef {
978 name: dispatch_node,
979 generics: Vec::new(),
980 params: vec![
981 (node, int_ty),
982 (event, text_ty),
983 (self.lgx_value_param(), text_ty),
984 ],
985 body: self.alloc_block(body),
986 return_type: Some(bool_ty),
987 is_native: false,
988 native_path: None,
989 is_exported: false,
990 export_target: None,
991 opt_flags: crate::optimization::OptimizationConfig::all_on(),
992 };
993 self.deferred_functions.push(node_half);
994 let value = self.lgx_value_param();
995 self.deferred_functions.push(Stmt::FunctionDef {
996 name: answer,
997 generics: Vec::new(),
998 params: vec![(lookup, int_ty), (value, text_ty), (node, int_ty)],
999 body: self.alloc_block(arms),
1000 return_type: Some(bool_ty),
1001 is_native: false,
1002 native_path: None,
1003 is_exported: false,
1004 export_target: None,
1005 opt_flags: crate::optimization::OptimizationConfig::all_on(),
1006 });
1007 let name = self.interner.intern(dispatch_fn_name());
1008 Some(Stmt::FunctionDef {
1009 name,
1010 generics: Vec::new(),
1011 params: vec![
1012 (selector, text_ty),
1013 (event, text_ty),
1014 (self.lgx_value_param(), text_ty),
1015 ],
1016 body: self.alloc_block(selector_body),
1017 return_type: Some(bool_ty),
1018 is_native: false,
1019 native_path: None,
1020 is_exported: false,
1021 export_target: None,
1022 opt_flags: crate::optimization::OptimizationConfig::all_on(),
1023 })
1024 }
1025
1026 fn outbox_flush_arm(&mut self, event: Symbol) -> Stmt<'a> {
1033 let flush = self.interner.intern(outbox_flush_event());
1034 let is_flush = self.te_bin(BinaryOpKind::Eq, self.te_id(event), self.te_text(flush));
1035 let mut arm: Vec<Stmt<'a>> = Vec::new();
1036 let count = self.interner.intern(outbox_count_binding());
1037 let taken_count = self.interner.intern("__lgx_on");
1038 arm.push(Stmt::Let {
1039 var: taken_count,
1040 ty: None,
1041 value: self.te_id(count),
1042 mutable: false,
1043 });
1044 arm.push(Stmt::Set {
1045 target: count,
1046 value: self.te_int(0),
1047 });
1048 let mut taken = Vec::new();
1049 for (list, local) in [
1050 (outbox_selector_binding(), "__lgx_os"),
1051 (outbox_event_binding(), "__lgx_oe"),
1052 (outbox_value_binding(), "__lgx_ov"),
1053 ] {
1054 let list = self.interner.intern(list);
1055 let local = self.interner.intern(local);
1056 arm.push(Stmt::Let {
1057 var: local,
1058 ty: None,
1059 value: self.te_id(list),
1060 mutable: false,
1061 });
1062 let empty = self.empty_outbox_list();
1063 arm.push(Stmt::Set {
1064 target: list,
1065 value: empty,
1066 });
1067 taken.push(local);
1068 }
1069 let at = self.interner.intern("__lgx_oi");
1070 arm.push(Stmt::Let {
1071 var: at,
1072 ty: None,
1073 value: self.te_int(1),
1074 mutable: true,
1075 });
1076 let entry: Vec<&'a Expr<'a>> = taken
1077 .iter()
1078 .map(|list| {
1079 self.ctx.alloc_imperative_expr(Expr::Index {
1080 collection: self.te_id(*list),
1081 index: self.te_id(at),
1082 })
1083 })
1084 .collect();
1085 let dispatch = self.interner.intern(dispatch_fn_name());
1086 let step = self.te_bin(BinaryOpKind::Add, self.te_id(at), self.te_int(1));
1087 let deliver = self.alloc_block(vec![
1088 Stmt::Call {
1089 function: dispatch,
1090 args: entry,
1091 },
1092 Stmt::Set {
1093 target: at,
1094 value: step,
1095 },
1096 ]);
1097 let more = self.te_bin(BinaryOpKind::LtEq, self.te_id(at), self.te_id(taken_count));
1098 arm.push(Stmt::While {
1099 cond: more,
1100 body: deliver,
1101 decreasing: None,
1102 });
1103 arm.push(Stmt::Return {
1104 value: Some(self.te_bool(true)),
1105 });
1106 Stmt::If {
1107 cond: is_flush,
1108 then_block: self.alloc_block(arm),
1109 else_block: None,
1110 }
1111 }
1112
1113 pub(crate) fn listener_closure(&mut self, call: Stmt<'a>) -> &'a Expr<'a> {
1120 self.host_callback_closure(vec![call])
1121 }
1122
1123 pub(crate) fn host_callback_closure(&mut self, call: Vec<Stmt<'a>>) -> &'a Expr<'a> {
1131 let ev_param = self.interner.intern("__lgx_event");
1132 let int_ty: &'a TypeExpr<'a> = self
1133 .ctx
1134 .alloc_type_expr(TypeExpr::Primitive(self.interner.intern("Int")));
1135 let body = self.alloc_block(call);
1136 self.ctx.alloc_imperative_expr(Expr::Closure {
1137 params: vec![(ev_param, int_ty)],
1138 body: crate::ast::stmt::ClosureBody::Block(body),
1139 return_type: None,
1140 })
1141 }
1142
1143 pub(crate) fn listen_flags_call(
1148 &mut self,
1149 node: &'a Expr<'a>,
1150 event: Symbol,
1151 bits: u8,
1152 ) -> Stmt<'a> {
1153 let flagged = self.interner.intern("listenFlags");
1154 let event_text = self.te_text(event);
1155 Stmt::Call {
1156 function: flagged,
1157 args: vec![node, event_text, self.te_int(i64::from(bits))],
1158 }
1159 }
1160
1161 pub(crate) fn handler_key(&mut self, modifiers: &[EventModifier]) -> Option<Symbol> {
1165 EventModifier::key_of(modifiers).map(|k| self.interner.intern(&k))
1166 }
1167
1168 pub(crate) fn dispatch_site_arm(&mut self, which: usize, site: &ListenerSite) -> Stmt<'a> {
1170 let id = self.interner.intern("__lgx_id");
1171 let cond = self.te_bin(BinaryOpKind::Eq, self.te_id(id), self.te_int(which as i64));
1172 let mut run: Vec<Stmt<'a>> = Vec::new();
1173 match site {
1174 ListenerSite::Action {
1175 component,
1176 action,
1177 argc,
1178 carries_value,
1179 once,
1180 ..
1181 } => {
1182 if once.is_some() {
1183 run.extend(self.dispatch_once_gate(*component));
1184 }
1185 let name = self.interner.resolve(*component).to_string();
1186 let a = self.interner.resolve(*action).to_string();
1187 let act = self.interner.intern(&action_fn_name(&name, &a));
1188 let lookup = self.interner.intern("__lgx_lk");
1189 let mut call_args = vec![self.key_expr()];
1190 for i in 0..*argc {
1191 let var = self.interner.intern(&listener_arg_binding(&name, &a, i));
1192 call_args.push(self.ctx.alloc_imperative_expr(Expr::Index {
1193 collection: self.te_id(var),
1194 index: self.te_id(lookup),
1195 }));
1196 }
1197 if *carries_value {
1198 let carried = self.lgx_value_param();
1199 call_args.push(self.te_id(carried));
1200 }
1201 run.push(Stmt::Call {
1202 function: act,
1203 args: call_args,
1204 });
1205 }
1206 ListenerSite::Bind {
1210 component,
1211 field,
1212 checked: _,
1213 } => {
1214 let name = self.interner.resolve(*component).to_string();
1215 let write = self
1216 .interner
1217 .intern(&bind_fn_name(&name, self.interner.resolve(*field)));
1218 let value = self.lgx_value_param();
1219 let key = self.key_expr();
1220 run.push(Stmt::Call {
1221 function: write,
1222 args: vec![key, self.te_id(value)],
1223 });
1224 }
1225 ListenerSite::DefaultToggle {
1231 component,
1232 field,
1233 radio,
1234 } => {
1235 let name = self.interner.resolve(*component).to_string();
1236 let write = self
1237 .interner
1238 .intern(&bind_fn_name(&name, self.interner.resolve(*field)));
1239 let yes = self.interner.intern("true");
1240 let now = self.interner.intern("__lgx_now");
1241 run.push(Stmt::Let {
1242 var: now,
1243 ty: None,
1244 value: self.te_text(yes),
1245 mutable: true,
1246 });
1247 if !*radio {
1248 let no = self.interner.intern("false");
1249 let current = self.state_read(&name, *field);
1250 let flip = Stmt::Set {
1251 target: now,
1252 value: self.te_text(no),
1253 };
1254 run.push(Stmt::If {
1255 cond: current,
1256 then_block: self.alloc_block(vec![flip]),
1257 else_block: None,
1258 });
1259 }
1260 let key = self.key_expr();
1261 run.push(Stmt::Call {
1262 function: write,
1263 args: vec![key, self.te_id(now)],
1264 });
1265 }
1266 ListenerSite::CallbackProp {
1269 component,
1270 prop,
1271 argc,
1272 once,
1273 ..
1274 } => {
1275 let cname = self.interner.resolve(*component).to_string();
1276 let pname = self.interner.resolve(*prop).to_string();
1277 let tag_map = self.interner.intern(&callback_tag_binding(&cname, &pname));
1278 let this_key = self.key_expr();
1279 let has_identity = self.ctx.alloc_imperative_expr(Expr::Contains {
1280 collection: self.te_id(tag_map),
1281 value: this_key,
1282 });
1283 let missing = self.ctx.alloc_imperative_expr(Expr::Not {
1284 operand: has_identity,
1285 });
1286 run.push(Stmt::If {
1287 cond: missing,
1288 then_block: self.alloc_block(vec![Stmt::Return {
1289 value: Some(self.te_bool(false)),
1290 }]),
1291 else_block: None,
1292 });
1293 if once.is_some() {
1294 run.extend(self.dispatch_once_gate(*component));
1295 }
1296 let lookup = self.interner.intern("__lgx_lk");
1297 let mut call_args = vec![self.key_expr()];
1298 for i in 0..*argc {
1299 let var = self
1300 .interner
1301 .intern(&callback_arg_binding(&cname, &pname, i));
1302 call_args.push(self.ctx.alloc_imperative_expr(Expr::Index {
1303 collection: self.te_id(var),
1304 index: self.te_id(lookup),
1305 }));
1306 }
1307 let trampoline = self
1308 .interner
1309 .intern(&callback_trampoline_fn_name(&cname, &pname));
1310 run.push(Stmt::Call {
1311 function: trampoline,
1312 args: call_args,
1313 });
1314 }
1315 ListenerSite::ScrollNear { component, action } => {
1316 let name = self.interner.resolve(*component).to_string();
1317 let a = self.interner.resolve(*action).to_string();
1318 let act = self.interner.intern(&action_fn_name(&name, &a));
1319 let node = self.interner.intern("__lgx_node");
1320 let gap_of = self.interner.intern("scrollGap");
1321 let gap = self.te_call(gap_of, vec![self.te_id(node)]);
1322 let far = self.te_bin(BinaryOpKind::Gt, gap, self.te_int(32));
1323 run.push(Stmt::If {
1324 cond: far,
1325 then_block: self.alloc_block(vec![Stmt::Return {
1326 value: Some(self.te_bool(false)),
1327 }]),
1328 else_block: None,
1329 });
1330 run.push(Stmt::Call {
1331 function: act,
1332 args: vec![self.key_expr()],
1333 });
1334 }
1335 }
1336 run.push(Stmt::Return {
1337 value: Some(self.te_bool(true)),
1338 });
1339 Stmt::If {
1340 cond,
1341 then_block: self.alloc_block(run),
1342 else_block: None,
1343 }
1344 }
1345
1346 fn dispatch_once_gate(&mut self, component: Symbol) -> Vec<Stmt<'a>> {
1350 let id = self.interner.intern("__lgx_oid");
1351 let lookup = self.interner.intern("__lgx_lk");
1352 let ids = self.interner.intern(listener_once_binding());
1353 let read = self.ctx.alloc_imperative_expr(Expr::Index {
1354 collection: self.te_id(ids),
1355 index: self.te_id(lookup),
1356 });
1357 let mut out = vec![Stmt::Let {
1358 var: id,
1359 ty: None,
1360 value: read,
1361 mutable: false,
1362 }];
1363 out.push(self.once_spent_guard(id));
1364 out.extend(self.spend_once(component, id));
1365 out
1366 }
1367
1368 pub(crate) fn once_spent_guard(&mut self, id: Symbol) -> Stmt<'a> {
1370 let spent = self.interner.intern(once_spent_binding());
1371 let is_spent = self.ctx.alloc_imperative_expr(Expr::Contains {
1372 collection: self.te_id(spent),
1373 value: self.te_id(id),
1374 });
1375 Stmt::If {
1376 cond: is_spent,
1377 then_block: self.alloc_block(vec![Stmt::Return {
1378 value: Some(self.te_bool(false)),
1379 }]),
1380 else_block: None,
1381 }
1382 }
1383
1384 pub(crate) fn spend_once(&mut self, component: Symbol, id: Symbol) -> Vec<Stmt<'a>> {
1387 let spent = self.interner.intern(once_spent_binding());
1388 let mut out = vec![Stmt::SetIndex {
1389 collection: self.te_id(spent),
1390 index: self.te_id(id),
1391 value: self.te_bool(true),
1392 }];
1393 if !self.once_recorded(component) {
1394 return out;
1395 }
1396 let name = self.interner.resolve(component).to_string();
1397 let record = self.interner.intern(&once_record_binding(&name));
1398 let list = self.interner.intern("__lgx_ol");
1399 let text = self.interner.intern("Text");
1400 let fresh = self.new_seq_expr(text);
1401 out.push(Stmt::Let {
1402 var: list,
1403 ty: None,
1404 value: fresh,
1405 mutable: true,
1406 });
1407 let key = self.key_expr();
1408 let known = self.ctx.alloc_imperative_expr(Expr::Contains {
1409 collection: self.te_id(record),
1410 value: key,
1411 });
1412 let key = self.key_expr();
1413 let held = self.ctx.alloc_imperative_expr(Expr::Index {
1414 collection: self.te_id(record),
1415 index: key,
1416 });
1417 out.push(Stmt::If {
1418 cond: known,
1419 then_block: self.alloc_block(vec![Stmt::Set {
1420 target: list,
1421 value: held,
1422 }]),
1423 else_block: None,
1424 });
1425 out.push(Stmt::Push {
1426 value: self.te_id(id),
1427 collection: self.te_id(list),
1428 });
1429 let key = self.key_expr();
1430 out.push(Stmt::SetIndex {
1431 collection: self.te_id(record),
1432 index: key,
1433 value: self.te_id(list),
1434 });
1435 out
1436 }
1437
1438 pub(crate) fn once_recorded(&self, component: Symbol) -> bool {
1441 self.lgx_all_components
1442 .iter()
1443 .any(|d| d.name == component && has_once_handler(d))
1444 && unmountable_components(&self.lgx_all_components).contains(&component)
1445 }
1446
1447 pub(crate) fn lgx_value_param(&mut self) -> Symbol {
1449 self.interner.intern("__lgx_val")
1450 }
1451
1452 pub(crate) fn te_bool(&self, b: bool) -> &'a Expr<'a> {
1453 self.ctx
1454 .alloc_imperative_expr(Expr::Literal(Literal::Boolean(b)))
1455 }
1456
1457 pub(crate) fn lower_lgx_raise(&mut self) -> Stmt<'a> {
1466 let selector = self.interner.intern("__lgx_sel");
1467 let event = self.interner.intern("__lgx_event");
1468 let carried = self.lgx_value_param();
1471 let dispatch = self.interner.intern(dispatch_fn_name());
1472 let two = self.te_int(2);
1473 let body = vec![
1474 Stmt::Call {
1475 function: dispatch,
1476 args: vec![self.te_id(selector), self.te_id(event), self.te_id(carried)],
1477 },
1478 Stmt::Return { value: Some(two) },
1479 ];
1480 self.lgx_driver_fn(raise_fn_name(), vec![selector, event, carried], body)
1481 }
1482
1483 pub(crate) fn lgx_driver_fn(
1484 &mut self,
1485 name: &str,
1486 params: Vec<Symbol>,
1487 body: Vec<Stmt<'a>>,
1488 ) -> Stmt<'a> {
1489 let text_ty: &'a TypeExpr<'a> = self
1490 .ctx
1491 .alloc_type_expr(TypeExpr::Primitive(self.interner.intern("Text")));
1492 let int_ty: &'a TypeExpr<'a> = self
1493 .ctx
1494 .alloc_type_expr(TypeExpr::Primitive(self.interner.intern("Int")));
1495 let name = self.interner.intern(name);
1496 Stmt::FunctionDef {
1497 name,
1498 generics: Vec::new(),
1499 params: params.into_iter().map(|p| (p, text_ty)).collect(),
1500 body: self.alloc_block(body),
1501 return_type: Some(int_ty),
1502 is_native: false,
1503 native_path: None,
1504 is_exported: false,
1505 export_target: None,
1506 opt_flags: crate::optimization::OptimizationConfig::all_on(),
1507 }
1508 }
1509
1510 pub(crate) fn lower_bind_writeback(
1512 &mut self,
1513 def: &ComponentDef<'a>,
1514 field: Symbol,
1515 kind: BoundInput,
1516 ) -> Option<Stmt<'a>> {
1517 let component = self.interner.resolve(def.name).to_string();
1518 let param = self.interner.intern("__lgx_typed");
1519 let stored = self.bound_value(kind, param);
1522 let set = Stmt::Set {
1526 target: field,
1527 value: stored,
1528 };
1529 let body = self.turn_body_with(def, self.alloc_block(vec![set]), true, Some("bind"))?;
1530 let text_ty: &'a TypeExpr<'a> = self
1531 .ctx
1532 .alloc_type_expr(TypeExpr::Primitive(self.interner.intern("Text")));
1533 let name = self
1534 .interner
1535 .intern(&bind_fn_name(&component, self.interner.resolve(field)));
1536 Some(Stmt::FunctionDef {
1537 name,
1538 generics: Vec::new(),
1539 params: vec![
1543 (self.interner.intern(instance_key_param()), text_ty),
1544 (param, text_ty),
1545 ],
1546 body,
1547 return_type: None,
1548 is_native: false,
1549 native_path: None,
1550 is_exported: false,
1551 export_target: None,
1552 opt_flags: crate::optimization::OptimizationConfig::all_on(),
1553 })
1554 }
1555
1556 pub(crate) fn bound_value(&mut self, kind: BoundInput, param: Symbol) -> &'a Expr<'a> {
1560 match kind {
1561 k if k.writes_checked() => {
1562 let t = self.interner.intern("true");
1563 self.te_bin(BinaryOpKind::Eq, self.te_id(param), self.te_text(t))
1564 }
1565 _ => self.te_id(param),
1566 }
1567 }
1568}