Skip to main content

arity_parser/ast/
expr.rs

1//! The [`Expr`] union over "any R expression", and the [`HasArgList`] shared
2//! trait for the argument-bearing nodes.
3//!
4//! R's leaf expressions are **bare tokens**, not `LITERAL` nodes (`1 + 2` is
5//! `BINARY_EXPR { INT, PLUS, INT }`; a bare name is an `IDENT`). So [`Expr`]
6//! casts from a [`SyntaxElement`] (node *or* token) and carries both node
7//! variants and token-atom variants — a single `match Expr::cast(el)` then
8//! covers every operand a consumer meets, with no separate "is it an IDENT
9//! token" arm.
10
11use rowan::TextRange;
12use rowan::ast::support;
13
14use crate::ast::AstNode;
15use crate::ast::nodes::{
16    Arg, ArgList, AssignmentExpr, BinaryExpr, BlockExpr, CallExpr, ForExpr, FunctionExpr, IfExpr,
17    ParenExpr, RepeatExpr, Subset2Expr, SubsetExpr, UnaryExpr, WhileExpr,
18};
19use crate::ast::tokens::{AstToken, ComplexLit, FloatLit, Ident, IntLit, StringLit};
20use crate::syntax::{RLanguage, SyntaxElement, SyntaxKind, SyntaxNode};
21
22/// Any R expression: a compound node (`BINARY_EXPR`, `CALL_EXPR`, …) or a leaf
23/// atom token (a name or a literal).
24#[derive(Debug, Clone, PartialEq, Eq, Hash)]
25pub enum Expr {
26    Assignment(AssignmentExpr),
27    Binary(BinaryExpr),
28    Unary(UnaryExpr),
29    Paren(ParenExpr),
30    Call(CallExpr),
31    Subset(SubsetExpr),
32    Subset2(Subset2Expr),
33    If(IfExpr),
34    For(ForExpr),
35    While(WhileExpr),
36    Repeat(RepeatExpr),
37    Function(FunctionExpr),
38    Block(BlockExpr),
39    /// A bare identifier — also covers R's special constants (`TRUE`, `NA`, …);
40    /// call [`Ident::constant`] to distinguish.
41    Name(Ident),
42    IntLiteral(IntLit),
43    FloatLiteral(FloatLit),
44    StringLiteral(StringLit),
45    ComplexLiteral(ComplexLit),
46}
47
48/// Dispatch `$body` over every variant, binding the inner wrapper to `$x`.
49macro_rules! for_each_variant {
50    ($self:ident, $x:ident => $body:expr) => {
51        match $self {
52            Expr::Assignment($x) => $body,
53            Expr::Binary($x) => $body,
54            Expr::Unary($x) => $body,
55            Expr::Paren($x) => $body,
56            Expr::Call($x) => $body,
57            Expr::Subset($x) => $body,
58            Expr::Subset2($x) => $body,
59            Expr::If($x) => $body,
60            Expr::For($x) => $body,
61            Expr::While($x) => $body,
62            Expr::Repeat($x) => $body,
63            Expr::Function($x) => $body,
64            Expr::Block($x) => $body,
65            Expr::Name($x) => $body,
66            Expr::IntLiteral($x) => $body,
67            Expr::FloatLiteral($x) => $body,
68            Expr::StringLiteral($x) => $body,
69            Expr::ComplexLiteral($x) => $body,
70        }
71    };
72}
73
74impl Expr {
75    /// Cast a CST element to an expression. A node dispatches on its kind; a
76    /// token becomes an atom variant (name or literal). Trivia, operators,
77    /// keywords, and punctuation are not expressions and yield `None`.
78    pub fn cast(element: SyntaxElement) -> Option<Expr> {
79        match element {
80            SyntaxElement::Node(node) => Self::cast_node(node),
81            SyntaxElement::Token(token) => Some(match token.kind() {
82                SyntaxKind::IDENT => Expr::Name(Ident::cast(token)?),
83                SyntaxKind::INT => Expr::IntLiteral(IntLit::cast(token)?),
84                SyntaxKind::FLOAT => Expr::FloatLiteral(FloatLit::cast(token)?),
85                SyntaxKind::STRING => Expr::StringLiteral(StringLit::cast(token)?),
86                SyntaxKind::COMPLEX => Expr::ComplexLiteral(ComplexLit::cast(token)?),
87                _ => return None,
88            }),
89        }
90    }
91
92    /// Cast a node to a compound expression. `None` for a non-expression node
93    /// (e.g. `ARG_LIST`, `ROOT`) — a leaf atom is a token, so use [`Expr::cast`]
94    /// when the element may be either.
95    pub fn cast_node(node: SyntaxNode) -> Option<Expr> {
96        Some(match node.kind() {
97            SyntaxKind::ASSIGNMENT_EXPR => Expr::Assignment(AssignmentExpr::cast(node)?),
98            SyntaxKind::BINARY_EXPR => Expr::Binary(BinaryExpr::cast(node)?),
99            SyntaxKind::UNARY_EXPR => Expr::Unary(UnaryExpr::cast(node)?),
100            SyntaxKind::PAREN_EXPR => Expr::Paren(ParenExpr::cast(node)?),
101            SyntaxKind::CALL_EXPR => Expr::Call(CallExpr::cast(node)?),
102            SyntaxKind::SUBSET_EXPR => Expr::Subset(SubsetExpr::cast(node)?),
103            SyntaxKind::SUBSET2_EXPR => Expr::Subset2(Subset2Expr::cast(node)?),
104            SyntaxKind::IF_EXPR => Expr::If(IfExpr::cast(node)?),
105            SyntaxKind::FOR_EXPR => Expr::For(ForExpr::cast(node)?),
106            SyntaxKind::WHILE_EXPR => Expr::While(WhileExpr::cast(node)?),
107            SyntaxKind::REPEAT_EXPR => Expr::Repeat(RepeatExpr::cast(node)?),
108            SyntaxKind::FUNCTION_EXPR => Expr::Function(FunctionExpr::cast(node)?),
109            SyntaxKind::BLOCK_EXPR => Expr::Block(BlockExpr::cast(node)?),
110            _ => return None,
111        })
112    }
113
114    /// The underlying CST element (a node for compound expressions, a token for
115    /// atoms).
116    pub fn syntax(&self) -> SyntaxElement {
117        for_each_variant!(self, x => x.syntax().clone().into())
118    }
119
120    /// The source range of the expression.
121    pub fn text_range(&self) -> TextRange {
122        self.syntax().text_range()
123    }
124
125    /// Whether this is a primary/atomic expression — one that can be prefixed
126    /// with `!` (or dropped) without reparenthesizing. Namely a name, a literal,
127    /// a call, a parenthesized expression, or an index (`[`/`[[`). This is the
128    /// guard a negating rewrite (`x == FALSE` → `!x`) needs to stay correct.
129    pub fn is_atom(&self) -> bool {
130        matches!(
131            self,
132            Expr::Name(_)
133                | Expr::IntLiteral(_)
134                | Expr::FloatLiteral(_)
135                | Expr::StringLiteral(_)
136                | Expr::ComplexLiteral(_)
137                | Expr::Call(_)
138                | Expr::Paren(_)
139                | Expr::Subset(_)
140                | Expr::Subset2(_)
141        )
142    }
143}
144
145/// A node that owns an argument list: `CALL_EXPR` (`f(…)`), `SUBSET_EXPR`
146/// (`x[…]`), and `SUBSET2_EXPR` (`x[[…]]`).
147pub trait HasArgList: AstNode<Language = RLanguage> {
148    /// The `ARG_LIST` child, if present.
149    fn arg_list(&self) -> Option<ArgList> {
150        support::child(self.syntax())
151    }
152
153    /// The arguments, each split into optional name and value via [`Arg`].
154    fn args(&self) -> impl Iterator<Item = Arg> {
155        self.arg_list()
156            .map(|list| list.args().collect::<Vec<_>>())
157            .unwrap_or_default()
158            .into_iter()
159    }
160
161    /// The value of the `n`th positional (unnamed) argument, 0-indexed.
162    fn nth_positional(&self, n: usize) -> Option<SyntaxElement> {
163        self.args()
164            .filter(|arg| !arg.is_named())
165            .nth(n)
166            .and_then(|arg| arg.value())
167    }
168
169    /// The value of the argument named `name`, if present.
170    fn named_arg(&self, name: &str) -> Option<SyntaxElement> {
171        self.args()
172            .find(|arg| arg.name().as_deref() == Some(name))
173            .and_then(|arg| arg.value())
174    }
175}
176
177impl HasArgList for CallExpr {}
178impl HasArgList for SubsetExpr {}
179impl HasArgList for Subset2Expr {}