use super::ops::{BinaryOp, UnaryOp};
use crate::named::named;
pub(crate) type At = usize;
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum Literal {
Int(i64),
Uint(u64),
Double(f64),
String(String),
Bool(bool),
Null,
}
named! {
pub(crate) enum Macro {
All = "all",
Exists = "exists",
ExistsOne = "exists_one",
Filter = "filter",
Map = "map",
}
}
impl Macro {
pub(crate) fn signature(self) -> &'static str {
match self {
Self::All => "list.all(x, predicate) → bool",
Self::Exists => "list.exists(x, predicate) → bool",
Self::ExistsOne => "list.exists_one(x, predicate) → bool",
Self::Filter => "list.filter(x, predicate) → list",
Self::Map => "list.map(x, expression) → list",
}
}
pub(crate) fn doc(self) -> &'static str {
match self {
Self::All => "true when every element satisfies the predicate",
Self::Exists => "true when any element satisfies the predicate",
Self::ExistsOne => "true when exactly one element satisfies the predicate",
Self::Filter => "the elements that satisfy the predicate",
Self::Map => {
"each element made into another; `map(x, filter, expression)` keeps those the filter keeps"
}
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum Expr {
Literal(Literal, At),
Ident(String, At),
Select(Box<Expr>, String, At),
Index(Box<Expr>, Box<Expr>, At),
Unary(UnaryOp, Box<Expr>, At),
Binary(BinaryOp, Box<Expr>, Box<Expr>, At),
Call {
target: Option<Box<Expr>>,
name: String,
args: Vec<Expr>,
at: At,
},
And(Box<Expr>, Box<Expr>, At),
Or(Box<Expr>, Box<Expr>, At),
Conditional(Box<Expr>, Box<Expr>, Box<Expr>, At),
List(Vec<Expr>, At),
Map(Vec<(Expr, Expr)>, At),
Has(Box<Expr>, String, At),
Comprehension {
kind: Macro,
range: Box<Expr>,
var: String,
filter: Option<Box<Expr>>,
body: Box<Expr>,
at: At,
},
}
impl Expr {
pub(crate) fn at(&self) -> At {
match self {
Self::Literal(_, at)
| Self::Ident(_, at)
| Self::Select(_, _, at)
| Self::Index(_, _, at)
| Self::Unary(_, _, at)
| Self::Binary(_, _, _, at)
| Self::Call { at, .. }
| Self::And(_, _, at)
| Self::Or(_, _, at)
| Self::Conditional(_, _, _, at)
| Self::List(_, at)
| Self::Map(_, at)
| Self::Has(_, _, at)
| Self::Comprehension { at, .. } => *at,
}
}
pub(crate) fn start(&self) -> At {
match self {
Self::Select(operand, ..)
| Self::Index(operand, ..)
| Self::Binary(_, operand, ..)
| Self::And(operand, ..)
| Self::Or(operand, ..)
| Self::Conditional(operand, ..)
| Self::Call {
target: Some(operand),
..
} => operand.start(),
_ => self.at(),
}
}
pub(crate) fn is_md_call(target: Option<&Self>, name: &str, names: &[&str]) -> bool {
matches!(target, Some(Self::Ident(namespace, _)) if namespace == "md")
&& names.contains(&name)
}
}