use std::{collections::{HashMap, HashSet}};
use slotmap::{SlotMap, DefaultKey};
use anyhow::{Result, anyhow};
use strum::IntoEnumIterator;
use strum_macros::{EnumIter};
use crate::parser::{self, CompileTimeConstant, Identifier};
use crate::parser::ConstantSubstitution;
pub type ParticleID = DefaultKey;
pub type SimulationID = DefaultKey;
pub type InteractionID = DefaultKey;
pub type InteractionQuantityID = DefaultKey;
pub type FunctionID = DefaultKey;
#[derive(Debug)]
struct Index<ID: slotmap::Key, T> {
elements: SlotMap<ID, T>,
name_table: HashMap<String, ID>
}
impl<ID: slotmap::Key, T> Index<ID,T> {
pub fn new() -> Self {
Self {
elements: SlotMap::with_key(),
name_table: HashMap::new()
}
}
pub fn insert(&mut self, element: T, name: String) -> Option<T> {
let result = match self.name_table.remove(&name) {
Some(key) => Some(self.elements.remove(key).unwrap()),
None => None
};
let key = self.elements.insert(element);
self.name_table.insert(name, key);
result
}
pub fn get(&self, id: &ID) -> Option<&T> {
self.elements.get(*id)
}
pub fn get_with_name(&self, name: &str) -> Option<(ID, &T)> {
let key = match self.name_table.get(name) {
None => return None,
Some(key) => key
};
Some((*key, self.elements.get(*key).unwrap()))
}
pub fn iter(&self) -> impl Iterator<Item=(ID, &T)> {
self.elements.iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item=(ID, &mut T)> {
self.elements.iter_mut()
}
}
pub struct ParticleIndex {
index: Index<ParticleID, ParticleIndexEntry>
}
impl ParticleIndex {
pub(crate) fn new(mut parsed_particles: Vec<parser::Particle>) -> Result<Self> {
let mut particle_index = Self { index: Index::new() };
while !parsed_particles.is_empty() {
while let Some(particle) = parsed_particles.pop() {
let name = particle.name.clone();
match particle_index.index.insert(ParticleIndexEntry::new(particle, &particle_index)?, name) {
None => {},
Some(entry) => { return Err(
anyhow!("Particle {} illegally redefined", entry.get_name())
)}
}
}
}
Ok(particle_index)
}
pub(crate) fn get(&self, particle_id: ParticleID) -> Option<&ParticleIndexEntry> {
self.index.get(&particle_id)
}
pub(crate) fn get_particle_by_name(&self, particle_name: &str) -> Option<(ParticleID, &ParticleIndexEntry)> {
self.index.get_with_name(particle_name)
}
pub(crate) fn substitute_constant(&mut self, name: &str, value: &parser::SubstitutionValue) -> Result<()> {
for (_, particle_definition) in &mut self.index.iter_mut() {
particle_definition.substitute_constant(name, value)?
}
Ok(())
}
}
pub type MemberID = DefaultKey;
#[derive(Debug)]
pub struct ParticleIndexEntry {
particle: parser::Particle,
member_index: Index<MemberID, MemberIndexEntry>
}
impl ParticleIndexEntry {
pub(crate) fn new(mut parsed_particle: parser::Particle, _particle_index: &ParticleIndex) -> Result<Self> {
let mut member_index = Index::new();
for member in parsed_particle.members {
let name = member.name.clone();
match member_index.insert(MemberIndexEntry::new(member), name) {
None => {},
Some(entry) => { return Err(
anyhow!("Member {} of particle {} illegally redefined",
entry.get_name(), parsed_particle.name)
)}
}
}
parsed_particle.members = vec![];
Ok(ParticleIndexEntry {
particle: parsed_particle,
member_index
})
}
pub(crate) fn substitute_constant(&mut self, name: &str, value: &parser::SubstitutionValue) -> Result<()> {
for (_, member_definition) in &mut self.member_index.iter_mut() {
member_definition.substitute_constant(name, value)?
}
Ok(())
}
pub fn get_members(&self) -> impl Iterator<Item = (MemberID, &MemberIndexEntry)> {
self.member_index.iter()
}
pub fn get_name(&self) -> &str {
&self.particle.name
}
pub fn get_member_by_name<'a>(&'a self, member_name: &str) -> Option<(MemberID, &'a MemberIndexEntry)> {
self.member_index.get_with_name(member_name)
}
pub fn get_position_member<'a>(&'a self) -> Option<(MemberID, &'a MemberIndexEntry)> {
for (member_id, member) in self.member_index.iter() {
if member.is_position() {
return Some((member_id, member));
}
}
None
}
pub fn get_member<'a>(&'a self, member_id: &MemberID) -> Option<&'a MemberIndexEntry> {
self.member_index.get(member_id)
}
}
#[derive(Debug, Clone)]
pub struct MemberIndexEntry {
member: parser::ParticleMember
}
impl MemberIndexEntry {
pub(crate) fn new(parsed_member: parser::ParticleMember) -> Self {
Self {
member: parsed_member
}
}
pub(crate) fn get_member_size(&self) -> Result<usize> {
self.member.typ.get_size()
}
pub(crate) fn substitute_constant(&mut self, name: &str, value: &parser::SubstitutionValue) -> Result<()> {
self.member.substitute_constant(name, value)
}
pub(crate) fn is_mutable(&self) -> bool {
self.member.mutable
}
pub(crate) fn is_position(&self) -> bool {
self.member.is_position
}
pub(crate) fn get_type(&self) -> &parser::FipsType {
&self.member.typ
}
pub(crate) fn get_name(&self) -> &str {
&self.member.name
}
}
pub struct SimulationIndex {
index: Index<SimulationID, SimulationIndexEntry>
}
impl SimulationIndex {
pub(crate) fn new(parsed_simulations: Vec<parser::Simulation>,
particle_index: &ParticleIndex)
-> Result<Self> {
let mut index = Index::new();
for simulation in parsed_simulations {
let name = simulation.name.clone();
match index.insert(SimulationIndexEntry::new(simulation, particle_index)?, name) {
None => {},
Some(entry) => { return Err(
anyhow!("Particle {} illegally redefined", entry.get_name())
)}
}
}
Ok(Self { index })
}
pub(crate) fn get_simulation_by_name(&self, name: &str) -> Option<(SimulationID, &SimulationIndexEntry)> {
self.index.get_with_name(name)
}
pub(crate) fn get_simulation(&self, simulation_id: &SimulationID) -> Option<&SimulationIndexEntry> {
self.index.get(simulation_id)
}
}
pub struct SimulationIndexEntry {
simulation: parser::Simulation,
default_particle: Option<ParticleID>,
blocks: Vec<SimulationBlock>
}
impl SimulationIndexEntry {
pub(crate) fn new(mut simulation: parser::Simulation,
particle_index: &ParticleIndex)
-> Result<Self> {
let default_particle = simulation.default_particle.as_ref()
.map(|default_particle_name|
particle_index.get_particle_by_name(&default_particle_name)
.map(|(particle_id,_)| particle_id)
.ok_or(anyhow!("Cannot find default particle {} for simulation {}",
default_particle_name, simulation.name))
).transpose()?;
let blocks = simulation.blocks;
simulation.blocks = vec![];
let blocks = blocks.into_iter()
.map(|simulation_block| {
let (kind, subblocks) = match simulation_block {
parser::SimulationBlock::Once(once_block) => (
SimulationBlockKind::Once(once_block.step),
once_block.subblocks
),
parser::SimulationBlock::Step(step_block) => (
SimulationBlockKind::Step(step_block.step_range),
step_block.subblocks
),
};
let statement_blocks = subblocks.into_iter().map(|subblock| {
let particle_filter = match subblock.particle {
None => SimulationParticleFilter::Default,
Some(particle_name) => {
let (particle_id,_) = particle_index.get_particle_by_name(&particle_name)
.ok_or(anyhow!("Cannot find particle {} referenced in simulation {}",
particle_name, simulation.name))?;
SimulationParticleFilter::Single(particle_id)
}
};
Ok(SimulationStatementBlock {
particle_filter,
statements: subblock.statements
})
}).collect::<Result<Vec<_>>>()?;
Ok(SimulationBlock {
kind, statement_blocks
})
}).collect::<Result<Vec<_>>>()?;
Ok(Self {
simulation,
default_particle,
blocks
})
}
pub(crate) fn get_name(&self) -> &str {
&self.simulation.name
}
pub(crate) fn get_default_particle(&self) -> Option<ParticleID> {
self.default_particle
}
pub(crate) fn get_blocks(&self) -> &[SimulationBlock] {
&self.blocks
}
}
pub enum SimulationBlockKind {
Once(parser::CompileTimeConstant<usize>),
Step(parser::StepRange)
}
pub struct SimulationBlock {
pub kind: SimulationBlockKind,
pub statement_blocks: Vec<SimulationStatementBlock>
}
pub enum SimulationParticleFilter {
Default,
Single(ParticleID)
}
pub struct SimulationStatementBlock {
pub particle_filter: SimulationParticleFilter,
pub statements: Vec<parser::Statement>
}
pub struct InteractionIndex {
index: Index<InteractionID, InteractionIndexEntry>
}
impl InteractionIndex {
pub(crate) fn new(parsed_interactions: Vec<parser::Interaction>,
particle_index: &ParticleIndex) -> Result<Self>
{
let mut index = Index::new();
for interaction in parsed_interactions {
let name = interaction.name.clone();
match index.insert(InteractionIndexEntry::new(interaction, particle_index)?, name) {
None => {},
Some(entry) => { return Err(
anyhow!("Interaction {} illegally redefined", entry.get_name())
)}
}
}
Ok(Self { index })
}
pub(crate) fn get_interaction_by_name(&self, interaction_name: &str) -> Option<(InteractionID, &InteractionIndexEntry)> {
self.index.get_with_name(interaction_name)
}
pub(crate) fn get(&self, interaction_id: InteractionID) -> Option<&InteractionIndexEntry> {
self.index.get(&interaction_id)
}
pub(crate) fn iter(&self) -> impl Iterator<Item=(InteractionID, &InteractionIndexEntry)> {
self.index.iter()
}
pub(crate) fn substitute_constant(&mut self, name: &str, value: &parser::SubstitutionValue) -> Result<()> {
for (_, interaction_definition) in &mut self.index.iter_mut() {
interaction_definition.substitute_constant(name, value)?
}
Ok(())
}
}
pub struct InteractionIndexEntry {
interaction: parser::Interaction,
particle_id_a: ParticleID,
particle_id_b: ParticleID,
quantity_index: Index<InteractionQuantityID, InteractionQuantityIndexEntry>
}
impl InteractionIndexEntry {
pub(crate) fn new(mut interaction: parser::Interaction, particle_index: &ParticleIndex) -> Result<Self> {
let mut quantity_index = Index::new();
for quantity in interaction.quantities {
let name = quantity.name.clone();
match quantity_index.insert(InteractionQuantityIndexEntry::new(quantity), name) {
None => {},
Some(entry) => { return Err(
anyhow!("Quantitiy {} of interaction {} illegally redefined",
entry.get_name(), interaction.name)
)}
}
}
interaction.quantities = vec![];
let particle_id_a = particle_index.get_particle_by_name(&interaction.type_a)
.ok_or(anyhow!("Cannot resolve particle type {} in interaction {}",
&interaction.type_a, interaction.name))?.0;
let particle_id_b = particle_index.get_particle_by_name(&interaction.type_b)
.ok_or(anyhow!("Cannot resolve particle type {} in interaction {}",
&interaction.type_a, interaction.name))?.0;
Ok(Self {
interaction,
quantity_index,
particle_id_a, particle_id_b
})
}
pub(crate) fn get_name(&self) -> &str {
&self.interaction.name
}
pub(crate) fn get_type_a(&self) -> &str {
&self.interaction.type_a
}
pub(crate) fn get_type_b(&self) -> &str {
&self.interaction.type_b
}
pub(crate) fn get_name_a(&self) -> &Identifier {
&self.interaction.name_a
}
pub(crate) fn get_name_b(&self) -> &Identifier {
&self.interaction.name_b
}
pub(crate) fn get_distance_vec(&self) -> Option<&String> {
self.interaction.distance_vec.as_ref()
}
pub(crate) fn get_quantity(&self, quantity_id: InteractionQuantityID) -> Option<&InteractionQuantityIndexEntry> {
self.quantity_index.get(&quantity_id)
}
pub(crate) fn get_cutoff(&self) -> CompileTimeConstant<f64> {
self.interaction.cutoff.clone()
}
pub(crate) fn get_distance_identifier(&self) -> &Identifier {
&self.interaction.distance
}
pub(crate) fn get_common_block(&self) -> Option<&Vec<parser::Statement>> {
self.interaction.common_block.as_ref()
}
pub(crate) fn get_quantity_by_name(&self, quantity: &str) -> Option<(InteractionQuantityID, &InteractionQuantityIndexEntry)> {
self.quantity_index.get_with_name(quantity)
}
pub(crate) fn iter(&self) -> impl Iterator<Item = (InteractionQuantityID, &InteractionQuantityIndexEntry)> {
self.quantity_index.iter()
}
pub(crate) fn get_affected_particles(&self, _particle_index: &ParticleIndex) -> Result<HashSet<ParticleID>> {
let mut affected_particles = HashSet::new();
affected_particles.insert(self.particle_id_a);
affected_particles.insert(self.particle_id_b);
Ok(affected_particles)
}
pub(crate) fn substitute_constant(&mut self, name: &str, value: &parser::SubstitutionValue) -> Result<()> {
self.interaction.cutoff.substitute_constant(name, value)
}
}
pub struct InteractionQuantityIndexEntry {
quantity: parser::InteractionQuantity
}
impl InteractionQuantityIndexEntry {
pub(crate) fn new(quantity: parser::InteractionQuantity) -> Self {
Self {
quantity
}
}
pub(crate) fn get_name(&self) -> &str {
&self.quantity.name
}
pub(crate) fn get_target_a(&self) -> &str {
&self.quantity.target_a
}
pub(crate) fn get_target_b(&self) -> &str {
&self.quantity.target_b
}
pub(crate) fn get_expression(&self) -> &parser::Expression {
&self.quantity.expression
}
pub(crate) fn get_reduction_method(&self) -> &parser::ReductionMethod {
&self.quantity.reduction_method
}
pub(crate) fn get_symmetry(&self) -> parser::InteractionSymmetry {
self.quantity.symmetry
}
}
pub(crate) struct FunctionIndex {
index: Index<FunctionID, FunctionIndexEntry>
}
impl FunctionIndex {
pub(crate) fn new(externs: Vec<parser::ExternFunctionDecl>) -> Result<Self> {
let mut index = Index::new();
for builtin in BuiltinFunction::get_all() {
let name = builtin.get_name().to_string();
match index.insert(FunctionIndexEntry::Builtin(builtin), name) {
Some(entry) => { return Err(anyhow!("Internal: builtin name {} duplicated", entry.get_name())) },
None => {},
}
}
for externfunc in externs {
let name = externfunc.name.clone();
match index.insert(FunctionIndexEntry::Extern(externfunc), name) {
Some(entry) => { return Err(anyhow!("Cannot redefine function {}", entry.get_name())) },
None => {},
}
}
Ok(Self {
index
})
}
pub(crate) fn get(&self, function_id: FunctionID) -> Option<&FunctionIndexEntry> {
self.index.get(&function_id)
}
pub(crate) fn get_functions(&self) -> impl Iterator<Item = (FunctionID, &FunctionIndexEntry)> {
self.index.iter()
}
pub(crate) fn substitute_constant(&mut self, name: &str, value: &parser::SubstitutionValue) -> Result<()> {
for (_, function_def) in &mut self.index.iter_mut() {
function_def.substitute_constant(name, value)?
}
Ok(())
}
}
pub(crate) enum FunctionIndexEntry {
Extern(parser::ExternFunctionDecl),
Builtin(BuiltinFunction)
}
impl FunctionIndexEntry {
pub(crate) fn get_name(&self) -> &str {
match self {
FunctionIndexEntry::Extern(externfunc) => { &externfunc.name },
FunctionIndexEntry::Builtin(builtin) => { builtin.get_name() },
}
}
pub(crate) fn needs_callback_target_ptr(&self) -> bool {
match self {
FunctionIndexEntry::Extern(_) => { false }, FunctionIndexEntry::Builtin(builtin) => { builtin.needs_callback_target_ptr() },
}
}
pub(crate) fn returns_array(&self) -> bool {
match self {
FunctionIndexEntry::Extern(externfunc) => {
match externfunc.return_type {
parser::FipsType::Double | parser::FipsType::Int64 => false,
parser::FipsType::Array { .. } => true,
}
},
FunctionIndexEntry::Builtin(builtin) => { builtin.returns_array() },
}
}
pub(crate) fn substitute_constant(&mut self, name: &str, value: &parser::SubstitutionValue) -> Result<()> {
match self {
FunctionIndexEntry::Extern(func_decl) => { func_decl.substitute_constant(name, value) },
FunctionIndexEntry::Builtin(_) => Ok(()),
}
}
}
#[derive(EnumIter)]
pub(crate) enum BuiltinFunction {
Sqrt,
Sin,
Cos,
RandomNormal
}
impl BuiltinFunction {
pub(crate) fn get_all() -> Vec<Self> {
Self::iter().collect::<Vec<_>>()
}
pub(crate) fn get_name(&self) -> &'static str {
match self {
Self::Sqrt => "sqrt",
Self::Sin => "sin",
Self::Cos => "cos",
Self::RandomNormal => "random_normal",
}
}
pub(crate) fn needs_callback_target_ptr(&self) -> bool {
match self {
BuiltinFunction::Sqrt => false,
BuiltinFunction::Sin => false,
BuiltinFunction::Cos => false,
BuiltinFunction::RandomNormal => true,
}
}
pub(crate) fn returns_array(&self) -> bool {
match self {
Self::Sqrt => false,
Self::Sin => false,
Self::Cos => false,
Self::RandomNormal => false,
}
}
}