1use rucc_session::Std;
67use rucc_target::TargetInfo;
68
69use crate::remarks::Remarks;
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum Encoding {
74 Plain,
76 Wide,
78 Utf8,
80 Utf16,
82 Utf32,
84}
85
86impl Encoding {
87 #[must_use]
89 pub fn element_width(self, target: &TargetInfo) -> u32 {
90 match self {
91 Encoding::Plain | Encoding::Utf8 => 8,
92 Encoding::Wide => target.wchar_width,
93 Encoding::Utf16 => 16,
94 Encoding::Utf32 => 32,
95 }
96 }
97
98 #[must_use]
100 pub fn is_signed(self, target: &TargetInfo) -> bool {
101 match self {
102 Encoding::Plain => target.char_is_signed,
103 Encoding::Wide => target.wchar_is_signed,
104 Encoding::Utf8 | Encoding::Utf16 | Encoding::Utf32 => false,
105 }
106 }
107
108 #[must_use]
110 pub const fn prefix(self) -> &'static str {
111 match self {
112 Encoding::Plain => "",
113 Encoding::Wide => "L",
114 Encoding::Utf8 => "u8",
115 Encoding::Utf16 => "u",
116 Encoding::Utf32 => "U",
117 }
118 }
119
120 #[must_use]
123 pub fn read_prefix(text: &str) -> Encoding {
124 Encoding::read(text.as_bytes()).0
125 }
126
127 fn read(bytes: &[u8]) -> (Encoding, usize) {
129 match bytes {
130 [b'u', b'8', ..] => (Encoding::Utf8, 2),
131 [b'u', ..] => (Encoding::Utf16, 1),
132 [b'U', ..] => (Encoding::Utf32, 1),
133 [b'L', ..] => (Encoding::Wide, 1),
134 _ => (Encoding::Plain, 0),
135 }
136 }
137
138 fn since(self, character: bool) -> Std {
141 match self {
142 Encoding::Plain | Encoding::Wide => Std::C89,
143 Encoding::Utf8 if character => Std::C23,
144 Encoding::Utf8 | Encoding::Utf16 | Encoding::Utf32 => Std::C11,
145 }
146 }
147}
148
149#[derive(Debug, Clone, Copy, PartialEq, Eq)]
151pub struct CharConstant {
152 pub value: i64,
155 pub encoding: Encoding,
157 pub remarks: Remarks,
159}
160
161#[derive(Debug, Clone, PartialEq, Eq)]
163pub struct StringLiteral {
164 pub elements: Vec<u32>,
168 pub encoding: Encoding,
170 pub remarks: Remarks,
172}
173
174impl StringLiteral {
175 #[must_use]
178 pub fn bytes(&self, target: &TargetInfo) -> Vec<u8> {
179 let width = self.encoding.element_width(target) / 8;
180 let mut bytes = Vec::with_capacity((self.elements.len() + 1) * width as usize);
181 for element in self.elements.iter().copied().chain([0]) {
182 let taken = &element.to_le_bytes()[..width as usize];
183 if target.little_endian {
184 bytes.extend_from_slice(taken);
185 } else {
186 bytes.extend(taken.iter().rev());
187 }
188 }
189 bytes
190 }
191}
192
193#[derive(Debug, Clone, Copy, PartialEq, Eq)]
195pub enum LiteralError {
196 NotALiteral,
199 Empty,
201 TooLong,
204 NoHexDigits,
206 IncompleteUcn,
208 InvalidUcn,
211 NamedUcn,
213 InvalidUtf8,
216 PrefixNotInDialect,
218 MixedEncodings,
221}
222
223impl LiteralError {
224 #[must_use]
226 pub const fn message(self) -> &'static str {
227 match self {
228 LiteralError::NotALiteral => "not a character constant or a string literal",
229 LiteralError::Empty => "empty character constant",
230 LiteralError::TooLong => "character constant too long for its type",
231 LiteralError::NoHexDigits => "\\x used with no following hex digits",
232 LiteralError::IncompleteUcn => "incomplete universal character name",
233 LiteralError::InvalidUcn => "not a valid universal character",
234 LiteralError::NamedUcn => "named universal character escapes are not supported yet",
235 LiteralError::InvalidUtf8 => "failure to convert the source to the execution charset",
236 LiteralError::PrefixNotInDialect => {
237 "this encoding prefix is not available in this dialect"
238 }
239 LiteralError::MixedEncodings => {
240 "unsupported non-standard concatenation of string literals"
241 }
242 }
243 }
244}
245
246pub fn character(text: &str, std: Std, target: &TargetInfo) -> Result<CharConstant, LiteralError> {
253 let (encoding, body) = open(text, b'\'', std, true)?;
254 let width = encoding.element_width(target);
255 let mut reader = Reader { bytes: body, index: 0, std, remarks: Remarks::NONE };
256
257 let mut value: u64 = 0;
261 let mut count = 0u32;
262 while let Some(piece) = reader.next(width)? {
263 for element in piece.elements(width) {
264 value = (value << width) | u64::from(element);
265 count += 1;
266 }
267 }
268 let mut remarks = reader.remarks;
269
270 let type_width = if encoding == Encoding::Plain { 32 } else { width };
273 let capacity = type_width / width;
274 match count {
275 0 => return Err(LiteralError::Empty),
276 1 => {}
277 _ if encoding == Encoding::Utf8 => return Err(LiteralError::TooLong),
278 _ if count > capacity => remarks = remarks.with(Remarks::TOO_LONG),
279 _ => remarks = remarks.with(Remarks::MULTICHARACTER),
280 }
281
282 let (bits, signed) = if count == 1 {
286 (width, encoding.is_signed(target))
287 } else {
288 (type_width, encoding == Encoding::Plain || encoding.is_signed(target))
289 };
290 Ok(CharConstant { value: narrow(value, bits, signed), encoding, remarks })
291}
292
293pub fn string(text: &str, std: Std, target: &TargetInfo) -> Result<StringLiteral, LiteralError> {
300 strings(std::slice::from_ref(&text), std, target)
301}
302
303pub fn strings(
319 texts: &[&str],
320 std: Std,
321 target: &TargetInfo,
322) -> Result<StringLiteral, LiteralError> {
323 let mut bodies = Vec::with_capacity(texts.len());
324 let mut encoding = Encoding::Plain;
325 for text in texts {
326 let (found, body) = open(text, b'"', std, false)?;
327 if found != Encoding::Plain {
328 if encoding != Encoding::Plain && encoding != found {
329 return Err(LiteralError::MixedEncodings);
330 }
331 encoding = found;
332 }
333 bodies.push(body);
334 }
335
336 let width = encoding.element_width(target);
337 let mut elements = Vec::new();
338 let mut remarks = Remarks::NONE;
339 for body in bodies {
340 let mut reader = Reader { bytes: body, index: 0, std, remarks: Remarks::NONE };
341 while let Some(piece) = reader.next(width)? {
342 elements.extend(piece.elements(width));
343 }
344 remarks = remarks.with(reader.remarks);
345 }
346 Ok(StringLiteral { elements, encoding, remarks })
347}
348
349fn open(
351 text: &str,
352 quote: u8,
353 std: Std,
354 character: bool,
355) -> Result<(Encoding, &[u8]), LiteralError> {
356 let bytes = text.as_bytes();
357 let (encoding, prefix) = Encoding::read(bytes);
358 if std < encoding.since(character) {
359 return Err(LiteralError::PrefixNotInDialect);
360 }
361 let rest = &bytes[prefix..];
362 match rest {
363 [first, .., last] if *first == quote && *last == quote => {
364 Ok((encoding, &rest[1..rest.len() - 1]))
365 }
366 _ => Err(LiteralError::NotALiteral),
367 }
368}
369
370fn narrow(value: u64, bits: u32, signed: bool) -> i64 {
372 let masked = if bits >= 64 { value } else { value & ((1u64 << bits) - 1) };
373 if signed && bits < 64 && masked >> (bits - 1) & 1 == 1 {
374 (masked | !((1u64 << bits) - 1)) as i64
376 } else {
377 masked as i64
378 }
379}
380
381#[derive(Debug, Clone, Copy, PartialEq, Eq)]
383enum Piece {
384 Char(u32),
387 Value(u32),
390}
391
392impl Piece {
393 fn elements(self, width: u32) -> Vec<u32> {
395 let code = match self {
396 Piece::Value(value) => return vec![value],
397 Piece::Char(code) => code,
398 };
399 match width {
400 8 => {
401 let mut buffer = [0u8; 4];
402 let text = char::from_u32(code)
403 .map(|character| character.encode_utf8(&mut buffer).len())
404 .unwrap_or(0);
405 buffer[..text].iter().map(|&byte| u32::from(byte)).collect()
406 }
407 16 if code > 0xffff => {
410 let value = code - 0x1_0000;
411 vec![0xd800 + (value >> 10), 0xdc00 + (value & 0x3ff)]
412 }
413 _ => vec![code],
414 }
415 }
416}
417
418struct Reader<'a> {
420 bytes: &'a [u8],
422 index: usize,
424 std: Std,
426 remarks: Remarks,
428}
429
430impl Reader<'_> {
431 fn next(&mut self, width: u32) -> Result<Option<Piece>, LiteralError> {
433 let Some(&byte) = self.bytes.get(self.index) else {
434 return Ok(None);
435 };
436 self.index += 1;
437 if byte == b'\\' {
438 return self.escape(width).map(Some);
439 }
440 if byte < 0x80 {
441 return Ok(Some(Piece::Char(u32::from(byte))));
442 }
443 if width == 8 {
447 return Ok(Some(Piece::Value(u32::from(byte))));
448 }
449 let length = utf8_length(byte).ok_or(LiteralError::InvalidUtf8)?;
452 let end = self.index - 1 + length;
453 let text = self
454 .bytes
455 .get(self.index - 1..end)
456 .and_then(|slice| std::str::from_utf8(slice).ok())
457 .ok_or(LiteralError::InvalidUtf8)?;
458 let character = text.chars().next().ok_or(LiteralError::InvalidUtf8)?;
459 self.index = end;
460 Ok(Some(Piece::Char(character as u32)))
461 }
462
463 fn escape(&mut self, width: u32) -> Result<Piece, LiteralError> {
465 let Some(&byte) = self.bytes.get(self.index) else {
466 return Err(LiteralError::NotALiteral);
468 };
469 self.index += 1;
470 let simple = match byte {
471 b'n' => Some(0x0a),
472 b't' => Some(0x09),
473 b'r' => Some(0x0d),
474 b'a' => Some(0x07),
475 b'b' => Some(0x08),
476 b'f' => Some(0x0c),
477 b'v' => Some(0x0b),
478 b'\\' | b'\'' | b'"' | b'?' => Some(u32::from(byte)),
479 _ => None,
480 };
481 if let Some(value) = simple {
482 return Ok(Piece::Value(value));
483 }
484 match byte {
485 b'e' | b'E' => {
487 self.remarks = self.remarks.with(Remarks::NON_ISO_ESCAPE);
488 Ok(Piece::Value(0x1b))
489 }
490 b'0'..=b'7' => Ok(Piece::Value(self.octal(byte, width))),
491 b'x' => self.hex(width).map(Piece::Value),
492 b'u' | b'U' => self.ucn(byte).map(Piece::Char),
493 b'N' => Err(LiteralError::NamedUcn),
494 _ => {
497 self.remarks = self.remarks.with(Remarks::UNKNOWN_ESCAPE);
498 Ok(Piece::Value(u32::from(byte)))
499 }
500 }
501 }
502
503 fn octal(&mut self, first: u8, width: u32) -> u32 {
506 let mut value = u32::from(first - b'0');
507 for _ in 0..2 {
508 match self.bytes.get(self.index) {
509 Some(&byte @ b'0'..=b'7') => {
510 value = value * 8 + u32::from(byte - b'0');
511 self.index += 1;
512 }
513 _ => break,
514 }
515 }
516 self.fit(value, width, Remarks::OCTAL_ESCAPE_OUT_OF_RANGE)
517 }
518
519 fn hex(&mut self, width: u32) -> Result<u32, LiteralError> {
521 let mut value: u64 = 0;
522 let mut digits = 0;
523 while let Some(digit) = self.bytes.get(self.index).and_then(|&byte| hex_digit(byte)) {
524 value = value.saturating_mul(16).saturating_add(u64::from(digit));
527 digits += 1;
528 self.index += 1;
529 }
530 if digits == 0 {
531 return Err(LiteralError::NoHexDigits);
532 }
533 Ok(self.fit(
534 u32::try_from(value).unwrap_or(u32::MAX),
535 width,
536 Remarks::HEX_ESCAPE_OUT_OF_RANGE,
537 ))
538 }
539
540 fn ucn(&mut self, marker: u8) -> Result<u32, LiteralError> {
542 if self.bytes.get(self.index) == Some(&b'{') {
543 return Err(LiteralError::NamedUcn);
544 }
545 let digits = if marker == b'u' { 4 } else { 8 };
546 let mut value: u32 = 0;
547 for _ in 0..digits {
548 let Some(digit) = self.bytes.get(self.index).and_then(|&byte| hex_digit(byte)) else {
549 return Err(LiteralError::IncompleteUcn);
550 };
551 value = value * 16 + digit;
552 self.index += 1;
553 }
554 let allowed_low = matches!(value, 0x24 | 0x40 | 0x60);
558 if (value < 0xa0 && !allowed_low) || (0xd800..=0xdfff).contains(&value) || value > 0x10ffff
559 {
560 return Err(LiteralError::InvalidUcn);
561 }
562 if self.std < Std::C99 {
563 self.remarks = self.remarks.with(Remarks::UCN);
564 }
565 Ok(value)
566 }
567
568 fn fit(&mut self, value: u32, width: u32, out_of_range: Remarks) -> u32 {
572 if width >= 32 {
573 return value;
574 }
575 let mask = (1u32 << width) - 1;
576 if value & !mask != 0 {
577 self.remarks = self.remarks.with(out_of_range);
578 }
579 value & mask
580 }
581}
582
583fn hex_digit(byte: u8) -> Option<u32> {
585 char::from(byte).to_digit(16)
586}
587
588fn utf8_length(byte: u8) -> Option<usize> {
591 match byte {
592 0x00..=0x7f => Some(1),
593 0xc2..=0xdf => Some(2),
594 0xe0..=0xef => Some(3),
595 0xf0..=0xf4 => Some(4),
596 _ => None,
597 }
598}
599
600#[cfg(test)]
601mod tests {
602 use rucc_target::Triple;
603
604 use super::*;
605
606 fn linux() -> TargetInfo {
607 TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a known triple"))
608 }
609
610 fn windows() -> TargetInfo {
611 TargetInfo::new("x86_64-pc-windows-msvc".parse::<Triple>().expect("a known triple"))
612 }
613
614 fn arm() -> TargetInfo {
615 TargetInfo::new("aarch64-unknown-linux-gnu".parse::<Triple>().expect("a known triple"))
616 }
617
618 fn ch(text: &str) -> i64 {
620 character(text, Std::C23, &linux()).expect("a character constant").value
621 }
622
623 fn ch_remarks(text: &str) -> Remarks {
625 character(text, Std::C23, &linux()).expect("a character constant").remarks
626 }
627
628 fn ch_error(text: &str) -> LiteralError {
630 character(text, Std::C23, &linux()).expect_err("not a character constant")
631 }
632
633 fn str_elements(text: &str) -> Vec<u32> {
635 string(text, Std::C23, &linux()).expect("a string literal").elements
636 }
637
638 fn str_bytes(text: &str) -> Vec<u8> {
640 string(text, Std::C23, &linux()).expect("a string literal").bytes(&linux())
641 }
642
643 #[test]
644 fn the_ordinary_cases_are_the_characters_they_look_like() {
645 assert_eq!(ch("'a'"), 0x61);
646 assert_eq!(ch(r"'\n'"), 0x0a);
647 assert_eq!(ch(r"'\0'"), 0);
648 assert_eq!(ch(r"'\\'"), 0x5c);
649 assert_eq!(ch(r"'\''"), 0x27);
650 assert_eq!(ch(r#"'\"'"#), 0x22);
651 assert_eq!(ch(r"'\?'"), 0x3f);
652 assert_eq!(str_elements(r#""hi""#), vec![0x68, 0x69]);
653 }
654
655 #[test]
659 fn a_high_character_takes_the_sign_of_plain_char() {
660 assert_eq!(ch(r"'\xff'"), -1);
661 assert_eq!(ch(r"'\377'"), -1);
662 assert_eq!(character(r"'\xff'", Std::C23, &arm()).expect("a constant").value, 255);
663 assert_eq!(ch(r"u8'\xff'"), 255);
665 }
666
667 #[test]
671 fn an_escape_too_big_for_its_element_is_truncated_and_says_so() {
672 let out = character(r"'\x1ff'", Std::C23, &linux()).expect("a constant");
673 assert_eq!(out.value, -1);
674 assert!(out.remarks.has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
675 let out = character(r"'\400'", Std::C23, &linux()).expect("a constant");
676 assert_eq!(out.value, 0);
677 assert!(out.remarks.has(Remarks::OCTAL_ESCAPE_OUT_OF_RANGE));
678 assert!(!out.remarks.has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
679 assert!(!ch_remarks(r"L'\x1ff'").has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
681 assert_eq!(ch(r"L'\x1ff'"), 0x1ff);
682 }
683
684 #[test]
688 fn adjacent_literals_agree_on_one_encoding_or_none_at_all() {
689 let target = linux();
690 let wide = strings(&[r#"L"a""#, r#""b""#], Std::C23, &target).expect("a string");
691 assert_eq!(wide.encoding, Encoding::Wide);
692 assert_eq!(wide.elements, vec![0x61, 0x62]);
693 assert_eq!(wide.bytes(&target).len(), 12);
694 let other_way = strings(&[r#""a""#, r#"L"b""#], Std::C23, &target).expect("a string");
695 assert_eq!(other_way.encoding, Encoding::Wide);
696 assert_eq!(other_way.bytes(&target).len(), 12);
697
698 let u8_run = strings(&[r#"u8"a""#, r#""b""#], Std::C23, &target).expect("a string");
699 assert_eq!(u8_run.encoding, Encoding::Utf8);
700 assert_eq!(u8_run.bytes(&target).len(), 3);
701
702 let mixed = strings(&[r#"L"a""#, r#""é""#], Std::C23, &target).expect("a string");
704 assert_eq!(mixed.elements, vec![0x61, 0xe9]);
705
706 for run in [[r#"u8"a""#, r#"u"b""#], [r#"u8"a""#, r#"L"b""#], [r#"u"a""#, r#"L"b""#]] {
707 assert_eq!(
708 strings(&run, Std::C23, &target).expect_err("two prefixes in one run"),
709 LiteralError::MixedEncodings
710 );
711 }
712
713 assert_eq!(
715 strings(&[r#""hi""#], Std::C23, &target).expect("a string").elements,
716 vec![0x68, 0x69]
717 );
718 }
719
720 #[test]
724 fn more_than_one_character_shifts_them_together() {
725 assert_eq!(ch("'ab'"), 0x6162);
726 assert_eq!(ch("'abc'"), 0x616263);
727 assert_eq!(ch("'abcd'"), 0x61626364);
728 assert_eq!(ch("'abcde'"), 0x62636465);
729 assert_eq!(ch(r"'\xff\xfe'"), 0xfffe);
730 assert_eq!(ch(r"'\xff\xff\xff\xff'"), -1);
731 assert_eq!(ch(r"'\x80\x00'"), 0x8000);
732
733 assert!(ch_remarks("'ab'").has(Remarks::MULTICHARACTER));
734 assert!(ch_remarks("'abcd'").has(Remarks::MULTICHARACTER));
735 assert!(ch_remarks("'abcde'").has(Remarks::TOO_LONG));
736 assert!(!ch_remarks("'abcde'").has(Remarks::MULTICHARACTER));
737 assert!(!ch_remarks("'a'").has(Remarks::MULTICHARACTER));
738 }
739
740 #[test]
743 fn a_prefixed_constant_holds_one_character_and_keeps_the_last() {
744 for text in [r"L'ab'", r"u'ab'", r"U'ab'"] {
745 let out = character(text, Std::C23, &linux()).expect("a constant");
746 assert_eq!(out.value, 0x62, "{text}");
747 assert!(out.remarks.has(Remarks::TOO_LONG), "{text}");
748 }
749 assert_eq!(ch_error("u8'ab'"), LiteralError::TooLong);
750 assert_eq!(ch_error("u8'é'"), LiteralError::TooLong);
751 }
752
753 #[test]
754 fn the_empty_constant_has_no_value_to_have() {
755 assert_eq!(ch_error("''"), LiteralError::Empty);
756 assert_eq!(ch_error("L''"), LiteralError::Empty);
757 assert_eq!(str_elements(r#""""#), Vec::<u32>::new());
759 assert_eq!(str_bytes(r#""""#), vec![0]);
760 }
761
762 #[test]
765 fn a_source_character_is_encoded_and_an_escape_is_not() {
766 assert_eq!(ch("'é'"), 0xc3a9);
767 assert_eq!(ch("L'é'"), 0xe9);
768 assert_eq!(ch("u'€'"), 0x20ac);
769 assert_eq!(ch(r"U'\U0001F600'"), 0x1f600);
770 assert_eq!(ch(r"'\U0001F600'"), i64::from(0xf09f_9880u32 as i32));
773 assert!(ch_remarks(r"'\U0001F600'").has(Remarks::MULTICHARACTER));
774 }
775
776 #[test]
779 fn the_escapes_outside_the_standard_still_have_values() {
780 assert_eq!(ch(r"'\e'"), 0x1b);
781 assert!(ch_remarks(r"'\e'").has(Remarks::NON_ISO_ESCAPE));
782 assert_eq!(ch(r"'\q'"), 0x71);
783 assert!(ch_remarks(r"'\q'").has(Remarks::UNKNOWN_ESCAPE));
784 assert_eq!(ch_error(r"'\x'"), LiteralError::NoHexDigits);
785 assert_eq!(ch_error(r"'\N{LATIN SMALL LETTER A}'"), LiteralError::NamedUcn);
786 }
787
788 #[test]
792 fn a_universal_character_name_may_not_name_just_anything() {
793 assert_eq!(ch("'\\u0024'"), 0x24);
794 assert_eq!(ch("'\\u00e9'"), 0xc3a9);
795 assert_eq!(ch_error("'\\u0041'"), LiteralError::InvalidUcn);
796 assert_eq!(ch_error(r"'\ud800'"), LiteralError::InvalidUcn);
797 assert_eq!(ch_error(r"'\u00'"), LiteralError::IncompleteUcn);
798 assert_eq!(ch_error(r"'\U00110000'"), LiteralError::InvalidUcn);
800 }
801
802 #[test]
803 fn a_universal_character_name_before_c99_is_worth_a_remark() {
804 let out = character("'\\u00e9'", Std::C89, &linux()).expect("a constant");
805 assert!(out.remarks.has(Remarks::UCN));
806 let out = character("'\\u00e9'", Std::C99, &linux()).expect("a constant");
807 assert!(!out.remarks.has(Remarks::UCN));
808 }
809
810 #[test]
813 fn an_octal_escape_ends_and_a_hex_escape_does_not() {
814 assert_eq!(str_elements(r#""\1234""#), vec![0x53, 0x34]);
815 assert_eq!(str_elements(r#""\x41z""#), vec![0x41, 0x7a]);
816 assert_eq!(str_elements(r#""\x41""#), vec![0x41]);
817 }
818
819 #[test]
822 fn a_string_is_as_many_bytes_as_its_encoding_makes_it() {
823 assert_eq!(str_bytes(r#""abc""#).len(), 4);
824 assert_eq!(str_bytes(r#"L"abc""#).len(), 16);
825 assert_eq!(str_bytes(r#"u"abc""#).len(), 8);
826 assert_eq!(str_bytes(r#"U"abc""#).len(), 16);
827 assert_eq!(str_bytes(r#"u8"abc""#).len(), 4);
828 assert_eq!(str_bytes(r#""a\0b""#), vec![0x61, 0x00, 0x62, 0x00]);
830 assert_eq!(str_bytes(r#""é""#), vec![0xc3, 0xa9, 0x00]);
831 }
832
833 #[test]
836 fn utf16_splits_the_characters_that_do_not_fit_into_a_surrogate_pair() {
837 assert_eq!(
838 str_elements(r#"u8"é€😀""#),
839 vec![0xc3, 0xa9, 0xe2, 0x82, 0xac, 0xf0, 0x9f, 0x98, 0x80]
840 );
841 assert_eq!(str_elements(r#"u"€😀""#), vec![0x20ac, 0xd83d, 0xde00]);
842 assert_eq!(str_elements(r#"U"€😀""#), vec![0x20ac, 0x1f600]);
843 }
844
845 #[test]
848 fn a_wide_literal_is_whatever_the_target_makes_wchar_t() {
849 let text = r#"L"a😀""#;
850 let here = string(text, Std::C23, &linux()).expect("a string");
851 assert_eq!(here.elements, vec![0x61, 0x1f600]);
852 assert_eq!(here.bytes(&linux()).len(), 12);
853 let there = string(text, Std::C23, &windows()).expect("a string");
854 assert_eq!(there.elements, vec![0x61, 0xd83d, 0xde00]);
855 assert_eq!(there.bytes(&windows()).len(), 8);
856 assert_eq!(character(r"L'\xffffffff'", Std::C23, &linux()).expect("a constant").value, -1);
859 assert_eq!(
860 character(r"L'\xffffffff'", Std::C23, &arm()).expect("a constant").value,
861 0xffff_ffff
862 );
863 }
864
865 #[test]
869 fn the_bytes_come_out_in_the_targets_order() {
870 let mut big = linux();
871 big.little_endian = false;
872 let literal = string(r#"u"ab""#, Std::C23, &big).expect("a string");
873 assert_eq!(literal.bytes(&big), vec![0x00, 0x61, 0x00, 0x62, 0x00, 0x00]);
874 assert_eq!(literal.bytes(&linux()), vec![0x61, 0x00, 0x62, 0x00, 0x00, 0x00]);
875 }
876
877 #[test]
880 fn a_prefix_is_only_available_in_the_dialect_that_has_it() {
881 assert!(character("L'a'", Std::C89, &linux()).is_ok());
882 assert_eq!(
883 character("u'a'", Std::C99, &linux()).expect_err("not in C99"),
884 LiteralError::PrefixNotInDialect
885 );
886 assert!(character("u'a'", Std::C11, &linux()).is_ok());
887 assert!(string(r#"u8"a""#, Std::C11, &linux()).is_ok());
888 assert_eq!(
889 character("u8'a'", Std::C11, &linux()).expect_err("not in C11"),
890 LiteralError::PrefixNotInDialect
891 );
892 assert!(character("u8'a'", Std::C23, &linux()).is_ok());
893 }
894
895 #[test]
898 fn an_element_is_as_wide_as_the_encoding_and_the_target_agree() {
899 let target = linux();
900 assert_eq!(Encoding::Plain.element_width(&target), 8);
901 assert_eq!(Encoding::Utf8.element_width(&target), 8);
902 assert_eq!(Encoding::Utf16.element_width(&target), 16);
903 assert_eq!(Encoding::Utf32.element_width(&target), 32);
904 assert_eq!(Encoding::Wide.element_width(&target), 32);
905 assert_eq!(Encoding::Wide.element_width(&windows()), 16);
906
907 assert!(Encoding::Plain.is_signed(&target));
908 assert!(!Encoding::Plain.is_signed(&arm()));
909 assert!(Encoding::Wide.is_signed(&target));
910 assert!(!Encoding::Wide.is_signed(&arm()));
911 assert!(!Encoding::Utf8.is_signed(&target));
912 assert!(!Encoding::Utf16.is_signed(&target));
913 assert!(!Encoding::Utf32.is_signed(&target));
914 }
915
916 #[test]
917 fn a_spelling_that_is_not_a_literal_is_refused_rather_than_guessed_at() {
918 assert_eq!(ch_error("a"), LiteralError::NotALiteral);
919 assert_eq!(ch_error("'a"), LiteralError::NotALiteral);
920 assert_eq!(
921 string("'a'", Std::C23, &linux()).expect_err("not a string"),
922 LiteralError::NotALiteral
923 );
924 assert_eq!(ch_error("'"), LiteralError::NotALiteral);
925 }
926
927 #[test]
928 fn every_error_has_something_to_print() {
929 for error in [
930 LiteralError::NotALiteral,
931 LiteralError::Empty,
932 LiteralError::TooLong,
933 LiteralError::NoHexDigits,
934 LiteralError::IncompleteUcn,
935 LiteralError::InvalidUcn,
936 LiteralError::NamedUcn,
937 LiteralError::InvalidUtf8,
938 LiteralError::PrefixNotInDialect,
939 LiteralError::MixedEncodings,
940 ] {
941 assert!(!error.message().is_empty());
942 }
943 }
944}