4using System.Collections.Generic;
5using System.Globalization;
13using System.Threading.Tasks;
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>();
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;
32 private int unnamedIndex = 1;
39 this.len = this.text.Length;
40 this.lenm1 = this.len - 1;
53 root = await this.ParseDefinitions()
67 public string Text => this.text;
91 internal void SkipWhiteSpace()
95 while (this.pos < this.len)
97 ch = this.text[this.pos];
99 if (ch <=
' ' || ch == (
char)160)
101 else if (ch ==
'-' && this.pos < this.lenm1 && this.text[this.pos + 1] ==
'-')
105 while (this.pos < this.len)
107 ch = this.text[this.pos];
108 if (ch ==
'\r' || ch ==
'\n')
111 if (ch ==
'-' && this.pos < this.len - 1 && this.text[this.pos + 1] ==
'-')
120 else if (ch ==
'/' && this.pos < this.lenm1 && this.text[this.pos + 1] ==
'*')
124 while ((this.pos < this.len && this.text[this.pos] !=
'*') ||
125 (this.pos < this.lenm1 && this.text[this.pos + 1] !=
'/'))
135 internal char NextChar()
137 if (this.pos < this.len)
138 return this.text[this.pos++];
143 internal char PeekNextChar()
145 if (this.pos < this.len)
146 return this.text[this.pos];
151 internal string NextToken()
153 this.SkipWhiteSpace();
155 char ch = this.NextChar();
157 if (
char.IsLetter(ch) ||
char.IsDigit(ch) || ch ==
'-' || ch ==
'_')
159 int Start = this.pos - 1;
161 while (this.pos < this.len && (
char.IsLetter(ch = this.text[this.pos]) ||
char.IsDigit(ch) || ch ==
'-' || ch ==
'_'))
164 return this.text.Substring(Start, this.pos - Start);
171 switch (this.PeekNextChar())
175 switch (this.PeekNextChar())
186 return new string(ch, 1);
190 if (this.PeekNextChar() ==
'.')
193 if (this.PeekNextChar() ==
'.')
205 if (this.PeekNextChar() ==
'[')
214 if (this.PeekNextChar() ==
']')
223 return new string(ch, 1);
228 internal string PeekNextToken()
230 this.SkipWhiteSpace();
233 string s = this.NextToken();
238 internal void AssertNextToken(
string ExpectedToken)
240 string s = this.NextToken();
241 if (s != ExpectedToken)
242 throw this.SyntaxError(ExpectedToken +
" expected.");
250 private async Task<Asn1Definitions> ParseDefinitions()
252 string Identifier = this.ParseTypeNameIdentifier();
253 string s = this.PeekNextToken();
258 Oid = this.ParseOid();
259 s = this.PeekNextToken();
262 if (s !=
"DEFINITIONS")
263 throw this.SyntaxError(
"DEFINITIONS expected.");
268 bool Abstract =
false;
272 switch (s = this.NextToken())
278 throw this.SyntaxError(
"TAGS already specified.");
295 this.AssertNextToken(
"TAGS");
298 case "ABSTRACT-SYNTAX":
307 throw this.SyntaxError(
"::= expected.");
316 return new Asn1Definitions(Identifier, Oid, Tags, Abstract, Body,
this);
319 private async Task<Asn1Module> ParseModule()
323 this.AssertNextToken(
"BEGIN");
325 List<Asn1Import> Imports =
null;
326 List<string> Exports =
null;
330 s = this.PeekNextToken();
337 Imports =
new List<Asn1Import>();
339 List<string> Identifiers =
new List<string>();
343 string Identifier = this.ParseIdentifier();
346 s = this.PeekNextToken();
351 Identifiers.Add(Identifier);
352 ModuleRef = this.ParseTypeNameIdentifier();
354 Imports.Add(
new Asn1Import(Identifiers.ToArray(), ModuleRef,
this));
357 s = this.PeekNextToken();
364 ModuleRef = Identifier;
365 Identifier = this.ParseIdentifier();
366 Imports.Add(
new Asn1Import(
new string[] { ModuleRef }, Identifier,
this));
367 s = this.PeekNextToken();
370 Identifiers.Add(Identifier);
383 throw this.SyntaxError(
"Unexpected token.");
387 if (Identifiers.Count > 0)
388 Imports.Add(
new Asn1Import(Identifiers.ToArray(),
string.Empty,
this));
396 if (!Doc.namedNodes.TryGetValue(Identifier, out
Asn1Node ImportedNode))
397 throw this.SyntaxError(Identifier +
" not found in " + Import.
Module);
399 this.namedNodes[Identifier] = ImportedNode;
402 this.aliases[Identifier] = TypeDef;
405 this.values[Identifier] = ValueDef;
409 else if (s ==
"EXPORTS")
414 Exports =
new List<string>();
418 string Identifier = this.ParseIdentifier();
419 Exports.Add(Identifier);
421 s = this.PeekNextToken();
434 throw this.SyntaxError(
"; expected");
443 List<Asn1Node> Items =
new List<Asn1Node>();
445 while ((s = this.PeekNextToken()) !=
"END")
447 if (
string.IsNullOrEmpty(s))
448 throw this.SyntaxError(
"END expected.");
450 Asn1Node Node = this.ParseStatement();
454 this.namedNodes[NamedNode.Name] = Node;
459 return new Asn1Module(Imports?.ToArray(), Exports?.ToArray(), Items.ToArray());
464 string s = this.PeekNextToken();
465 if (
string.IsNullOrEmpty(s))
466 throw this.SyntaxError(
"Unexpected end of file.");
471 if (
char.IsLetter(ch))
473 int PosBak = this.pos;
475 this.pos += s.Length;
476 s2 = this.PeekNextToken();
480 if (!
char.IsUpper(ch))
481 throw this.SyntaxError(
"XML notation not supported.");
483 this.pos += s2.Length;
485 s2 = this.PeekNextToken();
487 else if (s2 ==
"MACRO")
490 this.AssertNextToken(
"::=");
491 this.AssertNextToken(
"BEGIN");
492 this.AssertNextToken(
"TYPE");
493 this.AssertNextToken(
"NOTATION");
494 this.AssertNextToken(
"::=");
498 this.AssertNextToken(
"VALUE");
499 this.AssertNextToken(
"NOTATION");
500 this.AssertNextToken(
"::=");
505 while (this.PeekNextToken() !=
"END")
507 string Name = this.ParseIdentifier();
508 this.AssertNextToken(
"::=");
512 this.AssertNextToken(
"END");
516 else if (this.namedNodes.TryGetValue(s2, out
Asn1Node Node) &&
519 this.pos += s2.Length;
521 Asn1Value Value = Macro.ParseValue(
this);
524 this.values[s] = ValueDef;
530 if (
char.IsUpper(ch))
533 s =
"unnamed" + (this.unnamedIndex++).
ToString();
545 s2 = this.NextToken();
549 this.AssertNextToken(
":");
550 s2 = this.NextToken();
557 s2 = this.NextToken();
562 s2 = this.NextToken();
567 s2 = this.NextToken();
571 if (!
int.TryParse(s2, out
int i))
572 throw this.SyntaxError(
"Tag expected.");
575 i |= ((int)Class) << 6;
579 this.AssertNextToken(
"]");
581 s2 = this.PeekNextToken();
584 if (
char.IsUpper(ch))
588 if (this.namedNodes.TryGetValue(s2, out
Asn1Node Node) &&
591 this.pos += s2.Length;
592 Definition = Macro.ParseType(
this);
595 Definition = this.ParseType(s,
true);
600 this.aliases[s] = TypeDef;
606 if (!IsTypeIdentifier(s2))
607 throw this.SyntaxError(
"Type name expected.");
609 Asn1Type Type = this.ParseType(s,
false);
611 if (this.PeekNextToken() ==
"::=")
617 this.values[s] = ValueDef;
631 throw this.SyntaxError(
"Identifier expected.");
637 List<UserDefinedItem> Options =
null;
639 while (this.PeekNextToken() ==
"|")
644 Options =
new List<UserDefinedItem>();
647 Item = this.ParseUserDefinedOption(EndKeyWord);
662 List<UserDefinedItem> Items =
new List<UserDefinedItem>();
664 int PosBak = this.pos;
666 while ((s = this.PeekNextToken()) != EndKeyWord && s !=
"::=" && s !=
"|")
671 Items =
new List<UserDefinedItem>();
677 Item = this.ParseUserDefinedItem();
681 throw this.SyntaxError(
"Items expected.");
686 throw this.SyntaxError(
"Items expected.");
689 if (Items.Count == 1)
692 else if (Items is
null)
702 string s = this.PeekNextToken();
707 throw this.SyntaxError(
"String label expected.");
712 if (!IsIdentifier(s))
713 throw this.SyntaxError(
"Identifier or literal expected.");
715 this.pos += s.Length;
717 if (this.PeekNextToken() ==
"(")
721 string Name = this.ParseIdentifier();
722 if (this.PeekNextToken() ==
")")
729 Asn1Type Type = this.ParseType(Name,
false);
731 this.AssertNextToken(
")");
742 this.AssertNextToken(
"(");
744 this.AssertNextToken(
")");
753 string s = this.PeekNextToken();
758 Result =
new Asn1Or(Result, this.ParseAnds());
759 s = this.PeekNextToken();
769 string s = this.PeekNextToken();
774 Result =
new Asn1And(Result, this.ParseRestrictionRule());
775 s = this.PeekNextToken();
783 string s = this.PeekNextToken();
791 this.AssertNextToken(
")");
797 return new Asn1Size(this.ParseSet());
805 return new Asn1From(this.ParseSet());
813 this.AssertNextToken(
"BY");
819 this.AssertNextToken(
"COMPONENTS");
824 return new Asn1InSet(this.ParseUnions());
830 this.AssertNextToken(
"(");
832 this.AssertNextToken(
")");
839 Asn1Values Result = this.ParseIntersections();
841 string s = this.PeekNextToken();
843 while (s ==
"|" || s ==
"UNION")
845 this.pos += s.Length;
846 Result =
new Asn1Union(Result, this.ParseIntersections());
847 s = this.PeekNextToken();
857 string s = this.PeekNextToken();
859 while (s ==
"^" || s ==
"INTERSECTION")
861 this.pos += s.Length;
862 Result =
new Asn1Union(Result, this.ParseIntervals());
863 s = this.PeekNextToken();
871 string s = this.PeekNextToken();
878 this.AssertNextToken(
")");
891 if (this.PeekNextToken() ==
"..")
903 private string ParseIdentifier()
905 string s = this.NextToken();
907 if (!IsIdentifier(s))
908 throw this.SyntaxError(
"Identifier expected.");
913 private string ParseTypeNameIdentifier()
915 string s = this.NextToken();
917 if (!IsTypeIdentifier(s))
918 throw this.SyntaxError(
"Type name identifier expected.");
923 private static bool IsIdentifier(
string s)
925 return !
string.IsNullOrEmpty(s) &&
char.IsLetter(s[0]);
928 private static bool IsTypeIdentifier(
string s)
931 return !
string.IsNullOrEmpty(s) &&
char.IsLetter(ch = s[0]) &&
char.IsUpper(ch);
934 private static bool IsFieldIdentifier(
string s)
937 return !
string.IsNullOrEmpty(s) &&
char.IsLetter(ch = s[0]) &&
char.IsLower(ch);
942 Asn1Node[] Values = this.ParseValues();
946 internal Asn1Type ParseType(
string Name,
bool TypeDef)
948 bool Implicit =
false;
950 switch (this.PeekNextToken())
958 Asn1Type Result = this.ParseDataType(Name, TypeDef);
959 Result.Implicit = Implicit;
965 s2 = this.PeekNextToken();
970 Result.Restriction = this.ParseRestriction();
976 List<Asn1NamedValue> NamedOptions =
new List<Asn1NamedValue>();
980 s2 = this.NextToken();
981 if (!IsFieldIdentifier(s2))
982 throw this.SyntaxError(
"Value name expected.");
984 if (this.PeekNextToken() ==
"(")
988 NamedOptions.Add(
new Asn1NamedValue(s2, this.ParseValue(),
this));
990 if (this.NextToken() !=
")")
991 throw this.SyntaxError(
") expected");
996 s2 = this.NextToken();
1002 Result.NamedOptions = NamedOptions.ToArray();
1006 throw this.SyntaxError(
"Unexpected token.");
1012 Result.Optional =
true;
1017 Result.Present =
true;
1022 Result.Absent =
true;
1027 Result.Optional =
true;
1028 Result.Default = this.ParseValue();
1033 Result.Unique =
true;
1042 private Asn1Type ParseDataType(
string Name,
bool TypeDef)
1044 string s = this.NextToken();
1045 if (
string.IsNullOrEmpty(s))
1046 throw this.SyntaxError(
"Unexpected end of file.");
1054 this.AssertNextToken(
"STRING");
1067 s = this.PeekNextToken();
1071 Asn1Node[] Nodes = this.ParseList();
1076 this.namedNodes[FieldDef.Name] = FieldDef;
1082 throw this.SyntaxError(
"{ expected.");
1094 s = this.PeekNextToken();
1098 Asn1Node[] Nodes = this.ParseValues();
1102 throw this.SyntaxError(
"{ expected.");
1104 case "GeneralizedTime":
1107 case "GeneralString":
1110 case "GraphicString":
1119 case "ISO646String":
1125 case "NumericString":
1129 this.AssertNextToken(
"IDENTIFIER");
1133 this.AssertNextToken(
"STRING");
1136 case "PrintableString":
1143 this.AssertNextToken(
"OID");
1146 case "RELATIVE-OID":
1150 s = this.PeekNextToken();
1154 Asn1Node[] Nodes = this.ParseList();
1155 return new Asn1Set(Name, TypeDef, Nodes);
1165 s = this.PeekNextToken();
1169 if (!(Size is
null))
1170 throw this.SyntaxError(
"SIZE already specified.");
1173 Size = this.ParseSet();
1181 throw this.SyntaxError(
"Unexpected token.");
1184 this.AssertNextToken(
"OF");
1187 throw this.SyntaxError(
"SIZE expected.");
1189 s = this.ParseTypeNameIdentifier();
1194 throw this.SyntaxError(
"{ expected.");
1197 s = this.PeekNextToken();
1201 Asn1Node[] Nodes = this.ParseList();
1214 s = this.PeekNextToken();
1218 if (!(Size is
null))
1219 throw this.SyntaxError(
"SIZE already specified.");
1222 Size = this.ParseSet();
1230 throw this.SyntaxError(
"Unexpected token.");
1234 if (this.NextToken() !=
"OF")
1235 throw this.SyntaxError(
"Unexpected token.");
1237 return new Asn1SequenceOf(Name, TypeDef, Size, this.ParseType(Name, TypeDef));
1243 case "TeletexString":
1249 case "UniversalString":
1258 case "VideotexString":
1261 case "VisibleString":
1264 case "ObjectDescriptor":
1271 throw this.SyntaxError(
"Token not implemented.");
1274 if (
char.IsUpper(s[0]))
1277 throw this.SyntaxError(
"Type name expected.");
1283 this.AssertNextToken(
"{");
1285 List<Asn1Node> Items =
new List<Asn1Node>();
1289 Items.Add(this.ParseStatement());
1291 switch (this.PeekNextToken())
1299 return Items.ToArray();
1302 throw this.SyntaxError(
", or } expected.");
1309 this.AssertNextToken(
"{");
1311 List<Asn1Node> Items =
new List<Asn1Node>();
1315 Items.Add(this.ParseValue());
1317 switch (this.PeekNextToken())
1325 return Items.ToArray();
1330 this.AssertNextToken(
")");
1332 int LastIndex = Items.Count - 1;
1335 Items[LastIndex] =
new Asn1NamedValue(Ref.Identifier, Value,
this);
1337 throw this.SyntaxError(
"Invalid value reference.");
1341 throw this.SyntaxError(
", or } expected.");
1348 return this.ParseValue(
true);
1351 private Asn1Value ParseValue(
bool AllowNamed)
1353 string s = this.PeekNextToken();
1354 if (
string.IsNullOrEmpty(s))
1355 throw this.SyntaxError(
"Expected value.");
1388 int Start = ++this.pos;
1391 while (this.pos < this.len && this.text[this.pos] !=
'"')
1394 if (this.pos >= this.len)
1395 throw this.SyntaxError(
"\" expected.");
1397 s = this.text.Substring(Start, this.pos - Start);
1405 while (this.pos < this.len && this.text[this.pos] !=
'\'')
1408 if (this.pos >= this.len)
1409 throw this.SyntaxError(
"' expected.");
1411 s = this.text.Substring(Start, this.pos - Start);
1414 switch (this.NextToken())
1419 if (
long.TryParse(s, NumberStyles.HexNumber,
null, out
long l))
1422 throw this.SyntaxError(
"Invalid hexadecimal string.");
1427 if (
long.TryParse(s, out l))
1430 throw this.SyntaxError(
"Invalid decimal string.");
1435 if (TryParseBinary(s, out l))
1438 throw this.SyntaxError(
"Invalid binary string.");
1443 if (TryParseOctal(s, out l))
1446 throw this.SyntaxError(
"Invalid octal string.");
1449 throw this.SyntaxError(
"Unexpected token.");
1455 List<Asn1Value> Items =
new List<Asn1Value>();
1462 s = this.PeekNextToken();
1476 return new Asn1Oid(Items.ToArray());
1481 if (Items.Count == 0)
1487 throw this.SyntaxError(
"Unexpected token.");
1493 if (
char.IsLetter(s[0]))
1495 this.pos += s.Length;
1497 switch (this.PeekNextToken())
1501 throw this.SyntaxError(
"Value expected.");
1504 Asn1Value Value = this.ParseValue(
false);
1516 if (!
char.IsUpper(s[0]))
1519 throw this.SyntaxError(
"Type references not permitted here.");
1526 bool Sign = s.StartsWith(
"-");
1533 if (ulong.TryParse(s, out ulong l))
1535 this.pos += s.Length;
1537 ch = this.PeekNextChar();
1539 if ((ch ==
'.' && this.pos < this.lenm1 && this.text[this.pos + 1] !=
'.') ||
1540 ch ==
'e' || ch ==
'E')
1546 DecPos = this.pos++;
1547 while (this.pos < this.len &&
char.IsDigit(ch = this.text[this.pos]))
1551 if (ch ==
'e' || ch ==
'E')
1555 if ((ch = this.PeekNextChar()) ==
'-' || ch ==
'+')
1558 while (this.pos < this.len &&
char.IsDigit(this.text[this.pos]))
1562 s = this.text.Substring(Start, this.pos - Start);
1564 string s2 = NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
1565 if (DecPos.HasValue && s2 !=
".")
1566 s = s.Replace(
".", s2);
1568 if (!
double.TryParse(s, out
double d))
1569 throw this.SyntaxError(
"Invalid floating-point number.");
1574 if (l <=
long.MaxValue)
1577 throw this.SyntaxError(
"Number does not fit into a 64-bit signed integer.");
1585 throw this.SyntaxError(
"Value expected.");
1588 private static bool TryParseBinary(
string s, out
long l)
1592 foreach (
char ch
in s)
1594 if (ch < '0' || ch >
'1')
1602 l |= (byte)(ch -
'0');
1608 private static bool TryParseOctal(
string s, out
long l)
1612 foreach (
char ch
in s)
1614 if (ch < '0' || ch >
'7')
1622 l |= (byte)(ch -
'0');
1635 StringBuilder Output =
new StringBuilder();
1637 return Output.ToString();
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;");
Represents an ASN.1 document.
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.
async Task< Asn1Document > CreateAsync(string Text, string Location, string[] ImportFolders)
Represents an ASN.1 document.
Asn1Definitions Root
ASN.1 Root node
string Location
Location of document.
string[] ImportFolders
Import folders.
Represents a ASN.1 CHOICE construct.
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.
string[] Identifiers
Identifiers to import.
async Task< Asn1Document > LoadDocument()
Loads the ASN.1 document to import.
string Module
Module reference.
Represents an ASN.1 module.
Base class for all ASN.1 nodes.
Abstract base class for ASN.1 restrictions.
Represents a ASN.1 SEQUENCE construct.
Represents a ASN.1 SEQUENCE OF construct.
Represents a ASN.1 SET construct.
Represents a ASN.1 SET OF construct.
Represents an ASN.1 Type definition.
Abstract base class for ASN.1 types.
virtual bool ConstructedType
If the type is a constructed type.
Abstract base class for values.
Abstract base class for sets of values
Supporting syntax in a macro
Abstract base class for user-defined parts in macros
Un-typed user-defined part.
Restricted to elements in set.
Either restriction applies
All elements (in current context).
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
OBJECT IDENTIFIER, or RELATIVE-OID
PrintableString a-z, A-Z, ' () +,-.?:/= and SPACE
TeletexString CCITT and T.101 character sets
Represents an ASN.1 type reference.
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 a named value.
Represents an ASN.1 Object ID
Represents a restricted ASN.1 value reference.
Represents an ASN.1 value reference.
Static class managing loading of resources stored as embedded resources or in content files.
static async Task< string > ReadAllTextAsync(string FileName)
Reads a text file asynchronously.
Asn1Tags
How ASN.1 tags are managed.
CSharpExportPass
Defines different C# export passes.
delegate string ToString(IElement Element)
Delegate for callback methods that convert an element value to a string.