use thiserror::Error;
use tree_sitter::CaptureQuantifier;
use tree_sitter::QueryMatch;
use tree_sitter::Tree;
use crate::ast::File;
use crate::execution::error::ExecutionError;
use crate::functions::Functions;
use crate::graph::Graph;
use crate::graph::Value;
use crate::variables::Globals;
use crate::Identifier;
pub(crate) mod error;
mod lazy;
mod strict;
impl File {
pub fn execute<'a, 'tree>(
&self,
tree: &'tree Tree,
source: &'tree str,
config: &ExecutionConfig,
cancellation_flag: &dyn CancellationFlag,
) -> Result<Graph<'tree>, ExecutionError> {
let mut graph = Graph::new();
self.execute_into(&mut graph, tree, source, config, cancellation_flag)?;
Ok(graph)
}
pub fn execute_into<'a, 'tree>(
&self,
graph: &mut Graph<'tree>,
tree: &'tree Tree,
source: &'tree str,
config: &ExecutionConfig,
cancellation_flag: &dyn CancellationFlag,
) -> Result<(), ExecutionError> {
if config.lazy {
self.execute_lazy_into(graph, tree, source, config, cancellation_flag)
} else {
self.execute_strict_into(graph, tree, source, config, cancellation_flag)
}
}
pub(self) fn check_globals(&self, globals: &mut Globals) -> Result<(), ExecutionError> {
for global in &self.globals {
match globals.get(&global.name) {
None => {
if let Some(default) = &global.default {
globals
.add(global.name.clone(), default.to_string().into())
.map_err(|_| {
ExecutionError::DuplicateVariable(format!(
"global variable {} already defined",
global.name
))
})?;
} else {
return Err(ExecutionError::MissingGlobalVariable(
global.name.as_str().to_string(),
));
}
}
Some(value) => {
if global.quantifier == CaptureQuantifier::ZeroOrMore
|| global.quantifier == CaptureQuantifier::OneOrMore
{
if value.as_list().is_err() {
return Err(ExecutionError::ExpectedList(
global.name.as_str().to_string(),
));
}
}
}
}
}
Ok(())
}
}
pub struct ExecutionConfig<'a, 'g> {
pub(crate) functions: &'a Functions,
pub(crate) globals: &'a Globals<'g>,
pub(crate) lazy: bool,
pub(crate) location_attr: Option<Identifier>,
pub(crate) variable_name_attr: Option<Identifier>,
}
impl<'a, 'g> ExecutionConfig<'a, 'g> {
pub fn new(functions: &'a Functions, globals: &'a Globals<'g>) -> Self {
Self {
functions,
globals,
lazy: false,
location_attr: None,
variable_name_attr: None,
}
}
pub fn debug_attributes(
self,
location_attr: Identifier,
variable_name_attr: Identifier,
) -> Self {
Self {
functions: self.functions,
globals: self.globals,
lazy: self.lazy,
location_attr: location_attr.into(),
variable_name_attr: variable_name_attr.into(),
}
}
pub fn lazy(self, lazy: bool) -> Self {
Self {
functions: self.functions,
globals: self.globals,
lazy,
location_attr: self.location_attr,
variable_name_attr: self.variable_name_attr,
}
}
}
pub trait CancellationFlag {
fn check(&self, at: &'static str) -> Result<(), CancellationError>;
}
pub struct NoCancellation;
impl CancellationFlag for NoCancellation {
fn check(&self, _at: &'static str) -> Result<(), CancellationError> {
Ok(())
}
}
#[derive(Debug, Error)]
#[error("Cancelled at \"{0}\"")]
pub struct CancellationError(pub &'static str);
pub(self) fn query_capture_value<'tree>(
index: usize,
quantifier: CaptureQuantifier,
mat: &QueryMatch<'_, 'tree>,
graph: &mut Graph<'tree>,
) -> Value {
let mut nodes = mat
.captures
.iter()
.filter(|c| c.index as usize == index)
.map(|c| c.node);
match quantifier {
CaptureQuantifier::Zero => unreachable!(),
CaptureQuantifier::One => {
let syntax_node = graph.add_syntax_node(nodes.next().expect("missing capture"));
syntax_node.into()
}
CaptureQuantifier::ZeroOrMore | CaptureQuantifier::OneOrMore => {
let syntax_nodes = nodes
.map(|n| graph.add_syntax_node(n.clone()).into())
.collect::<Vec<Value>>();
syntax_nodes.into()
}
CaptureQuantifier::ZeroOrOne => match nodes.next() {
None => Value::Null.into(),
Some(node) => {
let syntax_node = graph.add_syntax_node(node);
syntax_node.into()
}
},
}
}