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
79 changes: 17 additions & 62 deletions crates/plotnik-cli/src/commands/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PathBuf>,
Expand All @@ -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) {
Expand Down
87 changes: 87 additions & 0 deletions crates/plotnik-cli/src/commands/run_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -121,3 +125,86 @@ pub fn build_trivia_types(module: &Module) -> Vec<u16> {
.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<u16>,
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,
}
}
79 changes: 17 additions & 62 deletions crates/plotnik-cli/src/commands/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PathBuf>,
Expand All @@ -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,
Expand Down