1use rucc_base::{Interner, Symbol};
36use rucc_diag::{Diagnostic, Span};
37use rucc_session::Std;
38use rucc_target::TargetInfo;
39
40use crate::keyword::{Keyword, Keywords};
41use crate::literal::{CharConstant, LiteralError, StringLiteral};
42use crate::number::{FloatConstant, IntConstant, IntError};
43use crate::remarks::Remarks;
44use crate::token::{PpToken, PpTokenKind, Punct, TokenFlags};
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
51pub enum TokenKind {
52 Keyword(Keyword),
54 Ident,
56 Int,
58 Float,
60 Char,
62 Str,
65 Punct(Punct),
67 Eof,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub struct Token {
77 pub kind: TokenKind,
79 pub flags: TokenFlags,
82 pub value: u32,
85 pub span: Span,
87}
88
89impl Token {
90 #[inline]
92 #[must_use]
93 pub const fn is_eof(self) -> bool {
94 matches!(self.kind, TokenKind::Eof)
95 }
96
97 #[inline]
99 #[must_use]
100 pub const fn keyword(self) -> Option<Keyword> {
101 match self.kind {
102 TokenKind::Keyword(word) => Some(word),
103 _ => None,
104 }
105 }
106
107 #[inline]
109 #[must_use]
110 pub const fn punct(self) -> Option<Punct> {
111 match self.kind {
112 TokenKind::Punct(punct) => Some(punct),
113 _ => None,
114 }
115 }
116
117 #[inline]
119 #[must_use]
120 pub const fn ident(self) -> Option<Symbol> {
121 match self.kind {
122 TokenKind::Ident => Some(Symbol::from_raw(self.value)),
123 _ => None,
124 }
125 }
126}
127
128#[derive(Debug, Default)]
130pub struct Tokens {
131 pub tokens: Vec<Token>,
133 pub ints: Vec<IntConstant>,
135 pub floats: Vec<FloatConstant>,
137 pub chars: Vec<CharConstant>,
139 pub strings: Vec<StringLiteral>,
141}
142
143impl Tokens {
144 #[must_use]
146 pub fn int(&self, token: Token) -> Option<&IntConstant> {
147 match token.kind {
148 TokenKind::Int => self.ints.get(token.value as usize),
149 _ => None,
150 }
151 }
152
153 #[must_use]
155 pub fn float(&self, token: Token) -> Option<&FloatConstant> {
156 match token.kind {
157 TokenKind::Float => self.floats.get(token.value as usize),
158 _ => None,
159 }
160 }
161
162 #[must_use]
164 pub fn character(&self, token: Token) -> Option<&CharConstant> {
165 match token.kind {
166 TokenKind::Char => self.chars.get(token.value as usize),
167 _ => None,
168 }
169 }
170
171 #[must_use]
173 pub fn string(&self, token: Token) -> Option<&StringLiteral> {
174 match token.kind {
175 TokenKind::Str => self.strings.get(token.value as usize),
176 _ => None,
177 }
178 }
179}
180
181#[derive(Debug, Clone, Copy)]
183pub struct Convert<'a> {
184 pub keywords: &'a Keywords,
186 pub interner: &'a Interner,
188 pub target: &'a TargetInfo,
190 pub std: Std,
192 pub pedantic: bool,
195}
196
197#[must_use]
203pub fn convert(pp: &[PpToken], cx: &Convert<'_>) -> (Tokens, Vec<Diagnostic>) {
204 let mut out = Tokens { tokens: Vec::with_capacity(pp.len()), ..Tokens::default() };
205 let mut diagnostics = Vec::new();
206 let mut index = 0;
207 while index < pp.len() {
208 let token = pp[index];
209 index += 1;
210 match token.kind {
211 PpTokenKind::Ident => out.tokens.push(identifier(token, cx)),
212 PpTokenKind::Number => {
213 out.tokens.push(number(
214 token,
215 cx,
216 &mut out.ints,
217 &mut out.floats,
218 &mut diagnostics,
219 ));
220 }
221 PpTokenKind::CharConst => {
222 out.tokens.push(char_const(token, cx, &mut out.chars, &mut diagnostics));
223 }
224 PpTokenKind::StringLit => {
225 let start = index - 1;
229 while pp.get(index).is_some_and(|next| next.kind == PpTokenKind::StringLit) {
230 index += 1;
231 }
232 let run = &pp[start..index];
233 out.tokens.push(string_lit(run, cx, &mut out.strings, &mut diagnostics));
234 }
235 PpTokenKind::Punct(punct) => out.tokens.push(Token {
236 kind: TokenKind::Punct(punct),
237 flags: token.flags,
238 value: 0,
239 span: token.span,
240 }),
241 PpTokenKind::Eof => out.tokens.push(Token {
242 kind: TokenKind::Eof,
243 flags: token.flags,
244 value: 0,
245 span: token.span,
246 }),
247 PpTokenKind::Other | PpTokenKind::HeaderName => {
252 let text = spelling(token, cx);
253 diagnostics
254 .push(Diagnostic::error(format!("stray '{text}' in program"), token.span));
255 }
256 }
257 }
258 if out.tokens.last().is_none_or(|last| !last.is_eof()) {
259 let end =
262 out.tokens.last().map_or(Span::new(0, 0), |last| Span::new(last.span.hi, last.span.hi));
263 out.tokens.push(Token {
264 kind: TokenKind::Eof,
265 flags: TokenFlags::EMPTY,
266 value: 0,
267 span: end,
268 });
269 }
270 (out, diagnostics)
271}
272
273fn spelling<'a>(token: PpToken, cx: &Convert<'a>) -> &'a str {
275 token.value.map_or("", |symbol| cx.interner.resolve(symbol))
276}
277
278fn identifier(token: PpToken, cx: &Convert<'_>) -> Token {
280 let symbol = token.value.expect("an identifier carries its spelling");
281 let kind = match cx.keywords.get(symbol) {
282 Some(word) => TokenKind::Keyword(word),
283 None => TokenKind::Ident,
284 };
285 Token { kind, flags: token.flags, value: symbol.raw(), span: token.span }
286}
287
288fn number(
290 token: PpToken,
291 cx: &Convert<'_>,
292 ints: &mut Vec<IntConstant>,
293 floats: &mut Vec<FloatConstant>,
294 diagnostics: &mut Vec<Diagnostic>,
295) -> Token {
296 let text = spelling(token, cx);
297 match crate::number::integer(text, cx.std, cx.target) {
300 Ok(value) => {
301 report(value.remarks, None, token.span, cx, diagnostics);
302 ints.push(value);
303 let index = u32::try_from(ints.len() - 1).expect("that many constants in one file");
304 Token { kind: TokenKind::Int, flags: token.flags, value: index, span: token.span }
305 }
306 Err(IntError::Floating) => match crate::number::floating(text, cx.std, cx.target) {
307 Ok(value) => {
308 report(value.remarks, Some(value.ty.name()), token.span, cx, diagnostics);
309 floats.push(value);
310 let index =
311 u32::try_from(floats.len() - 1).expect("that many constants in one file");
312 Token { kind: TokenKind::Float, flags: token.flags, value: index, span: token.span }
313 }
314 Err(error) => {
315 diagnostics.push(Diagnostic::error(error.message(), token.span));
316 floats.push(zero_float(cx));
319 let index =
320 u32::try_from(floats.len() - 1).expect("that many constants in one file");
321 Token { kind: TokenKind::Float, flags: token.flags, value: index, span: token.span }
322 }
323 },
324 Err(error) => {
325 diagnostics.push(Diagnostic::error(error.message(), token.span));
326 ints.push(IntConstant {
327 value: 0,
328 ty: crate::number::IntConstantType::Standard(rucc_types::IntKind::Int),
329 remarks: Remarks::NONE,
330 });
331 let index = u32::try_from(ints.len() - 1).expect("that many constants in one file");
332 Token { kind: TokenKind::Int, flags: token.flags, value: index, span: token.span }
333 }
334 }
335}
336
337fn zero_float(cx: &Convert<'_>) -> FloatConstant {
339 let ty = crate::number::FloatConstantType::Double;
340 FloatConstant {
341 value: rucc_base::float::Float::zero(ty.format(cx.target), false),
342 ty,
343 imaginary: false,
344 remarks: Remarks::NONE,
345 }
346}
347
348fn char_const(
350 token: PpToken,
351 cx: &Convert<'_>,
352 chars: &mut Vec<CharConstant>,
353 diagnostics: &mut Vec<Diagnostic>,
354) -> Token {
355 let text = spelling(token, cx);
356 let value = match crate::literal::character(text, cx.std, cx.target) {
357 Ok(value) => {
358 report(value.remarks, None, token.span, cx, diagnostics);
359 value
360 }
361 Err(error) => {
362 diagnostics.push(Diagnostic::error(error.message(), token.span));
363 CharConstant {
364 value: 0,
365 encoding: crate::literal::Encoding::Plain,
366 remarks: Remarks::NONE,
367 }
368 }
369 };
370 chars.push(value);
371 let index = u32::try_from(chars.len() - 1).expect("that many constants in one file");
372 Token { kind: TokenKind::Char, flags: token.flags, value: index, span: token.span }
373}
374
375fn string_lit(
377 run: &[PpToken],
378 cx: &Convert<'_>,
379 strings: &mut Vec<StringLiteral>,
380 diagnostics: &mut Vec<Diagnostic>,
381) -> Token {
382 let first = run[0];
383 let span = first.span.to(run[run.len() - 1].span);
384 let texts: Vec<&str> = run.iter().map(|token| spelling(*token, cx)).collect();
385 let value = match crate::literal::strings(&texts, cx.std, cx.target) {
386 Ok(value) => {
387 report(value.remarks, None, span, cx, diagnostics);
388 value
389 }
390 Err(error) => {
391 diagnostics.push(Diagnostic::error(error.message(), span));
392 let encoding = if error == LiteralError::MixedEncodings {
395 crate::literal::Encoding::Plain
396 } else {
397 crate::literal::Encoding::read_prefix(texts[0])
398 };
399 StringLiteral { elements: Vec::new(), encoding, remarks: Remarks::NONE }
400 }
401 };
402 strings.push(value);
403 let index = u32::try_from(strings.len() - 1).expect("that many literals in one file");
404 Token { kind: TokenKind::Str, flags: first.flags, value: index, span }
405}
406
407fn report(
413 remarks: Remarks,
414 type_name: Option<&str>,
415 span: Span,
416 cx: &Convert<'_>,
417 diagnostics: &mut Vec<Diagnostic>,
418) {
419 if remarks.is_none() {
420 return;
421 }
422
423 let always: [(Remarks, &str); 6] = [
426 (Remarks::MULTICHARACTER, "multi-character character constant"),
427 (Remarks::TOO_LONG, "character constant too long for its type"),
428 (Remarks::UNKNOWN_ESCAPE, "unknown escape sequence"),
429 (Remarks::HEX_ESCAPE_OUT_OF_RANGE, "hex escape sequence out of range"),
430 (Remarks::OCTAL_ESCAPE_OUT_OF_RANGE, "octal escape sequence out of range"),
431 (Remarks::UNSIGNED, "integer constant is so large that it is unsigned"),
432 ];
433 for (remark, message) in always {
434 if remarks.has(remark) {
435 diagnostics.push(Diagnostic::warning(message, span));
436 }
437 }
438 if remarks.has(Remarks::OUT_OF_RANGE) {
439 let ty = type_name.unwrap_or("double");
440 diagnostics
441 .push(Diagnostic::warning(format!("floating constant exceeds range of '{ty}'"), span));
442 }
443 if remarks.has(Remarks::TRUNCATED) {
444 diagnostics.push(Diagnostic::warning("floating constant truncated to zero", span));
445 }
446
447 if !cx.pedantic {
448 return;
449 }
450 let pedantic: [(Remarks, &str); 9] = [
453 (Remarks::NON_ISO_ESCAPE, "non-ISO-standard escape sequence"),
454 (Remarks::DOUBLE_SUFFIX, "suffix for double constant is a GCC extension"),
455 (Remarks::IMAGINARY, "imaginary constants are a GCC extension"),
456 (Remarks::BINARY, "binary constants are a C23 feature or GCC extension"),
457 (Remarks::EXTENDED_SUFFIX, "non-standard suffix on floating constant"),
458 (Remarks::HEX_FLOAT, "use of C99 hexadecimal floating constant"),
459 (Remarks::LONG_LONG, "use of C99 long long integer constant"),
460 (Remarks::SEPARATORS, "digit separators are a C23 feature"),
461 (Remarks::BIT_INT, "'_BitInt' constants are a C23 feature"),
462 ];
463 for (remark, message) in pedantic {
464 if remarks.has(remark) {
465 diagnostics.push(Diagnostic::warning(message, span));
466 }
467 }
468 if remarks.has(Remarks::UCN) {
469 diagnostics.push(Diagnostic::warning(
470 "universal character names are only valid in C++ and C99",
471 span,
472 ));
473 }
474}
475
476#[cfg(test)]
477mod tests {
478 use rucc_target::Triple;
479
480 use super::*;
481 use crate::lexer::{Options, tokenize};
482
483 struct Fixture {
486 interner: Interner,
487 keywords: Keywords,
488 target: TargetInfo,
489 std: Std,
490 pedantic: bool,
491 }
492
493 impl Fixture {
494 fn new(std: Std) -> Fixture {
495 let mut interner = Interner::new();
496 let keywords = Keywords::new(&mut interner, std, true);
497 let target =
498 TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
499 Fixture { interner, keywords, target, std, pedantic: false }
500 }
501
502 fn run(&mut self, src: &str) -> (Tokens, Vec<String>) {
504 let (pp, lex_diagnostics) =
505 tokenize(src.as_bytes(), 0, Options::new(), &mut self.interner);
506 assert!(lex_diagnostics.is_empty(), "the scanner disliked the source: {src}");
507 let cx = Convert {
508 keywords: &self.keywords,
509 interner: &self.interner,
510 target: &self.target,
511 std: self.std,
512 pedantic: self.pedantic,
513 };
514 let (tokens, diagnostics) = convert(&pp, &cx);
515 (tokens, diagnostics.iter().map(|d| d.message.clone()).collect())
516 }
517 }
518
519 fn kinds(src: &str) -> Vec<TokenKind> {
520 Fixture::new(Std::C23).run(src).0.tokens.iter().map(|t| t.kind).collect()
521 }
522
523 #[test]
524 fn a_token_is_sixteen_bytes() {
525 assert_eq!(size_of::<Token>(), 16);
528 }
529
530 #[test]
531 fn a_declaration_converts_into_keywords_an_identifier_and_a_constant() {
532 assert_eq!(
533 kinds("int x = 1;"),
534 vec![
535 TokenKind::Keyword(Keyword::Int),
536 TokenKind::Ident,
537 TokenKind::Punct(Punct::Eq),
538 TokenKind::Int,
539 TokenKind::Punct(Punct::Semi),
540 TokenKind::Eof,
541 ]
542 );
543 }
544
545 #[test]
547 fn the_dialect_decides_which_identifiers_are_keywords() {
548 let mut c89 = Fixture::new(Std::C89);
549 let (tokens, _) = c89.run("restrict");
550 assert_eq!(tokens.tokens[0].kind, TokenKind::Ident);
551 let mut c99 = Fixture::new(Std::C99);
552 let (tokens, _) = c99.run("restrict");
553 assert_eq!(tokens.tokens[0].kind, TokenKind::Keyword(Keyword::Restrict));
554 }
555
556 #[test]
557 fn a_number_becomes_whichever_kind_of_constant_it_is() {
558 let mut fixture = Fixture::new(Std::C23);
559 let (tokens, diagnostics) = fixture.run("1 2.5 0x1p3 1u");
560 assert!(diagnostics.is_empty());
561 let kinds: Vec<_> = tokens.tokens.iter().map(|t| t.kind).collect();
562 assert_eq!(
563 kinds,
564 vec![
565 TokenKind::Int,
566 TokenKind::Float,
567 TokenKind::Float,
568 TokenKind::Int,
569 TokenKind::Eof
570 ]
571 );
572 assert_eq!(tokens.int(tokens.tokens[0]).expect("an integer").value, 1);
573 assert!(tokens.float(tokens.tokens[1]).is_some());
574 assert_eq!(tokens.tokens[2].value, 1);
577 assert_eq!(tokens.tokens[3].value, 1);
578 assert_eq!(tokens.int(tokens.tokens[3]).expect("an integer").value, 1);
579 assert!(tokens.float(tokens.tokens[0]).is_none());
581 assert!(tokens.string(tokens.tokens[0]).is_none());
582 }
583
584 #[test]
587 fn adjacent_string_literals_become_one_token() {
588 let mut fixture = Fixture::new(Std::C23);
589 let (tokens, diagnostics) = fixture.run(r#"char *s = "a" "b" L"c";"#);
590 assert!(diagnostics.is_empty(), "{diagnostics:?}");
591 let literal = tokens
592 .tokens
593 .iter()
594 .find(|t| t.kind == TokenKind::Str)
595 .copied()
596 .expect("a string literal");
597 let value = tokens.string(literal).expect("the literal");
598 assert_eq!(value.elements, vec![0x61, 0x62, 0x63]);
599 assert_eq!(value.encoding, crate::literal::Encoding::Wide);
600 assert_eq!(tokens.tokens.iter().filter(|t| t.kind == TokenKind::Str).count(), 1);
601 assert_eq!(literal.span.lo, 10);
603 assert_eq!(literal.span.hi, 22);
604 }
605
606 #[test]
607 fn a_character_constant_carries_its_value_and_its_warning() {
608 let mut fixture = Fixture::new(Std::C23);
609 let (tokens, diagnostics) = fixture.run("'ab'");
610 assert_eq!(diagnostics, vec!["multi-character character constant".to_owned()]);
611 assert_eq!(tokens.character(tokens.tokens[0]).expect("a constant").value, 0x6162);
612 }
613
614 #[test]
617 fn the_warnings_that_need_no_flag_are_given_without_one() {
618 let mut fixture = Fixture::new(Std::C17);
619 let (_, diagnostics) = fixture.run(r"'abcde' '\q' '\x1ff' '\400' 1e400 1e-400");
620 assert_eq!(
621 diagnostics,
622 vec![
623 "character constant too long for its type".to_owned(),
624 "unknown escape sequence".to_owned(),
625 "hex escape sequence out of range".to_owned(),
626 "octal escape sequence out of range".to_owned(),
627 "floating constant exceeds range of 'double'".to_owned(),
628 "floating constant truncated to zero".to_owned(),
629 ]
630 );
631 }
632
633 #[test]
635 fn the_warnings_that_need_pedantic_wait_for_it() {
636 let mut quiet = Fixture::new(Std::C17);
637 let (_, diagnostics) = quiet.run(r"1.0d 1.0i 0b1010 '\e'");
638 assert!(diagnostics.is_empty(), "{diagnostics:?}");
639
640 let mut loud = Fixture::new(Std::C17);
641 loud.pedantic = true;
642 let (_, diagnostics) = loud.run(r"1.0d 1.0i 0b1010 '\e'");
643 assert_eq!(
644 diagnostics,
645 vec![
646 "suffix for double constant is a GCC extension".to_owned(),
647 "imaginary constants are a GCC extension".to_owned(),
648 "binary constants are a C23 feature or GCC extension".to_owned(),
649 "non-ISO-standard escape sequence".to_owned(),
650 ]
651 );
652 }
653
654 #[test]
655 fn the_overflow_warning_names_the_type_the_constant_actually_has() {
656 let mut fixture = Fixture::new(Std::C23);
657 let (_, diagnostics) = fixture.run("1e400f");
658 assert_eq!(diagnostics, vec!["floating constant exceeds range of 'float'".to_owned()]);
659 }
660
661 #[test]
664 fn a_constant_that_will_not_convert_still_leaves_a_token_behind() {
665 let mut fixture = Fixture::new(Std::C23);
666 let (tokens, diagnostics) = fixture.run("int x = 1.2.3;");
667 assert_eq!(diagnostics.len(), 1);
668 let kinds: Vec<_> = tokens.tokens.iter().map(|t| t.kind).collect();
669 assert_eq!(
670 kinds,
671 vec![
672 TokenKind::Keyword(Keyword::Int),
673 TokenKind::Ident,
674 TokenKind::Punct(Punct::Eq),
675 TokenKind::Float,
676 TokenKind::Punct(Punct::Semi),
677 TokenKind::Eof,
678 ]
679 );
680
681 let mut fixture = Fixture::new(Std::C23);
682 let (tokens, diagnostics) = fixture.run("int x = 42ux;");
683 assert_eq!(diagnostics, vec!["invalid suffix on integer constant".to_owned()]);
684 assert_eq!(tokens.int(tokens.tokens[3]).expect("a stand in").value, 0);
685 }
686
687 #[test]
688 fn a_run_of_literals_with_two_prefixes_is_refused_the_way_gcc_refuses_it() {
689 let mut fixture = Fixture::new(Std::C23);
690 let (tokens, diagnostics) = fixture.run(r#"u"a" L"b""#);
691 assert_eq!(
692 diagnostics,
693 vec!["unsupported non-standard concatenation of string literals".to_owned()]
694 );
695 assert!(tokens.string(tokens.tokens[0]).expect("a stand in").elements.is_empty());
696 }
697
698 #[test]
701 fn a_stray_byte_is_an_error_here_and_nowhere_earlier() {
702 let mut fixture = Fixture::new(Std::C23);
703 let (tokens, diagnostics) = fixture.run("a ` b");
704 assert_eq!(diagnostics, vec!["stray '`' in program".to_owned()]);
705 let kinds: Vec<_> = tokens.tokens.iter().map(|t| t.kind).collect();
706 assert_eq!(kinds, vec![TokenKind::Ident, TokenKind::Ident, TokenKind::Eof]);
707 }
708
709 #[test]
710 fn the_stream_always_ends_in_end_of_file() {
711 let mut fixture = Fixture::new(Std::C23);
712 let (tokens, _) = fixture.run("");
713 assert_eq!(tokens.tokens.len(), 1);
714 assert!(tokens.tokens[0].is_eof());
715 let (tokens, _) = convert(
717 &[],
718 &Convert {
719 keywords: &fixture.keywords,
720 interner: &fixture.interner,
721 target: &fixture.target,
722 std: fixture.std,
723 pedantic: false,
724 },
725 );
726 assert_eq!(tokens.tokens.len(), 1);
727 assert!(tokens.tokens[0].is_eof());
728 }
729
730 #[test]
731 fn a_token_says_what_it_is_without_the_caller_matching_on_the_kind() {
732 let mut fixture = Fixture::new(Std::C23);
733 let (tokens, _) = fixture.run("int x;");
734 assert_eq!(tokens.tokens[0].keyword(), Some(Keyword::Int));
735 assert_eq!(tokens.tokens[0].ident(), None);
736 assert!(tokens.tokens[1].ident().is_some());
737 assert_eq!(tokens.tokens[2].punct(), Some(Punct::Semi));
738 assert_eq!(tokens.tokens[2].keyword(), None);
739 }
740
741 #[test]
744 fn the_flags_come_through_from_the_preprocessing_token() {
745 let mut fixture = Fixture::new(Std::C23);
746 let (tokens, _) = fixture.run("a\n b");
747 assert!(tokens.tokens[0].flags.has(TokenFlags::START_OF_LINE));
748 assert!(tokens.tokens[1].flags.has(TokenFlags::START_OF_LINE));
749 assert!(tokens.tokens[1].flags.has(TokenFlags::LEADING_SPACE));
750 }
751}