Class: CPreprocessorConditionals

Inherits:
Object
  • Object
show all
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

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

Returns:

  • (Boolean)


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

#resetObject

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