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
105
106
107
108
109
110
111
112
113
114
115
116
117
use std::cmp::{max, min};
use std::fmt;
use std::fmt::{Display, Formatter};
use std::path::PathBuf;

use crate::common::delimit::comma_delm;
use crate::common::position::Position;
use crate::common::result::{an_or_a, Cause, format_err, WithCause, WithSource};
use crate::parse::ast::AST;
use crate::parse::lex::result::LexErr;
use crate::parse::lex::token::Lex;
use crate::parse::lex::token::Token;

const SYNTAX_ERR_MAX_DEPTH: usize = 1;

pub type ParseResult<T = Box<AST>> = Result<T, Box<ParseErr>>;

#[derive(Debug, Clone)]
pub struct ParseErr {
    pub pos: Position,
    pub msg: String,
    pub source: Option<String>,
    pub path: Option<PathBuf>,
    pub causes: Vec<Cause>,
}

impl WithCause for ParseErr {
    fn with_cause(self, msg: &str, pos: Position) -> Self {
        let causes = {
            let mut new_causes = self.causes.clone();
            let msg = format!("While parsing {}{msg}", an_or_a(msg));

            new_causes.push(Cause::new(&msg, pos));
            new_causes
        };
        ParseErr { causes, ..self }
    }
}

impl WithSource for ParseErr {
    fn with_source(self, source: &Option<String>, path: &Option<PathBuf>) -> ParseErr {
        ParseErr { source: source.clone(), path: path.clone(), ..self }
    }
}

impl From<LexErr> for ParseErr {
    fn from(lex_err: LexErr) -> Self {
        ParseErr {
            pos: Position::from(lex_err.pos),
            msg: lex_err.msg,
            source: lex_err.source,
            path: lex_err.path,
            causes: vec![],
        }
    }
}

pub fn expected_one_of(tokens: &[Token], actual: &Lex, parsing: &str) -> ParseErr {
    let msg = format!(
        "Expected one of [{}] while parsing {}{parsing}, but found token '{}'",
        comma_delm(tokens),
        an_or_a(parsing),
        actual.token
    );
    ParseErr { pos: actual.pos, msg, source: None, path: None, causes: vec![] }
}

pub fn expected(expected: &Token, actual: &Lex, parsing: &str) -> ParseErr {
    let msg = format!(
        "Expected {}{expected} token while parsing {}{parsing}, but found {}",
        an_or_a(expected),
        an_or_a(parsing),
        actual.token
    );
    ParseErr { pos: actual.pos, msg, source: None, path: None, causes: vec![] }
}

pub fn custom(msg: &str, position: Position) -> ParseErr {
    ParseErr { pos: position, msg: title_case(msg), source: None, path: None, causes: vec![] }
}

pub fn eof_expected_one_of(tokens: &[Token], parsing: &str) -> ParseErr {
    ParseErr {
        pos: Position::invisible(),
        msg: match tokens {
            tokens if tokens.len() > 1 => format!(
                "Expected one of [{}] tokens while parsing {}{parsing}",
                comma_delm(tokens),
                an_or_a(parsing),
            ),
            tokens if tokens.len() == 1 => format!(
                "Expected a {} token while parsing {}{parsing}",
                comma_delm(tokens),
                an_or_a(parsing),
            ),
            _ => format!("Expected a token while parsing {}{parsing}", an_or_a(parsing)),
        },
        source: None,
        path: None,
        causes: vec![],
    }
}

fn title_case(s: &str) -> String {
    let mut tile_case = String::from(s);
    if let Some(first) = tile_case.get_mut(0..1) {
        first.make_ascii_uppercase();
    }
    tile_case
}

impl Display for ParseErr {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let causes = &self.causes[0..min(max(self.causes.len() as i32 - 1, 0) as usize, SYNTAX_ERR_MAX_DEPTH)];
        format_err(f, &self.msg, &self.path, Some(self.pos), &self.source, causes)
    }
}