From: Christian Tietze Date: Wed, 27 Dec 2017 09:31:33 +0000 (+0100) Subject: add cocoaconv.rb enum conversion script X-Git-Tag: 6.3.0^2~13^2~4 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ecd20c606014bc330b6187a82478f90813378676;p=multimarkdown add cocoaconv.rb enum conversion script --- diff --git a/swift/cocoaconv.rb b/swift/cocoaconv.rb new file mode 100755 index 0000000..cd29daf --- /dev/null +++ b/swift/cocoaconv.rb @@ -0,0 +1,229 @@ +#!/usr/bin/env ruby + +require 'optparse' + +CURRENT_PATH = File.expand_path(File.dirname(__FILE__)) +FALLBACK_PATH = File.join(CURRENT_PATH, "..", "build-xcode", "Debug", "include", "libMultiMarkdown", "libMultiMarkdown.h") + +options = {:mode => :nsenum} +OptionParser.new do |parser| + parser.banner = "Usage: #{$0} [options] path/to/libMultiMarkdown.h" + + parser.separator "" + parser.separator "Without an input path, the script uses this default relative project location:" + parser.separator "../build-xcode/Debug/include/libMultiMarkdown/libMultiMarkdown.h" + parser.separator "" + + parser.on("-h", "--help", "Prints this help") do + puts parser + exit + end + + parser.on("-m", "--mode [MODE]", [:swift, :nsenum], + "Select generator:", + " nsenum Generates Objective-C NS_ENUM wrappers. (Default)", + " swift Generates Swift enum descriptions.") do |mode| + options[:mode] = mode + end + + parser.on("-o", "--output [PATH]", String, + "Write output to file instead of STDOUT. (Optional)") do |path| + options[:outpath] = path + end +end.parse! + + +################################################################################ +## Types to perform the conversion +################################################################################ + + +module CustomStringConvertible + def extension + indentation = " " + cases = case_descriptions + .map { |line| indentation + line } + .join("\n") + + return %Q{ +extension #{type_name}: CustomStringConvertible { + public var description: String { + switch self { +#{cases} + } + } +}} + end + + private + + def case_descriptions + cases.map { |c| CustomStringConvertible.description_for(self.type_name, c) } + end + + def indented_case_descriptions(indent) + case_descriptions + .map { |line| indent + line } + .join("\n") + end + + def self.description_for(type_name, enum_case) + case_name = case_only(enum_case) + swift_case_name = case_name.camelize(lowercase_first: true) + %Q{case .#{swift_case_name}: return "#{type_name}.#{swift_case_name}"} + end + + def self.case_only(line) + line[/\w+/] + end +end + +class String + def camelize(lowercase_first: false) + self + .split('_') + .map.with_index { |part, i| + if lowercase_first && i == 0 + part.downcase + else + part.capitalize + end } + .join + end +end + +module NSEnum + def type_name + type.camelize + end + + def self.type_names(type) + return "MMD6#{type.camelize}", type.camelize + end + + def ns_enum + type_name, swift_type_name = NSEnum.type_names(type) + ns_enum_cases = cases + .map { |line| NSEnum.case(type_name, line) } + .join("\n") + + %Q{typedef NS_ENUM(NSUInteger, #{type_name}) { +#{ns_enum_cases} +} NS_SWIFT_NAME(#{swift_type_name});} + end + + def self.case(type_name, line) + if /(?\s*)(?\w+)(?.*)/ =~ line + return %Q{#{indent}#{type_name}#{casename.camelize} = #{casename},} + end + return line + end +end + +class Enum + include CustomStringConvertible + include NSEnum + + attr_accessor :type, :cases + + def initialize(type) + @type = type + @cases = [] + end + + def <<(line) + return if !Enum.is_enum_case(line) + cases << line + end + + def self.is_enum_case(line) + return false if line.strip.empty? + return false if line.include?("}") + return false if line.include?("{") + return true + end +end + +class Enums + attr_reader :enums + + def initialize(stream) + lines = stream.readlines + @enums = parse_enums(lines) + end + + def ns_enums + @enums.map(&:ns_enum) + end + + def swift_descriptions + @enums.map(&:extension) + end + + private + + def parse_enums(lines) + enums = [] + enumbuffer = nil + lines.each do |line| + type = line[/^\s*enum (\w+)\s*\{/, 1] + if !type.nil? + enumbuffer = Enum.new(type) + elsif !enumbuffer.nil? + if line.start_with?("}") + enums << enumbuffer + enumbuffer = nil + else + enumbuffer << line + end + else + # nop; discard line + end + end + return enums + end +end + +def file_stream_from_argv + path = ARGV.shift + return nil if path.nil? + File.open(path, "r") +end + +def fallback_stream + return nil unless File.exists?(FALLBACK_PATH) + File.open(FALLBACK_PATH, "r") +end + + +################################################################################ +## Script execution itself +################################################################################ + + +input = file_stream_from_argv || fallback_stream +if input.nil? + puts "Failed to read `#{FALLBACK_PATH}`" + exit -1 +end +enums = Enums.new(input) +input.close + + +result = if options[:mode] == :nsenum + enums.ns_enums.join("\n\n") + elsif options[:mode] == :swift + enums.swift_descriptions.join("\n") + else + puts "Illegal mode: #{options[:mode]}" + exit -1 + end + + +output = if options[:outpath].nil? + $stdout + else + File.open(options[:outpath], "w") + end +output.puts result +output.close