use std::sync::Arc;
use imbl::{vector, Vector};
use nom::{branch::alt, bytes::complete::tag, character::complete::{char, multispace0}, combinator::{opt, peek}, multi::fold_many0, sequence::{delimited, preceded, terminated}, IResult, Parser};
use rustc_hash::FxHashMap;
use crate::{model::{Graph, Profile}, parser::{context::ParseContext, doc::StofParseError, expr::expr, statement::{block, statement}, whitespace::whitespace}, runtime::{instruction::Instruction, instructions::{block::Block, switch::SwitchIns}}};
pub fn switch_statement(input: &str) -> IResult<&str, Vector<Arc<dyn Instruction>>, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, value_expr) = preceded(terminated(tag("switch"), multispace0), delimited(char('('), expr, char(')'))).parse(input)?;
let (input, cases) = preceded(preceded(multispace0, char('{')), case_statements).parse(input)?;
let (input, default_ins) = opt(default_case).parse(input)?;
let (input, _) = whitespace(input)?;
let (input, _) = char('}')(input)?;
let mut map = FxHashMap::default();
let mut graph = Graph::default();
let mut context = ParseContext::new(&mut graph, Profile::default()); let mut stacked_values = Vec::new();
for case in cases {
let val = context.eval(case.expr).expect("failed to evaluate switch statement value expr");
if let Some(ins) = case.ins {
let block = Arc::new(Block { ins }) as Arc<dyn Instruction>;
for sv in stacked_values.drain(..) {
map.insert(sv, block.clone());
}
map.insert(val, block);
} else {
stacked_values.push(val);
}
}
let switch_ins = SwitchIns {
map,
def: default_ins
};
Ok((input, vector![value_expr, Arc::new(switch_ins)]))
}
fn case_statements(input: &str) -> IResult<&str, Vector<Case>, StofParseError> {
fold_many0(
case,
Vector::default,
move |mut statements, current| {
statements.push_back(current);
statements
}
).parse(input)
}
#[derive(Debug, Clone)]
struct Case {
pub expr: Arc<dyn Instruction>,
pub ins: Option<Vector<Arc<dyn Instruction>>>, }
fn case(input: &str) -> IResult<&str, Case, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, expr) = preceded(tag("case"), preceded(multispace0, expr)).parse(input)?;
let (input, _) = delimited(multispace0, char(':'), multispace0).parse(input)?;
let (input, peeked) = opt(peek(alt((tag("default"), tag("case"))))).parse(input)?;
if peeked.is_some() { return Ok((input, Case { expr, ins: None })); }
let (input, ins) = opt(alt((
block,
statement
))).parse(input)?;
Ok((input, Case {
expr,
ins
}))
}
fn default_case(input: &str) -> IResult<&str, Vector<Arc<dyn Instruction>>, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, _) = preceded(tag("default"), delimited(multispace0, char(':'), multispace0)).parse(input)?;
let (input, ins) = alt((
block,
statement
)).parse(input)?;
Ok((input, ins))
}