1pub mod lexer;
4
5use std::path::Path;
6
7use crate::{
8 ast::{Directive, DirectiveTrait},
9 lexer::line_column2,
10};
11
12use self::lexer::*;
13
14use anyhow::Context;
15use nom::{
16 combinator::{fail, map, map_opt, verify},
17 sequence::tuple,
18 IResult,
19};
20
21#[derive(Debug, Clone, Default)]
22pub struct Apache;
23
24impl DirectiveTrait<Apache> for Directive<Apache> {
25 fn parse(input: &[u8]) -> anyhow::Result<Vec<Self>> {
26 let res = parse_block(input).map_err(|err| {
27 err.map(|e| {
28 let ((l, c), pos) = line_column2(input, e.input).unwrap();
29 anyhow::anyhow!("{pos}({l}:{c}) err: {:?}", e.code)
30 })
31 })?;
32 Ok(res.1)
33 }
34
35 fn resolve_include_inner(mut self, dir: &Path, out: &mut Vec<Self>) -> anyhow::Result<()> {
36 let optional = self.name.eq_ignore_ascii_case("IncludeOptional");
37 if self.name.eq_ignore_ascii_case("include") || optional {
38 let path = Path::new(
39 self.args
40 .get(0)
41 .context("include directive expect one arg")?
42 .as_str(),
43 );
44 for path in glob::glob(
45 &if path.is_absolute() {
46 path.to_path_buf()
47 } else {
48 dir.join(path)
49 }
50 .to_string_lossy(),
51 )?
52 .flatten()
53 {
54 if optional && !path.exists() {
55 continue;
56 }
57 let data = std::fs::read(&path)?;
58 for c in Self::parse(&data).with_context(|| format!("parse {path:?}"))? {
59 c.resolve_include_inner(dir, out)?;
60 }
61 }
62 } else {
63 self.resolve_include(dir)?;
64 out.push(self);
65 }
66 Ok(())
67 }
68}
69
70fn parse_block(mut input: &[u8]) -> IResult<&[u8], Vec<Directive<Apache>>> {
71 let mut result = vec![];
72 loop {
73 let (rest, tok) = tokenizer(input)?;
74 match tok {
75 Token::NewLine => {
76 input = rest;
77 continue;
78 }
79 Token::CloseTag | Token::Eof => break,
80 _ => {}
81 }
82 let res = parse_one(rest, tok)?;
83 result.push(res.1);
84 input = res.0;
85 }
86 Ok((input, result))
87}
88
89fn parse_one<'a>(input: &'a [u8], tok: Token<'a>) -> IResult<&'a [u8], Directive<Apache>> {
90 match tok {
91 Token::OpenTag => {
92 let (rest, name) = map_opt(tokenizer, |tok| tok.ident())(input)?;
93 map(
94 tuple((
95 parse_args::<false>,
96 verify(tokenizer, |tok| tok == &Token::EndTag),
97 parse_block,
98 verify(tuple((tokenizer, tokenizer, tokenizer)), |&(b, tag, e)| {
99 b == Token::CloseTag
100 && tag
101 .raw_string()
102 .unwrap_or_default()
103 .eq_ignore_ascii_case(name)
104 && e == Token::EndTag
105 }),
106 )),
107 |(args, _, children, _)| Directive {
108 name: name.into(),
109 args,
110 children: Some(children),
111 ..Default::default()
112 },
113 )(rest)
114 }
115 Token::Literal(l) => map(parse_args::<true>, |args| Directive {
116 name: l.raw.into(),
117 args,
118 children: None,
119 ..Default::default()
120 })(input),
121 _ => fail(input),
122 }
123}
124
125fn parse_args<const NL: bool>(mut input: &[u8]) -> IResult<&[u8], Vec<String>> {
126 let mut result = vec![];
127 loop {
128 let (rest, tok) = inner_tokenizer::<NL>(input)?;
129 if let Some(l) = tok.literal() {
130 result.push(l.into());
131 } else {
132 break;
133 }
134 input = rest;
135 }
136 Ok((input, result))
138}