1use rucc_session::Std;
71use rucc_target::TargetInfo;
72
73use crate::remarks::Remarks;
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum Encoding {
78 Plain,
80 Wide,
82 Utf8,
84 Utf16,
86 Utf32,
88}
89
90impl Encoding {
91 #[must_use]
93 pub fn element_width(self, target: &TargetInfo) -> u32 {
94 match self {
95 Encoding::Plain | Encoding::Utf8 => 8,
96 Encoding::Wide => target.wchar_width,
97 Encoding::Utf16 => 16,
98 Encoding::Utf32 => 32,
99 }
100 }
101
102 #[must_use]
104 pub fn is_signed(self, target: &TargetInfo) -> bool {
105 match self {
106 Encoding::Plain => target.char_is_signed,
107 Encoding::Wide => target.wchar_is_signed,
108 Encoding::Utf8 | Encoding::Utf16 | Encoding::Utf32 => false,
109 }
110 }
111
112 #[must_use]
114 pub const fn prefix(self) -> &'static str {
115 match self {
116 Encoding::Plain => "",
117 Encoding::Wide => "L",
118 Encoding::Utf8 => "u8",
119 Encoding::Utf16 => "u",
120 Encoding::Utf32 => "U",
121 }
122 }
123
124 #[must_use]
127 pub fn read_prefix(text: &str) -> Encoding {
128 Encoding::read(text.as_bytes()).0
129 }
130
131 fn read(bytes: &[u8]) -> (Encoding, usize) {
133 match bytes {
134 [b'u', b'8', ..] => (Encoding::Utf8, 2),
135 [b'u', ..] => (Encoding::Utf16, 1),
136 [b'U', ..] => (Encoding::Utf32, 1),
137 [b'L', ..] => (Encoding::Wide, 1),
138 _ => (Encoding::Plain, 0),
139 }
140 }
141
142 fn since(self, character: bool, gnu: bool) -> Std {
149 match self {
150 Encoding::Plain | Encoding::Wide => Std::C89,
151 Encoding::Utf8 if character => Std::C23,
152 Encoding::Utf8 | Encoding::Utf16 | Encoding::Utf32 if gnu => Std::C99,
153 Encoding::Utf8 | Encoding::Utf16 | Encoding::Utf32 => Std::C11,
154 }
155 }
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160pub struct CharConstant {
161 pub value: i64,
164 pub encoding: Encoding,
166 pub remarks: Remarks,
168}
169
170impl CharConstant {
171 #[must_use]
180 pub fn spell(self) -> String {
181 let mut out = String::from(self.encoding.prefix());
182 out.push('\'');
183 match self.encoding {
184 Encoding::Plain | Encoding::Utf8 if !(-128..=255).contains(&self.value) => {
185 let bits = self.value as u32;
186 let mut writing = false;
187 for shift in [24, 16, 8, 0] {
188 let byte = (bits >> shift) as u8;
189 writing |= byte != 0;
190 if writing {
191 out.push_str(&format!("\\x{byte:02x}"));
192 }
193 }
194 }
195 Encoding::Plain | Encoding::Utf8 => {
196 let byte = self.value as u8;
197 escape(u32::from(byte), '\'', &mut out);
198 }
199 _ => escape(self.value as u32, '\'', &mut out),
200 }
201 out.push('\'');
202 out
203 }
204}
205
206#[derive(Debug, Clone, PartialEq, Eq)]
208pub struct StringLiteral {
209 pub elements: Vec<u32>,
213 pub encoding: Encoding,
215 pub remarks: Remarks,
217}
218
219impl StringLiteral {
220 #[must_use]
223 pub fn bytes(&self, target: &TargetInfo) -> Vec<u8> {
224 let width = self.encoding.element_width(target) / 8;
225 let mut bytes = Vec::with_capacity((self.elements.len() + 1) * width as usize);
226 for element in self.elements.iter().copied().chain([0]) {
227 let taken = &element.to_le_bytes()[..width as usize];
228 if target.little_endian {
229 bytes.extend_from_slice(taken);
230 } else {
231 bytes.extend(taken.iter().rev());
232 }
233 }
234 bytes
235 }
236
237 #[must_use]
244 pub fn spell(&self) -> String {
245 let prefix = self.encoding.prefix();
246 let wide = !matches!(self.encoding, Encoding::Plain | Encoding::Utf8);
247 let mut out = String::from(prefix);
248 out.push('"');
249 let mut ran_on = false;
250 for &element in &self.elements {
251 match printable(element) {
252 Some(ch) => {
253 if ran_on && ch.is_ascii_hexdigit() {
254 out.push('"');
255 out.push(' ');
256 out.push_str(prefix);
257 out.push('"');
258 }
259 escape(element, '"', &mut out);
260 ran_on = false;
261 }
262 None if wide => {
263 out.push_str(&format!("\\x{element:x}"));
264 ran_on = true;
265 }
266 None => {
267 out.push_str(&format!("\\{element:03o}"));
268 ran_on = false;
269 }
270 }
271 }
272 out.push('"');
273 out
274 }
275}
276
277fn printable(element: u32) -> Option<char> {
282 match element {
283 0x20..=0x7e => char::from_u32(element),
284 _ => None,
285 }
286}
287
288fn escape(element: u32, quote: char, out: &mut String) {
290 match printable(element) {
291 Some(ch) if ch == quote || ch == '\\' => {
292 out.push('\\');
293 out.push(ch);
294 }
295 Some('?') if out.ends_with('?') => out.push_str("\\?"),
298 Some(ch) => out.push(ch),
299 None => out.push_str(&format!("\\x{element:x}")),
300 }
301}
302
303#[derive(Debug, Clone, Copy, PartialEq, Eq)]
305pub enum LiteralError {
306 NotALiteral,
309 Empty,
311 TooLong,
314 NoHexDigits,
316 IncompleteUcn,
318 InvalidUcn,
321 NamedUcn,
323 InvalidUtf8,
326 PrefixNotInDialect,
328 MixedEncodings,
331}
332
333impl LiteralError {
334 #[must_use]
336 pub const fn message(self) -> &'static str {
337 match self {
338 LiteralError::NotALiteral => "not a character constant or a string literal",
339 LiteralError::Empty => "empty character constant",
340 LiteralError::TooLong => "character constant too long for its type",
341 LiteralError::NoHexDigits => "\\x used with no following hex digits",
342 LiteralError::IncompleteUcn => "incomplete universal character name",
343 LiteralError::InvalidUcn => "not a valid universal character",
344 LiteralError::NamedUcn => "named universal character escapes are not supported yet",
345 LiteralError::InvalidUtf8 => "failure to convert the source to the execution charset",
346 LiteralError::PrefixNotInDialect => {
347 "this encoding prefix is not available in this dialect"
348 }
349 LiteralError::MixedEncodings => {
350 "unsupported non-standard concatenation of string literals"
351 }
352 }
353 }
354}
355
356pub fn character(
363 text: &str,
364 std: Std,
365 gnu: bool,
366 target: &TargetInfo,
367) -> Result<CharConstant, LiteralError> {
368 let (encoding, body) = open(text, b'\'', std, gnu, true)?;
369 let width = encoding.element_width(target);
370 let mut reader = Reader { bytes: body, index: 0, std, remarks: Remarks::NONE };
371
372 let mut value: u64 = 0;
376 let mut count = 0u32;
377 while let Some(piece) = reader.next(width)? {
378 for element in piece.elements(width) {
379 value = (value << width) | u64::from(element);
380 count += 1;
381 }
382 }
383 let mut remarks = reader.remarks;
384
385 let type_width = if encoding == Encoding::Plain { 32 } else { width };
388 let capacity = type_width / width;
389 match count {
390 0 => return Err(LiteralError::Empty),
391 1 => {}
392 _ if encoding == Encoding::Utf8 => return Err(LiteralError::TooLong),
393 _ if count > capacity => remarks = remarks.with(Remarks::TOO_LONG),
394 _ => remarks = remarks.with(Remarks::MULTICHARACTER),
395 }
396
397 let (bits, signed) = if count == 1 {
401 (width, encoding.is_signed(target))
402 } else {
403 (type_width, encoding == Encoding::Plain || encoding.is_signed(target))
404 };
405 Ok(CharConstant { value: narrow(value, bits, signed), encoding, remarks })
406}
407
408pub fn string(
415 text: &str,
416 std: Std,
417 gnu: bool,
418 target: &TargetInfo,
419) -> Result<StringLiteral, LiteralError> {
420 strings(std::slice::from_ref(&text), std, gnu, target)
421}
422
423pub fn strings(
439 texts: &[&str],
440 std: Std,
441 gnu: bool,
442 target: &TargetInfo,
443) -> Result<StringLiteral, LiteralError> {
444 let mut bodies = Vec::with_capacity(texts.len());
445 let mut encoding = Encoding::Plain;
446 for text in texts {
447 let (found, body) = open(text, b'"', std, gnu, false)?;
448 if found != Encoding::Plain {
449 if encoding != Encoding::Plain && encoding != found {
450 return Err(LiteralError::MixedEncodings);
451 }
452 encoding = found;
453 }
454 bodies.push(body);
455 }
456
457 let width = encoding.element_width(target);
458 let mut elements = Vec::new();
459 let mut remarks = Remarks::NONE;
460 for body in bodies {
461 let mut reader = Reader { bytes: body, index: 0, std, remarks: Remarks::NONE };
462 while let Some(piece) = reader.next(width)? {
463 elements.extend(piece.elements(width));
464 }
465 remarks = remarks.with(reader.remarks);
466 }
467 Ok(StringLiteral { elements, encoding, remarks })
468}
469
470fn open(
472 text: &str,
473 quote: u8,
474 std: Std,
475 gnu: bool,
476 character: bool,
477) -> Result<(Encoding, &[u8]), LiteralError> {
478 let bytes = text.as_bytes();
479 let (encoding, prefix) = Encoding::read(bytes);
480 if std < encoding.since(character, gnu) {
481 return Err(LiteralError::PrefixNotInDialect);
482 }
483 let rest = &bytes[prefix..];
484 match rest {
485 [first, .., last] if *first == quote && *last == quote => {
486 Ok((encoding, &rest[1..rest.len() - 1]))
487 }
488 _ => Err(LiteralError::NotALiteral),
489 }
490}
491
492fn narrow(value: u64, bits: u32, signed: bool) -> i64 {
494 let masked = if bits >= 64 { value } else { value & ((1u64 << bits) - 1) };
495 if signed && bits < 64 && masked >> (bits - 1) & 1 == 1 {
496 (masked | !((1u64 << bits) - 1)) as i64
498 } else {
499 masked as i64
500 }
501}
502
503#[derive(Debug, Clone, Copy, PartialEq, Eq)]
505enum Piece {
506 Char(u32),
509 Value(u32),
512}
513
514impl Piece {
515 fn elements(self, width: u32) -> Vec<u32> {
517 let code = match self {
518 Piece::Value(value) => return vec![value],
519 Piece::Char(code) => code,
520 };
521 match width {
522 8 => {
523 let mut buffer = [0u8; 4];
524 let text = char::from_u32(code)
525 .map(|character| character.encode_utf8(&mut buffer).len())
526 .unwrap_or(0);
527 buffer[..text].iter().map(|&byte| u32::from(byte)).collect()
528 }
529 16 if code > 0xffff => {
532 let value = code - 0x1_0000;
533 vec![0xd800 + (value >> 10), 0xdc00 + (value & 0x3ff)]
534 }
535 _ => vec![code],
536 }
537 }
538}
539
540struct Reader<'a> {
542 bytes: &'a [u8],
544 index: usize,
546 std: Std,
548 remarks: Remarks,
550}
551
552impl Reader<'_> {
553 fn next(&mut self, width: u32) -> Result<Option<Piece>, LiteralError> {
555 let Some(&byte) = self.bytes.get(self.index) else {
556 return Ok(None);
557 };
558 self.index += 1;
559 if byte == b'\\' {
560 return self.escape(width).map(Some);
561 }
562 if byte < 0x80 {
563 return Ok(Some(Piece::Char(u32::from(byte))));
564 }
565 if width == 8 {
569 return Ok(Some(Piece::Value(u32::from(byte))));
570 }
571 let length = utf8_length(byte).ok_or(LiteralError::InvalidUtf8)?;
574 let end = self.index - 1 + length;
575 let text = self
576 .bytes
577 .get(self.index - 1..end)
578 .and_then(|slice| std::str::from_utf8(slice).ok())
579 .ok_or(LiteralError::InvalidUtf8)?;
580 let character = text.chars().next().ok_or(LiteralError::InvalidUtf8)?;
581 self.index = end;
582 Ok(Some(Piece::Char(character as u32)))
583 }
584
585 fn escape(&mut self, width: u32) -> Result<Piece, LiteralError> {
587 let Some(&byte) = self.bytes.get(self.index) else {
588 return Err(LiteralError::NotALiteral);
590 };
591 self.index += 1;
592 let simple = match byte {
593 b'n' => Some(0x0a),
594 b't' => Some(0x09),
595 b'r' => Some(0x0d),
596 b'a' => Some(0x07),
597 b'b' => Some(0x08),
598 b'f' => Some(0x0c),
599 b'v' => Some(0x0b),
600 b'\\' | b'\'' | b'"' | b'?' => Some(u32::from(byte)),
601 _ => None,
602 };
603 if let Some(value) = simple {
604 return Ok(Piece::Value(value));
605 }
606 match byte {
607 b'e' | b'E' => {
609 self.remarks = self.remarks.with(Remarks::NON_ISO_ESCAPE);
610 Ok(Piece::Value(0x1b))
611 }
612 b'0'..=b'7' => Ok(Piece::Value(self.octal(byte, width))),
613 b'x' => self.hex(width).map(Piece::Value),
614 b'u' | b'U' => self.ucn(byte).map(Piece::Char),
615 b'N' => Err(LiteralError::NamedUcn),
616 _ => {
619 self.remarks = self.remarks.with(Remarks::UNKNOWN_ESCAPE);
620 Ok(Piece::Value(u32::from(byte)))
621 }
622 }
623 }
624
625 fn octal(&mut self, first: u8, width: u32) -> u32 {
628 let mut value = u32::from(first - b'0');
629 for _ in 0..2 {
630 match self.bytes.get(self.index) {
631 Some(&byte @ b'0'..=b'7') => {
632 value = value * 8 + u32::from(byte - b'0');
633 self.index += 1;
634 }
635 _ => break,
636 }
637 }
638 self.fit(value, width, Remarks::OCTAL_ESCAPE_OUT_OF_RANGE)
639 }
640
641 fn hex(&mut self, width: u32) -> Result<u32, LiteralError> {
643 let mut value: u64 = 0;
644 let mut digits = 0;
645 while let Some(digit) = self.bytes.get(self.index).and_then(|&byte| hex_digit(byte)) {
646 value = value.saturating_mul(16).saturating_add(u64::from(digit));
649 digits += 1;
650 self.index += 1;
651 }
652 if digits == 0 {
653 return Err(LiteralError::NoHexDigits);
654 }
655 Ok(self.fit(
656 u32::try_from(value).unwrap_or(u32::MAX),
657 width,
658 Remarks::HEX_ESCAPE_OUT_OF_RANGE,
659 ))
660 }
661
662 fn ucn(&mut self, marker: u8) -> Result<u32, LiteralError> {
664 if self.bytes.get(self.index) == Some(&b'{') {
665 return Err(LiteralError::NamedUcn);
666 }
667 let digits = if marker == b'u' { 4 } else { 8 };
668 let mut value: u32 = 0;
669 for _ in 0..digits {
670 let Some(digit) = self.bytes.get(self.index).and_then(|&byte| hex_digit(byte)) else {
671 return Err(LiteralError::IncompleteUcn);
672 };
673 value = value * 16 + digit;
674 self.index += 1;
675 }
676 let allowed_low = matches!(value, 0x24 | 0x40 | 0x60);
680 if (value < 0xa0 && !allowed_low) || (0xd800..=0xdfff).contains(&value) || value > 0x10ffff
681 {
682 return Err(LiteralError::InvalidUcn);
683 }
684 if self.std < Std::C99 {
685 self.remarks = self.remarks.with(Remarks::UCN);
686 }
687 Ok(value)
688 }
689
690 fn fit(&mut self, value: u32, width: u32, out_of_range: Remarks) -> u32 {
694 if width >= 32 {
695 return value;
696 }
697 let mask = (1u32 << width) - 1;
698 if value & !mask != 0 {
699 self.remarks = self.remarks.with(out_of_range);
700 }
701 value & mask
702 }
703}
704
705fn hex_digit(byte: u8) -> Option<u32> {
707 char::from(byte).to_digit(16)
708}
709
710fn utf8_length(byte: u8) -> Option<usize> {
713 match byte {
714 0x00..=0x7f => Some(1),
715 0xc2..=0xdf => Some(2),
716 0xe0..=0xef => Some(3),
717 0xf0..=0xf4 => Some(4),
718 _ => None,
719 }
720}
721
722#[cfg(test)]
723mod tests {
724 use rucc_target::Triple;
725
726 use super::*;
727
728 fn linux() -> TargetInfo {
729 TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a known triple"))
730 }
731
732 fn windows() -> TargetInfo {
733 TargetInfo::new("x86_64-pc-windows-msvc".parse::<Triple>().expect("a known triple"))
734 }
735
736 fn arm() -> TargetInfo {
737 TargetInfo::new("aarch64-unknown-linux-gnu".parse::<Triple>().expect("a known triple"))
738 }
739
740 fn ch(text: &str) -> i64 {
742 character(text, Std::C23, false, &linux()).expect("a character constant").value
743 }
744
745 fn ch_remarks(text: &str) -> Remarks {
747 character(text, Std::C23, false, &linux()).expect("a character constant").remarks
748 }
749
750 fn ch_error(text: &str) -> LiteralError {
752 character(text, Std::C23, false, &linux()).expect_err("not a character constant")
753 }
754
755 fn str_elements(text: &str) -> Vec<u32> {
757 string(text, Std::C23, false, &linux()).expect("a string literal").elements
758 }
759
760 fn str_bytes(text: &str) -> Vec<u8> {
762 string(text, Std::C23, false, &linux()).expect("a string literal").bytes(&linux())
763 }
764
765 #[test]
766 fn the_ordinary_cases_are_the_characters_they_look_like() {
767 assert_eq!(ch("'a'"), 0x61);
768 assert_eq!(ch(r"'\n'"), 0x0a);
769 assert_eq!(ch(r"'\0'"), 0);
770 assert_eq!(ch(r"'\\'"), 0x5c);
771 assert_eq!(ch(r"'\''"), 0x27);
772 assert_eq!(ch(r#"'\"'"#), 0x22);
773 assert_eq!(ch(r"'\?'"), 0x3f);
774 assert_eq!(str_elements(r#""hi""#), vec![0x68, 0x69]);
775 }
776
777 #[test]
781 fn a_high_character_takes_the_sign_of_plain_char() {
782 assert_eq!(ch(r"'\xff'"), -1);
783 assert_eq!(ch(r"'\377'"), -1);
784 assert_eq!(character(r"'\xff'", Std::C23, false, &arm()).expect("a constant").value, 255);
785 assert_eq!(ch(r"u8'\xff'"), 255);
787 }
788
789 #[test]
793 fn an_escape_too_big_for_its_element_is_truncated_and_says_so() {
794 let out = character(r"'\x1ff'", Std::C23, false, &linux()).expect("a constant");
795 assert_eq!(out.value, -1);
796 assert!(out.remarks.has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
797 let out = character(r"'\400'", Std::C23, false, &linux()).expect("a constant");
798 assert_eq!(out.value, 0);
799 assert!(out.remarks.has(Remarks::OCTAL_ESCAPE_OUT_OF_RANGE));
800 assert!(!out.remarks.has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
801 assert!(!ch_remarks(r"L'\x1ff'").has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
803 assert_eq!(ch(r"L'\x1ff'"), 0x1ff);
804 }
805
806 #[test]
810 fn adjacent_literals_agree_on_one_encoding_or_none_at_all() {
811 let target = linux();
812 let wide = strings(&[r#"L"a""#, r#""b""#], Std::C23, false, &target).expect("a string");
813 assert_eq!(wide.encoding, Encoding::Wide);
814 assert_eq!(wide.elements, vec![0x61, 0x62]);
815 assert_eq!(wide.bytes(&target).len(), 12);
816 let other_way =
817 strings(&[r#""a""#, r#"L"b""#], Std::C23, false, &target).expect("a string");
818 assert_eq!(other_way.encoding, Encoding::Wide);
819 assert_eq!(other_way.bytes(&target).len(), 12);
820
821 let u8_run = strings(&[r#"u8"a""#, r#""b""#], Std::C23, false, &target).expect("a string");
822 assert_eq!(u8_run.encoding, Encoding::Utf8);
823 assert_eq!(u8_run.bytes(&target).len(), 3);
824
825 let mixed = strings(&[r#"L"a""#, r#""é""#], Std::C23, false, &target).expect("a string");
827 assert_eq!(mixed.elements, vec![0x61, 0xe9]);
828
829 for run in [[r#"u8"a""#, r#"u"b""#], [r#"u8"a""#, r#"L"b""#], [r#"u"a""#, r#"L"b""#]] {
830 assert_eq!(
831 strings(&run, Std::C23, false, &target).expect_err("two prefixes in one run"),
832 LiteralError::MixedEncodings
833 );
834 }
835
836 assert_eq!(
838 strings(&[r#""hi""#], Std::C23, false, &target).expect("a string").elements,
839 vec![0x68, 0x69]
840 );
841 }
842
843 #[test]
847 fn more_than_one_character_shifts_them_together() {
848 assert_eq!(ch("'ab'"), 0x6162);
849 assert_eq!(ch("'abc'"), 0x616263);
850 assert_eq!(ch("'abcd'"), 0x61626364);
851 assert_eq!(ch("'abcde'"), 0x62636465);
852 assert_eq!(ch(r"'\xff\xfe'"), 0xfffe);
853 assert_eq!(ch(r"'\xff\xff\xff\xff'"), -1);
854 assert_eq!(ch(r"'\x80\x00'"), 0x8000);
855
856 assert!(ch_remarks("'ab'").has(Remarks::MULTICHARACTER));
857 assert!(ch_remarks("'abcd'").has(Remarks::MULTICHARACTER));
858 assert!(ch_remarks("'abcde'").has(Remarks::TOO_LONG));
859 assert!(!ch_remarks("'abcde'").has(Remarks::MULTICHARACTER));
860 assert!(!ch_remarks("'a'").has(Remarks::MULTICHARACTER));
861 }
862
863 #[test]
866 fn a_prefixed_constant_holds_one_character_and_keeps_the_last() {
867 for text in [r"L'ab'", r"u'ab'", r"U'ab'"] {
868 let out = character(text, Std::C23, false, &linux()).expect("a constant");
869 assert_eq!(out.value, 0x62, "{text}");
870 assert!(out.remarks.has(Remarks::TOO_LONG), "{text}");
871 }
872 assert_eq!(ch_error("u8'ab'"), LiteralError::TooLong);
873 assert_eq!(ch_error("u8'é'"), LiteralError::TooLong);
874 }
875
876 #[test]
877 fn the_empty_constant_has_no_value_to_have() {
878 assert_eq!(ch_error("''"), LiteralError::Empty);
879 assert_eq!(ch_error("L''"), LiteralError::Empty);
880 assert_eq!(str_elements(r#""""#), Vec::<u32>::new());
882 assert_eq!(str_bytes(r#""""#), vec![0]);
883 }
884
885 #[test]
888 fn a_source_character_is_encoded_and_an_escape_is_not() {
889 assert_eq!(ch("'é'"), 0xc3a9);
890 assert_eq!(ch("L'é'"), 0xe9);
891 assert_eq!(ch("u'€'"), 0x20ac);
892 assert_eq!(ch(r"U'\U0001F600'"), 0x1f600);
893 assert_eq!(ch(r"'\U0001F600'"), i64::from(0xf09f_9880u32 as i32));
896 assert!(ch_remarks(r"'\U0001F600'").has(Remarks::MULTICHARACTER));
897 }
898
899 #[test]
902 fn the_escapes_outside_the_standard_still_have_values() {
903 assert_eq!(ch(r"'\e'"), 0x1b);
904 assert!(ch_remarks(r"'\e'").has(Remarks::NON_ISO_ESCAPE));
905 assert_eq!(ch(r"'\q'"), 0x71);
906 assert!(ch_remarks(r"'\q'").has(Remarks::UNKNOWN_ESCAPE));
907 assert_eq!(ch_error(r"'\x'"), LiteralError::NoHexDigits);
908 assert_eq!(ch_error(r"'\N{LATIN SMALL LETTER A}'"), LiteralError::NamedUcn);
909 }
910
911 #[test]
915 fn a_universal_character_name_may_not_name_just_anything() {
916 assert_eq!(ch("'\\u0024'"), 0x24);
917 assert_eq!(ch("'\\u00e9'"), 0xc3a9);
918 assert_eq!(ch_error("'\\u0041'"), LiteralError::InvalidUcn);
919 assert_eq!(ch_error(r"'\ud800'"), LiteralError::InvalidUcn);
920 assert_eq!(ch_error(r"'\u00'"), LiteralError::IncompleteUcn);
921 assert_eq!(ch_error(r"'\U00110000'"), LiteralError::InvalidUcn);
923 }
924
925 #[test]
926 fn a_universal_character_name_before_c99_is_worth_a_remark() {
927 let out = character("'\\u00e9'", Std::C89, false, &linux()).expect("a constant");
928 assert!(out.remarks.has(Remarks::UCN));
929 let out = character("'\\u00e9'", Std::C99, false, &linux()).expect("a constant");
930 assert!(!out.remarks.has(Remarks::UCN));
931 }
932
933 #[test]
936 fn an_octal_escape_ends_and_a_hex_escape_does_not() {
937 assert_eq!(str_elements(r#""\1234""#), vec![0x53, 0x34]);
938 assert_eq!(str_elements(r#""\x41z""#), vec![0x41, 0x7a]);
939 assert_eq!(str_elements(r#""\x41""#), vec![0x41]);
940 }
941
942 #[test]
945 fn a_string_is_as_many_bytes_as_its_encoding_makes_it() {
946 assert_eq!(str_bytes(r#""abc""#).len(), 4);
947 assert_eq!(str_bytes(r#"L"abc""#).len(), 16);
948 assert_eq!(str_bytes(r#"u"abc""#).len(), 8);
949 assert_eq!(str_bytes(r#"U"abc""#).len(), 16);
950 assert_eq!(str_bytes(r#"u8"abc""#).len(), 4);
951 assert_eq!(str_bytes(r#""a\0b""#), vec![0x61, 0x00, 0x62, 0x00]);
953 assert_eq!(str_bytes(r#""é""#), vec![0xc3, 0xa9, 0x00]);
954 }
955
956 #[test]
959 fn utf16_splits_the_characters_that_do_not_fit_into_a_surrogate_pair() {
960 assert_eq!(
961 str_elements(r#"u8"é€😀""#),
962 vec![0xc3, 0xa9, 0xe2, 0x82, 0xac, 0xf0, 0x9f, 0x98, 0x80]
963 );
964 assert_eq!(str_elements(r#"u"€😀""#), vec![0x20ac, 0xd83d, 0xde00]);
965 assert_eq!(str_elements(r#"U"€😀""#), vec![0x20ac, 0x1f600]);
966 }
967
968 #[test]
971 fn a_wide_literal_is_whatever_the_target_makes_wchar_t() {
972 let text = r#"L"a😀""#;
973 let here = string(text, Std::C23, false, &linux()).expect("a string");
974 assert_eq!(here.elements, vec![0x61, 0x1f600]);
975 assert_eq!(here.bytes(&linux()).len(), 12);
976 let there = string(text, Std::C23, false, &windows()).expect("a string");
977 assert_eq!(there.elements, vec![0x61, 0xd83d, 0xde00]);
978 assert_eq!(there.bytes(&windows()).len(), 8);
979 assert_eq!(
982 character(r"L'\xffffffff'", Std::C23, false, &linux()).expect("a constant").value,
983 -1
984 );
985 assert_eq!(
986 character(r"L'\xffffffff'", Std::C23, false, &arm()).expect("a constant").value,
987 0xffff_ffff
988 );
989 }
990
991 #[test]
995 fn the_bytes_come_out_in_the_targets_order() {
996 let mut big = linux();
997 big.little_endian = false;
998 let literal = string(r#"u"ab""#, Std::C23, false, &big).expect("a string");
999 assert_eq!(literal.bytes(&big), vec![0x00, 0x61, 0x00, 0x62, 0x00, 0x00]);
1000 assert_eq!(literal.bytes(&linux()), vec![0x61, 0x00, 0x62, 0x00, 0x00, 0x00]);
1001 }
1002
1003 #[test]
1006 fn a_prefix_is_only_available_in_the_dialect_that_has_it() {
1007 assert!(character("L'a'", Std::C89, false, &linux()).is_ok());
1008 assert_eq!(
1009 character("u'a'", Std::C99, false, &linux()).expect_err("not in C99"),
1010 LiteralError::PrefixNotInDialect
1011 );
1012 assert!(character("u'a'", Std::C11, false, &linux()).is_ok());
1013 assert!(string(r#"u8"a""#, Std::C11, false, &linux()).is_ok());
1014 assert_eq!(
1015 character("u8'a'", Std::C11, false, &linux()).expect_err("not in C11"),
1016 LiteralError::PrefixNotInDialect
1017 );
1018 assert!(character("u8'a'", Std::C23, false, &linux()).is_ok());
1019 }
1020
1021 #[test]
1024 fn the_gnu_dialects_have_the_string_prefixes_earlier_and_the_character_one_at_the_same_time() {
1025 assert!(string(r#"u8"a""#, Std::C99, true, &linux()).is_ok());
1026 assert!(string(r#"u"a""#, Std::C99, true, &linux()).is_ok());
1027 assert!(string(r#"U"a""#, Std::C99, true, &linux()).is_ok());
1028 assert!(character("u'a'", Std::C99, true, &linux()).is_ok());
1029 assert_eq!(
1030 string(r#"u8"a""#, Std::C89, true, &linux()).expect_err("not in gnu89"),
1031 LiteralError::PrefixNotInDialect
1032 );
1033 assert_eq!(
1034 character("u8'a'", Std::C17, true, &linux()).expect_err("not in gnu17"),
1035 LiteralError::PrefixNotInDialect
1036 );
1037 }
1038
1039 #[test]
1042 fn an_element_is_as_wide_as_the_encoding_and_the_target_agree() {
1043 let target = linux();
1044 assert_eq!(Encoding::Plain.element_width(&target), 8);
1045 assert_eq!(Encoding::Utf8.element_width(&target), 8);
1046 assert_eq!(Encoding::Utf16.element_width(&target), 16);
1047 assert_eq!(Encoding::Utf32.element_width(&target), 32);
1048 assert_eq!(Encoding::Wide.element_width(&target), 32);
1049 assert_eq!(Encoding::Wide.element_width(&windows()), 16);
1050
1051 assert!(Encoding::Plain.is_signed(&target));
1052 assert!(!Encoding::Plain.is_signed(&arm()));
1053 assert!(Encoding::Wide.is_signed(&target));
1054 assert!(!Encoding::Wide.is_signed(&arm()));
1055 assert!(!Encoding::Utf8.is_signed(&target));
1056 assert!(!Encoding::Utf16.is_signed(&target));
1057 assert!(!Encoding::Utf32.is_signed(&target));
1058 }
1059
1060 #[test]
1061 fn a_spelling_that_is_not_a_literal_is_refused_rather_than_guessed_at() {
1062 assert_eq!(ch_error("a"), LiteralError::NotALiteral);
1063 assert_eq!(ch_error("'a"), LiteralError::NotALiteral);
1064 assert_eq!(
1065 string("'a'", Std::C23, false, &linux()).expect_err("not a string"),
1066 LiteralError::NotALiteral
1067 );
1068 assert_eq!(ch_error("'"), LiteralError::NotALiteral);
1069 }
1070
1071 #[test]
1072 fn every_error_has_something_to_print() {
1073 for error in [
1074 LiteralError::NotALiteral,
1075 LiteralError::Empty,
1076 LiteralError::TooLong,
1077 LiteralError::NoHexDigits,
1078 LiteralError::IncompleteUcn,
1079 LiteralError::InvalidUcn,
1080 LiteralError::NamedUcn,
1081 LiteralError::InvalidUtf8,
1082 LiteralError::PrefixNotInDialect,
1083 LiteralError::MixedEncodings,
1084 ] {
1085 assert!(!error.message().is_empty());
1086 }
1087 }
1088}