Class: PartializerUtils

Inherits:
Object show all
Includes:
Partials
Defined in:
lib/ceedling/partials/partializer_utils.rb

Constant Summary

Constants included from Partials

Partials::ACCUMULATE, Partials::DEDUCT, Partials::MOCK_PRIVATE, Partials::MOCK_PUBLIC, Partials::PRIVATE, Partials::PUBLIC, Partials::TEST_PRIVATE, Partials::TEST_PUBLIC

Instance Method Summary collapse

Methods included from Partials

manufacture_function_declaration, manufacture_function_definition

Instance Method Details

#format_line_number_list(funcs) ⇒ Array<String>

Format per-function line-number results as a list of human-readable strings.

Produces one entry per function in the form "name(): <line_num>". Functions with no resolved line number are rendered with 'N/A'. The returned array is passed directly to @loginator.log_list for debug output.

Parameters:

  • funcs (Array<CFunctionDefinition>)

    Functions with name and line_num fields

Returns:



182
183
184
185
186
# File 'lib/ceedling/partials/partializer_utils.rb', line 182

def format_line_number_list(funcs)
  funcs.map do |func|
    "#{func.name}(): #{func.line_num.nil? ? 'N/A' : func.line_num.to_s()}"
  end
end

#locate_function_in_source(code_block:, filepath:) ⇒ Integer?

Locate a function's line number by searching the original C source file.

Used when the global fallback mode is active — i.e., preprocessed output is unavailable for all functions in the current context. Delegates directly to code_finder.find_in_c_file.

Parameters:

  • code_block (String)

    Function definition text to search for

  • filepath (String)

    Path to the C source file to search

Returns:

  • (Integer, nil)

    1-indexed source line number, or nil if not found



148
149
150
# File 'lib/ceedling/partials/partializer_utils.rb', line 148

def locate_function_in_source(code_block:, filepath:)
  @code_finder.find_in_c_file(filepath, code_block)
end

#locate_function_via_preprocessed(code_block:, filepath:, preprocessed_filepath:) ⇒ Integer?

Locate a function's line number using preprocessed output with C source fallback.

Two-strategy lookup for a single function:

1. Try the GCC-preprocessed directives-only file — exact match preserving line markers.
2. If that yields nil, fall back to the original C source file.

Returns line number or nil if neither approach succeeds.

Parameters:

  • code_block (String)

    Function definition text to search for

  • filepath (String)

    Path to the original C source file (fallback target)

  • preprocessed_filepath (String)

    Path to the preprocessed directives-only file

Returns:

  • (Integer, nil)

    1-indexed source line number, or nil if not found



164
165
166
167
168
169
170
171
172
# File 'lib/ceedling/partials/partializer_utils.rb', line 164

def locate_function_via_preprocessed(code_block:, filepath:, preprocessed_filepath:)
  line_num = @code_finder.find_in_preprpocessed_file(preprocessed_filepath, code_block)
  return line_num unless line_num.nil?

  msg = "Using fallback C function location search for #{filepath}"
  @loginator.log( msg, Verbosity::OBNOXIOUS, LogLabels::WARNING )

  return @code_finder.find_in_c_file(filepath, code_block)
end

#matches_visibility?(decorators, visibility) ⇒ Boolean

Check if function decorators match the desired visibility

Returns:

  • (Boolean)


24
25
26
27
28
29
30
31
32
33
# File 'lib/ceedling/partials/partializer_utils.rb', line 24

def matches_visibility?(decorators, visibility)
  case visibility
  when PUBLIC
    return !is_function_private?(decorators)
  when PRIVATE
    return is_function_private?(decorators)
  else
    PartializerRuntime.raise_on_option(visibility)
  end
end

#rename_c_identifier(text, old_name, new_name) ⇒ String

Rename a C identifier throughout a text block with token-bounded substitution.

Replaces all occurrences of old_name that are bounded by C identifier boundaries with new_name. Token boundaries use Ruby \b (word boundary between \w = [a-zA-Z0-9_] and \W), which exactly matches C identifier boundaries. For example, renaming count:

- Matches:     `count = 0`, `(count)`, `count==5`, `*count`, `count[0]`
- No match:    `count_down`, `up_count`, `recount`

Note: This method has no comment-awareness. Callers that need comment content preserved verbatim (e.g., no-op placeholders) should use replace_declaration_with_noop with an opaque placeholder and restore the original text after renaming.

Parameters:

  • text (String)

    Code text to process

  • old_name (String)

    Identifier to replace

  • new_name (String)

    Replacement identifier

Returns:

  • (String)

    Modified text with all token-bounded occurrences renamed



