1use crate::{
2 ast::{Constraint, Constraints, FieldRule, FieldType, Value},
3 token::{Token, tokenize},
4};
5
6pub struct Parser {
10 tokens: Vec<Token>,
11 pos: usize,
12}
13
14impl Parser {
15 pub fn new(tokens: Vec<Token>) -> Self {
16 Self { tokens, pos: 0 }
17 }
18 fn peek(&self) -> Option<&Token> {
19 self.tokens.get(self.pos)
20 }
21 fn next(&mut self) -> Option<Token> {
22 let t = self.tokens.get(self.pos).cloned();
23 self.pos += 1;
24 t
25 }
26 fn expect(&mut self, expected: &Token) -> Result<(), String> {
27 let t = self.next().ok_or("Unexpected EOF")?;
28 if &t != expected {
29 return Err(format!("Expected {:?}, got {:?}", expected, t));
30 }
31 Ok(())
32 }
33
34 pub fn parse_program(&mut self) -> Result<Vec<FieldRule>, String> {
36 self.expect(&Token::LParen)?;
37 let mut rules = Vec::new();
38 loop {
39 if matches!(self.peek(), Some(Token::RParen)) {
40 self.next();
41 break;
42 }
43 let field = self.parse_field(false)?;
44 rules.push(field);
45
46 match self.peek() {
47 Some(Token::Comma) => {
48 self.next();
49 }
50 Some(Token::RParen) => {}
51 _ => {
52 return Err("Expected ',' or ')'".into());
53 }
54 }
55 }
56 Ok(rules)
57 }
58
59 pub fn parse_field(&mut self, nameless: bool) -> Result<FieldRule, String> {
60 let (name, optional) = if nameless {
64 (String::new(), false)
65 } else {
66 let name = match self.next() {
67 Some(Token::Ident(s)) => s,
68 t => {
69 return Err(format!("Expected field name, got {:?}", t));
70 }
71 };
72
73 let optional = matches!(self.peek(), Some(Token::Question));
74 if optional {
75 self.next();
76 }
77
78 (name, optional)
79 };
80
81 if !nameless {
82 self.expect(&Token::Colon)?;
83 }
84
85 let mut union_types = Vec::new();
89 loop {
90 let ty = match self.next() {
91 Some(Token::Ident(s)) => match s.as_str() {
92 "string" => FieldType::String,
93 "int" => FieldType::Int,
94 "float" => FieldType::Float,
95 "bool" => FieldType::Bool,
96 "object" => FieldType::Object,
97 "array" => FieldType::Array,
98 "email" => FieldType::Email,
99 "uri" => FieldType::Uri,
100 "uuid" => FieldType::Uuid,
101 "ip" => FieldType::Ip,
102 "mac" => FieldType::Mac,
103 "date" => FieldType::Date,
104 "datetime" => FieldType::DateTime,
105 "time" => FieldType::Time,
106 "timestamp" => FieldType::Timestamp,
107 "color" => FieldType::Color,
108 "hostname" => FieldType::Hostname,
109 "slug" => FieldType::Slug,
110 "hex" => FieldType::Hex,
111 "base64" => FieldType::Base64,
112 "password" => FieldType::Password,
113 "token" => FieldType::Token,
114
115 t => {
116 return Err(format!("Unknown type {}", t));
117 }
118 },
119 t => {
120 return Err(format!("Expected type, got {:?}", t));
121 }
122 };
123
124 union_types.push(ty);
125
126 if matches!(self.peek(), Some(Token::Pipe)) {
127 self.next();
128 } else {
129 break;
130 }
131 }
132
133 let field_type = union_types[0].clone();
134
135 let mut sub_rule = None;
136 let mut children = None;
137 let mut constraints = Vec::new();
138 let mut enum_values = None;
139 let mut default = None;
140 let is_array = field_type == FieldType::Array;
141
142 if is_array && matches!(self.peek(), Some(Token::Lt)) {
147 self.next(); let sub = self.parse_field(true)?;
150 sub_rule = Some(Box::new(FieldRule {
152 field: String::new(), field_type: sub.field_type,
154 required: sub.required,
155 default: sub.default,
156 enum_values: sub.enum_values,
157 union_types: sub.union_types,
158 constraints: sub.constraints,
159 rule: sub.rule,
160 children: sub.children,
161 is_array: sub.is_array,
162 }));
163 self.expect(&Token::Gt)?;
164 }
165
166 if field_type == FieldType::Object && matches!(self.peek(), Some(Token::LParen)) {
170 self.next(); let mut inner = Vec::new();
172
173 loop {
174 if matches!(self.peek(), Some(Token::RParen)) {
175 self.next(); break;
177 }
178
179 inner.push(self.parse_field(false)?);
180
181 match self.peek() {
182 Some(Token::Comma) => {
183 self.next();
184 }
185 Some(Token::RParen) => {}
186 _ => {
187 return Err("Expected ',' or ')' in object".into());
188 }
189 }
190 }
191
192 children = Some(inner);
193 }
194
195 loop {
199 match self.peek() {
200 Some(Token::LBracket) => {
202 constraints.push(self.parse_range(&field_type)?);
203 }
204
205 Some(Token::LParen) => {
206 if field_type == FieldType::Object {
207 return Err("Unexpected '(' after object definition".into());
208 }
209 constraints.push(self.parse_range(&field_type)?);
210 }
211
212 Some(Token::Ident(s)) if s == "regex" => {
214 self.next();
215 self.expect(&Token::LParen)?;
216 let pattern = match self.next() {
217 Some(Token::Ident(p)) => p,
218 t => {
219 return Err(format!("Expected pattern, got {:?}", t));
220 }
221 };
222 self.expect(&Token::RParen)?;
223 constraints.push(Constraint::Regex(pattern));
224 }
225
226 Some(Token::Ident(s)) if s == "enum" => {
228 self.next();
229 self.expect(&Token::LParen)?;
230 let mut vals = Vec::new();
231
232 loop {
233 match self.next() {
234 Some(Token::Number(s)) => {
235 let v = self
237 .parse_token_number_as_type(&Token::Number(s), &field_type)?;
238 vals.push(v);
239 }
240 Some(Token::Ident(v)) => {
241 if field_type == FieldType::Bool {
243 match v.as_str() {
244 "true" => vals.push(Value::Bool(true)),
245 "false" => vals.push(Value::Bool(false)),
246 _ => vals.push(Value::String(v)),
247 }
248 } else {
249 vals.push(Value::String(v));
250 }
251 }
252 t => {
253 return Err(format!("Expected enum value, got {:?}", t));
254 }
255 }
256
257 match self.peek() {
258 Some(Token::Comma) => {
259 self.next();
260 }
261 Some(Token::RParen) => {
262 self.next();
263 break;
264 }
265 _ => {
266 return Err("Expected ',' or ')' in enum".into());
267 }
268 }
269 }
270
271 enum_values = Some(vals);
272 }
273
274 Some(Token::Equal) => {
276 self.next();
277 let token = self.next().ok_or("Expected default value")?;
278
279let val = match token {
280 Token::Number(s) => {
281 if field_type == FieldType::String {
283 Value::String(s)
284 } else {
285 self.parse_token_number_as_type(&Token::Number(s), &field_type)?
287 }
288 }
289 Token::Ident(s) => {
290 if field_type == FieldType::Bool {
291 match s.as_str() {
292 "true" => Value::Bool(true),
293 "false" => Value::Bool(false),
294 _ => {
295 return Err(format!("Invalid bool '{}'", s));
296 }
297 }
298 } else {
299 Value::String(s)
301 }
302 }
303 t => {
304 return Err(format!("Unexpected default value {:?}", t));
305 }
306 };
307
308 default = Some(val);
309 }
310
311 _ => {
312 break;
313 }
314 }
315 }
316
317 Ok(FieldRule {
318 field: name,
319 field_type,
320 required: if nameless { true } else { !optional },
321 default,
322 enum_values,
323 union_types: if union_types.len() > 1 {
324 Some(union_types)
325 } else {
326 None
327 },
328 constraints: if constraints.is_empty() {
329 None
330 } else {
331 Some(Constraints { items: constraints })
332 },
333 rule: sub_rule,
334 children,
335 is_array,
336 })
337 }
338
339 fn parse_token_number_as_type(
341 &self,
342 token: &Token,
343 field_type: &FieldType,
344 ) -> Result<Value, String> {
345 match token {
346 Token::Number(s) => {
347 match field_type {
348 FieldType::Int => {
350 if let Ok(i) = s.parse::<i64>() {
351 Ok(Value::Int(i))
352 } else if let Ok(f) = s.parse::<f64>() {
353 Ok(Value::Float(f))
355 } else {
356 Err(format!("Invalid integer '{}'", s))
357 }
358 }
359 FieldType::Float => s
360 .parse::<f64>()
361 .map(Value::Float)
362 .map_err(|_| format!("Invalid float '{}'", s)),
363 FieldType::String => s
365 .parse::<i64>()
366 .map(Value::Int)
367 .map_err(|_| format!("Invalid length number '{}'", s)),
368 _ => Err(format!(
369 "Range only supports int/float/string, got {:?}",
370 field_type
371 )),
372 }
373 }
374 _ => Err("Expected a number token".into()),
375 }
376 }
377
378 fn parse_range(&mut self, field_type: &FieldType) -> Result<Constraint, String> {
380 let min_inclusive = matches!(self.peek(), Some(Token::LBracket));
381 self.next(); let min_token = self.next().ok_or("Expected min number")?;
384 let mut min = self.parse_token_number_as_type(&min_token, field_type)?;
385
386 self.expect(&Token::Comma)?;
387
388 let max_token = self.next().ok_or("Expected max number")?;
389 let mut max = self.parse_token_number_as_type(&max_token, field_type)?;
390
391 if let FieldType::Int = field_type {
393 if let Value::Float(f) = min {
394 min = Value::Int(f.ceil() as i64); }
396 if let Value::Float(f) = max {
397 max = Value::Int(f.floor() as i64); }
399 }
400 let max_inclusive = match self.next() {
403 Some(Token::RBracket) => true,
404 Some(Token::RParen) => false,
405 t => {
406 return Err(format!("Expected closing bracket or paren, got {:?}", t));
407 }
408 };
409
410 Ok(Constraint::Range {
411 min,
412 max,
413 min_inclusive,
414 max_inclusive,
415 })
416 }
417
418 pub fn parse_rules(input: &str) -> Result<Vec<FieldRule>, String> {
419 let tokens = tokenize(input)?;
420 let mut parser = Parser::new(tokens);
421 parser.parse_program()
422 }
423}