class ApiDiff::KotlinBCVParser
Biggest Drawback: Does not support optionals :-/
Public Instance Methods
parse(content)
click to toggle source
# File lib/api_diff/kotlin_bcv_parser.rb, line 4 def parse(content) Property.readonly_keyword = "val" unless @options[:normalize] sections = content.scan(/^.+?{$.*?^}$/m) sections.each do |section| section.strip! first_line = section.split("\n")[0] if first_line.include?(" : java/lang/Enum") parse_enum(section) elsif first_line.include?("interface class") parse_interface(section) elsif first_line.match?(/public.+class/) parse_class(section) end end normalize!(api) if @options[:normalize] end
Private Instance Methods
extract_properties(type)
click to toggle source
# File lib/api_diff/kotlin_bcv_parser.rb, line 87 def extract_properties(type) getters = type.functions.select { |f| f.signature.match(/fun get[A-Z](\w+)? \(\)/) } getters.each do |getter| setter_name = getter.name.gsub(/^get/, "set") setter = type.functions.find { |f| f.signature.match(/fun #{setter_name} \(#{getter.return_type}\)/) } type.functions.delete getter type.functions.delete setter if setter name = getter.name.gsub(/^get/, "") if name == name.upcase # complete uppercase -> complete lowercase name.downcase! else name[0] = name[0].downcase end type.properties << Property.new( name: name, type: getter.return_type, writable: (setter != nil), static: getter.is_static? ) end end
map_jvm_types(types)
click to toggle source
# File lib/api_diff/kotlin_bcv_parser.rb, line 115 def map_jvm_types(types) mapping = { "Z" => "Boolean", "B" => "Byte", "C" => "Char", "S" => "Short", "I" => "Int", "J" => "Long", "F" => "Float", "D" => "Double", "V" => "Void" } vm_types_regexp = /(?<array>\[)?(?<type>Z|B|C|S|I|J|F|D|V|(L(?<class>[^;]+);))/ all_matches(types, vm_types_regexp).map do |match| if match[:class] result = unqualify(transform_package_path(match[:class])) else result = mapping[match[:type]] end result = "[#{result}]" if match[:array] result end end
normalize!(api)
click to toggle source
# File lib/api_diff/kotlin_bcv_parser.rb, line 139 def normalize!(api) api.classes.reject! { |c| c.parents == ["Factory"] } api.classes.reject! { |c| c.name.start_with? "Dagger" } # remove abstract & final # fun -> func # <init> -> init # remove space before ( (api.classes + api.interfaces + api.enums).flat_map(&:functions).each do |f| f.signature.gsub!(/(?:abstract )?(?:final )?fun (<?\w+>?) \(/, "func \\1(") f.signature.gsub!("func <init>", "init") end # enum screaming case -> camel case api.enums.each do |e| e.cases = e.cases.map do |c| # supports double _ by preserving one of them c.scan(/_?[A-Z0-9]+_?/).map.with_index do |p, index| p.gsub(/_$/, "").downcase.gsub(/^_?\w/) { |m| index == 0 ? m : m.upcase } end.join end end end
parse_class(class_content)
click to toggle source
# File lib/api_diff/kotlin_bcv_parser.rb, line 25 def parse_class(class_content) qualified_name = transform_package_path class_content.match(/public.+class ([^\s]+)/)[1] cls = Class.new(unqualify(qualified_name), qualified_name) cls.parents = parse_parents(class_content) cls.functions = parse_functions(class_content) extract_properties(cls) api.classes << cls end
parse_enum(enum_content)
click to toggle source
# File lib/api_diff/kotlin_bcv_parser.rb, line 46 def parse_enum(enum_content) qualified_name = transform_package_path enum_content.match(/public.+class ([^\s]+)/)[1] enum = Enum.new(unqualify(qualified_name), qualified_name) enum.cases = parse_enum_cases(enum_content) enum.functions = parse_functions(enum_content) extract_properties(enum) api.enums << enum end
parse_enum_cases(content)
click to toggle source
# File lib/api_diff/kotlin_bcv_parser.rb, line 80 def parse_enum_cases(content) case_regexp = /public static final field (?<name>[A-Z_0-9]+)/ all_matches(content, case_regexp).map do |match| match[:name] end end
parse_functions(content)
click to toggle source
# File lib/api_diff/kotlin_bcv_parser.rb, line 62 def parse_functions(content) method_regexp = /public (?<signature>(?<static>static )?.*fun (?:(<(?<init>init)>)|(?<name>[^\s]+)) \((?<params>.*)\))(?<return_type>.+)$/ all_matches(content, method_regexp).map do |match| next if match[:name]&.start_with? "component" # don't add data class `componentX` methods params_range = ((match.begin(:params) - match.begin(:signature))...(match.end(:params) - match.begin(:signature))) signature = match[:signature] signature[params_range] = map_jvm_types(match[:params]).join(", ") signature.gsub!(/synthetic ?/, "") # synthetic or not, it's part of the API Function.new( name: (match[:name] || match[:init]), signature: signature, return_type: match[:init].nil? ? map_jvm_types(match[:return_type]).join : nil, static: !match[:static].nil?, constructor: (not match[:init].nil?) ) end.compact end
parse_interface(interface_content)
click to toggle source
# File lib/api_diff/kotlin_bcv_parser.rb, line 35 def parse_interface(interface_content) qualified_name = transform_package_path interface_content.match(/public.+class ([^\s]+)/)[1] interface = Interface.new(unqualify(qualified_name), qualified_name) interface.parents = parse_parents(interface_content) interface.functions = parse_functions(interface_content) extract_properties(interface) api.interfaces << interface end
parse_parents(content)
click to toggle source
# File lib/api_diff/kotlin_bcv_parser.rb, line 56 def parse_parents(content) parents_match = content.match(/\A.+?: (.+?) \{$/) return [] if parents_match.nil? parents_match[1].split(",").map { |p| unqualify(transform_package_path(p.strip)) } end
transform_package_path(path)
click to toggle source
# File lib/api_diff/kotlin_bcv_parser.rb, line 111 def transform_package_path(path) path.gsub("/", ".") end