Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
Asn1Document.cs
1using System;
2using System.IO;
3using System.Text;
4using System.Collections.Generic;
5using System.Globalization;
13using System.Threading.Tasks;
14
15namespace Waher.Content.Asn1
16{
20 public class Asn1Document
21 {
22 internal readonly Dictionary<string, Asn1Node> namedNodes = new Dictionary<string, Asn1Node>();
23 internal readonly Dictionary<string, Asn1TypeDefinition> aliases = new Dictionary<string, Asn1TypeDefinition>();
24 internal readonly Dictionary<string, Asn1FieldValueDefinition> values = new Dictionary<string, Asn1FieldValueDefinition>();
25 internal int pos = 0;
26 private readonly string text;
27 private readonly string location;
28 private readonly string[] importFolders;
29 private readonly int len;
30 private readonly int lenm1;
31 private Asn1Definitions root;
32 private int unnamedIndex = 1;
33
34 private Asn1Document(string Text, string Location, string[] ImportFolders)
35 {
36 this.text = Text;
37 this.location = Location;
38 this.importFolders = ImportFolders;
39 this.len = this.text.Length;
40 this.lenm1 = this.len - 1;
41 }
42
49 public async Task<Asn1Document> CreateAsync(string Text, string Location, string[] ImportFolders)
50 {
52 {
53 root = await this.ParseDefinitions()
54 };
55
56 return Result;
57 }
58
62 public Asn1Definitions Root => this.root;
63
67 public string Text => this.text;
68
72 public string Location => this.location;
73
77 public string[] ImportFolders => this.importFolders;
78
85 public static async Task<Asn1Document> FromFile(string FileName, string[] ImportFolders)
86 {
87 string Text = await Resources.ReadAllTextAsync(FileName);
88 return new Asn1Document(Text, FileName, ImportFolders);
89 }
90
91 internal void SkipWhiteSpace()
92 {
93 char ch;
94
95 while (this.pos < this.len)
96 {
97 ch = this.text[this.pos];
98
99 if (ch <= ' ' || ch == (char)160)
100 this.pos++;
101 else if (ch == '-' && this.pos < this.lenm1 && this.text[this.pos + 1] == '-')
102 {
103 this.pos += 2;
104
105 while (this.pos < this.len)
106 {
107 ch = this.text[this.pos];
108 if (ch == '\r' || ch == '\n')
109 break;
110
111 if (ch == '-' && this.pos < this.len - 1 && this.text[this.pos + 1] == '-')
112 {
113 this.pos += 2;
114 break;
115 }
116
117 this.pos++;
118 }
119 }
120 else if (ch == '/' && this.pos < this.lenm1 && this.text[this.pos + 1] == '*')
121 {
122 this.pos += 2;
123
124 while ((this.pos < this.len && this.text[this.pos] != '*') ||
125 (this.pos < this.lenm1 && this.text[this.pos + 1] != '/'))
126 {
127 this.pos++;
128 }
129 }
130 else
131 break;
132 }
133 }
134
135 internal char NextChar()
136 {
137 if (this.pos < this.len)
138 return this.text[this.pos++];
139 else
140 return (char)0;
141 }
142
143 internal char PeekNextChar()
144 {
145 if (this.pos < this.len)
146 return this.text[this.pos];
147 else
148 return (char)0;
149 }
150
151 internal string NextToken()
152 {
153 this.SkipWhiteSpace();
154
155 char ch = this.NextChar();
156
157 if (char.IsLetter(ch) || char.IsDigit(ch) || ch == '-' || ch == '_')
158 {
159 int Start = this.pos - 1;
160
161 while (this.pos < this.len && (char.IsLetter(ch = this.text[this.pos]) || char.IsDigit(ch) || ch == '-' || ch == '_'))
162 this.pos++;
163
164 return this.text.Substring(Start, this.pos - Start);
165 }
166 else
167 {
168 switch (ch)
169 {
170 case ':':
171 switch (this.PeekNextChar())
172 {
173 case ':':
174 this.pos++;
175 switch (this.PeekNextChar())
176 {
177 case '=':
178 this.pos++;
179 return "::=";
180
181 default:
182 return "::";
183 }
184
185 default:
186 return new string(ch, 1);
187 }
188
189 case '.':
190 if (this.PeekNextChar() == '.')
191 {
192 this.pos++;
193 if (this.PeekNextChar() == '.')
194 {
195 this.pos++;
196 return "...";
197 }
198 else
199 return "..";
200 }
201 else
202 return ".";
203
204 case '[':
205 if (this.PeekNextChar() == '[')
206 {
207 this.pos++;
208 return "[[";
209 }
210 else
211 return "[";
212
213 case ']':
214 if (this.PeekNextChar() == ']')
215 {
216 this.pos++;
217 return "]]";
218 }
219 else
220 return "]";
221
222 default:
223 return new string(ch, 1);
224 }
225 }
226 }
227
228 internal string PeekNextToken()
229 {
230 this.SkipWhiteSpace();
231
232 int Bak = this.pos;
233 string s = this.NextToken();
234 this.pos = Bak;
235 return s;
236 }
237
238 internal void AssertNextToken(string ExpectedToken)
239 {
240 string s = this.NextToken();
241 if (s != ExpectedToken)
242 throw this.SyntaxError(ExpectedToken + " expected.");
243 }
244
245 internal Asn1SyntaxException SyntaxError(string Message)
246 {
247 return new Asn1SyntaxException(Message, this.text, this.pos);
248 }
249
250 private async Task<Asn1Definitions> ParseDefinitions()
251 {
252 string Identifier = this.ParseTypeNameIdentifier();
253 string s = this.PeekNextToken();
254 Asn1Oid Oid = null;
255
256 if (s == "{")
257 {
258 Oid = this.ParseOid();
259 s = this.PeekNextToken();
260 }
261
262 if (s != "DEFINITIONS")
263 throw this.SyntaxError("DEFINITIONS expected.");
264
265 this.pos += 11;
266
267 Asn1Tags? Tags = null;
268 bool Abstract = false;
269
270 while (true)
271 {
272 switch (s = this.NextToken())
273 {
274 case "AUTOMATIC":
275 case "IMPLICIT":
276 case "EXPLICIT":
277 if (Tags.HasValue)
278 throw this.SyntaxError("TAGS already specified.");
279
280 switch (s)
281 {
282 case "AUTOMATIC":
283 Tags = Asn1Tags.Automatic;
284 break;
285
286 case "IMPLICIT":
287 Tags = Asn1Tags.Implicit;
288 break;
289
290 case "EXPLICIT":
291 Tags = Asn1Tags.Explicit;
292 break;
293 }
294
295 this.AssertNextToken("TAGS");
296 break;
297
298 case "ABSTRACT-SYNTAX":
299 Abstract = true;
300 break;
301
302 case "::=":
303 s = null;
304 break;
305
306 default:
307 throw this.SyntaxError("::= expected.");
308 }
309
310 if (s is null)
311 break;
312 }
313
314 Asn1Module Body = await this.ParseModule();
315
316 return new Asn1Definitions(Identifier, Oid, Tags, Abstract, Body, this);
317 }
318
319 private async Task<Asn1Module> ParseModule()
320 {
321 string s;
322
323 this.AssertNextToken("BEGIN");
324
325 List<Asn1Import> Imports = null;
326 List<string> Exports = null;
327
328 do
329 {
330 s = this.PeekNextToken();
331
332 if (s == "IMPORTS")
333 {
334 this.pos += 7;
335
336 if (Imports is null)
337 Imports = new List<Asn1Import>();
338
339 List<string> Identifiers = new List<string>();
340
341 do
342 {
343 string Identifier = this.ParseIdentifier();
344 string ModuleRef;
345
346 s = this.PeekNextToken();
347
348 if (s == "FROM")
349 {
350 this.pos += 4;
351 Identifiers.Add(Identifier);
352 ModuleRef = this.ParseTypeNameIdentifier();
353
354 Imports.Add(new Asn1Import(Identifiers.ToArray(), ModuleRef, this));
355 Identifiers.Clear();
356
357 s = this.PeekNextToken();
358 if (s != ";")
359 continue;
360 }
361 else if (s == ".")
362 {
363 this.pos++;
364 ModuleRef = Identifier;
365 Identifier = this.ParseIdentifier();
366 Imports.Add(new Asn1Import(new string[] { ModuleRef }, Identifier, this));
367 s = this.PeekNextToken();
368 }
369 else
370 Identifiers.Add(Identifier);
371
372 if (s == ",")
373 {
374 this.pos++;
375 continue;
376 }
377 else if (s == ";")
378 {
379 this.pos++;
380 break;
381 }
382 else
383 throw this.SyntaxError("Unexpected token.");
384 }
385 while (true);
386
387 if (Identifiers.Count > 0)
388 Imports.Add(new Asn1Import(Identifiers.ToArray(), string.Empty, this));
389
390 foreach (Asn1Import Import in Imports)
391 {
392 Asn1Document Doc = await Import.LoadDocument();
393
394 foreach (string Identifier in Import.Identifiers)
395 {
396 if (!Doc.namedNodes.TryGetValue(Identifier, out Asn1Node ImportedNode))
397 throw this.SyntaxError(Identifier + " not found in " + Import.Module);
398
399 this.namedNodes[Identifier] = ImportedNode;
400
401 if (Doc.aliases.TryGetValue(Identifier, out Asn1TypeDefinition TypeDef))
402 this.aliases[Identifier] = TypeDef;
403
404 if (Doc.values.TryGetValue(Identifier, out Asn1FieldValueDefinition ValueDef))
405 this.values[Identifier] = ValueDef;
406 }
407 }
408 }
409 else if (s == "EXPORTS")
410 {
411 this.pos += 7;
412
413 if (Exports is null)
414 Exports = new List<string>();
415
416 do
417 {
418 string Identifier = this.ParseIdentifier();
419 Exports.Add(Identifier);
420
421 s = this.PeekNextToken();
422
423 if (s == ",")
424 {
425 this.pos++;
426 continue;
427 }
428 else if (s == ";")
429 {
430 this.pos++;
431 break;
432 }
433 else
434 throw this.SyntaxError("; expected");
435 }
436 while (true);
437 }
438 else
439 break;
440 }
441 while (true);
442
443 List<Asn1Node> Items = new List<Asn1Node>();
444
445 while ((s = this.PeekNextToken()) != "END")
446 {
447 if (string.IsNullOrEmpty(s))
448 throw this.SyntaxError("END expected.");
449
450 Asn1Node Node = this.ParseStatement();
451 Items.Add(Node);
452
453 if (Node is INamedNode NamedNode)
454 this.namedNodes[NamedNode.Name] = Node;
455 }
456
457 this.pos += 3;
458
459 return new Asn1Module(Imports?.ToArray(), Exports?.ToArray(), Items.ToArray());
460 }
461
462 private Asn1Node ParseStatement()
463 {
464 string s = this.PeekNextToken();
465 if (string.IsNullOrEmpty(s))
466 throw this.SyntaxError("Unexpected end of file.");
467
468 char ch = s[0];
469 string s2;
470
471 if (char.IsLetter(ch))
472 {
473 int PosBak = this.pos;
474
475 this.pos += s.Length;
476 s2 = this.PeekNextToken();
477
478 if (s2 == "::=")
479 {
480 if (!char.IsUpper(ch)) // !Type = XML notation
481 throw this.SyntaxError("XML notation not supported.");
482
483 this.pos += s2.Length;
484
485 s2 = this.PeekNextToken();
486 }
487 else if (s2 == "MACRO")
488 {
489 this.pos += 5;
490 this.AssertNextToken("::=");
491 this.AssertNextToken("BEGIN");
492 this.AssertNextToken("TYPE");
493 this.AssertNextToken("NOTATION");
494 this.AssertNextToken("::=");
495
496 UserDefinedItem TypeNotation = this.ParseUserDefinedOptions("VALUE");
497
498 this.AssertNextToken("VALUE");
499 this.AssertNextToken("NOTATION");
500 this.AssertNextToken("::=");
501
502 UserDefinedItem ValueNotation = this.ParseUserDefinedOptions("END");
503 List<SupportingSyntax> SupportingSyntax = new List<SupportingSyntax>();
504
505 while (this.PeekNextToken() != "END")
506 {
507 string Name = this.ParseIdentifier();
508 this.AssertNextToken("::=");
509 SupportingSyntax.Add(new SupportingSyntax(Name, this.ParseUserDefinedOptions("END")));
510 }
511
512 this.AssertNextToken("END");
513
514 return new Asn1Macro(s, TypeNotation, ValueNotation, SupportingSyntax.ToArray(), this);
515 }
516 else if (this.namedNodes.TryGetValue(s2, out Asn1Node Node) &&
517 Node is Asn1Macro Macro)
518 {
519 this.pos += s2.Length;
520
521 Asn1Value Value = Macro.ParseValue(this);
522 Asn1FieldValueDefinition ValueDef = new Asn1FieldValueDefinition(s, Macro.GetValueType(), Value, this);
523
524 this.values[s] = ValueDef;
525
526 return ValueDef;
527 }
528 else
529 {
530 if (char.IsUpper(ch)) // Type or macro
531 {
532 s2 = s;
533 s = "unnamed" + (this.unnamedIndex++).ToString();
534 ch = 'u';
535 this.pos = PosBak;
536 }
537 }
538
539 int? Tag = null;
540 TagClass? Class = null;
541
542 if (s2 == "[")
543 {
544 this.pos++;
545 s2 = this.NextToken();
546
547 if (s2 == "TAG")
548 {
549 this.AssertNextToken(":");
550 s2 = this.NextToken();
551 }
552
553 switch (s2)
554 {
555 case "APPLICATION":
556 Class = TagClass.Application;
557 s2 = this.NextToken();
558 break;
559
560 case "PRIVATE":
561 Class = TagClass.Private;
562 s2 = this.NextToken();
563 break;
564
565 case "UNIVERSAL":
566 Class = TagClass.Universal;
567 s2 = this.NextToken();
568 break;
569 }
570
571 if (!int.TryParse(s2, out int i))
572 throw this.SyntaxError("Tag expected.");
573
574 if (Class.HasValue)
575 i |= ((int)Class) << 6;
576
577 Tag = i;
578
579 this.AssertNextToken("]");
580
581 s2 = this.PeekNextToken();
582 }
583
584 if (char.IsUpper(ch)) // Type
585 {
586 Asn1Type Definition;
587
588 if (this.namedNodes.TryGetValue(s2, out Asn1Node Node) &&
589 Node is Asn1Macro Macro)
590 {
591 this.pos += s2.Length;
592 Definition = Macro.ParseType(this);
593 }
594 else
595 Definition = this.ParseType(s, true);
596
597 Asn1TypeDefinition TypeDef = new Asn1TypeDefinition(s, Tag, Definition);
598
599 if (!Definition.ConstructedType)
600 this.aliases[s] = TypeDef;
601
602 return TypeDef;
603 }
604 else // name
605 {
606 if (!IsTypeIdentifier(s2))
607 throw this.SyntaxError("Type name expected.");
608
609 Asn1Type Type = this.ParseType(s, false);
610
611 if (this.PeekNextToken() == "::=")
612 {
613 this.pos += 3;
614 Asn1Value Value = this.ParseValue();
615 Asn1FieldValueDefinition ValueDef = new Asn1FieldValueDefinition(s, Type, Value, this);
616
617 this.values[s] = ValueDef;
618
619 return ValueDef;
620 }
621 else
622 return new Asn1FieldDefinition(s, Tag, Type);
623 }
624 }
625 else if (s == "...")
626 {
627 this.pos += 3;
628 return new Asn1Extension();
629 }
630 else
631 throw this.SyntaxError("Identifier expected.");
632 }
633
634 private UserDefinedItem ParseUserDefinedOptions(string EndKeyWord)
635 {
636 UserDefinedItem Item = this.ParseUserDefinedOption(EndKeyWord);
637 List<UserDefinedItem> Options = null;
638
639 while (this.PeekNextToken() == "|")
640 {
641 this.pos++;
642
643 if (Options is null)
644 Options = new List<UserDefinedItem>();
645
646 Options.Add(Item);
647 Item = this.ParseUserDefinedOption(EndKeyWord);
648 }
649
650 if (Options is null)
651 return Item;
652 else
653 {
654 Options.Add(Item);
655 return new UserDefinedOptions(Options.ToArray());
656 }
657 }
658
659 private UserDefinedItem ParseUserDefinedOption(string EndKeyWord)
660 {
661 UserDefinedItem Item = null;
662 List<UserDefinedItem> Items = new List<UserDefinedItem>();
663 string s;
664 int PosBak = this.pos;
665
666 while ((s = this.PeekNextToken()) != EndKeyWord && s != "::=" && s != "|")
667 {
668 if (!(Item is null))
669 {
670 if (Items is null)
671 Items = new List<UserDefinedItem>();
672
673 Items.Add(Item);
674 }
675
676 PosBak = this.pos;
677 Item = this.ParseUserDefinedItem();
678 }
679
680 if (Item is null)
681 throw this.SyntaxError("Items expected.");
682
683 if (s == "::=")
684 {
685 if (Items is null)
686 throw this.SyntaxError("Items expected.");
687
688 this.pos = PosBak;
689 if (Items.Count == 1)
690 return Items[0];
691 }
692 else if (Items is null)
693 return Item;
694 else
695 Items.Add(Item);
696
697 return new UserDefinedOption(Items.ToArray());
698 }
699
700 private UserDefinedItem ParseUserDefinedItem()
701 {
702 string s = this.PeekNextToken();
703
704 if (s == "\"")
705 {
706 if (!(this.ParseValue() is Asn1StringValue Label))
707 throw this.SyntaxError("String label expected.");
708
709 return new UserDefinedLiteral(Label.Value);
710 }
711
712 if (!IsIdentifier(s))
713 throw this.SyntaxError("Identifier or literal expected.");
714
715 this.pos += s.Length;
716
717 if (this.PeekNextToken() == "(")
718 {
719 this.pos++;
720
721 string Name = this.ParseIdentifier();
722 if (this.PeekNextToken() == ")")
723 {
724 this.pos++;
725 return new UserDefinedSpecifiedPart(s, string.Empty, new Asn1TypeReference(Name, this));
726 }
727 else
728 {
729 Asn1Type Type = this.ParseType(Name, false);
730
731 this.AssertNextToken(")");
732
733 return new UserDefinedSpecifiedPart(s, Name, Type);
734 }
735 }
736 else
737 return new UserDefinedPart(s);
738 }
739
740 private Asn1Restriction ParseRestriction()
741 {
742 this.AssertNextToken("(");
743 Asn1Restriction Result = this.ParseOrs();
744 this.AssertNextToken(")");
745
746 return Result;
747 }
748
749 private Asn1Restriction ParseOrs()
750 {
751 Asn1Restriction Result = this.ParseAnds();
752
753 string s = this.PeekNextToken();
754
755 while (s == "|")
756 {
757 this.pos++;
758 Result = new Asn1Or(Result, this.ParseAnds());
759 s = this.PeekNextToken();
760 }
761
762 return Result;
763 }
764
765 private Asn1Restriction ParseAnds()
766 {
767 Asn1Restriction Result = this.ParseRestrictionRule();
768
769 string s = this.PeekNextToken();
770
771 while (s == "^")
772 {
773 this.pos++;
774 Result = new Asn1And(Result, this.ParseRestrictionRule());
775 s = this.PeekNextToken();
776 }
777
778 return Result;
779 }
780
781 private Asn1Restriction ParseRestrictionRule()
782 {
783 string s = this.PeekNextToken();
784
785 switch (s)
786 {
787 case "(":
788 this.pos++;
789
790 Asn1Restriction Result = this.ParseOrs();
791 this.AssertNextToken(")");
792
793 return Result;
794
795 case "SIZE":
796 this.pos += 4;
797 return new Asn1Size(this.ParseSet());
798
799 case "PATTERN":
800 this.pos += 7;
801 return new Asn1Pattern(this.ParseValue());
802
803 case "FROM":
804 this.pos += 4;
805 return new Asn1From(this.ParseSet());
806
807 case "CONTAINING":
808 this.pos += 10;
809 return new Asn1Containing(this.ParseValue());
810
811 case "ENCODED":
812 this.pos += 7;
813 this.AssertNextToken("BY");
814
815 return new Asn1EncodedBy(this.ParseValue());
816
817 case "WITH":
818 this.pos += 4;
819 this.AssertNextToken("COMPONENTS");
820
821 return new Asn1WithComponents(this.ParseValue());
822
823 default:
824 return new Asn1InSet(this.ParseUnions());
825 }
826 }
827
828 private Asn1Values ParseSet()
829 {
830 this.AssertNextToken("(");
831 Asn1Values Result = this.ParseUnions();
832 this.AssertNextToken(")");
833
834 return Result;
835 }
836
837 private Asn1Values ParseUnions()
838 {
839 Asn1Values Result = this.ParseIntersections();
840
841 string s = this.PeekNextToken();
842
843 while (s == "|" || s == "UNION")
844 {
845 this.pos += s.Length;
846 Result = new Asn1Union(Result, this.ParseIntersections());
847 s = this.PeekNextToken();
848 }
849
850 return Result;
851 }
852
853 private Asn1Values ParseIntersections()
854 {
855 Asn1Values Result = this.ParseIntervals();
856
857 string s = this.PeekNextToken();
858
859 while (s == "^" || s == "INTERSECTION")
860 {
861 this.pos += s.Length;
862 Result = new Asn1Union(Result, this.ParseIntervals());
863 s = this.PeekNextToken();
864 }
865
866 return Result;
867 }
868
869 private Asn1Values ParseIntervals()
870 {
871 string s = this.PeekNextToken();
872
873 if (s == "(")
874 {
875 this.pos++;
876
877 Asn1Values Result = this.ParseUnions();
878 this.AssertNextToken(")");
879
880 return Result;
881 }
882 else if (s == "ALL")
883 {
884 this.pos += 3;
885 return new Asn1All();
886 }
887 else
888 {
889 Asn1Value Value = this.ParseValue();
890
891 if (this.PeekNextToken() == "..")
892 {
893 this.pos += 2;
894 Asn1Value Value2 = this.ParseValue();
895
896 return new Asn1Interval(Value, Value2);
897 }
898 else
899 return new Asn1Element(Value);
900 }
901 }
902
903 private string ParseIdentifier()
904 {
905 string s = this.NextToken();
906
907 if (!IsIdentifier(s))
908 throw this.SyntaxError("Identifier expected.");
909
910 return s;
911 }
912
913 private string ParseTypeNameIdentifier()
914 {
915 string s = this.NextToken();
916
917 if (!IsTypeIdentifier(s))
918 throw this.SyntaxError("Type name identifier expected.");
919
920 return s;
921 }
922
923 private static bool IsIdentifier(string s)
924 {
925 return !string.IsNullOrEmpty(s) && char.IsLetter(s[0]);
926 }
927
928 private static bool IsTypeIdentifier(string s)
929 {
930 char ch;
931 return !string.IsNullOrEmpty(s) && char.IsLetter(ch = s[0]) && char.IsUpper(ch);
932 }
933
934 private static bool IsFieldIdentifier(string s)
935 {
936 char ch;
937 return !string.IsNullOrEmpty(s) && char.IsLetter(ch = s[0]) && char.IsLower(ch);
938 }
939
940 private Asn1Oid ParseOid()
941 {
942 Asn1Node[] Values = this.ParseValues();
943 return new Asn1Oid(Values);
944 }
945
946 internal Asn1Type ParseType(string Name, bool TypeDef)
947 {
948 bool Implicit = false;
949
950 switch (this.PeekNextToken())
951 {
952 case "IMPLICIT":
953 this.pos += 8;
954 Implicit = true;
955 break;
956 }
957
958 Asn1Type Result = this.ParseDataType(Name, TypeDef);
959 Result.Implicit = Implicit;
960
961 string s2;
962
963 while (true)
964 {
965 s2 = this.PeekNextToken();
966
967 switch (s2)
968 {
969 case "(":
970 Result.Restriction = this.ParseRestriction();
971 break;
972
973 case "{":
974 this.pos++;
975
976 List<Asn1NamedValue> NamedOptions = new List<Asn1NamedValue>();
977
978 while (true)
979 {
980 s2 = this.NextToken();
981 if (!IsFieldIdentifier(s2))
982 throw this.SyntaxError("Value name expected.");
983
984 if (this.PeekNextToken() == "(")
985 {
986 this.pos++;
987
988 NamedOptions.Add(new Asn1NamedValue(s2, this.ParseValue(), this));
989
990 if (this.NextToken() != ")")
991 throw this.SyntaxError(") expected");
992 }
993 else
994 NamedOptions.Add(new Asn1NamedValue(s2, null, this));
995
996 s2 = this.NextToken();
997
998 if (s2 == ",")
999 continue;
1000 else if (s2 == "}")
1001 {
1002 Result.NamedOptions = NamedOptions.ToArray();
1003 break;
1004 }
1005 else
1006 throw this.SyntaxError("Unexpected token.");
1007 }
1008 break;
1009
1010 case "OPTIONAL":
1011 this.pos += 8;
1012 Result.Optional = true;
1013 break;
1014
1015 case "PRESENT":
1016 this.pos += 7;
1017 Result.Present = true;
1018 break;
1019
1020 case "ABSENT":
1021 this.pos += 6;
1022 Result.Absent = true;
1023 break;
1024
1025 case "DEFAULT":
1026 this.pos += 7;
1027 Result.Optional = true;
1028 Result.Default = this.ParseValue();
1029 break;
1030
1031 case "UNIQUE":
1032 this.pos += 6;
1033 Result.Unique = true;
1034 break;
1035
1036 default:
1037 return Result;
1038 }
1039 }
1040 }
1041
1042 private Asn1Type ParseDataType(string Name, bool TypeDef)
1043 {
1044 string s = this.NextToken();
1045 if (string.IsNullOrEmpty(s))
1046 throw this.SyntaxError("Unexpected end of file.");
1047
1048 switch (s)
1049 {
1050 case "ANY":
1051 return new Asn1Any();
1052
1053 case "BIT":
1054 this.AssertNextToken("STRING");
1055 return new Asn1BitString();
1056
1057 case "BMPString":
1058 return new Asn1BmpString();
1059
1060 case "BOOLEAN":
1061 return new Asn1Boolean();
1062
1063 case "CHARACTER":
1064 return new Asn1Character();
1065
1066 case "CHOICE":
1067 s = this.PeekNextToken();
1068
1069 if (s == "{")
1070 {
1071 Asn1Node[] Nodes = this.ParseList();
1072
1073 foreach (Asn1Node Node in Nodes)
1074 {
1075 if (Node is Asn1FieldDefinition FieldDef && !this.namedNodes.ContainsKey(FieldDef.Name))
1076 this.namedNodes[FieldDef.Name] = FieldDef;
1077 }
1078
1079 return new Asn1Choice(Name, TypeDef, Nodes);
1080 }
1081 else
1082 throw this.SyntaxError("{ expected.");
1083
1084 case "DATE":
1085 return new Asn1Date();
1086
1087 case "DATE-TIME":
1088 return new Asn1DateTime();
1089
1090 case "DURATION":
1091 return new Asn1Duration();
1092
1093 case "ENUMERATED":
1094 s = this.PeekNextToken();
1095
1096 if (s == "{")
1097 {
1098 Asn1Node[] Nodes = this.ParseValues();
1099 return new Asn1Enumeration(Name, TypeDef, Nodes);
1100 }
1101 else
1102 throw this.SyntaxError("{ expected.");
1103
1104 case "GeneralizedTime":
1105 return new Asn1GeneralizedTime();
1106
1107 case "GeneralString":
1108 return new Asn1GeneralString();
1109
1110 case "GraphicString":
1111 return new Asn1GraphicString();
1112
1113 case "IA5String":
1114 return new Asn1Ia5String();
1115
1116 case "INTEGER":
1117 return new Asn1Integer();
1118
1119 case "ISO646String":
1120 return new Asn1Iso646String();
1121
1122 case "NULL":
1123 return new Asn1Null();
1124
1125 case "NumericString":
1126 return new Asn1NumericString();
1127
1128 case "OBJECT":
1129 this.AssertNextToken("IDENTIFIER");
1130 return new Asn1ObjectIdentifier(false);
1131
1132 case "OCTET":
1133 this.AssertNextToken("STRING");
1134 return new Asn1OctetString();
1135
1136 case "PrintableString":
1137 return new Asn1PrintableString();
1138
1139 case "REAL":
1140 return new Asn1Real();
1141
1142 case "RELATIVE":
1143 this.AssertNextToken("OID");
1144 return new Asn1ObjectIdentifier(true);
1145
1146 case "RELATIVE-OID":
1147 return new Asn1ObjectIdentifier(true);
1148
1149 case "SET":
1150 s = this.PeekNextToken();
1151
1152 if (s == "{")
1153 {
1154 Asn1Node[] Nodes = this.ParseList();
1155 return new Asn1Set(Name, TypeDef, Nodes);
1156 }
1157 else if (s == "(")
1158 {
1159 this.pos++;
1160
1161 Asn1Values Size = null;
1162
1163 while (true)
1164 {
1165 s = this.PeekNextToken();
1166
1167 if (s == "SIZE")
1168 {
1169 if (!(Size is null))
1170 throw this.SyntaxError("SIZE already specified.");
1171
1172 this.pos += 4;
1173 Size = this.ParseSet();
1174 }
1175 else if (s == ")")
1176 {
1177 this.pos++;
1178 break;
1179 }
1180 else
1181 throw this.SyntaxError("Unexpected token.");
1182 }
1183
1184 this.AssertNextToken("OF");
1185
1186 if (Size is null)
1187 throw this.SyntaxError("SIZE expected.");
1188
1189 s = this.ParseTypeNameIdentifier();
1190
1191 return new Asn1SetOf(Size, s);
1192 }
1193 else
1194 throw this.SyntaxError("{ expected.");
1195
1196 case "SEQUENCE":
1197 s = this.PeekNextToken();
1198
1199 if (s == "{")
1200 {
1201 Asn1Node[] Nodes = this.ParseList();
1202 return new Asn1Sequence(Name, TypeDef, Nodes);
1203 }
1204 else
1205 {
1206 Asn1Values Size = null;
1207
1208 if (s == "(")
1209 {
1210 this.pos++;
1211
1212 while (true)
1213 {
1214 s = this.PeekNextToken();
1215
1216 if (s == "SIZE")
1217 {
1218 if (!(Size is null))
1219 throw this.SyntaxError("SIZE already specified.");
1220
1221 this.pos += 4;
1222 Size = this.ParseSet();
1223 }
1224 else if (s == ")")
1225 {
1226 this.pos++;
1227 break;
1228 }
1229 else
1230 throw this.SyntaxError("Unexpected token.");
1231 }
1232 }
1233
1234 if (this.NextToken() != "OF")
1235 throw this.SyntaxError("Unexpected token.");
1236
1237 return new Asn1SequenceOf(Name, TypeDef, Size, this.ParseType(Name, TypeDef));
1238 }
1239
1240 case "T61String":
1241 return new Asn1T61String();
1242
1243 case "TeletexString":
1244 return new Asn1TeletexString();
1245
1246 case "TIME-OF-DAY":
1247 return new Asn1TimeOfDay();
1248
1249 case "UniversalString":
1250 return new Asn1UniversalString();
1251
1252 case "UTCTime":
1253 return new Asn1UtcTime();
1254
1255 case "UTF8String":
1256 return new Asn1Utf8String();
1257
1258 case "VideotexString":
1259 return new Asn1VideotexString();
1260
1261 case "VisibleString":
1262 return new Asn1VisibleString();
1263
1264 case "ObjectDescriptor":
1265 case "EXTERNAL":
1266 case "EMBEDDED":
1267 case "CLASS":
1268 case "COMPONENTS":
1269 case "INSTANCE":
1270 case "OID-IRI":
1271 throw this.SyntaxError("Token not implemented.");
1272
1273 default:
1274 if (char.IsUpper(s[0]))
1275 return new Asn1TypeReference(s, this);
1276 else
1277 throw this.SyntaxError("Type name expected.");
1278 }
1279 }
1280
1281 private Asn1Node[] ParseList()
1282 {
1283 this.AssertNextToken("{");
1284
1285 List<Asn1Node> Items = new List<Asn1Node>();
1286
1287 while (true)
1288 {
1289 Items.Add(this.ParseStatement());
1290
1291 switch (this.PeekNextToken())
1292 {
1293 case ",":
1294 this.pos++;
1295 continue;
1296
1297 case "}":
1298 this.pos++;
1299 return Items.ToArray();
1300
1301 default:
1302 throw this.SyntaxError(", or } expected.");
1303 }
1304 }
1305 }
1306
1307 private Asn1Node[] ParseValues()
1308 {
1309 this.AssertNextToken("{");
1310
1311 List<Asn1Node> Items = new List<Asn1Node>();
1312
1313 while (true)
1314 {
1315 Items.Add(this.ParseValue());
1316
1317 switch (this.PeekNextToken())
1318 {
1319 case ",":
1320 this.pos++;
1321 continue;
1322
1323 case "}":
1324 this.pos++;
1325 return Items.ToArray();
1326
1327 case "(":
1328 this.pos++;
1329 Asn1Value Value = this.ParseValue();
1330 this.AssertNextToken(")");
1331
1332 int LastIndex = Items.Count - 1;
1333
1334 if (Items[LastIndex] is Asn1ValueReference Ref)
1335 Items[LastIndex] = new Asn1NamedValue(Ref.Identifier, Value, this);
1336 else
1337 throw this.SyntaxError("Invalid value reference.");
1338 break;
1339
1340 default:
1341 throw this.SyntaxError(", or } expected.");
1342 }
1343 }
1344 }
1345
1346 internal Asn1Value ParseValue()
1347 {
1348 return this.ParseValue(true);
1349 }
1350
1351 private Asn1Value ParseValue(bool AllowNamed)
1352 {
1353 string s = this.PeekNextToken();
1354 if (string.IsNullOrEmpty(s))
1355 throw this.SyntaxError("Expected value.");
1356
1357 switch (s)
1358 {
1359 case "FALSE":
1360 this.pos += 5;
1361 return new Asn1BooleanValue(false);
1362
1363 case "MAX":
1364 this.pos += 3;
1365 return new Asn1Max();
1366
1367 case "MIN":
1368 this.pos += 3;
1369 return new Asn1Min();
1370
1371 case "TRUE":
1372 this.pos += 4;
1373 return new Asn1BooleanValue(true);
1374
1375 case "INF":
1376 this.pos += 3;
1377 return new Asn1FloatingPointValue(double.PositiveInfinity);
1378
1379 case "NaN":
1380 this.pos += 3;
1381 return new Asn1FloatingPointValue(double.NaN);
1382
1383 case "...":
1384 this.pos += 3;
1385 return new Asn1Extension();
1386
1387 case "\"":
1388 int Start = ++this.pos;
1389 char ch;
1390
1391 while (this.pos < this.len && this.text[this.pos] != '"')
1392 this.pos++;
1393
1394 if (this.pos >= this.len)
1395 throw this.SyntaxError("\" expected.");
1396
1397 s = this.text.Substring(Start, this.pos - Start);
1398 this.pos++;
1399
1400 return new Asn1StringValue(s);
1401
1402 case "'":
1403 Start = ++this.pos;
1404
1405 while (this.pos < this.len && this.text[this.pos] != '\'')
1406 this.pos++;
1407
1408 if (this.pos >= this.len)
1409 throw this.SyntaxError("' expected.");
1410
1411 s = this.text.Substring(Start, this.pos - Start);
1412 this.pos++;
1413
1414 switch (this.NextToken())
1415 {
1416 case "H":
1417 case "h":
1418 this.pos++;
1419 if (long.TryParse(s, NumberStyles.HexNumber, null, out long l))
1420 return new Asn1IntegerValue(l);
1421 else
1422 throw this.SyntaxError("Invalid hexadecimal string.");
1423
1424 case "D":
1425 case "d":
1426 this.pos++;
1427 if (long.TryParse(s, out l))
1428 return new Asn1IntegerValue(l);
1429 else
1430 throw this.SyntaxError("Invalid decimal string.");
1431
1432 case "B":
1433 case "b":
1434 this.pos++;
1435 if (TryParseBinary(s, out l))
1436 return new Asn1IntegerValue(l);
1437 else
1438 throw this.SyntaxError("Invalid binary string.");
1439
1440 case "O":
1441 case "o":
1442 this.pos++;
1443 if (TryParseOctal(s, out l))
1444 return new Asn1IntegerValue(l);
1445 else
1446 throw this.SyntaxError("Invalid octal string.");
1447
1448 default:
1449 throw this.SyntaxError("Unexpected token.");
1450 }
1451
1452 case "{":
1453 this.pos++;
1454
1455 List<Asn1Value> Items = new List<Asn1Value>();
1456 bool Oid = false;
1457
1458 while (true)
1459 {
1460 Asn1Value Value = this.ParseValue();
1461
1462 s = this.PeekNextToken();
1463
1464 switch (s)
1465 {
1466 case ",":
1467 this.pos++;
1468 Items.Add(Value);
1469 break;
1470
1471 case "}":
1472 this.pos++;
1473 Items.Add(Value);
1474
1475 if (Oid)
1476 return new Asn1Oid(Items.ToArray());
1477 else
1478 return new Asn1Array(Items.ToArray());
1479
1480 default:
1481 if (Items.Count == 0)
1482 Oid = true;
1483
1484 if (Oid)
1485 Items.Add(Value);
1486 else
1487 throw this.SyntaxError("Unexpected token.");
1488 break;
1489 }
1490 }
1491
1492 default:
1493 if (char.IsLetter(s[0]))
1494 {
1495 this.pos += s.Length;
1496
1497 switch (this.PeekNextToken())
1498 {
1499 case ":":
1500 if (!AllowNamed)
1501 throw this.SyntaxError("Value expected.");
1502
1503 this.pos++;
1504 Asn1Value Value = this.ParseValue(false);
1505 return new Asn1NamedValue(s, Value, this);
1506
1507 case "(":
1508 Asn1Restriction Restriction = this.ParseRestriction();
1509
1510 if (Restriction is Asn1InSet Set && Set.Set is Asn1Element Element)
1511 return new Asn1NamedValue(s, Element.Element, this);
1512 else
1513 return new Asn1RestrictedValueReference(s, Restriction, this);
1514
1515 default:
1516 if (!char.IsUpper(s[0]))
1517 return new Asn1ValueReference(s, this);
1518 else
1519 throw this.SyntaxError("Type references not permitted here.");
1520 }
1521 }
1522 else
1523 {
1524 Start = this.pos;
1525
1526 bool Sign = s.StartsWith("-");
1527 if (Sign)
1528 {
1529 s = s.Substring(1);
1530 this.pos++;
1531 }
1532
1533 if (ulong.TryParse(s, out ulong l))
1534 {
1535 this.pos += s.Length;
1536
1537 ch = this.PeekNextChar();
1538
1539 if ((ch == '.' && this.pos < this.lenm1 && this.text[this.pos + 1] != '.') ||
1540 ch == 'e' || ch == 'E')
1541 {
1542 int? DecPos = null;
1543
1544 if (ch == '.')
1545 {
1546 DecPos = this.pos++;
1547 while (this.pos < this.len && char.IsDigit(ch = this.text[this.pos]))
1548 this.pos++;
1549 }
1550
1551 if (ch == 'e' || ch == 'E')
1552 {
1553 this.pos++;
1554
1555 if ((ch = this.PeekNextChar()) == '-' || ch == '+')
1556 this.pos++;
1557
1558 while (this.pos < this.len && char.IsDigit(this.text[this.pos]))
1559 this.pos++;
1560 }
1561
1562 s = this.text.Substring(Start, this.pos - Start);
1563
1564 string s2 = NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
1565 if (DecPos.HasValue && s2 != ".")
1566 s = s.Replace(".", s2);
1567
1568 if (!double.TryParse(s, out double d))
1569 throw this.SyntaxError("Invalid floating-point number.");
1570
1571 return new Asn1FloatingPointValue(d);
1572 }
1573
1574 if (l <= long.MaxValue)
1575 return new Asn1IntegerValue(Sign ? -(long)l : (long)l);
1576 else if (Sign)
1577 throw this.SyntaxError("Number does not fit into a 64-bit signed integer.");
1578 else
1579 return new Asn1UnsignedIntegerValue(l);
1580 }
1581 }
1582 break;
1583 }
1584
1585 throw this.SyntaxError("Value expected.");
1586 }
1587
1588 private static bool TryParseBinary(string s, out long l)
1589 {
1590 l = 0;
1591
1592 foreach (char ch in s)
1593 {
1594 if (ch < '0' || ch > '1')
1595 return false;
1596
1597 long l2 = l;
1598 l <<= 1;
1599 if (l < l2)
1600 return false;
1601
1602 l |= (byte)(ch - '0');
1603 }
1604
1605 return true;
1606 }
1607
1608 private static bool TryParseOctal(string s, out long l)
1609 {
1610 l = 0;
1611
1612 foreach (char ch in s)
1613 {
1614 if (ch < '0' || ch > '7')
1615 return false;
1616
1617 long l2 = l;
1618 l <<= 3;
1619 if (l < l2)
1620 return false;
1621
1622 l |= (byte)(ch - '0');
1623 }
1624
1625 return true;
1626 }
1627
1633 public string ExportCSharp(CSharpExportSettings Settings)
1634 {
1635 StringBuilder Output = new StringBuilder();
1636 this.ExportCSharp(Output, Settings);
1637 return Output.ToString();
1638 }
1639
1645 public void ExportCSharp(StringBuilder Output, CSharpExportSettings Settings)
1646 {
1647 CSharpExportState State = new CSharpExportState(Settings);
1648
1649 Output.AppendLine("using System;");
1650 Output.AppendLine("using System.Text;");
1651 Output.AppendLine("using System.Collections.Generic;");
1652 Output.AppendLine("using Waher.Content;");
1653 Output.AppendLine("using Waher.Content.Asn1;");
1654
1655 this.root?.ExportCSharp(Output, State, 0, CSharpExportPass.Explicit);
1656 }
1657 }
1658}
Represents an ASN.1 document.
Definition: Asn1Document.cs:21
void ExportCSharp(StringBuilder Output, CSharpExportSettings Settings)
Exports ASN.1 schemas to C#
string ExportCSharp(CSharpExportSettings Settings)
Exports ASN.1 schemas to C#
static async Task< Asn1Document > FromFile(string FileName, string[] ImportFolders)
Read from file.
Definition: Asn1Document.cs:85
async Task< Asn1Document > CreateAsync(string Text, string Location, string[] ImportFolders)
Represents an ASN.1 document.
Definition: Asn1Document.cs:49
Asn1Definitions Root
ASN.1 Root node
Definition: Asn1Document.cs:62
string Location
Location of document.
Definition: Asn1Document.cs:72
string[] ImportFolders
Import folders.
Definition: Asn1Document.cs:77
Represents a ASN.1 CHOICE construct.
Definition: Asn1Choice.cs:13
Represents a collection of ASN.1 definitions.
override async Task ExportCSharp(StringBuilder Output, CSharpExportState State, int Indent, CSharpExportPass Pass)
Exports to C#
Represents an ASN.1 field definition.
Represents an ASN.1 field value definition.
Represents one import instruction.
Definition: Asn1Import.cs:14
string[] Identifiers
Identifiers to import.
Definition: Asn1Import.cs:37
async Task< Asn1Document > LoadDocument()
Loads the ASN.1 document to import.
Definition: Asn1Import.cs:48
string Module
Module reference.
Definition: Asn1Import.cs:42
Represents an ASN.1 module.
Definition: Asn1Module.cs:12
Base class for all ASN.1 nodes.
Definition: Asn1Node.cs:38
Abstract base class for ASN.1 restrictions.
Represents a ASN.1 SEQUENCE construct.
Definition: Asn1Sequence.cs:12
Represents a ASN.1 SEQUENCE OF construct.
Represents a ASN.1 SET construct.
Definition: Asn1Set.cs:12
Represents a ASN.1 SET OF construct.
Definition: Asn1SetOf.cs:11
Represents an ASN.1 Type definition.
Abstract base class for ASN.1 types.
Definition: Asn1Type.cs:13
virtual bool ConstructedType
If the type is a constructed type.
Definition: Asn1Type.cs:105
Abstract base class for values.
Definition: Asn1Value.cs:11
Abstract base class for sets of values
Definition: Asn1Values.cs:11
Abstract base class for user-defined parts in macros
Restricted to elements in set.
Definition: Asn1InSet.cs:11
Either restriction applies
Definition: Asn1Or.cs:11
All elements (in current context).
Definition: Asn1All.cs:11
BmpString (utf-16-be encoded string) Basic Multilingual Plane of ISO/IEC/ITU 10646-1
GeneralString all registered graphic and character sets plus SPACE and DELETE
GraphicString all registered G sets and SPACE
IA5String International ASCII characters (International Alphabet 5)
NumericString 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, and SPACE
PrintableString a-z, A-Z, ' () +,-.?:/= and SPACE
TeletexString CCITT and T.101 character sets
UniversalString ISO10646 character set
UTF8String any character from a recognized alphabet (including ASCII control characters)
VideotexString CCITT's T.100 and T.101 character sets
VisibleString International ASCII printing character sets
Represents an ASN.1 Object ID
Definition: Asn1Oid.cs:12
Static class managing loading of resources stored as embedded resources or in content files.
Definition: Resources.cs:15
static async Task< string > ReadAllTextAsync(string FileName)
Reads a text file asynchronously.
Definition: Resources.cs:205
Asn1Tags
How ASN.1 tags are managed.
CSharpExportPass
Defines different C# export passes.
Definition: Asn1Node.cs:12
TagClass
TAG class
Definition: TagClass.cs:11
delegate string ToString(IElement Element)
Delegate for callback methods that convert an element value to a string.