ios_parser 0.5.2 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/ext/ios_parser/c_lexer/lexer.c +90 -14
- data/lib/ios_parser.rb +18 -1
- data/lib/ios_parser/ios.rb +26 -15
- data/lib/ios_parser/ios/command.rb +18 -9
- data/lib/ios_parser/lexer.rb +82 -20
- data/lib/ios_parser/token.rb +8 -0
- data/lib/ios_parser/version.rb +1 -1
- data/spec/lib/ios_parser/ios_spec.rb +25 -21
- data/spec/lib/ios_parser/lexer_spec.rb +95 -53
- data/spec/lib/ios_parser_spec.rb +38 -14
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: abbb84cb462b8506f141ce88c1333f8f25e8bc9f31e5bc166d2f76f275044529
|
4
|
+
data.tar.gz: 7ed4f70c4396caae3d84df7dc17176b5a4c1e1f4eede31cdf63af8d34c61285f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 209623e73042e19dd5bb9108f3aee56ab357e2b84ad388f028e1055cf90eee73fc9040c448e6e394729880a3690abc38e2c945371dac73a7004db3bab78180a2
|
7
|
+
data.tar.gz: dc1b41a7d3e821d9bfdb2ce9e4b6cad8012396b2ba3ed1e39b56eecc7582870e829e8cb512ebca9a68ffb281c13aab1c5c7d6af48c53cec9ede3bbcc58078bb5
|
data/.travis.yml
CHANGED
@@ -14,4 +14,4 @@ matrix:
|
|
14
14
|
env: JRUBY_OPTS='-Xcompat.version=2.0'
|
15
15
|
bundler_args: --without guard
|
16
16
|
before_install:
|
17
|
-
- if [ "jruby" != "$TRAVIS_RUBY_VERSION" ]; then gem install bundler --without guard; fi
|
17
|
+
- if [ "jruby" != "$TRAVIS_RUBY_VERSION" ]; then gem i rubygems-update -v '<3' && update_rubygems; gem install bundler -v 1.17.3 --without guard; fi
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
static VALUE rb_mIOSParser;
|
4
4
|
static VALUE rb_cCLexer;
|
5
|
+
static VALUE rb_cToken;
|
5
6
|
VALUE rb_eLexError;
|
6
7
|
|
7
8
|
typedef enum lex_token_state {
|
@@ -21,6 +22,9 @@ struct LexInfo {
|
|
21
22
|
size_t pos;
|
22
23
|
size_t token_start;
|
23
24
|
size_t token_length;
|
25
|
+
size_t line;
|
26
|
+
size_t start_of_line;
|
27
|
+
size_t token_line;
|
24
28
|
lex_token_state token_state;
|
25
29
|
VALUE tokens;
|
26
30
|
int indent;
|
@@ -46,13 +50,16 @@ typedef struct LexInfo LexInfo;
|
|
46
50
|
|
47
51
|
#define CURRENT_CHAR(LEX) LEX->text[LEX->pos]
|
48
52
|
#define TOKEN_EMPTY(LEX) LEX->token_length <= 0
|
53
|
+
#define TOKEN_VALUE(TOK) RSTRUCT_GET(TOK, 0)
|
49
54
|
|
50
|
-
#define
|
51
|
-
#define ADD_TOKEN(LEX, TOK) rb_ary_push(LEX->tokens, MAKE_TOKEN(LEX, TOK))
|
55
|
+
#define ADD_TOKEN(LEX, TOK) rb_ary_push(LEX->tokens, make_token(LEX, TOK))
|
52
56
|
|
53
57
|
#define CMD_LEN(CMD) (sizeof(CMD) - 1)
|
58
|
+
|
59
|
+
static VALUE make_token(LexInfo *lex, VALUE tok);
|
60
|
+
|
54
61
|
int is_certificate(LexInfo *lex) {
|
55
|
-
VALUE
|
62
|
+
VALUE indent_token, indent, command_token, command;
|
56
63
|
int token_count, indent_pos, command_pos;
|
57
64
|
|
58
65
|
token_count = RARRAY_LEN(lex->tokens);
|
@@ -62,16 +69,15 @@ int is_certificate(LexInfo *lex) {
|
|
62
69
|
command_pos = token_count - 5;
|
63
70
|
if (command_pos < 0) { return 0; }
|
64
71
|
|
65
|
-
|
66
|
-
indent =
|
72
|
+
indent_token = rb_ary_entry(lex->tokens, indent_pos);
|
73
|
+
indent = TOKEN_VALUE(indent_token);
|
67
74
|
if (TYPE(indent) != T_SYMBOL) { return 0; }
|
68
75
|
if (rb_intern("INDENT") != SYM2ID(indent)) { return 0; }
|
69
76
|
|
70
|
-
|
71
|
-
if (TYPE(
|
72
|
-
if (RARRAY_LEN(command_ary) < 2) { return 0; }
|
77
|
+
command_token = rb_ary_entry(lex->tokens, command_pos);
|
78
|
+
if (TYPE(command_token) != T_STRUCT) { return 0; }
|
73
79
|
|
74
|
-
command =
|
80
|
+
command = TOKEN_VALUE(command_token);
|
75
81
|
if (TYPE(command) != T_STRING) { return 0; }
|
76
82
|
|
77
83
|
StringValue(command);
|
@@ -89,7 +95,7 @@ int is_banner_begin(LexInfo *lex) {
|
|
89
95
|
if (banner_pos < 0) { return 0; }
|
90
96
|
|
91
97
|
banner_ary = rb_ary_entry(lex->tokens, banner_pos);
|
92
|
-
banner =
|
98
|
+
banner = TOKEN_VALUE(banner_ary);
|
93
99
|
if (TYPE(banner) != T_STRING) { return 0; }
|
94
100
|
|
95
101
|
StringValue(banner);
|
@@ -148,6 +154,14 @@ static void deallocate(void * lex) {
|
|
148
154
|
xfree(lex);
|
149
155
|
}
|
150
156
|
|
157
|
+
static VALUE make_token(LexInfo *lex, VALUE tok) {
|
158
|
+
return rb_struct_new(rb_cToken,
|
159
|
+
tok,
|
160
|
+
rb_int_new(lex->token_start),
|
161
|
+
rb_int_new(lex->line),
|
162
|
+
rb_int_new(lex->token_start - lex->start_of_line + 1));
|
163
|
+
}
|
164
|
+
|
151
165
|
static void mark(void *ptr) {
|
152
166
|
LexInfo *lex = (LexInfo *)ptr;
|
153
167
|
rb_gc_mark(lex->tokens);
|
@@ -164,6 +178,9 @@ static VALUE initialize(VALUE self, VALUE input_text) {
|
|
164
178
|
|
165
179
|
lex->text = NULL;
|
166
180
|
lex->pos = 0;
|
181
|
+
lex->line = 1;
|
182
|
+
lex->start_of_line = 0;
|
183
|
+
lex->token_line = 0;
|
167
184
|
lex->token_start = 0;
|
168
185
|
lex->token_length = 0;
|
169
186
|
lex->token_state = LEX_STATE_ROOT;
|
@@ -180,6 +197,22 @@ static void process_root(LexInfo * lex);
|
|
180
197
|
static void process_start_of_line(LexInfo * lex);
|
181
198
|
static void start_banner(LexInfo * lex);
|
182
199
|
|
200
|
+
static void find_start_of_line(LexInfo *lex, size_t from) {
|
201
|
+
size_t pos = from;
|
202
|
+
|
203
|
+
for (;;) {
|
204
|
+
if (IS_NEWLINE(lex->text[pos])) {
|
205
|
+
lex->start_of_line = pos + 1;
|
206
|
+
return;
|
207
|
+
} else if (pos <= 0) {
|
208
|
+
lex->start_of_line = 0;
|
209
|
+
return;
|
210
|
+
} else {
|
211
|
+
pos--;
|
212
|
+
}
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
183
216
|
static void process_newline(LexInfo *lex) {
|
184
217
|
delimit(lex);
|
185
218
|
|
@@ -189,6 +222,8 @@ static void process_newline(LexInfo *lex) {
|
|
189
222
|
lex->pos = lex->pos + 1;
|
190
223
|
lex->token_start = lex->pos;
|
191
224
|
lex->token_length = 0;
|
225
|
+
lex->token_line = 0;
|
226
|
+
lex->line = lex->line + 1;
|
192
227
|
return;
|
193
228
|
}
|
194
229
|
|
@@ -196,6 +231,7 @@ static void process_newline(LexInfo *lex) {
|
|
196
231
|
ADD_TOKEN(lex, ID2SYM(rb_intern("EOL")));
|
197
232
|
lex->token_state = LEX_STATE_INDENT;
|
198
233
|
lex->indent = 0;
|
234
|
+
lex->line = lex->line + 1;
|
199
235
|
}
|
200
236
|
|
201
237
|
static void process_space(LexInfo *lex) {
|
@@ -204,11 +240,23 @@ static void process_space(LexInfo *lex) {
|
|
204
240
|
|
205
241
|
static void process_comment(LexInfo *lex) {
|
206
242
|
char c = CURRENT_CHAR(lex);
|
243
|
+
int token_count = RARRAY_LEN(lex->tokens);
|
244
|
+
VALUE last_token, last_value;
|
245
|
+
|
246
|
+
if (0 < token_count) {
|
247
|
+
last_token = rb_ary_entry(lex->tokens, token_count - 1);
|
248
|
+
last_value = TOKEN_VALUE(last_token);
|
249
|
+
|
250
|
+
if (TYPE(last_value) != T_SYMBOL) {
|
251
|
+
ADD_TOKEN(lex, ID2SYM(rb_intern("EOL")));
|
252
|
+
}
|
253
|
+
}
|
207
254
|
|
208
255
|
if (IS_NEWLINE(c)) {
|
209
256
|
delimit(lex);
|
210
257
|
lex->token_state = LEX_STATE_INDENT;
|
211
258
|
lex->indent = 0;
|
259
|
+
lex->line = lex->line + 1;
|
212
260
|
}
|
213
261
|
}
|
214
262
|
|
@@ -269,7 +317,7 @@ static void process_integer(LexInfo *lex) {
|
|
269
317
|
}
|
270
318
|
|
271
319
|
static void process_certificate(LexInfo *lex) {
|
272
|
-
char quit[
|
320
|
+
char quit[6] = "quit\n";
|
273
321
|
|
274
322
|
strncpy(quit, &CURRENT_CHAR(lex) - 5, 5);
|
275
323
|
|
@@ -295,17 +343,31 @@ static void process_certificate(LexInfo *lex) {
|
|
295
343
|
lex->token_length = 0;
|
296
344
|
|
297
345
|
lex->token_start = lex->pos;
|
346
|
+
lex->line = lex->line + lex->token_line - 1;
|
347
|
+
find_start_of_line(lex, lex->pos);
|
298
348
|
ADD_TOKEN(lex, ID2SYM(rb_intern("CERTIFICATE_END")));
|
299
349
|
|
300
|
-
|
350
|
+
find_start_of_line(lex, lex->pos - 2);
|
351
|
+
lex->start_of_line++;
|
352
|
+
lex->token_start = lex->pos;
|
353
|
+
ADD_TOKEN(lex, ID2SYM(rb_intern("EOL")));
|
354
|
+
|
355
|
+
lex->token_state = LEX_STATE_INDENT;
|
356
|
+
lex->indent = 0;
|
357
|
+
lex->line = lex->line + 1;
|
358
|
+
|
301
359
|
process_start_of_line(lex);
|
302
360
|
} else {
|
361
|
+
if (IS_NEWLINE(CURRENT_CHAR(lex))) {
|
362
|
+
lex->token_line++;
|
363
|
+
}
|
303
364
|
lex->token_length++;
|
304
365
|
}
|
305
366
|
}
|
306
367
|
|
307
368
|
static void start_certificate(LexInfo *lex) {
|
308
369
|
lex->indent_pos--;
|
370
|
+
lex->token_line = 0;
|
309
371
|
rb_ary_pop(lex->tokens);
|
310
372
|
rb_ary_pop(lex->tokens);
|
311
373
|
ADD_TOKEN(lex, ID2SYM(rb_intern("CERTIFICATE_BEGIN")));
|
@@ -331,15 +393,22 @@ static void process_banner(LexInfo *lex) {
|
|
331
393
|
lex->token_length++;
|
332
394
|
delimit(lex);
|
333
395
|
lex->token_start = lex->pos;
|
396
|
+
lex->line = lex->line + lex->token_line;
|
397
|
+
find_start_of_line(lex, lex->pos);
|
334
398
|
ADD_TOKEN(lex, ID2SYM(rb_intern("BANNER_END")));
|
335
399
|
if (lex->text[lex->pos + 1] == 'C') { lex->pos++; }
|
336
400
|
} else if (!lex->banner_delimiter && is_banner_end_string(lex)) {
|
337
401
|
lex->token_length -= 1;
|
338
402
|
delimit(lex);
|
339
403
|
lex->token_start = lex->pos;
|
404
|
+
lex->line = lex->line + lex->token_line;
|
405
|
+
find_start_of_line(lex, lex->pos);
|
340
406
|
ADD_TOKEN(lex, ID2SYM(rb_intern("BANNER_END")));
|
341
407
|
} else {
|
342
|
-
|
408
|
+
if (IS_NEWLINE(lex->text[lex->pos + lex->token_length])) {
|
409
|
+
lex->token_line++;
|
410
|
+
}
|
411
|
+
lex->token_length++;
|
343
412
|
}
|
344
413
|
}
|
345
414
|
|
@@ -347,12 +416,17 @@ static void start_banner(LexInfo *lex) {
|
|
347
416
|
char c = CURRENT_CHAR(lex);
|
348
417
|
lex->banner_delimiter = (c == '\n') ? 0 : c;
|
349
418
|
ADD_TOKEN(lex, ID2SYM(rb_intern("BANNER_BEGIN")));
|
419
|
+
if ('\n' == lex->text[lex->pos + 1]) lex->line++;
|
350
420
|
if ('\n' == lex->text[lex->pos + 2]) lex->pos++;
|
351
421
|
}
|
352
422
|
|
353
423
|
static void process_start_of_line(LexInfo *lex) {
|
354
424
|
char c = CURRENT_CHAR(lex);
|
355
425
|
|
426
|
+
if (lex->indent == 0) {
|
427
|
+
lex->start_of_line = lex->pos;
|
428
|
+
}
|
429
|
+
|
356
430
|
if (IS_SPACE(c)) {
|
357
431
|
lex->indent++;
|
358
432
|
return;
|
@@ -487,7 +561,8 @@ static VALUE call(VALUE self, VALUE input_text) {
|
|
487
561
|
}
|
488
562
|
|
489
563
|
delimit(lex);
|
490
|
-
lex->token_start = lex->pos;
|
564
|
+
lex->token_start = lex->pos - 1;
|
565
|
+
lex->line = lex->line - 1;
|
491
566
|
|
492
567
|
for (; lex->indent_pos > 0; lex->indent_pos--) {
|
493
568
|
ADD_TOKEN(lex, ID2SYM(rb_intern("DEDENT")));
|
@@ -501,6 +576,7 @@ void Init_c_lexer() {
|
|
501
576
|
rb_cCLexer = rb_define_class_under(rb_mIOSParser, "CLexer", rb_cObject);
|
502
577
|
rb_eLexError = rb_define_class_under(rb_mIOSParser, "LexError",
|
503
578
|
rb_eStandardError);
|
579
|
+
rb_cToken = rb_path2class("IOSParser::Token");
|
504
580
|
rb_define_alloc_func(rb_cCLexer, allocate);
|
505
581
|
rb_define_method(rb_cCLexer, "initialize", initialize, 0);
|
506
582
|
rb_define_method(rb_cCLexer, "call", call, 1);
|
data/lib/ios_parser.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'json'
|
2
|
+
require 'ios_parser/token'
|
2
3
|
|
3
4
|
module IOSParser
|
4
5
|
class LexError < StandardError; end
|
@@ -6,11 +7,27 @@ module IOSParser
|
|
6
7
|
def self.lexer
|
7
8
|
if const_defined?(:PureLexer)
|
8
9
|
PureLexer
|
10
|
+
else
|
11
|
+
c_lexer
|
12
|
+
end
|
13
|
+
rescue LoadError
|
14
|
+
pure_lexer
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.c_lexer
|
18
|
+
if RUBY_VERSION < '2.1'
|
19
|
+
warn 'The C Lexer requires Ruby 2.1 or later. The pure Ruby lexer will '\
|
20
|
+
'be used instead. You can eliminate this warning by upgrading ruby '\
|
21
|
+
'or explicitly using the pure-Ruby lexer '\
|
22
|
+
"(require 'ios_parser/pure')"
|
23
|
+
pure_lexer
|
9
24
|
else
|
10
25
|
require_relative 'ios_parser/c_lexer'
|
11
26
|
CLexer
|
12
27
|
end
|
13
|
-
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.pure_lexer
|
14
31
|
require 'ios_parser/lexer'
|
15
32
|
PureLexer
|
16
33
|
end
|
data/lib/ios_parser/ios.rb
CHANGED
@@ -11,6 +11,7 @@ module IOSParser
|
|
11
11
|
@document = Document.new(nil)
|
12
12
|
@parent = parent
|
13
13
|
@lexer = lexer
|
14
|
+
@indent = 0
|
14
15
|
end
|
15
16
|
|
16
17
|
def tokens
|
@@ -29,40 +30,50 @@ module IOSParser
|
|
29
30
|
|
30
31
|
def section(parent = nil)
|
31
32
|
[].tap do |commands|
|
32
|
-
until tokens.empty? || tokens.first.
|
33
|
+
until tokens.empty? || tokens.first.value == :DEDENT
|
33
34
|
commands.push(command(parent, @document))
|
34
35
|
end
|
35
|
-
tokens.shift # discard :DEDENT
|
36
|
+
token = tokens.shift # discard :DEDENT
|
37
|
+
@indent -= 1 if token && token.value == :DEDENT
|
36
38
|
end
|
37
39
|
end
|
38
40
|
|
39
41
|
def command(parent = nil, document = nil)
|
40
|
-
|
41
|
-
|
42
|
+
opts = {
|
43
|
+
tokens: command_tokens,
|
44
|
+
parent: parent,
|
45
|
+
document: document,
|
46
|
+
indent: @indent
|
47
|
+
}
|
42
48
|
|
43
49
|
Command.new(opts).tap do |cmd|
|
44
50
|
cmd.commands = subsections(cmd)
|
45
51
|
end
|
46
52
|
end
|
47
53
|
|
54
|
+
def command_tokens
|
55
|
+
toks = []
|
56
|
+
until tokens.empty? || tokens.first.value == :EOL
|
57
|
+
tok = tokens.shift
|
58
|
+
toks << tok unless argument_to_discard?(tok.value)
|
59
|
+
end
|
60
|
+
tokens.shift # discard :EOL
|
61
|
+
toks
|
62
|
+
end
|
63
|
+
|
64
|
+
def argument_to_discard?(arg)
|
65
|
+
arguments_to_discard.include?(arg)
|
66
|
+
end
|
67
|
+
|
48
68
|
def arguments_to_discard
|
49
69
|
[:INDENT, :DEDENT,
|
50
70
|
:CERTIFICATE_BEGIN, :CERTIFICATE_END,
|
51
71
|
:BANNER_BEGIN, :BANNER_END]
|
52
72
|
end
|
53
73
|
|
54
|
-
def arguments
|
55
|
-
[].tap do |args|
|
56
|
-
until tokens.empty? || tokens.first.last == :EOL
|
57
|
-
_, arg = tokens.shift
|
58
|
-
args << arg unless arguments_to_discard.include?(arg)
|
59
|
-
end
|
60
|
-
tokens.shift # discard :EOL
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
74
|
def subsections(parent = nil)
|
65
|
-
if !tokens.empty? && tokens.first.
|
75
|
+
if !tokens.empty? && tokens.first.value == :INDENT
|
76
|
+
@indent += 1
|
66
77
|
tokens.shift # discard :INDENT
|
67
78
|
section(parent)
|
68
79
|
else
|
@@ -6,19 +6,22 @@ module IOSParser
|
|
6
6
|
class Command
|
7
7
|
include Enumerable
|
8
8
|
include Queryable
|
9
|
-
attr_accessor :
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
@args = args
|
9
|
+
attr_accessor :commands, :parent, :document, :indent, :tokens
|
10
|
+
def initialize(tokens: [], commands: [],
|
11
|
+
parent: nil, document: nil, indent: nil)
|
12
|
+
@tokens = tokens
|
14
13
|
@commands = commands
|
15
14
|
@parent = parent
|
16
|
-
@pos = pos
|
17
15
|
@document = document
|
16
|
+
@indent = indent || 0
|
17
|
+
end
|
18
|
+
|
19
|
+
def args
|
20
|
+
tokens.map(&:value)
|
18
21
|
end
|
19
22
|
|
20
23
|
def name
|
21
|
-
|
24
|
+
tokens.first.value
|
22
25
|
end
|
23
26
|
|
24
27
|
def ==(other)
|
@@ -37,6 +40,10 @@ module IOSParser
|
|
37
40
|
parent ? parent.path + [parent.line] : []
|
38
41
|
end
|
39
42
|
|
43
|
+
def pos
|
44
|
+
tokens.first && tokens.first.pos
|
45
|
+
end
|
46
|
+
|
40
47
|
def indentation(base: 0)
|
41
48
|
' ' * (path.length - base)
|
42
49
|
end
|
@@ -48,9 +55,10 @@ module IOSParser
|
|
48
55
|
|
49
56
|
def inspect
|
50
57
|
"<IOSParser::IOS::Command:0x#{object_id.to_s(16)} "\
|
51
|
-
"@
|
58
|
+
"@tokens=#{tokens.inspect}, "\
|
52
59
|
"@commands=#{commands.inspect}, "\
|
53
60
|
"@pos=#{pos.inspect}, "\
|
61
|
+
"@indent=#{indent}, "\
|
54
62
|
"@document=<IOSParser::IOS::Document:0x#{document.object_id.to_s(16)}>>"
|
55
63
|
end
|
56
64
|
|
@@ -63,7 +71,8 @@ module IOSParser
|
|
63
71
|
{
|
64
72
|
args: args,
|
65
73
|
commands: commands.map(&:to_hash),
|
66
|
-
pos: pos
|
74
|
+
pos: pos,
|
75
|
+
indent: indent
|
67
76
|
}
|
68
77
|
end
|
69
78
|
|
data/lib/ios_parser/lexer.rb
CHANGED
@@ -3,6 +3,7 @@ module IOSParser
|
|
3
3
|
LexError = IOSParser::LexError
|
4
4
|
|
5
5
|
attr_accessor :tokens, :token, :indents, :indent, :state, :char,
|
6
|
+
:line, :start_of_line, :token_line,
|
6
7
|
:string_terminator
|
7
8
|
|
8
9
|
def initialize
|
@@ -12,8 +13,10 @@ module IOSParser
|
|
12
13
|
@indent = 0
|
13
14
|
@indents = [0]
|
14
15
|
@state = :root
|
15
|
-
@
|
16
|
-
@
|
16
|
+
@this_char = -1
|
17
|
+
@line = 1
|
18
|
+
@start_of_line = 0
|
19
|
+
@token_line = 0
|
17
20
|
end
|
18
21
|
|
19
22
|
def call(input_text)
|
@@ -57,18 +60,37 @@ module IOSParser
|
|
57
60
|
end
|
58
61
|
end
|
59
62
|
|
60
|
-
def make_token(value, pos: nil)
|
63
|
+
def make_token(value, pos: nil, col: nil)
|
61
64
|
pos ||= @token_start || @this_char
|
65
|
+
col ||= pos - start_of_line + 1
|
62
66
|
@token_start = nil
|
63
|
-
|
67
|
+
Token.new(value, pos, line, col)
|
68
|
+
end
|
69
|
+
|
70
|
+
def find_start_of_line(from: @this_char)
|
71
|
+
from.downto(0) do |pos|
|
72
|
+
if @text[pos] == "\n"
|
73
|
+
self.start_of_line = pos + 1
|
74
|
+
return start_of_line
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
self.line_start = 0
|
64
79
|
end
|
65
80
|
|
66
81
|
def comment
|
67
82
|
self.state = :comment
|
68
|
-
|
83
|
+
tokens << make_token(:EOL) if tokens.last &&
|
84
|
+
!tokens.last.value.is_a?(Symbol)
|
85
|
+
comment_newline if newline?
|
86
|
+
end
|
87
|
+
|
88
|
+
def comment_newline
|
69
89
|
delimit
|
90
|
+
self.start_of_line = @this_char + 1
|
70
91
|
self.state = :line_start
|
71
92
|
self.indent = 0
|
93
|
+
self.line += 1
|
72
94
|
end
|
73
95
|
|
74
96
|
def comment?
|
@@ -81,16 +103,22 @@ module IOSParser
|
|
81
103
|
|
82
104
|
def banner_begin
|
83
105
|
self.state = :banner
|
106
|
+
self.token_line = 0
|
84
107
|
tokens << make_token(:BANNER_BEGIN)
|
85
108
|
@token_start = @this_char + 2
|
86
109
|
@banner_delimiter = char == "\n" ? 'EOF' : char
|
110
|
+
return unless @text[@this_char + 1] == "\n"
|
111
|
+
self.token_line -= 1
|
112
|
+
self.line += 1
|
87
113
|
end
|
88
114
|
|
89
115
|
def banner_begin?
|
90
|
-
tokens[-2] && tokens[-2].
|
116
|
+
tokens[-2] && tokens[-2].value == 'banner'
|
91
117
|
end
|
92
118
|
|
93
119
|
def banner
|
120
|
+
self.token_line += 1 if newline?
|
121
|
+
|
94
122
|
if banner_end_char?
|
95
123
|
banner_end_char
|
96
124
|
elsif banner_end_string?
|
@@ -103,7 +131,10 @@ module IOSParser
|
|
103
131
|
def banner_end_string
|
104
132
|
self.state = :root
|
105
133
|
token.chomp!(@banner_delimiter[0..-2])
|
106
|
-
tokens << make_token(token)
|
134
|
+
tokens << make_token(token)
|
135
|
+
self.line += token_line
|
136
|
+
find_start_of_line
|
137
|
+
tokens << make_token(:BANNER_END)
|
107
138
|
self.token = ''
|
108
139
|
end
|
109
140
|
|
@@ -114,7 +145,10 @@ module IOSParser
|
|
114
145
|
def banner_end_char
|
115
146
|
self.state = :root
|
116
147
|
banner_end_clean_token
|
117
|
-
tokens << make_token(token)
|
148
|
+
tokens << make_token(token)
|
149
|
+
self.line += token_line
|
150
|
+
find_start_of_line
|
151
|
+
tokens << make_token(:BANNER_END)
|
118
152
|
self.token = ''
|
119
153
|
end
|
120
154
|
|
@@ -136,42 +170,60 @@ module IOSParser
|
|
136
170
|
end
|
137
171
|
|
138
172
|
def banner_garbage?(pos)
|
139
|
-
tokens[pos].
|
173
|
+
tokens[pos].value == :BANNER_END && tokens[pos + 1].value == 'C'
|
140
174
|
end
|
141
175
|
|
142
176
|
def certificate_begin?
|
143
|
-
tokens[-6] && tokens[-6].
|
144
|
-
tokens[-5] && tokens[-5].
|
177
|
+
tokens[-6] && tokens[-6].value == :INDENT &&
|
178
|
+
tokens[-5] && tokens[-5].value == 'certificate'
|
145
179
|
end
|
146
180
|
|
147
181
|
def certificate_begin
|
148
182
|
self.state = :certificate
|
149
183
|
indents.pop
|
150
|
-
tokens[-2..-1] = [make_token(:CERTIFICATE_BEGIN, pos: tokens[-1]
|
184
|
+
tokens[-2..-1] = [make_token(:CERTIFICATE_BEGIN, pos: tokens[-1].pos)]
|
185
|
+
self.token_line = 0
|
151
186
|
certificate
|
152
187
|
end
|
153
188
|
|
154
189
|
def certificate
|
155
|
-
token
|
190
|
+
if token.end_with?("quit\n")
|
191
|
+
certificate_end
|
192
|
+
else
|
193
|
+
self.token_line += 1 if char == "\n"
|
194
|
+
token << char
|
195
|
+
end
|
156
196
|
end
|
157
197
|
|
158
198
|
def certificate_end
|
159
199
|
tokens.concat certificate_end_tokens
|
200
|
+
self.line += 1
|
160
201
|
update_indentation
|
161
202
|
@token_start = @this_char
|
162
203
|
|
163
204
|
@token = ''
|
164
205
|
self.state = :line_start
|
165
206
|
self.indent = 0
|
207
|
+
self.line += 1
|
166
208
|
root
|
167
209
|
end
|
168
210
|
|
211
|
+
# rubocop: disable AbcSize
|
169
212
|
def certificate_end_tokens
|
170
|
-
[
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
213
|
+
cluster = []
|
214
|
+
cluster << make_token(certificate_token_value, pos: tokens[-1].pos)
|
215
|
+
self.line += self.token_line - 1
|
216
|
+
cluster << make_token(:CERTIFICATE_END, pos: @this_char, col: 1)
|
217
|
+
find_start_of_line(from: @this_char - 2)
|
218
|
+
cluster << make_token(:EOL,
|
219
|
+
pos: @this_char,
|
220
|
+
col: @this_char - start_of_line)
|
221
|
+
cluster
|
222
|
+
end
|
223
|
+
# rubocop: enable AbcSize
|
224
|
+
|
225
|
+
def certificate_token_value
|
226
|
+
token[0..-6].gsub!(/\s+/, ' ').strip
|
175
227
|
end
|
176
228
|
|
177
229
|
def integer
|
@@ -237,7 +289,7 @@ module IOSParser
|
|
237
289
|
|
238
290
|
def space
|
239
291
|
delimit
|
240
|
-
self.indent += 1 if tokens.last && tokens.last.
|
292
|
+
self.indent += 1 if tokens.last && tokens.last.value == :EOL
|
241
293
|
end
|
242
294
|
|
243
295
|
def space?
|
@@ -268,6 +320,8 @@ module IOSParser
|
|
268
320
|
self.state = :line_start
|
269
321
|
self.indent = 0
|
270
322
|
tokens << make_token(:EOL)
|
323
|
+
self.start_of_line = @this_char + 1
|
324
|
+
self.line += 1
|
271
325
|
end
|
272
326
|
|
273
327
|
def newline?
|
@@ -304,7 +358,14 @@ module IOSParser
|
|
304
358
|
end
|
305
359
|
|
306
360
|
def pop_dedent
|
307
|
-
|
361
|
+
col =
|
362
|
+
if tokens.last.line == line
|
363
|
+
tokens.last.col
|
364
|
+
else
|
365
|
+
1
|
366
|
+
end
|
367
|
+
|
368
|
+
tokens << make_token(:DEDENT, col: col)
|
308
369
|
indents.pop
|
309
370
|
end
|
310
371
|
|
@@ -321,6 +382,7 @@ module IOSParser
|
|
321
382
|
end
|
322
383
|
|
323
384
|
delimit
|
385
|
+
self.line -= 1
|
324
386
|
update_indentation
|
325
387
|
scrub_banner_garbage
|
326
388
|
tokens
|
data/lib/ios_parser/version.rb
CHANGED
@@ -25,22 +25,26 @@ module IOSParser
|
|
25
25
|
commands: [{ args: ['police', 300_000_000, 1_000_000,
|
26
26
|
'exceed-action',
|
27
27
|
'policed-dscp-transmit'],
|
28
|
-
commands: [
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
commands: [
|
29
|
+
{ args: %w[set dscp cs1],
|
30
|
+
commands: [], pos: 114, indent: 3 }
|
31
|
+
],
|
32
|
+
pos: 50, indent: 2 }],
|
33
|
+
pos: 24, indent: 1 },
|
32
34
|
|
33
35
|
{ args: %w[class other_service],
|
34
36
|
commands: [{ args: ['police', 600_000_000, 1_000_000,
|
35
37
|
'exceed-action',
|
36
38
|
'policed-dscp-transmit'],
|
37
|
-
commands: [
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
commands: [
|
40
|
+
{ args: %w[set dscp cs2],
|
41
|
+
commands: [], pos: 214, indent: 3 },
|
42
|
+
{ args: ['command_with_no_args'],
|
43
|
+
commands: [], pos: 230, indent: 3 }
|
44
|
+
],
|
45
|
+
pos: 150, indent: 2 }],
|
46
|
+
pos: 128, indent: 1 }],
|
47
|
+
pos: 0, indent: 0 }]
|
44
48
|
}
|
45
49
|
end
|
46
50
|
|
@@ -59,9 +63,9 @@ module IOSParser
|
|
59
63
|
it('can be searched by an exact command') do
|
60
64
|
expect(subject.find_all(name: 'set').map(&:to_hash))
|
61
65
|
.to eq [{ args: %w[set dscp cs1],
|
62
|
-
commands: [], pos: 114 },
|
66
|
+
commands: [], pos: 114, indent: 3 },
|
63
67
|
{ args: %w[set dscp cs2],
|
64
|
-
commands: [], pos: 214 }]
|
68
|
+
commands: [], pos: 214, indent: 3 }]
|
65
69
|
end
|
66
70
|
|
67
71
|
context 'can be searched by name and the first argument' do
|
@@ -92,8 +96,8 @@ module IOSParser
|
|
92
96
|
[{ args: ['police', 300_000_000, 1_000_000, 'exceed-action',
|
93
97
|
'policed-dscp-transmit'],
|
94
98
|
commands: [{ args: %w[set dscp cs1],
|
95
|
-
commands: [], pos: 114 }],
|
96
|
-
pos: 50 }]
|
99
|
+
commands: [], pos: 114, indent: 3 }],
|
100
|
+
pos: 50, indent: 2 }]
|
97
101
|
end
|
98
102
|
|
99
103
|
context 'integer query' do
|
@@ -115,7 +119,7 @@ module IOSParser
|
|
115
119
|
.find('set')
|
116
120
|
.to_hash)
|
117
121
|
.to eq(args: %w[set dscp cs1],
|
118
|
-
commands: [], pos: 114)
|
122
|
+
commands: [], pos: 114, indent: 3)
|
119
123
|
end
|
120
124
|
end # context 'nested search'
|
121
125
|
|
@@ -252,15 +256,15 @@ module IOSParser
|
|
252
256
|
cmd_ary = [
|
253
257
|
{ args: ['ip', 'route', '10.0.0.1', '255.255.255.255',
|
254
258
|
'Null0'],
|
255
|
-
commands: [], pos: 0 },
|
259
|
+
commands: [], pos: 0, indent: 0 },
|
256
260
|
{ args: ['ip', 'route', '9.9.9.199', '255.255.255.255',
|
257
261
|
'42.42.42.142', 'name', 'PONIES'],
|
258
|
-
commands: [], pos: 40 },
|
262
|
+
commands: [], pos: 40, indent: 0 },
|
259
263
|
{ args: ['ip', 'route', 'vrf', 'Mgmt-intf', '0.0.0.0',
|
260
264
|
'0.0.0.0', '9.9.9.199'],
|
261
|
-
commands: [], pos: 100 },
|
265
|
+
commands: [], pos: 100, indent: 0 },
|
262
266
|
{ args: ['ip', 'route', '0.0.0.0/0', '11.11.0.111', 120],
|
263
|
-
commands: [], pos: 149 }
|
267
|
+
commands: [], pos: 149, indent: 0 }
|
264
268
|
]
|
265
269
|
|
266
270
|
expect(result.find_all('ip route').map(&:to_hash)).to eq(cmd_ary)
|
@@ -269,7 +273,7 @@ module IOSParser
|
|
269
273
|
|
270
274
|
cmd_hash = { args: ['ip', 'route', '9.9.9.199', '255.255.255.255',
|
271
275
|
'42.42.42.142', 'name', 'PONIES'],
|
272
|
-
commands: [], pos: 40 }
|
276
|
+
commands: [], pos: 40, indent: 0 }
|
273
277
|
expect(result.find('ip route 9.9.9.199').to_hash).to eq(cmd_hash)
|
274
278
|
end # end context '#call'
|
275
279
|
|
@@ -40,11 +40,11 @@ END
|
|
40
40
|
'set', 'dscp', 'cs2', :EOL, :DEDENT, :DEDENT, :DEDENT]
|
41
41
|
end
|
42
42
|
|
43
|
-
subject { klass.new.call(input).map(&:
|
43
|
+
subject { klass.new.call(input).map(&:value) }
|
44
44
|
it('enclosed in symbols') { should == output }
|
45
45
|
|
46
46
|
it('enclosed in symbols (using the pure ruby lexer)') do
|
47
|
-
expect(subject_pure.map(&:
|
47
|
+
expect(subject_pure.map(&:value)).to eq output
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
@@ -75,12 +75,12 @@ END
|
|
75
75
|
|
76
76
|
it 'pure' do
|
77
77
|
tokens = IOSParser::PureLexer.new.call(input)
|
78
|
-
expect(tokens.map(&:
|
78
|
+
expect(tokens.map(&:value)).to eq expectation
|
79
79
|
end # it 'pure' do
|
80
80
|
|
81
81
|
it 'default' do
|
82
82
|
tokens = IOSParser.lexer.new.call(input)
|
83
|
-
expect(tokens.map(&:
|
83
|
+
expect(tokens.map(&:value)).to eq expectation
|
84
84
|
end # it 'c' do
|
85
85
|
end # context 'indented region' do
|
86
86
|
end # context 'ASR indented regions' do
|
@@ -97,9 +97,11 @@ END
|
|
97
97
|
end
|
98
98
|
|
99
99
|
let(:output) do
|
100
|
-
[[0, 'banner'], [7,
|
101
|
-
[
|
102
|
-
[
|
100
|
+
[[0, 1, 1, 'banner'], [7, 1, 8, 'foobar'],
|
101
|
+
[14, 1, 15, :BANNER_BEGIN],
|
102
|
+
[16, 2, 17, "asdf 1234 9786 asdf\nline 2\nline 3\n "],
|
103
|
+
[52, 5, 3, :BANNER_END], [53, 5, 4, :EOL]]
|
104
|
+
.map { |pos, line, col, val| Token.new(val, pos, line, col) }
|
103
105
|
end
|
104
106
|
|
105
107
|
it('tokenized and enclosed in symbols') { should == output }
|
@@ -119,8 +121,8 @@ END
|
|
119
121
|
['banner', 'exec', :BANNER_BEGIN, content, :BANNER_END, :EOL]
|
120
122
|
end
|
121
123
|
|
122
|
-
it { expect(subject.map(&:
|
123
|
-
it { expect(subject_pure.map(&:
|
124
|
+
it { expect(subject.map(&:value)).to eq output }
|
125
|
+
it { expect(subject_pure.map(&:value)).to eq output }
|
124
126
|
end
|
125
127
|
|
126
128
|
context 'complex eos banner' do
|
@@ -131,14 +133,14 @@ END
|
|
131
133
|
['banner', 'motd', :BANNER_BEGIN, content, :BANNER_END, :EOL]
|
132
134
|
end
|
133
135
|
|
134
|
-
it { expect(subject.map(&:
|
135
|
-
it { expect(subject_pure.map(&:
|
136
|
+
it { expect(subject.map(&:value)).to eq output }
|
137
|
+
it { expect(subject_pure.map(&:value)).to eq output }
|
136
138
|
end
|
137
139
|
|
138
140
|
context 'decimal number' do
|
139
141
|
let(:input) { 'boson levels at 93.2' }
|
140
142
|
let(:output) { ['boson', 'levels', 'at', 93.2] }
|
141
|
-
subject { klass.new.call(input).map(&:
|
143
|
+
subject { klass.new.call(input).map(&:value) }
|
142
144
|
it('converts to Float') { should == output }
|
143
145
|
end
|
144
146
|
|
@@ -156,29 +158,33 @@ END
|
|
156
158
|
end
|
157
159
|
|
158
160
|
let(:output) do
|
159
|
-
[[0, 'crypto'],
|
160
|
-
[7, 'pki'],
|
161
|
-
[11, 'certificate'],
|
162
|
-
[23, 'chain'],
|
163
|
-
[29, 'TP-self-signed-0123456789'],
|
164
|
-
[54, :EOL],
|
165
|
-
[56, :INDENT],
|
166
|
-
[56, 'certificate'],
|
167
|
-
[68, 'self-signed'],
|
168
|
-
[80, '01'],
|
169
|
-
[85, :CERTIFICATE_BEGIN],
|
170
|
-
[85,
|
161
|
+
[[0, 1, 1, 'crypto'],
|
162
|
+
[7, 1, 8, 'pki'],
|
163
|
+
[11, 1, 12, 'certificate'],
|
164
|
+
[23, 1, 24, 'chain'],
|
165
|
+
[29, 1, 30, 'TP-self-signed-0123456789'],
|
166
|
+
[54, 1, 55, :EOL],
|
167
|
+
[56, 2, 2, :INDENT],
|
168
|
+
[56, 2, 2, 'certificate'],
|
169
|
+
[68, 2, 14, 'self-signed'],
|
170
|
+
[80, 2, 26, '01'],
|
171
|
+
[85, 3, 3, :CERTIFICATE_BEGIN],
|
172
|
+
[85, 3, 3,
|
171
173
|
'FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF '\
|
172
174
|
'FFFFFFFF EEEEEEEE EEEEEEEE EEEEEEEE EEEEEEEE EEEEEEEE EEEEEEEE '\
|
173
175
|
'EEEEEEEE EEEEEEEE DDDDDDDD DDDDDDDD DDDDDDDD DDDDDDDD DDDDDDDD '\
|
174
176
|
'DDDDDDDD DDDDDDDD DDDDDDDD AAAA'],
|
175
|
-
[323, :CERTIFICATE_END],
|
176
|
-
[323, :EOL],
|
177
|
-
[323, :DEDENT]]
|
177
|
+
[323, 6, 1, :CERTIFICATE_END],
|
178
|
+
[323, 6, 13, :EOL],
|
179
|
+
[323, 7, 1, :DEDENT]]
|
180
|
+
.map { |pos, line, col, val| Token.new(val, pos, line, col) }
|
178
181
|
end
|
179
182
|
|
180
183
|
subject { klass.new.call(input) }
|
181
|
-
|
184
|
+
|
185
|
+
it('tokenized') do
|
186
|
+
expect(subject).to eq output
|
187
|
+
end
|
182
188
|
|
183
189
|
it('tokenized (using the pure ruby lexer)') do
|
184
190
|
expect(subject_pure).to eq output
|
@@ -187,8 +193,8 @@ END
|
|
187
193
|
|
188
194
|
context 'comments' do
|
189
195
|
let(:input) { 'ip addr 127.0.0.0.1 ! asdfsdf' }
|
190
|
-
let(:output) { ['ip', 'addr', '127.0.0.0.1'] }
|
191
|
-
subject { klass.new.call(input).map(&:
|
196
|
+
let(:output) { ['ip', 'addr', '127.0.0.0.1', :EOL] }
|
197
|
+
subject { klass.new.call(input).map(&:value) }
|
192
198
|
it('dropped') { should == output }
|
193
199
|
end
|
194
200
|
|
@@ -211,20 +217,20 @@ END
|
|
211
217
|
]
|
212
218
|
end
|
213
219
|
|
214
|
-
it { expect(subject_pure.map(&:
|
215
|
-
it { expect(subject.map(&:
|
220
|
+
it { expect(subject_pure.map(&:value)).to eq output }
|
221
|
+
it { expect(subject.map(&:value)).to eq output }
|
216
222
|
end # context 'quoted octothorpe' do
|
217
223
|
|
218
224
|
context 'vlan range' do
|
219
225
|
let(:input) { 'switchport trunk allowed vlan 50-90' }
|
220
226
|
let(:output) do
|
221
227
|
[
|
222
|
-
[0, 'switchport'],
|
223
|
-
[11, 'trunk'],
|
224
|
-
[17, 'allowed'],
|
225
|
-
[25, 'vlan'],
|
226
|
-
[30, '50-90']
|
227
|
-
]
|
228
|
+
[0, 1, 1, 'switchport'],
|
229
|
+
[11, 1, 12, 'trunk'],
|
230
|
+
[17, 1, 18, 'allowed'],
|
231
|
+
[25, 1, 26, 'vlan'],
|
232
|
+
[30, 1, 31, '50-90']
|
233
|
+
].map { |pos, line, col, val| Token.new(val, pos, line, col) }
|
228
234
|
end
|
229
235
|
it { should == output }
|
230
236
|
end # context 'vlan range' do
|
@@ -247,31 +253,31 @@ END
|
|
247
253
|
]
|
248
254
|
end
|
249
255
|
|
250
|
-
it { expect(subject_pure.map(&:
|
256
|
+
it { expect(subject_pure.map(&:value)).to eq output }
|
251
257
|
end
|
252
258
|
|
253
259
|
context '# in the middle of a line is not a comment' do
|
254
260
|
let(:input) { "vlan 1\n name #31337" }
|
255
261
|
let(:output) { ['vlan', 1, :EOL, :INDENT, 'name', '#31337', :DEDENT] }
|
256
262
|
|
257
|
-
it { expect(subject_pure.map(&:
|
258
|
-
it { expect(subject.map(&:
|
263
|
+
it { expect(subject_pure.map(&:value)).to eq output }
|
264
|
+
it { expect(subject.map(&:value)).to eq output }
|
259
265
|
end
|
260
266
|
|
261
267
|
context '# at the start of a line is a comment' do
|
262
268
|
let(:input) { "vlan 1\n# comment\nvlan 2" }
|
263
269
|
let(:output) { ['vlan', 1, :EOL, 'vlan', 2] }
|
264
270
|
|
265
|
-
it { expect(subject_pure.map(&:
|
266
|
-
it { expect(subject.map(&:
|
271
|
+
it { expect(subject_pure.map(&:value)).to eq output }
|
272
|
+
it { expect(subject.map(&:value)).to eq output }
|
267
273
|
end
|
268
274
|
|
269
275
|
context '# after indentation is a comment' do
|
270
276
|
let(:input) { "vlan 1\n # comment\nvlan 2" }
|
271
277
|
let(:output) { ['vlan', 1, :EOL, :INDENT, :DEDENT, 'vlan', 2] }
|
272
278
|
|
273
|
-
it { expect(subject_pure.map(&:
|
274
|
-
it { expect(subject.map(&:
|
279
|
+
it { expect(subject_pure.map(&:value)).to eq output }
|
280
|
+
it { expect(subject.map(&:value)).to eq output }
|
275
281
|
end
|
276
282
|
|
277
283
|
context 'unterminated quoted string' do
|
@@ -297,23 +303,59 @@ END
|
|
297
303
|
end
|
298
304
|
|
299
305
|
let(:expected) do
|
306
|
+
expected_full.map(&:value)
|
307
|
+
end
|
308
|
+
|
309
|
+
let(:expected_full) do
|
300
310
|
[
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
311
|
+
[0, 1, 1, 'router'],
|
312
|
+
[7, 1, 8, 'static'],
|
313
|
+
[13, 1, 14, :EOL],
|
314
|
+
[15, 2, 2, :INDENT],
|
315
|
+
[15, 2, 2, 'address-family'],
|
316
|
+
[30, 2, 17, 'ipv4'],
|
317
|
+
[35, 2, 22, 'unicast'],
|
318
|
+
[42, 2, 29, :EOL],
|
319
|
+
[47, 4, 2, 'address-family'],
|
320
|
+
[62, 4, 17, 'ipv6'],
|
321
|
+
[67, 4, 22, 'unicast'],
|
322
|
+
[74, 4, 29, :EOL],
|
323
|
+
[74, 4, 29, :DEDENT]
|
324
|
+
].map { |pos, line, col, val| Token.new(val, pos, line, col) }
|
307
325
|
end
|
308
326
|
|
309
327
|
it 'lexes both subcommands' do
|
310
|
-
expect(subject.map(&:
|
328
|
+
expect(subject.map(&:value)).to eq expected
|
311
329
|
end
|
312
330
|
|
313
331
|
it 'lexes both subcommands (with the pure ruby lexer)' do
|
314
|
-
expect(subject_pure.map(&:
|
332
|
+
expect(subject_pure.map(&:value)).to eq expected
|
333
|
+
end
|
334
|
+
|
335
|
+
it 'lexes position, line, and column' do
|
336
|
+
expect(subject).to eq expected_full
|
337
|
+
end
|
338
|
+
|
339
|
+
it 'lexes position, line, and column (with the pure ruby lexer)' do
|
340
|
+
expect(subject_pure).to eq expected_full
|
315
341
|
end
|
316
342
|
end
|
343
|
+
|
344
|
+
context 'comment at end of line' do
|
345
|
+
let(:input) do
|
346
|
+
<<-END.unindent
|
347
|
+
description !
|
348
|
+
switchport access vlan 2
|
349
|
+
END
|
350
|
+
end
|
351
|
+
|
352
|
+
let(:output) do
|
353
|
+
['description', :EOL, 'switchport', 'access', 'vlan', 2, :EOL]
|
354
|
+
end
|
355
|
+
|
356
|
+
it { expect(subject_pure.map(&:value)).to eq output }
|
357
|
+
it { expect(subject.map(&:value)).to eq output }
|
358
|
+
end # context 'comment at end of line' do
|
317
359
|
end
|
318
360
|
end
|
319
361
|
end
|
data/spec/lib/ios_parser_spec.rb
CHANGED
@@ -24,22 +24,26 @@ describe IOSParser do
|
|
24
24
|
commands: [{ args: ['police', 300_000_000, 1_000_000,
|
25
25
|
'exceed-action',
|
26
26
|
'policed-dscp-transmit'],
|
27
|
-
commands: [
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
commands: [
|
28
|
+
{ args: %w[set dscp cs1],
|
29
|
+
commands: [], pos: 114, indent: 3 }
|
30
|
+
],
|
31
|
+
pos: 50, indent: 2 }],
|
32
|
+
pos: 24, indent: 1 },
|
31
33
|
|
32
34
|
{ args: %w[class other_service],
|
33
35
|
commands: [{ args: ['police', 600_000_000, 1_000_000,
|
34
36
|
'exceed-action',
|
35
37
|
'policed-dscp-transmit'],
|
36
|
-
commands: [
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
commands: [
|
39
|
+
{ args: %w[set dscp cs2],
|
40
|
+
commands: [], pos: 214, indent: 3 },
|
41
|
+
{ args: ['command_with_no_args'],
|
42
|
+
commands: [], pos: 230, indent: 3 }
|
43
|
+
],
|
44
|
+
pos: 150, indent: 2 }],
|
45
|
+
pos: 128, indent: 1 }],
|
46
|
+
pos: 0, indent: 0 }]
|
43
47
|
}
|
44
48
|
end
|
45
49
|
|
@@ -70,15 +74,18 @@ describe IOSParser do
|
|
70
74
|
{
|
71
75
|
args: %w[description blah blah blah],
|
72
76
|
commands: [],
|
73
|
-
pos: 29
|
77
|
+
pos: 29,
|
78
|
+
indent: 1
|
74
79
|
},
|
75
80
|
{
|
76
81
|
args: ['match', 'access-group', 'fred'],
|
77
82
|
commands: [],
|
78
|
-
pos: 57
|
83
|
+
pos: 57,
|
84
|
+
indent: 1
|
79
85
|
}
|
80
86
|
],
|
81
|
-
pos: 0
|
87
|
+
pos: 0,
|
88
|
+
indent: 0
|
82
89
|
}
|
83
90
|
]
|
84
91
|
}
|
@@ -92,5 +99,22 @@ describe IOSParser do
|
|
92
99
|
expect(actual).to eq(output)
|
93
100
|
end
|
94
101
|
end # context "partial outdent" do
|
102
|
+
|
103
|
+
context 'comment at end of line' do
|
104
|
+
let(:input) do
|
105
|
+
<<END.unindent
|
106
|
+
description !
|
107
|
+
switchport access vlan 2
|
108
|
+
END
|
109
|
+
end
|
110
|
+
|
111
|
+
subject { described_class.parse(input) }
|
112
|
+
|
113
|
+
it 'parses both commands' do
|
114
|
+
should be_a IOSParser::IOS::Document
|
115
|
+
expect(subject.find(starts_with: 'description')).not_to be_nil
|
116
|
+
expect(subject.find(starts_with: 'switchport')).not_to be_nil
|
117
|
+
end
|
118
|
+
end
|
95
119
|
end # describe '.parse'
|
96
120
|
end # describe IOSParser
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ios_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Miller
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-01-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -84,6 +84,7 @@ files:
|
|
84
84
|
- lib/ios_parser/ios/queryable.rb
|
85
85
|
- lib/ios_parser/lexer.rb
|
86
86
|
- lib/ios_parser/pure.rb
|
87
|
+
- lib/ios_parser/token.rb
|
87
88
|
- lib/ios_parser/version.rb
|
88
89
|
- spec/lib/ios_parser/ios/queryable_spec.rb
|
89
90
|
- spec/lib/ios_parser/ios_spec.rb
|