1use crate::error::PsError;
11use crate::file_store::FileStore;
12use crate::object::EntityId;
13
14#[derive(Debug, Clone, PartialEq)]
16pub enum Token {
17 Int(i32),
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::<i32>() {
512 return Some(Token::Int(v));
513 }
514 if let Ok(v) = s.parse::<i64>() {
515 return Some(Token::Real(v as f64));
516 }
517 s.parse::<f64>().ok().map(Token::Real)
518}
519
520fn try_parse_radix(token: &[u8]) -> Option<Token> {
522 let s = std::str::from_utf8(token).ok()?;
523 let hash_pos = s.find('#')?;
524 let base_str = &s[..hash_pos];
525 let digits_str = &s[hash_pos + 1..];
526 if digits_str.is_empty() {
527 return None;
528 }
529 let base: u32 = base_str.parse().ok()?;
530 if !(2..=36).contains(&base) {
531 return None;
532 }
533 let value = i64::from_str_radix(digits_str, base).ok()?;
534 if value >= i32::MIN as i64 && value <= i32::MAX as i64 {
535 Some(Token::Int(value as i32))
536 } else {
537 Some(Token::Real(value as f64))
538 }
539}
540
541pub fn stream_next_token(
548 files: &mut FileStore,
549 entity: EntityId,
550) -> Result<Option<(Token, u32)>, PsError> {
551 let mut newlines = 0u32;
552
553 let first = loop {
555 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
556 None => return Ok(None),
557 Some(b) if is_whitespace(b) => {
558 if b == b'\n' || b == b'\r' {
559 newlines += 1;
560 }
561 continue;
562 }
563 Some(b'%') => {
564 loop {
566 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
567 None => return Ok(None),
568 Some(b'\n') | Some(b'\r') => {
569 newlines += 1;
570 break;
571 }
572 Some(_) => {}
573 }
574 }
575 continue;
576 }
577 Some(b) => break b,
578 }
579 };
580
581 let token = match first {
582 b'(' => stream_scan_string(files, entity)?,
583 b'<' => match files.read_byte(entity).map_err(|_| PsError::IOError)? {
584 Some(b'<') => Token::DictBegin,
585 Some(b'~') => stream_scan_ascii85(files, entity)?,
586 other => {
587 if let Some(b) = other {
588 files.putback_bytes(entity, &[b]);
589 }
590 stream_scan_hex_string(files, entity)?
591 }
592 },
593 b'>' => match files.read_byte(entity).map_err(|_| PsError::IOError)? {
594 Some(b'>') => Token::DictEnd,
595 _ => return Err(PsError::SyntaxError),
596 },
597 b'{' => Token::ProcBegin,
598 b'}' => Token::ProcEnd,
599 b'[' => Token::ArrayBegin,
600 b']' => Token::ArrayEnd,
601 b'/' => match files.read_byte(entity).map_err(|_| PsError::IOError)? {
602 Some(b'/') => {
603 let name = stream_read_name_bytes(files, entity)?;
604 Token::ImmediateName(name)
605 }
606 Some(b) if !is_whitespace(b) && !is_delimiter(b) => {
607 files.putback_bytes(entity, &[b]);
608 let name = stream_read_name_bytes(files, entity)?;
609 Token::LiteralName(name)
610 }
611 other => {
612 if let Some(b) = other {
613 files.putback_bytes(entity, &[b]);
614 }
615 Token::LiteralName(Vec::new())
616 }
617 },
618 128..=159 => Token::BinaryTokenByte(first),
620 _ => {
621 let mut token_bytes = vec![first];
623 loop {
624 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
625 None => break,
626 Some(b) if is_whitespace(b) => {
627 if b == b'\n' || b == b'\r' {
631 newlines += 1;
632 }
633 break;
634 }
635 Some(b) if is_delimiter(b) => {
636 files.putback_bytes(entity, &[b]);
637 break;
638 }
639 Some(b @ 128..=159) => {
641 files.putback_bytes(entity, &[b]);
642 break;
643 }
644 Some(b) => token_bytes.push(b),
645 }
646 }
647 try_parse_number_token(&token_bytes).unwrap_or(Token::Name(token_bytes, true))
648 }
649 };
650
651 Ok(Some((token, newlines)))
652}
653
654fn stream_read_name_bytes(files: &mut FileStore, entity: EntityId) -> Result<Vec<u8>, PsError> {
658 let mut name = Vec::new();
659 loop {
660 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
661 None => break,
662 Some(b) if is_whitespace(b) || is_delimiter(b) || is_binary_token_byte(b) => {
663 files.putback_bytes(entity, &[b]);
664 break;
665 }
666 Some(b) => name.push(b),
667 }
668 }
669 Ok(name)
670}
671
672fn stream_scan_string(files: &mut FileStore, entity: EntityId) -> Result<Token, PsError> {
674 let mut result = Vec::new();
675 let mut depth: u32 = 1;
676
677 while depth > 0 {
678 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
679 None => return Err(PsError::SyntaxError),
680 Some(b'(') => {
681 depth += 1;
682 result.push(b'(');
683 }
684 Some(b')') => {
685 depth -= 1;
686 if depth > 0 {
687 result.push(b')');
688 }
689 }
690 Some(b'\\') => {
691 let esc = files
692 .read_byte(entity)
693 .map_err(|_| PsError::IOError)?
694 .ok_or(PsError::SyntaxError)?;
695 match esc {
696 b'n' => result.push(b'\n'),
697 b'r' => result.push(b'\r'),
698 b't' => result.push(b'\t'),
699 b'b' => result.push(0x08),
700 b'f' => result.push(0x0C),
701 b'\\' => result.push(b'\\'),
702 b'(' => result.push(b'('),
703 b')' => result.push(b')'),
704 b'\n' => {} b'\r' => {
706 if let Some(next) = files.read_byte(entity).map_err(|_| PsError::IOError)?
708 && next != b'\n'
709 {
710 files.putback_bytes(entity, &[next]);
711 }
712 }
713 b'0'..=b'7' => {
714 let mut val = esc - b'0';
715 for _ in 0..2 {
716 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
717 Some(b @ b'0'..=b'7') => val = (val << 3) | (b - b'0'),
718 Some(other) => {
719 files.putback_bytes(entity, &[other]);
720 break;
721 }
722 None => break,
723 }
724 }
725 result.push(val);
726 }
727 _ => result.push(esc),
728 }
729 }
730 Some(b) => result.push(b),
731 }
732 }
733
734 Ok(Token::String(result))
735}
736
737fn stream_scan_hex_string(files: &mut FileStore, entity: EntityId) -> Result<Token, PsError> {
739 let mut result = Vec::new();
740 let mut nibble: Option<u8> = None;
741
742 loop {
743 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
744 None => return Err(PsError::SyntaxError),
745 Some(b'>') => {
746 if let Some(high) = nibble {
747 result.push(high << 4);
748 }
749 return Ok(Token::String(result));
750 }
751 Some(b) if is_whitespace(b) => continue,
752 Some(b) => {
753 let digit = match b {
754 b'0'..=b'9' => b - b'0',
755 b'a'..=b'f' => b - b'a' + 10,
756 b'A'..=b'F' => b - b'A' + 10,
757 _ => return Err(PsError::SyntaxError),
758 };
759 match nibble {
760 None => nibble = Some(digit),
761 Some(high) => {
762 result.push((high << 4) | digit);
763 nibble = None;
764 }
765 }
766 }
767 }
768 }
769}
770
771fn stream_scan_ascii85(files: &mut FileStore, entity: EntityId) -> Result<Token, PsError> {
773 let mut encoded = Vec::new();
774
775 loop {
776 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
777 None => return Err(PsError::SyntaxError),
778 Some(b'~') => match files.read_byte(entity).map_err(|_| PsError::IOError)? {
779 Some(b'>') => return Ok(Token::String(decode_ascii85(&encoded)?)),
780 _ => return Err(PsError::SyntaxError),
781 },
782 Some(b) if is_whitespace(b) => continue,
783 Some(b) => encoded.push(b),
784 }
785 }
786}
787
788#[cfg(test)]
789mod tests {
790 use super::*;
791
792 fn tokenize_all(input: &[u8]) -> Vec<Token> {
793 let mut t = Tokenizer::new(input);
794 let mut tokens = Vec::new();
795 while let Ok(Some(tok)) = t.next_token() {
796 tokens.push(tok);
797 }
798 tokens
799 }
800
801 #[test]
802 fn test_integers() {
803 assert_eq!(tokenize_all(b"42"), vec![Token::Int(42)]);
804 assert_eq!(tokenize_all(b"-7"), vec![Token::Int(-7)]);
805 assert_eq!(tokenize_all(b"+3"), vec![Token::Int(3)]);
806 assert_eq!(tokenize_all(b"0"), vec![Token::Int(0)]);
807 }
808
809 #[test]
810 fn test_reals() {
811 assert_eq!(tokenize_all(b"2.5"), vec![Token::Real(2.5)]);
812 assert_eq!(tokenize_all(b"-0.5"), vec![Token::Real(-0.5)]);
813 assert_eq!(tokenize_all(b"1e10"), vec![Token::Real(1e10)]);
814 assert_eq!(tokenize_all(b"1.5E-3"), vec![Token::Real(1.5e-3)]);
815 }
816
817 #[test]
818 fn test_radix() {
819 assert_eq!(tokenize_all(b"16#FF"), vec![Token::Int(255)]);
820 assert_eq!(tokenize_all(b"2#1010"), vec![Token::Int(10)]);
821 assert_eq!(tokenize_all(b"8#77"), vec![Token::Int(63)]);
822 }
823
824 #[test]
825 fn test_names() {
826 assert_eq!(
827 tokenize_all(b"add"),
828 vec![Token::Name(b"add".to_vec(), true)]
829 );
830 assert_eq!(
831 tokenize_all(b"/foo"),
832 vec![Token::LiteralName(b"foo".to_vec())]
833 );
834 assert_eq!(
835 tokenize_all(b"//bar"),
836 vec![Token::ImmediateName(b"bar".to_vec())]
837 );
838 }
839
840 #[test]
841 fn test_string_basic() {
842 assert_eq!(
843 tokenize_all(b"(hello)"),
844 vec![Token::String(b"hello".to_vec())]
845 );
846 }
847
848 #[test]
849 fn test_string_escapes() {
850 assert_eq!(
851 tokenize_all(b"(a\\nb)"),
852 vec![Token::String(b"a\nb".to_vec())]
853 );
854 assert_eq!(
855 tokenize_all(b"(a\\\\b)"),
856 vec![Token::String(b"a\\b".to_vec())]
857 );
858 assert_eq!(
859 tokenize_all(b"(\\110\\145\\154\\154\\157)"),
860 vec![Token::String(b"Hello".to_vec())]
861 );
862 }
863
864 #[test]
865 fn test_string_balanced_parens() {
866 assert_eq!(
867 tokenize_all(b"(a(b)c)"),
868 vec![Token::String(b"a(b)c".to_vec())]
869 );
870 }
871
872 #[test]
873 fn test_hex_string() {
874 assert_eq!(
875 tokenize_all(b"<48656C6C6F>"),
876 vec![Token::String(b"Hello".to_vec())]
877 );
878 assert_eq!(tokenize_all(b"<0>"), vec![Token::String(vec![0x00])]);
880 }
881
882 #[test]
883 fn test_procedures() {
884 let tokens = tokenize_all(b"{ add }");
885 assert_eq!(
886 tokens,
887 vec![
888 Token::ProcBegin,
889 Token::Name(b"add".to_vec(), true),
890 Token::ProcEnd,
891 ]
892 );
893 }
894
895 #[test]
896 fn test_comments() {
897 let tokens = tokenize_all(b"3 % comment\n4 add");
898 assert_eq!(
899 tokens,
900 vec![
901 Token::Int(3),
902 Token::Int(4),
903 Token::Name(b"add".to_vec(), true),
904 ]
905 );
906 }
907
908 #[test]
909 fn test_full_program() {
910 let tokens = tokenize_all(b"3 4 add 7 eq { (YES\\n) print } { (NO\\n) print } ifelse");
911 assert_eq!(tokens.len(), 14);
912 assert_eq!(tokens[0], Token::Int(3));
913 assert_eq!(tokens[1], Token::Int(4));
914 assert_eq!(tokens[2], Token::Name(b"add".to_vec(), true));
915 assert_eq!(tokens[3], Token::Int(7));
916 assert_eq!(tokens[4], Token::Name(b"eq".to_vec(), true));
917 assert_eq!(tokens[5], Token::ProcBegin);
918 assert_eq!(tokens[6], Token::String(b"YES\n".to_vec()));
919 assert_eq!(tokens[7], Token::Name(b"print".to_vec(), true));
920 assert_eq!(tokens[8], Token::ProcEnd);
921 assert_eq!(tokens[9], Token::ProcBegin);
922 assert_eq!(tokens[10], Token::String(b"NO\n".to_vec()));
923 assert_eq!(tokens[11], Token::Name(b"print".to_vec(), true));
924 assert_eq!(tokens[12], Token::ProcEnd);
925 assert_eq!(tokens[13], Token::Name(b"ifelse".to_vec(), true));
926 }
927
928 #[test]
929 fn test_dict_delimiters() {
930 let tokens = tokenize_all(b"<< /foo 42 >>");
931 assert_eq!(
932 tokens,
933 vec![
934 Token::DictBegin,
935 Token::LiteralName(b"foo".to_vec()),
936 Token::Int(42),
937 Token::DictEnd,
938 ]
939 );
940 }
941}