Skip to content

Instantly share code, notes, and snippets.

@jasonmp85
Last active August 29, 2015 14:14
Show Gist options
  • Save jasonmp85/75d02b0b477ade819e73 to your computer and use it in GitHub Desktop.
Save jasonmp85/75d02b0b477ade819e73 to your computer and use it in GitHub Desktop.
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