1use crate::{Error, ErrorKind, Scalar};
2use crate::{
3 Utf8Encoding, Windows1252Encoding,
4 data::is_boundary,
5 text::{ObjectReader, Operator},
6};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum TextToken<'a> {
11 Array {
13 end: usize,
15
16 mixed: bool,
18 },
19
20 Object {
30 end: usize,
32
33 mixed: bool,
35 },
36
37 MixedContainer,
40
41 Unquoted(Scalar<'a>),
43
44 Quoted(Scalar<'a>),
46
47 Parameter(Scalar<'a>),
59
60 UndefinedParameter(Scalar<'a>),
68
69 Operator(Operator),
71
72 End(usize),
74
75 Header(Scalar<'a>),
83}
84
85impl<'a> TextToken<'a> {
86 pub fn as_scalar(&self) -> Option<Scalar<'a>> {
96 match self {
97 TextToken::Header(s)
98 | TextToken::Unquoted(s)
99 | TextToken::Quoted(s)
100 | TextToken::Parameter(s)
101 | TextToken::UndefinedParameter(s) => Some(*s),
102 _ => None,
103 }
104 }
105}
106
107#[derive(Debug, Default)]
109pub struct TextTapeParser;
110
111impl TextTapeParser {
112 pub fn new() -> Self {
114 TextTapeParser
115 }
116
117 pub fn parse_slice(self, data: &[u8]) -> Result<TextTape<'_>, Error> {
119 let mut res = TextTape::default();
120 self.parse_slice_into_tape(data, &mut res)?;
121 Ok(res)
122 }
123
124 pub fn parse_slice_into_tape<'a>(
126 self,
127 data: &'a [u8],
128 tape: &mut TextTape<'a>,
129 ) -> Result<(), Error> {
130 let token_tape = &mut tape.token_tape;
131 token_tape.clear();
132 token_tape.reserve(data.len() / 5);
133 let mut state = ParserState {
134 data,
135 original_length: data.len(),
136 token_tape,
137 utf8_bom: false,
138 };
139
140 state.parse()?;
141 tape.utf8_bom = state.utf8_bom;
142
143 Ok(())
144 }
145}
146
147struct ParserState<'a, 'b> {
148 data: &'a [u8],
149 original_length: usize,
150 token_tape: &'b mut Vec<TextToken<'a>>,
151 utf8_bom: bool,
152}
153
154#[derive(Debug, Default)]
156pub struct TextTape<'a> {
157 token_tape: Vec<TextToken<'a>>,
158 utf8_bom: bool,
159}
160
161impl<'a> TextTape<'a> {
162 pub fn windows1252_reader(&self) -> ObjectReader<'a, '_, Windows1252Encoding> {
164 ObjectReader::new(self, Windows1252Encoding::new())
165 }
166
167 pub fn utf8_reader(&self) -> ObjectReader<'a, '_, Utf8Encoding> {
169 ObjectReader::new(self, Utf8Encoding::new())
170 }
171}
172
173#[derive(Debug, PartialEq)]
174enum ParseState {
175 Key,
176 KeyValueSeparator,
177 ObjectValue,
178 ArrayValue,
179 ParseOpen,
180}
181
182fn parse_quote_scalar_fallback(d: &[u8]) -> Result<(Scalar<'_>, &[u8]), Error> {
187 let mut pos = 1;
188 while pos < d.len() {
189 if d[pos] == b'\\' {
190 pos += 2;
191 } else if d[pos] == b'"' {
192 let scalar = Scalar::new(&d[1..pos]);
193 return Ok((scalar, &d[pos + 1..]));
194 } else {
195 pos += 1;
196 }
197 }
198
199 Err(Error::eof())
200}
201
202#[cfg(not(target_arch = "x86_64"))]
203fn parse_quote_scalar(d: &[u8]) -> Result<(Scalar<'_>, &[u8]), Error> {
204 use crate::util::{contains_zero_byte, repeat_byte};
205 let sd = &d[1..];
206 unsafe {
207 let start_ptr = sd.as_ptr();
208 let end_ptr = start_ptr.add(sd.len() / 8 * 8);
209
210 let mut ptr = start_ptr;
211 while ptr < end_ptr {
212 let acc = (ptr as *const u64).read_unaligned();
213 if contains_zero_byte(acc ^ repeat_byte(b'\\')) {
214 break;
215 } else if contains_zero_byte(acc ^ repeat_byte(b'"')) {
216 while *ptr != b'"' {
217 ptr = ptr.offset(1);
218 }
219
220 let offset = sub(ptr, start_ptr);
221 let (scalar, rest) = sd.split_at(offset);
222 let s = Scalar::new(scalar);
223 return Ok((s, &rest[1..]));
224 }
225 ptr = ptr.offset(8);
226 }
227 }
228
229 parse_quote_scalar_fallback(d)
230}
231
232#[cfg(target_arch = "x86_64")]
233fn parse_quote_scalar(d: &[u8]) -> Result<(Scalar<'_>, &[u8]), Error> {
234 #[target_feature(enable = "sse2")]
235 unsafe fn inner(d: &[u8]) -> Result<(Scalar<'_>, &[u8]), Error> {
236 unsafe {
237 use core::arch::x86_64::*;
245 let haystack = &d[1..];
246 let start_ptr = haystack.as_ptr();
247 let mut ptr = start_ptr;
248 let loop_size = std::mem::size_of::<__m128i>();
249 let end_ptr = start_ptr.add(haystack.len() / loop_size * loop_size);
250 let quote = _mm_set1_epi8(b'"' as i8);
251 let slash = _mm_set1_epi8(b'\\' as i8);
252
253 while ptr < end_ptr {
254 let reg = _mm_loadu_si128(ptr as *const __m128i);
255 let slash_found = _mm_cmpeq_epi8(slash, reg);
256 if _mm_movemask_epi8(slash_found) != 0 {
257 break;
258 }
259
260 let quote_found = _mm_cmpeq_epi8(quote, reg);
261 let mask = _mm_movemask_epi8(quote_found);
262 if mask != 0 {
263 let at = sub(ptr, start_ptr);
264 let end_idx = at + (mask.trailing_zeros() as usize);
265 let scalar = std::slice::from_raw_parts(start_ptr, end_idx);
266 let scalar = Scalar::new(scalar);
267 return Ok((scalar, &haystack[end_idx + 1..]));
268 }
269
270 ptr = ptr.add(loop_size);
271 }
272
273 parse_quote_scalar_fallback(d)
274 }
275 }
276
277 unsafe { inner(d) }
279}
280
281#[inline]
282fn split_at_scalar_fallback(d: &[u8]) -> (Scalar<'_>, &[u8]) {
283 let start_ptr = d.as_ptr();
284 let end_ptr = unsafe { start_ptr.add(d.len()) };
285
286 let nind = unsafe { forward_search(start_ptr, end_ptr, is_boundary) };
287 let mut ind = nind.unwrap_or(d.len());
288
289 ind = std::cmp::max(ind, 1);
291 let (scalar, rest) = d.split_at(ind);
292 (Scalar::new(scalar), rest)
293}
294
295#[cfg(not(target_arch = "x86_64"))]
296#[inline]
297fn split_at_scalar(d: &[u8]) -> (Scalar<'_>, &[u8]) {
298 split_at_scalar_fallback(d)
299}
300
301#[cfg(target_arch = "x86_64")]
302#[inline]
303fn split_at_scalar(d: &[u8]) -> (Scalar<'_>, &[u8]) {
304 #[target_feature(enable = "sse2")]
305 #[inline]
306 #[allow(overflowing_literals)]
307 unsafe fn inner(d: &[u8]) -> (Scalar<'_>, &[u8]) {
308 unsafe {
309 use core::arch::x86_64::*;
310 let start_ptr = d.as_ptr();
311 let loop_size = std::mem::size_of::<__m128i>();
312 let end_ptr = d.as_ptr_range().end.sub(loop_size.min(d.len()));
313 let mut ptr = start_ptr;
314
315 while ptr < end_ptr {
365 let input = _mm_loadu_si128(ptr as *const __m128i);
366 let t0 = _mm_cmpeq_epi8(input, _mm_set1_epi8(9));
367 let mut result = t0;
368 let t1 = _mm_cmpeq_epi8(input, _mm_set1_epi8(10));
369 result = _mm_or_si128(result, t1);
370 let t2 = _mm_cmpeq_epi8(input, _mm_set1_epi8(13));
371 result = _mm_or_si128(result, t2);
372 let t3 = _mm_cmpeq_epi8(input, _mm_set1_epi8(32));
373 result = _mm_or_si128(result, t3);
374 let t4 = _mm_cmpeq_epi8(input, _mm_set1_epi8(35));
375 result = _mm_or_si128(result, t4);
376 let t5 = _mm_cmpeq_epi8(input, _mm_set1_epi8(59));
377 result = _mm_or_si128(result, t5);
378 let t6 = _mm_cmpeq_epi8(input, _mm_set1_epi8(60));
379 result = _mm_or_si128(result, t6);
380 let t7 = _mm_cmpeq_epi8(input, _mm_set1_epi8(61));
381 result = _mm_or_si128(result, t7);
382 let t8 = _mm_cmpeq_epi8(input, _mm_set1_epi8(62));
383 result = _mm_or_si128(result, t8);
384 let t9 = _mm_cmpeq_epi8(input, _mm_set1_epi8(123));
385 result = _mm_or_si128(result, t9);
386 let t10 = _mm_cmpeq_epi8(input, _mm_set1_epi8(125));
387 result = _mm_or_si128(result, t10);
388 let t11 = _mm_cmpeq_epi8(input, _mm_set1_epi8(91));
389 result = _mm_or_si128(result, t11);
390 let t12 = _mm_cmpeq_epi8(input, _mm_set1_epi8(93));
391 result = _mm_or_si128(result, t12);
392
393 let found_mask = _mm_movemask_epi8(result);
394 if found_mask != 0 {
395 let at = sub(ptr, start_ptr);
396 let end_idx = at + (found_mask.trailing_zeros() as usize);
397 let end_idx = std::cmp::max(end_idx, 1);
398 let scalar = std::slice::from_raw_parts(start_ptr, end_idx);
399 let scalar = Scalar::new(scalar);
400 return (scalar, &d[end_idx..]);
401 }
402 ptr = ptr.add(loop_size);
403 }
404
405 split_at_scalar_fallback(d)
406 }
407 }
408
409 unsafe { inner(d) }
411}
412
413impl<'a> TextTape<'a> {
414 pub fn new() -> Self {
416 Default::default()
417 }
418
419 pub fn from_slice(data: &[u8]) -> Result<TextTape<'_>, Error> {
421 TextTapeParser.parse_slice(data)
422 }
423
424 pub fn parser() -> TextTapeParser {
426 TextTapeParser
427 }
428
429 pub fn tokens(&self) -> &[TextToken<'a>] {
431 self.token_tape.as_slice()
432 }
433
434 pub fn tokens_mut(&mut self) -> &mut [TextToken<'a>] {
469 self.token_tape.as_mut_slice()
470 }
471
472 pub fn utf8_bom(&self) -> bool {
474 self.utf8_bom
475 }
476}
477
478impl<'a> ParserState<'a, '_> {
479 fn offset(&self, data: &[u8]) -> usize {
480 self.original_length - data.len()
481 }
482
483 #[inline]
485 fn skip_ws_t(&self, data: &'a [u8]) -> Option<&'a [u8]> {
486 unsafe {
487 let start_ptr = data.as_ptr_range().start;
488 let end_ptr = data.as_ptr_range().end;
489 let mut ptr = start_ptr;
490 while ptr < end_ptr {
491 match *ptr {
492 b' ' | b'\t' | b'\n' | b'\r' | b';' => {}
493 b'#' => loop {
494 ptr = ptr.add(1);
495 if ptr == end_ptr {
496 return None;
497 } else if *ptr == b'\n' {
498 break;
499 }
500 },
501 _ => {
502 let rest = std::slice::from_raw_parts(ptr, sub(end_ptr, ptr));
503 return Some(rest);
504 }
505 }
506 ptr = ptr.add(1);
507 }
508 }
509
510 None
511 }
512
513 #[inline]
514 fn parse_quote_scalar(&mut self, d: &'a [u8]) -> Result<&'a [u8], Error> {
515 let (scalar, rest) = parse_quote_scalar(d)?;
516 self.token_tape.push(TextToken::Quoted(scalar));
517 Ok(rest)
518 }
519
520 #[inline(never)]
521 fn parse_variable(&mut self, d: &'a [u8]) -> Result<&'a [u8], Error> {
522 if d.get(1).is_some_and(|&x| x == b'[') {
524 let mut pos = 2;
525 while pos < d.len() {
526 if d[pos] == b']' {
527 let (scalar, rest) = d.split_at(pos + 1);
528 let scalar = Scalar::new(scalar);
529 self.token_tape.push(TextToken::Unquoted(scalar));
530 return Ok(rest);
531 } else {
532 pos += 1;
533 }
534 }
535
536 Err(Error::eof())
537 } else {
538 let (scalar, rest) = split_at_scalar(d);
539 self.token_tape.push(TextToken::Unquoted(scalar));
540 Ok(rest)
541 }
542 }
543
544 #[inline]
545 fn parse_scalar(&mut self, d: &'a [u8]) -> &'a [u8] {
546 let (scalar, rest) = split_at_scalar(d);
547 self.token_tape.push(TextToken::Unquoted(scalar));
548 rest
549 }
550
551 #[inline]
553 pub fn parse(&mut self) -> Result<(), Error> {
554 let mut data = self.data;
555 let mut state = ParseState::Key;
556
557 self.utf8_bom = data.get(..3).is_some_and(|x| x == [0xef, 0xbb, 0xbf]);
558 if self.utf8_bom {
559 data = &data[3..];
560 }
561
562 let mut mixed_mode = false;
563 let mut parent_ind = 0;
564 loop {
565 let d = match self.skip_ws_t(data) {
566 Some(d) => d,
567 None => {
568 if state != ParseState::Key {
569 return Err(Error::eof());
570 }
571
572 if parent_ind == 0 {
573 return Ok(());
574 } else {
575 let grand_ind = match self.token_tape.get(parent_ind) {
577 Some(TextToken::Array { end, .. }) => *end,
578 Some(TextToken::Object { end, .. }) => *end,
579 _ => 0,
580 };
581
582 if grand_ind == 0 {
583 let end = self.token_tape.len();
584 self.token_tape.push(TextToken::End(parent_ind));
585 self.token_tape[parent_ind] = TextToken::Object { end, mixed: false };
586 return Ok(());
587 } else {
588 return Err(Error::eof());
589 }
590 }
591 }
592 };
593
594 data = d;
595 match state {
596 ParseState::Key => {
597 match data[0] {
598 b'}' | b']' => {
599 let saved_mixed = mixed_mode;
600 let grand_ind = match self.token_tape.get(parent_ind) {
601 Some(TextToken::Array { end, .. }) => *end,
602 Some(TextToken::Object { end, .. }) => *end,
603 _ => 0,
604 };
605
606 match self.token_tape.get(grand_ind) {
607 Some(TextToken::Array { mixed, .. }) => {
608 mixed_mode = *mixed;
609 state = ParseState::ArrayValue;
610 }
611 Some(TextToken::Object { mixed, .. }) => {
612 mixed_mode = *mixed;
613 state = if mixed_mode {
614 ParseState::ArrayValue
615 } else {
616 ParseState::Key
617 }
618 }
619 _ => {
620 mixed_mode = false;
621 state = ParseState::Key;
622 }
623 };
624
625 let end_idx = self.token_tape.len();
626 if parent_ind == 0 && grand_ind == 0 {
627 data = &data[1..];
629 continue;
630 }
631
632 self.token_tape.push(TextToken::End(parent_ind));
633 self.token_tape[parent_ind] = TextToken::Object {
634 end: end_idx,
635 mixed: saved_mixed,
636 };
637 parent_ind = grand_ind;
638 data = &data[1..];
639 }
640
641 b'{' => {
643 data = self.skip_ws_t(&data[1..]).ok_or_else(Error::eof)?;
644 if data[0] == b'}' {
645 data = &data[1..];
646 continue;
647 }
648
649 if let Some(last) = self.token_tape.last_mut()
650 && let TextToken::Unquoted(header) = last
651 {
652 *last = TextToken::Header(*header);
653 self.token_tape.push(TextToken::Array {
654 end: 0,
655 mixed: false,
656 });
657 state = ParseState::ParseOpen;
658 continue;
659 }
660
661 return Err(Error::new(ErrorKind::InvalidSyntax {
662 offset: self.offset(data),
663 msg: String::from("invalid syntax for token headers"),
664 }));
665 }
666
667 b'[' => {
668 data = self.parse_parameter_definition(
669 data,
670 &mut parent_ind,
671 &mut state,
672 false,
673 )?;
674 }
675
676 b'"' => {
677 data = self.parse_quote_scalar(data)?;
678 state = ParseState::KeyValueSeparator;
679 }
680
681 b'@' => {
682 data = self.parse_variable(data)?;
683 state = ParseState::KeyValueSeparator;
684 }
685
686 _ => {
687 data = self.parse_scalar(data);
688 state = ParseState::KeyValueSeparator;
689 }
690 }
691 }
692 ParseState::KeyValueSeparator => match data {
693 [b'<', b'=', ..] => {
694 self.token_tape
695 .push(TextToken::Operator(Operator::LessThanEqual));
696 data = &data[2..];
697 state = ParseState::ObjectValue;
698 }
699 [b'<', ..] => {
700 self.token_tape
701 .push(TextToken::Operator(Operator::LessThan));
702 data = &data[1..];
703 state = ParseState::ObjectValue;
704 }
705 [b'>', b'=', ..] => {
706 self.token_tape
707 .push(TextToken::Operator(Operator::GreaterThanEqual));
708 data = &data[2..];
709 state = ParseState::ObjectValue;
710 }
711 [b'>', ..] => {
712 self.token_tape
713 .push(TextToken::Operator(Operator::GreaterThan));
714 data = &data[1..];
715 state = ParseState::ObjectValue;
716 }
717 [b'!', b'=', ..] => {
718 self.token_tape
719 .push(TextToken::Operator(Operator::NotEqual));
720 data = &data[2..];
721 state = ParseState::ObjectValue;
722 }
723 [b'?', b'=', ..] => {
724 self.token_tape.push(TextToken::Operator(Operator::Exists));
725 data = &data[2..];
726 state = ParseState::ObjectValue;
727 }
728 [b'=', b'=', ..] => {
729 self.token_tape.push(TextToken::Operator(Operator::Exact));
730 data = &data[2..];
731 state = ParseState::ObjectValue;
732 }
733 [b'=', ..] if mixed_mode => {
734 self.token_tape.push(TextToken::Operator(Operator::Equal));
735 data = &data[1..];
736 }
737 [b'=', ..] => {
738 data = &data[1..];
739 state = ParseState::ObjectValue;
740 }
741 [b'{', ..] => {
742 state = ParseState::ObjectValue;
743 }
744 [b'}', ..] => {
745 self.token_tape
746 .insert(self.token_tape.len() - 1, TextToken::MixedContainer);
747 state = ParseState::ArrayValue;
748 mixed_mode = true;
749 }
750 _ => {
751 self.token_tape
752 .insert(self.token_tape.len() - 1, TextToken::MixedContainer);
753 state = ParseState::ArrayValue;
754 mixed_mode = true;
755 }
756 },
757 ParseState::ObjectValue => match data[0] {
758 b'{' => {
759 self.token_tape.push(TextToken::Array {
760 end: 0,
761 mixed: false,
762 });
763 state = ParseState::ParseOpen;
764 data = &data[1..];
765 }
766
767 b'}' => {
768 return Err(Error::new(ErrorKind::InvalidSyntax {
769 msg: String::from("encountered '}' for object value"),
770 offset: self.offset(data),
771 }));
772 }
773
774 b'"' => {
775 data = self.parse_quote_scalar(data)?;
776 state = ParseState::Key;
777 }
778 b'@' => {
779 data = self.parse_variable(data)?;
780 state = ParseState::Key;
781 }
782 _ => {
783 data = self.parse_scalar(data);
784 state = ParseState::Key;
785 }
786 },
787 ParseState::ParseOpen => {
788 match data[0] {
789 b'}' => {
791 let ind = self.token_tape.len() - 1;
792
793 match self.token_tape.get(parent_ind) {
794 Some(TextToken::Array { mixed, .. }) => {
795 mixed_mode = *mixed;
796 state = ParseState::ArrayValue;
797 }
798 Some(TextToken::Object { mixed, .. }) => {
799 mixed_mode = *mixed;
800 state = if mixed_mode {
801 ParseState::ArrayValue
802 } else {
803 ParseState::Key
804 }
805 }
806 _ => {
807 mixed_mode = false;
808 state = ParseState::Key;
809 }
810 };
811
812 self.token_tape[ind] = TextToken::Array {
813 end: ind + 1,
814 mixed: false,
815 };
816 self.token_tape.push(TextToken::End(ind));
817 data = &data[1..];
818 continue;
819 }
820
821 b'[' => {
823 if mixed_mode {
824 return Err(Error::new(ErrorKind::InvalidSyntax {
825 msg: String::from(
826 "mixed object and array container not expected",
827 ),
828 offset: self.offset(data),
829 }));
830 }
831
832 data = self.parse_parameter_definition(
833 data,
834 &mut parent_ind,
835 &mut state,
836 true,
837 )?;
838 continue;
839 }
840
841 b'{' => {
843 let scratch = self.skip_ws_t(&data[1..]).ok_or_else(Error::eof)?;
844 if scratch[0] == b'}' {
845 data = &scratch[1..];
846 continue;
847 }
848
849 mixed_mode = false;
850 let ind = self.token_tape.len() - 1;
851 self.token_tape[ind] = TextToken::Array {
852 end: parent_ind,
853 mixed: mixed_mode,
854 };
855 parent_ind = ind;
856 state = ParseState::ArrayValue;
857 continue;
858 }
859 b'"' => {
860 data = self.parse_quote_scalar(data)?;
861 }
862 b'@' => {
863 data = self.parse_variable(data)?;
864 }
865 _ => {
866 data = self.parse_scalar(data);
867 }
868 }
869
870 if mixed_mode
871 && let Some(
872 TextToken::Array { mixed, .. } | TextToken::Object { mixed, .. },
873 ) = self.token_tape.get_mut(parent_ind)
874 {
875 *mixed = mixed_mode;
876 }
877
878 mixed_mode = false;
879 data = self.skip_ws_t(data).ok_or_else(Error::eof)?;
880 match data[0] {
881 b'=' | b'>' | b'<' | b'?' | b'!' => {
882 let ind = self.token_tape.len() - 2;
883 self.token_tape[ind] = TextToken::Object {
884 end: parent_ind,
885 mixed: mixed_mode,
886 };
887 parent_ind = ind;
888 state = ParseState::KeyValueSeparator;
889 }
890 _ => {
891 let ind = self.token_tape.len() - 2;
892 self.token_tape[ind] = TextToken::Array {
893 end: parent_ind,
894 mixed: mixed_mode,
895 };
896 parent_ind = ind;
897 state = ParseState::ArrayValue;
898 }
899 }
900 }
901 ParseState::ArrayValue => match data[0] {
902 b'{' => {
903 self.token_tape.push(TextToken::Array {
904 end: 0,
905 mixed: false,
906 });
907 state = ParseState::ParseOpen;
908 data = &data[1..];
909 }
910 b'}' => {
911 let saved_mixed_mode = mixed_mode;
912 let (grand_ind, is_array) = match self.token_tape.get(parent_ind) {
913 Some(TextToken::Array { end, .. }) => (*end, true),
914 Some(TextToken::Object { end, .. }) => (*end, false),
915 _ => (0, false),
916 };
917
918 match self.token_tape.get(grand_ind) {
919 Some(TextToken::Array { mixed, .. }) => {
920 mixed_mode = *mixed;
921 state = ParseState::ArrayValue;
922 }
923 Some(TextToken::Object { mixed, .. }) => {
924 mixed_mode = *mixed;
925 state = if mixed_mode {
926 ParseState::ArrayValue
927 } else {
928 ParseState::Key
929 }
930 }
931 _ => {
932 mixed_mode = false;
933 state = ParseState::Key;
934 }
935 };
936
937 if parent_ind == 0 && grand_ind == 0 {
938 return Err(Error::new(ErrorKind::StackEmpty {
939 offset: self.offset(data),
940 }));
941 }
942
943 let end_idx = self.token_tape.len();
944 self.token_tape[parent_ind] = if is_array {
945 TextToken::Array {
946 end: end_idx,
947 mixed: saved_mixed_mode,
948 }
949 } else {
950 TextToken::Object {
951 end: end_idx,
952 mixed: saved_mixed_mode,
953 }
954 };
955
956 self.token_tape.push(TextToken::End(parent_ind));
957 parent_ind = grand_ind;
958 data = &data[1..];
959 }
960 b'"' => {
961 data = self.parse_quote_scalar(data)?;
962 state = ParseState::ArrayValue;
963 }
964 b'@' => {
965 data = self.parse_variable(data)?;
966 state = ParseState::ArrayValue;
967 }
968 b'<' | b'>' | b'!' | b'=' => {
969 if !mixed_mode {
970 let can_precede_operator = match self.token_tape.last() {
971 Some(token) if token.as_scalar().is_some() => true,
973 Some(TextToken::End(start_idx)) => {
976 if matches!(
977 self.token_tape.get(*start_idx),
978 Some(TextToken::Object { .. })
979 | Some(TextToken::Array { .. })
980 ) {
981 if data[0] == b'=' {
983 data = &data[1..];
984 continue;
985 }
986 }
987 false
988 }
989 _ => false,
990 };
991
992 if can_precede_operator {
993 self.token_tape
994 .insert(self.token_tape.len() - 1, TextToken::MixedContainer);
995 mixed_mode = true;
996 } else {
997 return Err(Error::new(ErrorKind::InvalidSyntax {
998 msg: String::from("expected a scalar to precede an operator"),
999 offset: self.offset(data) - 1,
1000 }));
1001 }
1002 }
1003
1004 match data {
1005 [b'<', b'=', ..] => {
1006 self.token_tape
1007 .push(TextToken::Operator(Operator::LessThanEqual));
1008 data = &data[2..];
1009 }
1010 [b'<', ..] => {
1011 self.token_tape
1012 .push(TextToken::Operator(Operator::LessThan));
1013 data = &data[1..];
1014 }
1015 [b'>', b'=', ..] => {
1016 self.token_tape
1017 .push(TextToken::Operator(Operator::GreaterThanEqual));
1018 data = &data[2..];
1019 }
1020 [b'>', ..] => {
1021 self.token_tape
1022 .push(TextToken::Operator(Operator::GreaterThan));
1023 data = &data[1..];
1024 }
1025 [b'!', b'=', ..] => {
1026 self.token_tape
1027 .push(TextToken::Operator(Operator::NotEqual));
1028 data = &data[2..];
1029 }
1030 [b'=', b'=', ..] => {
1031 self.token_tape.push(TextToken::Operator(Operator::Exact));
1032 data = &data[2..];
1033 }
1034 [b'=', ..] => {
1035 self.token_tape.push(TextToken::Operator(Operator::Equal));
1036 data = &data[1..];
1037 }
1038 _ => {
1039 return Err(Error::new(ErrorKind::InvalidSyntax {
1040 msg: String::from("unrecognized operator"),
1041 offset: self.offset(data) - 1,
1042 }));
1043 }
1044 }
1045 }
1046 _ => {
1047 data = self.parse_scalar(data);
1048 state = ParseState::ArrayValue;
1049 }
1050 },
1051 }
1052 }
1053 }
1054
1055 fn parse_parameter_definition(
1056 &mut self,
1057 data: &'a [u8],
1058 parent_ind: &mut usize,
1059 state: &mut ParseState,
1060 initial: bool,
1061 ) -> Result<&'a [u8], Error> {
1062 if !matches!(data.get(1), Some(&x) if x == b'[') {
1063 return Err(Error::new(ErrorKind::InvalidSyntax {
1064 offset: self.offset(data),
1065 msg: String::from("expected start of parameter definition"),
1066 }));
1067 }
1068
1069 if initial {
1072 let ind = self.token_tape.len() - 1;
1073 self.token_tape[ind] = TextToken::Object {
1074 end: *parent_ind,
1075 mixed: false,
1076 };
1077 *parent_ind = ind;
1078 }
1079
1080 let is_undefined = matches!(data.get(2), Some(&x) if x == b'!');
1081 let data = data
1082 .get(2 + is_undefined as usize..)
1083 .ok_or_else(Error::eof)?;
1084
1085 if data.is_empty() {
1086 return Err(Error::eof());
1087 }
1088
1089 let (scalar, data) = split_at_scalar(data);
1090 if !matches!(data.first(), Some(&x) if x == b']') {
1091 return Err(Error::new(ErrorKind::InvalidSyntax {
1092 offset: self.offset(data),
1093 msg: String::from("expected end of parameter name"),
1094 }));
1095 }
1096
1097 let data = &data[1..];
1098
1099 let token = if is_undefined {
1100 TextToken::UndefinedParameter(scalar)
1101 } else {
1102 TextToken::Parameter(scalar)
1103 };
1104
1105 self.token_tape.push(token);
1106
1107 let data = self.skip_ws_t(data).ok_or_else(Error::eof)?;
1111 let (key_or_value, data) = split_at_scalar(data);
1112 let data = self.skip_ws_t(data).ok_or_else(Error::eof)?;
1113 if data[0] == b']' {
1114 self.token_tape.push(TextToken::Unquoted(key_or_value));
1115 *state = ParseState::Key;
1116 Ok(&data[1..])
1117 } else {
1118 let grand_ind = *parent_ind;
1119 *parent_ind = self.token_tape.len();
1120 self.token_tape.push(TextToken::Object {
1121 end: grand_ind,
1122 mixed: false,
1123 });
1124 self.token_tape.push(TextToken::Unquoted(key_or_value));
1125 *state = ParseState::KeyValueSeparator;
1126 Ok(data)
1127 }
1128 }
1129}
1130
1131fn sub(a: *const u8, b: *const u8) -> usize {
1132 debug_assert!(a >= b);
1133 (a as usize) - (b as usize)
1134}
1135
1136#[inline(always)]
1137unsafe fn forward_search<F: Fn(u8) -> bool>(
1138 start_ptr: *const u8,
1139 end_ptr: *const u8,
1140 confirm: F,
1141) -> Option<usize> {
1142 unsafe {
1143 let mut ptr = start_ptr;
1144 while ptr < end_ptr {
1145 if confirm(*ptr) {
1146 return Some(sub(ptr, start_ptr));
1147 }
1148 ptr = ptr.offset(1);
1149 }
1150
1151 None
1152 }
1153}
1154
1155#[cfg(test)]
1156mod tests {
1157 use super::*;
1158
1159 fn parse(data: &[u8]) -> Result<TextTape<'_>, Error> {
1160 TextTape::from_slice(data)
1161 }
1162
1163 #[test]
1164 fn test_size_of_text_token() {
1165 let token_size = std::mem::size_of::<TextToken>();
1166 assert!(token_size <= 24);
1167 assert_eq!(
1168 token_size,
1169 std::mem::size_of::<Scalar>() + std::mem::size_of::<usize>()
1170 );
1171 }
1172
1173 #[test]
1174 fn test_binary_tokens_dont_need_to_be_dropped() {
1175 assert!(!std::mem::needs_drop::<TextToken>());
1176 }
1177
1178 #[test]
1179 fn test_simple_event() {
1180 let data = b"foo=bar";
1181
1182 assert_eq!(
1183 parse(&data[..]).unwrap().token_tape,
1184 vec![
1185 TextToken::Unquoted(Scalar::new(b"foo")),
1186 TextToken::Unquoted(Scalar::new(b"bar")),
1187 ]
1188 );
1189 }
1190
1191 #[test]
1192 fn test_simple_event_with_spaces() {
1193 let data = b" \t\t foo =bar \r\ndef=\tqux";
1194
1195 assert_eq!(
1196 parse(&data[..]).unwrap().token_tape,
1197 vec![
1198 TextToken::Unquoted(Scalar::new(b"foo")),
1199 TextToken::Unquoted(Scalar::new(b"bar")),
1200 TextToken::Unquoted(Scalar::new(b"def")),
1201 TextToken::Unquoted(Scalar::new(b"qux")),
1202 ]
1203 );
1204 }
1205
1206 #[test]
1207 fn test_scalars_with_quotes() {
1208 let data = br#""foo"="bar" "3"="1444.11.11""#;
1209 assert_eq!(
1210 parse(&data[..]).unwrap().token_tape,
1211 vec![
1212 TextToken::Quoted(Scalar::new(b"foo")),
1213 TextToken::Quoted(Scalar::new(b"bar")),
1214 TextToken::Quoted(Scalar::new(b"3")),
1215 TextToken::Quoted(Scalar::new(b"1444.11.11")),
1216 ]
1217 );
1218 }
1219
1220 #[test]
1221 fn test_scalars_with_quoteas_delimiters() {
1222 let data = br#""foo"="bar"3="1444.11.11""#;
1223 assert_eq!(
1224 parse(&data[..]).unwrap().token_tape,
1225 vec![
1226 TextToken::Quoted(Scalar::new(b"foo")),
1227 TextToken::Quoted(Scalar::new(b"bar")),
1228 TextToken::Unquoted(Scalar::new(b"3")),
1229 TextToken::Quoted(Scalar::new(b"1444.11.11")),
1230 ]
1231 );
1232 }
1233
1234 #[test]
1235 fn test_escaped_quotes() {
1236 let data = br#"name = "Joe \"Captain\" Rogers""#;
1237
1238 assert_eq!(
1239 parse(&data[..]).unwrap().token_tape,
1240 vec![
1241 TextToken::Unquoted(Scalar::new(b"name")),
1242 TextToken::Quoted(Scalar::new(br#"Joe \"Captain\" Rogers"#)),
1243 ]
1244 );
1245 }
1246
1247 #[test]
1248 fn test_escaped_quotes_short() {
1249 let data = br#"name = "J Rogers \"a""#;
1250
1251 assert_eq!(
1252 parse(&data[..]).unwrap().token_tape,
1253 vec![
1254 TextToken::Unquoted(Scalar::new(b"name")),
1255 TextToken::Quoted(Scalar::new(br#"J Rogers \"a"#)),
1256 ]
1257 );
1258 }
1259
1260 #[test]
1261 fn test_escaped_quotes_crazy() {
1262 let data = br#"custom_name="THE !@#$%^&*( '\"LEGION\"')""#;
1263
1264 assert_eq!(
1265 parse(&data[..]).unwrap().token_tape,
1266 vec![
1267 TextToken::Unquoted(Scalar::new(b"custom_name")),
1268 TextToken::Quoted(Scalar::new(br#"THE !@#$%^&*( '\"LEGION\"')"#)),
1269 ]
1270 );
1271 }
1272
1273 #[test]
1274 fn test_quotes_with_escape_sequences() {
1275 let data = b"custom_name=\"ab \x15D ( ID: 691 )\x15!\"";
1277 assert_eq!(
1278 parse(&data[..]).unwrap().token_tape,
1279 vec![
1280 TextToken::Unquoted(Scalar::new(b"custom_name")),
1281 TextToken::Quoted(Scalar::new(b"ab \x15D ( ID: 691 )\x15!")),
1282 ]
1283 );
1284 }
1285
1286 #[test]
1287 fn test_numbers_are_scalars() {
1288 let data = b"foo=1.000";
1289
1290 assert_eq!(
1291 parse(&data[..]).unwrap().token_tape,
1292 vec![
1293 TextToken::Unquoted(Scalar::new(b"foo")),
1294 TextToken::Unquoted(Scalar::new(b"1.000")),
1295 ]
1296 );
1297 }
1298
1299 #[test]
1300 fn test_object_event() {
1301 let data = b"foo={bar=qux}";
1302
1303 assert_eq!(
1304 parse(&data[..]).unwrap().token_tape,
1305 vec![
1306 TextToken::Unquoted(Scalar::new(b"foo")),
1307 TextToken::Object {
1308 end: 4,
1309 mixed: false
1310 },
1311 TextToken::Unquoted(Scalar::new(b"bar")),
1312 TextToken::Unquoted(Scalar::new(b"qux")),
1313 TextToken::End(1),
1314 ]
1315 );
1316 }
1317
1318 #[test]
1319 fn test_object_multi_field_event() {
1320 let data = b"foo={bar=1 qux=28}";
1321
1322 assert_eq!(
1323 parse(&data[..]).unwrap().token_tape,
1324 vec![
1325 TextToken::Unquoted(Scalar::new(b"foo")),
1326 TextToken::Object {
1327 end: 6,
1328 mixed: false
1329 },
1330 TextToken::Unquoted(Scalar::new(b"bar")),
1331 TextToken::Unquoted(Scalar::new(b"1")),
1332 TextToken::Unquoted(Scalar::new(b"qux")),
1333 TextToken::Unquoted(Scalar::new(b"28")),
1334 TextToken::End(1),
1335 ]
1336 );
1337 }
1338
1339 #[test]
1340 fn test_text_parser_tape() {
1341 let mut tape = TextTape::new();
1342
1343 let data = b"foo={bar=1 qux=28}";
1344 TextTape::parser()
1345 .parse_slice_into_tape(data, &mut tape)
1346 .unwrap();
1347
1348 assert_eq!(
1349 tape.tokens(),
1350 vec![
1351 TextToken::Unquoted(Scalar::new(b"foo")),
1352 TextToken::Object {
1353 end: 6,
1354 mixed: false
1355 },
1356 TextToken::Unquoted(Scalar::new(b"bar")),
1357 TextToken::Unquoted(Scalar::new(b"1")),
1358 TextToken::Unquoted(Scalar::new(b"qux")),
1359 TextToken::Unquoted(Scalar::new(b"28")),
1360 TextToken::End(1),
1361 ]
1362 );
1363
1364 let data2 = b"foo2={bar2=3 qux2=29}";
1365 TextTape::parser()
1366 .parse_slice_into_tape(data2, &mut tape)
1367 .unwrap();
1368
1369 assert_eq!(
1370 tape.tokens(),
1371 vec![
1372 TextToken::Unquoted(Scalar::new(b"foo2")),
1373 TextToken::Object {
1374 end: 6,
1375 mixed: false
1376 },
1377 TextToken::Unquoted(Scalar::new(b"bar2")),
1378 TextToken::Unquoted(Scalar::new(b"3")),
1379 TextToken::Unquoted(Scalar::new(b"qux2")),
1380 TextToken::Unquoted(Scalar::new(b"29")),
1381 TextToken::End(1),
1382 ]
1383 );
1384 }
1385
1386 #[test]
1387 fn test_array_event() {
1388 let data = b"versions={\r\n\t\"1.28.3.0\"\r\n}";
1389
1390 assert_eq!(
1391 parse(&data[..]).unwrap().token_tape,
1392 vec![
1393 TextToken::Unquoted(Scalar::new(b"versions")),
1394 TextToken::Array {
1395 end: 3,
1396 mixed: false
1397 },
1398 TextToken::Quoted(Scalar::new(b"1.28.3.0")),
1399 TextToken::End(1),
1400 ]
1401 );
1402 }
1403
1404 #[test]
1405 fn test_array_multievent() {
1406 let data = b"versions={\r\n\t\"1.28.3.0\"\r\n foo}";
1407
1408 assert_eq!(
1409 parse(&data[..]).unwrap().token_tape,
1410 vec![
1411 TextToken::Unquoted(Scalar::new(b"versions")),
1412 TextToken::Array {
1413 end: 4,
1414 mixed: false
1415 },
1416 TextToken::Quoted(Scalar::new(b"1.28.3.0")),
1417 TextToken::Unquoted(Scalar::new(b"foo")),
1418 TextToken::End(1),
1419 ]
1420 );
1421 }
1422
1423 #[test]
1424 fn test_no_equal_object_event() {
1425 let data = b"foo{bar=qux}";
1426
1427 assert_eq!(
1428 parse(&data[..]).unwrap().token_tape,
1429 vec![
1430 TextToken::Unquoted(Scalar::new(b"foo")),
1431 TextToken::Object {
1432 end: 4,
1433 mixed: false
1434 },
1435 TextToken::Unquoted(Scalar::new(b"bar")),
1436 TextToken::Unquoted(Scalar::new(b"qux")),
1437 TextToken::End(1),
1438 ]
1439 );
1440 }
1441
1442 #[test]
1443 fn test_no_equal_object_field() {
1444 let data = br#"
1445 dlc002 = {
1446 name = "Arachnoid Portrait Pack"
1447 steam_id = "447680"
1448 gog_store_id = ""
1449 paradoxplaza_store_url ""
1450 category = "content_pack"
1451 show = no
1452 recommendations = {}
1453 }"#;
1454 assert_eq!(
1455 parse(&data[..]).unwrap().token_tape,
1456 vec![
1457 TextToken::Unquoted(Scalar::new(b"dlc002")),
1458 TextToken::Object {
1459 end: 21,
1460 mixed: false
1461 },
1462 TextToken::Unquoted(Scalar::new(b"name")),
1463 TextToken::Quoted(Scalar::new(b"Arachnoid Portrait Pack")),
1464 TextToken::Unquoted(Scalar::new(b"steam_id")),
1465 TextToken::Quoted(Scalar::new(b"447680")),
1466 TextToken::Unquoted(Scalar::new(b"gog_store_id")),
1467 TextToken::Quoted(Scalar::new(b"")),
1468 TextToken::MixedContainer,
1469 TextToken::Unquoted(Scalar::new(b"paradoxplaza_store_url")),
1470 TextToken::Quoted(Scalar::new(b"")),
1471 TextToken::Unquoted(Scalar::new(b"category")),
1472 TextToken::Operator(Operator::Equal),
1473 TextToken::Quoted(Scalar::new(b"content_pack")),
1474 TextToken::Unquoted(Scalar::new(b"show")),
1475 TextToken::Operator(Operator::Equal),
1476 TextToken::Unquoted(Scalar::new(b"no")),
1477 TextToken::Unquoted(Scalar::new(b"recommendations")),
1478 TextToken::Operator(Operator::Equal),
1479 TextToken::Array {
1480 end: 20,
1481 mixed: false
1482 },
1483 TextToken::End(19),
1484 TextToken::End(1),
1485 ]
1486 );
1487 }
1488
1489 #[test]
1490 fn test_empty_array() {
1491 let data = b"discovered_by={}";
1492
1493 assert_eq!(
1494 parse(&data[..]).unwrap().token_tape,
1495 vec![
1496 TextToken::Unquoted(Scalar::new(b"discovered_by")),
1497 TextToken::Array {
1498 end: 2,
1499 mixed: false
1500 },
1501 TextToken::End(1),
1502 ]
1503 );
1504 }
1505
1506 #[test]
1507 fn test_array_of_objects() {
1508 let data = b"stats={{id=0 type=general} {id=1 type=admiral}}";
1509
1510 assert_eq!(
1511 parse(&data[..]).unwrap().token_tape,
1512 vec![
1513 TextToken::Unquoted(Scalar::new(b"stats")),
1514 TextToken::Array {
1515 end: 14,
1516 mixed: false
1517 },
1518 TextToken::Object {
1519 end: 7,
1520 mixed: false
1521 },
1522 TextToken::Unquoted(Scalar::new(b"id")),
1523 TextToken::Unquoted(Scalar::new(b"0")),
1524 TextToken::Unquoted(Scalar::new(b"type")),
1525 TextToken::Unquoted(Scalar::new(b"general")),
1526 TextToken::End(2),
1527 TextToken::Object {
1528 end: 13,
1529 mixed: false
1530 },
1531 TextToken::Unquoted(Scalar::new(b"id")),
1532 TextToken::Unquoted(Scalar::new(b"1")),
1533 TextToken::Unquoted(Scalar::new(b"type")),
1534 TextToken::Unquoted(Scalar::new(b"admiral")),
1535 TextToken::End(8),
1536 TextToken::End(1),
1537 ]
1538 );
1539 }
1540
1541 #[test]
1542 fn test_empty_objects() {
1543 let data = b"history={{} 1629.11.10={core=AAA}}";
1544
1545 assert_eq!(
1546 parse(&data[..]).unwrap().token_tape,
1547 vec![
1548 TextToken::Unquoted(Scalar::new(b"history")),
1549 TextToken::Object {
1550 end: 7,
1551 mixed: false
1552 },
1553 TextToken::Unquoted(Scalar::new(b"1629.11.10")),
1554 TextToken::Object {
1555 end: 6,
1556 mixed: false
1557 },
1558 TextToken::Unquoted(Scalar::new(b"core")),
1559 TextToken::Unquoted(Scalar::new(b"AAA")),
1560 TextToken::End(3),
1561 TextToken::End(1),
1562 ]
1563 );
1564 }
1565
1566 #[test]
1567 fn test_empty_multi_objects() {
1568 let data = b"history={{} {} 1629.11.10={core=AAA}}";
1569
1570 assert_eq!(
1571 parse(&data[..]).unwrap().token_tape,
1572 vec![
1573 TextToken::Unquoted(Scalar::new(b"history")),
1574 TextToken::Object {
1575 end: 7,
1576 mixed: false
1577 },
1578 TextToken::Unquoted(Scalar::new(b"1629.11.10")),
1579 TextToken::Object {
1580 end: 6,
1581 mixed: false
1582 },
1583 TextToken::Unquoted(Scalar::new(b"core")),
1584 TextToken::Unquoted(Scalar::new(b"AAA")),
1585 TextToken::End(3),
1586 TextToken::End(1),
1587 ]
1588 );
1589 }
1590
1591 #[test]
1592 fn test_empty_objects2() {
1593 let data = b"foo={bar=val {}} me=you";
1594
1595 assert_eq!(
1596 parse(&data[..]).unwrap().token_tape,
1597 vec![
1598 TextToken::Unquoted(Scalar::new(b"foo")),
1599 TextToken::Object {
1600 end: 4,
1601 mixed: false
1602 },
1603 TextToken::Unquoted(Scalar::new(b"bar")),
1604 TextToken::Unquoted(Scalar::new(b"val")),
1605 TextToken::End(1),
1606 TextToken::Unquoted(Scalar::new(b"me")),
1607 TextToken::Unquoted(Scalar::new(b"you")),
1608 ]
1609 );
1610 }
1611
1612 #[test]
1613 fn test_empty_objects3() {
1614 let data = b"foo={bar=val { } a=b} me=you";
1615
1616 assert_eq!(
1617 parse(&data[..]).unwrap().token_tape,
1618 vec![
1619 TextToken::Unquoted(Scalar::new(b"foo")),
1620 TextToken::Object {
1621 end: 6,
1622 mixed: false
1623 },
1624 TextToken::Unquoted(Scalar::new(b"bar")),
1625 TextToken::Unquoted(Scalar::new(b"val")),
1626 TextToken::Unquoted(Scalar::new(b"a")),
1627 TextToken::Unquoted(Scalar::new(b"b")),
1628 TextToken::End(1),
1629 TextToken::Unquoted(Scalar::new(b"me")),
1630 TextToken::Unquoted(Scalar::new(b"you")),
1631 ]
1632 );
1633 }
1634
1635 #[test]
1636 fn test_spanning_objects() {
1637 let data = b"army={name=abc} army={name=def}";
1638
1639 assert_eq!(
1640 parse(&data[..]).unwrap().token_tape,
1641 vec![
1642 TextToken::Unquoted(Scalar::new(b"army")),
1643 TextToken::Object {
1644 end: 4,
1645 mixed: false
1646 },
1647 TextToken::Unquoted(Scalar::new(b"name")),
1648 TextToken::Unquoted(Scalar::new(b"abc")),
1649 TextToken::End(1),
1650 TextToken::Unquoted(Scalar::new(b"army")),
1651 TextToken::Object {
1652 end: 9,
1653 mixed: false
1654 },
1655 TextToken::Unquoted(Scalar::new(b"name")),
1656 TextToken::Unquoted(Scalar::new(b"def")),
1657 TextToken::End(6),
1658 ]
1659 );
1660 }
1661
1662 #[test]
1663 fn test_regression() {
1664 let data = [0, 32, 34, 0];
1665 assert!(parse(&data[..]).is_err());
1666 }
1667
1668 #[test]
1669 fn test_regression2() {
1670 let data = [0, 4, 33, 0];
1671 let _ = parse(&data[..]);
1672 }
1673
1674 #[test]
1675 #[cfg_attr(miri, ignore)] fn test_too_heavily_nested() {
1677 let mut data = Vec::new();
1678 data.extend_from_slice(b"foo=");
1679 for _ in 0..100000 {
1680 data.extend_from_slice(b"{");
1681 }
1682 assert!(parse(&data[..]).is_err());
1683 }
1684
1685 #[test]
1686 fn test_no_ws_comment() {
1687 let data = b"foo=abc#def\nbar=qux";
1688
1689 assert_eq!(
1690 parse(&data[..]).unwrap().token_tape,
1691 vec![
1692 TextToken::Unquoted(Scalar::new(b"foo")),
1693 TextToken::Unquoted(Scalar::new(b"abc")),
1694 TextToken::Unquoted(Scalar::new(b"bar")),
1695 TextToken::Unquoted(Scalar::new(b"qux")),
1696 ]
1697 );
1698 }
1699
1700 #[test]
1701 fn test_bom() {
1702 let data = b"\xef\xbb\xbf#hello";
1703 let tape = parse(&data[..]).unwrap();
1704
1705 assert_eq!(tape.token_tape, vec![]);
1706 assert!(tape.utf8_bom);
1707 }
1708
1709 #[test]
1710 fn test_period_in_identifiers() {
1711 let data = b"flavor_tur.8=yes";
1712
1713 assert_eq!(
1714 parse(&data[..]).unwrap().token_tape,
1715 vec![
1716 TextToken::Unquoted(Scalar::new(b"flavor_tur.8")),
1717 TextToken::Unquoted(Scalar::new(b"yes")),
1718 ]
1719 );
1720 }
1721
1722 #[test]
1723 fn test_dashed_identifiers() {
1724 let data = b"dashed-identifier=yes";
1726
1727 assert_eq!(
1728 parse(&data[..]).unwrap().token_tape,
1729 vec![
1730 TextToken::Unquoted(Scalar::new(b"dashed-identifier")),
1731 TextToken::Unquoted(Scalar::new(b"yes")),
1732 ]
1733 );
1734 }
1735
1736 #[test]
1737 fn test_colon_values() {
1738 let data = b"province_id = event_target:agenda_province";
1739
1740 assert_eq!(
1741 parse(&data[..]).unwrap().token_tape,
1742 vec![
1743 TextToken::Unquoted(Scalar::new(b"province_id")),
1744 TextToken::Unquoted(Scalar::new(b"event_target:agenda_province")),
1745 ]
1746 );
1747 }
1748
1749 #[test]
1750 fn test_parameter_syntax_with_values() {
1751 let data = b"mult = value:job_weights_research_modifier|JOB|head_researcher|";
1754 assert_eq!(
1755 parse(&data[..]).unwrap().token_tape,
1756 vec![
1757 TextToken::Unquoted(Scalar::new(b"mult")),
1758 TextToken::Unquoted(Scalar::new(
1759 b"value:job_weights_research_modifier|JOB|head_researcher|"
1760 )),
1761 ]
1762 );
1763 }
1764
1765 #[test]
1766 fn test_variables() {
1767 let data = b"@planet_standard_scale = 11";
1768
1769 assert_eq!(
1770 parse(&data[..]).unwrap().token_tape,
1771 vec![
1772 TextToken::Unquoted(Scalar::new(b"@planet_standard_scale")),
1773 TextToken::Unquoted(Scalar::new(b"11")),
1774 ]
1775 );
1776 }
1777
1778 #[test]
1779 fn test_interpolated_variable() {
1780 let data = b"position = { @[1-leopard_x] @leopard_y }";
1781 assert_eq!(
1782 parse(&data[..]).unwrap().token_tape,
1783 vec![
1784 TextToken::Unquoted(Scalar::new(b"position")),
1785 TextToken::Array {
1786 end: 4,
1787 mixed: false
1788 },
1789 TextToken::Unquoted(Scalar::new(b"@[1-leopard_x]")),
1790 TextToken::Unquoted(Scalar::new(b"@leopard_y")),
1791 TextToken::End(1),
1792 ]
1793 );
1794 }
1795
1796 #[test]
1797 fn test_variable_value() {
1798 let data = b"window_name = @default_window_name";
1799 assert_eq!(
1800 parse(&data[..]).unwrap().token_tape,
1801 vec![
1802 TextToken::Unquoted(Scalar::new(b"window_name")),
1803 TextToken::Unquoted(Scalar::new(b"@default_window_name")),
1804 ]
1805 );
1806 }
1807
1808 #[test]
1809 fn test_equal_identifier() {
1810 let data = br#"=="bar""#;
1811
1812 assert_eq!(
1813 parse(&data[..]).unwrap().token_tape,
1814 vec![
1815 TextToken::Unquoted(Scalar::new(b"=")),
1816 TextToken::Quoted(Scalar::new(b"bar")),
1817 ]
1818 );
1819 }
1820
1821 #[test]
1822 #[cfg_attr(miri, ignore)] fn test_many_line_comment() {
1824 let mut data = Vec::new();
1825 data.extend_from_slice(b"foo=1.000\n");
1826 for _ in 0..100000 {
1827 data.extend_from_slice(b"# this is a comment\n");
1828 }
1829 data.extend_from_slice(b"foo=2.000\n");
1830
1831 assert_eq!(
1832 parse(&data[..]).unwrap().token_tape,
1833 vec![
1834 TextToken::Unquoted(Scalar::new(b"foo")),
1835 TextToken::Unquoted(Scalar::new(b"1.000")),
1836 TextToken::Unquoted(Scalar::new(b"foo")),
1837 TextToken::Unquoted(Scalar::new(b"2.000")),
1838 ]
1839 );
1840 }
1841
1842 #[test]
1843 fn test_terminating_comment() {
1844 let data = b"# boo\r\n# baa\r\nfoo=a\r\n# bee";
1845 assert_eq!(
1846 parse(&data[..]).unwrap().token_tape,
1847 vec![
1848 TextToken::Unquoted(Scalar::new(b"foo")),
1849 TextToken::Unquoted(Scalar::new(b"a")),
1850 ]
1851 );
1852 }
1853
1854 #[test]
1855 fn test_rgb_trick() {
1856 let data = b"name = rgb ";
1857
1858 assert_eq!(
1859 parse(&data[..]).unwrap().token_tape,
1860 vec![
1861 TextToken::Unquoted(Scalar::new(b"name")),
1862 TextToken::Unquoted(Scalar::new(b"rgb")),
1863 ]
1864 );
1865 }
1866
1867 #[test]
1868 fn test_rgb_trick2() {
1869 let data = b"name = rgb type = 4713";
1870
1871 assert_eq!(
1872 parse(&data[..]).unwrap().token_tape,
1873 vec![
1874 TextToken::Unquoted(Scalar::new(b"name")),
1875 TextToken::Unquoted(Scalar::new(b"rgb")),
1876 TextToken::Unquoted(Scalar::new(b"type")),
1877 TextToken::Unquoted(Scalar::new(b"4713")),
1878 ]
1879 );
1880 }
1881
1882 #[test]
1883 fn test_rgb_trick3() {
1884 let data = b"name = rgbeffect";
1885
1886 assert_eq!(
1887 parse(&data[..]).unwrap().token_tape,
1888 vec![
1889 TextToken::Unquoted(Scalar::new(b"name")),
1890 TextToken::Unquoted(Scalar::new(b"rgbeffect")),
1891 ]
1892 );
1893 }
1894
1895 #[test]
1896 fn test_rgb() {
1897 let data = b"color = rgb { 100 200 150 } ";
1898
1899 assert_eq!(
1900 parse(&data[..]).unwrap().token_tape,
1901 vec![
1902 TextToken::Unquoted(Scalar::new(b"color")),
1903 TextToken::Header(Scalar::new(b"rgb")),
1904 TextToken::Array {
1905 end: 6,
1906 mixed: false
1907 },
1908 TextToken::Unquoted(Scalar::new(b"100")),
1909 TextToken::Unquoted(Scalar::new(b"200")),
1910 TextToken::Unquoted(Scalar::new(b"150")),
1911 TextToken::End(2),
1912 ]
1913 );
1914 }
1915
1916 #[test]
1917 fn test_hsv_trick() {
1918 let data = b"name = hsv ";
1919
1920 assert_eq!(
1921 parse(&data[..]).unwrap().token_tape,
1922 vec![
1923 TextToken::Unquoted(Scalar::new(b"name")),
1924 TextToken::Unquoted(Scalar::new(b"hsv")),
1925 ]
1926 );
1927 }
1928
1929 #[test]
1930 fn test_hsv_trick2() {
1931 let data = b"name = hsv type = 4713";
1932
1933 assert_eq!(
1934 parse(&data[..]).unwrap().token_tape,
1935 vec![
1936 TextToken::Unquoted(Scalar::new(b"name")),
1937 TextToken::Unquoted(Scalar::new(b"hsv")),
1938 TextToken::Unquoted(Scalar::new(b"type")),
1939 TextToken::Unquoted(Scalar::new(b"4713")),
1940 ]
1941 );
1942 }
1943
1944 #[test]
1945 fn test_hsv_trick3() {
1946 let data = b"name = hsveffect";
1947
1948 assert_eq!(
1949 parse(&data[..]).unwrap().token_tape,
1950 vec![
1951 TextToken::Unquoted(Scalar::new(b"name")),
1952 TextToken::Unquoted(Scalar::new(b"hsveffect")),
1953 ]
1954 );
1955 }
1956
1957 #[test]
1958 fn test_hsv() {
1959 let data = b"color = hsv { 0.43 0.86 0.61 } ";
1960
1961 assert_eq!(
1962 parse(&data[..]).unwrap().token_tape,
1963 vec![
1964 TextToken::Unquoted(Scalar::new(b"color")),
1965 TextToken::Header(Scalar::new(b"hsv")),
1966 TextToken::Array {
1967 end: 6,
1968 mixed: false
1969 },
1970 TextToken::Unquoted(Scalar::new(b"0.43")),
1971 TextToken::Unquoted(Scalar::new(b"0.86")),
1972 TextToken::Unquoted(Scalar::new(b"0.61")),
1973 TextToken::End(2),
1974 ]
1975 );
1976 }
1977
1978 #[test]
1979 fn test_hsv360() {
1980 let data = b"color = hsv360{ 25 75 63 }";
1981
1982 assert_eq!(
1983 parse(&data[..]).unwrap().token_tape,
1984 vec![
1985 TextToken::Unquoted(Scalar::new(b"color")),
1986 TextToken::Header(Scalar::new(b"hsv360")),
1987 TextToken::Array {
1988 end: 6,
1989 mixed: false
1990 },
1991 TextToken::Unquoted(Scalar::new(b"25")),
1992 TextToken::Unquoted(Scalar::new(b"75")),
1993 TextToken::Unquoted(Scalar::new(b"63")),
1994 TextToken::End(2),
1995 ]
1996 );
1997 }
1998
1999 #[test]
2000 fn test_cylindrical() {
2001 let data = b"color = cylindrical{ 150 3 0 }";
2002
2003 assert_eq!(
2004 parse(&data[..]).unwrap().token_tape,
2005 vec![
2006 TextToken::Unquoted(Scalar::new(b"color")),
2007 TextToken::Header(Scalar::new(b"cylindrical")),
2008 TextToken::Array {
2009 end: 6,
2010 mixed: false
2011 },
2012 TextToken::Unquoted(Scalar::new(b"150")),
2013 TextToken::Unquoted(Scalar::new(b"3")),
2014 TextToken::Unquoted(Scalar::new(b"0")),
2015 TextToken::End(2),
2016 ]
2017 );
2018 }
2019
2020 #[test]
2021 fn test_hex() {
2022 let data = b"color = hex { aabbccdd }";
2023
2024 assert_eq!(
2025 parse(&data[..]).unwrap().token_tape,
2026 vec![
2027 TextToken::Unquoted(Scalar::new(b"color")),
2028 TextToken::Header(Scalar::new(b"hex")),
2029 TextToken::Array {
2030 end: 4,
2031 mixed: false
2032 },
2033 TextToken::Unquoted(Scalar::new(b"aabbccdd")),
2034 TextToken::End(2),
2035 ]
2036 );
2037 }
2038
2039 #[test]
2040 fn test_list_header() {
2041 let data = b"mild_winter = LIST { 3700 3701 }";
2042
2043 assert_eq!(
2044 parse(&data[..]).unwrap().token_tape,
2045 vec![
2046 TextToken::Unquoted(Scalar::new(b"mild_winter")),
2047 TextToken::Header(Scalar::new(b"LIST")),
2048 TextToken::Array {
2049 end: 5,
2050 mixed: false
2051 },
2052 TextToken::Unquoted(Scalar::new(b"3700")),
2053 TextToken::Unquoted(Scalar::new(b"3701")),
2054 TextToken::End(2),
2055 ]
2056 );
2057 }
2058
2059 #[test]
2078 fn test_operator_early_eof() {
2079 let data = b"a{b=}";
2080 assert!(TextTape::from_slice(&data[..]).is_err());
2081 }
2082
2083 #[test]
2084 fn test_less_than_equal_operator() {
2085 let data = b"scope:attacker.primary_title.tier <= tier_county";
2086 assert_eq!(
2087 parse(&data[..]).unwrap().token_tape,
2088 vec![
2089 TextToken::Unquoted(Scalar::new(b"scope:attacker.primary_title.tier")),
2090 TextToken::Operator(Operator::LessThanEqual),
2091 TextToken::Unquoted(Scalar::new(b"tier_county")),
2092 ]
2093 );
2094 }
2095
2096 #[test]
2097 fn test_less_than_operator() {
2098 let data = b"count < 2";
2099 assert_eq!(
2100 parse(&data[..]).unwrap().token_tape,
2101 vec![
2102 TextToken::Unquoted(Scalar::new(b"count")),
2103 TextToken::Operator(Operator::LessThan),
2104 TextToken::Unquoted(Scalar::new(b"2")),
2105 ]
2106 );
2107 }
2108
2109 #[test]
2110 fn test_greater_than_operator() {
2111 let data = b"age > 16";
2112 assert_eq!(
2113 parse(&data[..]).unwrap().token_tape,
2114 vec![
2115 TextToken::Unquoted(Scalar::new(b"age")),
2116 TextToken::Operator(Operator::GreaterThan),
2117 TextToken::Unquoted(Scalar::new(b"16")),
2118 ]
2119 );
2120 }
2121
2122 #[test]
2123 fn test_greater_than_equal_operator() {
2124 let data = b"intrigue >= high_skill_rating";
2125 assert_eq!(
2126 parse(&data[..]).unwrap().token_tape,
2127 vec![
2128 TextToken::Unquoted(Scalar::new(b"intrigue")),
2129 TextToken::Operator(Operator::GreaterThanEqual),
2130 TextToken::Unquoted(Scalar::new(b"high_skill_rating")),
2131 ]
2132 );
2133 }
2134
2135 #[test]
2136 fn test_not_equal_operator() {
2137 let data = b"count != 2";
2138 assert_eq!(
2139 parse(&data[..]).unwrap().token_tape,
2140 vec![
2141 TextToken::Unquoted(Scalar::new(b"count")),
2142 TextToken::Operator(Operator::NotEqual),
2143 TextToken::Unquoted(Scalar::new(b"2")),
2144 ]
2145 );
2146 }
2147
2148 #[test]
2149 fn test_not_exact_operator() {
2150 let data = b"start_date == 1066.9.15";
2151 assert_eq!(
2152 parse(&data[..]).unwrap().token_tape,
2153 vec![
2154 TextToken::Unquoted(Scalar::new(b"start_date")),
2155 TextToken::Operator(Operator::Exact),
2156 TextToken::Unquoted(Scalar::new(b"1066.9.15")),
2157 ]
2158 );
2159 }
2160
2161 #[test]
2162 fn test_operator_in_object() {
2163 let data = b"value={ date < 1941.7.7 is_preparing=true }";
2164 assert_eq!(
2165 parse(&data[..]).unwrap().token_tape,
2166 vec![
2167 TextToken::Unquoted(Scalar::new(b"value")),
2168 TextToken::Object {
2169 end: 7,
2170 mixed: false
2171 },
2172 TextToken::Unquoted(Scalar::new(b"date")),
2173 TextToken::Operator(Operator::LessThan),
2174 TextToken::Unquoted(Scalar::new(b"1941.7.7")),
2175 TextToken::Unquoted(Scalar::new(b"is_preparing")),
2176 TextToken::Unquoted(Scalar::new(b"true")),
2177 TextToken::End(1),
2178 ]
2179 );
2180 }
2181
2182 #[test]
2183 fn test_exists_operator() {
2184 let data = b"c:RUS ?= this";
2185
2186 assert_eq!(
2187 parse(&data[..]).unwrap().token_tape,
2188 vec![
2189 TextToken::Unquoted(Scalar::new(b"c:RUS")),
2190 TextToken::Operator(Operator::Exists),
2191 TextToken::Unquoted(Scalar::new(b"this")),
2192 ]
2193 );
2194 }
2195
2196 #[test]
2197 fn test_exists_operator_in_object_first_pair() {
2198 let data = b"obj = { foo ?= 1 }";
2199
2200 let result = parse(&data[..]).unwrap().token_tape;
2201
2202 assert_eq!(
2203 result,
2204 vec![
2205 TextToken::Unquoted(Scalar::new(b"obj")),
2206 TextToken::Object {
2207 end: 5,
2208 mixed: false
2209 },
2210 TextToken::Unquoted(Scalar::new(b"foo")),
2211 TextToken::Operator(Operator::Exists),
2212 TextToken::Unquoted(Scalar::new(b"1")),
2213 TextToken::End(1),
2214 ]
2215 );
2216 }
2217
2218 #[test]
2219 fn test_not_equal_operator_in_object_first_pair() {
2220 let data = b"obj = { foo != 1 }";
2221
2222 let result = parse(&data[..]).unwrap().token_tape;
2223
2224 assert_eq!(
2225 result,
2226 vec![
2227 TextToken::Unquoted(Scalar::new(b"obj")),
2228 TextToken::Object {
2229 end: 5,
2230 mixed: false
2231 },
2232 TextToken::Unquoted(Scalar::new(b"foo")),
2233 TextToken::Operator(Operator::NotEqual),
2234 TextToken::Unquoted(Scalar::new(b"1")),
2235 TextToken::End(1),
2236 ]
2237 );
2238 }
2239
2240 #[test]
2241 fn test_extraneous_closing_bracket() {
2242 let data = b"foo = { bar } } ";
2243
2244 assert_eq!(
2245 parse(&data[..]).unwrap().token_tape,
2246 vec![
2247 TextToken::Unquoted(Scalar::new(b"foo")),
2248 TextToken::Array {
2249 end: 3,
2250 mixed: false
2251 },
2252 TextToken::Unquoted(Scalar::new(b"bar")),
2253 TextToken::End(1),
2254 ]
2255 );
2256 }
2257
2258 #[test]
2259 fn test_extraneous_closing_bracket2() {
2260 let data = b"a{} b=r}";
2261 assert_eq!(
2262 parse(&data[..]).unwrap().token_tape,
2263 vec![
2264 TextToken::Unquoted(Scalar::new(b"a")),
2265 TextToken::Array {
2266 end: 2,
2267 mixed: false
2268 },
2269 TextToken::End(1),
2270 TextToken::Unquoted(Scalar::new(b"b")),
2271 TextToken::Unquoted(Scalar::new(b"r")),
2272 ]
2273 );
2274 }
2275
2276 #[test]
2277 fn test_extraneous_closing_bracket3() {
2278 let data = b"a=c}}";
2279 assert_eq!(
2280 parse(&data[..]).unwrap().token_tape,
2281 vec![
2282 TextToken::Unquoted(Scalar::new(b"a")),
2283 TextToken::Unquoted(Scalar::new(b"c")),
2284 ]
2285 );
2286 }
2287
2288 #[test]
2289 fn test_extraneous_bracket() {
2290 let data = br#"color = { 121 163 114 } } army_names = {"Armata di $PROVINCE$"}"#;
2292
2293 assert_eq!(
2294 parse(&data[..]).unwrap().token_tape,
2295 vec![
2296 TextToken::Unquoted(Scalar::new(b"color")),
2297 TextToken::Array {
2298 end: 5,
2299 mixed: false
2300 },
2301 TextToken::Unquoted(Scalar::new(b"121")),
2302 TextToken::Unquoted(Scalar::new(b"163")),
2303 TextToken::Unquoted(Scalar::new(b"114")),
2304 TextToken::End(1),
2305 TextToken::Unquoted(Scalar::new(b"army_names")),
2306 TextToken::Array {
2307 end: 9,
2308 mixed: false
2309 },
2310 TextToken::Quoted(Scalar::new(b"Armata di $PROVINCE$")),
2311 TextToken::End(7),
2312 ]
2313 );
2314 }
2315
2316 #[test]
2317 fn test_missing_bracket() {
2318 let data = br#"BRA_GAR_01 = { ordered = { 1 = { "%da Divisao de Infantaria" } }"#;
2319 assert_eq!(
2320 parse(&data[..]).unwrap().token_tape,
2321 vec![
2322 TextToken::Unquoted(Scalar::new(b"BRA_GAR_01")),
2323 TextToken::Object {
2324 end: 9,
2325 mixed: false
2326 },
2327 TextToken::Unquoted(Scalar::new(b"ordered")),
2328 TextToken::Object {
2329 end: 8,
2330 mixed: false
2331 },
2332 TextToken::Unquoted(Scalar::new(b"1")),
2333 TextToken::Array {
2334 end: 7,
2335 mixed: false
2336 },
2337 TextToken::Quoted(Scalar::new(b"%da Divisao de Infantaria")),
2338 TextToken::End(5),
2339 TextToken::End(3),
2340 TextToken::End(1),
2341 ]
2342 );
2343 }
2344
2345 #[test]
2346 fn test_unquoted_non_ascii() {
2347 let data = b"jean_jaur\xe8s = bar ";
2349
2350 assert_eq!(
2351 parse(&data[..]).unwrap().token_tape,
2352 vec![
2353 TextToken::Unquoted(Scalar::new(b"jean_jaur\xe8s")),
2354 TextToken::Unquoted(Scalar::new(b"bar")),
2355 ]
2356 );
2357 }
2358
2359 #[test]
2360 fn test_parameter_value() {
2361 let data = b"generate_advisor = { [[effect] $effect$ ] }";
2362 assert_eq!(
2363 parse(&data[..]).unwrap().token_tape,
2364 vec![
2365 TextToken::Unquoted(Scalar::new(b"generate_advisor")),
2366 TextToken::Object {
2367 end: 4,
2368 mixed: false
2369 },
2370 TextToken::Parameter(Scalar::new(b"effect")),
2371 TextToken::Unquoted(Scalar::new(b"$effect$")),
2372 TextToken::End(1),
2373 ]
2374 );
2375 }
2376
2377 #[test]
2378 fn test_parameter_object() {
2379 let data = b"generate_advisor = { [[scaled_skill] a=b ] }";
2380 assert_eq!(
2381 parse(&data[..]).unwrap().token_tape,
2382 vec![
2383 TextToken::Unquoted(Scalar::new(b"generate_advisor")),
2384 TextToken::Object {
2385 end: 7,
2386 mixed: false
2387 },
2388 TextToken::Parameter(Scalar::new(b"scaled_skill")),
2389 TextToken::Object {
2390 end: 6,
2391 mixed: false
2392 },
2393 TextToken::Unquoted(Scalar::new(b"a")),
2394 TextToken::Unquoted(Scalar::new(b"b")),
2395 TextToken::End(3),
2396 TextToken::End(1),
2397 ]
2398 );
2399 }
2400
2401 #[test]
2402 fn test_parameter_object2() {
2403 let data = b"generate_advisor = { [[add] if = { a=b }] }";
2404 assert_eq!(
2405 parse(&data[..]).unwrap().token_tape,
2406 vec![
2407 TextToken::Unquoted(Scalar::new(b"generate_advisor")),
2408 TextToken::Object {
2409 end: 10,
2410 mixed: false
2411 },
2412 TextToken::Parameter(Scalar::new(b"add")),
2413 TextToken::Object {
2414 end: 9,
2415 mixed: false
2416 },
2417 TextToken::Unquoted(Scalar::new(b"if")),
2418 TextToken::Object {
2419 end: 8,
2420 mixed: false
2421 },
2422 TextToken::Unquoted(Scalar::new(b"a")),
2423 TextToken::Unquoted(Scalar::new(b"b")),
2424 TextToken::End(5),
2425 TextToken::End(3),
2426 TextToken::End(1),
2427 ]
2428 );
2429 }
2430
2431 #[test]
2432 fn test_parameter_object3() {
2433 let data = b"generate_advisor = { [[add] if = { a=b }] [[remove] if={c=d}] }";
2434 assert_eq!(
2435 parse(&data[..]).unwrap().token_tape,
2436 vec![
2437 TextToken::Unquoted(Scalar::new(b"generate_advisor")),
2438 TextToken::Object {
2439 end: 18,
2440 mixed: false
2441 },
2442 TextToken::Parameter(Scalar::new(b"add")),
2443 TextToken::Object {
2444 end: 9,
2445 mixed: false
2446 },
2447 TextToken::Unquoted(Scalar::new(b"if")),
2448 TextToken::Object {
2449 end: 8,
2450 mixed: false
2451 },
2452 TextToken::Unquoted(Scalar::new(b"a")),
2453 TextToken::Unquoted(Scalar::new(b"b")),
2454 TextToken::End(5),
2455 TextToken::End(3),
2456 TextToken::Parameter(Scalar::new(b"remove")),
2457 TextToken::Object {
2458 end: 17,
2459 mixed: false
2460 },
2461 TextToken::Unquoted(Scalar::new(b"if")),
2462 TextToken::Object {
2463 end: 16,
2464 mixed: false
2465 },
2466 TextToken::Unquoted(Scalar::new(b"c")),
2467 TextToken::Unquoted(Scalar::new(b"d")),
2468 TextToken::End(13),
2469 TextToken::End(11),
2470 TextToken::End(1),
2471 ]
2472 );
2473 }
2474
2475 #[test]
2476 fn test_undefined_parameter_object() {
2477 let data = b"generate_advisor = { [[!scaled_skill] a=b ] }";
2478 assert_eq!(
2479 parse(&data[..]).unwrap().token_tape,
2480 vec![
2481 TextToken::Unquoted(Scalar::new(b"generate_advisor")),
2482 TextToken::Object {
2483 end: 7,
2484 mixed: false
2485 },
2486 TextToken::UndefinedParameter(Scalar::new(b"scaled_skill")),
2487 TextToken::Object {
2488 end: 6,
2489 mixed: false
2490 },
2491 TextToken::Unquoted(Scalar::new(b"a")),
2492 TextToken::Unquoted(Scalar::new(b"b")),
2493 TextToken::End(3),
2494 TextToken::End(1),
2495 ]
2496 );
2497 }
2498
2499 #[test]
2500 fn test_text_number_plus() {
2501 let data = b"pop_happiness = +0.10";
2502 assert_eq!(
2503 parse(&data[..]).unwrap().token_tape,
2504 vec![
2505 TextToken::Unquoted(Scalar::new(b"pop_happiness")),
2506 TextToken::Unquoted(Scalar::new(b"+0.10")),
2507 ]
2508 );
2509 }
2510
2511 #[test]
2512 fn test_skip_semicolon() {
2513 let data = b"value=\"win\"; a=b";
2514 assert_eq!(
2515 parse(&data[..]).unwrap().token_tape,
2516 vec![
2517 TextToken::Unquoted(Scalar::new(b"value")),
2518 TextToken::Quoted(Scalar::new(b"win")),
2519 TextToken::Unquoted(Scalar::new(b"a")),
2520 TextToken::Unquoted(Scalar::new(b"b")),
2521 ]
2522 );
2523 }
2524
2525 #[test]
2526 fn test_mixed_container_1() {
2527 let data = b"levels={a=b 10}";
2528 assert_eq!(
2529 parse(&data[..]).unwrap().token_tape,
2530 vec![
2531 TextToken::Unquoted(Scalar::new(b"levels")),
2532 TextToken::Object {
2533 end: 6,
2534 mixed: true
2535 },
2536 TextToken::Unquoted(Scalar::new(b"a")),
2537 TextToken::Unquoted(Scalar::new(b"b")),
2538 TextToken::MixedContainer,
2539 TextToken::Unquoted(Scalar::new(b"10")),
2540 TextToken::End(1),
2541 ]
2542 );
2543 }
2544
2545 #[test]
2546 fn test_mixed_container_2() {
2547 let data = b"levels={a=b 10 20}";
2548 assert_eq!(
2549 parse(&data[..]).unwrap().token_tape,
2550 vec![
2551 TextToken::Unquoted(Scalar::new(b"levels")),
2552 TextToken::Object {
2553 end: 7,
2554 mixed: true
2555 },
2556 TextToken::Unquoted(Scalar::new(b"a")),
2557 TextToken::Unquoted(Scalar::new(b"b")),
2558 TextToken::MixedContainer,
2559 TextToken::Unquoted(Scalar::new(b"10")),
2560 TextToken::Unquoted(Scalar::new(b"20")),
2561 TextToken::End(1),
2562 ]
2563 );
2564 }
2565
2566 #[test]
2567 fn test_mixed_container_3() {
2568 let data = b"levels={a=b 10 c=d}";
2569 assert_eq!(
2570 parse(&data[..]).unwrap().token_tape,
2571 vec![
2572 TextToken::Unquoted(Scalar::new(b"levels")),
2573 TextToken::Object {
2574 end: 9,
2575 mixed: true
2576 },
2577 TextToken::Unquoted(Scalar::new(b"a")),
2578 TextToken::Unquoted(Scalar::new(b"b")),
2579 TextToken::MixedContainer,
2580 TextToken::Unquoted(Scalar::new(b"10")),
2581 TextToken::Unquoted(Scalar::new(b"c")),
2582 TextToken::Operator(Operator::Equal),
2583 TextToken::Unquoted(Scalar::new(b"d")),
2584 TextToken::End(1),
2585 ]
2586 );
2587 }
2588
2589 #[test]
2590 fn test_mixed_container_4() {
2591 let data = b"levels={a=b 10 c=d 20}";
2592 assert_eq!(
2593 parse(&data[..]).unwrap().token_tape,
2594 vec![
2595 TextToken::Unquoted(Scalar::new(b"levels")),
2596 TextToken::Object {
2597 end: 10,
2598 mixed: true
2599 },
2600 TextToken::Unquoted(Scalar::new(b"a")),
2601 TextToken::Unquoted(Scalar::new(b"b")),
2602 TextToken::MixedContainer,
2603 TextToken::Unquoted(Scalar::new(b"10")),
2604 TextToken::Unquoted(Scalar::new(b"c")),
2605 TextToken::Operator(Operator::Equal),
2606 TextToken::Unquoted(Scalar::new(b"d")),
2607 TextToken::Unquoted(Scalar::new(b"20")),
2608 TextToken::End(1),
2609 ]
2610 );
2611 }
2612
2613 #[test]
2614 fn test_mixed_container_5() {
2615 let data = b"16778374={ levels={ 10 0=2 1=2 } }";
2616
2617 assert_eq!(
2618 parse(&data[..]).unwrap().token_tape,
2619 vec![
2620 TextToken::Unquoted(Scalar::new(b"16778374")),
2621 TextToken::Object {
2622 end: 13,
2623 mixed: false
2624 },
2625 TextToken::Unquoted(Scalar::new(b"levels")),
2626 TextToken::Array {
2627 end: 12,
2628 mixed: true
2629 },
2630 TextToken::Unquoted(Scalar::new(b"10")),
2631 TextToken::MixedContainer,
2632 TextToken::Unquoted(Scalar::new(b"0")),
2633 TextToken::Operator(Operator::Equal),
2634 TextToken::Unquoted(Scalar::new(b"2")),
2635 TextToken::Unquoted(Scalar::new(b"1")),
2636 TextToken::Operator(Operator::Equal),
2637 TextToken::Unquoted(Scalar::new(b"2")),
2638 TextToken::End(3),
2639 TextToken::End(1),
2640 ]
2641 );
2642 }
2643
2644 #[test]
2645 fn test_mixed_container_6() {
2646 let data = b"levels={ 10 0=2 1=2 } foo={bar=qux}";
2647 assert_eq!(
2648 parse(&data[..]).unwrap().token_tape,
2649 vec![
2650 TextToken::Unquoted(Scalar::new(b"levels")),
2651 TextToken::Array {
2652 end: 10,
2653 mixed: true
2654 },
2655 TextToken::Unquoted(Scalar::new(b"10")),
2656 TextToken::MixedContainer,
2657 TextToken::Unquoted(Scalar::new(b"0")),
2658 TextToken::Operator(Operator::Equal),
2659 TextToken::Unquoted(Scalar::new(b"2")),
2660 TextToken::Unquoted(Scalar::new(b"1")),
2661 TextToken::Operator(Operator::Equal),
2662 TextToken::Unquoted(Scalar::new(b"2")),
2663 TextToken::End(1),
2664 TextToken::Unquoted(Scalar::new(b"foo")),
2665 TextToken::Object {
2666 end: 15,
2667 mixed: false
2668 },
2669 TextToken::Unquoted(Scalar::new(b"bar")),
2670 TextToken::Unquoted(Scalar::new(b"qux")),
2671 TextToken::End(12),
2672 ]
2673 );
2674 }
2675
2676 #[test]
2677 fn test_mixed_container_7() {
2678 let data = br#"brittany_area = { #5
2679 color = { 118 99 151 }
2680 169 170 171 172 4384
2681 }"#;
2682
2683 assert_eq!(
2684 parse(&data[..]).unwrap().token_tape,
2685 vec![
2686 TextToken::Unquoted(Scalar::new(b"brittany_area")),
2687 TextToken::Object {
2688 end: 14,
2689 mixed: true
2690 },
2691 TextToken::Unquoted(Scalar::new(b"color")),
2692 TextToken::Array {
2693 end: 7,
2694 mixed: false
2695 },
2696 TextToken::Unquoted(Scalar::new(b"118")),
2697 TextToken::Unquoted(Scalar::new(b"99")),
2698 TextToken::Unquoted(Scalar::new(b"151")),
2699 TextToken::End(3),
2700 TextToken::MixedContainer,
2701 TextToken::Unquoted(Scalar::new(b"169")),
2702 TextToken::Unquoted(Scalar::new(b"170")),
2703 TextToken::Unquoted(Scalar::new(b"171")),
2704 TextToken::Unquoted(Scalar::new(b"172")),
2705 TextToken::Unquoted(Scalar::new(b"4384")),
2706 TextToken::End(1),
2707 ]
2708 );
2709 }
2710
2711 #[test]
2712 fn test_mixed_container_8() {
2713 let data = br#"brittany_area = { #5
2714 color = { 118 99 151 }
2715 169
2716 }"#;
2717
2718 assert_eq!(
2719 parse(&data[..]).unwrap().token_tape,
2720 vec![
2721 TextToken::Unquoted(Scalar::new(b"brittany_area")),
2722 TextToken::Object {
2723 end: 10,
2724 mixed: true
2725 },
2726 TextToken::Unquoted(Scalar::new(b"color")),
2727 TextToken::Array {
2728 end: 7,
2729 mixed: false
2730 },
2731 TextToken::Unquoted(Scalar::new(b"118")),
2732 TextToken::Unquoted(Scalar::new(b"99")),
2733 TextToken::Unquoted(Scalar::new(b"151")),
2734 TextToken::End(3),
2735 TextToken::MixedContainer,
2736 TextToken::Unquoted(Scalar::new(b"169")),
2737 TextToken::End(1),
2738 ]
2739 );
2740 }
2741
2742 #[test]
2743 fn test_mixed_container_9() {
2744 let data = b"a {{b=c d c}}";
2745 assert_eq!(
2746 parse(&data[..]).unwrap().token_tape,
2747 vec![
2748 TextToken::Unquoted(Scalar::new(b"a")),
2749 TextToken::Array {
2750 end: 9,
2751 mixed: false
2752 },
2753 TextToken::Object {
2754 end: 8,
2755 mixed: true
2756 },
2757 TextToken::Unquoted(Scalar::new(b"b")),
2758 TextToken::Unquoted(Scalar::new(b"c")),
2759 TextToken::MixedContainer,
2760 TextToken::Unquoted(Scalar::new(b"d")),
2761 TextToken::Unquoted(Scalar::new(b"c")),
2762 TextToken::End(2),
2763 TextToken::End(1)
2764 ]
2765 );
2766 }
2767
2768 #[test]
2769 fn test_mixed_container_10() {
2770 let data = br"on_actions = {
2771 faith_holy_order_land_acquisition_pulse
2772 delay = { days = { 5 10 }}
2773 faith_heresy_events_pulse
2774 delay = { days = { 15 20 }}
2775 faith_fervor_events_pulse
2776 }";
2777 assert_eq!(
2778 parse(&data[..]).unwrap().token_tape,
2779 vec![
2780 TextToken::Unquoted(Scalar::new(b"on_actions")),
2781 TextToken::Array {
2782 end: 24,
2783 mixed: true
2784 },
2785 TextToken::Unquoted(Scalar::new(b"faith_holy_order_land_acquisition_pulse")),
2786 TextToken::MixedContainer,
2787 TextToken::Unquoted(Scalar::new(b"delay")),
2788 TextToken::Operator(Operator::Equal),
2789 TextToken::Object {
2790 end: 12,
2791 mixed: false
2792 },
2793 TextToken::Unquoted(Scalar::new(b"days")),
2794 TextToken::Array {
2795 end: 11,
2796 mixed: false
2797 },
2798 TextToken::Unquoted(Scalar::new(b"5")),
2799 TextToken::Unquoted(Scalar::new(b"10")),
2800 TextToken::End(8),
2801 TextToken::End(6),
2802 TextToken::Unquoted(Scalar::new(b"faith_heresy_events_pulse")),
2803 TextToken::Unquoted(Scalar::new(b"delay")),
2804 TextToken::Operator(Operator::Equal),
2805 TextToken::Object {
2806 end: 22,
2807 mixed: false
2808 },
2809 TextToken::Unquoted(Scalar::new(b"days")),
2810 TextToken::Array {
2811 end: 21,
2812 mixed: false
2813 },
2814 TextToken::Unquoted(Scalar::new(b"15")),
2815 TextToken::Unquoted(Scalar::new(b"20")),
2816 TextToken::End(18),
2817 TextToken::End(16),
2818 TextToken::Unquoted(Scalar::new(b"faith_fervor_events_pulse")),
2819 TextToken::End(1)
2820 ]
2821 );
2822 }
2823
2824 #[test]
2825 fn test_dont_fail_on_list_keyword() {
2826 let data = br" simple_cross_flag = {
2827 pattern = list christian_emblems_list
2828 color1 = list normal_colors
2829 }";
2830
2831 assert_eq!(
2832 parse(&data[..]).unwrap().token_tape,
2833 vec![
2834 TextToken::Unquoted(Scalar::new(b"simple_cross_flag")),
2835 TextToken::Object {
2836 end: 10,
2837 mixed: true
2838 },
2839 TextToken::Unquoted(Scalar::new(b"pattern")),
2840 TextToken::Unquoted(Scalar::new(b"list")),
2841 TextToken::MixedContainer,
2842 TextToken::Unquoted(Scalar::new(b"christian_emblems_list")),
2843 TextToken::Unquoted(Scalar::new(b"color1")),
2844 TextToken::Operator(Operator::Equal),
2845 TextToken::Unquoted(Scalar::new(b"list")),
2846 TextToken::Unquoted(Scalar::new(b"normal_colors")),
2847 TextToken::End(1)
2848 ]
2849 );
2850 }
2851
2852 #[test]
2853 fn test_parameter_eof() {
2854 let data = b"[[";
2855 TextTape::from_slice(data).unwrap_err();
2856 }
2857
2858 #[test]
2859 fn incomplete_object_fail_to_parse() {
2860 let data = b"T&}";
2861 TextTape::from_slice(data).unwrap_err();
2862 }
2863
2864 #[test]
2865 fn test_initial_end_does_not_panic() {
2866 let res = parse(&b"}"[..]);
2867 assert!(res.is_ok() || res.is_err());
2868 }
2869
2870 #[test]
2871 fn test_object_template_syntax() {
2872 let data = br"obj={
2873 { a = b }={ 1 2 3 }
2874 }";
2875
2876 assert_eq!(
2877 parse(&data[..]).unwrap().token_tape,
2878 vec![
2879 TextToken::Unquoted(Scalar::new(b"obj")),
2880 TextToken::Array {
2881 end: 11,
2882 mixed: false
2883 },
2884 TextToken::Object {
2885 end: 5,
2886 mixed: false
2887 },
2888 TextToken::Unquoted(Scalar::new(b"a")),
2889 TextToken::Unquoted(Scalar::new(b"b")),
2890 TextToken::End(2),
2891 TextToken::Array {
2892 end: 10,
2893 mixed: false
2894 },
2895 TextToken::Unquoted(Scalar::new(b"1")),
2896 TextToken::Unquoted(Scalar::new(b"2")),
2897 TextToken::Unquoted(Scalar::new(b"3")),
2898 TextToken::End(6),
2899 TextToken::End(1)
2900 ]
2901 );
2902 }
2903
2904 #[test]
2905 fn test_object_template_syntax2() {
2906 let data = br"obj={
2907 { foo }={ 1000 }
2908 }";
2909
2910 assert_eq!(
2911 parse(&data[..]).unwrap().token_tape,
2912 vec![
2913 TextToken::Unquoted(Scalar::new(b"obj")),
2914 TextToken::Array {
2915 end: 8,
2916 mixed: false
2917 },
2918 TextToken::Array {
2919 end: 4,
2920 mixed: false
2921 },
2922 TextToken::Unquoted(Scalar::new(b"foo")),
2923 TextToken::End(2),
2924 TextToken::Array {
2925 end: 7,
2926 mixed: false
2927 },
2928 TextToken::Unquoted(Scalar::new(b"1000")),
2929 TextToken::End(5),
2930 TextToken::End(1)
2931 ]
2932 );
2933 }
2934
2935 #[test]
2936 fn test_object_template_syntax_simple_scalar() {
2937 let data = br"obj={ { a=b }=16 }";
2938 let tape = TextTape::from_slice(data).unwrap();
2939 let tokens = &tape.tokens();
2940
2941 assert_eq!(
2942 tokens,
2943 &[
2944 TextToken::Unquoted(Scalar::new(b"obj")),
2945 TextToken::Array {
2946 end: 7,
2947 mixed: false
2948 },
2949 TextToken::Object {
2950 end: 5,
2951 mixed: false
2952 },
2953 TextToken::Unquoted(Scalar::new(b"a")),
2954 TextToken::Unquoted(Scalar::new(b"b")),
2955 TextToken::End(2),
2956 TextToken::Unquoted(Scalar::new(b"16")),
2957 TextToken::End(1),
2958 ]
2959 );
2960 }
2961
2962 #[test]
2963 fn test_object_template_syntax_scalar_value() {
2964 let data = br"obj={ { 31=16 }=16 { 32=17 }=18 }";
2965 assert_eq!(
2966 parse(&data[..]).unwrap().token_tape,
2967 vec![
2968 TextToken::Unquoted(Scalar::new(b"obj")),
2969 TextToken::Array {
2970 end: 12,
2971 mixed: false
2972 },
2973 TextToken::Object {
2974 end: 5,
2975 mixed: false
2976 },
2977 TextToken::Unquoted(Scalar::new(b"31")),
2978 TextToken::Unquoted(Scalar::new(b"16")),
2979 TextToken::End(2),
2980 TextToken::Unquoted(Scalar::new(b"16")),
2981 TextToken::Object {
2982 end: 10,
2983 mixed: false
2984 },
2985 TextToken::Unquoted(Scalar::new(b"32")),
2986 TextToken::Unquoted(Scalar::new(b"17")),
2987 TextToken::End(7),
2988 TextToken::Unquoted(Scalar::new(b"18")),
2989 TextToken::End(1)
2990 ]
2991 );
2992 }
2993
2994 #[test]
2995 fn test_semicolon_as_whitespace() {
2996 let data = b"foo = 0.3;";
2997 let tape = TextTape::from_slice(&data[..]).unwrap();
2998 assert_eq!(
2999 tape.tokens(),
3000 vec![
3001 TextToken::Unquoted(Scalar::new(b"foo")),
3002 TextToken::Unquoted(Scalar::new(b"0.3")),
3003 ]
3004 );
3005 }
3006
3007 #[test]
3008 fn test_multiple_semicolons_as_whitespace() {
3009 let data = b"a = 1; b = 2;; c = 3;";
3010 let tape = TextTape::from_slice(&data[..]).unwrap();
3011 assert_eq!(
3012 tape.tokens(),
3013 vec![
3014 TextToken::Unquoted(Scalar::new(b"a")),
3015 TextToken::Unquoted(Scalar::new(b"1")),
3016 TextToken::Unquoted(Scalar::new(b"b")),
3017 TextToken::Unquoted(Scalar::new(b"2")),
3018 TextToken::Unquoted(Scalar::new(b"c")),
3019 TextToken::Unquoted(Scalar::new(b"3")),
3020 ]
3021 );
3022 }
3023}