Rust · 9609 bytes Raw Blame History
1 //! Expression AST nodes.
2 //!
3 //! Represents all Fortran expression forms: literals, names, operators,
4 //! function calls, array constructors, component access, and more.
5
6 /// A Fortran expression.
7 #[derive(Debug, Clone, PartialEq)]
8 #[allow(clippy::enum_variant_names)]
9 pub enum Expr {
10 // ---- Literals ----
11 /// Integer literal: `42`, `42_8`, `42_int64`
12 IntegerLiteral { text: String, kind: Option<String> },
13 /// Real literal: `3.14`, `1.0d0`, `6.022e23`, `1.0_8`, `.5`, `5.`
14 RealLiteral { text: String, kind: Option<String> },
15 /// String literal: `'hello'`, `"hello"`, `'it''s'`
16 StringLiteral { value: String, kind: Option<String> },
17 /// Logical literal: `.true.`, `.false.`, `.true._4`
18 LogicalLiteral { value: bool, kind: Option<String> },
19 /// Complex literal: `(1.0, 2.0)`
20 ComplexLiteral {
21 real: Box<SpannedExpr>,
22 imag: Box<SpannedExpr>,
23 },
24 /// BOZ literal: `B'1010'`, `O'777'`, `Z'FF'`
25 BozLiteral { text: String, base: BozBase },
26
27 // ---- Names and access ----
28 /// Simple name (variable, function, type, etc.)
29 Name { name: String },
30 /// Component access: `x%field`, `x%inner%deep`
31 ComponentAccess {
32 base: Box<SpannedExpr>,
33 component: String,
34 },
35
36 // ---- Operations ----
37 /// Unary operation: `-x`, `.not. x`, `+x`
38 UnaryOp {
39 op: UnaryOp,
40 operand: Box<SpannedExpr>,
41 },
42 /// Binary operation: `a + b`, `x .and. y`, `s // t`
43 BinaryOp {
44 op: BinaryOp,
45 left: Box<SpannedExpr>,
46 right: Box<SpannedExpr>,
47 },
48
49 // ---- Calls and subscripts ----
50 // Note: A(I) is ambiguous — could be array access, function call, or substring.
51 // The parser produces FunctionCall for all of them; sema disambiguates.
52 /// Function call or array access: `sin(x)`, `a(i,j)`, `s(1:5)`
53 FunctionCall {
54 callee: Box<SpannedExpr>,
55 args: Vec<Argument>,
56 },
57
58 // ---- Array constructors ----
59 /// Array constructor: `[1, 2, 3]` or `(/ 1, 2, 3 /)`
60 ArrayConstructor {
61 type_spec: Option<String>, // [integer :: 1, 2, 3]
62 values: Vec<AcValue>,
63 },
64
65 // ---- Parenthesized ----
66 /// Parenthesized expression: `(x + y)`
67 ParenExpr { inner: Box<SpannedExpr> },
68 }
69
70 /// An expression with source location.
71 pub type SpannedExpr = super::Spanned<Expr>;
72
73 impl SpannedExpr {
74 /// Pretty-print the expression with full parenthesization for testing.
75 pub fn to_sexpr(&self) -> String {
76 match &self.node {
77 Expr::IntegerLiteral { text, .. } => text.clone(),
78 Expr::RealLiteral { text, .. } => text.clone(),
79 Expr::StringLiteral { value, .. } => format!("'{}'", value),
80 Expr::LogicalLiteral { value, .. } => {
81 if *value {
82 ".true.".into()
83 } else {
84 ".false.".into()
85 }
86 }
87 Expr::ComplexLiteral { real, imag } => {
88 format!("({}, {})", real.to_sexpr(), imag.to_sexpr())
89 }
90 Expr::BozLiteral { text, .. } => text.clone(),
91 Expr::Name { name } => name.clone(),
92 Expr::ComponentAccess { base, component } => {
93 format!("{}%{}", base.to_sexpr(), component)
94 }
95 Expr::UnaryOp { op, operand } => {
96 format!("({} {})", op, operand.to_sexpr())
97 }
98 Expr::BinaryOp { op, left, right } => {
99 format!("({} {} {})", left.to_sexpr(), op, right.to_sexpr())
100 }
101 Expr::FunctionCall { callee, args } => {
102 let args_str: Vec<String> = args
103 .iter()
104 .map(|a| {
105 if let Some(kw) = &a.keyword {
106 format!("{}={}", kw, a.value.to_sexpr())
107 } else {
108 a.value.to_sexpr()
109 }
110 })
111 .collect();
112 format!("{}({})", callee.to_sexpr(), args_str.join(", "))
113 }
114 Expr::ArrayConstructor { values, type_spec } => {
115 let vals: Vec<String> = values
116 .iter()
117 .map(|v| match v {
118 AcValue::Expr(e) => e.to_sexpr(),
119 AcValue::ImpliedDo(ido) => {
120 let vals: Vec<String> = ido
121 .values
122 .iter()
123 .map(|v| match v {
124 AcValue::Expr(e) => e.to_sexpr(),
125 AcValue::ImpliedDo(_) => "(nested-implied-do)".into(),
126 })
127 .collect();
128 let step_str = ido
129 .step
130 .as_ref()
131 .map_or(String::new(), |s| format!(", {}", s.to_sexpr()));
132 format!(
133 "({}, {}={}, {}{})",
134 vals.join(", "),
135 ido.var,
136 ido.start.to_sexpr(),
137 ido.end.to_sexpr(),
138 step_str
139 )
140 }
141 })
142 .collect();
143 if let Some(ts) = type_spec {
144 format!("[{} :: {}]", ts, vals.join(", "))
145 } else {
146 format!("[{}]", vals.join(", "))
147 }
148 }
149 Expr::ParenExpr { inner } => {
150 format!("({})", inner.to_sexpr())
151 }
152 }
153 }
154 }
155
156 /// BOZ literal base.
157 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
158 pub enum BozBase {
159 Binary,
160 Octal,
161 Hex,
162 }
163
164 /// Unary operators.
165 #[derive(Debug, Clone, PartialEq, Eq)]
166 pub enum UnaryOp {
167 Plus, // +
168 Minus, // -
169 Not, // .not.
170 Defined(String), // .myop.
171 }
172
173 impl std::fmt::Display for UnaryOp {
174 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175 match self {
176 UnaryOp::Plus => write!(f, "+"),
177 UnaryOp::Minus => write!(f, "-"),
178 UnaryOp::Not => write!(f, ".not."),
179 UnaryOp::Defined(s) => write!(f, ".{}.", s),
180 }
181 }
182 }
183
184 /// Binary operators.
185 #[derive(Debug, Clone, PartialEq, Eq)]
186 pub enum BinaryOp {
187 // Arithmetic
188 Add, // +
189 Sub, // -
190 Mul, // *
191 Div, // /
192 Pow, // **
193
194 // String
195 Concat, // //
196
197 // Comparison
198 Eq, // == or .eq.
199 Ne, // /= or .ne.
200 Lt, // < or .lt.
201 Le, // <= or .le.
202 Gt, // > or .gt.
203 Ge, // >= or .ge.
204
205 // Logical
206 And, // .and.
207 Or, // .or.
208 Eqv, // .eqv.
209 Neqv, // .neqv.
210
211 // User-defined
212 Defined(String), // .myop.
213 }
214
215 impl std::fmt::Display for BinaryOp {
216 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217 match self {
218 BinaryOp::Add => write!(f, "+"),
219 BinaryOp::Sub => write!(f, "-"),
220 BinaryOp::Mul => write!(f, "*"),
221 BinaryOp::Div => write!(f, "/"),
222 BinaryOp::Pow => write!(f, "**"),
223 BinaryOp::Concat => write!(f, "//"),
224 BinaryOp::Eq => write!(f, "=="),
225 BinaryOp::Ne => write!(f, "/="),
226 BinaryOp::Lt => write!(f, "<"),
227 BinaryOp::Le => write!(f, "<="),
228 BinaryOp::Gt => write!(f, ">"),
229 BinaryOp::Ge => write!(f, ">="),
230 BinaryOp::And => write!(f, ".and."),
231 BinaryOp::Or => write!(f, ".or."),
232 BinaryOp::Eqv => write!(f, ".eqv."),
233 BinaryOp::Neqv => write!(f, ".neqv."),
234 BinaryOp::Defined(s) => write!(f, ".{}.", s),
235 }
236 }
237 }
238
239 /// A function/subroutine argument (positional or keyword).
240 /// The value is a SectionSubscript to support both plain expressions
241 /// and range subscripts like a(1:5) or a(1:10:2).
242 #[derive(Debug, Clone, PartialEq)]
243 pub struct Argument {
244 pub keyword: Option<String>,
245 pub value: SectionSubscript,
246 }
247
248 /// Array constructor value — either an expression or an implied-do loop.
249 #[derive(Debug, Clone, PartialEq)]
250 pub enum AcValue {
251 Expr(SpannedExpr),
252 ImpliedDo(Box<ImpliedDoLoop>),
253 }
254
255 /// Implied-do loop contents (boxed to shrink AcValue from ~288 to ~16 bytes).
256 #[derive(Debug, Clone, PartialEq)]
257 pub struct ImpliedDoLoop {
258 pub values: Vec<AcValue>,
259 pub var: String,
260 pub start: SpannedExpr,
261 pub end: SpannedExpr,
262 pub step: Option<SpannedExpr>,
263 }
264
265 /// Section subscript — either a single element or a range (for array sections/substrings).
266 #[derive(Debug, Clone, PartialEq)]
267 pub enum SectionSubscript {
268 Element(SpannedExpr),
269 Range {
270 start: Option<SpannedExpr>,
271 end: Option<SpannedExpr>,
272 stride: Option<SpannedExpr>,
273 },
274 }
275
276 impl SectionSubscript {
277 pub fn to_sexpr(&self) -> String {
278 match self {
279 SectionSubscript::Element(e) => e.to_sexpr(),
280 SectionSubscript::Range { start, end, stride } => {
281 let s = start.as_ref().map_or(String::new(), |e| e.to_sexpr());
282 let e = end.as_ref().map_or(String::new(), |e| e.to_sexpr());
283 if let Some(st) = stride {
284 format!("{}:{}:{}", s, e, st.to_sexpr())
285 } else {
286 format!("{}:{}", s, e)
287 }
288 }
289 }
290 }
291 }
292