Class: CExtractorDeclarations

Inherits:
Object
  • Object
show all
Includes:
CExtractorConstants, CExtractorTypes
Defined in:
lib/ceedling/c_extractor/c_extractor_declarations.rb

Constant Summary

Constants included from CExtractorConstants

CExtractorConstants::C11_SPECIFIER_KEYWORDS, CExtractorConstants::DECORATOR_KEYWORDS, CExtractorConstants::DEFAULT_CHUNK_SIZE, CExtractorConstants::DEFAULT_MAX_FUNCTION_LENGTH, CExtractorConstants::DEFAULT_MAX_LINE_LENGTH, CExtractorConstants::MODIFIER_KEYWORDS, CExtractorConstants::MSVC_CALLING_CONVENTIONS, CExtractorConstants::PRIVATE_KEYWORDS, CExtractorConstants::TYPE_KEYWORDS, CExtractorConstants::TYPE_QUALIFIER_KEYWORDS

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#max_line_length=(value) ⇒ Object (writeonly)

Sets the attribute max_line_length

Parameters:

  • value

    the value to set the attribute max_line_length to.



19
20
21
# File 'lib/ceedling/c_extractor/c_extractor_declarations.rb', line 19

def max_line_length=(value)
  @max_line_length = value
end

Instance Method Details

#setupObject



21
22
23
24
25
# File 'lib/ceedling/c_extractor/c_extractor_declarations.rb', line 21

def setup()
  # Aliases
  @code_text       = @c_extractor_code_text
  @max_line_length = DEFAULT_MAX_LINE_LENGTH
end

#try_extract_variable(scanner) ⇒ Object

Attempts to extract a complete variable declaration from the scanner

Scans forward from the current scanner position looking for a complete C variable declaration terminated by a semicolon. Handles complex declaration syntax including:

- Simple variables: `int x;`
- Pointers: `char* ptr;`, `int** buffer;`
- Arrays: `int arr[10];`, `char matrix[3][4];`
- Initializers: `int x = 5;`, `int arr[] = {1, 2, 3};`
- String literals: `char* str = "hello";`
- Qualifiers: `const int MAX;`, `static volatile int flag;`
- Function pointers: `void (*callback)(int);`
- Complex nested structures with balanced parentheses, brackets, and braces
- Compound declarations: `int x, y;` expanded to one struct per variable

The extraction process:

1. Tracks nesting depth of (), [], and {} to handle complex declarations
2. Properly handles string literals (both " and ') including escape sequences
3. Skips comments (both // line comments and /* block comments */)
4. Stops at the first semicolon found at depth 0 (outside all nesting)
5. Validates the extracted text looks like a valid declaration
6. Expands compound declarations and parses each into a CVariableDeclaration struct

Parameters:

scanner: StringScanner positioned at potential start of variable declaration

Returns: Array of [success, declarations]

  • success: Boolean indicating if a valid declaration was found
  • declarations: Array of CVariableDeclaration structs (nil if not found)

Side effects:

On success: Advances scanner position past the semicolon
On failure: Resets scanner position to starting position

Safety:

Enforces max_line_length limit to prevent infinite loops on malformed input


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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/ceedling/c_extractor/c_extractor_declarations.rb', line 62

def try_extract_variable(scanner)
  start_pos = scanner.pos

  # Tracks paren, bracket, and brace depth simultaneously with inline string state machine and max_line_length guard — not suitable for collect_balanced()
  paren_depth = 0
  bracket_depth = 0
  brace_depth = 0
  in_string = false
  string_char = nil

  # Scan until we find a semicolon at depth 0.
  # If we reach the end of string scanner, we failed to find something.
  until scanner.eos?
    char = scanner.peek(1)

    # Safety check -- prevent infinite loops on malformed input
    if (scanner.pos - start_pos) > @max_line_length
      scanner.pos = start_pos
      return [false, nil]
    end

    # Handle string literals
    if in_string
      if char == '\\'
        scanner.getch
        scanner.getch unless scanner.eos?
        next
      elsif char == string_char
        scanner.getch
        in_string = false
        string_char = nil
        next
      else
        scanner.getch
        next
      end
    end

    case char
    when '"', "'"
      in_string = true
      string_char = char
      scanner.getch
    when '/'
      # Handle comments
      if scanner.peek(2) =~ %r{^(/[/*])}
        if scanner.peek(2) == '//'
          # Line comment -- skip to end of line
          scanner.scan_until(/\n/) || scanner.terminate
        elsif scanner.peek(2) == '/*'
          # Block comment -- skip to closing */
          scanner.pos += 2
          scanner.scan_until(%r{\*/})
        else
          scanner.getch
        end
      else
        scanner.getch
      end
    when '='
      # Track assignment for initializer detection
      scanner.getch
    when '('
      paren_depth += 1
      scanner.getch
    when ')'
      paren_depth -= 1
      scanner.getch
      # Unbalanced parentheses -- not a valid declaration
      if paren_depth < 0
        scanner.pos = start_pos
        return [false, nil]
      end
    when '['
      bracket_depth += 1
      scanner.getch
    when ']'
      bracket_depth -= 1
      scanner.getch
      # Unbalanced brackets -- not a valid declaration
      if bracket_depth < 0
        scanner.pos = start_pos
        return [false, nil]
      end
    when '{'
      # Braces after '=' are initializers, not code blocks
      brace_depth += 1
      scanner.getch
    when '}'
      brace_depth -= 1
      scanner.getch
      # Unbalanced braces -- not a valid declaration
      if brace_depth < 0
        scanner.pos = start_pos
        return [false, nil]
      end
    when ';'
      # Found semicolon - check if it's at depth 0
      if paren_depth == 0 && bracket_depth == 0 && brace_depth == 0
        # This is the end of a declaration
        scanner.getch  # Consume the semicolon

        # Extract the declaration
        declaration = scanner.string[start_pos...scanner.pos]

        # Verify this looks like a valid declaration
        # Must have at least a type and identifier
        # Can end with: word character, ], ), }, or " (for string initializers)
        if declaration =~ /\w+.*[\w\]\)\}"']\s*;$/
          return [true, expand_and_parse(declaration)]
        else
          scanner.pos = start_pos
          return [false, nil]
        end
      else
        # Semicolon inside parens, brackets, or braces -- keep scanning
        scanner.getch
      end
    else
      scanner.getch
    end
  end

  # Reached end without finding a complete declaration
  scanner.pos = start_pos
  [false, nil]
end