ios_parser 0.5.1-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,327 @@
1
+ module IOSParser
2
+ class PureLexer
3
+ LexError = IOSParser::LexError
4
+
5
+ attr_accessor :tokens, :token, :indents, :indent, :state, :char,
6
+ :string_terminator
7
+
8
+ def initialize
9
+ @text = ''
10
+ @token = ''
11
+ @tokens = []
12
+ @indent = 0
13
+ @indents = [0]
14
+ @state = :root
15
+ @token_char = 0
16
+ @this_char = -1
17
+ end
18
+
19
+ def call(input_text)
20
+ @text = input_text
21
+
22
+ input_text.each_char.with_index do |c, i|
23
+ @this_char = i
24
+ self.char = c
25
+ send(state)
26
+ end
27
+
28
+ finalize
29
+ end
30
+
31
+ ROOT_TRANSITIONS = [
32
+ :space,
33
+ :banner_begin,
34
+ :certificate_begin,
35
+ :newline,
36
+ :comment,
37
+ :integer,
38
+ :quoted_string,
39
+ :word
40
+ ].freeze
41
+
42
+ def root
43
+ @token_start ||= @this_char
44
+
45
+ ROOT_TRANSITIONS.each do |meth|
46
+ return send(meth) if send(:"#{meth}?")
47
+ end
48
+
49
+ raise LexError, "Unknown character #{char.inspect}"
50
+ end
51
+
52
+ def root_line_start
53
+ if lead_comment?
54
+ comment
55
+ else
56
+ root
57
+ end
58
+ end
59
+
60
+ def make_token(value, pos: nil)
61
+ pos ||= @token_start || @this_char
62
+ @token_start = nil
63
+ [pos, value]
64
+ end
65
+
66
+ def comment
67
+ self.state = :comment
68
+ update_indentation
69
+ self.state = :root if newline?
70
+ end
71
+
72
+ def comment?
73
+ char == '!'
74
+ end
75
+
76
+ def lead_comment?
77
+ char == '#' || char == '!'
78
+ end
79
+
80
+ def banner_begin
81
+ self.state = :banner
82
+ tokens << make_token(:BANNER_BEGIN)
83
+ @token_start = @this_char + 2
84
+ @banner_delimiter = char == "\n" ? 'EOF' : char
85
+ end
86
+
87
+ def banner_begin?
88
+ tokens[-2] && tokens[-2].last == 'banner'
89
+ end
90
+
91
+ def banner
92
+ if banner_end_char?
93
+ banner_end_char
94
+ elsif banner_end_string?
95
+ banner_end_string
96
+ else
97
+ token << char
98
+ end
99
+ end
100
+
101
+ def banner_end_string
102
+ self.state = :root
103
+ token.chomp!(@banner_delimiter[0..-2])
104
+ tokens << make_token(token) << make_token(:BANNER_END)
105
+ self.token = ''
106
+ end
107
+
108
+ def banner_end_string?
109
+ @banner_delimiter.size > 1 && (token + char).end_with?(@banner_delimiter)
110
+ end
111
+
112
+ def banner_end_char
113
+ self.state = :root
114
+ banner_end_clean_token
115
+ tokens << make_token(token) << make_token(:BANNER_END)
116
+ self.token = ''
117
+ end
118
+
119
+ def banner_end_char?
120
+ char == @banner_delimiter && (@text[@this_char - 1] == "\n" ||
121
+ @text[@this_char + 1] == "\n")
122
+ end
123
+
124
+ def banner_end_clean_token
125
+ token.slice!(0) if token[0] == 'C'
126
+ token.slice!(0) if ["\n", ' '].include?(token[0])
127
+ end
128
+
129
+ def scrub_banner_garbage
130
+ tokens.each_index do |i|
131
+ next unless tokens[i + 1]
132
+ tokens.slice!(i + 1) if banner_garbage?(i)
133
+ end
134
+ end
135
+
136
+ def banner_garbage?(pos)
137
+ tokens[pos].last == :BANNER_END && tokens[pos + 1].last == 'C'
138
+ end
139
+
140
+ def certificate_begin?
141
+ tokens[-6] && tokens[-6].last == :INDENT &&
142
+ tokens[-5] && tokens[-5].last == 'certificate'
143
+ end
144
+
145
+ def certificate_begin
146
+ self.state = :certificate
147
+ indents.pop
148
+ tokens[-2..-1] = [make_token(:CERTIFICATE_BEGIN, pos: tokens[-1][0])]
149
+ certificate
150
+ end
151
+
152
+ def certificate
153
+ token[-5..-1] == "quit\n" ? certificate_end : token << char
154
+ end
155
+
156
+ def certificate_end
157
+ tokens.concat certificate_end_tokens
158
+ update_indentation
159
+ @token_start = @this_char
160
+
161
+ @token = ''
162
+ self.state = :line_start
163
+ self.indent = 0
164
+ root
165
+ end
166
+
167
+ def certificate_end_tokens
168
+ [
169
+ make_token(token[0..-6].gsub!(/\s+/, ' ').strip, pos: tokens[-1][0]),
170
+ make_token(:CERTIFICATE_END, pos: @this_char),
171
+ make_token(:EOL, pos: @this_char)
172
+ ]
173
+ end
174
+
175
+ def integer
176
+ self.state = :integer
177
+ if dot? then decimal
178
+ elsif digit? then token << char
179
+ elsif word? then word
180
+ else root
181
+ end
182
+ end
183
+
184
+ def integer_token
185
+ token[0] == '0' ? word_token : make_token(Integer(token))
186
+ end
187
+
188
+ def digit?
189
+ ('0'..'9').cover? char
190
+ end
191
+ alias integer? digit?
192
+
193
+ def dot?
194
+ char == '.'
195
+ end
196
+
197
+ def decimal
198
+ self.state = :decimal
199
+ if digit? then token << char
200
+ elsif dot? then token << char
201
+ elsif word? then word
202
+ else root
203
+ end
204
+ end
205
+
206
+ def decimal_token
207
+ if token.count('.') > 1 || token[-1] == '.'
208
+ word_token
209
+ else
210
+ make_token(Float(token))
211
+ end
212
+ end
213
+
214
+ def decimal?
215
+ dot? || digit?
216
+ end
217
+
218
+ def word
219
+ self.state = :word
220
+ word? ? token << char : root
221
+ end
222
+
223
+ def word_token
224
+ make_token(token)
225
+ end
226
+
227
+ def word?
228
+ digit? || dot? ||
229
+ ('a'..'z').cover?(char) ||
230
+ ('A'..'Z').cover?(char) ||
231
+ ['-', '+', '$', ':', '/', ',', '(', ')', '|', '*', '#', '=', '<', '>',
232
+ '!', '"', '&', '@', ';', '%', '~', '{', '}', "'", '?', '[', ']', '_',
233
+ '^', '\\', '`'].include?(char)
234
+ end
235
+
236
+ def space
237
+ delimit
238
+ self.indent += 1 if tokens.last && tokens.last.last == :EOL
239
+ end
240
+
241
+ def space?
242
+ char == ' ' || char == "\t" || char == "\r"
243
+ end
244
+
245
+ def quoted_string
246
+ self.state = :quoted_string
247
+ token << char
248
+ if string_terminator.nil?
249
+ self.string_terminator = char
250
+ elsif char == string_terminator
251
+ delimit
252
+ end
253
+ end
254
+
255
+ def quoted_string_token
256
+ make_token(token)
257
+ end
258
+
259
+ def quoted_string?
260
+ char == '"' || char == "'"
261
+ end
262
+
263
+ def newline
264
+ delimit
265
+ return banner_begin if banner_begin?
266
+ self.state = :line_start
267
+ self.indent = 0
268
+ tokens << make_token(:EOL)
269
+ end
270
+
271
+ def newline?
272
+ char == "\n"
273
+ end
274
+
275
+ def line_start
276
+ if space?
277
+ self.indent += 1
278
+ else
279
+ update_indentation
280
+ root_line_start
281
+ end
282
+ end
283
+
284
+ def delimit
285
+ return if token.empty?
286
+
287
+ unless respond_to?(:"#{state}_token")
288
+ pos = @token_start || @this_char
289
+ raise LexError, "Unterminated #{state} starting at #{pos}: "\
290
+ "#{@text[pos..pos + 20].inspect}"
291
+ end
292
+
293
+ tokens << send(:"#{state}_token")
294
+ self.state = :root
295
+ self.token = ''
296
+ end
297
+
298
+ def update_indentation
299
+ pop_dedent while 1 < indents.size && indent <= indents[-2]
300
+ push_indent if indent > indents.last
301
+ self.indent = 0
302
+ end
303
+
304
+ def pop_dedent
305
+ tokens << make_token(:DEDENT)
306
+ indents.pop
307
+ end
308
+
309
+ def push_indent
310
+ tokens << make_token(:INDENT)
311
+ indents.push(indent)
312
+ end
313
+
314
+ def finalize
315
+ if state == :quoted_string
316
+ pos = @text.rindex(string_terminator)
317
+ raise LexError, "Unterminated quoted string starting at #{pos}: "\
318
+ "#{@text[pos..pos + 20]}"
319
+ end
320
+
321
+ delimit
322
+ update_indentation
323
+ scrub_banner_garbage
324
+ tokens
325
+ end
326
+ end # class PureLexer
327
+ end # module IOSParser
@@ -0,0 +1,2 @@
1
+ require_relative 'lexer'
2
+ require_relative '../ios_parser'
@@ -0,0 +1,7 @@
1
+ module IOSParser
2
+ class << self
3
+ def version
4
+ '0.5.1'
5
+ end
6
+ end
7
+ end
data/lib/ios_parser.rb ADDED
@@ -0,0 +1,37 @@
1
+ require 'json'
2
+
3
+ module IOSParser
4
+ class LexError < StandardError; end
5
+
6
+ def self.lexer
7
+ if const_defined?(:PureLexer)
8
+ PureLexer
9
+ else
10
+ require_relative 'ios_parser/c_lexer'
11
+ CLexer
12
+ end
13
+ rescue LoadError
14
+ require 'ios_parser/lexer'
15
+ PureLexer
16
+ end
17
+
18
+ Lexer = lexer
19
+ end
20
+
21
+ require_relative 'ios_parser/ios'
22
+
23
+ module IOSParser
24
+ class << self
25
+ def parse(input)
26
+ IOSParser::IOS.new.call(input)
27
+ end
28
+
29
+ def hash_to_ios(hash)
30
+ IOSParser::IOS::Document.from_hash(hash)
31
+ end
32
+
33
+ def json_to_ios(text)
34
+ hash_to_ios JSON.parse(text)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,157 @@
1
+ require_relative '../../../spec_helper'
2
+ require 'ios_parser'
3
+
4
+ module IOSParser
5
+ class IOS
6
+ describe Queryable do
7
+ let(:input) { <<-END.unindent }
8
+ policy-map mypolicy_in
9
+ class some_service
10
+ police 300000000 1000000 exceed-action policed-dscp-transmit
11
+ set dscp cs1
12
+ class my_service
13
+ police 600000000 1000000 exceed-action policed-dscp-transmit
14
+ set dscp cs2
15
+ command_with_no_args
16
+ END
17
+
18
+ let(:expectation) { 'set dscp cs1' }
19
+ let(:parsed) { IOSParser.parse(input) }
20
+ subject { parsed.find(matcher.freeze).line }
21
+
22
+ describe '#find' do
23
+ context 'shortcut matcher' do
24
+ describe String do
25
+ let(:matcher) { 'set dscp cs1'.freeze }
26
+ it { should == expectation }
27
+ end
28
+
29
+ describe Regexp do
30
+ let(:matcher) { /set .* cs1/ }
31
+ it { should == expectation }
32
+ end
33
+
34
+ describe Proc do
35
+ let(:expectation) { 'command_with_no_args' }
36
+ let(:matcher) { ->(c) { c.args.count == 1 } }
37
+ it { should == expectation }
38
+ end
39
+ end # context 'shortcut matcher' do
40
+
41
+ context 'explicit matcher form of shortcut matcher' do
42
+ describe String do
43
+ let(:matcher) { { starts_with: 'set dscp cs1'.freeze }.freeze }
44
+ it { should == expectation }
45
+ end
46
+
47
+ describe Regexp do
48
+ let(:matcher) { { line: /set .* cs1/ }.freeze }
49
+ it { should == expectation }
50
+ end
51
+
52
+ describe Proc do
53
+ let(:expectation) { 'command_with_no_args' }
54
+ let(:matcher) { { procedure: ->(c) { c.args.count == 1 } }.freeze }
55
+ it { should == expectation }
56
+ end
57
+ end # context 'explicit matcher form of shortcut matcher' do
58
+
59
+ context 'matcher: contains' do
60
+ describe String do
61
+ let(:matcher) { { contains: 'dscp cs1'.freeze }.freeze }
62
+ it { should == expectation }
63
+ end
64
+
65
+ describe Array do
66
+ let(:matcher) do
67
+ { contains: ['dscp'.freeze, 'cs1'.freeze].freeze }.freeze
68
+ end
69
+ it { should == expectation }
70
+ end
71
+ end # context 'matcher: contains' do
72
+
73
+ context 'matcher: ends_with' do
74
+ let(:expectation) { 'class my_service' }
75
+
76
+ describe String do
77
+ let(:matcher) { { ends_with: 'my_service'.freeze }.freeze }
78
+ it { should == expectation }
79
+ end
80
+
81
+ describe Array do
82
+ let(:matcher) { { ends_with: ['my_service'.freeze].freeze }.freeze }
83
+ it { should == expectation }
84
+ end
85
+ end # context 'matcher: ends_with' do
86
+
87
+ context 'matcher: all' do
88
+ let(:matcher) { { all: ['set'.freeze, /cs1/].freeze }.freeze }
89
+ it { should == expectation }
90
+ end
91
+
92
+ context 'matcher: parent' do
93
+ let(:matcher) { { parent: /police 3/ }.freeze }
94
+ it { should == expectation }
95
+ end
96
+
97
+ context 'matcher: any' do
98
+ let(:matcher) { { any: [/asdf/, /cs1/, /qwerwqe/].freeze }.freeze }
99
+ it { should == expectation }
100
+ end
101
+
102
+ context 'matcher: any (with a hash)' do
103
+ let(:matcher) do
104
+ {
105
+ any: { depth: 0, procedure: ->(c) { c.args.count == 1 } }.freeze
106
+ }.freeze
107
+ end
108
+
109
+ it do
110
+ expect(parsed.find_all(matcher).map(&:line))
111
+ .to eq ['policy-map mypolicy_in', 'command_with_no_args']
112
+ end
113
+ end
114
+
115
+ context 'matcher: depth' do
116
+ let(:matcher) { { depth: 3 }.freeze }
117
+ it { should == expectation }
118
+ end
119
+
120
+ context 'matcher: none' do
121
+ let(:matcher) do
122
+ { none: [/policy/, /class/, /police/].freeze }.freeze
123
+ end
124
+ it { should == expectation }
125
+ end
126
+
127
+ context 'matcher: not_all' do
128
+ let(:matcher) do
129
+ {
130
+ all: {
131
+ none: /policy/,
132
+ not: /class/,
133
+ not_all: /police/
134
+ }.freeze
135
+ }.freeze
136
+ end
137
+
138
+ it do
139
+ expect(parsed.find(not_all: [/policy/, /class/]).line)
140
+ .to eq 'policy-map mypolicy_in'
141
+ end
142
+ it { should == expectation }
143
+ end
144
+
145
+ context 'matcher: any_child' do
146
+ let(:matcher) { { not: { any_child: /dscp/ }.freeze }.freeze }
147
+ it { should == expectation }
148
+ end
149
+
150
+ context 'matcher: no_child' do
151
+ let(:matcher) { { no_child: /dscp/ }.freeze }
152
+ it { should == expectation }
153
+ end
154
+ end # describe '#find' do
155
+ end # describe Queryable
156
+ end # class IOS
157
+ end # module IOSParser