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
118
119
120
121
122
123
124
125
126
127
128
use std::fmt::format;
use logos::Logos;
use crate::error::ParseError;
use crate::token;

#[cfg(windows)]
const LINE_SEP: &'static str = "\r\n";
#[cfg(not(windows))]
const LINE_SEP: &'static str = "\n";

fn create_marker(pos: usize) -> String {
    let mut del = String::new();
    del.push_str(LINE_SEP);
    for _ in 0..pos {
        del.push_str(" ");
    }
    del.push_str("^");
    del.push_str(LINE_SEP);
    del
}

/// A simple wrapper for `Logos` that works with &str
pub(crate) struct LexIt<'a, T>
    where
        T: Logos<'a, Source=str>,
{
    tokens_str: Vec<&'a str>,
    pub(crate) tokens: Vec<T>,
}

impl<'a, T> LexIt<'a, T>
    where
        T: Logos<'a, Source=str> + PartialEq,
{
    /// construct a new Lexer or return an error
    pub(crate) fn new(source: &'a str) -> Result<Self, ParseError<'a>>
        where
            T::Extras: Default,
    {
        let mut delegate = T::lexer(source);
        let mut tokens = vec![];
        let mut tokens_str = vec![];

        while let Some(t) = delegate.next() {
            match t {
                Ok(tok) => {
                    tokens.push(tok);
                    tokens_str.push(delegate.slice());
                }
                Err(_) => {
                    return Err(ParseError::BadToken(delegate.slice(), delegate.span()));
                }
            }

        }

        Ok(LexIt { tokens_str, tokens })
    }

    pub(crate) fn token(&self, pos: usize) -> Result<(&T, usize), ParseError<'a>> {
        match self.tokens.get(pos) {
            None => Err(ParseError::ReachedEOF(pos)),
            Some(t) => Ok((t, pos)),
        }
    }

    pub(crate) fn env(&self, pos: usize) -> String {
        let mut str = String::new();
        let max = self.tokens_str.len() - 1;

        let left = pos - 3;
        let right = pos + 3;

        let left = if left <= 0 { 0 } else { left };
        let right = if right >= max { max } else { right };

        let mut idx = left;
        let mut line = 0;
        let mut pos_in_line = 0;
        for p in &self.tokens_str[left..=right] {
            str.push_str(*p);
            if idx == pos {
                line = str.lines().count() - 1;
                pos_in_line = str.lines().last().map(|s| s.len() - p.len()).unwrap_or(0);
            }
            idx = idx + 1;
        }

        let mut final_str = String::new();

        for (i, s) in str.lines().into_iter().enumerate() {
            final_str.push_str(s);
            if i == line {
                final_str.push_str(create_marker(pos_in_line).as_str());
            }
        }

        final_str
    }

    pub(crate) fn len(&self) -> usize {
        self.tokens.len()
    }
}

#[cfg(test)]
mod tests {
    use crate::parser::{EmptyToken, Parsit};
    use crate::token;
    use logos::Logos;
    use crate::step::Step;

    #[derive(Logos, PartialEq)]
    pub enum T<'a> {
        #[regex(r"[a-zA-Z-]+")]
        Word(&'a str),

        #[token("|")]
        Del,
    }

    #[test]
    fn test() {
        let pit: Parsit<T> = Parsit::new("abc|bcd|a|b|x").unwrap();
        let x = pit.env::<EmptyToken>(&Step::Fail(9));
        println!("{}", x)
    }
}