3using System.Globalization;
11using System.Threading.Tasks;
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>();
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;
31 private int unnamedIndex = 1;
38 this.len = this.text.Length;
39 this.lenm1 = this.len - 1;
52 root = await this.ParseDefinitions()
66 public string Text => this.text;
90 internal void SkipWhiteSpace()
94 while (this.pos < this.len)
96 ch = this.text[this.pos];
98 if (ch <=
' ' || ch == (
char)160)
100 else if (ch ==
'-' && this.pos < this.lenm1 && this.text[this.pos + 1] ==
'-')
104 while (this.pos < this.len)
106 ch = this.text[this.pos];
107 if (ch ==
'\r' || ch ==
'\n')
110 if (ch ==
'-' && this.pos < this.len - 1 && this.text[this.pos + 1] ==
'-')
119 else if (ch ==
'/' && this.pos < this.lenm1 && this.text[this.pos + 1] ==
'*')
123 while ((this.pos < this.len && this.text[this.pos] !=
'*') ||
124 (this.pos < this.lenm1 && this.text[this.pos + 1] !=
'/'))
134 internal char NextChar()
136 if (this.pos < this.len)
137 return this.text[this.pos++];
142 internal char PeekNextChar()
144 if (this.pos < this.len)
145 return this.text[this.pos];
150 internal string NextToken()
152 this.SkipWhiteSpace();
154 char ch = this.NextChar();
156 if (
char.IsLetter(ch) ||
char.IsDigit(ch) || ch ==
'-' || ch ==
'_')
158 int Start = this.pos - 1;
160 while (this.pos < this.len && (
char.IsLetter(ch = this.text[this.pos]) ||
char.IsDigit(ch) || ch ==
'-' || ch ==
'_'))
163 return this.text.Substring(Start, this.pos - Start);
170 switch (this.PeekNextChar())
174 switch (this.PeekNextChar())
185 return new string(ch, 1);
189 if (this.PeekNextChar() ==
'.')
192 if (this.PeekNextChar() ==
'.')
204 if (this.PeekNextChar() ==
'[')
213 if (this.PeekNextChar() ==
']')
222 return new string(ch, 1);
227 internal string PeekNextToken()
229 this.SkipWhiteSpace();
232 string s = this.NextToken();
237 internal void AssertNextToken(
string ExpectedToken)
239 string s = this.NextToken();
240 if (s != ExpectedToken)
241 throw this.SyntaxError(ExpectedToken +
" expected.");
249 private async Task<Asn1Definitions> ParseDefinitions()
251 string Identifier = this.ParseTypeNameIdentifier();
252 string s = this.PeekNextToken();
257 Oid = this.ParseOid();
258 s = this.PeekNextToken();
261 if (s !=
"DEFINITIONS")
262 throw this.SyntaxError(
"DEFINITIONS expected.");
267 bool Abstract =
false;
271 switch (s = this.NextToken())
277 throw this.SyntaxError(
"TAGS already specified.");
294 this.AssertNextToken(
"TAGS");
297 case "ABSTRACT-SYNTAX":
306 throw this.SyntaxError(
"::= expected.");
315 return new Asn1Definitions(Identifier, Oid, Tags, Abstract, Body,
this);
318 private async Task<Asn1Module> ParseModule()
322 this.AssertNextToken(
"BEGIN");
324 List<Asn1Import> Imports =
null;
325 List<string> Exports =
null;
329 s = this.PeekNextToken();
336 Imports =
new List<Asn1Import>();
338 List<string> Identifiers =
new List<string>();
342 string Identifier = this.ParseIdentifier();
345 s = this.PeekNextToken();
350 Identifiers.Add(Identifier);
351 ModuleRef = this.ParseTypeNameIdentifier();
353 Imports.Add(
new Asn1Import(Identifiers.ToArray(), ModuleRef,
this));
356 s = this.PeekNextToken();
363 ModuleRef = Identifier;
364 Identifier = this.ParseIdentifier();
365 Imports.Add(
new Asn1Import(
new string[] { ModuleRef }, Identifier,
this));
366 s = this.PeekNextToken();
369 Identifiers.Add(Identifier);
382 throw this.SyntaxError(
"Unexpected token.");
386 if (Identifiers.Count > 0)
387 Imports.Add(
new Asn1Import(Identifiers.ToArray(),
string.Empty,
this));
395 if (!Doc.namedNodes.TryGetValue(Identifier, out
Asn1Node ImportedNode))
396 throw this.SyntaxError(Identifier +
" not found in " + Import.
Module);
398 this.namedNodes[Identifier] = ImportedNode;
401 this.aliases[Identifier] = TypeDef;
404 this.values[Identifier] = ValueDef;
408 else if (s ==
"EXPORTS")
413 Exports =
new List<string>();
417 string Identifier = this.ParseIdentifier();
418 Exports.Add(Identifier);
420 s = this.PeekNextToken();
433 throw this.SyntaxError(
"; expected");
442 List<Asn1Node> Items =
new List<Asn1Node>();
444 while ((s = this.PeekNextToken()) !=
"END")
446 if (
string.IsNullOrEmpty(s))
447 throw this.SyntaxError(
"END expected.");
449 Asn1Node Node = this.ParseStatement();
453 this.namedNodes[NamedNode.Name] = Node;
458 return new Asn1Module(Imports?.ToArray(), Exports?.ToArray(), Items.ToArray());
463 string s = this.PeekNextToken();
464 if (
string.IsNullOrEmpty(s))
465 throw this.SyntaxError(
"Unexpected end of file.");
470 if (
char.IsLetter(ch))
472 int PosBak = this.pos;
474 this.pos += s.Length;
475 s2 = this.PeekNextToken();
479 if (!
char.IsUpper(ch))
480 throw this.SyntaxError(
"XML notation not supported.");
482 this.pos += s2.Length;
484 s2 = this.PeekNextToken();
486 else if (s2 ==
"MACRO")
489 this.AssertNextToken(
"::=");
490 this.AssertNextToken(
"BEGIN");
491 this.AssertNextToken(
"TYPE");
492 this.AssertNextToken(
"NOTATION");
493 this.AssertNextToken(
"::=");
497 this.AssertNextToken(
"VALUE");
498 this.AssertNextToken(
"NOTATION");
499 this.AssertNextToken(
"::=");
504 while (this.PeekNextToken() !=
"END")
506 string Name = this.ParseIdentifier();
507 this.AssertNextToken(
"::=");
511 this.AssertNextToken(
"END");
515 else if (this.namedNodes.TryGetValue(s2, out
Asn1Node Node) &&
518 this.pos += s2.Length;
520 Asn1Value Value = Macro.ParseValue(
this);
523 this.values[s] = ValueDef;
529 if (
char.IsUpper(ch))
532 s =
"unnamed" + (this.unnamedIndex++).
ToString();
544 s2 = this.NextToken();
548 this.AssertNextToken(
":");
549 s2 = this.NextToken();
556 s2 = this.NextToken();
561 s2 = this.NextToken();
566 s2 = this.NextToken();
570 if (!
int.TryParse(s2, out
int i))
571 throw this.SyntaxError(
"Tag expected.");
574 i |= ((int)Class) << 6;
578 this.AssertNextToken(
"]");
580 s2 = this.PeekNextToken();
583 if (
char.IsUpper(ch))
587 if (this.namedNodes.TryGetValue(s2, out
Asn1Node Node) &&
590 this.pos += s2.Length;
591 Definition = Macro.ParseType(
this);
594 Definition = this.ParseType(s,
true);
599 this.aliases[s] = TypeDef;
605 if (!IsTypeIdentifier(s2))
606 throw this.SyntaxError(
"Type name expected.");
608 Asn1Type Type = this.ParseType(s,
false);
610 if (this.PeekNextToken() ==
"::=")
616 this.values[s] = ValueDef;
630 throw this.SyntaxError(
"Identifier expected.");
636 List<UserDefinedItem> Options =
null;
638 while (this.PeekNextToken() ==
"|")
643 Options =
new List<UserDefinedItem>();
646 Item = this.ParseUserDefinedOption(EndKeyWord);
661 List<UserDefinedItem> Items =
new List<UserDefinedItem>();
663 int PosBak = this.pos;
665 while ((s = this.PeekNextToken()) != EndKeyWord && s !=
"::=" && s !=
"|")
670 Items =
new List<UserDefinedItem>();
676 Item = this.ParseUserDefinedItem();
680 throw this.SyntaxError(
"Items expected.");
685 throw this.SyntaxError(
"Items expected.");
688 if (Items.Count == 1)
691 else if (Items is
null)
701 string s = this.PeekNextToken();
706 throw this.SyntaxError(
"String label expected.");
711 if (!IsIdentifier(s))
712 throw this.SyntaxError(
"Identifier or literal expected.");
714 this.pos += s.Length;
716 if (this.PeekNextToken() ==
"(")
720 string Name = this.ParseIdentifier();
721 if (this.PeekNextToken() ==
")")
728 Asn1Type Type = this.ParseType(Name,
false);
730 this.AssertNextToken(
")");
741 this.AssertNextToken(
"(");
743 this.AssertNextToken(
")");
752 string s = this.PeekNextToken();
757 Result =
new Asn1Or(Result, this.ParseAnds());
758 s = this.PeekNextToken();
768 string s = this.PeekNextToken();
773 Result =
new Asn1And(Result, this.ParseRestrictionRule());
774 s = this.PeekNextToken();
782 string s = this.PeekNextToken();
790 this.AssertNextToken(
")");
796 return new Asn1Size(this.ParseSet());
804 return new Asn1From(this.ParseSet());
812 this.AssertNextToken(
"BY");
818 this.AssertNextToken(
"COMPONENTS");
823 return new Asn1InSet(this.ParseUnions());
829 this.AssertNextToken(
"(");
831 this.AssertNextToken(
")");
838 Asn1Values Result = this.ParseIntersections();
840 string s = this.PeekNextToken();
842 while (s ==
"|" || s ==
"UNION")
844 this.pos += s.Length;
845 Result =
new Asn1Union(Result, this.ParseIntersections());
846 s = this.PeekNextToken();
856 string s = this.PeekNextToken();
858 while (s ==
"^" || s ==
"INTERSECTION")
860 this.pos += s.Length;
861 Result =
new Asn1Union(Result, this.ParseIntervals());
862 s = this.PeekNextToken();
870 string s = this.PeekNextToken();
877 this.AssertNextToken(
")");
890 if (this.PeekNextToken() ==
"..")
902 private string ParseIdentifier()
904 string s = this.NextToken();
906 if (!IsIdentifier(s))
907 throw this.SyntaxError(
"Identifier expected.");
912 private string ParseTypeNameIdentifier()
914 string s = this.NextToken();
916 if (!IsTypeIdentifier(s))
917 throw this.SyntaxError(
"Type name identifier expected.");
922 private static bool IsIdentifier(
string s)
924 return !
string.IsNullOrEmpty(s) &&
char.IsLetter(s[0]);
927 private static bool IsTypeIdentifier(
string s)
930 return !
string.IsNullOrEmpty(s) &&
char.IsLetter(ch = s[0]) &&
char.IsUpper(ch);
933 private static bool IsFieldIdentifier(
string s)
936 return !
string.IsNullOrEmpty(s) &&
char.IsLetter(ch = s[0]) &&
char.IsLower(ch);
941 Asn1Node[] Values = this.ParseValues();
945 internal Asn1Type ParseType(
string Name,
bool TypeDef)
947 bool Implicit =
false;
949 switch (this.PeekNextToken())
957 Asn1Type Result = this.ParseDataType(Name, TypeDef);
958 Result.Implicit = Implicit;
964 s2 = this.PeekNextToken();
969 Result.Restriction = this.ParseRestriction();
975 List<Asn1NamedValue> NamedOptions =
new List<Asn1NamedValue>();
979 s2 = this.NextToken();
980 if (!IsFieldIdentifier(s2))
981 throw this.SyntaxError(
"Value name expected.");
983 if (this.PeekNextToken() ==
"(")
987 NamedOptions.Add(
new Asn1NamedValue(s2, this.ParseValue(),
this));
989 if (this.NextToken() !=
")")
990 throw this.SyntaxError(
") expected");
995 s2 = this.NextToken();
1001 Result.NamedOptions = NamedOptions.ToArray();
1005 throw this.SyntaxError(
"Unexpected token.");
1011 Result.Optional =
true;
1016 Result.Present =
true;
1021 Result.Absent =
true;
1026 Result.Optional =
true;
1027 Result.Default = this.ParseValue();
1032 Result.Unique =
true;
1041 private Asn1Type ParseDataType(
string Name,
bool TypeDef)
1043 string s = this.NextToken();
1044 if (
string.IsNullOrEmpty(s))
1045 throw this.SyntaxError(
"Unexpected end of file.");
1053 this.AssertNextToken(
"STRING");
1066 s = this.PeekNextToken();
1070 Asn1Node[] Nodes = this.ParseList();
1075 this.namedNodes[FieldDef.Name] = FieldDef;
1081 throw this.SyntaxError(
"{ expected.");
1093 s = this.PeekNextToken();
1097 Asn1Node[] Nodes = this.ParseValues();
1101 throw this.SyntaxError(
"{ expected.");
1103 case "GeneralizedTime":
1106 case "GeneralString":
1109 case "GraphicString":
1118 case "ISO646String":
1124 case "NumericString":
1128 this.AssertNextToken(
"IDENTIFIER");
1132 this.AssertNextToken(
"STRING");
1135 case "PrintableString":
1142 this.AssertNextToken(
"OID");
1145 case "RELATIVE-OID":
1149 s = this.PeekNextToken();
1153 Asn1Node[] Nodes = this.ParseList();
1154 return new Asn1Set(Name, TypeDef, Nodes);
1164 s = this.PeekNextToken();
1168 if (!(Size is
null))
1169 throw this.SyntaxError(
"SIZE already specified.");
1172 Size = this.ParseSet();
1180 throw this.SyntaxError(
"Unexpected token.");
1183 this.AssertNextToken(
"OF");
1186 throw this.SyntaxError(
"SIZE expected.");
1188 s = this.ParseTypeNameIdentifier();
1193 throw this.SyntaxError(
"{ expected.");
1196 s = this.PeekNextToken();
1200 Asn1Node[] Nodes = this.ParseList();
1213 s = this.PeekNextToken();
1217 if (!(Size is
null))
1218 throw this.SyntaxError(
"SIZE already specified.");
1221 Size = this.ParseSet();
1229 throw this.SyntaxError(
"Unexpected token.");
1233 if (this.NextToken() !=
"OF")
1234 throw this.SyntaxError(
"Unexpected token.");
1236 return new Asn1SequenceOf(Name, TypeDef, Size, this.ParseType(Name, TypeDef));
1242 case "TeletexString":
1248 case "UniversalString":
1257 case "VideotexString":
1260 case "VisibleString":
1263 case "ObjectDescriptor":
1270 throw this.SyntaxError(
"Token not implemented.");
1273 if (
char.IsUpper(s[0]))
1276 throw this.SyntaxError(
"Type name expected.");
1282 this.AssertNextToken(
"{");
1284 List<Asn1Node> Items =
new List<Asn1Node>();
1288 Items.Add(this.ParseStatement());
1290 switch (this.PeekNextToken())
1298 return Items.ToArray();
1301 throw this.SyntaxError(
", or } expected.");
1308 this.AssertNextToken(
"{");
1310 List<Asn1Node> Items =
new List<Asn1Node>();
1314 Items.Add(this.ParseValue());
1316 switch (this.PeekNextToken())
1324 return Items.ToArray();
1329 this.AssertNextToken(
")");
1331 int LastIndex = Items.Count - 1;
1334 Items[LastIndex] =
new Asn1NamedValue(Ref.Identifier, Value,
this);
1336 throw this.SyntaxError(
"Invalid value reference.");
1340 throw this.SyntaxError(
", or } expected.");
1347 return this.ParseValue(
true);
1350 private Asn1Value ParseValue(
bool AllowNamed)
1352 string s = this.PeekNextToken();
1353 if (
string.IsNullOrEmpty(s))
1354 throw this.SyntaxError(
"Expected value.");
1387 int Start = ++this.pos;
1390 while (this.pos < this.len && this.text[this.pos] !=
'"')
1393 if (this.pos >= this.len)
1394 throw this.SyntaxError(
"\" expected.");
1396 s = this.text.Substring(Start, this.pos - Start);
1404 while (this.pos < this.len && this.text[this.pos] !=
'\'')
1407 if (this.pos >= this.len)
1408 throw this.SyntaxError(
"' expected.");
1410 s = this.text.Substring(Start, this.pos - Start);
1413 switch (this.NextToken())
1418 if (
long.TryParse(s, NumberStyles.HexNumber,
null, out
long l))
1421 throw this.SyntaxError(
"Invalid hexadecimal string.");
1426 if (
long.TryParse(s, out l))
1429 throw this.SyntaxError(
"Invalid decimal string.");
1434 if (TryParseBinary(s, out l))
1437 throw this.SyntaxError(
"Invalid binary string.");
1442 if (TryParseOctal(s, out l))
1445 throw this.SyntaxError(
"Invalid octal string.");
1448 throw this.SyntaxError(
"Unexpected token.");
1454 List<Asn1Value> Items =
new List<Asn1Value>();
1461 s = this.PeekNextToken();
1475 return new Asn1Oid(Items.ToArray());
1480 if (Items.Count == 0)
1486 throw this.SyntaxError(
"Unexpected token.");
1492 if (
char.IsLetter(s[0]))
1494 this.pos += s.Length;
1496 switch (this.PeekNextToken())
1500 throw this.SyntaxError(
"Value expected.");
1503 Asn1Value Value = this.ParseValue(
false);
1515 if (!
char.IsUpper(s[0]))
1518 throw this.SyntaxError(
"Type references not permitted here.");
1525 bool Sign = s.StartsWith(
"-");
1532 if (ulong.TryParse(s, out ulong l))
1534 this.pos += s.Length;
1536 ch = this.PeekNextChar();
1538 if ((ch ==
'.' && this.pos < this.lenm1 && this.text[this.pos + 1] !=
'.') ||
1539 ch ==
'e' || ch ==
'E')
1545 DecPos = this.pos++;
1546 while (this.pos < this.len &&
char.IsDigit(ch = this.text[this.pos]))
1550 if (ch ==
'e' || ch ==
'E')
1554 if ((ch = this.PeekNextChar()) ==
'-' || ch ==
'+')
1557 while (this.pos < this.len &&
char.IsDigit(this.text[this.pos]))
1561 s = this.text.Substring(Start, this.pos - Start);
1563 string s2 = NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
1564 if (DecPos.HasValue && s2 !=
".")
1565 s = s.Replace(
".", s2);
1567 if (!
double.TryParse(s, out
double d))
1568 throw this.SyntaxError(
"Invalid floating-point number.");
1573 if (l <=
long.MaxValue)
1576 throw this.SyntaxError(
"Number does not fit into a 64-bit signed integer.");
1584 throw this.SyntaxError(
"Value expected.");
1587 private static bool TryParseBinary(
string s, out
long l)
1591 foreach (
char ch
in s)
1593 if (ch < '0' || ch >
'1')
1601 l |= (byte)(ch -
'0');
1607 private static bool TryParseOctal(
string s, out
long l)
1611 foreach (
char ch
in s)
1613 if (ch < '0' || ch >
'7')
1621 l |= (byte)(ch -
'0');
1634 StringBuilder Output =
new StringBuilder();
1636 return Output.ToString();
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;");
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 async Task< string > ReadAllTextAsync(string FileName)
Reads a text file asynchronously.
class Set(Array Elements, byte[] SubSection)
A generic set class used if dedicated security objects cannot be found.
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.