Expand description
Macro expansion, conditionals, include resolution, and the header cache.
Design: spec/05-preprocessor.md. Layer rank 5, see spec/18-package-layout.md.
§Status
Macro expansion is implemented: object-like and function-like macros, # and ##,
variadics in both the standard and the GNU spelling, __VA_OPT__, and the GNU comma
swallowing extension, all on hide sets rather than on a depth counter.
Every token that comes out of a macro carries the chain of macros it came out of, so an error inside a macro three headers deep prints the way to it rather than only the two ends of it. The chain is interned, so a hundred token replacement list costs one node.
Directives are implemented: #define, #undef, the whole conditional family with the
#if expression evaluator, #error, #warning, #line, #pragma, the _Pragma
operator, and #include and #include_next against a search path that follows GCC’s
order. #embed produces its bytes as tokens, and the fast path that avoids making
them at all waits on the parser.
A header is read once. #pragma once and the multiple include optimization, which spots
the ordinary #ifndef wrapper and skips the file rather than reading it and throwing the
result away, both do that.
The __has_* family is implemented. __has_include and __has_include_next ask the
search path the same question the directive on the same line would ask it, and the rest
answer out of the matrix in rucc-gnu, which means they answer no for almost everything
until the parser lands. That is the point of them. They answer in ordinary text as well as
in a #if, because both GCC and clang make them builtin macros rather than something only
the conditional parser knows about. The exception is the three whose operand is a header
name, __has_include, __has_include_next and __has_embed, which both compilers refuse
outside a directive and so does this one.
The predefined macro set is generated from the target description rather than hardcoded,
and arrives as two synthetic files, <built-in> and <command-line>, so that a
diagnostic about one of them says where it came from. __DATE__ and __TIME__ are in it
because they are fixed for a translation unit. The ones that are not fixed, __FILE__,
__FILE_NAME__, __BASE_FILE__, __LINE__, __INCLUDE_LEVEL__ and __COUNTER__, are
answered by the expander out of the source map at the place they are used.
print writes the token stream back out the way -E does, with GCC’s line markers, GCC’s
blank line padding, the indentation the source had, and a space wherever two tokens would
otherwise read back as one. -P turns the markers and the padding off.
use rucc_base::Interner;
use rucc_diag::SourceMap;
use rucc_lex::PpTokenKind;
use rucc_pp::{Context, Preprocessor};
use rucc_session::{MemoryFileSystem, SearchPath};
let mut fs = MemoryFileSystem::new();
fs.insert("/square.h", b"#define SQUARE(x) ((x) * (x))\n".to_vec());
let mut interner = Interner::new();
let mut sources = SourceMap::new();
let main = b"#include \"square.h\"\n#if SQUARE(2) == 4\nSQUARE(3)\n#endif\n";
let file = sources.add("/main.c", main.to_vec())?;
let search = SearchPath::new();
let mut cx = Context::new(&mut interner, &mut sources, &fs, &search);
let mut pp = Preprocessor::new();
let out = pp.run(file, &mut cx);
assert!(pp.diagnostics().is_empty());
let spelled: Vec<&str> = out
.iter()
.map(|t| match t.kind {
PpTokenKind::Punct(p) => p.as_str(),
_ => interner.resolve(t.value.unwrap()),
})
.collect();
assert_eq!(spelled.concat(), "((3)*(3))");Every crate in the workspace is published, and publishing implies a promise. This one is
tier 3: its Rust API is explicitly unstable and will change without a major version bump.
Depend on the rucc binary’s behaviour, not on this.
Structs§
- Context
- Everything phase 4 needs from outside itself.
- Expander
- Macro expansion state that outlives a single expansion.
- Gnuc
Version - The GCC release the compiler claims to be, as
__GNUC__,__GNUC_MINOR__and__GNUC_PATCHLEVEL__. - HideSet
- An interned set of macro names.
- Hide
Sets - The interning table for hide sets.
- Line
Directive - A
#linedirective, kept for the source map to apply. - Macro
Def - A
#define. - Macro
Table - Every macro currently defined.
- Predef
- Everything the predefined set is built from that is not the target.
- Preprocessor
- Translation phase 4 over one file.
- Print
Options - What
-Ewas asked for. - Step
- One macro traversed on the way from the user’s text to a token.
- Timestamp
- The translation date, as
__DATE__and__TIME__spell it. - Tok
- A token in flight through macro expansion.
- TraceId
- A pointer into a
Tracestable, orTraceId::NONEfor a token the user wrote. - Traces
- The interning table for expansion traces.
Enums§
- Builtin
- A predefined macro whose value is a question rather than a replacement list.
Constants§
- BUILT_
IN - The name a diagnostic about the generated set points at.
- COMMAND_
LINE - The name a diagnostic about
-Dor-Upoints at. - MILESTONE
- The milestone in
spec/17-milestones.mdthat fills this crate in.
Functions§
- dump_
macros - Every macro that is defined, as
#definelines, sorted by name and newline terminated. - parse_
define - Parses the tokens after
#defineinto a definition. - Renders
tokensthe way-Eprints them.