1use std::collections::BTreeMap;
8use std::num::NonZeroU16;
9
10use crate::analyze::type_check::TypeId;
11use plotnik_bytecode::{
12 Call, EffectOp, EffectOpcode, Nav, Opcode, PredicateOp, Return, StepAddr, StepId, Trampoline,
13 select_match_opcode,
14};
15
16#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
28pub enum NodeTypeIR {
29 #[default]
31 Any,
32 Named(Option<NonZeroU16>),
36 Anonymous(Option<NonZeroU16>),
40}
41
42impl NodeTypeIR {
43 pub fn to_bytes(self) -> (u8, u16) {
48 match self {
49 Self::Any => (0b00, 0),
50 Self::Named(opt) => (0b01, opt.map(|n| n.get()).unwrap_or(0)),
51 Self::Anonymous(opt) => (0b10, opt.map(|n| n.get()).unwrap_or(0)),
52 }
53 }
54
55 pub fn from_bytes(node_kind: u8, node_type: u16) -> Self {
57 match node_kind {
58 0b00 => Self::Any,
59 0b01 => Self::Named(NonZeroU16::new(node_type)),
60 0b10 => Self::Anonymous(NonZeroU16::new(node_type)),
61 _ => panic!("invalid node_kind: {node_kind}"),
62 }
63 }
64
65 pub fn type_id(&self) -> Option<NonZeroU16> {
67 match self {
68 Self::Any => None,
69 Self::Named(opt) | Self::Anonymous(opt) => *opt,
70 }
71 }
72
73 pub fn is_any(&self) -> bool {
75 matches!(self, Self::Any)
76 }
77
78 pub fn is_named(&self) -> bool {
80 matches!(self, Self::Named(_))
81 }
82
83 pub fn is_anonymous(&self) -> bool {
85 matches!(self, Self::Anonymous(_))
86 }
87}
88
89#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
91pub struct Label(pub u32);
92
93impl Label {
94 #[inline]
96 pub fn resolve(self, map: &BTreeMap<Label, StepAddr>) -> StepAddr {
97 *map.get(&self).expect("label not in layout")
98 }
99}
100
101#[derive(Clone, Copy, Debug)]
110pub enum MemberRef {
111 Absolute(u16),
113 Deferred {
117 field_name: plotnik_core::Symbol,
119 field_type: TypeId,
121 },
122 DeferredByIndex {
125 parent_type: TypeId,
127 relative_index: u16,
129 },
130}
131
132impl MemberRef {
133 pub fn absolute(index: u16) -> Self {
135 Self::Absolute(index)
136 }
137
138 pub fn deferred(field_name: plotnik_core::Symbol, field_type: TypeId) -> Self {
140 Self::Deferred {
141 field_name,
142 field_type,
143 }
144 }
145
146 pub fn deferred_by_index(parent_type: TypeId, relative_index: u16) -> Self {
148 Self::DeferredByIndex {
149 parent_type,
150 relative_index,
151 }
152 }
153
154 pub fn resolve<F, G>(self, lookup_member: F, get_member_base: G) -> u16
159 where
160 F: Fn(plotnik_core::Symbol, TypeId) -> Option<u16>,
161 G: Fn(TypeId) -> Option<u16>,
162 {
163 match self {
164 Self::Absolute(n) => n,
165 Self::Deferred {
166 field_name,
167 field_type,
168 } => lookup_member(field_name, field_type)
169 .expect("deferred member reference must resolve"),
170 Self::DeferredByIndex {
171 parent_type,
172 relative_index,
173 } => {
174 get_member_base(parent_type).expect("deferred member base must resolve")
175 + relative_index
176 }
177 }
178 }
179}
180
181#[derive(Clone, Debug)]
184pub struct EffectIR {
185 pub opcode: EffectOpcode,
186 pub payload: usize,
188 pub member_ref: Option<MemberRef>,
190}
191
192impl EffectIR {
193 pub fn simple(opcode: EffectOpcode, payload: usize) -> Self {
195 Self {
196 opcode,
197 payload,
198 member_ref: None,
199 }
200 }
201
202 pub fn with_member(opcode: EffectOpcode, member_ref: MemberRef) -> Self {
204 Self {
205 opcode,
206 payload: 0,
207 member_ref: Some(member_ref),
208 }
209 }
210
211 pub fn node() -> Self {
213 Self::simple(EffectOpcode::Node, 0)
214 }
215
216 pub fn text() -> Self {
218 Self::simple(EffectOpcode::Text, 0)
219 }
220
221 pub fn null() -> Self {
223 Self::simple(EffectOpcode::Null, 0)
224 }
225
226 pub fn push() -> Self {
228 Self::simple(EffectOpcode::Push, 0)
229 }
230
231 pub fn start_arr() -> Self {
233 Self::simple(EffectOpcode::Arr, 0)
234 }
235
236 pub fn end_arr() -> Self {
238 Self::simple(EffectOpcode::EndArr, 0)
239 }
240
241 pub fn start_obj() -> Self {
243 Self::simple(EffectOpcode::Obj, 0)
244 }
245
246 pub fn end_obj() -> Self {
248 Self::simple(EffectOpcode::EndObj, 0)
249 }
250
251 pub fn start_enum() -> Self {
253 Self::simple(EffectOpcode::Enum, 0)
254 }
255
256 pub fn end_enum() -> Self {
258 Self::simple(EffectOpcode::EndEnum, 0)
259 }
260
261 pub fn suppress_begin() -> Self {
263 Self::simple(EffectOpcode::SuppressBegin, 0)
264 }
265
266 pub fn suppress_end() -> Self {
268 Self::simple(EffectOpcode::SuppressEnd, 0)
269 }
270
271 pub fn resolve<F, G>(&self, lookup_member: F, get_member_base: G) -> EffectOp
276 where
277 F: Fn(plotnik_core::Symbol, TypeId) -> Option<u16>,
278 G: Fn(TypeId) -> Option<u16>,
279 {
280 let payload = if let Some(member_ref) = self.member_ref {
281 member_ref.resolve(&lookup_member, &get_member_base) as usize
282 } else {
283 self.payload
284 };
285 EffectOp::new(self.opcode, payload)
286 }
287}
288
289#[derive(Clone, Debug)]
294pub enum PredicateValueIR {
295 String(plotnik_bytecode::StringId),
297 Regex(plotnik_bytecode::StringId),
299}
300
301#[derive(Clone, Debug)]
306pub struct PredicateIR {
307 pub op: PredicateOp,
308 pub value: PredicateValueIR,
309}
310
311impl PredicateIR {
312 pub fn string(op: PredicateOp, value: plotnik_bytecode::StringId) -> Self {
314 Self {
315 op,
316 value: PredicateValueIR::String(value),
317 }
318 }
319
320 pub fn regex(op: PredicateOp, pattern_id: plotnik_bytecode::StringId) -> Self {
322 Self {
323 op,
324 value: PredicateValueIR::Regex(pattern_id),
325 }
326 }
327
328 pub fn op_byte(&self) -> u8 {
330 self.op.to_byte()
331 }
332}
333
334#[derive(Clone, Debug)]
336pub enum InstructionIR {
337 Match(MatchIR),
338 Call(CallIR),
339 Return(ReturnIR),
340 Trampoline(TrampolineIR),
341}
342
343impl InstructionIR {
344 #[inline]
346 pub fn label(&self) -> Label {
347 match self {
348 Self::Match(m) => m.label,
349 Self::Call(c) => c.label,
350 Self::Return(r) => r.label,
351 Self::Trampoline(t) => t.label,
352 }
353 }
354
355 pub fn size(&self) -> usize {
357 match self {
358 Self::Match(m) => m.size(),
359 Self::Call(_) | Self::Return(_) | Self::Trampoline(_) => 8,
360 }
361 }
362
363 pub fn successors(&self) -> Vec<Label> {
365 match self {
366 Self::Match(m) => m.successors.clone(),
367 Self::Call(c) => vec![c.next],
368 Self::Return(_) => vec![],
369 Self::Trampoline(t) => vec![t.next],
370 }
371 }
372
373 pub fn resolve<F, G, R>(
379 &self,
380 map: &BTreeMap<Label, StepAddr>,
381 lookup_member: F,
382 get_member_base: G,
383 lookup_regex: R,
384 ) -> Vec<u8>
385 where
386 F: Fn(plotnik_core::Symbol, TypeId) -> Option<u16>,
387 G: Fn(TypeId) -> Option<u16>,
388 R: Fn(plotnik_bytecode::StringId) -> Option<u16>,
389 {
390 match self {
391 Self::Match(m) => m.resolve(map, lookup_member, get_member_base, lookup_regex),
392 Self::Call(c) => c.resolve(map).to_vec(),
393 Self::Return(r) => r.resolve().to_vec(),
394 Self::Trampoline(t) => t.resolve(map).to_vec(),
395 }
396 }
397}
398
399#[derive(Clone, Debug)]
401pub struct MatchIR {
402 pub label: Label,
404 pub nav: Nav,
406 pub node_type: NodeTypeIR,
408 pub node_field: Option<NonZeroU16>,
410 pub pre_effects: Vec<EffectIR>,
412 pub neg_fields: Vec<u16>,
414 pub post_effects: Vec<EffectIR>,
416 pub predicate: Option<PredicateIR>,
418 pub successors: Vec<Label>,
420}
421
422impl MatchIR {
423 pub fn terminal(label: Label) -> Self {
425 Self {
426 label,
427 nav: Nav::Epsilon,
428 node_type: NodeTypeIR::Any,
429 node_field: None,
430 pre_effects: vec![],
431 neg_fields: vec![],
432 post_effects: vec![],
433 predicate: None,
434 successors: vec![],
435 }
436 }
437
438 pub fn at(label: Label) -> Self {
440 Self::terminal(label)
441 }
442
443 pub fn epsilon(label: Label, next: Label) -> Self {
445 Self::at(label).next(next)
446 }
447
448 pub fn nav(mut self, nav: Nav) -> Self {
450 self.nav = nav;
451 self
452 }
453
454 pub fn node_type(mut self, t: NodeTypeIR) -> Self {
456 self.node_type = t;
457 self
458 }
459
460 pub fn node_field(mut self, f: impl Into<Option<NonZeroU16>>) -> Self {
462 self.node_field = f.into();
463 self
464 }
465
466 pub fn neg_field(mut self, f: u16) -> Self {
468 self.neg_fields.push(f);
469 self
470 }
471
472 pub fn pre_effect(mut self, e: EffectIR) -> Self {
474 self.pre_effects.push(e);
475 self
476 }
477
478 pub fn post_effect(mut self, e: EffectIR) -> Self {
480 self.post_effects.push(e);
481 self
482 }
483
484 pub fn neg_fields(mut self, fields: impl IntoIterator<Item = u16>) -> Self {
486 self.neg_fields.extend(fields);
487 self
488 }
489
490 pub fn pre_effects(mut self, effects: impl IntoIterator<Item = EffectIR>) -> Self {
492 self.pre_effects.extend(effects);
493 self
494 }
495
496 pub fn post_effects(mut self, effects: impl IntoIterator<Item = EffectIR>) -> Self {
498 self.post_effects.extend(effects);
499 self
500 }
501
502 pub fn predicate(mut self, p: PredicateIR) -> Self {
504 self.predicate = Some(p);
505 self
506 }
507
508 pub fn next(mut self, s: Label) -> Self {
510 self.successors = vec![s];
511 self
512 }
513
514 pub fn next_many(mut self, s: Vec<Label>) -> Self {
516 self.successors = s;
517 self
518 }
519
520 pub fn size(&self) -> usize {
522 let can_use_match8 = self.pre_effects.is_empty()
524 && self.neg_fields.is_empty()
525 && self.post_effects.is_empty()
526 && self.predicate.is_none()
527 && self.successors.len() <= 1;
528
529 if can_use_match8 {
530 return 8;
531 }
532
533 let predicate_slots = if self.predicate.is_some() { 2 } else { 0 };
536 let slots = self.pre_effects.len()
537 + self.neg_fields.len()
538 + self.post_effects.len()
539 + predicate_slots
540 + self.successors.len();
541
542 select_match_opcode(slots).map(|op| op.size()).unwrap_or(64)
543 }
544
545 pub fn resolve<F, G, R>(
551 &self,
552 map: &BTreeMap<Label, StepAddr>,
553 lookup_member: F,
554 get_member_base: G,
555 lookup_regex: R,
556 ) -> Vec<u8>
557 where
558 F: Fn(plotnik_core::Symbol, TypeId) -> Option<u16>,
559 G: Fn(TypeId) -> Option<u16>,
560 R: Fn(plotnik_bytecode::StringId) -> Option<u16>,
561 {
562 let can_use_match8 = self.pre_effects.is_empty()
563 && self.neg_fields.is_empty()
564 && self.post_effects.is_empty()
565 && self.predicate.is_none()
566 && self.successors.len() <= 1;
567
568 let opcode = if can_use_match8 {
569 Opcode::Match8
570 } else {
571 let predicate_slots = if self.predicate.is_some() { 2 } else { 0 };
572 let slots_needed = self.pre_effects.len()
573 + self.neg_fields.len()
574 + self.post_effects.len()
575 + predicate_slots
576 + self.successors.len();
577 select_match_opcode(slots_needed).expect("instruction too large")
578 };
579
580 let size = opcode.size();
581 let mut bytes = vec![0u8; size];
582
583 let (node_kind, node_type_val) = self.node_type.to_bytes();
585 bytes[0] = (node_kind << 4) | (opcode as u8); bytes[1] = self.nav.to_byte();
587 bytes[2..4].copy_from_slice(&node_type_val.to_le_bytes());
588 let node_field_val = self.node_field.map(|n| n.get()).unwrap_or(0);
589 bytes[4..6].copy_from_slice(&node_field_val.to_le_bytes());
590
591 if opcode == Opcode::Match8 {
592 let next = self
593 .successors
594 .first()
595 .map(|&l| l.resolve(map))
596 .unwrap_or(0);
597 bytes[6..8].copy_from_slice(&next.to_le_bytes());
598 } else {
599 let pre_count = self.pre_effects.len();
600 let neg_count = self.neg_fields.len();
601 let post_count = self.post_effects.len();
602 let succ_count = self.successors.len();
603 let has_predicate = self.predicate.is_some();
604
605 assert!(
608 pre_count <= 7,
609 "pre_effects overflow: {pre_count} > 7 (use emit_match_with_cascade)"
610 );
611 assert!(neg_count <= 7, "neg_fields overflow: {neg_count} > 7");
612 assert!(post_count <= 7, "post_effects overflow: {post_count} > 7");
613 assert!(succ_count <= 31, "successors overflow: {succ_count} > 31");
614
615 let counts = ((pre_count as u16) << 13)
616 | ((neg_count as u16) << 10)
617 | ((post_count as u16) << 7)
618 | ((succ_count as u16) << 2)
619 | ((has_predicate as u16) << 1);
620 bytes[6..8].copy_from_slice(&counts.to_le_bytes());
621
622 let mut offset = 8;
623 for effect in &self.pre_effects {
624 let resolved = effect.resolve(&lookup_member, &get_member_base);
625 bytes[offset..offset + 2].copy_from_slice(&resolved.to_bytes());
626 offset += 2;
627 }
628 for &field in &self.neg_fields {
629 bytes[offset..offset + 2].copy_from_slice(&field.to_le_bytes());
630 offset += 2;
631 }
632 for effect in &self.post_effects {
633 let resolved = effect.resolve(&lookup_member, &get_member_base);
634 bytes[offset..offset + 2].copy_from_slice(&resolved.to_bytes());
635 offset += 2;
636 }
637 if let Some(pred) = &self.predicate {
639 let is_regex = matches!(pred.value, PredicateValueIR::Regex(_));
640 let op_and_flags = (pred.op_byte() as u16) | ((is_regex as u16) << 8);
641 bytes[offset..offset + 2].copy_from_slice(&op_and_flags.to_le_bytes());
642 offset += 2;
643
644 let value_ref = match &pred.value {
645 PredicateValueIR::String(string_id) => string_id.get(),
646 PredicateValueIR::Regex(string_id) => {
647 lookup_regex(*string_id).expect("regex predicate must be interned")
648 }
649 };
650 bytes[offset..offset + 2].copy_from_slice(&value_ref.to_le_bytes());
651 offset += 2;
652 }
653 for &label in &self.successors {
654 let addr = label.resolve(map);
655 bytes[offset..offset + 2].copy_from_slice(&addr.to_le_bytes());
656 offset += 2;
657 }
658 }
659
660 bytes
661 }
662
663 #[inline]
665 pub fn is_epsilon(&self) -> bool {
666 self.nav == Nav::Epsilon
667 }
668}
669
670impl From<MatchIR> for InstructionIR {
671 fn from(m: MatchIR) -> Self {
672 Self::Match(m)
673 }
674}
675
676#[derive(Clone, Debug)]
678pub struct CallIR {
679 pub label: Label,
681 pub nav: Nav,
683 pub node_field: Option<NonZeroU16>,
685 pub next: Label,
687 pub target: Label,
689}
690
691impl CallIR {
692 pub fn new(label: Label, target: Label, next: Label) -> Self {
694 Self {
695 label,
696 nav: Nav::Stay,
697 node_field: None,
698 next,
699 target,
700 }
701 }
702
703 pub fn nav(mut self, nav: Nav) -> Self {
705 self.nav = nav;
706 self
707 }
708
709 pub fn node_field(mut self, f: impl Into<Option<NonZeroU16>>) -> Self {
711 self.node_field = f.into();
712 self
713 }
714
715 pub fn resolve(&self, map: &BTreeMap<Label, StepAddr>) -> [u8; 8] {
717 Call::new(
718 self.nav,
719 self.node_field,
720 StepId::new(self.next.resolve(map)),
721 StepId::new(self.target.resolve(map)),
722 )
723 .to_bytes()
724 }
725}
726
727impl From<CallIR> for InstructionIR {
728 fn from(c: CallIR) -> Self {
729 Self::Call(c)
730 }
731}
732
733#[derive(Clone, Debug)]
735pub struct ReturnIR {
736 pub label: Label,
738}
739
740impl ReturnIR {
741 pub fn new(label: Label) -> Self {
743 Self { label }
744 }
745
746 pub fn resolve(&self) -> [u8; 8] {
748 Return::new().to_bytes()
749 }
750}
751
752impl From<ReturnIR> for InstructionIR {
753 fn from(r: ReturnIR) -> Self {
754 Self::Return(r)
755 }
756}
757
758#[derive(Clone, Debug)]
763pub struct TrampolineIR {
764 pub label: Label,
766 pub next: Label,
768}
769
770impl TrampolineIR {
771 pub fn new(label: Label, next: Label) -> Self {
773 Self { label, next }
774 }
775
776 pub fn resolve(&self, map: &BTreeMap<Label, StepAddr>) -> [u8; 8] {
778 Trampoline::new(StepId::new(self.next.resolve(map))).to_bytes()
779 }
780}
781
782impl From<TrampolineIR> for InstructionIR {
783 fn from(t: TrampolineIR) -> Self {
784 Self::Trampoline(t)
785 }
786}
787
788#[derive(Clone, Debug)]
790pub struct LayoutResult {
791 pub(crate) label_to_step: BTreeMap<Label, StepAddr>,
793 pub(crate) total_steps: u16,
795}
796
797impl LayoutResult {
798 pub fn new(label_to_step: BTreeMap<Label, StepAddr>, total_steps: u16) -> Self {
800 Self {
801 label_to_step,
802 total_steps,
803 }
804 }
805
806 pub fn empty() -> Self {
808 Self {
809 label_to_step: BTreeMap::new(),
810 total_steps: 0,
811 }
812 }
813
814 pub fn label_to_step(&self) -> &BTreeMap<Label, StepAddr> {
815 &self.label_to_step
816 }
817 pub fn total_steps(&self) -> u16 {
818 self.total_steps
819 }
820}