Skip to main content

r/parser/
ast.rs

1/// Source span — byte offsets into the source text.
2/// Used to resolve file:line info for stack traces.
3///
4/// `PartialEq` always returns true — spans are metadata for diagnostics
5/// and must not affect semantic comparisons (e.g., `identical()` on language objects).
6#[derive(Debug, Clone, Copy)]
7pub struct Span {
8    /// Byte offset of the start of this expression in the source.
9    pub start: u32,
10    /// Byte offset of the end of this expression.
11    pub end: u32,
12}
13
14impl PartialEq for Span {
15    fn eq(&self, _other: &Self) -> bool {
16        true // Spans are metadata — never affect semantic equality
17    }
18}
19
20impl Eq for Span {}
21
22/// AST node types for the R language
23
24#[derive(Debug, Clone, PartialEq)]
25pub enum Expr {
26    /// NULL literal
27    Null,
28    /// NA (with optional type)
29    Na(NaType),
30    /// Inf
31    Inf,
32    /// NaN
33    NaN,
34    /// Boolean literal
35    Bool(bool),
36    /// Integer literal
37    Integer(i64),
38    /// Double/float literal
39    Double(f64),
40    /// Complex literal (imaginary part only, e.g. 2i)
41    Complex(f64),
42    /// String literal
43    String(String),
44    /// Identifier/symbol
45    Symbol(String),
46    /// ... (dots)
47    Dots,
48    /// ..1, ..2, etc.
49    DotDot(u32),
50
51    /// Unary operation
52    UnaryOp { op: UnaryOp, operand: Box<Expr> },
53    /// Binary operation
54    BinaryOp {
55        op: BinaryOp,
56        lhs: Box<Expr>,
57        rhs: Box<Expr>,
58    },
59    /// Assignment
60    Assign {
61        op: AssignOp,
62        target: Box<Expr>,
63        value: Box<Expr>,
64    },
65
66    /// Function call
67    Call {
68        func: Box<Expr>,
69        args: Vec<Arg>,
70        span: Option<Span>,
71    },
72    /// Single bracket indexing: x[i]
73    Index {
74        object: Box<Expr>,
75        indices: Vec<Arg>,
76    },
77    /// Double bracket indexing: x[[i]]
78    IndexDouble {
79        object: Box<Expr>,
80        indices: Vec<Arg>,
81    },
82    /// Dollar access: x$name
83    Dollar { object: Box<Expr>, member: String },
84    /// Slot access: x@slot
85    Slot { object: Box<Expr>, member: String },
86    /// Namespace access: pkg::name
87    NsGet { namespace: Box<Expr>, name: String },
88    /// Internal namespace access: pkg:::name
89    NsGetInt { namespace: Box<Expr>, name: String },
90
91    /// Formula: ~ expr or lhs ~ rhs
92    Formula {
93        lhs: Option<Box<Expr>>,
94        rhs: Option<Box<Expr>>,
95    },
96
97    /// if/else expression
98    If {
99        condition: Box<Expr>,
100        then_body: Box<Expr>,
101        else_body: Option<Box<Expr>>,
102    },
103    /// for loop
104    For {
105        var: String,
106        iter: Box<Expr>,
107        body: Box<Expr>,
108    },
109    /// while loop
110    While {
111        condition: Box<Expr>,
112        body: Box<Expr>,
113    },
114    /// repeat loop
115    Repeat { body: Box<Expr> },
116    /// break
117    Break,
118    /// next (continue)
119    Next,
120    /// return
121    Return(Option<Box<Expr>>),
122
123    /// Block (curly braces)
124    Block(Vec<Expr>),
125
126    /// Function definition
127    Function { params: Vec<Param>, body: Box<Expr> },
128
129    /// Program (sequence of top-level expressions)
130    Program(Vec<Expr>),
131}
132
133#[derive(Debug, Clone, PartialEq)]
134pub struct Arg {
135    pub name: Option<String>,
136    pub value: Option<Expr>,
137}
138
139#[derive(Debug, Clone, PartialEq)]
140pub struct Param {
141    pub name: String,
142    pub default: Option<Expr>,
143    pub is_dots: bool,
144}
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147pub enum NaType {
148    Logical,
149    Integer,
150    Real,
151    Character,
152    Complex,
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum UnaryOp {
157    Neg,
158    Pos,
159    Not,
160    #[allow(dead_code)]
161    Formula,
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
165pub enum BinaryOp {
166    Add,
167    Sub,
168    Mul,
169    Div,
170    Pow,
171    Mod,
172    IntDiv,
173    Eq,
174    Ne,
175    Lt,
176    Gt,
177    Le,
178    Ge,
179    And,
180    AndScalar,
181    Or,
182    OrScalar,
183    Range,
184    Pipe,
185    AssignPipe, // %<>% — pipe and assign back to LHS
186    TeePipe,    // %T>% — pipe for side effect, return LHS
187    ExpoPipe,   // %$%  — expose LHS names to RHS
188    Special(SpecialOp),
189    #[allow(dead_code)]
190    Tilde,
191    #[allow(dead_code)]
192    DoubleTilde,
193}
194
195impl BinaryOp {
196    /// Returns true for comparison operators (==, !=, <, >, <=, >=).
197    pub fn is_comparison(&self) -> bool {
198        matches!(
199            self,
200            BinaryOp::Eq | BinaryOp::Ne | BinaryOp::Lt | BinaryOp::Gt | BinaryOp::Le | BinaryOp::Ge
201        )
202    }
203}
204
205#[derive(Debug, Clone, PartialEq, Eq)]
206pub enum SpecialOp {
207    In,
208    MatMul,
209    Kronecker,
210    Walrus,
211    /// User-defined `%op%` operator, carrying the full name (e.g. `%||%`).
212    Other(String),
213}
214
215#[derive(Debug, Clone, Copy, PartialEq, Eq)]
216pub enum AssignOp {
217    LeftAssign,       // <-
218    SuperAssign,      // <<-
219    Equals,           // =
220    RightAssign,      // ->
221    RightSuperAssign, // ->>
222}