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
use lalrpop_util::ParseError;
use lalrpop_util::ParseError::*;

/// Converts the default ParseError shape into something easier to handle.
pub fn simplify_parse_error<'input>(
    error: ParseError<usize, (usize, &'input str), ()>
    ) -> ParseError<usize, String, ()>
{
    match error {
        InvalidToken { location } => InvalidToken { location },
        UnrecognizedToken { token, expected } => {
            let token = token.map(|(start, (_, tok), end)| (start, tok.into(), end));
            UnrecognizedToken {token, expected}
        }
        ExtraToken { token: (start, (_, tok), end) } => {
            let token = (start, tok.into(), end);
            ExtraToken { token }
        },
        User { error } => User { error },
    }
}

fn code_error(code: &str, tok_pos: usize) {
    let code = format!("\n\n{}", code);
    let code = code.lines().collect::<Vec<_>>();
    let mut pos: isize = 0;
    for (i, lines) in (&code[..]).windows(3).enumerate() {
        if pos + lines[2].len() as isize >= tok_pos as isize {

            let arrow_len = (tok_pos as isize) - (pos - 6);
            let omit_left = if arrow_len > 60 { arrow_len as usize - 60 } else { 0 };

            // prints line no. and a 70-char window into line
            let print_line = |n: usize, mut line: &str| {
                if line.len() >= omit_left {
                    line = &line[omit_left..];
                    if line.len() > 70 {
                        line = &line[..70];
                    }
                }
                line = line.trim_right();
                if !line.is_empty() {
                    println!("{:>3} | {}", n, line);
                } else {
                    println!("{:>3} |", n);
                }
            };

            if i > 1 {
                print_line(i - 1, &lines[0]);
            }
            if i > 0 {
                print_line(i, &lines[1]);
            }
            print_line(i + 1, &lines[2]);

            if arrow_len > 0 {
                let arrow_len = arrow_len as usize - omit_left;
                println!("{}^", "~".repeat(arrow_len));
            }
            return;
        }
        pos += (lines[2].len() as isize) + 1;
    }
}

// Print out errors smartly
pub fn print_parse_error(code: &str, err: &ParseError<usize, String, ()>) {
    match *err {
        ParseError::InvalidToken { location: loc } => {
            println!("Error: Invalid token:");
            code_error(code, loc);
        }
        ParseError::UnrecognizedToken {
            token: Some((loc, ref tok, _)),
            ..
        } => {
            println!("Error: Unrecognized token `{}`:", tok);
            code_error(code, loc);
        }
        ParseError::ExtraToken { token: (loc, ref tok, _) } => {
            println!("Error: Extra token `{}`:", tok);
            code_error(code, loc);
        }
        _ => (),
    }
}