<commit_before>use im::HashMap;

use super::ast::{self, Ast, AstId, AstKind, AstString, Call, Int, Lambda, Symbol, Text};
use super::cst::{Cst, CstId, CstKind};
use super::error::CompilerError;

pub trait LowerCstToAst {
    fn into_ast(self) -> (Vec<Ast>, HashMap<AstId, CstId>, Vec<CompilerError>);
}
impl LowerCstToAst for Vec<Cst> {
    fn into_ast(self) -> (Vec<Ast>, HashMap<AstId, CstId>, Vec<CompilerError>) {
        let mut context = LoweringContext::new();
        let asts = (&mut context).lower_csts(self);
        (asts, context.id_mapping, context.errors)
    }
}

struct LoweringContext {
    next_id: usize,
    id_mapping: HashMap<AstId, CstId>,
    errors: Vec<CompilerError>,
}
impl LoweringContext {
    fn new() -> LoweringContext {
        LoweringContext {
            next_id: 0,
            id_mapping: HashMap::new(),
            errors: vec![],
        }
    }
    fn lower_csts(&mut self, csts: Vec<Cst>) -> Vec<Ast> {
        csts.into_iter().map(|it| self.lower_cst(it)).collect()
    }
    fn lower_cst(&mut self, cst: Cst) -> Ast {
        match cst.kind {
            CstKind::EqualsSign { .. } => self.create_ast(cst.id, AstKind::Error),
            CstKind::OpeningParenthesis { .. } => self.create_ast(cst.id, AstKind::Error),
            CstKind::ClosingParenthesis { .. } => self.create_ast(cst.id, AstKind::Error),
            CstKind::OpeningCurlyBrace { .. } => self.create_ast(cst.id, AstKind::Error),
            CstKind::ClosingCurlyBrace { .. } => self.create_ast(cst.id, AstKind::Error),
            CstKind::Arrow { .. } => self.create_ast(cst.id, AstKind::Error),
            CstKind::Int { value, .. } => self.create_ast(cst.id, AstKind::Int(Int(value))),
            CstKind::Text { value, .. } => {
                let string = self.create_string(cst.id, value);
                self.create_ast(cst.id, AstKind::Text(Text(string)))
            }
            CstKind::Identifier { .. } => {
                panic!("Tried to lower an identifier from CST to AST.")
            }
            CstKind::Symbol { value, .. } => {
                let string = self.create_string(cst.id, value);
                self.create_ast(cst.id, AstKind::Symbol(Symbol(string)))
            }
            CstKind::LeadingWhitespace { child, .. } => self.lower_cst(*child),
            CstKind::LeadingComment { child, .. } => self.lower_cst(*child),
            CstKind::TrailingWhitespace { child, .. } => self.lower_cst(*child),
            CstKind::TrailingComment { child, .. } => self.lower_cst(*child),
            CstKind::Parenthesized {
                opening_parenthesis,
                inner,
                closing_parenthesis,
            } => {
                assert!(
                matches!(opening_parenthesis.unwrap_whitespace_and_comment().kind, CstKind::OpeningParenthesis { .. }),
                "Expected an opening parenthesis to start a parenthesized expression, but found `{}`.",
                *opening_parenthesis
            );
                assert!(
                matches!(
                    closing_parenthesis.unwrap_whitespace_and_comment().kind,
                    CstKind::ClosingParenthesis { .. }
                ),
                "Expected a closing parenthesis to end a parenthesized expression, but found `{}`.",
                *closing_parenthesis
            );
                self.lower_cst(*inner)
            }
            CstKind::Lambda {
                opening_curly_brace,
                parameters_and_arrow,
                body,
                closing_curly_brace,
            } => {
                assert!(
                    matches!(
                        opening_curly_brace.unwrap_whitespace_and_comment().kind,
                        CstKind::OpeningCurlyBrace { .. }
                    ),
                    "Expected an opening curly brace at the beginning of a lambda, but found {}.",
                    opening_curly_brace,
                );
                let parameters = if let Some((parameters, arrow)) = parameters_and_arrow {
                    let parameters = self.lower_parameters(parameters);

                    assert!(
                        matches!(
                            arrow.unwrap_whitespace_and_comment().kind,
                            CstKind::Arrow { .. }
                        ),
                        "Expected an arrow after the parameters in a lambda, but found `{}`.",
                        arrow
                    );

                    parameters
                } else {
                    vec![]
                };

                let body = self.lower_csts(body);

                assert!(
                    matches!(
                        closing_curly_brace.unwrap_whitespace_and_comment().kind,
                        CstKind::ClosingCurlyBrace { .. }
                    ),
                    "Expected a closing curly brace at the end of a lambda, but found {}.",
                    closing_curly_brace
                );

                self.create_ast(cst.id, AstKind::Lambda(Lambda { parameters, body }))
            }
            CstKind::Call { name, arguments } => {
                let name = name.unwrap_whitespace_and_comment();
                let name = match name {
                    Cst {
                        id,
                        kind: CstKind::Identifier { value, .. },
                    } => self.create_string(id.to_owned(), value.to_owned()),
                    _ => {
                        panic!(
                            "Expected a symbol for the name of a call, but found `{}`.",
                            name
                        );
                    }
                };

                let arguments = self.lower_csts(arguments);
                self.create_ast(cst.id, AstKind::Call(Call { name, arguments }))
            }
            CstKind::Assignment {
                name,
                parameters,
                equals_sign,
                body,
            } => {
                let name = self.lower_identifier(*name);

                let parameters = self.lower_parameters(parameters);
                assert!(
                    matches!(
                        equals_sign.unwrap_whitespace_and_comment().kind,
                        CstKind::EqualsSign { .. }
                    ),
                    "Expected an equals sign for the assignment."
                );

                let body = self.lower_csts(body);

                self.create_ast(
                    cst.id,
                    AstKind::Assignment(ast::Assignment {
                        name,
                        parameters,
                        body,
                    }),
                )
            }
            CstKind::Error { message, .. } => {
                self.errors.push(CompilerError {
                    span: todo!(),
                    message: message.to_owned(),
                });
                self.create_ast(cst.id, AstKind::Error)
            }
        }
    }

