From 13617f5cade0ff8d6ad9b41ad32ed9bfb9ff6346 Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Sun, 4 Jan 2026 18:00:20 -0300 Subject: [PATCH] refactor: extract `prepare_query` helper for exec/trace commands --- crates/plotnik-cli/src/commands/exec.rs | 79 ++++------------- crates/plotnik-cli/src/commands/run_common.rs | 87 +++++++++++++++++++ crates/plotnik-cli/src/commands/trace.rs | 79 ++++------------- 3 files changed, 121 insertions(+), 124 deletions(-) diff --git a/crates/plotnik-cli/src/commands/exec.rs b/crates/plotnik-cli/src/commands/exec.rs index 2cf3c296..702838bf 100644 --- a/crates/plotnik-cli/src/commands/exec.rs +++ b/crates/plotnik-cli/src/commands/exec.rs @@ -2,17 +2,12 @@ use std::path::PathBuf; -use plotnik_lib::bytecode::Module; -use plotnik_lib::emit::emit_linked; -use plotnik_lib::Colors; -use plotnik_lib::QueryBuilder; - use plotnik_lib::engine::{ debug_verify_type, FuelLimits, Materializer, RuntimeError, ValueMaterializer, VM, }; +use plotnik_lib::Colors; -use super::query_loader::load_query_source; -use super::run_common; +use super::run_common::{self, PreparedQuery, QueryInput}; pub struct ExecArgs { pub query_path: Option, @@ -26,61 +21,21 @@ pub struct ExecArgs { } pub fn run(args: ExecArgs) { - if let Err(msg) = run_common::validate( - args.query_text.is_some() || args.query_path.is_some(), - args.source_text.is_some() || args.source_path.is_some(), - args.source_text.is_some(), - args.lang.is_some(), - ) { - eprintln!("error: {}", msg); - std::process::exit(1); - } - - 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); - std::process::exit(1); - } - }; - - if source_map.is_empty() { - eprintln!("error: query cannot be empty"); - std::process::exit(1); - } - - let source_code = run_common::load_source( - args.source_text.as_deref(), - args.source_path.as_deref(), - args.query_path.as_deref(), - ); - let lang = run_common::resolve_lang(args.lang.as_deref(), args.source_path.as_deref()); - - let query = match QueryBuilder::new(source_map).parse() { - Ok(parsed) => parsed.analyze().link(&lang), - Err(e) => { - eprintln!("error: {}", e); - std::process::exit(1); - } - }; - - if !query.is_valid() { - eprint!( - "{}", - query - .diagnostics() - .render_colored(query.source_map(), args.color) - ); - std::process::exit(1); - } - - let bytecode = emit_linked(&query).expect("emit failed"); - let module = Module::from_bytes(bytecode).expect("module load failed"); - - let entrypoint = run_common::resolve_entrypoint(&module, args.entry.as_deref()); - let tree = lang.parse(&source_code); - let trivia_types = run_common::build_trivia_types(&module); + let PreparedQuery { + module, + entrypoint, + tree, + trivia_types, + source_code, + } = run_common::prepare_query(QueryInput { + query_path: args.query_path.as_deref(), + query_text: args.query_text.as_deref(), + source_path: args.source_path.as_deref(), + source_text: args.source_text.as_deref(), + lang: args.lang.as_deref(), + entry: args.entry.as_deref(), + color: args.color, + }); let vm = VM::new(&tree, trivia_types, FuelLimits::default()); let effects = match vm.execute(&module, &entrypoint) { diff --git a/crates/plotnik-cli/src/commands/run_common.rs b/crates/plotnik-cli/src/commands/run_common.rs index 9dfa08c8..a55dfb49 100644 --- a/crates/plotnik-cli/src/commands/run_common.rs +++ b/crates/plotnik-cli/src/commands/run_common.rs @@ -4,10 +4,14 @@ use std::fs; use std::io::{self, Read}; use std::path::Path; +use arborium_tree_sitter as tree_sitter; use plotnik_langs::Lang; 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; /// Load source code from file, stdin, or inline text. pub fn load_source( @@ -121,3 +125,86 @@ pub fn build_trivia_types(module: &Module) -> Vec { .map(|i| trivia_view.get(i).node_type) .collect() } + +/// Common input parameters for exec/trace commands. +pub struct QueryInput<'a> { + pub query_path: Option<&'a Path>, + pub query_text: Option<&'a str>, + pub source_path: Option<&'a Path>, + pub source_text: Option<&'a str>, + pub lang: Option<&'a str>, + pub entry: Option<&'a str>, + pub color: bool, +} + +/// Prepared query ready for execution. +pub struct PreparedQuery { + pub module: Module, + pub entrypoint: Entrypoint, + pub tree: tree_sitter::Tree, + pub trivia_types: Vec, + pub source_code: String, +} + +/// Load, parse, analyze, link, and emit a query. +/// Exits on any error. +pub fn prepare_query(input: QueryInput) -> PreparedQuery { + if let Err(msg) = validate( + input.query_text.is_some() || input.query_path.is_some(), + input.source_text.is_some() || input.source_path.is_some(), + input.source_text.is_some(), + input.lang.is_some(), + ) { + eprintln!("error: {}", msg); + std::process::exit(1); + } + + let source_map = match load_query_source(input.query_path, input.query_text) { + Ok(map) => map, + Err(msg) => { + eprintln!("error: {}", msg); + std::process::exit(1); + } + }; + + if source_map.is_empty() { + eprintln!("error: query cannot be empty"); + std::process::exit(1); + } + + let source_code = load_source(input.source_text, input.source_path, input.query_path); + let lang = resolve_lang(input.lang, input.source_path); + + let query = match QueryBuilder::new(source_map).parse() { + Ok(parsed) => parsed.analyze().link(&lang), + Err(e) => { + eprintln!("error: {}", e); + std::process::exit(1); + } + }; + + if !query.is_valid() { + eprint!( + "{}", + query + .diagnostics() + .render_colored(query.source_map(), input.color) + ); + std::process::exit(1); + } + + let bytecode = emit_linked(&query).expect("emit failed"); + let module = Module::from_bytes(bytecode).expect("module load failed"); + + let entrypoint = resolve_entrypoint(&module, input.entry); + let tree = lang.parse(&source_code); + let trivia_types = build_trivia_types(&module); + + PreparedQuery { + module, + entrypoint, + tree, + trivia_types, + source_code, + } +} diff --git a/crates/plotnik-cli/src/commands/trace.rs b/crates/plotnik-cli/src/commands/trace.rs index e3424ffc..8830bb68 100644 --- a/crates/plotnik-cli/src/commands/trace.rs +++ b/crates/plotnik-cli/src/commands/trace.rs @@ -2,18 +2,13 @@ use std::path::PathBuf; -use plotnik_lib::bytecode::Module; -use plotnik_lib::emit::emit_linked; -use plotnik_lib::Colors; -use plotnik_lib::QueryBuilder; - use plotnik_lib::engine::{ debug_verify_type, FuelLimits, Materializer, PrintTracer, RuntimeError, ValueMaterializer, Verbosity, VM, }; +use plotnik_lib::Colors; -use super::query_loader::load_query_source; -use super::run_common; +use super::run_common::{self, PreparedQuery, QueryInput}; pub struct TraceArgs { pub query_path: Option, @@ -29,61 +24,21 @@ pub struct TraceArgs { } pub fn run(args: TraceArgs) { - if let Err(msg) = run_common::validate( - args.query_text.is_some() || args.query_path.is_some(), - args.source_text.is_some() || args.source_path.is_some(), - args.source_text.is_some(), - args.lang.is_some(), - ) { - eprintln!("error: {}", msg); - std::process::exit(1); - } - - 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); - std::process::exit(1); - } - }; - - if source_map.is_empty() { - eprintln!("error: query cannot be empty"); - std::process::exit(1); - } - - let source_code = run_common::load_source( - args.source_text.as_deref(), - args.source_path.as_deref(), - args.query_path.as_deref(), - ); - let lang = run_common::resolve_lang(args.lang.as_deref(), args.source_path.as_deref()); - - let query = match QueryBuilder::new(source_map).parse() { - Ok(parsed) => parsed.analyze().link(&lang), - Err(e) => { - eprintln!("error: {}", e); - std::process::exit(1); - } - }; - - if !query.is_valid() { - eprint!( - "{}", - query - .diagnostics() - .render_colored(query.source_map(), args.color) - ); - std::process::exit(1); - } - - let bytecode = emit_linked(&query).expect("emit failed"); - let module = Module::from_bytes(bytecode).expect("module load failed"); - - let entrypoint = run_common::resolve_entrypoint(&module, args.entry.as_deref()); - let tree = lang.parse(&source_code); - let trivia_types = run_common::build_trivia_types(&module); + let PreparedQuery { + module, + entrypoint, + tree, + trivia_types, + source_code, + } = run_common::prepare_query(QueryInput { + query_path: args.query_path.as_deref(), + query_text: args.query_text.as_deref(), + source_path: args.source_path.as_deref(), + source_text: args.source_text.as_deref(), + lang: args.lang.as_deref(), + entry: args.entry.as_deref(), + color: args.color, + }); let limits = FuelLimits { exec_fuel: args.fuel,