1use crate::error::PsError;
11use crate::file_store::FileStore;
12use crate::object::EntityId;
13
14#[derive(Debug, Clone, PartialEq)]
16pub enum Token {
17 Int(i64),
18 Real(f64),
19 Name(Vec<u8>, bool), LiteralName(Vec<u8>), ImmediateName(Vec<u8>), String(Vec<u8>), ProcBegin, ProcEnd, ArrayBegin, ArrayEnd, DictBegin, DictEnd, BinaryTokenByte(u8),
31 Eof,
32}
33
34pub struct Tokenizer<'a> {
36 input: &'a [u8],
37 pos: usize,
38}
39
40impl<'a> Tokenizer<'a> {
41 pub fn new(input: &'a [u8]) -> Self {
42 Self { input, pos: 0 }
43 }
44
45 pub fn position(&self) -> usize {
47 self.pos
48 }
49
50 pub fn remaining_from(&self, pos: usize) -> &[u8] {
52 &self.input[pos..]
53 }
54
55 pub fn advance(&mut self, n: usize) {
57 self.pos += n;
58 }
59
60 pub fn next_token(&mut self) -> Result<Option<Token>, PsError> {
62 self.skip_whitespace_and_comments();
63
64 if self.pos >= self.input.len() {
65 return Ok(None);
66 }
67
68 let b = self.input[self.pos];
69 match b {
70 b'(' => self.scan_string().map(Some),
71 b'<' => {
72 if self.pos + 1 < self.input.len() && self.input[self.pos + 1] == b'<' {
73 self.pos += 2;
74 Ok(Some(Token::DictBegin))
75 } else if self.pos + 1 < self.input.len() && self.input[self.pos + 1] == b'~' {
76 self.scan_ascii85_string().map(Some)
77 } else {
78 self.scan_hex_string().map(Some)
79 }
80 }
81 b'>' => {
82 if self.pos + 1 < self.input.len() && self.input[self.pos + 1] == b'>' {
83 self.pos += 2;
84 Ok(Some(Token::DictEnd))
85 } else {
86 Err(PsError::SyntaxError)
87 }
88 }
89 b'{' => {
90 self.pos += 1;
91 Ok(Some(Token::ProcBegin))
92 }
93 b'}' => {
94 self.pos += 1;
95 Ok(Some(Token::ProcEnd))
96 }
97 b'[' => {
98 self.pos += 1;
99 Ok(Some(Token::ArrayBegin))
100 }
101 b']' => {
102 self.pos += 1;
103 Ok(Some(Token::ArrayEnd))
104 }
105 b'/' => {
106 self.pos += 1;
107 if self.pos < self.input.len() && self.input[self.pos] == b'/' {
108 self.pos += 1;
109 Ok(Some(self.scan_immediate_name()))
110 } else {
111 Ok(Some(self.scan_literal_name()))
112 }
113 }
114 128..=159 => {
116 self.pos += 1;
117 Ok(Some(Token::BinaryTokenByte(b)))
118 }
119 _ => {
120 if let Some(tok) = self.try_scan_number() {
122 Ok(Some(tok))
123 } else {
124 Ok(Some(self.scan_name()))
125 }
126 }
127 }
128 }
129
130 fn skip_whitespace_and_comments(&mut self) {
131 while self.pos < self.input.len() {
132 let b = self.input[self.pos];
133 if Self::is_whitespace(b) {
134 self.pos += 1;
135 } else if b == b'%' {
136 while self.pos < self.input.len()
138 && self.input[self.pos] != b'\n'
139 && self.input[self.pos] != b'\r'
140 {
141 self.pos += 1;
142 }
143 } else {
144 break;
145 }
146 }
147 }
148
149 fn try_scan_number(&mut self) -> Option<Token> {
151 let start = self.pos;
152 let bytes = self.input;
153 let len = bytes.len();
154
155 if start >= len {
156 return None;
157 }
158
159 let mut end = start;
161 while end < len
162 && !Self::is_whitespace(bytes[end])
163 && !Self::is_delimiter(bytes[end])
164 && !is_binary_token_byte(bytes[end])
165 {
166 end += 1;
167 }
168
169 if end == start {
170 return None;
171 }
172
173 let token_bytes = &bytes[start..end];
174
175 if let Some(tok) = try_parse_number_token(token_bytes) {
176 self.pos = end;
177 Some(tok)
178 } else {
179 None
180 }
181 }
182
183 fn scan_name(&mut self) -> Token {
184 let start = self.pos;
185 while self.pos < self.input.len()
186 && !Self::is_whitespace(self.input[self.pos])
187 && !Self::is_delimiter(self.input[self.pos])
188 && !is_binary_token_byte(self.input[self.pos])
189 {
190 self.pos += 1;
191 }
192 let name = self.input[start..self.pos].to_vec();
193 Token::Name(name, true) }
195
196 fn scan_literal_name(&mut self) -> Token {
197 let start = self.pos;
198 while self.pos < self.input.len()
199 && !Self::is_whitespace(self.input[self.pos])
200 && !Self::is_delimiter(self.input[self.pos])
201 && !is_binary_token_byte(self.input[self.pos])
202 {
203 self.pos += 1;
204 }
205 let name = self.input[start..self.pos].to_vec();
206 Token::LiteralName(name)
207 }
208
209 fn scan_immediate_name(&mut self) -> Token {
210 let start = self.pos;
211 while self.pos < self.input.len()
212 && !Self::is_whitespace(self.input[self.pos])
213 && !Self::is_delimiter(self.input[self.pos])
214 && !is_binary_token_byte(self.input[self.pos])
215 {
216 self.pos += 1;
217 }
218 let name = self.input[start..self.pos].to_vec();
219 Token::ImmediateName(name)
220 }
221
222 fn scan_string(&mut self) -> Result<Token, PsError> {
224 self.pos += 1; let mut result = Vec::new();
226 let mut depth = 1;
227
228 while self.pos < self.input.len() && depth > 0 {
229 let b = self.input[self.pos];
230 match b {
231 b'(' => {
232 depth += 1;
233 result.push(b'(');
234 self.pos += 1;
235 }
236 b')' => {
237 depth -= 1;
238 if depth > 0 {
239 result.push(b')');
240 }
241 self.pos += 1;
242 }
243 b'\\' => {
244 self.pos += 1;
245 if self.pos >= self.input.len() {
246 return Err(PsError::SyntaxError);
247 }
248 let esc = self.input[self.pos];
249 match esc {
250 b'n' => {
251 result.push(b'\n');
252 self.pos += 1;
253 }
254 b'r' => {
255 result.push(b'\r');
256 self.pos += 1;
257 }
258 b't' => {
259 result.push(b'\t');
260 self.pos += 1;
261 }
262 b'b' => {
263 result.push(0x08);
264 self.pos += 1;
265 }
266 b'f' => {
267 result.push(0x0C);
268 self.pos += 1;
269 }
270 b'\\' => {
271 result.push(b'\\');
272 self.pos += 1;
273 }
274 b'(' => {
275 result.push(b'(');
276 self.pos += 1;
277 }
278 b')' => {
279 result.push(b')');
280 self.pos += 1;
281 }
282 b'\n' => {
283 self.pos += 1;
285 }
286 b'\r' => {
287 self.pos += 1;
289 if self.pos < self.input.len() && self.input[self.pos] == b'\n' {
290 self.pos += 1;
291 }
292 }
293 b'0'..=b'7' => {
294 let mut val: u8 = esc - b'0';
296 self.pos += 1;
297 for _ in 0..2 {
298 if self.pos < self.input.len()
299 && self.input[self.pos] >= b'0'
300 && self.input[self.pos] <= b'7'
301 {
302 val = (val << 3) | (self.input[self.pos] - b'0');
303 self.pos += 1;
304 } else {
305 break;
306 }
307 }
308 result.push(val);
309 }
310 _ => {
311 result.push(esc);
313 self.pos += 1;
314 }
315 }
316 }
317 _ => {
318 result.push(b);
319 self.pos += 1;
320 }
321 }
322 }
323
324 if depth != 0 {
325 return Err(PsError::SyntaxError);
326 }
327
328 Ok(Token::String(result))
329 }
330
331 fn scan_hex_string(&mut self) -> Result<Token, PsError> {
333 self.pos += 1; let mut result = Vec::new();
335 let mut nibble: Option<u8> = None;
336
337 while self.pos < self.input.len() {
338 let b = self.input[self.pos];
339 if b == b'>' {
340 self.pos += 1;
341 if let Some(high) = nibble {
343 result.push(high << 4);
344 }
345 return Ok(Token::String(result));
346 }
347
348 if Self::is_whitespace(b) {
349 self.pos += 1;
350 continue;
351 }
352
353 let digit = match b {
354 b'0'..=b'9' => b - b'0',
355 b'a'..=b'f' => b - b'a' + 10,
356 b'A'..=b'F' => b - b'A' + 10,
357 _ => return Err(PsError::SyntaxError),
358 };
359
360 match nibble {
361 None => nibble = Some(digit),
362 Some(high) => {
363 result.push((high << 4) | digit);
364 nibble = None;
365 }
366 }
367 self.pos += 1;
368 }
369
370 Err(PsError::SyntaxError) }
372
373 fn scan_ascii85_string(&mut self) -> Result<Token, PsError> {
375 self.pos += 2; let mut encoded = Vec::new();
377
378 while self.pos < self.input.len() {
379 let b = self.input[self.pos];
380 if b == b'~' {
381 self.pos += 1;
382 if self.pos < self.input.len() && self.input[self.pos] == b'>' {
383 self.pos += 1;
384 return Ok(Token::String(Self::decode_ascii85(&encoded)?));
385 }
386 return Err(PsError::SyntaxError);
387 }
388 if !Self::is_whitespace(b) {
389 encoded.push(b);
390 }
391 self.pos += 1;
392 }
393
394 Err(PsError::SyntaxError)
395 }
396
397 fn decode_ascii85(data: &[u8]) -> Result<Vec<u8>, PsError> {
398 decode_ascii85(data)
399 }
400
401 fn is_whitespace(b: u8) -> bool {
402 is_whitespace(b)
403 }
404
405 fn is_delimiter(b: u8) -> bool {
406 is_delimiter(b)
407 }
408}
409
410fn is_whitespace(b: u8) -> bool {
414 b <= b' '
415}
416
417fn is_binary_token_byte(b: u8) -> bool {
419 (128..=159).contains(&b)
420}
421
422fn is_delimiter(b: u8) -> bool {
424 matches!(
425 b,
426 b'(' | b')' | b'<' | b'>' | b'[' | b']' | b'{' | b'}' | b'/' | b'%'
427 )
428}
429
430fn decode_ascii85(data: &[u8]) -> Result<Vec<u8>, PsError> {
432 let mut result = Vec::new();
433 let mut i = 0;
434
435 while i < data.len() {
436 if data[i] == b'z' {
437 result.extend_from_slice(&[0, 0, 0, 0]);
438 i += 1;
439 continue;
440 }
441
442 let mut group = [0u8; 5];
443 let mut count = 0;
444 while count < 5 && i < data.len() && data[i] != b'z' {
445 if data[i] < b'!' || data[i] > b'u' {
446 return Err(PsError::SyntaxError);
447 }
448 group[count] = data[i] - b'!';
449 count += 1;
450 i += 1;
451 }
452
453 if count < 2 {
454 if count == 1 {
455 return Err(PsError::SyntaxError);
456 }
457 break;
458 }
459
460 for g in group.iter_mut().skip(count) {
462 *g = 84;
463 }
464
465 let mut value: u32 = 0;
466 for &g in &group {
467 value = value
468 .checked_mul(85)
469 .and_then(|v| v.checked_add(g as u32))
470 .ok_or(PsError::SyntaxError)?;
471 }
472
473 let bytes = value.to_be_bytes();
474 let output_count = count - 1;
475 result.extend_from_slice(&bytes[..output_count]);
476 }
477
478 Ok(result)
479}
480
481fn try_parse_number_token(token_bytes: &[u8]) -> Option<Token> {
483 if token_bytes.is_empty() {
484 return None;
485 }
486
487 if let Some(result) = try_parse_radix(token_bytes) {
489 return Some(result);
490 }
491
492 let s = std::str::from_utf8(token_bytes).ok()?;
493
494 let first = token_bytes[0];
495 let looks_numeric = first.is_ascii_digit()
496 || ((first == b'+' || first == b'-')
497 && token_bytes.len() > 1
498 && (token_bytes[1].is_ascii_digit() || token_bytes[1] == b'.'))
499 || (first == b'.' && token_bytes.len() > 1 && token_bytes[1].is_ascii_digit());
500
501 if !looks_numeric {
502 return None;
503 }
504
505 let is_real = s.contains('.') || s.contains('e') || s.contains('E');
506
507 if is_real {
508 return s.parse::<f64>().ok().map(Token::Real);
509 }
510
511 if let Ok(v) = s.parse::<i64>() {
514 return Some(Token::Int(v));
515 }
516 s.parse::<f64>().ok().map(Token::Real)
517}
518
519fn try_parse_radix(token: &[u8]) -> Option<Token> {
521 let s = std::str::from_utf8(token).ok()?;
522 let hash_pos = s.find('#')?;
523 let base_str = &s[..hash_pos];
524 let digits_str = &s[hash_pos + 1..];
525 if digits_str.is_empty() {
526 return None;
527 }
528 let base: u32 = base_str.parse().ok()?;
529 if !(2..=36).contains(&base) {
530 return None;
531 }
532 let value = i64::from_str_radix(digits_str, base).ok()?;
535 Some(Token::Int(value))
536}
537
538pub fn stream_next_token(
545 files: &mut FileStore,
546 entity: EntityId,
547) -> Result<Option<(Token, u32)>, PsError> {
548 let mut newlines = 0u32;
549
550 let first = loop {
552 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
553 None => return Ok(None),
554 Some(b) if is_whitespace(b) => {
555 if b == b'\n' || b == b'\r' {
556 newlines += 1;
557 }
558 continue;
559 }
560 Some(b'%') => {
561 loop {
563 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
564 None => return Ok(None),
565 Some(b'\n') | Some(b'\r') => {
566 newlines += 1;
567 break;
568 }
569 Some(_) => {}
570 }
571 }
572 continue;
573 }
574 Some(b) => break b,
575 }
576 };
577
578 let token = match first {
579 b'(' => stream_scan_string(files, entity)?,
580 b'<' => match files.read_byte(entity).map_err(|_| PsError::IOError)? {
581 Some(b'<') => Token::DictBegin,
582 Some(b'~') => stream_scan_ascii85(files, entity)?,
583 other => {
584 if let Some(b) = other {
585 files.putback_bytes(entity, &[b]);
586 }
587 stream_scan_hex_string(files, entity)?
588 }
589 },
590 b'>' => match files.read_byte(entity).map_err(|_| PsError::IOError)? {
591 Some(b'>') => Token::DictEnd,
592 _ => return Err(PsError::SyntaxError),
593 },
594 b'{' => Token::ProcBegin,
595 b'}' => Token::ProcEnd,
596 b'[' => Token::ArrayBegin,
597 b']' => Token::ArrayEnd,
598 b'/' => match files.read_byte(entity).map_err(|_| PsError::IOError)? {
599 Some(b'/') => {
600 let name = stream_read_name_bytes(files, entity)?;
601 Token::ImmediateName(name)
602 }
603 Some(b) if !is_whitespace(b) && !is_delimiter(b) => {
604 files.putback_bytes(entity, &[b]);
605 let name = stream_read_name_bytes(files, entity)?;
606 Token::LiteralName(name)
607 }
608 other => {
609 if let Some(b) = other {
610 files.putback_bytes(entity, &[b]);
611 }
612 Token::LiteralName(Vec::new())
613 }
614 },
615 128..=159 => Token::BinaryTokenByte(first),
617 _ => {
618 let mut token_bytes = vec![first];
620 loop {
621 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
622 None => break,
623 Some(b) if is_whitespace(b) => {
624 if b == b'\n' || b == b'\r' {
628 newlines += 1;
629 }
630 break;
631 }
632 Some(b) if is_delimiter(b) => {
633 files.putback_bytes(entity, &[b]);
634 break;
635 }
636 Some(b @ 128..=159) => {
638 files.putback_bytes(entity, &[b]);
639 break;
640 }
641 Some(b) => token_bytes.push(b),
642 }
643 }
644 try_parse_number_token(&token_bytes).unwrap_or(Token::Name(token_bytes, true))
645 }
646 };
647
648 Ok(Some((token, newlines)))
649}
650
651fn stream_read_name_bytes(files: &mut FileStore, entity: EntityId) -> Result<Vec<u8>, PsError> {
655 let mut name = Vec::new();
656 loop {
657 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
658 None => break,
659 Some(b) if is_whitespace(b) || is_delimiter(b) || is_binary_token_byte(b) => {
660 files.putback_bytes(entity, &[b]);
661 break;
662 }
663 Some(b) => name.push(b),
664 }
665 }
666 Ok(name)
667}
668
669fn stream_scan_string(files: &mut FileStore, entity: EntityId) -> Result<Token, PsError> {
671 let mut result = Vec::new();
672 let mut depth: u32 = 1;
673
674 while depth > 0 {
675 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
676 None => return Err(PsError::SyntaxError),
677 Some(b'(') => {
678 depth += 1;
679 result.push(b'(');
680 }
681 Some(b')') => {
682 depth -= 1;
683 if depth > 0 {
684 result.push(b')');
685 }
686 }
687 Some(b'\\') => {
688 let esc = files
689 .read_byte(entity)
690 .map_err(|_| PsError::IOError)?
691 .ok_or(PsError::SyntaxError)?;
692 match esc {
693 b'n' => result.push(b'\n'),
694 b'r' => result.push(b'\r'),
695 b't' => result.push(b'\t'),
696 b'b' => result.push(0x08),
697 b'f' => result.push(0x0C),
698 b'\\' => result.push(b'\\'),
699 b'(' => result.push(b'('),
700 b')' => result.push(b')'),
701 b'\n' => {} b'\r' => {
703 if let Some(next) = files.read_byte(entity).map_err(|_| PsError::IOError)?
705 && next != b'\n'
706 {
707 files.putback_bytes(entity, &[next]);
708 }
709 }
710 b'0'..=b'7' => {
711 let mut val = esc - b'0';
712 for _ in 0..2 {
713 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
714 Some(b @ b'0'..=b'7') => val = (val << 3) | (b - b'0'),
715 Some(other) => {
716 files.putback_bytes(entity, &[other]);
717 break;
718 }
719 None => break,
720 }
721 }
722 result.push(val);
723 }
724 _ => result.push(esc),
725 }
726 }
727 Some(b) => result.push(b),
728 }
729 }
730
731 Ok(Token::String(result))
732}
733
734fn stream_scan_hex_string(files: &mut FileStore, entity: EntityId) -> Result<Token, PsError> {
736 let mut result = Vec::new();
737 let mut nibble: Option<u8> = None;
738
739 loop {
740 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
741 None => return Err(PsError::SyntaxError),
742 Some(b'>') => {
743 if let Some(high) = nibble {
744 result.push(high << 4);
745 }
746 return Ok(Token::String(result));
747 }
748 Some(b) if is_whitespace(b) => continue,
749 Some(b) => {
750 let digit = match b {
751 b'0'..=b'9' => b - b'0',
752 b'a'..=b'f' => b - b'a' + 10,
753 b'A'..=b'F' => b - b'A' + 10,
754 _ => return Err(PsError::SyntaxError),
755 };
756 match nibble {
757 None => nibble = Some(digit),
758 Some(high) => {
759 result.push((high << 4) | digit);
760 nibble = None;
761 }
762 }
763 }
764 }
765 }
766}
767
768fn stream_scan_ascii85(files: &mut FileStore, entity: EntityId) -> Result<Token, PsError> {
770 let mut encoded = Vec::new();
771
772 loop {
773 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
774 None => return Err(PsError::SyntaxError),
775 Some(b'~') => match files.read_byte(entity).map_err(|_| PsError::IOError)? {
776 Some(b'>') => return Ok(Token::String(decode_ascii85(&encoded)?)),
777 _ => return Err(PsError::SyntaxError),
778 },
779 Some(b) if is_whitespace(b) => continue,
780 Some(b) => encoded.push(b),
781 }
782 }
783}
784
785#[cfg(test)]
786mod tests {
787 use super::*;
788
789 fn tokenize_all(input: &[u8]) -> Vec<Token> {
790 let mut t = Tokenizer::new(input);
791 let mut tokens = Vec::new();
792 while let Ok(Some(tok)) = t.next_token() {
793 tokens.push(tok);
794 }
795 tokens
796 }
797
798 #[test]
799 fn test_integers() {
800 assert_eq!(tokenize_all(b"42"), vec![Token::Int(42)]);
801 assert_eq!(tokenize_all(b"-7"), vec![Token::Int(-7)]);
802 assert_eq!(tokenize_all(b"+3"), vec![Token::Int(3)]);
803 assert_eq!(tokenize_all(b"0"), vec![Token::Int(0)]);
804 }
805
806 #[test]
807 fn test_reals() {
808 assert_eq!(tokenize_all(b"2.5"), vec![Token::Real(2.5)]);
809 assert_eq!(tokenize_all(b"-0.5"), vec![Token::Real(-0.5)]);
810 assert_eq!(tokenize_all(b"1e10"), vec![Token::Real(1e10)]);
811 assert_eq!(tokenize_all(b"1.5E-3"), vec![Token::Real(1.5e-3)]);
812 }
813
814 #[test]
815 fn test_radix() {
816 assert_eq!(tokenize_all(b"16#FF"), vec![Token::Int(255)]);
817 assert_eq!(tokenize_all(b"2#1010"), vec![Token::Int(10)]);
818 assert_eq!(tokenize_all(b"8#77"), vec![Token::Int(63)]);
819 }
820
821 #[test]
822 fn test_names() {
823 assert_eq!(
824 tokenize_all(b"add"),
825 vec![Token::Name(b"add".to_vec(), true)]
826 );
827 assert_eq!(
828 tokenize_all(b"/foo"),
829 vec![Token::LiteralName(b"foo".to_vec())]
830 );
831 assert_eq!(
832 tokenize_all(b"//bar"),
833 vec![Token::ImmediateName(b"bar".to_vec())]
834 );
835 }
836
837 #[test]
838 fn test_string_basic() {
839 assert_eq!(
840 tokenize_all(b"(hello)"),
841 vec![Token::String(b"hello".to_vec())]
842 );
843 }
844
845 #[test]
846 fn test_string_escapes() {
847 assert_eq!(
848 tokenize_all(b"(a\\nb)"),
849 vec![Token::String(b"a\nb".to_vec())]
850 );
851 assert_eq!(
852 tokenize_all(b"(a\\\\b)"),
853 vec![Token::String(b"a\\b".to_vec())]
854 );
855 assert_eq!(
856 tokenize_all(b"(\\110\\145\\154\\154\\157)"),
857 vec![Token::String(b"Hello".to_vec())]
858 );
859 }
860
861 #[test]
862 fn test_string_balanced_parens() {
863 assert_eq!(
864 tokenize_all(b"(a(b)c)"),
865 vec![Token::String(b"a(b)c".to_vec())]
866 );
867 }
868
869 #[test]
870 fn test_hex_string() {
871 assert_eq!(
872 tokenize_all(b"<48656C6C6F>"),
873 vec![Token::String(b"Hello".to_vec())]
874 );
875 assert_eq!(tokenize_all(b"<0>"), vec![Token::String(vec![0x00])]);
877 }
878
879 #[test]
880 fn test_procedures() {
881 let tokens = tokenize_all(b"{ add }");
882 assert_eq!(
883 tokens,
884 vec![
885 Token::ProcBegin,
886 Token::Name(b"add".to_vec(), true),
887 Token::ProcEnd,
888 ]
889 );
890 }
891
892 #[test]
893 fn test_comments() {
894 let tokens = tokenize_all(b"3 % comment\n4 add");
895 assert_eq!(
896 tokens,
897 vec![
898 Token::Int(3),
899 Token::Int(4),
900 Token::Name(b"add".to_vec(), true),
901 ]
902 );
903 }
904
905 #[test]
906 fn test_full_program() {
907 let tokens = tokenize_all(b"3 4 add 7 eq { (YES\\n) print } { (NO\\n) print } ifelse");
908 assert_eq!(tokens.len(), 14);
909 assert_eq!(tokens[0], Token::Int(3));
910 assert_eq!(tokens[1], Token::Int(4));
911 assert_eq!(tokens[2], Token::Name(b"add".to_vec(), true));
912 assert_eq!(tokens[3], Token::Int(7));
913 assert_eq!(tokens[4], Token::Name(b"eq".to_vec(), true));
914 assert_eq!(tokens[5], Token::ProcBegin);
915 assert_eq!(tokens[6], Token::String(b"YES\n".to_vec()));
916 assert_eq!(tokens[7], Token::Name(b"print".to_vec(), true));
917 assert_eq!(tokens[8], Token::ProcEnd);
918 assert_eq!(tokens[9], Token::ProcBegin);
919 assert_eq!(tokens[10], Token::String(b"NO\n".to_vec()));
920 assert_eq!(tokens[11], Token::Name(b"print".to_vec(), true));
921 assert_eq!(tokens[12], Token::ProcEnd);
922 assert_eq!(tokens[13], Token::Name(b"ifelse".to_vec(), true));
923 }
924
925 #[test]
926 fn test_dict_delimiters() {
927 let tokens = tokenize_all(b"<< /foo 42 >>");
928 assert_eq!(
929 tokens,
930 vec![
931 Token::DictBegin,
932 Token::LiteralName(b"foo".to_vec()),
933 Token::Int(42),
934 Token::DictEnd,
935 ]
936 );
937 }
938}