netsnmp 0.2.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "mib/parser"
4
+
5
+ module NETSNMP
6
+ module MIB
7
+ using IsNumericExtensions
8
+
9
+ OIDREGEX = /^[\d\.]*$/
10
+
11
+ module_function
12
+
13
+ MIBDIRS = ENV.fetch("MIBDIRS", File.join("/usr", "share", "snmp", "mibs")).split(":")
14
+ PARSER = Parser.new
15
+ @parser_mutex = Mutex.new
16
+ @modules_loaded = []
17
+ @object_identifiers = {}
18
+
19
+ # Translates na identifier, such as "sysDescr", into an OID
20
+ def oid(identifier)
21
+ prefix, *suffix = case identifier
22
+ when Array
23
+ identifier
24
+ else
25
+ identifier.split(".", 2)
26
+ end
27
+
28
+ # early exit if it's an OID already
29
+ unless prefix.integer?
30
+ load_defaults
31
+ # load module if need be
32
+ idx = prefix.index("::")
33
+ if idx
34
+ mod = prefix[0..(idx - 1)]
35
+ type = prefix[(idx + 2)..-1]
36
+ unless module_loaded?(mod)
37
+ return unless load(mod)
38
+ end
39
+ else
40
+ type = prefix
41
+ end
42
+
43
+ return if type.nil? || type.empty?
44
+
45
+ prefix = @object_identifiers[type] ||
46
+ raise(Error, "can't convert #{type} to OID")
47
+
48
+ end
49
+
50
+ [prefix, *suffix].join(".")
51
+ end
52
+
53
+ def identifier(oid)
54
+ @object_identifiers.select do |_, ids_oid|
55
+ oid.start_with?(ids_oid)
56
+ end.sort_by(&:size).first
57
+ end
58
+
59
+ #
60
+ # Loads a MIB. Can be called multiple times, as it'll load it once.
61
+ #
62
+ # Accepts the MIB name in several ways:
63
+ #
64
+ # MIB.load("SNMPv2-MIB")
65
+ # MIB.load("SNMPv2-MIB.txt")
66
+ # MIB.load("/path/to/SNMPv2-MIB.txt")
67
+ #
68
+ def load(mod)
69
+ unless File.file?(mod)
70
+ moddir = nil
71
+ MIBDIRS.each do |mibdir|
72
+ if File.exist?(File.join(mibdir, mod))
73
+ moddir = File.join(mibdir, mod)
74
+ break
75
+ elsif File.extname(mod).empty? && File.exist?(File.join(mibdir, "#{mod}.txt"))
76
+ moddir = File.join(mibdir, "#{mod}.txt")
77
+ break
78
+ end
79
+ end
80
+ return false unless moddir
81
+ mod = moddir
82
+ end
83
+ return true if @modules_loaded.include?(mod)
84
+ do_load(mod)
85
+ @modules_loaded << mod
86
+ true
87
+ end
88
+
89
+ def module_loaded?(mod)
90
+ if File.file?(mod)
91
+ @modules_loaded.include?(mod)
92
+ else
93
+ @modules_loaded.map { |path| File.basename(path, ".*") }.include?(mod)
94
+ end
95
+ end
96
+
97
+ TYPES = ["OBJECT IDENTIFIER", "OBJECT-TYPE", "MODULE-IDENTITY"].freeze
98
+
99
+ STATIC_MIB_TO_OID = {
100
+ "iso" => "1"
101
+ }.freeze
102
+
103
+ #
104
+ # Loads the MIB all the time, where +mod+ is the absolute path to the MIB.
105
+ #
106
+ def do_load(mod)
107
+ data = @parser_mutex.synchronize { PARSER.parse(File.read(mod)) }
108
+
109
+ imports = load_imports(data[:imports])
110
+
111
+ declarations = Hash[
112
+ data[:declarations].reject { |dec| !dec.key?(:name) || !TYPES.include?(dec[:type]) }
113
+ .map { |dec| [String(dec[:name]), String(dec[:value]).split(/ +/)] }
114
+ ]
115
+
116
+ declarations.each do |nme, value|
117
+ store_oid_in_identifiers(nme, value, imports: imports, declarations: declarations)
118
+ end
119
+ end
120
+
121
+ def store_oid_in_identifiers(nme, value, imports:, declarations:)
122
+ oid = value.flat_map do |cp|
123
+ if cp.integer?
124
+ cp
125
+ elsif @object_identifiers.key?(cp)
126
+ @object_identifiers[cp]
127
+ elsif declarations.key?(cp)
128
+ store_oid_in_identifiers(cp, declarations[cp], imports: imports, declarations: declarations)
129
+ @object_identifiers[cp]
130
+ else
131
+ STATIC_MIB_TO_OID[cp] || begin
132
+ imported_mod, = imports.find do |_, identifiers|
133
+ identifiers.include?(cp)
134
+ end
135
+
136
+ raise Error, "didn't find a module to import \"#{cp}\" from" unless imported_mod
137
+
138
+ load(imported_mod)
139
+
140
+ @object_identifiers[cp]
141
+ end
142
+ end
143
+ end.join(".")
144
+
145
+ @object_identifiers[nme] = oid
146
+ end
147
+
148
+ #
149
+ # Reformats the import lists into an hash indexed by module name, to a list of
150
+ # imported names
151
+ #
152
+ def load_imports(imports)
153
+ return unless imports
154
+
155
+ imports = [imports] unless imports.respond_to?(:to_ary)
156
+ imports.each_with_object({}) do |import, imp|
157
+ imp[String(import[:name])] = case import[:ids]
158
+ when Hash
159
+ [String(import[:ids][:name])]
160
+ else
161
+ import[:ids].map { |id| String(id[:name]) }
162
+ end
163
+ end
164
+ end
165
+
166
+ def load_defaults
167
+ # loading the defaults MIBS
168
+ load("SNMPv2-MIB")
169
+ load("IF-MIB")
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,750 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parslet"
4
+
5
+ module NETSNMP::MIB
6
+ class Parser < Parslet::Parser
7
+ root :mibfile
8
+
9
+ def spaced(character = nil)
10
+ if character.nil? && block_given?
11
+ yield >> space.repeat
12
+ else
13
+ str(character) >> space.repeat
14
+ end
15
+ end
16
+
17
+ def curly(atom)
18
+ str("{") >> space.repeat >> atom >> space.repeat >> str("}")
19
+ end
20
+
21
+ def bracketed(atom)
22
+ str("(") >> space.repeat >> atom >> space.repeat >> str(")")
23
+ end
24
+
25
+ def square_bracketed(atom)
26
+ str("[") >> space.repeat >> atom >> space.repeat >> str("]")
27
+ end
28
+
29
+ def with_separator(atom, separator = nil)
30
+ if separator
31
+ sep = if separator.is_a?(String)
32
+ space.repeat >> str(separator) >> space.repeat
33
+ else
34
+ separator
35
+ end
36
+
37
+ atom >> (sep >> atom).repeat
38
+ else
39
+ atom >> (space.repeat >> atom).repeat
40
+ end
41
+ end
42
+
43
+ rule(:mibfile) do
44
+ space.repeat >> modules.maybe
45
+ end
46
+
47
+ rule(:modules) do
48
+ with_separator(mod)
49
+ end
50
+
51
+ rule(:mod) do
52
+ spaced { module_name.as(:name) } >> module_oid >>
53
+ spaced("DEFINITIONS") >> colon_colon_part >>
54
+ spaced("BEGIN") >>
55
+ exports_part.as(:exports) >>
56
+ linkage_part.as(:imports) >>
57
+ declaration_part.as(:declarations) >>
58
+ spaced("END")
59
+ end
60
+
61
+ rule(:module_name) { uppercase_identifier }
62
+
63
+ rule(:module_oid) do
64
+ spaced { curly(object_identifier) }.maybe
65
+ end
66
+
67
+ rule(:declaration_part) do
68
+ spaced { declarations }.maybe
69
+ end
70
+
71
+ rule(:exports_part) do
72
+ spaced { exports_clause }.maybe
73
+ end
74
+
75
+ rule(:exports_clause) do
76
+ spaced("EXPORTS") >> spaced { import_identifiers } >> str(";")
77
+ end
78
+
79
+ rule(:linkage_part) do
80
+ spaced { linkage_clause }.maybe
81
+ end
82
+
83
+ rule(:linkage_clause) do
84
+ spaced("IMPORTS") >> spaced { import_part } >> str(";")
85
+ end
86
+
87
+ rule(:import_part) do
88
+ imports.maybe
89
+ end
90
+
91
+ rule(:imports) { with_separator(import) }
92
+
93
+ rule(:import) do
94
+ spaced { import_identifiers.as(:ids) } >> spaced("FROM") >> module_name.as(:name)
95
+ end
96
+
97
+ rule(:import_identifiers) { with_separator(import_identifier.as(:name), ",") }
98
+
99
+ rule(:import_identifier) do
100
+ lowercase_identifier | uppercase_identifier | imported_keyword
101
+ end
102
+
103
+ rule(:imported_keyword) do
104
+ imported_smi_keyword |
105
+ str("BITS") |
106
+ str("Integer32") |
107
+ str("IpAddress") |
108
+ str("MANDATORY-GROUPS") |
109
+ str("MODULE-COMPLIANCE") |
110
+ str("MODULE-IDENTITY") |
111
+ str("OBJECT-GROUP") |
112
+ str("OBJECT-IDENTITY") |
113
+ str("OBJECT-TYPE") |
114
+ str("Opaque") |
115
+ str("TEXTUAL-CONVENTION") |
116
+ str("TimeTicks") |
117
+ str("Unsigned32")
118
+ end
119
+
120
+ rule(:imported_smi_keyword) do
121
+ str("AGENT-CAPABILITIES") |
122
+ str("Counter32") |
123
+ str("Counter64") |
124
+ str("Gauge32") |
125
+ str("NOTIFICATION-GROUP") |
126
+ str("NOTIFICATION-TYPE") |
127
+ str("TRAP-TYPE")
128
+ end
129
+
130
+ rule(:declarations) do
131
+ with_separator(declaration)
132
+ end
133
+
134
+ rule(:declaration) do
135
+ type_declaration |
136
+ value_declaration |
137
+ object_identity_clause |
138
+ object_type_clause |
139
+ traptype_clause |
140
+ notification_type_clause |
141
+ module_identity_clause |
142
+ module_compliance_clause |
143
+ object_group_clause |
144
+ notification_group_clause |
145
+ agent_capabilities_clause |
146
+ macro_clause
147
+ end
148
+
149
+ rule(:macro_clause) do
150
+ spaced { macro_name.as(:name) } >> spaced { str("MACRO").as(:type) } >> colon_colon_part >>
151
+ spaced("BEGIN") >>
152
+ # ignoring macro clauses
153
+ match("^(?!END)").repeat >>
154
+ spaced("END")
155
+ end
156
+
157
+ rule(:macro_name) do
158
+ str("MODULE-IDENTITY") |
159
+ str("OBJECT-TYPE") |
160
+ str("TRAP-TYPE") |
161
+ str("NOTIFICATION-TYPE") |
162
+ str("OBJECT-IDENTITY") |
163
+ str("TEXTUAL-CONVENTION") |
164
+ str("OBJECT-GROUP") |
165
+ str("NOTIFICATION-GROUP") |
166
+ str("MODULE-COMPLIANCE") |
167
+ str("AGENT-CAPABILITIES")
168
+ end
169
+
170
+ rule(:agent_capabilities_clause) do
171
+ spaced { lowercase_identifier.as(:name) } >>
172
+ spaced { str("AGENT-CAPABILITIES").as(:type) } >>
173
+ spaced("PRODUCT-RELEASE") >> spaced { text } >>
174
+ spaced("STATUS") >> spaced { status } >>
175
+ spaced("DESCRIPTION") >> spaced { text } >>
176
+ spaced { refer_part }.maybe >>
177
+ spaced { module_part_capabilities }.maybe >>
178
+ colon_colon_part >> curly(object_identifier)
179
+ end
180
+
181
+ rule(:module_part_capabilities) do
182
+ modules_capabilities
183
+ end
184
+
185
+ rule(:modules_capabilities) do
186
+ with_separator(module_capabilities)
187
+ end
188
+
189
+ rule(:module_capabilities) do
190
+ spaced("SUPPORTS") >>
191
+ module_name_capabilities >>
192
+ spaced("INCLUDES") >> curly(capabilities_groups) >>
193
+ spaced { variation_part }.maybe
194
+ end
195
+
196
+ rule(:module_name_capabilities) do
197
+ spaced { uppercase_identifier } >> object_identifier | uppercase_identifier
198
+ end
199
+
200
+ rule(:capabilities_groups) do
201
+ with_separator(capabilities_group)
202
+ end
203
+
204
+ rule(:capabilities_group) { objectIdentifier }
205
+
206
+ rule(:variation_part) do
207
+ variations
208
+ end
209
+
210
+ rule(:variations) do
211
+ with_separator(variation)
212
+ end
213
+
214
+ rule(:variation) do
215
+ spaced("VARIATION") >> object_identifier >>
216
+ spaced { syntax_part }.maybe >>
217
+ spaced { write_syntax_part } >>
218
+ spaced { variation_access_part }.maybe >>
219
+ spaced { creation_part }.maybe >>
220
+ spaced { def_val_part }.maybe >>
221
+ spaced("DESCRIPTION") >> text
222
+ end
223
+
224
+ rule(:variation_access_part) do
225
+ spaced("ACCESS") >> variation_access
226
+ end
227
+
228
+ rule(:variation_access) { lowercase_identifier }
229
+
230
+ rule(:creation_part) do
231
+ spaced("CREATION-REQUIRES") >> curly(cells)
232
+ end
233
+
234
+ rule(:cells) { with_separator(cell, ",") }
235
+
236
+ rule(:cell) { object_identifier }
237
+
238
+ rule(:notification_group_clause) do
239
+ spaced { lowercase_identifier.as(:name) } >>
240
+ spaced { str("NOTIFICATION-GROUP").as(:type) } >>
241
+ spaced { notifications_part } >>
242
+ spaced("STATUS") >> spaced { status } >>
243
+ spaced("DESCRIPTION") >> spaced { text } >>
244
+ spaced { refer_part }.maybe >>
245
+ colon_colon_part >> curly(object_identifier)
246
+ end
247
+
248
+ rule(:notifications_part) do
249
+ spaced("NOTIFICATIONS") >> curly(notifications)
250
+ end
251
+
252
+ rule(:notifications) do
253
+ with_separator(notification, ",")
254
+ end
255
+
256
+ rule(:notification) do
257
+ notification_name
258
+ end
259
+
260
+ rule(:object_group_clause) do
261
+ spaced { lowercase_identifier.as(:name) } >>
262
+ spaced { str("OBJECT-GROUP").as(:type) } >>
263
+ spaced { object_group_objects_part } >>
264
+ spaced("STATUS") >> spaced { status } >>
265
+ spaced("DESCRIPTION") >> spaced { text } >>
266
+ spaced { refer_part }.maybe >>
267
+ colon_colon_part >> curly(object_identifier)
268
+ end
269
+
270
+ rule(:object_group_objects_part) do
271
+ spaced("OBJECTS") >> curly(objects)
272
+ end
273
+
274
+ rule(:module_compliance_clause) do
275
+ spaced { lowercase_identifier.as(:name) } >>
276
+ spaced { str("MODULE-COMPLIANCE").as(:type) } >>
277
+ spaced("STATUS") >> spaced { status } >>
278
+ spaced("DESCRIPTION") >> spaced { text } >>
279
+ spaced { refer_part }.maybe >>
280
+ spaced { compliance_modules } >>
281
+ colon_colon_part >> curly(object_identifier)
282
+ end
283
+
284
+ rule(:compliance_modules) do
285
+ with_separator(compliance_module)
286
+ end
287
+
288
+ rule(:compliance_module) do
289
+ spaced { str("MODULE") >> (space_in_line.repeat(1) >> compliance_module_name).maybe } >>
290
+ spaced { mandatory_part }.maybe >>
291
+ compliances.maybe
292
+ end
293
+
294
+ rule(:compliance_module_name) do
295
+ uppercase_identifier
296
+ end
297
+
298
+ rule(:mandatory_part) do
299
+ spaced("MANDATORY-GROUPS") >> curly(mandatory_groups)
300
+ end
301
+
302
+ rule(:compliances) do
303
+ with_separator(compliance).as(:compliances)
304
+ end
305
+
306
+ rule(:compliance) do
307
+ compliance_group | compliance_object
308
+ end
309
+
310
+ rule(:compliance_group) do
311
+ spaced { str("GROUP").as(:type) } >> spaced { object_identifier.as(:name) } >>
312
+ spaced("DESCRIPTION") >> text
313
+ end
314
+
315
+ rule(:compliance_object) do
316
+ spaced { str("OBJECT").as(:type) } >>
317
+ spaced { object_identifier.as(:name) } >>
318
+ spaced { syntax_part }.maybe >>
319
+ spaced { write_syntax_part }.maybe >>
320
+ spaced { access_part }.maybe >>
321
+ spaced("DESCRIPTION") >> text
322
+ end
323
+
324
+ rule(:syntax_part) do
325
+ spaced("SYNTAX") >> syntax.as(:syntax)
326
+ end
327
+
328
+ rule(:write_syntax_part) do
329
+ (spaced("WRITE-SYNTAX") >> spaced { syntax }).maybe
330
+ end
331
+
332
+ rule(:access_part) do
333
+ (spaced("MIN-ACCESS") >> spaced { access }).maybe
334
+ end
335
+
336
+ rule(:mandatory_groups) do
337
+ with_separator(mandatory_group, ",").as(:groups)
338
+ end
339
+
340
+ rule(:mandatory_group) { object_identifier.as(:name) }
341
+
342
+ rule(:module_identity_clause) do
343
+ spaced { lowercase_identifier.as(:name) } >>
344
+ spaced { str("MODULE-IDENTITY").as(:type) } >>
345
+ (spaced("SUBJECT-CATEGORIES") >> curly(category_ids)).maybe >>
346
+ spaced("LAST-UPDATED") >> spaced { ext_utc_time } >>
347
+ spaced("ORGANIZATION") >> spaced { text.as(:organization) } >>
348
+ spaced("CONTACT-INFO") >> spaced { text.as(:contact_info) } >>
349
+ spaced("DESCRIPTION") >> spaced { text } >>
350
+ spaced { revisions }.maybe >>
351
+ colon_colon_part >> curly(object_identifier.as(:value))
352
+ end
353
+
354
+ rule(:ext_utc_time) { text }
355
+
356
+ rule(:revisions) do
357
+ with_separator(revision)
358
+ end
359
+
360
+ rule(:revision) do
361
+ spaced("REVISION") >> spaced { ext_utc_time } >>
362
+ spaced("DESCRIPTION") >>
363
+ text
364
+ end
365
+
366
+ rule(:category_ids) do
367
+ with_separator(category_id, ",")
368
+ end
369
+
370
+ rule(:category_id) do
371
+ spaced { lowercase_identifier } >> bracketed(number) | lowercase_identifier
372
+ end
373
+
374
+ rule(:notification_type_clause) do
375
+ spaced { lowercase_identifier.as(:name) } >>
376
+ spaced { str("NOTIFICATION-TYPE").as(:type) } >>
377
+ spaced { notification_objects_part }.maybe >>
378
+ spaced("STATUS") >> spaced { status } >>
379
+ spaced("DESCRIPTION") >> spaced { text } >>
380
+ spaced { refer_part }.maybe >>
381
+ colon_colon_part >> curly(notification_name)
382
+ end
383
+
384
+ rule(:notification_objects_part) do
385
+ spaced("OBJECTS") >> curly(objects)
386
+ end
387
+
388
+ rule(:objects) do
389
+ with_separator(object, ",")
390
+ end
391
+
392
+ rule(:object) do
393
+ object_identifier
394
+ end
395
+
396
+ rule(:notification_name) do
397
+ object_identifier
398
+ end
399
+
400
+ rule(:traptype_clause) do
401
+ spaced { fuzzy_lowercase_identifier.as(:name) } >>
402
+ spaced { str("TRAP-TYPE").as(:type) } >> spaced { enterprise_part } >>
403
+ spaced { var_part }.maybe >>
404
+ spaced { descr_part }.maybe >>
405
+ spaced { refer_part }.maybe >>
406
+ colon_colon_part >> number
407
+ end
408
+
409
+ rule(:enterprise_part) do
410
+ spaced("ENTERPRISE") >> object_identifier |
411
+ spaced("ENTERPRISE") >> curly(object_identifier)
412
+ end
413
+
414
+ rule(:var_part) do
415
+ spaced("VARIABLES") >> curly(var_types)
416
+ end
417
+
418
+ rule(:var_types) do
419
+ with_separator(var_type, ",")
420
+ end
421
+
422
+ rule(:var_type) { object_identifier }
423
+
424
+ rule(:descr_part) do
425
+ spaced("DESCRIPTION") >> text
426
+ end
427
+
428
+ rule(:object_type_clause) do
429
+ spaced { lowercase_identifier.as(:name) } >>
430
+ spaced { str("OBJECT-TYPE").as(:type) } >>
431
+ spaced { syntax_part }.maybe >>
432
+ spaced { units_part }.maybe >>
433
+ spaced { max_access_part }.maybe >>
434
+ (spaced("STATUS") >> spaced { status }).maybe >>
435
+ spaced { description_clause }.maybe >>
436
+ spaced { refer_part }.maybe >>
437
+ spaced { index_part }.maybe >>
438
+ spaced { mib_index }.maybe >>
439
+ spaced { def_val_part }.maybe >>
440
+ colon_colon_part >> curly(object_identifier.as(:value))
441
+ end
442
+
443
+ rule(:object_identity_clause) do
444
+ spaced { lowercase_identifier.as(:name) } >>
445
+ spaced { str("OBJECT-IDENTITY").as(:type) } >>
446
+ spaced("STATUS") >> spaced { status } >>
447
+ spaced("DESCRIPTION") >> spaced { text } >>
448
+ spaced { refer_part }.maybe >>
449
+ colon_colon_part >> curly(object_identifier)
450
+ end
451
+
452
+ rule(:units_part) do
453
+ spaced("UNITS") >> text.as(:units)
454
+ end
455
+
456
+ rule(:max_access_part) do
457
+ spaced("MAX-ACCESS") >> access | spaced("ACCESS") >> access
458
+ end
459
+
460
+ rule(:access) { lowercase_identifier }
461
+
462
+ rule(:description_clause) do
463
+ spaced("DESCRIPTION") >> text
464
+ end
465
+
466
+ rule(:index_part) do
467
+ spaced("AUGMENTS") >> curly(entry)
468
+ end
469
+
470
+ rule(:mib_index) do
471
+ spaced("INDEX") >> curly(index_types)
472
+ end
473
+
474
+ rule(:def_val_part) do
475
+ spaced("DEFVAL") >> curly(valueof_simple_syntax)
476
+ end
477
+
478
+ rule(:valueof_simple_syntax) do
479
+ value | lowercase_identifier | text | curly(object_identifiers_defval)
480
+ end
481
+
482
+ rule(:object_identifiers_defval) do
483
+ with_separator(object_identifier_defval)
484
+ end
485
+
486
+ rule(:object_identifier_defval) do
487
+ spaced { lowercase_identifier } >> bracketed(number) |
488
+ number
489
+ end
490
+
491
+ rule(:index_types) do
492
+ with_separator(index_type, ",")
493
+ end
494
+
495
+ rule(:index_type) do
496
+ spaced("IMPLIED") >> idx | idx
497
+ end
498
+
499
+ rule(:idx) do
500
+ object_identifier
501
+ end
502
+
503
+ rule(:entry) do
504
+ object_identifier
505
+ end
506
+
507
+ rule(:value_declaration) do
508
+ spaced { fuzzy_lowercase_identifier.as(:name) } >>
509
+ spaced { str("OBJECT IDENTIFIER").as(:type) } >>
510
+ colon_colon_part >> curly(object_identifier.as(:value))
511
+ end
512
+
513
+ rule(:fuzzy_lowercase_identifier) do
514
+ lowercase_identifier | uppercase_identifier
515
+ end
516
+
517
+ rule(:object_identifier) do
518
+ sub_identifiers
519
+ end
520
+
521
+ rule(:sub_identifiers) do
522
+ with_separator(sub_identifier, space_in_line.repeat)
523
+ end
524
+
525
+ rule(:sub_identifier) do
526
+ fuzzy_lowercase_identifier |
527
+ number |
528
+ spaced { lowercase_identifier } >> bracketed(number)
529
+ end
530
+
531
+ rule(:type_declaration) do
532
+ spaced { type_name.as(:vartype) } >> colon_colon_part >> type_declaration_rhs
533
+ end
534
+
535
+ rule(:type_name) do
536
+ uppercase_identifier | type_smi
537
+ end
538
+
539
+ rule(:type_smi) do
540
+ type_smi_and_sppi | type_smi_only
541
+ end
542
+
543
+ rule(:type_declaration_rhs) do
544
+ spaced { choice_clause } |
545
+ spaced { str("TEXTUAL-CONVENTION") } >>
546
+ spaced { display_part }.maybe >>
547
+ spaced("STATUS") >> spaced { status } >>
548
+ spaced("DESCRIPTION") >> spaced { text } >>
549
+ spaced { refer_part }.maybe >>
550
+ spaced("SYNTAX") >> syntax |
551
+ syntax
552
+ end
553
+
554
+ rule(:refer_part) do
555
+ spaced("REFERENCE") >> text
556
+ end
557
+
558
+ rule(:choice_clause) do
559
+ # Ignoring choice syntax
560
+ spaced { str("CHOICE").as(:type) } >> curly(match("[^\}]").repeat)
561
+ end
562
+
563
+ rule(:syntax) do
564
+ object_syntax | spaced("BITS").as(:type) >> curly(named_bits)
565
+ end
566
+
567
+ rule(:display_part) do
568
+ spaced("DISPLAY-HINT") >> text
569
+ end
570
+
571
+ rule(:named_bits) do
572
+ with_separator(named_bit, ",")
573
+ end
574
+
575
+ rule(:named_bit) do
576
+ spaced { lowercase_identifier } >> bracketed(number)
577
+ end
578
+
579
+ rule(:object_syntax) do
580
+ conceptual_table |
581
+ entry_type |
582
+ simple_syntax |
583
+ application_syntax |
584
+ type_tag >> simple_syntax |
585
+ row.as(:value)
586
+ end
587
+
588
+ rule(:simple_syntax) do
589
+ spaced { str("INTEGER").as(:type) } >> (integer_subtype | enum_spec).maybe |
590
+ spaced { str("Integer32").as(:type) >> space } >> integer_subtype.maybe |
591
+ spaced { str("OCTET STRING").as(:type) } >> octetstring_subtype.maybe |
592
+ spaced { str("OBJECT IDENTIFIER").as(:type) } >> any_subtype |
593
+ spaced { uppercase_identifier.as(:type) } >> (integer_subtype | enum_spec | octetstring_subtype)
594
+ end
595
+
596
+ rule(:application_syntax) do
597
+ spaced { str("IpAddress").as(:type) >> space } >> any_subtype |
598
+ spaced { str("NetworkAddress").as(:type) >> space } >> any_subtype |
599
+ spaced { str("Counter32").as(:type) >> space } >> integer_subtype.maybe |
600
+ spaced { str("Gauge32").as(:type) >> space } >> integer_subtype.maybe |
601
+ spaced { str("Unsigned32").as(:type) >> space } >> integer_subtype.maybe |
602
+ spaced { str("TimeTicks").as(:type) >> space } >> any_subtype |
603
+ spaced { str("Opaque").as(:type) >> space } >> octetstring_subtype.maybe |
604
+ spaced { str("Counter64").as(:type) >> space } >> integer_subtype.maybe
605
+ end
606
+
607
+ rule(:conceptual_table) do
608
+ spaced { str("SEQUENCE OF").as(:type) } >> row.as(:value)
609
+ end
610
+
611
+ rule(:entry_type) do
612
+ spaced { str("SEQUENCE").as(:type) } >> curly(sequence_items)
613
+ end
614
+
615
+ rule(:type_tag) do
616
+ spaced { square_bracketed(spaced("APPLICATION") >> number.as(:application_type)) } >> spaced("IMPLICIT") |
617
+ spaced { square_bracketed(spaced("UNIVERSAL") >> number.as(:universal_type)) } >> spaced("IMPLICIT")
618
+ end
619
+
620
+ rule(:sequence_items) do
621
+ with_separator(sequence_item, ",")
622
+ end
623
+
624
+ rule(:sequence_item) do
625
+ spaced { lowercase_identifier } >> spaced { sequence_syntax }
626
+ end
627
+
628
+ rule(:sequence_syntax) do
629
+ str("BITS") |
630
+ sequence_object_syntax |
631
+ spaced { uppercase_identifier } >> any_subtype
632
+ end
633
+
634
+ rule(:sequence_object_syntax) do
635
+ sequence_simple_syntax | sequence_application_syntax
636
+ end
637
+
638
+ rule(:sequence_simple_syntax) do
639
+ spaced("INTEGER") >> any_subtype |
640
+ spaced("Integer32") >> any_subtype |
641
+ spaced("OCTET STRING") >> any_subtype |
642
+ spaced("OBJECT IDENTIFIER") >> any_subtype
643
+ end
644
+
645
+ rule(:sequence_application_syntax) do
646
+ spaced { str("IpAddress") >> space } >> any_subtype |
647
+ spaced { str("COUNTER32") } >> any_subtype |
648
+ spaced { str("Gauge32") >> space } >> any_subtype |
649
+ spaced { str("Unsigned32") >> space } >> any_subtype |
650
+ spaced { str("TimeTicks") >> space } >> any_subtype |
651
+ str("Opaque") |
652
+ spaced { str("Counter64") >> space } >> any_subtype
653
+ end
654
+
655
+ rule(:row) { uppercase_identifier }
656
+
657
+ rule(:integer_subtype) { bracketed(ranges) }
658
+
659
+ rule(:octetstring_subtype) do
660
+ bracketed(spaced("SIZE") >> bracketed(ranges))
661
+ end
662
+
663
+ rule(:any_subtype) do
664
+ (integer_subtype | octetstring_subtype | enum_spec).maybe
665
+ end
666
+
667
+ rule(:enum_spec) { curly(enum_items) }
668
+
669
+ rule(:enum_items) do
670
+ with_separator(enum_item.as(:enum), ",")
671
+ end
672
+
673
+ rule(:enum_item) do
674
+ fuzzy_lowercase_identifier.as(:name) >> space.repeat >> bracketed(number.as(:value))
675
+ end
676
+
677
+ rule(:ranges) do
678
+ with_separator(range.as(:range), "|")
679
+ end
680
+
681
+ rule(:range) do
682
+ value.as(:min) >> space.repeat >> (str("..") >> space.repeat >> value.as(:max)).maybe
683
+ end
684
+
685
+ rule(:value) do
686
+ number | hexstring | binstring
687
+ end
688
+
689
+ rule(:status) { lowercase_identifier }
690
+
691
+ rule(:uppercase_identifier) do
692
+ match("[A-Z]") >> match("[A-Za-z0-9\-]").repeat
693
+ end
694
+
695
+ rule(:lowercase_identifier) do
696
+ match("[a-z]") >> match("[A-Za-z0-9\-]").repeat
697
+ end
698
+
699
+ rule(:type_smi_and_sppi) do
700
+ str("IpAddress") | str("TimeTicks") | str("Opaque") | str("Integer32") | str("Unsigned32")
701
+ end
702
+
703
+ rule(:type_smi_only) do
704
+ str("Counter") | str("Gauge32") | str("Counter64")
705
+ end
706
+
707
+ rule(:colon_colon_part) { spaced("::=") }
708
+
709
+ rule(:space_in_line) { match('[ \t]').repeat(1) }
710
+ rule(:cr) { match("\n") }
711
+ rule(:space) do
712
+ # this rule match all not important text
713
+ (match('[ \t\r\n]') | comment_line).repeat(1)
714
+ end
715
+
716
+ rule(:comment_line) do
717
+ (match('\-\-') >> match('[^\n]').repeat >> match('\n'))
718
+ end
719
+
720
+ rule(:space?) { space.maybe }
721
+ rule(:digit) { match["0-9"] }
722
+ rule(:hexchar) { match("[0-9a-fA-F]") }
723
+ rule(:empty) { str("") }
724
+ rule(:number) do
725
+ (
726
+ str("-").maybe >> (
727
+ str("0") | (match("[1-9]") >> digit.repeat)
728
+ ) >> (
729
+ str(".") >> digit.repeat(1)
730
+ ).maybe >> (
731
+ match("[eE]") >> (str("+") | str("-")).maybe >> digit.repeat(1)
732
+ ).maybe
733
+ ).repeat(1)
734
+ end
735
+
736
+ rule(:hexstring) do
737
+ str("'") >> hexchar.repeat >> str("'") >> match("[hH]")
738
+ end
739
+
740
+ rule(:binstring) do
741
+ str("'") >> match["0-1"].repeat >> str("'")
742
+ end
743
+
744
+ rule(:text) do
745
+ str('"') >> (
746
+ str('\\') >> any | str('"').absent? >> any
747
+ ).repeat >> str('"')
748
+ end
749
+ end
750
+ end