nginx_discovery/parser/
parse.rs1use crate::ast::{Config, Directive, Value};
3use crate::error::{Error, Result};
4use crate::parser::{Lexer, Token, TokenKind};
6
7pub struct Parser {
9 tokens: Vec<Token>,
11 pos: usize,
13}
14
15impl Parser {
16 pub fn new(input: &str) -> Result<Self> {
22 let mut lexer = Lexer::new(input);
23 let tokens = lexer.tokenize()?;
24
25 Ok(Self { tokens, pos: 0 })
26 }
27
28 pub fn parse(&mut self) -> Result<Config> {
37 let mut directives = Vec::new();
38
39 while !self.is_eof() {
40 if self.check_comment() {
42 self.advance();
43 continue;
44 }
45
46 let directive = self.parse_directive()?;
47 directives.push(directive);
48 }
49
50 Ok(Config::with_directives(directives))
51 }
52
53 fn parse_directive(&mut self) -> Result<Directive> {
55 let _start_token = self.current();
56 let name = self.expect_word()?;
57
58 let mut args = Vec::new();
59
60 while !self.check(&TokenKind::Semicolon)
62 && !self.check(&TokenKind::LeftBrace)
63 && !self.is_eof()
64 {
65 if self.check_comment() {
66 self.advance();
67 continue;
68 }
69
70 let arg = self.parse_value()?;
71 args.push(arg);
72 }
73
74 if self.check(&TokenKind::LeftBrace) {
76 self.advance(); let children = self.parse_block_contents()?;
80
81 self.expect(&TokenKind::RightBrace)?;
82
83 Ok(Directive::block_with_values(name, args, children))
84 } else {
85 self.expect(&TokenKind::Semicolon)?;
87
88 Ok(Directive::simple_with_values(name, args))
89 }
90 }
91
92 fn parse_block_contents(&mut self) -> Result<Vec<Directive>> {
94 let mut directives = Vec::new();
95
96 while !self.check(&TokenKind::RightBrace) && !self.is_eof() {
97 if self.check_comment() {
98 self.advance();
99 continue;
100 }
101
102 let directive = self.parse_directive()?;
103 directives.push(directive);
104 }
105
106 Ok(directives)
107 }
108
109 fn parse_value(&mut self) -> Result<Value> {
111 let token = self.current();
112
113 let value = match &token.kind {
114 TokenKind::String(s) => Value::single_quoted(s.clone()),
115 TokenKind::Word(s) | TokenKind::Number(s) => Value::literal(s.clone()),
116 TokenKind::Variable(s) => Value::variable(s.clone()),
117 _ => {
118 return Err(Error::syntax(
119 "expected value",
120 token.span.line,
121 token.span.col,
122 Some("word, string, number, or variable".to_string()),
123 Some(format!("{}", token.kind)),
124 ));
125 }
126 };
127
128 self.advance();
129 Ok(value)
130 }
131
132 fn expect(&mut self, kind: &TokenKind) -> Result<Token> {
134 let token = self.current().clone(); if std::mem::discriminant(&token.kind) == std::mem::discriminant(kind) {
137 self.advance();
138 Ok(token) } else {
140 Err(Error::syntax(
141 "unexpected token".to_string(),
142 token.span.line,
143 token.span.col,
144 Some(format!("{kind}")),
145 Some(format!("{}", token.kind)),
146 ))
147 }
148 }
149
150 fn expect_word(&mut self) -> Result<String> {
152 let token = self.current();
153
154 if let TokenKind::Word(name) = &token.kind {
155 let result = name.clone();
156 self.advance();
157 Ok(result)
158 } else {
159 Err(Error::syntax(
160 "expected directive name",
161 token.span.line,
162 token.span.col,
163 Some("word".to_string()),
164 Some(format!("{}", token.kind)),
165 ))
166 }
167 }
168
169 fn current(&self) -> &Token {
171 self.tokens
172 .get(self.pos)
173 .unwrap_or(&self.tokens[self.tokens.len() - 1])
174 }
175
176 fn check(&self, kind: &TokenKind) -> bool {
178 if self.is_eof() {
179 return false;
180 }
181 std::mem::discriminant(&self.current().kind) == std::mem::discriminant(kind)
182 }
183
184 fn check_comment(&self) -> bool {
186 matches!(self.current().kind, TokenKind::Comment(_))
187 }
188
189 fn is_eof(&self) -> bool {
191 matches!(self.current().kind, TokenKind::Eof)
192 }
193
194 fn advance(&mut self) {
196 if !self.is_eof() {
197 self.pos += 1;
198 }
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn test_parse_simple_directive() {
208 let input = "user nginx;";
209 let mut parser = Parser::new(input).unwrap();
210 let config = parser.parse().unwrap();
211
212 assert_eq!(config.directives.len(), 1);
213 assert_eq!(config.directives[0].name(), "user");
214 assert_eq!(config.directives[0].args().len(), 1);
215 }
216
217 #[test]
218 fn test_parse_multiple_directives() {
219 let input = "user nginx;\nworker_processes auto;";
220 let mut parser = Parser::new(input).unwrap();
221 let config = parser.parse().unwrap();
222
223 assert_eq!(config.directives.len(), 2);
224 assert_eq!(config.directives[0].name(), "user");
225 assert_eq!(config.directives[1].name(), "worker_processes");
226 }
227
228 #[test]
229 fn test_parse_block_directive() {
230 let input = "server { listen 80; }";
231 let mut parser = Parser::new(input).unwrap();
232 let config = parser.parse().unwrap();
233
234 assert_eq!(config.directives.len(), 1);
235 assert!(config.directives[0].is_block());
236 assert_eq!(config.directives[0].children().unwrap().len(), 1);
237 }
238
239 #[test]
240 fn test_parse_nested_blocks() {
241 let input = r"
242http {
243 server {
244 listen 80;
245 location / {
246 root /var/www;
247 }
248 }
249}
250";
251 let mut parser = Parser::new(input).unwrap();
252 let config = parser.parse().unwrap();
253
254 assert_eq!(config.directives.len(), 1);
255
256 let http = &config.directives[0];
257 assert_eq!(http.name(), "http");
258 assert_eq!(http.children().unwrap().len(), 1);
259
260 let server = &http.children().unwrap()[0];
261 assert_eq!(server.name(), "server");
262 assert_eq!(server.children().unwrap().len(), 2); }
264
265 #[test]
266 fn test_parse_with_comments() {
267 let input = r"
268# Main config
269user nginx; # Run as nginx
270";
271 let mut parser = Parser::new(input).unwrap();
272 let config = parser.parse().unwrap();
273
274 assert_eq!(config.directives.len(), 1);
276 assert_eq!(config.directives[0].name(), "user");
277 }
278
279 #[test]
280 fn test_parse_strings() {
281 let input = r#"root "/var/www/html";"#;
282 let mut parser = Parser::new(input).unwrap();
283 let config = parser.parse().unwrap();
284
285 assert_eq!(config.directives.len(), 1);
286 assert_eq!(config.directives[0].args().len(), 1);
287 }
288
289 #[test]
290 fn test_parse_variables() {
291 let input = "set $host localhost;";
292 let mut parser = Parser::new(input).unwrap();
293 let config = parser.parse().unwrap();
294
295 assert_eq!(config.directives.len(), 1);
296 assert!(config.directives[0].args()[0].is_variable());
297 }
298}