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 +7 -0
- data/bin/csharp_2_swift +4 -0
- data/lib/core_ext.rb +34 -0
- data/lib/csharp_2_swift.rb +340 -0
- metadata +64 -0
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
|
data/bin/csharp_2_swift
ADDED
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: []
|