fips_md/codegen/analysis/
symbol.rs

1//! Structs related to symbol table construction and manipulation
2
3use anyhow::{anyhow, Result};
4
5use std::collections::HashMap;
6
7use crate::{parser::{FipsType, SubstitutionValue}, runtime::{FunctionID, MemberID, ParticleIndexEntry}, utils::FipsValue};
8
9/// Symbol table used by the code generator. T is the type of data attached to the symbols.
10#[derive(Clone)]
11pub struct SymbolTable<T> {
12    /// The actual symbol table
13    symbols: HashMap<String, FipsSymbol<T>>,
14    /// Additional symbol tables used for resolution (after this table go
15    /// _backwards_ through the vec)
16    subtables: Vec<SymbolTable<T>>
17}
18
19// Quick macro for to print error messages that can still refer to the key used
20macro_rules! insert_or_error {
21    ($map:expr, $key:expr, $val:expr, $err:expr) => {
22        if $map.contains_key(&$key) { // Key is only borrowed here
23            Err($err)                 // <- Usage here is fine
24        }
25        else {
26            $map.insert($key, $val);  // <- only moved if successful
27            Ok(())
28        }
29    }
30}
31
32impl<T> SymbolTable<T> {
33    pub fn new() -> Self {
34        Self {
35            symbols: HashMap::new(),
36            subtables: vec![]
37        }
38    }
39
40    /// Create symbol table from particle definition
41    pub fn from_particle(particle_def: &ParticleIndexEntry) -> Self {
42        let mut symbol_table = Self::new();
43        for (member_id, member_def) in particle_def.get_members() {
44            symbol_table.add_particle_member(member_def.get_name().into(), member_id)
45                .expect("Inconsistent member names!");
46        }
47        symbol_table
48    }
49
50    /// Add a new constant entry to the symbol table from a substitution value
51    pub fn add_constant_from_substitution(&mut self, name: String, value: &SubstitutionValue) -> Result<()> {
52        insert_or_error!(self.symbols, name, 
53            FipsSymbol::from_substitution(&value),
54            anyhow!("Cannot redefine constant with name {}", name))
55    }
56
57    pub fn add_constant_f64(&mut self, name: String, value: f64) -> Result<()> {
58        insert_or_error!(self.symbols, name, 
59            FipsSymbol::from_f64(value),
60            anyhow!("Cannot redefine constant with name {}", name))
61    }
62
63    pub fn add_local_symbol(&mut self, name: String, typ: FipsType) -> Result<()> {
64        insert_or_error!(self.symbols, name,
65            FipsSymbol::new_local(typ),
66            anyhow!("Cannot redefine local variable with name {}", name)
67        )
68    }
69
70    pub fn add_local_symbol_with_value(&mut self, name: String, typ: FipsType, value: T) -> Result<()> {
71        insert_or_error!(self.symbols, name,
72            FipsSymbol::new_local_with_value(typ, value),
73            anyhow!("Cannot redefine local variable with name {}", name)
74        )
75    }
76
77    pub fn add_particle_member(&mut self, name: String, member_id: MemberID) -> Result<()> {
78        insert_or_error!(self.symbols, name,
79            FipsSymbol::new_particle_member(member_id),
80            anyhow!("Cannot redefine particle member variable with name {}", name)
81        )
82    }
83
84    pub fn add_function(&mut self, name: String, function_id: FunctionID) -> Result<()> {
85        insert_or_error!(self.symbols, name,
86            FipsSymbol::new_function(function_id),
87            anyhow!("Cannot redefine function with name {}", name)
88        )
89    }
90
91    /// Add another symbol table to this symbol table
92    pub fn push_table(&mut self, symbols: SymbolTable<T>) {
93        self.subtables.push(symbols)
94    }
95
96    /// Pop the last symbol table pushed
97    pub fn pop_table(&mut self) -> Option<SymbolTable<T>> {
98        self.subtables.pop()
99    }
100
101    /// Convert the symbol table to a different type parameter (resets all symbol values)
102    pub fn convert<U>(self) -> SymbolTable<U> {
103        let symbols = self.symbols.into_iter()
104            .map(|(name, symbol)| (name, symbol.convert()))
105            .collect::<HashMap<_,_>>();
106        let subtables = self.subtables.into_iter()
107            .map(|table| table.convert())
108            .collect::<Vec<_>>();
109        SymbolTable {
110            symbols,
111            subtables
112        }
113    }
114
115    /// Create an iterator over all symbols
116    pub fn iter_mut(&mut self) -> impl Iterator<Item=(&String, &mut FipsSymbol<T>)> {
117        // I cannot get this to work with just chaining IterMut due to recursive
118        // return type (i.e. concrete return type cannot be resolved)
119        // There is probably a nicer, more performant solution...
120        let mut total_symbols = vec![];
121        for symbol_pair in &mut self.symbols {
122            total_symbols.push(symbol_pair)
123        }
124        for subtable in &mut self.subtables {
125            for symbol_pair in subtable.iter_mut() {
126                total_symbols.push(symbol_pair);
127            }
128        }
129        total_symbols.into_iter()
130    }
131
132    /// Create an iterator over all symbols
133    pub fn iter(&self) -> impl Iterator<Item=(&String, &FipsSymbol<T>)> {
134        // I cannot get this to work with just chaining IterMut due to recursive
135        // return type (i.e. concrete return type cannot be resolved)
136        // There is probably a nicer, more performant solution...
137        let mut total_symbols = vec![];
138        for symbol_pair in &self.symbols {
139            total_symbols.push(symbol_pair)
140        }
141        for subtable in &self.subtables {
142            for symbol_pair in subtable.iter() {
143                total_symbols.push(symbol_pair);
144            }
145        }
146        total_symbols.into_iter()
147    }
148
149    pub fn resolve_symbol(&self, name: &str) -> Option<&FipsSymbol<T>> {
150        // Check self
151        if let Some(symbol) = self.symbols.get(name) {
152            return Some(symbol)
153        };
154        for subtable in self.subtables.iter().rev() {
155            if let Some(symbol) = subtable.resolve_symbol(name) {
156                return Some(symbol)
157            }
158        };
159        None
160    }
161}
162
163
164
165/// A symbol, i.e. an LLVM Value attached to a name like a variable, function, constant, etc.
166#[derive(Clone)]
167pub struct FipsSymbol<T> {
168    pub(crate) kind: FipsSymbolKind,
169    pub(crate) value: Option<T>
170}
171
172impl<T> FipsSymbol<T> {
173    pub fn from_substitution(value: &SubstitutionValue) -> Self {
174        Self {
175            kind: FipsSymbolKind::from_subsitution(value),
176            value: None
177        }
178    }
179
180    pub fn from_f64(value: f64) -> Self {
181        Self {
182            kind: FipsSymbolKind::from_f64(value),
183            value: None
184        }
185    }
186
187    pub fn new_local(typ: FipsType) -> Self {
188        Self {
189            kind: FipsSymbolKind::LocalVariable(typ),
190            value: None
191        }
192    }
193
194    pub fn new_local_with_value(typ: FipsType, value: T) -> Self {
195        Self {
196            kind: FipsSymbolKind::LocalVariable(typ),
197            value: Some(value)
198        }
199    }
200
201    pub fn new_particle_member(member_id: MemberID) -> Self {
202        Self {
203            kind: FipsSymbolKind::ParticleMember(member_id),
204            value: None
205        }
206    }
207
208    pub fn new_function(function_id: FunctionID) -> Self {
209        Self {
210            kind: FipsSymbolKind::Function(function_id),
211            value: None
212        }
213    }
214
215    pub fn convert<U>(self) -> FipsSymbol<U> {
216        FipsSymbol {
217            kind: self.kind,
218            value: None
219        }
220    }
221
222    pub fn set_value(&mut self, value: T) {
223        self.value = Some(value)
224    }
225}
226
227/// Different kinds of symbols
228#[derive(Clone)]
229pub enum FipsSymbolKind {
230    /// A symbolic constant
231    Constant(FipsValue),
232    /// A local variable
233    LocalVariable(FipsType),
234    /// A particle member (we don't need the particle ID since a single thread only works
235    /// on one type of particle)
236    ParticleMember(MemberID),
237    /// A global function
238    Function(FunctionID)
239}
240
241impl FipsSymbolKind {
242    /// Crate new symbol from substitution value
243    pub fn from_subsitution(val: &SubstitutionValue) -> Self {
244        Self::Constant(val.clone().into())
245    }
246
247    pub fn from_f64(val: f64) -> Self {
248        Self::Constant(val.into())
249    }
250}