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
161impl CharConstant {
162 #[must_use]
171 pub fn spell(self) -> String {
172 let mut out = String::from(self.encoding.prefix());
173 out.push('\'');
174 match self.encoding {
175 Encoding::Plain | Encoding::Utf8 if !(-128..=255).contains(&self.value) => {
176 let bits = self.value as u32;
177 let mut writing = false;
178 for shift in [24, 16, 8, 0] {
179 let byte = (bits >> shift) as u8;
180 writing |= byte != 0;
181 if writing {
182 out.push_str(&format!("\\x{byte:02x}"));
183 }
184 }
185 }
186 Encoding::Plain | Encoding::Utf8 => {
187 let byte = self.value as u8;
188 escape(u32::from(byte), '\'', &mut out);
189 }
190 _ => escape(self.value as u32, '\'', &mut out),
191 }
192 out.push('\'');
193 out
194 }
195}
196
197#[derive(Debug, Clone, PartialEq, Eq)]
199pub struct StringLiteral {
200 pub elements: Vec<u32>,
204 pub encoding: Encoding,
206 pub remarks: Remarks,
208}
209
210impl StringLiteral {
211 #[must_use]
214 pub fn bytes(&self, target: &TargetInfo) -> Vec<u8> {
215 let width = self.encoding.element_width(target) / 8;
216 let mut bytes = Vec::with_capacity((self.elements.len() + 1) * width as usize);
217 for element in self.elements.iter().copied().chain([0]) {
218 let taken = &element.to_le_bytes()[..width as usize];
219 if target.little_endian {
220 bytes.extend_from_slice(taken);
221 } else {
222 bytes.extend(taken.iter().rev());
223 }
224 }
225 bytes
226 }
227
228 #[must_use]
235 pub fn spell(&self) -> String {
236 let prefix = self.encoding.prefix();
237 let wide = !matches!(self.encoding, Encoding::Plain | Encoding::Utf8);
238 let mut out = String::from(prefix);
239 out.push('"');
240 let mut ran_on = false;
241 for &element in &self.elements {
242 match printable(element) {
243 Some(ch) => {
244 if ran_on && ch.is_ascii_hexdigit() {
245 out.push('"');
246 out.push(' ');
247 out.push_str(prefix);
248 out.push('"');
249 }
250 escape(element, '"', &mut out);
251 ran_on = false;
252 }
253 None if wide => {
254 out.push_str(&format!("\\x{element:x}"));
255 ran_on = true;
256 }
257 None => {
258 out.push_str(&format!("\\{element:03o}"));
259 ran_on = false;
260 }
261 }
262 }
263 out.push('"');
264 out
265 }
266}
267
268fn printable(element: u32) -> Option<char> {
273 match element {
274 0x20..=0x7e => char::from_u32(element),
275 _ => None,
276 }
277}
278
279fn escape(element: u32, quote: char, out: &mut String) {
281 match printable(element) {
282 Some(ch) if ch == quote || ch == '\\' => {
283 out.push('\\');
284 out.push(ch);
285 }
286 Some('?') if out.ends_with('?') => out.push_str("\\?"),
289 Some(ch) => out.push(ch),
290 None => out.push_str(&format!("\\x{element:x}")),
291 }
292}
293
294#[derive(Debug, Clone, Copy, PartialEq, Eq)]
296pub enum LiteralError {
297 NotALiteral,
300 Empty,
302 TooLong,
305 NoHexDigits,
307 IncompleteUcn,
309 InvalidUcn,
312 NamedUcn,
314 InvalidUtf8,
317 PrefixNotInDialect,
319 MixedEncodings,
322}
323
324impl LiteralError {
325 #[must_use]
327 pub const fn message(self) -> &'static str {
328 match self {
329 LiteralError::NotALiteral => "not a character constant or a string literal",
330 LiteralError::Empty => "empty character constant",
331 LiteralError::TooLong => "character constant too long for its type",
332 LiteralError::NoHexDigits => "\\x used with no following hex digits",
333 LiteralError::IncompleteUcn => "incomplete universal character name",
334 LiteralError::InvalidUcn => "not a valid universal character",
335 LiteralError::NamedUcn => "named universal character escapes are not supported yet",
336 LiteralError::InvalidUtf8 => "failure to convert the source to the execution charset",
337 LiteralError::PrefixNotInDialect => {
338 "this encoding prefix is not available in this dialect"
339 }
340 LiteralError::MixedEncodings => {
341 "unsupported non-standard concatenation of string literals"
342 }
343 }
344 }
345}
346
347pub fn character(text: &str, std: Std, target: &TargetInfo) -> Result<CharConstant, LiteralError> {
354 let (encoding, body) = open(text, b'\'', std, true)?;
355 let width = encoding.element_width(target);
356 let mut reader = Reader { bytes: body, index: 0, std, remarks: Remarks::NONE };
357
358 let mut value: u64 = 0;
362 let mut count = 0u32;
363 while let Some(piece) = reader.next(width)? {
364 for element in piece.elements(width) {
365 value = (value << width) | u64::from(element);
366 count += 1;
367 }
368 }
369 let mut remarks = reader.remarks;
370
371 let type_width = if encoding == Encoding::Plain { 32 } else { width };
374 let capacity = type_width / width;
375 match count {
376 0 => return Err(LiteralError::Empty),
377 1 => {}
378 _ if encoding == Encoding::Utf8 => return Err(LiteralError::TooLong),
379 _ if count > capacity => remarks = remarks.with(Remarks::TOO_LONG),
380 _ => remarks = remarks.with(Remarks::MULTICHARACTER),
381 }
382
383 let (bits, signed) = if count == 1 {
387 (width, encoding.is_signed(target))
388 } else {
389 (type_width, encoding == Encoding::Plain || encoding.is_signed(target))
390 };
391 Ok(CharConstant { value: narrow(value, bits, signed), encoding, remarks })
392}
393
394pub fn string(text: &str, std: Std, target: &TargetInfo) -> Result<StringLiteral, LiteralError> {
401 strings(std::slice::from_ref(&text), std, target)
402}
403
404pub fn strings(
420 texts: &[&str],
421 std: Std,
422 target: &TargetInfo,
423) -> Result<StringLiteral, LiteralError> {
424 let mut bodies = Vec::with_capacity(texts.len());
425 let mut encoding = Encoding::Plain;
426 for text in texts {
427 let (found, body) = open(text, b'"', std, false)?;
428 if found != Encoding::Plain {
429 if encoding != Encoding::Plain && encoding != found {
430 return Err(LiteralError::MixedEncodings);
431 }
432 encoding = found;
433 }
434 bodies.push(body);
435 }
436
437 let width = encoding.element_width(target);
438 let mut elements = Vec::new();
439 let mut remarks = Remarks::NONE;
440 for body in bodies {
441 let mut reader = Reader { bytes: body, index: 0, std, remarks: Remarks::NONE };
442 while let Some(piece) = reader.next(width)? {
443 elements.extend(piece.elements(width));
444 }
445 remarks = remarks.with(reader.remarks);
446 }
447 Ok(StringLiteral { elements, encoding, remarks })
448}
449
450fn open(
452 text: &str,
453 quote: u8,
454 std: Std,
455 character: bool,
456) -> Result<(Encoding, &[u8]), LiteralError> {
457 let bytes = text.as_bytes();
458 let (encoding, prefix) = Encoding::read(bytes);
459 if std < encoding.since(character) {
460 return Err(LiteralError::PrefixNotInDialect);
461 }
462 let rest = &bytes[prefix..];
463 match rest {
464 [first, .., last] if *first == quote && *last == quote => {
465 Ok((encoding, &rest[1..rest.len() - 1]))
466 }
467 _ => Err(LiteralError::NotALiteral),
468 }
469}
470
471fn narrow(value: u64, bits: u32, signed: bool) -> i64 {
473 let masked = if bits >= 64 { value } else { value & ((1u64 << bits) - 1) };
474 if signed && bits < 64 && masked >> (bits - 1) & 1 == 1 {
475 (masked | !((1u64 << bits) - 1)) as i64
477 } else {
478 masked as i64
479 }
480}
481
482#[derive(Debug, Clone, Copy, PartialEq, Eq)]
484enum Piece {
485 Char(u32),
488 Value(u32),
491}
492
493impl Piece {
494 fn elements(self, width: u32) -> Vec<u32> {
496 let code = match self {
497 Piece::Value(value) => return vec![value],
498 Piece::Char(code) => code,
499 };
500 match width {
501 8 => {
502 let mut buffer = [0u8; 4];
503 let text = char::from_u32(code)
504 .map(|character| character.encode_utf8(&mut buffer).len())
505 .unwrap_or(0);
506 buffer[..text].iter().map(|&byte| u32::from(byte)).collect()
507 }
508 16 if code > 0xffff => {
511 let value = code - 0x1_0000;
512 vec![0xd800 + (value >> 10), 0xdc00 + (value & 0x3ff)]
513 }
514 _ => vec![code],
515 }
516 }
517}
518
519struct Reader<'a> {
521 bytes: &'a [u8],
523 index: usize,
525 std: Std,
527 remarks: Remarks,
529}
530
531impl Reader<'_> {
532 fn next(&mut self, width: u32) -> Result<Option<Piece>, LiteralError> {
534 let Some(&byte) = self.bytes.get(self.index) else {
535 return Ok(None);
536 };
537 self.index += 1;
538 if byte == b'\\' {
539 return self.escape(width).map(Some);
540 }
541 if byte < 0x80 {
542 return Ok(Some(Piece::Char(u32::from(byte))));
543 }
544 if width == 8 {
548 return Ok(Some(Piece::Value(u32::from(byte))));
549 }
550 let length = utf8_length(byte).ok_or(LiteralError::InvalidUtf8)?;
553 let end = self.index - 1 + length;
554 let text = self
555 .bytes
556 .get(self.index - 1..end)
557 .and_then(|slice| std::str::from_utf8(slice).ok())
558 .ok_or(LiteralError::InvalidUtf8)?;
559 let character = text.chars().next().ok_or(LiteralError::InvalidUtf8)?;
560 self.index = end;
561 Ok(Some(Piece::Char(character as u32)))
562 }
563
564 fn escape(&mut self, width: u32) -> Result<Piece, LiteralError> {
566 let Some(&byte) = self.bytes.get(self.index) else {
567 return Err(LiteralError::NotALiteral);
569 };
570 self.index += 1;
571 let simple = match byte {
572 b'n' => Some(0x0a),
573 b't' => Some(0x09),
574 b'r' => Some(0x0d),
575 b'a' => Some(0x07),
576 b'b' => Some(0x08),
577 b'f' => Some(0x0c),
578 b'v' => Some(0x0b),
579 b'\\' | b'\'' | b'"' | b'?' => Some(u32::from(byte)),
580 _ => None,
581 };
582 if let Some(value) = simple {
583 return Ok(Piece::Value(value));
584 }
585 match byte {
586 b'e' | b'E' => {
588 self.remarks = self.remarks.with(Remarks::NON_ISO_ESCAPE);
589 Ok(Piece::Value(0x1b))
590 }
591 b'0'..=b'7' => Ok(Piece::Value(self.octal(byte, width))),
592 b'x' => self.hex(width).map(Piece::Value),
593 b'u' | b'U' => self.ucn(byte).map(Piece::Char),
594 b'N' => Err(LiteralError::NamedUcn),
595 _ => {
598 self.remarks = self.remarks.with(Remarks::UNKNOWN_ESCAPE);
599 Ok(Piece::Value(u32::from(byte)))
600 }
601 }
602 }
603
604 fn octal(&mut self, first: u8, width: u32) -> u32 {
607 let mut value = u32::from(first - b'0');
608 for _ in 0..2 {
609 match self.bytes.get(self.index) {
610 Some(&byte @ b'0'..=b'7') => {
611 value = value * 8 + u32::from(byte - b'0');
612 self.index += 1;
613 }
614 _ => break,
615 }
616 }
617 self.fit(value, width, Remarks::OCTAL_ESCAPE_OUT_OF_RANGE)
618 }
619
620 fn hex(&mut self, width: u32) -> Result<u32, LiteralError> {
622 let mut value: u64 = 0;
623 let mut digits = 0;
624 while let Some(digit) = self.bytes.get(self.index).and_then(|&byte| hex_digit(byte)) {
625 value = value.saturating_mul(16).saturating_add(u64::from(digit));
628 digits += 1;
629 self.index += 1;
630 }
631 if digits == 0 {
632 return Err(LiteralError::NoHexDigits);
633 }
634 Ok(self.fit(
635 u32::try_from(value).unwrap_or(u32::MAX),
636 width,
637 Remarks::HEX_ESCAPE_OUT_OF_RANGE,
638 ))
639 }
640
641 fn ucn(&mut self, marker: u8) -> Result<u32, LiteralError> {
643 if self.bytes.get(self.index) == Some(&b'{') {
644 return Err(LiteralError::NamedUcn);
645 }
646 let digits = if marker == b'u' { 4 } else { 8 };
647 let mut value: u32 = 0;
648 for _ in 0..digits {
649 let Some(digit) = self.bytes.get(self.index).and_then(|&byte| hex_digit(byte)) else {
650 return Err(LiteralError::IncompleteUcn);
651 };
652 value = value * 16 + digit;
653 self.index += 1;
654 }
655 let allowed_low = matches!(value, 0x24 | 0x40 | 0x60);
659 if (value < 0xa0 && !allowed_low) || (0xd800..=0xdfff).contains(&value) || value > 0x10ffff
660 {
661 return Err(LiteralError::InvalidUcn);
662 }
663 if self.std < Std::C99 {
664 self.remarks = self.remarks.with(Remarks::UCN);
665 }
666 Ok(value)
667 }
668
669 fn fit(&mut self, value: u32, width: u32, out_of_range: Remarks) -> u32 {
673 if width >= 32 {
674 return value;
675 }
676 let mask = (1u32 << width) - 1;
677 if value & !mask != 0 {
678 self.remarks = self.remarks.with(out_of_range);
679 }
680 value & mask
681 }
682}
683
684fn hex_digit(byte: u8) -> Option<u32> {
686 char::from(byte).to_digit(16)
687}
688
689fn utf8_length(byte: u8) -> Option<usize> {
692 match byte {
693 0x00..=0x7f => Some(1),
694 0xc2..=0xdf => Some(2),
695 0xe0..=0xef => Some(3),
696 0xf0..=0xf4 => Some(4),
697 _ => None,
698 }
699}
700
701#[cfg(test)]
702mod tests {
703 use rucc_target::Triple;
704
705 use super::*;
706
707 fn linux() -> TargetInfo {
708 TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a known triple"))
709 }
710
711 fn windows() -> TargetInfo {
712 TargetInfo::new("x86_64-pc-windows-msvc".parse::<Triple>().expect("a known triple"))
713 }
714
715 fn arm() -> TargetInfo {
716 TargetInfo::new("aarch64-unknown-linux-gnu".parse::<Triple>().expect("a known triple"))
717 }
718
719 fn ch(text: &str) -> i64 {
721 character(text, Std::C23, &linux()).expect("a character constant").value
722 }
723
724 fn ch_remarks(text: &str) -> Remarks {
726 character(text, Std::C23, &linux()).expect("a character constant").remarks
727 }
728
729 fn ch_error(text: &str) -> LiteralError {
731 character(text, Std::C23, &linux()).expect_err("not a character constant")
732 }
733
734 fn str_elements(text: &str) -> Vec<u32> {
736 string(text, Std::C23, &linux()).expect("a string literal").elements
737 }
738
739 fn str_bytes(text: &str) -> Vec<u8> {
741 string(text, Std::C23, &linux()).expect("a string literal").bytes(&linux())
742 }
743
744 #[test]
745 fn the_ordinary_cases_are_the_characters_they_look_like() {
746 assert_eq!(ch("'a'"), 0x61);
747 assert_eq!(ch(r"'\n'"), 0x0a);
748 assert_eq!(ch(r"'\0'"), 0);
749 assert_eq!(ch(r"'\\'"), 0x5c);
750 assert_eq!(ch(r"'\''"), 0x27);
751 assert_eq!(ch(r#"'\"'"#), 0x22);
752 assert_eq!(ch(r"'\?'"), 0x3f);
753 assert_eq!(str_elements(r#""hi""#), vec![0x68, 0x69]);
754 }
755
756 #[test]
760 fn a_high_character_takes_the_sign_of_plain_char() {
761 assert_eq!(ch(r"'\xff'"), -1);
762 assert_eq!(ch(r"'\377'"), -1);
763 assert_eq!(character(r"'\xff'", Std::C23, &arm()).expect("a constant").value, 255);
764 assert_eq!(ch(r"u8'\xff'"), 255);
766 }
767
768 #[test]
772 fn an_escape_too_big_for_its_element_is_truncated_and_says_so() {
773 let out = character(r"'\x1ff'", Std::C23, &linux()).expect("a constant");
774 assert_eq!(out.value, -1);
775 assert!(out.remarks.has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
776 let out = character(r"'\400'", Std::C23, &linux()).expect("a constant");
777 assert_eq!(out.value, 0);
778 assert!(out.remarks.has(Remarks::OCTAL_ESCAPE_OUT_OF_RANGE));
779 assert!(!out.remarks.has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
780 assert!(!ch_remarks(r"L'\x1ff'").has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
782 assert_eq!(ch(r"L'\x1ff'"), 0x1ff);
783 }
784
785 #[test]
789 fn adjacent_literals_agree_on_one_encoding_or_none_at_all() {
790 let target = linux();
791 let wide = strings(&[r#"L"a""#, r#""b""#], Std::C23, &target).expect("a string");
792 assert_eq!(wide.encoding, Encoding::Wide);
793 assert_eq!(wide.elements, vec![0x61, 0x62]);
794 assert_eq!(wide.bytes(&target).len(), 12);
795 let other_way = strings(&[r#""a""#, r#"L"b""#], Std::C23, &target).expect("a string");
796 assert_eq!(other_way.encoding, Encoding::Wide);
797 assert_eq!(other_way.bytes(&target).len(), 12);
798
799 let u8_run = strings(&[r#"u8"a""#, r#""b""#], Std::C23, &target).expect("a string");
800 assert_eq!(u8_run.encoding, Encoding::Utf8);
801 assert_eq!(u8_run.bytes(&target).len(), 3);
802
803 let mixed = strings(&[r#"L"a""#, r#""é""#], Std::C23, &target).expect("a string");
805 assert_eq!(mixed.elements, vec![0x61, 0xe9]);
806
807 for run in [[r#"u8"a""#, r#"u"b""#], [r#"u8"a""#, r#"L"b""#], [r#"u"a""#, r#"L"b""#]] {
808 assert_eq!(
809 strings(&run, Std::C23, &target).expect_err("two prefixes in one run"),
810 LiteralError::MixedEncodings
811 );
812 }
813
814 assert_eq!(
816 strings(&[r#""hi""#], Std::C23, &target).expect("a string").elements,
817 vec![0x68, 0x69]
818 );
819 }
820
821 #[test]
825 fn more_than_one_character_shifts_them_together() {
826 assert_eq!(ch("'ab'"), 0x6162);
827 assert_eq!(ch("'abc'"), 0x616263);
828 assert_eq!(ch("'abcd'"), 0x61626364);
829 assert_eq!(ch("'abcde'"), 0x62636465);
830 assert_eq!(ch(r"'\xff\xfe'"), 0xfffe);
831 assert_eq!(ch(r"'\xff\xff\xff\xff'"), -1);
832 assert_eq!(ch(r"'\x80\x00'"), 0x8000);
833
834 assert!(ch_remarks("'ab'").has(Remarks::MULTICHARACTER));
835 assert!(ch_remarks("'abcd'").has(Remarks::MULTICHARACTER));
836 assert!(ch_remarks("'abcde'").has(Remarks::TOO_LONG));
837 assert!(!ch_remarks("'abcde'").has(Remarks::MULTICHARACTER));
838 assert!(!ch_remarks("'a'").has(Remarks::MULTICHARACTER));
839 }
840
841 #[test]
844 fn a_prefixed_constant_holds_one_character_and_keeps_the_last() {
845 for text in [r"L'ab'", r"u'ab'", r"U'ab'"] {
846 let out = character(text, Std::C23, &linux()).expect("a constant");
847 assert_eq!(out.value, 0x62, "{text}");
848 assert!(out.remarks.has(Remarks::TOO_LONG), "{text}");
849 }
850 assert_eq!(ch_error("u8'ab'"), LiteralError::TooLong);
851 assert_eq!(ch_error("u8'é'"), LiteralError::TooLong);
852 }
853
854 #[test]
855 fn the_empty_constant_has_no_value_to_have() {
856 assert_eq!(ch_error("''"), LiteralError::Empty);
857 assert_eq!(ch_error("L''"), LiteralError::Empty);
858 assert_eq!(str_elements(r#""""#), Vec::<u32>::new());
860 assert_eq!(str_bytes(r#""""#), vec![0]);
861 }
862
863 #[test]
866 fn a_source_character_is_encoded_and_an_escape_is_not() {
867 assert_eq!(ch("'é'"), 0xc3a9);
868 assert_eq!(ch("L'é'"), 0xe9);
869 assert_eq!(ch("u'€'"), 0x20ac);
870 assert_eq!(ch(r"U'\U0001F600'"), 0x1f600);
871 assert_eq!(ch(r"'\U0001F600'"), i64::from(0xf09f_9880u32 as i32));
874 assert!(ch_remarks(r"'\U0001F600'").has(Remarks::MULTICHARACTER));
875 }
876
877 #[test]
880 fn the_escapes_outside_the_standard_still_have_values() {
881 assert_eq!(ch(r"'\e'"), 0x1b);
882 assert!(ch_remarks(r"'\e'").has(Remarks::NON_ISO_ESCAPE));
883 assert_eq!(ch(r"'\q'"), 0x71);
884 assert!(ch_remarks(r"'\q'").has(Remarks::UNKNOWN_ESCAPE));
885 assert_eq!(ch_error(r"'\x'"), LiteralError::NoHexDigits);
886 assert_eq!(ch_error(r"'\N{LATIN SMALL LETTER A}'"), LiteralError::NamedUcn);
887 }
888
889 #[test]
893 fn a_universal_character_name_may_not_name_just_anything() {
894 assert_eq!(ch("'\\u0024'"), 0x24);
895 assert_eq!(ch("'\\u00e9'"), 0xc3a9);
896 assert_eq!(ch_error("'\\u0041'"), LiteralError::InvalidUcn);
897 assert_eq!(ch_error(r"'\ud800'"), LiteralError::InvalidUcn);
898 assert_eq!(ch_error(r"'\u00'"), LiteralError::IncompleteUcn);
899 assert_eq!(ch_error(r"'\U00110000'"), LiteralError::InvalidUcn);
901 }
902
903 #[test]
904 fn a_universal_character_name_before_c99_is_worth_a_remark() {
905 let out = character("'\\u00e9'", Std::C89, &linux()).expect("a constant");
906 assert!(out.remarks.has(Remarks::UCN));
907 let out = character("'\\u00e9'", Std::C99, &linux()).expect("a constant");
908 assert!(!out.remarks.has(Remarks::UCN));
909 }
910
911 #[test]
914 fn an_octal_escape_ends_and_a_hex_escape_does_not() {
915 assert_eq!(str_elements(r#""\1234""#), vec![0x53, 0x34]);
916 assert_eq!(str_elements(r#""\x41z""#), vec![0x41, 0x7a]);
917 assert_eq!(str_elements(r#""\x41""#), vec![0x41]);
918 }
919
920 #[test]
923 fn a_string_is_as_many_bytes_as_its_encoding_makes_it() {
924 assert_eq!(str_bytes(r#""abc""#).len(), 4);
925 assert_eq!(str_bytes(r#"L"abc""#).len(), 16);
926 assert_eq!(str_bytes(r#"u"abc""#).len(), 8);
927 assert_eq!(str_bytes(r#"U"abc""#).len(), 16);
928 assert_eq!(str_bytes(r#"u8"abc""#).len(), 4);
929 assert_eq!(str_bytes(r#""a\0b""#), vec![0x61, 0x00, 0x62, 0x00]);
931 assert_eq!(str_bytes(r#""é""#), vec![0xc3, 0xa9, 0x00]);
932 }
933
934 #[test]
937 fn utf16_splits_the_characters_that_do_not_fit_into_a_surrogate_pair() {
938 assert_eq!(
939 str_elements(r#"u8"é€😀""#),
940 vec![0xc3, 0xa9, 0xe2, 0x82, 0xac, 0xf0, 0x9f, 0x98, 0x80]
941 );
942 assert_eq!(str_elements(r#"u"€😀""#), vec![0x20ac, 0xd83d, 0xde00]);
943 assert_eq!(str_elements(r#"U"€😀""#), vec![0x20ac, 0x1f600]);
944 }
945
946 #[test]
949 fn a_wide_literal_is_whatever_the_target_makes_wchar_t() {
950 let text = r#"L"a😀""#;
951 let here = string(text, Std::C23, &linux()).expect("a string");
952 assert_eq!(here.elements, vec![0x61, 0x1f600]);
953 assert_eq!(here.bytes(&linux()).len(), 12);
954 let there = string(text, Std::C23, &windows()).expect("a string");
955 assert_eq!(there.elements, vec![0x61, 0xd83d, 0xde00]);
956 assert_eq!(there.bytes(&windows()).len(), 8);
957 assert_eq!(character(r"L'\xffffffff'", Std::C23, &linux()).expect("a constant").value, -1);
960 assert_eq!(
961 character(r"L'\xffffffff'", Std::C23, &arm()).expect("a constant").value,
962 0xffff_ffff
963 );
964 }
965
966 #[test]
970 fn the_bytes_come_out_in_the_targets_order() {
971 let mut big = linux();
972 big.little_endian = false;
973 let literal = string(r#"u"ab""#, Std::C23, &big).expect("a string");
974 assert_eq!(literal.bytes(&big), vec![0x00, 0x61, 0x00, 0x62, 0x00, 0x00]);
975 assert_eq!(literal.bytes(&linux()), vec![0x61, 0x00, 0x62, 0x00, 0x00, 0x00]);
976 }
977
978 #[test]
981 fn a_prefix_is_only_available_in_the_dialect_that_has_it() {
982 assert!(character("L'a'", Std::C89, &linux()).is_ok());
983 assert_eq!(
984 character("u'a'", Std::C99, &linux()).expect_err("not in C99"),
985 LiteralError::PrefixNotInDialect
986 );
987 assert!(character("u'a'", Std::C11, &linux()).is_ok());
988 assert!(string(r#"u8"a""#, Std::C11, &linux()).is_ok());
989 assert_eq!(
990 character("u8'a'", Std::C11, &linux()).expect_err("not in C11"),
991 LiteralError::PrefixNotInDialect
992 );
993 assert!(character("u8'a'", Std::C23, &linux()).is_ok());
994 }
995
996 #[test]
999 fn an_element_is_as_wide_as_the_encoding_and_the_target_agree() {
1000 let target = linux();
1001 assert_eq!(Encoding::Plain.element_width(&target), 8);
1002 assert_eq!(Encoding::Utf8.element_width(&target), 8);
1003 assert_eq!(Encoding::Utf16.element_width(&target), 16);
1004 assert_eq!(Encoding::Utf32.element_width(&target), 32);
1005 assert_eq!(Encoding::Wide.element_width(&target), 32);
1006 assert_eq!(Encoding::Wide.element_width(&windows()), 16);
1007
1008 assert!(Encoding::Plain.is_signed(&target));
1009 assert!(!Encoding::Plain.is_signed(&arm()));
1010 assert!(Encoding::Wide.is_signed(&target));
1011 assert!(!Encoding::Wide.is_signed(&arm()));
1012 assert!(!Encoding::Utf8.is_signed(&target));
1013 assert!(!Encoding::Utf16.is_signed(&target));
1014 assert!(!Encoding::Utf32.is_signed(&target));
1015 }
1016
1017 #[test]
1018 fn a_spelling_that_is_not_a_literal_is_refused_rather_than_guessed_at() {
1019 assert_eq!(ch_error("a"), LiteralError::NotALiteral);
1020 assert_eq!(ch_error("'a"), LiteralError::NotALiteral);
1021 assert_eq!(
1022 string("'a'", Std::C23, &linux()).expect_err("not a string"),
1023 LiteralError::NotALiteral
1024 );
1025 assert_eq!(ch_error("'"), LiteralError::NotALiteral);
1026 }
1027
1028 #[test]
1029 fn every_error_has_something_to_print() {
1030 for error in [
1031 LiteralError::NotALiteral,
1032 LiteralError::Empty,
1033 LiteralError::TooLong,
1034 LiteralError::NoHexDigits,
1035 LiteralError::IncompleteUcn,
1036 LiteralError::InvalidUcn,
1037 LiteralError::NamedUcn,
1038 LiteralError::InvalidUtf8,
1039 LiteralError::PrefixNotInDialect,
1040 LiteralError::MixedEncodings,
1041 ] {
1042 assert!(!error.message().is_empty());
1043 }
1044 }
1045}