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}