-
Notifications
You must be signed in to change notification settings - Fork 35
/
bc-checkparams.rb
executable file
·274 lines (247 loc) · 5.94 KB
/
bc-checkparams.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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
#! /usr/bin/ruby
require 'optparse'
require 'pathname'
require 'set'
bindir = Pathname.new(__FILE__).realpath.dirname
$LOAD_PATH.unshift((bindir + '../lib').realpath)
require 'bitclust/preprocessor'
require 'bitclust/lineinput'
require 'bitclust/parseutils'
require 'bitclust/methodsignature'
def main
option = OptionParser.new
version = '1.9.1'
option.banner = "Usage: #{File.basename($0, '.*')} <filename>"
option.on('--ruby=[VER]', "The version of Ruby interpreter"){|ver|
version = ver
}
option.on('--help', 'Prints this message and quit.') {
puts option.help
exit 0
}
begin
option.parse!(ARGV)
rescue OptionParser::ParseError => err
$stderr.puts err.message
exit 1
end
unless ARGV.size == 1
$stderr.puts "wrong number of arguments"
$stderr.puts option.help
exit 1
end
filename = ARGV[0]
path = Pathname.new(filename)
params = { "version" => version }
parser = BitClust::RDParser.new(BitClust::Preprocessor.read(path, params))
parser.parse
end
module BitClust
class RDParser
def initialize(src)
@f = LineInput.new(StringIO.new(src))
@option = { :force => true }
end
def parse
while @f.next?
case @f.peek
when /\A---/
method_entry_chunk
else
@f.gets
end
end
end
def method_entry_chunk
method_signatures = []
@f.while_match(/\A---/) do |line|
method_signatures << method_signature(line)
end
props = {}
@f.while_match(/\A:/) do |line|
k, v = line.sub(/\A:/, '').split(':', 2)
props[k.strip] = v.strip
end
params = Set.new
while @f.next?
case @f.peek
when /\A===+/
@f.gets
when /\A==?/
if @option[:force]
break
else
raise "method entry includes headline: #{@f.peek.inspect}"
end
when /\A---/
break
when /\A\s+\*\s/
ulist
when /\A\s+\(\d+\)\s/
olist
when /\A:\s/
dlist
when %r<\A//emlist\{>
emlist
when /\A\s+\S/
list
when /@see/
see
when /\A@[a-z]/
method_info.each do |param|
params << param
end
else
if @f.peek.strip.empty?
@f.gets
else
method_entry_paragraph
end
end
end
check_parameters(method_signatures, params)
end
def headline(line)
# nop
end
def ulist
@f.while_match(/\A\s+\*\s/) do |line|
@f.while_match(/\A\s+[^\*\s]/) do |cont|
# nop
end
end
end
def olist
@f.while_match(/\A\s+\(\d+\)/) do |line|
@f.while_match(/\A\s+(?!\(\d+\))\S/) do |cont|
# nop
end
end
end
def dlist
while @f.next? and /\A:/ =~ @f.peek
@f.while_match(/\A:/) do |line|
# nop
end
dd_with_p
end
end
# empty lines separate paragraphs.
def dd_with_p
while /\A(?:\s|\z)/ =~ @f.peek or %r!\A//emlist\{! =~ @f.peek
case @f.peek
when /\A$/
@f.gets
when /\A[ \t]/
@f.while_match(/\A[ \t]/) do |line|
# nop
end
when %r!\A//emlist\{!
emlist
else
raise 'must not happen'
end
end
end
# empty lines do not separate paragraphs.
def dd_without_p
while /\A[ \t]/ =~ @f.peek or %r!\A//emlist\{! =~ @f.peek
case @f.peek
when /\A[ \t]/
@f.while_match(/\A[ \t]/) do |line|
# nop
end
when %r!\A//emlist\{!
emlist
end
end
end
def emlist
@f.gets # discard "//emlist{"
@f.until_terminator(%r<\A//\}>) do |line|
# nop
end
end
def list
@f.break(/\A\S/)
end
def see
@f.gets
@f.span(/\A\s+\S/)
end
def paragraph
read_paragraph(@f).each do |line|
# nop
end
end
def read_paragraph(f)
f.span(%r<\A(?!---|=|//emlist\{)\S>)
end
def method_info
params = []
while @f.next? and /\A\@(?!see)\w+|\A$/ =~ @f.peek
header = @f.gets
next if /\A$/ =~ header
cmd = header.slice!(/\A\@\w+/)
@f.ungets(header)
case cmd
when '@param', '@arg'
name = header.slice!(/\A\s*\w+/) || '?'
params << name.strip
when '@raise'
# nop
when '@return'
# nop
when '@todo'
# nop
when '@undef'
# nop
else
$stderr.puts "[UNKNOWN_META_INFO] #{cmd}"
end
dd_without_p
end
params
rescue
$stderr.puts "#{@f.lineno}: #{@sig.friendly_string}"
$stderr.puts params.inspect
$stderr.puts @sig.params.inspect
end
def method_entry_paragraph
read_method_entry_paragraph(@f).each do |line|
# nop
end
end
def read_method_entry_paragraph(f)
f.span(%r<\A(?!---|=|//emlist\{|@[a-z])\S>)
end
def method_signature(sig_line)
@sig = MethodSignature.parse(sig_line)
end
private
def check_parameters(method_signatures, params)
params_in_signature = extract_params(method_signatures)
unless params_in_signature == params
puts "#{@f.lineno}:"
method_signatures.each do |signature|
puts signature.friendly_string
puts "signature: #{signature.params}"
end
puts "@params: #{params.to_a.join(", ")}"
puts "-" * 72
end
end
def extract_params(method_signatures)
method_signatures.map do |signature|
next unless signature.params
signature.params.split(",").map do |param|
param = param.tr("*&", "")
param = param.gsub(/\=.*/, "")
param = param.gsub(/.*:/, "")
param.strip
end
end.compact.flatten.to_set
end
end
end
main