csharp_2_swift 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1496d1ff9c80e94e08aa557f873fdfa0458a7012
4
+ data.tar.gz: 9fc703df6da430b68f24503cc7fa367f7eb63490
5
+ SHA512:
6
+ metadata.gz: 153645de56b17b7a9bc84f88c040d3f72ef0edf66cabbaed03659201e9809bc4a7cf93439042584747547093840bbdb7c00623c19ec8a184b33c10e6ab980bbb
7
+ data.tar.gz: a14a8626a045a54356658a5f70212e6d9e8e18f2900a462b990a6f43bca2ec76991f8a5fa146159e7fa7628711e8ef486cfba17de90815fd28dfeda91b6588cb
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'csharp_2_swift'
4
+ Csharp2Swift.new.execute(ARGV)
data/lib/core_ext.rb ADDED
@@ -0,0 +1,34 @@
1
+ class String
2
+ def camelcase(*separators)
3
+ case separators.first
4
+ when Symbol, TrueClass, FalseClass, NilClass
5
+ first_letter = separators.shift
6
+ end
7
+
8
+ separators = ['_'] if separators.empty?
9
+
10
+ str = self.dup
11
+
12
+ separators.each do |s|
13
+ str = str.gsub(/(?:#{s}+)([a-z])/){ $1.upcase }
14
+ end
15
+
16
+ case first_letter
17
+ when :upper, true
18
+ str = str.gsub(/(\A|\s)([a-z])/){ $1 + $2.upcase }
19
+ when :lower, false
20
+ str = str.gsub(/(\A|\s)([A-Z])/){ $1 + $2.downcase }
21
+ end
22
+
23
+ str
24
+ end
25
+
26
+ def upper_camelcase(*separators)
27
+ camelcase(:upper, *separators)
28
+ end
29
+
30
+ def lower_camelcase(*separators)
31
+ camelcase(:lower, *separators)
32
+ end
33
+
34
+ end
@@ -0,0 +1,340 @@
1
+ require 'ostruct'
2
+ require 'optparse'
3
+ require 'colorize'
4
+ require_relative './core_ext.rb'
5
+
6
+ $VERSION='1.0.1-20151230.3'
7
+
8
+ class Csharp2Swift
9
+
10
+ def initialize
11
+ @renamed_vars = {}
12
+ @renamed_methods = {}
13
+ end
14
+
15
+ def parse(args)
16
+ options = OpenStruct.new
17
+ options.output_filename = ''
18
+ options.input_filename = nil
19
+ options.convert_simple_for_loops = false
20
+
21
+ opt_parser = OptionParser.new do |opts|
22
+ opts.banner = %Q(Roughly Convert C# to Swift. Version #{$VERSION}
23
+ Copyright (c) John Lyon-Smith, 2015.
24
+ Usage: #{File.basename(__FILE__)} [options] FILE
25
+ )
26
+ opts.separator %Q(Description:
27
+ This tool does a rough conversion of C# source code to Swift. The goal of the tool
28
+ is to do most of the easy stuff that simply requires a lot of typing effort, and allow you
29
+ to concentrate on the more difficult aspects of the conversion, such as library and
30
+ framework usage.
31
+ )
32
+ opts.separator %Q(Options:
33
+ )
34
+
35
+ opts.on("-o", "--output FILE", String, "The output file. Default is the same as the input file.") do |file|
36
+ options.output_filename = File.expand_path(file)
37
+ end
38
+
39
+ opts.on("--convert_simple_for_loops", "Convert simple range based for loops, e.g. for(int i = 0; i < 10; i++) { }",
40
+ "to range based loops of the form for i in 0..<10 { }") do |convert|
41
+ options.convert_simple_for_loops = convert
42
+ end
43
+
44
+ opts.on_tail("-?", "--help", "Show this message") do
45
+ puts opts
46
+ exit
47
+ end
48
+ end
49
+
50
+ opt_parser.parse!(args)
51
+ options.input_filename = args.pop
52
+ if options.input_filename == nil
53
+ error 'Need to specify a file to process'
54
+ exit
55
+ end
56
+ options.input_filename = File.expand_path(options.input_filename)
57
+ options
58
+ end
59
+
60
+ def execute(args)
61
+ options = self.parse(args)
62
+
63
+ if !File.exist?(options.input_filename)
64
+ error "File #{options.input_filename} does not exist"
65
+ exit
66
+ end
67
+
68
+ if options.output_filename.length == 0
69
+ error "An output file must be specified."
70
+ end
71
+
72
+ options.output_filename = File.expand_path(options.output_filename)
73
+ content = read_file(options.input_filename)
74
+
75
+ # Things that clean up the code and make other regex's easier
76
+ remove_eol_semicolons(content)
77
+ join_open_brace_to_last_line(content)
78
+ remove_region(content)
79
+ remove_endregion(content)
80
+ remove_namespace_using(content)
81
+ convert_this_to_self(content)
82
+ convert_int_type(content)
83
+ convert_string_type(content)
84
+ convert_bool_type(content)
85
+ convert_float_type(content)
86
+ convert_double_type(content)
87
+ convert_list_list_type(content)
88
+ convert_list_array_type(content)
89
+ convert_list_type(content)
90
+ convert_debug_assert(content)
91
+ remove_new(content)
92
+ insert_import(content)
93
+
94
+ # Slightly more complicated stuff
95
+ remove_namespace(content)
96
+ convert_property(content)
97
+ remove_get_set(content)
98
+ convert_const_field(content)
99
+ convert_field(content)
100
+ constructors_to_inits(content)
101
+ convert_method_decl_to_func_decl(content)
102
+ convert_locals(content)
103
+ convert_if(content)
104
+ convert_next_line_else(content)
105
+
106
+ # Optional stuff
107
+ convert_simple_range_for_loop(content) if options.convert_simple_for_loops
108
+
109
+ # Global search/replace
110
+ @renamed_vars.each { |v, nv|
111
+ content.gsub!(Regexp.new("\\." + v + "\\b"), '.' + nv)
112
+ }
113
+ @renamed_methods.each { |m, nm|
114
+ content.gsub!(Regexp.new('\\b' + m + '\\('), nm + '(')
115
+ }
116
+
117
+ write_file(options.output_filename, content)
118
+
119
+ puts "\"#{options.input_filename}\" -> \"#{options.output_filename}\""
120
+
121
+ @renamed_vars.each {|k,v| puts k + ' -> ' + v}
122
+ @renamed_methods.each {|k,v| puts k + '() -> ' + v + '()'}
123
+ end
124
+
125
+ def remove_eol_semicolons(content)
126
+ content.gsub!(/; *$/m, '')
127
+ end
128
+
129
+ def join_open_brace_to_last_line(content)
130
+ re = / *\{$/m
131
+ m = re.match(content)
132
+ s = ' {'
133
+
134
+ while m != nil do
135
+ offset = m.offset(0)
136
+ start = offset[0]
137
+ content.slice!(offset[0]..offset[1])
138
+ content.insert(start - 1, s)
139
+ m = re.match(content, start - 1 + s.length)
140
+ end
141
+ end
142
+
143
+ def convert_this_to_self(content)
144
+ content.gsub!(/this\./, 'self.')
145
+ end
146
+
147
+ def remove_region(content)
148
+ content.gsub!(/ *#region.*\n/, '')
149
+ end
150
+
151
+ def remove_endregion(content)
152
+ content.gsub!(/ *#endregion.*\n/, '')
153
+ end
154
+
155
+ def remove_namespace_using(content)
156
+ content.gsub!(/ *using (?!\().*\n/, '')
157
+ end
158
+
159
+ def convert_int_type(content)
160
+ content.gsub!(/\bint\b/, 'Int')
161
+ end
162
+
163
+ def convert_string_type(content)
164
+ content.gsub!(/\bstring\b/, 'Int')
165
+ end
166
+
167
+ def convert_bool_type(content)
168
+ content.gsub!(/\bbool\b/, 'Bool')
169
+ end
170
+
171
+ def convert_float_type(content)
172
+ content.gsub!(/\bfloat\b/, 'Float')
173
+ end
174
+
175
+ def convert_double_type(content)
176
+ content.gsub!(/\bdouble\b/, 'Double')
177
+ end
178
+
179
+ def convert_list_type(content)
180
+ content.gsub!(/(?:List|IList)<(\w+)>/, '[\\1]')
181
+ end
182
+
183
+ def convert_list_list_type(content)
184
+ content.gsub!(/(?:List|IList)<(?:List|IList)<(\w+)>>/, '[[\\1]]')
185
+ end
186
+
187
+ def convert_list_array_type(content)
188
+ content.gsub!(/(?:List|IList)<(\w+)>\[\]/, '[[\\1]]')
189
+ end
190
+
191
+ def convert_debug_assert(content)
192
+ content.gsub!(/Debug\.Assert\(/, 'assert(')
193
+ end
194
+
195
+ def remove_new(content)
196
+ content.gsub!(/new /, '')
197
+ end
198
+
199
+ def insert_import(content)
200
+ content.insert(0, "import Foundation\n")
201
+ end
202
+
203
+ def remove_namespace(content)
204
+ re = / *namespace +.+ *\{$/
205
+ m = re.match(content)
206
+
207
+ if m == nil
208
+ return
209
+ end
210
+
211
+ i = m.end(0)
212
+ n = 1
213
+ bol = (content[i] == "\n")
214
+ while i < content.length do
215
+ c = content[i]
216
+
217
+ if bol and c == " " and i + 3 < content.length
218
+ content.slice!(i..(i + 3))
219
+ c = content[i]
220
+ end
221
+
222
+ case c
223
+ when "{"
224
+ n += 1
225
+ when "}"
226
+ n -= 1
227
+ if n == 0
228
+ content.slice!(i) # Take out the end curly
229
+ content.slice!(m.begin(0)..m.end(0)) # Take out the original namespace
230
+ break
231
+ end
232
+ when "\n"
233
+ bol = true
234
+ else
235
+ bol = false
236
+ end
237
+ i += 1
238
+ end
239
+ end
240
+
241
+ def convert_const_field(content)
242
+ content.gsub!(/(^ *)(?:public|private|internal) +const +(.+?) +(.+?)( *= *.*?|)$/) { |m|
243
+ v = $3
244
+ nv = v.lower_camelcase
245
+ @renamed_vars[v] = nv
246
+ $1 + 'let ' + nv + ': ' + $2 + $4
247
+ }
248
+ end
249
+
250
+ def convert_field(content)
251
+ content.gsub!(/(^ *)(?:public|private|internal) +(\w+) +(\w+)( *= *.*?|)$/) { |m|
252
+ $1 + 'private var ' + $3 + ': ' + $2 + $4
253
+ }
254
+ end
255
+
256
+ def convert_property(content)
257
+ content.gsub!(/(^ *)(?:public|private|internal) +(?!class)([A-Za-z0-9_\[\]<>]+) +(\w+)(?: *\{)/) { |m|
258
+ v = $3
259
+ nv = v.lower_camelcase
260
+ @renamed_vars[v] = nv
261
+ $1 + 'var ' + nv + ': ' + $2 + ' {'
262
+ }
263
+ end
264
+
265
+ def remove_get_set(content)
266
+ content.gsub!(/{ *get; *set; *}$/, '')
267
+ end
268
+
269
+ def constructors_to_inits(content)
270
+ re = /(?:(?:public|internal|private) +|)class +(\w+)/
271
+ m = re.match(content)
272
+ while m != nil do
273
+ content.gsub!(Regexp.new('(?:(?:public|internal) +|)' + m.captures[0] + " *\\("), 'init(')
274
+ m = re.match(content, m.end(0))
275
+ end
276
+
277
+ content.gsub!(/init\((.*)\)/) { |m|
278
+ 'init(' + swap_args($1) + ')'
279
+ }
280
+ end
281
+
282
+ def convert_method_decl_to_func_decl(content)
283
+ # TODO: Override should be captured and re-inserted
284
+ content.gsub!(/(?:(?:public|internal|private) +)(?:override +|)(.+) +(.*)\((.*)\) *\{/) { |m|
285
+ f = $2
286
+ nf = f.lower_camelcase
287
+ @renamed_methods[f] = nf
288
+ if $1 == "void"
289
+ 'func ' + nf + '(' + swap_args($3) + ') {'
290
+ else
291
+ 'func ' + nf + '(' + swap_args($3) + ') -> ' + $1 + ' {'
292
+ end
293
+ }
294
+ end
295
+
296
+ def convert_locals(content)
297
+ content.gsub!(/^( *)(?!return|import)([A-Za-z0-9_\[\]<>]+) +(\w+)(?:( *= *.+)|)$/, '\\1let \\3\\4')
298
+ end
299
+
300
+ def convert_if(content)
301
+ content.gsub!(/if *\((.*)\) +\{/, 'if \\1 {')
302
+ content.gsub!(/if *\((.*?)\)\n( +)(.*?)\n/m) { |m|
303
+ s = $2.length > 4 ? $2[0...-4] : s
304
+ 'if ' + $1 + " {\n" + $2 + $3 + "\n" + s + "}\n"
305
+ }
306
+ end
307
+
308
+ def convert_next_line_else(content)
309
+ content.gsub!(/\}\n +else \{/m, '} else {')
310
+ end
311
+
312
+ def convert_simple_range_for_loop(content)
313
+ content.gsub!(/for \(.+ +(\w+) = (.+); \1 < (.*); \1\+\+\)/, 'for \\1 in \\2..<\\3')
314
+ content.gsub!(/for \(.+ +(\w+) = (.+); \1 >= (.*); \1\-\-\)/, 'for \\1 in (\\3...\\2).reverse()')
315
+ end
316
+
317
+ def swap_args(arg_string)
318
+ args = arg_string.split(/, */)
319
+ args.collect! { |arg|
320
+ a = arg.split(' ')
321
+ a[1] + ': ' + a[0]
322
+ }
323
+ args.join(', ')
324
+ end
325
+
326
+ def read_file(filename)
327
+ content = nil
328
+ File.open(filename, 'rb') { |f| content = f.read() }
329
+ content
330
+ end
331
+
332
+ def write_file(filename, content)
333
+ File.open(filename, 'w') { |f| f.write(content) }
334
+ end
335
+
336
+ def error(msg)
337
+ STDERR.puts "error: #{msg}".red
338
+ end
339
+
340
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: csharp_2_swift
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - John Lyon-smith
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colorize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.7.7
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.7.7
27
+ description: |
28
+ A tool that does a simple conversion of C# code to Swift.
29
+ It hits the major stuff, like re-ordering parameters, field/property declarations, for loop syntax
30
+ and leaves you to figure out the framework related stuff.
31
+ email: john@jamoki.com
32
+ executables:
33
+ - csharp_2_swift
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - lib/csharp_2_swift.rb
38
+ - lib/core_ext.rb
39
+ - bin/csharp_2_swift
40
+ homepage: http://rubygems.org/gems/csharp_2_swift
41
+ licenses:
42
+ - MIT
43
+ metadata: {}
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ~>
51
+ - !ruby/object:Gem::Version
52
+ version: '2.0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 2.0.14
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: Simple C# to Swift Converter
64
+ test_files: []