veryl-simulator 0.20.0

A modern hardware description language
Documentation
use miette::{Diagnostic, SourceSpan};
use thiserror::Error;
use veryl_analyzer::multi_sources::{MultiSources, Source};
use veryl_parser::token_range::TokenRange;
use veryl_parser::veryl_token::TokenSource;

#[derive(Error, Diagnostic, Debug)]
pub enum SimulatorError {
    #[diagnostic(severity(Error), code(top_module_not_found))]
    #[error("top module \"{module_name}\" not found")]
    TopModuleNotFound { module_name: String },

    #[diagnostic(severity(Error), code(no_initial_block))]
    #[error("no initial block found in module \"{module_name}\"")]
    NoInitialBlock {
        module_name: String,
        #[source_code]
        input: MultiSources,
        #[label("this module has no initial block")]
        error_location: SourceSpan,
        token_source: TokenSource,
    },

    #[diagnostic(severity(Error), code(test_failed))]
    #[error("{message}")]
    TestFailed { message: String },

    #[diagnostic(severity(Error), code(io_error))]
    #[error("{message}")]
    IoError { message: String },

    #[diagnostic(severity(Error), code(unresolved_expression))]
    #[error("unresolved expression")]
    UnresolvedExpression {
        #[source_code]
        input: MultiSources,
        #[label("could not resolve this expression")]
        error_location: SourceSpan,
        token_source: TokenSource,
    },

    #[diagnostic(severity(Error), code(recursive_function))]
    #[error("recursive function \"{function_name}\" cannot be inlined")]
    RecursiveFunction {
        function_name: String,
        #[source_code]
        input: MultiSources,
        #[label("recursive call here")]
        error_location: SourceSpan,
        token_source: TokenSource,
    },

    #[diagnostic(severity(Error), code(unsupported_description))]
    #[error("unsupported description")]
    UnsupportedDescription {
        #[source_code]
        input: MultiSources,
        #[label("this description is not supported by the simulator")]
        error_location: SourceSpan,
        token_source: TokenSource,
    },

    #[diagnostic(severity(Error), code(combinational_loop))]
    #[error("combinational loop detected")]
    CombinationalLoop {
        #[source_code]
        input: MultiSources,
        #[label("Error location")]
        error_location: SourceSpan,
        #[label(collection, "involved in loop")]
        loop_participants: Vec<SourceSpan>,
        token_source: TokenSource,
    },
}

fn source_with_context(
    token: &TokenRange,
    context: &[TokenRange],
) -> (MultiSources, Vec<SourceSpan>) {
    let path = token.beg.source.to_string();
    let text = token.beg.source.get_text();

    let mut base = text.len();
    let mut ranges = Vec::new();
    let mut sources = Vec::new();

    sources.push(Source { path, text });

    for x in context.iter().rev() {
        let path = x.beg.source.to_string();
        let text = x.beg.source.get_text();

        let mut range = *x;
        range.offset(base as u32);
        ranges.push(range.into());

        base += text.len();

        sources.push(Source { path, text });
    }

    let sources = MultiSources { sources };
    (sources, ranges)
}

impl SimulatorError {
    pub fn no_initial_block(module_name: &str, token: &TokenRange) -> Self {
        let path = token.beg.source.to_string();
        let text = token.beg.source.get_text();
        let input = MultiSources {
            sources: vec![Source { path, text }],
        };
        SimulatorError::NoInitialBlock {
            module_name: module_name.to_string(),
            input,
            error_location: (*token).into(),
            token_source: token.beg.source,
        }
    }

    pub fn unresolved_expression(token: &TokenRange) -> Self {
        let path = token.beg.source.to_string();
        let text = token.beg.source.get_text();
        let input = MultiSources {
            sources: vec![Source { path, text }],
        };
        SimulatorError::UnresolvedExpression {
            input,
            error_location: (*token).into(),
            token_source: token.beg.source,
        }
    }

    pub fn unsupported_description(token: &TokenRange) -> Self {
        let path = token.beg.source.to_string();
        let text = token.beg.source.get_text();
        let input = MultiSources {
            sources: vec![Source { path, text }],
        };
        SimulatorError::UnsupportedDescription {
            input,
            error_location: (*token).into(),
            token_source: token.beg.source,
        }
    }

    pub fn recursive_function(function_name: &str, token: &TokenRange) -> Self {
        let path = token.beg.source.to_string();
        let text = token.beg.source.get_text();
        let input = MultiSources {
            sources: vec![Source { path, text }],
        };
        SimulatorError::RecursiveFunction {
            function_name: function_name.to_string(),
            input,
            error_location: (*token).into(),
            token_source: token.beg.source,
        }
    }

    pub fn combinational_loop(token: &TokenRange, participants: &[TokenRange]) -> Self {
        let (input, loop_participants) = source_with_context(token, participants);
        SimulatorError::CombinationalLoop {
            input,
            error_location: (*token).into(),
            loop_participants,
            token_source: token.beg.source,
        }
    }
}