<commit_before>use std::convert::TryInto;

use crate::{
    cache::{DefinitionInfoId, DefinitionKind},
    parser::ast,
    types::pattern::{Case, DecisionTree, VariantTag},
    util::fmap,
};

use super::monomorphisation::{Context, Definition};
use crate::hir;

impl<'c> Context<'c> {
    pub fn monomorphise_match(&mut self, match_: &ast::Match<'c>) -> hir::Ast {
        let match_prelude = self.store_initial_value(match_);
        let decision_tree = self.monomorphise_tree(match_.decision_tree.as_ref().unwrap());
        let branches = fmap(&match_.branches, |branch| self.monomorphise(&branch.1));
        let result_type = self.convert_type(match_.typ.as_ref().unwrap());

        hir::Ast::Sequence(hir::Sequence {
            statements: vec![match_prelude, hir::Ast::Match(hir::Match { branches, decision_tree, result_type })],
        })
    }

    /// Compile the expression to match on and store it in the DefinitionInfoId expected
    /// by the first Case of the DecisionTree
    fn store_initial_value(&mut self, match_: &ast::Match<'c>) -> hir::Ast {
        let value = self.monomorphise(match_.expression.as_ref());

        if let Some(DecisionTree::Switch(id, _)) = &match_.decision_tree {
            let (def, new_id) = self.fresh_definition(value);
            let typ = self.follow_all_bindings(self.cache[*id].typ.as_ref().unwrap().as_monotype());
            self.definitions.insert((*id, typ), new_id.into());
            def
        } else {
            value
        }
    }

    fn monomorphise_tree(&mut self, tree: &DecisionTree) -> hir::DecisionTree {
        match tree {
            DecisionTree::Leaf(index) => hir::DecisionTree::Leaf(*index),
            DecisionTree::Switch(id_to_match_on, cases) => self.monomorphise_switch(*id_to_match_on, cases),
            DecisionTree::Fail => {
                unreachable!("Patterns should be verified to be complete before monomorphisation")
            },
        }
    }

    fn monomorphise_switch(&mut self, id_to_match_on: DefinitionInfoId, cases: &[Case]) -> hir::DecisionTree {
        let typ = self.cache[id_to_match_on].typ.as_ref().unwrap().as_monotype();

        let value = match self.lookup_definition(id_to_match_on, typ) {
            Some(Definition::Normal(variable)) => variable,
            _ => unreachable!(),
        };

        if cases.len() == 1 {
            // If there's only 1 case we must be destructuring a struct, no need to check a tag
            self.monomorphise_case_no_tag_value(&cases[0], value.definition_id)
        } else {
            let (cases, match_all_case) = self.split_cases(cases);

            let typ = typ.clone();
            let monomorphised_type = self.convert_type(&typ);

            let cases = fmap(cases, |case| self.monomorphise_case(case, value.clone()));
            let else_case =
                match_all_case.map(|case| Box::new(self.monomorphise_case_no_tag_value(case, value.definition_id)));

            let tag = self.extract_tag(value, &monomorphised_type);
            hir::DecisionTree::Switch { int_to_switch_on: Box::new(tag), cases, else_case }
        }
    }

    fn monomorphise_case(&mut self, case: &Case, match_value: hir::DefinitionInfo) -> (u32, hir::DecisionTree) {
        let tree = if case.fields.is_empty() {
            self.monomorphise_tree(&case.branch)
        } else {
            // variable = value = reinterpret match_value as variant_type
            let value = self.cast_to_variant_type(match_value, case);
            let variable = self.next_unique_id();
            let field_bindings = self.bind_patterns(variable, case);

            let mut tree = self.monomorphise_tree(&case.branch);

            for definition in field_bindings.into_iter().rev() {
                tree = hir::DecisionTree::Definition(definition, Box::new(tree));
            }

            let expr = Box::new(value);
            let cast_definition = hir::Definition { variable, expr };

            hir::DecisionTree::Definition(cast_definition, Box::new(tree))
        };

        let expected_tag_value = self.get_tag_value(case);
        (expected_tag_value as u32, tree)
    }

    fn monomorphise_case_no_tag_value(&mut self, case: &Case, match_value: hir::DefinitionId) -> hir::DecisionTree {
        let field_bindings = self.bind_patterns(match_value, case);

        let mut tree = self.monomorphise_tree(&case.branch);

        for definition in field_bindings.into_iter().rev() {
            tree = hir::DecisionTree::Definition(definition, Box::new(tree));
        }

        tree
    }

