1mod error;
16mod quoted_strings;
17mod wrapped_parsers;
18
19use std::{num::NonZeroU8, str::FromStr};
20
21use lexical::{
22 FromLexicalWithOptions, NumberFormatBuilder, ParseFloatOptions, ParseIntegerOptions,
23};
24use nom::{
25 bytes::complete::{is_a, take_till, take_while, take_while1},
26 character::complete::one_of,
27 combinator::{all_consuming, cut, map, peek, recognize, value},
28 multi::many0,
29 sequence::{pair, preceded, terminated, tuple},
30 Finish, IResult, Slice as _,
31};
32use nom_locate::LocatedSpan;
33use wrapped_parsers::{alt, tag, tag_no_case};
34
35pub use super::token::{KeywordToken, Token, TokenWithLocation};
36use crate::parser::lexer::wrapped_parsers::expecting;
37use crate::parser::token::token_with_location;
38pub(crate) use error::InternalLexError;
39pub use error::{LexError, LexErrorKind};
40
41#[derive(Debug, Copy, Clone, PartialEq, Eq, strum::Display, strum::EnumString)]
42#[strum(serialize_all = "SCREAMING-KEBAB-CASE")]
43pub enum Command {
44 Add,
45 And,
46 Ashr,
47 Call,
48 Capture,
49 Convert,
50 Declare,
51 #[strum(to_string = "DEFCAL")]
52 DefCal,
53 #[strum(to_string = "DEFCIRCUIT")]
54 DefCircuit,
55 #[strum(to_string = "DEFFRAME")]
56 DefFrame,
57 #[strum(to_string = "DEFGATE")]
58 DefGate,
59 #[strum(to_string = "DEFWAVEFORM")]
60 DefWaveform,
61 Delay,
62 Div,
63 Eq,
64 Exchange,
65 Fence,
66 GE,
67 GT,
68 Halt,
69 Include,
70 Ior,
71 Jump,
72 JumpUnless,
73 JumpWhen,
74 Label,
75 LE,
76 Load,
77 LT,
78 Measure,
79 Move,
80 Mul,
81 Neg,
82 Nop,
83 Not,
84 Pragma,
85 Pulse,
86 RawCapture,
87 Reset,
88 SetFrequency,
89 SetPhase,
90 SetScale,
91 ShiftFrequency,
92 ShiftPhase,
93 Shl,
94 Shr,
95 Store,
96 Sub,
97 SwapPhases,
98 Wait,
99 Xor,
100}
101
102#[derive(Debug, Clone, PartialEq, Eq, strum::Display, strum::EnumString)]
103#[strum(serialize_all = "UPPERCASE")]
104pub enum DataType {
105 Bit,
106 Octet,
107 Real,
108 Integer,
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, strum::Display, strum::EnumString)]
112#[strum(serialize_all = "UPPERCASE")]
113pub enum Modifier {
114 Controlled,
115 Dagger,
116 Forked, }
118
119#[derive(Debug, Clone, PartialEq, Eq, strum::Display)]
120pub enum Operator {
121 #[strum(serialize = "^")]
122 Caret,
123 #[strum(serialize = "-")]
124 Minus,
125 #[strum(serialize = "+")]
126 Plus,
127 #[strum(serialize = "/")]
128 Slash,
129 #[strum(serialize = "*")]
130 Star,
131}
132
133pub type LexInput<'a> = LocatedSpan<&'a str>;
134pub(crate) type InternalLexResult<'a, T = Token, E = InternalLexError<'a>> =
135 IResult<LexInput<'a>, T, E>;
136pub type LexResult<'a, T = Token, E = LexError> = IResult<LexInput<'a>, T, E>;
137
138pub(crate) fn lex(input: LexInput) -> Result<Vec<TokenWithLocation>, LexError> {
140 all_consuming(_lex)(input)
141 .finish()
142 .map(|(_, tokens)| tokens)
143 .map_err(LexError::from)
144}
145
146fn _lex(input: LexInput) -> InternalLexResult<Vec<TokenWithLocation>> {
147 terminated(
148 many0(alt(
149 "indentation or a token preceded by whitespace",
150 (lex_indent, preceded(many0(tag(" ")), lex_token)),
151 )),
152 many0(one_of("\n\t ")),
153 )(input)
154}
155
156fn lex_indent(input: LexInput) -> InternalLexResult<TokenWithLocation> {
159 alt(
160 "indentation",
161 (
162 token_with_location(value(Token::Indentation, tag(" "))),
163 token_with_location(value(Token::Indentation, tag("\t"))),
164 ),
165 )(input)
166}
167
168fn lex_token(input: LexInput) -> InternalLexResult<TokenWithLocation> {
169 alt(
170 "a token",
171 (
172 token_with_location(lex_comment),
173 token_with_location(lex_punctuation),
174 token_with_location(lex_target),
175 token_with_location(lex_string),
176 token_with_location(lex_operator),
177 token_with_location(lex_variable),
178 token_with_location(lex_keyword_or_identifier),
179 token_with_location(lex_number),
180 ),
181 )(input)
182}
183
184fn lex_comment(input: LexInput) -> InternalLexResult {
185 let (input, _) = tag("#")(input)?;
186 let (input, content) = take_till(|c| c == '\n')(input)?;
187 Ok((input, Token::Comment(content.to_string())))
188}
189
190fn keyword_or_identifier(identifier: String) -> Token {
191 fn parse<T: FromStr>(token: impl Fn(T) -> Token, identifier: &str) -> Result<Token, T::Err> {
192 T::from_str(identifier).map(token)
193 }
194
195 parse(KeywordToken::into, &identifier)
196 .or_else(|_| parse(Token::Command, &identifier))
197 .or_else(|_| parse(Token::DataType, &identifier))
198 .or_else(|_| parse(Token::Modifier, &identifier))
199 .unwrap_or(Token::Identifier(identifier))
200}
201
202fn is_valid_identifier_leading_character(chr: char) -> bool {
203 chr.is_ascii_alphabetic() || chr == '_'
204}
205
206fn is_valid_identifier_end_character(chr: char) -> bool {
207 is_valid_identifier_leading_character(chr) || chr.is_ascii_digit()
208}
209
210fn is_dash(chr: char) -> bool {
211 chr == '-'
212}
213
214fn lex_identifier_raw(input: LexInput) -> InternalLexResult<String> {
215 expecting(
216 "a valid identifier",
217 map(
218 tuple::<_, _, InternalLexError, _>((
219 take_while1(is_valid_identifier_leading_character),
220 take_while(is_valid_identifier_end_character),
221 recognize(many0(pair(
222 take_while1(is_dash),
223 take_while1(is_valid_identifier_end_character),
224 ))),
225 )),
226 |(leading, middle, trailing_dash_vars)| {
227 format!("{leading}{middle}{trailing_dash_vars}")
228 },
229 ),
230 )(input)
231}
232
233fn lex_keyword_or_identifier(input: LexInput) -> InternalLexResult {
234 let (input, identifier) = lex_identifier_raw(input)?;
235 let token = keyword_or_identifier(identifier);
236 Ok((input, token))
237}
238
239fn lex_target(input: LexInput) -> InternalLexResult {
240 let (input, _) = tag("@")(input)?;
241 let (input, label) = lex_identifier_raw(input)?;
242 Ok((input, Token::Target(label)))
243}
244
245const fn number_format(radix: u8, prefix: Option<NonZeroU8>) -> u128 {
251 NumberFormatBuilder::new()
252 .mantissa_radix(radix)
253 .exponent_base(NonZeroU8::new(radix))
254 .exponent_radix(NonZeroU8::new(10))
255 .base_prefix(prefix)
256 .digit_separator(NonZeroU8::new(b'_'))
257 .required_integer_digits(false) .required_fraction_digits(false) .required_exponent_digits(true) .required_mantissa_digits(true) .no_positive_mantissa_sign(true) .required_mantissa_sign(false) .no_exponent_notation(false) .no_positive_exponent_sign(false) .required_exponent_sign(false) .no_exponent_without_fraction(false) .no_special(true) .no_integer_leading_zeros(false) .no_float_leading_zeros(false) .required_exponent_notation(false) .case_sensitive_exponent(false) .case_sensitive_base_prefix(false) .digit_separator_flags(true) .integer_leading_digit_separator(false) .fraction_leading_digit_separator(false) .special_digit_separator(false) .build_strict()
278}
279
280const INTEGER_OPTIONS: ParseIntegerOptions = ParseIntegerOptions::new();
281const FLOAT_OPTIONS: ParseFloatOptions = ParseFloatOptions::builder()
282 .exponent(b'e')
283 .decimal_point(b'.')
284 .build_strict();
285
286fn lex_and_parse_number<N: FromLexicalWithOptions, const FORMAT: u128>(
287 options: &'static N::Options,
288) -> impl FnMut(LexInput) -> InternalLexResult<N> {
289 #[inline(always)]
290 fn parse<N: FromLexicalWithOptions, const FORMAT: u128>(
291 input: LexInput,
292 options: &'static N::Options,
293 ) -> lexical::Result<(N, usize)> {
294 let result @ (_, len) =
295 lexical::parse_partial_with_options::<N, _, FORMAT>(input, options)?;
296
297 if const {
300 NumberFormatBuilder::rebuild(FORMAT)
301 .get_base_prefix()
302 .is_some()
303 } && len == 2
304 {
305 return Err(lexical::Error::EmptyInteger(2));
306 }
307
308 if let Some(dot) = input.slice(..len).find("._") {
311 let include_dot = dot + 1;
312 let number =
313 lexical::parse_with_options::<N, _, FORMAT>(input.slice(..include_dot), options)?;
314 return Ok((number, include_dot));
315 }
316
317 Ok(result)
318 }
319
320 move |input| {
321 let (num, len) = parse::<N, FORMAT>(input, options).map_err(|lex_err| {
322 let error = InternalLexError::from_kind(input, lex_err.into());
323 match lex_err {
324 lexical::Error::Overflow(_) | lexical::Error::Underflow(_) => {
325 nom::Err::Failure(error)
327 }
328 _ => nom::Err::Error(error),
329 }
330 })?;
331
332 Ok((input.slice(len..), num))
333 }
334}
335
336fn raw_lex_integer<const PREFIX: u8, const FORMAT: u128>(
337 input: LexInput,
338) -> InternalLexResult<u64> {
339 const {
340 assert!(PREFIX <= 127, "PREFIX must be an ASCII character");
341 }
342
343 if PREFIX == 0 {
344 lex_and_parse_number::<u64, FORMAT>(&INTEGER_OPTIONS)(input)
345 } else {
346 let (_, _) = peek(tag_no_case(std::str::from_utf8(&[b'0', PREFIX]).unwrap()))(input)?;
347 cut(lex_and_parse_number::<u64, FORMAT>(&INTEGER_OPTIONS))(input)
348 }
349}
350
351macro_rules! def_radix {
352 ($name:ident, $radix:literal $(,)?) => {
353 def_radix!($name, $radix, 0);
354 };
355 ($name:ident, $radix:literal, $prefix:literal $(,)?) => {
356 paste::paste! {
357 #[inline]
358 fn [< lex_ $name _integer >](input: LexInput) -> InternalLexResult<u64> {
359 raw_lex_integer::<$prefix, { number_format($radix, NonZeroU8::new($prefix)) }>(
360 input
361 )
362 }
363 }
364 };
365}
366
367def_radix!(binary, 2, b'b');
368def_radix!(decimal, 10);
369def_radix!(octal, 8, b'o');
370def_radix!(hexadecimal, 16, b'x');
371
372fn lex_decimal_number(input: LexInput) -> InternalLexResult {
373 let parse_float = |input| {
374 let (input, float) = cut(lex_and_parse_number::<f64, { number_format(10, None) }>(
375 &FLOAT_OPTIONS,
376 ))(input)?;
377
378 if !float.is_finite() {
379 return Err(nom::Err::Failure(InternalLexError::from_kind(
380 input,
381 lexical::Error::Overflow(0).into(),
382 )));
383 }
384
385 Ok((input, Token::Float(float)))
386 };
387
388 if input.as_bytes().first() == Some(&b'.') {
389 parse_float(input)
390 } else {
391 let (input_if_int, int) = lex_decimal_integer(input)?;
392 if input_if_int
393 .as_bytes()
394 .first()
395 .is_some_and(|next| b".eE".contains(next))
396 {
397 parse_float(input)
399 } else {
400 Ok((input_if_int, Token::Integer(int)))
401 }
402 }
403}
404
405fn lex_number(input: LexInput) -> InternalLexResult {
406 alt(
407 "number",
408 (
409 map(lex_binary_integer, Token::Integer),
410 map(lex_octal_integer, Token::Integer),
411 map(lex_hexadecimal_integer, Token::Integer),
412 lex_decimal_number,
413 ),
414 )(input)
415}
416
417fn lex_operator(input: LexInput) -> InternalLexResult {
418 use Operator::*;
419 map(
420 alt(
421 "an operator",
422 (
423 value(Caret, tag("^")),
424 value(Minus, tag("-")),
425 value(Plus, tag("+")),
426 value(Slash, tag("/")),
427 value(Star, tag("*")),
428 ),
429 ),
430 Token::Operator,
431 )(input)
432}
433
434fn recognize_newlines(input: LexInput) -> InternalLexResult<LexInput> {
435 alt(
436 "one or more newlines",
437 (
438 is_a::<_, _, InternalLexError>("\n"),
439 is_a::<_, _, InternalLexError>("\r\n"),
440 ),
441 )(input)
442}
443
444fn lex_punctuation(input: LexInput) -> InternalLexResult {
445 use Token::*;
446 alt(
447 "punctuation",
448 (
449 value(Bang, tag("!")),
450 value(Colon, tag(":")),
451 value(Comma, tag(",")),
452 value(
453 Indentation,
454 alt("four spaces or a tab character", (tag(" "), tag("\t"))),
455 ),
456 value(LBracket, tag("[")),
457 value(LParenthesis, tag("(")),
458 value(NewLine, recognize_newlines),
459 value(RBracket, tag("]")),
460 value(RParenthesis, tag(")")),
461 value(Semicolon, tag(";")),
462 ),
463 )(input)
464}
465
466fn lex_string(input: LexInput) -> InternalLexResult {
467 map(quoted_strings::unescaped_quoted_string, Token::String)(input)
468}
469
470fn lex_variable(input: LexInput) -> InternalLexResult {
471 map(preceded(tag("%"), lex_identifier_raw), |ident| {
472 Token::Variable(ident)
473 })(input)
474}
475
476#[cfg(test)]
477mod tests {
478 use nom_locate::LocatedSpan;
479 use rstest::*;
480
481 use crate::parser::{common::tests::KITCHEN_SINK_QUIL, DataType};
482
483 use super::{lex, Command, Operator, Token};
484
485 #[test]
486 fn comment() {
487 let input = LocatedSpan::new("# hello\n#world\n#\n#");
488 let tokens = lex(input).unwrap();
489 assert_eq!(
490 tokens,
491 vec![
492 Token::Comment(" hello".to_owned()),
493 Token::NewLine,
494 Token::Comment("world".to_owned()),
495 Token::NewLine,
496 Token::Comment("".to_owned()),
497 Token::NewLine,
498 Token::Comment("".to_owned())
499 ]
500 )
501 }
502
503 #[test]
504 fn keywords() {
505 let input = LocatedSpan::new("DEFGATE DEFCIRCUIT JUMP-WHEN MATRIX LOAD load LOAD-MEMORY");
506 let tokens = lex(input).unwrap();
507 assert_eq!(
508 tokens,
509 vec![
510 Token::Command(Command::DefGate),
511 Token::Command(Command::DefCircuit),
512 Token::Command(Command::JumpWhen),
513 Token::Matrix,
514 Token::Command(Command::Load),
515 Token::Identifier(String::from("load")),
516 Token::Identifier(String::from("LOAD-MEMORY"))
517 ]
518 )
519 }
520
521 #[rstest]
522 #[case::bin_dot("0b10.1", [Token::Integer(0b10), Token::Float(0.1)])]
523 #[case::oct_dot("0o777.7", [Token::Integer(0o777), Token::Float(0.7)])]
524 #[case::hex_dot("0x3.4", [Token::Integer(0x3), Token::Float(0.4)])]
525 #[case::imaginary("1i", [Token::Integer(1), Token::Identifier("i".to_owned())])]
526 #[case::complex(
527 "1 + 2i",
528 [
529 Token::Integer(1),
530 Token::Operator(Operator::Plus),
531 Token::Integer(2),
532 Token::Identifier("i".to_owned()),
533 ],
534 )]
535 #[case::bin_imaginary("0b10i", [Token::Integer(0b10), Token::Identifier("i".to_owned())])]
536 #[case::oct_imaginary("0o10i", [Token::Integer(0o10), Token::Identifier("i".to_owned())])]
537 #[case::hex_imaginary("0x10i", [Token::Integer(0x10), Token::Identifier("i".to_owned())])]
538 #[case::zero_dot_underscore_one("0._1", [Token::Float(0.0), Token::Identifier("_1".to_owned())])]
539 #[case::zero_dot_underscore("0._", [Token::Float(0.0), Token::Identifier("_".to_owned())])]
540 fn tokenization<const N: usize>(#[case] input: &str, #[case] expected: [Token; N]) {
541 let tokens = lex(LocatedSpan::new(input)).expect("lexing error");
542 assert_eq!(
543 tokens,
544 expected,
545 "lexing {input:?}:\n\
546 - got: {plain_tokens:?},\n\
547 - expected: {expected:?}",
548 plain_tokens = tokens
549 .iter()
550 .map(|located| located.as_token())
551 .collect::<Vec<_>>(),
552 );
553 }
554
555 #[rstest]
556 #[case::bin_prefix_only("0b")]
557 #[case::oct_prefix_only("0o")]
558 #[case::hex_prefix_only("0x")]
559 #[case::bin_prefix_dot("0b.")]
560 #[case::oct_prefix_dot("0o.")]
561 #[case::hex_prefix_dot("0x.")]
562 #[case::bin_prefix_dot_one("0b.1")]
563 #[case::oct_prefix_dot_one("0o.1")]
564 #[case::hex_prefix_dot_one("0x.1")]
565 #[case::int_too_big("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")]
566 #[case::float_too_big("1e1_000_000")]
567 #[case::dot_underscore_one("._1")]
568 #[case::dot_underscore("._")]
569 fn bad_token(#[case] input: &str) {
570 let _ = lex(LocatedSpan::new(input)).expect_err("lexing error");
571 }
572
573 macro_rules! number_test_builder {
574 {
575 { $($cases:tt)* }
576 $(#[$meta:meta])*
577 $number:literal,
578 $($rest:tt)*
579 } => {
580 number_test_builder! {
581 { $($cases)* }
582 $(#[$meta])*
583 { name: ($number), input: stringify!($number), expected: $number },
584 $($rest)*
585 }
586 };
587
588 {
589 { $($cases:tt)* }
590 $(#[$meta:meta])*
591 lit($($piece:tt)+) => $number:literal,
592 $($rest:tt)*
593 } => {
594 number_test_builder! {
595 { $($cases)* }
596 $(#[$meta])*
597 {
598 name: ($($piece)+),
599 input: concat!($(stringify!($piece)),+),
600 expected: $number,
601 },
602 $($rest)*
603 }
604 };
605
606 {
607 { $($cases:tt)* }
608 $(#[$meta:meta])*
609 name($($piece:tt)+) => $number:literal,
610 $($rest:tt)*
611 } => {
612 number_test_builder! {
613 { $($cases)* }
614 $(#[$meta])*
615 {
616 name: ($($piece)+),
617 input: stringify!($number),
618 expected: $number,
619 },
620 $($rest)*
621 }
622 };
623
624 (
625 { $($cases:tt)* }
626 $(#[$meta:meta])*
627 {
628 name: ($($name_fragment:tt)+),
629 input: $input:expr,
630 expected: $number:expr$(,)?
631 },
632 $($rest:tt)*
633 ) => {
634 paste::paste! {
635 number_test_builder! {
636 {
637 $($cases)*
638 #[allow(
639 non_snake_case,
640 clippy::inconsistent_digit_grouping,
641 clippy::unusual_byte_groupings,
642 clippy::mixed_case_hex_literals,
643 clippy::zero_prefixed_literal,
644 )]
645 $(#[$meta])*
646 #[case::[<lex$(_$name_fragment)*>]($input, $number)]
647 }
648 $($rest)*
649 }
650 }
651 };
652
653 (
654 {
655 { $test_name:ident, $token:ident, $ty:ty }
656 $($cases:tt)*
657 }
658 ) => {
659 #[rstest]
660 $($cases)*
661 fn $test_name(#[case] input: &str, #[case] expected: $ty) {
662 tokenization(input, [Token::$token(expected)])
663 }
664 }
665 }
666
667 macro_rules! integer_tests {
668 ($($input:tt)*) => {
669 const _: () = assert!(matches!(
671 stringify!(0x_12__34_aBCd__).as_bytes(),
672 b"0x_12__34_aBCd__"
673 ));
674
675 number_test_builder! { { {integer, Integer, u64} } $($input)* }
676 }
677 }
678
679 macro_rules! float_tests {
680 ($($input:tt)*) => {
681 number_test_builder! { { {float, Float, f64} } $($input)* }
682 }
683 }
684
685 integer_tests! {
686 0,
687 1,
688 2_,
689 3__,
690 4_5,
691 6__7,
692 8__9__10,
693 0000042,
694 0x0,
695 0x7F,
696 lit(0X7f) => 0x7f,
697 0x__CaFe__B0bA__1234__,
698 0o0,
699 0o10,
700 lit(0O10) => 0o10,
701 0o__777__555__,
702 0b0,
703 0b101010,
704 lit(0B101010) => 0b101010,
705 0b__1111__0000__,
706 }
707
708 float_tests! {
709 name(0dot) => 0.,
710 { name: (dot0), input: ".0", expected: 0.0 },
711 name(0dot0) => 0.0,
712 name(1dot) => 1.,
713 { name: (dot1), input: ".1", expected: 0.1 },
714 name(1dot1) => 1.1,
715 name(1__2__dot3__4__eplus__1__5__) => 1__2__.3__4__e+__1__5__,
716 name(1__2__dot3__4__eminus__1__5__) => 1__2__.3__4__e-__1__5__,
717 {
718 name: (1__2__dot3__4__e__1__5__),
719 input: "1__2__.3__4__e__1__5__",
720 expected: 1__2__.3__4__e+__1__5__
721 },
722 1e5,
723 { name: (1dote5), input: "1.e5", expected: 1e5 },
724 { name: (dot1e5), input: ".1e5", expected: 0.1e5 },
725 name(1dot1e5) => 1.1e5,
726 }
727
728 #[test]
729 fn a_bunch_of_numbers() {
730 let input = LocatedSpan::new("2 2i 2.0 2e3 2.0e3 (1+2i)");
731 let tokens = lex(input).unwrap();
732 assert_eq!(
733 tokens,
734 vec![
735 Token::Integer(2),
736 Token::Integer(2),
737 Token::Identifier("i".to_owned()),
738 Token::Float(2.0),
739 Token::Float(2000f64),
740 Token::Float(2000f64),
741 Token::LParenthesis,
742 Token::Integer(1),
743 Token::Operator(Operator::Plus),
744 Token::Integer(2),
745 Token::Identifier("i".to_owned()),
746 Token::RParenthesis
747 ]
748 )
749 }
750
751 #[test]
752 fn string() {
753 let input = LocatedSpan::new("\"hello\"\n\"world\"");
754 let tokens = lex(input).unwrap();
755 assert_eq!(
756 tokens,
757 vec![
758 Token::String("hello".to_owned()),
759 Token::NewLine,
760 Token::String("world".to_owned())
761 ]
762 )
763 }
764
765 #[test]
766 fn gate_operation() {
767 let input = LocatedSpan::new("I 0; RX 1\nCZ 0 1");
768 let tokens = lex(input).unwrap();
769 assert_eq!(
770 tokens,
771 vec![
772 Token::Identifier("I".to_owned()),
773 Token::Integer(0),
774 Token::Semicolon,
775 Token::Identifier("RX".to_owned()),
776 Token::Integer(1),
777 Token::NewLine,
778 Token::Identifier("CZ".to_owned()),
779 Token::Integer(0),
780 Token::Integer(1),
781 ]
782 )
783 }
784
785 #[test]
786 fn label() {
787 let input = LocatedSpan::new("@hello\n@world");
788 let tokens = lex(input).unwrap();
789 assert_eq!(
790 tokens,
791 vec![
792 Token::Target("hello".to_owned()),
793 Token::NewLine,
794 Token::Target("world".to_owned())
795 ]
796 )
797 }
798
799 #[test]
800 fn indentation() {
801 let input = LocatedSpan::new(" ");
802 let tokens = lex(input).unwrap();
803 assert_eq!(tokens, vec![Token::Indentation,])
804 }
805
806 #[test]
807 fn indented_block() {
808 let input = LocatedSpan::new("DEFGATE Name AS PERMUTATION:\n\t1,0\n 0,1");
809 let tokens = lex(input).unwrap();
810 assert_eq!(
811 tokens,
812 vec![
813 Token::Command(Command::DefGate),
814 Token::Identifier("Name".to_owned()),
815 Token::As,
816 Token::Permutation,
817 Token::Colon,
818 Token::NewLine,
819 Token::Indentation,
820 Token::Integer(1),
821 Token::Comma,
822 Token::Integer(0),
823 Token::NewLine,
824 Token::Indentation,
825 Token::Integer(0),
826 Token::Comma,
827 Token::Integer(1),
828 ]
829 )
830 }
831
832 #[test]
833 fn surrounding_whitespace() {
834 let input = LocatedSpan::new("\nI 0\n \n");
835 let tokens = lex(input).unwrap();
836 assert_eq!(
837 tokens,
838 vec![
839 Token::NewLine,
840 Token::Identifier("I".to_owned()),
841 Token::Integer(0),
842 Token::NewLine,
843 Token::Indentation,
844 Token::NewLine
845 ]
846 )
847 }
848
849 #[rstest(input, expected,
850 case("_", vec![Token::Identifier("_".to_string())]),
851 case("a", vec![Token::Identifier("a".to_string())]),
852 case("_a-2_b-2_", vec![Token::Identifier("_a-2_b-2_".to_string())]),
853 case("a-2-%var", vec![
854 Token::Identifier("a-2".to_string()),
855 Token::Operator(Operator::Minus),
856 Token::Variable("var".to_string())
857 ]),
858 case("BIT", vec![Token::DataType(DataType::Bit)]),
859 case("BITS", vec![Token::Identifier("BITS".to_string())]),
860 case("NaN", vec![Token::Identifier("NaN".to_string())]),
861 case("nan", vec![Token::Identifier("nan".to_string())]),
862 case("NaNa", vec![Token::Identifier("NaNa".to_string())]),
863 case("nana", vec![Token::Identifier("nana".to_string())]),
864 case("INF", vec![Token::Identifier("INF".to_string())]),
865 case("Infinity", vec![Token::Identifier("Infinity".to_string())]),
866 case("Inferior", vec![Token::Identifier("Inferior".to_string())]),
867 case("-NaN", vec![Token::Operator(Operator::Minus), Token::Identifier("NaN".to_string())]),
868 case("-inf", vec![Token::Operator(Operator::Minus), Token::Identifier("inf".to_string())]),
869 case("-Infinity", vec![
870 Token::Operator(Operator::Minus),
871 Token::Identifier("Infinity".to_string())
872 ]),
873 case("-inferior", vec![
874 Token::Operator(Operator::Minus),
875 Token::Identifier("inferior".to_string())
876 ]),
877 )]
878 fn it_lexes_identifier(input: &str, expected: Vec<Token>) {
879 let input = LocatedSpan::new(input);
880 let tokens = lex(input).unwrap();
881 assert_eq!(tokens, expected);
882 }
883
884 #[rstest(input, not_expected,
885 case("a-", vec![Token::Identifier("_-".to_string())]),
886 case("-a", vec![Token::Identifier("-a".to_string())]),
887 case("a\\", vec![Token::Identifier("_\\".to_string())]),
888 )]
889 fn it_fails_to_lex_identifier(input: &str, not_expected: Vec<Token>) {
890 let input = LocatedSpan::new(input);
891 if let Ok(tokens) = lex(input) {
892 assert_ne!(tokens, not_expected);
893 }
894 }
895
896 #[test]
898 fn kitchen_sink() {
899 let input = LocatedSpan::new(KITCHEN_SINK_QUIL);
900
901 lex(input).unwrap();
902 }
903}