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
10 changes: 4 additions & 6 deletions crates/plotnik-cli/src/commands/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
use std::path::PathBuf;

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

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

Expand Down Expand Up @@ -37,7 +35,7 @@ pub fn run(args: ExecArgs) {
color: args.color,
});

let vm = VM::new(&tree, trivia_types, FuelLimits::default());
let vm = VM::builder(&tree).trivia_types(trivia_types).build();
let effects = match vm.execute(&module, 0, &entrypoint) {
Ok(effects) => effects,
Err(RuntimeError::NoMatch) => {
Expand All @@ -50,12 +48,12 @@ pub fn run(args: ExecArgs) {
};

let materializer = ValueMaterializer::new(&source_code, module.types(), module.strings());
let value = materializer.materialize(effects.as_slice(), entrypoint.result_type);
let value = materializer.materialize(effects.as_slice(), entrypoint.result_type());

let colors = Colors::new(args.color);

// Debug-only: verify output matches declared type
debug_verify_type(&value, entrypoint.result_type, &module, colors);
debug_verify_type(&value, entrypoint.result_type(), &module, colors);

let output = value.format(args.pretty, colors);
println!("{}", output);
Expand Down
14 changes: 6 additions & 8 deletions crates/plotnik-cli/src/commands/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use std::fs;
use std::io::{self, Write};
use std::path::PathBuf;

use plotnik_lib::Colors;
use plotnik_lib::QueryBuilder;
use plotnik_lib::bytecode::Module;
use plotnik_lib::typegen::typescript;
Expand Down Expand Up @@ -106,13 +105,12 @@ pub fn run(args: InferArgs) {
};
// Only use colors when outputting to stdout (not to file)
let use_colors = args.color && args.output.is_none();
let config = typescript::Config {
export: args.export,
emit_node_type: !args.no_node_type,
verbose_nodes: args.verbose_nodes,
void_type,
colors: Colors::new(use_colors),
};
let config = typescript::Config::new()
.export(args.export)
.emit_node_type(!args.no_node_type)
.verbose_nodes(args.verbose_nodes)
.void_type(void_type)
.colored(use_colors);
let output = typescript::emit_with_config(&module, config);

// Write output
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 @@ -122,7 +122,7 @@ pub fn validate(
pub fn build_trivia_types(module: &Module) -> Vec<u16> {
let trivia_view = module.trivia();
(0..trivia_view.len())
.map(|i| trivia_view.get(i).node_type)
.map(|i| trivia_view.get(i).node_type())
.collect()
}

Expand Down
21 changes: 11 additions & 10 deletions crates/plotnik-cli/src/commands/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use std::path::PathBuf;

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

use super::run_common::{self, PreparedQuery, QueryInput};
Expand Down Expand Up @@ -40,13 +39,15 @@ pub fn run(args: TraceArgs) {
color: args.color,
});

let limits = FuelLimits {
exec_fuel: args.fuel,
..Default::default()
};
let vm = VM::new(&tree, trivia_types, limits);
let vm = VM::builder(&tree)
.trivia_types(trivia_types)
.exec_fuel(args.fuel)
.build();
let colors = Colors::new(args.color);
let mut tracer = PrintTracer::new(&source_code, &module, args.verbosity, colors);
let mut tracer = PrintTracer::builder(&source_code, &module)
.verbosity(args.verbosity)
.colored(args.color)
.build();

let effects = match vm.execute_with(&module, 0, &entrypoint, &mut tracer) {
Ok(effects) => {
Expand All @@ -70,10 +71,10 @@ pub fn run(args: TraceArgs) {

println!("{}---{}", colors.dim, colors.reset);
let materializer = ValueMaterializer::new(&source_code, module.types(), module.strings());
let value = materializer.materialize(effects.as_slice(), entrypoint.result_type);
let value = materializer.materialize(effects.as_slice(), entrypoint.result_type());

// Debug-only: verify output matches declared type
debug_verify_type(&value, entrypoint.result_type, &module, colors);
debug_verify_type(&value, entrypoint.result_type(), &module, colors);

let output = value.format(true, colors);
println!("{}", output);
Expand Down
127 changes: 74 additions & 53 deletions crates/plotnik-lib/src/bytecode/dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use super::format::{LineBuilder, Symbol, format_effect, nav_symbol_epsilon, widt
use super::ids::TypeId;
use super::instructions::StepId;
use super::module::{Instruction, Module};
use super::type_meta::TypeKind;
use super::type_meta::{TypeData, TypeKind};
use super::{Call, Match, Return, Trampoline};

/// Generate a human-readable dump of the bytecode module.
Expand Down Expand Up @@ -79,20 +79,20 @@ impl DumpContext {
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();
step_labels.insert(ep.target, name);
let name = strings.get(ep.name()).to_string();
step_labels.insert(ep.target(), name);
}

let mut node_type_names = BTreeMap::new();
for i in 0..node_types.len() {
let t = node_types.get(i);
node_type_names.insert(t.id, strings.get(t.name).to_string());
node_type_names.insert(t.id(), strings.get(t.name()).to_string());
}

let mut node_field_names = BTreeMap::new();
for i in 0..node_fields.len() {
let f = node_fields.get(i);
node_field_names.insert(f.id, strings.get(f.name).to_string());
node_field_names.insert(f.id(), strings.get(f.name()).to_string());
}

// Collect all strings for unlinked mode lookups
Expand Down Expand Up @@ -182,53 +182,74 @@ fn dump_types_defs(out: &mut String, module: &Module, ctx: &DumpContext) {
// All types are now in type_defs, including builtins
for i in 0..types.defs_count() {
let def = types.get_def(i);
let kind = def.type_kind().expect("valid type kind");

let formatted = match kind {
// Primitive types
TypeKind::Void => "<Void>".to_string(),
TypeKind::Node => "<Node>".to_string(),
TypeKind::String => "<String>".to_string(),
// Composite types
TypeKind::Struct => format!("Struct M{:0mw$}:{}", def.data, def.count),
TypeKind::Enum => format!("Enum M{:0mw$}:{}", def.data, def.count),
// Wrapper types
TypeKind::Optional => format!("Optional(T{:0tw$})", def.data),
TypeKind::ArrayZeroOrMore => format!("ArrayStar(T{:0tw$})", def.data),
TypeKind::ArrayOneOrMore => format!("ArrayPlus(T{:0tw$})", def.data),
TypeKind::Alias => format!("Alias(T{:0tw$})", def.data),
};

// Generate comment for non-primitives (comments are dim)
let comment = match kind {
TypeKind::Void | TypeKind::Node | TypeKind::String => String::new(),
TypeKind::Struct => {
let fields: Vec<_> = types
.members_of(&def)
.map(|m| strings.get(m.name).to_string())
.collect();
format!("{} ; {{ {} }}{}", c.dim, fields.join(", "), c.reset)
}
TypeKind::Enum => {
let variants: Vec<_> = types
.members_of(&def)
.map(|m| strings.get(m.name).to_string())
.collect();
format!("{} ; {}{}", c.dim, variants.join(" | "), c.reset)
}
TypeKind::Optional => {
let inner_name = format_type_name(TypeId(def.data), module, ctx);
format!("{} ; {}?{}", c.dim, inner_name, c.reset)
let (formatted, comment) = match def.classify() {
TypeData::Primitive(kind) => {
let name = match kind {
TypeKind::Void => "<Void>",
TypeKind::Node => "<Node>",
TypeKind::String => "<String>",
_ => unreachable!(),
};
(name.to_string(), String::new())
}
TypeKind::ArrayZeroOrMore => {
let inner_name = format_type_name(TypeId(def.data), module, ctx);
format!("{} ; {}*{}", c.dim, inner_name, c.reset)
TypeData::Wrapper { kind, inner } => {
let formatted = match kind {
TypeKind::Optional => format!("Optional(T{:0tw$})", inner.0),
TypeKind::ArrayZeroOrMore => format!("ArrayStar(T{:0tw$})", inner.0),
TypeKind::ArrayOneOrMore => format!("ArrayPlus(T{:0tw$})", inner.0),
TypeKind::Alias => format!("Alias(T{:0tw$})", inner.0),
_ => unreachable!(),
};
let comment = match kind {
TypeKind::Optional => {
let inner_name = format_type_name(inner, module, ctx);
format!("{} ; {}?{}", c.dim, inner_name, c.reset)
}
TypeKind::ArrayZeroOrMore => {
let inner_name = format_type_name(inner, module, ctx);
format!("{} ; {}*{}", c.dim, inner_name, c.reset)
}
TypeKind::ArrayOneOrMore => {
let inner_name = format_type_name(inner, module, ctx);
format!("{} ; {}+{}", c.dim, inner_name, c.reset)
}
TypeKind::Alias => String::new(),
_ => unreachable!(),
};
(formatted, comment)
}
TypeKind::ArrayOneOrMore => {
let inner_name = format_type_name(TypeId(def.data), module, ctx);
format!("{} ; {}+{}", c.dim, inner_name, c.reset)
TypeData::Composite {
kind,
member_start,
member_count,
} => {
let formatted = match kind {
TypeKind::Struct => {
format!("Struct M{:0mw$}:{}", member_start, member_count)
}
TypeKind::Enum => format!("Enum M{:0mw$}:{}", member_start, member_count),
_ => unreachable!(),
};
let comment = match kind {
TypeKind::Struct => {
let fields: Vec<_> = types
.members_of(&def)
.map(|m| strings.get(m.name()).to_string())
.collect();
format!("{} ; {{ {} }}{}", c.dim, fields.join(", "), c.reset)
}
TypeKind::Enum => {
let variants: Vec<_> = types
.members_of(&def)
.map(|m| strings.get(m.name()).to_string())
.collect();
format!("{} ; {}{}", c.dim, variants.join(" | "), c.reset)
}
_ => unreachable!(),
};
(formatted, comment)
}
TypeKind::Alias => String::new(),
};

writeln!(out, "T{i:0tw$} = {formatted}{comment}").unwrap();
Expand Down Expand Up @@ -288,7 +309,7 @@ fn format_type_name(type_id: TypeId, module: &Module, ctx: &DumpContext) -> Stri

// Check if it's a primitive type
if let Some(def) = types.get(type_id)
&& let Some(kind) = def.type_kind()
&& let TypeData::Primitive(kind) = def.classify()
&& let Some(name) = kind.primitive_name()
{
return format!("<{}>", name);
Expand All @@ -297,8 +318,8 @@ fn format_type_name(type_id: TypeId, module: &Module, ctx: &DumpContext) -> Stri
// Try to find a name in types.names
for i in 0..types.names_count() {
let entry = types.get_name(i);
if entry.type_id == type_id {
return strings.get(entry.name).to_string();
if entry.type_id() == type_id {
return strings.get(entry.name()).to_string();
}
}

Expand All @@ -320,8 +341,8 @@ fn dump_entrypoints(out: &mut String, module: &Module, ctx: &DumpContext) {
let mut entries: Vec<_> = (0..entrypoints.len())
.map(|i| {
let ep = entrypoints.get(i);
let name = strings.get(ep.name);
(name, ep.target, ep.result_type.0)
let name = strings.get(ep.name());
(name, ep.target(), ep.result_type().0)
})
.collect();
entries.sort_by_key(|(name, _, _)| *name);
Expand Down
16 changes: 14 additions & 2 deletions crates/plotnik-lib/src/bytecode/effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,16 @@ impl EffectOpcode {

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct EffectOp {
pub opcode: EffectOpcode,
pub payload: usize,
pub(crate) opcode: EffectOpcode,
pub(crate) payload: usize,
}

impl EffectOp {
/// Create a new effect operation.
pub fn new(opcode: EffectOpcode, payload: usize) -> Self {
Self { opcode, payload }
}

pub fn from_bytes(bytes: [u8; 2]) -> Self {
let raw = u16::from_le_bytes(bytes);
let opcode = EffectOpcode::from_u8((raw >> 10) as u8);
Expand All @@ -64,4 +69,11 @@ impl EffectOp {
let raw = ((self.opcode as u16) << 10) | ((self.payload as u16) & 0x3FF);
raw.to_le_bytes()
}

pub fn opcode(&self) -> EffectOpcode {
self.opcode
}
pub fn payload(&self) -> usize {
self.payload
}
}
25 changes: 8 additions & 17 deletions crates/plotnik-lib/src/bytecode/effects_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,28 @@ use super::*;

#[test]
fn roundtrip_with_payload() {
let op = EffectOp {
opcode: EffectOpcode::Set,
payload: 42,
};
let op = EffectOp::new(EffectOpcode::Set, 42);
let bytes = op.to_bytes();
let decoded = EffectOp::from_bytes(bytes);
assert_eq!(decoded.opcode, EffectOpcode::Set);
assert_eq!(decoded.payload, 42);
assert_eq!(decoded.opcode(), EffectOpcode::Set);
assert_eq!(decoded.payload(), 42);
}

#[test]
fn roundtrip_no_payload() {
let op = EffectOp {
opcode: EffectOpcode::Node,
payload: 0,
};
let op = EffectOp::new(EffectOpcode::Node, 0);
let bytes = op.to_bytes();
let decoded = EffectOp::from_bytes(bytes);
assert_eq!(decoded.opcode, EffectOpcode::Node);
assert_eq!(decoded.payload, 0);
assert_eq!(decoded.opcode(), EffectOpcode::Node);
assert_eq!(decoded.payload(), 0);
}

#[test]
fn max_payload() {
let op = EffectOp {
opcode: EffectOpcode::Enum,
payload: 1023,
};
let op = EffectOp::new(EffectOpcode::Enum, 1023);
let bytes = op.to_bytes();
let decoded = EffectOp::from_bytes(bytes);
assert_eq!(decoded.payload, 1023);
assert_eq!(decoded.payload(), 1023);
}

#[test]
Expand Down
Loading