fips_md/parser/
substitution.rs

1//! Compile time constant substitution
2
3use std::fmt::Display;
4
5use anyhow::{Result, anyhow};
6
7use crate::utils::FipsValue;
8
9use super::*;
10
11/// Helper to contain possible values for substitution 
12/// (kind of a reverse of FipsValue: this is used to represent *Rust* types given
13/// for substitution without assuming that they are also valid types in FIPS)
14#[derive(Debug, Clone)]
15pub enum SubstitutionValue {
16    /// i64 analogous to Int64 FIPS type
17    I64(i64),
18    /// f64 analogous to Double FIPS type
19    F64(f64),
20    /// usize for arrays
21    Usize(usize)
22}
23
24// Try to convert the sub
25impl Into<FipsValue> for SubstitutionValue {
26    fn into(self) -> FipsValue {
27        match self {
28            Self::I64(value) => FipsValue::Int64(value),
29            Self::F64(value) => FipsValue::Double(value),
30            Self::Usize(value) => FipsValue::Int64(value as i64),
31        }
32    }
33}
34
35impl Display for SubstitutionValue {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        match self {
38            SubstitutionValue::I64(v) => write!(f, "i64 ({})", v),
39            SubstitutionValue::F64(v) => write!(f, "f64 ({})", v),
40            SubstitutionValue::Usize(v) => write!(f, "usize ({})", v),
41        }
42    }
43}
44
45pub trait ConstantSubstitution {
46    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()>;
47}
48
49/* -- Base cases -- */
50
51impl ConstantSubstitution for CompileTimeConstant<i64> {
52    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
53        match self {
54            CompileTimeConstant::Identifier(ident_name) if ident_name == name => {
55                if let SubstitutionValue::I64(value) = value {
56                    self.substitute(*value);
57                    Ok(())
58                }
59                else {
60                    Err(anyhow!("Invalid type for substitution: expected i64, but got {}", value))
61                }
62            }
63            // TODO: might be sensible to replace existing substitutions as well
64            _ => Ok(())
65        }
66    }
67}
68
69impl ConstantSubstitution for CompileTimeConstant<f64> {
70    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
71        match self {
72            CompileTimeConstant::Identifier(ident_name) if ident_name == name => {
73                if let SubstitutionValue::F64(value) = value {
74                    self.substitute(*value);
75                    Ok(())
76                }
77                else {
78                    Err(anyhow!("Invalid type for substitution: expected f64, but got {}", value))
79                }
80            } 
81            _ => Ok(())
82        }
83    }
84}
85
86impl ConstantSubstitution for CompileTimeConstant<usize> {
87    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
88        match self {
89            CompileTimeConstant::Identifier(ident_name) if ident_name == name => {
90                if let SubstitutionValue::Usize(value) = value {
91                    self.substitute(*value);
92                    Ok(())
93                }
94                else {
95                    Err(anyhow!("Invalid type for substitution: expected usize, but got {}", value))
96                }
97            } 
98            _ => Ok(())
99        }
100    }
101}
102
103// impl ConstantSubstitution for Atom {
104//     fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
105//         match self {
106//             Atom::Literal(_) => Ok(()),
107//             Atom::Variable(variable_name) => {
108//                 if name == variable_name {
109//                     // Create new literal
110//                     let literal = match value {
111//                         SubstitutionValue::I64(value) => Literal::Int64(*value),
112//                         SubstitutionValue::Double(value) => Literal::Double(*value),
113//                         // TODO: There is no usize type in FIPS yet, so we just
114//                         // silently cast to i64. Maybe there is a better solution
115//                         SubstitutionValue::Usize(value) => Literal::Int64(*value as i64),
116//                     };
117//                     let tmp = Atom::Literal(literal);
118//                     std::mem::swap(self, &mut tmp);
119//                     todo!()
120//                 }
121//                 else {
122//                     Ok(())
123//                 }
124//             }
125//         }
126//     }
127// }
128
129/* -- General -- */
130
131impl ConstantSubstitution for FipsType {
132    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
133        // Substitution can only happen in array types
134        match self {
135            FipsType::Double | FipsType::Int64 => Ok(()),
136            FipsType::Array {typ, length} => {
137                typ.substitute_constant(name, value)?;
138                length.substitute_constant(name, value)
139            }
140        }
141    }
142}
143
144impl ConstantSubstitution for Statement {
145    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
146        match self {
147            Statement::Let(statement) => statement.substitute_constant(name, value),
148            Statement::Assign(statement) => statement.substitute_constant(name, value),
149            Statement::Update(_) => Ok(()),
150            Statement::Call(_) => Ok(())
151        }
152    }
153}
154
155impl ConstantSubstitution for LetStatement {
156    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
157        // Error if trying to shadow the constant with a local binding
158        if self.name == name {
159            return Err(anyhow!("Cannot shadow compile-time constant {} with local binding", name));
160        }
161        self.initial.substitute_constant(name, value)
162    }
163}
164
165impl ConstantSubstitution for AssignStatement {
166    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
167        // Error if trying to assign to the constant
168        if self.assignee == name {
169            return Err(anyhow!("Cannot assign to compile-time constant {}", name));
170        }
171        // TODO: Assignee substitutions once place expressions exist
172        if let Some(index) = self.index.as_mut() {
173            index.substitute_constant(name, value)?;
174        }
175        self.value.substitute_constant(name, value)
176    }
177}
178
179impl ConstantSubstitution for Expression {
180    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
181        match self {
182            // Somewhat surprisingly, atoms are not substituted here
183            // They are resolved with the other variables at a later stage
184            Expression::Atom(_) => Ok(()),
185            Expression::BinaryOperation(binop) => binop.substitute_constant(name, value),
186            Expression::FunctionCall(call) => call.substitute_constant(name, value),
187            Expression::Block(block) => block.substitute_constant(name, value),
188            Expression::Indexing(indexing) => indexing.substitute_constant(name, value),
189            Expression::AdHocArray(adhocarray) => adhocarray.substitute_constant(name, value),
190        }
191    }
192}
193
194impl ConstantSubstitution for BinaryOperation {
195    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
196        self.lhs.substitute_constant(name, value)?;
197        self.rhs.substitute_constant(name, value)
198    }
199}
200
201impl ConstantSubstitution for FunctionCall {
202    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
203        for parameter in &mut self.parameters {
204            parameter.substitute_constant(name, value)?;
205        }
206        Ok(())
207    }
208}
209
210impl ConstantSubstitution for BlockExpression {
211    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
212        for statement in &mut self.statements {
213            statement.substitute_constant(name, value)?;
214        }
215        self.expression.substitute_constant(name, value)
216    }
217}
218
219impl ConstantSubstitution for AtIndex {
220    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
221        self.index.substitute_constant(name, value)
222    }
223}
224
225impl ConstantSubstitution for AdHocArrayExpression {
226    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
227        for element in &mut self.elements {
228            element.substitute_constant(name, value)?;
229        }
230        Ok(())
231    }
232}
233
234/* -- Particles -- */
235
236impl ConstantSubstitution for Particle {
237    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
238        // Substitution might happen in members
239        for member in &mut self.members {
240            member.substitute_constant(name, value)?;
241        }
242        Ok(())
243    }
244}
245
246impl ConstantSubstitution for ParticleMember {
247    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
248        // Substitution might happen in type
249        self.typ.substitute_constant(name, value)
250    }
251}
252
253/* -- Simulation -- */
254
255impl ConstantSubstitution for Simulation {
256    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
257        // Substitution might happen in blocks
258        for block in &mut self.blocks {
259            block.substitute_constant(name, value)?;
260        }
261        Ok(())
262    }
263}
264
265impl ConstantSubstitution for SimulationBlock {
266    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
267        match self {
268            SimulationBlock::Once(block) => block.substitute_constant(name, value),
269            SimulationBlock::Step(block) => block.substitute_constant(name, value)
270        }
271    }
272}
273
274impl ConstantSubstitution for OnceBlock {
275    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
276        self.step.substitute_constant(name, value)?;
277        for subblock in &mut self.subblocks {
278            subblock.substitute_constant(name, value)?;
279        }
280        Ok(())
281    }
282}
283
284impl ConstantSubstitution for StepBlock {
285    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
286        self.step_range.substitute_constant(name, value)?;
287        for subblock in &mut self.subblocks {
288            subblock.substitute_constant(name, value)?;
289        }
290        Ok(())
291    }
292}
293
294impl ConstantSubstitution for StepRange {
295    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
296        self.start.substitute_constant(name, value)?;
297        self.end.substitute_constant(name, value)?;
298        self.step.substitute_constant(name, value)
299    }
300}
301
302impl ConstantSubstitution for SimulationSubBlock {
303    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
304        for statement in &mut self.statements {
305            statement.substitute_constant(name, value)?;
306        }
307        Ok(())
308    }
309}
310
311/* -- Externs -- */
312
313impl ConstantSubstitution for ExternFunctionDecl {
314    fn substitute_constant(&mut self, name: &str, value: &SubstitutionValue) -> Result<()> {
315        for parameter_type in &mut self.parameter_types {
316            parameter_type.substitute_constant(name, value)?;
317        }
318        self.return_type.substitute_constant(name, value)
319    }
320}