use starlark_derive::VisitSpanMut;
use starlark_syntax::slice_vec_ext::SliceExt;
use starlark_syntax::syntax::ast::ClauseP;
use starlark_syntax::syntax::ast::ForClauseP;
use crate::eval::compiler::error::CompilerInternalError;
use crate::eval::compiler::expr::ExprCompiled;
use crate::eval::compiler::expr_bool::ExprCompiledBool;
use crate::eval::compiler::known::list_to_tuple;
use crate::eval::compiler::opt_ctx::OptCtx;
use crate::eval::compiler::scope::payload::CstExpr;
use crate::eval::compiler::scope::payload::CstPayload;
use crate::eval::compiler::span::IrSpanned;
use crate::eval::compiler::stmt::AssignCompiledValue;
use crate::eval::compiler::Compiler;
impl Compiler<'_, '_, '_, '_> {
pub fn list_comprehension(
&mut self,
x: &CstExpr,
for_: &ForClauseP<CstPayload>,
clauses: &[ClauseP<CstPayload>],
) -> Result<ExprCompiled, CompilerInternalError> {
let clauses = self.compile_clauses(for_, clauses)?;
let x = self.expr(x)?;
Ok(ExprCompiled::compr(ComprCompiled::List(
Box::new(x),
clauses,
)))
}
pub fn dict_comprehension(
&mut self,
k: &CstExpr,
v: &CstExpr,
for_: &ForClauseP<CstPayload>,
clauses: &[ClauseP<CstPayload>],
) -> Result<ExprCompiled, CompilerInternalError> {
let clauses = self.compile_clauses(for_, clauses)?;
let k = self.expr(k)?;
let v = self.expr(v)?;
Ok(ExprCompiled::compr(ComprCompiled::Dict(
Box::new((k, v)),
clauses,
)))
}
fn compile_ifs(
&mut self,
clauses: &mut Vec<ClauseP<CstPayload>>,
) -> Result<(Option<ForClauseP<CstPayload>>, Vec<IrSpanned<ExprCompiled>>), CompilerInternalError>
{
let mut ifs = Vec::new();
while let Some(x) = clauses.pop() {
match x {
ClauseP::For(f) => {
ifs.reverse();
return Ok((Some(f), ifs));
}
ClauseP::If(x) => {
let x = self.expr_truth(&x)?;
if let ExprCompiledBool::Const(true) = &x.node {
continue;
}
ifs.push(x.into_expr());
}
}
}
ifs.reverse();
Ok((None, ifs))
}
fn compile_clauses(
&mut self,
for_: &ForClauseP<CstPayload>,
clauses: &[ClauseP<CstPayload>],
) -> Result<ClausesCompiled, CompilerInternalError> {
let over = self.expr(&list_to_tuple(&for_.over))?;
let mut clauses = clauses.to_vec();
let mut res = Vec::new();
loop {
let (next_for, ifs) = self.compile_ifs(&mut clauses)?;
match next_for {
None => {
let last = ClauseCompiled {
var: self.assign_target(&for_.var)?,
over,
ifs,
};
return Ok(ClausesCompiled::new(res, last));
}
Some(f) => {
res.push(ClauseCompiled {
over: self.expr(&f.over)?,
var: self.assign_target(&f.var)?,
ifs,
});
}
}
}
}
}
#[derive(Clone, Debug, VisitSpanMut)]
pub(crate) enum ComprCompiled {
List(Box<IrSpanned<ExprCompiled>>, ClausesCompiled),
Dict(
Box<(IrSpanned<ExprCompiled>, IrSpanned<ExprCompiled>)>,
ClausesCompiled,
),
}
impl ComprCompiled {
pub(crate) fn clauses(&self) -> &ClausesCompiled {
match self {
ComprCompiled::List(_, clauses) => clauses,
ComprCompiled::Dict(_, clauses) => clauses,
}
}
pub(crate) fn optimize(&self, ctx: &mut OptCtx) -> ExprCompiled {
match self {
ComprCompiled::List(ref x, ref clauses) => {
let clauses = clauses.optimize(ctx);
ExprCompiled::compr(ComprCompiled::List(Box::new(x.optimize(ctx)), clauses))
}
ComprCompiled::Dict(k_v, ref clauses) => {
let (k, v) = &**k_v;
let clauses = clauses.optimize(ctx);
ExprCompiled::compr(ComprCompiled::Dict(
Box::new((k.optimize(ctx), v.optimize(ctx))),
clauses.optimize(ctx),
))
}
}
}
}
#[derive(Clone, Debug, VisitSpanMut)]
pub(crate) struct ClauseCompiled {
pub(crate) var: IrSpanned<AssignCompiledValue>,
pub(crate) over: IrSpanned<ExprCompiled>,
pub(crate) ifs: Vec<IrSpanned<ExprCompiled>>,
}
impl ClauseCompiled {
fn optimize(&self, ctx: &mut OptCtx) -> ClauseCompiled {
let ClauseCompiled {
ref var,
ref over,
ref ifs,
} = *self;
ClauseCompiled {
var: var.optimize(ctx),
over: over.optimize(ctx),
ifs: ifs
.iter()
.filter_map(|e| {
let e = e.optimize(ctx);
let e = ExprCompiledBool::new(e);
match &e.node {
ExprCompiledBool::Const(true) => None,
_ => Some(e.into_expr()),
}
})
.collect(),
}
}
}
#[derive(Clone, Debug, VisitSpanMut)]
pub(crate) struct ClausesCompiled {
clauses: Vec<ClauseCompiled>,
}
impl ClausesCompiled {
fn new(clauses: Vec<ClauseCompiled>, last: ClauseCompiled) -> ClausesCompiled {
let mut clauses = clauses;
clauses.push(last);
ClausesCompiled { clauses }
}
pub(crate) fn is_nop(&self) -> bool {
self.split_last().0.over.is_iterable_empty()
}
pub(crate) fn split_last(&self) -> (&ClauseCompiled, &[ClauseCompiled]) {
self.clauses.split_last().unwrap()
}
fn optimize(&self, ctx: &mut OptCtx) -> ClausesCompiled {
ClausesCompiled {
clauses: self.clauses.map(|c| c.optimize(ctx)),
}
}
}