use std::convert::TryFrom;
use std::ops::Deref;
use crate::check::constrain::constraint::builder::ConstrBuilder;
use crate::check::constrain::constraint::Constraint;
use crate::check::constrain::constraint::expected::Expect::*;
use crate::check::constrain::constraint::expected::Expected;
use crate::check::constrain::generate::{Constrained, gen_vec, generate};
use crate::check::constrain::generate::env::Environment;
use crate::check::context::clss::{COLLECTION, DICT, LIST, SET, TUPLE};
use crate::check::context::Context;
use crate::check::context::function::{ITER, NEXT};
use crate::check::ident::Identifier;
use crate::check::name::{Any, Empty, Name, Union};
use crate::check::name::string_name::StringName;
use crate::check::result::TypeErr;
use crate::common::position::Position;
use crate::parse::ast::{AST, Node};
pub fn gen_coll(ast: &AST, env: &Environment, ctx: &Context, constr: &mut ConstrBuilder)
-> Constrained {
match &ast.node {
Node::Set { elements } | Node::List { elements } => {
gen_vec(elements, env, false, ctx, constr)?;
gen_col(ast, env, constr)?;
Ok(env.clone())
}
Node::Dict { elements } => {
let elements: Vec<AST> = elements.iter().flat_map(|(from, to)| [from.clone(), to.clone()]).collect();
gen_vec(&elements, env, false, ctx, constr)?;
gen_col(ast, env, constr)?;
Ok(env.clone())
}
Node::Tuple { elements } => {
let res = gen_vec(elements, env, env.is_def_mode, ctx, constr)?;
gen_col(ast, env, constr)?;
Ok(res)
}
Node::DictBuilder { from, to, conditions } => {
let builder_env = gen_builder(ast, from, Some(to), conditions, env, ctx, constr)?;
let set = AST::new(ast.pos, Node::Dict { elements: vec![(from.deref().clone(), to.deref().clone())] });
gen_col(&set, &builder_env, constr)
}
Node::SetBuilder { item, conditions } => {
let builder_env = gen_builder(ast, item, None, conditions, env, ctx, constr)?;
let item = retrieve_nested_builder_item(item);
let set = AST::new(ast.pos, Node::Set { elements: vec![item] });
gen_col(&set, &builder_env, constr)
}
Node::ListBuilder { item, conditions } => {
let builder_env = gen_builder(ast, item, None, conditions, env, ctx, constr)?;
let item = retrieve_nested_builder_item(item);
let set = AST::new(ast.pos, Node::List { elements: vec![item] });
gen_col(&set, &builder_env, constr)
}
_ => Err(vec![TypeErr::new(ast.pos, "Expected collection")]),
}
}
fn retrieve_nested_builder_item(ast: &AST) -> AST {
match &ast.node {
Node::SetBuilder { item, .. } => *item.clone(),
Node::ListBuilder { item, .. } => *item.clone(),
_ => ast.clone()
}
}
fn gen_builder(ast: &AST, item: &AST, pair: Option<&AST>, conditions: &[AST], env: &Environment, ctx: &Context, constr: &mut ConstrBuilder) -> Constrained {
if let Some(cond) = conditions.first() {
let Node::In { left, right } = &cond.node else {
let msg = format!("Expected in, was {}", cond.node);
return Err(vec![TypeErr::new(cond.pos, &msg)]);
};
generate(right, env, ctx, constr)?;
let conds_env = generate(left, &env.is_def_mode(true), ctx, constr)?.is_def_mode(false);
let conds_env = constr_col_lookup(left, right, &conds_env, constr)?;
generate(item, &conds_env, ctx, constr)?;
if let Some(pair) = pair {
generate(pair, &conds_env, ctx, constr)?;
}
if let Some(conditions) = conditions.strip_prefix(&[cond.clone()]) {
for cond in conditions {
generate(cond, &conds_env, ctx, constr)?;
let cond = Expected::from(cond);
constr.add_constr(&Constraint::truthy("comprehension condition", &cond), &conds_env);
}
}
Ok(if env.is_def_mode { conds_env } else { env.clone() })
} else {
Err(vec![TypeErr::new(ast.pos, "Builder must have a least one element")])
}
}
fn gen_col(collection: &AST, env: &Environment, constr: &mut ConstrBuilder) -> Constrained {
let (col_ty, col_items_ty) = match &collection.node {
Node::Set { elements } => (SET, gen_col_items(elements, env, constr)?),
Node::List { elements } => (LIST, gen_col_items(elements, env, constr)?),
Node::Dict { elements } => {
let (mut from_names, mut to_names) = (vec![], vec![]);
for (from, to) in elements {
let exp_from = Expected::from(from);
let name_from = constr.temp_name();
from_names.push(name_from.clone());
let exp_from_ty = Expected::new(from.pos, &Type { name: name_from });
constr.add("collection element", &exp_from_ty, &exp_from, env);
let exp_to = Expected::from(to);
let name_to = constr.temp_name();
to_names.push(name_to.clone());
let exp_to_ty = Expected::new(to.pos, &Type { name: name_to });
constr.add("collection element", &exp_to_ty, &exp_to, env);
}
let from_name = from_names.iter().fold(Name::empty(), |acc, n| acc.union(n));
let to_name = to_names.iter().fold(Name::empty(), |acc, n| acc.union(n));
let col_exp = Type { name: Name::from(&StringName::new(DICT, &[from_name, to_name])) };
let col_exp = Expected::new(collection.pos, &col_exp);
constr.add("collection", &col_exp, &Expected::from(collection), env);
return Ok(env.clone());
}
Node::Tuple { elements } => {
let mut names = vec![];
for element in elements {
let exp_element = Expected::from(element);
let new_name = constr.temp_name();
names.push(new_name.clone());
let exp_ty = Expected::new(element.pos, &Type { name: new_name });
constr.add("collection element", &exp_ty, &exp_element, env);
}
let col_exp = Type { name: Name::from(&StringName::new(TUPLE, &names)) };
let col_exp = Expected::new(collection.pos, &col_exp);
constr.add("collection", &col_exp, &Expected::from(collection), env);
return Ok(env.clone());
}
_ => (COLLECTION, Name::any())
};
let col_exp = Type { name: Name::from(&StringName::new(col_ty, &[col_items_ty])) };
let col_exp = Expected::new(collection.pos, &col_exp);
constr.add("collection", &col_exp, &Expected::from(collection), env);
Ok(env.clone())
}
fn gen_col_items(elements: &[AST], env: &Environment, constr: &mut ConstrBuilder) -> Constrained<Name> {
let mut name = Name::empty();
for element in elements {
let exp_element = Expected::from(element);
let new_name = constr.temp_name();
name = name.union(&new_name);
let exp_ty = Expected::new(element.pos, &Type { name: new_name });
constr.add("collection element", &exp_ty, &exp_element, env);
}
Ok(if name.is_empty() { Name::any() } else { name })
}
pub fn constr_col_lookup(lookup: &AST, col: &AST, env: &Environment, constr: &mut ConstrBuilder)
-> Constrained {
let mut env = env.clone();
let (col_ty, iter_ty) = (constr.temp_name(), constr.temp_name());
let exp_col = Expected::from(col);
let fun = Function { name: StringName::from(ITER), args: vec![exp_col.clone()] };
let col_iterator = Expected::new(exp_col.pos, &Access {
entity: Box::from(exp_col.clone()),
name: Box::new(Expected::new(exp_col.pos, &fun)),
});
let iter_ty = Expected::new(Position::invisible(), &Type { name: iter_ty });
let iter_constr = Constraint::new("iterable", &iter_ty, &col_iterator);
constr.add_constr(&iter_constr, &env);
let fun = Function { name: StringName::from(NEXT), args: vec![iter_ty.clone()] };
let next_access = Access {
entity: Box::from(iter_ty),
name: Box::new(Expected::new(exp_col.pos, &fun)),
};
let next_ty = Expected::new(exp_col.pos, &Type { name: col_ty.clone() });
let next_constr = Constraint::new("iterable", &next_ty, &Expected::new(exp_col.pos, &next_access));
constr.add_constr(&next_constr, &env);
for (mutable, var) in Identifier::try_from(lookup)?.fields(lookup.pos)? {
constr.insert_var(&var);
env = env.insert_var(mutable, &var, &Expected::any(lookup.pos), &constr.var_mapping);
}
let exp_lookup_temp = Expected::new(lookup.pos, &Type { name: col_ty });
constr.add("lookup type", &exp_lookup_temp, &Expected::from(lookup), &env);
Ok(env)
}
#[cfg(test)]
mod tests {
use crate::check::ast::NodeTy;
use crate::check::check_all;
use crate::check::name::Name;
use crate::parse::parse;
#[test]
fn for_col_variable_ty() {
let src = "def a := 0 ..= 2\nfor i in a do\n print(\"hello\")";
let ast = parse(src).unwrap();
let result = check_all(&[*ast]).unwrap();
let statements = if let NodeTy::Block { statements } = &result[0].node {
statements.clone()
} else {
panic!()
};
let (col, expr) = match &statements[1].node {
NodeTy::For { col, expr, .. } => (col.clone(), expr.clone()),
other => panic!("Expected for: {:?}", other)
};
assert_eq!(expr.ty, Some(Name::from("Int")));
assert_eq!(col.ty, Some(Name::from("Int")));
}
}