use std::sync::Arc;
use arcstr::ArcStr;
use imbl::{vector, Vector};
use nom::{bytes::complete::tag, branch::alt, character::complete::{char, multispace0}, combinator::opt, sequence::{delimited, preceded, terminated}, IResult, Parser};
use crate::{parser::{doc::StofParseError, expr::expr, ident::ident, statement::{assign::assign, declare::declare_statement, noscope_block, statement}, whitespace::whitespace}, runtime::{instruction::Instruction, instructions::{block::Block, whiles::WhileIns, Base}, Val}};
pub fn for_loop(input: &str) -> IResult<&str, Vector<Arc<dyn Instruction>>, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, loop_tag) = opt(terminated(preceded(char('^'), ident), multispace0)).parse(input)?;
let (input, control) = preceded(terminated(tag("for"), multispace0), delimited(char('('), declare_test_inc, char(')'))).parse(input)?;
let (input, ins) = alt((
noscope_block,
statement
)).parse(input)?;
let mut tag = None;
if let Some(ltag) = loop_tag {
tag = Some(ArcStr::from(ltag));
}
let mut declare = None;
if let Some(statement) = control.0 {
declare = Some(Arc::new(Block { ins: statement }) as Arc<dyn Instruction>);
}
let mut test_expr: Arc<dyn Instruction> = Arc::new(Base::Literal(Val::Bool(true)));
if let Some(expr) = control.1 {
test_expr = expr;
}
let mut inc = None;
if let Some(statement) = control.2 {
inc = Some(Arc::new(Block { ins: statement }) as Arc<dyn Instruction>);
}
let while_ins = WhileIns {
tag,
test: test_expr,
ins,
declare,
inc,
};
Ok((input, vector![Arc::new(while_ins) as Arc<dyn Instruction>]))
}
fn declare_test_inc(input: &str) -> IResult<&str, (Option<Vector<Arc<dyn Instruction>>>, Option<Arc<dyn Instruction>>, Option<Vector<Arc<dyn Instruction>>>), StofParseError> {
let (input, _) = multispace0(input)?;
let (input, declaration) = opt(declare_statement).parse(input)?;
let (input, _) = delimited(multispace0, char(';'), multispace0).parse(input)?;
let (input, test_expr) = opt(expr).parse(input)?;
let (input, _) = delimited(multispace0, char(';'), multispace0).parse(input)?;
let (input, increment) = opt(assign).parse(input)?;
let (input, _) = multispace0(input)?;
Ok((input, (declaration, test_expr, increment)))
}