use std::sync::Arc;
use nom::{branch::alt, bytes::complete::tag, character::complete::{char, multispace0}, combinator::{opt, recognize}, multi::{many0, separated_list0, separated_list1}, sequence::{delimited, preceded}, IResult, Parser};
use crate::{model::SId, parser::{doc::StofParseError, expr::expr, ident::ident, whitespace::whitespace}, runtime::{instruction::Instruction, instructions::{block::Block, call::{FuncCall, NamedArg}, Base}}};
pub fn graph_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, null_check) = opt(char('?')).parse(input)?;
let (input, as_ref) = opt(char('&')).parse(input)?;
let (mut input, first) = var_func(input, false, as_ref.is_some(), null_check.is_some())?;
let rest;
let additional;
if null_check.is_some() {
if as_ref.is_some() {
(rest, additional) = many0(alt((
preceded(alt((tag("."), tag("?."))), null_ref_chained_var_func),
null_ref_chained_var_func
))).parse(input)?;
} else {
(rest, additional) = many0(alt((
preceded(alt((tag("."), tag("?."))), null_chained_var_func),
null_chained_var_func
))).parse(input)?;
}
} else {
if as_ref.is_some() {
(rest, additional) = many0(alt((
preceded(tag("?."), null_ref_chained_var_func),
preceded(char('.'), ref_chained_var_func),
ref_chained_var_func
))).parse(input)?;
} else {
(rest, additional) = many0(alt((
preceded(tag("?."), null_chained_var_func),
preceded(char('.'), chained_var_func),
chained_var_func
))).parse(input)?;
}
}
input = rest;
if additional.is_empty() {
return Ok((input, first));
}
let mut block = Block::default();
block.ins.push_back(first);
for ins in additional { block.ins.push_back(ins); }
Ok((input, Arc::new(block)))
}
pub fn chained_var_func(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
var_func(input, true, false, false)
}
fn null_chained_var_func(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
var_func(input, true, false, true)
}
fn ref_chained_var_func(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
var_func(input, true, true, false)
}
fn null_ref_chained_var_func(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
var_func(input, true, true, true)
}
pub(self) fn var_func(input: &str, chained: bool, as_ref: bool, check_null: bool) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
if chained && input.starts_with('[') {
let (input, idx) = index_expr(input)?;
return Ok((input, Arc::new(FuncCall {
as_ref,
cnull: check_null,
stack: chained,
func: None,
search: Some("at".into()),
args: idx.into_iter().collect(),
oself: None,
})));
}
let (input, path) = variable_expr(input)?;
let mut path = path.to_string();
let (mut input, mut call) = opt(call_expr).parse(input)?;
if call.is_none() {
let (inner, idx) = opt(index_expr).parse(input)?;
if idx.is_some() {
path.push_str(".at");
input = inner;
call = idx;
}
}
if let Some(args) = call {
Ok((input, Arc::new(FuncCall {
as_ref,
cnull: check_null,
stack: chained,
func: None,
search: Some(path.into()),
args: args.into_iter().collect(),
oself: None,
})))
} else {
path = path.replace('?', "");
Ok((input, Arc::new(Base::LoadVariable(path.into(), chained, as_ref))))
}
}
pub(self) fn variable_expr(input: &str) -> IResult<&str, &str, StofParseError> {
recognize(separated_list1(alt((tag("."), tag("?."))), ident)).parse(input)
}
pub(super) fn call_expr(input: &str) -> IResult<&str, Vec<Arc<dyn Instruction>>, StofParseError> {
delimited(
char('('),
separated_list0(char(','), call_arg),
char(')')
).parse(input)
}
pub(self) fn call_arg(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, _) = multispace0(input)?;
let (input, ins) = alt((
named_arg,
expr
)).parse(input)?;
let (input, _) = multispace0(input)?;
Ok((input, ins))
}
pub(self) fn named_arg(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, name) = ident(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = char('=').parse(input)?;
let (input, _) = multispace0(input)?;
let (input, ins) = expr(input)?;
Ok((input, Arc::new(NamedArg { name: SId::from(name), ins })))
}
pub(self) fn index_expr(input: &str) -> IResult<&str, Vec<Arc<dyn Instruction>>, StofParseError> {
delimited(
char('['),
separated_list1(char(','), call_arg),
char(']')
).parse(input)
}