1use std::ops::Deref;
4
5use crate::lexer::{line_column2, Literal};
6use nom::{
7    branch::alt,
8    bytes::complete::{escaped, tag},
9    character::{
10        complete::{char as cchar, multispace0, none_of},
11        streaming::anychar,
12    },
13    combinator::{eof, fail, map, map_opt, map_res, opt, value},
14    error::{context, VerboseError},
15    number::complete::be_u8,
16    sequence::{delimited, tuple},
17    Parser,
18};
19use regex::Regex;
20
21pub type IResult<I, O> = nom::IResult<I, O, VerboseError<I>>;
22
23#[derive(Debug, PartialEq, Eq, Clone, Copy)]
24pub enum Token<'a> {
25    Slash,
26    DoubleSlash,
27    LeftBracket,
28    RightBracket,
29    Eof,
30    Operator(Op),
31    Literal(Literal<'a>),
32}
33
34#[derive(Debug, PartialEq, Eq, Clone, Copy)]
35pub enum Op {
36    Equal,
37    Match,
38}
39
40#[derive(Debug)]
41#[repr(transparent)]
42pub struct CPathBuf(pub Vec<Item>);
43
44impl<'a> Deref for CPathBuf {
45    type Target = CPath;
46
47    fn deref(&self) -> &Self::Target {
48        CPath::new(&self.0)
49    }
50}
51
52impl CPathBuf {
53    pub fn parse(path: &str) -> anyhow::Result<Self> {
54        let res = parse_cpath(path.as_ref()).map_err(|err| {
55            err.map(|e| {
56                let errs = e
57                    .errors
58                    .iter()
59                    .map(|(i, code)| {
60                        let ((l, c), pos) = line_column2(path.as_ref(), i).unwrap();
61                        format!("0x{pos:x}({l}:{c}) err: {:?}", code)
62                    })
63                    .collect::<Vec<_>>();
64                anyhow::anyhow!("{}", errs.join("\n"))
65            })
66        })?;
67        Ok(res.1)
68    }
69}
70
71#[derive(Debug)]
72#[repr(transparent)]
73pub struct CPath(pub [Item]);
74
75impl Deref for CPath {
76    type Target = [Item];
77
78    fn deref(&self) -> &Self::Target {
79        &self.0
80    }
81}
82
83impl CPath {
84    pub fn new<'a>(items: &'a [Item]) -> &'a Self {
85        unsafe { &*(items as *const _ as *const Self) }
86    }
87
88    pub fn peek<'a>(&'a self) -> Option<(&'a Item, &'a Self, bool)> {
89        let (mut first, mut rest) = self.0.split_first()?;
90        let mut anylevel = false;
91        while first.filter.any_level() {
92            anylevel = true;
93            let (a, b) = rest.split_first()?;
94            first = a;
95            rest = b;
96        }
97        Some((first, Self::new(rest), anylevel))
98    }
99}
100
101#[derive(Debug)]
102pub struct Item {
103    pub filter: Box<Filter>,
104    pub cond: Option<Box<Cond>>,
105}
106
107#[derive(Debug)]
108pub enum Filter {
109    Eq(String),
110    Re(Regex),
111    Any,
112    AnyLevel,
113}
114
115impl Filter {
116    pub fn any_level(&self) -> bool {
117        matches!(self, Self::AnyLevel)
118    }
119}
120
121#[derive(Debug)]
122pub enum Cond {
123    Exists(Regex),
124    ChildExists(Item),
125    Equal { name: String, value: String },
126    Match { name: String, regex: Regex },
127}
128
129fn literal(input: &[u8]) -> IResult<&[u8], Token> {
130    let (_, mut first) = be_u8(input)?;
131
132    let (input, raw) = match first {
133        b'"' => map_res(
134            map(
136                delimited(
137                    cchar(first as _),
138                    opt(escaped(none_of(r#"\""#), '\\', anychar)),
139                    cchar(first as _),
140                ),
141                Option::unwrap_or_default,
142            ),
143            std::str::from_utf8,
144        )(input),
145        b'\'' => map_res(
146            map(
148                delimited(
149                    cchar(first as _),
150                    opt(escaped(none_of(r#"\'"#), '\\', anychar)),
151                    cchar(first as _),
152                ),
153                Option::unwrap_or_default,
154            ),
155            std::str::from_utf8,
156        )(input),
157        _ => {
158            first = 0;
159            map_res(
160                escaped(none_of(" \t\r\n'\"\\[]/=~"), '\\', anychar),
161                std::str::from_utf8,
162            )(input)
163        }
164    }?;
165    Ok((input, Token::Literal(Literal { raw, quote: first })))
166}
167
168pub fn token(input: &[u8]) -> IResult<&[u8], Token> {
169    use Token::*;
170
171    map(
172        tuple((
173            multispace0,
174            alt((
175                eof.map(|_| Eof),
176                value(DoubleSlash, tag(b"//")),
177                value(Slash, tag(b"/")),
178                value(LeftBracket, tag(b"[")),
179                value(RightBracket, tag(b"]")),
180                value(Operator(Op::Equal), tag(b"=")),
181                value(Operator(Op::Match), tag(b"~")),
182                literal,
183            )),
184        )),
185        |x| x.1,
186    )(input)
187}
188
189fn expect_map<'a, T: 'a>(
190    fun: impl Fn(Token<'a>) -> Option<T> + 'a,
191) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], T> + 'a {
192    map_res(token, move |tok| {
193        fun(tok).ok_or(nom::error::ErrorKind::Fail)
194    })
195}
196
197fn expect<'a>(tk: Token<'a>) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], ()> + 'a {
198    context(
199        "expect token",
200        map_res(token, move |tok| {
201            if tok == tk {
202                Ok(())
203            } else {
204                Err(nom::error::ErrorKind::Fail)
205            }
206        }),
207    )
208}
209
210fn expect_literal<'a>() -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], Literal<'a>> + 'a {
211    context(
212        "expect literal",
213        map_opt(token, |tok| match tok {
214            Token::Literal(lit) => Some(lit),
215            _ => None,
216        }),
217    )
218}
219
220fn expect_op<'a>() -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], Op> + 'a {
221    context(
222        "expect operator",
223        expect_map(|tok| match tok {
224            Token::Operator(op) => Some(op),
225            _ => None,
226        }),
227    )
228}
229
230fn parse_cond(input: &[u8]) -> IResult<&[u8], Cond> {
231    fn parser(input: &[u8]) -> IResult<&[u8], Cond> {
232        let (rest, tok) = token(input)?;
233        match tok {
234            Token::Slash => map(parse_item, |item| Cond::ChildExists(item))(rest),
235            Token::Literal(lit) => alt((
236                tuple((expect_op(), expect_literal())).map(|(op, val)| {
237                    let name = lit.to_string();
238                    match op {
239                        Op::Equal => Cond::Equal {
240                            name,
241                            value: val.to_string(),
242                        },
243                        Op::Match => Cond::Match {
244                            name,
245                            regex: Regex::new(&val.to_string()).unwrap(),
246                        },
247                    }
248                }),
249                expect(Token::RightBracket)
250                    .map(|_| Cond::Exists(Regex::new(&lit.to_string()).unwrap())),
251            ))(rest),
252            _ => fail(input),
253        }
254    }
255
256    context(
257        "parse cond",
258        delimited(
259            expect(Token::LeftBracket),
260            parser,
261            expect(Token::RightBracket),
262        ),
263    )(input)
264}
265
266fn parse_item(input: &[u8]) -> IResult<&[u8], Item> {
267    let parse_filter = map(expect_literal(), |lit| {
268        Box::new(Filter::Re(Regex::new(&lit.to_string()).unwrap()))
269    });
270    context(
271        "parse item",
272        map(tuple((parse_filter, opt(parse_cond))), |(filter, cond)| {
273            Item {
274                filter,
275                cond: cond.map(Box::new),
276            }
277        }),
278    )(input)
279}
280
281fn parse_cpath(mut input: &[u8]) -> IResult<&[u8], CPathBuf> {
282    let opt_item = &mut alt((
283        expect(Token::DoubleSlash).map(|_| {
284            Some(Item {
285                filter: Filter::AnyLevel.into(),
286                cond: None,
287            })
288        }),
289        map(tuple((expect(Token::Slash), opt(parse_item))), |x| x.1),
290        parse_item.map(|x| Some(x)),
291        expect(Token::Eof).map(|_| None),
292    ));
293
294    let mut res = vec![];
295    loop {
296        let (rest, item) = opt_item(input)?;
297        input = rest;
298
299        if let Some(item) = item {
300            res.push(item);
301        } else {
302            break;
303        }
304    }
305
306    Ok((input, CPathBuf(res)))
307}