1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
use std::cmp::max;

use colored::Colorize;
use symboscript_types::lexer::{Token, TokenKind::*};

pub fn output_tokens_colored(text: &str, tokens: &Vec<Token>, show_tokens: Option<bool>) {
    let show_tokens = show_tokens.unwrap_or(false);
    let mut last_start;
    let mut last_end = 0;

    for token in tokens {
        last_start = token.start;
        print!("{}", text[last_end..last_start].to_string());
        last_end = token.end;

        let s = if show_tokens {
            format!("<{}>", text[token.start..token.end].to_string())
        } else {
            format!("{}", text[token.start..token.end].to_string())
        };

        match token.kind {
            Identifier => print!("{}", s.yellow()),

            Plus | Minus | Multiply | Divide | Power | Assign | Equal | Range | FormulaAssign
            | And | Or | Xor | Not | BitAnd | BitOr | BitNot | BitXor | BitLeftShift
            | BitRightShift => {
                print!("{}", s.green())
            }

            Number => print!("{}", s.blue()),

            LParen | RParen | LBrace | RBrace => print!("{}", s.cyan()),

            If | Else | While | For | Loop | Let | Return | Break | Continue | Function | True
            | False | In => print!("{}", s.magenta()),

            Str => print!("{}", s.truecolor(206, 145, 120)),

            DocComment => print!("{}", s.green()),

            _ => print!("{}", s),
        }
    }

    print!("{}", text[last_end..].to_string());

    println!();
}

pub fn report_error(path: &str, source: &str, error: &str, start: usize, end: usize) {
    let line_start = max(source[..start].lines().count(), 1);
    let line_end = max(source[..end].lines().count(), 1);

    let column_start = start - source[..start].rfind('\n').unwrap_or(0);
    let column_end = end - source[..end].rfind('\n').unwrap_or(0);

    let near_text = source.lines().nth(line_end - 1).unwrap_or("").trim_end();

    let line_n = format!("{line_end} |");

    let error_pointer = (" ".repeat(column_start + line_n.len())
        + "^".repeat(column_end - column_start).as_str())
    .red()
    .bold();

    let error_pointer_text = error.red().bold();

    let file_src = format!(
        "--> {}:{}:{}-{}:{} ({start} - {end})",
        path, line_start, column_start, line_end, column_end
    );

    println!("{}", file_src.blue().bold());

    for i in line_start..line_end {
        println!(
            "{} {}",
            format!("{} |", i).blue().bold(),
            source.lines().nth(i - 1).unwrap_or("")
        );
    }

    println!(
        "{} {near_text}\n{error_pointer} {error_pointer_text}",
        line_n.to_string().blue().bold(),
    );

    std::process::exit(1);
}