1 | /*
|
---|
2 | * Created by SharpDevelop.
|
---|
3 | * User: lextm
|
---|
4 | * Date: 2008/5/17
|
---|
5 | * Time: 16:50
|
---|
6 | *
|
---|
7 | * To change this template use Tools | Options | Coding | Edit Standard Headers.
|
---|
8 | */
|
---|
9 |
|
---|
10 | using System;
|
---|
11 | using System.Collections.Generic;
|
---|
12 | using System.IO;
|
---|
13 | using System.Text;
|
---|
14 | using Lextm.SharpSnmpLib.Mib.Elements.Types;
|
---|
15 |
|
---|
16 | namespace Lextm.SharpSnmpLib.Mib
|
---|
17 | {
|
---|
18 | /// <summary>
|
---|
19 | /// Lexer class that parses MIB files into symbol list.
|
---|
20 | /// </summary>
|
---|
21 | public sealed class Lexer
|
---|
22 | {
|
---|
23 | private readonly SymbolList _symbols = new SymbolList();
|
---|
24 |
|
---|
25 | public Lexer(string file)
|
---|
26 | : this(file, new StreamReader(file))
|
---|
27 | {
|
---|
28 | }
|
---|
29 |
|
---|
30 | public Lexer(string file, TextReader stream)
|
---|
31 | {
|
---|
32 | this.Parse(file, stream);
|
---|
33 | }
|
---|
34 |
|
---|
35 |
|
---|
36 | public ISymbolEnumerator GetEnumerator()
|
---|
37 | {
|
---|
38 | return _symbols.GetSymbolEnumerator();
|
---|
39 | }
|
---|
40 |
|
---|
41 |
|
---|
42 | #region Parsing of MIB File
|
---|
43 |
|
---|
44 | private class ParseParams
|
---|
45 | {
|
---|
46 | public string File;
|
---|
47 | public StringBuilder Temp = new StringBuilder();
|
---|
48 | public bool StringSection = false;
|
---|
49 | public bool AssignSection = false;
|
---|
50 | public bool AssignAhead = false;
|
---|
51 | public bool DotSection = false;
|
---|
52 |
|
---|
53 | }
|
---|
54 |
|
---|
55 | /// <summary>
|
---|
56 | /// Parses MIB file to symbol list.
|
---|
57 | /// </summary>
|
---|
58 | /// <param name="file">File</param>
|
---|
59 | /// <param name="stream">File stream</param>
|
---|
60 | private void Parse(string file, TextReader stream)
|
---|
61 | {
|
---|
62 | if (stream == null)
|
---|
63 | {
|
---|
64 | throw new ArgumentNullException("stream");
|
---|
65 | }
|
---|
66 |
|
---|
67 | ParseParams pp = new ParseParams();
|
---|
68 | pp.File = file;
|
---|
69 |
|
---|
70 | string line;
|
---|
71 | int row = 0;
|
---|
72 | while ((line = stream.ReadLine()) != null)
|
---|
73 | {
|
---|
74 | if (!pp.StringSection && line.TrimStart().StartsWith("--", StringComparison.Ordinal))
|
---|
75 | {
|
---|
76 | row++;
|
---|
77 | continue; // commented line
|
---|
78 | }
|
---|
79 |
|
---|
80 | ParseLine(pp, line, row);
|
---|
81 | row++;
|
---|
82 | }
|
---|
83 | }
|
---|
84 |
|
---|
85 | private void ParseLine(ParseParams pp, string line, int row)
|
---|
86 | {
|
---|
87 | line = line + "\n";
|
---|
88 | int count = line.Length;
|
---|
89 | for (int i = 0; i < count; i++)
|
---|
90 | {
|
---|
91 | char current = line[i];
|
---|
92 | bool moveNext = Parse(pp, current, row, i);
|
---|
93 | if (moveNext)
|
---|
94 | {
|
---|
95 | break;
|
---|
96 | }
|
---|
97 | }
|
---|
98 | }
|
---|
99 |
|
---|
100 | private bool Parse(ParseParams pp, char current, int row, int column)
|
---|
101 | {
|
---|
102 | switch (current)
|
---|
103 | {
|
---|
104 | case '\n':
|
---|
105 | case '{':
|
---|
106 | case '}':
|
---|
107 | case '(':
|
---|
108 | case ')':
|
---|
109 | case '[':
|
---|
110 | case ']':
|
---|
111 | case ';':
|
---|
112 | case ',':
|
---|
113 | case '|':
|
---|
114 | if (!pp.StringSection)
|
---|
115 | {
|
---|
116 | bool moveNext = ParseLastSymbol(pp, row, column);
|
---|
117 | if (moveNext)
|
---|
118 | {
|
---|
119 | _symbols.Add(CreateSpecialSymbol(pp.File, '\n', row, column));
|
---|
120 | return true;
|
---|
121 | }
|
---|
122 |
|
---|
123 | _symbols.Add(CreateSpecialSymbol(pp.File, current, row, column));
|
---|
124 | return false;
|
---|
125 | }
|
---|
126 |
|
---|
127 | break;
|
---|
128 | case '"':
|
---|
129 | pp.StringSection = !pp.StringSection;
|
---|
130 | break;
|
---|
131 | case '\r':
|
---|
132 | return false;
|
---|
133 | default:
|
---|
134 | if ((int)current == 0x1A)
|
---|
135 | {
|
---|
136 | // IMPORTANT: ignore invisible characters such as SUB.
|
---|
137 | return false;
|
---|
138 | }
|
---|
139 |
|
---|
140 | if (Char.IsWhiteSpace(current) && !pp.AssignSection && !pp.StringSection)
|
---|
141 | {
|
---|
142 | bool moveNext = ParseLastSymbol(pp, row, column);
|
---|
143 | if (moveNext)
|
---|
144 | {
|
---|
145 | _symbols.Add(CreateSpecialSymbol(pp.File, '\n', row, column));
|
---|
146 | return true;
|
---|
147 | }
|
---|
148 |
|
---|
149 | return false;
|
---|
150 | }
|
---|
151 |
|
---|
152 | if (pp.AssignAhead)
|
---|
153 | {
|
---|
154 | pp.AssignAhead = false;
|
---|
155 | ParseLastSymbol(pp, row, column);
|
---|
156 | break;
|
---|
157 | }
|
---|
158 |
|
---|
159 | if (pp.DotSection && current != '.')
|
---|
160 | {
|
---|
161 | ParseLastSymbol(pp, row, column);
|
---|
162 | pp.DotSection = false;
|
---|
163 | }
|
---|
164 |
|
---|
165 | if (current == '.' && !pp.StringSection)
|
---|
166 | {
|
---|
167 | if (!pp.DotSection)
|
---|
168 | {
|
---|
169 | ParseLastSymbol(pp, row, column);
|
---|
170 | pp.DotSection = true;
|
---|
171 | }
|
---|
172 | }
|
---|
173 |
|
---|
174 | if (current == ':' && !pp.StringSection)
|
---|
175 | {
|
---|
176 | if (!pp.AssignSection)
|
---|
177 | {
|
---|
178 | ParseLastSymbol(pp, row, column);
|
---|
179 | }
|
---|
180 |
|
---|
181 | pp.AssignSection = true;
|
---|
182 | }
|
---|
183 |
|
---|
184 | if (current == '=' && !pp.StringSection)
|
---|
185 | {
|
---|
186 | pp.AssignSection = false;
|
---|
187 | pp.AssignAhead = true;
|
---|
188 | }
|
---|
189 |
|
---|
190 | break;
|
---|
191 | }
|
---|
192 |
|
---|
193 | pp.Temp.Append(current);
|
---|
194 | return false;
|
---|
195 | }
|
---|
196 |
|
---|
197 | private bool ParseLastSymbol(ParseParams pp, int row, int column)
|
---|
198 | {
|
---|
199 | if (pp.Temp.Length > 0)
|
---|
200 | {
|
---|
201 | Symbol s = new Symbol(pp.File, pp.Temp.ToString(), row, column);
|
---|
202 |
|
---|
203 | pp.Temp.Length = 0;
|
---|
204 |
|
---|
205 | if (s.ToString().StartsWith(Symbol.Comment.ToString()))
|
---|
206 | {
|
---|
207 | // ignore the rest symbols on this line because they are in comment.
|
---|
208 | return true;
|
---|
209 | }
|
---|
210 |
|
---|
211 | _symbols.Add(s);
|
---|
212 | }
|
---|
213 |
|
---|
214 | return false;
|
---|
215 | }
|
---|
216 |
|
---|
217 | private static Symbol CreateSpecialSymbol(string file, char value, int row, int column)
|
---|
218 | {
|
---|
219 | string str;
|
---|
220 | switch (value)
|
---|
221 | {
|
---|
222 | case '\n':
|
---|
223 | str = Environment.NewLine;
|
---|
224 | break;
|
---|
225 | case '{':
|
---|
226 | str = "{";
|
---|
227 | break;
|
---|
228 | case '}':
|
---|
229 | str = "}";
|
---|
230 | break;
|
---|
231 | case '(':
|
---|
232 | str = "(";
|
---|
233 | break;
|
---|
234 | case ')':
|
---|
235 | str = ")";
|
---|
236 | break;
|
---|
237 | case '[':
|
---|
238 | str = "[";
|
---|
239 | break;
|
---|
240 | case ']':
|
---|
241 | str = "]";
|
---|
242 | break;
|
---|
243 | case ';':
|
---|
244 | str = ";";
|
---|
245 | break;
|
---|
246 | case ',':
|
---|
247 | str = ",";
|
---|
248 | break;
|
---|
249 | case '|':
|
---|
250 | str = "|";
|
---|
251 | break;
|
---|
252 | default:
|
---|
253 | throw new ArgumentException("value is not a special character");
|
---|
254 | }
|
---|
255 |
|
---|
256 | return new Symbol(file, str, row, column);
|
---|
257 | }
|
---|
258 |
|
---|
259 | #endregion
|
---|
260 |
|
---|
261 | #region Static Parse Helper
|
---|
262 |
|
---|
263 | public static ITypeAssignment ParseBasicTypeDef(IModule module, string name, ISymbolEnumerator symbols, bool isMacroSyntax = false)
|
---|
264 | {
|
---|
265 | Symbol current = symbols.NextNonEOLSymbol();
|
---|
266 |
|
---|
267 | if (current == Symbol.Bits)
|
---|
268 | {
|
---|
269 | return new BitsType(module, name, symbols);
|
---|
270 | }
|
---|
271 | if (IntegerType.IsIntegerType(current))
|
---|
272 | {
|
---|
273 | return new IntegerType(module, name, current, symbols);
|
---|
274 | }
|
---|
275 | if (UnsignedType.IsUnsignedType(current))
|
---|
276 | {
|
---|
277 | return new UnsignedType(module, name, current, symbols);
|
---|
278 | }
|
---|
279 | if (current == Symbol.Opaque)
|
---|
280 | {
|
---|
281 | return new OpaqueType(module, name, symbols);
|
---|
282 | }
|
---|
283 | if (current == Symbol.IpAddress)
|
---|
284 | {
|
---|
285 | return new IpAddressType(module, name, symbols);
|
---|
286 | }
|
---|
287 | if (current == Symbol.TextualConvention)
|
---|
288 | {
|
---|
289 | return new TextualConvention(module, name, symbols);
|
---|
290 | }
|
---|
291 | if (current == Symbol.Octet)
|
---|
292 | {
|
---|
293 | Symbol next = symbols.NextNonEOLSymbol();
|
---|
294 |
|
---|
295 | if (next == Symbol.String)
|
---|
296 | {
|
---|
297 | return new OctetStringType(module, name, symbols);
|
---|
298 | }
|
---|
299 |
|
---|
300 | symbols.PutBack(next);
|
---|
301 | }
|
---|
302 | if (current == Symbol.Object)
|
---|
303 | {
|
---|
304 | Symbol next = symbols.NextNonEOLSymbol();
|
---|
305 |
|
---|
306 | if (next == Symbol.Identifier)
|
---|
307 | {
|
---|
308 | return new ObjectIdentifierType(module, name, symbols);
|
---|
309 | }
|
---|
310 |
|
---|
311 | symbols.PutBack(next);
|
---|
312 | }
|
---|
313 | if (current == Symbol.Sequence)
|
---|
314 | {
|
---|
315 | Symbol next = symbols.NextNonEOLSymbol();
|
---|
316 |
|
---|
317 | if (next == Symbol.Of)
|
---|
318 | {
|
---|
319 | return new SequenceOf(module, name, symbols);
|
---|
320 | }
|
---|
321 | else
|
---|
322 | {
|
---|
323 | symbols.PutBack(next);
|
---|
324 | return new Sequence(module, name, symbols);
|
---|
325 | }
|
---|
326 | }
|
---|
327 | if (current == Symbol.Choice)
|
---|
328 | {
|
---|
329 | return new Choice(module, name, symbols);
|
---|
330 | }
|
---|
331 |
|
---|
332 |
|
---|
333 | return new TypeAssignment(module, name, current, symbols, isMacroSyntax);
|
---|
334 | }
|
---|
335 |
|
---|
336 | public static void ParseOidValue(ISymbolEnumerator symbols, out string parent, out uint value)
|
---|
337 | {
|
---|
338 | parent = null;
|
---|
339 | value = 0;
|
---|
340 |
|
---|
341 | Symbol current = symbols.NextNonEOLSymbol();
|
---|
342 | current.Expect(Symbol.OpenBracket);
|
---|
343 |
|
---|
344 | Symbol previous = null;
|
---|
345 | StringBuilder longParent = new StringBuilder();
|
---|
346 |
|
---|
347 | current = symbols.NextNonEOLSymbol();
|
---|
348 | longParent.Append(current);
|
---|
349 |
|
---|
350 | while ((current = symbols.NextNonEOLSymbol()) != null)
|
---|
351 | {
|
---|
352 | bool succeeded;
|
---|
353 |
|
---|
354 | if (current == Symbol.OpenParentheses)
|
---|
355 | {
|
---|
356 | longParent.Append(current);
|
---|
357 |
|
---|
358 | current = symbols.NextNonEOLSymbol();
|
---|
359 | succeeded = UInt32.TryParse(current.ToString(), out value);
|
---|
360 | current.Assert(succeeded, "not a decimal");
|
---|
361 | longParent.Append(current);
|
---|
362 | current = symbols.NextNonEOLSymbol();
|
---|
363 | current.Expect(Symbol.CloseParentheses);
|
---|
364 | longParent.Append(current);
|
---|
365 | continue;
|
---|
366 | }
|
---|
367 |
|
---|
368 | if (current == Symbol.CloseBracket)
|
---|
369 | {
|
---|
370 | parent = longParent.ToString();
|
---|
371 | return;
|
---|
372 | }
|
---|
373 |
|
---|
374 | succeeded = UInt32.TryParse(current.ToString(), out value);
|
---|
375 | if (succeeded)
|
---|
376 | {
|
---|
377 | // numerical way
|
---|
378 | while ((current = symbols.NextNonEOLSymbol()) != Symbol.CloseBracket)
|
---|
379 | {
|
---|
380 | longParent.Append(".").Append(value);
|
---|
381 | succeeded = UInt32.TryParse(current.ToString(), out value);
|
---|
382 | current.Assert(succeeded, "not a decimal");
|
---|
383 | }
|
---|
384 |
|
---|
385 | current.Expect(Symbol.CloseBracket);
|
---|
386 | parent = longParent.ToString();
|
---|
387 | return;
|
---|
388 | }
|
---|
389 |
|
---|
390 | longParent.Append(".");
|
---|
391 | longParent.Append(current);
|
---|
392 | current = symbols.NextNonEOLSymbol();
|
---|
393 | current.Expect(Symbol.OpenParentheses);
|
---|
394 | longParent.Append(current);
|
---|
395 | current = symbols.NextNonEOLSymbol();
|
---|
396 | succeeded = UInt32.TryParse(current.ToString(), out value);
|
---|
397 | current.Assert(succeeded, "not a decimal");
|
---|
398 | longParent.Append(current);
|
---|
399 | current = symbols.NextNonEOLSymbol();
|
---|
400 | current.Expect(Symbol.CloseParentheses);
|
---|
401 | longParent.Append(current);
|
---|
402 | previous = current;
|
---|
403 | }
|
---|
404 |
|
---|
405 | throw MibException.Create("end of file reached", previous);
|
---|
406 | }
|
---|
407 |
|
---|
408 |
|
---|
409 | public static ValueRanges DecodeRanges(ISymbolEnumerator symbols)
|
---|
410 | {
|
---|
411 | ValueRanges result = new ValueRanges();
|
---|
412 |
|
---|
413 | Symbol startSymbol = symbols.NextNonEOLSymbol();
|
---|
414 | Symbol current = startSymbol;
|
---|
415 | current.Expect(Symbol.OpenParentheses);
|
---|
416 |
|
---|
417 | while (current != Symbol.CloseParentheses)
|
---|
418 | {
|
---|
419 | Symbol value1Symbol = symbols.NextNonEOLSymbol();
|
---|
420 |
|
---|
421 | if ((value1Symbol == Symbol.Size) && !result.IsSizeDeclaration)
|
---|
422 | {
|
---|
423 | result.IsSizeDeclaration = true;
|
---|
424 | symbols.NextNonEOLSymbol().Expect(Symbol.OpenParentheses);
|
---|
425 | continue;
|
---|
426 | }
|
---|
427 |
|
---|
428 | // check for valid number
|
---|
429 | Int64? value1 = DecodeNumber(value1Symbol);
|
---|
430 | if (!value1.HasValue)
|
---|
431 | {
|
---|
432 | value1Symbol.Assert(false, "Invalid range declaration!");
|
---|
433 | }
|
---|
434 |
|
---|
435 | // process next symbol
|
---|
436 | ValueRange range;
|
---|
437 | current = symbols.NextNonEOLSymbol();
|
---|
438 |
|
---|
439 | if (current == Symbol.DoubleDot)
|
---|
440 | {
|
---|
441 | // its a continous range
|
---|
442 | Symbol value2Symbol = symbols.NextNonEOLSymbol();
|
---|
443 | Int64? value2 = DecodeNumber(value2Symbol);
|
---|
444 | value2Symbol.Assert(value2.HasValue && (value2.Value >= value1.Value), "Invalid range declaration!");
|
---|
445 |
|
---|
446 | if (value2.Value == value1.Value)
|
---|
447 | {
|
---|
448 | range = new ValueRange(value1.Value, null);
|
---|
449 | }
|
---|
450 | else
|
---|
451 | {
|
---|
452 | range = new ValueRange(value1.Value, value2.Value);
|
---|
453 | }
|
---|
454 |
|
---|
455 | current = symbols.NextNonEOLSymbol();
|
---|
456 | }
|
---|
457 | else
|
---|
458 | {
|
---|
459 | // its a single number
|
---|
460 | range = new ValueRange(value1.Value, null);
|
---|
461 | }
|
---|
462 |
|
---|
463 | // validate range
|
---|
464 | if (result.IsSizeDeclaration)
|
---|
465 | {
|
---|
466 | value1Symbol.Assert(range.Start >= 0, "Invalid range declaration! Size must be greater than 0");
|
---|
467 | }
|
---|
468 |
|
---|
469 | result.Add(range);
|
---|
470 |
|
---|
471 | // check next symbol
|
---|
472 | current.Expect(Symbol.Pipe, Symbol.CloseParentheses);
|
---|
473 | }
|
---|
474 |
|
---|
475 | if (result.IsSizeDeclaration)
|
---|
476 | {
|
---|
477 | current = symbols.NextNonEOLSymbol();
|
---|
478 | current.Expect(Symbol.CloseParentheses);
|
---|
479 | }
|
---|
480 |
|
---|
481 | // validate ranges in between
|
---|
482 | for (int i=0; i<result.Count; i++)
|
---|
483 | {
|
---|
484 | for (int k=i+1; k<result.Count; k++)
|
---|
485 | {
|
---|
486 | startSymbol.Assert(!result[i].IntersectsWith(result[k]), "Invalid range declaration! Overlapping of ranges!");
|
---|
487 | }
|
---|
488 | }
|
---|
489 |
|
---|
490 | return result;
|
---|
491 | }
|
---|
492 |
|
---|
493 | public static Int64? DecodeNumber(Symbol number)
|
---|
494 | {
|
---|
495 | Int64 result;
|
---|
496 | string numString = (number != null) ? number.ToString() : null;
|
---|
497 |
|
---|
498 | if (!String.IsNullOrEmpty(numString))
|
---|
499 | {
|
---|
500 | if (numString.StartsWith("'") && (numString.Length > 3))
|
---|
501 | {
|
---|
502 | // search second apostrophe
|
---|
503 | int end = numString.IndexOf('\'', 1);
|
---|
504 | if (end == (numString.Length - 2))
|
---|
505 | {
|
---|
506 | try
|
---|
507 | {
|
---|
508 | switch (numString[numString.Length - 1])
|
---|
509 | {
|
---|
510 | case 'b':
|
---|
511 | case 'B':
|
---|
512 | result = Convert.ToInt64(numString.Substring(1, numString.Length - 3), 2);
|
---|
513 | return result;
|
---|
514 | case 'h':
|
---|
515 | case 'H':
|
---|
516 | result = Convert.ToInt64(numString.Substring(1, numString.Length - 3), 16);
|
---|
517 | return result;
|
---|
518 | }
|
---|
519 | }
|
---|
520 | catch
|
---|
521 | {
|
---|
522 | }
|
---|
523 | }
|
---|
524 | }
|
---|
525 | else if (Int64.TryParse(numString, out result))
|
---|
526 | {
|
---|
527 | return result;
|
---|
528 | }
|
---|
529 | }
|
---|
530 |
|
---|
531 | return null;
|
---|
532 | }
|
---|
533 |
|
---|
534 | public static ValueMap DecodeEnumerations(ISymbolEnumerator symbols)
|
---|
535 | {
|
---|
536 | Symbol current = symbols.NextNonEOLSymbol();
|
---|
537 | current.Expect(Symbol.OpenBracket);
|
---|
538 |
|
---|
539 | ValueMap map = new ValueMap();
|
---|
540 | do
|
---|
541 | {
|
---|
542 | current = symbols.NextNonEOLSymbol();
|
---|
543 | string identifier = current.ToString();
|
---|
544 |
|
---|
545 | current = symbols.NextNonEOLSymbol();
|
---|
546 | current.Expect(Symbol.OpenParentheses);
|
---|
547 |
|
---|
548 | current = symbols.NextNonEOLSymbol();
|
---|
549 | Int64 enumValue;
|
---|
550 | if (Int64.TryParse(current.ToString(), out enumValue))
|
---|
551 | {
|
---|
552 | try
|
---|
553 | {
|
---|
554 | // Have to include the number as it seems repeated identifiers are allowed ??
|
---|
555 | map.Add(enumValue, String.Format("{0}({1})", identifier, enumValue));
|
---|
556 | }
|
---|
557 | catch (ArgumentException ex)
|
---|
558 | {
|
---|
559 | current.Assert(false, ex.Message);
|
---|
560 | }
|
---|
561 | }
|
---|
562 | else
|
---|
563 | {
|
---|
564 | // Need to get "DefinedValue".
|
---|
565 | }
|
---|
566 |
|
---|
567 | current = symbols.NextNonEOLSymbol();
|
---|
568 | current.Expect(Symbol.CloseParentheses);
|
---|
569 |
|
---|
570 | current = symbols.NextNonEOLSymbol();
|
---|
571 | } while (current == Symbol.Comma);
|
---|
572 |
|
---|
573 | current.Expect(Symbol.CloseBracket);
|
---|
574 |
|
---|
575 | return map;
|
---|
576 | }
|
---|
577 |
|
---|
578 | #endregion
|
---|
579 |
|
---|
580 | }
|
---|
581 | }
|
---|