Skip to content

Instantly share code, notes, and snippets.

@NigelThorne
Created November 29, 2011 02:53
Show Gist options
  • Save NigelThorne/1403153 to your computer and use it in GitHub Desktop.
Save NigelThorne/1403153 to your computer and use it in GitHub Desktop.
Parse C C++ C# enum definition to make a ruby hash of the values and names
# Parse C C++ C# enum definition to make a ruby hash of the values and names
# By Nigel Thorne (nwt) www.nigelthorne.com
# https://gist.github.com/1403153
require 'rubygems'
require 'parslet'
include Parslet
class Array
def to_h
self.inject({}){|t,n| raise "already assigned #{n[0]} to #{t[n[0]]}, can't assign to #{n[1]}" if t[n[0]]; t[n[0]] = n[1];t }
end
end
class Hash
def inverse
self.inject({}){|t,n| raise "already assigned #{n[1]} to #{t[n[1]]}, can't assign to #{n[0]}" if t[n[1]]; t[n[1]] = n[0];t }
end
end
# Constructs a parser using a Parser Expression Grammar
class MiniP < Parslet::Parser
rule(:integer) { match('[0-9]').repeat(1).as(:int) >> space? }
rule(:comma) { str(',') >> space? }
rule(:eol) { str("\n") >> space? }
rule(:space) { match('\s').repeat(1) }
rule(:space?) { space.maybe }
rule(:operator) { match('[=]') >> space? }
rule(:label) { (match('[a-zA-Z_]') >> match('[a-zA-Z_0-9]').repeat).as(:identifier) }
rule(:assignment) { (label.as(:key) >> space? >> (operator >> integer.as(:value)).maybe).as(:pair) }
rule(:ignored) { comment_line | (str("enum").absent? >> any) }
rule(:comment_line) { space? >> str('//') >>(eol.absent? >> any).repeat >> eol >> space? }
rule(:comment_block){ comment_line.repeat(1)}
rule(:enum_value) { comment_block.maybe >> space? >> assignment }
rule(:enum_subsequent_value) { comma >> comment_block.maybe >> space? >> assignment }
rule(:enum_body) { enum_value >> enum_subsequent_value.repeat }
rule(:enumeration) { str("enum") >> space >> label.as(:name) >> space? >> str("{") >>
space? >> enum_body.as(:values) >> space? >> comma.maybe >>
str("}") >> str(";").maybe }
rule(:file) { ignored.repeat >> (enumeration >> ignored.repeat).repeat.as(:definitions) }
root :file
end
class ToEnglish < Parslet::Transform
rule(:int => simple(:int)) {int.to_i}
rule(:identifier => simple(:identifier)){identifier.to_s.gsub(/[A-Z][^A-Z]/){|x| " "+x}.strip}
rule(:key => simple(:key), :value => simple(:value)) { "#{value}\t#{key}" }
rule(:name => simple(:name), :values => subtree(:values)) { "#{name}\n#{values.join("\n")}" }
rule(:definitions => subtree(:definitions)) {definitions.join("\n\n")}
end
class ToHash < Parslet::Transform
rule(:int => simple(:int)) {int.to_i}
rule(:identifier => simple(:identifier)){identifier.to_s}
rule(:key => simple(:key), :value => simple(:value)) { |val|
val[:value] ||= (@last_value.nil? ? 0 : @last_value + 1)
@last_value = val[:value].to_i
[val[:key].to_s, val[:value].to_i]
}
rule(:key => simple(:key)) { |val|
val[:value] ||= (@last_value.nil? ? 0 : @last_value + 1)
@last_value = val[:value].to_i
[val[:key].to_s, val[:value].to_i]
}
rule(:pair => subtree(:pair)) {pair}
rule(:name => simple(:name), :values => subtree(:values)) { [name.to_s, values.to_h ] }
rule(:definitions => subtree(:definitions)) {definitions}
end
def parse_enums(filename)
begin
parser = MiniP.new
transf = ToHash.new
transf.apply(parser.parse(File.read(filename))).to_h
rescue Parslet::ParseFailed => error
puts error, parser.root.error_tree
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment