use crate::expressions::{
BiOperator, ExprProgress, ExprType, ExprWithContext, FunctionCall, GetSeries, ImportExpr,
InputVar, MapFunction, MapSeries, Position, RawExpr, SetSeries, SetVariable, UniOperator,
VarType,
};
use crate::network::{PropCondition, PropNodes, PropOrder};
use crate::parser::{
components::*,
errors::{MatchErr, ParseErrorType},
tasks::{attr_type, propagation},
tokenizer::Token,
};
use crate::structs::NadiStructExpr;
use crate::tasks::TaskKeyword;
use crate::udf::UserFunction;
use nom::{
branch::alt,
combinator::{cut, map, opt, value},
multi::{many0, many1, separated_list0, separated_list1},
sequence::{delimited, pair, preceded, separated_pair, terminated, tuple},
};
use std::path::PathBuf;
macro_rules! parse_err {
($inp:ident, $msg:literal) => {
Err(nom::Err::Error(
MatchErr::new($inp).ty(&ParseErrorType::InvalidExpression($msg)),
))
};
}
fn set_pos(ty: ExprType<RawExpr>, tk: &[Token<'_>]) -> RawExpr {
RawExpr::new(ty, tk.position())
}
pub fn raw_expr<'a, 'b: 'a, F>(mut f: F) -> impl FnMut(&'a [Token<'b>]) -> MatchRes<'a, 'b, RawExpr>
where
F: nom::Parser<&'a [Token<'b>], ExprType<RawExpr>, MatchErr<'a, 'b>>,
{
move |i: &'a [Token<'b>]| match f.parse(i) {
Ok((rest, o)) => Ok((
rest,
RawExpr::new(
o,
i.position(),
),
)),
Err(e) => Err(e),
}
}
pub fn value_expression<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
alt((
expr_with_context,
expr_maybe_range,
map_series,
get_series,
map(function_call, ExprType::Function),
map(template_val, ExprType::Render),
array_expr,
table_expr,
uni_operator_expr,
if_else_expr,
try_catch_expr,
for_each_expr,
while_expr,
loop_expr,
))(inp)
}
pub fn expression<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
alt((
expr_set_variable,
expr_set_series,
value_expression,
import_expr,
value(ExprType::Continue, kw_continue),
value(ExprType::None, none),
progress_expr,
map(
preceded(kw_error, after_space(string_val)),
ExprType::UserError,
),
map(
preceded(kw_return, opt(after_space(raw_expr(complete_expression)))),
|e| ExprType::Return(e.map(Box::new)),
),
map(
preceded(kw_break, opt(after_space(raw_expr(complete_expression)))),
|e| ExprType::Break(e.map(Box::new)),
),
))(inp)
}
pub fn import_expr<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
let (rest, (tasks, name, path)) = tuple((
alt((value(true, kw_exec), value(false, kw_import))),
after_space(variable),
opt(preceded(
after_space(kw_from),
after_space(map(string_val, PathBuf::from)),
)),
))(inp)?;
Ok((
rest,
ExprType::Import(ImportExpr {
name: name.content.to_string(),
path,
tasks,
}),
))
}
pub fn expr_with_context<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
let (rest, (vt, op, expr)) = tuple((
variable_type,
opt(alt((
value((true, false), after_space(kw_do)),
value((true, true), after_space(kw_dopar)),
))),
alt((
maybe_space(raw_expr(expression_block)),
after_space(raw_expr(complete_expression)),
)),
))(inp)?;
let (silent, parallel) = op.unwrap_or((false, false));
Ok((
rest,
ExprType::WithContext(ExprWithContext::new(vt, expr, silent, parallel)),
))
}
pub fn expr_set_variable<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
map(
tuple((
opt(terminated(variable_type, dot)),
task_dot_variable,
opt(maybe_space(preceded(colon, maybe_space(attr_type)))),
maybe_space(assignment),
maybe_space(raw_expr(complete_value_expression)),
opt(semicolon),
)),
|(vt, (var, indices), ty, _, expr, silent)| {
ExprType::SetVar(SetVariable::new(
InputVar::new(vt.clone(), var.clone(), indices.clone(), inp.position()),
ty,
expr,
silent.is_some(),
))
},
)(inp)
}
pub fn expr_literal<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
map(primitives, ExprType::Value)(inp)
}
pub fn expr_maybe_range<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
let (rest, (start, step, end)) = tuple((
alt((input_variable, expr_literal)),
opt(preceded(
colon,
maybe_space(raw_expr(alt((input_variable, expr_literal)))),
)),
opt(preceded(
colon,
maybe_space(raw_expr(alt((input_variable, expr_literal)))),
)),
))(inp)?;
if let Some(step) = step {
if let Some(end) = end {
Ok((
rest,
ExprType::Range(
Box::new(set_pos(start, inp)),
Some(Box::new(step)),
Box::new(end),
),
))
} else {
Ok((
rest,
ExprType::Range(Box::new(set_pos(start, inp)), None, Box::new(step)),
))
}
} else {
Ok((rest, start))
}
}
pub fn array_expr<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
delimited(
bracket_start,
alt((
map(
tuple((
maybe_newline(raw_expr(alt((expression_block, value_expression)))),
delimited(
maybe_space(kw_for),
after_space(map(variable, |v| v.content.to_string())),
after_space(kw_in),
),
after_space(raw_expr(value_expression)),
)),
|(expr, var, parent)| ExprType::ArrayGen(Box::new(expr), var, Box::new(parent)),
),
map(
separated_list0(
maybe_space(comma),
maybe_newline(raw_expr(alt((
complete_value_expression,
preceded(star, map(input_variable_only, ExprType::Args)),
)))),
),
ExprType::Array,
),
)),
maybe_newline(bracket_end),
)(inp)
}
pub fn table_expr<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
delimited(
brace_start,
alt((
map(
tuple((
maybe_newline(raw_expr(alt((expression_block, value_expression)))),
maybe_space(assignment),
maybe_newline(raw_expr(alt((expression_block, value_expression)))),
delimited(
maybe_space(kw_for),
after_space(separated_pair(
map(variable, |v| v.content.to_string()),
after_space(comma),
after_space(map(variable, |v| v.content.to_string())),
)),
after_space(kw_in),
),
after_space(raw_expr(expression)),
)),
|(key, _, expr, (var1, var2), parent)| {
ExprType::MapGen(Box::new(key), Box::new(expr), var1, var2, Box::new(parent))
},
),
map(opt(maybe_newline(kw_args)), |exprs| {
ExprType::Map(exprs.unwrap_or_default())
}),
)),
maybe_newline(brace_end),
)(inp)
}
pub fn expression_group<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
delimited(
paren_start,
maybe_newline(multi_expression),
err_ctx(&ParseErrorType::Unclosed(")"), maybe_newline(paren_end)),
)(inp)
}
pub fn expression_block<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
delimited(
brace_start,
maybe_newline(multi_expression),
cut(err_ctx(
&ParseErrorType::Unclosed("}"),
maybe_newline(brace_end),
)),
)(inp)
}
pub fn progress_expr<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
map(
tuple((
kw_progress,
maybe_space(raw_expr(alt((expression, expression_group)))),
maybe_space(assignment),
maybe_space(raw_expr(alt((expression, expression_group)))),
maybe_space(kw_in),
maybe_space(raw_expr(alt((expression, expression_group)))),
)),
|(_, label, _, prog, _, total)| ExprType::Progress(ExprProgress::new(label, prog, total)),
)(inp)
}
pub fn uni_operator_expr<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
let (rest, (op, expr)) = pair(
alt((
value(UniOperator::Not, not),
value(UniOperator::Negative, dash),
value(UniOperator::Positive, plus),
)),
maybe_newline(raw_expr(alt((expression_group, expression)))),
)(inp)?;
Ok((rest, ExprType::UniOp(op, Box::new(expr))))
}
struct BiOpExpr {
first: RawExpr,
args: Vec<(BiOperator, RawExpr)>,
}
impl BiOpExpr {
fn parse(self) -> Option<ExprType<RawExpr>> {
let mut operand_stack: Vec<RawExpr> = Vec::new();
let mut operator_stack: Vec<BiOperator> = Vec::new();
operand_stack.push(self.first);
for (op, expr) in self.args {
while let Some(top_op) = operator_stack.last() {
if top_op.precedence() >= op.precedence() {
let top = operator_stack.pop()?;
let res = top.expr_from_stack(&mut operand_stack)?;
operand_stack.push(res);
} else {
break;
}
}
operator_stack.push(op);
operand_stack.push(expr);
}
while let Some(op) = operator_stack.pop() {
let res = op.expr_from_stack(&mut operand_stack)?;
operand_stack.push(res);
}
Some(ExprType::Multi(operand_stack, false))
}
}
pub fn complete_expression<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
let (rest, (first, args)) = pair(
alt((expression, expression_group)),
many0(pair(
maybe_space(bi_operator),
maybe_newline(cut(err_ctx(
&ParseErrorType::IncompleteExpression,
raw_expr(alt((expression, expression_group))),
))),
)),
)(inp)?;
let expr = if args.is_empty() {
first
} else {
BiOpExpr {
first: set_pos(first, inp),
args,
}
.parse()
.ok_or(nom::Err::Error(
MatchErr::new(inp).ty(&ParseErrorType::IncompleteExpression),
))?
};
Ok((rest, expr))
}
pub fn complete_value_expression<'a, 'b>(
inp: &'a [Token<'b>],
) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
let (rest, (first, args)) = pair(
alt((value_expression, expression_group)),
many0(pair(
maybe_newline(bi_operator),
maybe_newline(cut(err_ctx(
&ParseErrorType::IncompleteExpression,
raw_expr(alt((value_expression, expression_group))),
))),
)),
)(inp)?;
let expr = if args.is_empty() {
first
} else {
BiOpExpr {
first: set_pos(first, inp),
args,
}
.parse()
.ok_or(nom::Err::Error(
MatchErr::new(inp).ty(&ParseErrorType::IncompleteExpression),
))?
};
Ok((rest, expr))
}
pub fn bi_operator<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, BiOperator> {
alt((
value(BiOperator::In, kw_in),
value(BiOperator::Match, kw_match),
value(BiOperator::Multiply, star),
value(BiOperator::IntDivide, pair(slash, slash)),
value(BiOperator::Divide, slash),
value(BiOperator::Modulus, percentage),
value(BiOperator::Add, plus),
value(BiOperator::Substract, dash),
value(BiOperator::And, and),
value(BiOperator::Or, or),
value(BiOperator::Equal, pair(assignment, assignment)),
value(BiOperator::NotEqual, pair(not, assignment)),
value(BiOperator::LessThanEqual, pair(angle_start, assignment)),
value(BiOperator::GreaterThanEqual, pair(angle_end, assignment)),
value(BiOperator::LessThan, angle_start),
value(BiOperator::GreaterThan, angle_end),
))(inp)
}
pub fn nadi_struct_expr<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, NadiStructExpr<RawExpr>> {
let (rest, (name, fields)) = tuple((
variable_name,
delimited(
maybe_space(brace_start),
maybe_newline(newline_separated(tuple((
variable_name,
preceded(
maybe_space(assignment),
maybe_space(raw_expr(complete_expression)),
),
maybe_space(comma),
)))),
maybe_newline(brace_end),
),
))(inp)?;
let mut nstr = NadiStructExpr::with_name(name);
for (fd, val, _) in fields {
nstr.values.insert(fd.into(), val);
}
Ok((rest, nstr))
}
pub fn if_else_expr<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
let (rest, (_, cond, iftrue, iffalse)) = tuple((
kw_if,
maybe_newline(raw_expr(expression_group)),
maybe_newline(raw_expr(expression_block)),
opt(preceded(
maybe_newline(kw_else),
alt((
after_space(raw_expr(if_else_expr)),
maybe_newline(raw_expr(expression_block)),
)),
)),
))(inp)?;
Ok((
rest,
ExprType::IfElse(Box::new(cond), Box::new(iftrue), iffalse.map(Box::new)),
))
}
pub fn try_catch_expr<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
let (rest, (_, try_blk, _, catch_blk)) = tuple((
kw_try,
maybe_newline(raw_expr(expression_block)),
maybe_newline(kw_catch),
maybe_newline(raw_expr(expression_block)),
))(inp)?;
Ok((
rest,
ExprType::TryCatch(Box::new(try_blk), Box::new(catch_blk)),
))
}
pub fn for_each_expr<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
let (rest, (var, parent, expr)) = tuple((
delimited(
kw_for,
after_space(map(variable, |v| v.content.to_string())),
after_space(kw_in),
),
after_space(raw_expr(expression)),
maybe_newline(raw_expr(expression_block)),
))(inp)?;
Ok((
rest,
ExprType::ForEach(var, Box::new(parent), Box::new(expr)),
))
}
pub fn while_expr<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
let (rest, (cond, expr)) = tuple((
preceded(kw_while, after_space(raw_expr(expression_group))),
maybe_newline(raw_expr(expression_block)),
))(inp)?;
Ok((rest, ExprType::While(Box::new(cond), Box::new(expr))))
}
pub fn loop_expr<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
let (rest, expr) = preceded(kw_loop, after_space(raw_expr(expression_block)))(inp)?;
Ok((rest, ExprType::Loop(Box::new(expr))))
}
pub fn maybe_silent_expression<'a, 'b>(
inp: &'a [Token<'b>],
) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
let (rest, (expr, silent)) = pair(complete_expression, opt(maybe_space(semicolon)))(inp)?;
Ok((
rest,
if silent.is_some() {
ExprType::Silent(Box::new(set_pos(expr, inp)))
} else {
expr
},
))
}
pub fn multi_expression<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
map(newline_separated(raw_expr(maybe_silent_expression)), |m| {
ExprType::Multi(m, false)
})(inp)
}
pub fn variable_type<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, VarType> {
let (rest, (kw, prop)) = pair(keyword_val, propagation)(inp)?;
let node = match (&kw, &prop) {
(TaskKeyword::Node, Some(p)) => {
if p.order != PropOrder::default() || p.condition != PropCondition::default() {
return Err(nom::Err::Failure(
MatchErr::new(inp).ty(&ParseErrorType::InvalidPropagation),
));
};
match &p.nodes {
PropNodes::All => None,
PropNodes::List(lst) => {
if let [n] = lst.as_slice() {
Some(n.to_string())
} else {
return Err(nom::Err::Failure(
MatchErr::new(inp).ty(&ParseErrorType::InvalidPropagation),
));
}
}
PropNodes::Path(_) => {
return Err(nom::Err::Failure(
MatchErr::new(inp).ty(&ParseErrorType::InvalidPropagation),
));
}
}
}
_ => None,
};
match VarType::from_keyword(&kw, prop, node) {
Some(v) => Ok((rest, v)),
None => Err(nom::Err::Error(
MatchErr::new(inp).ty(&ParseErrorType::InvalidKeyword),
)),
}
}
pub fn input_variable_only<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, InputVar> {
map(
tuple((opt(terminated(variable_type, dot)), task_dot_variable)),
|(vt, (var, indices))| InputVar::new(vt, var, indices, inp.position()),
)(inp)
}
pub fn input_variable<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
map(
tuple((
opt(terminated(variable_type, dot)),
task_dot_variable,
opt(maybe_space(pair(
question,
opt(maybe_space(raw_expr(alt((expression, expression_group))))),
))),
)),
|(vt, (var, indices), q)| {
let var = InputVar::new(vt, var, indices, inp.position());
if let Some((_, val)) = q {
let var = set_pos(ExprType::Var(var), inp);
if let Some(val) = val {
ExprType::IfElse(
Box::new(set_pos(ExprType::Check(Box::new(var.clone())), inp)),
Box::new(var),
Some(Box::new(val)),
)
} else {
ExprType::Check(Box::new(var))
}
} else {
ExprType::Var(var)
}
},
)(inp)
}
pub fn get_series<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
map(series_variable, ExprType::Series)(inp)
}
pub fn series_variable<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, GetSeries> {
map(
tuple((
opt(variable_type),
tuple((dollar, opt(dollar), variable)),
opt(raw_expr(alt((
delimited(
bracket_start,
maybe_space(value_expression),
maybe_space(bracket_end),
),
array_expr,
)))),
opt(question),
)),
|(vt, (_, ts, var), ind, q)| {
GetSeries::new(vt, ts.is_some(), var.content.to_string(), ind, q.is_some())
},
)(inp)
}
pub fn map_series<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
map(
tuple((
alt((
map(series_variable, |v| vec![v]),
delimited(
paren_start,
separated_list1(maybe_space(comma), maybe_space(series_variable)),
maybe_space(paren_end),
),
)),
maybe_space(pair(path_sep, opt(plus))),
cut(maybe_space(alt((
map(function_def, MapFunction::Defn),
map(
preceded(
at,
separated_list1(dot, map(variable, |v| v.content.to_string())),
),
|v| MapFunction::Pointer(v.join(".")),
),
)))),
)),
|(srs, (_, ac), func)| ExprType::MapSeries(MapSeries::new(srs, func, ac.is_some())),
)(inp)
}
pub fn expr_set_series<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, ExprType<RawExpr>> {
let (rest, (inpvar, ty, _, expr)) = tuple((
series_variable,
opt(maybe_space(preceded(colon, maybe_space(attr_type)))),
maybe_space(assignment),
maybe_space(raw_expr(alt((map_series, complete_value_expression)))),
))(inp)?;
if inpvar.check {
parse_err!(inp, "can't assign a check series expression")
} else {
Ok((rest, ExprType::SetSeries(SetSeries::new(inpvar, ty, expr))))
}
}
pub fn kw_arg<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, (String, RawExpr)> {
separated_pair(
map(variable, |t| t.content.to_string()),
maybe_space(assignment),
cut(err_ctx(
&ParseErrorType::MissingValue,
maybe_space(raw_expr(complete_value_expression)),
)),
)(inp)
}
pub fn kw_args<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, Vec<(String, RawExpr)>> {
separated_list1(
maybe_space(comma),
maybe_newline(alt((
kw_arg,
map(
raw_expr(map(
preceded(pair(star, star), input_variable_only),
ExprType::KwArgs,
)),
|v| (String::new(), v),
),
))),
)(inp)
}
pub fn pos_args<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, Vec<RawExpr>> {
separated_list1(
comma,
maybe_newline(raw_expr(alt((
complete_value_expression,
preceded(star, map(input_variable_only, ExprType::Args)),
)))),
)(inp)
}
pub fn pos_vars<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, Vec<String>> {
separated_list1(
comma,
maybe_newline(map(variable, |v| v.content.to_string())),
)(inp)
}
type FuncDefArgKwarg = (Vec<String>, Vec<(String, RawExpr)>);
type FuncCallArgKwarg = (Vec<RawExpr>, Vec<(String, RawExpr)>);
pub fn funcdef_args<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, FuncDefArgKwarg> {
err_ctx(
&ParseErrorType::InvalidFunctionParameters,
alt((
value(
(vec![], vec![]),
pair(paren_start, maybe_newline(paren_end)),
),
delimited(
paren_start,
map(pos_vars, |a| (a, vec![])),
maybe_newline(paren_end),
),
delimited(
paren_start,
map(kw_args, |a| (vec![], a)),
maybe_newline(paren_end),
),
delimited(
paren_start,
pair(
many1(terminated(
maybe_newline(map(variable, |v| v.content.to_string())),
maybe_newline(comma),
)),
maybe_newline(kw_args),
),
maybe_newline(paren_end),
),
)),
)(inp)
}
pub fn func_args<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, FuncCallArgKwarg> {
err_ctx(
&ParseErrorType::InvalidFunctionParameters,
alt((
value(
(vec![], vec![]),
pair(paren_start, maybe_newline(paren_end)),
),
delimited(
paren_start,
map(pos_args, |a| (a, vec![])),
maybe_newline(paren_end),
),
delimited(
paren_start,
map(kw_args, |a| (vec![], a)),
maybe_newline(paren_end),
),
delimited(
paren_start,
pair(
many1(terminated(
maybe_newline(raw_expr(complete_value_expression)),
maybe_newline(comma),
)),
maybe_newline(kw_args),
),
maybe_newline(paren_end),
),
)),
)(inp)
}
pub fn function_call<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, FunctionCall<RawExpr>> {
let (rest, (ty, name, (args, kwargs))) = tuple((
opt(terminated(variable_type, dot)),
function,
cut(func_args),
))(inp)?;
Ok((
rest,
FunctionCall::new(
ty,
name.content.to_string(),
args,
kwargs.into_iter().collect(),
inp.position(),
),
))
}
pub fn function_body<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, Vec<RawExpr>> {
delimited(
brace_start,
newline_separated(raw_expr(complete_expression)),
cut(err_ctx(
&ParseErrorType::Unclosed("}"),
maybe_newline(brace_end),
)),
)(inp)
}
pub fn function_def<'a, 'b>(inp: &'a [Token<'b>]) -> MatchRes<'a, 'b, UserFunction> {
let (rest, (_, name, (args, kwargs), exprs)) = tuple((
kw_func,
maybe_space(opt(function)),
maybe_space(cut(funcdef_args)),
maybe_newline(raw_expr(map(function_body, |v| ExprType::Multi(v, false)))),
))(inp)?;
Ok((
rest,
UserFunction::new(name.map(|n| n.content.to_string()), args, kwargs, exprs),
))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::attrs::{AttrMap, Attribute, HasAttributes};
use crate::eval::{Eval, EvalCtx, EvalErrorType};
use crate::functions::NadiFunctions;
use crate::network::Network;
use crate::parser::tokenizer::get_tokens;
use crate::tasks::{TaskContext, TaskContextEnv};
use rstest::{fixture, rstest};
use std::collections::HashMap;
use std::sync::{Arc, Mutex, OnceLock};
static mut NADI_FUNCS: OnceLock<NadiFunctions> = OnceLock::new();
#[fixture]
fn context() -> TaskContext {
#[allow(static_mut_refs)]
let functions = unsafe { NADI_FUNCS.get_or_init(NadiFunctions::internals) }.clone();
let (sender, _receiver) = std::sync::mpsc::channel();
let mut ctx = TaskContext {
network: Network::default(),
functions,
udf: HashMap::new(),
structs: HashMap::new(),
env: TaskContextEnv::new(),
hook: Vec::new(),
changed: Arc::new(Mutex::new(false)),
channel: sender,
};
ctx.env.set_attr("xyz", 12.into());
ctx
}
#[rstest]
#[case("12")]
#[case("2.12")]
#[case("- 2.12")]
#[case("xyz")]
#[case("!(xyz)")]
#[case("!(-xyz)")]
pub fn expression_valid_test(#[case] txt: &str) {
let tokens = Token::validate(get_tokens(txt)).unwrap();
let (rest, _) = expression(&tokens).unwrap();
assert_eq!(rest, vec![]);
}
#[rstest]
#[case("12")]
#[case("2.12")]
#[case("- 2.12")]
#[case("xyz")]
#[case("!(xyz)")]
#[case("!(-xyz)")]
#[case("xyz + 12")]
#[should_panic]
#[case("xyz | yzx * 12 + true % func(call)")]
#[case("(xyz | yzx) * (12 + true)")]
#[should_panic]
#[case("(xyz |* yzx) * (12 + true)")]
pub fn compl_expr_valid_test(#[case] txt: &str) {
let tokens = Token::validate(get_tokens(txt)).unwrap();
let (rest, _) = complete_expression(&tokens).unwrap();
assert_eq!(rest, vec![]);
}
#[rstest]
#[case("sth()")]
#[case("sth.sth()")]
#[case("sth.sth(12)")]
#[case("sth.sth(-zyx2)")]
#[case("sth.sth(y=12)")]
#[case("sth.sth(2.12, y=12, y2=43)")]
#[case("sth.sth(2.12, y=12, y2=43 + values * 1.23)")]
#[should_panic]
#[case("sth.sth(2.12, y=12, 43)")]
pub fn function_call_valid_test(#[case] txt: &str) {
let tokens = Token::validate(get_tokens(txt)).unwrap();
let (rest, _) = function_call(&tokens).unwrap();
assert_eq!(rest, vec![]);
}
#[rstest]
#[case("$y -> @some_func")]
#[case("$y -> @sth.sth")]
#[case("$y -> func(x) {x + 1}")]
#[case("($x, inputs$y) -> @sth.sth")]
#[case("($x, $y) -> func(x, y) {x + y}")]
pub fn map_series_valid_test(#[case] txt: &str) {
let tokens = Token::validate(get_tokens(txt)).unwrap();
let (rest, _) = map_series(&tokens).unwrap();
assert_eq!(rest, vec![]);
}
#[rstest]
#[case("node$x = sth()")]
#[case("env$y = sth.sth()")]
#[case("nodes$xyz = $y -> @some_func")]
#[case("nodes$xyz = $y -> @sth.sth")]
#[case("nodes$xyz = $y -> func(x) {x + 1}")]
#[case("nodes$xyz = ($x, inputs$y) -> @sth.sth")]
#[case("nodes$xy = ($x, $y) -> func(x, y) {x + y}")]
pub fn set_series_valid_test(#[case] txt: &str) {
let tokens = Token::validate(get_tokens(txt)).unwrap();
let (rest, _) = expr_set_series(&tokens).unwrap();
assert_eq!(rest, vec![]);
}
#[rstest]
#[case("12", 12.into())]
#[case("2.12", 2.12.into())]
#[case("- 2.12", (-2.12).into())]
#[case("inf - 2.12", (f64::INFINITY).into())]
#[case("xyz", 12.into())]
#[should_panic]
#[case("!(xyz)", 12.into())]
#[should_panic]
#[case("(xyz2)", 12.into())]
#[case("(-xyz)", (-12).into())]
#[case("xyz + 12", 24.into())]
#[case("2*(xyz - 10) + 12", 16.into())]
#[case("(xyz >= 10) | false", true.into())]
#[should_panic]
#[case("(xyz - 1) * (12 + true)", 143.into())]
#[case("1 + 2 * 2", 5.into())]
#[case("1 + 2 * (2 - 5)", (-5).into())]
#[case("(2 - 5) * 2 + 1", (-5).into())]
#[case("(2 - 1) + 1", 2.into())]
#[case("10 // 5 + 2", 4.into())]
pub fn compl_expr_eval_test(context: TaskContext, #[case] txt: &str, #[case] val: Attribute) {
let tokens = Token::validate(get_tokens(txt)).unwrap();
let (rest, expr) = raw_expr(complete_expression)(&tokens).unwrap();
assert_eq!(rest, vec![]);
let ectx = EvalCtx::default();
let mut loc = AttrMap::new();
loc.set_attr("xyz", 12.into());
let res = expr.eval_value(&context, &ectx, &mut loc).unwrap();
assert_eq!(res, val);
}
#[rstest]
#[case("1 + 2 * 2", (5).into())]
#[case("1 + 2 * (2 - 5)", (-5).into())]
#[case("(2 - 5) * 2 + 1", (-5).into())]
#[case("(2 - 1) + 1", (2).into())]
#[case("10 // 5 + 2", (4).into())]
#[case("10 % 5 + 2", (2).into())]
#[case("1 + 2 * (3 - 2)", (3).into())]
#[case("(3 + 2) * 2 - 1", (9).into())]
#[case("((10 - 2) / (2 + 1)) * 3", (8.0).into())]
#[case("2 * (4 - 2) + 3", (7).into())]
#[case("(8 - 6) / 2 + 1", (2.0).into())]
#[case("(9 - 5) * (3 + 1)", (16).into())]
#[case("(7 + 3) * (2 - 1)", (10).into())]
#[case("10 // (2 * 2) + 1", (3).into())]
#[case("(4 + 2) / (3 - 1) + 2", (5.0).into())]
#[case("(8 - 5) * (2 + 1)", (9).into())]
#[case("(6 - 3) / (3 + 1) + 1", (1.75).into())]
#[case("12 // (4 * 2) + 2", (3).into())]
#[case("(10 + 2) * (5 - 3)", (24).into())]
#[case("(9 + 1) / (4 - 2) + 2", (7.0).into())]
#[case("(8 + 6) // (2 * 2) + 1", (4).into())]
#[case("(7 - 3) * (5 + 1)", (24).into())]
#[case("3 * ((6 - 2) / (4 - 1) + 2)", (10.0).into())]
#[case("18 // (9 * 2) + 3", (4).into())]
#[case("(11 + 5) * (8 - 4)", (64).into())]
#[case("(10 + 3) / (7 - 2) + 3", (5.6).into())]
#[case("(10 + 3) % (7 - 2) + 3", (6).into())]
#[case("(9 + 1) // (5 * 2) + 2", (3).into())]
#[case("(8 + 2) * (7 - 1)", (60).into())]
#[case("(7 - 1) / (6 - 2) + 3", (4.5).into())]
#[case("20 // (10 * 2) + 5", (6).into())]
#[case("(13 + 9) * (11 - 5)", (132).into())]
#[case("(12 + 4) / (9 - 1) + 6", (8.0).into())]
#[case("(11 - 3) * (10 + 2)", (96).into())]
#[case("(10 - 2) // (8 - 4) + 5", (7).into())]
#[case("(9 + 3) / (7 - 1) + 6", (8.0).into())]
#[case("(8 + 1) * (7 - 2)", (45).into())]
#[case("(7 + 1) / (6 - 2) + 5", (7.0).into())]
#[case("25 // (13 * 2) + 7", (7).into())]
#[case("(16 + 9) * (15 - 7)", (200).into())]
#[case("(15 + 3) / (12 - 2) + 8", (9.8).into())]
#[case("(14 - 4) * (13 + 1)", (140).into())]
#[case("(13 - 3) // (11 - 1) + 9", (10).into())]
#[case("(12 + 2) * (11 - 2)", (126).into())]
#[case("(11 - 1) / (10 - 2) + 10", (11.25).into())]
#[case("30 // (15 * 2) + 12", (13).into())]
#[case("(19 + 13) * (18 - 9)", (288).into())]
#[case("true & true", true.into())]
#[case("true & (2 < 0.9)", false.into())]
#[case("10 <=9 & true", false.into())]
#[case("false & false", false.into())]
#[case("true | true", true.into())]
#[case("true | false", true.into())]
#[case("false | (xyz > -12)", true.into())]
#[case("false | false", false.into())]
#[case("! true", false.into())]
#[case("! false", true.into())]
#[case("!(true & true)", false.into())]
#[case("!(true & false)", true.into())]
#[case("!!(false & true)", false.into())]
pub fn compl_expr_eval_test_2(context: TaskContext, #[case] txt: &str, #[case] val: Attribute) {
let tokens = Token::validate(get_tokens(txt)).unwrap();
let (rest, expr) = raw_expr(complete_expression)(&tokens).unwrap();
assert_eq!(rest, vec![]);
let ectx = EvalCtx::default();
let mut loc = AttrMap::new();
loc.set_attr("xyz", 12.into());
let res = expr.eval_value(&context, &ectx, &mut loc).unwrap();
assert_eq!(res, val);
}
#[rstest]
#[case("12 + 2", "14")]
#[case("true | false", "true")]
#[case("false | false | true & false", "false")]
#[case("5 + 12 - 2 + 0 * 100", "15")]
#[case("false | (false & true)", "false")]
#[case("true | 12", "true")]
#[case("12 > 12", "false")]
#[case("(xyz >= 10) | false", "(xyz >= 10) | false")]
#[should_panic]
#[case("(xyz - 1) * (12 + true)", "(xyz - 1) * 13")]
pub fn compl_expr_simplify_test(context: TaskContext, #[case] txt: &str, #[case] simpl: &str) {
let tokens = Token::validate(get_tokens(txt)).unwrap();
let (rest, expr) = raw_expr(complete_expression)(&tokens).unwrap();
assert_eq!(rest, vec![]);
let ectx = EvalCtx::default();
let mut loc = AttrMap::new();
loc.set_attr("xyz", 12.into());
let res1 = expr.eval_value(&context, &ectx, &mut loc).unwrap();
let tokens = Token::validate(get_tokens(simpl)).unwrap();
let (rest, expr) = raw_expr(complete_expression)(&tokens).unwrap();
assert_eq!(rest, vec![]);
let ectx = EvalCtx::default();
let res2 = expr.eval_value(&context, &ectx, &mut loc).unwrap();
assert_eq!(res1, res2);
}
#[rstest]
#[case("- true", EvalErrorType::NotANumber)]
#[case("12 | true", EvalErrorType::NotABool)]
#[case("1 * (12 + true)", EvalErrorType::InvalidOperation)]
#[case("xyz * (true + true)", EvalErrorType::InvalidOperation)]
#[case("(xyz + 1) * (true + true)", EvalErrorType::InvalidOperation)]
#[case("(\"1\") * (12 + true)", EvalErrorType::InvalidOperation)]
#[case("(xyz + \"1\") * (12 + true)", EvalErrorType::InvalidOperation)]
pub fn compl_expr_error_test(
context: TaskContext,
#[case] txt: &str,
#[case] err: EvalErrorType,
) {
let tokens = Token::validate(get_tokens(txt)).unwrap();
let (rest, expr) = raw_expr(complete_expression)(&tokens).unwrap();
assert_eq!(rest, vec![]);
let ectx = EvalCtx::default();
let mut loc = AttrMap::new();
loc.set_attr("xyz", 12.into());
let res = expr.eval(&context, &ectx, &mut loc).err().unwrap();
assert_eq!(*res.ty, err);
}
}