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