forked from leoMehlig/TDLib-iOS
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparser.rb
210 lines (199 loc) · 6.1 KB
/
parser.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# frozen_string_literal: true
class TDType
attr_accessor :description, :name, :fields, :result
def initialize
@fields = {}
end
def gen
string = "/// #{@description}\n"
if name.casecmp(result).zero?
string += "public struct #{result}: Codable, Equatable, FunctionResult {\n"
else
letters = @name.split('')
letters.first.upcase!
@name = letters.join
string += "public struct #{@name}: Codable, Equatable, TDFunction {\n"
string += "\tpublic typealias Result = #{result}\n"
end
for field in fields.each_value do
string += "\t/// #{field.description}\n"
name = field.escaped_name.camel_case
string += "\tpublic let #{name}: #{field.cased_type}\n"
end
string += "\t/// #{@description}\n"
string += "\t/// - Parameters:\n" unless fields.empty?
fields.each_value do |field|
string += "\t/// - #{field.formattedName}: #{field.description}\n"
end
string += "\tpublic init("
for field in fields.each_value do
string += "#{field.formattedName}: #{field.cased_type}, "
end
string = string[0...-2] unless fields.empty?
string += ") {\n"
for field in fields.each_value do
string += "\t\tself.#{field.formattedName} = #{field.escaped_name.camel_case}\n"
end
string += "\t}\n}"
string
end
end
class TDEnum
attr_accessor :cases, :name, :description
def initialize
@cases = []
end
def gen
string = "/// #{@description}\n"
unless cases.first.name.camel_case.start_with?(@name.lower_first.camel_case)
string += "// sourcery: noPrefix = true\n"
end
string += "public indirect enum #{name}: Codable, Equatable, FunctionResult, TDEnum, EquatableEnum {\n"
for enum_case in cases do
string += "\t/// #{enum_case.description}\n"
enum_case.fields.each_value do |field|
string += "\t/// - #{field.formattedName}: #{field.description}\n"
end
case_name = enum_case.name.camel_case
if case_name.start_with?(@name.lower_first.camel_case)
case_name = case_name[@name.length..-1].lower_first.escaped
end
string += "\tcase #{case_name}"
unless enum_case.fields.empty?
string += '('
for field in enum_case.fields.each_value do
string += "#{field.formattedName}: #{field.cased_type}, "
end
string = string[0...-2]
string += ')'
end
string += "\n"
end
string += '}'
string
end
end
class Field
attr_accessor :description, :name, :type
def formattedName
@name.camel_case
end
def escaped_name
return '`protocol`' if @name == 'protocol'
return '`subscript`' if @name == 'subscript'
@name
end
def cased_type
@type = 'TDInt64' if @type == 'int64'
@type = @type.un_vector
letters = @type.split('')
letters.first.upcase!
return letters.join + '?' if @description.include?('may be null') || @description.include?('for bots only')
letters.join
end
end
class String
def camel_case
split('_').inject([]) { |buffer, e| buffer.push(buffer.empty? ? e : e.capitalize) }.join
end
def escaped
return '`protocol`' if self == 'protocol'
return '`private`' if self == 'private'
return '`subscript`' if self == 'subscript'
return '`default`' if self == 'default'
self
end
def lower_first
letters = split('')
letters.first.downcase!
letters.join
end
def un_vector
vector_match = scan(/^vector<(.+?)>$/)[0]
if vector_match && !vector_match.empty?
letters = vector_match[0].un_vector.split('')
letters.first.upcase!
return "[#{letters.join}]"
end
self
end
end
all = []
current = nil
comment_string = ''
File.readlines('../Carthage/Checkouts/TDJSON/td_api.tl').each do |line|
if line.length > 1
if line.start_with?('//')
comment_string = comment_string + ' ' + line[2..-2]
else
unless comment_string.empty?
comment_string.scan(/@(.+?)\s([^@]+)/) do |match|
if match[0] == 'class'
all.push(current) if current
current = TDEnum.new
current.name = match[1].strip
elsif match[0] == 'description'
if current.instance_of?(TDEnum) && !current.description
current.description = match[1]
else
type = TDType.new
type.description = match[1]
if current.instance_of?(TDEnum)
current.cases.push(type)
else
all.push(current) if current
current = type
end
end
else
field = Field.new
field.name = match[0].strip
field.name = 'description' if field.name == 'param_description'
field.description = match[1]
if current.instance_of?(TDEnum)
current.cases.last.fields[field.name] = field
else
current.fields[field.name] = field
end
end
end
comment_string = ''
end
type_match = line.scan(/^([^\s]+?)\s([^=]*?)\=\s(.+?)\;$/)[0]
if current && type_match && type_match.length >= 3
current_enum = nil
if current.instance_of?(TDEnum)
current_enum = current
current = current_enum.cases.last
end
current.name = type_match[0]
current.result = type_match[2]
type_match[1].scan(/(([^\s\:]+?)\:([^\s]+))/) do |match|
name = match[1]
field = current.fields[name]
field.type = match[2]
current.fields[name] = field
end
if current_enum
if current.result != current_enum.name
current_enum.cases.delete(current_enum.cases.last)
all.push(current_enum)
else
current = current_enum
end
end
end
end
end
end
all.push(current) if current
current = nil
File.open('../TDLib/Generated/TDLib.generated.swift', 'w') do |file|
file.write("public typealias Int53 = Int64\n")
file.write("public typealias Bytes = Data\n\n")
for type in all do
puts type.gen
file.write(type.gen + "\n\n")
end
end
puts `cd .. && sourcery`