Skip to main content

rucc_lex/
literal.rs

1//! Character constants and string literals: the escapes, the encoding prefixes, and what the
2//! elements end up being.
3//!
4//! Design: `spec/06-lexer-and-parser.md` section 6.1.
5//!
6//! This is the last piece of phase 7 that is about spellings. A literal arrives here as the
7//! bytes the user wrote, prefix and quotes included, and leaves as elements: one value per
8//! element of the array a string is, or one number for the character constant. What an element
9//! is depends on the prefix and on the target, which is most of the work.
10//!
11//! The execution character set is UTF-8 and the wide one is UTF-32, or UTF-16 where `wchar_t`
12//! is sixteen bits, which is Windows. That is what both compilers default to and it is the only
13//! choice that makes a UTF-8 source file mean what it looks like. `-fexec-charset` is not
14//! implemented and would change these answers if it ever is.
15//!
16//! # What an escape is worth
17//!
18//! There are two kinds of escape and the difference matters more than it looks. `é` names
19//! a character, so it is encoded in whatever the literal's encoding is, and in a plain string it
20//! becomes the two bytes `c3 a9`. `\xe9` is a value, not a character, so it is that one element
21//! and nothing encodes it. gcc 13.3 agrees on both: `"é"` is three bytes long and
22//! `"\xe9"` is two.
23//!
24//! A value escape that does not fit its element is truncated with a warning, and the element is
25//! what decides, not the type: `u8'\xff'` is fine and `'\x1ff'` is not, and both are eight bits
26//! wide. Octal runs to three digits and stops, so `"\1234"` is `S4` and not one escape, while
27//! hexadecimal runs as far as there are hexadecimal digits.
28//!
29//! An escape whose letter means nothing is that letter, with a warning, so `'\q'` is `'q'`.
30//! `\e` is the escape character in both compilers and in no standard. `\N{NAME}` is refused,
31//! because gcc 13.3 has it in C++23 alone and inventing our own answer would be worse.
32//!
33//! # Universal character names
34//!
35//! A UCN may not name a character in the basic character set, so a UCN that spells out the
36//! letter `A` is an error even though `'A'` is a constant. The exception is the three characters
37//! `$`, `@` and the backquote, which are below a space in the table and allowed anyway.
38//! Surrogates are refused. gcc still reports the basic character case in C23, where the standard
39//! relaxed it, so this follows gcc and not the paper. Before C99 a UCN is converted with a remark
40//! rather than refused, which is also what gcc does.
41//!
42//! A code point above `\U0010ffff` is an error here and a warning in gcc, which then encodes
43//! the value as though UTF-8 went that far. clang refuses it, this refuses it, and it is the
44//! one deliberate difference in this module.
45//!
46//! # Character constants
47//!
48//! A plain character constant is an `int` and not a `char`, and its value is the character
49//! converted to `int`, so `'\xff'` is minus one where plain `char` is signed and 255 where it
50//! is not. More than one character is implementation defined and both compilers shift them
51//! together, so `'ab'` is `0x6162`, with a warning. Past the width of the type the ones at the
52//! front fall off, so `'abcde'` is `0x62636465`, and gcc says "too long" there instead of
53//! "multi-character" rather than as well as it. A wide constant has room for exactly one, so
54//! `L'ab'` is `'b'` with the same warning, and a `u8` constant with two characters is an error
55//! rather than a warning. All measured on gcc 13.3, x86-64 Linux.
56//!
57//! # The prefixes and the dialects
58//!
59//! `L` is C89, `u` and `U` are C11, `u8` on a string is C11 and `u8` on a character constant is
60//! C23. The three C11 ones are a GNU extension from gnu99 on and the C23 one is not an extension
61//! at all, so `u8'a'` needs C23 in gnu23 as much as in c23. Both halves of that are measured
62//! against gcc 16 rather than reasoned about.
63//!
64//! In an older dialect gcc lexes `u8'a'` as the identifier `u8` followed by a character
65//! constant, which is a different token stream rather than a different constant. The scanner
66//! here reads it as one token in every dialect, which is the simpler rule and a divergence in
67//! the token stream that no real program can see, so the dialect is checked at this point
68//! instead and the constant is refused with a message that names the dialect it needs.
69
70use rucc_session::Std;
71use rucc_target::TargetInfo;
72
73use crate::remarks::Remarks;
74
75/// The encoding prefix of a character constant or a string literal.
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum Encoding {
78    /// No prefix, whose element is a `char`.
79    Plain,
80    /// `L`, whose element is a `wchar_t` and so is a fact about the target.
81    Wide,
82    /// `u8`, whose element is a `char8_t`, which is an `unsigned char`.
83    Utf8,
84    /// `u`, whose element is a `char16_t`, which is a `uint_least16_t`.
85    Utf16,
86    /// `U`, whose element is a `char32_t`, which is a `uint_least32_t`.
87    Utf32,
88}
89
90impl Encoding {
91    /// The width of one element in bits.
92    #[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    /// Whether the element type is signed, which only `char` and `wchar_t` can be.
103    #[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    /// The prefix this encoding is written with, which is empty for a plain literal.
113    #[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    /// The prefix a spelling was written with, for a caller that needs the encoding of a
125    /// literal it could not convert.
126    #[must_use]
127    pub fn read_prefix(text: &[u8]) -> Encoding {
128        Encoding::read(text).0
129    }
130
131    /// The prefix at the front of a spelling, and how many bytes it took.
132    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    /// The first dialect that has this prefix, which is not the same for a character constant
143    /// as for a string literal and not the same in a GNU dialect as in a strict one.
144    ///
145    /// gcc offers the C11 prefixes as an extension from gnu99 on, all four of them, and does
146    /// not offer `u8` on a character constant as one: that is C23 in every dialect, gnu23
147    /// included. Both halves of that are measured against gcc 16 rather than reasoned about.
148    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/// A converted character constant.
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160pub struct CharConstant {
161    /// The value, already converted to the constant's type, which is why it can be negative:
162    /// `'\xff'` is minus one where plain `char` is signed.
163    pub value: i64,
164    /// The prefix it was written with.
165    pub encoding: Encoding,
166    /// What is worth saying about it, for the caller that holds the span.
167    pub remarks: Remarks,
168}
169
170impl CharConstant {
171    /// The spelling this constant would be written with, quotes and prefix included.
172    ///
173    /// It is a spelling and not the spelling: the one the author wrote is gone by the time
174    /// there is a value here. What it guarantees is that reading it back gives this constant,
175    /// which is what a printer downstream needs and what a message quoting a constant wants.
176    ///
177    /// A constant holding more than one character is written as the bytes it was shifted
178    /// together from, most significant first, which is how it is read back.
179    #[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/// A converted string literal.
207#[derive(Debug, Clone, PartialEq, Eq)]
208pub struct StringLiteral {
209    /// One value per element of the array, without the terminating zero, since the zero belongs
210    /// to the type and not to the spelling. An element is a byte in a plain or `u8` literal, a
211    /// UTF-16 code unit in a `u` one, and a code point in a `U` one.
212    pub elements: Vec<u32>,
213    /// The prefix it was written with.
214    pub encoding: Encoding,
215    /// What is worth saying about it, for the caller that holds the span.
216    pub remarks: Remarks,
217}
218
219impl StringLiteral {
220    /// The bytes this literal becomes in the object, terminator included, in the target's byte
221    /// order.
222    #[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    /// The spelling this literal would be written with, quotes and prefix included.
238    ///
239    /// A byte escape takes three octal digits rather than two hexadecimal ones, because an
240    /// octal escape stops after three digits and a hexadecimal one runs on until the digits do.
241    /// Where the elements are too wide for octal there is no such spelling, so the literal is
242    /// closed and reopened instead, which phase 7 joins back into the one literal it came from.
243    #[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
277/// The character an element is, where it is one that can be written as itself.
278///
279/// Everything outside printable ASCII is escaped, whatever the element means, because the
280/// output has no encoding of its own to be right about and an escape is right in all of them.
281fn printable(element: u32) -> Option<char> {
282    match element {
283        0x20..=0x7e => char::from_u32(element),
284        _ => None,
285    }
286}
287
288/// Writes one element of a literal, escaped if it has to be.
289fn 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        // Two question marks in a row are a trigraph where a compiler is told to look for one,
296        // so the second is written as an escape and the first never needs to be.
297        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/// Why a spelling is not a literal.
304#[derive(Debug, Clone, Copy, PartialEq, Eq)]
305pub enum LiteralError {
306    /// The spelling is not a character constant or a string literal at all, which the caller
307    /// cannot reach from a token the scanner produced.
308    NotALiteral,
309    /// `''`, which has no character in it to have a value.
310    Empty,
311    /// More characters than the type has room for, in the one encoding where that is an error
312    /// rather than a warning.
313    TooLong,
314    /// `\x` with no hexadecimal digit after it.
315    NoHexDigits,
316    /// A universal character name that stops before its digits are done.
317    IncompleteUcn,
318    /// A universal character name that may not name what it names: a character in the basic
319    /// character set, a surrogate, or a code point past the end of Unicode.
320    InvalidUcn,
321    /// `\N{NAME}`, which gcc 13.3 has in C++23 and in no C dialect.
322    NamedUcn,
323    /// A byte in the source that is not part of a character, in a literal whose encoding has to
324    /// know what the characters are.
325    InvalidUtf8,
326    /// An encoding prefix the dialect does not have.
327    PrefixNotInDialect,
328    /// A run of adjacent string literals written with two different prefixes, which neither
329    /// compiler has an answer for.
330    MixedEncodings,
331}
332
333impl LiteralError {
334    /// What to print, in GCC's words where GCC has any.
335    #[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
356/// Converts the spelling of a character constant into a value.
357///
358/// # Errors
359///
360/// [`LiteralError`], for a spelling that is not a character constant or that holds an escape
361/// that is not one.
362pub fn character(
363    text: &[u8],
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    // The elements are shifted together into one number, which is what both compilers do with a
373    // constant holding more than one, and the ones that fall off the top are the ones the
374    // warning is about.
375    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    // A plain constant is an `int` however many characters it holds, and every other kind is
386    // exactly one element wide, so that is how many characters fit.
387    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    // One character is converted to the constant's type from the element's, which is where the
398    // sign of a plain `char` gets in. More than one is already a number of the constant's type
399    // and nothing sign extends it from any narrower width.
400    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
408/// Converts the spelling of a string literal into its elements.
409///
410/// # Errors
411///
412/// [`LiteralError`], for a spelling that is not a string literal or that holds an escape that
413/// is not one.
414pub fn string(
415    text: &[u8],
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
423/// Converts a run of adjacent string literals into the one literal they are.
424///
425/// The encoding of the result is the prefixed one when any of them is prefixed, so `L"a" "b"`
426/// and `"a" L"b"` are both wide, and two different prefixes in one run is an error rather than
427/// a choice. That is measured on gcc 13.3, which puts it exactly that way: "unsupported
428/// non-standard concatenation of string literals".
429///
430/// The bodies are read in the encoding of the whole run rather than each in its own, which
431/// matters for a character rather than an escape: the accented letter in `L"a" "e-acute"` is
432/// one wide element and not the two bytes it would have been on its own.
433///
434/// # Errors
435///
436/// [`LiteralError`], for a spelling that is not a string literal, a run mixing two prefixes, or
437/// an escape that is not one.
438pub fn strings(
439    texts: &[&[u8]],
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
470/// Reads the prefix and the quotes, and hands back the encoding and what is between them.
471fn open(
472    text: &[u8],
473    quote: u8,
474    std: Std,
475    gnu: bool,
476    character: bool,
477) -> Result<(Encoding, &[u8]), LiteralError> {
478    let bytes = text;
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
492/// Cuts a value down to `bits` and reads it back as signed or unsigned.
493fn 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        // The bits above the width are the sign, which is what makes `'\xff'` minus one.
497        (masked | !((1u64 << bits) - 1)) as i64
498    } else {
499        masked as i64
500    }
501}
502
503/// One piece of a literal, before it is turned into elements.
504#[derive(Debug, Clone, Copy, PartialEq, Eq)]
505enum Piece {
506    /// A character, which the encoding has to encode: a character from the source or one named
507    /// by a universal character name.
508    Char(u32),
509    /// A value written as a value, with `\x` or with octal digits, which is one element as
510    /// written and is not encoded.
511    Value(u32),
512}
513
514impl Piece {
515    /// The elements this piece becomes in an encoding of the given element width.
516    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            // UTF-16 is the one encoding where a character can take two elements, which is why
530            // a wide string is not the same length on Windows as it is anywhere else.
531            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
540/// Reads the body of a literal one character at a time.
541struct Reader<'a> {
542    /// What is between the quotes.
543    bytes: &'a [u8],
544    /// How far in the reader is.
545    index: usize,
546    /// The dialect, which decides what is worth a remark.
547    std: Std,
548    /// What the literal has earned so far.
549    remarks: Remarks,
550}
551
552impl Reader<'_> {
553    /// The next piece, or [`None`] at the end of the literal.
554    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        // A byte above ASCII begins a character in the source, which is UTF-8. A narrow literal
566        // is UTF-8 too, so its bytes go through untouched and nothing has to be able to decode
567        // them; a wide one has to know which character this is before it can encode it again.
568        if width == 8 {
569            return Ok(Some(Piece::Value(u32::from(byte))));
570        }
571        // Only this one character is decoded, and not the rest of the literal, because what
572        // comes after it may be an escape holding a byte that no character begins with.
573        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    /// The piece an escape sequence is worth, with the backslash already read.
586    fn escape(&mut self, width: u32) -> Result<Piece, LiteralError> {
587        let Some(&byte) = self.bytes.get(self.index) else {
588            // The scanner reports the missing quote, and there is nothing here to convert.
589            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            // The escape character, which both compilers have and no standard does.
608            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            // An escape that means nothing is the character itself, which both compilers do
617            // after a warning rather than refusing the program.
618            _ => {
619                self.remarks = self.remarks.with(Remarks::UNKNOWN_ESCAPE);
620                Ok(Piece::Value(u32::from(byte)))
621            }
622        }
623    }
624
625    /// An octal escape, which is at most three digits however many follow, so that `"\1234"` is
626    /// two characters.
627    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    /// A hexadecimal escape, which runs as far as there are hexadecimal digits.
642    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            // A value far past the width is truncated anyway, so the accumulator stops growing
647            // rather than overflowing, and the remark still gets made.
648            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    /// A universal character name, with its `u` or `U` already read.
663    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        // The basic character set is off limits, and so is everything below ` ` except the
677        // three characters the standard lets through. gcc still says so in C23, where the
678        // wording was relaxed, so this follows the compiler rather than the paper.
679        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    /// Cuts an escape down to the element it is written in, and says so when that loses
691    /// something. Which remark that is depends on how the escape was written, because GCC words
692    /// the hexadecimal case and the octal one differently.
693    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
705/// The value of a hexadecimal digit, and [`None`] when the byte is not one.
706fn hex_digit(byte: u8) -> Option<u32> {
707    char::from(byte).to_digit(16)
708}
709
710/// How many bytes the character starting with this one takes, and [`None`] when no character
711/// starts with it.
712fn 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    /// The value of a character constant on x86-64 Linux, in C23.
741    fn ch(text: &str) -> i64 {
742        character(text.as_bytes(), Std::C23, false, &linux()).expect("a character constant").value
743    }
744
745    /// The remarks a character constant earns on x86-64 Linux, in C23.
746    fn ch_remarks(text: &str) -> Remarks {
747        character(text.as_bytes(), Std::C23, false, &linux()).expect("a character constant").remarks
748    }
749
750    /// What a character constant goes wrong with.
751    fn ch_error(text: &str) -> LiteralError {
752        character(text.as_bytes(), Std::C23, false, &linux()).expect_err("not a character constant")
753    }
754
755    /// The elements of a string literal on x86-64 Linux, in C23.
756    fn str_elements(text: &str) -> Vec<u32> {
757        string(text.as_bytes(), Std::C23, false, &linux()).expect("a string literal").elements
758    }
759
760    /// The bytes a string literal becomes on x86-64 Linux, terminator included.
761    fn str_bytes(text: &str) -> Vec<u8> {
762        string(text.as_bytes(), Std::C23, false, &linux())
763            .expect("a string literal")
764            .bytes(&linux())
765    }
766
767    #[test]
768    fn the_ordinary_cases_are_the_characters_they_look_like() {
769        assert_eq!(ch("'a'"), 0x61);
770        assert_eq!(ch(r"'\n'"), 0x0a);
771        assert_eq!(ch(r"'\0'"), 0);
772        assert_eq!(ch(r"'\\'"), 0x5c);
773        assert_eq!(ch(r"'\''"), 0x27);
774        assert_eq!(ch(r#"'\"'"#), 0x22);
775        assert_eq!(ch(r"'\?'"), 0x3f);
776        assert_eq!(str_elements(r#""hi""#), vec![0x68, 0x69]);
777    }
778
779    /// A single character in a plain constant goes through plain `char` on the way to `int`,
780    /// which is the whole reason `'\xff'` is a negative number on one target and a positive
781    /// one on another.
782    #[test]
783    fn a_high_character_takes_the_sign_of_plain_char() {
784        assert_eq!(ch(r"'\xff'"), -1);
785        assert_eq!(ch(r"'\377'"), -1);
786        assert_eq!(character(br"'\xff'", Std::C23, false, &arm()).expect("a constant").value, 255);
787        // Not a `char`, so nothing sign extends it.
788        assert_eq!(ch(r"u8'\xff'"), 255);
789    }
790
791    /// Measured on GCC 13.3, x86-64 Linux. A value escape is truncated to its element and the
792    /// warning is about the truncation, not about the type. The two spellings get two remarks
793    /// because GCC gives them two wordings.
794    #[test]
795    fn an_escape_too_big_for_its_element_is_truncated_and_says_so() {
796        let out = character(br"'\x1ff'", Std::C23, false, &linux()).expect("a constant");
797        assert_eq!(out.value, -1);
798        assert!(out.remarks.has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
799        let out = character(br"'\400'", Std::C23, false, &linux()).expect("a constant");
800        assert_eq!(out.value, 0);
801        assert!(out.remarks.has(Remarks::OCTAL_ESCAPE_OUT_OF_RANGE));
802        assert!(!out.remarks.has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
803        // Wide enough to hold it, so there is nothing to say.
804        assert!(!ch_remarks(r"L'\x1ff'").has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
805        assert_eq!(ch(r"L'\x1ff'"), 0x1ff);
806    }
807
808    /// Measured on GCC 13.3: a plain literal in a run takes the prefix of its neighbour, two
809    /// different prefixes are an error, and the bodies are read in the encoding of the run, so
810    /// a character in the plain part is one wide element rather than its UTF-8 bytes.
811    #[test]
812    fn adjacent_literals_agree_on_one_encoding_or_none_at_all() {
813        let target = linux();
814        let wide = strings(&[br#"L"a""#, br#""b""#], Std::C23, false, &target).expect("a string");
815        assert_eq!(wide.encoding, Encoding::Wide);
816        assert_eq!(wide.elements, vec![0x61, 0x62]);
817        assert_eq!(wide.bytes(&target).len(), 12);
818        let other_way =
819            strings(&[br#""a""#, br#"L"b""#], Std::C23, false, &target).expect("a string");
820        assert_eq!(other_way.encoding, Encoding::Wide);
821        assert_eq!(other_way.bytes(&target).len(), 12);
822
823        let u8_run =
824            strings(&[br#"u8"a""#, br#""b""#], Std::C23, false, &target).expect("a string");
825        assert_eq!(u8_run.encoding, Encoding::Utf8);
826        assert_eq!(u8_run.bytes(&target).len(), 3);
827
828        // The plain part is read as wide, so the accented letter is one element and not two.
829        let mixed = strings(&[br#"L"a""#, r#""é""#.as_bytes()], Std::C23, false, &target)
830            .expect("a string");
831        assert_eq!(mixed.elements, vec![0x61, 0xe9]);
832
833        for run in
834            [[&br#"u8"a""#[..], br#"u"b""#], [br#"u8"a""#, br#"L"b""#], [br#"u"a""#, br#"L"b""#]]
835        {
836            assert_eq!(
837                strings(&run, Std::C23, false, &target).expect_err("two prefixes in one run"),
838                LiteralError::MixedEncodings
839            );
840        }
841
842        // A run of one is the same thing as the literal on its own.
843        assert_eq!(
844            strings(&[br#""hi""#], Std::C23, false, &target).expect("a string").elements,
845            vec![0x68, 0x69]
846        );
847    }
848
849    /// Also measured on GCC 13.3. The characters are shifted together, the ones past the width
850    /// of the type fall off the front, and past that point GCC says "too long" instead of
851    /// "multi-character" rather than as well as it.
852    #[test]
853    fn more_than_one_character_shifts_them_together() {
854        assert_eq!(ch("'ab'"), 0x6162);
855        assert_eq!(ch("'abc'"), 0x616263);
856        assert_eq!(ch("'abcd'"), 0x61626364);
857        assert_eq!(ch("'abcde'"), 0x62636465);
858        assert_eq!(ch(r"'\xff\xfe'"), 0xfffe);
859        assert_eq!(ch(r"'\xff\xff\xff\xff'"), -1);
860        assert_eq!(ch(r"'\x80\x00'"), 0x8000);
861
862        assert!(ch_remarks("'ab'").has(Remarks::MULTICHARACTER));
863        assert!(ch_remarks("'abcd'").has(Remarks::MULTICHARACTER));
864        assert!(ch_remarks("'abcde'").has(Remarks::TOO_LONG));
865        assert!(!ch_remarks("'abcde'").has(Remarks::MULTICHARACTER));
866        assert!(!ch_remarks("'a'").has(Remarks::MULTICHARACTER));
867    }
868
869    /// A wide constant has room for exactly one character, so two is already too many and the
870    /// last one is what survives. `u8` is the one encoding where GCC makes this an error.
871    #[test]
872    fn a_prefixed_constant_holds_one_character_and_keeps_the_last() {
873        for text in [r"L'ab'", r"u'ab'", r"U'ab'"] {
874            let out = character(text.as_bytes(), Std::C23, false, &linux()).expect("a constant");
875            assert_eq!(out.value, 0x62, "{text}");
876            assert!(out.remarks.has(Remarks::TOO_LONG), "{text}");
877        }
878        assert_eq!(ch_error("u8'ab'"), LiteralError::TooLong);
879        assert_eq!(ch_error("u8'é'"), LiteralError::TooLong);
880    }
881
882    #[test]
883    fn the_empty_constant_has_no_value_to_have() {
884        assert_eq!(ch_error("''"), LiteralError::Empty);
885        assert_eq!(ch_error("L''"), LiteralError::Empty);
886        // The empty string is fine, and is one element long once the terminator is there.
887        assert_eq!(str_elements(r#""""#), Vec::<u32>::new());
888        assert_eq!(str_bytes(r#""""#), vec![0]);
889    }
890
891    /// A character from the source is encoded in the literal's encoding, so the same `é` is
892    /// two bytes in one constant and one code point in another.
893    #[test]
894    fn a_source_character_is_encoded_and_an_escape_is_not() {
895        assert_eq!(ch("'é'"), 0xc3a9);
896        assert_eq!(ch("L'é'"), 0xe9);
897        assert_eq!(ch("u'€'"), 0x20ac);
898        assert_eq!(ch(r"U'\U0001F600'"), 0x1f600);
899        // The same character in a plain constant is its UTF-8 bytes, which makes it a
900        // multi-character constant and a negative number.
901        assert_eq!(ch(r"'\U0001F600'"), i64::from(0xf09f_9880u32 as i32));
902        assert!(ch_remarks(r"'\U0001F600'").has(Remarks::MULTICHARACTER));
903    }
904
905    /// Both compilers have `\e` and neither standard does, and an escape that means nothing is
906    /// the letter itself after a warning rather than an error.
907    #[test]
908    fn the_escapes_outside_the_standard_still_have_values() {
909        assert_eq!(ch(r"'\e'"), 0x1b);
910        assert!(ch_remarks(r"'\e'").has(Remarks::NON_ISO_ESCAPE));
911        assert_eq!(ch(r"'\q'"), 0x71);
912        assert!(ch_remarks(r"'\q'").has(Remarks::UNKNOWN_ESCAPE));
913        assert_eq!(ch_error(r"'\x'"), LiteralError::NoHexDigits);
914        assert_eq!(ch_error(r"'\N{LATIN SMALL LETTER A}'"), LiteralError::NamedUcn);
915    }
916
917    /// A universal character name may not name a character in the basic character set, which
918    /// GCC still enforces in C23 where the wording was relaxed, and the three characters below
919    /// a space that are allowed anyway are allowed here too.
920    #[test]
921    fn a_universal_character_name_may_not_name_just_anything() {
922        assert_eq!(ch("'\\u0024'"), 0x24);
923        assert_eq!(ch("'\\u00e9'"), 0xc3a9);
924        assert_eq!(ch_error("'\\u0041'"), LiteralError::InvalidUcn);
925        assert_eq!(ch_error(r"'\ud800'"), LiteralError::InvalidUcn);
926        assert_eq!(ch_error(r"'\u00'"), LiteralError::IncompleteUcn);
927        // GCC warns here and encodes the value anyway. clang refuses it and so does this.
928        assert_eq!(ch_error(r"'\U00110000'"), LiteralError::InvalidUcn);
929    }
930
931    #[test]
932    fn a_universal_character_name_before_c99_is_worth_a_remark() {
933        let out = character(b"'\\u00e9'", Std::C89, false, &linux()).expect("a constant");
934        assert!(out.remarks.has(Remarks::UCN));
935        let out = character(b"'\\u00e9'", Std::C99, false, &linux()).expect("a constant");
936        assert!(!out.remarks.has(Remarks::UCN));
937    }
938
939    /// Octal runs to three digits and stops, and hexadecimal runs as far as the digits go, so
940    /// `"\1234"` is two characters and `"\x41z"` is two as well.
941    #[test]
942    fn an_octal_escape_ends_and_a_hex_escape_does_not() {
943        assert_eq!(str_elements(r#""\1234""#), vec![0x53, 0x34]);
944        assert_eq!(str_elements(r#""\x41z""#), vec![0x41, 0x7a]);
945        assert_eq!(str_elements(r#""\x41""#), vec![0x41]);
946    }
947
948    /// The sizes GCC reports for these, which is the elements plus the terminator times the
949    /// width of one.
950    #[test]
951    fn a_string_is_as_many_bytes_as_its_encoding_makes_it() {
952        assert_eq!(str_bytes(r#""abc""#).len(), 4);
953        assert_eq!(str_bytes(r#"L"abc""#).len(), 16);
954        assert_eq!(str_bytes(r#"u"abc""#).len(), 8);
955        assert_eq!(str_bytes(r#"U"abc""#).len(), 16);
956        assert_eq!(str_bytes(r#"u8"abc""#).len(), 4);
957        // A zero in the middle is an element like any other, and the terminator is still added.
958        assert_eq!(str_bytes(r#""a\0b""#), vec![0x61, 0x00, 0x62, 0x00]);
959        assert_eq!(str_bytes(r#""é""#), vec![0xc3, 0xa9, 0x00]);
960    }
961
962    /// The one encoding where a character can take two elements, which is why a wide string is
963    /// not the same length on Windows as it is anywhere else.
964    #[test]
965    fn utf16_splits_the_characters_that_do_not_fit_into_a_surrogate_pair() {
966        assert_eq!(
967            str_elements(r#"u8"é€😀""#),
968            vec![0xc3, 0xa9, 0xe2, 0x82, 0xac, 0xf0, 0x9f, 0x98, 0x80]
969        );
970        assert_eq!(str_elements(r#"u"€😀""#), vec![0x20ac, 0xd83d, 0xde00]);
971        assert_eq!(str_elements(r#"U"€😀""#), vec![0x20ac, 0x1f600]);
972    }
973
974    /// A wide literal is UTF-16 on Windows and UTF-32 everywhere else, so the same three
975    /// characters are four elements on one target and three on the other.
976    #[test]
977    fn a_wide_literal_is_whatever_the_target_makes_wchar_t() {
978        let text = r#"L"a😀""#;
979        let here = string(text.as_bytes(), Std::C23, false, &linux()).expect("a string");
980        assert_eq!(here.elements, vec![0x61, 0x1f600]);
981        assert_eq!(here.bytes(&linux()).len(), 12);
982        let there = string(text.as_bytes(), Std::C23, false, &windows()).expect("a string");
983        assert_eq!(there.elements, vec![0x61, 0xd83d, 0xde00]);
984        assert_eq!(there.bytes(&windows()).len(), 8);
985        // And a wide character constant takes the sign of `wchar_t`, which is not the same on
986        // every target either.
987        assert_eq!(
988            character(br"L'\xffffffff'", Std::C23, false, &linux()).expect("a constant").value,
989            -1
990        );
991        assert_eq!(
992            character(br"L'\xffffffff'", Std::C23, false, &arm()).expect("a constant").value,
993            0xffff_ffff
994        );
995    }
996
997    /// Every target the compiler has is little-endian, so the other order is checked by
998    /// flipping the field rather than by naming a target, and this is the test that fails on
999    /// the day a big-endian one arrives with the layout still assuming otherwise.
1000    #[test]
1001    fn the_bytes_come_out_in_the_targets_order() {
1002        let mut big = linux();
1003        big.little_endian = false;
1004        let literal = string(br#"u"ab""#, Std::C23, false, &big).expect("a string");
1005        assert_eq!(literal.bytes(&big), vec![0x00, 0x61, 0x00, 0x62, 0x00, 0x00]);
1006        assert_eq!(literal.bytes(&linux()), vec![0x61, 0x00, 0x62, 0x00, 0x00, 0x00]);
1007    }
1008
1009    /// `L` is C89, `u` and `U` are C11, and `u8` is C11 on a string and C23 on a character
1010    /// constant, which is the one place the two differ.
1011    #[test]
1012    fn a_prefix_is_only_available_in_the_dialect_that_has_it() {
1013        assert!(character(b"L'a'", Std::C89, false, &linux()).is_ok());
1014        assert_eq!(
1015            character(b"u'a'", Std::C99, false, &linux()).expect_err("not in C99"),
1016            LiteralError::PrefixNotInDialect
1017        );
1018        assert!(character(b"u'a'", Std::C11, false, &linux()).is_ok());
1019        assert!(string(br#"u8"a""#, Std::C11, false, &linux()).is_ok());
1020        assert_eq!(
1021            character(b"u8'a'", Std::C11, false, &linux()).expect_err("not in C11"),
1022            LiteralError::PrefixNotInDialect
1023        );
1024        assert!(character(b"u8'a'", Std::C23, false, &linux()).is_ok());
1025    }
1026
1027    /// gcc 16 offers the three C11 string prefixes from gnu99 on, and offers `u8` on a
1028    /// character constant in no dialect before C23, gnu23 included.
1029    #[test]
1030    fn the_gnu_dialects_have_the_string_prefixes_earlier_and_the_character_one_at_the_same_time() {
1031        assert!(string(br#"u8"a""#, Std::C99, true, &linux()).is_ok());
1032        assert!(string(br#"u"a""#, Std::C99, true, &linux()).is_ok());
1033        assert!(string(br#"U"a""#, Std::C99, true, &linux()).is_ok());
1034        assert!(character(b"u'a'", Std::C99, true, &linux()).is_ok());
1035        assert_eq!(
1036            string(br#"u8"a""#, Std::C89, true, &linux()).expect_err("not in gnu89"),
1037            LiteralError::PrefixNotInDialect
1038        );
1039        assert_eq!(
1040            character(b"u8'a'", Std::C17, true, &linux()).expect_err("not in gnu17"),
1041            LiteralError::PrefixNotInDialect
1042        );
1043    }
1044
1045    /// The widths and signs the elements have, which is what the parser will turn into the
1046    /// type of the literal.
1047    #[test]
1048    fn an_element_is_as_wide_as_the_encoding_and_the_target_agree() {
1049        let target = linux();
1050        assert_eq!(Encoding::Plain.element_width(&target), 8);
1051        assert_eq!(Encoding::Utf8.element_width(&target), 8);
1052        assert_eq!(Encoding::Utf16.element_width(&target), 16);
1053        assert_eq!(Encoding::Utf32.element_width(&target), 32);
1054        assert_eq!(Encoding::Wide.element_width(&target), 32);
1055        assert_eq!(Encoding::Wide.element_width(&windows()), 16);
1056
1057        assert!(Encoding::Plain.is_signed(&target));
1058        assert!(!Encoding::Plain.is_signed(&arm()));
1059        assert!(Encoding::Wide.is_signed(&target));
1060        assert!(!Encoding::Wide.is_signed(&arm()));
1061        assert!(!Encoding::Utf8.is_signed(&target));
1062        assert!(!Encoding::Utf16.is_signed(&target));
1063        assert!(!Encoding::Utf32.is_signed(&target));
1064    }
1065
1066    #[test]
1067    fn a_spelling_that_is_not_a_literal_is_refused_rather_than_guessed_at() {
1068        assert_eq!(ch_error("a"), LiteralError::NotALiteral);
1069        assert_eq!(ch_error("'a"), LiteralError::NotALiteral);
1070        assert_eq!(
1071            string(b"'a'", Std::C23, false, &linux()).expect_err("not a string"),
1072            LiteralError::NotALiteral
1073        );
1074        assert_eq!(ch_error("'"), LiteralError::NotALiteral);
1075    }
1076
1077    #[test]
1078    fn every_error_has_something_to_print() {
1079        for error in [
1080            LiteralError::NotALiteral,
1081            LiteralError::Empty,
1082            LiteralError::TooLong,
1083            LiteralError::NoHexDigits,
1084            LiteralError::IncompleteUcn,
1085            LiteralError::InvalidUcn,
1086            LiteralError::NamedUcn,
1087            LiteralError::InvalidUtf8,
1088            LiteralError::PrefixNotInDialect,
1089            LiteralError::MixedEncodings,
1090        ] {
1091            assert!(!error.message().is_empty());
1092        }
1093    }
1094}