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