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};