use std::collections::HashMap;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct StackEffectDiagram {
pub mapping: Vec<usize>,
pub inputs: usize,
}
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum ParseError {
MissingDoubleDash,
AdditionalDoubleDash,
SymbolDefinedTwice {
symbol: String,
first: usize,
second: usize,
},
SymbolNotDefined {
symbol: String,
id: usize,
},
}
impl std::fmt::Display for ParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ParseError::MissingDoubleDash => write!(f, "Missing --"),
ParseError::AdditionalDoubleDash => write!(f, "Additional --"),
ParseError::SymbolDefinedTwice {
symbol,
first,
second,
} => write!(
f,
"Symbol {} defined twice at {} and {}",
symbol, first, second
),
ParseError::SymbolNotDefined { symbol, id } => {
write!(f, "Symbol {} not defined at {}", symbol, id)
}
}
}
}
pub fn parse(stack_effect: &str) -> Result<StackEffectDiagram, ParseError> {
let mut iter = stack_effect.split("--");
let pops = match iter.next() {
Some(p) => p,
None => return Err(ParseError::MissingDoubleDash),
};
let pushes = match iter.next() {
Some(p) => p,
None => return Err(ParseError::MissingDoubleDash),
};
if iter.next().is_some() {
return Err(ParseError::AdditionalDoubleDash);
}
let mut symbols_to_positions = HashMap::new();
for (i, symbol) in pops.split_whitespace().enumerate() {
if let Some(pos) = symbols_to_positions.get(symbol) {
return Err(ParseError::SymbolDefinedTwice {
symbol: symbol.to_string(),
first: *pos,
second: i,
});
} else {
symbols_to_positions.insert(symbol, i);
}
}
let input_size = symbols_to_positions.len();
let mut mapping = Vec::with_capacity(pushes.len());
for symbol in pushes.split_whitespace() {
if let Some(pos) = symbols_to_positions.get(symbol) {
mapping.push(*pos);
} else {
return Err(ParseError::SymbolNotDefined {
symbol: symbol.to_string(),
id: mapping.len(),
});
}
}
Ok(StackEffectDiagram {
mapping,
inputs: input_size,
})
}