use std::collections::HashMap;
use crate::execution::ExecutionError;
use crate::graph::Graph;
use crate::graph::Value;
use crate::Context;
use crate::DisplayWithContext;
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(ctx: &mut Context) -> Functions {
let mut functions = Functions::new();
functions.add(ctx.add_identifier("child-index"), stdlib::ChildIndex);
functions.add(ctx.add_identifier("node"), stdlib::Node);
functions.add(ctx.add_identifier("plus"), stdlib::Plus);
functions.add(ctx.add_identifier("replace"), stdlib::Replace);
functions.add(ctx.add_identifier("source-text"), stdlib::SourceText);
functions.add(ctx.add_identifier("start-row"), stdlib::StartRow);
functions.add(ctx.add_identifier("start-column"), stdlib::StartColumn);
functions.add(ctx.add_identifier("end-row"), stdlib::EndRow);
functions.add(ctx.add_identifier("end-column"), stdlib::EndColumn);
functions.add(ctx.add_identifier("node-type"), stdlib::NodeType);
functions.add(
ctx.add_identifier("named-child-count"),
stdlib::NamedChildCount,
);
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,
ctx: &Context,
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.display_with(ctx)
)))?;
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 ChildIndex;
impl Function for ChildIndex {
fn call(
&mut self,
graph: &mut Graph,
_source: &str,
parameters: &mut dyn Parameters,
) -> Result<Value, ExecutionError> {
let node = parameters.param()?.into_syntax_node(graph)?;
parameters.finish()?;
let parent = match node.parent() {
Some(parent) => parent,
None => return Err(anyhow!("Cannot call 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 child-index on a non-named child"))?;
Ok(Value::Integer(index as u32))
}
}
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 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.into_integer(graph)?;
}
Ok(Value::Integer(result))
}
}
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(graph)?;
let pattern = parameters.param()?.into_string(graph)?;
let pattern = Regex::new(&pattern).map_err(ExecutionError::other)?;
let replacement = parameters.param()?.into_string(graph)?;
parameters.finish()?;
Ok(Value::String(
pattern.replace_all(&text, replacement).to_string(),
))
}
}
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 = parameters.param()?.into_syntax_node(graph)?;
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 = parameters.param()?.into_syntax_node(graph)?;
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 = parameters.param()?.into_syntax_node(graph)?;
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 = parameters.param()?.into_syntax_node(graph)?;
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 = parameters.param()?.into_syntax_node(graph)?;
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 = parameters.param()?.into_syntax_node(graph)?;
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 = parameters.param()?.into_syntax_node(graph)?;
parameters.finish()?;
Ok(Value::Integer(node.named_child_count() as u32))
}
}
}