Files @ 97217a7b2d18
Branch filter:

Location: CSY/reowolf/src/protocol/eval/error.rs

97217a7b2d18 3.2 KiB application/rls-services+xml Show Annotation Show as Raw Download as Raw
Max Henger
feat: examples
use std::fmt;

use crate::protocol::{
    ast::*,
    Module,
    input_source::{ErrorStatement, StatementKind}
};
use super::executor::*;

/// Represents a stack frame recorded in an error
#[derive(Debug)]
pub struct EvalFrame {
    pub line: Option<u32>,
    pub module_name: String,
    pub procedure: String, // function or component
    pub is_func: bool,
}

impl fmt::Display for EvalFrame {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let func_or_comp = if self.is_func {
            "function "
        } else {
            "component"
        };

        let line_str = match self.line {
            Some(line_number) => line_number.to_string(),
            None => String::from("??"),
        };

        if self.module_name.is_empty() {
            write!(f, "{} {}:{}", func_or_comp, &self.procedure, line_str)
        } else {
            write!(f, "{} {}:{}:{}", func_or_comp, &self.module_name, &self.procedure, line_str)
        }
    }
}

/// Represents an error that ocurred during evaluation. Contains error
/// statements just like in parsing errors. Additionally may display the current
/// execution state.
#[derive(Debug)]
pub struct EvalError {
    pub(crate) statements: Vec<ErrorStatement>,
    pub(crate) frames: Vec<EvalFrame>,
}

impl EvalError {
    pub(crate) fn new_error_at_expr(prompt: &Prompt, modules: &[Module], heap: &Heap, expr_id: ExpressionId, msg: String) -> EvalError {
        // Create frames
        debug_assert!(!prompt.frames.is_empty());
        let mut frames = Vec::with_capacity(prompt.frames.len());
        let mut last_module_source = &modules[0].source;
        for frame in prompt.frames.iter() {
            let definition = &heap[frame.definition];
            let statement = &heap[frame.position];
            let statement_span = statement.maybe_span();

            // Lookup module name, if it has one
            let module = modules.iter().find(|m| m.root_id == definition.defined_in).unwrap();
            let module_name = if let Some(name) = &module.name {
                name.as_str().to_string()
            } else {
                String::new()
            };

            last_module_source = &module.source;
            frames.push(EvalFrame{
                line: statement_span.map(|v| v.begin.line),
                module_name,
                procedure: definition.identifier.value.as_str().to_string(),
                is_func: definition.kind == ProcedureKind::Function,
            });
        }

        let expr = &heap[expr_id];
        let statements = vec![
            ErrorStatement::from_source_at_span(StatementKind::Error, last_module_source, expr.full_span(), msg)
        ];

        EvalError{ statements, frames }
    }
}

impl fmt::Display for EvalError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Display error statement(s)
        self.statements[0].fmt(f)?;
        for statement in self.statements.iter().skip(1) {
            writeln!(f)?;
            statement.fmt(f)?;
        }

        // Display stack trace
        writeln!(f)?;
        writeln!(f, " +-  Stack trace:")?;
        for frame in self.frames.iter().rev() {
            write!(f, " | ")?;
            frame.fmt(f)?;
            writeln!(f)?;
        }

        Ok(())
    }
}