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: &str) -> Encoding {
128        Encoding::read(text.as_bytes()).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: &str,
364    std: Std,
365    gnu: bool,
366    target: &TargetInfo,
367) -> Result<CharConstant, LiteralError> {
368    let (encoding, body) = open(text, b'\'', std, gnu, true)?;
369    let width = encoding.element_width(target);
370    let mut reader = Reader { bytes: body, index: 0, std, remarks: Remarks::NONE };
371
372    // 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: &str,
416    std: Std,
417    gnu: bool,
418    target: &TargetInfo,
419) -> Result<StringLiteral, LiteralError> {
420    strings(std::slice::from_ref(&text), std, gnu, target)
421}
422
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: &[&str],
440    std: Std,
441    gnu: bool,
442    target: &TargetInfo,
443) -> Result<StringLiteral, LiteralError> {
444    let mut bodies = Vec::with_capacity(texts.len());
445    let mut encoding = Encoding::Plain;
446    for text in texts {
447        let (found, body) = open(text, b'"', std, gnu, false)?;
448        if found != Encoding::Plain {
449            if encoding != Encoding::Plain && encoding != found {
450                return Err(LiteralError::MixedEncodings);
451            }
452            encoding = found;
453        }
454        bodies.push(body);
455    }
456
457    let width = encoding.element_width(target);
458    let mut elements = Vec::new();
459    let mut remarks = Remarks::NONE;
460    for body in bodies {
461        let mut reader = Reader { bytes: body, index: 0, std, remarks: Remarks::NONE };
462        while let Some(piece) = reader.next(width)? {
463            elements.extend(piece.elements(width));
464        }
465        remarks = remarks.with(reader.remarks);
466    }
467    Ok(StringLiteral { elements, encoding, remarks })
468}
469
470/// Reads the prefix and the quotes, and hands back the encoding and what is between them.
471fn open(
472    text: &str,
473    quote: u8,
474    std: Std,
475    gnu: bool,
476    character: bool,
477) -> Result<(Encoding, &[u8]), LiteralError> {
478    let bytes = text.as_bytes();
479    let (encoding, prefix) = Encoding::read(bytes);
480    if std < encoding.since(character, gnu) {
481        return Err(LiteralError::PrefixNotInDialect);
482    }
483    let rest = &bytes[prefix..];
484    match rest {
485        [first, .., last] if *first == quote && *last == quote => {
486            Ok((encoding, &rest[1..rest.len() - 1]))
487        }
488        _ => Err(LiteralError::NotALiteral),
489    }
490}
491
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, 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, 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, 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, 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, Std::C23, false, &linux()).expect("a string literal").bytes(&linux())
763    }
764
765    #[test]
766    fn the_ordinary_cases_are_the_characters_they_look_like() {
767        assert_eq!(ch("'a'"), 0x61);
768        assert_eq!(ch(r"'\n'"), 0x0a);
769        assert_eq!(ch(r"'\0'"), 0);
770        assert_eq!(ch(r"'\\'"), 0x5c);
771        assert_eq!(ch(r"'\''"), 0x27);
772        assert_eq!(ch(r#"'\"'"#), 0x22);
773        assert_eq!(ch(r"'\?'"), 0x3f);
774        assert_eq!(str_elements(r#""hi""#), vec![0x68, 0x69]);
775    }
776
777    /// A single character in a plain constant goes through plain `char` on the way to `int`,
778    /// which is the whole reason `'\xff'` is a negative number on one target and a positive
779    /// one on another.
780    #[test]
781    fn a_high_character_takes_the_sign_of_plain_char() {
782        assert_eq!(ch(r"'\xff'"), -1);
783        assert_eq!(ch(r"'\377'"), -1);
784        assert_eq!(character(r"'\xff'", Std::C23, false, &arm()).expect("a constant").value, 255);
785        // Not a `char`, so nothing sign extends it.
786        assert_eq!(ch(r"u8'\xff'"), 255);
787    }
788
789    /// Measured on GCC 13.3, x86-64 Linux. A value escape is truncated to its element and the
790    /// warning is about the truncation, not about the type. The two spellings get two remarks
791    /// because GCC gives them two wordings.
792    #[test]
793    fn an_escape_too_big_for_its_element_is_truncated_and_says_so() {
794        let out = character(r"'\x1ff'", Std::C23, false, &linux()).expect("a constant");
795        assert_eq!(out.value, -1);
796        assert!(out.remarks.has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
797        let out = character(r"'\400'", Std::C23, false, &linux()).expect("a constant");
798        assert_eq!(out.value, 0);
799        assert!(out.remarks.has(Remarks::OCTAL_ESCAPE_OUT_OF_RANGE));
800        assert!(!out.remarks.has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
801        // Wide enough to hold it, so there is nothing to say.
802        assert!(!ch_remarks(r"L'\x1ff'").has(Remarks::HEX_ESCAPE_OUT_OF_RANGE));
803        assert_eq!(ch(r"L'\x1ff'"), 0x1ff);
804    }
805
806    /// Measured on GCC 13.3: a plain literal in a run takes the prefix of its neighbour, two
807    /// different prefixes are an error, and the bodies are read in the encoding of the run, so
808    /// a character in the plain part is one wide element rather than its UTF-8 bytes.
809    #[test]
810    fn adjacent_literals_agree_on_one_encoding_or_none_at_all() {
811        let target = linux();
812        let wide = strings(&[r#"L"a""#, r#""b""#], Std::C23, false, &target).expect("a string");
813        assert_eq!(wide.encoding, Encoding::Wide);
814        assert_eq!(wide.elements, vec![0x61, 0x62]);
815        assert_eq!(wide.bytes(&target).len(), 12);
816        let other_way =
817            strings(&[r#""a""#, r#"L"b""#], Std::C23, false, &target).expect("a string");
818        assert_eq!(other_way.encoding, Encoding::Wide);
819        assert_eq!(other_way.bytes(&target).len(), 12);
820
821        let u8_run = strings(&[r#"u8"a""#, r#""b""#], Std::C23, false, &target).expect("a string");
822        assert_eq!(u8_run.encoding, Encoding::Utf8);
823        assert_eq!(u8_run.bytes(&target).len(), 3);
824
825        // The plain part is read as wide, so the accented letter is one element and not two.
826        let mixed = strings(&[r#"L"a""#, r#""é""#], Std::C23, false, &target).expect("a string");
827        assert_eq!(mixed.elements, vec![0x61, 0xe9]);
828
829        for run in [[r#"u8"a""#, r#"u"b""#], [r#"u8"a""#, r#"L"b""#], [r#"u"a""#, r#"L"b""#]] {
830            assert_eq!(
831                strings(&run, Std::C23, false, &target).expect_err("two prefixes in one run"),
832                LiteralError::MixedEncodings
833            );
834        }
835
836        // A run of one is the same thing as the literal on its own.
837        assert_eq!(
838            strings(&[r#""hi""#], Std::C23, false, &target).expect("a string").elements,
839            vec![0x68, 0x69]
840        );
841    }
842
843    /// Also measured on GCC 13.3. The characters are shifted together, the ones past the width
844    /// of the type fall off the front, and past that point GCC says "too long" instead of
845    /// "multi-character" rather than as well as it.
846    #[test]
847    fn more_than_one_character_shifts_them_together() {
848        assert_eq!(ch("'ab'"), 0x6162);
849        assert_eq!(ch("'abc'"), 0x616263);
850        assert_eq!(ch("'abcd'"), 0x61626364);
851        assert_eq!(ch("'abcde'"), 0x62636465);
852        assert_eq!(ch(r"'\xff\xfe'"), 0xfffe);
853        assert_eq!(ch(r"'\xff\xff\xff\xff'"), -1);
854        assert_eq!(ch(r"'\x80\x00'"), 0x8000);
855
856        assert!(ch_remarks("'ab'").has(Remarks::MULTICHARACTER));
857        assert!(ch_remarks("'abcd'").has(Remarks::MULTICHARACTER));
858        assert!(ch_remarks("'abcde'").has(Remarks::TOO_LONG));
859        assert!(!ch_remarks("'abcde'").has(Remarks::MULTICHARACTER));
860        assert!(!ch_remarks("'a'").has(Remarks::MULTICHARACTER));
861    }
862
863    /// A wide constant has room for exactly one character, so two is already too many and the
864    /// last one is what survives. `u8` is the one encoding where GCC makes this an error.
865    #[test]
866    fn a_prefixed_constant_holds_one_character_and_keeps_the_last() {
867        for text in [r"L'ab'", r"u'ab'", r"U'ab'"] {
868            let out = character(text, Std::C23, false, &linux()).expect("a constant");
869            assert_eq!(out.value, 0x62, "{text}");
870            assert!(out.remarks.has(Remarks::TOO_LONG), "{text}");
871        }
872        assert_eq!(ch_error("u8'ab'"), LiteralError::TooLong);
873        assert_eq!(ch_error("u8'é'"), LiteralError::TooLong);
874    }
875
876    #[test]
877    fn the_empty_constant_has_no_value_to_have() {
878        assert_eq!(ch_error("''"), LiteralError::Empty);
879        assert_eq!(ch_error("L''"), LiteralError::Empty);
880        // The empty string is fine, and is one element long once the terminator is there.
881        assert_eq!(str_elements(r#""""#), Vec::<u32>::new());
882        assert_eq!(str_bytes(r#""""#), vec![0]);
883    }
884
885    /// A character from the source is encoded in the literal's encoding, so the same `é` is
886    /// two bytes in one constant and one code point in another.
887    #[test]
888    fn a_source_character_is_encoded_and_an_escape_is_not() {
889        assert_eq!(ch("'é'"), 0xc3a9);
890        assert_eq!(ch("L'é'"), 0xe9);
891        assert_eq!(ch("u'€'"), 0x20ac);
892        assert_eq!(ch(r"U'\U0001F600'"), 0x1f600);
893        // The same character in a plain constant is its UTF-8 bytes, which makes it a
894        // multi-character constant and a negative number.
895        assert_eq!(ch(r"'\U0001F600'"), i64::from(0xf09f_9880u32 as i32));
896        assert!(ch_remarks(r"'\U0001F600'").has(Remarks::MULTICHARACTER));
897    }
898
899    /// Both compilers have `\e` and neither standard does, and an escape that means nothing is
900    /// the letter itself after a warning rather than an error.
901    #[test]
902    fn the_escapes_outside_the_standard_still_have_values() {
903        assert_eq!(ch(r"'\e'"), 0x1b);
904        assert!(ch_remarks(r"'\e'").has(Remarks::NON_ISO_ESCAPE));
905        assert_eq!(ch(r"'\q'"), 0x71);
906        assert!(ch_remarks(r"'\q'").has(Remarks::UNKNOWN_ESCAPE));
907        assert_eq!(ch_error(r"'\x'"), LiteralError::NoHexDigits);
908        assert_eq!(ch_error(r"'\N{LATIN SMALL LETTER A}'"), LiteralError::NamedUcn);
909    }
910
911    /// A universal character name may not name a character in the basic character set, which
912    /// GCC still enforces in C23 where the wording was relaxed, and the three characters below
913    /// a space that are allowed anyway are allowed here too.
914    #[test]
915    fn a_universal_character_name_may_not_name_just_anything() {
916        assert_eq!(ch("'\\u0024'"), 0x24);
917        assert_eq!(ch("'\\u00e9'"), 0xc3a9);
918        assert_eq!(ch_error("'\\u0041'"), LiteralError::InvalidUcn);
919        assert_eq!(ch_error(r"'\ud800'"), LiteralError::InvalidUcn);
920        assert_eq!(ch_error(r"'\u00'"), LiteralError::IncompleteUcn);
921        // GCC warns here and encodes the value anyway. clang refuses it and so does this.
922        assert_eq!(ch_error(r"'\U00110000'"), LiteralError::InvalidUcn);
923    }
924
925    #[test]
926    fn a_universal_character_name_before_c99_is_worth_a_remark() {
927        let out = character("'\\u00e9'", Std::C89, false, &linux()).expect("a constant");
928        assert!(out.remarks.has(Remarks::UCN));
929        let out = character("'\\u00e9'", Std::C99, false, &linux()).expect("a constant");
930        assert!(!out.remarks.has(Remarks::UCN));
931    }
932
933    /// Octal runs to three digits and stops, and hexadecimal runs as far as the digits go, so
934    /// `"\1234"` is two characters and `"\x41z"` is two as well.
935    #[test]
936    fn an_octal_escape_ends_and_a_hex_escape_does_not() {
937        assert_eq!(str_elements(r#""\1234""#), vec![0x53, 0x34]);
938        assert_eq!(str_elements(r#""\x41z""#), vec![0x41, 0x7a]);
939        assert_eq!(str_elements(r#""\x41""#), vec![0x41]);
940    }
941
942    /// The sizes GCC reports for these, which is the elements plus the terminator times the
943    /// width of one.
944    #[test]
945    fn a_string_is_as_many_bytes_as_its_encoding_makes_it() {
946        assert_eq!(str_bytes(r#""abc""#).len(), 4);
947        assert_eq!(str_bytes(r#"L"abc""#).len(), 16);
948        assert_eq!(str_bytes(r#"u"abc""#).len(), 8);
949        assert_eq!(str_bytes(r#"U"abc""#).len(), 16);
950        assert_eq!(str_bytes(r#"u8"abc""#).len(), 4);
951        // A zero in the middle is an element like any other, and the terminator is still added.
952        assert_eq!(str_bytes(r#""a\0b""#), vec![0x61, 0x00, 0x62, 0x00]);
953        assert_eq!(str_bytes(r#""é""#), vec![0xc3, 0xa9, 0x00]);
954    }
955
956    /// The one encoding where a character can take two elements, which is why a wide string is
957    /// not the same length on Windows as it is anywhere else.
958    #[test]
959    fn utf16_splits_the_characters_that_do_not_fit_into_a_surrogate_pair() {
960        assert_eq!(
961            str_elements(r#"u8"é€😀""#),
962            vec![0xc3, 0xa9, 0xe2, 0x82, 0xac, 0xf0, 0x9f, 0x98, 0x80]
963        );
964        assert_eq!(str_elements(r#"u"€😀""#), vec![0x20ac, 0xd83d, 0xde00]);
965        assert_eq!(str_elements(r#"U"€😀""#), vec![0x20ac, 0x1f600]);
966    }
967
968    /// A wide literal is UTF-16 on Windows and UTF-32 everywhere else, so the same three
969    /// characters are four elements on one target and three on the other.
970    #[test]
971    fn a_wide_literal_is_whatever_the_target_makes_wchar_t() {
972        let text = r#"L"a😀""#;
973        let here = string(text, Std::C23, false, &linux()).expect("a string");
974        assert_eq!(here.elements, vec![0x61, 0x1f600]);
975        assert_eq!(here.bytes(&linux()).len(), 12);
976        let there = string(text, Std::C23, false, &windows()).expect("a string");
977        assert_eq!(there.elements, vec![0x61, 0xd83d, 0xde00]);
978        assert_eq!(there.bytes(&windows()).len(), 8);
979        // And a wide character constant takes the sign of `wchar_t`, which is not the same on
980        // every target either.
981        assert_eq!(
982            character(r"L'\xffffffff'", Std::C23, false, &linux()).expect("a constant").value,
983            -1
984        );
985        assert_eq!(
986            character(r"L'\xffffffff'", Std::C23, false, &arm()).expect("a constant").value,
987            0xffff_ffff
988        );
989    }
990
991    /// Every target the compiler has is little-endian, so the other order is checked by
992    /// flipping the field rather than by naming a target, and this is the test that fails on
993    /// the day a big-endian one arrives with the layout still assuming otherwise.
994    #[test]
995    fn the_bytes_come_out_in_the_targets_order() {
996        let mut big = linux();
997        big.little_endian = false;
998        let literal = string(r#"u"ab""#, Std::C23, false, &big).expect("a string");
999        assert_eq!(literal.bytes(&big), vec![0x00, 0x61, 0x00, 0x62, 0x00, 0x00]);
1000        assert_eq!(literal.bytes(&linux()), vec![0x61, 0x00, 0x62, 0x00, 0x00, 0x00]);
1001    }
1002
1003    /// `L` is C89, `u` and `U` are C11, and `u8` is C11 on a string and C23 on a character
1004    /// constant, which is the one place the two differ.
1005    #[test]
1006    fn a_prefix_is_only_available_in_the_dialect_that_has_it() {
1007        assert!(character("L'a'", Std::C89, false, &linux()).is_ok());
1008        assert_eq!(
1009            character("u'a'", Std::C99, false, &linux()).expect_err("not in C99"),
1010            LiteralError::PrefixNotInDialect
1011        );
1012        assert!(character("u'a'", Std::C11, false, &linux()).is_ok());
1013        assert!(string(r#"u8"a""#, Std::C11, false, &linux()).is_ok());
1014        assert_eq!(
1015            character("u8'a'", Std::C11, false, &linux()).expect_err("not in C11"),
1016            LiteralError::PrefixNotInDialect
1017        );
1018        assert!(character("u8'a'", Std::C23, false, &linux()).is_ok());
1019    }
1020
1021    /// gcc 16 offers the three C11 string prefixes from gnu99 on, and offers `u8` on a
1022    /// character constant in no dialect before C23, gnu23 included.
1023    #[test]
1024    fn the_gnu_dialects_have_the_string_prefixes_earlier_and_the_character_one_at_the_same_time() {
1025        assert!(string(r#"u8"a""#, Std::C99, true, &linux()).is_ok());
1026        assert!(string(r#"u"a""#, Std::C99, true, &linux()).is_ok());
1027        assert!(string(r#"U"a""#, Std::C99, true, &linux()).is_ok());
1028        assert!(character("u'a'", Std::C99, true, &linux()).is_ok());
1029        assert_eq!(
1030            string(r#"u8"a""#, Std::C89, true, &linux()).expect_err("not in gnu89"),
1031            LiteralError::PrefixNotInDialect
1032        );
1033        assert_eq!(
1034            character("u8'a'", Std::C17, true, &linux()).expect_err("not in gnu17"),
1035            LiteralError::PrefixNotInDialect
1036        );
1037    }
1038
1039    /// The widths and signs the elements have, which is what the parser will turn into the
1040    /// type of the literal.
1041    #[test]
1042    fn an_element_is_as_wide_as_the_encoding_and_the_target_agree() {
1043        let target = linux();
1044        assert_eq!(Encoding::Plain.element_width(&target), 8);
1045        assert_eq!(Encoding::Utf8.element_width(&target), 8);
1046        assert_eq!(Encoding::Utf16.element_width(&target), 16);
1047        assert_eq!(Encoding::Utf32.element_width(&target), 32);
1048        assert_eq!(Encoding::Wide.element_width(&target), 32);
1049        assert_eq!(Encoding::Wide.element_width(&windows()), 16);
1050
1051        assert!(Encoding::Plain.is_signed(&target));
1052        assert!(!Encoding::Plain.is_signed(&arm()));
1053        assert!(Encoding::Wide.is_signed(&target));
1054        assert!(!Encoding::Wide.is_signed(&arm()));
1055        assert!(!Encoding::Utf8.is_signed(&target));
1056        assert!(!Encoding::Utf16.is_signed(&target));
1057        assert!(!Encoding::Utf32.is_signed(&target));
1058    }
1059
1060    #[test]
1061    fn a_spelling_that_is_not_a_literal_is_refused_rather_than_guessed_at() {
1062        assert_eq!(ch_error("a"), LiteralError::NotALiteral);
1063        assert_eq!(ch_error("'a"), LiteralError::NotALiteral);
1064        assert_eq!(
1065            string("'a'", Std::C23, false, &linux()).expect_err("not a string"),
1066            LiteralError::NotALiteral
1067        );
1068        assert_eq!(ch_error("'"), LiteralError::NotALiteral);
1069    }
1070
1071    #[test]
1072    fn every_error_has_something_to_print() {
1073        for error in [
1074            LiteralError::NotALiteral,
1075            LiteralError::Empty,
1076            LiteralError::TooLong,
1077            LiteralError::NoHexDigits,
1078            LiteralError::IncompleteUcn,
1079            LiteralError::InvalidUcn,
1080            LiteralError::NamedUcn,
1081            LiteralError::InvalidUtf8,
1082            LiteralError::PrefixNotInDialect,
1083            LiteralError::MixedEncodings,
1084        ] {
1085            assert!(!error.message().is_empty());
1086        }
1087    }
1088}