csharp_2_swift 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []