use std::sync::Arc;
use nom::{branch::alt, bytes::complete::{escaped_transform, tag, take_until}, character::complete::{char, none_of}, combinator::{map, opt, value}, multi::fold_many0, sequence::delimited, IResult, Parser};
use crate::{parser::{doc::StofParseError, expr::expr, whitespace::whitespace}, runtime::{instruction::Instruction, instructions::{block::Block, Base, ADD, NOOP}, Val}};
pub fn formatted_string_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, inner) = inner_formatted(input)?;
match parse_inner(&inner) {
Ok((_, expr)) => {
Ok((input, expr))
},
Err(error) => {
Err(error)
}
}
}
fn inner_formatted(input: &str) -> IResult<&str, String, StofParseError> {
let normal = none_of("`\\"); let inner = escaped_transform(normal, '\\', alt((
value("\\", tag("\\")),
value("`", tag("`")),
value("\n", tag("n")),
value("\r", tag("r")),
value("\t", tag("t")),
)));
delimited(char('`'), map(opt(inner), |opt| opt.unwrap_or_default()), char('`')).parse(input)
}
fn parse_inner(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, mut res) = fold_many0(alt((
parse_inner_expr,
map(take_until("${"), |lit: &str| Arc::new(Base::Literal(Val::Str(lit.into()))) as Arc<dyn Instruction>)
)),
Vec::new,
|mut instructions, ins| {
instructions.push(ins);
instructions
}).parse(input)?;
if !input.is_empty() {
res.push(Arc::new(Base::Literal(Val::Str(input.into()))));
}
if res.is_empty() { return Ok((input, NOOP.clone())); }
else if res.len() == 1 { return Ok((input, res.pop().unwrap())); }
else {
let mut block = Block::default();
block.ins.push_back(res.pop().unwrap()); while !res.is_empty() {
block.ins.push_back(res.pop().unwrap()); block.ins.push_back(ADD.clone());
}
Ok((input, Arc::new(block)))
}
}
fn parse_inner_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, inner) = delimited(tag("${"), expr, tag("}")).parse(input)?;
Ok((input, inner))
}