Organizing C
#!/usr/bin/env ruby | |
require 'tsort' | |
require 'awesome_print' | |
# Quick-and-dirty wrapper around cflow to sort functions in a file | |
func_refs = {} | |
func_lvls = {} | |
cflow_cmd = %w[cflow -i_ -l --format=posix | |
--level-indent=1 | |
--omit-symbol-names | |
--omit-arguments] | |
line_regexp = /\A[^{]+{\s*(?<level>\d+)} (?<name>[^:]+): (?<rest>.*)\Z/ | |
entry_regexp = /\A[^<]*<(?<file>\S+) (?<line_number>\d+)>\z/ | |
ref_regexp = /\A(?<ref>\d+)\z/ | |
ext_regexp = /\A<>\z/ | |
Function = Struct.new(:name, :file, :line_number) | |
Group = Struct.new(:level, :functions) | |
entry_groups = IO.popen(cflow_cmd + ARGV) do |process| | |
process.each_line.each_with_index.map do |line, entry_number| | |
raise 'unexpected' unless (line_match = line_regexp.match(line)) | |
entry_number += 1 # cflow uses 1-based indexing | |
name = line_match[:name] | |
level = Integer(line_match[:level]) | |
rest = line_match[:rest] | |
function = case rest | |
when ref_regexp | |
ref = Integer($~[:ref]) | |
func_refs[ref] | |
when entry_regexp | |
file = $~[:file] | |
line_number = Integer($~[:line_number]) | |
Function.new(name, file, line_number) | |
when ext_regexp | |
Function.new(name, nil, nil) | |
else | |
ap rest | |
raise 'unexpected' | |
end | |
unless function | |
raise 'unexpected' | |
end | |
func_refs[entry_number] = function | |
(func_lvls[function] ||= []) << level if function.line_number | |
end | |
end | |
puts func_lvls.keys.sort_by { |key| [func_lvls[key].max, key.line_number] }.map(&:name).join("\n") |
#!/usr/bin/env ruby | |
require 'tsort' | |
require 'awesome_print' | |
# Quick wrapper to print strongly connected subgraphs of functions in C files | |
func_refs = {} | |
cflow_cmd = %w[cflow -i_ -l --format=posix | |
--level-indent=1 | |
--omit-symbol-names | |
--omit-arguments] | |
line_regexp = /\A[^{]+{\s*(?<level>\d+)} (?<name>[^:]+): (?<rest>.*)\Z/ | |
entry_regexp = /\A[^<]*<(?<file>\S+) (?<line_number>\d+)>\z/ | |
ref_regexp = /\A(?<ref>\d+)\z/ | |
ext_regexp = /\A<>\z/ | |
Function = Struct.new(:name, :file, :line_number) | |
Group = Struct.new(:level, :functions) | |
entry_groups = IO.popen(cflow_cmd + ARGV) do |process| | |
process.each_line.each_with_index.map do |line, entry_number| | |
raise 'unexpected' unless (line_match = line_regexp.match(line)) | |
entry_number += 1 # cflow uses 1-based indexing | |
name = line_match[:name] | |
level = Integer(line_match[:level]) | |
rest = line_match[:rest] | |
function = case rest | |
when ref_regexp | |
ref = Integer($~[:ref]) | |
func_refs[ref] | |
when entry_regexp | |
file = $~[:file] | |
line_number = Integer($~[:line_number]) | |
Function.new(name, file, line_number) | |
when ext_regexp | |
Function.new(name, nil, nil) | |
else | |
ap rest | |
raise 'unexpected' | |
end | |
unless function | |
raise 'unexpected' | |
end | |
func_refs[entry_number] = function | |
[level, function] | |
end | |
end.chunk(&:first).map do |level, entries| | |
Group.new(level, entries.map(&:last)) | |
end | |
class TsortableHash < Hash | |
include TSort | |
alias tsort_each_node each_key | |
def tsort_each_child(node, &block) | |
puts "input: #{node.name}" | |
children = (self[node] || []).select(&:line_number).uniq | |
# puts "\tchildren: #{children.map(&:name).join(',')}" | |
children.each(&block) | |
end | |
end | |
dependency_hash = TsortableHash.new | |
prefix = [] | |
previous_level = -1 | |
entry_groups.map(&:to_h).each do |level:, functions:| | |
raise 'error' if (previous_level + 1) != prefix.length | |
if level < previous_level | |
difference = previous_level - level | |
prefix.pop(difference + 1) | |
end | |
parent = prefix.last | |
(dependency_hash[parent] ||= []).concat(functions) if parent | |
prefix.push(functions.last) | |
previous_level = level | |
end | |
ap dependency_hash.strongly_connected_components |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment