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 match self.try_scan_number()? {
123 Some(tok) => Ok(Some(tok)),
124 None => 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) -> Result<Option<Token>, PsError> {
154 let start = self.pos;
155 let bytes = self.input;
156 let len = bytes.len();
157
158 if start >= len {
159 return Ok(None);
160 }
161
162 let mut end = start;
164 while end < len
165 && !Self::is_whitespace(bytes[end])
166 && !Self::is_delimiter(bytes[end])
167 && !is_binary_token_byte(bytes[end])
168 {
169 end += 1;
170 }
171
172 if end == start {
173 return Ok(None);
174 }
175
176 let token_bytes = &bytes[start..end];
177
178 match try_parse_number_token(token_bytes) {
179 Ok(Some(tok)) => {
180 self.pos = end;
181 Ok(Some(tok))
182 }
183 Ok(None) => Ok(None),
184 Err(e) => Err(e),
185 }
186 }
187
188 fn scan_name(&mut self) -> Token {
189 let start = self.pos;
190 while self.pos < self.input.len()
191 && !Self::is_whitespace(self.input[self.pos])
192 && !Self::is_delimiter(self.input[self.pos])
193 && !is_binary_token_byte(self.input[self.pos])
194 {
195 self.pos += 1;
196 }
197 let name = self.input[start..self.pos].to_vec();
198 Token::Name(name, true) }
200
201 fn scan_literal_name(&mut self) -> Token {
202 let start = self.pos;
203 while self.pos < self.input.len()
204 && !Self::is_whitespace(self.input[self.pos])
205 && !Self::is_delimiter(self.input[self.pos])
206 && !is_binary_token_byte(self.input[self.pos])
207 {
208 self.pos += 1;
209 }
210 let name = self.input[start..self.pos].to_vec();
211 Token::LiteralName(name)
212 }
213
214 fn scan_immediate_name(&mut self) -> Token {
215 let start = self.pos;
216 while self.pos < self.input.len()
217 && !Self::is_whitespace(self.input[self.pos])
218 && !Self::is_delimiter(self.input[self.pos])
219 && !is_binary_token_byte(self.input[self.pos])
220 {
221 self.pos += 1;
222 }
223 let name = self.input[start..self.pos].to_vec();
224 Token::ImmediateName(name)
225 }
226
227 fn scan_string(&mut self) -> Result<Token, PsError> {
229 self.pos += 1; let mut result = Vec::new();
231 let mut depth = 1;
232
233 while self.pos < self.input.len() && depth > 0 {
234 let b = self.input[self.pos];
235 match b {
236 b'(' => {
237 depth += 1;
238 result.push(b'(');
239 self.pos += 1;
240 }
241 b')' => {
242 depth -= 1;
243 if depth > 0 {
244 result.push(b')');
245 }
246 self.pos += 1;
247 }
248 b'\\' => {
249 self.pos += 1;
250 if self.pos >= self.input.len() {
251 return Err(PsError::SyntaxError);
252 }
253 let esc = self.input[self.pos];
254 match esc {
255 b'n' => {
256 result.push(b'\n');
257 self.pos += 1;
258 }
259 b'r' => {
260 result.push(b'\r');
261 self.pos += 1;
262 }
263 b't' => {
264 result.push(b'\t');
265 self.pos += 1;
266 }
267 b'b' => {
268 result.push(0x08);
269 self.pos += 1;
270 }
271 b'f' => {
272 result.push(0x0C);
273 self.pos += 1;
274 }
275 b'\\' => {
276 result.push(b'\\');
277 self.pos += 1;
278 }
279 b'(' => {
280 result.push(b'(');
281 self.pos += 1;
282 }
283 b')' => {
284 result.push(b')');
285 self.pos += 1;
286 }
287 b'\n' => {
288 self.pos += 1;
290 }
291 b'\r' => {
292 self.pos += 1;
294 if self.pos < self.input.len() && self.input[self.pos] == b'\n' {
295 self.pos += 1;
296 }
297 }
298 b'0'..=b'7' => {
299 let mut val: u8 = esc - b'0';
301 self.pos += 1;
302 for _ in 0..2 {
303 if self.pos < self.input.len()
304 && self.input[self.pos] >= b'0'
305 && self.input[self.pos] <= b'7'
306 {
307 val = (val << 3) | (self.input[self.pos] - b'0');
308 self.pos += 1;
309 } else {
310 break;
311 }
312 }
313 result.push(val);
314 }
315 _ => {
316 result.push(esc);
318 self.pos += 1;
319 }
320 }
321 }
322 _ => {
323 result.push(b);
324 self.pos += 1;
325 }
326 }
327 }
328
329 if depth != 0 {
330 return Err(PsError::SyntaxError);
331 }
332
333 Ok(Token::String(result))
334 }
335
336 fn scan_hex_string(&mut self) -> Result<Token, PsError> {
338 self.pos += 1; let mut result = Vec::new();
340 let mut nibble: Option<u8> = None;
341
342 while self.pos < self.input.len() {
343 let b = self.input[self.pos];
344 if b == b'>' {
345 self.pos += 1;
346 if let Some(high) = nibble {
348 result.push(high << 4);
349 }
350 return Ok(Token::String(result));
351 }
352
353 if Self::is_whitespace(b) {
354 self.pos += 1;
355 continue;
356 }
357
358 let digit = match b {
359 b'0'..=b'9' => b - b'0',
360 b'a'..=b'f' => b - b'a' + 10,
361 b'A'..=b'F' => b - b'A' + 10,
362 _ => return Err(PsError::SyntaxError),
363 };
364
365 match nibble {
366 None => nibble = Some(digit),
367 Some(high) => {
368 result.push((high << 4) | digit);
369 nibble = None;
370 }
371 }
372 self.pos += 1;
373 }
374
375 Err(PsError::SyntaxError) }
377
378 fn scan_ascii85_string(&mut self) -> Result<Token, PsError> {
380 self.pos += 2; let mut encoded = Vec::new();
382
383 while self.pos < self.input.len() {
384 let b = self.input[self.pos];
385 if b == b'~' {
386 self.pos += 1;
387 if self.pos < self.input.len() && self.input[self.pos] == b'>' {
388 self.pos += 1;
389 return Ok(Token::String(Self::decode_ascii85(&encoded)?));
390 }
391 return Err(PsError::SyntaxError);
392 }
393 if !Self::is_whitespace(b) {
394 encoded.push(b);
395 }
396 self.pos += 1;
397 }
398
399 Err(PsError::SyntaxError)
400 }
401
402 fn decode_ascii85(data: &[u8]) -> Result<Vec<u8>, PsError> {
403 decode_ascii85(data)
404 }
405
406 fn is_whitespace(b: u8) -> bool {
407 is_whitespace(b)
408 }
409
410 fn is_delimiter(b: u8) -> bool {
411 is_delimiter(b)
412 }
413}
414
415fn is_whitespace(b: u8) -> bool {
419 b <= b' '
420}
421
422fn is_binary_token_byte(b: u8) -> bool {
424 (128..=159).contains(&b)
425}
426
427fn is_delimiter(b: u8) -> bool {
429 matches!(
430 b,
431 b'(' | b')' | b'<' | b'>' | b'[' | b']' | b'{' | b'}' | b'/' | b'%'
432 )
433}
434
435fn decode_ascii85(data: &[u8]) -> Result<Vec<u8>, PsError> {
437 let mut result = Vec::new();
438 let mut i = 0;
439
440 while i < data.len() {
441 if data[i] == b'z' {
442 result.extend_from_slice(&[0, 0, 0, 0]);
443 i += 1;
444 continue;
445 }
446
447 let mut group = [0u8; 5];
448 let mut count = 0;
449 while count < 5 && i < data.len() && data[i] != b'z' {
450 if data[i] < b'!' || data[i] > b'u' {
451 return Err(PsError::SyntaxError);
452 }
453 group[count] = data[i] - b'!';
454 count += 1;
455 i += 1;
456 }
457
458 if count < 2 {
459 if count == 1 {
460 return Err(PsError::SyntaxError);
461 }
462 break;
463 }
464
465 for g in group.iter_mut().skip(count) {
467 *g = 84;
468 }
469
470 let mut value: u32 = 0;
471 for &g in &group {
472 value = value
473 .checked_mul(85)
474 .and_then(|v| v.checked_add(g as u32))
475 .ok_or(PsError::SyntaxError)?;
476 }
477
478 let bytes = value.to_be_bytes();
479 let output_count = count - 1;
480 result.extend_from_slice(&bytes[..output_count]);
481 }
482
483 Ok(result)
484}
485
486fn try_parse_number_token(token_bytes: &[u8]) -> Result<Option<Token>, PsError> {
492 if token_bytes.is_empty() {
493 return Ok(None);
494 }
495
496 if let Some(result) = try_parse_radix(token_bytes) {
498 return Ok(Some(result));
499 }
500
501 let Ok(s) = std::str::from_utf8(token_bytes) else {
502 return Ok(None);
503 };
504
505 let first = token_bytes[0];
506 let looks_numeric = first.is_ascii_digit()
507 || ((first == b'+' || first == b'-')
508 && token_bytes.len() > 1
509 && (token_bytes[1].is_ascii_digit() || token_bytes[1] == b'.'))
510 || (first == b'.' && token_bytes.len() > 1 && token_bytes[1].is_ascii_digit());
511
512 if !looks_numeric {
513 return Ok(None);
514 }
515
516 let is_real = s.contains('.') || s.contains('e') || s.contains('E');
517
518 if is_real {
519 return match s.parse::<f64>() {
520 Ok(v) => finite_real_token(v),
521 Err(_) => Ok(None),
522 };
523 }
524
525 if let Ok(v) = s.parse::<i64>() {
528 return Ok(Some(Token::Int(v)));
529 }
530 match s.parse::<f64>() {
531 Ok(v) => finite_real_token(v),
532 Err(_) => Ok(None),
533 }
534}
535
536fn finite_real_token(v: f64) -> Result<Option<Token>, PsError> {
559 if v.is_finite() {
560 Ok(Some(Token::Real(v)))
561 } else {
562 Ok(None)
563 }
564}
565
566fn try_parse_radix(token: &[u8]) -> Option<Token> {
568 let s = std::str::from_utf8(token).ok()?;
569 let hash_pos = s.find('#')?;
570 let base_str = &s[..hash_pos];
571 let digits_str = &s[hash_pos + 1..];
572 if digits_str.is_empty() {
573 return None;
574 }
575 let base: u32 = base_str.parse().ok()?;
576 if !(2..=36).contains(&base) {
577 return None;
578 }
579 let value = i64::from_str_radix(digits_str, base).ok()?;
582 Some(Token::Int(value))
583}
584
585pub fn stream_next_token(
592 files: &mut FileStore,
593 entity: EntityId,
594) -> Result<Option<(Token, u32)>, PsError> {
595 let mut newlines = 0u32;
596
597 let first = loop {
599 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
600 None => return Ok(None),
601 Some(b) if is_whitespace(b) => {
602 if b == b'\n' || b == b'\r' {
603 newlines += 1;
604 }
605 continue;
606 }
607 Some(b'%') => {
608 loop {
610 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
611 None => return Ok(None),
612 Some(b'\n') | Some(b'\r') => {
613 newlines += 1;
614 break;
615 }
616 Some(_) => {}
617 }
618 }
619 continue;
620 }
621 Some(b) => break b,
622 }
623 };
624
625 let token = match first {
626 b'(' => stream_scan_string(files, entity)?,
627 b'<' => match files.read_byte(entity).map_err(|_| PsError::IOError)? {
628 Some(b'<') => Token::DictBegin,
629 Some(b'~') => stream_scan_ascii85(files, entity)?,
630 other => {
631 if let Some(b) = other {
632 files.putback_bytes(entity, &[b]);
633 }
634 stream_scan_hex_string(files, entity)?
635 }
636 },
637 b'>' => match files.read_byte(entity).map_err(|_| PsError::IOError)? {
638 Some(b'>') => Token::DictEnd,
639 _ => return Err(PsError::SyntaxError),
640 },
641 b'{' => Token::ProcBegin,
642 b'}' => Token::ProcEnd,
643 b'[' => Token::ArrayBegin,
644 b']' => Token::ArrayEnd,
645 b'/' => match files.read_byte(entity).map_err(|_| PsError::IOError)? {
646 Some(b'/') => {
647 let name = stream_read_name_bytes(files, entity)?;
648 Token::ImmediateName(name)
649 }
650 Some(b) if !is_whitespace(b) && !is_delimiter(b) => {
651 files.putback_bytes(entity, &[b]);
652 let name = stream_read_name_bytes(files, entity)?;
653 Token::LiteralName(name)
654 }
655 other => {
656 if let Some(b) = other {
657 files.putback_bytes(entity, &[b]);
658 }
659 Token::LiteralName(Vec::new())
660 }
661 },
662 128..=159 => Token::BinaryTokenByte(first),
664 _ => {
665 let mut token_bytes = vec![first];
667 loop {
668 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
669 None => break,
670 Some(b) if is_whitespace(b) => {
671 if b == b'\n' || b == b'\r' {
675 newlines += 1;
676 }
677 break;
678 }
679 Some(b) if is_delimiter(b) => {
680 files.putback_bytes(entity, &[b]);
681 break;
682 }
683 Some(b @ 128..=159) => {
685 files.putback_bytes(entity, &[b]);
686 break;
687 }
688 Some(b) => token_bytes.push(b),
689 }
690 }
691 match try_parse_number_token(&token_bytes)? {
692 Some(tok) => tok,
693 None => Token::Name(token_bytes, true),
694 }
695 }
696 };
697
698 Ok(Some((token, newlines)))
699}
700
701fn stream_read_name_bytes(files: &mut FileStore, entity: EntityId) -> Result<Vec<u8>, PsError> {
705 let mut name = Vec::new();
706 loop {
707 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
708 None => break,
709 Some(b) if is_whitespace(b) || is_delimiter(b) || is_binary_token_byte(b) => {
710 files.putback_bytes(entity, &[b]);
711 break;
712 }
713 Some(b) => name.push(b),
714 }
715 }
716 Ok(name)
717}
718
719fn stream_scan_string(files: &mut FileStore, entity: EntityId) -> Result<Token, PsError> {
721 let mut result = Vec::new();
722 let mut depth: u32 = 1;
723
724 while depth > 0 {
725 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
726 None => return Err(PsError::SyntaxError),
727 Some(b'(') => {
728 depth += 1;
729 result.push(b'(');
730 }
731 Some(b')') => {
732 depth -= 1;
733 if depth > 0 {
734 result.push(b')');
735 }
736 }
737 Some(b'\\') => {
738 let esc = files
739 .read_byte(entity)
740 .map_err(|_| PsError::IOError)?
741 .ok_or(PsError::SyntaxError)?;
742 match esc {
743 b'n' => result.push(b'\n'),
744 b'r' => result.push(b'\r'),
745 b't' => result.push(b'\t'),
746 b'b' => result.push(0x08),
747 b'f' => result.push(0x0C),
748 b'\\' => result.push(b'\\'),
749 b'(' => result.push(b'('),
750 b')' => result.push(b')'),
751 b'\n' => {} b'\r' => {
753 if let Some(next) = files.read_byte(entity).map_err(|_| PsError::IOError)?
755 && next != b'\n'
756 {
757 files.putback_bytes(entity, &[next]);
758 }
759 }
760 b'0'..=b'7' => {
761 let mut val = esc - b'0';
762 for _ in 0..2 {
763 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
764 Some(b @ b'0'..=b'7') => val = (val << 3) | (b - b'0'),
765 Some(other) => {
766 files.putback_bytes(entity, &[other]);
767 break;
768 }
769 None => break,
770 }
771 }
772 result.push(val);
773 }
774 _ => result.push(esc),
775 }
776 }
777 Some(b) => result.push(b),
778 }
779 }
780
781 Ok(Token::String(result))
782}
783
784fn stream_scan_hex_string(files: &mut FileStore, entity: EntityId) -> Result<Token, PsError> {
786 let mut result = Vec::new();
787 let mut nibble: Option<u8> = None;
788
789 loop {
790 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
791 None => return Err(PsError::SyntaxError),
792 Some(b'>') => {
793 if let Some(high) = nibble {
794 result.push(high << 4);
795 }
796 return Ok(Token::String(result));
797 }
798 Some(b) if is_whitespace(b) => continue,
799 Some(b) => {
800 let digit = match b {
801 b'0'..=b'9' => b - b'0',
802 b'a'..=b'f' => b - b'a' + 10,
803 b'A'..=b'F' => b - b'A' + 10,
804 _ => return Err(PsError::SyntaxError),
805 };
806 match nibble {
807 None => nibble = Some(digit),
808 Some(high) => {
809 result.push((high << 4) | digit);
810 nibble = None;
811 }
812 }
813 }
814 }
815 }
816}
817
818fn stream_scan_ascii85(files: &mut FileStore, entity: EntityId) -> Result<Token, PsError> {
820 let mut encoded = Vec::new();
821
822 loop {
823 match files.read_byte(entity).map_err(|_| PsError::IOError)? {
824 None => return Err(PsError::SyntaxError),
825 Some(b'~') => match files.read_byte(entity).map_err(|_| PsError::IOError)? {
826 Some(b'>') => return Ok(Token::String(decode_ascii85(&encoded)?)),
827 _ => return Err(PsError::SyntaxError),
828 },
829 Some(b) if is_whitespace(b) => continue,
830 Some(b) => encoded.push(b),
831 }
832 }
833}
834
835#[cfg(test)]
836mod tests {
837 use super::*;
838
839 fn tokenize_all(input: &[u8]) -> Vec<Token> {
840 let mut t = Tokenizer::new(input);
841 let mut tokens = Vec::new();
842 while let Ok(Some(tok)) = t.next_token() {
843 tokens.push(tok);
844 }
845 tokens
846 }
847
848 #[test]
853 fn out_of_range_real_literal_does_not_become_infinity() {
854 for src in [
855 &b"1e999"[..],
856 &b"-1e999"[..],
857 &b"1.5e400"[..],
858 &b"1e309"[..],
859 &b"99999999999999999999999999e999"[..],
860 &b"5657564e574"[..],
863 ] {
864 let toks = tokenize_all(src);
865 assert!(
866 matches!(toks.as_slice(), [Token::Name(..)]),
867 "expected a name from {:?}, got {toks:?}",
868 String::from_utf8_lossy(src)
869 );
870 }
871 }
872
873 #[test]
876 fn representable_reals_still_scan() {
877 assert_eq!(tokenize_all(b"1e308"), vec![Token::Real(1e308)]);
878 assert_eq!(tokenize_all(b"-1e308"), vec![Token::Real(-1e308)]);
879 assert_eq!(tokenize_all(b"1e-308"), vec![Token::Real(1e-308)]);
880 assert_eq!(tokenize_all(b"1e38"), vec![Token::Real(1e38)]);
881 }
882
883 #[test]
887 fn underflowing_literals_scan_as_zero() {
888 assert_eq!(tokenize_all(b"1e-999"), vec![Token::Real(0.0)]);
889 }
890
891 #[test]
894 fn non_numeric_tokens_are_still_names() {
895 assert!(matches!(
896 tokenize_all(b"1e999x").as_slice(),
897 [Token::Name(..)]
898 ));
899 assert!(matches!(tokenize_all(b"foo").as_slice(), [Token::Name(..)]));
900 }
901
902 #[test]
903 fn test_integers() {
904 assert_eq!(tokenize_all(b"42"), vec![Token::Int(42)]);
905 assert_eq!(tokenize_all(b"-7"), vec![Token::Int(-7)]);
906 assert_eq!(tokenize_all(b"+3"), vec![Token::Int(3)]);
907 assert_eq!(tokenize_all(b"0"), vec![Token::Int(0)]);
908 }
909
910 #[test]
911 fn test_reals() {
912 assert_eq!(tokenize_all(b"2.5"), vec![Token::Real(2.5)]);
913 assert_eq!(tokenize_all(b"-0.5"), vec![Token::Real(-0.5)]);
914 assert_eq!(tokenize_all(b"1e10"), vec![Token::Real(1e10)]);
915 assert_eq!(tokenize_all(b"1.5E-3"), vec![Token::Real(1.5e-3)]);
916 }
917
918 #[test]
919 fn test_radix() {
920 assert_eq!(tokenize_all(b"16#FF"), vec![Token::Int(255)]);
921 assert_eq!(tokenize_all(b"2#1010"), vec![Token::Int(10)]);
922 assert_eq!(tokenize_all(b"8#77"), vec![Token::Int(63)]);
923 }
924
925 #[test]
926 fn test_names() {
927 assert_eq!(
928 tokenize_all(b"add"),
929 vec![Token::Name(b"add".to_vec(), true)]
930 );
931 assert_eq!(
932 tokenize_all(b"/foo"),
933 vec![Token::LiteralName(b"foo".to_vec())]
934 );
935 assert_eq!(
936 tokenize_all(b"//bar"),
937 vec![Token::ImmediateName(b"bar".to_vec())]
938 );
939 }
940
941 #[test]
942 fn test_string_basic() {
943 assert_eq!(
944 tokenize_all(b"(hello)"),
945 vec![Token::String(b"hello".to_vec())]
946 );
947 }
948
949 #[test]
950 fn test_string_escapes() {
951 assert_eq!(
952 tokenize_all(b"(a\\nb)"),
953 vec![Token::String(b"a\nb".to_vec())]
954 );
955 assert_eq!(
956 tokenize_all(b"(a\\\\b)"),
957 vec![Token::String(b"a\\b".to_vec())]
958 );
959 assert_eq!(
960 tokenize_all(b"(\\110\\145\\154\\154\\157)"),
961 vec![Token::String(b"Hello".to_vec())]
962 );
963 }
964
965 #[test]
966 fn test_string_balanced_parens() {
967 assert_eq!(
968 tokenize_all(b"(a(b)c)"),
969 vec![Token::String(b"a(b)c".to_vec())]
970 );
971 }
972
973 #[test]
974 fn test_hex_string() {
975 assert_eq!(
976 tokenize_all(b"<48656C6C6F>"),
977 vec![Token::String(b"Hello".to_vec())]
978 );
979 assert_eq!(tokenize_all(b"<0>"), vec![Token::String(vec![0x00])]);
981 }
982
983 #[test]
984 fn test_procedures() {
985 let tokens = tokenize_all(b"{ add }");
986 assert_eq!(
987 tokens,
988 vec![
989 Token::ProcBegin,
990 Token::Name(b"add".to_vec(), true),
991 Token::ProcEnd,
992 ]
993 );
994 }
995
996 #[test]
997 fn test_comments() {
998 let tokens = tokenize_all(b"3 % comment\n4 add");
999 assert_eq!(
1000 tokens,
1001 vec![
1002 Token::Int(3),
1003 Token::Int(4),
1004 Token::Name(b"add".to_vec(), true),
1005 ]
1006 );
1007 }
1008
1009 #[test]
1010 fn test_full_program() {
1011 let tokens = tokenize_all(b"3 4 add 7 eq { (YES\\n) print } { (NO\\n) print } ifelse");
1012 assert_eq!(tokens.len(), 14);
1013 assert_eq!(tokens[0], Token::Int(3));
1014 assert_eq!(tokens[1], Token::Int(4));
1015 assert_eq!(tokens[2], Token::Name(b"add".to_vec(), true));
1016 assert_eq!(tokens[3], Token::Int(7));
1017 assert_eq!(tokens[4], Token::Name(b"eq".to_vec(), true));
1018 assert_eq!(tokens[5], Token::ProcBegin);
1019 assert_eq!(tokens[6], Token::String(b"YES\n".to_vec()));
1020 assert_eq!(tokens[7], Token::Name(b"print".to_vec(), true));
1021 assert_eq!(tokens[8], Token::ProcEnd);
1022 assert_eq!(tokens[9], Token::ProcBegin);
1023 assert_eq!(tokens[10], Token::String(b"NO\n".to_vec()));
1024 assert_eq!(tokens[11], Token::Name(b"print".to_vec(), true));
1025 assert_eq!(tokens[12], Token::ProcEnd);
1026 assert_eq!(tokens[13], Token::Name(b"ifelse".to_vec(), true));
1027 }
1028
1029 #[test]
1030 fn test_dict_delimiters() {
1031 let tokens = tokenize_all(b"<< /foo 42 >>");
1032 assert_eq!(
1033 tokens,
1034 vec![
1035 Token::DictBegin,
1036 Token::LiteralName(b"foo".to_vec()),
1037 Token::Int(42),
1038 Token::DictEnd,
1039 ]
1040 );
1041 }
1042}