123
124
125
# File 'lib/ceedling/partials/partializer_utils.rb', line 123

def rename_c_identifier(text, old_name, new_name)
  text.gsub(/\b#{Regexp.escape(old_name)}\b/, new_name)
end

#replace_compound_declaration_with_noops(text, original_decl, placeholder, count) ⇒ String

Replace a C compound variable declaration with N no-op expressions and a single comment.

Generates count (void)0; expressions where the final one carries a comment containing placeholder. The result replaces the first occurrence of original_decl in text. The caller supplies count = number of variables in the compound statement and a single unique placeholder token. After all variable renames are complete, the caller restores original_decl by replacing the placeholder with the original text.

Block form of sub is used so that \ and & in the declaration are not interpreted as regex replacement backreferences.

Parameters:

  • text (String)

    Code text to modify (e.g., a function code_block or body)

  • original_decl (String)

    Declaration text to replace (should be stripped of surrounding whitespace)

  • placeholder (String)

    Opaque token to embed in the single trailing comment

  • count (Integer)

    Number of no-op expressions to insert (one per variable)

Returns:



100
101
102
103
104
# File 'lib/ceedling/partials/partializer_utils.rb', line 100

def replace_compound_declaration_with_noops(text, original_decl, placeholder, count)
  noops   = "(void)0; " * (count - 1)
  comment = "(void)0; /* `#{placeholder}` replaced with no-op plus variable renamed & promoted to module-scope */"
  text.sub(original_decl) { noops + comment }
end

#replace_declaration_with_noop(text, original_decl, placeholder) ⇒ String

Replace a C variable declaration in a text block with a no-op expression.

Substitutes the first occurrence of original_decl in text with:

(void)0; /* <placeholder> */

The caller supplies a unique placeholder token for the comment slot. This allows subsequent rename operations (which use simple token-bounded substitution with no comment awareness) to run without touching the comment. After all renames are complete, the caller restores original_decl by replacing the placeholder.

Block form of sub is used so that \ and & in the declaration are not interpreted as regex replacement backreferences.

Parameters:

  • text (String)

    Code text to modify (e.g., a function code_block or body)

  • original_decl (String)

    Declaration text to replace (should be stripped of surrounding whitespace)

  • placeholder (String)

    Opaque token to embed in the comment (caller replaces it afterwards)

Returns:

  • (String)

    Modified text with declaration replaced by a no-op expression



80
81
82
# File 'lib/ceedling/partials/partializer_utils.rb', line 80

def replace_declaration_with_noop(text, original_decl, placeholder)
  replace_compound_declaration_with_noops(text, original_decl, placeholder, 1)
end

#setupObject



18
19
20
21
# File 'lib/ceedling/partials/partializer_utils.rb', line 18

def setup()
  # Aliases
  @code_finder = @preprocessinator_code_finder
end

#stamp_source_filepaths(funcs, filepath) ⇒ Object

Stamp the originating source filepath onto each function in a collection.

Mutates each element of funcs in place by assigning filepath to its source_filepath field. Called before any line-number search so the field is always populated regardless of whether a line number is ultimately found.

Parameters:

  • funcs (Array<CFunctionDefinition>)

    Functions to annotate

  • filepath (String)

    Path to the originating C source file



135
136
137
# File 'lib/ceedling/partials/partializer_utils.rb', line 135

def stamp_source_filepaths(funcs, filepath)
  funcs.each { |func| func.source_filepath = filepath }
end

#transform_function(func, signature, output_type) ⇒ Object

Transform function to appropriate output format FunctionDefinition or FunctionDeclaration



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/ceedling/partials/partializer_utils.rb', line 36

def transform_function(func, signature, output_type)
  case output_type
  when :impl
    # Strip decorators from the front of code_block and count how many newlines were removed.
    # Adjust line_num upward so that emitted #line directives remain accurate.
    new_code_block, stripped_newlines = extract_code_block(func.code_block, func.decorators)
    Partials.manufacture_function_definition(
      name:            func.name,
      signature:       signature,
      source_filepath: func.source_filepath,
      line_num:        adjust_line_num(func.line_num, stripped_newlines),
      code_block:      new_code_block
    )
  when :interface
    # A signature sourced from a CFunctionDefinition never carries a trailing semicolon
    # (it's followed by a body, not one), but a signature sourced from a
    # CFunctionDeclaration always does (it's a bare prototype). Strip it here so
    # callers can render "#{signature};" uniformly regardless of which kind of
    # function this interface entry came from.
    Partials.manufacture_function_declaration(
      name: func.name,
      signature: signature.sub(/;\s*\z/, '')
    )
  else
    PartializerRuntime.raise_on_option(output_type)
  end
end