Skip to main content

Crate rucc_pp

Crate rucc_pp 

Source
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.
GnucVersion
The GCC release the compiler claims to be, as __GNUC__, __GNUC_MINOR__ and __GNUC_PATCHLEVEL__.
HideSet
An interned set of macro names.
HideSets
The interning table for hide sets.
LineDirective
A #line directive, kept for the source map to apply.
MacroDef
A #define.
MacroTable
Every macro currently defined.
Predef
Everything the predefined set is built from that is not the target.
Preprocessor
Translation phase 4 over one file.
PrintOptions
What -E was 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 Traces table, or TraceId::NONE for 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 -D or -U points at.
MILESTONE
The milestone in spec/17-milestones.md that fills this crate in.

Functions§

dump_macros
Every macro that is defined, as #define lines, sorted by name and newline terminated.
parse_define
Parses the tokens after #define into a definition.
print
Renders tokens the way -E prints them.