    fn lower_parameters(&mut self, csts: Vec<Cst>) -> Vec<AstString> {
        csts.into_iter()
            .filter_map(|it| self.lower_parameter(it))
            .collect()
    }
    fn lower_parameter(&mut self, cst: Cst) -> Option<AstString> {
        let cst = cst.unwrap_whitespace_and_comment();
        match cst.kind.clone() {
            CstKind::Call { name, arguments } => {
                let name = self.lower_identifier(*name);

                if !arguments.is_empty() {
                    self.errors.push(CompilerError {
                        span: cst.span(),
                        message: "Parameters can't have parameters themselves.".to_owned(),
                    });
                }<commit_msg>fix(compiler): create compiler error for CST errors during lowering<commit_after>use im::HashMap;

use super::ast::{self, Ast, AstId, AstKind, AstString, Call, Int, Lambda, Symbol, Text};
use super::cst::{Cst, CstId, CstKind};
use super::error::CompilerError;

pub trait LowerCstToAst {
    fn into_ast(self) -> (Vec<Ast>, HashMap<AstId, CstId>, Vec<CompilerError>);
}
impl LowerCstToAst for Vec<Cst> {
    fn into_ast(self) -> (Vec<Ast>, HashMap<AstId, CstId>, Vec<CompilerError>) {
        let mut context = LoweringContext::new();
        let asts = (&mut context).lower_csts(self);
        (asts, context.id_mapping, context.errors)
    }
}

struct LoweringContext {
    next_id: usize,
    id_mapping: HashMap<AstId, CstId>,
    errors: Vec<CompilerError>,
}
impl LoweringContext {
    fn new() -> LoweringContext {
        LoweringContext {
            next_id: 0,
            id_mapping: HashMap::new(),
            errors: vec![],
        }
    }
    fn lower_csts(&mut self, csts: Vec<Cst>) -> Vec<Ast> {
        csts.into_iter().map(|it| self.lower_cst(it)).collect()
    }
    fn lower_cst(&mut self, cst: Cst) -> Ast {
        match cst.kind {
            CstKind::EqualsSign { .. } => self.create_ast(cst.id, AstKind::Error),
            CstKind::OpeningParenthesis { .. } => self.create_ast(cst.id, AstKind::Error),
            CstKind::ClosingParenthesis { .. } => self.create_ast(cst.id, AstKind::Error),
            CstKind::OpeningCurlyBrace { .. } => self.create_ast(cst.id, AstKind::Error),
            CstKind::ClosingCurlyBrace { .. } => self.create_ast(cst.id, AstKind::Error),
            CstKind::Arrow { .. } => self.create_ast(cst.id, AstKind::Error),
            CstKind::Int { value, .. } => self.create_ast(cst.id, AstKind::Int(Int(value))),
            CstKind::Text { value, .. } => {
                let string = self.create_string(cst.id, value);
                self.create_ast(cst.id, AstKind::Text(Text(string)))
            }
            CstKind::Identifier { .. } => {
                panic!("Tried to lower an identifier from CST to AST.")
            }
            CstKind::Symbol { value, .. } => {
                let string = self.create_string(cst.id, value);
                self.create_ast(cst.id, AstKind::Symbol(Symbol(string)))
            }
            CstKind::LeadingWhitespace { child, .. } => self.lower_cst(*child),
            CstKind::LeadingComment { child, .. } => self.lower_cst(*child),
            CstKind::TrailingWhitespace { child, .. } => self.lower_cst(*child),
            CstKind::TrailingComment { child, .. } => self.lower_cst(*child),
            CstKind::Parenthesized {
                opening_parenthesis,
                inner,
                closing_parenthesis,
            } => {
                assert!(
                matches!(opening_parenthesis.unwrap_whitespace_and_comment().kind, CstKind::OpeningParenthesis { .. }),
                "Expected an opening parenthesis to start a parenthesized expression, but found `{}`.",
                *opening_parenthesis
            );
                assert!(
                matches!(
                    closing_parenthesis.unwrap_whitespace_and_comment().kind,
                    CstKind::ClosingParenthesis { .. }
                ),
                "Expected a closing parenthesis to end a parenthesized expression, but found `{}`.",
                *closing_parenthesis
            );
                self.lower_cst(*inner)
            }
            CstKind::Lambda {
                opening_curly_brace,
                parameters_and_arrow,
                body,
                closing_curly_brace,
            } => {
                assert!(
                    matches!(
                        opening_curly_brace.unwrap_whitespace_and_comment().kind,
                        CstKind::OpeningCurlyBrace { .. }
                    ),
                    "Expected an opening curly brace at the beginning of a lambda, but found {}.",
                    opening_curly_brace,
                );
                let parameters = if let Some((parameters, arrow)) = parameters_and_arrow {
                    let parameters = self.lower_parameters(parameters);

                    assert!(
                        matches!(
                            arrow.unwrap_whitespace_and_comment().kind,
                            CstKind::Arrow { .. }
                        ),
                        "Expected an arrow after the parameters in a lambda, but found `{}`.",
                        arrow
                    );

                    parameters
                } else {
                    vec![]
                };

                let body = self.lower_csts(body);

                assert!(
                    matches!(
                        closing_curly_brace.unwrap_whitespace_and_comment().kind,
                        CstKind::ClosingCurlyBrace { .. }
                    ),
                    "Expected a closing curly brace at the end of a lambda, but found {}.",
                    closing_curly_brace
                );

                self.create_ast(cst.id, AstKind::Lambda(Lambda { parameters, body }))
            }
            CstKind::Call { name, arguments } => {
                let name = name.unwrap_whitespace_and_comment();
                let name = match name {
                    Cst {
                        id,
                        kind: CstKind::Identifier { value, .. },
                    } => self.create_string(id.to_owned(), value.to_owned()),
                    _ => {
                        panic!(
                            "Expected a symbol for the name of a call, but found `{}`.",
                            name
                        );
                    }
                };

                let arguments = self.lower_csts(arguments);
                self.create_ast(cst.id, AstKind::Call(Call { name, arguments }))
            }
            CstKind::Assignment {
                name,
                parameters,
                equals_sign,
                body,
            } => {
                let name = self.lower_identifier(*name);

                let parameters = self.lower_parameters(parameters);
                assert!(
                    matches!(
                        equals_sign.unwrap_whitespace_and_comment().kind,
                        CstKind::EqualsSign { .. }
                    ),
                    "Expected an equals sign for the assignment."
                );

                let body = self.lower_csts(body);

                self.create_ast(
                    cst.id,
                    AstKind::Assignment(ast::Assignment {
                        name,
                        parameters,
                        body,
                    }),
                )
            }
            CstKind::Error { message, .. } => {
                self.errors.push(CompilerError {
                    span: cst.span(),
                    message: message.to_owned(),
                });
                self.create_ast(cst.id, AstKind::Error)
            }
        }
    }

    fn lower_parameters(&mut self, csts: Vec<Cst>) -> Vec<AstString> {
        csts.into_iter()
            .filter_map(|it| self.lower_parameter(it))
            .collect()
    }
    fn lower_parameter(&mut self, cst: Cst) -> Option<AstString> {
        let cst = cst.unwrap_whitespace_and_comment();
        match cst.kind.clone() {
            CstKind::Call { name, arguments } => {
                let name = self.lower_identifier(*name);

                if !arguments.is_empty() {
                    self.errors.push(CompilerError {
                        span: cst.span(),
                        message: "Parameters can't have parameters themselves.".to_owned(),
                    });
                }
{'fraction_non_alphanumeric': 0.11368731185550503, 'fraction_numerical': 0.00012809837955549862, 'mean_word_length': 1.3402278177458034, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 89, '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': '2136609', 'n_tokens_mistral': 4252, 'n_tokens_neox': 4023, 'n_words': 756}