• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Lexer(string file)25         public Lexer(string file)
26             : this(file, new StreamReader(file))
27         {
28         }
29 
Lexer(string file, TextReader stream)30         public Lexer(string file, TextReader stream)
31         {
32             this.Parse(file, stream);
33         }
34 
35 
GetEnumerator()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>
Parse(string file, TextReader stream)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 
ParseLine(ParseParams pp, string line, int row)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 
Parse(ParseParams pp, char current, int row, int column)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 
ParseLastSymbol(ParseParams pp, int row, int column)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 
CreateSpecialSymbol(string file, char value, int row, int column)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 
ParseBasicTypeDef(IModule module, string name, ISymbolEnumerator symbols, bool isMacroSyntax = false)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 
ParseOidValue(ISymbolEnumerator symbols, out string parent, out uint value)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 
DecodeRanges(ISymbolEnumerator symbols)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 continuous 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 
DecodeNumber(Symbol number)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 
DecodeEnumerations(ISymbolEnumerator symbols)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 }
582