use std::collections::HashMap;
use std::sync::Arc;
use crate::execution::error::ExecutionError;
use crate::graph::Graph;
use crate::graph::Value;
use crate::Identifier;
pub trait Function {
fn call(
&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, Arc<dyn Function + Send + Sync>>,
}
impl Functions {
pub fn new() -> Functions {
Functions::default()
}
pub fn stdlib() -> Functions {
let mut functions = Functions::new();
functions.add(Identifier::from("eq"), stdlib::Eq);
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("format"), stdlib::string::Format);
functions.add(Identifier::from("replace"), stdlib::string::Replace);
functions.add(Identifier::from("concat"), stdlib::list::Concat);
functions.add(Identifier::from("is-empty"), stdlib::list::IsEmpty);
functions.add(Identifier::from("join"), stdlib::list::Join);
functions.add(Identifier::from("length"), stdlib::list::Length);
functions
}
pub fn add<F>(&mut self, name: Identifier, function: F)
where
F: Function + Send + Sync + 'static,
{
self.functions.insert(name, Arc::new(function));
}
pub fn call(
&self,
name: &Identifier,
graph: &mut Graph,
source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let function = self
.functions
.get(name)
.ok_or(ExecutionError::UndefinedFunction(format!("{}", name)))?;
function.call(graph, source, parameters)
}
}
pub mod stdlib {
use regex::Regex;
use crate::execution::error::ExecutionError;
use crate::graph::Graph;
use crate::graph::Value;
use super::Function;
use super::Parameters;
pub struct Eq;
impl Function for Eq {
fn call(
&self,
_graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let left = parameters.param()?;
let right = parameters.param()?;
parameters.finish()?;
match &left {
Value::Null => match right {
Value::Null => return Ok(true.into()),
_ => return Ok(false.into()),
},
Value::Boolean(left) => match &right {
Value::Null => return Ok(false.into()),
Value::Boolean(right) => return Ok((left == right).into()),
_ => {}
},
Value::Integer(left) => match &right {
Value::Null => return Ok(false.into()),
Value::Integer(right) => return Ok((left == right).into()),
_ => {}
},
Value::String(left) => match &right {
Value::Null => return Ok(false.into()),
Value::String(right) => return Ok((left == right).into()),
_ => {}
},
Value::List(left) => match &right {
Value::Null => return Ok(false.into()),
Value::List(right) => return Ok((left == right).into()),
_ => {}
},
Value::Set(left) => match &right {
Value::Null => return Ok(false.into()),
Value::Set(right) => return Ok((left == right).into()),
_ => {}
},
Value::SyntaxNode(left) => match &right {
Value::Null => return Ok(false.into()),
Value::SyntaxNode(right) => return Ok((left == right).into()),
_ => {}
},
Value::GraphNode(left) => match &right {
Value::Null => return Ok(false.into()),
Value::GraphNode(right) => return Ok((left == right).into()),
_ => {}
},
};
Err(ExecutionError::FunctionFailed(
"eq".into(),
format!(
"Cannot compare values of different types: {} and {}",
left, right
),
))
}
}
pub struct IsNull;
impl Function for IsNull {
fn call(
&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(
&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(ExecutionError::FunctionFailed(
"named-child-index".into(),
format!("Cannot call named-child-index on the root node"),
))
}
};
let mut tree_cursor = parent.walk();
let index = parent
.named_children(&mut tree_cursor)
.position(|child| child == node)
.ok_or(ExecutionError::FunctionFailed(
"named-child-index".into(),
format!("Called named-child-index on a non-named child"),
))?;
Ok(Value::Integer(index as u32))
}
}
pub struct SourceText;
impl Function for SourceText {
fn call(
&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(
&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(
&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(
&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(
&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(
&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(
&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(
&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(
&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(
&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(
&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(
&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 Format;
impl Function for Format {
fn call(
&self,
_graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let format = parameters.param()?.into_string()?;
let mut result = String::new();
let mut it = format.chars().enumerate().into_iter();
while let Some((_, c)) = it.next() {
match c {
'{' => match it.next() {
Some((_, '{')) => result.push('{'),
Some((_, '}')) => {
let value = parameters.param()?;
result += &value.to_string();
},
Some((i, c)) => return Err(ExecutionError::FunctionFailed("format".into(), format!("Unexpected character `{}` after `{{` at position {} in format string `{}`. Expected `{{` or `}}`.", c, i + 1, format))),
None => return Err(ExecutionError::FunctionFailed("format".into(), format!("Unexpected end of format string `{}` after `{{`. Expected `{{` or `}}`.", format))),
},
'}' => match it.next() {
Some((_, '}')) => result.push('}'),
Some((i, c)) => return Err(ExecutionError::FunctionFailed("format".into(), format!("Unexpected character `{}` after `}}` at position {} in format string `{}`. Expected `}}`.", c, i + 1, format))),
None => return Err(ExecutionError::FunctionFailed("format".into(), format!("Unexpected end of format string `{}` after `{{. Expected `}}`.", format))),
},
c => result.push(c),
}
}
parameters.finish()?;
Ok(result.into())
}
}
pub struct Replace;
impl Function for Replace {
fn call(
&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(|e| {
ExecutionError::FunctionFailed("replace".into(), format!("{}", e))
})?;
let replacement = parameters.param()?.into_string()?;
parameters.finish()?;
Ok(Value::String(
pattern.replace_all(&text, replacement.as_str()).to_string(),
))
}
}
}
pub mod list {
use super::*;
pub struct Concat;
impl Function for Concat {
fn call(
&self,
_graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let mut result = Vec::new();
while let Ok(list) = parameters.param() {
result.append(&mut list.into_list()?);
}
Ok(result.into())
}
}
pub struct IsEmpty;
impl Function for IsEmpty {
fn call(
&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 Join;
impl Function for Join {
fn call(
&self,
_graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let list = parameters.param()?.into_list()?;
let sep = match parameters.param() {
Ok(sep) => sep.into_string()?,
Err(_) => "".to_string(),
};
parameters.finish()?;
let result = list
.into_iter()
.map(|x| format!("{}", x))
.collect::<Vec<_>>()
.join(&sep);
Ok(result.into())
}
}
pub struct Length;
impl Function for Length {
fn call(
&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())
}
}
}
}