aether-azathoth 0.5.3

A lightweight, embeddable domain-specific language (DSL) interpreter with rich standard library
Documentation
pub fn print_detailed_error(source: &str, error_msg: &str) {
    eprintln!("{}", error_msg);

    if let Some((line, col)) = extract_line_column(error_msg) {
        print_source_context(source, line, col);
    }
}

pub fn extract_line_column(error_msg: &str) -> Option<(usize, usize)> {
    if let Some(line_start) = error_msg.find("line ")
        && let Some(line_end) = error_msg[line_start..].find(',')
    {
        let line_str = &error_msg[line_start + 5..line_start + line_end];
        if let Ok(line) = line_str.trim().parse::<usize>()
            && let Some(col_start) = error_msg.find("column ")
        {
            let col_str = &error_msg[col_start + 7..];
            let col_end = col_str
                .find(|c: char| !c.is_numeric())
                .unwrap_or(col_str.len());
            if let Ok(col) = col_str[..col_end].trim().parse::<usize>() {
                return Some((line, col));
            }
        }
    }
    None
}

pub fn print_source_context(source: &str, error_line: usize, error_col: usize) {
    let lines: Vec<&str> = source.lines().collect();

    if error_line == 0 || error_line > lines.len() {
        return;
    }

    eprintln!();
    eprintln!("源代码位置:");

    if error_line > 1 {
        eprintln!("{:4} | {}", error_line - 1, lines[error_line - 2]);
    }

    eprintln!("{:4} | {}", error_line, lines[error_line - 1]);

    let indent = format!("{:4} | ", error_line);
    let pointer = " ".repeat(error_col.saturating_sub(1)) + "^";
    eprintln!("{}{}", indent, pointer);

    if error_line < lines.len() {
        eprintln!("{:4} | {}", error_line + 1, lines[error_line]);
    }
    eprintln!();
}