use std::collections::HashMap;
use crate::execution::ExecutionError;
use crate::graph::Graph;
use crate::graph::Value;
use crate::Identifier;
pub trait Function {
fn call(
&mut self,
graph: &mut Graph,
source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError>;
}
pub trait Parameters {
fn param(&mut self) -> Result<Value, ExecutionError>;
fn finish(&mut self) -> Result<(), ExecutionError>;
}
impl<I> Parameters for I
where
I: Iterator<Item = Value>,
{
fn param(&mut self) -> Result<Value, ExecutionError> {
let value = self
.next()
.ok_or(ExecutionError::InvalidParameters(format!(
"expected more parameters"
)))?;
Ok(value)
}
fn finish(&mut self) -> Result<(), ExecutionError> {
let value = self.next();
if value.is_some() {
return Err(ExecutionError::InvalidParameters(format!(
"unexpected extra parameter"
)));
}
Ok(())
}
}
#[derive(Default)]
pub struct Functions {
functions: HashMap<Identifier, Box<dyn Function>>,
}
impl Functions {
pub fn new() -> Functions {
Functions::default()
}
pub fn stdlib() -> Functions {
let mut functions = Functions::new();
functions.add(Identifier::from("is-null"), stdlib::IsNull);
functions.add(
Identifier::from("named-child-index"),
stdlib::syntax::NamedChildIndex,
);
functions.add(Identifier::from("source-text"), stdlib::syntax::SourceText);
functions.add(Identifier::from("start-row"), stdlib::syntax::StartRow);
functions.add(
Identifier::from("start-column"),
stdlib::syntax::StartColumn,
);
functions.add(Identifier::from("end-row"), stdlib::syntax::EndRow);
functions.add(Identifier::from("end-column"), stdlib::syntax::EndColumn);
functions.add(Identifier::from("node-type"), stdlib::syntax::NodeType);
functions.add(
Identifier::from("named-child-count"),
stdlib::syntax::NamedChildCount,
);
functions.add(Identifier::from("node"), stdlib::graph::Node);
functions.add(Identifier::from("not"), stdlib::bool::Not);
functions.add(Identifier::from("and"), stdlib::bool::And);
functions.add(Identifier::from("or"), stdlib::bool::Or);
functions.add(Identifier::from("plus"), stdlib::math::Plus);
functions.add(Identifier::from("replace"), stdlib::string::Replace);
functions.add(Identifier::from("is-empty"), stdlib::list::IsEmpty);
functions.add(Identifier::from("length"), stdlib::list::Length);
functions
}
pub fn add<F>(&mut self, name: Identifier, function: F)
where
F: Function + 'static,
{
self.functions.insert(name, Box::new(function));
}
pub fn call(
&mut self,
name: &Identifier,
graph: &mut Graph,
source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let function = self
.functions
.get_mut(name)
.ok_or(ExecutionError::UndefinedFunction(format!("{}", name)))?;
function.call(graph, source, parameters)
}
}
pub mod stdlib {
use anyhow::anyhow;
use regex::Regex;
use crate::execution::ExecutionError;
use crate::graph::Graph;
use crate::graph::Value;
use super::Function;
use super::Parameters;
pub struct IsNull;
impl Function for IsNull {
fn call(
&mut self,
_graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let parameter = parameters.param()?;
parameters.finish()?;
let result = if let Value::Null = parameter {
true
} else {
false
};
Ok(result.into())
}
}
pub mod syntax {
use super::*;
pub struct NamedChildIndex;
impl Function for NamedChildIndex {
fn call(
&mut self,
graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let node = graph[parameters.param()?.into_syntax_node_ref()?];
parameters.finish()?;
let parent = match node.parent() {
Some(parent) => parent,
None => {
return Err(anyhow!("Cannot call named-child-index on the root node").into())
}
};
let mut tree_cursor = parent.walk();
let index = parent
.named_children(&mut tree_cursor)
.position(|child| child == node)
.ok_or(anyhow!("Called named-child-index on a non-named child"))?;
Ok(Value::Integer(index as u32))
}
}
pub struct SourceText;
impl Function for SourceText {
fn call(
&mut self,
graph: &mut Graph,
source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let node = graph[parameters.param()?.into_syntax_node_ref()?];
parameters.finish()?;
Ok(Value::String(source[node.byte_range()].to_string()))
}
}
pub struct StartRow;
impl Function for StartRow {
fn call(
&mut self,
graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let node = graph[parameters.param()?.into_syntax_node_ref()?];
parameters.finish()?;
Ok(Value::Integer(node.start_position().row as u32))
}
}
pub struct StartColumn;
impl Function for StartColumn {
fn call(
&mut self,
graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let node = graph[parameters.param()?.into_syntax_node_ref()?];
parameters.finish()?;
Ok(Value::Integer(node.start_position().column as u32))
}
}
pub struct EndRow;
impl Function for EndRow {
fn call(
&mut self,
graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let node = graph[parameters.param()?.into_syntax_node_ref()?];
parameters.finish()?;
Ok(Value::Integer(node.end_position().row as u32))
}
}
pub struct EndColumn;
impl Function for EndColumn {
fn call(
&mut self,
graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let node = graph[parameters.param()?.into_syntax_node_ref()?];
parameters.finish()?;
Ok(Value::Integer(node.end_position().column as u32))
}
}
pub struct NodeType;
impl Function for NodeType {
fn call(
&mut self,
graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let node = graph[parameters.param()?.into_syntax_node_ref()?];
parameters.finish()?;
Ok(Value::String(node.kind().to_string()))
}
}
pub struct NamedChildCount;
impl Function for NamedChildCount {
fn call(
&mut self,
graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let node = graph[parameters.param()?.into_syntax_node_ref()?];
parameters.finish()?;
Ok(Value::Integer(node.named_child_count() as u32))
}
}
}
pub mod graph {
use super::*;
pub struct Node;
impl Function for Node {
fn call(
&mut self,
graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
parameters.finish()?;
let node = graph.add_graph_node();
Ok(Value::GraphNode(node))
}
}
}
pub mod bool {
use super::*;
pub struct Not;
impl Function for Not {
fn call(
&mut self,
_graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let result = !parameters.param()?.as_boolean()?;
parameters.finish()?;
Ok(result.into())
}
}
pub struct And;
impl Function for And {
fn call(
&mut self,
_graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let mut result = true;
while let Ok(parameter) = parameters.param() {
result &= parameter.as_boolean()?;
}
Ok(result.into())
}
}
pub struct Or;
impl Function for Or {
fn call(
&mut self,
_graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let mut result = false;
while let Ok(parameter) = parameters.param() {
result |= parameter.as_boolean()?;
}
Ok(result.into())
}
}
}
pub mod math {
use super::*;
pub struct Plus;
impl Function for Plus {
fn call(
&mut self,
_graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let mut result = 0;
while let Ok(parameter) = parameters.param() {
result += parameter.as_integer()?;
}
Ok(Value::Integer(result))
}
}
}
pub mod string {
use super::*;
pub struct Replace;
impl Function for Replace {
fn call(
&mut self,
_graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let text = parameters.param()?.into_string()?;
let pattern = parameters.param()?.into_string()?;
let pattern = Regex::new(&pattern).map_err(ExecutionError::other)?;
let replacement = parameters.param()?.into_string()?;
parameters.finish()?;
Ok(Value::String(
pattern.replace_all(&text, replacement).to_string(),
))
}
}
}
pub mod list {
use super::*;
pub struct IsEmpty;
impl Function for IsEmpty {
fn call(
&mut self,
_graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let list = parameters.param()?.into_list()?;
Ok(list.is_empty().into())
}
}
pub struct Length;
impl Function for Length {
fn call(
&mut self,
_graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let list = parameters.param()?.into_list()?;
Ok((list.len() as u32).into())
}
}
}
}