#![doc(hidden)]
#![allow(clippy::module_name_repetitions)]
use super::upable::Upable;
use super::{
base_expr, is_lit, path_eq, query, replace_last_shadow_use, ArrayPattern,
ArrayPredicatePattern, AssignPattern, BinExpr, BinOpKind, Comprehension, ComprehensionCase,
ConstDoc, EmitExpr, Env, EventPath, Expr, Field, FnDecl, FnDoc, Helper, Ident,
ImutComprehension, ImutComprehensionCase, ImutExpr, ImutExprInt, ImutMatch,
ImutPredicateClause, Invocable, Invoke, InvokeAggr, InvokeAggrFn, List, Literal, LocalPath,
Match, Merge, MetadataPath, ModDoc, NodeMetas, Patch, PatchOperation, Path, Pattern,
PredicateClause, PredicatePattern, Predicates, Record, RecordPattern, Recur, Script, Segment,
StatePath, TestExpr, TuplePattern, UnaryExpr, UnaryOpKind, Warning,
};
use crate::errors::{error_generic, error_oops, ErrorKind, Result};
use crate::impl_expr;
use crate::interpreter::{exec_binary, exec_unary};
use crate::pos::{Location, Range};
use crate::registry::CustomFn;
use crate::tilde::Extractor;
use crate::EventContext;
pub use base_expr::BaseExpr;
use halfbrown::HashMap;
pub use query::*;
use serde::Serialize;
use simd_json::value::borrowed;
use simd_json::{prelude::*, BorrowedValue as Value, KnownKey};
use std::borrow::Cow;
const NO_AGGRS: [InvokeAggrFn<'static>; 0] = [];
const NO_CONSTS: Vec<Value<'static>> = Vec::new();
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct ScriptRaw<'script> {
exprs: ExprsRaw<'script>,
doc: Option<Vec<Cow<'script, str>>>,
}
impl<'script> ScriptRaw<'script> {
pub(crate) fn new(exprs: ExprsRaw<'script>, doc: Option<Vec<Cow<'script, str>>>) -> Self {
Self { exprs, doc }
}
#[allow(clippy::too_many_lines)]
pub(crate) fn up_script<'registry>(
self,
mut helper: &mut Helper<'script, 'registry>,
) -> Result<(Script<'script>, Vec<Warning>)> {
helper
.consts
.insert(vec!["window".to_owned()], WINDOW_CONST_ID);
helper
.consts
.insert(vec!["group".to_owned()], GROUP_CONST_ID);
helper.consts.insert(vec!["args".to_owned()], ARGS_CONST_ID);
helper.const_values = vec![Value::null(); 3];
let mut exprs = vec![];
let last_idx = self.exprs.len() - 1;
for (i, e) in self.exprs.into_iter().enumerate() {
match e {
ExprRaw::Module(m) => {
m.define(&mut helper)?;
}
ExprRaw::Const {
name,
expr,
start,
end,
comment,
} => {
let name_v = vec![name.to_string()];
if helper
.consts
.insert(name_v.clone(), helper.const_values.len())
.is_some()
{
return Err(ErrorKind::DoubleConst(
Range::from((start, end)).expand_lines(2),
Range::from((start, end)),
name.to_string(),
)
.into());
}
let expr = expr.up(&mut helper)?;
if i == last_idx {
exprs.push(Expr::Imut(ImutExprInt::Local {
is_const: true,
idx: helper.const_values.len(),
mid: helper.add_meta_w_name(start, end, &name),
}))
}
let expr = expr.reduce(&helper)?;
let v = reduce2(expr, &helper)?;
let value_type = v.value_type();
helper.const_values.push(v);
helper.docs.consts.push(ConstDoc {
name,
doc: comment
.map(|d| d.iter().map(|l| l.trim()).collect::<Vec<_>>().join("\n")),
value_type,
});
}
#[allow(unreachable_code, unused_variables)]
#[cfg_attr(tarpaulin, skip)]
ExprRaw::FnDecl(f) => {
helper.docs.fns.push(f.doc());
let f = f.up(&mut helper)?;
let f = CustomFn {
name: f.name.id,
args: f.args.iter().map(|i| i.id.to_string()).collect(),
locals: f.locals,
body: f.body,
is_const: false, open: f.open,
inline: f.inline,
};
helper.register_fun(f)?;
}
other => exprs.push(other.up(&mut helper)?),
}
}
if let Some(e) = exprs.last_mut() {
if let Expr::Imut(ImutExprInt::Path(Path::Event(p))) = e {
if p.segments.is_empty() {
let expr = EmitExpr {
mid: p.mid(),
expr: ImutExprInt::Path(Path::Event(p.clone())),
port: None,
};
*e = Expr::Emit(Box::new(expr));
}
}
} else {
let expr = EmitExpr {
mid: 0,
expr: ImutExprInt::Path(Path::Event(EventPath {
mid: 0,
segments: vec![],
})),
port: None,
};
exprs.push(Expr::Emit(Box::new(expr)))
}
helper.docs.module = Some(ModDoc {
name: "self".into(),
doc: self
.doc
.map(|d| d.iter().map(|l| l.trim()).collect::<Vec<_>>().join("\n")),
});
Ok((
Script {
imports: vec![], exprs,
consts: helper.const_values.clone(),
aggregates: helper.aggregates.clone(),
windows: helper.windows.clone(),
locals: helper.locals.len(),
node_meta: helper.meta.clone(),
functions: helper.func_vec.clone(),
docs: helper.docs.clone(),
},
helper.warnings.clone(),
))
}
}
#[derive(Debug, PartialEq, Serialize, Clone)]
pub struct ModuleRaw<'script> {
pub start: Location,
pub end: Location,
pub name: IdentRaw<'script>,
pub exprs: ExprsRaw<'script>,
pub doc: Option<Vec<Cow<'script, str>>>,
}
impl_expr!(ModuleRaw);
impl<'script> ModuleRaw<'script> {
pub(crate) fn define<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<()> {
helper.module.push(self.name.id.to_string());
for e in self.exprs {
match e {
ExprRaw::Module(m) => {
m.define(helper)?;
}
ExprRaw::Const {
name,
expr,
start,
end,
..
} => {
let mut name_v = helper.module.clone();
name_v.push(name.to_string());
if helper.consts.contains_key(&name_v) {
return Err(ErrorKind::DoubleConst(
Range::from((start, end)).expand_lines(2),
Range::from((start, end)),
name.to_string(),
)
.into());
}
helper
.consts
.insert(name_v.clone(), helper.const_values.len());
let expr = expr.up(helper)?;
let v = reduce2(expr, &helper)?;
helper.const_values.push(v);
}
#[allow(unreachable_code, unused_variables)]
#[cfg_attr(tarpaulin, skip)]
ExprRaw::FnDecl(f) => {
let f = f.up(helper)?;
let f = CustomFn {
name: f.name.id,
args: f.args.iter().map(|i| i.id.to_string()).collect(),
locals: f.locals,
body: f.body,
is_const: false, open: f.open,
inline: f.inline,
};
helper.register_fun(f)?;
}
e => {
return error_generic(
&e,
&e,
&"Can't have expressions inside of modules",
&helper.meta,
)
}
}
}
helper.module.pop();
Ok(())
}
}
#[derive(Debug, PartialEq, Serialize, Clone)]
pub struct IdentRaw<'script> {
pub start: Location,
pub end: Location,
pub id: Cow<'script, str>,
}
impl_expr!(IdentRaw);
impl<'script> Upable<'script> for IdentRaw<'script> {
type Target = Ident<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
Ok(Self::Target {
mid: helper.add_meta_w_name(self.start, self.end, &self.id),
id: self.id,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct FieldRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) name: StringLitRaw<'script>,
pub(crate) value: ImutExprRaw<'script>,
}
impl<'script> Upable<'script> for FieldRaw<'script> {
type Target = Field<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let name = ImutExprRaw::String(self.name).up(helper)?;
Ok(Field {
mid: helper.add_meta(self.start, self.end),
name,
value: self.value.up(helper)?,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct RecordRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) fields: FieldsRaw<'script>,
}
impl_expr!(RecordRaw);
impl<'script> Upable<'script> for RecordRaw<'script> {
type Target = Record<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
Ok(Record {
mid: helper.add_meta(self.start, self.end),
fields: self.fields.up(helper)?,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct ListRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) exprs: ImutExprsRaw<'script>,
}
impl_expr!(ListRaw);
impl<'script> Upable<'script> for ListRaw<'script> {
type Target = List<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
Ok(List {
mid: helper.add_meta(self.start, self.end),
exprs: self.exprs.up(helper)?.into_iter().map(ImutExpr).collect(),
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct LiteralRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) value: Value<'script>,
}
impl_expr!(LiteralRaw);
impl<'script> Upable<'script> for LiteralRaw<'script> {
type Target = Literal<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
Ok(Literal {
mid: helper.add_meta(self.start, self.end),
value: self.value,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct StringLitRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) string: Cow<'script, str>,
pub(crate) exprs: ImutExprsRaw<'script>,
}
pub struct StrLitElements<'script>(
pub(crate) Vec<Cow<'script, str>>,
pub(crate) ImutExprsRaw<'script>,
);
impl<'script> From<StrLitElements<'script>> for StringLitRaw<'script> {
fn from(mut es: StrLitElements<'script>) -> StringLitRaw<'script> {
es.0.reverse();
es.1.reverse();
let string = if es.0.len() == 1 {
es.0.pop().unwrap_or_default()
} else {
let mut s = String::new();
for e in es.0 {
s.push_str(&e);
}
s.into()
};
StringLitRaw {
start: Location::default(),
end: Location::default(),
string,
exprs: es.1,
}
}
}
pub(crate) fn reduce2<'script>(
expr: ImutExprInt<'script>,
helper: &Helper,
) -> Result<Value<'script>> {
match expr {
ImutExprInt::Literal(Literal { value: v, .. }) => Ok(v),
ImutExprInt::Local {
is_const: true,
idx,
..
} => Ok(Value::from(idx)),
other => Err(ErrorKind::NotConstant(
other.extent(&helper.meta),
other.extent(&helper.meta).expand_lines(2),
)
.into()),
}
}
impl<'script> ImutExprInt<'script> {
#[allow(clippy::too_many_lines)]
pub(crate) fn reduce(self, helper: &Helper<'script, '_>) -> Result<Self> {
match self {
ImutExprInt::Unary(u) => match *u {
u1
@
UnaryExpr {
expr: ImutExprInt::Literal(_),
..
} => {
let expr = reduce2(u1.expr.clone(), &helper)?;
let value = if let Some(v) = exec_unary(u1.kind, &expr) {
v.into_owned()
} else {
let ex = u1.extent(&helper.meta);
return Err(ErrorKind::InvalidUnary(
ex.expand_lines(2),
ex,
u1.kind,
expr.value_type(),
)
.into());
};
let lit = Literal { mid: u1.mid, value };
Ok(ImutExprInt::Literal(lit))
}
u1 => Ok(ImutExprInt::Unary(Box::new(u1))),
},
ImutExprInt::Binary(b) => {
match *b {
b1
@
BinExpr {
lhs: ImutExprInt::Literal(_),
rhs: ImutExprInt::Literal(_),
..
} => {
let lhs = reduce2(b1.lhs.clone(), &helper)?;
let rhs = reduce2(b1.rhs.clone(), &helper)?;
let value =
exec_binary(&b1, &b1, &helper.meta, b1.kind, &lhs, &rhs)?.into_owned();
let lit = Literal { mid: b1.mid, value };
Ok(ImutExprInt::Literal(lit))
}
b1 => Ok(ImutExprInt::Binary(Box::new(b1))),
}
}
ImutExprInt::List(l) => {
if l.exprs.iter().map(|v| &v.0).all(is_lit) {
let elements: Result<Vec<Value>> =
l.exprs.into_iter().map(|v| reduce2(v.0, &helper)).collect();
Ok(ImutExprInt::Literal(Literal {
mid: l.mid,
value: Value::from(elements?),
}))
} else {
Ok(ImutExprInt::List(l))
}
}
ImutExprInt::Record(r) => {
if r.fields.iter().all(|f| is_lit(&f.name) && is_lit(&f.value)) {
let obj: Result<borrowed::Object> = r
.fields
.into_iter()
.map(|f| {
reduce2(f.name.clone(), &helper).and_then(|n| {
let n = n.as_str().unwrap_or_else(|| unreachable!());
reduce2(f.value, &helper).map(|v| (n.to_owned().into(), v))
})
})
.collect();
Ok(ImutExprInt::Literal(Literal {
mid: r.mid,
value: Value::from(obj?),
}))
} else {
Ok(ImutExprInt::Record(r))
}
}
ImutExprInt::Path(Path::Const(LocalPath {
is_const: true,
segments,
idx,
mid,
})) if segments.is_empty() && idx > LAST_RESERVED_CONST => {
if let Some(v) = helper.const_values.get(idx) {
let lit = Literal {
mid,
value: v.clone(),
};
Ok(ImutExprInt::Literal(lit))
} else {
error_generic(
&Range::from((
helper.meta.start(mid).unwrap_or_default(),
helper.meta.end(mid).unwrap_or_default(),
))
.expand_lines(2),
&Range::from((
helper.meta.start(mid).unwrap_or_default(),
helper.meta.end(mid).unwrap_or_default(),
)),
&format!(
"Invalid const reference to '{}'",
helper.meta.name_dflt(mid),
),
&helper.meta,
)
}
}
ImutExprInt::Invoke1(i)
| ImutExprInt::Invoke2(i)
| ImutExprInt::Invoke3(i)
| ImutExprInt::Invoke(i) => {
if i.invocable.is_const() && i.args.iter().all(|f| is_lit(&f.0)) {
let ex = i.extent(&helper.meta);
let args: Result<Vec<Value<'script>>> =
i.args.into_iter().map(|v| reduce2(v.0, &helper)).collect();
let args = args?;
let args2: Vec<&Value<'script>> = args.iter().collect();
let env = Env {
context: &EventContext::default(),
consts: &NO_CONSTS,
aggrs: &NO_AGGRS,
meta: &helper.meta,
recursion_limit: crate::recursion_limit(),
};
let v = i
.invocable
.invoke(&env, &args2)
.map_err(|e| e.into_err(&ex, &ex, Some(&helper.reg), &helper.meta))?
.into_static();
Ok(ImutExprInt::Literal(Literal {
value: v,
mid: i.mid,
}))
} else {
Ok(match i.args.len() {
1 => ImutExprInt::Invoke1(i),
2 => ImutExprInt::Invoke2(i),
3 => ImutExprInt::Invoke3(i),
_ => ImutExprInt::Invoke(i),
})
}
}
other => Ok(other),
}
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug, PartialEq, Serialize)]
pub enum ExprRaw<'script> {
Const {
name: Cow<'script, str>,
expr: ImutExprRaw<'script>,
start: Location,
end: Location,
comment: Option<Vec<Cow<'script, str>>>,
},
Module(ModuleRaw<'script>),
MatchExpr(Box<MatchRaw<'script>>),
Assign(Box<AssignRaw<'script>>),
Comprehension(Box<ComprehensionRaw<'script>>),
Drop {
start: Location,
end: Location,
},
Emit(Box<EmitExprRaw<'script>>),
FnDecl(AnyFnRaw<'script>),
Imut(ImutExprRaw<'script>),
}
impl<'script> Upable<'script> for ExprRaw<'script> {
type Target = Expr<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
Ok(match self {
ExprRaw::Module(ModuleRaw { start, end, .. }) => {
#[cfg_attr(tarpaulin, skip)]
return Err(ErrorKind::InvalidMod(
Range::from((start, end)).expand_lines(2),
Range::from((start, end)),
)
.into());
}
ExprRaw::Const { start, end, .. } => {
#[cfg_attr(tarpaulin, skip)]
return Err(ErrorKind::InvalidConst(
Range::from((start, end)).expand_lines(2),
Range::from((start, end)),
)
.into());
}
ExprRaw::MatchExpr(m) => Expr::Match(Box::new(m.up(helper)?)),
ExprRaw::Assign(a) => {
let path = a.path.up(helper)?;
let mid = helper.add_meta(a.start, a.end);
match a.expr.up(helper)? {
Expr::Imut(ImutExprInt::Merge(m)) => {
if path_eq(&path, &m.target) {
Expr::MergeInPlace(Box::new(*m))
} else {
Expr::Assign {
mid,
path,
expr: Box::new(ImutExprInt::Merge(m).into()),
}
}
}
Expr::Imut(ImutExprInt::Patch(m)) => {
if path_eq(&path, &m.target) {
Expr::PatchInPlace(Box::new(*m))
} else {
Expr::Assign {
mid,
path,
expr: Box::new(ImutExprInt::Patch(m).into()),
}
}
}
expr => Expr::Assign {
mid,
path,
expr: Box::new(expr),
},
}
}
ExprRaw::Comprehension(c) => Expr::Comprehension(Box::new(c.up(helper)?)),
ExprRaw::Drop { start, end } => {
let mid = helper.add_meta(start, end);
if !helper.can_emit {
return Err(ErrorKind::InvalidDrop(
Range(start, end).expand_lines(2),
Range(start, end),
)
.into());
}
Expr::Drop { mid }
}
ExprRaw::Emit(e) => Expr::Emit(Box::new(e.up(helper)?)),
ExprRaw::Imut(i) => i.up(helper)?.into(),
ExprRaw::FnDecl(f) => {
#[cfg_attr(tarpaulin, skip)]
return Err(ErrorKind::InvalidFn(
f.extent(&helper.meta).expand_lines(2),
f.extent(&helper.meta),
)
.into());
}
})
}
}
impl<'script> BaseExpr for ExprRaw<'script> {
fn mid(&self) -> usize {
0
}
fn s(&self, meta: &NodeMetas) -> Location {
match self {
ExprRaw::Const { start, .. } | ExprRaw::Drop { start, .. } => *start,
ExprRaw::Module(e) => e.s(meta),
ExprRaw::MatchExpr(e) => e.s(meta),
ExprRaw::Assign(e) => e.s(meta),
ExprRaw::Comprehension(e) => e.s(meta),
ExprRaw::Emit(e) => e.s(meta),
ExprRaw::FnDecl(e) => e.s(meta),
ExprRaw::Imut(e) => e.s(meta),
}
}
fn e(&self, meta: &NodeMetas) -> Location {
match self {
ExprRaw::Const { end, .. } | ExprRaw::Drop { end, .. } => *end,
ExprRaw::Module(e) => e.e(meta),
ExprRaw::MatchExpr(e) => e.e(meta),
ExprRaw::Assign(e) => e.e(meta),
ExprRaw::Comprehension(e) => e.e(meta),
ExprRaw::Emit(e) => e.e(meta),
ExprRaw::FnDecl(e) => e.e(meta),
ExprRaw::Imut(e) => e.e(meta),
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct FnDeclRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) name: IdentRaw<'script>,
pub(crate) args: Vec<IdentRaw<'script>>,
pub(crate) body: ExprsRaw<'script>,
pub(crate) doc: Option<Vec<Cow<'script, str>>>,
pub(crate) open: bool,
pub(crate) inline: bool,
}
impl_expr!(FnDeclRaw);
impl<'script> FnDeclRaw<'script> {
pub(crate) fn doc(&self) -> FnDoc<'script> {
FnDoc {
name: self.name.id.clone(),
args: self.args.iter().map(|a| a.id.clone()).collect(),
open: self.open,
doc: self
.doc
.clone()
.map(|d| d.iter().map(|l| l.trim()).collect::<Vec<_>>().join("\n")),
}
}
}
impl<'script> Upable<'script> for FnDeclRaw<'script> {
type Target = FnDecl<'script>;
#[cfg_attr(tarpaulin, skip)]
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let can_emit = helper.can_emit;
let mut aggrs = Vec::new();
let mut locals: HashMap<_, _> = self
.args
.iter()
.enumerate()
.map(|(i, a)| (a.id.to_string(), i))
.collect();
helper.can_emit = false;
helper.is_open = self.open;
helper.fn_argc = self.args.len();
helper.swap2(&mut aggrs, &mut locals);
helper.possible_leaf = true;
let body = self.body.up(helper)?;
helper.possible_leaf = false;
helper.swap2(&mut aggrs, &mut locals);
helper.can_emit = can_emit;
let name = self.name.up(helper)?;
Ok(FnDecl {
mid: helper.add_meta_w_name(self.start, self.end, &name.id),
name,
args: self.args.up(helper)?,
body,
locals: locals.len(),
open: self.open,
inline: self.inline,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub enum AnyFnRaw<'script> {
Match(MatchFnDeclRaw<'script>),
Normal(FnDeclRaw<'script>),
}
impl<'script> AnyFnRaw<'script> {
pub(crate) fn doc(&self) -> FnDoc<'script> {
match self {
AnyFnRaw::Match(f) => f.doc(),
AnyFnRaw::Normal(f) => f.doc(),
}
}
}
impl<'script> Upable<'script> for AnyFnRaw<'script> {
type Target = FnDecl<'script>;
#[cfg_attr(tarpaulin, skip)]
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
match self {
AnyFnRaw::Normal(f) => f.up(helper),
AnyFnRaw::Match(f) => f.up(helper),
}
}
}
impl<'script> BaseExpr for AnyFnRaw<'script> {
fn mid(&self) -> usize {
0
}
fn s(&self, _meta: &NodeMetas) -> Location {
match self {
AnyFnRaw::Match(m) => m.start,
AnyFnRaw::Normal(m) => m.start,
}
}
fn e(&self, _meta: &NodeMetas) -> Location {
match self {
AnyFnRaw::Match(m) => m.end,
AnyFnRaw::Normal(m) => m.end,
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct MatchFnDeclRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) name: IdentRaw<'script>,
pub(crate) args: Vec<IdentRaw<'script>>,
pub(crate) cases: Vec<PredicateClauseRaw<'script>>,
pub(crate) doc: Option<Vec<Cow<'script, str>>>,
pub(crate) open: bool,
pub(crate) inline: bool,
}
impl_expr!(MatchFnDeclRaw);
impl<'script> MatchFnDeclRaw<'script> {
pub(crate) fn doc(&self) -> FnDoc<'script> {
FnDoc {
name: self.name.id.clone(),
args: self.args.iter().map(|a| a.id.clone()).collect(),
open: self.open,
doc: self
.doc
.clone()
.map(|d| d.iter().map(|l| l.trim()).collect::<Vec<_>>().join("\n")),
}
}
}
impl<'script> Upable<'script> for MatchFnDeclRaw<'script> {
type Target = FnDecl<'script>;
#[cfg_attr(tarpaulin, skip)]
fn up<'registry>(mut self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let can_emit = helper.can_emit;
let mut aggrs = Vec::new();
let mut locals = HashMap::new();
let mut consts = HashMap::new();
consts.insert(vec!["args".to_owned()], ARGS_CONST_ID);
for (i, a) in self.args.iter().enumerate() {
locals.insert(a.id.to_string(), i);
}
helper.is_open = self.open;
helper.fn_argc = self.args.len();
helper.can_emit = false;
helper.swap(&mut aggrs, &mut consts, &mut locals);
let target = self
.args
.iter()
.map(|a| {
ImutExprRaw::Path(PathRaw::Local(LocalPathRaw {
start: a.start,
end: a.end,
segments: vec![SegmentRaw::from_id(a.clone())],
}))
})
.collect();
let mut patterns = Vec::new();
std::mem::swap(&mut self.cases, &mut patterns);
let patterns = patterns
.into_iter()
.map(|mut c: PredicateClauseRaw| {
if c.pattern == PatternRaw::Default {
c
} else {
let mut exprs: ExprsRaw<'script> = self
.args
.iter()
.enumerate()
.map(|(i, a)| {
ExprRaw::Assign(Box::new(AssignRaw {
start: c.start,
end: c.end,
path: PathRaw::Local(LocalPathRaw {
start: a.start,
end: a.end,
segments: vec![SegmentRaw::from_id(a.clone())],
}),
expr: ExprRaw::Imut(ImutExprRaw::Path(PathRaw::Local(
LocalPathRaw {
start: a.start,
end: a.end,
segments: vec![
SegmentRaw::from_str(FN_RES_NAME, a.start, a.end),
SegmentRaw::from_usize(i, a.start, a.end),
],
},
))),
}))
})
.collect();
exprs.append(&mut c.exprs);
c.exprs = exprs;
c
}
})
.collect();
let body = ExprRaw::MatchExpr(Box::new(MatchRaw {
start: self.start,
end: self.end,
target: ImutExprRaw::List(Box::new(ListRaw {
start: self.start,
end: self.end,
exprs: target,
})),
patterns,
}));
helper.possible_leaf = true;
let body = body.up(helper)?;
helper.possible_leaf = false;
let body = vec![body];
helper.swap(&mut aggrs, &mut consts, &mut locals);
helper.can_emit = can_emit;
let name = self.name.up(helper)?;
Ok(FnDecl {
mid: helper.add_meta_w_name(self.start, self.end, &name),
name,
args: self.args.up(helper)?,
body,
locals: locals.len(),
open: self.open,
inline: self.inline,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub enum ImutExprRaw<'script> {
Record(Box<RecordRaw<'script>>),
List(Box<ListRaw<'script>>),
Patch(Box<PatchRaw<'script>>),
Merge(Box<MergeRaw<'script>>),
Match(Box<ImutMatchRaw<'script>>),
Comprehension(Box<ImutComprehensionRaw<'script>>),
Path(PathRaw<'script>),
Binary(Box<BinExprRaw<'script>>),
Unary(Box<UnaryExprRaw<'script>>),
Literal(LiteralRaw<'script>),
Invoke(InvokeRaw<'script>),
Present {
path: PathRaw<'script>,
start: Location,
end: Location,
},
String(StringLitRaw<'script>),
Recur(RecurRaw<'script>),
}
impl<'script> Upable<'script> for ImutExprRaw<'script> {
type Target = ImutExprInt<'script>;
#[allow(clippy::too_many_lines)]
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let was_leaf = helper.possible_leaf;
helper.possible_leaf = false;
let r = Ok(match self {
ImutExprRaw::Recur(r) => {
helper.possible_leaf = was_leaf;
ImutExprInt::Recur(r.up(helper)?)
}
ImutExprRaw::Binary(b) => {
ImutExprInt::Binary(Box::new(b.up(helper)?)).reduce(helper)?
}
ImutExprRaw::Unary(u) => ImutExprInt::Unary(Box::new(u.up(helper)?)).reduce(helper)?,
ImutExprRaw::String(mut s) => {
let lit = ImutExprRaw::Literal(LiteralRaw {
start: s.start,
end: s.end,
value: Value::from(s.string),
});
if s.exprs.is_empty() {
lit.up(helper)?
} else {
let mut args = vec![lit];
args.append(&mut s.exprs);
ImutExprRaw::Invoke(InvokeRaw {
start: s.start,
end: s.end,
module: vec!["core".into(), "string".into()],
fun: "format".into(),
args,
})
.up(helper)?
.reduce(helper)?
}
}
ImutExprRaw::Record(r) => ImutExprInt::Record(r.up(helper)?).reduce(helper)?,
ImutExprRaw::List(l) => ImutExprInt::List(l.up(helper)?).reduce(helper)?,
ImutExprRaw::Patch(p) => ImutExprInt::Patch(Box::new(p.up(helper)?)).reduce(helper)?,
ImutExprRaw::Merge(m) => ImutExprInt::Merge(Box::new(m.up(helper)?)).reduce(helper)?,
ImutExprRaw::Present { path, start, end } => ImutExprInt::Present {
path: path.up(helper)?,
mid: helper.add_meta(start, end),
}
.reduce(helper)?,
ImutExprRaw::Path(p) => match p.up(helper)? {
Path::Local(LocalPath {
is_const,
mid,
idx,
ref segments,
}) if segments.is_empty() => ImutExprInt::Local { mid, idx, is_const },
p => ImutExprInt::Path(p),
}
.reduce(helper)?,
ImutExprRaw::Literal(l) => ImutExprInt::Literal(l.up(helper)?).reduce(helper)?,
ImutExprRaw::Invoke(i) => {
if i.is_aggregate(helper) {
ImutExprInt::InvokeAggr(i.into_aggregate().up(helper)?)
} else {
let i = i.up(helper)?;
let i = if i.can_inline() {
i.inline()?
} else {
match i.args.len() {
1 => ImutExprInt::Invoke1(i),
2 => ImutExprInt::Invoke2(i),
3 => ImutExprInt::Invoke3(i),
_ => ImutExprInt::Invoke(i),
}
};
i.reduce(helper)?
}
}
ImutExprRaw::Match(m) => {
helper.possible_leaf = was_leaf;
ImutExprInt::Match(Box::new(m.up(helper)?))
}
ImutExprRaw::Comprehension(c) => ImutExprInt::Comprehension(Box::new(c.up(helper)?)),
});
helper.possible_leaf = was_leaf;
r
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct RecurRaw<'script> {
pub start: Location,
pub end: Location,
pub exprs: ImutExprsRaw<'script>,
}
impl_expr!(RecurRaw);
impl<'script> Upable<'script> for RecurRaw<'script> {
type Target = Recur<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let was_leaf = helper.possible_leaf;
helper.possible_leaf = false;
if !was_leaf {
return Err(ErrorKind::InvalidRecur(
self.extent(&helper.meta).expand_lines(2),
self.extent(&helper.meta),
)
.into());
};
if (helper.is_open && helper.fn_argc < self.exprs.len())
|| (!helper.is_open && helper.fn_argc != self.exprs.len())
{
return error_generic(
&self,
&self,
&format!(
"Wrong number of arguments expected {} but got {}",
helper.fn_argc,
self.exprs.len()
),
&helper.meta,
);
}
let exprs = self.exprs.up(helper)?.into_iter().map(ImutExpr).collect();
helper.possible_leaf = was_leaf;
Ok(Recur {
mid: helper.add_meta(self.start, self.end),
argc: helper.fn_argc,
open: helper.is_open,
exprs,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct EmitExprRaw<'script> {
pub start: Location,
pub end: Location,
pub expr: ImutExprRaw<'script>,
pub port: Option<ImutExprRaw<'script>>,
}
impl_expr!(EmitExprRaw);
impl<'script> Upable<'script> for EmitExprRaw<'script> {
type Target = EmitExpr<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
if !helper.can_emit {
return Err(ErrorKind::InvalidEmit(
self.extent(&helper.meta).expand_lines(2),
self.extent(&helper.meta),
)
.into());
}
Ok(EmitExpr {
mid: helper.add_meta(self.start, self.end),
expr: self.expr.up(helper)?,
port: self.port.up(helper)?,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct AssignRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) path: PathRaw<'script>,
pub(crate) expr: ExprRaw<'script>,
}
impl_expr!(AssignRaw);
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct PredicateClauseRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) pattern: PatternRaw<'script>,
pub(crate) guard: Option<ImutExprRaw<'script>>,
pub(crate) exprs: ExprsRaw<'script>,
}
impl<'script> Upable<'script> for PredicateClauseRaw<'script> {
type Target = PredicateClause<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let was_leaf = helper.possible_leaf;
helper.possible_leaf = false;
let pattern = self.pattern.up(helper)?;
let guard = self.guard.up(helper)?;
helper.possible_leaf = was_leaf;
let exprs = self.exprs.up(helper)?;
if pattern.is_assign() {
helper.end_shadow_var();
}
Ok(PredicateClause {
mid: helper.add_meta(self.start, self.end),
pattern,
guard,
exprs,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct ImutPredicateClauseRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) pattern: PatternRaw<'script>,
pub(crate) guard: Option<ImutExprRaw<'script>>,
pub(crate) exprs: ImutExprsRaw<'script>,
}
impl<'script> Upable<'script> for ImutPredicateClauseRaw<'script> {
type Target = ImutPredicateClause<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let pattern = self.pattern.up(helper)?;
let exprs = self.exprs.up(helper)?.into_iter().map(ImutExpr).collect();
let guard = self.guard.up(helper)?;
if pattern.is_assign() {
helper.end_shadow_var();
}
Ok(ImutPredicateClause {
mid: helper.add_meta(self.start, self.end),
pattern,
guard,
exprs,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct PatchRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) target: ImutExprRaw<'script>,
pub(crate) operations: PatchOperationsRaw<'script>,
}
impl<'script> Upable<'script> for PatchRaw<'script> {
type Target = Patch<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let operations = self.operations.up(helper)?;
Ok(Patch {
mid: helper.add_meta(self.start, self.end),
target: self.target.up(helper)?,
operations,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub enum PatchOperationRaw<'script> {
Insert {
ident: ImutExprRaw<'script>,
expr: ImutExprRaw<'script>,
},
Upsert {
ident: ImutExprRaw<'script>,
expr: ImutExprRaw<'script>,
},
Update {
ident: ImutExprRaw<'script>,
expr: ImutExprRaw<'script>,
},
Erase {
ident: ImutExprRaw<'script>,
},
Copy {
from: ImutExprRaw<'script>,
to: ImutExprRaw<'script>,
},
Move {
from: ImutExprRaw<'script>,
to: ImutExprRaw<'script>,
},
Merge {
ident: ImutExprRaw<'script>,
expr: ImutExprRaw<'script>,
},
TupleMerge {
expr: ImutExprRaw<'script>,
},
}
impl<'script> Upable<'script> for PatchOperationRaw<'script> {
type Target = PatchOperation<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
use PatchOperationRaw::{Copy, Erase, Insert, Merge, Move, TupleMerge, Update, Upsert};
Ok(match self {
Insert { ident, expr } => PatchOperation::Insert {
ident: ident.up(helper)?,
expr: expr.up(helper)?,
},
Upsert { ident, expr } => PatchOperation::Upsert {
ident: ident.up(helper)?,
expr: expr.up(helper)?,
},
Update { ident, expr } => PatchOperation::Update {
ident: ident.up(helper)?,
expr: expr.up(helper)?,
},
Erase { ident } => PatchOperation::Erase {
ident: ident.up(helper)?,
},
Copy { from, to } => PatchOperation::Copy {
from: from.up(helper)?,
to: to.up(helper)?,
},
Move { from, to } => PatchOperation::Move {
from: from.up(helper)?,
to: to.up(helper)?,
},
Merge { ident, expr } => PatchOperation::Merge {
ident: ident.up(helper)?,
expr: expr.up(helper)?,
},
TupleMerge { expr } => PatchOperation::TupleMerge {
expr: expr.up(helper)?,
},
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct MergeRaw<'script> {
pub start: Location,
pub end: Location,
pub target: ImutExprRaw<'script>,
pub expr: ImutExprRaw<'script>,
}
impl<'script> Upable<'script> for MergeRaw<'script> {
type Target = Merge<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
Ok(Merge {
mid: helper.add_meta(self.start, self.end),
target: self.target.up(helper)?,
expr: self.expr.up(helper)?,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct ComprehensionRaw<'script> {
pub start: Location,
pub end: Location,
pub target: ImutExprRaw<'script>,
pub cases: ComprehensionCasesRaw<'script>,
}
impl_expr!(ComprehensionRaw);
impl<'script> Upable<'script> for ComprehensionRaw<'script> {
type Target = Comprehension<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let target = self.target.up(helper)?;
let (key_id, val_id) = helper.reserve_2_shadow();
let cases = self.cases.up(helper)?;
Ok(Comprehension {
mid: helper.add_meta(self.start, self.end),
target,
cases,
key_id,
val_id,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct ImutComprehensionRaw<'script> {
pub start: Location,
pub end: Location,
pub target: ImutExprRaw<'script>,
pub cases: ImutComprehensionCasesRaw<'script>,
}
impl<'script> Upable<'script> for ImutComprehensionRaw<'script> {
type Target = ImutComprehension<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let target = self.target.up(helper)?;
let (key_id, val_id) = helper.reserve_2_shadow();
let cases = self.cases.up(helper)?;
Ok(ImutComprehension {
mid: helper.add_meta(self.start, self.end),
target,
cases,
key_id,
val_id,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct ComprehensionCaseRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) key_name: Cow<'script, str>,
pub(crate) value_name: Cow<'script, str>,
pub(crate) guard: Option<ImutExprRaw<'script>>,
pub(crate) exprs: ExprsRaw<'script>,
}
impl<'script> Upable<'script> for ComprehensionCaseRaw<'script> {
type Target = ComprehensionCase<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let key_idx = helper.register_shadow_var(&self.key_name);
let val_idx = helper.register_shadow_var(&self.value_name);
let guard = self.guard.up(helper)?;
let mut exprs = self.exprs.up(helper)?;
if let Some(expr) = exprs.pop() {
exprs.push(replace_last_shadow_use(
val_idx,
replace_last_shadow_use(key_idx, expr),
));
};
helper.end_shadow_var();
helper.end_shadow_var();
Ok(ComprehensionCase {
mid: helper.add_meta(self.start, self.end),
key_name: self.key_name,
value_name: self.value_name,
guard,
exprs,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct ImutComprehensionCaseRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) key_name: Cow<'script, str>,
pub(crate) value_name: Cow<'script, str>,
pub(crate) guard: Option<ImutExprRaw<'script>>,
pub(crate) exprs: ImutExprsRaw<'script>,
}
impl<'script> Upable<'script> for ImutComprehensionCaseRaw<'script> {
type Target = ImutComprehensionCase<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
helper.register_shadow_var(&self.key_name);
helper.register_shadow_var(&self.value_name);
let guard = self.guard.up(helper)?;
let exprs = self.exprs.up(helper)?.into_iter().map(ImutExpr).collect();
helper.end_shadow_var();
helper.end_shadow_var();
Ok(ImutComprehensionCase {
mid: helper.add_meta(self.start, self.end),
key_name: self.key_name,
value_name: self.value_name,
guard,
exprs,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub enum PatternRaw<'script> {
Record(RecordPatternRaw<'script>),
Array(ArrayPatternRaw<'script>),
Tuple(TuplePatternRaw<'script>),
Expr(ImutExprRaw<'script>),
Assign(AssignPatternRaw<'script>),
DoNotCare,
Default,
}
impl<'script> Upable<'script> for PatternRaw<'script> {
type Target = Pattern<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
use PatternRaw::{Array, Assign, Default, DoNotCare, Expr, Record, Tuple};
Ok(match self {
Record(rp) => Pattern::Record(rp.up(helper)?),
Array(ap) => Pattern::Array(ap.up(helper)?),
Tuple(tp) => Pattern::Tuple(tp.up(helper)?),
Expr(expr) => Pattern::Expr(expr.up(helper)?),
Assign(ap) => Pattern::Assign(ap.up(helper)?),
DoNotCare => Pattern::DoNotCare,
Default => Pattern::Default,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub enum PredicatePatternRaw<'script> {
TildeEq {
assign: Cow<'script, str>,
lhs: Cow<'script, str>,
test: TestExprRaw,
},
Bin {
lhs: Cow<'script, str>,
rhs: ImutExprRaw<'script>,
kind: BinOpKind,
},
RecordPatternEq {
lhs: Cow<'script, str>,
pattern: RecordPatternRaw<'script>,
},
ArrayPatternEq {
lhs: Cow<'script, str>,
pattern: ArrayPatternRaw<'script>,
},
FieldPresent {
lhs: Cow<'script, str>,
},
FieldAbsent {
lhs: Cow<'script, str>,
},
}
impl<'script> Upable<'script> for PredicatePatternRaw<'script> {
type Target = PredicatePattern<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
use PredicatePatternRaw::{
ArrayPatternEq, Bin, FieldAbsent, FieldPresent, RecordPatternEq, TildeEq,
};
Ok(match self {
TildeEq { assign, lhs, test } => PredicatePattern::TildeEq {
assign,
key: KnownKey::from(lhs.clone()),
lhs,
test: Box::new(test.up(helper)?),
},
Bin { lhs, rhs, kind } => PredicatePattern::Bin {
key: KnownKey::from(lhs.clone()),
lhs,
rhs: rhs.up(helper)?,
kind,
},
RecordPatternEq { lhs, pattern } => PredicatePattern::RecordPatternEq {
key: KnownKey::from(lhs.clone()),
lhs,
pattern: pattern.up(helper)?,
},
ArrayPatternEq { lhs, pattern } => PredicatePattern::ArrayPatternEq {
key: KnownKey::from(lhs.clone()),
lhs,
pattern: pattern.up(helper)?,
},
FieldPresent { lhs } => PredicatePattern::FieldPresent {
key: KnownKey::from(lhs.clone()),
lhs,
},
FieldAbsent { lhs } => PredicatePattern::FieldAbsent {
key: KnownKey::from(lhs.clone()),
lhs,
},
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct RecordPatternRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) fields: PatternFieldsRaw<'script>,
}
impl<'script> Upable<'script> for RecordPatternRaw<'script> {
type Target = RecordPattern<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let fields = self.fields.up(helper)?;
let present_fields: Vec<Cow<str>> = fields
.iter()
.filter_map(|f| {
if let PredicatePattern::FieldPresent { lhs, .. } = f {
Some(lhs.clone())
} else {
None
}
})
.collect();
let absent_fields: Vec<Cow<str>> = fields
.iter()
.filter_map(|f| {
if let PredicatePattern::FieldAbsent { lhs, .. } = f {
Some(lhs.clone())
} else {
None
}
})
.collect();
for present in &present_fields {
let duplicated = fields.iter().any(|f| {
if let PredicatePattern::FieldPresent { .. } = f {
false
} else {
f.lhs() == present
}
});
if duplicated {
let extent = (self.start, self.end).into();
helper.warnings.push(Warning {
inner: extent,
outer: extent.expand_lines(2),
msg: format!("The field {} is checked with both present and another extractor, this is redundant as extractors imply presence. It may also overwrite the result of th extractor.", present),
})
}
}
for absent in &absent_fields {
let duplicated = fields.iter().any(|f| {
if let PredicatePattern::FieldAbsent { .. } = f {
false
} else {
f.lhs() == absent
}
});
if duplicated {
let extent = (self.start, self.end).into();
helper.warnings.push(Warning {
inner: extent,
outer: extent.expand_lines(2),
msg: format!("The field {} is checked with both absence and another extractor, this test can never be true.", absent),
})
}
}
Ok(RecordPattern {
mid: helper.add_meta(self.start, self.end),
fields,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub enum ArrayPredicatePatternRaw<'script> {
Expr(ImutExprRaw<'script>),
Tilde(TestExprRaw),
Record(RecordPatternRaw<'script>),
Ignore,
}
impl<'script> Upable<'script> for ArrayPredicatePatternRaw<'script> {
type Target = ArrayPredicatePattern<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
use ArrayPredicatePatternRaw::{Expr, Ignore, Record, Tilde};
Ok(match self {
Expr(expr) => ArrayPredicatePattern::Expr(expr.up(helper)?),
Tilde(te) => ArrayPredicatePattern::Tilde(te.up(helper)?),
Record(rp) => ArrayPredicatePattern::Record(rp.up(helper)?),
Ignore => ArrayPredicatePattern::Ignore,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct ArrayPatternRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) exprs: ArrayPredicatePatternsRaw<'script>,
}
impl<'script> Upable<'script> for ArrayPatternRaw<'script> {
type Target = ArrayPattern<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let exprs = self.exprs.up(helper)?;
Ok(ArrayPattern {
mid: helper.add_meta(self.start, self.end),
exprs,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct TuplePatternRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) exprs: ArrayPredicatePatternsRaw<'script>,
pub(crate) open: bool,
}
impl<'script> Upable<'script> for TuplePatternRaw<'script> {
type Target = TuplePattern<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let exprs = self.exprs.up(helper)?;
Ok(TuplePattern {
mid: helper.add_meta(self.start, self.end),
exprs,
open: self.open,
})
}
}
pub(crate) const FN_RES_NAME: &str = "__fn_assign_this_is_ugly";
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct AssignPatternRaw<'script> {
pub(crate) id: Cow<'script, str>,
pub(crate) pattern: Box<PatternRaw<'script>>,
}
impl<'script> Upable<'script> for AssignPatternRaw<'script> {
type Target = AssignPattern<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
Ok(AssignPattern {
idx: helper.register_shadow_var(&self.id),
id: self.id,
pattern: Box::new(self.pattern.up(helper)?),
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub enum PathRaw<'script> {
Local(LocalPathRaw<'script>),
Event(EventPathRaw<'script>),
State(StatePathRaw<'script>),
Meta(MetadataPathRaw<'script>),
Const(ConstPathRaw<'script>),
}
impl<'script> Upable<'script> for PathRaw<'script> {
type Target = Path<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
use PathRaw::{Const, Event, Local, Meta, State};
Ok(match self {
Local(p) => {
let p = p.up(helper)?;
if p.is_const {
Path::Const(p)
} else {
Path::Local(p)
}
}
Const(p) => Path::Const(p.up(helper)?),
Event(p) => Path::Event(p.up(helper)?),
State(p) => Path::State(p.up(helper)?),
Meta(p) => Path::Meta(p.up(helper)?),
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct SegmentRangeRaw<'script> {
pub(crate) start_lower: Location,
pub(crate) range_start: ImutExprRaw<'script>,
pub(crate) end_lower: Location,
pub(crate) start_upper: Location,
pub(crate) range_end: ImutExprRaw<'script>,
pub(crate) end_upper: Location,
}
impl<'script> Upable<'script> for SegmentRangeRaw<'script> {
type Target = Segment<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let SegmentRangeRaw {
start_lower,
range_start,
end_lower,
start_upper,
range_end,
end_upper,
} = self;
let lower_mid = helper.add_meta(start_lower, end_lower);
let upper_mid = helper.add_meta(start_upper, end_upper);
let mid = helper.add_meta(start_lower, end_upper);
Ok(Segment::Range {
lower_mid,
upper_mid,
range_start: Box::new(range_start.up(helper)?),
range_end: Box::new(range_end.up(helper)?),
mid,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct SegmentElementRaw<'script> {
pub(crate) expr: ImutExprRaw<'script>,
pub(crate) start: Location,
pub(crate) end: Location,
}
impl<'script> Upable<'script> for SegmentElementRaw<'script> {
type Target = Segment<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let SegmentElementRaw { expr, start, end } = self;
let expr = expr.up(helper)?;
let r = expr.extent(&helper.meta);
match expr {
ImutExprInt::Literal(l) => match reduce2(ImutExprInt::Literal(l), &helper)? {
Value::String(id) => {
let mid = helper.add_meta_w_name(start, end, &id);
Ok(Segment::Id {
key: KnownKey::from(id.clone()),
mid,
})
}
other => {
if let Some(idx) = other.as_usize() {
let mid = helper.add_meta(start, end);
Ok(Segment::Idx { idx, mid })
} else {
Err(ErrorKind::TypeConflict(
r.expand_lines(2),
r,
other.value_type(),
vec![ValueType::I64, ValueType::String],
)
.into())
}
}
},
expr => Ok(Segment::Element {
mid: helper.add_meta(start, end),
expr,
}),
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub enum SegmentRaw<'script> {
Element(Box<SegmentElementRaw<'script>>),
Range(Box<SegmentRangeRaw<'script>>),
}
impl<'script> Upable<'script> for SegmentRaw<'script> {
type Target = Segment<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
match self {
SegmentRaw::Element(e) => e.up(helper),
SegmentRaw::Range(r) => r.up(helper),
}
}
}
impl<'script> SegmentRaw<'script> {
pub fn from_id(id: IdentRaw<'script>) -> Self {
SegmentRaw::Element(Box::new(SegmentElementRaw {
start: id.start,
end: id.end,
expr: ImutExprRaw::Literal(LiteralRaw {
start: id.start,
end: id.end,
value: Value::from(id.id),
}),
}))
}
pub fn from_str(id: &'script str, start: Location, end: Location) -> Self {
SegmentRaw::Element(Box::new(SegmentElementRaw {
start,
end,
expr: ImutExprRaw::Literal(LiteralRaw {
start,
end,
value: Value::from(id),
}),
}))
}
pub fn from_usize(id: usize, start: Location, end: Location) -> Self {
SegmentRaw::Element(Box::new(SegmentElementRaw {
start,
end,
expr: ImutExprRaw::Literal(LiteralRaw {
start,
end,
value: Value::from(id),
}),
}))
}
}
impl<'script> From<ImutExprRaw<'script>> for ExprRaw<'script> {
fn from(imut: ImutExprRaw<'script>) -> ExprRaw<'script> {
ExprRaw::Imut(imut)
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct ConstPathRaw<'script> {
pub(crate) module: Vec<IdentRaw<'script>>,
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) segments: SegmentsRaw<'script>,
}
impl_expr!(ConstPathRaw);
impl<'script> Upable<'script> for ConstPathRaw<'script> {
type Target = LocalPath<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let segments = self.segments.up(helper)?;
let mut segments = segments.into_iter();
if let Some(Segment::Id { mid, .. }) = segments.next() {
let segments = segments.collect();
let id = helper.meta.name_dflt(mid);
let mid = helper.add_meta_w_name(self.start, self.end, &id);
let mut module_direct: Vec<String> =
self.module.iter().map(|m| m.id.to_string()).collect();
let mut module = helper.module.clone();
module.append(&mut module_direct);
module.push(id);
if let Some(idx) = helper.is_const(&module) {
Ok(LocalPath {
is_const: true,
idx: *idx,
mid,
segments,
})
} else {
error_generic(
&(self.start, self.end),
&(self.start, self.end),
&format!(
"The constant {} (absolute path) does is not defined.",
module.join("::")
),
&helper.meta,
)
}
} else {
error_oops(
&(self.start, self.end),
0xdead_0007,
"Empty local path",
&helper.meta,
)
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct LocalPathRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) segments: SegmentsRaw<'script>,
}
impl_expr!(LocalPathRaw);
impl<'script> Upable<'script> for LocalPathRaw<'script> {
type Target = LocalPath<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let segments = self.segments.up(helper)?;
let mut segments = segments.into_iter();
if let Some(Segment::Id { mid, .. }) = segments.next() {
let segments = segments.collect();
let id = helper.meta.name_dflt(mid);
let mid = helper.add_meta_w_name(self.start, self.end, &id);
let mut rel_path = if id != "args" && id != "window" && id != "group" {
helper.module.clone()
} else {
vec![]
};
rel_path.push(id.to_string());
if let Some(idx) = helper.is_const(&rel_path) {
Ok(LocalPath {
is_const: true,
idx: *idx,
mid,
segments,
})
} else {
let idx = helper.var_id(&id);
Ok(LocalPath {
is_const: false,
idx,
mid,
segments,
})
}
} else {
error_oops(
&(self.start, self.end),
0xdead_0008,
"Empty local path",
&helper.meta,
)
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct MetadataPathRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) segments: SegmentsRaw<'script>,
}
impl<'script> Upable<'script> for MetadataPathRaw<'script> {
type Target = MetadataPath<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let segments = self.segments.up(helper)?;
Ok(MetadataPath {
mid: helper.add_meta(self.start, self.end),
segments,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct EventPathRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) segments: SegmentsRaw<'script>,
}
impl<'script> Upable<'script> for EventPathRaw<'script> {
type Target = EventPath<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let segments = self.segments.up(helper)?;
Ok(EventPath {
mid: helper.add_meta(self.start, self.end),
segments,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct StatePathRaw<'script> {
pub start: Location,
pub end: Location,
pub segments: SegmentsRaw<'script>,
}
impl<'script> Upable<'script> for StatePathRaw<'script> {
type Target = StatePath<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let segments = self.segments.up(helper)?;
Ok(StatePath {
mid: helper.add_meta(self.start, self.end),
segments,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct BinExprRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) kind: BinOpKind,
pub(crate) lhs: ImutExprRaw<'script>,
pub(crate) rhs: ImutExprRaw<'script>,
}
impl<'script> Upable<'script> for BinExprRaw<'script> {
type Target = BinExpr<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
Ok(BinExpr {
mid: helper.add_meta(self.start, self.end),
kind: self.kind,
lhs: self.lhs.up(helper)?,
rhs: self.rhs.up(helper)?,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct UnaryExprRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) kind: UnaryOpKind,
pub(crate) expr: ImutExprRaw<'script>,
}
impl<'script> Upable<'script> for UnaryExprRaw<'script> {
type Target = UnaryExpr<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
Ok(UnaryExpr {
mid: helper.add_meta(self.start, self.end),
kind: self.kind,
expr: self.expr.up(helper)?,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct MatchRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) target: ImutExprRaw<'script>,
pub(crate) patterns: PredicatesRaw<'script>,
}
impl_expr!(MatchRaw);
impl<'script> Upable<'script> for MatchRaw<'script> {
type Target = Match<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let patterns: Predicates = self
.patterns
.into_iter()
.map(|v| v.up(helper))
.collect::<Result<_>>()?;
let defaults = patterns
.iter()
.filter(|p| {
p.pattern.is_default() || (p.pattern == Pattern::Default && p.guard.is_none())
})
.count();
match defaults {
0 => helper.warnings.push(Warning{
outer: Range(self.start, self.end),
inner: Range(self.start, self.end),
msg: "This match expression has no default clause, if the other clauses do not cover all possibilities this will lead to events being discarded with runtime errors.".into()
}),
x if x > 1 => helper.warnings.push(Warning{
outer: Range(self.start, self.end),
inner: Range(self.start, self.end),
msg: "A match statement with more then one default clause will never reach any but the first default clause.".into()
}),
_ => ()
}
Ok(Match {
mid: helper.add_meta(self.start, self.end),
target: self.target.up(helper)?,
patterns,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct ImutMatchRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) target: ImutExprRaw<'script>,
pub(crate) patterns: ImutPredicatesRaw<'script>,
}
impl<'script> Upable<'script> for ImutMatchRaw<'script> {
type Target = ImutMatch<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let patterns = self.patterns.up(helper)?;
let defaults = patterns.iter().filter(|p| p.pattern.is_default()).count();
match defaults {
0 => helper.warnings.push(Warning{
outer: Range(self.start, self.end),
inner: Range(self.start, self.end),
msg: "This match expression has no default clause, if the other clauses do not cover all possibilities this will lead to events being discarded with runtime errors.".into()
}),
x if x > 1 => helper.warnings.push(Warning{
outer: Range(self.start, self.end),
inner: Range(self.start, self.end),
msg: "A match statement with more then one default clause will never reach any but the first default clause.".into()
}),
_ => ()
}
let was_leaf = helper.possible_leaf;
helper.possible_leaf = false;
let r = Ok(ImutMatch {
mid: helper.add_meta(self.start, self.end),
target: self.target.up(helper)?,
patterns,
});
helper.possible_leaf = was_leaf;
r
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct InvokeRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) module: Vec<String>,
pub(crate) fun: String,
pub(crate) args: ImutExprsRaw<'script>,
}
impl_expr!(InvokeRaw);
impl<'script> Upable<'script> for InvokeRaw<'script> {
type Target = Invoke<'script>;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
if self.module.get(0) == Some(&String::from("core")) && self.module.len() == 2 {
let module = self.module.get(1).cloned().unwrap_or_default();
let invocable = helper
.reg
.find(&module, &self.fun)
.map_err(|e| e.into_err(&self, &self, Some(&helper.reg), &helper.meta))?;
let args = self.args.up(helper)?.into_iter().map(ImutExpr).collect();
let mf = format!("{}::{}", self.module.join("::"), self.fun);
Ok(Invoke {
mid: helper.add_meta_w_name(self.start, self.end, &mf),
module: self.module,
fun: self.fun,
invocable: Invocable::Intrinsic(invocable.clone()),
args,
})
} else {
let mut abs_module = helper.module.clone();
abs_module.extend_from_slice(&self.module);
abs_module.push(self.fun.clone());
if let Some(f) = helper.functions.get(&abs_module) {
if let Some(f) = helper.func_vec.get(*f) {
let invocable = Invocable::Tremor(f.clone());
let args = self.args.up(helper)?.into_iter().map(ImutExpr).collect();
let mf = abs_module.join("::");
Ok(Invoke {
mid: helper.add_meta_w_name(self.start, self.end, &mf),
module: self.module,
fun: self.fun,
invocable,
args,
})
} else {
let inner: Range = (self.start, self.end).into();
let outer: Range = inner.expand_lines(3);
Err(
ErrorKind::MissingFunction(outer, inner, self.module, self.fun, None)
.into(),
)
}
} else {
let inner: Range = (self.start, self.end).into();
let outer: Range = inner.expand_lines(3);
Err(ErrorKind::MissingFunction(outer, inner, self.module, self.fun, None).into())
}
}
}
}
impl<'script> InvokeRaw<'script> {
fn is_aggregate<'registry>(&self, helper: &mut Helper<'script, 'registry>) -> bool {
if self.module.get(0) == Some(&String::from("aggr")) && self.module.len() == 2 {
let module = self.module.get(1).cloned().unwrap_or_default();
helper.aggr_reg.find(&module, &self.fun).is_ok()
} else {
false
}
}
fn into_aggregate(self) -> InvokeAggrRaw<'script> {
let module = self.module.get(1).cloned().unwrap_or_default();
InvokeAggrRaw {
start: self.start,
end: self.end,
module,
fun: self.fun,
args: self.args,
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct InvokeAggrRaw<'script> {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) module: String,
pub(crate) fun: String,
pub(crate) args: ImutExprsRaw<'script>,
}
impl_expr!(InvokeAggrRaw);
impl<'script> Upable<'script> for InvokeAggrRaw<'script> {
type Target = InvokeAggr;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
if helper.is_in_aggr {
return Err(ErrorKind::AggrInAggr(
self.extent(&helper.meta),
self.extent(&helper.meta).expand_lines(2),
)
.into());
};
helper.is_in_aggr = true;
let invocable = helper
.aggr_reg
.find(&self.module, &self.fun)
.map_err(|e| e.into_err(&self, &self, Some(&helper.reg), &helper.meta))?
.clone();
if !invocable.valid_arity(self.args.len()) {
return Err(ErrorKind::BadArity(
self.extent(&helper.meta),
self.extent(&helper.meta).expand_lines(2),
self.module.clone(),
self.fun.clone(),
invocable.arity(),
self.args.len(),
)
.into());
}
if let Some(warning) = invocable.warning() {
helper.warnings.push(Warning {
inner: self.extent(&helper.meta),
outer: self.extent(&helper.meta),
msg: warning,
});
}
let aggr_id = helper.aggregates.len();
let args = self.args.up(helper)?.into_iter().map(ImutExpr).collect();
let mf = format!("{}::{}", self.module, self.fun);
let invoke_meta_id = helper.add_meta_w_name(self.start, self.end, &mf);
helper.aggregates.push(InvokeAggrFn {
mid: invoke_meta_id,
invocable,
args,
module: self.module.clone(),
fun: self.fun.clone(),
});
helper.is_in_aggr = false;
let aggr_meta_id = helper.add_meta_w_name(self.start, self.end, &mf);
Ok(InvokeAggr {
mid: aggr_meta_id,
module: self.module,
fun: self.fun,
aggr_id,
})
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct TestExprRaw {
pub(crate) start: Location,
pub(crate) end: Location,
pub(crate) id: String,
pub(crate) test: String,
}
impl<'script> Upable<'script> for TestExprRaw {
type Target = TestExpr;
fn up<'registry>(self, helper: &mut Helper<'script, 'registry>) -> Result<Self::Target> {
let mid = helper.add_meta(self.start, self.end);
match Extractor::new(&self.id, &self.test) {
Ok(ex) => Ok(TestExpr {
id: self.id,
test: self.test,
extractor: ex,
mid,
}),
Err(e) => Err(ErrorKind::InvalidExtractor(
self.extent(&helper.meta).expand_lines(2),
self.extent(&helper.meta),
self.id,
self.test,
e.msg,
)
.into()),
}
}
}
pub type ExprsRaw<'script> = Vec<ExprRaw<'script>>;
pub type ImutExprsRaw<'script> = Vec<ImutExprRaw<'script>>;
pub type FieldsRaw<'script> = Vec<FieldRaw<'script>>;
pub type SegmentsRaw<'script> = Vec<SegmentRaw<'script>>;
pub type PatternFieldsRaw<'script> = Vec<PredicatePatternRaw<'script>>;
pub type PredicatesRaw<'script> = Vec<PredicateClauseRaw<'script>>;
pub type ImutPredicatesRaw<'script> = Vec<ImutPredicateClauseRaw<'script>>;
pub type PatchOperationsRaw<'script> = Vec<PatchOperationRaw<'script>>;
pub type ComprehensionCasesRaw<'script> = Vec<ComprehensionCaseRaw<'script>>;
pub type ImutComprehensionCasesRaw<'script> = Vec<ImutComprehensionCaseRaw<'script>>;
pub type ArrayPredicatePatternsRaw<'script> = Vec<ArrayPredicatePatternRaw<'script>>;
pub type WithExprsRaw<'script> = Vec<(IdentRaw<'script>, ImutExprRaw<'script>)>;