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
use std::cmp::{max, min};
use std::fmt::{self, Debug, Formatter};
use std::str::from_utf8_unchecked;

use crate::source::{Source, SourceRange};
use crate::token::TokenType;

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum SyntaxErrorType {
    ExpectedNotFound,
    ExpectedSyntax(&'static str),
    ForLoopHeaderHasInvalidLhs,
    ForLoopHeaderHasMultipleDeclarators,
    ForLoopHeaderHasNoLhs,
    InvalidAssigmentTarget,
    LineTerminatorAfterArrowFunctionParameters,
    LineTerminatorAfterThrow,
    LineTerminatorAfterYield,
    LineTerminatorInRegex,
    LineTerminatorInString,
    MalformedLiteralNumber,
    JsxClosingTagMismatch,
    RequiredTokenNotFound(TokenType),
    TryStatementHasNoCatchOrFinally,
    UnexpectedEnd,
}

#[derive(Clone)]
pub struct SyntaxError {
    source: Source,
    position: usize,
    typ: SyntaxErrorType,
    actual_token: Option<TokenType>,
}

impl SyntaxError {
    pub fn new(
        typ: SyntaxErrorType,
        source: Source,
        position: usize,
        actual_token: Option<TokenType>,
    ) -> SyntaxError {
        SyntaxError {
            typ,
            source,
            position,
            actual_token,
        }
    }

    pub fn from_loc(
        loc: &SourceRange,
        typ: SyntaxErrorType,
        actual_token: Option<TokenType>,
    ) -> SyntaxError {
        SyntaxError {
            source: loc.source.clone(),
            typ,
            position: loc.start,
            actual_token,
        }
    }

    pub fn typ(&self) -> SyntaxErrorType {
        self.typ
    }
}

impl Debug for SyntaxError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.write_fmt(format_args!(
            "{:?} [position={} token={:?}] around ```{}```",
            self.typ,
            self.position,
            self.actual_token,
            unsafe {
                from_utf8_unchecked(
                    &self.source.code()[max(0, self.position as isize - 40) as usize
                        ..min(
                            self.source.code().len() as isize,
                            self.position as isize + 40,
                        ) as usize],
                )
            }
        ))
    }
}

impl PartialEq for SyntaxError {
    fn eq(&self, other: &Self) -> bool {
        self.typ == other.typ
    }
}

impl Eq for SyntaxError {}

pub type SyntaxResult<T> = Result<T, SyntaxError>;