    fn extract_tag(&mut self, value: hir::DefinitionInfo, typ: &hir::Type) -> hir::Ast {
        use hir::types::*;
        match typ {
            Type::Primitive(PrimitiveType::Integer(_)) => value.into(),
            Type::Tuple(_) => self.extract(value.into(), 0),
            _ => unreachable!(),
        }
    }

    /// Groups the given cases into an optional match-all case and a list of the other cases.
    fn split_cases<'a>(&self, cases: &'a [Case]) -> (&'a [Case], Option<&'a Case>) {
        let last = cases.last().unwrap();
        if last.tag == None {
            (&cases[0..cases.len() - 1], Some(last))
        } else {
            (cases, None)
        }
    }

    fn get_tag_value(&self, case: &Case) -> u8 {
        match case.tag.as_ref().unwrap() {
            VariantTag::True => 1,
            VariantTag::False => 0,
            VariantTag::Unit => 0,
            VariantTag::Literal(literal) => match literal {
                ast::LiteralKind::Integer(x, _) => (*x).try_into().unwrap(), // TODO: larger tags
                ast::LiteralKind::Float(_) => todo!(),
                ast::LiteralKind::String(_) => todo!(),
                ast::LiteralKind::Char(x) => (*x).try_into().unwrap(),
                ast::LiteralKind::Bool(_) => unreachable!(),
                ast::LiteralKind::Unit => unreachable!(),
            },
            VariantTag::UserDefined(id) => {
                match &self.cache[*id].definition {
                    Some(DefinitionKind::TypeConstructor { tag: Some(tag), .. }) => *tag,
                    _ => dbg!(0), //unreachable!(),
                }
            },
        }
    }

    fn bind_patterns(&mut self, variant: hir::DefinitionId, case: &Case) -> Vec<hir::Definition> {<commit_msg>Fix cast char to u8 (#97)<commit_after>use std::convert::TryInto;

use crate::{
    cache::{DefinitionInfoId, DefinitionKind},
    parser::ast,
    types::pattern::{Case, DecisionTree, VariantTag},
    util::fmap,
};

use super::monomorphisation::{Context, Definition};
use crate::hir;

impl<'c> Context<'c> {
    pub fn monomorphise_match(&mut self, match_: &ast::Match<'c>) -> hir::Ast {
        let match_prelude = self.store_initial_value(match_);
        let decision_tree = self.monomorphise_tree(match_.decision_tree.as_ref().unwrap());
        let branches = fmap(&match_.branches, |branch| self.monomorphise(&branch.1));
        let result_type = self.convert_type(match_.typ.as_ref().unwrap());

        hir::Ast::Sequence(hir::Sequence {
            statements: vec![match_prelude, hir::Ast::Match(hir::Match { branches, decision_tree, result_type })],
        })
    }

    /// Compile the expression to match on and store it in the DefinitionInfoId expected
    /// by the first Case of the DecisionTree
    fn store_initial_value(&mut self, match_: &ast::Match<'c>) -> hir::Ast {
        let value = self.monomorphise(match_.expression.as_ref());

        if let Some(DecisionTree::Switch(id, _)) = &match_.decision_tree {
            let (def, new_id) = self.fresh_definition(value);
            let typ = self.follow_all_bindings(self.cache[*id].typ.as_ref().unwrap().as_monotype());
            self.definitions.insert((*id, typ), new_id.into());
            def
        } else {
            value
        }
    }

    fn monomorphise_tree(&mut self, tree: &DecisionTree) -> hir::DecisionTree {
        match tree {
            DecisionTree::Leaf(index) => hir::DecisionTree::Leaf(*index),
            DecisionTree::Switch(id_to_match_on, cases) => self.monomorphise_switch(*id_to_match_on, cases),
            DecisionTree::Fail => {
                unreachable!("Patterns should be verified to be complete before monomorphisation")
            },
        }
    }

    fn monomorphise_switch(&mut self, id_to_match_on: DefinitionInfoId, cases: &[Case]) -> hir::DecisionTree {
        let typ = self.cache[id_to_match_on].typ.as_ref().unwrap().as_monotype();

        let value = match self.lookup_definition(id_to_match_on, typ) {
            Some(Definition::Normal(variable)) => variable,
            _ => unreachable!(),
        };

        if cases.len() == 1 {
            // If there's only 1 case we must be destructuring a struct, no need to check a tag
            self.monomorphise_case_no_tag_value(&cases[0], value.definition_id)
        } else {
            let (cases, match_all_case) = self.split_cases(cases);

            let typ = typ.clone();
            let monomorphised_type = self.convert_type(&typ);

            let cases = fmap(cases, |case| self.monomorphise_case(case, value.clone()));
            let else_case =
                match_all_case.map(|case| Box::new(self.monomorphise_case_no_tag_value(case, value.definition_id)));

            let tag = self.extract_tag(value, &monomorphised_type);
            hir::DecisionTree::Switch { int_to_switch_on: Box::new(tag), cases, else_case }
        }
    }

    fn monomorphise_case(&mut self, case: &Case, match_value: hir::DefinitionInfo) -> (u32, hir::DecisionTree) {
        let tree = if case.fields.is_empty() {
            self.monomorphise_tree(&case.branch)
        } else {
            // variable = value = reinterpret match_value as variant_type
            let value = self.cast_to_variant_type(match_value, case);
            let variable = self.next_unique_id();
            let field_bindings = self.bind_patterns(variable, case);

            let mut tree = self.monomorphise_tree(&case.branch);

            for definition in field_bindings.into_iter().rev() {
                tree = hir::DecisionTree::Definition(definition, Box::new(tree));
            }

            let expr = Box::new(value);
            let cast_definition = hir::Definition { variable, expr };

            hir::DecisionTree::Definition(cast_definition, Box::new(tree))
        };

        let expected_tag_value = self.get_tag_value(case);
        (expected_tag_value as u32, tree)
    }

    fn monomorphise_case_no_tag_value(&mut self, case: &Case, match_value: hir::DefinitionId) -> hir::DecisionTree {
        let field_bindings = self.bind_patterns(match_value, case);

        let mut tree = self.monomorphise_tree(&case.branch);

        for definition in field_bindings.into_iter().rev() {
            tree = hir::DecisionTree::Definition(definition, Box::new(tree));
        }

        tree
    }

    fn extract_tag(&mut self, value: hir::DefinitionInfo, typ: &hir::Type) -> hir::Ast {
        use hir::types::*;
        match typ {
            Type::Primitive(PrimitiveType::Integer(_)) => value.into(),
            Type::Tuple(_) => self.extract(value.into(), 0),
            _ => unreachable!(),
        }
    }

    /// Groups the given cases into an optional match-all case and a list of the other cases.
    fn split_cases<'a>(&self, cases: &'a [Case]) -> (&'a [Case], Option<&'a Case>) {
        let last = cases.last().unwrap();
        if last.tag == None {
            (&cases[0..cases.len() - 1], Some(last))
        } else {
            (cases, None)
        }
    }

    fn get_tag_value(&self, case: &Case) -> u8 {
        match case.tag.as_ref().unwrap() {
            VariantTag::True => 1,
            VariantTag::False => 0,
            VariantTag::Unit => 0,
            VariantTag::Literal(literal) => match literal {
                ast::LiteralKind::Integer(x, _) => (*x).try_into().unwrap(), // TODO: larger tags
                ast::LiteralKind::Float(_) => todo!(),
                ast::LiteralKind::String(_) => todo!(),
                ast::LiteralKind::Char(x) => {
                    let codepoint: u32 = (*x).into();
                    codepoint.try_into().unwrap()
                },
                ast::LiteralKind::Bool(_) => unreachable!(),
                ast::LiteralKind::Unit => unreachable!(),
            },
            VariantTag::UserDefined(id) => {
                match &self.cache[*id].definition {
                    Some(DefinitionKind::TypeConstructor { tag: Some(tag), .. }) => *tag,
                    _ => dbg!(0), //unreachable!(),
                }
            },
        }
    }

    fn bind_patterns(&mut self, variant: hir::DefinitionId, case: &Case) -> Vec<hir::Definition> {
{'fraction_non_alphanumeric': 0.14234234234234233, 'fraction_numerical': 0.002898550724637681, 'mean_word_length': 2.454004329004329, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 79, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2638068', 'n_tokens_mistral': 3883, 'n_tokens_neox': 3777, 'n_words': 759}