use rowan::TextRange;
use rowan::ast::support;
use crate::ast::AstNode;
use crate::ast::nodes::{
Arg, ArgList, AssignmentExpr, BinaryExpr, BlockExpr, CallExpr, ForExpr, FunctionExpr, IfExpr,
ParenExpr, RepeatExpr, Subset2Expr, SubsetExpr, UnaryExpr, WhileExpr,
};
use crate::ast::tokens::{AstToken, ComplexLit, FloatLit, Ident, IntLit, StringLit};
use crate::syntax::{RLanguage, SyntaxElement, SyntaxKind, SyntaxNode};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Expr {
Assignment(AssignmentExpr),
Binary(BinaryExpr),
Unary(UnaryExpr),
Paren(ParenExpr),
Call(CallExpr),
Subset(SubsetExpr),
Subset2(Subset2Expr),
If(IfExpr),
For(ForExpr),
While(WhileExpr),
Repeat(RepeatExpr),
Function(FunctionExpr),
Block(BlockExpr),
Name(Ident),
IntLiteral(IntLit),
FloatLiteral(FloatLit),
StringLiteral(StringLit),
ComplexLiteral(ComplexLit),
}
macro_rules! for_each_variant {
($self:ident, $x:ident => $body:expr) => {
match $self {
Expr::Assignment($x) => $body,
Expr::Binary($x) => $body,
Expr::Unary($x) => $body,
Expr::Paren($x) => $body,
Expr::Call($x) => $body,
Expr::Subset($x) => $body,
Expr::Subset2($x) => $body,
Expr::If($x) => $body,
Expr::For($x) => $body,
Expr::While($x) => $body,
Expr::Repeat($x) => $body,
Expr::Function($x) => $body,
Expr::Block($x) => $body,
Expr::Name($x) => $body,
Expr::IntLiteral($x) => $body,
Expr::FloatLiteral($x) => $body,
Expr::StringLiteral($x) => $body,
Expr::ComplexLiteral($x) => $body,
}
};
}
impl Expr {
pub fn cast(element: SyntaxElement) -> Option<Expr> {
match element {
SyntaxElement::Node(node) => Self::cast_node(node),
SyntaxElement::Token(token) => Some(match token.kind() {
SyntaxKind::IDENT => Expr::Name(Ident::cast(token)?),
SyntaxKind::INT => Expr::IntLiteral(IntLit::cast(token)?),
SyntaxKind::FLOAT => Expr::FloatLiteral(FloatLit::cast(token)?),
SyntaxKind::STRING => Expr::StringLiteral(StringLit::cast(token)?),
SyntaxKind::COMPLEX => Expr::ComplexLiteral(ComplexLit::cast(token)?),
_ => return None,
}),
}
}
pub fn cast_node(node: SyntaxNode) -> Option<Expr> {
Some(match node.kind() {
SyntaxKind::ASSIGNMENT_EXPR => Expr::Assignment(AssignmentExpr::cast(node)?),
SyntaxKind::BINARY_EXPR => Expr::Binary(BinaryExpr::cast(node)?),
SyntaxKind::UNARY_EXPR => Expr::Unary(UnaryExpr::cast(node)?),
SyntaxKind::PAREN_EXPR => Expr::Paren(ParenExpr::cast(node)?),
SyntaxKind::CALL_EXPR => Expr::Call(CallExpr::cast(node)?),
SyntaxKind::SUBSET_EXPR => Expr::Subset(SubsetExpr::cast(node)?),
SyntaxKind::SUBSET2_EXPR => Expr::Subset2(Subset2Expr::cast(node)?),
SyntaxKind::IF_EXPR => Expr::If(IfExpr::cast(node)?),
SyntaxKind::FOR_EXPR => Expr::For(ForExpr::cast(node)?),
SyntaxKind::WHILE_EXPR => Expr::While(WhileExpr::cast(node)?),
SyntaxKind::REPEAT_EXPR => Expr::Repeat(RepeatExpr::cast(node)?),
SyntaxKind::FUNCTION_EXPR => Expr::Function(FunctionExpr::cast(node)?),
SyntaxKind::BLOCK_EXPR => Expr::Block(BlockExpr::cast(node)?),
_ => return None,
})
}
pub fn syntax(&self) -> SyntaxElement {
for_each_variant!(self, x => x.syntax().clone().into())
}
pub fn text_range(&self) -> TextRange {
self.syntax().text_range()
}
pub fn is_atom(&self) -> bool {
matches!(
self,
Expr::Name(_)
| Expr::IntLiteral(_)
| Expr::FloatLiteral(_)
| Expr::StringLiteral(_)
| Expr::ComplexLiteral(_)
| Expr::Call(_)
| Expr::Paren(_)
| Expr::Subset(_)
| Expr::Subset2(_)
)
}
}
pub trait HasArgList: AstNode<Language = RLanguage> {
fn arg_list(&self) -> Option<ArgList> {
support::child(self.syntax())
}
fn args(&self) -> impl Iterator<Item = Arg> {
self.arg_list()
.map(|list| list.args().collect::<Vec<_>>())
.unwrap_or_default()
.into_iter()
}
fn nth_positional(&self, n: usize) -> Option<SyntaxElement> {
self.args()
.filter(|arg| !arg.is_named())
.nth(n)
.and_then(|arg| arg.value())
}
fn named_arg(&self, name: &str) -> Option<SyntaxElement> {
self.args()
.find(|arg| arg.name().as_deref() == Some(name))
.and_then(|arg| arg.value())
}
}
impl HasArgList for CallExpr {}
impl HasArgList for SubsetExpr {}
impl HasArgList for Subset2Expr {}