1use logos::{Logos, SpannedIter};
4use miette::SourceSpan;
5use std::fmt;
6
7fn to_source_span(span: logos::Span) -> SourceSpan {
8 SourceSpan::new(span.start.into(), span.end - span.start)
9}
10
11#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq, Default)]
13pub enum Error {
14 #[default]
16 #[error("unexpected token was encountered")]
17 UnexpectedToken,
18 #[error("an unterminated string was encountered")]
20 UnterminatedString,
21 #[error("an unterminated comment was encountered")]
23 UnterminatedComment,
24 #[error("disallowed bidirectional override codepoint `{c}`", c = .0.escape_unicode())]
26 DisallowedBidirectionalOverride(char),
27 #[error("codepoint `{c}` is discouraged by Unicode", c = .0.escape_unicode())]
29 DiscouragedUnicodeCodepoint(char),
30 #[error("disallowed control code '{c}'", c = .0.escape_unicode())]
32 DisallowedControlCode(char),
33}
34
35impl From<()> for Error {
36 fn from(_: ()) -> Self {
37 Error::UnexpectedToken
38 }
39}
40
41fn detect_invalid_input(source: &str) -> Result<(), (Error, SourceSpan)> {
42 for (offset, ch) in source.char_indices() {
43 match ch {
44 '\r' | '\t' | '\n' => {}
45
46 '\u{202a}' | '\u{202b}' | '\u{202c}' | '\u{202d}' | '\u{202e}' | '\u{2066}'
52 | '\u{2067}' | '\u{2068}' | '\u{2069}' => {
53 return Err((
54 Error::DisallowedBidirectionalOverride(ch),
55 SourceSpan::new(offset.into(), ch.len_utf8()),
56 ));
57 }
58
59 '\u{149}' | '\u{673}' | '\u{f77}' | '\u{f79}' | '\u{17a3}' | '\u{17a4}'
67 | '\u{17b4}' | '\u{17b5}' => {
68 return Err((
69 Error::DiscouragedUnicodeCodepoint(ch),
70 SourceSpan::new(offset.into(), ch.len_utf8()),
71 ));
72 }
73
74 ch if ch.is_control() => {
78 return Err((
79 Error::DisallowedControlCode(ch),
80 SourceSpan::new(offset.into(), ch.len_utf8()),
81 ));
82 }
83
84 _ => {}
85 }
86 }
87
88 Ok(())
89}
90
91#[derive(Logos, Debug, Clone, Copy, PartialEq, Eq)]
93#[logos(error = Error)]
94#[logos(skip r"[ \t\r\n\f]+")]
95#[logos(subpattern word = r"[a-z][a-z0-9]*|[A-Z][A-Z0-9]*")]
96#[logos(subpattern id = r"%?(?&word)(-(?&word))*")]
97#[logos(subpattern package_name = r"(?&id)(:(?&id))+")]
98#[logos(subpattern semver = r"([0-9]+)(\.[0-9a-zA-Z-\+]+)*")]
99pub enum Token {
100 #[regex(r"//[^\n]*", logos::skip)]
102 Comment,
103
104 #[token(r"/*", helpers::skip_block_comment)]
106 BlockComment,
107
108 #[regex(r"(?&id)")]
110 Ident,
111
112 #[token("\"", helpers::string)]
114 String,
115
116 #[regex(r"(?&package_name)(@(?&semver))?")]
118 PackageName,
119
120 #[regex(r"(?&package_name)(/(?&id))+(@(?&semver))?")]
122 PackagePath,
123
124 #[token("import")]
126 ImportKeyword,
127 #[token("with")]
129 WithKeyword,
130 #[token("type")]
132 TypeKeyword,
133 #[token("tuple")]
135 TupleKeyword,
136 #[token("list")]
138 ListKeyword,
139 #[token("option")]
141 OptionKeyword,
142 #[token("result")]
144 ResultKeyword,
145 #[token("borrow")]
147 BorrowKeyword,
148 #[token("resource")]
150 ResourceKeyword,
151 #[token("variant")]
153 VariantKeyword,
154 #[token("record")]
156 RecordKeyword,
157 #[token("flags")]
159 FlagsKeyword,
160 #[token("enum")]
162 EnumKeyword,
163 #[token("func")]
165 FuncKeyword,
166 #[token("static")]
168 StaticKeyword,
169 #[token("constructor")]
171 ConstructorKeyword,
172 #[token("u8")]
174 U8Keyword,
175 #[token("s8")]
177 S8Keyword,
178 #[token("u16")]
180 U16Keyword,
181 #[token("s16")]
183 S16Keyword,
184 #[token("u32")]
186 U32Keyword,
187 #[token("s32")]
189 S32Keyword,
190 #[token("u64")]
192 U64Keyword,
193 #[token("s64")]
195 S64Keyword,
196 #[token("f32")]
198 F32Keyword,
199 #[token("f64")]
201 F64Keyword,
202 #[token("char")]
204 CharKeyword,
205 #[token("bool")]
207 BoolKeyword,
208 #[token("string")]
210 StringKeyword,
211 #[token("interface")]
213 InterfaceKeyword,
214 #[token("world")]
216 WorldKeyword,
217 #[token("export")]
219 ExportKeyword,
220 #[token("new")]
222 NewKeyword,
223 #[token("let")]
225 LetKeyword,
226 #[token("use")]
228 UseKeyword,
229 #[token("include")]
231 IncludeKeyword,
232 #[token("as")]
234 AsKeyword,
235 #[token("package")]
237 PackageKeyword,
238 #[token("targets")]
240 TargetsKeyword,
241 #[token("async")]
243 AsyncKeyword,
244 #[token("stream")]
246 StreamKeyword,
247 #[token("future")]
249 FutureKeyword,
250 #[token("error-context")]
252 ErrorContextKeyword,
253
254 #[token(";")]
256 Semicolon,
257 #[token("{")]
259 OpenBrace,
260 #[token("}")]
262 CloseBrace,
263 #[token(":")]
265 Colon,
266 #[token("=")]
268 Equals,
269 #[token("(")]
271 OpenParen,
272 #[token(")")]
274 CloseParen,
275 #[token("->")]
277 Arrow,
278 #[token("<")]
280 OpenAngle,
281 #[token(">")]
283 CloseAngle,
284 #[token("_")]
286 Underscore,
287 #[token("[")]
289 OpenBracket,
290 #[token("]")]
292 CloseBracket,
293 #[token(".")]
295 Dot,
296 #[token("...")]
298 Ellipsis,
299 #[token(",")]
301 Comma,
302 #[token("/")]
304 Slash,
305 #[token("@")]
307 At,
308}
309
310impl fmt::Display for Token {
311 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312 match self {
313 Token::Comment | Token::BlockComment => write!(f, "comment"),
314 Token::Ident => write!(f, "identifier"),
315 Token::String => write!(f, "string literal"),
316 Token::PackageName => write!(f, "package name"),
317 Token::PackagePath => write!(f, "package path"),
318 Token::ImportKeyword => write!(f, "`import` keyword"),
319 Token::WithKeyword => write!(f, "`with` keyword"),
320 Token::TypeKeyword => write!(f, "`type` keyword"),
321 Token::TupleKeyword => write!(f, "`tuple` keyword"),
322 Token::ListKeyword => write!(f, "`list` keyword"),
323 Token::OptionKeyword => write!(f, "`option` keyword"),
324 Token::ResultKeyword => write!(f, "`result` keyword"),
325 Token::BorrowKeyword => write!(f, "`borrow` keyword"),
326 Token::ResourceKeyword => write!(f, "`resource` keyword"),
327 Token::VariantKeyword => write!(f, "`variant` keyword"),
328 Token::RecordKeyword => write!(f, "`record` keyword"),
329 Token::FlagsKeyword => write!(f, "`flags` keyword"),
330 Token::EnumKeyword => write!(f, "`enum` keyword"),
331 Token::FuncKeyword => write!(f, "`func` keyword"),
332 Token::StaticKeyword => write!(f, "`static` keyword"),
333 Token::ConstructorKeyword => write!(f, "`constructor` keyword"),
334 Token::U8Keyword => write!(f, "`u8` keyword"),
335 Token::S8Keyword => write!(f, "`s8` keyword"),
336 Token::U16Keyword => write!(f, "`u16` keyword"),
337 Token::S16Keyword => write!(f, "`s16` keyword"),
338 Token::U32Keyword => write!(f, "`u32` keyword"),
339 Token::S32Keyword => write!(f, "`s32` keyword"),
340 Token::U64Keyword => write!(f, "`u64` keyword"),
341 Token::S64Keyword => write!(f, "`s64` keyword"),
342 Token::F32Keyword => write!(f, "`f32` keyword"),
343 Token::F64Keyword => write!(f, "`f64` keyword"),
344 Token::CharKeyword => write!(f, "`char` keyword"),
345 Token::BoolKeyword => write!(f, "`bool` keyword"),
346 Token::StringKeyword => write!(f, "`string` keyword"),
347 Token::InterfaceKeyword => write!(f, "`interface` keyword"),
348 Token::WorldKeyword => write!(f, "`world` keyword"),
349 Token::ExportKeyword => write!(f, "`export` keyword"),
350 Token::NewKeyword => write!(f, "`new` keyword"),
351 Token::LetKeyword => write!(f, "`let` keyword"),
352 Token::UseKeyword => write!(f, "`use` keyword"),
353 Token::IncludeKeyword => write!(f, "`include` keyword"),
354 Token::AsKeyword => write!(f, "`as` keyword"),
355 Token::PackageKeyword => write!(f, "`package` keyword"),
356 Token::TargetsKeyword => write!(f, "`targets` keyword"),
357 Token::AsyncKeyword => write!(f, "`async` keyword"),
358 Token::StreamKeyword => write!(f, "`stream` keyword"),
359 Token::FutureKeyword => write!(f, "`future` keyword"),
360 Token::ErrorContextKeyword => write!(f, "`error-context` keyword"),
361 Token::Semicolon => write!(f, "`;`"),
362 Token::OpenBrace => write!(f, "`{{`"),
363 Token::CloseBrace => write!(f, "`}}`"),
364 Token::Colon => write!(f, "`:`"),
365 Token::Equals => write!(f, "`=`"),
366 Token::OpenParen => write!(f, "`(`"),
367 Token::CloseParen => write!(f, "`)`"),
368 Token::Arrow => write!(f, "`->`"),
369 Token::OpenAngle => write!(f, "`<`"),
370 Token::CloseAngle => write!(f, "`>`"),
371 Token::Underscore => write!(f, "`_`"),
372 Token::OpenBracket => write!(f, "`[`"),
373 Token::CloseBracket => write!(f, "`]`"),
374 Token::Dot => write!(f, "`.`"),
375 Token::Ellipsis => write!(f, "`...`"),
376 Token::Comma => write!(f, "`,`"),
377 Token::Slash => write!(f, "`/`"),
378 Token::At => write!(f, "`@`"),
379 }
380 }
381}
382
383mod helpers {
384 use super::{Error, Token};
385 use logos::{FilterResult, Logos};
386
387 #[derive(Logos, Debug, Clone, Copy, PartialEq, Eq)]
389 #[logos(error = Error)]
390 #[logos(skip r"[ \t\n\f]+")]
391 pub enum CommentToken<'a> {
392 #[regex(r"//[^\n]*")]
394 Comment(&'a str),
395
396 #[token(r"/*", block_comment)]
398 BlockComment(&'a str),
399 }
400
401 pub fn string(lex: &mut logos::Lexer<Token>) -> Result<(), Error> {
402 let remainder = lex.remainder();
403 let len = remainder.find('"').ok_or(Error::UnterminatedString)?;
404 lex.bump(len + 1 );
405 Ok(())
406 }
407
408 pub fn block_comment_length(bytes: &[u8]) -> Option<usize> {
409 let mut iter = bytes.iter().copied().peekable();
410 let mut depth = 1;
411 let mut len = 0;
412 while depth > 0 {
413 len += 1;
414 match iter.next()? {
415 b'/' if iter.peek() == Some(&b'*') => {
416 depth += 1;
417 len += 1;
418 iter.next();
419 }
420 b'*' if iter.peek() == Some(&b'/') => {
421 depth -= 1;
422 len += 1;
423 iter.next();
424 }
425 _ => {}
426 }
427 }
428
429 Some(len + 2 )
430 }
431
432 pub fn block_comment<'a>(
433 lex: &mut logos::Lexer<'a, CommentToken<'a>>,
434 ) -> Result<&'a str, Error> {
435 let span = lex.span();
436 match block_comment_length(lex.remainder().as_bytes()) {
437 Some(len) => {
438 let s = &lex.source()[span.start..span.start + len];
439 lex.bump(len - 2 );
440 Ok(s)
441 }
442 None => {
443 lex.bump(lex.remainder().len());
444 Err(Error::UnterminatedComment)
445 }
446 }
447 }
448
449 pub fn skip_block_comment(lex: &mut logos::Lexer<Token>) -> FilterResult<(), Error> {
450 match block_comment_length(lex.remainder().as_bytes()) {
451 Some(len) => {
452 lex.bump(len - 2 );
453 FilterResult::Skip
454 }
455 None => {
456 lex.bump(lex.remainder().len());
457 FilterResult::Error(Error::UnterminatedComment)
458 }
459 }
460 }
461}
462
463pub type LexerResult<T> = Result<T, Error>;
465
466pub struct Lexer<'a>(SpannedIter<'a, Token>);
468
469impl<'a> Lexer<'a> {
470 pub fn new(source: &'a str) -> Result<Self, (Error, SourceSpan)> {
472 detect_invalid_input(source)?;
473 Ok(Self(Token::lexer(source).spanned()))
474 }
475
476 pub fn source(&self, span: SourceSpan) -> &'a str {
478 &self.0.source()[span.offset()..span.offset() + span.len()]
479 }
480
481 pub fn span(&self) -> SourceSpan {
483 let mut span = self.0.span();
484 if span.end == self.0.source().len() {
485 span.start = span.start.saturating_sub(1);
491 span.end = span.start + 1;
492 }
493
494 to_source_span(span)
495 }
496
497 pub fn peek(&self) -> Option<(LexerResult<Token>, SourceSpan)> {
499 let mut lexer = self.0.clone();
500 lexer.next().map(|(r, s)| (r, to_source_span(s)))
501 }
502
503 pub fn peek2(&self) -> Option<(LexerResult<Token>, SourceSpan)> {
505 let mut lexer = self.0.clone();
506 lexer.next();
507 lexer.next().map(|(r, s)| (r, to_source_span(s)))
508 }
509
510 pub fn comments(&self) -> Result<Vec<(&'a str, SourceSpan)>, (Error, SourceSpan)> {
512 let mut comments = Vec::new();
513 let mut lexer = (*self.0).clone().morph::<helpers::CommentToken>().spanned();
514 while let Some((Ok(token), span)) = lexer.next() {
515 match token {
516 helpers::CommentToken::Comment(c) | helpers::CommentToken::BlockComment(c) => {
517 let c = if let Some(c) = c.strip_prefix("///") {
518 c.trim()
519 } else if let Some(c) = c.strip_prefix("/**") {
520 if c == "/" {
521 continue;
522 }
523 c.strip_suffix("*/").unwrap().trim()
524 } else {
525 continue;
526 };
527 comments.push((c, to_source_span(span)));
528 }
529 }
530 }
531 Ok(comments)
532 }
533}
534
535impl Iterator for Lexer<'_> {
536 type Item = (LexerResult<Token>, SourceSpan);
537
538 fn next(&mut self) -> Option<Self::Item> {
539 self.0.next().map(|(r, s)| (r, to_source_span(s)))
540 }
541}
542
543#[cfg(test)]
544mod test {
545 use super::*;
546 use logos::{Logos, Source};
547 use std::{fmt, ops::Range};
548
549 #[allow(clippy::type_complexity)]
552 pub fn assert_lex<'a, Token>(
553 source: &'a Token::Source,
554 tokens: &[(
555 Result<Token, Token::Error>,
556 <Token::Source as Source>::Slice<'a>,
557 Range<usize>,
558 )],
559 ) where
560 Token: Logos<'a> + fmt::Debug + PartialEq,
561 Token::Extras: Default,
562 {
563 let mut lex = Token::lexer(source);
564
565 for tuple in tokens {
566 assert_eq!(
567 &(lex.next().expect("unexpected end"), lex.slice(), lex.span()),
568 tuple
569 );
570 }
571
572 assert_eq!(lex.next(), None, "tokens remain");
573 }
574
575 #[test]
576 fn comments() {
577 assert_lex::<Token>(
578 r#"
579 //
580 // comment
581 /**/
582 /* a block comment */
583 /* a multi
584 line comment
585 */
586 /* a /* /* deeply */ nested */ block comment */
587 "#,
588 &[],
589 );
590 }
591
592 #[test]
593 fn unterminated_comment() {
594 let source = r#"/* /* unterminated */"#;
595
596 assert_lex::<Token>(
597 source,
598 &[(
599 Err(Error::UnterminatedComment),
600 "/* /* unterminated */",
601 0..21,
602 )],
603 );
604 }
605
606 #[test]
607 fn ident() {
608 assert_lex(
609 r#"
610 foo
611 foo123
612 f-b
613 foo-bar123
614 foo0123-bar0123-baz0123
615 %interface
616 foo123-BAR
617 "#,
618 &[
619 (Ok(Token::Ident), "foo", 13..16),
620 (Ok(Token::Ident), "foo123", 29..35),
621 (Ok(Token::Ident), "f-b", 48..51),
622 (Ok(Token::Ident), "foo-bar123", 64..74),
623 (Ok(Token::Ident), "foo0123-bar0123-baz0123", 87..110),
624 (Ok(Token::Ident), "%interface", 123..133),
625 (Ok(Token::Ident), "foo123-BAR", 146..156),
626 ],
627 );
628 }
629
630 #[test]
631 fn string() {
632 assert_lex(
633 r#"
634 ""
635 "foo"
636 "foo bar"
637 "foo
638 bar"
639 "#,
640 &[
641 (Ok(Token::String), "\"\"", 13..15),
642 (Ok(Token::String), "\"foo\"", 28..33),
643 (Ok(Token::String), "\"foo bar\"", 46..56),
644 (Ok(Token::String), "\"foo\n bar\"", 69..90),
645 ],
646 );
647 }
648
649 #[test]
650 fn package_path() {
651 assert_lex(
652 r#"
653foo:bar/baz/qux/jam
654foo:bar:baz:qux/jam
655foo:bar/baz@0.0.4
656foo:bar/baz@1.2.3
657foo:bar/baz@10.20.30
658foo:bar/baz@1.1.2-prerelease+meta
659foo:bar/baz@1.1.2+meta
660foo:bar/baz@1.1.2+meta-valid
661foo:bar/baz@1.0.0-alpha
662foo:bar/baz@1.0.0-beta
663foo:bar/baz@1.0.0-alpha.beta
664foo:bar/baz@1.0.0-alpha.beta.1
665foo:bar/baz@1.0.0-alpha.1
666foo:bar/baz@1.0.0-alpha0.valid
667foo:bar/baz@1.0.0-alpha.0valid
668foo:bar/baz@1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay
669foo:bar/baz@1.0.0-rc.1+build.1
670foo:bar/baz@2.0.0-rc.1+build.123
671foo:bar/baz@1.2.3-beta
672foo:bar/baz@10.2.3-DEV-SNAPSHOT
673foo:bar/baz@1.2.3-SNAPSHOT-123
674foo:bar/baz@1.0.0
675foo:bar/baz@2.0.0
676foo:bar/baz@1.1.7
677foo:bar/baz@2.0.0+build.1848
678foo:bar/baz@2.0.1-alpha.1227
679foo:bar/baz@1.0.0-alpha+beta
680foo:bar/baz@1.2.3----RC-SNAPSHOT.12.9.1--.12+788
681foo:bar/baz@1.2.3----R-S.12.9.1--.12+meta
682foo:bar/baz@1.2.3----RC-SNAPSHOT.12.9.1--.12
683foo:bar/baz@1.0.0+0.build.1-rc.10000aaa-kk-0.1
684foo:bar/baz@99999999999999999999999.999999999999999999.99999999999999999
685foo:bar/baz@1.0.0-0A.is.legal
686"#,
687 &[
688 (Ok(Token::PackagePath), "foo:bar/baz/qux/jam", 1..20),
689 (Ok(Token::PackagePath), "foo:bar:baz:qux/jam", 21..40),
690 (Ok(Token::PackagePath), "foo:bar/baz@0.0.4", 41..58),
691 (Ok(Token::PackagePath), "foo:bar/baz@1.2.3", 59..76),
692 (Ok(Token::PackagePath), "foo:bar/baz@10.20.30", 77..97),
693 (
694 Ok(Token::PackagePath),
695 "foo:bar/baz@1.1.2-prerelease+meta",
696 98..131,
697 ),
698 (Ok(Token::PackagePath), "foo:bar/baz@1.1.2+meta", 132..154),
699 (
700 Ok(Token::PackagePath),
701 "foo:bar/baz@1.1.2+meta-valid",
702 155..183,
703 ),
704 (Ok(Token::PackagePath), "foo:bar/baz@1.0.0-alpha", 184..207),
705 (Ok(Token::PackagePath), "foo:bar/baz@1.0.0-beta", 208..230),
706 (
707 Ok(Token::PackagePath),
708 "foo:bar/baz@1.0.0-alpha.beta",
709 231..259,
710 ),
711 (
712 Ok(Token::PackagePath),
713 "foo:bar/baz@1.0.0-alpha.beta.1",
714 260..290,
715 ),
716 (
717 Ok(Token::PackagePath),
718 "foo:bar/baz@1.0.0-alpha.1",
719 291..316,
720 ),
721 (
722 Ok(Token::PackagePath),
723 "foo:bar/baz@1.0.0-alpha0.valid",
724 317..347,
725 ),
726 (
727 Ok(Token::PackagePath),
728 "foo:bar/baz@1.0.0-alpha.0valid",
729 348..378,
730 ),
731 (
732 Ok(Token::PackagePath),
733 "foo:bar/baz@1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay",
734 379..445,
735 ),
736 (
737 Ok(Token::PackagePath),
738 "foo:bar/baz@1.0.0-rc.1+build.1",
739 446..476,
740 ),
741 (
742 Ok(Token::PackagePath),
743 "foo:bar/baz@2.0.0-rc.1+build.123",
744 477..509,
745 ),
746 (Ok(Token::PackagePath), "foo:bar/baz@1.2.3-beta", 510..532),
747 (
748 Ok(Token::PackagePath),
749 "foo:bar/baz@10.2.3-DEV-SNAPSHOT",
750 533..564,
751 ),
752 (
753 Ok(Token::PackagePath),
754 "foo:bar/baz@1.2.3-SNAPSHOT-123",
755 565..595,
756 ),
757 (Ok(Token::PackagePath), "foo:bar/baz@1.0.0", 596..613),
758 (Ok(Token::PackagePath), "foo:bar/baz@2.0.0", 614..631),
759 (Ok(Token::PackagePath), "foo:bar/baz@1.1.7", 632..649),
760 (
761 Ok(Token::PackagePath),
762 "foo:bar/baz@2.0.0+build.1848",
763 650..678,
764 ),
765 (
766 Ok(Token::PackagePath),
767 "foo:bar/baz@2.0.1-alpha.1227",
768 679..707,
769 ),
770 (
771 Ok(Token::PackagePath),
772 "foo:bar/baz@1.0.0-alpha+beta",
773 708..736,
774 ),
775 (
776 Ok(Token::PackagePath),
777 "foo:bar/baz@1.2.3----RC-SNAPSHOT.12.9.1--.12+788",
778 737..785,
779 ),
780 (
781 Ok(Token::PackagePath),
782 "foo:bar/baz@1.2.3----R-S.12.9.1--.12+meta",
783 786..827,
784 ),
785 (
786 Ok(Token::PackagePath),
787 "foo:bar/baz@1.2.3----RC-SNAPSHOT.12.9.1--.12",
788 828..872,
789 ),
790 (
791 Ok(Token::PackagePath),
792 "foo:bar/baz@1.0.0+0.build.1-rc.10000aaa-kk-0.1",
793 873..919,
794 ),
795 (
796 Ok(Token::PackagePath),
797 "foo:bar/baz@99999999999999999999999.999999999999999999.99999999999999999",
798 920..992,
799 ),
800 (
801 Ok(Token::PackagePath),
802 "foo:bar/baz@1.0.0-0A.is.legal",
803 993..1022,
804 ),
805 ],
806 );
807 }
808
809 #[test]
810 fn keywords() {
811 assert_lex(
812 r#"
813import
814with
815type
816tuple
817list
818option
819result
820borrow
821resource
822variant
823record
824flags
825enum
826func
827static
828constructor
829u8
830s8
831u16
832s16
833u32
834s32
835u64
836s64
837f32
838f64
839char
840bool
841string
842interface
843world
844export
845new
846let
847use
848include
849as
850package
851targets
852async
853stream
854future
855error-context
856 "#,
857 &[
858 (Ok(Token::ImportKeyword), "import", 1..7),
859 (Ok(Token::WithKeyword), "with", 8..12),
860 (Ok(Token::TypeKeyword), "type", 13..17),
861 (Ok(Token::TupleKeyword), "tuple", 18..23),
862 (Ok(Token::ListKeyword), "list", 24..28),
863 (Ok(Token::OptionKeyword), "option", 29..35),
864 (Ok(Token::ResultKeyword), "result", 36..42),
865 (Ok(Token::BorrowKeyword), "borrow", 43..49),
866 (Ok(Token::ResourceKeyword), "resource", 50..58),
867 (Ok(Token::VariantKeyword), "variant", 59..66),
868 (Ok(Token::RecordKeyword), "record", 67..73),
869 (Ok(Token::FlagsKeyword), "flags", 74..79),
870 (Ok(Token::EnumKeyword), "enum", 80..84),
871 (Ok(Token::FuncKeyword), "func", 85..89),
872 (Ok(Token::StaticKeyword), "static", 90..96),
873 (Ok(Token::ConstructorKeyword), "constructor", 97..108),
874 (Ok(Token::U8Keyword), "u8", 109..111),
875 (Ok(Token::S8Keyword), "s8", 112..114),
876 (Ok(Token::U16Keyword), "u16", 115..118),
877 (Ok(Token::S16Keyword), "s16", 119..122),
878 (Ok(Token::U32Keyword), "u32", 123..126),
879 (Ok(Token::S32Keyword), "s32", 127..130),
880 (Ok(Token::U64Keyword), "u64", 131..134),
881 (Ok(Token::S64Keyword), "s64", 135..138),
882 (Ok(Token::F32Keyword), "f32", 139..142),
883 (Ok(Token::F64Keyword), "f64", 143..146),
884 (Ok(Token::CharKeyword), "char", 147..151),
885 (Ok(Token::BoolKeyword), "bool", 152..156),
886 (Ok(Token::StringKeyword), "string", 157..163),
887 (Ok(Token::InterfaceKeyword), "interface", 164..173),
888 (Ok(Token::WorldKeyword), "world", 174..179),
889 (Ok(Token::ExportKeyword), "export", 180..186),
890 (Ok(Token::NewKeyword), "new", 187..190),
891 (Ok(Token::LetKeyword), "let", 191..194),
892 (Ok(Token::UseKeyword), "use", 195..198),
893 (Ok(Token::IncludeKeyword), "include", 199..206),
894 (Ok(Token::AsKeyword), "as", 207..209),
895 (Ok(Token::PackageKeyword), "package", 210..217),
896 (Ok(Token::TargetsKeyword), "targets", 218..225),
897 (Ok(Token::AsyncKeyword), "async", 226..231),
898 (Ok(Token::StreamKeyword), "stream", 232..238),
899 (Ok(Token::FutureKeyword), "future", 239..245),
900 (Ok(Token::ErrorContextKeyword), "error-context", 246..259),
901 ],
902 );
903 }
904
905 #[test]
906 fn symbols() {
907 assert_lex(
908 r#";{}:=()-><>_[]. ...,/@"#,
909 &[
910 (Ok(Token::Semicolon), ";", 0..1),
911 (Ok(Token::OpenBrace), "{", 1..2),
912 (Ok(Token::CloseBrace), "}", 2..3),
913 (Ok(Token::Colon), ":", 3..4),
914 (Ok(Token::Equals), "=", 4..5),
915 (Ok(Token::OpenParen), "(", 5..6),
916 (Ok(Token::CloseParen), ")", 6..7),
917 (Ok(Token::Arrow), "->", 7..9),
918 (Ok(Token::OpenAngle), "<", 9..10),
919 (Ok(Token::CloseAngle), ">", 10..11),
920 (Ok(Token::Underscore), "_", 11..12),
921 (Ok(Token::OpenBracket), "[", 12..13),
922 (Ok(Token::CloseBracket), "]", 13..14),
923 (Ok(Token::Dot), ".", 14..15),
924 (Ok(Token::Ellipsis), "...", 16..19),
925 (Ok(Token::Comma), ",", 19..20),
926 (Ok(Token::Slash), "/", 20..21),
927 (Ok(Token::At), "@", 21..22),
928 ],
929 );
930 }
931}