diff --git a/crates/plotnik-cli/src/commands/debug/mod.rs b/crates/plotnik-cli/src/commands/debug/mod.rs index 0d07fbdb..2c95154d 100644 --- a/crates/plotnik-cli/src/commands/debug/mod.rs +++ b/crates/plotnik-cli/src/commands/debug/mod.rs @@ -56,12 +56,9 @@ pub fn run(args: DebugArgs) { let show_query = has_query_input && !args.symbols && !args.graph && !args.types; let show_source = has_source_input; - let show_headers = (show_query || args.symbols) && show_source; + let show_both_graphs = args.graph_raw && args.graph; if show_query && let Some(ref q) = query { - if show_headers { - println!("=== QUERY ==="); - } print!( "{}", q.printer() @@ -76,9 +73,6 @@ pub fn run(args: DebugArgs) { if args.symbols && let Some(ref q) = query { - if show_headers { - println!("=== SYMBOLS ==="); - } print!( "{}", q.printer() @@ -93,28 +87,40 @@ pub fn run(args: DebugArgs) { && let Some(q) = query.take() { let (q, pre_opt_dump) = q.build_graph_with_pre_opt_dump(); + let mut needs_separator = false; if args.graph_raw { - println!("=== GRAPH (raw) ==="); + if show_both_graphs { + println!("(pre-optimization)"); + } print!("{}", pre_opt_dump); + needs_separator = true; } if args.graph { - println!("=== GRAPH ==="); + if needs_separator { + println!(); + } + if show_both_graphs { + println!("(post-optimization)"); + } print!("{}", q.graph().dump_live(q.dead_nodes())); + needs_separator = true; } if args.types { - println!("=== TYPES ==="); + if needs_separator { + println!(); + } print!("{}", q.type_info().dump()); } return; } if show_source { + if show_query || args.symbols { + println!(); + } let resolved_lang = resolve_lang(&args.lang, &args.source_text, &args.source_file); let source_code = load_source(&args.source_text, &args.source_file); let tree = parse_tree(&source_code, resolved_lang); - if show_headers { - println!("=== SOURCE ==="); - } print!("{}", dump_source(&tree, &source_code, args.raw)); } diff --git a/crates/plotnik-lib/src/ir/emit.rs b/crates/plotnik-lib/src/ir/emit.rs index c0e72c64..9c351cf6 100644 --- a/crates/plotnik-lib/src/ir/emit.rs +++ b/crates/plotnik-lib/src/ir/emit.rs @@ -17,7 +17,7 @@ use super::{ }; use crate::query::graph::{BuildEffect, BuildGraph, BuildMatcher, BuildNode, RefMarker}; -use crate::query::typing::TypeInferenceResult; +use crate::query::infer::TypeInferenceResult; /// Callback for resolving node kind names to IDs. pub trait NodeKindResolver { @@ -727,7 +727,7 @@ impl<'src, 'g, R: NodeKindResolver> QueryEmitter<'src, 'g, R> { mod tests { use super::*; use crate::query::graph::{BuildEffect, BuildGraph, BuildMatcher, BuildNode}; - use crate::query::typing::TypeInferenceResult; + use crate::query::infer::TypeInferenceResult; use std::num::NonZeroU16; fn make_resolver() -> MapResolver { diff --git a/crates/plotnik-lib/src/query/graph_build_tests.rs b/crates/plotnik-lib/src/query/graph_build_tests.rs index 82345f76..04145597 100644 --- a/crates/plotnik-lib/src/query/graph_build_tests.rs +++ b/crates/plotnik-lib/src/query/graph_build_tests.rs @@ -17,122 +17,122 @@ fn snapshot_optimized(input: &str) -> String { #[test] fn simple_named_node() { insta::assert_snapshot!(snapshot("Q = (identifier)"), @r" - Q = N0 + Q = (0) - N0: (identifier) → ∅ + (0) —(identifier)→ (✓) "); } #[test] fn named_node_with_capture() { insta::assert_snapshot!(snapshot("Q = (identifier) @id"), @r" - Q = N0 + Q = (0) - N0: (identifier) [Capture] → N1 - N1: ε [Field(id)] → ∅ + (0) —(identifier)—[CaptureNode]→ (1) + (1) —𝜀—[Field(id)]→ (✓) "); } #[test] fn named_node_with_children() { insta::assert_snapshot!(snapshot("Q = (function_definition (identifier))"), @r" - Q = N0 + Q = (0) - N0: (function_definition) → N1 - N1: [Down] (identifier) → N2 - N2: [Up(1)] ε → ∅ + (0) —(function_definition)→ (1) + (1) —{↘}—(identifier)→ (2) + (2) —{↗¹}—𝜀→ (✓) "); } #[test] fn sequence() { insta::assert_snapshot!(snapshot("Q = { (a) (b) }"), @r" - Q = N1 + Q = (1) - N0: ε → N1 - N1: [Next] (a) → N2 - N2: [Next] (b) → ∅ + (0) —𝜀→ (1) + (1) —{→}—(a)→ (2) + (2) —{→}—(b)→ (✓) "); } #[test] fn sequence_with_captures() { insta::assert_snapshot!(snapshot("Q = { (a) @x (b) @y }"), @r" - Q = N1 + Q = (1) - N0: ε → N1 - N1: [Next] (a) [Capture] → N2 - N2: ε [Field(x)] → N3 - N3: [Next] (b) [Capture] → N4 - N4: ε [Field(y)] → ∅ + (0) —𝜀→ (1) + (1) —{→}—(a)—[CaptureNode]→ (2) + (2) —𝜀—[Field(x)]→ (3) + (3) —{→}—(b)—[CaptureNode]→ (4) + (4) —𝜀—[Field(y)]→ (✓) "); } #[test] fn alternation_untagged() { insta::assert_snapshot!(snapshot("Q = [ (a) (b) ]"), @r" - Q = N0 + Q = (0) - N0: ε → N2, N3 - N1: ε → ∅ - N2: (a) → N1 - N3: (b) → N1 + (0) —𝜀→ (2), (3) + (1) —𝜀→ (✓) + (2) —(a)→ (1) + (3) —(b)→ (1) "); } #[test] fn alternation_tagged() { insta::assert_snapshot!(snapshot("Q = [ A: (a) @x B: (b) @y ]"), @r" - Q = N0 - - N0: ε → N3, N7 - N1: ε → ∅ - N2: ε [Variant(A)] → N3 - N3: (a) [Variant(A)] [Capture] → N5 - N4: ε [Field(x)] → N5 - N5: ε [Field(x)] [EndVariant] → N1 - N6: ε [Variant(B)] → N7 - N7: (b) [Variant(B)] [Capture] → N9 - N8: ε [Field(y)] → N9 - N9: ε [Field(y)] [EndVariant] → N1 + Q = (0) + + (0) —𝜀→ (3), (7) + (1) —𝜀→ (✓) + (2) —𝜀—[StartVariant(A)]→ (3) + (3) —(a)—[StartVariant(A), CaptureNode]→ (5) + (4) —𝜀—[Field(x)]→ (5) + (5) —𝜀—[Field(x), EndVariant]→ (1) + (6) —𝜀—[StartVariant(B)]→ (7) + (7) —(b)—[StartVariant(B), CaptureNode]→ (9) + (8) —𝜀—[Field(y)]→ (9) + (9) —𝜀—[Field(y), EndVariant]→ (1) "); } #[test] fn quantifier_star() { insta::assert_snapshot!(snapshot("Q = (identifier)*"), @r" - Q = N1 + Q = (1) - N0: (identifier) → N3 - N1: ε [StartArray] → N4 - N2: ε [EndArray] → ∅ - N3: ε [Push] → N4 - N4: ε → N0, N2 + (0) —(identifier)→ (3) + (1) —𝜀—[StartArray]→ (4) + (2) —𝜀—[EndArray]→ (✓) + (3) —𝜀—[PushElement]→ (4) + (4) —𝜀→ (0), (2) "); } #[test] fn quantifier_plus() { insta::assert_snapshot!(snapshot("Q = (identifier)+"), @r" - Q = N1 + Q = (1) - N0: (identifier) → N4 - N1: ε [StartArray] → N0 - N2: ε [EndArray] → ∅ - N3: ε [Push] → N4 - N4: ε [Push] → N0, N2 + (0) —(identifier)→ (4) + (1) —𝜀—[StartArray]→ (0) + (2) —𝜀—[EndArray]→ (✓) + (3) —𝜀—[PushElement]→ (4) + (4) —𝜀—[PushElement]→ (0), (2) "); } #[test] fn quantifier_optional() { insta::assert_snapshot!(snapshot("Q = (identifier)?"), @r" - Q = N1 + Q = (1) - N0: (identifier) → N2 - N1: ε → N0, N3 - N2: ε → ∅ - N3: ε [Clear] → N2 + (0) —(identifier)→ (2) + (1) —𝜀→ (0), (3) + (2) —𝜀→ (✓) + (3) —𝜀—[ClearCurrent]→ (2) "); } @@ -143,74 +143,74 @@ fn reference() { B = (A) "#}; insta::assert_snapshot!(snapshot(input), @r" - A = N0 - B = N1 + A = (0) + B = (1) - N0: (identifier) → ∅ - N1: ε +Enter(0, A) → N0, N2 - N2: ε +Exit(0) → ∅ + (0) —(identifier)→ (✓) + (1) ——𝜀→ (0), (2) + (2) —𝜀—→ (✓) "); } #[test] fn anonymous_node() { insta::assert_snapshot!(snapshot(r#"Q = "hello""#), @r#" - Q = N0 + Q = (0) - N0: "hello" → ∅ + (0) —"hello"→ (✓) "#); } #[test] fn wildcard() { insta::assert_snapshot!(snapshot("Q = (_)"), @r" - Q = N0 + Q = (0) - N0: _ → ∅ + (0) —(🞵)→ (✓) "); } #[test] fn field_constraint() { insta::assert_snapshot!(snapshot("Q = (function name: (identifier))"), @r" - Q = N0 + Q = (0) - N0: (function) → N1 - N1: [Down] (identifier) @name → N2 - N2: [Up(1)] ε → ∅ + (0) —(function)→ (1) + (1) —{↘}—(identifier)@name→ (2) + (2) —{↗¹}—𝜀→ (✓) "); } #[test] fn to_string_annotation() { insta::assert_snapshot!(snapshot("Q = (identifier) @name ::string"), @r" - Q = N0 + Q = (0) - N0: (identifier) [Capture] [ToString] → N1 - N1: ε [Field(name)] → ∅ + (0) —(identifier)—[CaptureNode, ToString]→ (1) + (1) —𝜀—[Field(name)]→ (✓) "); } #[test] fn anchor_first_child() { insta::assert_snapshot!(snapshot("Q = (parent . (child))"), @r" - Q = N0 + Q = (0) - N0: (parent) → N1 - N1: [Down.] (child) → N2 - N2: [Up(1)] ε → ∅ + (0) —(parent)→ (1) + (1) —{↘.}—(child)→ (2) + (2) —{↗¹}—𝜀→ (✓) "); } #[test] fn anchor_sibling() { insta::assert_snapshot!(snapshot("Q = (parent (a) . (b))"), @r" - Q = N0 + Q = (0) - N0: (parent) → N1 - N1: [Down] (a) → N2 - N2: [Next.] (b) → N3 - N3: [Up(1)] ε → ∅ + (0) —(parent)→ (1) + (1) —{↘}—(a)→ (2) + (2) —{→·}—(b)→ (3) + (3) —{↗¹}—𝜀→ (✓) "); } @@ -221,22 +221,22 @@ fn anchor_sibling() { #[test] fn optimized_simple() { insta::assert_snapshot!(snapshot_optimized("Q = (identifier) @id"), @r" - Q = N0 + Q = (0) - N0: (identifier) [Capture] → N1 - N1: ε [Field(id)] → ∅ + (0) —(identifier)—[CaptureNode]→ (1) + (1) —𝜀—[Field(id)]→ (✓) "); } #[test] fn optimized_sequence() { insta::assert_snapshot!(snapshot_optimized("Q = { (a) @x (b) @y }"), @r" - Q = N1 + Q = (1) - N1: [Next] (a) [Capture] → N2 - N2: ε [Field(x)] → N3 - N3: [Next] (b) [Capture] → N4 - N4: ε [Field(y)] → ∅ + (1) —{→}—(a)—[CaptureNode]→ (2) + (2) —𝜀—[Field(x)]→ (3) + (3) —{→}—(b)—[CaptureNode]→ (4) + (4) —𝜀—[Field(y)]→ (✓) "); } @@ -254,14 +254,14 @@ fn symbol_table_reuse() { assert!(query.graph().definition("Baz").is_some()); insta::assert_snapshot!(query.graph().dump(), @r" - Foo = N0 - Bar = N1 - Baz = N3 - - N0: (identifier) → ∅ - N1: ε +Enter(0, Foo) → N0, N2 - N2: ε +Exit(0) → ∅ - N3: ε +Enter(1, Bar) → N1, N4 - N4: ε +Exit(1) → ∅ + Foo = (0) + Bar = (1) + Baz = (3) + + (0) —(identifier)→ (✓) + (1) ——𝜀→ (0), (2) + (2) —𝜀—→ (✓) + (3) ——𝜀→ (1), (4) + (4) —𝜀—→ (✓) "); } diff --git a/crates/plotnik-lib/src/query/graph_dump.rs b/crates/plotnik-lib/src/query/graph_dump.rs index 0fc3f19f..89b3e39c 100644 --- a/crates/plotnik-lib/src/query/graph_dump.rs +++ b/crates/plotnik-lib/src/query/graph_dump.rs @@ -1,6 +1,6 @@ //! Dump helpers for graph inspection and testing. -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::fmt::Write; use crate::ir::{Nav, NavKind}; @@ -39,9 +39,37 @@ impl<'a, 'src> GraphPrinter<'a, 'src> { out } + fn node_width(&self) -> usize { + let max_id = self.graph.iter().map(|(id, _)| id).max().unwrap_or(0); + if max_id == 0 { + 1 + } else { + ((max_id as f64).log10().floor() as usize) + 1 + } + } + + fn format_node_id(&self, id: NodeId, width: usize) -> String { + format!("({:0width$})", id, width = width) + } + fn format(&self, w: &mut String) -> std::fmt::Result { + let width = self.node_width(); + + // Build ref_id → name lookup from Enter nodes + let ref_names: HashMap = self + .graph + .iter() + .filter_map(|(_, node)| { + if let RefMarker::Enter { ref_id } = &node.ref_marker { + Some((*ref_id, node.ref_name.unwrap_or("?"))) + } else { + None + } + }) + .collect(); + for (name, entry) in self.graph.definitions() { - writeln!(w, "{} = N{}", name, entry)?; + writeln!(w, "{} = {}", name, self.format_node_id(entry, width))?; } if self.graph.definitions().next().is_some() { writeln!(w)?; @@ -54,34 +82,51 @@ impl<'a, 'src> GraphPrinter<'a, 'src> { continue; } + // Source node + write!(w, "{}", self.format_node_id(id, width))?; + + // Dead node short-circuit if is_dead { - write!(w, "N{}: ✗ ", id)?; - } else { - write!(w, "N{}: ", id)?; + writeln!(w, " → (⨯)")?; + continue; } + write!(w, " —")?; + + // Navigation (omit for Stay) if !node.nav.is_stay() { - write!(w, "[{}] ", format_nav(&node.nav))?; + write!(w, "{}—", format_nav(&node.nav))?; + } + + // Enter ref marker (before matcher) + if let RefMarker::Enter { .. } = &node.ref_marker { + let name = node.ref_name.unwrap_or("?"); + write!(w, "<{}>—", name)?; } + // Matcher self.format_matcher(w, &node.matcher)?; - match &node.ref_marker { - RefMarker::None => {} - RefMarker::Enter { ref_id } => { - let name = node.ref_name.unwrap_or("?"); - write!(w, " +Enter({}, {})", ref_id, name)?; - } - RefMarker::Exit { ref_id } => { - write!(w, " +Exit({})", ref_id)?; - } + // Exit ref marker (after matcher) + if let RefMarker::Exit { ref_id } = &node.ref_marker { + let name = ref_names.get(ref_id).copied().unwrap_or("?"); + write!(w, "—<{}>", name)?; } - for effect in &node.effects { - write!(w, " [{}]", format_effect(effect))?; + // Effects + if !node.effects.is_empty() { + write!(w, "—[")?; + for (i, effect) in node.effects.iter().enumerate() { + if i > 0 { + write!(w, ", ")?; + } + write!(w, "{}", format_effect(effect))?; + } + write!(w, "]")?; } - self.format_successors(w, &node.successors)?; + // Successors + self.format_successors(w, &node.successors, width)?; writeln!(w)?; } @@ -91,7 +136,7 @@ impl<'a, 'src> GraphPrinter<'a, 'src> { fn format_matcher(&self, w: &mut String, matcher: &BuildMatcher<'src>) -> std::fmt::Result { match matcher { - BuildMatcher::Epsilon => write!(w, "ε"), + BuildMatcher::Epsilon => write!(w, "𝜀"), BuildMatcher::Node { kind, field, @@ -99,72 +144,96 @@ impl<'a, 'src> GraphPrinter<'a, 'src> { } => { write!(w, "({})", kind)?; if let Some(f) = field { - write!(w, " @{}", f)?; + write!(w, "@{}", f)?; } for neg in negated_fields { - write!(w, " !{}", neg)?; + write!(w, "!{}", neg)?; } Ok(()) } BuildMatcher::Anonymous { literal, field } => { write!(w, "\"{}\"", literal)?; if let Some(f) = field { - write!(w, " @{}", f)?; + write!(w, "@{}", f)?; } Ok(()) } BuildMatcher::Wildcard { field } => { - write!(w, "_")?; + write!(w, "(🞵)")?; if let Some(f) = field { - write!(w, " @{}", f)?; + write!(w, "@{}", f)?; } Ok(()) } } } - fn format_successors(&self, w: &mut String, successors: &[NodeId]) -> std::fmt::Result { + fn format_successors( + &self, + w: &mut String, + successors: &[NodeId], + width: usize, + ) -> std::fmt::Result { let live_succs: Vec<_> = successors .iter() .filter(|s| self.dead_nodes.map(|d| !d.contains(s)).unwrap_or(true)) .collect(); if live_succs.is_empty() { - write!(w, " → ∅") + write!(w, "→ (✓)") } else { - write!(w, " → ")?; - let succs: Vec<_> = live_succs.iter().map(|s| format!("N{}", s)).collect(); - write!(w, "{}", succs.join(", ")) + write!(w, "→ ")?; + for (i, s) in live_succs.iter().enumerate() { + if i > 0 { + write!(w, ", ")?; + } + write!(w, "{}", self.format_node_id(**s, width))?; + } + Ok(()) } } } fn format_nav(nav: &Nav) -> String { match nav.kind { - NavKind::Stay => "Stay".to_string(), - NavKind::Next => "Next".to_string(), - NavKind::NextSkipTrivia => "Next.".to_string(), - NavKind::NextExact => "Next!".to_string(), - NavKind::Down => "Down".to_string(), - NavKind::DownSkipTrivia => "Down.".to_string(), - NavKind::DownExact => "Down!".to_string(), - NavKind::Up => format!("Up({})", nav.level), - NavKind::UpSkipTrivia => format!("Up.({})", nav.level), - NavKind::UpExact => format!("Up!({})", nav.level), + NavKind::Stay => "{˟}".to_string(), + NavKind::Next => "{→}".to_string(), + NavKind::NextSkipTrivia => "{→·}".to_string(), + NavKind::NextExact => "{→!}".to_string(), + NavKind::Down => "{↘}".to_string(), + NavKind::DownSkipTrivia => "{↘.}".to_string(), + NavKind::DownExact => "{↘!}".to_string(), + NavKind::Up => format!("{{↗{}}}", to_superscript(nav.level)), + NavKind::UpSkipTrivia => format!("{{↗·{}}}", to_superscript(nav.level)), + NavKind::UpExact => format!("{{↗!{}}}", to_superscript(nav.level)), + } +} + +fn to_superscript(n: u8) -> String { + const SUPERSCRIPTS: [char; 10] = ['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹']; + if n == 0 { + return "⁰".to_string(); + } + let mut result = String::new(); + let mut num = n; + while num > 0 { + result.insert(0, SUPERSCRIPTS[(num % 10) as usize]); + num /= 10; } + result } fn format_effect(effect: &BuildEffect) -> String { match effect { - BuildEffect::CaptureNode => "Capture".to_string(), - BuildEffect::ClearCurrent => "Clear".to_string(), + BuildEffect::CaptureNode => "CaptureNode".to_string(), + BuildEffect::ClearCurrent => "ClearCurrent".to_string(), BuildEffect::StartArray { .. } => "StartArray".to_string(), - BuildEffect::PushElement => "Push".to_string(), + BuildEffect::PushElement => "PushElement".to_string(), BuildEffect::EndArray => "EndArray".to_string(), - BuildEffect::StartObject { .. } => "StartObj".to_string(), - BuildEffect::EndObject => "EndObj".to_string(), + BuildEffect::StartObject { .. } => "StartObject".to_string(), + BuildEffect::EndObject => "EndObject".to_string(), BuildEffect::Field { name, .. } => format!("Field({})", name), - BuildEffect::StartVariant(v) => format!("Variant({})", v), + BuildEffect::StartVariant(v) => format!("StartVariant({})", v), BuildEffect::EndVariant => "EndVariant".to_string(), BuildEffect::ToString => "ToString".to_string(), } diff --git a/crates/plotnik-lib/src/query/graph_master_test.rs b/crates/plotnik-lib/src/query/graph_master_test.rs index 1d77456a..f332ec21 100644 --- a/crates/plotnik-lib/src/query/graph_master_test.rs +++ b/crates/plotnik-lib/src/query/graph_master_test.rs @@ -245,449 +245,359 @@ fn golden_master_comprehensive() { TRANSITION GRAPH ═══════════════════════════════════════════════════════════════════════════════ - SimpleCapture = N0 - StringCapture = N2 - MultiCapture = N4 - AnchorFirst = N10 - AnchorLast = N14 - AnchorSibling = N18 - DeepNest = N24 - StarQuant = N32 - PlusQuant = N40 - OptQuant = N48 - QisNode = N61 - QisSequence = N72 - NoQis = N81 - TaggedRoot = N85 - TaggedCaptured = N95 - TaggedMulti = N110 - UntaggedSymmetric = N124 - UntaggedAsymmetric = N130 - UntaggedCaptured = N136 - CapturedSeq = N145 - UncapturedSeq = N155 - NestedScopes = N166 - Identifier = N178 - RefSimple = N180 - RefCaptured = N182 - RefChain = N185 - CardinalityJoin = N187 - NestedQuant = N207 - Complex = N212 - WildcardCapture = N262 - StringLiteral = N264 - NoCaptures = N266 - EmptyBranch = N267 - - N0: (identifier) [Capture] → N1 - N1: ε [Field(name)] → ∅ - N2: (identifier) [Capture] [ToString] → N3 - N3: ε [Field(name)] → ∅ - N4: (function) → N5 - N5: [Down] (identifier) @name [Capture] [ToString] → N6 - N6: ε [Field(fn_name)] → N7 - N7: [Next] (block) @body [Capture] → N8 - N8: ε [Field(fn_body)] → N9 - N9: [Up(1)] ε → ∅ - N10: (parent) → N11 - N11: [Down.] (first_child) [Capture] → N12 - N12: ε [Field(first)] → N13 - N13: [Up(1)] ε → ∅ - N14: (parent) → N15 - N15: [Down] (last_child) [Capture] → N16 - N16: ε [Field(last)] → N17 - N17: [Up.(1)] ε → ∅ - N18: (parent) → N19 - N19: [Down] (a) [Capture] → N20 - N20: ε [Field(left)] → N21 - N21: [Next.] (b) [Capture] → N22 - N22: ε [Field(right)] → N23 - N23: [Up(1)] ε → ∅ - N24: (a) → N25 - N25: [Down] (b) → N26 - N26: [Down] (c) → N27 - N27: [Down] (d) [Capture] → N28 - N28: ε [Field(deep)] → N31 - N31: [Up(3)] ε → ∅ - N32: (container) → N34 - N33: [Down] (item) [Capture] → N36 - N34: ε [StartArray] → N37 - N36: ε [Push] → N37 - N37: ε → N33, N38 - N38: ε [EndArray] [Field(items)] → N39 - N39: [Up(1)] ε → ∅ - N40: (container) → N42 - N41: [Down] (item) [Capture] → N45 - N42: ε [StartArray] → N41 - N45: ε [Push] → N41, N46 - N46: ε [EndArray] [Field(items)] → N47 - N47: [Up(1)] ε → ∅ - N48: (container) → N50 - N49: [Down] (item) [Capture] → N53 - N50: ε → N49, N52 - N52: ε [Clear] → N53 - N53: ε [Field(maybe_item)] → N54 - N54: [Up(1)] ε → ∅ - N55: (function) [StartObj] → N56 - N56: [Down] (identifier) @name [Capture] → N57 - N57: ε [Field(name)] → N58 - N58: [Next] (block) @body [Capture] → N59 - N59: ε [Field(body)] → N65 - N61: ε [StartArray] → N66 - N62: ε [EndArray] → ∅ - N65: [Up(1)] ε [EndObj] [Push] → N66 - N66: ε → N55, N62 - N67: ε [StartObj] → N68 - N68: [Next] (key) [Capture] → N69 - N69: ε [Field(key)] → N70 - N70: [Next] (value) [Capture] → N76 - N72: ε [StartArray] → N77 - N73: ε [EndArray] → ∅ - N76: ε [Field(value)] [EndObj] [Push] → N77 - N77: ε → N67, N73 - N79: [Next] (item) [Capture] → N83 - N81: ε [StartArray] → N84 - N82: ε [EndArray] → ∅ - N83: ε [Field(item)] [Push] → N84 - N84: ε → N79, N82 - N85: ε → N88, N92 - N86: ε → ∅ - N88: (success) [Variant(Ok)] [Capture] → N90 - N90: ε [Field(val)] [EndVariant] → N86 - N92: (error) [Variant(Err)] [Capture] [ToString] → N94 - N94: ε [Field(msg)] [EndVariant] → N86 - N95: (wrapper) → N106 - N96: [Down] ε → N99, N103 - N99: (left_node) [Variant(Left)] [Capture] [Capture] → N101 - N101: ε [Field(l)] [EndVariant] → N108 - N103: (right_node) [Variant(Right)] [Capture] [Capture] → N105 - N105: ε [Field(r)] [EndVariant] → N108 - N106: ε [StartObj] → N96 - N108: ε [EndObj] [Field(choice)] → N109 - N109: [Up(1)] ε → ∅ - N110: ε → N113, N117 - N111: ε → ∅ - N113: (node) [Variant(Simple)] [Capture] → N115 - N115: ε [Field(val)] [EndVariant] → N111 - N117: (pair) [Variant(Complex)] [StartObj] → N118 - N118: [Down] (key) [Capture] → N119 - N119: ε [Field(k)] → N120 - N120: [Next] (value) [Capture] → N121 - N121: ε [Field(v)] → N123 - N123: [Up(1)] ε [EndObj] [EndVariant] → N111 - N124: ε → N126, N128 - N125: ε → ∅ - N126: (a) [Capture] → N127 - N127: ε [Field(val)] → N125 - N128: (b) [Capture] → N129 - N129: ε [Field(val)] → N125 - N130: ε → N132, N134 - N131: ε → ∅ - N132: (a) [Capture] → N133 - N133: ε [Field(x)] → N131 - N134: (b) [Capture] → N135 - N135: ε [Field(y)] → N131 - N136: ε [StartObj] → N138, N140 - N138: (a) [Capture] [Capture] → N139 - N139: ε [Field(x)] → N144 - N140: (b) [Capture] [Capture] → N141 - N141: ε [Field(y)] → N144 - N144: ε [EndObj] [Field(data)] → ∅ - N145: (outer) → N151 - N146: [Down] ε → N147 - N147: [Next] (inner) [Capture] [Capture] → N148 - N148: ε [Field(x)] → N149 - N149: [Next] (inner2) [Capture] → N153 - N151: ε [StartObj] → N146 - N153: ε [Field(y)] [EndObj] [Field(nested)] → N154 - N154: [Up(1)] ε → ∅ - N155: (outer) → N156 - N156: [Down] ε → N157 - N157: [Next] (inner) [Capture] → N158 - N158: ε [Field(x)] → N159 - N159: [Next] (inner2) [Capture] → N160 - N160: ε [Field(y)] → N161 - N161: [Up(1)] ε → ∅ - N163: [Next] ε → N164 - N164: [Next] (a) [Capture] [Capture] [Capture] → N172 - N166: ε [StartObj] [StartObj] → N163 - N169: [Next] ε → N170 - N170: [Next] (b) [Capture] [Capture] → N177 - N172: ε [Field(a)] [EndObj] [Field(inner1)] [StartObj] → N169 - N177: ε [Field(b)] [EndObj] [Field(inner2)] [EndObj] [Field(outer)] → ∅ - N178: (identifier) [Capture] → N179 - N179: ε [Field(id)] → ∅ - N180: ε +Enter(0, Identifier) → N178, N181 - N181: ε +Exit(0) → ∅ - N182: ε +Enter(1, Identifier) → N178, N183 - N183: ε +Exit(1) [Capture] → N184 - N184: ε [Field(captured_id)] → ∅ - N185: ε +Enter(2, RefSimple) → N180, N186 - N186: ε +Exit(2) → ∅ - N187: ε → N189, N191 - N188: [Up(1)] ε → ∅ - N189: (single) [Capture] → N190 - N190: ε [Field(item)] → N188 - N191: (multi) → N193 - N192: [Down] (x) [Capture] → N196 - N193: ε [StartArray] → N192 - N196: ε [Push] → N192, N197 - N197: ε [EndArray] [Field(item)] → N188 - N199: (_) [Capture] → N201 - N200: [Down] (item) [Capture] → N203 - N201: ε [StartArray] → N204 - N203: ε [Push] → N204 - N204: ε → N200, N205 - N205: ε [EndArray] [Field(inner)] → N210 - N207: ε [StartArray] → N199 - N210: [Up(1)] ε [Push] → N199, N211 - N211: ε [EndArray] [Field(outer)] → ∅ - N212: (module) → N213 - N213: [Down] (identifier) @name [Capture] [ToString] → N216 - N215: [Next.] (import) [Capture] → N218 - N216: ε [Field(mod_name)] [StartArray] → N219 - N218: ε [Push] → N219 - N219: ε → N215, N220 - N220: ε [EndArray] [Field(imports)] → N221 - N221: [Next] (block) @body → N251 - N222: [Down] ε → N223 - N223: [Next] ε → N226, N244 - N226: (function) [Variant(Func)] [StartObj] [Capture] → N227 - N227: [Down] (identifier) @name [Capture] [ToString] → N228 - N228: ε [Field(fn_name)] → N229 - N229: [Next] (parameters) @params → N233 - N230: [Down] ε → N231 - N231: [Next] (param) [Capture] [Capture] → N235 - N233: ε [StartArray] → N236 - N235: ε [Field(p)] [Push] → N236 - N236: ε → N230, N237 - N237: ε [EndArray] [Field(params)] → N238 - N238: [Up(1)] ε → N239 - N239: [Next] (block) @body [Capture] → N240 - N240: ε [Field(fn_body)] → N242 - N242: [Up(1)] ε [EndObj] [EndVariant] → N255 - N244: (class) [Variant(Class)] [StartObj] [Capture] → N245 - N245: [Down] (identifier) @name [Capture] [ToString] → N246 - N246: ε [Field(cls_name)] → N247 - N247: [Next] (class_body) @body [Capture] → N248 - N248: ε [Field(cls_body)] → N250 - N250: [Up(1)] ε [EndObj] [EndVariant] → N255 - N251: ε [StartObj] [StartArray] → N256 - N253: ε [StartObj] → N222 - N255: ε [EndObj] [Push] → N256 - N256: ε → N253, N259 - N259: ε [EndArray] [EndObj] [Field(items)] → N260 - N260: [Up(1)] ε → N261 - N261: [Up.(1)] ε → ∅ - N262: _ [Capture] → N263 - N263: ε [Field(any)] → ∅ - N264: "+" [Capture] → N265 - N265: ε [Field(op)] → ∅ - N266: (identifier) → ∅ - N267: ε → N270, N274 - N268: ε → ∅ - N270: (value) [Variant(Some)] [Capture] → N272 - N272: ε [Field(val)] [EndVariant] → N268 - N274: (none_marker) [Variant(None)] → N275 - N275: ε [EndVariant] → N268 + SimpleCapture = (000) + StringCapture = (002) + MultiCapture = (004) + AnchorFirst = (010) + AnchorLast = (014) + AnchorSibling = (018) + DeepNest = (024) + StarQuant = (032) + PlusQuant = (040) + OptQuant = (048) + QisNode = (061) + QisSequence = (072) + NoQis = (081) + TaggedRoot = (085) + TaggedCaptured = (095) + TaggedMulti = (110) + UntaggedSymmetric = (124) + UntaggedAsymmetric = (130) + UntaggedCaptured = (136) + CapturedSeq = (145) + UncapturedSeq = (155) + NestedScopes = (166) + Identifier = (178) + RefSimple = (180) + RefCaptured = (182) + RefChain = (185) + CardinalityJoin = (187) + NestedQuant = (207) + Complex = (212) + WildcardCapture = (262) + StringLiteral = (264) + NoCaptures = (266) + EmptyBranch = (267) + + (000) —(identifier)—[CaptureNode]→ (001) + (001) —𝜀—[Field(name)]→ (✓) + (002) —(identifier)—[CaptureNode, ToString]→ (003) + (003) —𝜀—[Field(name)]→ (✓) + (004) —(function)→ (005) + (005) —{↘}—(identifier)@name—[CaptureNode, ToString]→ (006) + (006) —𝜀—[Field(fn_name)]→ (007) + (007) —{→}—(block)@body—[CaptureNode]→ (008) + (008) —𝜀—[Field(fn_body)]→ (009) + (009) —{↗¹}—𝜀→ (✓) + (010) —(parent)→ (011) + (011) —{↘.}—(first_child)—[CaptureNode]→ (012) + (012) —𝜀—[Field(first)]→ (013) + (013) —{↗¹}—𝜀→ (✓) + (014) —(parent)→ (015) + (015) —{↘}—(last_child)—[CaptureNode]→ (016) + (016) —𝜀—[Field(last)]→ (017) + (017) —{↗·¹}—𝜀→ (✓) + (018) —(parent)→ (019) + (019) —{↘}—(a)—[CaptureNode]→ (020) + (020) —𝜀—[Field(left)]→ (021) + (021) —{→·}—(b)—[CaptureNode]→ (022) + (022) —𝜀—[Field(right)]→ (023) + (023) —{↗¹}—𝜀→ (✓) + (024) —(a)→ (025) + (025) —{↘}—(b)→ (026) + (026) —{↘}—(c)→ (027) + (027) —{↘}—(d)—[CaptureNode]→ (028) + (028) —𝜀—[Field(deep)]→ (031) + (031) —{↗³}—𝜀→ (✓) + (032) —(container)→ (034) + (033) —{↘}—(item)—[CaptureNode]→ (036) + (034) —𝜀—[StartArray]→ (037) + (036) —𝜀—[PushElement]→ (037) + (037) —𝜀→ (033), (038) + (038) —𝜀—[EndArray, Field(items)]→ (039) + (039) —{↗¹}—𝜀→ (✓) + (040) —(container)→ (042) + (041) —{↘}—(item)—[CaptureNode]→ (045) + (042) —𝜀—[StartArray]→ (041) + (045) —𝜀—[PushElement]→ (041), (046) + (046) —𝜀—[EndArray, Field(items)]→ (047) + (047) —{↗¹}—𝜀→ (✓) + (048) —(container)→ (050) + (049) —{↘}—(item)—[CaptureNode]→ (053) + (050) —𝜀→ (049), (052) + (052) —𝜀—[ClearCurrent]→ (053) + (053) —𝜀—[Field(maybe_item)]→ (054) + (054) —{↗¹}—𝜀→ (✓) + (055) —(function)—[StartObject]→ (056) + (056) —{↘}—(identifier)@name—[CaptureNode]→ (057) + (057) —𝜀—[Field(name)]→ (058) + (058) —{→}—(block)@body—[CaptureNode]→ (059) + (059) —𝜀—[Field(body)]→ (065) + (061) —𝜀—[StartArray]→ (066) + (062) —𝜀—[EndArray]→ (✓) + (065) —{↗¹}—𝜀—[EndObject, PushElement]→ (066) + (066) —𝜀→ (055), (062) + (067) —𝜀—[StartObject]→ (068) + (068) —{→}—(key)—[CaptureNode]→ (069) + (069) —𝜀—[Field(key)]→ (070) + (070) —{→}—(value)—[CaptureNode]→ (076) + (072) —𝜀—[StartArray]→ (077) + (073) —𝜀—[EndArray]→ (✓) + (076) —𝜀—[Field(value), EndObject, PushElement]→ (077) + (077) —𝜀→ (067), (073) + (079) —{→}—(item)—[CaptureNode]→ (083) + (081) —𝜀—[StartArray]→ (084) + (082) —𝜀—[EndArray]→ (✓) + (083) —𝜀—[Field(item), PushElement]→ (084) + (084) —𝜀→ (079), (082) + (085) —𝜀→ (088), (092) + (086) —𝜀→ (✓) + (088) —(success)—[StartVariant(Ok), CaptureNode]→ (090) + (090) —𝜀—[Field(val), EndVariant]→ (086) + (092) —(error)—[StartVariant(Err), CaptureNode, ToString]→ (094) + (094) —𝜀—[Field(msg), EndVariant]→ (086) + (095) —(wrapper)→ (106) + (096) —{↘}—𝜀→ (099), (103) + (099) —(left_node)—[StartVariant(Left), CaptureNode, CaptureNode]→ (101) + (101) —𝜀—[Field(l), EndVariant]→ (108) + (103) —(right_node)—[StartVariant(Right), CaptureNode, CaptureNode]→ (105) + (105) —𝜀—[Field(r), EndVariant]→ (108) + (106) —𝜀—[StartObject]→ (096) + (108) —𝜀—[EndObject, Field(choice)]→ (109) + (109) —{↗¹}—𝜀→ (✓) + (110) —𝜀→ (113), (117) + (111) —𝜀→ (✓) + (113) —(node)—[StartVariant(Simple), CaptureNode]→ (115) + (115) —𝜀—[Field(val), EndVariant]→ (111) + (117) —(pair)—[StartVariant(Complex), StartObject]→ (118) + (118) —{↘}—(key)—[CaptureNode]→ (119) + (119) —𝜀—[Field(k)]→ (120) + (120) —{→}—(value)—[CaptureNode]→ (121) + (121) —𝜀—[Field(v)]→ (123) + (123) —{↗¹}—𝜀—[EndObject, EndVariant]→ (111) + (124) —𝜀→ (126), (128) + (125) —𝜀→ (✓) + (126) —(a)—[CaptureNode]→ (127) + (127) —𝜀—[Field(val)]→ (125) + (128) —(b)—[CaptureNode]→ (129) + (129) —𝜀—[Field(val)]→ (125) + (130) —𝜀→ (132), (134) + (131) —𝜀→ (✓) + (132) —(a)—[CaptureNode]→ (133) + (133) —𝜀—[Field(x)]→ (131) + (134) —(b)—[CaptureNode]→ (135) + (135) —𝜀—[Field(y)]→ (131) + (136) —𝜀—[StartObject]→ (138), (140) + (138) —(a)—[CaptureNode, CaptureNode]→ (139) + (139) —𝜀—[Field(x)]→ (144) + (140) —(b)—[CaptureNode, CaptureNode]→ (141) + (141) —𝜀—[Field(y)]→ (144) + (144) —𝜀—[EndObject, Field(data)]→ (✓) + (145) —(outer)→ (151) + (146) —{↘}—𝜀→ (147) + (147) —{→}—(inner)—[CaptureNode, CaptureNode]→ (148) + (148) —𝜀—[Field(x)]→ (149) + (149) —{→}—(inner2)—[CaptureNode]→ (153) + (151) —𝜀—[StartObject]→ (146) + (153) —𝜀—[Field(y), EndObject, Field(nested)]→ (154) + (154) —{↗¹}—𝜀→ (✓) + (155) —(outer)→ (156) + (156) —{↘}—𝜀→ (157) + (157) —{→}—(inner)—[CaptureNode]→ (158) + (158) —𝜀—[Field(x)]→ (159) + (159) —{→}—(inner2)—[CaptureNode]→ (160) + (160) —𝜀—[Field(y)]→ (161) + (161) —{↗¹}—𝜀→ (✓) + (163) —{→}—𝜀→ (164) + (164) —{→}—(a)—[CaptureNode, CaptureNode, CaptureNode]→ (172) + (166) —𝜀—[StartObject, StartObject]→ (163) + (169) —{→}—𝜀→ (170) + (170) —{→}—(b)—[CaptureNode, CaptureNode]→ (177) + (172) —𝜀—[Field(a), EndObject, Field(inner1), StartObject]→ (169) + (177) —𝜀—[Field(b), EndObject, Field(inner2), EndObject, Field(outer)]→ (✓) + (178) —(identifier)—[CaptureNode]→ (179) + (179) —𝜀—[Field(id)]→ (✓) + (180) ——𝜀→ (178), (181) + (181) —𝜀—→ (✓) + (182) ——𝜀→ (178), (183) + (183) —𝜀——[CaptureNode]→ (184) + (184) —𝜀—[Field(captured_id)]→ (✓) + (185) ——𝜀→ (180), (186) + (186) —𝜀—→ (✓) + (187) —𝜀→ (189), (191) + (188) —{↗¹}—𝜀→ (✓) + (189) —(single)—[CaptureNode]→ (190) + (190) —𝜀—[Field(item)]→ (188) + (191) —(multi)→ (193) + (192) —{↘}—(x)—[CaptureNode]→ (196) + (193) —𝜀—[StartArray]→ (192) + (196) —𝜀—[PushElement]→ (192), (197) + (197) —𝜀—[EndArray, Field(item)]→ (188) + (199) —(_)—[CaptureNode]→ (201) + (200) —{↘}—(item)—[CaptureNode]→ (203) + (201) —𝜀—[StartArray]→ (204) + (203) —𝜀—[PushElement]→ (204) + (204) —𝜀→ (200), (205) + (205) —𝜀—[EndArray, Field(inner)]→ (210) + (207) —𝜀—[StartArray]→ (199) + (210) —{↗¹}—𝜀—[PushElement]→ (199), (211) + (211) —𝜀—[EndArray, Field(outer)]→ (✓) + (212) —(module)→ (213) + (213) —{↘}—(identifier)@name—[CaptureNode, ToString]→ (216) + (215) —{→·}—(import)—[CaptureNode]→ (218) + (216) —𝜀—[Field(mod_name), StartArray]→ (219) + (218) —𝜀—[PushElement]→ (219) + (219) —𝜀→ (215), (220) + (220) —𝜀—[EndArray, Field(imports)]→ (221) + (221) —{→}—(block)@body→ (251) + (222) —{↘}—𝜀→ (223) + (223) —{→}—𝜀→ (226), (244) + (226) —(function)—[StartVariant(Func), StartObject, CaptureNode]→ (227) + (227) —{↘}—(identifier)@name—[CaptureNode, ToString]→ (228) + (228) —𝜀—[Field(fn_name)]→ (229) + (229) —{→}—(parameters)@params→ (233) + (230) —{↘}—𝜀→ (231) + (231) —{→}—(param)—[CaptureNode, CaptureNode]→ (235) + (233) —𝜀—[StartArray]→ (236) + (235) —𝜀—[Field(p), PushElement]→ (236) + (236) —𝜀→ (230), (237) + (237) —𝜀—[EndArray, Field(params)]→ (238) + (238) —{↗¹}—𝜀→ (239) + (239) —{→}—(block)@body—[CaptureNode]→ (240) + (240) —𝜀—[Field(fn_body)]→ (242) + (242) —{↗¹}—𝜀—[EndObject, EndVariant]→ (255) + (244) —(class)—[StartVariant(Class), StartObject, CaptureNode]→ (245) + (245) —{↘}—(identifier)@name—[CaptureNode, ToString]→ (246) + (246) —𝜀—[Field(cls_name)]→ (247) + (247) —{→}—(class_body)@body—[CaptureNode]→ (248) + (248) —𝜀—[Field(cls_body)]→ (250) + (250) —{↗¹}—𝜀—[EndObject, EndVariant]→ (255) + (251) —𝜀—[StartObject, StartArray]→ (256) + (253) —𝜀—[StartObject]→ (222) + (255) —𝜀—[EndObject, PushElement]→ (256) + (256) —𝜀→ (253), (259) + (259) —𝜀—[EndArray, EndObject, Field(items)]→ (260) + (260) —{↗¹}—𝜀→ (261) + (261) —{↗·¹}—𝜀→ (✓) + (262) —(🞵)—[CaptureNode]→ (263) + (263) —𝜀—[Field(any)]→ (✓) + (264) —"+"—[CaptureNode]→ (265) + (265) —𝜀—[Field(op)]→ (✓) + (266) —(identifier)→ (✓) + (267) —𝜀→ (270), (274) + (268) —𝜀→ (✓) + (270) —(value)—[StartVariant(Some), CaptureNode]→ (272) + (272) —𝜀—[Field(val), EndVariant]→ (268) + (274) —(none_marker)—[StartVariant(None)]→ (275) + (275) —𝜀—[EndVariant]→ (268) ═══════════════════════════════════════════════════════════════════════════════ TYPE INFERENCE ═══════════════════════════════════════════════════════════════════════════════ - === Entrypoints === - Identifier → T3 - RefSimple → Void - WildcardCapture → T4 - UntaggedSymmetric → T5 - UntaggedCaptured → T9 - UntaggedAsymmetric → T12 - UncapturedSeq → T13 - TaggedRoot → T14 - TaggedMulti → T16 - TaggedCaptured → T18 - StringLiteral → T19 - StringCapture → T20 - StarQuant → T22 - SimpleCapture → T23 - RefChain → Void - RefCaptured → T24 - QisSequence → T26 - QisNode → T28 - PlusQuant → T30 - OptQuant → T32 - NoQis → T34 - NoCaptures → Void - NestedScopes → T38 - NestedQuant → T41 - MultiCapture → T42 - EmptyBranch → T43 - DeepNest → T44 - Complex → T54 - CardinalityJoin → T56 - CapturedSeq → T58 - AnchorSibling → T59 - AnchorLast → T60 - AnchorFirst → T61 - - === Types === - T3: Record Identifier { - id: Node - } - T4: Record WildcardCapture { - any: Node - } - T5: Record UntaggedSymmetric { - val: Node - } - T6: Optional → Node - T7: Optional → Node - T8: Record UntaggedCapturedScope6 { - x: T6 - y: T7 - } - T9: Record UntaggedCaptured { - data: T8 - } - T10: Optional → Node - T11: Optional → Node - T12: Record UntaggedAsymmetric { - x: T10 - y: T11 - } - T13: Record UncapturedSeq { - x: Node - y: Node - } - T14: Enum TaggedRoot { - Ok: Node - Err: String - } - T15: Record TaggedMultiScope15 { - k: Node - v: Node - } - T16: Enum TaggedMulti { - Simple: Node - Complex: T15 - } - T17: Enum TaggedCapturedScope17 { - Left: Node - Right: Node - } - T18: Record TaggedCaptured { - choice: T17 - } - T19: Record StringLiteral { - op: Node - } - T20: Record StringCapture { - name: String - } - T21: ArrayStar → Node - T22: Record StarQuant { - items: T21 - } - T23: Record SimpleCapture { - name: Node - } - T24: Record RefCaptured { - captured_id: T3 - } - T25: Record QisSequenceScope25 { - key: Node - value: Node - } - T26: ArrayStar → T25 - T27: Record QisNodeScope27 { - name: Node - body: Node - } - T28: ArrayStar → T27 - T29: ArrayPlus → Node - T30: Record PlusQuant { - items: T29 - } - T31: Optional → Node - T32: Record OptQuant { - maybe_item: T31 - } - T33: ArrayStar → Node - T34: Record NoQis { - item: T33 - } - T35: Record NestedScopesScope35 { - a: Node - } - T36: Record NestedScopesScope36 { - b: Node - } - T37: Record NestedScopesScope37 { - inner1: T35 - inner2: T36 - } - T38: Record NestedScopes { - outer: T37 - } - T39: ArrayStar → Node - T40: ArrayPlus → Node - T41: Record NestedQuant { - inner: T39 - outer: T40 - } - T42: Record MultiCapture { - fn_name: String - fn_body: Node - } - T43: Enum EmptyBranch { - Some: Node - None: Void - } - T44: Record DeepNest { - deep: Node - } - T45: Optional → String - T46: ArrayStar → Node - T47: ArrayStar → Node - T48: Optional → Node - T49: Optional → String - T50: Optional → Node - T51: Record ComplexScope45 { - fn_name: T45 - p: T46 - params: T47 - fn_body: T48 - cls_name: T49 - cls_body: T50 - } - T52: ArrayStar → T51 - T53: ArrayStar → Node - T54: Record Complex { - mod_name: String - imports: T53 - items: T52 - } - T55: ArrayPlus → Node - T56: Record CardinalityJoin { - item: T55 - } - T57: Record CapturedSeqScope57 { - x: Node - y: Node - } - T58: Record CapturedSeq { - nested: T57 - } - T59: Record AnchorSibling { - left: Node - right: Node - } - T60: Record AnchorLast { - last: Node - } - T61: Record AnchorFirst { - first: Node - } + RefSimple = () + RefChain = () + QisSequence = T26 + QisNode = T28 + NoCaptures = () + + Identifier = { id: Node } + WildcardCapture = { any: Node } + UntaggedSymmetric = { val: Node } + UntaggedCapturedScope6 = { + x: Node? + y: Node? + } + UntaggedCaptured = { data: UntaggedCapturedScope6 } + UntaggedAsymmetric = { + x: Node? + y: Node? + } + UncapturedSeq = { + x: Node + y: Node + } + TaggedRoot = { + Ok => Node + Err => str + } + TaggedMultiScope15 = { + k: Node + v: Node + } + TaggedMulti = { + Simple => Node + Complex => TaggedMultiScope15 + } + TaggedCapturedScope17 = { + Left => Node + Right => Node + } + TaggedCaptured = { choice: TaggedCapturedScope17 } + StringLiteral = { op: Node } + StringCapture = { name: str } + StarQuant = { items: [Node] } + SimpleCapture = { name: Node } + RefCaptured = { captured_id: Identifier } + QisSequenceScope25 = { + key: Node + value: Node + } + T26 = [QisSequenceScope25] + QisNodeScope27 = { + name: Node + body: Node + } + T28 = [QisNodeScope27] + PlusQuant = { items: [Node]⁺ } + OptQuant = { maybe_item: Node? } + NoQis = { item: [Node] } + NestedScopesScope35 = { a: Node } + NestedScopesScope36 = { b: Node } + NestedScopesScope37 = { + inner1: NestedScopesScope35 + inner2: NestedScopesScope36 + } + NestedScopes = { outer: NestedScopesScope37 } + NestedQuant = { + inner: [Node] + outer: [Node]⁺ + } + MultiCapture = { + fn_name: str + fn_body: Node + } + EmptyBranch = { + Some => Node + None => () + } + DeepNest = { deep: Node } + ComplexScope45 = { + fn_name: str? + p: [Node] + params: [Node] + fn_body: Node? + cls_name: str? + cls_body: Node? + } + T52 = [ComplexScope45] + Complex = { + mod_name: str + imports: [Node] + items: T52 + } + CardinalityJoin = { item: [Node]⁺ } + CapturedSeqScope57 = { + x: Node + y: Node + } + CapturedSeq = { nested: CapturedSeqScope57 } + AnchorSibling = { + left: Node + right: Node + } + AnchorLast = { last: Node } + AnchorFirst = { first: Node } "#); } @@ -728,110 +638,86 @@ fn golden_navigation_patterns() { TRANSITION GRAPH ═══════════════════════════════════════════════════════════════════════════════ - NavStay = N0 - NavDown = N2 - NavDownAnchor = N6 - NavNext = N10 - NavNextAnchor = N16 - NavUp = N22 - NavUpAnchor = N28 - NavUpMulti = N32 - NavMixed = N42 - - N0: (root) [Capture] → N1 - N1: ε [Field(r)] → ∅ - N2: (parent) → N3 - N3: [Down] (child) [Capture] → N4 - N4: ε [Field(c)] → N5 - N5: [Up(1)] ε → ∅ - N6: (parent) → N7 - N7: [Down.] (child) [Capture] → N8 - N8: ε [Field(c)] → N9 - N9: [Up(1)] ε → ∅ - N10: (parent) → N11 - N11: [Down] (a) [Capture] → N12 - N12: ε [Field(a)] → N13 - N13: [Next] (b) [Capture] → N14 - N14: ε [Field(b)] → N15 - N15: [Up(1)] ε → ∅ - N16: (parent) → N17 - N17: [Down] (a) [Capture] → N18 - N18: ε [Field(a)] → N19 - N19: [Next.] (b) [Capture] → N20 - N20: ε [Field(b)] → N21 - N21: [Up(1)] ε → ∅ - N22: (a) → N23 - N23: [Down] (b) → N24 - N24: [Down] (c) [Capture] → N25 - N25: ε [Field(c)] → N27 - N27: [Up(2)] ε → ∅ - N28: (parent) → N29 - N29: [Down] (child) [Capture] → N30 - N30: ε [Field(c)] → N31 - N31: [Up.(1)] ε → ∅ - N32: (a) → N33 - N33: [Down] (b) → N34 - N34: [Down] (c) → N35 - N35: [Down] (d) → N36 - N36: [Down] (e) [Capture] → N37 - N37: ε [Field(e)] → N41 - N41: [Up(4)] ε → ∅ - N42: (outer) → N43 - N43: [Down.] (first) [Capture] → N44 - N44: ε [Field(f)] → N45 - N45: [Next] (middle) [Capture] → N46 - N46: ε [Field(m)] → N47 - N47: [Next.] (last) [Capture] → N48 - N48: ε [Field(l)] → N49 - N49: [Up.(1)] ε → ∅ + NavStay = (00) + NavDown = (02) + NavDownAnchor = (06) + NavNext = (10) + NavNextAnchor = (16) + NavUp = (22) + NavUpAnchor = (28) + NavUpMulti = (32) + NavMixed = (42) + + (00) —(root)—[CaptureNode]→ (01) + (01) —𝜀—[Field(r)]→ (✓) + (02) —(parent)→ (03) + (03) —{↘}—(child)—[CaptureNode]→ (04) + (04) —𝜀—[Field(c)]→ (05) + (05) —{↗¹}—𝜀→ (✓) + (06) —(parent)→ (07) + (07) —{↘.}—(child)—[CaptureNode]→ (08) + (08) —𝜀—[Field(c)]→ (09) + (09) —{↗¹}—𝜀→ (✓) + (10) —(parent)→ (11) + (11) —{↘}—(a)—[CaptureNode]→ (12) + (12) —𝜀—[Field(a)]→ (13) + (13) —{→}—(b)—[CaptureNode]→ (14) + (14) —𝜀—[Field(b)]→ (15) + (15) —{↗¹}—𝜀→ (✓) + (16) —(parent)→ (17) + (17) —{↘}—(a)—[CaptureNode]→ (18) + (18) —𝜀—[Field(a)]→ (19) + (19) —{→·}—(b)—[CaptureNode]→ (20) + (20) —𝜀—[Field(b)]→ (21) + (21) —{↗¹}—𝜀→ (✓) + (22) —(a)→ (23) + (23) —{↘}—(b)→ (24) + (24) —{↘}—(c)—[CaptureNode]→ (25) + (25) —𝜀—[Field(c)]→ (27) + (27) —{↗²}—𝜀→ (✓) + (28) —(parent)→ (29) + (29) —{↘}—(child)—[CaptureNode]→ (30) + (30) —𝜀—[Field(c)]→ (31) + (31) —{↗·¹}—𝜀→ (✓) + (32) —(a)→ (33) + (33) —{↘}—(b)→ (34) + (34) —{↘}—(c)→ (35) + (35) —{↘}—(d)→ (36) + (36) —{↘}—(e)—[CaptureNode]→ (37) + (37) —𝜀—[Field(e)]→ (41) + (41) —{↗⁴}—𝜀→ (✓) + (42) —(outer)→ (43) + (43) —{↘.}—(first)—[CaptureNode]→ (44) + (44) —𝜀—[Field(f)]→ (45) + (45) —{→}—(middle)—[CaptureNode]→ (46) + (46) —𝜀—[Field(m)]→ (47) + (47) —{→·}—(last)—[CaptureNode]→ (48) + (48) —𝜀—[Field(l)]→ (49) + (49) —{↗·¹}—𝜀→ (✓) ═══════════════════════════════════════════════════════════════════════════════ TYPE INFERENCE ═══════════════════════════════════════════════════════════════════════════════ - === Entrypoints === - NavUpMulti → T3 - NavUpAnchor → T4 - NavUp → T5 - NavStay → T6 - NavNextAnchor → T7 - NavNext → T8 - NavMixed → T9 - NavDownAnchor → T10 - NavDown → T11 - - === Types === - T3: Record NavUpMulti { - e: Node - } - T4: Record NavUpAnchor { - c: Node - } - T5: Record NavUp { - c: Node - } - T6: Record NavStay { - r: Node - } - T7: Record NavNextAnchor { - a: Node - b: Node - } - T8: Record NavNext { - a: Node - b: Node - } - T9: Record NavMixed { - f: Node - m: Node - l: Node - } - T10: Record NavDownAnchor { - c: Node - } - T11: Record NavDown { - c: Node - } + NavUpMulti = { e: Node } + NavUpAnchor = { c: Node } + NavUp = { c: Node } + NavStay = { r: Node } + NavNextAnchor = { + a: Node + b: Node + } + NavNext = { + a: Node + b: Node + } + NavMixed = { + f: Node + m: Node + l: Node + } + NavDownAnchor = { c: Node } + NavDown = { c: Node } "); } @@ -874,152 +760,123 @@ fn golden_type_inference() { TRANSITION GRAPH ═══════════════════════════════════════════════════════════════════════════════ - FlatScope = N0 - BaseWithCapture = N8 - RefOpaque = N10 - RefCaptured = N12 - TaggedAtRoot = N15 - TaggedInline = N25 - CardMult = N45 - QisTwo = N54 - NoQisOne = N63 - MissingField = N67 - SyntheticNames = N85 - - N0: (a) → N1 - N1: [Down] (b) → N2 - N2: [Down] (c) → N3 - N3: [Down] (d) [Capture] → N4 - N4: ε [Field(val)] → N7 - N7: [Up(3)] ε → ∅ - N8: (identifier) [Capture] → N9 - N9: ε [Field(name)] → ∅ - N10: ε +Enter(0, BaseWithCapture) → N8, N11 - N11: ε +Exit(0) → ∅ - N12: ε +Enter(1, BaseWithCapture) → N8, N13 - N13: ε +Exit(1) [Capture] → N14 - N14: ε [Field(result)] → ∅ - N15: ε → N18, N22 - N16: ε → ∅ - N18: (a) [Variant(A)] [Capture] → N20 - N20: ε [Field(x)] [EndVariant] → N16 - N22: (b) [Variant(B)] [Capture] → N24 - N24: ε [Field(y)] [EndVariant] → N16 - N25: (wrapper) → N26 - N26: [Down] ε → N29, N33 - N29: (a) [Variant(A)] [Capture] → N31 - N31: ε [Field(x)] [EndVariant] → N36 - N33: (b) [Variant(B)] [Capture] → N35 - N35: ε [Field(y)] [EndVariant] → N36 - N36: [Up(1)] ε → ∅ - N37: (_) → N39 - N38: [Down] (item) [Capture] → N42 - N39: ε [StartArray] → N38 - N42: ε [Push] → N38, N43 - N43: ε [EndArray] [Field(items)] → N47 - N45: ε [StartArray] → N48 - N46: ε [EndArray] → ∅ - N47: [Up(1)] ε [Push] → N48 - N48: ε → N37, N46 - N49: ε [StartObj] → N50 - N50: [Next] (a) [Capture] → N51 - N51: ε [Field(x)] → N52 - N52: [Next] (b) [Capture] → N58 - N54: ε [StartArray] → N59 - N55: ε [EndArray] → ∅ - N58: ε [Field(y)] [EndObj] [Push] → N59 - N59: ε → N49, N55 - N61: [Next] (a) [Capture] → N65 - N63: ε [StartArray] → N66 - N64: ε [EndArray] → ∅ - N65: ε [Field(x)] [Push] → N66 - N66: ε → N61, N64 - N67: ε → N70, N80 - N68: ε → ∅ - N70: (full) [Variant(Full)] [StartObj] → N71 - N71: [Down] (a) [Capture] → N72 - N72: ε [Field(a)] → N73 - N73: [Next] (b) [Capture] → N74 - N74: ε [Field(b)] → N75 - N75: [Next] (c) [Capture] → N76 - N76: ε [Field(c)] → N78 - N78: [Up(1)] ε [EndObj] [EndVariant] → N68 - N80: (partial) [Variant(Partial)] → N81 - N81: [Down] (a) [Capture] → N82 - N82: ε [Field(a)] → N84 - N84: [Up(1)] ε [EndVariant] → N68 - N85: (foo) → N89 - N86: [Down] ε → N87 - N87: [Next] (bar) [Capture] [Capture] → N91 - N89: ε [StartObj] → N86 - N91: ε [Field(bar)] [EndObj] [Field(baz)] → N92 - N92: [Up(1)] ε → ∅ + FlatScope = (00) + BaseWithCapture = (08) + RefOpaque = (10) + RefCaptured = (12) + TaggedAtRoot = (15) + TaggedInline = (25) + CardMult = (45) + QisTwo = (54) + NoQisOne = (63) + MissingField = (67) + SyntheticNames = (85) + + (00) —(a)→ (01) + (01) —{↘}—(b)→ (02) + (02) —{↘}—(c)→ (03) + (03) —{↘}—(d)—[CaptureNode]→ (04) + (04) —𝜀—[Field(val)]→ (07) + (07) —{↗³}—𝜀→ (✓) + (08) —(identifier)—[CaptureNode]→ (09) + (09) —𝜀—[Field(name)]→ (✓) + (10) ——𝜀→ (08), (11) + (11) —𝜀—→ (✓) + (12) ——𝜀→ (08), (13) + (13) —𝜀——[CaptureNode]→ (14) + (14) —𝜀—[Field(result)]→ (✓) + (15) —𝜀→ (18), (22) + (16) —𝜀→ (✓) + (18) —(a)—[StartVariant(A), CaptureNode]→ (20) + (20) —𝜀—[Field(x), EndVariant]→ (16) + (22) —(b)—[StartVariant(B), CaptureNode]→ (24) + (24) —𝜀—[Field(y), EndVariant]→ (16) + (25) —(wrapper)→ (26) + (26) —{↘}—𝜀→ (29), (33) + (29) —(a)—[StartVariant(A), CaptureNode]→ (31) + (31) —𝜀—[Field(x), EndVariant]→ (36) + (33) —(b)—[StartVariant(B), CaptureNode]→ (35) + (35) —𝜀—[Field(y), EndVariant]→ (36) + (36) —{↗¹}—𝜀→ (✓) + (37) —(_)→ (39) + (38) —{↘}—(item)—[CaptureNode]→ (42) + (39) —𝜀—[StartArray]→ (38) + (42) —𝜀—[PushElement]→ (38), (43) + (43) —𝜀—[EndArray, Field(items)]→ (47) + (45) —𝜀—[StartArray]→ (48) + (46) —𝜀—[EndArray]→ (✓) + (47) —{↗¹}—𝜀—[PushElement]→ (48) + (48) —𝜀→ (37), (46) + (49) —𝜀—[StartObject]→ (50) + (50) —{→}—(a)—[CaptureNode]→ (51) + (51) —𝜀—[Field(x)]→ (52) + (52) —{→}—(b)—[CaptureNode]→ (58) + (54) —𝜀—[StartArray]→ (59) + (55) —𝜀—[EndArray]→ (✓) + (58) —𝜀—[Field(y), EndObject, PushElement]→ (59) + (59) —𝜀→ (49), (55) + (61) —{→}—(a)—[CaptureNode]→ (65) + (63) —𝜀—[StartArray]→ (66) + (64) —𝜀—[EndArray]→ (✓) + (65) —𝜀—[Field(x), PushElement]→ (66) + (66) —𝜀→ (61), (64) + (67) —𝜀→ (70), (80) + (68) —𝜀→ (✓) + (70) —(full)—[StartVariant(Full), StartObject]→ (71) + (71) —{↘}—(a)—[CaptureNode]→ (72) + (72) —𝜀—[Field(a)]→ (73) + (73) —{→}—(b)—[CaptureNode]→ (74) + (74) —𝜀—[Field(b)]→ (75) + (75) —{→}—(c)—[CaptureNode]→ (76) + (76) —𝜀—[Field(c)]→ (78) + (78) —{↗¹}—𝜀—[EndObject, EndVariant]→ (68) + (80) —(partial)—[StartVariant(Partial)]→ (81) + (81) —{↘}—(a)—[CaptureNode]→ (82) + (82) —𝜀—[Field(a)]→ (84) + (84) —{↗¹}—𝜀—[EndVariant]→ (68) + (85) —(foo)→ (89) + (86) —{↘}—𝜀→ (87) + (87) —{→}—(bar)—[CaptureNode, CaptureNode]→ (91) + (89) —𝜀—[StartObject]→ (86) + (91) —𝜀—[Field(bar), EndObject, Field(baz)]→ (92) + (92) —{↗¹}—𝜀→ (✓) ═══════════════════════════════════════════════════════════════════════════════ TYPE INFERENCE ═══════════════════════════════════════════════════════════════════════════════ - === Entrypoints === - BaseWithCapture → T3 - TaggedInline → T6 - TaggedAtRoot → T7 - SyntheticNames → T9 - RefOpaque → Void - RefCaptured → T10 - QisTwo → T12 - NoQisOne → T14 - MissingField → T16 - FlatScope → T17 - CardMult → T19 - - === Types === - T3: Record BaseWithCapture { - name: Node - } - T4: Optional → Node - T5: Optional → Node - T6: Record TaggedInline { - x: T4 - y: T5 - } - T7: Enum TaggedAtRoot { - A: Node - B: Node - } - T8: Record SyntheticNamesScope8 { - bar: Node - } - T9: Record SyntheticNames { - baz: T8 - } - T10: Record RefCaptured { - result: T3 - } - T11: Record QisTwoScope11 { - x: Node - y: Node - } - T12: ArrayStar → T11 - T13: ArrayStar → Node - T14: Record NoQisOne { - x: T13 - } - T15: Record MissingFieldScope15 { - a: Node - b: Node - c: Node - } - T16: Enum MissingField { - Full: T15 - Partial: Node - } - T17: Record FlatScope { - val: Node - } - T18: ArrayStar → Node - T19: Record CardMult { - items: T18 - } + RefOpaque = () + QisTwo = T12 + + BaseWithCapture = { name: Node } + TaggedInline = { + x: Node? + y: Node? + } + TaggedAtRoot = { + A => Node + B => Node + } + SyntheticNamesScope8 = { bar: Node } + SyntheticNames = { baz: SyntheticNamesScope8 } + RefCaptured = { result: BaseWithCapture } + QisTwoScope11 = { + x: Node + y: Node + } + T12 = [QisTwoScope11] + NoQisOne = { x: [Node] } + MissingFieldScope15 = { + a: Node + b: Node + c: Node + } + MissingField = { + Full => MissingFieldScope15 + Partial => Node + } + FlatScope = { val: Node } + CardMult = { items: [Node] } "); } @@ -1051,83 +908,60 @@ fn golden_effect_patterns() { TRANSITION GRAPH ═══════════════════════════════════════════════════════════════════════════════ - EffCapture = N0 - EffToString = N2 - EffArray = N4 - EffObject = N12 - EffVariant = N20 - EffClear = N33 - - N0: (node) [Capture] → N1 - N1: ε [Field(name)] → ∅ - N2: (node) [Capture] [ToString] → N3 - N3: ε [Field(name)] → ∅ - N4: (container) → N6 - N5: [Down] (item) [Capture] → N8 - N6: ε [StartArray] → N9 - N8: ε [Push] → N9 - N9: ε → N5, N10 - N10: ε [EndArray] [Field(items)] → N11 - N11: [Up(1)] ε → ∅ - N12: ε [StartObj] → N13 - N13: [Next] (a) [Capture] [Capture] → N14 - N14: ε [Field(x)] → N15 - N15: [Next] (b) [Capture] → N19 - N19: ε [Field(y)] [EndObj] [Field(obj)] → ∅ - N20: ε [StartObj] → N23, N27 - N23: (a) [Variant(A)] [Capture] [Capture] → N25 - N25: ε [Field(x)] [EndVariant] → N32 - N27: (b) [Variant(B)] [Capture] [Capture] → N29 - N29: ε [Field(y)] [EndVariant] → N32 - N32: ε [EndObj] [Field(choice)] → ∅ - N33: (container) → N35 - N34: [Down] (item) [Capture] → N38 - N35: ε → N34, N37 - N37: ε [Clear] → N38 - N38: ε [Field(maybe)] → N39 - N39: [Up(1)] ε → ∅ + EffCapture = (00) + EffToString = (02) + EffArray = (04) + EffObject = (12) + EffVariant = (20) + EffClear = (33) + + (00) —(node)—[CaptureNode]→ (01) + (01) —𝜀—[Field(name)]→ (✓) + (02) —(node)—[CaptureNode, ToString]→ (03) + (03) —𝜀—[Field(name)]→ (✓) + (04) —(container)→ (06) + (05) —{↘}—(item)—[CaptureNode]→ (08) + (06) —𝜀—[StartArray]→ (09) + (08) —𝜀—[PushElement]→ (09) + (09) —𝜀→ (05), (10) + (10) —𝜀—[EndArray, Field(items)]→ (11) + (11) —{↗¹}—𝜀→ (✓) + (12) —𝜀—[StartObject]→ (13) + (13) —{→}—(a)—[CaptureNode, CaptureNode]→ (14) + (14) —𝜀—[Field(x)]→ (15) + (15) —{→}—(b)—[CaptureNode]→ (19) + (19) —𝜀—[Field(y), EndObject, Field(obj)]→ (✓) + (20) —𝜀—[StartObject]→ (23), (27) + (23) —(a)—[StartVariant(A), CaptureNode, CaptureNode]→ (25) + (25) —𝜀—[Field(x), EndVariant]→ (32) + (27) —(b)—[StartVariant(B), CaptureNode, CaptureNode]→ (29) + (29) —𝜀—[Field(y), EndVariant]→ (32) + (32) —𝜀—[EndObject, Field(choice)]→ (✓) + (33) —(container)→ (35) + (34) —{↘}—(item)—[CaptureNode]→ (38) + (35) —𝜀→ (34), (37) + (37) —𝜀—[ClearCurrent]→ (38) + (38) —𝜀—[Field(maybe)]→ (39) + (39) —{↗¹}—𝜀→ (✓) ═══════════════════════════════════════════════════════════════════════════════ TYPE INFERENCE ═══════════════════════════════════════════════════════════════════════════════ - === Entrypoints === - EffVariant → T4 - EffToString → T5 - EffObject → T7 - EffClear → T9 - EffCapture → T10 - EffArray → T12 - - === Types === - T3: Enum EffVariantScope3 { - A: Node - B: Node - } - T4: Record EffVariant { - choice: T3 - } - T5: Record EffToString { - name: String - } - T6: Record EffObjectScope6 { - x: Node - y: Node - } - T7: Record EffObject { - obj: T6 + EffVariantScope3 = { + A => Node + B => Node } - T8: Optional → Node - T9: Record EffClear { - maybe: T8 - } - T10: Record EffCapture { - name: Node - } - T11: ArrayStar → Node - T12: Record EffArray { - items: T11 + EffVariant = { choice: EffVariantScope3 } + EffToString = { name: str } + EffObjectScope6 = { + x: Node + y: Node } + EffObject = { obj: EffObjectScope6 } + EffClear = { maybe: Node? } + EffCapture = { name: Node } + EffArray = { items: [Node] } "); } @@ -1162,98 +996,73 @@ fn golden_quantifier_graphs() { TRANSITION GRAPH ═══════════════════════════════════════════════════════════════════════════════ - GreedyStar = N1 - GreedyPlus = N7 - Optional = N13 - LazyStar = N18 - LazyPlus = N24 - QuantSeq = N34 - NestedQuant = N48 - - N0: (a) [Capture] → N3 - N1: ε [StartArray] → N4 - N3: ε [Push] → N4 - N4: ε → N0, N5 - N5: ε [EndArray] [Field(items)] → ∅ - N6: (a) [Capture] → N10 - N7: ε [StartArray] → N6 - N10: ε [Push] → N6, N11 - N11: ε [EndArray] [Field(items)] → ∅ - N12: (a) [Capture] → N16 - N13: ε → N12, N15 - N15: ε [Clear] → N16 - N16: ε [Field(maybe)] → ∅ - N17: (a) [Capture] → N20 - N18: ε [StartArray] → N21 - N20: ε [Push] → N21 - N21: ε → N22, N17 - N22: ε [EndArray] [Field(items)] → ∅ - N23: (a) [Capture] → N27 - N24: ε [StartArray] → N23 - N27: ε [Push] → N28, N23 - N28: ε [EndArray] [Field(items)] → ∅ - N29: ε [StartObj] → N30 - N30: [Next] (a) [Capture] → N31 - N31: ε [Field(x)] → N32 - N32: [Next] (b) [Capture] → N38 - N34: ε [StartArray] → N39 - N35: ε [EndArray] → ∅ - N38: ε [Field(y)] [EndObj] [Push] → N39 - N39: ε → N29, N35 - N40: (outer) [Capture] → N42 - N41: [Down] (inner) [Capture] → N44 - N42: ε [StartArray] → N45 - N44: ε [Push] → N45 - N45: ε → N41, N46 - N46: ε [EndArray] [Field(inners)] → N51 - N48: ε [StartArray] → N40 - N51: [Up(1)] ε [Push] → N40, N52 - N52: ε [EndArray] [Field(outers)] → ∅ + GreedyStar = (01) + GreedyPlus = (07) + Optional = (13) + LazyStar = (18) + LazyPlus = (24) + QuantSeq = (34) + NestedQuant = (48) + + (00) —(a)—[CaptureNode]→ (03) + (01) —𝜀—[StartArray]→ (04) + (03) —𝜀—[PushElement]→ (04) + (04) —𝜀→ (00), (05) + (05) —𝜀—[EndArray, Field(items)]→ (✓) + (06) —(a)—[CaptureNode]→ (10) + (07) —𝜀—[StartArray]→ (06) + (10) —𝜀—[PushElement]→ (06), (11) + (11) —𝜀—[EndArray, Field(items)]→ (✓) + (12) —(a)—[CaptureNode]→ (16) + (13) —𝜀→ (12), (15) + (15) —𝜀—[ClearCurrent]→ (16) + (16) —𝜀—[Field(maybe)]→ (✓) + (17) —(a)—[CaptureNode]→ (20) + (18) —𝜀—[StartArray]→ (21) + (20) —𝜀—[PushElement]→ (21) + (21) —𝜀→ (22), (17) + (22) —𝜀—[EndArray, Field(items)]→ (✓) + (23) —(a)—[CaptureNode]→ (27) + (24) —𝜀—[StartArray]→ (23) + (27) —𝜀—[PushElement]→ (28), (23) + (28) —𝜀—[EndArray, Field(items)]→ (✓) + (29) —𝜀—[StartObject]→ (30) + (30) —{→}—(a)—[CaptureNode]→ (31) + (31) —𝜀—[Field(x)]→ (32) + (32) —{→}—(b)—[CaptureNode]→ (38) + (34) —𝜀—[StartArray]→ (39) + (35) —𝜀—[EndArray]→ (✓) + (38) —𝜀—[Field(y), EndObject, PushElement]→ (39) + (39) —𝜀→ (29), (35) + (40) —(outer)—[CaptureNode]→ (42) + (41) —{↘}—(inner)—[CaptureNode]→ (44) + (42) —𝜀—[StartArray]→ (45) + (44) —𝜀—[PushElement]→ (45) + (45) —𝜀→ (41), (46) + (46) —𝜀—[EndArray, Field(inners)]→ (51) + (48) —𝜀—[StartArray]→ (40) + (51) —{↗¹}—𝜀—[PushElement]→ (40), (52) + (52) —𝜀—[EndArray, Field(outers)]→ (✓) ═══════════════════════════════════════════════════════════════════════════════ TYPE INFERENCE ═══════════════════════════════════════════════════════════════════════════════ - === Entrypoints === - QuantSeq → T4 - Optional → T6 - NestedQuant → T9 - LazyStar → T11 - LazyPlus → T13 - GreedyStar → T15 - GreedyPlus → T17 - - === Types === - T3: Record QuantSeqScope3 { - x: Node - y: Node - } - T4: ArrayStar → T3 - T5: Optional → Node - T6: Record Optional { - maybe: T5 - } - T7: ArrayStar → Node - T8: ArrayPlus → Node - T9: Record NestedQuant { - inners: T7 - outers: T8 - } - T10: ArrayStar → Node - T11: Record LazyStar { - items: T10 - } - T12: ArrayPlus → Node - T13: Record LazyPlus { - items: T12 - } - T14: ArrayStar → Node - T15: Record GreedyStar { - items: T14 + QuantSeq = T04 + + QuantSeqScope3 = { + x: Node + y: Node } - T16: ArrayPlus → Node - T17: Record GreedyPlus { - items: T16 + T04 = [QuantSeqScope3] + Optional = { maybe: Node? } + NestedQuant = { + inners: [Node] + outers: [Node]⁺ } + LazyStar = { items: [Node] } + LazyPlus = { items: [Node]⁺ } + GreedyStar = { items: [Node] } + GreedyPlus = { items: [Node]⁺ } "); } diff --git a/crates/plotnik-lib/src/query/typing.rs b/crates/plotnik-lib/src/query/infer.rs similarity index 89% rename from crates/plotnik-lib/src/query/typing.rs rename to crates/plotnik-lib/src/query/infer.rs index d6faf758..6793517d 100644 --- a/crates/plotnik-lib/src/query/typing.rs +++ b/crates/plotnik-lib/src/query/infer.rs @@ -924,97 +924,3 @@ impl<'a> Query<'a> { } } } - -// ───────────────────────────────────────────────────────────────────────────── -// Display and helpers -// ───────────────────────────────────────────────────────────────────────────── - -impl TypeInferenceResult<'_> { - pub fn dump(&self) -> String { - let mut out = String::new(); - - out.push_str("=== Entrypoints ===\n"); - for (name, type_id) in &self.entrypoint_types { - out.push_str(&format!("{} → {}\n", name, format_type_id(*type_id))); - } - - if !self.type_defs.is_empty() { - out.push_str("\n=== Types ===\n"); - for (idx, def) in self.type_defs.iter().enumerate() { - let type_id = 3 + idx as TypeId; - let name = def.name.unwrap_or(""); - match def.kind { - TypeKind::Record => { - out.push_str(&format!("T{}: Record {} {{\n", type_id, name)); - for member in &def.members { - out.push_str(&format!( - " {}: {}\n", - member.name, - format_type_id(member.ty) - )); - } - out.push_str("}\n"); - } - TypeKind::Enum => { - out.push_str(&format!("T{}: Enum {} {{\n", type_id, name)); - for member in &def.members { - out.push_str(&format!( - " {}: {}\n", - member.name, - format_type_id(member.ty) - )); - } - out.push_str("}\n"); - } - TypeKind::Optional => { - let inner = def.inner_type.map(format_type_id).unwrap_or_default(); - out.push_str(&format!("T{}: Optional {} → {}\n", type_id, name, inner)); - } - TypeKind::ArrayStar => { - let inner = def.inner_type.map(format_type_id).unwrap_or_default(); - out.push_str(&format!("T{}: ArrayStar {} → {}\n", type_id, name, inner)); - } - TypeKind::ArrayPlus => { - let inner = def.inner_type.map(format_type_id).unwrap_or_default(); - out.push_str(&format!("T{}: ArrayPlus {} → {}\n", type_id, name, inner)); - } - } - } - } - - if !self.errors.is_empty() { - out.push_str("\n=== Errors ===\n"); - for err in &self.errors { - let types = err - .types_found - .iter() - .map(|t| t.to_string()) - .collect::>() - .join(", "); - out.push_str(&format!( - "field `{}` in `{}`: incompatible types [{}]\n", - err.field, err.definition, types - )); - } - } - - out - } - - pub fn dump_diagnostics(&self, source: &str) -> String { - self.diagnostics.render_filtered(source) - } - - pub fn has_errors(&self) -> bool { - !self.errors.is_empty() - } -} - -fn format_type_id(id: TypeId) -> String { - match id { - TYPE_VOID => "Void".to_string(), - TYPE_NODE => "Node".to_string(), - TYPE_STR => "String".to_string(), - _ => format!("T{}", id), - } -} diff --git a/crates/plotnik-lib/src/query/infer_dump.rs b/crates/plotnik-lib/src/query/infer_dump.rs new file mode 100644 index 00000000..0e6af2f9 --- /dev/null +++ b/crates/plotnik-lib/src/query/infer_dump.rs @@ -0,0 +1,232 @@ +//! Dump helpers for type inference inspection and testing. + +use std::fmt::Write; + +use crate::ir::{TYPE_NODE, TYPE_STR, TYPE_VOID, TypeId, TypeKind}; + +use super::infer::TypeInferenceResult; + +impl TypeInferenceResult<'_> { + pub fn dump(&self) -> String { + let mut out = String::new(); + let printer = TypePrinter::new(self); + printer.format(&mut out).expect("String write never fails"); + out + } + + pub fn dump_diagnostics(&self, source: &str) -> String { + self.diagnostics.render_filtered(source) + } + + pub fn has_errors(&self) -> bool { + !self.errors.is_empty() + } +} + +struct TypePrinter<'a, 'src> { + result: &'a TypeInferenceResult<'src>, + width: usize, +} + +impl<'a, 'src> TypePrinter<'a, 'src> { + fn new(result: &'a TypeInferenceResult<'src>) -> Self { + let total_types = 3 + result.type_defs.len(); + let width = if total_types == 0 { + 1 + } else { + ((total_types as f64).log10().floor() as usize) + 1 + }; + Self { result, width } + } + + fn format(&self, w: &mut String) -> std::fmt::Result { + // Entrypoints (skip redundant Foo = Foo) + for (name, type_id) in &self.result.entrypoint_types { + let type_name = self.get_type_name(*type_id); + if type_name.as_deref() == Some(*name) { + continue; + } + writeln!(w, "{} = {}", name, self.format_type(*type_id))?; + } + + let has_entrypoints = self + .result + .entrypoint_types + .iter() + .any(|(name, id)| self.get_type_name(*id).as_deref() != Some(*name)); + + // Type definitions (skip inlinable types) + let mut first_typedef = true; + for (idx, def) in self.result.type_defs.iter().enumerate() { + let type_id = 3 + idx as TypeId; + + if self.is_inlinable(type_id) { + continue; + } + + if first_typedef && has_entrypoints { + writeln!(w)?; + } + first_typedef = false; + + let header = self.format_type_header(type_id, def.name); + + match def.kind { + TypeKind::Record => { + if def.members.len() == 1 { + let m = &def.members[0]; + writeln!( + w, + "{} = {{ {}: {} }}", + header, + m.name, + self.format_type(m.ty) + )?; + } else { + writeln!(w, "{} = {{", header)?; + for member in &def.members { + writeln!(w, " {}: {}", member.name, self.format_type(member.ty))?; + } + writeln!(w, "}}")?; + } + } + TypeKind::Enum => { + if def.members.len() == 1 { + let m = &def.members[0]; + writeln!( + w, + "{} = {{ {} => {} }}", + header, + m.name, + self.format_type(m.ty) + )?; + } else { + writeln!(w, "{} = {{", header)?; + for member in &def.members { + writeln!(w, " {} => {}", member.name, self.format_type(member.ty))?; + } + writeln!(w, "}}")?; + } + } + TypeKind::Optional => { + let inner = def + .inner_type + .map(|t| self.format_type(t)) + .unwrap_or_default(); + writeln!(w, "{} = {}?", header, inner)?; + } + TypeKind::ArrayStar => { + let inner = def + .inner_type + .map(|t| self.format_type(t)) + .unwrap_or_default(); + writeln!(w, "{} = [{}]", header, inner)?; + } + TypeKind::ArrayPlus => { + let inner = def + .inner_type + .map(|t| self.format_type(t)) + .unwrap_or_default(); + writeln!(w, "{} = [{}]⁺", header, inner)?; + } + } + } + + // Errors + if !self.result.errors.is_empty() { + if has_entrypoints || !first_typedef { + writeln!(w)?; + } + writeln!(w, "Errors:")?; + for err in &self.result.errors { + let types = err + .types_found + .iter() + .map(|t| t.to_string()) + .collect::>() + .join(", "); + writeln!( + w, + " field `{}` in `{}`: incompatible types [{}]", + err.field, err.definition, types + )?; + } + } + + Ok(()) + } + + fn get_type_name(&self, id: TypeId) -> Option { + if id < 3 { + return None; + } + let idx = (id - 3) as usize; + self.result + .type_defs + .get(idx) + .and_then(|def| def.name.map(|s| s.to_string())) + } + + /// Returns true if the type should be inlined rather than shown as separate definition. + /// Inlinable: wrapper types (Optional/Array*) around primitives or other inlinable types. + fn is_inlinable(&self, id: TypeId) -> bool { + if id < 3 { + return true; // primitives are always inlinable + } + let idx = (id - 3) as usize; + let Some(def) = self.result.type_defs.get(idx) else { + return false; + }; + + // Named types are not inlined (they have semantic meaning) + if def.name.is_some() { + return false; + } + + match def.kind { + TypeKind::Record | TypeKind::Enum => false, + TypeKind::Optional | TypeKind::ArrayStar | TypeKind::ArrayPlus => { + def.inner_type.map(|t| self.is_inlinable(t)).unwrap_or(true) + } + } + } + + fn format_type_header(&self, type_id: TypeId, name: Option<&str>) -> String { + match name { + Some(n) => n.to_string(), + None => format!("T{:0width$}", type_id, width = self.width), + } + } + + fn format_type(&self, id: TypeId) -> String { + match id { + TYPE_VOID => "()".to_string(), + TYPE_NODE => "Node".to_string(), + TYPE_STR => "str".to_string(), + _ => { + let idx = (id - 3) as usize; + if let Some(def) = self.result.type_defs.get(idx) { + // Named types: use name + if let Some(name) = def.name { + return name.to_string(); + } + + // Inlinable wrappers: format inline + if self.is_inlinable(id) { + let inner = def + .inner_type + .map(|t| self.format_type(t)) + .unwrap_or_default(); + return match def.kind { + TypeKind::Optional => format!("{}?", inner), + TypeKind::ArrayStar => format!("[{}]", inner), + TypeKind::ArrayPlus => format!("[{}]⁺", inner), + _ => format!("T{:0width$}", id, width = self.width), + }; + } + } + format!("T{:0width$}", id, width = self.width) + } + } + } +} diff --git a/crates/plotnik-lib/src/query/typing_tests.rs b/crates/plotnik-lib/src/query/infer_tests.rs similarity index 62% rename from crates/plotnik-lib/src/query/typing_tests.rs rename to crates/plotnik-lib/src/query/infer_tests.rs index efd5735e..97bad87e 100644 --- a/crates/plotnik-lib/src/query/typing_tests.rs +++ b/crates/plotnik-lib/src/query/infer_tests.rs @@ -16,7 +16,6 @@ fn infer_with_graph(source: &str) -> String { .expect("parse should succeed") .build_graph(); let mut out = String::new(); - out.push_str("=== Graph ===\n"); out.push_str(&query.graph().dump_live(query.dead_nodes())); out.push('\n'); out.push_str(&query.type_info().dump()); @@ -30,44 +29,37 @@ fn debug_star_quantifier_graph() { .expect("parse should succeed") .build_graph_with_pre_opt_dump(); let mut out = String::new(); - out.push_str("=== Graph (before optimization - what type inference sees) ===\n"); + out.push_str("(pre-optimization)\n"); out.push_str(&pre_opt_dump); - out.push_str("\n=== Graph (after optimization) ===\n"); + out.push_str("\n(post-optimization)\n"); out.push_str(&query.graph().dump_live(query.dead_nodes())); out.push('\n'); out.push_str(&query.type_info().dump()); insta::assert_snapshot!(out, @r" - === Graph (before optimization - what type inference sees) === - Foo = N4 - - N0: (_) → N1 - N1: [Down] (item) [Capture] → N2 - N2: ε [Field(items)] → N3 - N3: [Up(1)] ε → N6 - N4: ε [StartArray] → N7 - N5: ε [EndArray] → ∅ - N6: ε [Push] → N7 - N7: ε → N0, N5 - - === Graph (after optimization) === - Foo = N4 - - N0: (_) → N1 - N1: [Down] (item) [Capture] → N2 - N2: ε [Field(items)] → N6 - N4: ε [StartArray] → N7 - N5: ε [EndArray] → ∅ - N6: [Up(1)] ε [Push] → N7 - N7: ε → N0, N5 - - === Entrypoints === - Foo → T4 - - === Types === - T3: ArrayStar → Node - T4: Record Foo { - items: T3 - } + (pre-optimization) + Foo = (4) + + (0) —(_)→ (1) + (1) —{↘}—(item)—[CaptureNode]→ (2) + (2) —𝜀—[Field(items)]→ (3) + (3) —{↗¹}—𝜀→ (6) + (4) —𝜀—[StartArray]→ (7) + (5) —𝜀—[EndArray]→ (✓) + (6) —𝜀—[PushElement]→ (7) + (7) —𝜀→ (0), (5) + + (post-optimization) + Foo = (4) + + (0) —(_)→ (1) + (1) —{↘}—(item)—[CaptureNode]→ (2) + (2) —𝜀—[Field(items)]→ (6) + (4) —𝜀—[StartArray]→ (7) + (5) —𝜀—[EndArray]→ (✓) + (6) —{↗¹}—𝜀—[PushElement]→ (7) + (7) —𝜀→ (0), (5) + + Foo = { items: [Node] } "); } @@ -75,19 +67,12 @@ fn debug_star_quantifier_graph() { fn debug_graph_structure() { let result = infer_with_graph("Foo = (identifier) @name"); insta::assert_snapshot!(result, @r" - === Graph === - Foo = N0 - - N0: (identifier) [Capture] → N1 - N1: ε [Field(name)] → ∅ + Foo = (0) - === Entrypoints === - Foo → T3 + (0) —(identifier)—[CaptureNode]→ (1) + (1) —𝜀—[Field(name)]→ (✓) - === Types === - T3: Record Foo { - name: Node - } + Foo = { name: Node } "); } @@ -103,35 +88,25 @@ fn debug_incompatible_types_graph() { .build_graph(); let mut out = String::new(); - out.push_str("=== Graph (after optimization) ===\n"); out.push_str(&query.graph().dump_live(query.dead_nodes())); - out.push_str("\n=== Dead nodes count: "); - out.push_str(&query.dead_nodes().len().to_string()); - out.push_str(" ===\n\n"); + out.push_str(&format!("\n(dead nodes: {})\n\n", query.dead_nodes().len())); out.push_str(&query.type_info().dump()); insta::assert_snapshot!(out, @r" - === Graph (after optimization) === - Foo = N0 + Foo = (0) - N0: ε → N2, N4 - N1: ε → ∅ - N2: (a) [Capture] → N3 - N3: ε [Field(v)] → N1 - N4: (b) [Capture] [ToString] → N5 - N5: ε [Field(v)] → N1 + (0) —𝜀→ (2), (4) + (1) —𝜀→ (✓) + (2) —(a)—[CaptureNode]→ (3) + (3) —𝜀—[Field(v)]→ (1) + (4) —(b)—[CaptureNode, ToString]→ (5) + (5) —𝜀—[Field(v)]→ (1) - === Dead nodes count: 0 === + (dead nodes: 0) - === Entrypoints === - Foo → T3 - - === Types === - T3: Record Foo { - v: Node - } + Foo = { v: Node } - === Errors === - field `v` in `Foo`: incompatible types [Node, String] + Errors: + field `v` in `Foo`: incompatible types [Node, String] "); } @@ -142,42 +117,22 @@ fn debug_incompatible_types_graph() { #[test] fn single_node_capture() { let result = infer("Foo = (identifier) @name"); - insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T3 - - === Types === - T3: Record Foo { - name: Node - } - "); + insta::assert_snapshot!(result, @"Foo = { name: Node }"); } #[test] fn string_capture() { let result = infer("Foo = (identifier) @name ::string"); - insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T3 - - === Types === - T3: Record Foo { - name: String - } - "); + insta::assert_snapshot!(result, @"Foo = { name: str }"); } #[test] fn multiple_captures_flat() { let result = infer("Foo = (a (b) @x (c) @y)"); insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T3 - - === Types === - T3: Record Foo { - x: Node - y: Node + Foo = { + x: Node + y: Node } "); } @@ -185,10 +140,7 @@ fn multiple_captures_flat() { #[test] fn no_captures_void() { let result = infer("Foo = (identifier)"); - insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → Void - "); + insta::assert_snapshot!(result, @"Foo = ()"); } // ───────────────────────────────────────────────────────────────────────────── @@ -203,17 +155,11 @@ fn captured_sequence_creates_struct() { let result = infer(input); insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T4 - - === Types === - T3: Record FooScope3 { - x: Node - y: Node - } - T4: Record Foo { - z: T3 + FooScope3 = { + x: Node + y: Node } + Foo = { z: FooScope3 } "); } @@ -225,20 +171,12 @@ fn nested_captured_sequence() { let result = infer(input); insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T5 - - === Types === - T3: Record FooScope3 { - b: Node - } - T4: Record FooScope4 { - a: Node - nested: T3 - } - T5: Record Foo { - root: T4 + FooScope3 = { b: Node } + FooScope4 = { + a: Node + nested: FooScope3 } + Foo = { root: FooScope4 } "); } @@ -250,13 +188,9 @@ fn sequence_without_capture_propagates() { let result = infer(input); insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T3 - - === Types === - T3: Record Foo { - x: Node - y: Node + Foo = { + x: Node + y: Node } "); } @@ -272,15 +206,7 @@ fn untagged_alternation_symmetric() { "#}; let result = infer(input); - insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T3 - - === Types === - T3: Record Foo { - v: Node - } - "); + insta::assert_snapshot!(result, @"Foo = { v: Node }"); } #[test] @@ -291,15 +217,9 @@ fn untagged_alternation_asymmetric() { let result = infer(input); insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T5 - - === Types === - T3: Optional → Node - T4: Optional → Node - T5: Record Foo { - x: T3 - y: T4 + Foo = { + x: Node? + y: Node? } "); } @@ -312,13 +232,9 @@ fn tagged_alternation_uncaptured_propagates() { let result = infer(input); insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T3 - - === Types === - T3: Enum Foo { - A: Node - B: Node + Foo = { + A => Node + B => Node } "); } @@ -331,17 +247,11 @@ fn tagged_alternation_captured_creates_enum() { let result = infer(input); insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T4 - - === Types === - T3: Enum FooScope3 { - A: Node - B: Node - } - T4: Record Foo { - choice: T3 + FooScope3 = { + A => Node + B => Node } + Foo = { choice: FooScope3 } "); } @@ -353,19 +263,11 @@ fn captured_untagged_alternation_creates_struct() { let result = infer(input); insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T6 - - === Types === - T3: Optional → Node - T4: Optional → Node - T5: Record FooScope3 { - x: T3 - y: T4 - } - T6: Record Foo { - val: T5 + FooScope3 = { + x: Node? + y: Node? } + Foo = { val: FooScope3 } "); } @@ -376,46 +278,19 @@ fn captured_untagged_alternation_creates_struct() { #[test] fn star_quantifier() { let result = infer("Foo = ((item) @items)*"); - insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T4 - - === Types === - T3: ArrayStar → Node - T4: Record Foo { - items: T3 - } - "); + insta::assert_snapshot!(result, @"Foo = { items: [Node] }"); } #[test] fn plus_quantifier() { let result = infer("Foo = ((item) @items)+"); - insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T4 - - === Types === - T3: ArrayPlus → Node - T4: Record Foo { - items: T3 - } - "); + insta::assert_snapshot!(result, @"Foo = { items: [Node]⁺ }"); } #[test] fn optional_quantifier() { let result = infer("Foo = ((item) @maybe)?"); - insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T4 - - === Types === - T3: Optional → Node - T4: Record Foo { - maybe: T3 - } - "); + insta::assert_snapshot!(result, @"Foo = { maybe: Node? }"); } #[test] @@ -427,15 +302,13 @@ fn quantifier_on_sequence() { let result = infer(input); insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T4 + Foo = T4 - === Types === - T3: Record FooScope3 { - x: Node - y: Node + FooScope3 = { + x: Node + y: Node } - T4: ArrayStar → T3 + T4 = [FooScope3] "); } @@ -453,16 +326,7 @@ fn qis_single_capture_no_trigger() { "#}; let result = infer(input); - insta::assert_snapshot!(result, @r" - === Entrypoints === - Single → T4 - - === Types === - T3: ArrayStar → Node - T4: Record Single { - item: T3 - } - "); + insta::assert_snapshot!(result, @"Single = { item: [Node] }"); } #[test] @@ -476,17 +340,13 @@ fn qis_alternation_in_sequence() { let result = infer(input); insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T6 - - === Types === - T3: Optional → Node - T4: Optional → Node - T5: Record FooScope3 { - x: T3 - y: T4 + Foo = T6 + + FooScope3 = { + x: Node? + y: Node? } - T6: ArrayStar → T5 + T6 = [FooScope3] "); } @@ -501,34 +361,25 @@ fn quantified_seq_with_inline_tagged_alt() { let result = infer_with_graph(input); insta::assert_snapshot!(result, @r" - === Graph === - Test = N11 - - N0: ε [StartObj] → N1 - N1: [Next] ε → N4, N8 - N4: (a) [Variant(A)] [Capture] [Capture] → N6 - N6: ε [Field(x)] [EndVariant] → N15 - N8: (b) [Variant(B)] [Capture] [Capture] → N10 - N10: ε [Field(y)] [EndVariant] → N15 - N11: ε [StartObj] [StartArray] → N16 - N15: ε [EndObj] [Push] → N16 - N16: ε → N0, N19 - N19: ε [EndArray] [EndObj] [Field(items)] → ∅ - - === Entrypoints === - Test → T7 - - === Types === - T3: Optional → Node - T4: Optional → Node - T5: Record TestScope3 { - x: T3 - y: T4 - } - T6: ArrayStar → T5 - T7: Record Test { - items: T6 - } + Test = (11) + + (00) —𝜀—[StartObject]→ (01) + (01) —{→}—𝜀→ (04), (08) + (04) —(a)—[StartVariant(A), CaptureNode, CaptureNode]→ (06) + (06) —𝜀—[Field(x), EndVariant]→ (15) + (08) —(b)—[StartVariant(B), CaptureNode, CaptureNode]→ (10) + (10) —𝜀—[Field(y), EndVariant]→ (15) + (11) —𝜀—[StartObject, StartArray]→ (16) + (15) —𝜀—[EndObject, PushElement]→ (16) + (16) —𝜀→ (00), (19) + (19) —𝜀—[EndArray, EndObject, Field(items)]→ (✓) + + TestScope3 = { + x: Node? + y: Node? + } + T6 = [TestScope3] + Test = { items: T6 } "); } @@ -554,26 +405,19 @@ fn incompatible_types_in_alternation() { let result = infer_with_graph(input); insta::assert_snapshot!(result, @r" - === Graph === - Foo = N0 - - N0: ε → N2, N4 - N1: ε → ∅ - N2: (a) [Capture] → N3 - N3: ε [Field(v)] → N1 - N4: (b) [Capture] [ToString] → N5 - N5: ε [Field(v)] → N1 - - === Entrypoints === - Foo → T3 - - === Types === - T3: Record Foo { - v: Node - } + Foo = (0) - === Errors === - field `v` in `Foo`: incompatible types [Node, String] + (0) —𝜀→ (2), (4) + (1) —𝜀→ (✓) + (2) —(a)—[CaptureNode]→ (3) + (3) —𝜀—[Field(v)]→ (1) + (4) —(b)—[CaptureNode, ToString]→ (5) + (5) —𝜀—[Field(v)]→ (1) + + Foo = { v: Node } + + Errors: + field `v` in `Foo`: incompatible types [Node, String] "); } @@ -590,17 +434,10 @@ fn multiple_definitions() { let result = infer(input); insta::assert_snapshot!(result, @r" - === Entrypoints === - Func → T3 - Class → T4 - - === Types === - T3: Record Func { - name: Node - } - T4: Record Class { - name: Node - body: Node + Func = { name: Node } + Class = { + name: Node + body: Node } "); } @@ -616,41 +453,17 @@ fn deeply_nested_node() { "#}; let result = infer(input); - insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T3 - - === Types === - T3: Record Foo { - val: Node - } - "); + insta::assert_snapshot!(result, @"Foo = { val: Node }"); } #[test] fn wildcard_capture() { let result = infer("Foo = _ @any"); - insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T3 - - === Types === - T3: Record Foo { - any: Node - } - "); + insta::assert_snapshot!(result, @"Foo = { any: Node }"); } #[test] fn string_literal_capture() { let result = infer(r#"Foo = "+" @op"#); - insta::assert_snapshot!(result, @r" - === Entrypoints === - Foo → T3 - - === Types === - T3: Record Foo { - op: Node - } - "); + insta::assert_snapshot!(result, @"Foo = { op: Node }"); } diff --git a/crates/plotnik-lib/src/query/mod.rs b/crates/plotnik-lib/src/query/mod.rs index 321bf01e..90d45f18 100644 --- a/crates/plotnik-lib/src/query/mod.rs +++ b/crates/plotnik-lib/src/query/mod.rs @@ -18,19 +18,20 @@ pub mod graph; mod graph_build; mod graph_dump; mod graph_optimize; +pub mod infer; +mod infer_dump; #[cfg(feature = "plotnik-langs")] pub mod link; pub mod recursion; pub mod shapes; pub mod symbol_table; -pub mod typing; pub use graph::{BuildEffect, BuildGraph, BuildMatcher, BuildNode, Fragment, NodeId, RefMarker}; pub use graph_optimize::OptimizeStats; -pub use symbol_table::UNNAMED_DEF; -pub use typing::{ +pub use infer::{ InferredMember, InferredTypeDef, TypeDescription, TypeInferenceResult, UnificationError, }; +pub use symbol_table::UNNAMED_DEF; #[cfg(test)] mod alt_kinds_tests; @@ -40,6 +41,8 @@ mod graph_build_tests; mod graph_master_test; #[cfg(test)] mod graph_qis_tests; +#[cfg(test)] +mod infer_tests; #[cfg(all(test, feature = "plotnik-langs"))] mod link_tests; #[cfg(test)] @@ -52,8 +55,6 @@ mod recursion_tests; mod shapes_tests; #[cfg(test)] mod symbol_table_tests; -#[cfg(test)] -mod typing_tests; use std::collections::{HashMap, HashSet};