pp_priv.h (22228B)
1 /* pp_priv.h — shared types, helpers, and cross-module forward declarations 2 * for the preprocessor split (pp.c / pp_expand.c / pp_directive.c). 3 * NOT part of the public API; included only within lang/cpp/pp/. */ 4 5 #ifndef KIT_PP_PRIV_H 6 #define KIT_PP_PRIV_H 7 8 #include <kit/support/symtab.h> 9 #include <stdlib.h> 10 #include <string.h> 11 12 #include "cpp_support.h" 13 #include "pp/pp.h" 14 15 /* ============================================================ 16 * Internal token kinds 17 * ============================================================ */ 18 19 /* Outside the range used by the lexer (TOK_KW_LAST = 0x1000). */ 20 #define TOK_PP_PARAM ((u16)0x1100) 21 #define TOK_PP_PLACEMARKER ((u16)0x1101) /* empty-arg substitution marker */ 22 23 /* ============================================================ 24 * Types 25 * ============================================================ */ 26 27 typedef struct Macro { 28 Sym name; 29 LocRef def_loc; 30 u8 is_func; 31 u8 is_variadic; 32 /* Pure cache of the body (NOT part of macro identity): 1 iff the body 33 * contains at least one TOK_PP_PASTE (`##`). Lets a no-`##` body be 34 * replayed by pointer without the per-invocation copy + paste pass. */ 35 u8 has_paste; 36 u8 pad[1]; 37 /* cpplib-style macro-disabled count (replaces the Prosser hideset): the 38 * number of this macro's replacement-list frames currently live on the 39 * source stack. Incremented when a replacement frame for this macro is 40 * pushed, decremented when it pops (see push_buf_uniform/replay + src_pop). 41 * While > 0 the macro is unavailable for expansion, so a recursive 42 * occurrence in its own (or a nested) replacement is returned with the 43 * permanent TF_NO_EXPAND bit set instead of re-expanding. Auto-zeros: do_define 44 * mints Macro via arena_znew. */ 45 u16 disabled_depth; 46 u32 n_params; 47 Sym* params; /* parameter names */ 48 Tok* body; /* body tokens; TOK_PP_PARAM kind + aux=param idx */ 49 u32 body_len; 50 } Macro; 51 52 /* MacroTab = Sym -> Macro* (NULL = not a macro). Sym-indexed dense table: 53 * the macro binding is read with one in-range load on every identifier in 54 * the expander's hot loop, replacing a per-identifier hashmap probe. */ 55 KIT_SYMTAB_DEFINE(MacroTab, Macro*); 56 57 typedef enum { SRC_LEX = 1, SRC_BUF = 2 } SrcKind; 58 59 /* Multiple-include-guard detection state for a SRC_LEX file (the classic 60 * "controlling macro" optimization). A file whose entire body is wrapped in 61 * `#ifndef GUARD ... #endif` (with nothing but whitespace/comments outside) 62 * can be skipped on a later #include while GUARD is defined — it would emit no 63 * tokens anyway. Detected live as the file is first lexed (no second scan): 64 * START -> IN on the opening `#ifndef GUARD`; IN -> AFTER on the matching 65 * `#endif`; any code before the opening directive or after the controlling 66 * `#endif` -> FAILED. Committed to the IncCache at src_pop (see pp.c). */ 67 typedef enum GuardState { 68 GUARD_START = 0, /* only whitespace/newlines seen so far */ 69 GUARD_IN = 1, /* inside the controlling #ifndef ... #endif */ 70 GUARD_AFTER = 2, /* controlling #endif closed; only ws may follow */ 71 GUARD_FAILED = 3 /* not a whole-file guard */ 72 } GuardState; 73 74 typedef struct TokSrc { 75 u8 kind; 76 /* When set on a SRC_BUF: src_next_raw returns TOK_EOF when this is 77 * the top source and it's exhausted, instead of popping. The caller 78 * (e.g. argument pre-expansion) explicitly pops the scope when done. 79 * This bounds expansion to a single argument's token stream. */ 80 u8 scope_top; 81 /* Read-time loc/flags override for a pointer-replayed macro body (SRC_BUF). 82 * When has_loc_override, every token read from this buffer gets 83 * out->loc = loc_override, and the FIRST token (s->i == 0 at read) also has 84 * its BOL/SPACE bits replaced by first_flags_or. This reproduces the 85 * mutations expand_object_macro used to bake into a fresh body copy, so the 86 * immutable definition-time m->body can be replayed in place. 0 = no 87 * override (every push_buf/push_buf_uniform memsets the whole TokSrc). */ 88 u8 has_loc_override; 89 u8 pad[1]; 90 u16 first_flags_or; 91 /* SRC_LEX */ 92 Lexer* lex; 93 /* SRC_BUF */ 94 Tok* toks; 95 /* cpplib macro-disabled frame owner. Non-NULL only on a macro 96 * replacement-list frame (object-like replay, function-like substituted body, 97 * paste result); pushing such a frame increments owner->disabled_depth and 98 * src_pop decrements it, so the macro stays unavailable for the whole rescan 99 * of its replacement (and any nested replacement above it). NULL on every 100 * non-macro buffer (pushed-back peek tokens, #pragma/#embed payloads, 101 * arg-prescan scopes); every push_buf* memsets the whole TokSrc, so NULL is 102 * the default. */ 103 Macro* disabled_owner; 104 /* Per-token loc applied when has_loc_override (see scope_top block). */ 105 LocRef loc_override; 106 u32 i; 107 u32 n; 108 /* #line state is recorded positionally in the source's SrcInfo overlay 109 * segments (see SrcInfo / pp_materialize_loc), not on the frame: a lean 110 * token carries only a byte offset, and line/file are resolved on demand. */ 111 /* SRC_LEX only: the index into pp->inc_dirs from which a `#include_next` 112 * (or `__has_include_next`) appearing in this file begins its search — 113 * i.e. one past the search dir this file was itself found in. 0 for the 114 * top-level translation unit and for files found via the includer-relative 115 * ("...") step or an absolute path, so an `#include_next` there scans the 116 * whole search path (matching GCC). */ 117 u32 inc_next_start; 118 /* Multiple-include-guard detection (SRC_LEX only). guard_if_base is the 119 * if-stack depth at file entry, used to recognize the controlling #endif; 120 * path_key is the IncCache key (interned resolved path, 0 for the top-level 121 * TU and other non-cached sources) used to commit the memo at src_pop. */ 122 u8 guard_state; /* GuardState */ 123 u8 once; /* #pragma once seen in this file */ 124 Sym guard_macro; 125 u32 guard_if_base; 126 Sym path_key; 127 } TokSrc; 128 129 typedef enum IfState { 130 IF_INCLUDE = 1, /* group active, emit code */ 131 IF_SEEK_TRUE = 2, /* skip, looking for the first true elif/else */ 132 IF_DONE = 3, /* skip, already had a true branch */ 133 } IfState; 134 135 typedef struct IfFrame { 136 u8 state; 137 u8 has_else; 138 u8 pad[2]; 139 LocRef loc; 140 } IfFrame; 141 142 /* ============================================================ 143 * Source-info registry (lazy loc + retained text) 144 * ============================================================ */ 145 146 /* A #line overlay segment: from byte offset `off` onward (until the next 147 * segment), the reported line number is the physical line plus `delta`, and 148 * __FILE__ reports `file` (0 = the source's registry name). Recorded by do_line 149 * in offset order; consulted by pp_materialize_loc / pp_materialize_file. */ 150 typedef struct LineSeg { 151 u32 off; 152 i32 delta; 153 Sym file; 154 } LineSeg; 155 156 /* Per-source retained metadata, keyed by file_id. The folded logical buffer and 157 * splice table are retained until pp_free so that (a) TEXT_SRC spellings stay 158 * resolvable after the lexer is popped (macro bodies, parser lookahead/replay), 159 * and (b) line/col can be reconstructed lazily from a byte offset. The line 160 * index is built on first materialization by scanning the buffer for '\n' and 161 * merging the splice fold points. */ 162 typedef struct SrcInfo { 163 const char* buf; /* folded logical buffer (borrowed/arena/owned) */ 164 u32 len; 165 u8 owns_buf; /* PP frees buf at pp_free (a folded heap copy) */ 166 u8 line_built; /* line_off has been computed */ 167 u32 shebang_off; 168 u32* splices; /* fold offsets; PP frees at pp_free */ 169 u32 nsplices; 170 u32* line_off; /* line_off[k] = start offset of physical line k+1 */ 171 u32 nlines; 172 u32 line_cursor; /* last line index returned — loc materialization is 173 * near-monotonic, so this makes the common lookup O(1) */ 174 LineSeg* segs; /* #line overlay, offset-ordered */ 175 u32 nsegs; 176 u32 segs_cap; 177 } SrcInfo; 178 179 /* Sym-keyed hashmaps below (include caches). See core/hashmap.h. */ 180 #include <kit/support/hashmap.h> 181 182 /* IncCache = Sym(resolved-path) -> cached header bytes. The same guarded 183 * header included many times across a TU is read from disk once; later 184 * inclusions reuse the arena-resident bytes (lex_open only borrows 185 * them and the pp arena keeps them alive until pp_free), saving the 186 * open+fstat+read+close + malloc/memcpy/free per repeat. Keyed strictly 187 * on the resolved-path STRING so distinct spellings/symlinks never 188 * over-merge; the cached bytes are byte-identical to a fresh read. */ 189 typedef struct IncEntry { 190 const u8* data; 191 size_t size; 192 /* Multiple-include optimization (committed at src_pop, consulted at the next 193 * #include of the same resolved path). guard = the file's controlling 194 * #ifndef macro (0 = none); once = the file carried #pragma once. */ 195 Sym guard; 196 u8 once; 197 } IncEntry; 198 static inline u32 inc_hash_(Sym s) { return kit_hash_u32((u32)s); } 199 KIT_HASHMAP_DEFINE(IncCache, Sym, IncEntry, inc_hash_); 200 201 /* IncResolveMap = Sym(spelling key) -> resolved (path Sym, system flag). 202 * Memoizes the winner of the -I/-isystem dir search so a repeated request 203 * for the same header spelling skips the ENOENT storm of failed opens on 204 * the earlier dirs. The key folds the system flag (and, for quoted form, 205 * the includer directory) into the spelling so the resolved result is the 206 * exact one a fresh search would produce — byte-identical resolved path 207 * and resolved_system flag feeding source_add_include / DWARF / -M. */ 208 typedef struct IncResolved { 209 Sym path; /* interned resolved path string */ 210 u8 system; /* the resolved_system flag (winning -isystem) */ 211 u32 next_start; /* inc_dirs index a #include_next from the */ 212 /* resolved file starts at (see TokSrc) */ 213 } IncResolved; 214 static inline u32 incres_hash_(Sym s) { return kit_hash_u32((u32)s); } 215 KIT_HASHMAP_DEFINE(IncResolveMap, Sym, IncResolved, incres_hash_); 216 217 /* ============================================================ 218 * Pp struct (definition shared across all three TUs) 219 * ============================================================ */ 220 221 struct Pp { 222 Compiler* c; 223 Pool* pool; 224 225 /* Source stack — top of stack is sources[nsources-1]. */ 226 TokSrc* sources; 227 u32 nsources; 228 u32 sources_cap; 229 230 /* Per-file retained metadata, indexed by compiler file_id (dense from 1). 231 * Holds the retained source buffer + splice table + lazy line index + #line 232 * overlay so loc/text materialize after the lexer is popped. */ 233 SrcInfo* srcinfo; 234 u32 srcinfo_cap; 235 236 /* Macro table (Sym-indexed dense; value = Macro*, NULL = not a macro). */ 237 MacroTab macros; 238 239 /* Header-content cache (key = interned resolved-path Sym, value = 240 * arena-resident bytes). Collapses N re-includes of the same header to 241 * one read + N-1 cache hits. Lazily initialized on first #include; its 242 * slot table is backed by inc_cache_heap (an arena adapter over 243 * pp->arena), so nothing needs freeing at pp_free — the arena is. */ 244 IncCache inc_cache; 245 KitHeap inc_cache_heap; 246 u8 inc_cache_ready; 247 248 /* Include-resolution memo (spelling -> winning resolved path + system 249 * flag). Shares inc_cache_heap (gated by inc_cache_ready). */ 250 IncResolveMap inc_resolve; 251 252 /* Conditional inclusion stack (#if / #ifdef / #ifndef → #endif). */ 253 IfFrame* ifstk; 254 u32 ifstk_n; 255 u32 ifstk_cap; 256 257 /* Include directories (stage 9). */ 258 struct { 259 const char* path; 260 u8 system; 261 }* inc_dirs; 262 u32 ninc_dirs; 263 u32 inc_dirs_cap; 264 265 /* Current #pragma pack maximum field alignment. 0 means natural. */ 266 u32 pack_align; 267 u32 pack_stack[16]; 268 u32 pack_stack_n; 269 270 /* Permanent arena: macro bodies, params, #include file data. 271 * Lives until pp_free. */ 272 KitArena* arena; 273 274 /* Transient expansion scratch: the macro-substitution token buffers 275 * (tv_grow), arg slices, paste/stringize buffers -- everything that 276 * backs a SRC_BUF or is consumed within one expansion. Reset to its 277 * high-water mark by pp_next_raw whenever the source stack drains back to a 278 * lexer (no SRC_BUF live), so memory is O(expansion depth), not 279 * O(expansions). Nothing the caller keeps points in here: tokens are 280 * returned by value and spellings are interned in the pool. */ 281 KitArena* xarena; 282 283 /* Cached interned identifiers used for directive recognition. */ 284 Sym sym_define; 285 Sym sym_undef; 286 Sym sym_include; 287 Sym sym_include_next; /* GCC/clang #include_next extension */ 288 Sym sym_assembler; /* __ASSEMBLER__ — asm-with-cpp `#` leniency */ 289 Sym sym_has_include; /* __has_include() #if operator */ 290 Sym sym_has_include_next; /* __has_include_next() #if operator */ 291 Sym sym_if; 292 Sym sym_ifdef; 293 Sym sym_ifndef; 294 Sym sym_elif; 295 Sym sym_else; 296 Sym sym_endif; 297 Sym sym_line; 298 Sym sym_pragma; 299 Sym sym_once; /* "once" — #pragma once (multiple-include opt) */ 300 Sym sym_error; 301 Sym sym_warning; 302 Sym sym_embed; 303 Sym sym_defined; 304 Sym sym_va_args; 305 Sym sym_line__; /* __LINE__ */ 306 Sym sym_file__; /* __FILE__ */ 307 Sym sym_date__; /* __DATE__ */ 308 Sym sym_time__; /* __TIME__ */ 309 Sym sym_stdc__; /* __STDC__ */ 310 Sym sym_stdc_hosted__; 311 Sym sym_stdc_version__; 312 Sym sym__pragma; /* _Pragma operator */ 313 Sym sym_pragma_kw; /* "pragma" — for synthesized #pragma */ 314 315 /* Pre-formatted "Mmm dd yyyy" / "hh:mm:ss" string spellings for 316 * __DATE__ and __TIME__, derived from SOURCE_DATE_EPOCH (or 317 * time(NULL) if unset). */ 318 Sym val_date_str; 319 Sym val_time_str; 320 321 /* Token-paste (`##`) scratch. paste_name_sym is the interned "<paste>" name, 322 * cached so each paste open doesn't re-intern the literal. paste_lex is a 323 * single reused memory lexer (re-pointed via lex_reset_mem per paste) so the 324 * thousands of tiny paste buffers don't each alloc/free a 288B Lexer. Both 325 * are lazily initialized on the first paste and torn down in pp_free. */ 326 Sym paste_name_sym; 327 Lexer* paste_lex; 328 329 /* Defined-operator handling during #if expansion. 330 * 331 * The first prepass in eval_if_expr replaces `defined X` / `defined 332 * (X)` literally found in the directive line, but `defined()` can 333 * also come from macro bodies (mingw's intrin-impl.h uses 334 * `defined(__INTRINSIC_DEFINED_ ## name)` inside a #define). When 335 * the expander processes such a body, the identifier inside 336 * `defined(...)` must NOT be macro-expanded — otherwise an empty 337 * macro X would turn `defined(X)` into `defined()` and the 338 * post-expansion prepass would reject it. 339 * 340 * This pair of fields tracks the state across `pp_next_raw` calls 341 * within `expand_for_if`: 342 * in_if_expansion: 1 inside an #if's expand_arg_to_eof call 343 * defined_skip: 0 normally; 2 after emitting `defined` (a bare 344 * `defined IDENT` marks the IDENT and resets, while 345 * a `(` advances to 3); 3 after `defined (` (waiting 346 * for the operand IDENT, advancing to 4); 4 after 347 * `defined ( IDENT` (waiting for the closing `)`). 348 * The expander uses these to mark the operand IDENT TF_NO_EXPAND 349 * before the macro-expansion check at the head of pp_next_raw. */ 350 u8 in_if_expansion; 351 u8 defined_skip; 352 /* Set while read_directive_line is pulling a directive's own tokens from the 353 * lexer, so the multiple-include-guard content check in src_next_raw_into 354 * doesn't count directive-internal tokens (e.g. the `ifndef` / macro name of 355 * the guard itself) as file content. */ 356 u8 reading_directive; 357 /* cc parser-feed mode: set when the primary source is pushed with 358 * SRC_PARSER_FEED, so every SRC_LEX lexer (primary + each #include) is opened 359 * with newline suppression (the parser drops non-directive newlines). The 360 * -E / cpp path leaves this clear so newlines surface for text 361 * reconstruction. */ 362 u8 parser_feed; 363 }; 364 365 /* ============================================================ 366 * Allocation helpers (defined in pp.c, used everywhere) 367 * ============================================================ */ 368 369 static inline Heap* pp_heap(Pp* pp) { 370 return kit_compiler_context(pp->c)->heap; 371 } 372 373 static inline void* pp_xrealloc(Pp* pp, void* p, size_t old_n, size_t new_n, 374 size_t align) { 375 Heap* h = pp_heap(pp); 376 void* q = h->realloc(h, p, old_n, new_n, align); 377 if (!q) compiler_panic(pp->c, (SrcLoc){0, 0, 0}, "pp: out of memory"); 378 return q; 379 } 380 381 static inline void pp_xfree(Pp* pp, void* p, size_t n) { 382 if (p) pp_heap(pp)->free(pp_heap(pp), p, n); 383 } 384 385 /* ============================================================ 386 * Lean-token constructors (synthetic tokens) 387 * ============================================================ */ 388 389 /* text_none_ref / text_sym_ref / locref_none live in lex.h (shared with the 390 * parser). text_intern_ref additionally interns, so it needs the Pp pool. */ 391 static inline TextRef text_intern_ref(Pp* pp, KitSlice sl) { 392 return text_sym_ref(kit_sym_intern(pp->pool->c, sl)); 393 } 394 395 /* ============================================================ 396 * Token-vector helpers 397 * ============================================================ */ 398 399 typedef struct TokVec { 400 Tok* data; 401 u32 n; 402 u32 cap; 403 } TokVec; 404 405 static inline void tv_grow(Pp* pp, TokVec* v, u32 want) { 406 u32 nc; 407 if (v->cap >= want) return; 408 nc = v->cap ? v->cap * 2 : 8; 409 while (nc < want) nc *= 2; 410 { 411 Tok* nb = arena_array(pp->xarena, Tok, nc); 412 if (v->n) memcpy(nb, v->data, sizeof(Tok) * v->n); 413 v->data = nb; 414 v->cap = nc; 415 } 416 } 417 418 static inline void tv_push(Pp* pp, TokVec* v, Tok t) { 419 tv_grow(pp, v, v->n + 1); 420 v->data[v->n++] = t; 421 } 422 423 /* Growable char buffer (arena-backed). */ 424 typedef struct CharBuf { 425 char* data; 426 u32 len; 427 u32 cap; 428 } CharBuf; 429 430 static inline void cb_append(Pp* pp, CharBuf* b, const char* s, u32 n) { 431 if (b->len + n > b->cap) { 432 u32 nc = b->cap ? b->cap * 2 : 64; 433 while (nc < b->len + n) nc *= 2; 434 { 435 char* nb = (char*)arena_alloc(pp->xarena, nc, 1); 436 if (b->len) memcpy(nb, b->data, b->len); 437 b->data = nb; 438 b->cap = nc; 439 } 440 } 441 if (n) memcpy(b->data + b->len, s, n); 442 b->len += n; 443 } 444 445 static inline void cb_putc(Pp* pp, CharBuf* b, char c) { 446 cb_append(pp, b, &c, 1); 447 } 448 449 /* ============================================================ 450 * Cross-module forward declarations 451 * ============================================================ */ 452 453 /* --- pp.c (source stack) → pp_expand.c, pp_directive.c --- */ 454 /* Out-pointer form (hot path); src_next_raw is the by-value shim over it for 455 * the cold/general callers. */ 456 void src_next_raw_into(Pp* pp, Tok* out, u8* src_kind_out); 457 Tok src_next_raw(Pp* pp, u8* src_kind_out); 458 void src_push(Pp* pp, TokSrc s); 459 void src_pop(Pp* pp); 460 void push_buf(Pp* pp, Tok* toks, u32 n); 461 /* Push a macro replacement-list buffer. `owner` is the macro whose replacement 462 * this is (NULL for a non-macro payload): a non-NULL owner increments 463 * owner->disabled_depth at push and src_pop decrements it, so the macro stays 464 * unavailable for the whole rescan of its replacement (cpplib disabled-frame 465 * model). */ 466 void push_buf_uniform(Pp* pp, Tok* toks, u32 n, Macro* owner); 467 /* Push an immutable, read-only token buffer (e.g. a no-`##` macro body) for 468 * pointer-replay. The buffer is never written through s->toks; per-read 469 * loc/flags overrides reproduce the mutations the old body-copy path baked in. 470 * `owner` follows the same disabled-frame rule as push_buf_uniform. */ 471 void push_buf_replay(Pp* pp, const Tok* toks, u32 n, Macro* owner, 472 LocRef loc_override, u16 first_flags_or); 473 474 /* pp_next_raw (public, in pp.h) is the mutual-recursion entry: 475 * expand_arg_to_eof and the -E loop call it; it drives directives and 476 * expansion. pp_next_into is the internal newline-dropping pull (skip_nl=1) 477 * used by pp_next_parse. */ 478 void pp_next_into(Pp* pp, Tok* out); 479 480 /* --- SrcInfo registry (pp.c) → pp_directive.c --- */ 481 /* Get (growing as needed) the SrcInfo for a file_id. */ 482 SrcInfo* pp_srcinfo(Pp* pp, u32 file_id); 483 /* Register a freshly-opened, about-to-be-pushed lexer: adopt its folded buffer 484 * + splice table + shebang offset into the SrcInfo registry (retained to 485 * pp_free) so loc/text stay materializable after the lexer is popped. */ 486 void pp_register_srcinfo(Pp* pp, Lexer* lex); 487 /* Record a #line overlay segment (effect offset, line delta, file name sym). */ 488 void pp_add_line_seg(Pp* pp, u32 file_id, u32 off, i32 delta, Sym file); 489 /* Physical line at a location, ignoring #line overlay (used by do_line). */ 490 u32 pp_phys_line(Pp* pp, LocRef loc); 491 /* __FILE__ name sym at a location (overlay file if set, else registry name). */ 492 Sym pp_materialize_file(Pp* pp, LocRef loc); 493 494 /* --- pp_expand.c → pp.c, pp_directive.c --- */ 495 void expand_arg_to_eof(Pp* pp, Tok* in, u32 nin, TokVec* out); 496 497 /* Macro binding lookup/define/undef. Inlined Sym-indexed loads (no hashmap 498 * probe): mt_get runs on every identifier the expander sees. NULL/absent = 499 * not a macro; #undef stores NULL. */ 500 static inline Macro* mt_get(Pp* pp, Sym name) { 501 return MacroTab_get(&pp->macros, name); 502 } 503 static inline void mt_put(Pp* pp, Sym name, Macro* m) { 504 MacroTab_set(&pp->macros, name, m); 505 } 506 static inline void mt_del(Pp* pp, Sym name) { 507 MacroTab_set(&pp->macros, name, NULL); 508 } 509 510 /* --- pp_directive.c → pp_expand.c --- */ 511 i64 eval_if_expr(Pp* pp, const Tok* line, u32 n, LocRef loc); 512 void process_directive(Pp* pp, LocRef hash_loc); 513 514 /* --- pp_directive.c internal helpers called from pp_expand.c --- */ 515 void emit_pragma_line(Pp* pp, const Tok* line, u32 n, LocRef loc); 516 int peek_for_invoke_paren(Pp* pp, int* ws_has_space_out); 517 int try_expand_pragma_op(Pp* pp, const Tok* invoke); 518 519 /* --- pp_directive.c: read_directive_line (used by pp.c/pp_define) --- */ 520 void read_directive_line(Pp* pp, Tok** out_toks, u32* out_n); 521 522 /* --- pp_expand.c: do_define / do_undef (used by pp.c/pp_define) --- */ 523 void do_define(Pp* pp, const Tok* line, u32 n); 524 void do_undef(Pp* pp, const Tok* line, u32 n); 525 526 /* --- pp_directive.c helpers needed by pp_expand.c (_Pragma) --- */ 527 TokSrc* current_lex_src(Pp* pp); 528 529 #endif /* KIT_PP_PRIV_H */