symboscript_utils/
lib.rs

1use std::cmp::max;
2
3use colored::Colorize;
4use symboscript_types::lexer::{Token, TokenKind::*};
5
6pub fn output_tokens_colored(text: &str, tokens: &Vec<Token>, show_tokens: Option<bool>) {
7    let show_tokens = show_tokens.unwrap_or(false);
8    let mut last_start;
9    let mut last_end = 0;
10
11    for token in tokens {
12        last_start = token.start;
13        print!("{}", text[last_end..last_start].to_string());
14        last_end = token.end;
15
16        let s = if show_tokens {
17            format!("<{}>", text[token.start..token.end].to_string())
18        } else {
19            format!("{}", text[token.start..token.end].to_string())
20        };
21
22        print!("{}", {
23            match token.kind {
24                Identifier => s.yellow(),
25
26                Plus | Minus | Star | Slash | Caret | Assign | Equal | Range | FormulaAssign
27                | AmpersandAmpersand | PipePipe | Xor | ExclamationMark | Ampersand | Pipe
28                | Tilde | BitXor | BitLeftShift | BitRightShift => s.green(),
29
30                Number => s.blue(),
31
32                LParen | RParen | LAngle | RAngle => s.cyan(),
33
34                If | Else | While | For | Loop | Let | Return | Break | Continue | Function
35                | True | False | In => s.magenta(),
36
37                Str => s.truecolor(206, 145, 120),
38
39                DocComment | Comment => s.green(),
40
41                _ => s.into(),
42            }
43        });
44    }
45
46    print!("{}", text[last_end..].to_string());
47
48    println!();
49}
50
51pub fn report_error(path: &str, source: &str, error: &str, start: usize, end: usize) {
52    let line_start = max(source[..start].lines().count(), 1);
53    let line_end = max(source[..end].lines().count(), 1);
54
55    let mut column_start = start - source[..start].rfind('\n').unwrap_or(0);
56    let mut column_end = end - source[..end].rfind('\n').unwrap_or(0);
57
58    if line_start == 1 || line_end == 1 {
59        column_end += 1;
60        column_start += 1;
61    }
62
63    if column_end < column_start {
64        column_end = source[..end].rfind('\n').unwrap_or(0);
65    }
66
67    let near_text = source.lines().nth(line_end - 1).unwrap_or("").trim_end();
68
69    let line_n = format!("{line_end} |");
70
71    let error_pointer = (" ".repeat(column_start + line_n.len())
72        + "^".repeat(column_end - column_start).as_str())
73    .red()
74    .bold();
75
76    let error_pointer_text = error
77        .replace(
78            "\n",
79            &format!(
80                "\n{} ",
81                " ".repeat(column_end - column_start) + &" ".repeat(column_start + line_n.len())
82            ),
83        )
84        .red()
85        .bold();
86
87    let file_src = format!(
88        "--> {}:{}:{}-{}:{} ({start} - {end})",
89        path, line_start, column_start, line_end, column_end
90    );
91
92    println!("{}", file_src.blue().bold());
93
94    for i in line_start..line_end {
95        println!(
96            "{} {}",
97            format!("{} |", i).blue().bold(),
98            source.lines().nth(i - 1).unwrap_or("")
99        );
100    }
101
102    println!(
103        "{} {near_text}\n{error_pointer} {error_pointer_text}",
104        line_n.to_string().blue().bold(),
105    );
106
107    std::process::exit(1);
108}