Class: CExtractorDeclarations
- 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
-
#max_line_length ⇒ Object
writeonly
Sets the attribute max_line_length.
Instance Method Summary collapse
- #setup ⇒ Object
-
#try_extract_variable(scanner) ⇒ Object
Attempts to extract a complete variable declaration from the scanner.
Instance Attribute Details
#max_line_length=(value) ⇒ Object (writeonly)
Sets the attribute max_line_length
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
#setup ⇒ Object
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, (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 |