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.
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.