Skip to content

Commit 7c2cdb3

Browse files
authored
Update for the KDL 1.0 spec (changes ported across from kdl4j with enhancements)
* Ported KDL1.0 changes across but unit tests are broken * Port a minor syntax fix from kdl4j * Fix BOM on unit tests/KDL output, and import a whole ton of example testcases from kdl4j * import all testcases from main KDL repo * Fix parsing booleans with type annotations, and some compiler warnings * Split out KDL types into their own files * Implement BigInteger support for KDL with radix preservation * More test compliance around string escaping * Radix stuff for conformance testing * more numeric compliance * Final compliance tests. Extremely large/small scientific notation values are out of scope for KDL.NET at this time * Update README.md * Update KDL config for KdlDotNet1.1.0 * Create .gitattributes
1 parent af05574 commit 7c2cdb3

File tree

415 files changed

+1757
-640
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

415 files changed

+1757
-640
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* -text

KdlDotNet.Extensions.Configuration/src/KdlDotNet.Extensions.Configuration.csproj

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,20 @@
66
<LangVersion>8.0</LangVersion>
77
<AssemblyName>KdlDotNet.Extensions.Configuration</AssemblyName>
88

9-
<PackageVersion>5.0.1</PackageVersion>
10-
<AssemblyVersion>5.0.1</AssemblyVersion>
11-
<FileVersion>5.0.1</FileVersion>
12-
<Version>5.0.1</Version>
9+
<PackageVersion>5.1.0</PackageVersion>
10+
<AssemblyVersion>5.1.0</AssemblyVersion>
11+
<FileVersion>5.1.0</FileVersion>
12+
<Version>5.1.0</Version>
1313
<Authors>Orion Edwards</Authors>
1414
<Company />
1515
<PackageLicenseExpression>CC-BY-4.0</PackageLicenseExpression>
1616
<PackageProjectUrl>https://github.com/borland/kdl-net</PackageProjectUrl>
1717
<Description>ASP.NET Core Configuration provider for KDL files; .NET 5 distribution</Description>
18-
<Copyright>Orion Edwards 2020</Copyright>
18+
<Copyright>Orion Edwards 2021</Copyright>
1919
</PropertyGroup>
2020

2121
<ItemGroup>
22-
<PackageReference Include="KdlDotNet" Version="1.0.1" />
22+
<PackageReference Include="KdlDotNet" Version="1.1.0" />
2323
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
2424
<PackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="5.0.0" />
2525
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="5.0.0" />

