1use crate::base62::decode_base62;
39use crate::error::{DxError, Result};
40use crate::schema::{Schema, TypeHint};
41use crate::tokenizer::{Token, Tokenizer};
42use crate::types::{DxArray, DxObject, DxTable, DxValue};
43use rustc_hash::FxHashMap;
44
45pub struct Parser<'a> {
47 tokenizer: Tokenizer<'a>,
48 aliases: FxHashMap<String, String>,
50 anchors: Vec<DxValue>,
52 prefix_stack: Vec<String>,
54 schemas: FxHashMap<String, Schema>,
56 auto_counters: FxHashMap<String, i64>,
58}
59
60impl<'a> Parser<'a> {
61 pub fn new(input: &'a [u8]) -> Self {
63 Self {
64 tokenizer: Tokenizer::new(input),
65 aliases: FxHashMap::default(),
66 anchors: Vec::new(),
67 auto_counters: FxHashMap::default(),
68 prefix_stack: Vec::new(),
69 schemas: FxHashMap::default(),
70 }
71 }
72
73 pub fn parse(&mut self) -> Result<DxValue> {
75 let mut root = DxObject::new();
76
77 loop {
78 self.tokenizer.skip_whitespace();
79 if self.tokenizer.is_eof() {
80 break;
81 }
82
83 let token = self.tokenizer.peek_token()?;
84 match token {
85 Token::Eof => break,
86 Token::Newline => {
87 self.tokenizer.next_token()?;
88 continue;
89 }
90 Token::Dollar => {
91 if self.is_alias_definition()? {
94 self.parse_alias()?;
95 } else {
96 let (key, value) = self.parse_key_value_with_alias()?;
98 root.insert(key, value);
99 }
100 }
101 Token::Ident(_) | Token::Caret => {
102 let (key, value) = self.parse_key_value()?;
104 root.insert(key, value);
105 }
106 _ => {
107 return Err(DxError::InvalidSyntax {
108 pos: self.tokenizer.pos(),
109 msg: format!("Unexpected token: {:?}", token),
110 });
111 }
112 }
113 }
114
115 Ok(DxValue::Object(root))
116 }
117
118 fn is_alias_definition(&mut self) -> Result<bool> {
120 let saved_pos = self.tokenizer.pos();
122
123 self.tokenizer.next_token()?;
125
126 let alias_token = self.tokenizer.next_token()?;
128 if !matches!(alias_token, Token::Ident(_)) {
129 self.tokenizer.reset_to(saved_pos);
131 return Ok(false);
132 }
133
134 self.tokenizer.skip_whitespace();
136 let next = self.tokenizer.peek_token()?;
137 let is_definition = matches!(next, Token::Equals);
138
139 self.tokenizer.reset_to(saved_pos);
141
142 Ok(is_definition)
143 }
144
145 fn parse_key_value_with_alias(&mut self) -> Result<(String, DxValue)> {
147 self.tokenizer.next_token()?;
149
150 let full_ident = match self.tokenizer.next_token()? {
152 Token::Ident(bytes) => std::str::from_utf8(bytes)?.to_string(),
153 _ => {
154 return Err(DxError::InvalidSyntax {
155 pos: self.tokenizer.pos(),
156 msg: "Expected alias name after $".to_string(),
157 });
158 }
159 };
160
161 let (alias, suffix) = if let Some(dot_pos) = full_ident.find('.') {
163 (&full_ident[..dot_pos], Some(&full_ident[dot_pos..]))
164 } else {
165 (full_ident.as_str(), None)
166 };
167
168 let resolved = self
170 .aliases
171 .get(alias)
172 .cloned()
173 .ok_or_else(|| DxError::UnknownAlias(alias.to_string()))?;
174
175 let key = if let Some(suffix) = suffix {
177 format!("{}{}", resolved, suffix)
178 } else {
179 resolved
180 };
181
182 self.tokenizer.skip_whitespace();
184 let operator = self.tokenizer.next_token()?;
185
186 let value = match operator {
187 Token::Colon => self.parse_value()?,
188 Token::Bang => DxValue::Bool(true),
189 Token::Void => DxValue::Null,
190 _ => {
191 return Err(DxError::InvalidSyntax {
192 pos: self.tokenizer.pos(),
193 msg: format!("Expected :, !, or ? after key, got {:?}", operator),
194 });
195 }
196 };
197
198 Ok((key, value))
199 }
200
201 fn parse_alias(&mut self) -> Result<()> {
203 self.tokenizer.next_token()?; let alias = match self.tokenizer.next_token()? {
206 Token::Ident(bytes) => std::str::from_utf8(bytes)?.to_string(),
207 _ => {
208 return Err(DxError::InvalidSyntax {
209 pos: self.tokenizer.pos(),
210 msg: "Expected alias name after $".to_string(),
211 });
212 }
213 };
214
215 if !matches!(self.tokenizer.next_token()?, Token::Equals) {
217 return Err(DxError::InvalidSyntax {
218 pos: self.tokenizer.pos(),
219 msg: "Expected = after alias".to_string(),
220 });
221 }
222
223 let value = match self.tokenizer.next_token()? {
224 Token::Ident(bytes) => std::str::from_utf8(bytes)?.to_string(),
225 _ => {
226 return Err(DxError::InvalidSyntax {
227 pos: self.tokenizer.pos(),
228 msg: "Expected value after alias =".to_string(),
229 });
230 }
231 };
232
233 self.aliases.insert(alias, value);
234 Ok(())
235 }
236
237 fn parse_key_value(&mut self) -> Result<(String, DxValue)> {
239 let mut key = String::new();
240
241 if matches!(self.tokenizer.peek_token()?, Token::Caret) {
243 self.tokenizer.next_token()?;
244 if let Some(prefix) = self.prefix_stack.last() {
245 key.push_str(prefix);
246 key.push('.');
247 }
248 }
249
250 if self.prefix_stack.len() > crate::error::MAX_RECURSION_DEPTH {
252 return Err(DxError::recursion_limit_exceeded(self.prefix_stack.len()));
253 }
254
255 match self.tokenizer.next_token()? {
257 Token::Ident(bytes) => {
258 let key_str = std::str::from_utf8(bytes)?;
259 if let Some(alias_key) = key_str.strip_prefix('$') {
261 if let Some(resolved) = self.aliases.get(alias_key) {
262 key.push_str(resolved);
263 } else {
264 return Err(DxError::UnknownAlias(alias_key.to_string()));
265 }
266 } else {
267 key.push_str(key_str);
268 }
269 }
270 _ => {
271 return Err(DxError::InvalidSyntax {
272 pos: self.tokenizer.pos(),
273 msg: "Expected key".to_string(),
274 });
275 }
276 }
277
278 let full_key = key.clone();
280
281 self.tokenizer.skip_whitespace();
283 let operator = self.tokenizer.next_token()?;
284
285 let value = match operator {
286 Token::Colon => {
287 self.prefix_stack.push(full_key.clone());
289 let val = self.parse_value()?;
290 self.prefix_stack.pop();
291 val
292 }
293 Token::Equals => {
294 self.parse_table_definition(&key)?
296 }
297 Token::Stream => {
298 self.parse_stream_array()?
300 }
301 Token::Bang => {
302 DxValue::Bool(true)
304 }
305 Token::Void => {
306 DxValue::Null
308 }
309 _ => {
310 return Err(DxError::InvalidSyntax {
311 pos: self.tokenizer.pos(),
312 msg: format!("Expected :, =, or > after key, got {:?}", operator),
313 });
314 }
315 };
316
317 Ok((key, value))
318 }
319
320 fn parse_value(&mut self) -> Result<DxValue> {
327 self.tokenizer.skip_whitespace();
328
329 let token = self.tokenizer.next_token()?;
330 match token {
331 Token::Eof => Err(DxError::UnexpectedEof(self.tokenizer.pos())),
332 Token::Newline => {
333 Err(DxError::UnexpectedEof(self.tokenizer.pos()))
335 }
336 Token::Null | Token::Void => Ok(DxValue::Null),
337 Token::True => Ok(DxValue::Bool(true)),
338 Token::False => Ok(DxValue::Bool(false)),
339 Token::Int(i) => Ok(DxValue::Int(i)),
340 Token::Float(f) => Ok(DxValue::Float(f)),
341 Token::Ditto => Err(DxError::DittoNoPrevious(self.tokenizer.pos())),
342 Token::At => {
343 let anchor_token = self.tokenizer.next_token()?;
345 let anchor_id = match anchor_token {
346 Token::Eof => {
347 return Err(DxError::UnexpectedEof(self.tokenizer.pos()));
348 }
349 Token::Int(i) => i as usize,
350 _ => {
351 return Err(DxError::InvalidSyntax {
352 pos: self.tokenizer.pos(),
353 msg: "Expected number after @".to_string(),
354 });
355 }
356 };
357 self.anchors
358 .get(anchor_id)
359 .cloned()
360 .ok_or_else(|| DxError::UnknownAnchor(anchor_id.to_string()))
361 }
362 Token::Ident(bytes) => {
363 let s = std::str::from_utf8(bytes)?;
364 Ok(DxValue::String(s.to_string()))
365 }
366 _ => Err(DxError::InvalidSyntax {
367 pos: self.tokenizer.pos(),
368 msg: format!("Unexpected token in value: {:?}", token),
369 }),
370 }
371 }
372
373 fn parse_stream_array(&mut self) -> Result<DxValue> {
375 let mut values = Vec::new();
376
377 loop {
378 self.tokenizer.skip_whitespace();
379
380 let token = self.tokenizer.peek_token()?;
381 if matches!(token, Token::Newline | Token::Eof) {
382 break;
383 }
384
385 let val = self.parse_value()?;
386 values.push(val);
387
388 self.tokenizer.skip_whitespace();
389 if matches!(self.tokenizer.peek_token()?, Token::Pipe) {
390 self.tokenizer.next_token()?; } else {
392 break;
393 }
394 }
395
396 Ok(DxValue::Array(DxArray::stream(values)))
397 }
398
399 fn parse_table_definition(&mut self, name: &str) -> Result<DxValue> {
401 self.tokenizer.skip_whitespace();
403 let schema_line = self.tokenizer.read_until(b'\n');
404 let schema_str = std::str::from_utf8(schema_line)?;
405
406 let schema = Schema::parse_definition(name.to_string(), schema_str)?;
407 self.schemas.insert(name.to_string(), schema.clone());
408
409 if matches!(self.tokenizer.peek(), Some(b'\n')) {
411 self.tokenizer.advance(1);
412 }
413
414 let mut table = DxTable::new(schema.clone());
416 let mut prev_row: Option<Vec<DxValue>> = None;
417
418 loop {
419 self.tokenizer.skip_whitespace();
420
421 let token = self.tokenizer.peek_token()?;
423
424 if matches!(token, Token::Eof) {
426 break;
427 }
428
429 if matches!(token, Token::Newline) {
431 self.tokenizer.next_token()?;
432 continue;
433 }
434
435 if matches!(token, Token::Ident(_)) {
437 let saved_pos = self.tokenizer.pos();
438 self.tokenizer.next_token()?; let next = self.tokenizer.peek_token()?;
440 self.tokenizer.reset_to(saved_pos);
441
442 if matches!(
443 next,
444 Token::Colon
445 | Token::Equals
446 | Token::Stream
447 | Token::Bang
448 | Token::Void
449 | Token::Caret
450 | Token::Dollar
451 ) {
452 break;
453 }
454 }
455
456 let row = self.parse_table_row(&schema, prev_row.as_ref())?;
458
459 if row.is_empty() {
460 break;
461 }
462
463 prev_row = Some(row.clone());
464 table.add_row(row).map_err(DxError::SchemaError)?;
465
466 if table.rows.len() > crate::error::MAX_TABLE_ROWS {
468 return Err(DxError::table_too_large(table.rows.len()));
469 }
470
471 self.tokenizer.skip_whitespace();
473 if matches!(self.tokenizer.peek(), Some(b'\n')) {
474 self.tokenizer.advance(1);
475 }
476 }
477
478 Ok(DxValue::Table(table))
479 }
480
481 fn parse_table_row(
489 &mut self,
490 schema: &Schema,
491 prev_row: Option<&Vec<DxValue>>,
492 ) -> Result<Vec<DxValue>> {
493 let mut row = Vec::with_capacity(schema.columns.len());
494
495 for (col_idx, column) in schema.columns.iter().enumerate() {
496 self.tokenizer.skip_whitespace();
497
498 if matches!(column.type_hint, TypeHint::AutoIncrement) {
500 let counter = self.auto_counters.entry(schema.name.clone()).or_insert(1);
501 row.push(DxValue::Int(*counter));
502 *counter += 1;
503 continue;
504 }
505
506 let token = self.tokenizer.peek_token()?;
507
508 if matches!(token, Token::Eof) {
510 if row.is_empty() {
512 return Ok(row);
513 }
514 return Err(DxError::UnexpectedEof(self.tokenizer.pos()));
516 }
517
518 if matches!(token, Token::Newline) {
520 if row.is_empty() {
522 return Ok(row);
523 }
524 return Err(DxError::InvalidSyntax {
526 pos: self.tokenizer.pos(),
527 msg: format!(
528 "Incomplete table row: expected {} columns, got {}",
529 schema.columns.len(),
530 row.len()
531 ),
532 });
533 }
534
535 if matches!(token, Token::Ditto) {
537 self.tokenizer.next_token()?;
538 if let Some(prev) = prev_row {
539 if col_idx < prev.len() {
540 row.push(prev[col_idx].clone());
541 } else {
542 return Err(DxError::DittoNoPrevious(self.tokenizer.pos()));
543 }
544 } else {
545 return Err(DxError::DittoNoPrevious(self.tokenizer.pos()));
546 }
547 continue;
548 }
549
550 let value = match column.type_hint {
552 TypeHint::Int => match self.tokenizer.next_token()? {
553 Token::Eof => {
554 return Err(DxError::UnexpectedEof(self.tokenizer.pos()));
555 }
556 Token::Int(i) => DxValue::Int(i),
557 Token::Ditto => {
558 if let Some(prev) = prev_row {
559 if col_idx < prev.len() {
560 prev[col_idx].clone()
561 } else {
562 return Err(DxError::DittoNoPrevious(self.tokenizer.pos()));
563 }
564 } else {
565 return Err(DxError::DittoNoPrevious(self.tokenizer.pos()));
566 }
567 }
568 other => {
569 return Err(DxError::TypeMismatch {
570 expected: "int".to_string(),
571 actual: format!("{:?}", other),
572 });
573 }
574 },
575 TypeHint::Float => match self.tokenizer.next_token()? {
576 Token::Eof => {
577 return Err(DxError::UnexpectedEof(self.tokenizer.pos()));
578 }
579 Token::Float(f) => DxValue::Float(f),
580 Token::Int(i) => DxValue::Float(i as f64),
581 other => {
582 return Err(DxError::TypeMismatch {
583 expected: "float".to_string(),
584 actual: format!("{:?}", other),
585 });
586 }
587 },
588 TypeHint::Bool => match self.tokenizer.next_token()? {
589 Token::Eof => {
590 return Err(DxError::UnexpectedEof(self.tokenizer.pos()));
591 }
592 Token::True => DxValue::Bool(true),
593 Token::False => DxValue::Bool(false),
594 other => {
595 return Err(DxError::TypeMismatch {
596 expected: "bool".to_string(),
597 actual: format!("{:?}", other),
598 });
599 }
600 },
601 TypeHint::Base62 => {
602 match self.tokenizer.next_token()? {
604 Token::Eof => {
605 return Err(DxError::UnexpectedEof(self.tokenizer.pos()));
606 }
607 Token::Ident(bytes) => {
608 let s = std::str::from_utf8(bytes)?;
609 let n = decode_base62(s)?;
610 DxValue::Int(n as i64)
611 }
612 Token::Int(i) => DxValue::Int(i), other => {
614 return Err(DxError::TypeMismatch {
615 expected: "base62".to_string(),
616 actual: format!("{:?}", other),
617 });
618 }
619 }
620 }
621 TypeHint::String => {
622 let next_is_number = col_idx + 1 < schema.columns.len()
624 && matches!(
625 schema.columns[col_idx + 1].type_hint,
626 TypeHint::Int | TypeHint::Float | TypeHint::Base62
627 );
628 let bytes = self.tokenizer.read_string_vacuum(next_is_number);
629 let s = std::str::from_utf8(bytes)?.trim().to_string();
630 DxValue::String(s)
631 }
632 TypeHint::AutoIncrement => {
633 unreachable!("AutoIncrement handled before loop")
635 }
636 TypeHint::Auto => self.parse_value()?,
637 };
638
639 row.push(value);
640 }
641
642 Ok(row)
643 }
644}
645
646#[must_use = "parsing result should be used"]
717pub fn parse(input: &[u8]) -> Result<DxValue> {
718 if input.len() > crate::error::MAX_INPUT_SIZE {
720 return Err(DxError::input_too_large(input.len()));
721 }
722
723 let mut parser = Parser::new(input);
724 parser.parse()
725}
726
727pub fn parse_str(input: &str) -> Result<DxValue> {
746 parse(input.as_bytes())
747}
748
749pub fn parse_stream<R: std::io::Read>(reader: R) -> Result<DxValue> {
776 let mut buffer = Vec::new();
777 let mut reader = reader;
778 reader.read_to_end(&mut buffer)?;
779 parse(&buffer)
780}
781
782#[cfg(test)]
783mod tests {
784 use super::*;
785
786 #[test]
787 fn test_simple_parse() {
788 let input = b"name:Alice
789age:30
790active:+";
791
792 let result = parse(input).unwrap();
793 if let DxValue::Object(obj) = result {
794 assert_eq!(obj.get("name"), Some(&DxValue::String("Alice".to_string())));
795 assert_eq!(obj.get("age"), Some(&DxValue::Int(30)));
796 assert_eq!(obj.get("active"), Some(&DxValue::Bool(true)));
797 } else {
798 panic!("Expected object");
799 }
800 }
801
802 #[test]
803 fn test_table_parse() {
804 let input = b"users=id%i name%s active%b
8051 Alice +
8062 Bob -";
807
808 let result = parse(input).unwrap();
809 if let DxValue::Object(obj) = result {
810 if let Some(DxValue::Table(table)) = obj.get("users") {
811 assert_eq!(table.row_count(), 2);
812 assert_eq!(table.rows[0][0], DxValue::Int(1));
813 assert_eq!(table.rows[0][1], DxValue::String("Alice".to_string()));
814 } else {
815 panic!("Expected table");
816 }
817 }
818 }
819
820 #[test]
821 fn test_stream_array() {
822 let input = b"tags>alpha|beta|gamma";
823
824 let result = parse(input).unwrap();
825 if let DxValue::Object(obj) = result {
826 if let Some(DxValue::Array(arr)) = obj.get("tags") {
827 assert!(arr.is_stream);
828 assert_eq!(arr.values.len(), 3);
829 } else {
830 panic!("Expected array");
831 }
832 }
833 }
834
835 #[test]
836 fn test_alias() {
837 let input = b"$c=context
838$c.task:Mission";
839
840 let result = parse(input).unwrap();
841 if let DxValue::Object(obj) = result {
842 assert_eq!(
843 obj.get("context.task"),
844 Some(&DxValue::String("Mission".to_string()))
845 );
846 }
847 }
848
849 #[test]
850 fn test_ditto() {
851 let input = b"data=id%i name%s
8521 Alice
853_ Bob";
854
855 let result = parse(input).unwrap();
856 if let DxValue::Object(obj) = result {
857 if let Some(DxValue::Table(table)) = obj.get("data") {
858 assert_eq!(table.rows[1][0], DxValue::Int(1)); }
860 }
861 }
862
863 #[test]
864 fn test_eof_in_value() {
865 let input = b"key:";
867 let result = parse(input);
868 assert!(result.is_err(), "EOF after colon should error");
869 if let Err(DxError::UnexpectedEof(pos)) = result {
870 assert!(pos <= input.len(), "Position should be within input bounds");
871 }
872 }
873
874 #[test]
875 fn test_eof_in_anchor_reference() {
876 let input = b"key:@";
878 let result = parse(input);
879 assert!(result.is_err(), "EOF after @ should error");
880 }
881
882 #[test]
883 fn test_empty_input_parses() {
884 let result = parse(b"");
886 assert!(result.is_ok(), "Empty input should parse successfully");
887 if let Ok(DxValue::Object(obj)) = result {
888 assert!(
889 obj.fields.is_empty(),
890 "Empty input should produce empty object"
891 );
892 }
893 }
894
895 fn _assert_dx_value_send<T: Send>() {}
901
902 fn _assert_dx_value_sync<T: Sync>() {}
904
905 #[test]
906 fn test_dx_value_is_send_sync() {
907 _assert_dx_value_send::<DxValue>();
909 _assert_dx_value_sync::<DxValue>();
910 }
911
912 #[test]
913 fn test_parser_is_send() {
914 fn assert_send<T: Send>() {}
916 assert_send::<Parser<'_>>();
917 }
918
919 #[test]
920 fn test_parse_is_stateless() {
921 use std::sync::Arc;
925 use std::thread;
926
927 let input = Arc::new(b"name:Test\nvalue:42".to_vec());
928 let num_threads = 4;
929
930 let handles: Vec<_> = (0..num_threads)
931 .map(|_| {
932 let input_clone = Arc::clone(&input);
933 thread::spawn(move || parse(&input_clone))
934 })
935 .collect();
936
937 let results: Vec<_> = handles
938 .into_iter()
939 .map(|h| h.join().expect("Thread panicked"))
940 .collect();
941
942 let first = results[0].as_ref().expect("First parse failed");
944 for (i, result) in results.iter().enumerate().skip(1) {
945 let value = result
946 .as_ref()
947 .unwrap_or_else(|_| panic!("Parse {} failed", i));
948 assert_eq!(first, value, "Thread {} produced different result", i);
949 }
950 }
951}