use tree_sitter::Node;
use crate::spans::{Point, Span};
use super::from_node::{FromNode, FromNodeError};
use crate::ast;
#[derive(Debug, PartialEq, Clone)]
pub enum Command {
Generic(GenericCommand),
Fix(ast::FixDef),
Compute(ast::ComputeDef),
VariableDef(ast::VariableDef),
Shell(Span),
Error(Span),
}
impl Command {
pub fn span(&self) -> Span {
match self {
Command::Generic(cmd) => cmd.span(),
Command::Fix(cmd) => cmd.span,
Command::Compute(cmd) => cmd.span,
Command::VariableDef(cmd) => cmd.span,
Command::Shell(span) => *span,
Command::Error(span) => *span,
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct GenericCommand {
pub name: ast::Word,
pub args: Vec<ast::Argument>,
pub start: Point,
pub end: Point,
pub start_byte: usize,
pub end_byte: usize,
}
impl GenericCommand {
pub fn span(&self) -> Span {
Span {
start: self.start,
end: self.end,
}
}
}
impl FromNode for GenericCommand {
type Error = FromNodeError;
fn from_node(node: &Node, text: &str) -> Result<Self, FromNodeError> {
let mut cursor = node.walk();
let start = node.start_position();
let end = node.end_position();
let start_byte = node.start_byte();
let end_byte = node.end_byte();
let mut args = vec![];
debug_assert!(cursor.node() == *node);
if !cursor.goto_first_child() {
return Err(FromNodeError::PartialNode(
"missing command name".to_owned(),
));
}
let name = ast::Word::from_node(&cursor.node(), text)?;
while cursor.goto_next_sibling() {
for node in cursor.node().children(&mut cursor) {
args.push(ast::Argument::from_node(&node, text)?);
}
}
Ok(GenericCommand {
name,
args,
start: start.into(),
end: end.into(),
start_byte,
end_byte,
})
}
}
impl FromNode for Command {
type Error = (Self, FromNodeError);
fn from_node(node: &Node, text: &str) -> Result<Self, (Self, FromNodeError)> {
let error_span = |e| (Self::Error(node.range().into()), e);
let mut result = match node.kind() {
"fix" => Ok(Self::Fix(
ast::FixDef::from_node(node, text).map_err(error_span)?,
)),
"compute" => Ok(Self::Compute(
ast::ComputeDef::from_node(node, text).map_err(error_span)?,
)),
"variable_def" | "variable_del" => Ok(Self::VariableDef(
ast::VariableDef::from_node(node, text).map_err(error_span)?,
)),
"shell" => Ok(Self::Shell(node.range().into())),
"command" => Ok(Self::Generic(
GenericCommand::from_node(node, text).map_err(error_span)?,
)),
"ERROR" => Ok(Self::Error(node.range().into())),
#[cfg(feature = "ast_panics")]
c => panic!("unknown command kind {c}"),
_ => Ok(Self::Error(node.range().into())),
};
if let Ok(Self::Error(span)) = result {
result = match node.child(0).map(|node| node.kind()) {
Some("compute") => Ok(Self::Compute(
ast::ComputeDef::from_node(node, text).map_err(error_span)?,
)),
Some("fix_id") => Ok(Self::Fix(
ast::FixDef::from_node(node, text).map_err(error_span)?,
)),
Some("variable") => Ok(Self::VariableDef(
ast::VariableDef::from_node(node, text).map_err(error_span)?,
)),
_ => Ok(Self::Error(span)),
};
}
result
}
}