1use crate::lexer::{Lexer, Token, TokenType};
2use crate::{ConfArgument, ConfComment, ConfDirective, ConfError, ConfOptions, ConfUnit};
3
4pub struct Parser<'a> {
6 lexer: Lexer<'a>,
8 current_token: Token,
10 options: ConfOptions,
12 current_depth: usize,
14}
15
16impl<'a> Parser<'a> {
17 pub fn new(input: &'a str, options: ConfOptions) -> Result<Self, ConfError> {
19 let mut lexer = Lexer::new(input, options.clone());
20 let current_token = lexer.next_token()?;
21
22 Ok(Self {
23 lexer,
24 current_token,
25 options,
26 current_depth: 0,
27 })
28 }
29
30 fn advance(&mut self) -> Result<(), ConfError> {
32 self.current_token = self.lexer.next_token()?;
33 Ok(())
34 }
35
36 pub fn parse(&mut self) -> Result<ConfUnit, ConfError> {
38 let mut directives = Vec::new();
39 let mut comments = Vec::new();
40
41 while self.current_token.token_type != TokenType::Eof {
42 match self.current_token.token_type {
43 TokenType::Comment => {
44 let comment = self.parse_comment()?;
45 comments.push(comment);
46 }
47 TokenType::Newline | TokenType::Whitespace | TokenType::Continuation => {
48 self.advance()?;
49 }
50 _ => {
51 let directive = self.parse_directive()?;
52 directives.push(directive);
53 }
54 }
55 }
56
57 Ok(ConfUnit {
58 directives,
59 comments,
60 })
61 }
62
63 fn parse_comment(&mut self) -> Result<ConfComment, ConfError> {
65 if self.current_token.token_type != TokenType::Comment {
66 return Err(ConfError::ParserError {
67 position: self.current_token.span.start,
68 message: "Expected comment".to_string(),
69 });
70 }
71
72 let span = self.current_token.span.clone();
73 let content = self.lexer.input()[span.clone()].to_string();
74 let is_multi_line = content.starts_with("/*");
75
76 self.advance()?;
77
78 Ok(ConfComment {
79 content,
80 span,
81 is_multi_line,
82 })
83 }
84
85 fn parse_directive(&mut self) -> Result<ConfDirective, ConfError> {
87 if self.current_depth >= self.options.max_depth {
89 return Err(ConfError::ParserError {
90 position: self.current_token.span.start,
91 message: format!(
92 "Maximum directive depth of {} exceeded",
93 self.options.max_depth
94 ),
95 });
96 }
97
98 if self.current_token.token_type != TokenType::Argument {
100 return Err(ConfError::ParserError {
101 position: self.current_token.span.start,
102 message: "Expected directive name".to_string(),
103 });
104 }
105
106 let name_span = self.current_token.span.clone();
107 let name_value = self.lexer.input()[name_span.clone()].to_string();
108 let name = ConfArgument {
109 value: name_value,
110 span: name_span,
111 is_quoted: self.current_token.is_quoted,
112 is_triple_quoted: self.current_token.is_triple_quoted,
113 is_expression: self.current_token.is_expression,
114 };
115
116 self.advance()?;
117
118 let mut arguments = Vec::new();
120 while self.current_token.token_type == TokenType::Argument
121 || self.current_token.token_type == TokenType::Continuation
122 {
123 if self.current_token.token_type == TokenType::Continuation {
125 self.advance()?;
126 continue;
127 }
128
129 let arg_span = self.current_token.span.clone();
130 let arg_value = self.lexer.input()[arg_span.clone()].to_string();
131 let argument = ConfArgument {
132 value: arg_value,
133 span: arg_span,
134 is_quoted: self.current_token.is_quoted,
135 is_triple_quoted: self.current_token.is_triple_quoted,
136 is_expression: self.current_token.is_expression,
137 };
138
139 arguments.push(argument);
140 self.advance()?;
141 }
142
143 let mut children = Vec::new();
145 if self.current_token.token_type == TokenType::LeftCurlyBrace {
146 self.advance()?; self.current_depth += 1;
148
149 while self.current_token.token_type == TokenType::Newline {
151 self.advance()?;
152 }
153
154 while self.current_token.token_type != TokenType::RightCurlyBrace
156 && self.current_token.token_type != TokenType::Eof
157 {
158 match self.current_token.token_type {
159 TokenType::Comment => {
160 let _comment = self.parse_comment()?;
161 }
163 TokenType::Newline | TokenType::Whitespace => {
164 self.advance()?;
165 }
166 _ => {
167 let directive = self.parse_directive()?;
168 children.push(directive);
169 }
170 }
171 }
172
173 if self.current_token.token_type != TokenType::RightCurlyBrace {
175 return Err(ConfError::ParserError {
176 position: self.current_token.span.start,
177 message: "Expected '}'".to_string(),
178 });
179 }
180
181 self.advance()?; self.current_depth -= 1;
183 } else if self.current_token.token_type == TokenType::Semicolon {
184 self.advance()?; } else if self.current_token.token_type != TokenType::Newline
186 && self.current_token.token_type != TokenType::Eof
187 && self.current_token.token_type != TokenType::Continuation
188 {
189 return Err(ConfError::ParserError {
190 position: self.current_token.span.start,
191 message: "Expected ';', '{', or newline".to_string(),
192 });
193 }
194
195 Ok(ConfDirective {
196 name,
197 arguments,
198 children,
199 })
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 #[test]
208 fn test_parser_new() {
209 let input = "test";
210 let options = ConfOptions::default();
211 let parser = Parser::new(input, options);
212 assert!(parser.is_ok());
213 }
214
215 #[test]
216 fn test_parser_parse_empty() {
217 let input = "";
218 let options = ConfOptions::default();
219 let mut parser = Parser::new(input, options).unwrap();
220 let result = parser.parse();
221 assert!(result.is_ok());
222 let conf_unit = result.unwrap();
223 assert_eq!(conf_unit.directives.len(), 0);
224 assert_eq!(conf_unit.comments.len(), 0);
225 }
226
227 #[test]
228 fn test_parser_parse_simple_directive() {
229 let input = "server localhost";
230 let options = ConfOptions::default();
231 let mut parser = Parser::new(input, options).unwrap();
232 let result = parser.parse();
233 assert!(result.is_ok());
234 let conf_unit = result.unwrap();
235 assert_eq!(conf_unit.directives.len(), 1);
236 assert_eq!(conf_unit.directives[0].name.value, "server");
237 assert_eq!(conf_unit.directives[0].arguments.len(), 1);
238 assert_eq!(conf_unit.directives[0].arguments[0].value, "localhost");
239 }
240
241 #[test]
242 fn test_parser_parse_block_directive() {
243 let input = "server {\n listen 80;\n}";
244 let options = ConfOptions::default();
245 let mut parser = Parser::new(input, options).unwrap();
246 let result = parser.parse();
247 assert!(result.is_ok());
248 let conf_unit = result.unwrap();
249 assert_eq!(conf_unit.directives.len(), 1);
250 assert_eq!(conf_unit.directives[0].name.value, "server");
251 assert_eq!(conf_unit.directives[0].arguments.len(), 0);
252 assert_eq!(conf_unit.directives[0].children.len(), 1);
253 assert_eq!(conf_unit.directives[0].children[0].name.value, "listen");
254 assert_eq!(conf_unit.directives[0].children[0].arguments.len(), 1);
255 assert_eq!(conf_unit.directives[0].children[0].arguments[0].value, "80");
256 }
257
258 #[test]
259 fn test_parser_parse_with_comments() {
260 let input = "# Comment\nserver localhost";
261 let options = ConfOptions {
262 allow_c_style_comments: true,
263 ..Default::default()
264 };
265 let mut parser = Parser::new(input, options).unwrap();
266 let result = parser.parse();
267 assert!(result.is_ok());
268 let conf_unit = result.unwrap();
269 assert_eq!(conf_unit.directives.len(), 1);
270 assert_eq!(conf_unit.comments.len(), 1);
271 assert_eq!(conf_unit.comments[0].content, "# Comment");
272 }
273
274 #[test]
275 fn test_parser_max_depth() {
276 let input = "a { b { c { d { e { f { g { h { i { j { k { } } } } } } } } } } }";
277 let options = ConfOptions {
278 max_depth: 5,
279 ..Default::default()
280 };
281 let mut parser = Parser::new(input, options).unwrap();
282 let result = parser.parse();
283 assert!(result.is_err());
284 if let Err(ConfError::ParserError { message, .. }) = result {
285 assert!(message.contains("Maximum directive depth"));
286 } else {
287 panic!("Expected ParserError");
288 }
289 }
290}