Class: CPreprocessorConditionals
- Defined in:
- lib/ceedling/preprocess/c_preprocessor_conditionals.rb
Overview
Lightweight C Preprocessor Conditional Block Tracker
Tracks the active/inactive state of C conditional blocks
(#ifdef/#ifndef/#if/#elif/#else/#endif) against a list of
defined macro names. Intended for use in text-only (fallback) preprocessing
paths where a real C preprocessor is unavailable.
Feed one logical line at a time to process_directive. After each call,
active? returns whether the current position is inside an active block.
Nesting is fully tracked via a stack.
Evaluation rules:
#ifdef MACRO → active when MACRO is in defines list
#ifndef MACRO → active when MACRO is NOT in defines list
#if 0 → always inactive
#if 1 → always active
#if defined(MACRO) → active when MACRO in defines list
#if !defined(MACRO) → active when MACRO NOT in defines list
#elif defined(MACRO) → active when prior branch was inactive AND MACRO in defines
#else → active when all prior branches were inactive
#endif → pops the innermost stack frame
Complex #if expression → treated as active (conservative: include the block)
Expects lines pre-cleaned by code_lines / clean_encoding (comments
stripped, continuations joined). Does not raise on multi-byte characters.
Known limitation: A #ifdef appearing inside a multi-line C block comment
may be misidentified as a real directive if the caller uses raw (non-comment-
stripped) lines. This is an accepted limitation of lightweight text processing.
Instance Method Summary collapse
-
#active? ⇒ Boolean
True when the current position is inside an active conditional block (or outside all blocks, which is always active).
-
#initialize(defines) ⇒ CPreprocessorConditionals
constructor
A new instance of CPreprocessorConditionals.
-
#process_directive(line) ⇒ Object
Feed one logical, comment-stripped, continuation-joined line.
-
#reset ⇒ Object
Reset state for reuse (e.g. across multiple files).
Constructor Details
#initialize(defines) ⇒ CPreprocessorConditionals
Returns a new instance of CPreprocessorConditionals.
44 45 46 47 48 |
# File 'lib/ceedling/preprocess/c_preprocessor_conditionals.rb', line 44 def initialize(defines) # Normalize defines array: strip leading -D, strip =value suffix, keep name only @defined_macros = normalize_defines(defines) @stack = [] # Each frame: {active: bool, seen_true_branch: bool} end |
Instance Method Details
#active? ⇒ Boolean
True when the current position is inside an active conditional block (or outside all blocks, which is always active).
104 105 106 107 108 109 |
# File 'lib/ceedling/preprocess/c_preprocessor_conditionals.rb', line 104 def active? # If outside all blocks, active by default. # Otherwise, the innermost frame must be active. return true if @stack.empty? @stack.last[:active] end |
#process_directive(line) ⇒ Object
Feed one logical, comment-stripped, continuation-joined line. Updates the conditional block state.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/ceedling/preprocess/c_preprocessor_conditionals.rb', line 52 def process_directive(line) _line = line.clean_encoding.lstrip case _line when /^#\s*ifdef\s+(\w+)/ push( macro_defined?($1) ) when /^#\s*ifndef\s+(\w+)/ push( !macro_defined?($1) ) when /^#\s*if\s+0\b/ push( false ) when /^#\s*if\s+1\b/ push( true ) when /^#\s*if\s+!\s*defined\s*\(\s*(\w+)\s*\)/ push( !macro_defined?($1) ) when /^#\s*if\s+defined\s*\(\s*(\w+)\s*\)/ push( macro_defined?($1) ) when /^#\s*if\b/ # Complex or unrecognized #if expression — treat as active (conservative) push( true ) when /^#\s*elif\s+!\s*defined\s*\(\s*(\w+)\s*\)/ handle_elif( !macro_defined?($1) ) when /^#\s*elif\s+defined\s*\(\s*(\w+)\s*\)/ handle_elif( macro_defined?($1) ) when /^#\s*elif\s+0\b/ handle_elif( false ) when /^#\s*elif\s+1\b/ handle_elif( true ) when /^#\s*elif\b/ # Complex or unrecognized #elif expression — treat as active (conservative) handle_elif( true ) when /^#\s*else\b/ handle_else when /^#\s*endif\b/ @stack.pop unless @stack.empty? end end |
#reset ⇒ Object
Reset state for reuse (e.g. across multiple files)
112 113 114 |
# File 'lib/ceedling/preprocess/c_preprocessor_conditionals.rb', line 112 def reset @stack.clear end |