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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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())
        };

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

                Plus | Minus | Star | Slash | Caret | Assign | Equal | Range | FormulaAssign
                | AmpersandAmpersand | PipePipe | Xor | ExclamationMark | Ampersand | Pipe
                | Tilde | BitXor | BitLeftShift | BitRightShift => s.green(),

                Number => s.blue(),

                LParen | RParen | LAngle | RAngle => s.cyan(),

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

                Str => s.truecolor(206, 145, 120),

                DocComment | Comment => s.green(),

                _ => s.into(),
            }
        });
    }

    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 mut column_start = start - source[..start].rfind('\n').unwrap_or(0);
    let mut column_end = end - source[..end].rfind('\n').unwrap_or(0);

    if line_start == 1 || line_end == 1 {
        column_end += 1;
        column_start += 1;
    }

    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
        .replace(
            "\n",
            &format!(
                "\n{} ",
                " ".repeat(column_end - column_start) + &" ".repeat(column_start + line_n.len())
            ),
        )
        .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);
}