Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions crates/plotnik-cli/src/commands/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@ pub struct CheckArgs {
}

pub fn run(args: CheckArgs) {
let source_map = match load_query_source(
args.query_path.as_deref(),
args.query_text.as_deref(),
) {
let source_map = match load_query_source(args.query_path.as_deref(), args.query_text.as_deref())
{
Ok(map) => map,
Err(msg) => {
eprintln!("error: {}", msg);
Expand Down
16 changes: 9 additions & 7 deletions crates/plotnik-cli/src/commands/dump.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::path::PathBuf;

use plotnik_lib::Colors;
use plotnik_lib::QueryBuilder;
use plotnik_lib::bytecode::{Module, dump};
use plotnik_lib::Colors;

use super::lang_resolver::{resolve_lang, resolve_lang_required, suggest_language};
use super::query_loader::load_query_source;
Expand All @@ -15,10 +15,8 @@ pub struct DumpArgs {
}

pub fn run(args: DumpArgs) {
let source_map = match load_query_source(
args.query_path.as_deref(),
args.query_text.as_deref(),
) {
let source_map = match load_query_source(args.query_path.as_deref(), args.query_text.as_deref())
{
Ok(map) => map,
Err(msg) => {
eprintln!("error: {}", msg);
Expand Down Expand Up @@ -64,7 +62,9 @@ pub fn run(args: DumpArgs) {
if !linked.is_valid() {
eprint!(
"{}",
linked.diagnostics().render_colored(linked.source_map(), args.color)
linked
.diagnostics()
.render_colored(linked.source_map(), args.color)
);
std::process::exit(1);
}
Expand All @@ -73,7 +73,9 @@ pub fn run(args: DumpArgs) {
if !query.is_valid() {
eprint!(
"{}",
query.diagnostics().render_colored(query.source_map(), args.color)
query
.diagnostics()
.render_colored(query.source_map(), args.color)
);
std::process::exit(1);
}
Expand Down
4 changes: 2 additions & 2 deletions crates/plotnik-cli/src/commands/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

use std::path::PathBuf;

use plotnik_lib::Colors;
use plotnik_lib::engine::{
debug_verify_type, FuelLimits, Materializer, RuntimeError, ValueMaterializer, VM,
FuelLimits, Materializer, RuntimeError, VM, ValueMaterializer, debug_verify_type,
};
use plotnik_lib::Colors;

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

Expand Down
14 changes: 8 additions & 6 deletions crates/plotnik-cli/src/commands/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,8 @@ pub fn run(args: InferArgs) {
std::process::exit(1);
}

let source_map = match load_query_source(
args.query_path.as_deref(),
args.query_text.as_deref(),
) {
let source_map = match load_query_source(args.query_path.as_deref(), args.query_text.as_deref())
{
Ok(map) => map,
Err(msg) => {
eprintln!("error: {}", msg);
Expand Down Expand Up @@ -80,7 +78,9 @@ pub fn run(args: InferArgs) {
if !linked.is_valid() {
eprint!(
"{}",
linked.diagnostics().render_colored(linked.source_map(), args.color)
linked
.diagnostics()
.render_colored(linked.source_map(), args.color)
);
std::process::exit(1);
}
Expand All @@ -89,7 +89,9 @@ pub fn run(args: InferArgs) {
if !query.is_valid() {
eprint!(
"{}",
query.diagnostics().render_colored(query.source_map(), args.color)
query
.diagnostics()
.render_colored(query.source_map(), args.color)
);
std::process::exit(1);
}
Expand Down
7 changes: 2 additions & 5 deletions crates/plotnik-cli/src/commands/lang_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ pub fn resolve_lang(explicit: Option<&str>, query_path: Option<&Path>) -> Option

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

/// Suggest similar language names for typos.
Expand Down Expand Up @@ -59,9 +58,7 @@ fn levenshtein(a: &str, b: &str) -> usize {
curr[0] = i;
for j in 1..=n {
let cost = usize::from(a_chars[i - 1] != b_chars[j - 1]);
curr[j] = (prev[j] + 1)
.min(curr[j - 1] + 1)
.min(prev[j - 1] + cost);
curr[j] = (prev[j] + 1).min(curr[j - 1] + 1).min(prev[j - 1] + cost);
}
std::mem::swap(&mut prev, &mut curr);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/plotnik-cli/src/commands/run_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use std::path::Path;

use arborium_tree_sitter as tree_sitter;
use plotnik_langs::Lang;
use plotnik_lib::QueryBuilder;
use plotnik_lib::bytecode::{Entrypoint, Module};
use plotnik_lib::emit::emit_linked;
use plotnik_lib::QueryBuilder;

use super::lang_resolver::{resolve_lang_required, suggest_language};
use super::query_loader::load_query_source;
Expand Down
6 changes: 3 additions & 3 deletions crates/plotnik-cli/src/commands/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

use std::path::PathBuf;

use plotnik_lib::Colors;
use plotnik_lib::engine::{
debug_verify_type, FuelLimits, Materializer, PrintTracer, RuntimeError, ValueMaterializer,
Verbosity, VM,
FuelLimits, Materializer, PrintTracer, RuntimeError, VM, ValueMaterializer, Verbosity,
debug_verify_type,
};
use plotnik_lib::Colors;

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

Expand Down
11 changes: 9 additions & 2 deletions crates/plotnik-cli/src/commands/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ pub fn run(args: TreeArgs) {
}
};

let lang = resolve_lang(&args.lang, args.source_path.as_deref(), args.source_text.is_some());
let lang = resolve_lang(
&args.lang,
args.source_path.as_deref(),
args.source_text.is_some(),
);
let tree = lang.parse(&source);
print!("{}", dump_tree(&tree, &source, args.raw, args.spans));
}
Expand Down Expand Up @@ -107,7 +111,10 @@ fn format_node_with_field(
let span_suffix = if show_spans {
let start = node.start_position();
let end = node.end_position();
format!(" [{}:{}-{}:{}]", start.row, start.column, end.row, end.column)
format!(
" [{}:{}-{}:{}]",
start.row, start.column, end.row, end.column
)
} else {
String::new()
};
Expand Down
4 changes: 3 additions & 1 deletion crates/plotnik-lib/src/analyze/type_check/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,9 @@ impl<'a, 'd> InferenceVisitor<'a, 'd> {
TypeFlow::Void => {
// Scalar list: void inner -> array of Node (or Ref)
let element = self.get_recursive_ref_type(inner).unwrap_or(TYPE_NODE);
let array_type = self.ctx.intern_type(TypeShape::Array { element, non_empty });
let array_type = self
.ctx
.intern_type(TypeShape::Array { element, non_empty });
TypeFlow::Scalar(array_type)
}
TypeFlow::Scalar(t) => {
Expand Down
47 changes: 29 additions & 18 deletions crates/plotnik-lib/src/bytecode/dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ use std::fmt::Write as _;

use crate::colors::Colors;

use super::format::{format_effect, nav_symbol_epsilon, width_for_count, LineBuilder, Symbol};
use super::format::{LineBuilder, Symbol, format_effect, nav_symbol_epsilon, width_for_count};
use super::ids::{QTypeId, StepId};
use super::module::{Instruction, Module};
use super::type_meta::TypeKind;
use super::{Call, Match, Return};
use super::{Call, Match, Return, Trampoline};

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


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

let mut step_labels = BTreeMap::new();
// Preamble always starts at step 0
step_labels.insert(0, "_ObjWrap".to_string());
for i in 0..entrypoints.len() {
let ep = entrypoints.get(i);
let name = strings.get(ep.name).to_string();
Expand Down Expand Up @@ -249,10 +250,7 @@ fn dump_types_members(out: &mut String, module: &Module, ctx: &DumpContext) {
writeln!(
out,
"M{i:0mw$}: S{:0sw$} → T{:0tw$} {}; {name}: {type_name}{}",
member.name.0,
member.type_id.0,
c.dim,
c.reset
member.name.0, member.type_id.0, c.dim, c.reset
)
.unwrap();
}
Expand All @@ -274,11 +272,7 @@ fn dump_types_names(out: &mut String, module: &Module, ctx: &DumpContext) {
writeln!(
out,
"N{i:0nw$}: S{:0sw$} → T{:0tw$} {}; {}{name}{}",
entry.name.0,
entry.type_id.0,
c.dim,
c.blue,
c.reset
entry.name.0, entry.type_id.0, c.dim, c.blue, c.reset
)
.unwrap();
}
Expand Down Expand Up @@ -356,10 +350,16 @@ fn dump_code(out: &mut String, module: &Module, ctx: &DumpContext) {
writeln!(out, "{}[transitions]{}", c.blue, c.reset).unwrap();

let mut step = 0u16;
let mut first_label = true;
while (step as usize) < transitions_count {
// Check if this step has a label (using raw u16)
if let Some(label) = ctx.step_labels.get(&step) {
writeln!(out, "\n{}{label}{}:", c.blue, c.reset).unwrap();
if first_label {
writeln!(out, "{}{label}{}:", c.blue, c.reset).unwrap();
first_label = false;
} else {
writeln!(out, "\n{}{label}{}:", c.blue, c.reset).unwrap();
}
}

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


fn instruction_step_count(instr: &Instruction) -> u16 {
match instr {
Instruction::Match(m) => {
Expand All @@ -400,11 +399,10 @@ fn instruction_step_count(instr: &Instruction) -> u16 {
8 // Match64
}
}
Instruction::Call(_) | Instruction::Return(_) => 1,
Instruction::Call(_) | Instruction::Return(_) | Instruction::Trampoline(_) => 1,
}
}


fn format_instruction(
step: u16,
instr: &Instruction,
Expand All @@ -416,10 +414,10 @@ fn format_instruction(
Instruction::Match(m) => format_match(step, m, module, ctx, step_width),
Instruction::Call(c) => format_call(step, c, module, ctx, step_width),
Instruction::Return(r) => format_return(step, r, module, ctx, step_width),
Instruction::Trampoline(t) => format_trampoline(step, t, ctx, step_width),
}
}


fn format_match(
step: u16,
m: &Match,
Expand Down Expand Up @@ -571,6 +569,20 @@ fn format_return(
builder.pad_successors(prefix, "▶")
}

fn format_trampoline(step: u16, t: &Trampoline, _ctx: &DumpContext, step_width: usize) -> String {
let builder = LineBuilder::new(step_width);
let prefix = format!(
" {:0sw$} {} ",
step,
Symbol::EMPTY.format(),
sw = step_width
);
let content = "Trampoline";
let successors = format!("{:0w$}", t.next.get(), w = step_width);
let base = format!("{prefix}{content}");
builder.pad_successors(base, &successors)
}

/// Format a step ID, showing entrypoint label or numeric ID.
fn format_step(step: StepId, ctx: &DumpContext, step_width: usize) -> String {
let c = &ctx.colors;
Expand All @@ -580,4 +592,3 @@ fn format_step(step: StepId, ctx: &DumpContext, step_width: usize) -> String {
format!("{:0w$}", step.get(), w = step_width)
}
}

2 changes: 1 addition & 1 deletion crates/plotnik-lib/src/bytecode/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
//! | | pad | | (sym) | | | | |
//! ```

use super::EffectOp;
use super::effects::EffectOpcode;
use super::nav::Nav;
use super::EffectOp;

// =============================================================================
// Column Layout
Expand Down
16 changes: 8 additions & 8 deletions crates/plotnik-lib/src/bytecode/ids.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,28 @@ use super::constants::STEP_SIZE;

/// Index into the Transitions section (8-byte steps).
///
/// Uses NonZeroU16 to make StepId(0) unrepresentable - terminal state
/// is expressed through absence (empty successors, None) rather than
/// a magic value.
/// Step 0 is a valid address (preamble starts there).
/// In successor fields, raw value 0 means "terminal" — this sentinel
/// is handled by decoding logic, not by the type.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[repr(transparent)]
pub struct StepId(pub NonZeroU16);
pub struct StepId(pub u16);

impl StepId {
/// Create a new StepId. Panics if n == 0.
#[inline]
pub fn new(n: u16) -> Self {
Self(NonZeroU16::new(n).expect("StepId cannot be 0"))
Self(n)
}

/// Get the raw u16 value.
#[inline]
pub fn get(self) -> u16 {
self.0.get()
self.0
}

#[inline]
pub fn byte_offset(self) -> usize {
self.0.get() as usize * STEP_SIZE
self.0 as usize * STEP_SIZE
}
}

Expand Down Expand Up @@ -66,6 +65,7 @@ mod tests {

#[test]
fn step_id_byte_offset() {
assert_eq!(StepId::new(0).byte_offset(), 0);
assert_eq!(StepId::new(1).byte_offset(), 8);
assert_eq!(StepId::new(10).byte_offset(), 80);
}
Expand Down
Loading