1use alloc::string::String;
2#[cfg(test)]
3use alloc::vec;
4use alloc::vec::Vec;
5use core::char;
6use core::fmt;
7use core::result::Result;
8use core::str;
9
10use self::Token::*;
11
12#[derive(Clone)]
13pub struct Tokenizer<'a> {
14 input: &'a str,
15 span_offset: u32,
16 chars: CrlfFold<'a>,
17}
18
19#[derive(Clone)]
20struct CrlfFold<'a> {
21 chars: str::CharIndices<'a>,
22}
23
24#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
29pub struct Span {
30 start: u32,
31 end: u32,
32}
33
34impl Default for Span {
35 fn default() -> Span {
36 Span {
37 start: u32::MAX,
38 end: u32::MAX,
39 }
40 }
41}
42
43impl Span {
44 pub fn new(start: u32, end: u32) -> Span {
45 let span = Span { start, end };
46 assert!(span.is_known(), "cannot create a span with u32::MAX");
47 span
48 }
49
50 pub fn adjust(&mut self, offset: u32) {
52 if self.is_known() {
53 self.start += offset;
54 self.end += offset;
55 }
56 }
57
58 pub fn start(&self) -> u32 {
60 assert!(self.is_known(), "cannot get start of unknown span");
61 self.start
62 }
63
64 pub fn end(&self) -> u32 {
66 assert!(self.is_known(), "cannot get end of unknown span");
67 self.end
68 }
69
70 pub fn set_end(&mut self, new_end: u32) {
72 if !self.is_known() {
73 self.start = new_end;
74 }
75 self.end = new_end;
76 }
77
78 pub fn set_start(&mut self, new_start: u32) {
80 if !self.is_known() {
81 self.end = new_start;
82 }
83 self.start = new_start;
84 }
85
86 pub fn is_known(&self) -> bool {
88 self.start != u32::MAX && self.end != u32::MAX
89 }
90}
91
92#[derive(Eq, PartialEq, Debug, Copy, Clone)]
93pub enum Token {
94 Whitespace,
95 Comment,
96
97 Equals,
98 Comma,
99 Colon,
100 Period,
101 Semicolon,
102 LeftParen,
103 RightParen,
104 LeftBrace,
105 RightBrace,
106 LessThan,
107 GreaterThan,
108 RArrow,
109 Star,
110 At,
111 Slash,
112 Plus,
113 Minus,
114 StringLiteral,
115
116 Use,
117 Type,
118 Func,
119 U8,
120 U16,
121 U32,
122 U64,
123 S8,
124 S16,
125 S32,
126 S64,
127 F32,
128 F64,
129 Char,
130 Record,
131 Resource,
132 Own,
133 Borrow,
134 Flags,
135 Variant,
136 Enum,
137 Bool,
138 String_,
139 Option_,
140 Result_,
141 Future,
142 Stream,
143 ErrorContext,
144 List,
145 Map,
146 Underscore,
147 As,
148 From_,
149 Static,
150 Interface,
151 Tuple,
152 Import,
153 Export,
154 World,
155 Package,
156 Constructor,
157 Async,
158
159 Id,
160 ExplicitId,
161
162 Integer,
163
164 Include,
165 With,
166}
167
168#[derive(Eq, PartialEq, Debug)]
169#[allow(dead_code)]
170pub enum Error {
171 ControlCodepoint(u32, char),
172 DeprecatedCodepoint(u32, char),
173 ForbiddenCodepoint(u32, char),
174 InvalidCharInId(u32, char),
175 IdPartEmpty(u32),
176 Unexpected(u32, char),
177 UnterminatedComment(u32),
178 Wanted {
179 at: u32,
180 expected: &'static str,
181 found: &'static str,
182 },
183 InvalidUnicodeValue(u32, u32),
184 InvalidStringElement(u32, char),
185 InvalidStringEscape(u32, char),
186 WantedChar(u32, char),
187 UnexpectedEof(u32),
188 InvalidUtf8(u32, core::str::Utf8Error),
189 NumberTooBig(u32),
190 LoneUnderscore(u32),
191 InvalidHexDigit(u32, char),
192}
193
194impl<'a> Tokenizer<'a> {
195 pub fn new(input: &'a str, span_offset: u32) -> Result<Tokenizer<'a>, Error> {
196 detect_invalid_input(input)?;
197
198 let mut t = Tokenizer {
199 input,
200 span_offset,
201 chars: CrlfFold {
202 chars: input.char_indices(),
203 },
204 };
205 t.eatc('\u{feff}');
207 Ok(t)
208 }
209
210 pub fn expect_semicolon(&mut self) -> Result<(), Error> {
211 self.expect(Token::Semicolon)?;
212 Ok(())
213 }
214
215 pub fn get_span(&self, span: Span) -> &'a str {
216 let start = usize::try_from(span.start() - self.span_offset).unwrap();
217 let end = usize::try_from(span.end() - self.span_offset).unwrap();
218 &self.input[start..end]
219 }
220
221 pub fn parse_id(&self, span: Span) -> Result<&'a str, Error> {
222 let ret = self.get_span(span);
223 validate_id(span.start(), &ret)?;
224 Ok(ret)
225 }
226
227 pub fn parse_explicit_id(&self, span: Span) -> Result<&'a str, Error> {
228 let token = self.get_span(span);
229 let id_part = token.strip_prefix('%').unwrap();
230 validate_id(span.start(), id_part)?;
231 Ok(id_part)
232 }
233
234 pub fn next(&mut self) -> Result<Option<(Span, Token)>, Error> {
235 loop {
236 match self.next_raw()? {
237 Some((_, Token::Whitespace)) | Some((_, Token::Comment)) => {}
238 other => break Ok(other),
239 }
240 }
241 }
242
243 pub fn next_raw(&mut self) -> Result<Option<(Span, Token)>, Error> {
247 let (str_start, ch) = match self.chars.next() {
248 Some(pair) => pair,
249 None => return Ok(None),
250 };
251 let start = self.span_offset + u32::try_from(str_start).unwrap();
252 let token = match ch {
253 '\n' | '\t' | ' ' => {
254 while self.eatc(' ') || self.eatc('\t') || self.eatc('\n') {}
256 Whitespace
257 }
258 '/' => {
259 if self.eatc('/') {
261 for (_, ch) in &mut self.chars {
262 if ch == '\n' {
263 break;
264 }
265 }
266 Comment
267 } else if self.eatc('*') {
269 let mut depth = 1;
270 while depth > 0 {
271 let (_, ch) = match self.chars.next() {
272 Some(pair) => pair,
273 None => return Err(Error::UnterminatedComment(start)),
274 };
275 match ch {
276 '/' if self.eatc('*') => depth += 1,
277 '*' if self.eatc('/') => depth -= 1,
278 _ => {}
279 }
280 }
281 Comment
282 } else {
283 Slash
284 }
285 }
286 '=' => Equals,
287 ',' => Comma,
288 ':' => Colon,
289 '.' => Period,
290 ';' => Semicolon,
291 '(' => LeftParen,
292 ')' => RightParen,
293 '{' => LeftBrace,
294 '}' => RightBrace,
295 '<' => LessThan,
296 '>' => GreaterThan,
297 '*' => Star,
298 '@' => At,
299 '-' => {
300 if self.eatc('>') {
301 RArrow
302 } else {
303 Minus
304 }
305 }
306 '+' => Plus,
307 '%' => {
308 let mut iter = self.chars.clone();
309 if let Some((_, ch)) = iter.next() {
310 if is_keylike_start(ch) {
311 self.chars = iter.clone();
312 while let Some((_, ch)) = iter.next() {
313 if !is_keylike_continue(ch) {
314 break;
315 }
316 self.chars = iter.clone();
317 }
318 }
319 }
320 ExplicitId
321 }
322 '"' => {
323 self.expect_string_literal(start)?;
324 StringLiteral
325 }
326 ch if is_keylike_start(ch) => {
327 let remaining = self.chars.chars.as_str().len();
328 let mut iter = self.chars.clone();
329 while let Some((_, ch)) = iter.next() {
330 if !is_keylike_continue(ch) {
331 break;
332 }
333 self.chars = iter.clone();
334 }
335 let str_end =
336 str_start + ch.len_utf8() + (remaining - self.chars.chars.as_str().len());
337 match &self.input[str_start..str_end] {
338 "use" => Use,
339 "type" => Type,
340 "func" => Func,
341 "u8" => U8,
342 "u16" => U16,
343 "u32" => U32,
344 "u64" => U64,
345 "s8" => S8,
346 "s16" => S16,
347 "s32" => S32,
348 "s64" => S64,
349 "f32" => F32,
350 "f64" => F64,
351 "char" => Char,
352 "resource" => Resource,
353 "own" => Own,
354 "borrow" => Borrow,
355 "record" => Record,
356 "flags" => Flags,
357 "variant" => Variant,
358 "enum" => Enum,
359 "bool" => Bool,
360 "string" => String_,
361 "option" => Option_,
362 "result" => Result_,
363 "future" => Future,
364 "stream" => Stream,
365 "error-context" => ErrorContext,
366 "list" => List,
367 "map" => Map,
368 "_" => Underscore,
369 "as" => As,
370 "from" => From_,
371 "static" => Static,
372 "interface" => Interface,
373 "tuple" => Tuple,
374 "world" => World,
375 "import" => Import,
376 "export" => Export,
377 "package" => Package,
378 "constructor" => Constructor,
379 "include" => Include,
380 "with" => With,
381 "async" => Async,
382 _ => Id,
383 }
384 }
385
386 ch if ch.is_ascii_digit() => {
387 let mut iter = self.chars.clone();
388 while let Some((_, ch)) = iter.next() {
389 if !ch.is_ascii_digit() {
390 break;
391 }
392 self.chars = iter.clone();
393 }
394
395 Integer
396 }
397
398 ch => return Err(Error::Unexpected(start, ch)),
399 };
400 let end = match self.chars.clone().next() {
401 Some((i, _)) => i,
402 None => self.input.len(),
403 };
404
405 let end = self.span_offset + u32::try_from(end).unwrap();
406 Ok(Some((Span::new(start, end), token)))
407 }
408
409 pub fn eat(&mut self, expected: Token) -> Result<bool, Error> {
410 let mut other = self.clone();
411 match other.next()? {
412 Some((_span, found)) if expected == found => {
413 *self = other;
414 Ok(true)
415 }
416 Some(_) => Ok(false),
417 None => Ok(false),
418 }
419 }
420
421 pub fn expect(&mut self, expected: Token) -> Result<Span, Error> {
422 match self.next()? {
423 Some((span, found)) => {
424 if expected == found {
425 Ok(span)
426 } else {
427 Err(Error::Wanted {
428 at: span.start(),
429 expected: expected.describe(),
430 found: found.describe(),
431 })
432 }
433 }
434 None => Err(Error::Wanted {
435 at: self.span_offset + u32::try_from(self.input.len()).unwrap(),
436 expected: expected.describe(),
437 found: "eof",
438 }),
439 }
440 }
441
442 fn eatc(&mut self, ch: char) -> bool {
443 let mut iter = self.chars.clone();
444 match iter.next() {
445 Some((_, ch2)) if ch == ch2 => {
446 self.chars = iter;
447 true
448 }
449 _ => false,
450 }
451 }
452
453 fn expect_any_char(&mut self) -> Result<(u32, char), Error> {
454 let end = self.eof_span().end;
455 let (pos, c) = self.chars.next().ok_or(Error::UnexpectedEof(end))?;
456 let pos = u32::try_from(pos).unwrap();
457 Ok((pos, c))
458 }
459
460 fn expect_char(&mut self, expected: char) -> Result<(), Error> {
461 let (pos, actual) = self.expect_any_char()?;
462 if actual == expected {
463 Ok(())
464 } else {
465 Err(Error::WantedChar(pos, expected))
466 }
467 }
468
469 pub fn string_literal(&mut self, span: Span) -> Result<String, Error> {
470 let input = self.get_span(span);
471 let input = &input[1..];
472 Tokenizer::new(input, span.start + 1)?.expect_string_literal(span.start + 1)
473 }
474
475 fn expect_string_literal(&mut self, start: u32) -> Result<String, Error> {
476 let mut buf = Vec::new();
477 loop {
478 let (pos, c) = self.expect_any_char()?;
479 match c {
480 '"' => break,
481 '\\' => {
482 let (pos, c) = self.expect_any_char()?;
483 match c {
484 '"' => buf.push(b'"'),
485 '\'' => buf.push(b'\''),
486 't' => buf.push(b'\t'),
487 'n' => buf.push(b'\n'),
488 'r' => buf.push(b'\r'),
489 '\\' => buf.push(b'\\'),
490 'u' => {
491 self.expect_char('{')?;
492 let n = self.eat_hexnum()?;
493 let c = char::from_u32(n).ok_or(Error::InvalidUnicodeValue(pos, n))?;
494 buf.extend(c.encode_utf8(&mut [0; 4]).as_bytes());
495 self.expect_char('}')?;
496 }
497 c1 if c1.is_ascii_hexdigit() => {
498 let (_, c2) = self.eat_hexdigit()?;
499 buf.push(to_hex(c1) * 16 + c2);
500 }
501 c => return Err(Error::InvalidStringEscape(pos, c)),
502 }
503 }
504 c if (c as u32) < 0x20 || c as u32 == 0x7f => {
505 return Err(Error::InvalidStringElement(pos, c));
506 }
507 c => buf.extend(c.encode_utf8(&mut [0; 4]).as_bytes()),
508 }
509 }
510 match String::from_utf8(buf) {
511 Ok(s) => Ok(s),
512 Err(e) => Err(Error::InvalidUtf8(start, e.utf8_error())),
513 }
514 }
515
516 pub fn eof_span(&self) -> Span {
517 let end = self.span_offset + u32::try_from(self.input.len()).unwrap();
518 Span::new(end, end)
519 }
520
521 fn eat_hexnum(&mut self) -> Result<u32, Error> {
522 let (pos, n) = self.eat_hexdigit()?;
523 let mut last_underscore = false;
524 let mut n = n as u32;
525 loop {
526 if self.eatc('_') {
527 last_underscore = true;
528 continue;
529 }
530 let (pos, c) = self.clone().expect_any_char()?;
531 if !c.is_ascii_hexdigit() {
532 break;
533 }
534 last_underscore = false;
535 self.chars.next();
536 n = n
537 .checked_mul(16)
538 .and_then(|n| n.checked_add(to_hex(c) as u32))
539 .ok_or(Error::NumberTooBig(pos))?;
540 }
541 if last_underscore {
542 return Err(Error::LoneUnderscore(pos));
543 }
544 Ok(n)
545 }
546
547 fn eat_hexdigit(&mut self) -> Result<(u32, u8), Error> {
551 let (pos, ch) = self.expect_any_char()?;
552 if ch.is_ascii_hexdigit() {
553 Ok((pos, to_hex(ch)))
554 } else {
555 Err(Error::InvalidHexDigit(pos, ch))
556 }
557 }
558}
559
560fn to_hex(c: char) -> u8 {
561 match c {
562 'a'..='f' => c as u8 - b'a' + 10,
563 'A'..='F' => c as u8 - b'A' + 10,
564 _ => c as u8 - b'0',
565 }
566}
567
568impl<'a> Iterator for CrlfFold<'a> {
569 type Item = (usize, char);
570
571 fn next(&mut self) -> Option<(usize, char)> {
572 self.chars.next().map(|(i, c)| {
573 if c == '\r' {
574 let mut attempt = self.chars.clone();
575 if let Some((_, '\n')) = attempt.next() {
576 self.chars = attempt;
577 return (i, '\n');
578 }
579 }
580 (i, c)
581 })
582 }
583}
584
585fn detect_invalid_input(input: &str) -> Result<(), Error> {
586 for (pos, ch) in input.char_indices() {
588 match ch {
589 '\n' | '\r' | '\t' => {}
590
591 '\u{202a}' | '\u{202b}' | '\u{202c}' | '\u{202d}' | '\u{202e}' | '\u{2066}'
597 | '\u{2067}' | '\u{2068}' | '\u{2069}' => {
598 return Err(Error::ForbiddenCodepoint(u32::try_from(pos).unwrap(), ch));
599 }
600
601 '\u{149}' | '\u{673}' | '\u{f77}' | '\u{f79}' | '\u{17a3}' | '\u{17a4}'
609 | '\u{17b4}' | '\u{17b5}' => {
610 return Err(Error::DeprecatedCodepoint(u32::try_from(pos).unwrap(), ch));
611 }
612
613 ch if ch.is_control() => {
617 return Err(Error::ControlCodepoint(u32::try_from(pos).unwrap(), ch));
618 }
619
620 _ => {}
621 }
622 }
623
624 Ok(())
625}
626
627fn is_keylike_start(ch: char) -> bool {
628 unicode_ident::is_xid_start(ch) || ch == '_' || ch == '-'
631}
632
633fn is_keylike_continue(ch: char) -> bool {
634 unicode_ident::is_xid_continue(ch) || ch == '-'
636}
637
638pub fn validate_id(start: u32, id: &str) -> Result<(), Error> {
639 if id.is_empty() {
641 return Err(Error::IdPartEmpty(start));
642 }
643
644 for (idx, part) in id.split('-').enumerate() {
646 let Some(first_char) = part.chars().next() else {
649 return Err(Error::IdPartEmpty(start));
650 };
651 if idx == 0 && !first_char.is_ascii_alphabetic() {
652 return Err(Error::InvalidCharInId(start, first_char));
653 }
654 let mut upper = None;
655 for ch in part.chars() {
656 if ch.is_ascii_digit() {
657 } else if ch.is_ascii_uppercase() {
659 if upper.is_none() {
660 upper = Some(true);
661 } else if let Some(false) = upper {
662 return Err(Error::InvalidCharInId(start, ch));
663 }
664 } else if ch.is_ascii_lowercase() {
665 if upper.is_none() {
666 upper = Some(false);
667 } else if let Some(true) = upper {
668 return Err(Error::InvalidCharInId(start, ch));
669 }
670 } else {
671 return Err(Error::InvalidCharInId(start, ch));
672 }
673 }
674 }
675
676 Ok(())
677}
678
679impl Token {
680 pub fn describe(&self) -> &'static str {
681 match self {
682 Whitespace => "whitespace",
683 Comment => "a comment",
684 Equals => "'='",
685 Comma => "','",
686 Colon => "':'",
687 Period => "'.'",
688 Semicolon => "';'",
689 LeftParen => "'('",
690 RightParen => "')'",
691 LeftBrace => "'{'",
692 RightBrace => "'}'",
693 LessThan => "'<'",
694 GreaterThan => "'>'",
695 Use => "keyword `use`",
696 Type => "keyword `type`",
697 Func => "keyword `func`",
698 U8 => "keyword `u8`",
699 U16 => "keyword `u16`",
700 U32 => "keyword `u32`",
701 U64 => "keyword `u64`",
702 S8 => "keyword `s8`",
703 S16 => "keyword `s16`",
704 S32 => "keyword `s32`",
705 S64 => "keyword `s64`",
706 F32 => "keyword `f32`",
707 F64 => "keyword `f64`",
708 Char => "keyword `char`",
709 Own => "keyword `own`",
710 Borrow => "keyword `borrow`",
711 Resource => "keyword `resource`",
712 Record => "keyword `record`",
713 Flags => "keyword `flags`",
714 Variant => "keyword `variant`",
715 Enum => "keyword `enum`",
716 Bool => "keyword `bool`",
717 String_ => "keyword `string`",
718 Option_ => "keyword `option`",
719 Result_ => "keyword `result`",
720 Future => "keyword `future`",
721 Stream => "keyword `stream`",
722 ErrorContext => "keyword `error-context`",
723 List => "keyword `list`",
724 Map => "keyword `map`",
725 Underscore => "keyword `_`",
726 Id => "an identifier",
727 ExplicitId => "an '%' identifier",
728 RArrow => "`->`",
729 Star => "`*`",
730 At => "`@`",
731 Slash => "`/`",
732 Plus => "`+`",
733 Minus => "`-`",
734 As => "keyword `as`",
735 From_ => "keyword `from`",
736 Static => "keyword `static`",
737 Interface => "keyword `interface`",
738 Tuple => "keyword `tuple`",
739 Import => "keyword `import`",
740 Export => "keyword `export`",
741 World => "keyword `world`",
742 Package => "keyword `package`",
743 Constructor => "keyword `constructor`",
744 Integer => "an integer",
745 Include => "keyword `include`",
746 With => "keyword `with`",
747 Async => "keyword `async`",
748 StringLiteral => "a string literal",
749 }
750 }
751}
752
753impl core::error::Error for Error {}
754
755impl Error {
756 pub fn position(&self) -> u32 {
758 match self {
759 Error::ControlCodepoint(at, _)
760 | Error::DeprecatedCodepoint(at, _)
761 | Error::ForbiddenCodepoint(at, _)
762 | Error::InvalidCharInId(at, _)
763 | Error::IdPartEmpty(at)
764 | Error::Unexpected(at, _)
765 | Error::UnterminatedComment(at)
766 | Error::InvalidUnicodeValue(at, _)
767 | Error::InvalidStringElement(at, _)
768 | Error::InvalidStringEscape(at, _)
769 | Error::WantedChar(at, _)
770 | Error::UnexpectedEof(at)
771 | Error::InvalidUtf8(at, _)
772 | Error::NumberTooBig(at)
773 | Error::LoneUnderscore(at)
774 | Error::InvalidHexDigit(at, _)
775 | Error::Wanted { at, .. } => *at,
776 }
777 }
778
779 pub(crate) fn adjust_position(&mut self, offset: u32) {
780 match self {
781 Error::ControlCodepoint(at, _)
782 | Error::DeprecatedCodepoint(at, _)
783 | Error::ForbiddenCodepoint(at, _)
784 | Error::InvalidCharInId(at, _)
785 | Error::IdPartEmpty(at)
786 | Error::Unexpected(at, _)
787 | Error::UnterminatedComment(at)
788 | Error::InvalidUnicodeValue(at, _)
789 | Error::InvalidStringElement(at, _)
790 | Error::InvalidStringEscape(at, _)
791 | Error::WantedChar(at, _)
792 | Error::UnexpectedEof(at)
793 | Error::InvalidUtf8(at, _)
794 | Error::NumberTooBig(at)
795 | Error::LoneUnderscore(at)
796 | Error::InvalidHexDigit(at, _)
797 | Error::Wanted { at, .. } => *at += offset,
798 }
799 }
800}
801
802impl fmt::Display for Error {
803 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
804 match self {
805 Error::ControlCodepoint(_, ch) => write!(f, "Control code '{}'", ch.escape_unicode()),
806 Error::DeprecatedCodepoint(_, ch) => {
807 write!(
808 f,
809 "Codepoint {:?} is discouraged by Unicode",
810 ch.escape_unicode()
811 )
812 }
813 Error::ForbiddenCodepoint(_, ch) => {
814 write!(
815 f,
816 "Input contains bidirectional override codepoint {:?}",
817 ch.escape_unicode()
818 )
819 }
820 Error::Unexpected(_, ch) => write!(f, "unexpected character {ch:?}"),
821 Error::UnterminatedComment(_) => write!(f, "unterminated block comment"),
822 Error::Wanted {
823 expected, found, ..
824 } => write!(f, "expected {expected}, found {found}"),
825 Error::InvalidCharInId(_, ch) => write!(f, "invalid character in identifier {ch:?}"),
826 Error::IdPartEmpty(_) => write!(f, "identifiers must have characters between '-'s"),
827 Error::InvalidUnicodeValue(_, val) => write!(f, "invalid unicode value {val:#x}"),
828 Error::InvalidStringElement(_, c) => write!(f, "invalid string character {c:?}"),
829 Error::InvalidStringEscape(_, c) => write!(f, "invalid string escape {c:?}"),
830 Error::WantedChar(_, c) => write!(f, "expected character {c:?}"),
831 Error::UnexpectedEof(_) => write!(f, "unexpected end of file"),
832 Error::InvalidUtf8(_, err) => write!(f, "invalid UTF-8: {err}"),
833 Error::NumberTooBig(_) => write!(f, "number is too big to fit in a u32"),
834 Error::LoneUnderscore(_) => write!(f, "trailing underscore in number"),
835 Error::InvalidHexDigit(_, c) => write!(f, "invalid hex digit {c:?}"),
836 }
837 }
838}
839
840#[test]
841fn test_validate_id() {
842 validate_id(0, "apple").unwrap();
843 validate_id(0, "apple-pear").unwrap();
844 validate_id(0, "apple-pear-grape").unwrap();
845 validate_id(0, "a0").unwrap();
846 validate_id(0, "a").unwrap();
847 validate_id(0, "a-a").unwrap();
848 validate_id(0, "bool").unwrap();
849 validate_id(0, "APPLE").unwrap();
850 validate_id(0, "APPLE-PEAR").unwrap();
851 validate_id(0, "APPLE-PEAR-GRAPE").unwrap();
852 validate_id(0, "apple-PEAR-grape").unwrap();
853 validate_id(0, "APPLE-pear-GRAPE").unwrap();
854 validate_id(0, "ENOENT").unwrap();
855 validate_id(0, "is-XML").unwrap();
856 validate_id(0, "apple-0").unwrap();
857 validate_id(0, "a0-000-3d4a-54FF").unwrap();
858
859 assert!(validate_id(0, "").is_err());
860 assert!(validate_id(0, "0").is_err());
861 assert!(validate_id(0, "%").is_err());
862 assert!(validate_id(0, "$").is_err());
863 assert!(validate_id(0, "0a").is_err());
864 assert!(validate_id(0, ".").is_err());
865 assert!(validate_id(0, "·").is_err());
866 assert!(validate_id(0, "a a").is_err());
867 assert!(validate_id(0, "_").is_err());
868 assert!(validate_id(0, "-").is_err());
869 assert!(validate_id(0, "a-").is_err());
870 assert!(validate_id(0, "-a").is_err());
871 assert!(validate_id(0, "Apple").is_err());
872 assert!(validate_id(0, "applE").is_err());
873 assert!(validate_id(0, "-apple-pear").is_err());
874 assert!(validate_id(0, "apple-pear-").is_err());
875 assert!(validate_id(0, "apple_pear").is_err());
876 assert!(validate_id(0, "apple.pear").is_err());
877 assert!(validate_id(0, "apple pear").is_err());
878 assert!(validate_id(0, "apple/pear").is_err());
879 assert!(validate_id(0, "apple|pear").is_err());
880 assert!(validate_id(0, "apple-Pear").is_err());
881 assert!(validate_id(0, "()()").is_err());
882 assert!(validate_id(0, "").is_err());
883 assert!(validate_id(0, "*").is_err());
884 assert!(validate_id(0, "apple\u{5f3}pear").is_err());
885 assert!(validate_id(0, "apple\u{200c}pear").is_err());
886 assert!(validate_id(0, "apple\u{200d}pear").is_err());
887 assert!(validate_id(0, "apple--pear").is_err());
888 assert!(validate_id(0, "_apple").is_err());
889 assert!(validate_id(0, "apple_").is_err());
890 assert!(validate_id(0, "_Znwj").is_err());
891 assert!(validate_id(0, "__i386").is_err());
892 assert!(validate_id(0, "__i386__").is_err());
893 assert!(validate_id(0, "Москва").is_err());
894 assert!(validate_id(0, "garçon-hühnervögel-Москва-東京").is_err());
895 assert!(validate_id(0, "a0-000-3d4A-54Ff").is_err());
896 assert!(validate_id(0, "😼").is_err(), "non-identifier");
897 assert!(validate_id(0, "\u{212b}").is_err(), "non-ascii");
898}
899
900#[test]
901fn test_tokenizer() {
902 fn collect(s: &str) -> Result<Vec<Token>, Error> {
903 let mut t = Tokenizer::new(s, 0)?;
904 let mut tokens = Vec::new();
905 while let Some(token) = t.next()? {
906 tokens.push(token.1);
907 }
908 Ok(tokens)
909 }
910
911 assert_eq!(collect("").unwrap(), vec![]);
912 assert_eq!(collect("_").unwrap(), vec![Token::Underscore]);
913 assert_eq!(collect("apple").unwrap(), vec![Token::Id]);
914 assert_eq!(collect("apple-pear").unwrap(), vec![Token::Id]);
915 assert_eq!(collect("apple--pear").unwrap(), vec![Token::Id]);
916 assert_eq!(collect("apple-Pear").unwrap(), vec![Token::Id]);
917 assert_eq!(collect("apple-pear-grape").unwrap(), vec![Token::Id]);
918 assert_eq!(collect("apple pear").unwrap(), vec![Token::Id, Token::Id]);
919 assert_eq!(collect("_a_p_p_l_e_").unwrap(), vec![Token::Id]);
920 assert_eq!(collect("garçon").unwrap(), vec![Token::Id]);
921 assert_eq!(collect("hühnervögel").unwrap(), vec![Token::Id]);
922 assert_eq!(collect("москва").unwrap(), vec![Token::Id]);
923 assert_eq!(collect("東京").unwrap(), vec![Token::Id]);
924 assert_eq!(
925 collect("garçon-hühnervögel-москва-東京").unwrap(),
926 vec![Token::Id]
927 );
928 assert_eq!(collect("a0").unwrap(), vec![Token::Id]);
929 assert_eq!(collect("a").unwrap(), vec![Token::Id]);
930 assert_eq!(collect("%a").unwrap(), vec![Token::ExplicitId]);
931 assert_eq!(collect("%a-a").unwrap(), vec![Token::ExplicitId]);
932 assert_eq!(collect("%bool").unwrap(), vec![Token::ExplicitId]);
933 assert_eq!(collect("%").unwrap(), vec![Token::ExplicitId]);
934 assert_eq!(collect("APPLE").unwrap(), vec![Token::Id]);
935 assert_eq!(collect("APPLE-PEAR").unwrap(), vec![Token::Id]);
936 assert_eq!(collect("APPLE-PEAR-GRAPE").unwrap(), vec![Token::Id]);
937 assert_eq!(collect("apple-PEAR-grape").unwrap(), vec![Token::Id]);
938 assert_eq!(collect("APPLE-pear-GRAPE").unwrap(), vec![Token::Id]);
939 assert_eq!(collect("ENOENT").unwrap(), vec![Token::Id]);
940 assert_eq!(collect("is-XML").unwrap(), vec![Token::Id]);
941
942 assert_eq!(collect("func").unwrap(), vec![Token::Func]);
943 assert_eq!(
944 collect("a: func()").unwrap(),
945 vec![
946 Token::Id,
947 Token::Colon,
948 Token::Func,
949 Token::LeftParen,
950 Token::RightParen
951 ]
952 );
953
954 assert_eq!(collect("resource").unwrap(), vec![Token::Resource]);
955
956 assert_eq!(collect("own").unwrap(), vec![Token::Own]);
957 assert_eq!(
958 collect("own<some-id>").unwrap(),
959 vec![Token::Own, Token::LessThan, Token::Id, Token::GreaterThan]
960 );
961
962 assert_eq!(collect("borrow").unwrap(), vec![Token::Borrow]);
963 assert_eq!(
964 collect("borrow<some-id>").unwrap(),
965 vec![
966 Token::Borrow,
967 Token::LessThan,
968 Token::Id,
969 Token::GreaterThan
970 ]
971 );
972
973 assert!(collect("\u{149}").is_err(), "strongly discouraged");
974 assert!(collect("\u{673}").is_err(), "strongly discouraged");
975 assert!(collect("\u{17a3}").is_err(), "strongly discouraged");
976 assert!(collect("\u{17a4}").is_err(), "strongly discouraged");
977 assert!(collect("\u{202a}").is_err(), "bidirectional override");
978 assert!(collect("\u{2068}").is_err(), "bidirectional override");
979 assert!(collect("\u{0}").is_err(), "control code");
980 assert!(collect("\u{b}").is_err(), "control code");
981 assert!(collect("\u{c}").is_err(), "control code");
982 assert!(collect("\u{85}").is_err(), "control code");
983}
984
985#[test]
986fn test_strings() {
987 #[track_caller]
988 fn test(s: &str, expected: Result<&str, Error>) {
989 let actual = (|| {
990 let mut t = Tokenizer::new(s, 0)?;
991 let next = t.next()?;
992 assert!(
993 matches!(next, Some((_, Token::StringLiteral))),
994 "{s:?} didn't tokenize as a string"
995 );
996 assert!(t.next()?.is_none(), "extra tokens after string: {s:?}");
997 let (span, _) = next.unwrap();
998 t.string_literal(span)
999 })();
1000 match (&actual, &expected) {
1001 (Ok(actual), Ok(expected)) => assert_eq!(actual, expected),
1002 (Err(actual), Err(expected)) => assert_eq!(actual, expected),
1003 (Ok(_) | Err(_), _) => panic!("expected error {expected:?}, but got Ok({actual:?})"),
1004 }
1005 }
1006
1007 test("\"\"", Ok(""));
1009 test("\"a\"", Ok("a"));
1010 test("\"a b c\"", Ok("a b c"));
1011
1012 test("\"\\\"\"", Ok("\""));
1014 test("\"\\t\"", Ok("\t"));
1015 test("\"\\n\"", Ok("\n"));
1016 test("\"\\r\"", Ok("\r"));
1017 test("\"\\\\\"", Ok("\\"));
1018 test("\"\\h\"", Err(Error::InvalidStringEscape(2, 'h')));
1019
1020 test("\"\\00\"", Ok("\0"));
1022 test("\"\\01\"", Ok("\x01"));
1023 test("\"\\0f\"", Ok("\x0f"));
1024 test("\"\\0_1\"", Err(Error::InvalidHexDigit(3, '_')));
1025 test("\"\\0g\"", Err(Error::InvalidHexDigit(3, 'g')));
1026 #[allow(invalid_from_utf8)]
1027 test(
1028 "\"\\ff\"",
1029 Err(Error::InvalidUtf8(
1030 0,
1031 core::str::from_utf8(&[0xff]).unwrap_err(),
1032 )),
1033 );
1034
1035 test("\"\\u{0}\"", Ok("\0"));
1037 test("\"\\u{1}\"", Ok("\x01"));
1038 test("\"\\u{1_2_3_f}\"", Ok("\u{123f}"));
1039 test("\"\\u0000\"", Err(Error::WantedChar(3, '{')));
1040 test("\"\\u{0h\"", Err(Error::WantedChar(5, '}')));
1041 test("\"\\u{1_}\"", Err(Error::LoneUnderscore(4)));
1042 test(
1043 "\"\\u{fffffffffffffffffffff}\"",
1044 Err(Error::NumberTooBig(12)),
1045 );
1046 test(
1047 "\"\\u{ffff_ffff}\"",
1048 Err(Error::InvalidUnicodeValue(2, u32::MAX)),
1049 );
1050
1051 test("\"\t\"", Err(Error::InvalidStringElement(1, '\t')));
1052}