Skip to content

Commit 0bee91f

Browse files
authored
fix: implement call-site scoping for captured refs (#259)
1 parent 963f773 commit 0bee91f

93 files changed

Lines changed: 1801 additions & 1139 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/plotnik-cli/src/commands/check.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,8 @@ pub struct CheckArgs {
1414
}
1515

1616
pub fn run(args: CheckArgs) {
17-
let source_map = match load_query_source(
18-
args.query_path.as_deref(),
19-
args.query_text.as_deref(),
20-
) {
17+
let source_map = match load_query_source(args.query_path.as_deref(), args.query_text.as_deref())
18+
{
2119
Ok(map) => map,
2220
Err(msg) => {
2321
eprintln!("error: {}", msg);

crates/plotnik-cli/src/commands/dump.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use std::path::PathBuf;
22

3+
use plotnik_lib::Colors;
34
use plotnik_lib::QueryBuilder;
45
use plotnik_lib::bytecode::{Module, dump};
5-
use plotnik_lib::Colors;
66

77
use super::lang_resolver::{resolve_lang, resolve_lang_required, suggest_language};
88
use super::query_loader::load_query_source;
@@ -15,10 +15,8 @@ pub struct DumpArgs {
1515
}
1616

1717
pub fn run(args: DumpArgs) {
18-
let source_map = match load_query_source(
19-
args.query_path.as_deref(),
20-
args.query_text.as_deref(),
21-
) {
18+
let source_map = match load_query_source(args.query_path.as_deref(), args.query_text.as_deref())
19+
{
2220
Ok(map) => map,
2321
Err(msg) => {
2422
eprintln!("error: {}", msg);
@@ -64,7 +62,9 @@ pub fn run(args: DumpArgs) {
6462
if !linked.is_valid() {
6563
eprint!(
6664
"{}",
67-
linked.diagnostics().render_colored(linked.source_map(), args.color)
65+
linked
66+
.diagnostics()
67+
.render_colored(linked.source_map(), args.color)
6868
);
6969
std::process::exit(1);
7070
}
@@ -73,7 +73,9 @@ pub fn run(args: DumpArgs) {
7373
if !query.is_valid() {
7474
eprint!(
7575
"{}",
76-
query.diagnostics().render_colored(query.source_map(), args.color)
76+
query
77+
.diagnostics()
78+
.render_colored(query.source_map(), args.color)
7779
);
7880
std::process::exit(1);
7981
}

crates/plotnik-cli/src/commands/exec.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
33
use std::path::PathBuf;
44

5+
use plotnik_lib::Colors;
56
use plotnik_lib::engine::{
6-
debug_verify_type, FuelLimits, Materializer, RuntimeError, ValueMaterializer, VM,
7+
FuelLimits, Materializer, RuntimeError, VM, ValueMaterializer, debug_verify_type,
78
};
8-
use plotnik_lib::Colors;
99

1010
use super::run_common::{self, PreparedQuery, QueryInput};
1111

crates/plotnik-cli/src/commands/infer.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,8 @@ pub fn run(args: InferArgs) {
3131
std::process::exit(1);
3232
}
3333

34-
let source_map = match load_query_source(
35-
args.query_path.as_deref(),
36-
args.query_text.as_deref(),
37-
) {
34+
let source_map = match load_query_source(args.query_path.as_deref(), args.query_text.as_deref())
35+
{
3836
Ok(map) => map,
3937
Err(msg) => {
4038
eprintln!("error: {}", msg);
@@ -80,7 +78,9 @@ pub fn run(args: InferArgs) {
8078
if !linked.is_valid() {
8179
eprint!(
8280
"{}",
83-
linked.diagnostics().render_colored(linked.source_map(), args.color)
81+
linked
82+
.diagnostics()
83+
.render_colored(linked.source_map(), args.color)
8484
);
8585
std::process::exit(1);
8686
}
@@ -89,7 +89,9 @@ pub fn run(args: InferArgs) {
8989
if !query.is_valid() {
9090
eprint!(
9191
"{}",
92-
query.diagnostics().render_colored(query.source_map(), args.color)
92+
query
93+
.diagnostics()
94+
.render_colored(query.source_map(), args.color)
9395
);
9496
std::process::exit(1);
9597
}

crates/plotnik-cli/src/commands/lang_resolver.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ pub fn resolve_lang(explicit: Option<&str>, query_path: Option<&Path>) -> Option
2525

2626
/// Resolve language, returning an error message if unknown.
2727
pub fn resolve_lang_required(lang_name: &str) -> Result<Lang, String> {
28-
plotnik_langs::from_name(lang_name)
29-
.ok_or_else(|| format!("unknown language: '{}'", lang_name))
28+
plotnik_langs::from_name(lang_name).ok_or_else(|| format!("unknown language: '{}'", lang_name))
3029
}
3130

3231
/// Suggest similar language names for typos.
@@ -59,9 +58,7 @@ fn levenshtein(a: &str, b: &str) -> usize {
5958
curr[0] = i;
6059
for j in 1..=n {
6160
let cost = usize::from(a_chars[i - 1] != b_chars[j - 1]);
62-
curr[j] = (prev[j] + 1)
63-
.min(curr[j - 1] + 1)
64-
.min(prev[j - 1] + cost);
61+
curr[j] = (prev[j] + 1).min(curr[j - 1] + 1).min(prev[j - 1] + cost);
6562
}
6663
std::mem::swap(&mut prev, &mut curr);
6764
}

crates/plotnik-cli/src/commands/run_common.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ use std::path::Path;
66

77
use arborium_tree_sitter as tree_sitter;
88
use plotnik_langs::Lang;
9+
use plotnik_lib::QueryBuilder;
910
use plotnik_lib::bytecode::{Entrypoint, Module};
1011
use plotnik_lib::emit::emit_linked;
11-
use plotnik_lib::QueryBuilder;
1212

1313
use super::lang_resolver::{resolve_lang_required, suggest_language};
1414
use super::query_loader::load_query_source;

crates/plotnik-cli/src/commands/trace.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
33
use std::path::PathBuf;
44

5+
use plotnik_lib::Colors;
56
use plotnik_lib::engine::{
6-
debug_verify_type, FuelLimits, Materializer, PrintTracer, RuntimeError, ValueMaterializer,
7-
Verbosity, VM,
7+
FuelLimits, Materializer, PrintTracer, RuntimeError, VM, ValueMaterializer, Verbosity,
8+
debug_verify_type,
89
};
9-
use plotnik_lib::Colors;
1010

1111
use super::run_common::{self, PreparedQuery, QueryInput};
1212

crates/plotnik-cli/src/commands/tree.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ pub fn run(args: TreeArgs) {
2727
}
2828
};
2929

30-
let lang = resolve_lang(&args.lang, args.source_path.as_deref(), args.source_text.is_some());
30+
let lang = resolve_lang(
31+
&args.lang,
32+
args.source_path.as_deref(),
33+
args.source_text.is_some(),
34+
);
3135
let tree = lang.parse(&source);
3236
print!("{}", dump_tree(&tree, &source, args.raw, args.spans));
3337
}
@@ -107,7 +111,10 @@ fn format_node_with_field(
107111
let span_suffix = if show_spans {
108112
let start = node.start_position();
109113
let end = node.end_position();
110-
format!(" [{}:{}-{}:{}]", start.row, start.column, end.row, end.column)
114+
format!(
115+
" [{}:{}-{}:{}]",
116+
start.row, start.column, end.row, end.column
117+
)
111118
} else {
112119
String::new()
113120
};

crates/plotnik-lib/src/analyze/type_check/infer.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,9 @@ impl<'a, 'd> InferenceVisitor<'a, 'd> {
512512
TypeFlow::Void => {
513513
// Scalar list: void inner -> array of Node (or Ref)
514514
let element = self.get_recursive_ref_type(inner).unwrap_or(TYPE_NODE);
515-
let array_type = self.ctx.intern_type(TypeShape::Array { element, non_empty });
515+
let array_type = self
516+
.ctx
517+
.intern_type(TypeShape::Array { element, non_empty });
516518
TypeFlow::Scalar(array_type)
517519
}
518520
TypeFlow::Scalar(t) => {

crates/plotnik-lib/src/bytecode/dump.rs

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ use std::fmt::Write as _;
77

88
use crate::colors::Colors;
99

10-
use super::format::{format_effect, nav_symbol_epsilon, width_for_count, LineBuilder, Symbol};
10+
use super::format::{LineBuilder, Symbol, format_effect, nav_symbol_epsilon, width_for_count};
1111
use super::ids::{QTypeId, StepId};
1212
use super::module::{Instruction, Module};
1313
use super::type_meta::TypeKind;
14-
use super::{Call, Match, Return};
14+
use super::{Call, Match, Return, Trampoline};
1515

1616
/// Generate a human-readable dump of the bytecode module.
1717
pub fn dump(module: &Module, colors: Colors) -> String {
@@ -37,7 +37,6 @@ fn dump_header(out: &mut String, module: &Module, ctx: &DumpContext) {
3737
out.push('\n');
3838
}
3939

40-
4140
/// Context for dump formatting, precomputes lookups for O(1) access.
4241
struct DumpContext {
4342
/// Whether the bytecode is linked (contains grammar IDs vs StringIds).
@@ -74,6 +73,8 @@ impl DumpContext {
7473
let node_fields = module.node_fields();
7574

7675
let mut step_labels = BTreeMap::new();
76+
// Preamble always starts at step 0
77+
step_labels.insert(0, "_ObjWrap".to_string());
7778
for i in 0..entrypoints.len() {
7879
let ep = entrypoints.get(i);
7980
let name = strings.get(ep.name).to_string();
@@ -249,10 +250,7 @@ fn dump_types_members(out: &mut String, module: &Module, ctx: &DumpContext) {
249250
writeln!(
250251
out,
251252
"M{i:0mw$}: S{:0sw$} → T{:0tw$} {}; {name}: {type_name}{}",
252-
member.name.0,
253-
member.type_id.0,
254-
c.dim,
255-
c.reset
253+
member.name.0, member.type_id.0, c.dim, c.reset
256254
)
257255
.unwrap();
258256
}
@@ -274,11 +272,7 @@ fn dump_types_names(out: &mut String, module: &Module, ctx: &DumpContext) {
274272
writeln!(
275273
out,
276274
"N{i:0nw$}: S{:0sw$} → T{:0tw$} {}; {}{name}{}",
277-
entry.name.0,
278-
entry.type_id.0,
279-
c.dim,
280-
c.blue,
281-
c.reset
275+
entry.name.0, entry.type_id.0, c.dim, c.blue, c.reset
282276
)
283277
.unwrap();
284278
}
@@ -356,10 +350,16 @@ fn dump_code(out: &mut String, module: &Module, ctx: &DumpContext) {
356350
writeln!(out, "{}[transitions]{}", c.blue, c.reset).unwrap();
357351

358352
let mut step = 0u16;
353+
let mut first_label = true;
359354
while (step as usize) < transitions_count {
360355
// Check if this step has a label (using raw u16)
361356
if let Some(label) = ctx.step_labels.get(&step) {
362-
writeln!(out, "\n{}{label}{}:", c.blue, c.reset).unwrap();
357+
if first_label {
358+
writeln!(out, "{}{label}{}:", c.blue, c.reset).unwrap();
359+
first_label = false;
360+
} else {
361+
writeln!(out, "\n{}{label}{}:", c.blue, c.reset).unwrap();
362+
}
363363
}
364364

365365
let instr = module.decode_step_alloc(step);
@@ -373,7 +373,6 @@ fn dump_code(out: &mut String, module: &Module, ctx: &DumpContext) {
373373
}
374374
}
375375

376-
377376
fn instruction_step_count(instr: &Instruction) -> u16 {
378377
match instr {
379378
Instruction::Match(m) => {
@@ -400,11 +399,10 @@ fn instruction_step_count(instr: &Instruction) -> u16 {
400399
8 // Match64
401400
}
402401
}
403-
Instruction::Call(_) | Instruction::Return(_) => 1,
402+
Instruction::Call(_) | Instruction::Return(_) | Instruction::Trampoline(_) => 1,
404403
}
405404
}
406405

407-
408406
fn format_instruction(
409407
step: u16,
410408
instr: &Instruction,
@@ -416,10 +414,10 @@ fn format_instruction(
416414
Instruction::Match(m) => format_match(step, m, module, ctx, step_width),
417415
Instruction::Call(c) => format_call(step, c, module, ctx, step_width),
418416
Instruction::Return(r) => format_return(step, r, module, ctx, step_width),
417+
Instruction::Trampoline(t) => format_trampoline(step, t, ctx, step_width),
419418
}
420419
}
421420

422-
423421
fn format_match(
424422
step: u16,
425423
m: &Match,
@@ -571,6 +569,20 @@ fn format_return(
571569
builder.pad_successors(prefix, "▶")
572570
}
573571

572+
fn format_trampoline(step: u16, t: &Trampoline, _ctx: &DumpContext, step_width: usize) -> String {
573+
let builder = LineBuilder::new(step_width);
574+
let prefix = format!(
575+
" {:0sw$} {} ",
576+
step,
577+
Symbol::EMPTY.format(),
578+
sw = step_width
579+
);
580+
let content = "Trampoline";
581+
let successors = format!("{:0w$}", t.next.get(), w = step_width);
582+
let base = format!("{prefix}{content}");
583+
builder.pad_successors(base, &successors)
584+
}
585+
574586
/// Format a step ID, showing entrypoint label or numeric ID.
575587
fn format_step(step: StepId, ctx: &DumpContext, step_width: usize) -> String {
576588
let c = &ctx.colors;
@@ -580,4 +592,3 @@ fn format_step(step: StepId, ctx: &DumpContext, step_width: usize) -> String {
580592
format!("{:0w$}", step.get(), w = step_width)
581593
}
582594
}
583-

0 commit comments

Comments
 (0)