README.md

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ This is a C# implementation of a parser for the [KDL Document Language](https://
55
It is semi-literally ported from the Java KDL parser implementation [kdl4j](https://github.com/hkolbeck/kdl4j) by [Hannah Kolbeck](https://github.com/hkolbeck).
66
Many thanks for the original implementation.
77

8+
## Status
9+
10+
**The library is up to date as of the KDL 1.0.0. spec.** All testcases pass, except for scientific-notation numeric values outside the range representable by a `System.Double`, which KdlDotNet does not support at this time.
11+
12+
The scope of the library is small and it has what I would consider excellent unit test coverage.
13+
14+
I would be happy to use it in production in a constrained environment, such as loading configuration files, etc. It hasn't had proper performance benchmarking
15+
or profiling, so it is probably not suitable for use in a performance-critical codepath such as a tight loop.
16+
817
## Platform
918

1019
The library is built against .NET Standard 2.0, which means it should work on the .NET Desktop framework 4.7.2 or later, and .NET Core 2.0 and later. The unit tests run against both .NET 4.7.2 and .NET Core 3.1 and all passs.
@@ -17,15 +26,6 @@ Install the [KdlDotNet Nuget package](https://www.nuget.org/packages/KdlDotNet),
1726

1827
KDL-net is licensed under the Creative Commons Attribution 4.0 License.
1928

20-
## Status
21-
22-
The scope of the library is small and it has what I would consider excellent unit test coverage.
23-
24-
I would be happy to use it in production in a constrained environment, such as loading configuration files, etc. It hasn't had any proper performance benchmarking
25-
or profiling, so it is probably not suitable for use in a performance-critical codepath such as a tight loop.
26-
27-
One Caveat: The KDL language spec itself is not final and may still yet change so I can't in all honesty say this is a production library. In my personal view, the spec looks pretty solid, and if it does change, I wouldn't expect it to do so in a major breaking way; rather I'd expect simple clarification of edge cases and bug-fixes. Hopefully nothing dramatic.
28-
2929
## Why KDL?
3030

3131
I have successfully used this library to configure and boot up an ASP.NET Core application, using KDL in place of what would usually be a JSON file. KDL fits very nicely in this kind of environment. I've created an extension library Nuget Package called [KdlDotNet.Extensions.Configuration](https://www.nuget.org/packages/KdlDotNet.Extensions.Configuration/) to help with this.
@@ -89,7 +89,7 @@ If you want to do something other than configure asp.net core applications, you'
8989
Add a nuget package reference to the [KdlDotNet](https://www.nuget.org/packages/KdlDotNet/) package
9090

9191
```xml
92-
<PackageReference Include="KdlDotNet" Version="1.0.0" />
92+
<PackageReference Include="KdlDotNet" Version="1.1.0" />
9393
```
9494

9595
Then create a new instance of `KdlDotNet.KDLParser` and call the `Parse` method, either on a `Stream` if you have one from a file/network, or from a `string`.
@@ -113,8 +113,10 @@ kdl4j uses the java BigDecimal type as the internal representation for all numbe
113113
Presumably this was done for ease of implementation as it lets kdl4j have a simpler code path for handling numbers,
114114
however BigDecimal is a large and complex thing, weighing in at a minimum of 32 bytes per instance (over and above the surrounding KDLNumber object).
115115

116-
kdl-net in contrast uses an 8 byte storage structure, which is a union containing either an int32, int64, or double (8 byte floating point) value.
117-
This means kdl-net can't handle numbers that exceed the size of an int64 or double, however it should require a lot less memory and be faster.
118-
Given that the primary use-case for KDL at this stage seems to be human-readable configuration, this seems like a better tradeoff.
116+
KdlDotNet in contrast uses a hierarchy of classes derived from an abstract `KDLNumber` class, each of which uses different backing storage.
117+
When parsing KDL documents, KdlDotNet will attempt to use the smallest integer type in the series of `Int32`, `Int64`, `BigInteger`.
118+
All floating point or scientific-notation values are stored using `Double`.
119+
120+
This means KdlDotNet should require a lot less memory and be faster when dealing with numeric values.
119121

120122
**PULL REQUESTS APPRECIATED**

src/CharClasses.cs

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,18 @@ public static bool IsValidNumericStart(int c)
4646
*/
4747
public static bool IsValidBareIdChar(int c)
4848
{
49+
if (c <= 0x20 || c > 0x10FFFF)
50+
return false;
51+
4952
switch (c)
5053
{
51-
case '\n':
52-
case '\u000C':
53-
case '\r':
5454
case '\u0085':
5555
case '\u2028':
5656
case '\u2029':
5757
case '\\':
58+
case '/':
59+
case '(':
60+
case ')':
5861
case '{':
5962
case '}':
6063
case '<':
@@ -65,8 +68,6 @@ public static bool IsValidBareIdChar(int c)
6568
case '=':
6669
case ',':
6770
case '"':
68-
case '\u0009':
69-
case '\u0020':
7071
case '\u00A0':
7172
case '\u1680':
7273
case '\u2000':
@@ -95,15 +96,41 @@ public static bool IsValidBareIdChar(int c)
9596
* @param c the character to check
9697
* @return true if the character is valid, false otherwise
9798
*/
98-
public static bool IsValidBareIdStart(int c) => !IsValidDecimalChar(c) && IsValidBareIdChar(c);
99+
public static bool IsValidBareIdStart(int c)
100+
=> !IsValidDecimalChar(c) && IsValidBareIdChar(c);
101+
102+
/**
103+
* Check if a string is a valid bare identifier
104+
*
105+
* @param string the string to check
106+
* @return true if the string is a valid bare id, false otherwise
107+
*/
108+
public static bool IsValidBareId(string str)
109+
{
110+
if (str == "")
111+
return false;
112+
113+
var validBareIdStart = IsValidBareIdStart(str[0]);
114+
if (str.Length == 1 || !validBareIdStart)
115+
return validBareIdStart;
116+
117+
for (int i = 0; i < str.Length; i++)
118+
{
119+
if (!IsValidBareIdChar(str[i]))
120+
return false;
121+
}
122+
123+
return true;
124+
}
99125

100126
/**
101127
* Check if the character is a valid decimal digit
102128
*
103129
* @param c the character to check
104130
* @return true if the character is valid, false otherwise
105131
*/
106-
public static bool IsValidDecimalChar(int c) => ('0' <= c && c <= '9');
132+
public static bool IsValidDecimalChar(int c)
133+
=> ('0' <= c && c <= '9');
107134

108135
/**
109136
* Check if the character is a valid hexadecimal digit
@@ -149,15 +176,17 @@ public static bool IsValidHexChar(int c)
149176
* @param c the character to check
150177
* @return true if the character is valid, false otherwise
151178
*/
152-
public static bool IsValidOctalChar(int c) => ('0' <= c && c <= '7');
179+
public static bool IsValidOctalChar(int c)
180+
=> ('0' <= c && c <= '7');
153181

154182
/**
155183
* Check if the character is a valid binary digit
156184
*
157185
* @param c the character to check
158186
* @return true if the character is valid, false otherwise
159187
*/
160-
public static bool IsValidBinaryChar(int c) => (c == '0' || c == '1');
188+
public static bool IsValidBinaryChar(int c)
189+
=> (c == '0' || c == '1');
161190

162191
/**
163192
* Check if the character is contained in one of the three literal values: true, false, and null
@@ -247,9 +276,13 @@ public static bool IsUnicodeWhitespace(int c)
247276
* @return true if the character is printable unescaped, false otherwise
248277
*/
249278
public static bool IsPrintableAscii(int c)
250-
{
251-
return ' ' <= c && c <= '~' && c != '/' && c != '"';
252-
}
279+
=> (' ' <= c && c <= '~');
280+
281+
public static bool IsNonAscii(int c)
282+
=> c > 127;
283+
284+
public static bool MustEscape(int c)
285+
=> c == '\\' || c == '"';
253286

254287
private static readonly string ESC_BACKSLASH = "\\\\";
255288
private static readonly string ESC_BACKSPACE = "\\b";
@@ -266,7 +299,8 @@ public static bool IsPrintableAscii(int c)
266299
* @param c the character to check
267300
* @return An Optional wrapping the escape sequence string if the character needs to be escaped, or false otherwise
268301
*/
269-
public static string? GetCommonEscape(int c) => c switch {
302+
public static string? GetCommonEscape(int c) => c switch
303+
{
270304
'\\' => ESC_BACKSLASH,
271305
'\b' => ESC_BACKSPACE,
272306
'\n' => ESC_NEWLINE,
@@ -278,12 +312,30 @@ public static bool IsPrintableAscii(int c)
278312
_ => null,
279313
};
280314

315+
public static bool IsCommonEscape(int c)
316+
{
317+
switch (c)
318+
{
319+
case '\\':
320+
case '\b':
321+
case '\n':
322+
case '\f':
323+
case '\t':
324+
case '\r':
325+
case '"':
326+
return true;
327+
default:
328+
return false;
329+
}
330+
}
331+
281332
/**
282333
* Get the escape sequence for any character
283334
*
284335
* @param c the character to check
285336
* @return The escape sequence string
286337
*/
287-
public static string GetEscapeIncludingUnicode(int c) => GetCommonEscape(c) ?? string.Format("\\u{0:x}", c);
338+
public static string GetEscapeIncludingUnicode(int c)
339+
=> GetCommonEscape(c) ?? $"\\u{c:x}";
288340
}
289341
}

src/KDLBoolean.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System.IO;
2+
3+
#nullable enable
4+
5+
namespace KdlDotNet
6+
{
7+
/**
8+
* A KDL object holding a boolean value. New instances should not be created, instead use the TRUE or FALSE constants
9+
*/
10+
public class KDLBoolean : KDLValue<bool>
11+
{
12+
public static KDLBoolean? FromString(string str, string? type) => str switch
13+
{
14+
"true" => type == null ? True : new KDLBoolean(true, type),
15+
"false" => type == null ? False : new KDLBoolean(false, type),
16+
_ => null,
17+
};
18+
19+
public static KDLBoolean From(bool b, string? type = null)
20+
=> type == null ?
21+
b ? True : False :
22+
new KDLBoolean(b, type);
23+
24+
public static KDLBoolean True { get; } = new KDLBoolean(true);
25+
public static KDLBoolean False { get; } = new KDLBoolean(false);
26+
27+
private static readonly KDLString trueStr = KDLString.From("true");
28+
private static readonly KDLString falseStr = KDLString.From("false");
29+
30+
public KDLBoolean(bool value) : this(value, null) { }
31+
32+
public KDLBoolean(bool value, string? type) : base(type)
33+
=> Value = value;
34+
35+
public override bool Value { get; }
36+
37+
public override KDLString AsString() => Value ? trueStr : falseStr;
38+
public override KDLNumber? AsNumber() => null;
39+
public override KDLBoolean? AsBoolean() => this;
40+
41+
protected override void WriteKDLValue(StreamWriter writer, PrintConfig printConfig)
42+
=> writer.Write(Value ? "true" : "false");
43+
44+
public override bool IsBoolean => true;
45+
46+
public override string ToString() => $"KDLBoolean{{value='{Value}', type={Type}}}";
47+
48+
public override bool Equals(object? obj)
49+
=> obj is KDLBoolean other && other.Value == Value && Type == other.Type;
50+
51+
public override int GetHashCode() => Value.GetHashCode();
52+
}
53+
}

src/KDLDocument.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public void WriteKDLPretty(StreamWriter writer) // throws IOException
6262
public string ToKDLPretty(PrintConfig printConfig)
6363
{
6464
var writer = new MemoryStream();
65-
var bufferedWriter = new StreamWriter(writer, Encoding.UTF8);
65+
var bufferedWriter = new StreamWriter(writer, KDLObjectExtensions.UTF8NoByteOrderMarkerEncoding); // no BOM
6666

6767
WriteKDLPretty(bufferedWriter, printConfig);
6868
bufferedWriter.Flush();

0 commit comments

Comments
 (0)