ios_parser 0.5.2 → 0.6.0
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 +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
|