use crate::expr::Env; use crate::expr::Expr; use crate::expr::Pattern; use roc_collections::all::{MutMap, MutSet}; use roc_module::ident::TagName; use roc_module::symbol::Symbol; use crate::layout::Builtin; use crate::layout::Layout; /// COMPILE CASES type Label = u64; /// Users of this module will mainly interact with this function. It takes /// some normal branches and gives out a decision tree that has "labels" at all /// the leafs and a dictionary that maps these "labels" to the code that should /// run. pub fn compile(raw_branches: Vec<(Pattern<'_>, u64)>) -> DecisionTree { let formatted = raw_branches .into_iter() .map(|(pattern, index)| Branch { goal: index, patterns: vec![(Path::Empty, pattern)], }) .collect(); to_decision_tree(formatted) } #[derive(Clone, Debug, PartialEq)] pub enum DecisionTree<'a> { Match(Label), Decision { path: Path, edges: Vec<(Test<'a>, DecisionTree<'a>)>, default: Option>>, }, } #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum Test<'a> { IsCtor { tag_id: u8, tag_name: TagName, union: crate::pattern::Union, arguments: Vec>, }, IsInt(i64), // float patterns are stored as u64 so they are comparable/hashable IsFloat(u64), IsStr(Box), IsBit(bool), IsByte { tag_id: u8, num_alts: usize, }, } #[derive(Clone, Debug, PartialEq)] pub enum Path { Index { index: u64, path: Box }, Unbox(Box), Empty, } // ACTUALLY BUILD DECISION TREES #[derive(Clone, Debug, PartialEq)] struct Branch<'a> { goal: Label, patterns: Vec<(Path, Pattern<'a>)>, } fn to_decision_tree(raw_branches: Vec) -> DecisionTree { let branches: Vec<_> = raw_branches.into_iter().map(flatten_patterns).collect(); match check_for_match(&branches) { Some(goal) => DecisionTree::Match(goal), None => { // TODO remove clone let path = pick_path(branches.clone()); let (edges, fallback) = gather_edges(branches, &path); let mut decision_edges: Vec<_> = edges .into_iter() .map(|(a, b)| (a, to_decision_tree(b))) .collect(); match (decision_edges.split_last_mut(), fallback.split_last()) { (Some(((_tag, decision_tree), rest)), None) if rest.is_empty() => { // TODO remove clone decision_tree.clone() } (_, None) => DecisionTree::Decision { path, edges: decision_edges, default: None, }, (None, Some(_)) => to_decision_tree(fallback), _ => DecisionTree::Decision { path, edges: decision_edges, default: Some(Box::new(to_decision_tree(fallback))), }, } } } } fn is_complete(tests: &[Test]) -> bool { let length = tests.len(); debug_assert!(length > 0); match tests.get(length - 1) { None => unreachable!("should never happen"), Some(v) => match v { Test::IsCtor { union, .. } => length == union.alternatives.len(), Test::IsByte { num_alts, .. } => length == *num_alts, Test::IsBit(_) => length == 2, Test::IsInt(_) => false, Test::IsFloat(_) => false, Test::IsStr(_) => false, }, } } fn flatten_patterns(branch: Branch) -> Branch { let mut result = Vec::with_capacity(branch.patterns.len()); for path_pattern in branch.patterns { flatten(path_pattern, &mut result); } Branch { goal: branch.goal, patterns: result, } } fn flatten<'a>(path_pattern: (Path, Pattern<'a>), path_patterns: &mut Vec<(Path, Pattern<'a>)>) { match &path_pattern.1 { Pattern::AppliedTag { union, .. } => { if union.alternatives.len() == 1 { // case map dearg ctorArgs of // [arg] -> // flatten (Unbox path, arg) otherPathPatterns // // args -> // foldr flatten otherPathPatterns (subPositions path args) // subPositions :: Path -> [Can.Pattern] -> [(Path, Can.Pattern)] // subPositions path patterns = // Index.indexedMap (\index pattern -> (Index index path, pattern)) patterns // // // dearg :: Can.PatternCtorArg -> Can.Pattern // dearg (Can.PatternCtorArg _ _ pattern) = // pattern todo!() } else { path_patterns.push(path_pattern); } } _ => { path_patterns.push(path_pattern); } } } /// SUCCESSFULLY MATCH /// If the first branch has no more "decision points" we can finally take that /// path. If that is the case we give the resulting label and a mapping from free /// variables to "how to get their value". So a pattern like (Just (x,_)) will give /// us something like ("x" => value.0.0) fn check_for_match(branches: &Vec) -> Option