diff --git a/.build/assets/icon.ico b/.build/assets/icon.ico index 60e22b4..fe7b1ff 100644 Binary files a/.build/assets/icon.ico and b/.build/assets/icon.ico differ diff --git a/.build/assets/icon.png b/.build/assets/icon.png index 3605d76..6b6b1ae 100644 Binary files a/.build/assets/icon.png and b/.build/assets/icon.png differ diff --git a/.build/targets.cs b/.build/targets.cs index 1d9a4dd..653fa34 100644 --- a/.build/targets.cs +++ b/.build/targets.cs @@ -37,7 +37,7 @@ { const string configuration = "Release"; const string solution = "Kuddle.slnx"; - const string packProject = "src/Kuddle/Kuddle.csproj"; + const string packProject = "src/Kuddle.Net/Kuddle.Net.csproj"; var root = Directory.GetCurrentDirectory(); diff --git a/AGENTS.md b/AGENTS.md index 1514a95..67bdef9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,4 @@ -# Agent Guidelines for Kuddle +# Agent Guidelines for Kuddle.Net ## Build Commands diff --git a/Kuddle.slnx b/Kuddle.slnx index d6f6259..af84070 100644 --- a/Kuddle.slnx +++ b/Kuddle.slnx @@ -4,10 +4,8 @@ - - - - + + + - diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..a56c6b1 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 jamesshenry + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/docs/serialization-attributes.md b/docs/serialization-attributes.md new file mode 100644 index 0000000..9b9a544 --- /dev/null +++ b/docs/serialization-attributes.md @@ -0,0 +1,453 @@ +# Attribute Usage + +This document describes how to use Kuddle's serialization attributes to map between KDL documents and C# types. + +## Quick Reference + +| Attribute | Target | Purpose | +| ---------------------- | -------- | ------------------------------- | +| `[KdlType]` | Class | Override the expected node name | +| `[KdlArgument(index)]` | Property | Map to a positional argument | +| `[KdlProperty(key?)]` | Property | Map to a named property | +| `[KdlNode(name?)]` | Property | Map to child nodes | +| `[KdlIgnore]` | Property | Exclude from serialization | + +--- + +## Node Name Matching + +### Default Convention + +By default, the class name (lowercased) must match the KDL node name: + +```csharp +// Matches: package "my-lib" +public class Package { ... } +``` + +### Custom Node Name with `[KdlType]` + +Override the expected node name: + +```csharp +// Matches: db "primary" (not "database") +[KdlType("db")] +public class Database { ... } +``` + +--- + +## `[KdlArgument(index)]` — Positional Arguments + +Maps a property to a **positional argument** by zero-based index. + +### KDL + +```kdl +point 10 20 30 label="origin" +``` + +### C# Model + +```csharp +public class Point +{ + [KdlArgument(0)] + public int X { get; set; } + + [KdlArgument(1)] + public int Y { get; set; } + + [KdlArgument(2)] + public int Z { get; set; } + + [KdlProperty("label")] + public string? Label { get; set; } +} +``` + +### Rules + +1. **Index is required** — Each `[KdlArgument]` must specify its position +2. **Indices should be contiguous** — Gaps (0, 2 without 1) may cause errors +3. **Order matters** — During serialization, arguments are written in index order +4. **Scalar types only** — Arguments cannot be complex objects + +### Supported Types + +- `string` +- `int`, `long`, `double`, `decimal` +- `bool` +- `Guid` (with `(uuid)` type annotation) +- `DateTimeOffset` (with `(date-time)` type annotation) +- Nullable variants of all above + +--- + +## `[KdlProperty(key?)]` — Named Properties + +Maps a property to a **KDL property** (key=value pair). + +### KDL + +```kdl +dependency lodash version="4.17.21" optional=#false +``` + +### C# Model + +```csharp +public class Dependency +{ + [KdlArgument(0)] + public string Package { get; set; } = ""; + + [KdlProperty("version")] + public string Version { get; set; } = "*"; + + [KdlProperty("optional")] + public bool Optional { get; set; } +} +``` + +### Key Name Resolution + +1. **Explicit key**: `[KdlProperty("my-key")]` → matches `my-key=...` +2. **Implicit key**: `[KdlProperty]` → uses property name lowercased + +```csharp +[KdlProperty] // Matches "timeout=..." +public int Timeout { get; set; } + +[KdlProperty("max-retries")] // Matches "max-retries=..." +public int MaxRetries { get; set; } +``` + +### Rules + +1. **Last value wins** — Per KDL spec, if `key=1 key=2`, the value is `2` +2. **Missing properties use defaults** — No error if property absent in KDL +3. **Scalar types only** — Properties cannot be complex objects + +--- + +## `[KdlNode(name?)]` — Child Nodes + +Maps a property to **child nodes** within the parent's `{ }` block. + +### Basic Usage — Collection of Child Nodes + +#### KDL + +```kdl +project web-app version="1.0.0" { + dependency lodash version="4.17.21" + dependency react version="18.2.0" + devDependency jest version="29.0.0" +} +``` + +#### C# Model + +```csharp +public class Project +{ + [KdlArgument(0)] + public string Name { get; set; } = ""; + + [KdlProperty("version")] + public string Version { get; set; } = "1.0.0"; + + [KdlNode("dependency")] + public List Dependencies { get; set; } = []; + + [KdlNode("devDependency")] + public List DevDependencies { get; set; } = []; +} +``` + +### Single Complex Child + +When the property type is a **non-collection complex type**, it maps to a single child node: + +#### KDL + +```kdl +application myapp { + database { + host "localhost" + port 5432 + } +} +``` + +#### C# Model + +```csharp +public class Application +{ + [KdlArgument(0)] + public string Name { get; set; } = ""; + + [KdlNode("database")] + public DatabaseConfig? Database { get; set; } +} + +public class DatabaseConfig +{ + [KdlNode("host")] // Maps child node's Arg(0) + public string Host { get; set; } = ""; + + [KdlNode("port")] // Maps child node's Arg(0) + public int Port { get; set; } +} +``` + +### Scalar Child Node + +When the property type is a **scalar type**, it extracts `Arg(0)` from the child node: + +#### KDL + +```kdl +config { + timeout 5000 + enabled #true +} +``` + +#### C# Model + +```csharp +public class Config +{ + [KdlNode("timeout")] + public int Timeout { get; set; } + + [KdlNode("enabled")] + public bool Enabled { get; set; } +} +``` + +### Node Name Resolution + +1. **Explicit name**: `[KdlNode("my-items")]` → matches child nodes named `my-items` +2. **Implicit name**: `[KdlNode]` → uses property name lowercased + +### Rules + +1. **Collection types** → Collects all matching child nodes into the list/array +2. **Complex types** → Expects exactly one matching child node (throws if multiple) +3. **Scalar types** → Extracts `Arg(0)` from the single matching child node +4. **Supported collection types**: `List`, `T[]`, `IEnumerable`, `IList` + +--- + +## `[KdlIgnore]` — Exclude Properties + +Excludes a property from both serialization and deserialization: + +```csharp +public class User +{ + [KdlArgument(0)] + public string Username { get; set; } = ""; + + [KdlIgnore] + public string ComputedDisplayName => Username.ToUpperInvariant(); + + [KdlIgnore] + public DateTime LoadedAt { get; set; } = DateTime.UtcNow; +} +``` + +--- + +## Type Conversion + +### Automatic Type Mapping + +| C# Type | KDL Representation | +| ------------------- | ----------------------------------- | +| `string` | `"value"` or `bare-string` | +| `int`, `long` | `123`, `0xFF`, `0o77`, `0b1010` | +| `double`, `decimal` | `3.14`, `1.5e-10` | +| `bool` | `#true`, `#false` | +| `Guid` | `(uuid)"550e8400-..."` | +| `DateTimeOffset` | `(date-time)"2024-01-15T10:30:00Z"` | +| Nullable `T?` | Value or `#null` | + +### Type Annotations + +Type annotations in KDL provide parsing hints. The serializer recognizes: + +- `(uuid)` → `Guid` +- `(date-time)` → `DateTimeOffset` + +```kdl +user alice { + id (uuid)"550e8400-e29b-41d4-a716-446655440000" + createdAt (date-time)"2024-01-15T10:30:00Z" +} +``` + +--- + +## Document-Level vs Node-Level Deserialization + +### Single Node → Object + +When your type has `[KdlArgument]` or `[KdlProperty]` attributes, the deserializer expects **exactly one root node**: + +```csharp +var kdl = "package my-lib version=\"1.0\""; +var pkg = KdlSerializer.Deserialize(kdl); +``` + +### Multiple Nodes → Collection + +Use `DeserializeMany` for documents with multiple top-level nodes: + +```kdl +server web-1 host="10.0.0.1" +server web-2 host="10.0.0.2" +server api-1 host="10.0.1.1" +``` + +```csharp +var servers = KdlSerializer.DeserializeMany(kdl); +// Returns IEnumerable with 3 items +``` + +### Document as Container + +For a document where each node type maps to a different property: + +```kdl +name "my-project" +version "1.0.0" +author "Alice" +``` + +```csharp +public class Manifest +{ + [KdlNode("name")] + public string Name { get; set; } = ""; + + [KdlNode("version")] + public string Version { get; set; } = ""; + + [KdlNode("author")] + public string Author { get; set; } = ""; +} +``` + +--- + +## Complete Example + +### KDL Document + +```kdl +project web-app version="2.0.0" { + dependency lodash version="4.17.21" optional=#false + dependency react version="18.2.0" optional=#false + + devDependency jest version="29.0.0" + devDependency typescript version="5.0.0" + + author "Alice" { + email "alice@example.com" + url "https://github.com/alice" + } + + repository type="git" url="https://github.com/alice/web-app" +} +``` + +### C# Models + +```csharp +public class Project +{ + [KdlArgument(0)] + public string Name { get; set; } = ""; + + [KdlProperty("version")] + public string Version { get; set; } = "1.0.0"; + + [KdlNode("dependency")] + public List Dependencies { get; set; } = []; + + [KdlNode("devDependency")] + public List DevDependencies { get; set; } = []; + + [KdlNode("author")] + public Author? Author { get; set; } + + [KdlNode("repository")] + public Repository? Repository { get; set; } +} + +public class Dependency +{ + [KdlArgument(0)] + public string Package { get; set; } = ""; + + [KdlProperty("version")] + public string Version { get; set; } = "*"; + + [KdlProperty("optional")] + public bool Optional { get; set; } +} + +public class Author +{ + [KdlArgument(0)] + public string Name { get; set; } = ""; + + [KdlNode("email")] + public string? Email { get; set; } + + [KdlNode("url")] + public string? Url { get; set; } +} + +public class Repository +{ + [KdlProperty("type")] + public string Type { get; set; } = ""; + + [KdlProperty("url")] + public string Url { get; set; } = ""; +} +``` + +--- + +## Decision Tree: Which Attribute to Use? + +``` +Is the data a positional value after the node name? +├─ YES → [KdlArgument(index)] +└─ NO + │ + Is the data a key=value pair on the same line? + ├─ YES → [KdlProperty("key")] + └─ NO + │ + Is the data inside a { } children block? + ├─ YES → [KdlNode("childNodeName")] + └─ NO → Not mappable with current attributes +``` + +--- + +## Limitations & Notes + +1. **No dictionary support** — `Dictionary` types are not currently supported +2. **No polymorphism** — Cannot deserialize to derived types based on discriminator +3. **Case sensitivity** — Node/property name matching is case-insensitive by default +4. **Argument gaps** — Missing argument indices will throw; ensure contiguous indices +5. **Round-trip fidelity** — Comments, formatting, and slashdash elements are not preserved diff --git a/readme.md b/readme.md index d85a754..98a21f3 100644 --- a/readme.md +++ b/readme.md @@ -1,188 +1,29 @@ -```text -document := bom? version? nodes - -// Nodes -nodes := (line-space* node)* line-space* - -base-node := slashdash? type? node-space* string - (node-space+ slashdash? node-prop-or-arg)* - // slashdashed node-children must always be after props and args. - (node-space+ slashdash node-children)* - (node-space+ node-children)? - (node-space+ slashdash node-children)* - node-space* -node := base-node node-terminator -final-node := base-node node-terminator? - -// Entries -node-prop-or-arg := prop | value -node-children := '{' nodes final-node? '}' -node-terminator := single-line-comment | newline | ';' | eof - -prop := string node-space* '=' node-space* value -value := type? node-space* (string | number | keyword) -type := '(' node-space* string node-space* ')' - -// Strings -string := identifier-string | quoted-string | raw-string ¶ - -identifier-string := unambiguous-ident | signed-ident | dotted-ident -unambiguous-ident := - ((identifier-char - digit - sign - '.') identifier-char*) - - disallowed-keyword-strings -signed-ident := - sign ((identifier-char - digit - '.') identifier-char*)? -dotted-ident := - sign? '.' ((identifier-char - digit) identifier-char*)? -identifier-char := - unicode - unicode-space - newline - [\\/(){};\[\]"#=] - - disallowed-literal-code-points -disallowed-keyword-identifiers := - 'true' | 'false' | 'null' | 'inf' | '-inf' | 'nan' - -quoted-string := - '"' single-line-string-body '"' | - '"""' newline - (multi-line-string-body newline)? - (unicode-space | ws-escape)* '"""' -single-line-string-body := (string-character - newline)* -multi-line-string-body := (('"' | '""')? string-character)* -string-character := - '\\' (["\\bfnrts] | - 'u{' hex-unicode '}') | - ws-escape | - [^\\"] - disallowed-literal-code-points -ws-escape := '\\' (unicode-space | newline)+ -hex-digit := [0-9a-fA-F] -hex-unicode := hex-digit{1, 6} - surrogates -surrogates := [dD][8-9a-fA-F]hex-digit{2} -// U+D800-DFFF: D 8 00 -// D F FF - -raw-string := '#' raw-string-quotes '#' | '#' raw-string '#' -raw-string-quotes := - '"' single-line-raw-string-body '"' | - '"""' newline - (multi-line-raw-string-body newline)? - unicode-space* '"""' -single-line-raw-string-body := - '' | - (single-line-raw-string-char - '"') - single-line-raw-string-char*? | - '"' (single-line-raw-string-char - '"') - single-line-raw-string-char*? -single-line-raw-string-char := - unicode - newline - disallowed-literal-code-points -multi-line-raw-string-body := - (unicode - disallowed-literal-code-points)*? - -// Numbers -number := keyword-number | hex | octal | binary | decimal +# Kuddle.Net -decimal := sign? integer ('.' integer)? exponent? -exponent := ('e' | 'E') sign? integer -integer := digit (digit | '_')* -digit := [0-9] -sign := '+' | '-' +Kuddle.Net is a .NET implementation of a [KDL](https://kdl.dev) parser/serializer targeting [v2](https://kdl.dev/spec/) of the spec. +KDL is concise, human-readable language built for configuration and data exchange. Head to for more specifics on the KDL document language itself. -hex := sign? '0x' hex-digit (hex-digit | '_')* -octal := sign? '0o' [0-7] [0-7_]* -binary := sign? '0b' ('0' | '1') ('0' | '1' | '_')* +## Installation -// Keywords and booleans. -keyword := boolean | '#null' -keyword-number := '#inf' | '#-inf' | '#nan' -boolean := '#true' | '#false' - -// Specific code points -bom := '\u{FEFF}' -disallowed-literal-code-points := - See Table (Disallowed Literal Code Points) -unicode := Any Unicode Scalar Value -unicode-space := See Table - (All White_Space unicode characters which are not `newline`) - -// Comments -single-line-comment := '//' ^newline* (newline | eof) -multi-line-comment := '/*' commented-block -commented-block := - '*/' | (multi-line-comment | '*' | '/' | [^*/]+) commented-block -slashdash := '/-' line-space* - -// Whitespace -ws := unicode-space | multi-line-comment -escline := '\\' ws* (single-line-comment | newline | eof) -newline := See Table (All Newline White_Space) -// Whitespace where newlines are allowed. -line-space := node-space | newline | single-line-comment -// Whitespace within nodes, -// where newline-ish things must be esclined. -node-space := ws* escline ws* | ws+ - -// Version marker -version := - '/-' unicode-space* 'kdl-version' unicode-space+ ('1' | '2') - unicode-space* newline +```text +dotnet add package Kuddle.Net ``` -## Whitespace +## Usage -The following characters should be treated as non-Newline ({{newline}}) [white -space](https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt): +There are a few ways of using the library, the lower level `KdlReader` and `KdlWriter` classes, and the utility `KdlSerializer` class. For most use cases `KdlSerializer.Serialize`/`KdlSerializer.Deserialize` will be sufficient. -| Name | Code Pt | -| ------------------------- | -------- | -| Character Tabulation | `U+0009` | -| Space | `U+0020` | -| No-Break Space | `U+00A0` | -| Ogham Space Mark | `U+1680` | -| En Quad | `U+2000` | -| Em Quad | `U+2001` | -| En Space | `U+2002` | -| Em Space | `U+2003` | -| Three-Per-Em Space | `U+2004` | -| Four-Per-Em Space | `U+2005` | -| Six-Per-Em Space | `U+2006` | -| Figure Space | `U+2007` | -| Punctuation Space | `U+2008` | -| Thin Space | `U+2009` | -| Hair Space | `U+200A` | -| Narrow No-Break Space | `U+202F` | -| Medium Mathematical Space | `U+205F` | -| Ideographic Space | `U+3000` | +```cs +var dbKdl = """ +database main port=5432 +"""; -## Newline - -The following character sequences [should be treated as new -lines](https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-5/#G41643): - -| Acronym | Name | Code Pt | -| ------- | ----------------------------- | ------------------- | -| CRLF | Carriage Return and Line Feed | `U+000D` + `U+000A` | -| CR | Carriage Return | `U+000D` | -| LF | Line Feed | `U+000A` | -| NEL | Next Line | `U+0085` | -| VT | Vertical tab | `U+000B` | -| FF | Form Feed | `U+000C` | -| LS | Line Separator | `U+2028` | -| PS | Paragraph Separator | `U+2029` | - -Note that for the purpose of new lines, the specific sequence `CRLF` is -considered _a single newline_. +var dbConfig = KdlSerializer.Deserialize(dbKdl); +``` -## Disallowed Literal Code Points +KDL differs from other configuration languages like +yaml or toml in that it is node-based. The top-level document can consist of a collection of nodes, not args (e.g. `#true`, `#false`, `0xAF`) or properties (`key=#false`). To adhere to this, the serialization API depends on the use of several provided attributes to facilitate mapping `KDL` constructs to user defined types: -The following code points may not appear literally anywhere in the document. -They may be represented in Strings (but not Raw Strings) using Unicode Escapes ({{escapes}}) (`\u{...}`, -except for non Unicode Scalar Value, which can't be represented even as escapes). +### Attributes -- The codepoints `U+0000-0008` or the codepoints `U+000E-001F` (various - control characters). -- `U+007F` (the Delete control character). -- Any codepoint that is not a [Unicode Scalar - Value](https://unicode.org/glossary/#unicode_scalar_value) (`U+D800-DFFF`). -- `U+200E-200F`, `U+202A-202E`, and `U+2066-2069`, the [unicode - "direction control" - characters](https://www.w3.org/International/questions/qa-bidi-unicode-controls) -- `U+FEFF`, aka Zero-width Non-breaking Space (ZWNBSP)/Byte Order Mark (BOM), - except as the first code point in a document. +[how to use Kdl attributes](docs/serialization-attributes.md) diff --git a/sampleApp/Program.cs b/sampleApp/Program.cs deleted file mode 100644 index 3751555..0000000 --- a/sampleApp/Program.cs +++ /dev/null @@ -1,2 +0,0 @@ -// See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); diff --git a/sampleApp/sampleApp.csproj b/sampleApp/sampleApp.csproj deleted file mode 100644 index ed9781c..0000000 --- a/sampleApp/sampleApp.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - - Exe - net10.0 - enable - enable - - - diff --git a/samples/01-simple-node.kdl b/samples/01-simple-node.kdl new file mode 100644 index 0000000..9ee78ec --- /dev/null +++ b/samples/01-simple-node.kdl @@ -0,0 +1,4 @@ +// Simple node with arguments and properties +// Maps to a single object with scalar values + +package "my-awesome-lib" version="2.1.0" description="A really cool library" diff --git a/samples/02-multiple-arguments.kdl b/samples/02-multiple-arguments.kdl new file mode 100644 index 0000000..99e7d19 --- /dev/null +++ b/samples/02-multiple-arguments.kdl @@ -0,0 +1,7 @@ +// Node with multiple positional arguments +// Arguments are ordered and indexed (0, 1, 2...) + +command git commit -m Initial commit + +// Another example: coordinates +point 10 20 30 label="origin" diff --git a/samples/03-nested-children.kdl b/samples/03-nested-children.kdl new file mode 100644 index 0000000..ddce116 --- /dev/null +++ b/samples/03-nested-children.kdl @@ -0,0 +1,11 @@ +// Node with child nodes (children block) +// Children are nodes inside { } + +project web-app version="1.0.0" { + dependency lodash version="4.17.21" optional=#false + dependency react version="18.2.0" optional=#false + dependency typescript version="5.0.0" optional=#true + + devDependency jest version="29.0.0" + devDependency eslint version="8.0.0" +} diff --git a/samples/04-numeric-types.kdl b/samples/04-numeric-types.kdl new file mode 100644 index 0000000..81b14c4 --- /dev/null +++ b/samples/04-numeric-types.kdl @@ -0,0 +1,31 @@ +// Various numeric types and formats +// KDL supports integers, floats, hex, octal, binary + +config { + // Integer properties + timeout 5000 + maxRetries 3 + port 8080 + + // Large numbers (long) + maxFileSize 9223372036854775807 + + // Floating point + ratio 0.75 + pi 3.14159265358979 + + // Scientific notation + precision 1.5e-10 + + // Different bases (all represent the same value: 255) + hexValue 0xFF + octalValue 0o377 + binaryValue 0b11111111 + + // Negative numbers + offset -100 + temperature -273.15 + + // Underscores for readability + population 7_900_000_000 +} diff --git a/samples/05-type-annotations.kdl b/samples/05-type-annotations.kdl new file mode 100644 index 0000000..3cdf365 --- /dev/null +++ b/samples/05-type-annotations.kdl @@ -0,0 +1,30 @@ +// Type annotations provide hints about value interpretation +// Format: (type)value + +user alice { + id (uuid)"550e8400-e29b-41d4-a716-446655440000" + createdAt (date-time)"2024-01-15T10:30:00Z" + lastLogin (date-time)"2024-12-17T14:22:33.123Z" + + // Numeric type annotations + age (u8)28 + balance (decimal)"1234567.89" + score (f64)98.5 +} + +// Type annotation on node name (context/role annotation) +(admin)user "superuser" { + permissions "all" +} + +// Various string type annotations from the spec +metadata { + email (email)"user@example.com" + website (url)"https://example.com" + ip (ipv4)"192.168.1.1" + ipv6 (ipv6)"2001:0db8:85a3:0000:0000:8a2e:0370:7334" + country (country-2)"US" + duration (duration)"P1Y2M3DT4H5M6S" + pattern (regex)"^[a-z]+$" + binary (base64)"SGVsbG8gV29ybGQh" +} diff --git a/samples/06-booleans-and-null.kdl b/samples/06-booleans-and-null.kdl new file mode 100644 index 0000000..5b5ca71 --- /dev/null +++ b/samples/06-booleans-and-null.kdl @@ -0,0 +1,19 @@ +// Boolean and null values use # prefix in KDL 2.0 + +feature dark-mode { + enabled #true + beta #false + deprecatedAt #null +} + +settings { + // All the keyword values + debug #true + production #false + overrideConfig #null + + // Special float keywords + infiniteTimeout #inf + negativeInfinity #-inf + undefinedValue #nan +} diff --git a/samples/07-string-varieties.kdl b/samples/07-string-varieties.kdl new file mode 100644 index 0000000..c671b6b --- /dev/null +++ b/samples/07-string-varieties.kdl @@ -0,0 +1,33 @@ +// KDL supports multiple string syntaxes + +strings { + // Bare/identifier strings (no quotes needed for simple values) + simple hello-world + kebab my-kebab-case + + // Quoted strings (required for whitespace, special chars) + withSpaces "hello world" + withQuote "say \"hello\"" + withNewline "line1\nline2" + withTab "col1\tcol2" + unicode "emoji: \u{1F600}" + + // Raw strings (no escape processing) + rawPath #"C:\Users\name\file.txt"# + rawRegex #"^\d+\.\d+\.\d+$"# + rawWithQuotes ##"She said "hello""## + + // Multi-line strings + multiLine """ + This is a multi-line string. + The indentation of the closing quotes + determines the base indentation. + All lines are dedented accordingly. + """ + + // Raw multi-line + rawMultiLine #""" + No escapes here: \n \t \u{1234} + All literal backslashes. + """# +} diff --git a/samples/08-deeply-nested.kdl b/samples/08-deeply-nested.kdl new file mode 100644 index 0000000..99f2bf7 --- /dev/null +++ b/samples/08-deeply-nested.kdl @@ -0,0 +1,62 @@ +// Deeply nested structure - common in config files + +application "enterprise-app" version="3.0.0" { + database "primary" { + connection { + host "db.example.com" + port 5432 + ssl #true + + pool { + minSize 5 + maxSize 50 + idleTimeout 30000 + } + + credentials { + username "app_user" + // In real config, this would be a reference + passwordEnvVar "DB_PASSWORD" + } + } + + replica "read-replica-1" { + host "replica1.example.com" + port 5432 + readOnly #true + } + + replica "read-replica-2" { + host "replica2.example.com" + port 5432 + readOnly #true + } + } + + cache "redis" { + host "cache.example.com" + port 6379 + ttl 3600 + + cluster { + enabled #true + nodes 3 + } + } + + logging { + level "info" + format "json" + + output "console" { + colorize #true + } + + output "file" { + path "/var/log/app.log" + maxSize 10485760 + maxFiles 5 + compress #true + } + } +} diff --git a/samples/09-mixed-entries.kdl b/samples/09-mixed-entries.kdl new file mode 100644 index 0000000..94b07da --- /dev/null +++ b/samples/09-mixed-entries.kdl @@ -0,0 +1,25 @@ +// Arguments and properties can be freely intermixed +// This is valid KDL - order of args is preserved, props can appear anywhere + +// Properties between arguments +node 1 key=value 2 another=prop 3 + +// Real-world example: CLI-like syntax +run "npm" "install" --save=#true "lodash" --dev=#false + +// Build configuration with mixed entries +task "build" priority=high "src/**/*.ts" output="dist" "!src/**/*.test.ts" { + compiler "tsc" + watch #false +} + +// HTTP request definition +request "POST" "/api/users" content-type="application/json" { + header "Authorization" "Bearer token123" + header "X-Request-Id" (uuid)"550e8400-e29b-41d4-a716-446655440000" + + body { + name "John Doe" + email "john@example.com" + } +} diff --git a/samples/10-document-vs-node.kdl b/samples/10-document-vs-node.kdl new file mode 100644 index 0000000..7d77f6e --- /dev/null +++ b/samples/10-document-vs-node.kdl @@ -0,0 +1,24 @@ +// Document with multiple top-level nodes +// This tests DeserializeMany and document-level mapping + +// When deserializing as a document, each top-level node maps to a list item +// When deserializing as a single node, you'd wrap in a container + +server "web-1" host="10.0.0.1" port=80 { + role "frontend" + role "load-balancer" +} + +server "web-2" host="10.0.0.2" port=80 { + role "frontend" +} + +server "api-1" host="10.0.1.1" port=8080 { + role "backend" + role "api" +} + +server "db-1" host="10.0.2.1" port=5432 { + role "database" + role "primary" +} diff --git a/samples/11-slashdash-comments.kdl b/samples/11-slashdash-comments.kdl new file mode 100644 index 0000000..de7c4a5 --- /dev/null +++ b/samples/11-slashdash-comments.kdl @@ -0,0 +1,31 @@ +// Slashdash comments out entire elements +// The serializer should NOT see these in the parsed AST + +config { + // Regular comment - ignored + enabled #true + + /- disabled #true // This entire node is commented out + + database { + host "localhost" + /- port 5432 // Commented out argument + port 3306 // This is the actual port + + credentials user="admin" /- password="secret" // password prop commented out + } + + /- slashdashed-node { + // Entire children block commented out + this "is" ignored=#true + } + + // Slashdash can span multiple lines + /- + experimental { + feature1 #true + feature2 #true + } + + production #true +} diff --git a/samples/12-edge-cases.kdl b/samples/12-edge-cases.kdl new file mode 100644 index 0000000..cb0669b --- /dev/null +++ b/samples/12-edge-cases.kdl @@ -0,0 +1,53 @@ +// Edge cases and tricky scenarios + +// Empty nodes (valid - just a name) +empty-node + +// Node with only children, no args/props +container { + child1 + child2 +} + +// Same property multiple times - rightmost wins (per spec) +settings priority=1 priority=2 priority=3 // priority should be 3 + +// Unicode identifiers (bare strings) +日本語ノード value="Japanese node name" +émoji-🎉 value="Emoji in identifier" + +// Very long numbers +bignum 999999999999999999999999999999 + +// Numbers with many underscores +formatted 1_000_000_000 + +// Semicolon-separated nodes on one line +a; b; c + +// Children on same line +parent { child1; child2; child3 } + +// Line continuation +very-long-node \ + arg1 \ + arg2 \ + prop1="value1" \ + prop2="value2" \ + { + child + } + +// Property with same name as argument value (no conflict) +node "key" key="different-value" + +// Nested type annotations +typed-node (container)data { + item (u8)255 + item (i8)-128 +} + +// Null in various positions +nullable arg1=#null prop=#null { + child #null +} diff --git a/samples/13-real-world-package.kdl b/samples/13-real-world-package.kdl new file mode 100644 index 0000000..dd1a440 --- /dev/null +++ b/samples/13-real-world-package.kdl @@ -0,0 +1,47 @@ +// Real-world example: Package manifest (like package.json in KDL) + +package "kuddle" version="1.0.0" { + description "A KDL parser and serializer for .NET" + license "MIT" + + author "Henry" email="henry@example.com" { + url "https://github.com/henry" + } + + repository type="git" url="https://github.com/henry/kuddle" + + keywords "kdl" "parser" "serializer" "configuration" "dotnet" + + engines { + dotnet ">=8.0.0" + } + + scripts { + build "dotnet build" + test "dotnet test" + pack "dotnet pack" + publish "dotnet nuget push" + } + + dependency "Parlot" version="^1.0.0" { + optional #false + } + + devDependency "TUnit" version="^1.0.0" + devDependency "BenchmarkDotNet" version="^0.14.0" + + // Conditional dependencies + dependency "System.Text.Json" version=">=8.0.0" { + condition "net8.0" + } + + files "src/**/*.cs" "!src/**/*.Tests.cs" { + include "LICENSE" + include "README.md" + } + + exports { + main "./src/Kuddle/Kuddle.csproj" + types "./src/Kuddle/Kuddle.csproj" + } +} diff --git a/samples/14-real-world-ci.kdl b/samples/14-real-world-ci.kdl new file mode 100644 index 0000000..e8bc03e --- /dev/null +++ b/samples/14-real-world-ci.kdl @@ -0,0 +1,87 @@ +// Real-world example: CI/CD pipeline configuration + +pipeline "main" { + trigger { + branch "main" "develop" "release/*" + path "src/**" "tests/**" ".github/**" + + // Exclude paths + exclude "**/*.md" "docs/**" + } + + variables { + variable "DOTNET_VERSION" "8.0.x" + variable "BUILD_CONFIGURATION" "Release" + variable "NUGET_FEED" "https://api.nuget.org/v3/index.json" secret=#false + } + + stage "build" { + displayName "Build and Test" + + job "build-windows" { + pool "windows-latest" + + step "checkout" { + uses "actions/checkout@v4" + with { + fetch-depth 0 + } + } + + step "setup-dotnet" { + uses "actions/setup-dotnet@v4" + with { + dotnet-version "${{ variables.DOTNET_VERSION }}" + } + } + + step "restore" { + run "dotnet restore" + } + + step "build" { + run "dotnet build --configuration ${{ variables.BUILD_CONFIGURATION }} --no-restore" + } + + step "test" { + run "dotnet test --configuration ${{ variables.BUILD_CONFIGURATION }} --no-build --logger trx" + continueOnError #false + } + } + + job "build-linux" { + pool "ubuntu-latest" + dependsOn #null // Runs in parallel + + step "checkout" uses="actions/checkout@v4" + step "setup-dotnet" uses="actions/setup-dotnet@v4" + step "restore" run="dotnet restore" + step "build" run="dotnet build -c Release" + step "test" run="dotnet test -c Release" + } + } + + stage "publish" { + displayName "Publish Package" + dependsOn "build" + condition "eq(variables['Build.SourceBranch'], 'refs/heads/main')" + + job "publish-nuget" { + pool "ubuntu-latest" + + step "download-artifact" { + uses "actions/download-artifact@v4" + with { + name "nuget-package" + } + } + + step "push-nuget" { + run "dotnet nuget push **/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source ${{ variables.NUGET_FEED }}" + env { + NUGET_API_KEY "${{ secrets.NUGET_API_KEY }}" + } + } + } + } +} diff --git a/src/Kuddle.Benchmarks/ParserBenchmarks.cs b/src/Kuddle.Benchmarks/ParserBenchmarks.cs deleted file mode 100644 index d3ba66f..0000000 --- a/src/Kuddle.Benchmarks/ParserBenchmarks.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Jobs; -using Kuddle.AST; -using Kuddle.Parser; -using Parlot; -using Parlot.Fluent; - -namespace Kuddle.Benchmarks; - -[SimpleJob(RuntimeMoniker.Net10_0)] -[MemoryDiagnoser] -public class ParserBenchmarks -{ - private string _simpleDocument = string.Empty; - private string _complexDocument = string.Empty; - private string _largeDocument = string.Empty; - - private Parser _compiledParser = null!; - private Parser _nonCompiledParser = null!; - - [GlobalSetup] - public void Setup() - { - _simpleDocument = """ - node1 "value1" - node2 123 - node3 #true - """; - - _complexDocument = """ - package { - name "my-package" - version "1.0.0" - dependencies { - dep1 "^2.0.0" - dep2 "~1.5.0" - } - } - - config (type)"production" { - host "example.com" - port 8080 - ssl #true - features enabled=#true timeout=30 - } - - users { - user id=1 name="Alice" active=#true - user id=2 name="Bob" active=#false - user id=3 name="Charlie" active=#true - } - """; - - var largeDocBuilder = new System.Text.StringBuilder(); - for (int i = 0; i < 100; i++) - { - largeDocBuilder.AppendLine($"node{i} {{"); - for (int j = 0; j < 10; j++) - { - largeDocBuilder.AppendLine($" child{j} \"value{j}\" prop{j}={j}"); - } - largeDocBuilder.AppendLine("}"); - } - _largeDocument = largeDocBuilder.ToString(); - - _compiledParser = KuddleGrammar.Document.Compile(); - _nonCompiledParser = KuddleGrammar.Document; - } - - [Benchmark] - public KdlDocument? SimpleDocument_NonCompiled() - { - return _nonCompiledParser.Parse(_simpleDocument); - } - - [Benchmark] - public KdlDocument? SimpleDocument_Compiled() - { - return _compiledParser.Parse(_simpleDocument); - } - - [Benchmark] - public KdlDocument? ComplexDocument_NonCompiled() - { - return _nonCompiledParser.Parse(_complexDocument); - } - - [Benchmark] - public KdlDocument? ComplexDocument_Compiled() - { - return _compiledParser.Parse(_complexDocument); - } - - [Benchmark] - public KdlDocument? LargeDocument_NonCompiled() - { - return _nonCompiledParser.Parse(_largeDocument); - } - - [Benchmark] - public KdlDocument? LargeDocument_Compiled() - { - return _compiledParser.Parse(_largeDocument); - } -} diff --git a/src/Kuddle.Generators/Kuddle.Generators.csproj b/src/Kuddle.Generators/Kuddle.Generators.csproj deleted file mode 100644 index f86a614..0000000 --- a/src/Kuddle.Generators/Kuddle.Generators.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - netstandard2.0 - latest - enable - enable - true - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - diff --git a/src/Kuddle.Generators/TheGenerator.cs b/src/Kuddle.Generators/TheGenerator.cs deleted file mode 100644 index 45aee6f..0000000 --- a/src/Kuddle.Generators/TheGenerator.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections.Immutable; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Kuddle.Generators; - -[Generator] -public class TheGenerator : IIncrementalGenerator -{ - public void Initialize(IncrementalGeneratorInitializationContext context) - { - var provider = context - .SyntaxProvider.CreateSyntaxProvider( - predicate: static (node, _) => node is ClassDeclarationSyntax, - transform: static (ctx, _) => ctx.Node as ClassDeclarationSyntax - ) - .Where(m => m is not null); - - var compilation = context.CompilationProvider.Combine(provider.Collect()); - - context.RegisterSourceOutput(compilation, Execute); - } - - private void Execute( - SourceProductionContext context, - (Compilation Left, ImmutableArray Right) tuple - ) - { - var (compilation, list) = tuple; - - var theCode = """ -namespace ClassListGenerator; - -public static class ClassNames -{ - public static List Names = new() - { - "SomeClass" - }; -} -"""; - - context.AddSource("YourClassList.g.cs", theCode); - } -} diff --git a/src/Kuddle.Benchmarks/Kuddle.Benchmarks.csproj b/src/Kuddle.Net.Benchmarks/Kuddle.Net.Benchmarks.csproj similarity index 71% rename from src/Kuddle.Benchmarks/Kuddle.Benchmarks.csproj rename to src/Kuddle.Net.Benchmarks/Kuddle.Net.Benchmarks.csproj index 8998760..9cec005 100644 --- a/src/Kuddle.Benchmarks/Kuddle.Benchmarks.csproj +++ b/src/Kuddle.Net.Benchmarks/Kuddle.Net.Benchmarks.csproj @@ -1,16 +1,17 @@ + Kuddle.Benchmarks Exe net10.0 preview enable - disable + enable true - + diff --git a/src/Kuddle.Benchmarks/Program.cs b/src/Kuddle.Net.Benchmarks/Program.cs similarity index 55% rename from src/Kuddle.Benchmarks/Program.cs rename to src/Kuddle.Net.Benchmarks/Program.cs index c1ac613..181057c 100644 --- a/src/Kuddle.Benchmarks/Program.cs +++ b/src/Kuddle.Net.Benchmarks/Program.cs @@ -1,4 +1,4 @@ using BenchmarkDotNet.Running; using Kuddle.Benchmarks; -BenchmarkRunner.Run(); +BenchmarkRunner.Run(); diff --git a/src/Kuddle.Net.Benchmarks/SerializerBenchmarks.cs b/src/Kuddle.Net.Benchmarks/SerializerBenchmarks.cs new file mode 100644 index 0000000..5752f38 --- /dev/null +++ b/src/Kuddle.Net.Benchmarks/SerializerBenchmarks.cs @@ -0,0 +1,234 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using Kuddle.AST; +using Kuddle.Parser; +using Kuddle.Serialization; +using Parlot.Fluent; + +namespace Kuddle.Benchmarks; + +[SimpleJob(RuntimeMoniker.Net10_0)] +[MemoryDiagnoser] +public class SerializerBenchmarks +{ + private string _simpleDocument = string.Empty; + private string _complexDocument = string.Empty; + private string _largeDocument = string.Empty; + + private Parser _compiledParser = null!; + private Parser _nonCompiledParser = null!; + + // Serialization benchmarks + private Package _simplePackage = null!; + private string _simplePackageKdl = string.Empty; + + private Project _complexProject = null!; + private string _complexProjectKdl = string.Empty; + + private List _largePackageList = null!; + private string _largePackageListKdl = string.Empty; + + [GlobalSetup] + public void Setup() + { + _simpleDocument = """ + node1 "value1" + node2 123 + node3 #true + """; + + _complexDocument = """ + package { + name "my-package" + version "1.0.0" + dependencies { + dep1 "^2.0.0" + dep2 "~1.5.0" + } + } + + config (type)"production" { + host "example.com" + port 8080 + ssl #true + features enabled=#true timeout=30 + } + + users { + user id=1 name="Alice" active=#true + user id=2 name="Bob" active=#false + user id=3 name="Charlie" active=#true + } + """; + + var largeDocBuilder = new System.Text.StringBuilder(); + for (int i = 0; i < 100; i++) + { + largeDocBuilder.AppendLine($"node{i} {{"); + for (int j = 0; j < 10; j++) + { + largeDocBuilder.AppendLine($" child{j} \"value{j}\" prop{j}={j}"); + } + largeDocBuilder.AppendLine("}"); + } + _largeDocument = largeDocBuilder.ToString(); + + _compiledParser = KdlGrammar.Document.Compile(); + _nonCompiledParser = KdlGrammar.Document; + + // Setup serialization objects + _simplePackage = new Package + { + Name = "my-lib", + Version = "1.0.0", + Description = "A library", + }; + _simplePackageKdl = KdlSerializer.Serialize(_simplePackage); + + _complexProject = new Project + { + Name = "my-app", + Version = "2.0.0", + Dependencies = + [ + new Dependency { Package = "lodash", Version = "4.17.21" }, + new Dependency { Package = "react", Version = "18.0.0" }, + new Dependency { Package = "typescript", Version = "4.5.0" }, + ], + DevDependencies = + [ + new Dependency { Package = "jest", Version = "27.0.0" }, + new Dependency { Package = "eslint", Version = "8.0.0" }, + ], + }; + _complexProjectKdl = KdlSerializer.Serialize(_complexProject); + + _largePackageList = []; + for (int i = 0; i < 100; i++) + { + _largePackageList.Add( + new Package + { + Name = $"package{i}", + Version = $"1.{i}.0", + Description = $"Description for package {i}", + } + ); + } + _largePackageListKdl = KdlSerializer.SerializeMany(_largePackageList); + } + + [Benchmark] + public KdlDocument? SimpleDocument_NonCompiled() + { + return _nonCompiledParser.Parse(_simpleDocument); + } + + [Benchmark] + public KdlDocument? SimpleDocument_Compiled() + { + return _compiledParser.Parse(_simpleDocument); + } + + [Benchmark] + public KdlDocument? ComplexDocument_NonCompiled() + { + return _nonCompiledParser.Parse(_complexDocument); + } + + [Benchmark] + public KdlDocument? ComplexDocument_Compiled() + { + return _compiledParser.Parse(_complexDocument); + } + + [Benchmark] + public KdlDocument? LargeDocument_NonCompiled() + { + return _nonCompiledParser.Parse(_largeDocument); + } + + [Benchmark] + public KdlDocument? LargeDocument_Compiled() + { + return _compiledParser.Parse(_largeDocument); + } + + // Serialization benchmarks + [Benchmark] + public string SerializeSimplePackage() + { + return KdlSerializer.Serialize(_simplePackage); + } + + [Benchmark] + public string SerializeComplexProject() + { + return KdlSerializer.Serialize(_complexProject); + } + + [Benchmark] + public string SerializeLargePackageList() + { + return KdlSerializer.SerializeMany(_largePackageList); + } + + // Deserialization benchmarks + [Benchmark] + public Package? DeserializeSimplePackage() + { + return KdlSerializer.Deserialize(_simplePackageKdl); + } + + [Benchmark] + public Project? DeserializeComplexProject() + { + return KdlSerializer.Deserialize(_complexProjectKdl); + } + + [Benchmark] + public List? DeserializeLargePackageList() + { + return KdlSerializer.DeserializeMany(_largePackageListKdl).ToList(); + } + + // Test models + public class Package + { + [KdlArgument(0)] + public string Name { get; set; } = string.Empty; + + [KdlProperty("version")] + public string? Version { get; set; } + + [KdlProperty("description")] + public string? Description { get; set; } + } + + public class Project + { + [KdlArgument(0)] + public string Name { get; set; } = string.Empty; + + [KdlProperty("version")] + public string Version { get; set; } = "1.0.0"; + + [KdlNode("dependency")] + public List Dependencies { get; set; } = []; + + [KdlNode("devDependency")] + public List DevDependencies { get; set; } = []; + } + + public class Dependency + { + [KdlArgument(0)] + public string Package { get; set; } = string.Empty; + + [KdlProperty("version")] + public string Version { get; set; } = "*"; + + [KdlProperty("optional")] + public bool Optional { get; set; } + } +} diff --git a/src/Kuddle.Tests/AST/KdlNumberTests.cs b/src/Kuddle.Net.Tests/AST/KdlNumberTests.cs similarity index 100% rename from src/Kuddle.Tests/AST/KdlNumberTests.cs rename to src/Kuddle.Net.Tests/AST/KdlNumberTests.cs diff --git a/src/Kuddle.Tests/AST/KdlStringTests.cs b/src/Kuddle.Net.Tests/AST/KdlStringTests.cs similarity index 100% rename from src/Kuddle.Tests/AST/KdlStringTests.cs rename to src/Kuddle.Net.Tests/AST/KdlStringTests.cs diff --git a/src/Kuddle.Tests/Errors/ErrorHandlingTests.cs b/src/Kuddle.Net.Tests/Errors/ErrorHandlingTests.cs similarity index 98% rename from src/Kuddle.Tests/Errors/ErrorHandlingTests.cs rename to src/Kuddle.Net.Tests/Errors/ErrorHandlingTests.cs index 0c4a7bb..84c6ec0 100644 --- a/src/Kuddle.Tests/Errors/ErrorHandlingTests.cs +++ b/src/Kuddle.Net.Tests/Errors/ErrorHandlingTests.cs @@ -1,4 +1,5 @@ using Kuddle.Exceptions; +using Kuddle.Serialization; namespace Kuddle.Tests.Errors; @@ -12,7 +13,7 @@ private static async Task AssertParseFails( int? expectedLine = null ) { - var ex = await Assert.ThrowsAsync(async () => KuddleReader.Parse(kdl)); + var ex = await Assert.ThrowsAsync(async () => KdlReader.Read(kdl)); await Assert .That(ex.Message) diff --git a/src/Kuddle.Tests/Errors/test.kdl b/src/Kuddle.Net.Tests/Errors/test.kdl similarity index 100% rename from src/Kuddle.Tests/Errors/test.kdl rename to src/Kuddle.Net.Tests/Errors/test.kdl diff --git a/src/Kuddle.Tests/Extensions/DxTests.cs b/src/Kuddle.Net.Tests/Extensions/DxTests.cs similarity index 89% rename from src/Kuddle.Tests/Extensions/DxTests.cs rename to src/Kuddle.Net.Tests/Extensions/DxTests.cs index a15c293..aa230db 100644 --- a/src/Kuddle.Tests/Extensions/DxTests.cs +++ b/src/Kuddle.Net.Tests/Extensions/DxTests.cs @@ -1,6 +1,7 @@ using Kuddle.AST; using Kuddle.Extensions; using Kuddle.Parser; +using Kuddle.Serialization; namespace Kuddle.Tests.Extensions; @@ -9,7 +10,7 @@ public class DxTests [Test] public async Task TryGet_Int_Success() { - var doc = KuddleReader.Parse("node 123"); + var doc = KdlReader.Read("node 123"); var val = doc.Nodes[0].Arg(0)!; bool success = val.TryGetInt(out int result); @@ -21,7 +22,7 @@ public async Task TryGet_Int_Success() [Test] public async Task TryGet_Int_Failure_WrongType() { - var doc = KuddleReader.Parse("node \"hello\""); + var doc = KdlReader.Read("node \"hello\""); var val = doc.Nodes[0].Arg(0)!; bool success = val.TryGetInt(out int result); @@ -34,7 +35,7 @@ public async Task TryGet_Int_Failure_WrongType() public async Task TryGet_Int_Failure_Overflow() { // Value larger than Int32 - var doc = KuddleReader.Parse("node 9999999999"); + var doc = KdlReader.Read("node 9999999999"); var val = doc.Nodes[0].Arg(0)!; bool success = val.TryGetInt(out int result); @@ -45,7 +46,7 @@ public async Task TryGet_Int_Failure_Overflow() [Test] public async Task TryGet_Prop_Navigation_Success() { - var doc = KuddleReader.Parse("server port=8080"); + var doc = KdlReader.Read("server port=8080"); var node = doc.Nodes[0]; // Combine finding the prop and converting it @@ -64,7 +65,7 @@ public async Task TryGet_Prop_Navigation_Success() [Test] public async Task TryGet_Prop_Navigation_Missing() { - var doc = KuddleReader.Parse("server host=\"localhost\""); + var doc = KdlReader.Read("server host=\"localhost\""); var node = doc.Nodes[0]; var propVal = node.Prop("port")!; // Returns KdlNull @@ -78,7 +79,7 @@ public async Task TryGetUuid_ValidGuidString_ReturnsTrue() { var expected = Guid.NewGuid(); var kdl = $"node \"{expected}\""; // e.g. "f81d4fae-7dec-11d0-a765-00a0c91e6bf6" - var doc = KuddleReader.Parse(kdl); + var doc = KdlReader.Read(kdl); var val = doc.Nodes[0].Arg(0)!; bool success = val.TryGetUuid(out var result); @@ -90,7 +91,7 @@ public async Task TryGetUuid_ValidGuidString_ReturnsTrue() [Test] public async Task TryGetUuid_InvalidString_ReturnsFalse() { - var doc = KuddleReader.Parse("node \"not-a-guid\""); + var doc = KdlReader.Read("node \"not-a-guid\""); var val = doc.Nodes[0].Arg(0)!; bool success = val.TryGetUuid(out var result); @@ -104,7 +105,7 @@ public async Task TryGetUuid_FromAnnotatedValue_ReturnsTrue() { var expected = Guid.NewGuid(); var kdl = $"node (uuid)\"{expected}\""; - var doc = KuddleReader.Parse(kdl); + var doc = KdlReader.Read(kdl); var val = doc.Nodes[0].Arg(0)!; bool success = val.TryGetUuid(out var result); @@ -138,7 +139,7 @@ public async Task TryGetDateTime_ValidIso8601_ReturnsTrue() var now = DateTimeOffset.UtcNow; // Round-trip format "O" is standard for KDL/JSON var kdl = $"node \"{now:O}\""; - var doc = KuddleReader.Parse(kdl); + var doc = KdlReader.Read(kdl); var val = doc.Nodes[0].Arg(0)!; bool success = val.TryGetDateTime(out var result); @@ -153,7 +154,7 @@ public async Task TryGetDateTime_DateOnly_ReturnsTrue() { // YYYY-MM-DD var kdl = "node \"2023-10-25\""; - var doc = KuddleReader.Parse(kdl); + var doc = KdlReader.Read(kdl); var val = doc.Nodes[0].Arg(0)!; bool success = val.TryGetDateTime(out var result); @@ -167,7 +168,7 @@ public async Task TryGetDateTime_DateOnly_ReturnsTrue() [Test] public async Task TryGetDateTime_InvalidString_ReturnsFalse() { - var doc = KuddleReader.Parse("node \"tomorrow\""); + var doc = KdlReader.Read("node \"tomorrow\""); var val = doc.Nodes[0].Arg(0)!; bool success = val.TryGetDateTime(out var result); diff --git a/src/Kuddle.Tests/Grammar/CommentParserTests.cs b/src/Kuddle.Net.Tests/Grammar/CommentParserTests.cs similarity index 87% rename from src/Kuddle.Tests/Grammar/CommentParserTests.cs rename to src/Kuddle.Net.Tests/Grammar/CommentParserTests.cs index 6448ef5..29b7750 100644 --- a/src/Kuddle.Tests/Grammar/CommentParserTests.cs +++ b/src/Kuddle.Net.Tests/Grammar/CommentParserTests.cs @@ -7,7 +7,7 @@ public class CommentParserTests [Test] public async Task CanParseSimpleSingleLineComment() { - var sut = KuddleGrammar.SingleLineComment; + var sut = KdlGrammar.SingleLineComment; var comment = """// I am a single line comment"""; @@ -20,7 +20,7 @@ public async Task CanParseSimpleSingleLineComment() [Test] public async Task CanParseSimpleMultiLineComment() { - var sut = KuddleGrammar.MultiLineComment; + var sut = KdlGrammar.MultiLineComment; var comment = """ /* @@ -38,7 +38,7 @@ public async Task CanParseSimpleMultiLineComment() [Test] public async Task CanParseNestedMultiLineComment() { - var sut = KuddleGrammar.MultiLineComment; + var sut = KdlGrammar.MultiLineComment; var comment = """ /* diff --git a/src/Kuddle.Tests/Grammar/NodeParserTests.cs b/src/Kuddle.Net.Tests/Grammar/NodeParserTests.cs similarity index 93% rename from src/Kuddle.Tests/Grammar/NodeParserTests.cs rename to src/Kuddle.Net.Tests/Grammar/NodeParserTests.cs index b9745fb..a8f59d7 100644 --- a/src/Kuddle.Tests/Grammar/NodeParserTests.cs +++ b/src/Kuddle.Net.Tests/Grammar/NodeParserTests.cs @@ -10,7 +10,7 @@ public class NodeParsersTests [Test] public async Task Type_ParsesSimpleType() { - var sut = KuddleGrammar.Type; + var sut = KdlGrammar.Type; var input = "(string)"; bool success = sut.TryParse(input, out var result); @@ -22,7 +22,7 @@ public async Task Type_ParsesSimpleType() [Test] public async Task Prop_ParsesSimpleProperty() { - var sut = KuddleGrammar.Node; + var sut = KdlGrammar.Node; var input = "node key=value"; bool success = sut.TryParse(input, out var node); @@ -39,7 +39,7 @@ public async Task Prop_ParsesSimpleProperty() [Test] public async Task Node_ParsesComplexLine() { - var sut = KuddleGrammar.Node; + var sut = KdlGrammar.Node; // Test: Name, Arg, Prop, Type Annotation var input = "(my-type)node 123 key=\"value\";"; @@ -69,7 +69,7 @@ public async Task Node_ParsesComplexLine() [Test] public async Task Node_ParsesChildren() { - var sut = KuddleGrammar.Node; + var sut = KdlGrammar.Node; var input = "parent { child; }"; bool success = sut.TryParse(input, out var node, out var error); @@ -84,7 +84,7 @@ public async Task Node_ParsesChildren() [Test] public async Task Node_ParsesMixedContent() { - var sut = KuddleGrammar.Node; + var sut = KdlGrammar.Node; var input = "(type)node 10 prop=#true { child; }"; bool success = sut.TryParse(input, out var node); @@ -111,7 +111,7 @@ await Assert public async Task Node_SlashDash_SkipsNode() { // This tests the logic in 'Nodes' (plural) parser usually - var sut = KuddleGrammar.Document; + var sut = KdlGrammar.Document; var input = "node1; /- node2; node3;"; bool success = sut.TryParse(input, out var doc); @@ -125,7 +125,7 @@ public async Task Node_SlashDash_SkipsNode() [Test] public async Task Node_SlashDash_SkipsArg() { - var sut = KuddleGrammar.Node; + var sut = KdlGrammar.Node; var input = "node 1 /- 2 3"; bool success = sut.TryParse(input, out var node); @@ -145,7 +145,7 @@ public async Task Node_SlashDash_SkipsArg() [Test] public async Task SlashDash_SkipsProperty() { - var sut = KuddleGrammar.Node; + var sut = KdlGrammar.Node; var input = "node key=1 /- skipped=2 valid=3"; bool success = sut.TryParse(input, out var node); @@ -163,7 +163,7 @@ public async Task SlashDash_SkipsProperty() [Test] public async Task Node_SlashDash_SkipsChildrenBlock() { - var sut = KuddleGrammar.Node; + var sut = KdlGrammar.Node; // Parsing a node that has a slash-dashed children block var input = "node /- { child; }"; @@ -178,7 +178,7 @@ public async Task Node_SlashDash_SkipsChildrenBlock() [Test] public async Task Nodes_ParsesNodesWithWhitespace() { - var sut = KuddleGrammar.Nodes; + var sut = KdlGrammar.Nodes; var input = @" node1; diff --git a/src/Kuddle.Tests/Grammar/NumberParsersTests.cs b/src/Kuddle.Net.Tests/Grammar/NumberParsersTests.cs similarity index 87% rename from src/Kuddle.Tests/Grammar/NumberParsersTests.cs rename to src/Kuddle.Net.Tests/Grammar/NumberParsersTests.cs index e9ea2c8..6edcdef 100644 --- a/src/Kuddle.Tests/Grammar/NumberParsersTests.cs +++ b/src/Kuddle.Net.Tests/Grammar/NumberParsersTests.cs @@ -7,7 +7,7 @@ public class NumberParsersTests [Test] public async Task Decimal_ParsesPositiveInteger() { - var sut = KuddleGrammar.Decimal; + var sut = KdlGrammar.Decimal; var input = "42"; bool success = sut.TryParse(input, out var value); @@ -19,7 +19,7 @@ public async Task Decimal_ParsesPositiveInteger() [Test] public async Task Decimal_ParsesNegativeInteger() { - var sut = KuddleGrammar.Decimal; + var sut = KdlGrammar.Decimal; var input = "-42"; bool success = sut.TryParse(input, out var value); @@ -31,7 +31,7 @@ public async Task Decimal_ParsesNegativeInteger() [Test] public async Task Decimal_ParsesFractionalNumber() { - var sut = KuddleGrammar.Decimal; + var sut = KdlGrammar.Decimal; var input = "3.14159"; bool success = sut.TryParse(input, out var value); @@ -43,7 +43,7 @@ public async Task Decimal_ParsesFractionalNumber() [Test] public async Task Decimal_ParsesScientificNotation() { - var sut = KuddleGrammar.Decimal; + var sut = KdlGrammar.Decimal; var input = "1.23e-4"; bool success = sut.TryParse(input, out var value); @@ -55,7 +55,7 @@ public async Task Decimal_ParsesScientificNotation() [Test] public async Task Decimal_ParsesScientificNotationUppercase() { - var sut = KuddleGrammar.Decimal; + var sut = KdlGrammar.Decimal; var input = "6.02E23"; bool success = sut.TryParse(input, out var value); @@ -67,7 +67,7 @@ public async Task Decimal_ParsesScientificNotationUppercase() [Test] public async Task Decimal_ParsesWithUnderscoreSeparators() { - var sut = KuddleGrammar.Decimal; + var sut = KdlGrammar.Decimal; var input = "1_000_000"; bool success = sut.TryParse(input, out var value); @@ -79,7 +79,7 @@ public async Task Decimal_ParsesWithUnderscoreSeparators() [Test] public async Task Decimal_ParsesFractionalWithUnderscores() { - var sut = KuddleGrammar.Decimal; + var sut = KdlGrammar.Decimal; var input = "12_34.56_78"; bool success = sut.TryParse(input, out var value); @@ -91,7 +91,7 @@ public async Task Decimal_ParsesFractionalWithUnderscores() [Test] public async Task Hex_ParsesHexNumbers() { - var sut = KuddleGrammar.Hex; + var sut = KdlGrammar.Hex; var input = "0xFF"; bool success = sut.TryParse(input, out var value); @@ -103,7 +103,7 @@ public async Task Hex_ParsesHexNumbers() [Test] public async Task Hex_ParsesHexWithUnderscores() { - var sut = KuddleGrammar.Hex; + var sut = KdlGrammar.Hex; var input = "0x123_ABC"; bool success = sut.TryParse(input, out var value); @@ -115,7 +115,7 @@ public async Task Hex_ParsesHexWithUnderscores() [Test] public async Task Hex_ParsesNegativeHex() { - var sut = KuddleGrammar.Hex; + var sut = KdlGrammar.Hex; var input = "-0x42"; bool success = sut.TryParse(input, out var value); @@ -127,7 +127,7 @@ public async Task Hex_ParsesNegativeHex() [Test] public async Task Octal_ParsesOctalNumbers() { - var sut = KuddleGrammar.Octal; + var sut = KdlGrammar.Octal; var input = "0o777"; bool success = sut.TryParse(input, out var value); @@ -139,7 +139,7 @@ public async Task Octal_ParsesOctalNumbers() [Test] public async Task Octal_ParsesOctalWithUnderscores() { - var sut = KuddleGrammar.Octal; + var sut = KdlGrammar.Octal; var input = "0o123_456"; bool success = sut.TryParse(input, out var value); @@ -151,7 +151,7 @@ public async Task Octal_ParsesOctalWithUnderscores() [Test] public async Task Octal_ParsesNegativeOctal() { - var sut = KuddleGrammar.Octal; + var sut = KdlGrammar.Octal; var input = "-0o42"; bool success = sut.TryParse(input, out var value); @@ -163,7 +163,7 @@ public async Task Octal_ParsesNegativeOctal() [Test] public async Task Binary_ParsesBinaryNumbers() { - var sut = KuddleGrammar.Binary; + var sut = KdlGrammar.Binary; var input = "0b1010"; bool success = sut.TryParse(input, out var value); @@ -175,7 +175,7 @@ public async Task Binary_ParsesBinaryNumbers() [Test] public async Task Binary_ParsesBinaryWithUnderscores() { - var sut = KuddleGrammar.Binary; + var sut = KdlGrammar.Binary; var input = "0b1111_0000"; bool success = sut.TryParse(input, out var value); @@ -187,7 +187,7 @@ public async Task Binary_ParsesBinaryWithUnderscores() [Test] public async Task Binary_ParsesNegativeBinary() { - var sut = KuddleGrammar.Binary; + var sut = KdlGrammar.Binary; var input = "-0b101"; bool success = sut.TryParse(input, out var value); @@ -199,7 +199,7 @@ public async Task Binary_ParsesNegativeBinary() [Test] public async Task KeywordNumber_ParsesInfinity() { - var sut = KuddleGrammar.KeywordNumber; + var sut = KdlGrammar.KeywordNumber; var input = "#inf"; bool success = sut.TryParse(input, out var value); @@ -211,7 +211,7 @@ public async Task KeywordNumber_ParsesInfinity() [Test] public async Task KeywordNumber_ParsesNegativeInfinity() { - var sut = KuddleGrammar.KeywordNumber; + var sut = KdlGrammar.KeywordNumber; var input = "#-inf"; bool success = sut.TryParse(input, out var value); @@ -223,7 +223,7 @@ public async Task KeywordNumber_ParsesNegativeInfinity() [Test] public async Task KeywordNumber_ParsesNaN() { - var sut = KuddleGrammar.KeywordNumber; + var sut = KdlGrammar.KeywordNumber; var input = "#nan"; bool success = sut.TryParse(input, out var value); @@ -235,7 +235,7 @@ public async Task KeywordNumber_ParsesNaN() [Test] public async Task Number_ParsesDecimal() { - var sut = KuddleGrammar.Decimal; + var sut = KdlGrammar.Decimal; var input = "42"; bool success = sut.TryParse(input, out var value); @@ -247,7 +247,7 @@ public async Task Number_ParsesDecimal() [Test] public async Task Number_ParsesHex() { - var sut = KuddleGrammar.Hex; + var sut = KdlGrammar.Hex; var input = "0xFF"; bool success = sut.TryParse(input, out var value); @@ -259,7 +259,7 @@ public async Task Number_ParsesHex() [Test] public async Task Number_ParsesKeywordNumber() { - var sut = KuddleGrammar.KeywordNumber; + var sut = KdlGrammar.KeywordNumber; var input = "#inf"; bool success = sut.TryParse(input, out var value); @@ -271,7 +271,7 @@ public async Task Number_ParsesKeywordNumber() [Test] public async Task Decimal_RejectsDoubleDots() { - var sut = KuddleGrammar.Decimal.Eof(); + var sut = KdlGrammar.Decimal.Eof(); var input = "12.34.56"; bool success = sut.TryParse(input, out var value); diff --git a/src/Kuddle.Tests/Grammar/StringParserTests.cs b/src/Kuddle.Net.Tests/Grammar/StringParserTests.cs similarity index 91% rename from src/Kuddle.Tests/Grammar/StringParserTests.cs rename to src/Kuddle.Net.Tests/Grammar/StringParserTests.cs index 88050cc..622221f 100644 --- a/src/Kuddle.Tests/Grammar/StringParserTests.cs +++ b/src/Kuddle.Net.Tests/Grammar/StringParserTests.cs @@ -20,7 +20,7 @@ public class StringParserTests [Arguments("-negative")] public async Task SignedIdent_ParsesSignedIdentifier(string input) { - var sut = KuddleGrammar.SignedIdent; + var sut = KdlGrammar.SignedIdent; bool success = sut.TryParse(input, out var value); @@ -34,7 +34,7 @@ public async Task SignedIdent_ParsesSignedIdentifier(string input) [Arguments(".three")] public async Task DottedIdent_ParsesDottedIdentifier(string input) { - var sut = KuddleGrammar.DottedIdent; + var sut = KdlGrammar.DottedIdent; bool success = sut.TryParse(input, out var value); @@ -47,7 +47,7 @@ public async Task DottedIdent_ParsesDottedIdentifier(string input) [Arguments(".01")] public async Task DottedIdent_DoesNotParseNumberDottedNumber(string input) { - var sut = KuddleGrammar.DottedIdent; + var sut = KdlGrammar.DottedIdent; bool success = sut.TryParse(input, out var value); @@ -62,7 +62,7 @@ public async Task DottedIdent_DoesNotParseNumberDottedNumber(string input) [Arguments("test_case")] public async Task UnambiguousIdent_ParsesUnambiguousIdentifier(string input) { - var sut = KuddleGrammar.UnambiguousIdent; + var sut = KdlGrammar.UnambiguousIdent; bool success = sut.TryParse(input, out var value); @@ -76,7 +76,7 @@ public async Task UnambiguousIdent_ParsesUnambiguousIdentifier(string input) [Arguments(".one")] public async Task UnambiguousIdent_DoesNotParseInvalidIdentifier(string input) { - var sut = KuddleGrammar.UnambiguousIdent; + var sut = KdlGrammar.UnambiguousIdent; bool success = sut.TryParse(input, out var value); @@ -92,7 +92,7 @@ public async Task UnambiguousIdent_DoesNotParseInvalidIdentifier(string input) [Arguments("nan")] public async Task UnambiguousIdent_DoesNotParseDisallowedKeywordString(string input) { - var sut = KuddleGrammar.UnambiguousIdent; + var sut = KdlGrammar.UnambiguousIdent; bool success = sut.TryParse(input, out var value); @@ -112,7 +112,7 @@ public async Task UnambiguousIdent_DoesNotParseDisallowedKeywordString(string in [Arguments("-negative")] public async Task IdentifierString_ParsesIdentifierString(string input) { - var sut = KuddleGrammar.IdentifierString; + var sut = KdlGrammar.IdentifierString; bool success = sut.TryParse(input, out var value); @@ -124,7 +124,7 @@ public async Task IdentifierString_ParsesIdentifierString(string input) [Arguments("\"\\ \"")] public async Task WsEscape_ParsesWhiteSpace(string input) { - var sut = KuddleGrammar.QuotedString; + var sut = KdlGrammar.QuotedString; bool success = sut.TryParse(input, out var value, out var error); @@ -135,7 +135,7 @@ public async Task WsEscape_ParsesWhiteSpace(string input) [Test] public async Task QuotedString_ParsesSingleLineString() { - var sut = KuddleGrammar.QuotedString; + var sut = KdlGrammar.QuotedString; const string input = """ "hello world" @@ -149,7 +149,7 @@ public async Task QuotedString_ParsesSingleLineString() [Test] public async Task QuotedString_ParsesEmptyString() { - var sut = KuddleGrammar.QuotedString; + var sut = KdlGrammar.QuotedString; const string input = """ "" @@ -163,7 +163,7 @@ public async Task QuotedString_ParsesEmptyString() [Test] public async Task QuotedString_ParsesEmptyMultilineString() { - var sut = KuddleGrammar.QuotedString; + var sut = KdlGrammar.QuotedString; const string input = """" """ @@ -206,7 +206,7 @@ This is the base indentation )] public async Task MultiLineStringBody_HandlesVarious(string input, string expected) { - var sut = KuddleGrammar.MultiLineQuoted; + var sut = KdlGrammar.MultiLineQuoted; bool success = sut.TryParse(input, out var value); Debug.WriteLine(input); @@ -234,7 +234,7 @@ canis canem edit )] public async Task MultiLineQuotedString_CanParseMultiLine(string input, string expected) { - var sut = KuddleGrammar.MultiLineQuoted; + var sut = KdlGrammar.MultiLineQuoted; bool success = sut.TryParse(input, out var value, out var error); await Assert.That(success).IsTrue(); await Assert.That(value.ToString()).IsEqualTo(expected); @@ -249,7 +249,7 @@ public async Task MultiLineQuotedString_CanParseMultiLine(string input, string e )] public async Task QuotedString_HandlesUnicodeEscapes(string input, string expected) { - var sut = KuddleGrammar.QuotedString; + var sut = KdlGrammar.QuotedString; bool success = sut.TryParse(input, out var value); @@ -271,7 +271,7 @@ public async Task QuotedString_HandlesUnicodeEscapes(string input, string expect )] public async Task QuotedString_HandlesWhitespaceEscapes(string input) { - var sut = KuddleGrammar.QuotedString; + var sut = KdlGrammar.QuotedString; bool success = sut.TryParse(input, out var value, out var error); @@ -283,7 +283,7 @@ public async Task QuotedString_HandlesWhitespaceEscapes(string input) [Test] public async Task RawString_ParsesSimpleRawString() { - var sut = KuddleGrammar.RawString; + var sut = KdlGrammar.RawString; var input = """ #"\n will be literal"# @@ -297,7 +297,7 @@ public async Task RawString_ParsesSimpleRawString() [Test] public async Task RawString_ParsesRawStringWithQuotes() { - var sut = KuddleGrammar.RawString; + var sut = KdlGrammar.RawString; var input = "#\"content with \"quotes\"\"#"; bool success = sut.TryParse(input, out var value); @@ -309,7 +309,7 @@ public async Task RawString_ParsesRawStringWithQuotes() [Test] public async Task RawString_HandlesMultipleHashDelimiters() { - var sut = KuddleGrammar.RawString; + var sut = KdlGrammar.RawString; var input = """ ##"hello\n\r\asd"#world"## @@ -329,7 +329,7 @@ await Assert [Test] public async Task RawString_ParsesMultiLineRawString() { - var sut = KuddleGrammar.RawString; + var sut = KdlGrammar.RawString; var input = """"" #""" @@ -354,7 +354,7 @@ without escapes. [Test] public async Task IdentifierString_SetsStyleToBare() { - var sut = KuddleGrammar.IdentifierString; + var sut = KdlGrammar.IdentifierString; var input = "bare_identifier"; bool success = sut.TryParse(input, out var value); @@ -367,7 +367,7 @@ public async Task IdentifierString_SetsStyleToBare() [Test] public async Task QuotedString_SetsStyleToQuoted() { - var sut = KuddleGrammar.QuotedString; + var sut = KdlGrammar.QuotedString; var input = "\"quoted value\""; bool success = sut.TryParse(input, out var value); @@ -381,7 +381,7 @@ public async Task QuotedString_SetsStyleToQuoted() [Test] public async Task MultiLineString_SetsStyleToMultiline() { - var sut = KuddleGrammar.MultiLineQuoted; + var sut = KdlGrammar.MultiLineQuoted; var input = """" """ content @@ -399,7 +399,7 @@ public async Task MultiLineString_SetsStyleToMultiline() [Test] public async Task RawString_SingleLine_SetsStyleToRawAndQuoted() { - var sut = KuddleGrammar.RawString; + var sut = KdlGrammar.RawString; var input = "#\"\"raw content\"\"#"; bool success = sut.TryParse(input, out var value); @@ -416,7 +416,7 @@ public async Task RawString_SingleLine_SetsStyleToRawAndQuoted() [Test] public async Task RawString_MultiLine_SetsStyleToRawAndMultiline() { - var sut = KuddleGrammar.RawString; + var sut = KdlGrammar.RawString; var input = """" #""" multi @@ -438,7 +438,7 @@ public async Task RawString_MultiLine_SetsStyleToRawAndMultiline() [Test] public async Task String_UnifiedParser_DetectsBare() { - var sut = KuddleGrammar.String; + var sut = KdlGrammar.String; var input = "node_name"; bool success = sut.TryParse(input, out var value); @@ -450,7 +450,7 @@ public async Task String_UnifiedParser_DetectsBare() [Test] public async Task String_UnifiedParser_DetectsQuoted() { - var sut = KuddleGrammar.String; + var sut = KdlGrammar.String; var input = "\"node name\""; bool success = sut.TryParse(input, out var value); @@ -462,7 +462,7 @@ public async Task String_UnifiedParser_DetectsQuoted() [Test] public async Task String_UnifiedParser_DetectsRaw() { - var sut = KuddleGrammar.String; + var sut = KdlGrammar.String; var input = @"#""node name""#"; bool success = sut.TryParse(input, out var value); diff --git a/src/Kuddle.Tests/Grammar/WhiteSpaceParsersTests.cs b/src/Kuddle.Net.Tests/Grammar/WhiteSpaceParsersTests.cs similarity index 91% rename from src/Kuddle.Tests/Grammar/WhiteSpaceParsersTests.cs rename to src/Kuddle.Net.Tests/Grammar/WhiteSpaceParsersTests.cs index 2613baa..4dd9a40 100644 --- a/src/Kuddle.Tests/Grammar/WhiteSpaceParsersTests.cs +++ b/src/Kuddle.Net.Tests/Grammar/WhiteSpaceParsersTests.cs @@ -33,7 +33,7 @@ public class WhiteSpaceParsersTests [Arguments('\u3000')] public async Task Ws_ParsesUnicodeSpace(char input) { - var sut = KuddleGrammar.Ws; + var sut = KdlGrammar.Ws; bool success = sut.TryParse(input.ToString(), out var value); @@ -44,7 +44,7 @@ public async Task Ws_ParsesUnicodeSpace(char input) [Test] public async Task Ws_ParsesMultiLineComment() { - var sut = KuddleGrammar.Ws; + var sut = KdlGrammar.Ws; var input = "/* comment */"; bool success = sut.TryParse(input, out var value); @@ -56,7 +56,7 @@ public async Task Ws_ParsesMultiLineComment() [Test] public async Task EscLine_ParsesBackslashContinuation() { - var sut = KuddleGrammar.EscLine; + var sut = KdlGrammar.EscLine; var input = @"\ @@ -69,7 +69,7 @@ public async Task EscLine_ParsesBackslashContinuation() [Test] public async Task EscLine_ParsesBackslashWithComment() { - var sut = KuddleGrammar.EscLine; + var sut = KdlGrammar.EscLine; var input = @"\ // comment"; bool success = sut.TryParse(input, out var value); @@ -81,7 +81,7 @@ public async Task EscLine_ParsesBackslashWithComment() [Test] public async Task LineSpace_ParsesWhitespace() { - var sut = KuddleGrammar.LineSpace; + var sut = KdlGrammar.LineSpace; var input = " "; bool success = sut.TryParse(input, out var value); @@ -93,7 +93,7 @@ public async Task LineSpace_ParsesWhitespace() [Test] public async Task LineSpace_ParsesNewLine() { - var sut = KuddleGrammar.LineSpace; + var sut = KdlGrammar.LineSpace; var input = "\n"; bool success = sut.TryParse(input, out var value); @@ -105,7 +105,7 @@ public async Task LineSpace_ParsesNewLine() [Test] public async Task LineSpace_ParsesComment() { - var sut = KuddleGrammar.LineSpace; + var sut = KdlGrammar.LineSpace; var input = "// comment"; bool success = sut.TryParse(input, out var value); @@ -117,7 +117,7 @@ public async Task LineSpace_ParsesComment() [Test] public async Task NodeSpace_ParsesSimpleWhitespace() { - var sut = KuddleGrammar.NodeSpace; + var sut = KdlGrammar.NodeSpace; var input = " "; bool success = sut.TryParse(input, out var value); diff --git a/src/Kuddle.Tests/Kuddle.Tests.csproj b/src/Kuddle.Net.Tests/Kuddle.Net.Tests.csproj similarity index 78% rename from src/Kuddle.Tests/Kuddle.Tests.csproj rename to src/Kuddle.Net.Tests/Kuddle.Net.Tests.csproj index 765c78f..af186aa 100644 --- a/src/Kuddle.Tests/Kuddle.Tests.csproj +++ b/src/Kuddle.Net.Tests/Kuddle.Net.Tests.csproj @@ -1,5 +1,7 @@  + Kuddle.Tests + Kuddle.Net.Tests enable enable Exe @@ -10,7 +12,7 @@ - + Plugins { get; set; } = new(); + + [KdlNode("logging")] + public LogSettings? Logging { get; set; } + + [KdlNode("experiments")] + public Experiments? Experiments { get; set; } + } + + class Plugin + { + [KdlArgument(0)] + public string Name { get; set; } = ""; + } + + class LogSettings + { + [KdlNode("level")] + public string LogLevel { get; set; } = "info"; + } + + class Experiments + { + [KdlProperty("enabled")] + public bool Enabled { get; set; } + } + + // --- Tests --- + + [Test] + public async Task Deserialize_Document_MapsListsAndSingleObjects() + { + var kdl = """ + plugin "Analytics" + plugin "Authentication" + + logging { + level "debug" + } + """; + + var result = KdlSerializer.Deserialize(kdl); + + // Assert + await Assert.That(result.Plugins).Count().IsEqualTo(2); + await Assert.That(result.Plugins[0].Name).IsEqualTo("Analytics"); + await Assert.That(result.Plugins[1].Name).IsEqualTo("Authentication"); + + await Assert.That(result.Logging).IsNotNull(); + + await Assert.That(result.Logging!.LogLevel).IsEqualTo("debug"); + } + + [Test] + public async Task Deserialize_EmptyDocument_ReturnsInitializedObject() + { + var kdl = ""; + + var result = KdlSerializer.Deserialize(kdl); + + await Assert.That(result).IsNotNull(); + await Assert.That(result.Plugins).IsEmpty(); + await Assert.That(result.Logging).IsNull(); + } + + [Test] + public async Task Deserialize_PartialMatch_IgnoresUnmappedNodes() + { + var kdl = """ + plugin "Core" + + // This node is not in AppConfig + garbage_data { + ignore me + } + """; + + var result = KdlSerializer.Deserialize(kdl); + + await Assert.That(result.Plugins).Count().IsEqualTo(1); + await Assert.That(result.Plugins[0].Name).IsEqualTo("Core"); + + await Assert.That(result.Logging).IsNull(); + } + + [Test] + public async Task Deserialize_NestedStructure_WithProperties() + { + var kdl = + @" + experiments enabled=#true + "; + + var result = KdlSerializer.Deserialize(kdl); + + await Assert.That(result.Experiments).IsNotNull(); + await Assert.That(result.Experiments!.Enabled).IsTrue(); + } + + [Test] + public async Task Deserialize_AmbiguousSingleNode_ThrowsOrHandles() + { + var kdl = + @" + logging { level ""info"" } + logging { level ""error"" } + "; + + await Assert.ThrowsAsync(async () => + { + KdlSerializer.Deserialize(kdl); + }); + } +} diff --git a/src/Kuddle.Tests/Serialization/KuddleParsingTests.cs b/src/Kuddle.Net.Tests/Serialization/KuddleParsingTests.cs similarity index 77% rename from src/Kuddle.Tests/Serialization/KuddleParsingTests.cs rename to src/Kuddle.Net.Tests/Serialization/KuddleParsingTests.cs index 9cdc429..57ad418 100644 --- a/src/Kuddle.Tests/Serialization/KuddleParsingTests.cs +++ b/src/Kuddle.Net.Tests/Serialization/KuddleParsingTests.cs @@ -4,7 +4,7 @@ namespace Kuddle.Tests.Serialization; public class KuddleParsingTests { - readonly KuddleWriterOptions _options = new KuddleWriterOptions() with { RoundTrip = false }; + readonly KdlWriterOptions _options = new KdlWriterOptions() with { RoundTrip = false }; [Test] [MethodDataSource( @@ -16,7 +16,7 @@ public async Task TestBlockComment(ParsingTestData testData) if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -24,7 +24,7 @@ public async Task TestBlockComment(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } @@ -38,7 +38,7 @@ public async Task TestEscline(ParsingTestData testData) if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -46,7 +46,7 @@ public async Task TestEscline(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } @@ -60,7 +60,7 @@ public async Task TestMultilineRawString(ParsingTestData testData) if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -68,7 +68,7 @@ public async Task TestMultilineRawString(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } @@ -82,7 +82,7 @@ public async Task TestMultilineString(ParsingTestData testData) if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -90,18 +90,21 @@ public async Task TestMultilineString(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.ArgTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.ArgTestData) + )] public async Task TestArg(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); // Assumes KdlDocument has ToString() for serialization @@ -109,7 +112,7 @@ public async Task TestArg(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } @@ -123,7 +126,7 @@ public async Task TestBareIdent(ParsingTestData testData) if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -131,7 +134,7 @@ public async Task TestBareIdent(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } @@ -145,7 +148,7 @@ public async Task TestBinary(ParsingTestData testData) if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -153,18 +156,21 @@ public async Task TestBinary(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.BlankTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.BlankTestData) + )] public async Task TestBlank(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -172,7 +178,7 @@ public async Task TestBlank(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } @@ -186,7 +192,7 @@ public async Task TestBoolean(ParsingTestData testData) if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -194,18 +200,21 @@ public async Task TestBoolean(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.BomTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.BomTestData) + )] public async Task TestBom(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -213,7 +222,7 @@ public async Task TestBom(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } @@ -227,7 +236,7 @@ public async Task TestComment(ParsingTestData testData) if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -235,7 +244,7 @@ public async Task TestComment(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } @@ -249,7 +258,7 @@ public async Task TestCommented(ParsingTestData testData) if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -257,18 +266,21 @@ public async Task TestCommented(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.EmptyTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.EmptyTestData) + )] public async Task TestEmpty(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -276,18 +288,21 @@ public async Task TestEmpty(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.EscTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.EscTestData) + )] public async Task TestEsc(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -295,18 +310,21 @@ public async Task TestEsc(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.FalseTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.FalseTestData) + )] public async Task TestFalse(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -314,7 +332,7 @@ public async Task TestFalse(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } @@ -328,7 +346,7 @@ public async Task TestIllegal(ParsingTestData testData) if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -336,7 +354,7 @@ public async Task TestIllegal(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } @@ -350,7 +368,7 @@ public async Task TestBareEmoji(ParsingTestData testData) if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -358,18 +376,21 @@ public async Task TestBareEmoji(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.AllTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.AllTestData) + )] public async Task TestAll(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -377,7 +398,7 @@ public async Task TestAll(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } @@ -391,7 +412,7 @@ public async Task TestAsterisk(ParsingTestData testData) if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -399,7 +420,7 @@ public async Task TestAsterisk(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } @@ -413,7 +434,7 @@ public async Task TestBraces(ParsingTestData testData) if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -421,7 +442,7 @@ public async Task TestBraces(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } @@ -435,7 +456,7 @@ public async Task TestChevrons(ParsingTestData testData) if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -443,18 +464,21 @@ public async Task TestChevrons(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.CommaTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.CommaTestData) + )] public async Task TestComma(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -462,18 +486,21 @@ public async Task TestComma(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.CrlfTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.CrlfTestData) + )] public async Task TestCrlf(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -481,18 +508,21 @@ public async Task TestCrlf(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.DashTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.DashTestData) + )] public async Task TestDash(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -500,18 +530,21 @@ public async Task TestDash(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.DotTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.DotTestData) + )] public async Task TestDot(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -519,18 +552,21 @@ public async Task TestDot(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.EmojiTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.EmojiTestData) + )] public async Task TestEmoji(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -538,18 +574,21 @@ public async Task TestEmoji(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.EofTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.EofTestData) + )] public async Task TestEof(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -557,18 +596,21 @@ public async Task TestEof(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.ErrTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.ErrTestData) + )] public async Task TestErr(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -576,7 +618,7 @@ public async Task TestErr(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } @@ -590,7 +632,7 @@ public async Task TestEscaped(ParsingTestData testData) if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -598,18 +640,21 @@ public async Task TestEscaped(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.HexTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.HexTestData) + )] public async Task TestHex(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -617,7 +662,7 @@ public async Task TestHex(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } @@ -631,7 +676,7 @@ public async Task TestInitial(ParsingTestData testData) if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -639,18 +684,21 @@ public async Task TestInitial(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.IntTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.IntTestData) + )] public async Task TestInt(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -658,18 +706,21 @@ public async Task TestInt(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } [Test] - [MethodDataSource(typeof(KuddleParsingTestDataSources), nameof(KuddleParsingTestDataSources.JustTestData))] + [MethodDataSource( + typeof(KuddleParsingTestDataSources), + nameof(KuddleParsingTestDataSources.JustTestData) + )] public async Task TestJust(ParsingTestData testData) { if (File.Exists(testData.ExpectedFile)) { var inputKdl = await File.ReadAllTextAsync(testData.InputFile); - var doc = KuddleReader.Parse(inputKdl); + var doc = KdlReader.Read(inputKdl); var expected = await File.ReadAllTextAsync(testData.ExpectedFile); expected = expected.Replace("\r\n", "\n"); var serialized = doc.ToString(_options); @@ -677,7 +728,7 @@ public async Task TestJust(ParsingTestData testData) } else { - Assert.Throws(() => KuddleReader.Parse(testData.InputFile)); + Assert.Throws(() => KdlReader.Read(testData.InputFile)); } } } diff --git a/src/Kuddle.Tests/Serialization/KuddleWriterTests.cs b/src/Kuddle.Net.Tests/Serialization/KuddleWriterTests.cs similarity index 76% rename from src/Kuddle.Tests/Serialization/KuddleWriterTests.cs rename to src/Kuddle.Net.Tests/Serialization/KuddleWriterTests.cs index 357858c..ed5bb09 100644 --- a/src/Kuddle.Tests/Serialization/KuddleWriterTests.cs +++ b/src/Kuddle.Net.Tests/Serialization/KuddleWriterTests.cs @@ -10,9 +10,9 @@ public class KuddleWriterTests public async Task Write_SimpleNode_FormatsCorrectly() { var kdl = "node 1 2 key=\"val\""; - var doc = KuddleReader.Parse(kdl); + var doc = KdlReader.Read(kdl); - var output = KuddleWriter.Write(doc, KuddleWriterOptions.Default); + var output = KdlWriter.Write(doc, KdlWriterOptions.Default); await Assert.That(output.Trim()).IsEqualTo("node 1 2 key=\"val\""); } @@ -21,9 +21,9 @@ public async Task Write_SimpleNode_FormatsCorrectly() public async Task Write_NestedStructure_IndentsCorrectly() { var kdl = "parent { child; }"; - var doc = KuddleReader.Parse(kdl); + var doc = KdlReader.Read(kdl); - var output = KuddleWriter.Write(doc); + var output = KdlWriter.Write(doc); var expected = @"parent { child; @@ -36,9 +36,9 @@ public async Task Write_NestedStructure_IndentsCorrectly() public async Task Write_ComplexString_EscapesCorrectly() { var kdl = "node \"line1\\nline2\""; - var doc = KuddleReader.Parse(kdl); + var doc = KdlReader.Read(kdl); - var output = KuddleWriter.Write(doc); + var output = KdlWriter.Write(doc); await Assert.That(output.Trim()).IsEqualTo("node \"line1\\nline2\""); } @@ -51,7 +51,7 @@ public async Task Write_BareIdentifier_QuotesIfInvalid() Nodes = [new KdlNode(new KdlString("node name", StringKind.Quoted))], }; - var output = KuddleWriter.Write(doc); + var output = KdlWriter.Write(doc); await Assert.That(output.Trim()).IsEqualTo("\"node name\""); } diff --git a/src/Kuddle.Net.Tests/Serialization/NodeToObjectTests.cs b/src/Kuddle.Net.Tests/Serialization/NodeToObjectTests.cs new file mode 100644 index 0000000..62933b1 --- /dev/null +++ b/src/Kuddle.Net.Tests/Serialization/NodeToObjectTests.cs @@ -0,0 +1,107 @@ +using Kuddle.Exceptions; +using Kuddle.Serialization; + +namespace Kuddle.Tests.Serialization; + +public class NodeToObjectTests +{ + [KdlType("database")] + class DbConfig + { + [KdlArgument(0)] + public string Name { get; set; } = ""; + + [KdlProperty("port")] + public int Port { get; set; } + + [KdlProperty("enabled")] + public bool Enabled { get; set; } = true; + } + + public class Server + { + [KdlArgument(0)] + public string Host { get; set; } = ""; + } + + [Test] + public async Task Deserialize_ExplicitName_MapsArgumentsAndProperties() + { + var kdl = "database production port=5432 enabled=#false"; + + var result = KdlSerializer.Deserialize(kdl); + + await Assert.That(result).IsNotNull(); + await Assert.That(result.Name).IsEqualTo("production"); + await Assert.That(result.Port).IsEqualTo(5432); + await Assert.That(result.Enabled).IsFalse(); + } + + [Test] + public async Task Deserialize_ImplicitName_UsesClassName() + { + var kdl = "Server \"localhost\""; + + var result = KdlSerializer.Deserialize(kdl); + + await Assert.That(result).IsNotNull(); + await Assert.That(result.Host).IsEqualTo("localhost"); + } + + [Test] + public async Task Deserialize_MissingProperty_UsesDefault() + { + var kdl = "database \"local\" port=3000"; + + var result = KdlSerializer.Deserialize(kdl); + + await Assert.That(result.Name).IsEqualTo("local"); + await Assert.That(result.Enabled).IsTrue(); + } + + [Test] + public async Task Deserialize_MismatchNodeName_Throws() + { + var kdl = "table \"production\" port=5432"; + + await Assert.ThrowsAsync(async () => + { + KdlSerializer.Deserialize(kdl); + }); + } + + [Test] + public async Task Deserialize_MultipleRootNodes_Throws() + { + var kdl = + @" + database ""primary"" port=5432 + database ""replica"" port=5433 + "; + + await Assert.ThrowsAsync(async () => + { + KdlSerializer.Deserialize(kdl); + }); + } + + [Test] + public async Task Deserialize_TypeMismatch_Throws() + { + var kdl = "database \"db\" port=\"not-a-number\""; + + await Assert.ThrowsAsync(async () => + { + KdlSerializer.Deserialize(kdl); + }); + } + + [Test] + public async Task Deserialize_DocumentRoot_RejectsProperties() + { + var kdl = "database \"db\" port=5432 unknown_prop=123"; + + var result = KdlSerializer.Deserialize(kdl); + await Assert.That(result.Port).IsEqualTo(5432); + } +} diff --git a/src/Kuddle.Tests/Serialization/ObjectMapperTests.cs b/src/Kuddle.Net.Tests/Serialization/ObjectMapperTests.cs similarity index 86% rename from src/Kuddle.Tests/Serialization/ObjectMapperTests.cs rename to src/Kuddle.Net.Tests/Serialization/ObjectMapperTests.cs index 27dd4a8..718408c 100644 --- a/src/Kuddle.Tests/Serialization/ObjectMapperTests.cs +++ b/src/Kuddle.Net.Tests/Serialization/ObjectMapperTests.cs @@ -1,11 +1,10 @@ using Kuddle.AST; -using Kuddle.Exceptions; using Kuddle.Serialization; namespace Kuddle.Tests.Serialization; /// -/// Tests for await KdlSerializer.Deserialize() and KdlSerializer.Serialize() +/// Tests for KdlSerializer.Deserialize() and KdlSerializer.Serialize() /// which map between KDL documents and strongly-typed C# objects. /// public class ObjectMapperTests @@ -21,7 +20,7 @@ public async Task DeserializeSimpleObject_WithArgument_MapsCorrectly() """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.Name).IsEqualTo("my-lib"); @@ -36,7 +35,7 @@ public async Task DeserializeSimpleObject_WithProperties_MapsCorrectly() """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.Name).IsEqualTo("my-lib"); @@ -53,7 +52,7 @@ public async Task DeserializeObject_WithMissingOptionalProperty_UsesDefault() """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.Description).IsNull(); @@ -77,7 +76,7 @@ public async Task DeserializeObject_WithChildren_MapsChildListCorrectly() """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.Name).IsEqualTo("my-app"); @@ -101,7 +100,7 @@ public async Task DeserializeObject_WithMultipleChildTypes_MapsEachTypeToCorrect """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.Dependencies).Count().IsEqualTo(2); @@ -119,7 +118,7 @@ public async Task DeserializeObject_WithNoChildren_InitializesEmptyLists() """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.Dependencies).IsEmpty(); @@ -139,7 +138,7 @@ public async Task DeserializeObject_WithIntProperty_MapsCorrectly() """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.Timeout).IsEqualTo(5000); @@ -154,7 +153,7 @@ public async Task DeserializeObject_WithLongProperty_MapsCorrectly() """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.Retries).IsEqualTo(long.MaxValue); @@ -169,7 +168,7 @@ public async Task DeserializeObject_WithDoubleProperty_MapsCorrectly() """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.Ratio).IsEqualTo(3.14159).Within(0.00001); @@ -184,7 +183,7 @@ public async Task DeserializeObject_WithBoolProperty_MapsCorrectly() """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.Enabled).IsTrue(); @@ -199,7 +198,7 @@ public async Task DeserializeObject_WithAllNumericTypes_MapsAllCorrectly() """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.Timeout).IsEqualTo(500); @@ -222,7 +221,7 @@ public async Task DeserializeObject_WithGuidProperty_ParsesUuidTypeAnnotation() """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.Username).IsEqualTo("alice"); @@ -239,7 +238,7 @@ public async Task DeserializeObject_WithDateTimeProperty_ParsesDateTimeTypeAnnot """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.CreatedAt).IsEqualTo(now); @@ -309,7 +308,7 @@ public async Task RoundTrip_SimpleObject_PreservesData() // Act var kdl = KdlSerializer.Serialize(original); - var deserialized = await KdlSerializer.Deserialize(kdl); + var deserialized = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(deserialized.Name).IsEqualTo(original.Name); @@ -344,7 +343,7 @@ public async Task RoundTrip_NestedObject_PreservesData() // Act var kdl = KdlSerializer.Serialize(original); - var deserialized = await KdlSerializer.Deserialize(kdl); + var deserialized = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(deserialized.Name).IsEqualTo(original.Name); @@ -366,7 +365,7 @@ public async Task RoundTrip_NestedObject_PreservesData() // """; // // Act - // var result = await KdlSerializer.Deserialize(kdl); + // var result = KdlSerializer.Deserialize(kdl); // // Assert // await Assert.That(result).IsOfType(typeof(FileResource)); @@ -383,7 +382,7 @@ public async Task RoundTrip_NestedObject_PreservesData() // """; // // Act - // var result = await KdlSerializer.Deserialize(kdl); + // var result = KdlSerializer.Deserialize(kdl); // // Assert // await Assert.That(result).IsOfType(typeof(UrlResource)); @@ -404,7 +403,7 @@ public async Task DeserializeObject_WithEmptyString_MapsCorrectly() """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.Name).IsEqualTo(""); @@ -420,7 +419,7 @@ public async Task DeserializeObject_WithSpecialCharactersInString_MapsCorrectly( """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.Name).IsEqualTo("my-lib@1.0"); @@ -436,7 +435,7 @@ public async Task DeserializeObject_WithNegativeNumbers_MapsCorrectly() """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.Timeout).IsEqualTo(-1000); @@ -452,7 +451,7 @@ public async Task DeserializeObject_WithHexNumbers_MapsCorrectly() """; // Act - var result = await KdlSerializer.Deserialize(kdl); + var result = KdlSerializer.Deserialize(kdl); // Assert await Assert.That(result.Timeout).IsEqualTo(255); @@ -473,7 +472,7 @@ public async Task DeserializeObject_WithWrongNodeName_ThrowsException() // Act & Assert await Assert - .That(async () => await KdlSerializer.Deserialize(kdl)) + .That(async () => KdlSerializer.Deserialize(kdl)) .Throws(); } @@ -489,7 +488,7 @@ public async Task DeserializeToScalar_WithMultipleRootNodes_ThrowsException() // Act & Assert await Assert - .That(async () => await KdlSerializer.Deserialize(kdl)) + .That(async () => KdlSerializer.Deserialize(kdl)) .Throws(); } @@ -505,7 +504,7 @@ public async Task DeserializeToDictionary_ThrowsException() // Act & Assert await Assert - .That(async () => await KdlSerializer.Deserialize>(kdl)) + .That(() => KdlSerializer.Deserialize>(kdl)) .Throws() .WithMessageContaining("not supported"); } @@ -522,7 +521,7 @@ public async Task DeserializeToEnumerable_WithDifferentNodeNames_ThrowsException // Act & Assert await Assert - .That(async () => await KdlSerializer.Deserialize>(kdl)) + .That(async () => KdlSerializer.Deserialize>(kdl)) .Throws(); } @@ -536,7 +535,7 @@ public async Task DeserializeObject_WithInvalidNumericValue_ThrowsException() // Act & Assert await Assert - .That(async () => await KdlSerializer.Deserialize(kdl)) + .That(async () => KdlSerializer.Deserialize(kdl)) .Throws(); } @@ -550,24 +549,24 @@ public async Task DeserializeObject_WithMissingRequiredArgument_ThrowsException( // Act & Assert await Assert - .That(async () => await KdlSerializer.Deserialize(kdl)) + .That(async () => KdlSerializer.Deserialize(kdl)) .Throws(); } - [Test] - public async Task DeserializeObject_WhenTargetIsSimpleValue_ThrowsException() - { - // Arrange - var kdl = """ - package - """; + // [Test] + // public async Task DeserializeObject_WhenTargetIsSimpleValue_ThrowsException() + // { + // // Arrange + // var kdl = """ + // package + // """; - // Act & Assert - await Assert - .That(async () => await KdlSerializer.Deserialize(kdl)) - .Throws() - .WithMessageContaining("Cannot deserialize type"); - } + // // Act & Assert + // await Assert + // .That(async () => KdlSerializer.Deserialize(kdl)) + // .Throws() + // .WithMessageContaining("Cannot deserialize type"); + // } #endregion #region Test Models @@ -594,10 +593,10 @@ public class Project [KdlProperty("version")] public string Version { get; set; } = "1.0.0"; - [KdlChildren("dependency")] + [KdlNode("dependency")] public List Dependencies { get; set; } = []; - [KdlChildren("devDependency")] + [KdlNode("devDependency")] public List DevDependencies { get; set; } = []; } diff --git a/src/Kuddle.Net.Tests/Serialization/TypeAnnotationTests.cs b/src/Kuddle.Net.Tests/Serialization/TypeAnnotationTests.cs new file mode 100644 index 0000000..20a3c6f --- /dev/null +++ b/src/Kuddle.Net.Tests/Serialization/TypeAnnotationTests.cs @@ -0,0 +1,227 @@ +using System; +using Kuddle.Serialization; + +namespace Kuddle.Tests.Serialization; + +public class TypeAnnotationTests +{ + #region Test Models + + public class PersonWithAnnotations + { + [KdlArgument(0, "uuid")] + public Guid Id { get; set; } + + [KdlProperty("name")] + public string Name { get; set; } = ""; + + [KdlProperty("created", "date-time")] + public DateTimeOffset CreatedAt { get; set; } + + [KdlProperty("age", "i32")] + public int Age { get; set; } + } + + public class ProductWithTypeAnnotations + { + [KdlArgument(0)] + public string Name { get; set; } = ""; + + [KdlProperty("sku", "uuid")] + public Guid Sku { get; set; } + + [KdlProperty("price", "decimal64")] + public decimal Price { get; set; } + + [KdlProperty("stock", "u32")] + public uint StockCount { get; set; } + } + + public class EventWithChildAnnotations + { + [KdlArgument(0)] + public string Name { get; set; } = ""; + + [KdlNode("timestamp", "date-time")] + public DateTimeOffset Timestamp { get; set; } + + [KdlNode("correlation-id", "uuid")] + public Guid CorrelationId { get; set; } + } + + #endregion + + [Test] + public async Task Serialize_WithUuidTypeAnnotation_WritesAnnotation() + { + // Arrange + var person = new PersonWithAnnotations + { + Id = Guid.Parse("123e4567-e89b-12d3-a456-426614174000"), + Name = "Alice", + CreatedAt = new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero), + Age = 30, + }; + + // Act + var kdl = KdlSerializer.Serialize(person); + + // Assert + await Assert.That(kdl).Contains("(uuid)\"123e4567-e89b-12d3-a456-426614174000\""); + await Assert.That(kdl).Contains("(date-time)\"2024-01-01T00:00:00.0000000+00:00\""); + await Assert.That(kdl).Contains("(i32)30"); + } + + [Test] + public async Task Deserialize_WithUuidTypeAnnotation_ParsesCorrectly() + { + // Arrange + var kdl = """ + personwithannotations (uuid)"123e4567-e89b-12d3-a456-426614174000" name="Alice" created=(date-time)"2024-01-01T00:00:00.0000000+00:00" age=(i32)30 + """; + + // Act + var person = KdlSerializer.Deserialize(kdl); + + // Assert + await Assert.That(person.Id).IsEqualTo(Guid.Parse("123e4567-e89b-12d3-a456-426614174000")); + await Assert.That(person.Name).IsEqualTo("Alice"); + await Assert + .That(person.CreatedAt) + .IsEqualTo(new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero)); + await Assert.That(person.Age).IsEqualTo(30); + } + + [Test] + public async Task Serialize_WithMixedTypeAnnotations_WritesAllAnnotations() + { + // Arrange + var product = new ProductWithTypeAnnotations + { + Name = "Widget", + Sku = Guid.Parse("a1b2c3d4-e5f6-7890-abcd-ef1234567890"), + Price = 99.99m, + StockCount = 42, + }; + + // Act + var kdl = KdlSerializer.Serialize(product); + + // Assert + await Assert.That(kdl).Contains("Widget"); + await Assert.That(kdl).Contains("(uuid)\"a1b2c3d4-e5f6-7890-abcd-ef1234567890\""); + await Assert.That(kdl).Contains("(decimal64)99.99"); + await Assert.That(kdl).Contains("(u32)42"); + } + + [Test] + public async Task Deserialize_WithMixedTypeAnnotations_ParsesAllValues() + { + // Arrange + var kdl = """ + productwithtypeannotations "Widget" sku=(uuid)"a1b2c3d4-e5f6-7890-abcd-ef1234567890" price=(decimal64)99.99 stock=(u32)42 + """; + + // Act + var product = KdlSerializer.Deserialize(kdl); + + // Assert + await Assert.That(product.Name).IsEqualTo("Widget"); + await Assert + .That(product.Sku) + .IsEqualTo(Guid.Parse("a1b2c3d4-e5f6-7890-abcd-ef1234567890")); + await Assert.That(product.Price).IsEqualTo(99.99m); + await Assert.That(product.StockCount).IsEqualTo(42u); + } + + [Test] + public async Task Serialize_WithChildNodeAnnotations_WritesAnnotationsOnChildren() + { + // Arrange + var evt = new EventWithChildAnnotations + { + Name = "UserLoggedIn", + Timestamp = new DateTimeOffset(2024, 12, 17, 10, 30, 0, TimeSpan.Zero), + CorrelationId = Guid.Parse("11111111-2222-3333-4444-555555555555"), + }; + + // Act + var kdl = KdlSerializer.Serialize(evt); + + // Assert + await Assert + .That(kdl) + .Contains("timestamp (date-time)\"2024-12-17T10:30:00.0000000+00:00\""); + await Assert + .That(kdl) + .Contains("correlation-id (uuid)\"11111111-2222-3333-4444-555555555555\""); + } + + [Test] + public async Task Deserialize_WithChildNodeAnnotations_ParsesChildValues() + { + // Arrange + var kdl = """ + eventwithchildannotations "UserLoggedIn" { + timestamp (date-time)"2024-12-17T10:30:00.0000000+00:00" + correlation-id (uuid)"11111111-2222-3333-4444-555555555555" + } + """; + + // Act + var evt = KdlSerializer.Deserialize(kdl); + + // Assert + await Assert.That(evt.Name).IsEqualTo("UserLoggedIn"); + await Assert + .That(evt.Timestamp) + .IsEqualTo(new DateTimeOffset(2024, 12, 17, 10, 30, 0, TimeSpan.Zero)); + await Assert + .That(evt.CorrelationId) + .IsEqualTo(Guid.Parse("11111111-2222-3333-4444-555555555555")); + } + + [Test] + public async Task RoundTrip_WithTypeAnnotations_PreservesValues() + { + // Arrange + var original = new PersonWithAnnotations + { + Id = Guid.NewGuid(), + Name = "Bob", + CreatedAt = DateTimeOffset.UtcNow, + Age = 25, + }; + + // Act + var kdl = KdlSerializer.Serialize(original); + var deserialized = KdlSerializer.Deserialize(kdl); + + // Assert + await Assert.That(deserialized.Id).IsEqualTo(original.Id); + await Assert.That(deserialized.Name).IsEqualTo(original.Name); + await Assert.That(deserialized.CreatedAt).IsEqualTo(original.CreatedAt); + await Assert.That(deserialized.Age).IsEqualTo(original.Age); + } + + [Test] + public async Task Deserialize_WithoutTypeAnnotationInKdl_StillWorks() + { + // Arrange - KDL without type annotation, but C# model has [KdlTypeAnnotation] + // The deserializer should still work because Guid/DateTimeOffset have their own parsing logic + var kdl = """ + personwithannotations "123e4567-e89b-12d3-a456-426614174000" name="Alice" created="2024-01-01T00:00:00.0000000+00:00" age=30 + """; + + // Act + var person = KdlSerializer.Deserialize(kdl); + + // Assert - should parse without type annotations in the KDL + await Assert.That(person.Id).IsEqualTo(Guid.Parse("123e4567-e89b-12d3-a456-426614174000")); + await Assert.That(person.Name).IsEqualTo("Alice"); + await Assert + .That(person.CreatedAt) + .IsEqualTo(new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero)); + await Assert.That(person.Age).IsEqualTo(30); + } +} diff --git a/src/Kuddle.Tests/Validation/ReservedTypeValidatorTests.cs b/src/Kuddle.Net.Tests/Validation/ReservedTypeValidatorTests.cs similarity index 82% rename from src/Kuddle.Tests/Validation/ReservedTypeValidatorTests.cs rename to src/Kuddle.Net.Tests/Validation/ReservedTypeValidatorTests.cs index 22deb82..6f4c310 100644 --- a/src/Kuddle.Tests/Validation/ReservedTypeValidatorTests.cs +++ b/src/Kuddle.Net.Tests/Validation/ReservedTypeValidatorTests.cs @@ -1,5 +1,6 @@ using Kuddle.AST; using Kuddle.Exceptions; +using Kuddle.Serialization; using Kuddle.Validation; namespace Kuddle.Tests.Validation; @@ -15,7 +16,7 @@ public async Task Given_ValidU8_When_Validated_Then_NoExceptionThrown() var doc = Parse("node 255"); // Act & Assert - await Assert.That(() => KuddleReservedTypeValidator.Validate(doc)).ThrowsNothing(); + await Assert.That(() => KdlReservedTypeValidator.Validate(doc)).ThrowsNothing(); } [Test] @@ -26,7 +27,7 @@ public async Task Given_OverflowingU8_When_Validated_Then_ThrowsValidationExcept // Act var exception = await Assert - .That(() => KuddleReservedTypeValidator.Validate(doc)) + .That(() => KdlReservedTypeValidator.Validate(doc)) .Throws(); // Assert @@ -42,7 +43,7 @@ public async Task Given_StringForIntType_When_Validated_Then_ThrowsMismatchError // Act var exception = Assert.Throws(() => - KuddleReservedTypeValidator.Validate(doc) + KdlReservedTypeValidator.Validate(doc) ); // Assert @@ -57,7 +58,7 @@ public async Task Given_InvalidUuid_When_Validated_Then_ThrowsError() // Act var exception = Assert.Throws(() => - KuddleReservedTypeValidator.Validate(doc) + KdlReservedTypeValidator.Validate(doc) ); // Assert @@ -71,14 +72,14 @@ public async Task Given_NodeNameWithReservedType_When_Validated_Then_IgnoresIt() var doc = Parse("node (u8)123"); // Act & Assert - await Assert.That(() => KuddleReservedTypeValidator.Validate(doc)).ThrowsNothing(); + await Assert.That(() => KdlReservedTypeValidator.Validate(doc)).ThrowsNothing(); } private static KdlDocument Parse(string input) { - return KuddleReader.Parse( + return KdlReader.Read( input, - KuddleReaderOptions.Default with + KdlReaderOptions.Default with { ValidateReservedTypes = false, } diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/all_escapes.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/all_escapes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/all_escapes.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/all_escapes.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/all_node_fields.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/all_node_fields.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/all_node_fields.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/all_node_fields.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/arg_and_prop_same_name.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_and_prop_same_name.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/arg_and_prop_same_name.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_and_prop_same_name.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/arg_bare.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_bare.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/arg_bare.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_bare.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/arg_false_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_false_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/arg_false_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_false_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/arg_float_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_float_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/arg_float_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_float_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/arg_hex_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_hex_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/arg_hex_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_hex_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/arg_null_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_null_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/arg_null_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_null_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/arg_raw_string_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_raw_string_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/arg_raw_string_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_raw_string_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/arg_string_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_string_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/arg_string_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_string_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/arg_true_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_true_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/arg_true_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_true_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/arg_zero_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_zero_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/arg_zero_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/arg_zero_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/asterisk_in_block_comment.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/asterisk_in_block_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/asterisk_in_block_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/asterisk_in_block_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/bare_emoji.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/bare_emoji.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/bare_emoji.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/bare_emoji.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/bare_ident_dot.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/bare_ident_dot.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/bare_ident_dot.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/bare_ident_dot.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/bare_ident_sign.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/bare_ident_sign.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/bare_ident_sign.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/bare_ident_sign.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/bare_ident_sign_dot.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/bare_ident_sign_dot.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/bare_ident_sign_dot.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/bare_ident_sign_dot.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/binary.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/binary.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/binary.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/binary.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/binary_trailing_underscore.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/binary_trailing_underscore.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/binary_trailing_underscore.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/binary_trailing_underscore.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/binary_underscore.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/binary_underscore.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/binary_underscore.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/binary_underscore.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/blank_arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/blank_arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/blank_arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/blank_arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/blank_node_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/blank_node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/blank_node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/blank_node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/blank_prop_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/blank_prop_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/blank_prop_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/blank_prop_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/block_comment.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/block_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/block_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/block_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/block_comment_after_node.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/block_comment_after_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/block_comment_after_node.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/block_comment_after_node.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/block_comment_before_node.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/block_comment_before_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/block_comment_before_node.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/block_comment_before_node.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/block_comment_before_node_no_space.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/block_comment_before_node_no_space.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/block_comment_before_node_no_space.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/block_comment_before_node_no_space.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/block_comment_newline.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/block_comment_newline.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/block_comment_newline.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/block_comment_newline.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/bom_initial.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/bom_initial.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/bom_initial.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/bom_initial.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/boolean_arg.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/boolean_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/boolean_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/boolean_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/boolean_prop.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/boolean_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/boolean_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/boolean_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/braces_in_bare_id.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/braces_in_bare_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/braces_in_bare_id.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/braces_in_bare_id.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/chevrons_in_bare_id.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/chevrons_in_bare_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/chevrons_in_bare_id.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/chevrons_in_bare_id.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/comma_in_bare_id.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/comma_in_bare_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/comma_in_bare_id.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/comma_in_bare_id.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/comment_after_arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/comment_after_arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/comment_after_arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/comment_after_arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/comment_after_node_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/comment_after_node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/comment_after_node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/comment_after_node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/comment_after_prop_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/comment_after_prop_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/comment_after_prop_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/comment_after_prop_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/comment_and_newline.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/comment_and_newline.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/comment_and_newline.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/comment_and_newline.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/comment_in_arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/comment_in_arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/comment_in_arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/comment_in_arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/comment_in_node_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/comment_in_node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/comment_in_node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/comment_in_node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/comment_in_prop_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/comment_in_prop_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/comment_in_prop_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/comment_in_prop_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/commented_arg.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/commented_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/commented_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/commented_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/commented_child.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/commented_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/commented_child.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/commented_child.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/commented_line.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/commented_line.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/commented_line.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/commented_line.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/commented_node.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/commented_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/commented_node.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/commented_node.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/commented_prop.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/commented_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/commented_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/commented_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/crlf_between_nodes.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/crlf_between_nodes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/crlf_between_nodes.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/crlf_between_nodes.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/dash_dash.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/dash_dash.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/dash_dash.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/dash_dash.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/emoji.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/emoji.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/emoji.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/emoji.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/empty.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/empty.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/empty.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/empty.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/empty_child.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/empty_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/empty_child.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/empty_child.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/empty_child_different_lines.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/empty_child_different_lines.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/empty_child_different_lines.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/empty_child_different_lines.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/empty_child_same_line.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/empty_child_same_line.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/empty_child_same_line.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/empty_child_same_line.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/empty_child_whitespace.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/empty_child_whitespace.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/empty_child_whitespace.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/empty_child_whitespace.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/empty_line_comment.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/empty_line_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/empty_line_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/empty_line_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/empty_quoted_node_id.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/empty_quoted_node_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/empty_quoted_node_id.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/empty_quoted_node_id.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/empty_quoted_prop_key.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/empty_quoted_prop_key.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/empty_quoted_prop_key.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/empty_quoted_prop_key.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/empty_string_arg.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/empty_string_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/empty_string_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/empty_string_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/eof_after_escape.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/eof_after_escape.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/eof_after_escape.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/eof_after_escape.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/esc_multiple_newlines.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/esc_multiple_newlines.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/esc_multiple_newlines.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/esc_multiple_newlines.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/esc_newline_in_string.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/esc_newline_in_string.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/esc_newline_in_string.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/esc_newline_in_string.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/esc_unicode_in_string.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/esc_unicode_in_string.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/esc_unicode_in_string.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/esc_unicode_in_string.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/escaped_whitespace.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/escaped_whitespace.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/escaped_whitespace.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/escaped_whitespace.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/escline.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/escline.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/escline.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/escline.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/escline_after_semicolon.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_after_semicolon.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/escline_after_semicolon.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_after_semicolon.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/escline_alone.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_alone.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/escline_alone.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_alone.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/escline_empty_line.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_empty_line.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/escline_empty_line.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_empty_line.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/escline_end_of_node.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_end_of_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/escline_end_of_node.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_end_of_node.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/escline_in_child_block.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_in_child_block.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/escline_in_child_block.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_in_child_block.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/escline_line_comment.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_line_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/escline_line_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_line_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/escline_node.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/escline_node.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_node.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/escline_node_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/escline_node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/escline_slashdash.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_slashdash.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/escline_slashdash.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/escline_slashdash.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/false_prefix_in_bare_id.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/false_prefix_in_bare_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/false_prefix_in_bare_id.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/false_prefix_in_bare_id.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/false_prefix_in_prop_key.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/false_prefix_in_prop_key.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/false_prefix_in_prop_key.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/false_prefix_in_prop_key.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/floating_point_keywords.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/floating_point_keywords.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/floating_point_keywords.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/floating_point_keywords.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/hex.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/hex.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/hex.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/hex.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/hex_int.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/hex_int.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/hex_int.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/hex_int.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/hex_int_underscores.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/hex_int_underscores.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/hex_int_underscores.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/hex_int_underscores.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/hex_leading_zero.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/hex_leading_zero.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/hex_leading_zero.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/hex_leading_zero.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/initial_slashdash.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/initial_slashdash.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/initial_slashdash.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/initial_slashdash.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/int_multiple_underscore.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/int_multiple_underscore.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/int_multiple_underscore.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/int_multiple_underscore.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/just_block_comment.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/just_block_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/just_block_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/just_block_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/just_child.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/just_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/just_child.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/just_child.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/just_newline.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/just_newline.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/just_newline.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/just_newline.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/just_node_id.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/just_node_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/just_node_id.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/just_node_id.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/just_space.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/just_space.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/just_space.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/just_space.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/leading_newline.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/leading_newline.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/leading_newline.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/leading_newline.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/leading_zero_binary.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/leading_zero_binary.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/leading_zero_binary.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/leading_zero_binary.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/leading_zero_int.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/leading_zero_int.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/leading_zero_int.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/leading_zero_int.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/leading_zero_oct.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/leading_zero_oct.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/leading_zero_oct.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/leading_zero_oct.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_comment.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_nodes.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_nodes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_nodes.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_nodes.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_raw_string.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_raw_string.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_raw_string.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_raw_string.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_raw_string_containing_quotes.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_raw_string_containing_quotes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_raw_string_containing_quotes.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_raw_string_containing_quotes.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_raw_string_empty.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_raw_string_empty.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_raw_string_empty.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_raw_string_empty.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_raw_string_empty_indented.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_raw_string_empty_indented.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_raw_string_empty_indented.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_raw_string_empty_indented.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_raw_string_indented.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_raw_string_indented.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_raw_string_indented.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_raw_string_indented.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_string.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_string.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_containing_quotes.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_containing_quotes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_containing_quotes.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_containing_quotes.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_double_backslash.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_double_backslash.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_double_backslash.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_double_backslash.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_empty.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_empty.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_empty.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_empty.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_empty_indented.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_empty_indented.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_empty_indented.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_empty_indented.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_escape_delimiter.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_escape_delimiter.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_escape_delimiter.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_escape_delimiter.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_escape_in_closing_line.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_escape_in_closing_line.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_escape_in_closing_line.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_escape_in_closing_line.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_escape_in_closing_line_shallow.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_escape_in_closing_line_shallow.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_escape_in_closing_line_shallow.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_escape_in_closing_line_shallow.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_escape_newline_at_end.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_escape_newline_at_end.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_escape_newline_at_end.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_escape_newline_at_end.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_indented.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_indented.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_indented.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_indented.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_whitespace_only.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_whitespace_only.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_whitespace_only.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_whitespace_only.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_wrapped_binary.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_wrapped_binary.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/multiline_string_wrapped_binary.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/multiline_string_wrapped_binary.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/negative_exponent.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/negative_exponent.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/negative_exponent.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/negative_exponent.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/negative_float.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/negative_float.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/negative_float.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/negative_float.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/negative_int.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/negative_int.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/negative_int.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/negative_int.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/nested_block_comment.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/nested_block_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/nested_block_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/nested_block_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/nested_children.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/nested_children.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/nested_children.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/nested_children.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/nested_comments.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/nested_comments.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/nested_comments.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/nested_comments.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/nested_multiline_block_comment.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/nested_multiline_block_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/nested_multiline_block_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/nested_multiline_block_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/newline_between_nodes.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/newline_between_nodes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/newline_between_nodes.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/newline_between_nodes.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/newlines_in_block_comment.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/newlines_in_block_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/newlines_in_block_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/newlines_in_block_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/no_decimal_exponent.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/no_decimal_exponent.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/no_decimal_exponent.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/no_decimal_exponent.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/node_false.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/node_false.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/node_false.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/node_false.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/node_true.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/node_true.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/node_true.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/node_true.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/node_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/null_arg.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/null_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/null_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/null_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/null_prefix_in_bare_id.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/null_prefix_in_bare_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/null_prefix_in_bare_id.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/null_prefix_in_bare_id.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/null_prefix_in_prop_key.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/null_prefix_in_prop_key.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/null_prefix_in_prop_key.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/null_prefix_in_prop_key.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/null_prop.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/null_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/null_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/null_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/numeric_arg.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/numeric_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/numeric_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/numeric_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/numeric_prop.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/numeric_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/numeric_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/numeric_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/octal.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/octal.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/octal.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/octal.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/only_cr.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/only_cr.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/only_cr.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/only_cr.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/only_line_comment.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/only_line_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/only_line_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/only_line_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/only_line_comment_crlf.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/only_line_comment_crlf.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/only_line_comment_crlf.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/only_line_comment_crlf.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/only_line_comment_newline.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/only_line_comment_newline.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/only_line_comment_newline.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/only_line_comment_newline.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/optional_child_semicolon.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/optional_child_semicolon.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/optional_child_semicolon.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/optional_child_semicolon.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/parse_all_arg_types.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/parse_all_arg_types.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/parse_all_arg_types.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/parse_all_arg_types.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/positive_exponent.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/positive_exponent.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/positive_exponent.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/positive_exponent.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/positive_int.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/positive_int.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/positive_int.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/positive_int.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/preserve_duplicate_nodes.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/preserve_duplicate_nodes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/preserve_duplicate_nodes.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/preserve_duplicate_nodes.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/preserve_node_order.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/preserve_node_order.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/preserve_node_order.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/preserve_node_order.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/prop_false_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_false_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/prop_false_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_false_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/prop_float_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_float_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/prop_float_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_float_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/prop_hex_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_hex_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/prop_hex_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_hex_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/prop_identifier_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_identifier_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/prop_identifier_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_identifier_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/prop_null_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_null_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/prop_null_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_null_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/prop_raw_string_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_raw_string_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/prop_raw_string_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_raw_string_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/prop_string_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_string_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/prop_string_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_string_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/prop_true_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_true_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/prop_true_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_true_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/prop_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/prop_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/prop_zero_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_zero_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/prop_zero_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/prop_zero_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/question_mark_before_number.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/question_mark_before_number.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/question_mark_before_number.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/question_mark_before_number.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/quoted_arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/quoted_arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/quoted_arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/quoted_arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/quoted_node_name.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/quoted_node_name.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/quoted_node_name.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/quoted_node_name.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/quoted_node_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/quoted_node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/quoted_node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/quoted_node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/quoted_numeric.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/quoted_numeric.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/quoted_numeric.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/quoted_numeric.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/quoted_prop_name.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/quoted_prop_name.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/quoted_prop_name.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/quoted_prop_name.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/quoted_prop_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/quoted_prop_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/quoted_prop_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/quoted_prop_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/r_node.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/r_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/r_node.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/r_node.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/raw_arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/raw_arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/raw_node_name.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_node_name.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/raw_node_name.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_node_name.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/raw_node_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/raw_node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/raw_prop_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_prop_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/raw_prop_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_prop_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/raw_string_arg.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_string_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/raw_string_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_string_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/raw_string_backslash.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_string_backslash.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/raw_string_backslash.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_string_backslash.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/raw_string_hash_no_esc.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_string_hash_no_esc.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/raw_string_hash_no_esc.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_string_hash_no_esc.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/raw_string_just_backslash.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_string_just_backslash.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/raw_string_just_backslash.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_string_just_backslash.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/raw_string_multiple_hash.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_string_multiple_hash.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/raw_string_multiple_hash.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_string_multiple_hash.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/raw_string_newline.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_string_newline.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/raw_string_newline.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_string_newline.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/raw_string_prop.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_string_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/raw_string_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_string_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/raw_string_quote.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_string_quote.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/raw_string_quote.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/raw_string_quote.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/repeated_arg.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/repeated_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/repeated_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/repeated_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/repeated_prop.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/repeated_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/repeated_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/repeated_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/same_name_nodes.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/same_name_nodes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/same_name_nodes.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/same_name_nodes.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/sci_notation_large.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/sci_notation_large.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/sci_notation_large.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/sci_notation_large.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/sci_notation_small.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/sci_notation_small.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/sci_notation_small.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/sci_notation_small.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/semicolon_after_child.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/semicolon_after_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/semicolon_after_child.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/semicolon_after_child.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/semicolon_in_child.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/semicolon_in_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/semicolon_in_child.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/semicolon_in_child.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/semicolon_separated.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/semicolon_separated.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/semicolon_separated.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/semicolon_separated.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/semicolon_separated_nodes.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/semicolon_separated_nodes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/semicolon_separated_nodes.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/semicolon_separated_nodes.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/semicolon_terminated.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/semicolon_terminated.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/semicolon_terminated.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/semicolon_terminated.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/single_arg.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/single_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/single_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/single_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/single_prop.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/single_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/single_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/single_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_arg_after_newline_esc.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_arg_after_newline_esc.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_arg_after_newline_esc.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_arg_after_newline_esc.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_arg_before_newline_esc.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_arg_before_newline_esc.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_arg_before_newline_esc.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_arg_before_newline_esc.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_child.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_child.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_child.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_empty_child.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_empty_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_empty_child.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_empty_child.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_escline_before_arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_escline_before_arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_escline_before_arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_escline_before_arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_escline_before_children.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_escline_before_children.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_escline_before_children.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_escline_before_children.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_escline_before_node.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_escline_before_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_escline_before_node.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_escline_before_node.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_false_node.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_false_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_false_node.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_false_node.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_full_node.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_full_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_full_node.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_full_node.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_in_slashdash.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_in_slashdash.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_in_slashdash.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_in_slashdash.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_multi_line_comment_entry.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_multi_line_comment_entry.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_multi_line_comment_entry.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_multi_line_comment_entry.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_multi_line_comment_inline.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_multi_line_comment_inline.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_multi_line_comment_inline.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_multi_line_comment_inline.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_multiple_child_blocks.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_multiple_child_blocks.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_multiple_child_blocks.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_multiple_child_blocks.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_negative_number.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_negative_number.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_negative_number.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_negative_number.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_newline_before_children.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_newline_before_children.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_newline_before_children.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_newline_before_children.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_newline_before_entry.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_newline_before_entry.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_newline_before_entry.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_newline_before_entry.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_newline_before_node.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_newline_before_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_newline_before_node.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_newline_before_node.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_node_in_child.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_node_in_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_node_in_child.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_node_in_child.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_node_with_child.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_node_with_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_node_with_child.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_node_with_child.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_only_node.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_only_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_only_node.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_only_node.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_only_node_with_space.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_only_node_with_space.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_only_node_with_space.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_only_node_with_space.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_prop.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_raw_prop_key.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_raw_prop_key.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_raw_prop_key.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_raw_prop_key.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_repeated_prop.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_repeated_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_repeated_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_repeated_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_single_line_comment_entry.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_single_line_comment_entry.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_single_line_comment_entry.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_single_line_comment_entry.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/slashdash_single_line_comment_node.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_single_line_comment_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/slashdash_single_line_comment_node.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/slashdash_single_line_comment_node.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/space_after_arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/space_after_arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/space_after_arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/space_after_arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/space_after_node_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/space_after_node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/space_after_node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/space_after_node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/space_after_prop_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/space_after_prop_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/space_after_prop_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/space_after_prop_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/space_around_prop_marker.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/space_around_prop_marker.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/space_around_prop_marker.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/space_around_prop_marker.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/space_in_arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/space_in_arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/space_in_arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/space_in_arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/space_in_node_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/space_in_node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/space_in_node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/space_in_node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/space_in_prop_type.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/space_in_prop_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/space_in_prop_type.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/space_in_prop_type.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/string_arg.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/string_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/string_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/string_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/string_escaped_literal_whitespace.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/string_escaped_literal_whitespace.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/string_escaped_literal_whitespace.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/string_escaped_literal_whitespace.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/string_prop.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/string_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/string_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/string_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/tab_space.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/tab_space.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/tab_space.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/tab_space.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/trailing_crlf.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/trailing_crlf.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/trailing_crlf.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/trailing_crlf.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/trailing_underscore_hex.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/trailing_underscore_hex.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/trailing_underscore_hex.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/trailing_underscore_hex.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/trailing_underscore_octal.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/trailing_underscore_octal.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/trailing_underscore_octal.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/trailing_underscore_octal.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/true_prefix_in_bare_id.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/true_prefix_in_bare_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/true_prefix_in_bare_id.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/true_prefix_in_bare_id.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/true_prefix_in_prop_key.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/true_prefix_in_prop_key.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/true_prefix_in_prop_key.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/true_prefix_in_prop_key.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/two_nodes.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/two_nodes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/two_nodes.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/two_nodes.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/underscore_before_number.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/underscore_before_number.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/underscore_before_number.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/underscore_before_number.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/underscore_in_exponent.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/underscore_in_exponent.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/underscore_in_exponent.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/underscore_in_exponent.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/underscore_in_float.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/underscore_in_float.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/underscore_in_float.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/underscore_in_float.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/underscore_in_fraction.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/underscore_in_fraction.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/underscore_in_fraction.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/underscore_in_fraction.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/underscore_in_int.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/underscore_in_int.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/underscore_in_int.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/underscore_in_int.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/underscore_in_octal.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/underscore_in_octal.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/underscore_in_octal.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/underscore_in_octal.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/unicode_silly.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/unicode_silly.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/unicode_silly.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/unicode_silly.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/unusual_bare_id_chars_in_quoted_id.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/unusual_bare_id_chars_in_quoted_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/unusual_bare_id_chars_in_quoted_id.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/unusual_bare_id_chars_in_quoted_id.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/unusual_chars_in_bare_id.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/unusual_chars_in_bare_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/unusual_chars_in_bare_id.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/unusual_chars_in_bare_id.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/vertical_tab_whitespace.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/vertical_tab_whitespace.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/vertical_tab_whitespace.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/vertical_tab_whitespace.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/zero_float.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/zero_float.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/zero_float.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/zero_float.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/zero_int.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/zero_int.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/zero_int.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/zero_int.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/zero_space_before_slashdash_arg.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/zero_space_before_slashdash_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/zero_space_before_slashdash_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/zero_space_before_slashdash_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/zero_space_before_slashdash_children.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/zero_space_before_slashdash_children.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/zero_space_before_slashdash_children.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/zero_space_before_slashdash_children.kdl diff --git a/src/Kuddle.Tests/test_cases/expected_kdl/zero_space_before_slashdash_prop.kdl b/src/Kuddle.Net.Tests/test_cases/expected_kdl/zero_space_before_slashdash_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/expected_kdl/zero_space_before_slashdash_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/expected_kdl/zero_space_before_slashdash_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/input/all_escapes.kdl b/src/Kuddle.Net.Tests/test_cases/input/all_escapes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/all_escapes.kdl rename to src/Kuddle.Net.Tests/test_cases/input/all_escapes.kdl diff --git a/src/Kuddle.Tests/test_cases/input/all_node_fields.kdl b/src/Kuddle.Net.Tests/test_cases/input/all_node_fields.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/all_node_fields.kdl rename to src/Kuddle.Net.Tests/test_cases/input/all_node_fields.kdl diff --git a/src/Kuddle.Tests/test_cases/input/arg_and_prop_same_name.kdl b/src/Kuddle.Net.Tests/test_cases/input/arg_and_prop_same_name.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/arg_and_prop_same_name.kdl rename to src/Kuddle.Net.Tests/test_cases/input/arg_and_prop_same_name.kdl diff --git a/src/Kuddle.Tests/test_cases/input/arg_bare.kdl b/src/Kuddle.Net.Tests/test_cases/input/arg_bare.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/arg_bare.kdl rename to src/Kuddle.Net.Tests/test_cases/input/arg_bare.kdl diff --git a/src/Kuddle.Tests/test_cases/input/arg_false_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/arg_false_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/arg_false_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/arg_false_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/arg_float_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/arg_float_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/arg_float_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/arg_float_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/arg_hex_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/arg_hex_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/arg_hex_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/arg_hex_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/arg_null_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/arg_null_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/arg_null_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/arg_null_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/arg_raw_string_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/arg_raw_string_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/arg_raw_string_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/arg_raw_string_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/arg_string_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/arg_string_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/arg_string_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/arg_string_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/arg_true_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/arg_true_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/arg_true_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/arg_true_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/arg_zero_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/arg_zero_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/arg_zero_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/arg_zero_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/asterisk_in_block_comment.kdl b/src/Kuddle.Net.Tests/test_cases/input/asterisk_in_block_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/asterisk_in_block_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/input/asterisk_in_block_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/input/bare_emoji.kdl b/src/Kuddle.Net.Tests/test_cases/input/bare_emoji.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/bare_emoji.kdl rename to src/Kuddle.Net.Tests/test_cases/input/bare_emoji.kdl diff --git a/src/Kuddle.Tests/test_cases/input/bare_ident_dot.kdl b/src/Kuddle.Net.Tests/test_cases/input/bare_ident_dot.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/bare_ident_dot.kdl rename to src/Kuddle.Net.Tests/test_cases/input/bare_ident_dot.kdl diff --git a/src/Kuddle.Tests/test_cases/input/bare_ident_numeric_dot_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/bare_ident_numeric_dot_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/bare_ident_numeric_dot_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/bare_ident_numeric_dot_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/bare_ident_numeric_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/bare_ident_numeric_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/bare_ident_numeric_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/bare_ident_numeric_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/bare_ident_numeric_sign_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/bare_ident_numeric_sign_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/bare_ident_numeric_sign_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/bare_ident_numeric_sign_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/bare_ident_sign.kdl b/src/Kuddle.Net.Tests/test_cases/input/bare_ident_sign.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/bare_ident_sign.kdl rename to src/Kuddle.Net.Tests/test_cases/input/bare_ident_sign.kdl diff --git a/src/Kuddle.Tests/test_cases/input/bare_ident_sign_dot.kdl b/src/Kuddle.Net.Tests/test_cases/input/bare_ident_sign_dot.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/bare_ident_sign_dot.kdl rename to src/Kuddle.Net.Tests/test_cases/input/bare_ident_sign_dot.kdl diff --git a/src/Kuddle.Tests/test_cases/input/binary.kdl b/src/Kuddle.Net.Tests/test_cases/input/binary.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/binary.kdl rename to src/Kuddle.Net.Tests/test_cases/input/binary.kdl diff --git a/src/Kuddle.Tests/test_cases/input/binary_trailing_underscore.kdl b/src/Kuddle.Net.Tests/test_cases/input/binary_trailing_underscore.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/binary_trailing_underscore.kdl rename to src/Kuddle.Net.Tests/test_cases/input/binary_trailing_underscore.kdl diff --git a/src/Kuddle.Tests/test_cases/input/binary_underscore.kdl b/src/Kuddle.Net.Tests/test_cases/input/binary_underscore.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/binary_underscore.kdl rename to src/Kuddle.Net.Tests/test_cases/input/binary_underscore.kdl diff --git a/src/Kuddle.Tests/test_cases/input/blank_arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/blank_arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/blank_arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/blank_arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/blank_node_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/blank_node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/blank_node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/blank_node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/blank_prop_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/blank_prop_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/blank_prop_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/blank_prop_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/block_comment.kdl b/src/Kuddle.Net.Tests/test_cases/input/block_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/block_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/input/block_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/input/block_comment_after_node.kdl b/src/Kuddle.Net.Tests/test_cases/input/block_comment_after_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/block_comment_after_node.kdl rename to src/Kuddle.Net.Tests/test_cases/input/block_comment_after_node.kdl diff --git a/src/Kuddle.Tests/test_cases/input/block_comment_before_node.kdl b/src/Kuddle.Net.Tests/test_cases/input/block_comment_before_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/block_comment_before_node.kdl rename to src/Kuddle.Net.Tests/test_cases/input/block_comment_before_node.kdl diff --git a/src/Kuddle.Tests/test_cases/input/block_comment_before_node_no_space.kdl b/src/Kuddle.Net.Tests/test_cases/input/block_comment_before_node_no_space.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/block_comment_before_node_no_space.kdl rename to src/Kuddle.Net.Tests/test_cases/input/block_comment_before_node_no_space.kdl diff --git a/src/Kuddle.Tests/test_cases/input/block_comment_newline.kdl b/src/Kuddle.Net.Tests/test_cases/input/block_comment_newline.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/block_comment_newline.kdl rename to src/Kuddle.Net.Tests/test_cases/input/block_comment_newline.kdl diff --git a/src/Kuddle.Tests/test_cases/input/bom_initial.kdl b/src/Kuddle.Net.Tests/test_cases/input/bom_initial.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/bom_initial.kdl rename to src/Kuddle.Net.Tests/test_cases/input/bom_initial.kdl diff --git a/src/Kuddle.Tests/test_cases/input/bom_later_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/bom_later_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/bom_later_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/bom_later_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/boolean_arg.kdl b/src/Kuddle.Net.Tests/test_cases/input/boolean_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/boolean_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/input/boolean_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/input/boolean_prop.kdl b/src/Kuddle.Net.Tests/test_cases/input/boolean_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/boolean_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/input/boolean_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/input/braces_in_bare_id.kdl b/src/Kuddle.Net.Tests/test_cases/input/braces_in_bare_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/braces_in_bare_id.kdl rename to src/Kuddle.Net.Tests/test_cases/input/braces_in_bare_id.kdl diff --git a/src/Kuddle.Tests/test_cases/input/chevrons_in_bare_id.kdl b/src/Kuddle.Net.Tests/test_cases/input/chevrons_in_bare_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/chevrons_in_bare_id.kdl rename to src/Kuddle.Net.Tests/test_cases/input/chevrons_in_bare_id.kdl diff --git a/src/Kuddle.Tests/test_cases/input/comma_in_bare_id.kdl b/src/Kuddle.Net.Tests/test_cases/input/comma_in_bare_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/comma_in_bare_id.kdl rename to src/Kuddle.Net.Tests/test_cases/input/comma_in_bare_id.kdl diff --git a/src/Kuddle.Tests/test_cases/input/comment_after_arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/comment_after_arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/comment_after_arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/comment_after_arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/comment_after_node_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/comment_after_node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/comment_after_node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/comment_after_node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/comment_after_prop_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/comment_after_prop_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/comment_after_prop_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/comment_after_prop_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/comment_and_newline.kdl b/src/Kuddle.Net.Tests/test_cases/input/comment_and_newline.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/comment_and_newline.kdl rename to src/Kuddle.Net.Tests/test_cases/input/comment_and_newline.kdl diff --git a/src/Kuddle.Tests/test_cases/input/comment_in_arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/comment_in_arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/comment_in_arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/comment_in_arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/comment_in_node_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/comment_in_node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/comment_in_node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/comment_in_node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/comment_in_prop_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/comment_in_prop_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/comment_in_prop_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/comment_in_prop_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/commented_arg.kdl b/src/Kuddle.Net.Tests/test_cases/input/commented_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/commented_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/input/commented_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/input/commented_child.kdl b/src/Kuddle.Net.Tests/test_cases/input/commented_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/commented_child.kdl rename to src/Kuddle.Net.Tests/test_cases/input/commented_child.kdl diff --git a/src/Kuddle.Tests/test_cases/input/commented_line.kdl b/src/Kuddle.Net.Tests/test_cases/input/commented_line.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/commented_line.kdl rename to src/Kuddle.Net.Tests/test_cases/input/commented_line.kdl diff --git a/src/Kuddle.Tests/test_cases/input/commented_node.kdl b/src/Kuddle.Net.Tests/test_cases/input/commented_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/commented_node.kdl rename to src/Kuddle.Net.Tests/test_cases/input/commented_node.kdl diff --git a/src/Kuddle.Tests/test_cases/input/commented_prop.kdl b/src/Kuddle.Net.Tests/test_cases/input/commented_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/commented_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/input/commented_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/input/crlf_between_nodes.kdl b/src/Kuddle.Net.Tests/test_cases/input/crlf_between_nodes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/crlf_between_nodes.kdl rename to src/Kuddle.Net.Tests/test_cases/input/crlf_between_nodes.kdl diff --git a/src/Kuddle.Tests/test_cases/input/dash_dash.kdl b/src/Kuddle.Net.Tests/test_cases/input/dash_dash.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/dash_dash.kdl rename to src/Kuddle.Net.Tests/test_cases/input/dash_dash.kdl diff --git a/src/Kuddle.Tests/test_cases/input/dot_but_no_fraction_before_exponent_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/dot_but_no_fraction_before_exponent_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/dot_but_no_fraction_before_exponent_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/dot_but_no_fraction_before_exponent_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/dot_but_no_fraction_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/dot_but_no_fraction_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/dot_but_no_fraction_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/dot_but_no_fraction_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/dot_in_exponent_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/dot_in_exponent_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/dot_in_exponent_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/dot_in_exponent_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/dot_zero_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/dot_zero_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/dot_zero_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/dot_zero_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/emoji.kdl b/src/Kuddle.Net.Tests/test_cases/input/emoji.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/emoji.kdl rename to src/Kuddle.Net.Tests/test_cases/input/emoji.kdl diff --git a/src/Kuddle.Tests/test_cases/input/empty.kdl b/src/Kuddle.Net.Tests/test_cases/input/empty.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/empty.kdl rename to src/Kuddle.Net.Tests/test_cases/input/empty.kdl diff --git a/src/Kuddle.Tests/test_cases/input/empty_arg_type_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/empty_arg_type_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/empty_arg_type_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/empty_arg_type_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/empty_child.kdl b/src/Kuddle.Net.Tests/test_cases/input/empty_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/empty_child.kdl rename to src/Kuddle.Net.Tests/test_cases/input/empty_child.kdl diff --git a/src/Kuddle.Tests/test_cases/input/empty_child_different_lines.kdl b/src/Kuddle.Net.Tests/test_cases/input/empty_child_different_lines.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/empty_child_different_lines.kdl rename to src/Kuddle.Net.Tests/test_cases/input/empty_child_different_lines.kdl diff --git a/src/Kuddle.Tests/test_cases/input/empty_child_same_line.kdl b/src/Kuddle.Net.Tests/test_cases/input/empty_child_same_line.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/empty_child_same_line.kdl rename to src/Kuddle.Net.Tests/test_cases/input/empty_child_same_line.kdl diff --git a/src/Kuddle.Tests/test_cases/input/empty_child_whitespace.kdl b/src/Kuddle.Net.Tests/test_cases/input/empty_child_whitespace.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/empty_child_whitespace.kdl rename to src/Kuddle.Net.Tests/test_cases/input/empty_child_whitespace.kdl diff --git a/src/Kuddle.Tests/test_cases/input/empty_line_comment.kdl b/src/Kuddle.Net.Tests/test_cases/input/empty_line_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/empty_line_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/input/empty_line_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/input/empty_node_type_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/empty_node_type_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/empty_node_type_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/empty_node_type_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/empty_prop_type_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/empty_prop_type_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/empty_prop_type_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/empty_prop_type_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/empty_quoted_node_id.kdl b/src/Kuddle.Net.Tests/test_cases/input/empty_quoted_node_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/empty_quoted_node_id.kdl rename to src/Kuddle.Net.Tests/test_cases/input/empty_quoted_node_id.kdl diff --git a/src/Kuddle.Tests/test_cases/input/empty_quoted_prop_key.kdl b/src/Kuddle.Net.Tests/test_cases/input/empty_quoted_prop_key.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/empty_quoted_prop_key.kdl rename to src/Kuddle.Net.Tests/test_cases/input/empty_quoted_prop_key.kdl diff --git a/src/Kuddle.Tests/test_cases/input/empty_string_arg.kdl b/src/Kuddle.Net.Tests/test_cases/input/empty_string_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/empty_string_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/input/empty_string_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/input/eof_after_escape.kdl b/src/Kuddle.Net.Tests/test_cases/input/eof_after_escape.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/eof_after_escape.kdl rename to src/Kuddle.Net.Tests/test_cases/input/eof_after_escape.kdl diff --git a/src/Kuddle.Tests/test_cases/input/err_backslash_in_bare_id_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/err_backslash_in_bare_id_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/err_backslash_in_bare_id_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/err_backslash_in_bare_id_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/esc_multiple_newlines.kdl b/src/Kuddle.Net.Tests/test_cases/input/esc_multiple_newlines.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/esc_multiple_newlines.kdl rename to src/Kuddle.Net.Tests/test_cases/input/esc_multiple_newlines.kdl diff --git a/src/Kuddle.Tests/test_cases/input/esc_newline_in_string.kdl b/src/Kuddle.Net.Tests/test_cases/input/esc_newline_in_string.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/esc_newline_in_string.kdl rename to src/Kuddle.Net.Tests/test_cases/input/esc_newline_in_string.kdl diff --git a/src/Kuddle.Tests/test_cases/input/esc_unicode_in_string.kdl b/src/Kuddle.Net.Tests/test_cases/input/esc_unicode_in_string.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/esc_unicode_in_string.kdl rename to src/Kuddle.Net.Tests/test_cases/input/esc_unicode_in_string.kdl diff --git a/src/Kuddle.Tests/test_cases/input/escaped_whitespace.kdl b/src/Kuddle.Net.Tests/test_cases/input/escaped_whitespace.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/escaped_whitespace.kdl rename to src/Kuddle.Net.Tests/test_cases/input/escaped_whitespace.kdl diff --git a/src/Kuddle.Tests/test_cases/input/escline.kdl b/src/Kuddle.Net.Tests/test_cases/input/escline.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/escline.kdl rename to src/Kuddle.Net.Tests/test_cases/input/escline.kdl diff --git a/src/Kuddle.Tests/test_cases/input/escline_after_semicolon.kdl b/src/Kuddle.Net.Tests/test_cases/input/escline_after_semicolon.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/escline_after_semicolon.kdl rename to src/Kuddle.Net.Tests/test_cases/input/escline_after_semicolon.kdl diff --git a/src/Kuddle.Tests/test_cases/input/escline_alone.kdl b/src/Kuddle.Net.Tests/test_cases/input/escline_alone.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/escline_alone.kdl rename to src/Kuddle.Net.Tests/test_cases/input/escline_alone.kdl diff --git a/src/Kuddle.Tests/test_cases/input/escline_empty_line.kdl b/src/Kuddle.Net.Tests/test_cases/input/escline_empty_line.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/escline_empty_line.kdl rename to src/Kuddle.Net.Tests/test_cases/input/escline_empty_line.kdl diff --git a/src/Kuddle.Tests/test_cases/input/escline_end_of_node.kdl b/src/Kuddle.Net.Tests/test_cases/input/escline_end_of_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/escline_end_of_node.kdl rename to src/Kuddle.Net.Tests/test_cases/input/escline_end_of_node.kdl diff --git a/src/Kuddle.Tests/test_cases/input/escline_in_child_block.kdl b/src/Kuddle.Net.Tests/test_cases/input/escline_in_child_block.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/escline_in_child_block.kdl rename to src/Kuddle.Net.Tests/test_cases/input/escline_in_child_block.kdl diff --git a/src/Kuddle.Tests/test_cases/input/escline_line_comment.kdl b/src/Kuddle.Net.Tests/test_cases/input/escline_line_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/escline_line_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/input/escline_line_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/input/escline_node.kdl b/src/Kuddle.Net.Tests/test_cases/input/escline_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/escline_node.kdl rename to src/Kuddle.Net.Tests/test_cases/input/escline_node.kdl diff --git a/src/Kuddle.Tests/test_cases/input/escline_node_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/escline_node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/escline_node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/escline_node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/escline_slashdash.kdl b/src/Kuddle.Net.Tests/test_cases/input/escline_slashdash.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/escline_slashdash.kdl rename to src/Kuddle.Net.Tests/test_cases/input/escline_slashdash.kdl diff --git a/src/Kuddle.Tests/test_cases/input/false_prefix_in_bare_id.kdl b/src/Kuddle.Net.Tests/test_cases/input/false_prefix_in_bare_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/false_prefix_in_bare_id.kdl rename to src/Kuddle.Net.Tests/test_cases/input/false_prefix_in_bare_id.kdl diff --git a/src/Kuddle.Tests/test_cases/input/false_prefix_in_prop_key.kdl b/src/Kuddle.Net.Tests/test_cases/input/false_prefix_in_prop_key.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/false_prefix_in_prop_key.kdl rename to src/Kuddle.Net.Tests/test_cases/input/false_prefix_in_prop_key.kdl diff --git a/src/Kuddle.Tests/test_cases/input/false_prop_key_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/false_prop_key_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/false_prop_key_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/false_prop_key_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/floating_point_keyword_identifier_strings_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/floating_point_keyword_identifier_strings_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/floating_point_keyword_identifier_strings_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/floating_point_keyword_identifier_strings_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/floating_point_keywords.kdl b/src/Kuddle.Net.Tests/test_cases/input/floating_point_keywords.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/floating_point_keywords.kdl rename to src/Kuddle.Net.Tests/test_cases/input/floating_point_keywords.kdl diff --git a/src/Kuddle.Tests/test_cases/input/hash_in_id_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/hash_in_id_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/hash_in_id_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/hash_in_id_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/hex.kdl b/src/Kuddle.Net.Tests/test_cases/input/hex.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/hex.kdl rename to src/Kuddle.Net.Tests/test_cases/input/hex.kdl diff --git a/src/Kuddle.Tests/test_cases/input/hex_int.kdl b/src/Kuddle.Net.Tests/test_cases/input/hex_int.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/hex_int.kdl rename to src/Kuddle.Net.Tests/test_cases/input/hex_int.kdl diff --git a/src/Kuddle.Tests/test_cases/input/hex_int_underscores.kdl b/src/Kuddle.Net.Tests/test_cases/input/hex_int_underscores.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/hex_int_underscores.kdl rename to src/Kuddle.Net.Tests/test_cases/input/hex_int_underscores.kdl diff --git a/src/Kuddle.Tests/test_cases/input/hex_leading_zero.kdl b/src/Kuddle.Net.Tests/test_cases/input/hex_leading_zero.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/hex_leading_zero.kdl rename to src/Kuddle.Net.Tests/test_cases/input/hex_leading_zero.kdl diff --git a/src/Kuddle.Tests/test_cases/input/illegal_char_in_binary_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/illegal_char_in_binary_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/illegal_char_in_binary_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/illegal_char_in_binary_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/illegal_char_in_hex_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/illegal_char_in_hex_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/illegal_char_in_hex_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/illegal_char_in_hex_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/illegal_char_in_octal_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/illegal_char_in_octal_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/illegal_char_in_octal_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/illegal_char_in_octal_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/initial_slashdash.kdl b/src/Kuddle.Net.Tests/test_cases/input/initial_slashdash.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/initial_slashdash.kdl rename to src/Kuddle.Net.Tests/test_cases/input/initial_slashdash.kdl diff --git a/src/Kuddle.Tests/test_cases/input/int_multiple_underscore.kdl b/src/Kuddle.Net.Tests/test_cases/input/int_multiple_underscore.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/int_multiple_underscore.kdl rename to src/Kuddle.Net.Tests/test_cases/input/int_multiple_underscore.kdl diff --git a/src/Kuddle.Tests/test_cases/input/just_block_comment.kdl b/src/Kuddle.Net.Tests/test_cases/input/just_block_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/just_block_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/input/just_block_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/input/just_child.kdl b/src/Kuddle.Net.Tests/test_cases/input/just_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/just_child.kdl rename to src/Kuddle.Net.Tests/test_cases/input/just_child.kdl diff --git a/src/Kuddle.Tests/test_cases/input/just_newline.kdl b/src/Kuddle.Net.Tests/test_cases/input/just_newline.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/just_newline.kdl rename to src/Kuddle.Net.Tests/test_cases/input/just_newline.kdl diff --git a/src/Kuddle.Tests/test_cases/input/just_node_id.kdl b/src/Kuddle.Net.Tests/test_cases/input/just_node_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/just_node_id.kdl rename to src/Kuddle.Net.Tests/test_cases/input/just_node_id.kdl diff --git a/src/Kuddle.Tests/test_cases/input/just_space.kdl b/src/Kuddle.Net.Tests/test_cases/input/just_space.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/just_space.kdl rename to src/Kuddle.Net.Tests/test_cases/input/just_space.kdl diff --git a/src/Kuddle.Tests/test_cases/input/just_space_in_arg_type_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/just_space_in_arg_type_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/just_space_in_arg_type_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/just_space_in_arg_type_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/just_space_in_node_type_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/just_space_in_node_type_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/just_space_in_node_type_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/just_space_in_node_type_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/just_space_in_prop_type_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/just_space_in_prop_type_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/just_space_in_prop_type_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/just_space_in_prop_type_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/just_type_no_arg_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/just_type_no_arg_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/just_type_no_arg_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/just_type_no_arg_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/just_type_no_node_id_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/just_type_no_node_id_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/just_type_no_node_id_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/just_type_no_node_id_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/just_type_no_prop_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/just_type_no_prop_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/just_type_no_prop_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/just_type_no_prop_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/leading_newline.kdl b/src/Kuddle.Net.Tests/test_cases/input/leading_newline.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/leading_newline.kdl rename to src/Kuddle.Net.Tests/test_cases/input/leading_newline.kdl diff --git a/src/Kuddle.Tests/test_cases/input/leading_zero_binary.kdl b/src/Kuddle.Net.Tests/test_cases/input/leading_zero_binary.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/leading_zero_binary.kdl rename to src/Kuddle.Net.Tests/test_cases/input/leading_zero_binary.kdl diff --git a/src/Kuddle.Tests/test_cases/input/leading_zero_int.kdl b/src/Kuddle.Net.Tests/test_cases/input/leading_zero_int.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/leading_zero_int.kdl rename to src/Kuddle.Net.Tests/test_cases/input/leading_zero_int.kdl diff --git a/src/Kuddle.Tests/test_cases/input/leading_zero_oct.kdl b/src/Kuddle.Net.Tests/test_cases/input/leading_zero_oct.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/leading_zero_oct.kdl rename to src/Kuddle.Net.Tests/test_cases/input/leading_zero_oct.kdl diff --git a/src/Kuddle.Tests/test_cases/input/legacy_raw_string_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/legacy_raw_string_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/legacy_raw_string_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/legacy_raw_string_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/legacy_raw_string_hash_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/legacy_raw_string_hash_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/legacy_raw_string_hash_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/legacy_raw_string_hash_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_comment.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_nodes.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_nodes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_nodes.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_nodes.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_raw_string.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_raw_string.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_raw_string_containing_quotes.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string_containing_quotes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_raw_string_containing_quotes.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string_containing_quotes.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_raw_string_empty.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string_empty.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_raw_string_empty.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string_empty.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_raw_string_empty_indented.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string_empty_indented.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_raw_string_empty_indented.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string_empty_indented.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_raw_string_indented.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string_indented.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_raw_string_indented.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string_indented.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_raw_string_non_matching_prefix_character_error_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string_non_matching_prefix_character_error_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_raw_string_non_matching_prefix_character_error_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string_non_matching_prefix_character_error_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_raw_string_non_matching_prefix_count_error_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string_non_matching_prefix_count_error_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_raw_string_non_matching_prefix_count_error_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string_non_matching_prefix_count_error_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_raw_string_single_line_err_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string_single_line_err_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_raw_string_single_line_err_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string_single_line_err_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_raw_string_single_quote_err_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string_single_quote_err_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_raw_string_single_quote_err_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_raw_string_single_quote_err_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_containing_quotes.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_containing_quotes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_containing_quotes.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_containing_quotes.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_double_backslash.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_double_backslash.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_double_backslash.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_double_backslash.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_empty.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_empty.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_empty.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_empty.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_empty_indented.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_empty_indented.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_empty_indented.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_empty_indented.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_escape_delimiter.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_escape_delimiter.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_escape_delimiter.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_escape_delimiter.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_escape_in_closing_line.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_escape_in_closing_line.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_escape_in_closing_line.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_escape_in_closing_line.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_escape_in_closing_line_shallow.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_escape_in_closing_line_shallow.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_escape_in_closing_line_shallow.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_escape_in_closing_line_shallow.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_escape_newline_at_end.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_escape_newline_at_end.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_escape_newline_at_end.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_escape_newline_at_end.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_escape_newline_at_end_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_escape_newline_at_end_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_escape_newline_at_end_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_escape_newline_at_end_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_final_whitespace_escape_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_final_whitespace_escape_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_final_whitespace_escape_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_final_whitespace_escape_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_indented.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_indented.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_indented.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_indented.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_non_literal_prefix_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_non_literal_prefix_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_non_literal_prefix_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_non_literal_prefix_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_non_matching_prefix_character_error_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_non_matching_prefix_character_error_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_non_matching_prefix_character_error_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_non_matching_prefix_character_error_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_non_matching_prefix_count_error_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_non_matching_prefix_count_error_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_non_matching_prefix_count_error_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_non_matching_prefix_count_error_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_single_line_err_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_single_line_err_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_single_line_err_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_single_line_err_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_single_quote_err_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_single_quote_err_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_single_quote_err_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_single_quote_err_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_whitespace_only.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_whitespace_only.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_whitespace_only.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_whitespace_only.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiline_string_wrapped_binary.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiline_string_wrapped_binary.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiline_string_wrapped_binary.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiline_string_wrapped_binary.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiple_dots_in_float_before_exponent_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiple_dots_in_float_before_exponent_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiple_dots_in_float_before_exponent_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiple_dots_in_float_before_exponent_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiple_dots_in_float_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiple_dots_in_float_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiple_dots_in_float_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiple_dots_in_float_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiple_es_in_float_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiple_es_in_float_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiple_es_in_float_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiple_es_in_float_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/multiple_x_in_hex_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/multiple_x_in_hex_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/multiple_x_in_hex_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/multiple_x_in_hex_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/negative_exponent.kdl b/src/Kuddle.Net.Tests/test_cases/input/negative_exponent.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/negative_exponent.kdl rename to src/Kuddle.Net.Tests/test_cases/input/negative_exponent.kdl diff --git a/src/Kuddle.Tests/test_cases/input/negative_float.kdl b/src/Kuddle.Net.Tests/test_cases/input/negative_float.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/negative_float.kdl rename to src/Kuddle.Net.Tests/test_cases/input/negative_float.kdl diff --git a/src/Kuddle.Tests/test_cases/input/negative_int.kdl b/src/Kuddle.Net.Tests/test_cases/input/negative_int.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/negative_int.kdl rename to src/Kuddle.Net.Tests/test_cases/input/negative_int.kdl diff --git a/src/Kuddle.Tests/test_cases/input/nested_block_comment.kdl b/src/Kuddle.Net.Tests/test_cases/input/nested_block_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/nested_block_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/input/nested_block_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/input/nested_children.kdl b/src/Kuddle.Net.Tests/test_cases/input/nested_children.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/nested_children.kdl rename to src/Kuddle.Net.Tests/test_cases/input/nested_children.kdl diff --git a/src/Kuddle.Tests/test_cases/input/nested_comments.kdl b/src/Kuddle.Net.Tests/test_cases/input/nested_comments.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/nested_comments.kdl rename to src/Kuddle.Net.Tests/test_cases/input/nested_comments.kdl diff --git a/src/Kuddle.Tests/test_cases/input/nested_multiline_block_comment.kdl b/src/Kuddle.Net.Tests/test_cases/input/nested_multiline_block_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/nested_multiline_block_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/input/nested_multiline_block_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/input/newline_between_nodes.kdl b/src/Kuddle.Net.Tests/test_cases/input/newline_between_nodes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/newline_between_nodes.kdl rename to src/Kuddle.Net.Tests/test_cases/input/newline_between_nodes.kdl diff --git a/src/Kuddle.Tests/test_cases/input/newlines_in_block_comment.kdl b/src/Kuddle.Net.Tests/test_cases/input/newlines_in_block_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/newlines_in_block_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/input/newlines_in_block_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/input/no_decimal_exponent.kdl b/src/Kuddle.Net.Tests/test_cases/input/no_decimal_exponent.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/no_decimal_exponent.kdl rename to src/Kuddle.Net.Tests/test_cases/input/no_decimal_exponent.kdl diff --git a/src/Kuddle.Tests/test_cases/input/no_digits_in_hex_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/no_digits_in_hex_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/no_digits_in_hex_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/no_digits_in_hex_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/no_integer_digit_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/no_integer_digit_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/no_integer_digit_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/no_integer_digit_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/no_solidus_escape_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/no_solidus_escape_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/no_solidus_escape_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/no_solidus_escape_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/node_false.kdl b/src/Kuddle.Net.Tests/test_cases/input/node_false.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/node_false.kdl rename to src/Kuddle.Net.Tests/test_cases/input/node_false.kdl diff --git a/src/Kuddle.Tests/test_cases/input/node_true.kdl b/src/Kuddle.Net.Tests/test_cases/input/node_true.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/node_true.kdl rename to src/Kuddle.Net.Tests/test_cases/input/node_true.kdl diff --git a/src/Kuddle.Tests/test_cases/input/node_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/null_arg.kdl b/src/Kuddle.Net.Tests/test_cases/input/null_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/null_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/input/null_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/input/null_prefix_in_bare_id.kdl b/src/Kuddle.Net.Tests/test_cases/input/null_prefix_in_bare_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/null_prefix_in_bare_id.kdl rename to src/Kuddle.Net.Tests/test_cases/input/null_prefix_in_bare_id.kdl diff --git a/src/Kuddle.Tests/test_cases/input/null_prefix_in_prop_key.kdl b/src/Kuddle.Net.Tests/test_cases/input/null_prefix_in_prop_key.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/null_prefix_in_prop_key.kdl rename to src/Kuddle.Net.Tests/test_cases/input/null_prefix_in_prop_key.kdl diff --git a/src/Kuddle.Tests/test_cases/input/null_prop.kdl b/src/Kuddle.Net.Tests/test_cases/input/null_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/null_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/input/null_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/input/null_prop_key_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/null_prop_key_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/null_prop_key_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/null_prop_key_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/numeric_arg.kdl b/src/Kuddle.Net.Tests/test_cases/input/numeric_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/numeric_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/input/numeric_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/input/numeric_prop.kdl b/src/Kuddle.Net.Tests/test_cases/input/numeric_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/numeric_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/input/numeric_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/input/octal.kdl b/src/Kuddle.Net.Tests/test_cases/input/octal.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/octal.kdl rename to src/Kuddle.Net.Tests/test_cases/input/octal.kdl diff --git a/src/Kuddle.Tests/test_cases/input/only_cr.kdl b/src/Kuddle.Net.Tests/test_cases/input/only_cr.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/only_cr.kdl rename to src/Kuddle.Net.Tests/test_cases/input/only_cr.kdl diff --git a/src/Kuddle.Tests/test_cases/input/only_line_comment.kdl b/src/Kuddle.Net.Tests/test_cases/input/only_line_comment.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/only_line_comment.kdl rename to src/Kuddle.Net.Tests/test_cases/input/only_line_comment.kdl diff --git a/src/Kuddle.Tests/test_cases/input/only_line_comment_crlf.kdl b/src/Kuddle.Net.Tests/test_cases/input/only_line_comment_crlf.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/only_line_comment_crlf.kdl rename to src/Kuddle.Net.Tests/test_cases/input/only_line_comment_crlf.kdl diff --git a/src/Kuddle.Tests/test_cases/input/only_line_comment_newline.kdl b/src/Kuddle.Net.Tests/test_cases/input/only_line_comment_newline.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/only_line_comment_newline.kdl rename to src/Kuddle.Net.Tests/test_cases/input/only_line_comment_newline.kdl diff --git a/src/Kuddle.Tests/test_cases/input/optional_child_semicolon.kdl b/src/Kuddle.Net.Tests/test_cases/input/optional_child_semicolon.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/optional_child_semicolon.kdl rename to src/Kuddle.Net.Tests/test_cases/input/optional_child_semicolon.kdl diff --git a/src/Kuddle.Tests/test_cases/input/parens_in_bare_id_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/parens_in_bare_id_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/parens_in_bare_id_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/parens_in_bare_id_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/parse_all_arg_types.kdl b/src/Kuddle.Net.Tests/test_cases/input/parse_all_arg_types.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/parse_all_arg_types.kdl rename to src/Kuddle.Net.Tests/test_cases/input/parse_all_arg_types.kdl diff --git a/src/Kuddle.Tests/test_cases/input/positive_exponent.kdl b/src/Kuddle.Net.Tests/test_cases/input/positive_exponent.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/positive_exponent.kdl rename to src/Kuddle.Net.Tests/test_cases/input/positive_exponent.kdl diff --git a/src/Kuddle.Tests/test_cases/input/positive_int.kdl b/src/Kuddle.Net.Tests/test_cases/input/positive_int.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/positive_int.kdl rename to src/Kuddle.Net.Tests/test_cases/input/positive_int.kdl diff --git a/src/Kuddle.Tests/test_cases/input/preserve_duplicate_nodes.kdl b/src/Kuddle.Net.Tests/test_cases/input/preserve_duplicate_nodes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/preserve_duplicate_nodes.kdl rename to src/Kuddle.Net.Tests/test_cases/input/preserve_duplicate_nodes.kdl diff --git a/src/Kuddle.Tests/test_cases/input/preserve_node_order.kdl b/src/Kuddle.Net.Tests/test_cases/input/preserve_node_order.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/preserve_node_order.kdl rename to src/Kuddle.Net.Tests/test_cases/input/preserve_node_order.kdl diff --git a/src/Kuddle.Tests/test_cases/input/prop_false_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/prop_false_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/prop_false_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/prop_false_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/prop_float_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/prop_float_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/prop_float_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/prop_float_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/prop_hex_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/prop_hex_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/prop_hex_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/prop_hex_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/prop_identifier_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/prop_identifier_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/prop_identifier_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/prop_identifier_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/prop_null_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/prop_null_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/prop_null_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/prop_null_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/prop_raw_string_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/prop_raw_string_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/prop_raw_string_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/prop_raw_string_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/prop_string_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/prop_string_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/prop_string_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/prop_string_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/prop_true_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/prop_true_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/prop_true_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/prop_true_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/prop_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/prop_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/prop_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/prop_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/prop_zero_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/prop_zero_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/prop_zero_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/prop_zero_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/question_mark_before_number.kdl b/src/Kuddle.Net.Tests/test_cases/input/question_mark_before_number.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/question_mark_before_number.kdl rename to src/Kuddle.Net.Tests/test_cases/input/question_mark_before_number.kdl diff --git a/src/Kuddle.Tests/test_cases/input/quote_in_bare_id_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/quote_in_bare_id_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/quote_in_bare_id_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/quote_in_bare_id_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/quoted_arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/quoted_arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/quoted_arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/quoted_arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/quoted_node_name.kdl b/src/Kuddle.Net.Tests/test_cases/input/quoted_node_name.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/quoted_node_name.kdl rename to src/Kuddle.Net.Tests/test_cases/input/quoted_node_name.kdl diff --git a/src/Kuddle.Tests/test_cases/input/quoted_node_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/quoted_node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/quoted_node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/quoted_node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/quoted_numeric.kdl b/src/Kuddle.Net.Tests/test_cases/input/quoted_numeric.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/quoted_numeric.kdl rename to src/Kuddle.Net.Tests/test_cases/input/quoted_numeric.kdl diff --git a/src/Kuddle.Tests/test_cases/input/quoted_prop_name.kdl b/src/Kuddle.Net.Tests/test_cases/input/quoted_prop_name.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/quoted_prop_name.kdl rename to src/Kuddle.Net.Tests/test_cases/input/quoted_prop_name.kdl diff --git a/src/Kuddle.Tests/test_cases/input/quoted_prop_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/quoted_prop_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/quoted_prop_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/quoted_prop_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/r_node.kdl b/src/Kuddle.Net.Tests/test_cases/input/r_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/r_node.kdl rename to src/Kuddle.Net.Tests/test_cases/input/r_node.kdl diff --git a/src/Kuddle.Tests/test_cases/input/raw_arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/raw_arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/raw_arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/raw_arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/raw_node_name.kdl b/src/Kuddle.Net.Tests/test_cases/input/raw_node_name.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/raw_node_name.kdl rename to src/Kuddle.Net.Tests/test_cases/input/raw_node_name.kdl diff --git a/src/Kuddle.Tests/test_cases/input/raw_node_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/raw_node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/raw_node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/raw_node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/raw_prop_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/raw_prop_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/raw_prop_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/raw_prop_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/raw_string_arg.kdl b/src/Kuddle.Net.Tests/test_cases/input/raw_string_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/raw_string_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/input/raw_string_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/input/raw_string_backslash.kdl b/src/Kuddle.Net.Tests/test_cases/input/raw_string_backslash.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/raw_string_backslash.kdl rename to src/Kuddle.Net.Tests/test_cases/input/raw_string_backslash.kdl diff --git a/src/Kuddle.Tests/test_cases/input/raw_string_hash_no_esc.kdl b/src/Kuddle.Net.Tests/test_cases/input/raw_string_hash_no_esc.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/raw_string_hash_no_esc.kdl rename to src/Kuddle.Net.Tests/test_cases/input/raw_string_hash_no_esc.kdl diff --git a/src/Kuddle.Tests/test_cases/input/raw_string_just_backslash.kdl b/src/Kuddle.Net.Tests/test_cases/input/raw_string_just_backslash.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/raw_string_just_backslash.kdl rename to src/Kuddle.Net.Tests/test_cases/input/raw_string_just_backslash.kdl diff --git a/src/Kuddle.Tests/test_cases/input/raw_string_just_quote_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/raw_string_just_quote_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/raw_string_just_quote_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/raw_string_just_quote_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/raw_string_multiple_hash.kdl b/src/Kuddle.Net.Tests/test_cases/input/raw_string_multiple_hash.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/raw_string_multiple_hash.kdl rename to src/Kuddle.Net.Tests/test_cases/input/raw_string_multiple_hash.kdl diff --git a/src/Kuddle.Tests/test_cases/input/raw_string_newline.kdl b/src/Kuddle.Net.Tests/test_cases/input/raw_string_newline.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/raw_string_newline.kdl rename to src/Kuddle.Net.Tests/test_cases/input/raw_string_newline.kdl diff --git a/src/Kuddle.Tests/test_cases/input/raw_string_prop.kdl b/src/Kuddle.Net.Tests/test_cases/input/raw_string_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/raw_string_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/input/raw_string_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/input/raw_string_quote.kdl b/src/Kuddle.Net.Tests/test_cases/input/raw_string_quote.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/raw_string_quote.kdl rename to src/Kuddle.Net.Tests/test_cases/input/raw_string_quote.kdl diff --git a/src/Kuddle.Tests/test_cases/input/repeated_arg.kdl b/src/Kuddle.Net.Tests/test_cases/input/repeated_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/repeated_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/input/repeated_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/input/repeated_prop.kdl b/src/Kuddle.Net.Tests/test_cases/input/repeated_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/repeated_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/input/repeated_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/input/same_name_nodes.kdl b/src/Kuddle.Net.Tests/test_cases/input/same_name_nodes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/same_name_nodes.kdl rename to src/Kuddle.Net.Tests/test_cases/input/same_name_nodes.kdl diff --git a/src/Kuddle.Tests/test_cases/input/sci_notation_large.kdl b/src/Kuddle.Net.Tests/test_cases/input/sci_notation_large.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/sci_notation_large.kdl rename to src/Kuddle.Net.Tests/test_cases/input/sci_notation_large.kdl diff --git a/src/Kuddle.Tests/test_cases/input/sci_notation_small.kdl b/src/Kuddle.Net.Tests/test_cases/input/sci_notation_small.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/sci_notation_small.kdl rename to src/Kuddle.Net.Tests/test_cases/input/sci_notation_small.kdl diff --git a/src/Kuddle.Tests/test_cases/input/semicolon_after_child.kdl b/src/Kuddle.Net.Tests/test_cases/input/semicolon_after_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/semicolon_after_child.kdl rename to src/Kuddle.Net.Tests/test_cases/input/semicolon_after_child.kdl diff --git a/src/Kuddle.Tests/test_cases/input/semicolon_in_child.kdl b/src/Kuddle.Net.Tests/test_cases/input/semicolon_in_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/semicolon_in_child.kdl rename to src/Kuddle.Net.Tests/test_cases/input/semicolon_in_child.kdl diff --git a/src/Kuddle.Tests/test_cases/input/semicolon_missing_after_children_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/semicolon_missing_after_children_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/semicolon_missing_after_children_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/semicolon_missing_after_children_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/semicolon_separated.kdl b/src/Kuddle.Net.Tests/test_cases/input/semicolon_separated.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/semicolon_separated.kdl rename to src/Kuddle.Net.Tests/test_cases/input/semicolon_separated.kdl diff --git a/src/Kuddle.Tests/test_cases/input/semicolon_separated_nodes.kdl b/src/Kuddle.Net.Tests/test_cases/input/semicolon_separated_nodes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/semicolon_separated_nodes.kdl rename to src/Kuddle.Net.Tests/test_cases/input/semicolon_separated_nodes.kdl diff --git a/src/Kuddle.Tests/test_cases/input/semicolon_terminated.kdl b/src/Kuddle.Net.Tests/test_cases/input/semicolon_terminated.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/semicolon_terminated.kdl rename to src/Kuddle.Net.Tests/test_cases/input/semicolon_terminated.kdl diff --git a/src/Kuddle.Tests/test_cases/input/single_arg.kdl b/src/Kuddle.Net.Tests/test_cases/input/single_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/single_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/input/single_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/input/single_prop.kdl b/src/Kuddle.Net.Tests/test_cases/input/single_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/single_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/input/single_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slash_in_bare_id_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/slash_in_bare_id_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slash_in_bare_id_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slash_in_bare_id_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_after_arg_type_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_after_arg_type_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_after_arg_type_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_after_arg_type_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_after_node_type_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_after_node_type_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_after_node_type_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_after_node_type_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_after_prop_key_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_after_prop_key_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_after_prop_key_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_after_prop_key_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_after_prop_val_type_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_after_prop_val_type_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_after_prop_val_type_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_after_prop_val_type_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_after_type_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_after_type_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_after_type_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_after_type_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_arg_after_newline_esc.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_arg_after_newline_esc.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_arg_after_newline_esc.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_arg_after_newline_esc.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_arg_before_newline_esc.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_arg_before_newline_esc.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_arg_before_newline_esc.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_arg_before_newline_esc.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_before_children_end_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_before_children_end_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_before_children_end_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_before_children_end_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_before_eof_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_before_eof_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_before_eof_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_before_eof_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_before_prop_value_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_before_prop_value_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_before_prop_value_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_before_prop_value_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_before_semicolon_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_before_semicolon_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_before_semicolon_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_before_semicolon_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_between_child_blocks_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_between_child_blocks_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_between_child_blocks_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_between_child_blocks_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_child.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_child.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_child.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_child_block_before_entry_err_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_child_block_before_entry_err_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_child_block_before_entry_err_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_child_block_before_entry_err_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_empty_child.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_empty_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_empty_child.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_empty_child.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_escline_before_arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_escline_before_arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_escline_before_arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_escline_before_arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_escline_before_children.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_escline_before_children.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_escline_before_children.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_escline_before_children.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_escline_before_node.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_escline_before_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_escline_before_node.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_escline_before_node.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_false_node.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_false_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_false_node.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_false_node.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_full_node.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_full_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_full_node.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_full_node.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_in_slashdash.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_in_slashdash.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_in_slashdash.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_in_slashdash.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_inside_arg_type_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_inside_arg_type_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_inside_arg_type_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_inside_arg_type_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_inside_node_type_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_inside_node_type_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_inside_node_type_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_inside_node_type_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_multi_line_comment_entry.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_multi_line_comment_entry.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_multi_line_comment_entry.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_multi_line_comment_entry.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_multi_line_comment_inline.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_multi_line_comment_inline.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_multi_line_comment_inline.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_multi_line_comment_inline.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_multiple_child_blocks.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_multiple_child_blocks.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_multiple_child_blocks.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_multiple_child_blocks.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_negative_number.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_negative_number.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_negative_number.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_negative_number.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_newline_before_children.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_newline_before_children.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_newline_before_children.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_newline_before_children.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_newline_before_entry.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_newline_before_entry.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_newline_before_entry.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_newline_before_entry.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_newline_before_node.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_newline_before_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_newline_before_node.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_newline_before_node.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_node_in_child.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_node_in_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_node_in_child.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_node_in_child.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_node_with_child.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_node_with_child.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_node_with_child.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_node_with_child.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_only_node.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_only_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_only_node.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_only_node.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_only_node_with_space.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_only_node_with_space.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_only_node_with_space.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_only_node_with_space.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_prop.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_raw_prop_key.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_raw_prop_key.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_raw_prop_key.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_raw_prop_key.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_repeated_prop.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_repeated_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_repeated_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_repeated_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_single_line_comment_entry.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_single_line_comment_entry.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_single_line_comment_entry.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_single_line_comment_entry.kdl diff --git a/src/Kuddle.Tests/test_cases/input/slashdash_single_line_comment_node.kdl b/src/Kuddle.Net.Tests/test_cases/input/slashdash_single_line_comment_node.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/slashdash_single_line_comment_node.kdl rename to src/Kuddle.Net.Tests/test_cases/input/slashdash_single_line_comment_node.kdl diff --git a/src/Kuddle.Tests/test_cases/input/space_after_arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/space_after_arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/space_after_arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/space_after_arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/space_after_node_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/space_after_node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/space_after_node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/space_after_node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/space_after_prop_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/space_after_prop_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/space_after_prop_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/space_after_prop_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/space_around_prop_marker.kdl b/src/Kuddle.Net.Tests/test_cases/input/space_around_prop_marker.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/space_around_prop_marker.kdl rename to src/Kuddle.Net.Tests/test_cases/input/space_around_prop_marker.kdl diff --git a/src/Kuddle.Tests/test_cases/input/space_in_arg_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/space_in_arg_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/space_in_arg_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/space_in_arg_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/space_in_node_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/space_in_node_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/space_in_node_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/space_in_node_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/space_in_prop_type.kdl b/src/Kuddle.Net.Tests/test_cases/input/space_in_prop_type.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/space_in_prop_type.kdl rename to src/Kuddle.Net.Tests/test_cases/input/space_in_prop_type.kdl diff --git a/src/Kuddle.Tests/test_cases/input/square_bracket_in_bare_id_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/square_bracket_in_bare_id_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/square_bracket_in_bare_id_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/square_bracket_in_bare_id_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/string_arg.kdl b/src/Kuddle.Net.Tests/test_cases/input/string_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/string_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/input/string_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/input/string_escaped_literal_whitespace.kdl b/src/Kuddle.Net.Tests/test_cases/input/string_escaped_literal_whitespace.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/string_escaped_literal_whitespace.kdl rename to src/Kuddle.Net.Tests/test_cases/input/string_escaped_literal_whitespace.kdl diff --git a/src/Kuddle.Tests/test_cases/input/string_prop.kdl b/src/Kuddle.Net.Tests/test_cases/input/string_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/string_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/input/string_prop.kdl diff --git a/src/Kuddle.Tests/test_cases/input/tab_space.kdl b/src/Kuddle.Net.Tests/test_cases/input/tab_space.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/tab_space.kdl rename to src/Kuddle.Net.Tests/test_cases/input/tab_space.kdl diff --git a/src/Kuddle.Tests/test_cases/input/trailing_crlf.kdl b/src/Kuddle.Net.Tests/test_cases/input/trailing_crlf.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/trailing_crlf.kdl rename to src/Kuddle.Net.Tests/test_cases/input/trailing_crlf.kdl diff --git a/src/Kuddle.Tests/test_cases/input/trailing_underscore_hex.kdl b/src/Kuddle.Net.Tests/test_cases/input/trailing_underscore_hex.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/trailing_underscore_hex.kdl rename to src/Kuddle.Net.Tests/test_cases/input/trailing_underscore_hex.kdl diff --git a/src/Kuddle.Tests/test_cases/input/trailing_underscore_octal.kdl b/src/Kuddle.Net.Tests/test_cases/input/trailing_underscore_octal.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/trailing_underscore_octal.kdl rename to src/Kuddle.Net.Tests/test_cases/input/trailing_underscore_octal.kdl diff --git a/src/Kuddle.Tests/test_cases/input/true_prefix_in_bare_id.kdl b/src/Kuddle.Net.Tests/test_cases/input/true_prefix_in_bare_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/true_prefix_in_bare_id.kdl rename to src/Kuddle.Net.Tests/test_cases/input/true_prefix_in_bare_id.kdl diff --git a/src/Kuddle.Tests/test_cases/input/true_prefix_in_prop_key.kdl b/src/Kuddle.Net.Tests/test_cases/input/true_prefix_in_prop_key.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/true_prefix_in_prop_key.kdl rename to src/Kuddle.Net.Tests/test_cases/input/true_prefix_in_prop_key.kdl diff --git a/src/Kuddle.Tests/test_cases/input/true_prop_key_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/true_prop_key_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/true_prop_key_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/true_prop_key_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/two_nodes.kdl b/src/Kuddle.Net.Tests/test_cases/input/two_nodes.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/two_nodes.kdl rename to src/Kuddle.Net.Tests/test_cases/input/two_nodes.kdl diff --git a/src/Kuddle.Tests/test_cases/input/type_before_prop_key_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/type_before_prop_key_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/type_before_prop_key_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/type_before_prop_key_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unbalanced_raw_hashes_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unbalanced_raw_hashes_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unbalanced_raw_hashes_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unbalanced_raw_hashes_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/underscore_at_start_of_fraction_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/underscore_at_start_of_fraction_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/underscore_at_start_of_fraction_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/underscore_at_start_of_fraction_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/underscore_at_start_of_hex_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/underscore_at_start_of_hex_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/underscore_at_start_of_hex_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/underscore_at_start_of_hex_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/underscore_before_number.kdl b/src/Kuddle.Net.Tests/test_cases/input/underscore_before_number.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/underscore_before_number.kdl rename to src/Kuddle.Net.Tests/test_cases/input/underscore_before_number.kdl diff --git a/src/Kuddle.Tests/test_cases/input/underscore_in_exponent.kdl b/src/Kuddle.Net.Tests/test_cases/input/underscore_in_exponent.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/underscore_in_exponent.kdl rename to src/Kuddle.Net.Tests/test_cases/input/underscore_in_exponent.kdl diff --git a/src/Kuddle.Tests/test_cases/input/underscore_in_float.kdl b/src/Kuddle.Net.Tests/test_cases/input/underscore_in_float.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/underscore_in_float.kdl rename to src/Kuddle.Net.Tests/test_cases/input/underscore_in_float.kdl diff --git a/src/Kuddle.Tests/test_cases/input/underscore_in_fraction.kdl b/src/Kuddle.Net.Tests/test_cases/input/underscore_in_fraction.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/underscore_in_fraction.kdl rename to src/Kuddle.Net.Tests/test_cases/input/underscore_in_fraction.kdl diff --git a/src/Kuddle.Tests/test_cases/input/underscore_in_int.kdl b/src/Kuddle.Net.Tests/test_cases/input/underscore_in_int.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/underscore_in_int.kdl rename to src/Kuddle.Net.Tests/test_cases/input/underscore_in_int.kdl diff --git a/src/Kuddle.Tests/test_cases/input/underscore_in_octal.kdl b/src/Kuddle.Net.Tests/test_cases/input/underscore_in_octal.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/underscore_in_octal.kdl rename to src/Kuddle.Net.Tests/test_cases/input/underscore_in_octal.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_delete_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_delete_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_delete_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_delete_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_escaped_above_max_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_above_max_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_escaped_above_max_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_above_max_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_escaped_h1_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_h1_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_escaped_h1_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_h1_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_escaped_h2_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_h2_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_escaped_h2_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_h2_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_escaped_h3_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_h3_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_escaped_h3_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_h3_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_escaped_h4_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_h4_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_escaped_h4_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_h4_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_escaped_l1_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_l1_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_escaped_l1_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_l1_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_escaped_l2_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_l2_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_escaped_l2_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_l2_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_escaped_l3_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_l3_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_escaped_l3_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_l3_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_escaped_too_long_lead0_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_too_long_lead0_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_escaped_too_long_lead0_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_escaped_too_long_lead0_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_fsi_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_fsi_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_fsi_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_fsi_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_lre_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_lre_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_lre_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_lre_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_lri_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_lri_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_lri_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_lri_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_lrm_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_lrm_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_lrm_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_lrm_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_lro_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_lro_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_lro_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_lro_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_pdf_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_pdf_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_pdf_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_pdf_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_pdi_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_pdi_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_pdi_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_pdi_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_rle_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_rle_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_rle_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_rle_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_rli_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_rli_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_rli_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_rli_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_rlm_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_rlm_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_rlm_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_rlm_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_rlo_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_rlo_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_rlo_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_rlo_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_silly.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_silly.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_silly.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_silly.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unicode_under_0x20_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unicode_under_0x20_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unicode_under_0x20_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unicode_under_0x20_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unterminated_empty_node_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/unterminated_empty_node_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unterminated_empty_node_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unterminated_empty_node_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unusual_bare_id_chars_in_quoted_id.kdl b/src/Kuddle.Net.Tests/test_cases/input/unusual_bare_id_chars_in_quoted_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unusual_bare_id_chars_in_quoted_id.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unusual_bare_id_chars_in_quoted_id.kdl diff --git a/src/Kuddle.Tests/test_cases/input/unusual_chars_in_bare_id.kdl b/src/Kuddle.Net.Tests/test_cases/input/unusual_chars_in_bare_id.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/unusual_chars_in_bare_id.kdl rename to src/Kuddle.Net.Tests/test_cases/input/unusual_chars_in_bare_id.kdl diff --git a/src/Kuddle.Tests/test_cases/input/vertical_tab_whitespace.kdl b/src/Kuddle.Net.Tests/test_cases/input/vertical_tab_whitespace.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/vertical_tab_whitespace.kdl rename to src/Kuddle.Net.Tests/test_cases/input/vertical_tab_whitespace.kdl diff --git a/src/Kuddle.Tests/test_cases/input/zero_float.kdl b/src/Kuddle.Net.Tests/test_cases/input/zero_float.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/zero_float.kdl rename to src/Kuddle.Net.Tests/test_cases/input/zero_float.kdl diff --git a/src/Kuddle.Tests/test_cases/input/zero_int.kdl b/src/Kuddle.Net.Tests/test_cases/input/zero_int.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/zero_int.kdl rename to src/Kuddle.Net.Tests/test_cases/input/zero_int.kdl diff --git a/src/Kuddle.Tests/test_cases/input/zero_space_before_first_arg_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/zero_space_before_first_arg_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/zero_space_before_first_arg_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/zero_space_before_first_arg_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/zero_space_before_prop_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/zero_space_before_prop_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/zero_space_before_prop_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/zero_space_before_prop_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/zero_space_before_second_arg_fail.kdl b/src/Kuddle.Net.Tests/test_cases/input/zero_space_before_second_arg_fail.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/zero_space_before_second_arg_fail.kdl rename to src/Kuddle.Net.Tests/test_cases/input/zero_space_before_second_arg_fail.kdl diff --git a/src/Kuddle.Tests/test_cases/input/zero_space_before_slashdash_arg.kdl b/src/Kuddle.Net.Tests/test_cases/input/zero_space_before_slashdash_arg.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/zero_space_before_slashdash_arg.kdl rename to src/Kuddle.Net.Tests/test_cases/input/zero_space_before_slashdash_arg.kdl diff --git a/src/Kuddle.Tests/test_cases/input/zero_space_before_slashdash_children.kdl b/src/Kuddle.Net.Tests/test_cases/input/zero_space_before_slashdash_children.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/zero_space_before_slashdash_children.kdl rename to src/Kuddle.Net.Tests/test_cases/input/zero_space_before_slashdash_children.kdl diff --git a/src/Kuddle.Tests/test_cases/input/zero_space_before_slashdash_prop.kdl b/src/Kuddle.Net.Tests/test_cases/input/zero_space_before_slashdash_prop.kdl similarity index 100% rename from src/Kuddle.Tests/test_cases/input/zero_space_before_slashdash_prop.kdl rename to src/Kuddle.Net.Tests/test_cases/input/zero_space_before_slashdash_prop.kdl diff --git a/src/Kuddle/AST/KdlArgument.cs b/src/Kuddle.Net/AST/KdlArgument.cs similarity index 100% rename from src/Kuddle/AST/KdlArgument.cs rename to src/Kuddle.Net/AST/KdlArgument.cs diff --git a/src/Kuddle/AST/KdlBlock.cs b/src/Kuddle.Net/AST/KdlBlock.cs similarity index 100% rename from src/Kuddle/AST/KdlBlock.cs rename to src/Kuddle.Net/AST/KdlBlock.cs diff --git a/src/Kuddle/AST/KdlBool.cs b/src/Kuddle.Net/AST/KdlBool.cs similarity index 100% rename from src/Kuddle/AST/KdlBool.cs rename to src/Kuddle.Net/AST/KdlBool.cs diff --git a/src/Kuddle/AST/KdlDocument.cs b/src/Kuddle.Net/AST/KdlDocument.cs similarity index 61% rename from src/Kuddle/AST/KdlDocument.cs rename to src/Kuddle.Net/AST/KdlDocument.cs index 8a11baf..f87ac17 100644 --- a/src/Kuddle/AST/KdlDocument.cs +++ b/src/Kuddle.Net/AST/KdlDocument.cs @@ -7,13 +7,13 @@ public sealed record KdlDocument : KdlObject { public List Nodes { get; init; } = []; - public string ToString(KuddleWriterOptions? options = null) + public string ToString(KdlWriterOptions? options = null) { - return KuddleWriter.Write(this, options); + return KdlWriter.Write(this, options); } public override string ToString() { - return KuddleWriter.Write(this); + return KdlWriter.Write(this); } } diff --git a/src/Kuddle/AST/KdlEntry.cs b/src/Kuddle.Net/AST/KdlEntry.cs similarity index 100% rename from src/Kuddle/AST/KdlEntry.cs rename to src/Kuddle.Net/AST/KdlEntry.cs diff --git a/src/Kuddle/AST/KdlNode.cs b/src/Kuddle.Net/AST/KdlNode.cs similarity index 100% rename from src/Kuddle/AST/KdlNode.cs rename to src/Kuddle.Net/AST/KdlNode.cs diff --git a/src/Kuddle/AST/KdlNull.cs b/src/Kuddle.Net/AST/KdlNull.cs similarity index 100% rename from src/Kuddle/AST/KdlNull.cs rename to src/Kuddle.Net/AST/KdlNull.cs diff --git a/src/Kuddle/AST/KdlNumber.cs b/src/Kuddle.Net/AST/KdlNumber.cs similarity index 100% rename from src/Kuddle/AST/KdlNumber.cs rename to src/Kuddle.Net/AST/KdlNumber.cs diff --git a/src/Kuddle/AST/KdlObject.cs b/src/Kuddle.Net/AST/KdlObject.cs similarity index 100% rename from src/Kuddle/AST/KdlObject.cs rename to src/Kuddle.Net/AST/KdlObject.cs diff --git a/src/Kuddle/AST/KdlProperty.cs b/src/Kuddle.Net/AST/KdlProperty.cs similarity index 100% rename from src/Kuddle/AST/KdlProperty.cs rename to src/Kuddle.Net/AST/KdlProperty.cs diff --git a/src/Kuddle/AST/KdlSkippedEntry.cs b/src/Kuddle.Net/AST/KdlSkippedEntry.cs similarity index 100% rename from src/Kuddle/AST/KdlSkippedEntry.cs rename to src/Kuddle.Net/AST/KdlSkippedEntry.cs diff --git a/src/Kuddle/AST/KdlString.cs b/src/Kuddle.Net/AST/KdlString.cs similarity index 100% rename from src/Kuddle/AST/KdlString.cs rename to src/Kuddle.Net/AST/KdlString.cs diff --git a/src/Kuddle/AST/KdlTrivia.cs b/src/Kuddle.Net/AST/KdlTrivia.cs similarity index 100% rename from src/Kuddle/AST/KdlTrivia.cs rename to src/Kuddle.Net/AST/KdlTrivia.cs diff --git a/src/Kuddle/AST/KdlValue.cs b/src/Kuddle.Net/AST/KdlValue.cs similarity index 100% rename from src/Kuddle/AST/KdlValue.cs rename to src/Kuddle.Net/AST/KdlValue.cs diff --git a/src/Kuddle/AST/NumberBase.cs b/src/Kuddle.Net/AST/NumberBase.cs similarity index 100% rename from src/Kuddle/AST/NumberBase.cs rename to src/Kuddle.Net/AST/NumberBase.cs diff --git a/src/Kuddle/AST/StringKind.cs b/src/Kuddle.Net/AST/StringKind.cs similarity index 100% rename from src/Kuddle/AST/StringKind.cs rename to src/Kuddle.Net/AST/StringKind.cs diff --git a/src/Kuddle/Exceptions/KuddleParseException.cs b/src/Kuddle.Net/Exceptions/KuddleParseException.cs similarity index 100% rename from src/Kuddle/Exceptions/KuddleParseException.cs rename to src/Kuddle.Net/Exceptions/KuddleParseException.cs diff --git a/src/Kuddle/Exceptions/KuddleSerializationException.cs b/src/Kuddle.Net/Exceptions/KuddleSerializationException.cs similarity index 100% rename from src/Kuddle/Exceptions/KuddleSerializationException.cs rename to src/Kuddle.Net/Exceptions/KuddleSerializationException.cs diff --git a/src/Kuddle/Exceptions/KuddleValidationException.cs b/src/Kuddle.Net/Exceptions/KuddleValidationException.cs similarity index 100% rename from src/Kuddle/Exceptions/KuddleValidationException.cs rename to src/Kuddle.Net/Exceptions/KuddleValidationException.cs diff --git a/src/Kuddle/Extensions/KuddleNodeExtensions.cs b/src/Kuddle.Net/Extensions/KdlNodeExtensions.cs similarity index 97% rename from src/Kuddle/Extensions/KuddleNodeExtensions.cs rename to src/Kuddle.Net/Extensions/KdlNodeExtensions.cs index 7c90031..18a3bb3 100644 --- a/src/Kuddle/Extensions/KuddleNodeExtensions.cs +++ b/src/Kuddle.Net/Extensions/KdlNodeExtensions.cs @@ -2,7 +2,7 @@ namespace Kuddle.Extensions; -public static class KuddleNodeExtensions +public static class KdlNodeExtensions { extension(KdlNode node) { diff --git a/src/Kuddle/Extensions/KuddleValueExtensions.cs b/src/Kuddle.Net/Extensions/KdlValueExtensions.cs similarity index 98% rename from src/Kuddle/Extensions/KuddleValueExtensions.cs rename to src/Kuddle.Net/Extensions/KdlValueExtensions.cs index 662051c..73da3a7 100644 --- a/src/Kuddle/Extensions/KuddleValueExtensions.cs +++ b/src/Kuddle.Net/Extensions/KdlValueExtensions.cs @@ -4,7 +4,7 @@ namespace Kuddle.Extensions; -public static class KuddleValueExtensions +public static class KdlValueExtensions { extension(KdlValue value) { diff --git a/src/Kuddle/Extensions/ParserExtensions.cs b/src/Kuddle.Net/Extensions/ParserExtensions.cs similarity index 100% rename from src/Kuddle/Extensions/ParserExtensions.cs rename to src/Kuddle.Net/Extensions/ParserExtensions.cs diff --git a/src/Kuddle/Extensions/SpanExtensions.cs b/src/Kuddle.Net/Extensions/SpanExtensions.cs similarity index 76% rename from src/Kuddle/Extensions/SpanExtensions.cs rename to src/Kuddle.Net/Extensions/SpanExtensions.cs index d1791b7..367fe66 100644 --- a/src/Kuddle/Extensions/SpanExtensions.cs +++ b/src/Kuddle.Net/Extensions/SpanExtensions.cs @@ -6,16 +6,6 @@ public static class SpanExtensions { extension(ReadOnlySpan span) { - public bool Any(Func predicate) - { - foreach (var item in span) - { - if (predicate(item)) - return true; - } - return false; - } - public int MaxConsecutive(T target) { int max = 0; diff --git a/src/Kuddle.Net/Extensions/TypeExtensions.cs b/src/Kuddle.Net/Extensions/TypeExtensions.cs new file mode 100644 index 0000000..5aa58a6 --- /dev/null +++ b/src/Kuddle.Net/Extensions/TypeExtensions.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Kuddle.Serialization; + +internal static class TypeExtensions +{ + extension(Type type) + { + internal bool IsNodeDefinition => + type.GetProperties() + .Any(p => + p.GetCustomAttribute() != null + || p.GetCustomAttribute() != null + ); + internal bool IsComplexType => + !type.IsValueType + && !type.IsPrimitive + && type != typeof(string) + && type != typeof(object) + && !type.IsInterface + && !type.IsAbstract; + + internal bool IsDictionary => + type.IsGenericType + && type.GetInterfaces() + .Any(i => + i.IsGenericType + && ( + i.GetGenericTypeDefinition() == typeof(IDictionary<,>) + || i.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>) + ) + ); + + internal bool IsIEnumerable => + type != typeof(string) + && !type.IsDictionary + && type.IsAssignableTo(typeof(IEnumerable)); + + internal (PropertyInfo, KdlArgumentAttribute)[] GetKdlArgProps() => + type.GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Select(p => new + { + Property = p, + ArgAttr = p.GetCustomAttribute(), + }) + .Where(x => x.ArgAttr is not null) + .OrderBy(x => x.ArgAttr!.Index) + .Select(x => (x.Property, x.ArgAttr!)) + .ToArray(); + + internal (PropertyInfo, KdlPropertyAttribute)[] GetKdlPropProps() => + type.GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Select(p => new + { + Property = p, + PropAttr = p.GetCustomAttribute(), + }) + .Where(x => x.PropAttr is not null) + .Select(x => (x.Property, x.PropAttr!)) + .ToArray(); + + internal (PropertyInfo, KdlNodeAttribute)[] GetKdlChildProps() => + type.GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Select(p => new + { + Property = p, + ChildAttr = p.GetCustomAttribute(), + }) + .Where(x => x.ChildAttr is not null) + .Select(x => (x.Property, x.ChildAttr!)) + .ToArray(); + + public bool IsNullable() => Nullable.GetUnderlyingType(type) != null; + } +} diff --git a/src/Kuddle/GlobalSuppressions.cs b/src/Kuddle.Net/GlobalSuppressions.cs similarity index 100% rename from src/Kuddle/GlobalSuppressions.cs rename to src/Kuddle.Net/GlobalSuppressions.cs diff --git a/src/Kuddle.Net/Kuddle.Net.csproj b/src/Kuddle.Net/Kuddle.Net.csproj new file mode 100644 index 0000000..883fd4c --- /dev/null +++ b/src/Kuddle.Net/Kuddle.Net.csproj @@ -0,0 +1,43 @@ + + + Kuddle + Kuddle.Net + Kuddle.Net + disable + enable + net10.0 + preview + true + + + jamesshenry + jamesshenry + Kuddle.Net is a .NET implementation of a KDL parser/serializer targeting v2 of the spec. KDL is concise, human-readable language built for configuration and data exchange. + kdl;parser;serializer;configuration + LICENSE.md + https://github.com/jamesshenry/Kuddle.Net + https://github.com/jamesshenry/Kuddle.Net + git + readme.md + icon.png + Initial release of Kuddle.Net KDL parser/serializer. + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + diff --git a/src/Kuddle/Parser/CharacterSets.cs b/src/Kuddle.Net/Parser/CharacterSets.cs similarity index 51% rename from src/Kuddle/Parser/CharacterSets.cs rename to src/Kuddle.Net/Parser/CharacterSets.cs index b25fa3e..e5a5f5c 100644 --- a/src/Kuddle/Parser/CharacterSets.cs +++ b/src/Kuddle.Net/Parser/CharacterSets.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace Kuddle.Parser; @@ -71,18 +72,72 @@ internal static bool IsNewline(char c) }; } - // internal static bool IsDisallowedLiteralCodePoint(char c) - // { - // return c switch - // { - // >= '\u0000' and <= '\u0008' => true, // various control characters - // '\u007F' => true, - // >= '\uD800' and <= '\uDFFF' => true, // unicode scalar value - // (>= '\u200E' and <= '\u200F') - // or (>= '\u202A' and <= '\u202E') - // or (>= '\u2066' and <= '\u2069') => true, // unicode 'direction control' characters - // '\uFEFF' => true, // unicode BOM - // _ => false, - // }; - // } + public static readonly HashSet ReservedTypes = + [ + "i8", + "i16", + "i32", + "i64", + "u8", + "u16", + "u32", + "u64", + "f32", + "f64", + "decimal64", + "decimal128", + "date-time", + "time", + "date", + "duration", + "decimal", + "currency", + "country-2", + "country-3", + "ipv4", + "ipv6", + "url", + "uuid", + "regex", + "base64", + ]; + + /// + /// Maps KDL type annotations to their corresponding CLR types. + /// Not all reserved types have CLR equivalents (e.g., country codes, IP addresses as validated strings). + /// + public static readonly Dictionary TypeAnnotationToClrType = new() + { + ["i8"] = typeof(sbyte), + ["i16"] = typeof(short), + ["i32"] = typeof(int), + ["i64"] = typeof(long), + ["u8"] = typeof(byte), + ["u16"] = typeof(ushort), + ["u32"] = typeof(uint), + ["u64"] = typeof(ulong), + ["f32"] = typeof(float), + ["f64"] = typeof(double), + ["decimal64"] = typeof(decimal), + ["decimal128"] = typeof(decimal), + ["decimal"] = typeof(decimal), + ["date-time"] = typeof(DateTimeOffset), + ["time"] = typeof(TimeOnly), + ["date"] = typeof(DateOnly), + ["duration"] = typeof(TimeSpan), + ["uuid"] = typeof(Guid), + ["url"] = typeof(Uri), + ["base64"] = typeof(byte[]), + // These remain as strings with semantic meaning: + // "currency", "country-2", "country-3", "ipv4", "ipv6", "regex" + }; + + /// + /// Gets the CLR type for a KDL type annotation, or null if no mapping exists. + /// + public static Type? GetClrType(string? typeAnnotation) => + typeAnnotation is not null + && TypeAnnotationToClrType.TryGetValue(typeAnnotation, out var type) + ? type + : null; } diff --git a/src/Kuddle/Parser/DebugParser.cs b/src/Kuddle.Net/Parser/DebugParser.cs similarity index 100% rename from src/Kuddle/Parser/DebugParser.cs rename to src/Kuddle.Net/Parser/DebugParser.cs diff --git a/src/Kuddle/Parser/KuddleGrammar.cs b/src/Kuddle.Net/Parser/KdlGrammar.cs similarity index 95% rename from src/Kuddle/Parser/KuddleGrammar.cs rename to src/Kuddle.Net/Parser/KdlGrammar.cs index 27fd64f..cc260f7 100644 --- a/src/Kuddle/Parser/KuddleGrammar.cs +++ b/src/Kuddle.Net/Parser/KdlGrammar.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Text.RegularExpressions; using Kuddle.AST; using Kuddle.Extensions; @@ -11,7 +10,7 @@ namespace Kuddle.Parser; -public static class KuddleGrammar +public static class KdlGrammar { internal static readonly Parser Document; @@ -68,7 +67,7 @@ public static class KuddleGrammar IReadOnlyList >(); - static KuddleGrammar() + static KdlGrammar() { var nodeSpace = Deferred(); @@ -137,14 +136,13 @@ static KuddleGrammar() ) .Then(s => new TextSpan(s)) ); - // 1. Define a parser for Surrogate Pairs (valid emojis/symbols outside BMP) + var surrogatePair = Capture( Literals .Pattern(char.IsHighSurrogate, 1, 1) .And(Literals.Pattern(char.IsLowSurrogate, 1, 1)) ); - // 2. Define the standard character parser (checks for disallowed chars like lone surrogates) var singleChar = Literals.Pattern( c => c != '\\' && c != '"' && !IsDisallowedLiteralCodePoint(c), 1, @@ -158,20 +156,16 @@ static KuddleGrammar() var singleLineStringBody = ZeroOrMany(StringCharacter) .Then(x => { - // Fast path: no characters means empty string if (x.Count == 0) return new TextSpan(string.Empty); - // Fast path: single span can be returned directly if (x.Count == 1) return new TextSpan(x[0].Span.ToString()); - // Calculate total length to avoid StringBuilder resizing int totalLength = 0; for (int i = 0; i < x.Count; i++) totalLength += x[i].Length; - // Use string.Create for efficient concatenation return new TextSpan( string.Create( totalLength, @@ -266,7 +260,6 @@ static KuddleGrammar() .Then( (context, span) => { - // Use span-based comparison to avoid allocation for valid identifiers return IsReservedKeyword(span.Span) ? throw new ParseException( $"The keyword '{span}' cannot be used as an unquoted identifier. Wrap it in quotes: \"{span}\".", @@ -314,9 +307,7 @@ static KuddleGrammar() .AndSkip(Literals.Text("0b")) .And(Literals.Char('0').Or(Literals.Char('1'))) .And(ZeroOrMany(Literals.Pattern(c => c == '_' || IsBinaryChar(c)))) - ) - // .When((context, x) => x.Span[^1] != '_') - ; + ); Boolean = Literals .Text("#true") .Or(Literals.Text("#false")) @@ -338,7 +329,6 @@ static KuddleGrammar() Number = OneOf(KeywordNumber, Hex, Octal, Binary, Decimal) .Then((context, value) => new KdlNumber(value.Span.ToString())); - // Comments var multiLineComment = Deferred(); var openComment = Literals.Text("/*"); @@ -359,7 +349,6 @@ static KuddleGrammar() var lineSpace = Deferred(); SlashDash = Capture(Literals.Text("/-").And(ZeroOrMany(lineSpace))).Debug("SlashDash"); - // Whitespace Ws = Literals .Pattern(c => CharacterSets.IsWhiteSpace(c), minSize: 1, maxSize: 1) .Or(MultiLineComment) @@ -380,8 +369,6 @@ static KuddleGrammar() lineSpace.Parser = NodeSpace.Or(singleNewLine).Or(SingleLineComment); LineSpace = lineSpace; - // Entries - Type = Between( Literals.Char('('), ZeroOrMany(NodeSpace).SkipAnd(String).AndSkip(ZeroOrMany(NodeSpace)), @@ -469,7 +456,7 @@ static KuddleGrammar() { if (result.Item1.HasValue) return null; - // Filter skipped entries - use single pass allocation + var entries = result.Item4; var hasSkipped = false; for (int i = 0; i < entries.Count; i++) @@ -492,7 +479,6 @@ static KuddleGrammar() } else { - // No skipped entries - just copy to list filteredEntries = new List(entries.Count); for (int i = 0; i < entries.Count; i++) filteredEntries.Add(entries[i]); @@ -527,14 +513,12 @@ x.Item1 is null .AndSkip(ZeroOrMany(LineSpace)) .Then(list => { - // Single-pass filter to avoid Where().Select().ToList() triple allocation List? filtered = null; for (int i = 0; i < list.Count; i++) { var node = list[i]; if (node is null) { - // First null found - need to build filtered list if (filtered is null) { filtered = new List(list.Count); diff --git a/src/Kuddle/Parser/MultiLineStringParser.cs b/src/Kuddle.Net/Parser/MultiLineStringParser.cs similarity index 94% rename from src/Kuddle/Parser/MultiLineStringParser.cs rename to src/Kuddle.Net/Parser/MultiLineStringParser.cs index 962723c..8968089 100644 --- a/src/Kuddle/Parser/MultiLineStringParser.cs +++ b/src/Kuddle.Net/Parser/MultiLineStringParser.cs @@ -57,7 +57,6 @@ public override bool Parse(ParseContext context, ref ParseResult resu int matchIndex = searchOffset + relativeIndex; - // Count preceding backslashes int backslashCount = 0; int backScan = matchIndex - 1; @@ -90,15 +89,12 @@ private static KdlString ProcessMultiLineString( ParseContext context ) { - // Fast path: empty content if (rawInput.IsEmpty) return new KdlString(string.Empty, StringKind.MultiLine); - // Check what processing we need bool hasCR = rawInput.Contains('\r'); bool hasBackslash = rawInput.Contains('\\'); - // If no special characters, we can take a faster path ReadOnlySpan normalized; string? workingString = null; @@ -112,14 +108,12 @@ ParseContext context normalized = rawInput; } - // Handle whitespace escapes if present if (hasBackslash) { workingString = ResolveWsEscapes(workingString ?? normalized.ToString()); normalized = workingString.AsSpan(); } - // Find the last newline to extract the prefix (indentation) int lastNewLine = normalized.LastIndexOf('\n'); ReadOnlySpan prefix; @@ -136,7 +130,6 @@ ParseContext context contentBody = ReadOnlySpan.Empty; } - // Validate prefix contains only whitespace foreach (char c in prefix) { if (!CharacterSets.IsWhiteSpace(c)) @@ -148,10 +141,8 @@ ParseContext context } } - // Build dedented string string dedented = BuildDedentedString(contentBody, prefix, context); - // Unescape if needed string finalValue = hasBackslash ? UnescapeStandardKdl(dedented) : dedented; return new KdlString(finalValue, StringKind.MultiLine); @@ -159,7 +150,6 @@ ParseContext context private static string NormalizeNewlines(ReadOnlySpan input) { - // Count output size first int outputLength = 0; for (int i = 0; i < input.Length; i++) { @@ -207,12 +197,10 @@ ParseContext context if (contentBody.IsEmpty) return string.Empty; - // Skip leading newline int startPos = 0; if (contentBody[0] == '\n') startPos = 1; - // Fast path: no prefix means no dedentation needed if (prefix.IsEmpty) { var body = contentBody.Slice(startPos); @@ -221,7 +209,6 @@ ParseContext context return body.ToString(); } - // First pass: calculate output length and validate int outputLength = 0; int pos = startPos; @@ -256,14 +243,12 @@ ParseContext context pos = nextNewLine + 1; } - // Remove trailing newline if (outputLength > 0) outputLength--; if (outputLength <= 0) return string.Empty; - // Second pass: build string string prefixStr = prefix.ToString(); string contentStr = contentBody.ToString(); @@ -326,12 +311,10 @@ private static bool IsWhitespaceOnlyLine(ReadOnlySpan line) private static string ResolveWsEscapes(string input) { - // Fast path: no backslashes int backslashIdx = input.IndexOf('\\'); if (backslashIdx == -1) return input; - // Check if any backslash is followed by whitespace/newline (ws-escape pattern) bool hasWsEscape = false; for (int i = backslashIdx; i < input.Length - 1; i++) { @@ -349,8 +332,6 @@ private static string ResolveWsEscapes(string input) if (!hasWsEscape) return input; - // Need to process ws-escapes - // First pass: calculate output length int outputLength = 0; for (int i = 0; i < input.Length; i++) { @@ -358,15 +339,13 @@ private static string ResolveWsEscapes(string input) { int scanIdx = i + 1; - // Skip whitespace while (scanIdx < input.Length && (input[scanIdx] == ' ' || input[scanIdx] == '\t')) scanIdx++; - // Check for newline if (scanIdx < input.Length && input[scanIdx] == '\n') { scanIdx++; - // Skip whitespace after newline + while ( scanIdx < input.Length && (input[scanIdx] == ' ' || input[scanIdx] == '\t') ) @@ -377,7 +356,6 @@ private static string ResolveWsEscapes(string input) } else if (scanIdx >= input.Length && scanIdx > i + 1) { - // Trailing ws-escape i = scanIdx - 1; continue; } @@ -428,12 +406,10 @@ private static string ResolveWsEscapes(string input) private static string UnescapeStandardKdl(string input) { - // Fast path: no backslashes int backslashIdx = input.IndexOf('\\'); if (backslashIdx == -1) return input; - // First pass: calculate output length int outputLength = 0; for (int i = 0; i < input.Length; i++) { diff --git a/src/Kuddle/Parser/RawStringParser.cs b/src/Kuddle.Net/Parser/RawStringParser.cs similarity index 90% rename from src/Kuddle/Parser/RawStringParser.cs rename to src/Kuddle.Net/Parser/RawStringParser.cs index 961b039..3860a54 100644 --- a/src/Kuddle/Parser/RawStringParser.cs +++ b/src/Kuddle.Net/Parser/RawStringParser.cs @@ -18,7 +18,6 @@ public override bool Parse(ParseContext context, ref ParseResult resu var startPos = cursor.Position; int currentOffset = startPos.Offset; - // Count opening hashes int hashCount = 0; while (currentOffset < bufferSpan.Length && bufferSpan[currentOffset] == '#') { @@ -26,7 +25,6 @@ public override bool Parse(ParseContext context, ref ParseResult resu currentOffset++; } - // Count opening quotes int quoteCount = 0; while (currentOffset < bufferSpan.Length && bufferSpan[currentOffset] == '"') { @@ -48,7 +46,6 @@ public override bool Parse(ParseContext context, ref ParseResult resu bool isMultiline = quoteCount == 3; - // Build closing delimiter: quotes followed by hashes int needleLength = quoteCount + hashCount; Span needle = needleLength <= 256 ? stackalloc char[needleLength] : new char[needleLength]; @@ -70,7 +67,6 @@ public override bool Parse(ParseContext context, ref ParseResult resu var contentSpan = remainingBuffer.Slice(0, matchIndex); int totalLengthParsed = (currentOffset - startPos.Offset) + matchIndex + needleLength; - // Advance cursor for (int i = 0; i < totalLengthParsed; i++) cursor.Advance(); @@ -84,7 +80,6 @@ public override bool Parse(ParseContext context, ref ParseResult resu } else { - // Raw single-line strings have no escape processing content = contentSpan.ToString(); style = StringKind.Quoted | StringKind.Raw; } @@ -99,11 +94,9 @@ public override bool Parse(ParseContext context, ref ParseResult resu /// public static string ProcessMultiLineRawString(ReadOnlySpan rawInput) { - // Fast path: empty content if (rawInput.IsEmpty) return string.Empty; - // Check if we need newline normalization bool hasCR = rawInput.Contains('\r'); ReadOnlySpan normalized; @@ -111,7 +104,6 @@ public static string ProcessMultiLineRawString(ReadOnlySpan rawInput) if (hasCR) { - // Need to normalize - allocate once normalizedString = NormalizeNewlines(rawInput); normalized = normalizedString.AsSpan(); } @@ -120,7 +112,6 @@ public static string ProcessMultiLineRawString(ReadOnlySpan rawInput) normalized = rawInput; } - // Find the last newline to extract the prefix (indentation) int lastNewLine = normalized.LastIndexOf('\n'); ReadOnlySpan prefix; @@ -137,7 +128,6 @@ public static string ProcessMultiLineRawString(ReadOnlySpan rawInput) contentBody = ReadOnlySpan.Empty; } - // Validate prefix contains only whitespace foreach (char c in prefix) { if (c != ' ' && c != '\t') @@ -149,32 +139,27 @@ public static string ProcessMultiLineRawString(ReadOnlySpan rawInput) } } - // If no content body, return empty if (contentBody.IsEmpty) return string.Empty; - // Skip leading newline int pos = 0; if (contentBody[0] == '\n') pos = 1; - // Fast path: no prefix means no dedentation needed if (prefix.IsEmpty) { var body = contentBody.Slice(pos); - // Remove trailing newline if present + if (body.Length > 0 && body[^1] == '\n') body = body.Slice(0, body.Length - 1); return body.ToString(); } - // Need to process with dedentation return BuildDedentedString(contentBody, pos, prefix); } private static string NormalizeNewlines(ReadOnlySpan input) { - // Count output size to avoid StringBuilder resizing int outputLength = 0; for (int i = 0; i < input.Length; i++) { @@ -219,7 +204,6 @@ private static string BuildDedentedString( ReadOnlySpan prefix ) { - // First pass: calculate output length and validate int outputLength = 0; int pos = startPos; @@ -254,14 +238,12 @@ ReadOnlySpan prefix pos = nextNewLine + 1; } - // Remove trailing newline if (outputLength > 0) outputLength--; if (outputLength <= 0) return string.Empty; - // Second pass: build the string string prefixStr = prefix.ToString(); string contentStr = contentBody.ToString(); @@ -314,7 +296,6 @@ ReadOnlySpan prefix private static bool IsWhitespaceOnlyLine(ReadOnlySpan line) { - // Check all chars except the trailing newline for (int i = 0; i < line.Length - 1; i++) { if (!char.IsWhiteSpace(line[i])) diff --git a/src/Kuddle/Serialization/Attributes/KdlArgumentAttribute.cs b/src/Kuddle.Net/Serialization/Attributes/KdlArgumentAttribute.cs similarity index 59% rename from src/Kuddle/Serialization/Attributes/KdlArgumentAttribute.cs rename to src/Kuddle.Net/Serialization/Attributes/KdlArgumentAttribute.cs index ceda7b6..988719a 100644 --- a/src/Kuddle/Serialization/Attributes/KdlArgumentAttribute.cs +++ b/src/Kuddle.Net/Serialization/Attributes/KdlArgumentAttribute.cs @@ -3,7 +3,8 @@ namespace Kuddle.Serialization; [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] -public sealed class KdlArgumentAttribute(int index) : Attribute +public sealed class KdlArgumentAttribute(int index, string? typeAnnotation = null) + : KdlEntryAttribute(typeAnnotation) { public int Index { get; } = index; } diff --git a/src/Kuddle.Net/Serialization/Attributes/KdlEntryAttribute.cs b/src/Kuddle.Net/Serialization/Attributes/KdlEntryAttribute.cs new file mode 100644 index 0000000..27f0dba --- /dev/null +++ b/src/Kuddle.Net/Serialization/Attributes/KdlEntryAttribute.cs @@ -0,0 +1,20 @@ +using System; + +namespace Kuddle.Serialization; + +/// +/// Base class for KDL entry attributes. Only one entry attribute can be applied per property. +/// +[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] +public abstract class KdlEntryAttribute : Attribute +{ + /// + /// Optional KDL type annotation (e.g., "uuid", "date-time", "i32"). + /// + public string? TypeAnnotation { get; } + + protected KdlEntryAttribute(string? typeAnnotation = null) + { + TypeAnnotation = typeAnnotation; + } +} diff --git a/src/Kuddle/Serialization/Attributes/KdlIgnoreAttribute.cs b/src/Kuddle.Net/Serialization/Attributes/KdlIgnoreAttribute.cs similarity index 100% rename from src/Kuddle/Serialization/Attributes/KdlIgnoreAttribute.cs rename to src/Kuddle.Net/Serialization/Attributes/KdlIgnoreAttribute.cs diff --git a/src/Kuddle/Serialization/Attributes/KdlNodeAttribute.cs b/src/Kuddle.Net/Serialization/Attributes/KdlNodeAttribute.cs similarity index 58% rename from src/Kuddle/Serialization/Attributes/KdlNodeAttribute.cs rename to src/Kuddle.Net/Serialization/Attributes/KdlNodeAttribute.cs index 1ac32ef..23a03cc 100644 --- a/src/Kuddle/Serialization/Attributes/KdlNodeAttribute.cs +++ b/src/Kuddle.Net/Serialization/Attributes/KdlNodeAttribute.cs @@ -3,7 +3,8 @@ namespace Kuddle.Serialization; [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] -public sealed class KdlNodeAttribute(string? name = null) : Attribute +public sealed class KdlNodeAttribute(string? name = null, string? typeAnnotation = null) + : KdlEntryAttribute(typeAnnotation) { public string? Name { get; } = name; } diff --git a/src/Kuddle/Serialization/Attributes/KdlPropertyAttribute.cs b/src/Kuddle.Net/Serialization/Attributes/KdlPropertyAttribute.cs similarity index 57% rename from src/Kuddle/Serialization/Attributes/KdlPropertyAttribute.cs rename to src/Kuddle.Net/Serialization/Attributes/KdlPropertyAttribute.cs index 202d148..916208e 100644 --- a/src/Kuddle/Serialization/Attributes/KdlPropertyAttribute.cs +++ b/src/Kuddle.Net/Serialization/Attributes/KdlPropertyAttribute.cs @@ -3,7 +3,8 @@ namespace Kuddle.Serialization; [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] -public sealed class KdlPropertyAttribute(string? key = null) : Attribute +public sealed class KdlPropertyAttribute(string? key = null, string? typeAnnotation = null) + : KdlEntryAttribute(typeAnnotation) { public string? Key { get; } = key; } diff --git a/src/Kuddle.Net/Serialization/Attributes/KdlTypeAttribute.cs b/src/Kuddle.Net/Serialization/Attributes/KdlTypeAttribute.cs new file mode 100644 index 0000000..535030c --- /dev/null +++ b/src/Kuddle.Net/Serialization/Attributes/KdlTypeAttribute.cs @@ -0,0 +1,13 @@ +using System; + +namespace Kuddle.Serialization; + +[AttributeUsage( + AttributeTargets.Class | AttributeTargets.Property, + AllowMultiple = false, + Inherited = false +)] +public sealed class KdlTypeAttribute(string name) : Attribute +{ + public string Name { get; set; } = name; +} diff --git a/src/Kuddle/Serialization/KuddleReader.cs b/src/Kuddle.Net/Serialization/KdlReader.cs similarity index 50% rename from src/Kuddle/Serialization/KuddleReader.cs rename to src/Kuddle.Net/Serialization/KdlReader.cs index fdbb10e..a221251 100644 --- a/src/Kuddle/Serialization/KuddleReader.cs +++ b/src/Kuddle.Net/Serialization/KdlReader.cs @@ -1,17 +1,15 @@ using System; -using System.IO; -using System.Threading.Tasks; using Kuddle.AST; using Kuddle.Exceptions; using Kuddle.Parser; using Kuddle.Validation; using Parlot.Fluent; -namespace Kuddle; +namespace Kuddle.Serialization; -public static class KuddleReader +public static class KdlReader { - private static readonly Parser _parser = KuddleGrammar.Document.Compile(); + private static readonly Parser _parser = KdlGrammar.Document.Compile(); /// /// Parses a KDL string into a KdlDocument AST. @@ -20,10 +18,10 @@ public static class KuddleReader /// /// /// - public static KdlDocument Parse(string text, KuddleReaderOptions? options = null) + public static KdlDocument Read(string text, KdlReaderOptions? options = null) { ArgumentNullException.ThrowIfNull(text); - options ??= KuddleReaderOptions.Default; + options ??= KdlReaderOptions.Default; if (!_parser.TryParse(text, out var doc, out var error)) { @@ -41,30 +39,9 @@ public static KdlDocument Parse(string text, KuddleReaderOptions? options = null if (options.ValidateReservedTypes) { - KuddleReservedTypeValidator.Validate(doc); + KdlReservedTypeValidator.Validate(doc); } return doc; } - - // /// - // /// Reads a stream assuming UTF-8 encoding. - // /// - // public static async Task ReadAsync( - // Stream stream, - // KuddleReaderOptions? options = null - // ) - // { - // using var reader = new StreamReader(stream); - // var text = await reader.ReadToEndAsync(); - // return await ReadASync(text, options); - // } - - public static async Task ReadAsync( - string text, - KuddleReaderOptions? options = null - ) - { - return Parse(text, options); - } } diff --git a/src/Kuddle.Net/Serialization/KdlReaderOptions.cs b/src/Kuddle.Net/Serialization/KdlReaderOptions.cs new file mode 100644 index 0000000..b0841d5 --- /dev/null +++ b/src/Kuddle.Net/Serialization/KdlReaderOptions.cs @@ -0,0 +1,7 @@ +namespace Kuddle.Serialization; + +public record KdlReaderOptions +{ + public static KdlReaderOptions Default => new() { ValidateReservedTypes = true }; + public bool ValidateReservedTypes { get; init; } = true; +} diff --git a/src/Kuddle.Net/Serialization/KdlSerializer.cs b/src/Kuddle.Net/Serialization/KdlSerializer.cs new file mode 100644 index 0000000..388eab8 --- /dev/null +++ b/src/Kuddle.Net/Serialization/KdlSerializer.cs @@ -0,0 +1,533 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using Kuddle.AST; +using Kuddle.Extensions; + +namespace Kuddle.Serialization; + +/// +/// Serializes and deserializes C# objects to/from KDL format. +/// +public static class KdlSerializer +{ + private const StringComparison NodeNameComparison = StringComparison.OrdinalIgnoreCase; + + #region Deserialization + + /// + /// Deserializes a KDL document containing multiple nodes of type T. + /// + public static IEnumerable DeserializeMany( + string text, + KdlSerializerOptions? options = null, + CancellationToken cancellationToken = default + ) + where T : new() + { + var doc = KdlReader.Read(text); + var metadata = TypeMetadata.For(); + + foreach (var node in doc.Nodes) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (!node.Name.Value.Equals(metadata.NodeName, NodeNameComparison)) + { + throw new KuddleSerializationException( + $"Expected node '{metadata.NodeName}', found '{node.Name.Value}'." + ); + } + + var item = new T(); + MapNodeToObject(node, item, metadata); + yield return item; + } + } + + /// + /// Deserializes a KDL document to a single object of type T. + /// + public static T Deserialize(string text, KdlSerializerOptions? options = null) + where T : new() + { + var document = KdlReader.Read(text); + var metadata = TypeMetadata.For(); + + // Reject dictionary types + if (metadata.IsDictionary) + { + throw new KuddleSerializationException( + $"Dictionary deserialization is not supported. Type: {metadata.Type.Name}" + ); + } + + // Reject IEnumerable types (use DeserializeMany instead) + if (metadata.IsIEnumerable) + { + throw new KuddleSerializationException( + $"Cannot deserialize to collection type '{metadata.Type.Name}'. Use DeserializeMany() instead." + ); + } + + if (metadata.IsNodeDefinition) + { + // Type maps to a single KDL node + if (document.Nodes.Count != 1) + { + throw new KuddleSerializationException( + $"Expected exactly 1 root node for type '{typeof(T).Name}', found {document.Nodes.Count}." + ); + } + + var rootNode = document.Nodes[0]; + if (!rootNode.Name.Value.Equals(metadata.NodeName, NodeNameComparison)) + { + throw new KuddleSerializationException( + $"Expected node '{metadata.NodeName}', found '{rootNode.Name.Value}'." + ); + } + + var instance = new T(); + MapNodeToObject(rootNode, instance, metadata); + return instance; + } + else + { + // Type maps to a document with child nodes as properties + var instance = new T(); + MapChildNodes(document.Nodes, instance, metadata); + return instance; + } + } + + /// + /// Maps a KDL node's entries and children to an object instance. + /// + private static void MapNodeToObject(KdlNode node, object instance, TypeMetadata metadata) + { + // Map arguments + foreach (var mapping in metadata.ArgumentAttributes) + { + var argValue = node.Arg(mapping.ArgumentIndex); + if (argValue is null) + { + throw new KuddleSerializationException( + $"Missing required argument at index {mapping.ArgumentIndex}'." + ); + } + + SetPropertyValue(mapping.Property, instance, argValue, mapping.TypeAnnotation); + } + + // Map properties + foreach (var mapping in metadata.Properties) + { + var propKey = mapping.GetPropertyKey(); + var kdlValue = node.Prop(propKey); + + if (kdlValue is null) + { + continue; // Optional property, use default + } + + SetPropertyValue(mapping.Property, instance, kdlValue, mapping.TypeAnnotation); + } + + // Map child nodes + MapChildNodes(node.Children?.Nodes, instance, metadata); + } + + /// + /// Maps child KDL nodes to properties marked with [KdlNode]. + /// + private static void MapChildNodes( + IReadOnlyList? nodes, + object instance, + TypeMetadata metadata + ) + { + if (nodes is null || nodes.Count == 0) + { + return; + } + + foreach (var mapping in metadata.Children) + { + var nodeName = mapping.GetChildNodeName(); + var matchingNodes = GetMatchingNodes(nodes, nodeName); + + if (matchingNodes.Count == 0) + { + continue; + } + + var propType = mapping.Property.PropertyType; + var propMeta = TypeMetadata.For(propType); + + if (propMeta.IsIEnumerable) + { + SetCollectionProperty(mapping.Property, instance, matchingNodes); + } + else if (propMeta.IsComplexType) + { + if (matchingNodes.Count > 1) + { + throw new KuddleSerializationException( + $"Expected single node '{nodeName}' for property '{mapping.Property.Name}', found {matchingNodes.Count}." + ); + } + + SetComplexProperty(mapping.Property, instance, matchingNodes[0]); + } + else + { + // Scalar property - extract from first argument + if (matchingNodes.Count > 1) + { + throw new KuddleSerializationException( + $"Expected single node '{nodeName}' for scalar property '{mapping.Property.Name}', found {matchingNodes.Count}." + ); + } + + var argValue = matchingNodes[0].Arg(0); + if (argValue is not null) + { + SetPropertyValue(mapping.Property, instance, argValue, mapping.TypeAnnotation); + } + } + } + } + + private static List GetMatchingNodes(IReadOnlyList nodes, string nodeName) + { + var result = new List(); + foreach (var node in nodes) + { + if (node.Name.Value.Equals(nodeName, NodeNameComparison)) + { + result.Add(node); + } + } + return result; + } + + private static void SetPropertyValue( + PropertyInfo property, + object instance, + KdlValue kdlValue, + string? expectedTypeAnnotation = null + ) + { + var result = KdlValueConverter.FromKdlOrThrow( + kdlValue, + property.PropertyType, + $"Property: {property.DeclaringType?.Name}.{property.Name}", + expectedTypeAnnotation + ); + + property.SetValue(instance, result); + } + + private static void SetComplexProperty(PropertyInfo property, object instance, KdlNode node) + { + var childInstance = + Activator.CreateInstance(property.PropertyType) + ?? throw new KuddleSerializationException( + $"Failed to create instance of '{property.PropertyType.Name}' for property '{property.Name}'." + ); + + var childMetadata = TypeMetadata.For(property.PropertyType); + MapNodeToObject(node, childInstance, childMetadata); + + property.SetValue(instance, childInstance); + } + + private static void SetCollectionProperty( + PropertyInfo property, + object instance, + List nodes + ) + { + var elementType = GetCollectionElementType(property.PropertyType); + var metadata = TypeMetadata.For(property.PropertyType); + + if (!metadata.IsComplexType) + { + throw new KuddleSerializationException( + $"Collection element type '{elementType.Name}' must be a complex type for property '{property.Name}'." + ); + } + + var listType = typeof(List<>).MakeGenericType(elementType); + var list = (IList)Activator.CreateInstance(listType)!; + + var elementMetadata = TypeMetadata.For(elementType); + + foreach (var node in nodes) + { + var element = + Activator.CreateInstance(elementType) + ?? throw new KuddleSerializationException( + $"Failed to create instance of '{elementType.Name}'." + ); + + MapNodeToObject(node, element, elementMetadata); + list.Add(element); + } + + var finalValue = ConvertToTargetCollectionType(property.PropertyType, elementType, list); + property.SetValue(instance, finalValue); + } + + #endregion + + #region Serialization + + /// + /// Serializes an object to a KDL string. + /// + public static string Serialize(T instance, KdlSerializerOptions? options = null) + { + ArgumentNullException.ThrowIfNull(instance); + + var type = typeof(T); + var metadata = TypeMetadata.For(); + + if (!metadata.IsComplexType) + { + throw new KuddleSerializationException( + $"Cannot serialize primitive type '{type.Name}'. Only complex types are supported." + ); + } + + var doc = new KdlDocument(); + + if (metadata.IsDictionary) + { + throw new NotSupportedException("Dictionary serialization is not yet supported."); + } + + if (metadata.IsIEnumerable) + { + var nodes = SerializeCollection((IEnumerable)instance); + doc.Nodes.AddRange(nodes); + } + else + { + var node = SerializeToNode(instance); + doc.Nodes.Add(node); + } + + return KdlWriter.Write(doc); + } + + /// + /// Serializes multiple objects to a KDL string. + /// + public static string SerializeMany( + IEnumerable items, + KdlSerializerOptions? options = null, + CancellationToken cancellationToken = default + ) + { + ArgumentNullException.ThrowIfNull(items); + + var metadata = TypeMetadata.For(); + var doc = new KdlDocument(); + + foreach (var item in items) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (item is null) + { + continue; + } + + var node = SerializeToNode(item); + doc.Nodes.Add(node); + } + + return KdlWriter.Write(doc); + } + + private static KdlNode SerializeToNode(object instance, string? overrideNodeName = null) + { + var type = instance.GetType(); + var metadata = TypeMetadata.For(type); + var nodeName = overrideNodeName ?? metadata.NodeName; + + var entries = new List(); + + // Serialize arguments (in order) + foreach (var mapping in metadata.ArgumentAttributes) + { + var value = mapping.Property.GetValue(instance); + var typeAnnotation = mapping.TypeAnnotation; + var kdlValue = KdlValueConverter.ToKdlOrThrow( + value, + $"Argument property: {mapping.Property.Name}", + typeAnnotation + ); + + entries.Add(new KdlArgument(kdlValue)); + } + + // Serialize properties + foreach (var mapping in metadata.Properties) + { + var value = mapping.Property.GetValue(instance); + var typeAnnotation = mapping.TypeAnnotation; + + if (!KdlValueConverter.TryToKdl(value, out var kdlValue, typeAnnotation)) + { + continue; // Skip properties that can't be converted + } + + var key = mapping.GetPropertyKey(); + entries.Add(new KdlProperty(KdlValue.From(key), kdlValue)); + } + + // Serialize children + KdlBlock? childBlock = null; + + foreach (var mapping in metadata.Children) + { + var propValue = mapping.Property.GetValue(instance); + + if (propValue is null) + { + continue; + } + + childBlock ??= new KdlBlock(); + var childNodeName = mapping.GetChildNodeName(); + + var propType = mapping.Property.PropertyType; + var childMeta = TypeMetadata.For(propType); + + if (childMeta.IsIEnumerable) + { + var childNodes = SerializeCollection((IEnumerable)propValue, childNodeName); + childBlock.Nodes.AddRange(childNodes); + } + else if (childMeta.IsComplexType) + { + var childNode = SerializeToNode(propValue, childNodeName); + childBlock.Nodes.Add(childNode); + } + else + { + // Scalar value as a child node with single argument + var typeAnnotation = mapping.TypeAnnotation; + var kdlValue = KdlValueConverter.ToKdlOrThrow( + propValue, + $"Child scalar property: {mapping.Property.Name}", + typeAnnotation + ); + + var scalarNode = new KdlNode(KdlValue.From(childNodeName)) + { + Entries = [new KdlArgument(kdlValue)], + }; + childBlock.Nodes.Add(scalarNode); + } + } + + return new KdlNode(KdlValue.From(nodeName)) + { + Entries = entries, + Children = childBlock?.Nodes.Count > 0 ? childBlock : null, + }; + } + + private static IEnumerable SerializeCollection( + IEnumerable collection, + string? overrideNodeName = null + ) + { + foreach (var item in collection) + { + if (item is null) + { + continue; + } + + yield return SerializeToNode(item, overrideNodeName); + } + } + + #endregion + + #region Helpers + + private static Type GetCollectionElementType(Type collectionType) + { + if (collectionType.IsArray) + { + return collectionType.GetElementType()!; + } + + if (collectionType.IsGenericType) + { + return collectionType.GetGenericArguments()[0]; + } + + throw new KuddleSerializationException( + $"Unsupported collection type '{collectionType.FullName}'." + ); + } + + private static object ConvertToTargetCollectionType( + Type targetType, + Type elementType, + IList list + ) + { + if (targetType.IsArray) + { + var array = Array.CreateInstance(elementType, list.Count); + list.CopyTo(array, 0); + return array; + } + + if (targetType.IsAssignableFrom(list.GetType())) + { + return list; + } + + return list; + } + + #endregion +} + +/// +/// Options for KDL serialization and deserialization. +/// +public record KdlSerializerOptions +{ + /// + /// Whether to ignore null values when serializing. Default is true. + /// + public bool IgnoreNullValues { get; init; } = true; + + /// + /// Whether property/node name comparison is case-insensitive. Default is true. + /// + public bool CaseInsensitiveNames { get; init; } = true; + + /// + /// Whether to include type annotations in output. Default is true. + /// + public bool WriteTypeAnnotations { get; init; } = true; + + /// + /// Default options instance. + /// + public static KdlSerializerOptions Default { get; } = new(); +} diff --git a/src/Kuddle.Net/Serialization/KdlValueConverter.cs b/src/Kuddle.Net/Serialization/KdlValueConverter.cs new file mode 100644 index 0000000..389bbab --- /dev/null +++ b/src/Kuddle.Net/Serialization/KdlValueConverter.cs @@ -0,0 +1,221 @@ +using System; +using Kuddle.AST; +using Kuddle.Extensions; +using Kuddle.Parser; + +namespace Kuddle.Serialization; + +/// +/// Provides unified conversion between CLR values and KDL values. +/// +internal static class KdlValueConverter +{ + /// + /// Attempts to convert a KDL value to a CLR type. + /// + public static bool TryFromKdl(KdlValue kdlValue, Type targetType, out object? value) + { + value = default; + + if (kdlValue is KdlNull) + { + if (!IsNullable(targetType)) + { + return false; + } + value = null; + return true; + } + + var underlying = Nullable.GetUnderlyingType(targetType) ?? targetType; + + // String + if (underlying == typeof(string) && kdlValue.TryGetString(out var stringVal)) + { + value = stringVal; + return true; + } + + // Integers (signed) + if (underlying == typeof(int) && kdlValue.TryGetInt(out var intVal)) + { + value = intVal; + return true; + } + + if (underlying == typeof(long) && kdlValue.TryGetLong(out var longVal)) + { + value = longVal; + return true; + } + + if (underlying == typeof(short) && kdlValue.TryGetInt(out var shortVal)) + { + value = (short)shortVal; + return true; + } + + if (underlying == typeof(sbyte) && kdlValue.TryGetInt(out var sbyteVal)) + { + value = (sbyte)sbyteVal; + return true; + } + + // Integers (unsigned) + if (underlying == typeof(uint) && kdlValue.TryGetLong(out var uintVal)) + { + value = (uint)uintVal; + return true; + } + + if (underlying == typeof(ulong) && kdlValue.TryGetLong(out var ulongVal)) + { + value = (ulong)ulongVal; + return true; + } + + if (underlying == typeof(ushort) && kdlValue.TryGetInt(out var ushortVal)) + { + value = (ushort)ushortVal; + return true; + } + + if (underlying == typeof(byte) && kdlValue.TryGetInt(out var byteVal)) + { + value = (byte)byteVal; + return true; + } + + // Floating point + if (underlying == typeof(double) && kdlValue.TryGetDouble(out var doubleVal)) + { + value = doubleVal; + return true; + } + + if (underlying == typeof(decimal) && kdlValue.TryGetDecimal(out var decimalVal)) + { + value = decimalVal; + return true; + } + + if (underlying == typeof(float) && kdlValue.TryGetDouble(out var floatVal)) + { + value = (float)floatVal; + return true; + } + + // Boolean + if (underlying == typeof(bool) && kdlValue.TryGetBool(out var boolVal)) + { + value = boolVal; + return true; + } + + // Special types with type annotations + if (underlying == typeof(Guid) && kdlValue.TryGetUuid(out var uuid)) + { + value = uuid; + return true; + } + + if (underlying == typeof(DateTimeOffset) && kdlValue.TryGetDateTime(out var dto)) + { + value = dto; + return true; + } + + if (underlying == typeof(DateTime) && kdlValue.TryGetDateTime(out var dt)) + { + value = dt.DateTime; + return true; + } + + return false; + } + + /// + /// Attempts to convert a CLR value to a KDL value. + /// + public static bool TryToKdl(object? input, out KdlValue kdlValue, string? typeAnnotation = null) + { + if (input is null) + { + kdlValue = KdlValue.Null; + return true; + } + + kdlValue = input switch + { + string s => KdlValue.From(s), + int i => KdlValue.From(i), + long l => KdlValue.From(l), + short sh => KdlValue.From(sh), + sbyte sb => KdlValue.From(sb), + uint ui => KdlValue.From(ui), + ulong ul => KdlValue.From((long)ul), + ushort us => KdlValue.From(us), + byte by => KdlValue.From(by), + double d => KdlValue.From(d), + float f => KdlValue.From((double)f), + decimal m => KdlValue.From(m), + bool b => KdlValue.From(b), + Guid uuid => KdlValue.From(uuid), + DateTimeOffset dto => KdlValue.From(dto), + DateTime dt => KdlValue.From(new DateTimeOffset(dt)), + _ => null!, + }; + + if (kdlValue is not null && typeAnnotation is not null) + { + kdlValue = kdlValue with { TypeAnnotation = typeAnnotation }; + } + + return kdlValue is not null; + } + + /// + /// Converts a KDL value to a CLR type, throwing on failure. + /// + public static object? FromKdlOrThrow( + KdlValue kdlValue, + Type targetType, + string context, + string? expectedTypeAnnotation = null + ) + { + var finalTargetType = + CharacterSets.GetClrType(expectedTypeAnnotation ?? kdlValue.TypeAnnotation) + ?? targetType; + + if (!TryFromKdl(kdlValue, finalTargetType, out var result)) + { + throw new KuddleSerializationException( + $"Cannot convert KDL value '{kdlValue}' to {targetType.Name}. {context}" + ); + } + return result; + } + + /// + /// Converts a CLR value to a KDL value, throwing on failure. + /// + public static KdlValue ToKdlOrThrow( + object? input, + string context, + string? typeAnnotation = null + ) + { + if (!TryToKdl(input, out var kdlValue, typeAnnotation)) + { + var typeName = input?.GetType().Name ?? "null"; + throw new KuddleSerializationException( + $"Cannot convert CLR value of type '{typeName}' to KDL. {context}" + ); + } + return kdlValue; + } + + private static bool IsNullable(Type type) => + !type.IsValueType || Nullable.GetUnderlyingType(type) != null; +} diff --git a/src/Kuddle/Serialization/KuddleWriter.cs b/src/Kuddle.Net/Serialization/KdlWriter.cs similarity index 95% rename from src/Kuddle/Serialization/KuddleWriter.cs rename to src/Kuddle.Net/Serialization/KdlWriter.cs index 7ac405e..b757e52 100644 --- a/src/Kuddle/Serialization/KuddleWriter.cs +++ b/src/Kuddle.Net/Serialization/KdlWriter.cs @@ -5,20 +5,20 @@ namespace Kuddle.Serialization; -public class KuddleWriter +public class KdlWriter { - private readonly KuddleWriterOptions _options; + private readonly KdlWriterOptions _options; private readonly StringBuilder _sb = new(); private int _depth = 0; - public KuddleWriter(KuddleWriterOptions? options = null) + public KdlWriter(KdlWriterOptions? options = null) { - _options ??= options ?? KuddleWriterOptions.Default; + _options ??= options ?? KdlWriterOptions.Default; } - public static string Write(KdlDocument document, KuddleWriterOptions? options = null) + public static string Write(KdlDocument document, KdlWriterOptions? options = null) { - var writer = new KuddleWriter(options); + var writer = new KdlWriter(options); writer.WriteDocument(document); return writer._sb.ToString(); } diff --git a/src/Kuddle/Serialization/KuddleWriterOptions.cs b/src/Kuddle.Net/Serialization/KdlWriterOptions.cs similarity index 76% rename from src/Kuddle/Serialization/KuddleWriterOptions.cs rename to src/Kuddle.Net/Serialization/KdlWriterOptions.cs index 7a6112a..5a84e3b 100644 --- a/src/Kuddle/Serialization/KuddleWriterOptions.cs +++ b/src/Kuddle.Net/Serialization/KdlWriterOptions.cs @@ -1,8 +1,8 @@ namespace Kuddle.Serialization; -public record KuddleWriterOptions +public record KdlWriterOptions { - public static KuddleWriterOptions Default => new(); + public static KdlWriterOptions Default => new(); public string IndentChar { get; init; } = " "; public string NewLine { get; init; } = "\n"; diff --git a/src/Kuddle.Net/Serialization/TypeMetadata.cs b/src/Kuddle.Net/Serialization/TypeMetadata.cs new file mode 100644 index 0000000..7f2548a --- /dev/null +++ b/src/Kuddle.Net/Serialization/TypeMetadata.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; + +namespace Kuddle.Serialization; + +/// +/// Represents a mapping from a C# property to a KDL entry (argument, property, or child node). +/// +internal sealed record KdlEntryMapping(PropertyInfo Property, KdlEntryAttribute? Entry) +{ + public bool IsArgument => Entry is KdlArgumentAttribute; + public bool IsProperty => Entry is KdlPropertyAttribute; + public bool IsChildNode => Entry is KdlNodeAttribute; + + public int ArgumentIndex => Entry is KdlArgumentAttribute arg ? arg.Index : -1; + + public string GetPropertyKey() => + Entry is KdlPropertyAttribute prop + ? prop.Key ?? Property.Name.ToLowerInvariant() + : Property.Name.ToLowerInvariant(); + + public string GetChildNodeName() => + Entry is KdlNodeAttribute node + ? node.Name ?? Property.Name.ToLowerInvariant() + : Property.Name.ToLowerInvariant(); + + public string? TypeAnnotation => Entry?.TypeAnnotation; +} + +/// +/// Cached metadata about how a CLR type maps to/from KDL nodes. +/// +/// +[DebuggerDisplay("Type = {Type.Name,nq}")] +internal sealed class TypeMetadata +{ + private static readonly ConcurrentDictionary s_cache = new(); + + public Type Type { get; } + public string NodeName { get; } + public bool IsNodeDefinition => ArgumentAttributes.Count > 0 || Properties.Count > 0; + + /// Properties mapped to KDL arguments, sorted by index. + public IReadOnlyList ArgumentAttributes { get; } + + /// Properties mapped to KDL properties. + public IReadOnlyList Properties { get; } + + /// Properties mapped to child nodes. + public IReadOnlyList Children { get; } + + /// All writable, non-ignored properties. + public IReadOnlyList AllMappings { get; } + + private TypeMetadata(Type type) + { + Type = type; + + var kdlTypeAttr = type.GetCustomAttribute(); + NodeName = kdlTypeAttr?.Name ?? type.Name.ToLowerInvariant(); + + var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(p => p.CanWrite && p.GetCustomAttribute() == null) + .Select(p => new KdlEntryMapping(p, p.GetCustomAttribute())) + .ToList(); + + AllMappings = props; + + ArgumentAttributes = [.. props.Where(m => m.IsArgument).OrderBy(m => m.ArgumentIndex)]; + Properties = [.. props.Where(m => m.IsProperty)]; + Children = [.. props.Where(m => m.IsChildNode)]; + } + + /// + /// Gets or creates cached metadata for a type. + /// + public static TypeMetadata For(Type type) => s_cache.GetOrAdd(type, t => new TypeMetadata(t)); + + /// + /// Gets or creates cached metadata for a type. + /// + public static TypeMetadata For() => For(typeof(T)); + + /// + /// Checks if a type is a complex type (not primitive, not string, not interface/abstract). + /// + public bool IsComplexType => + !Type.IsValueType + && !Type.IsPrimitive + && Type != typeof(string) + && Type != typeof(object) + && !Type.IsInterface + && !Type.IsAbstract; + + /// + /// Checks if a type is enumerable (but not string or dictionary). + /// + public bool IsIEnumerable => + Type != typeof(string) && !IsDictionary && typeof(IEnumerable).IsAssignableFrom(Type); + + /// + /// Checks if a type is a dictionary. + /// + public bool IsDictionary => + Type.IsGenericType + && Type.GetInterfaces() + .Any(i => + i.IsGenericType + && ( + i.GetGenericTypeDefinition() == typeof(IDictionary<,>) + || i.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>) + ) + ); +} diff --git a/src/Kuddle/Validation/KuddleReservedTypeValidator.cs b/src/Kuddle.Net/Validation/KuddleReservedTypeValidator.cs similarity index 86% rename from src/Kuddle/Validation/KuddleReservedTypeValidator.cs rename to src/Kuddle.Net/Validation/KuddleReservedTypeValidator.cs index c8125e9..0a44848 100644 --- a/src/Kuddle/Validation/KuddleReservedTypeValidator.cs +++ b/src/Kuddle.Net/Validation/KuddleReservedTypeValidator.cs @@ -2,45 +2,15 @@ using System.Collections.Generic; using System.Net; using System.Net.Sockets; -using System.Text.Json; using System.Text.RegularExpressions; using Kuddle.AST; using Kuddle.Exceptions; +using Kuddle.Parser; namespace Kuddle.Validation; -public static class KuddleReservedTypeValidator +public static class KdlReservedTypeValidator { - private static readonly HashSet ReservedTypes = - [ - "i8", - "i16", - "i32", - "i64", - "u8", - "u16", - "u32", - "u64", - "f32", - "f64", - "decimal64", - "decimal128", - "date-time", - "time", - "date", - "duration", - "decimal", - "currency", - "country-2", - "country-3", - "ipv4", - "ipv6", - "url", - "uuid", - "regex", - "base64", - ]; - public static void Validate(KdlDocument doc) { var errors = new List(); @@ -83,7 +53,7 @@ private static void ValidateValue(KdlValue val, List erro { if (val.TypeAnnotation == null) return; - if (!ReservedTypes.Contains(val.TypeAnnotation)) + if (!CharacterSets.ReservedTypes.Contains(val.TypeAnnotation)) return; try @@ -174,7 +144,10 @@ private static void ValidateValue(KdlValue val, List erro catch (Exception) { errors.Add( - new KuddleValidationError($"Value '{val}' is not a valid '{val.TypeAnnotation}'.", val) + new KuddleValidationError( + $"Value '{val}' is not a valid '{val.TypeAnnotation}'.", + val + ) ); } } diff --git a/src/Kuddle/Kuddle.csproj b/src/Kuddle/Kuddle.csproj deleted file mode 100644 index c2c22f6..0000000 --- a/src/Kuddle/Kuddle.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - disable - enable - net10.0 - preview - true - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - diff --git a/src/Kuddle/Serialization/Attributes/KdlChildrenAttribute.cs b/src/Kuddle/Serialization/Attributes/KdlChildrenAttribute.cs deleted file mode 100644 index fc1e300..0000000 --- a/src/Kuddle/Serialization/Attributes/KdlChildrenAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Kuddle.Serialization; - -/// -/// Marks a property to receive child nodes of a specific type from the KDL node's children block. -/// -/// The KDL node name to match for children (e.g., "dependency"). -[AttributeUsage(AttributeTargets.Property)] -public class KdlChildrenAttribute(string childNodeName) : Attribute -{ - /// Gets the child node name to match. - public string ChildNodeName { get; } = childNodeName; -} diff --git a/src/Kuddle/Serialization/Attributes/KdlSerializer.cs b/src/Kuddle/Serialization/Attributes/KdlSerializer.cs deleted file mode 100644 index 02c37d1..0000000 --- a/src/Kuddle/Serialization/Attributes/KdlSerializer.cs +++ /dev/null @@ -1,459 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using Kuddle.AST; -using Kuddle.Extensions; - -namespace Kuddle.Serialization; - -public static class KdlSerializer -{ - public static async Task Deserialize(string text, KdlSerializerOptions? options = null) - { - var t = typeof(T); - KdlDocument document; - if (t.IsDictionary) - { - throw new KuddleSerializationException( - "Deserialization to dictionaries is not supported" - ); - } - else if (t.IsIEnumerable) - { - var genericTypeDef = t.GetGenericTypeDefinition(); - var args = t.GetGenericArguments(); - var listType = typeof(List<>).MakeGenericType(args); - var listInstance = (IList)Activator.CreateInstance(listType)!; - - document = await KuddleReader.ReadAsync(text); - - var firstName = document.Nodes.FirstOrDefault()?.Name; - - foreach (var node in document.Nodes) - { - if (node.Name != firstName) - { - throw new KuddleSerializationException( - "All root nodes must have the same name." - ); - } - } - - return (T)listInstance; - } - - if (!t.IsComplexType) - { - throw new KuddleSerializationException( - $"Cannot deserialize type '{t.FullName}' as a complex object. " - + "Ensure the type is a concrete class with a public parameterless constructor." - ); - } - var instance = Activator.CreateInstance(); - document = await KuddleReader.ReadAsync(text); - - if (document.Nodes.Count != 1) - { - throw new KuddleSerializationException( - "Kdl document must have a single root node to map to an instance T" - ); - } - if ( - !typeof(T).Name.Equals( - document.Nodes[0].Name.Value, - StringComparison.InvariantCultureIgnoreCase - ) - ) - { - throw new KuddleSerializationException( - $"Node name '{document.Nodes[0].Name}' does not match target type name '{typeof(T).Name}'." - ); - } - MapToInstance(document.Nodes[0], instance); - - return instance; - } - - private static void MapToInstance(KdlNode node, T instance) - { - var rootType = typeof(T); - - var props = instance! - .GetType() - .GetProperties(BindingFlags.Public | BindingFlags.Instance) - .Where(p => p.CanWrite && p.GetCustomAttribute() == null); - - foreach (var prop in props) - { - var argAttr = prop.GetCustomAttribute(); - if (argAttr is not null) - { - if (prop.PropertyType.IsComplexType) - throw new KuddleSerializationException(); - - var targetArg = - node.Arg(argAttr.Index) - ?? throw new KuddleSerializationException( - $"Expected Kdl argument at index {argAttr.Index} but found nothing" - ); - SetPropValue(prop, instance, targetArg); - continue; - } - - var propAttr = prop.GetCustomAttribute(); - if (propAttr is not null) - { - var propKey = propAttr.Key ?? prop.Name.ToLowerInvariant(); - var kdlProp = node.Prop(propKey); - - if (kdlProp is null) - continue; - - SetPropValue(prop, instance, kdlProp); - } - - var childAttr = prop.GetCustomAttribute(); - if (childAttr is not null) - { - var nodeName = childAttr.ChildNodeName ?? prop.Name.ToLowerInvariant(); - var groupedChildNodes = node.Children?.Nodes.GroupBy(n => n.Name) ?? []; - foreach (var childNode in groupedChildNodes) - { - if (childNode.Key.Value == nodeName) - { - SetCollectionPropValue(prop, instance, childNode); - } - } - } - } - } - - private static void SetCollectionPropValue( - PropertyInfo collectionProp, - object instance, - IEnumerable childNodeGroup - ) - { - var type = collectionProp.PropertyType; - if (!type.IsIEnumerable) - { - throw new NotSupportedException( - $"{nameof(SetCollectionPropValue)} expects a collection type, but received a {type.Name}" - ); - } - - var elementType = GetElementType(type); - if (!elementType.IsComplexType) - throw new KuddleSerializationException( - $"Collection element type '{elementType.Name}' must be a complex type" - ); - - var listType = typeof(List<>).MakeGenericType(elementType); - var list = (IList)Activator.CreateInstance(listType)!; - - foreach (var childNode in childNodeGroup) - { - var element = - Activator.CreateInstance(elementType) - ?? throw new KuddleSerializationException( - $"Failed to create instance of '{elementType.Name}'" - ); - - MapToInstance(childNode, element); - - list.Add(element); - } - - object finalValue = MapCollection(type, elementType, list); - - collectionProp.SetValue(instance, finalValue); - } - - private static object MapCollection(Type targetType, Type elementType, IList list) - { - if (targetType.IsArray) - { - var array = Array.CreateInstance(elementType, list.Count); - list.CopyTo(array, 0); - return array; - } - - if (targetType.IsAssignableFrom(list.GetType())) - { - return list; - } - - // IEnumerable, IReadOnlyList, etc. - return list; - } - - static Type GetElementType(Type collectionType) - { - return collectionType.IsArray ? collectionType.GetElementType()! - : collectionType.IsGenericType ? collectionType.GetGenericArguments()[0] - : throw new KuddleSerializationException( - $"Unsupported collection type '{collectionType.FullName}'" - ); - } - - private static void SetPropValue(PropertyInfo prop, object instance, KdlValue kdlValue) - { - if (!TryConvertFromKdlValue(kdlValue, prop.PropertyType, out var result)) - { - throw new KuddleSerializationException( - $"Cannot convert KDL value to property {prop.Name} ({prop.PropertyType})" - ); - } - - prop.SetValue(instance, result); - } - - private static bool TryConvertFromKdlValue( - KdlValue kdlValue, - Type targetType, - out object? result - ) - { - result = null; - if (kdlValue is KdlNull) - { - if (!targetType.IsNullable()) - return false; - result = null; - return true; - } - - Type underlying = Nullable.GetUnderlyingType(targetType) ?? targetType; - - if (underlying == typeof(string) && kdlValue.TryGetString(out var stringVal)) - { - result = stringVal; - return true; - } - - if (underlying == typeof(int) && kdlValue.TryGetInt(out var intVal)) - { - result = intVal; - return true; - } - - if (underlying == typeof(double) && kdlValue.TryGetDouble(out var doubleVal)) - { - result = doubleVal; - return true; - } - - if (underlying == typeof(long) && kdlValue.TryGetLong(out var longVal)) - { - result = longVal; - return true; - } - - if (underlying == typeof(decimal) && kdlValue.TryGetDecimal(out var decimalVal)) - { - result = decimalVal; - return true; - } - - if (underlying == typeof(bool) && kdlValue.TryGetBool(out var boolVal)) - { - result = boolVal; - return true; - } - - if (underlying == typeof(Guid) && kdlValue.TryGetUuid(out var uuid)) - { - result = uuid; - return true; - } - if (underlying == typeof(DateTimeOffset) && kdlValue.TryGetDateTime(out var datetime)) - { - result = datetime; - return true; - } - return false; - } - - public static string Serialize(T original) - { - var doc = new KdlDocument(); - var type = typeof(T); - if (!type.IsComplexType) - throw new KuddleSerializationException(); - - if (type.IsDictionary) - throw new NotSupportedException(); - if (type.IsIEnumerable) - { - var nodes = SerializeCollection(original as IEnumerable); - doc.Nodes.AddRange(nodes); - } - else - { - var node = SerializeNode(original); - doc.Nodes.Add(node); - } - - return KuddleWriter.Write(doc); - } - - private static KdlNode SerializeNode(object? instance, string? nodeName = null) - { - var type = instance?.GetType() ?? throw new ArgumentNullException(); - nodeName ??= - type.GetCustomAttribute()?.Name ?? type.Name.ToLowerInvariant(); - - var entries = new List(); - foreach (var argProp in type.GetKdlArgProps()) - { - var value = argProp.Item1.GetValue(instance); - - if (!TryConvertToKdlValue(value, out var kdlValue)) - continue; - - var arg = new KdlArgument(kdlValue); - entries.Add(arg); - } - - var propProps = type.GetKdlPropProps(); - - foreach (var (prop, kdlAttr) in propProps) - { - var key = kdlAttr.Key ?? prop.Name.ToLowerInvariant(); - if (!TryConvertToKdlValue(prop.GetValue(instance), out var value)) - continue; - - var kdlProp = new KdlProperty(KdlValue.From(key), value); - entries.Add(kdlProp); - } - - var childProps = type.GetKdlChildProps(); - var block = new KdlBlock(); - foreach (var (prop, childAttr) in childProps) - { - var childNodeName = childAttr.ChildNodeName ?? prop.Name.ToLowerInvariant(); - if (prop.PropertyType.IsDictionary) - throw new NotSupportedException(); - - if (prop.PropertyType.IsIEnumerable) - { - var col = prop.GetValue(instance); - var nodes = SerializeCollection(col as IEnumerable); - block.Nodes.AddRange(nodes); - } - } - return new KdlNode(KdlValue.From(nodeName)) - { - Entries = entries, - Children = block.Nodes.Count > 0 ? block : null, - }; - } - - private static bool TryConvertToKdlValue(object? input, out KdlValue kdlValue) - { - kdlValue = KdlValue.Null; - if (input is null) - return true; - - var type = input.GetType(); - if (type.IsComplexType) - throw new KuddleSerializationException("Kdl arguments should be simple scalar values"); - - kdlValue = input switch - { - string s => KdlValue.From(s), - int i => KdlValue.From(i), - double d => KdlValue.From(d), - long l => KdlValue.From(l), - decimal m => KdlValue.From(m), - bool b => KdlValue.From(b), - Guid uuid => KdlValue.From(uuid), - DateTimeOffset dto => KdlValue.From(dto), - _ => null!, - }; - return kdlValue is not null; - } - - private static IEnumerable SerializeCollection(IEnumerable? original) - { - var nodes = new List(); - foreach (var item in original!) - { - var node = SerializeNode(item); - nodes.Add(node); - } - - return nodes; - } -} - -public record KdlSerializerOptions { } - -internal static class TypeExtensions -{ - extension(Type type) - { - public bool IsComplexType => - !type.IsValueType - && !type.IsPrimitive - && type != typeof(string) - && type != typeof(object) - && !type.IsInterface - && !type.IsAbstract; - - public bool IsDictionary => - type.IsGenericType - && type.GetInterfaces() - .Any(i => - i.IsGenericType - && ( - i.GetGenericTypeDefinition() == typeof(IDictionary<,>) - || i.GetGenericTypeDefinition() == typeof(IReadOnlyDictionary<,>) - ) - ); - - public bool IsIEnumerable => - type != typeof(string) - && !type.IsDictionary - && type.IsAssignableTo(typeof(IEnumerable)); - - internal (PropertyInfo, KdlArgumentAttribute)[] GetKdlArgProps() => - type.GetProperties(BindingFlags.Public | BindingFlags.Instance) - .Select(p => new - { - Property = p, - ArgAttr = p.GetCustomAttribute(), - }) - .Where(x => x.ArgAttr is not null) - .OrderBy(x => x.ArgAttr!.Index) - .Select(x => (x.Property, x.ArgAttr!)) - .ToArray(); - - internal (PropertyInfo, KdlPropertyAttribute)[] GetKdlPropProps() => - type.GetProperties(BindingFlags.Public | BindingFlags.Instance) - .Select(p => new - { - Property = p, - PropAttr = p.GetCustomAttribute(), - }) - .Where(x => x.PropAttr is not null) - .Select(x => (x.Property, x.PropAttr!)) - .ToArray(); - - internal (PropertyInfo, KdlChildrenAttribute)[] GetKdlChildProps() => - type.GetProperties(BindingFlags.Public | BindingFlags.Instance) - .Select(p => new - { - Property = p, - ChildAttr = p.GetCustomAttribute(), - }) - .Where(x => x.ChildAttr is not null) - .Select(x => (x.Property, x.ChildAttr!)) - .ToArray(); - - public bool IsNullable() => Nullable.GetUnderlyingType(type) != null; - } -} diff --git a/src/Kuddle/Serialization/KuddleReaderOptions.cs b/src/Kuddle/Serialization/KuddleReaderOptions.cs deleted file mode 100644 index 95ab1bc..0000000 --- a/src/Kuddle/Serialization/KuddleReaderOptions.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Kuddle; - -public record KuddleReaderOptions -{ - public static KuddleReaderOptions Default => new() { ValidateReservedTypes = true }; - public bool ValidateReservedTypes { get; init; } = true; -} diff --git a/todo.md b/todo.md deleted file mode 100644 index 23f7452..0000000 --- a/todo.md +++ /dev/null @@ -1,86 +0,0 @@ -Here is your updated roadmap. - -You have effectively **completed Phase 1** (AST) and the heavy lifting of **Phase 2** (The Grammar/Parser Logic). - -### Phase 1: The High-Fidelity DOM (AST) ✅ - -*Goal: Define immutable data structures that represent the physical text of a KDL file.* - -* **1.1 Define the Base Object** - * [x] Create `KdlObject` with Trivia support. -* **1.2 Implement Semantic Identifiers** - * [x] Create `KdlIdentifier` (supports RawText and Type Annotations). -* **1.3 Implement Value Primitives** - * [x] `KdlValue` base class. - * [x] `KdlString` (Quoted, Raw, Multiline), `KdlBool`, `KdlNull`. - * [x] `KdlNumber` (with lazy parsing logic). -* **1.4 Implement Node Structure** - * [x] Create `KdlEntry` hierarchy (Argument, Property, SkippedEntry). - * [x] Create `KdlNode` (Name, Entries List, Children Block). -* **1.5 Implement Containers** - * [x] Create `KdlBlock` and `KdlDocument`. - -### Phase 2: The Parser Integration 🚧 - -*Goal: Build the engine that populates the AST while enforcing the KDL spec.* - -* **2.1 Tokenizer & Grammar** - * [x] Implement `KuddleGrammar` using Parlot. - * [x] Implement complex String parsing (Raw, Multiline, Escapes). - * [x] Implement Number parsing (Hex, Binary, Octal). -* **2.2 Structural Parsing** - * [x] Implement Node parsing (Name, Type, properties). - * [x] Implement Recursion (Node -> Children -> Nodes). - * [x] Implement "Slash-Dash" `/-` logic for skipping nodes/entries/children. -* **2.3 API Wrapper** - * [x] Create the public static `KdlParser.Parse(string input)` method that invokes `KuddleGrammar.Document.Parse(...)`. - * [x] Add `KdlParseException` wrapping Parlot errors with line/column info. -* **2.4 Reserved Type Validation** - * [x] **(New)** Add a post-parse visitor or validation pass to ensure `(u8)` values fit in bytes, `(uuid)` are valid GUIDs, etc. - -### Phase 3: The Developer Experience (DX) & Extensions - -*Goal: Provide a usable API without polluting the pure AST.* - -* **3.1 Value Extensions (The "TryGet" Pattern)** - * [x] Create `KdlValueExtensions`. - * [x] Implement `TryGetUuid`, `TryGetDateTime`, `TryGetInt`, etc. -* **3.2 Mutation Factories** - * [x] Add `KdlValue.From(Guid)`, `KdlValue.From(int)` helpers. -* **3.3 Navigation Helpers** - * [x] Add indexers: `node["propName"]`. - * [x] Add `node.Arguments` (computed view). - -### Phase 4: Serialization - -*Goal: Output KDL from the AST.* - -* **4.1 Round-Trip Writer** - * [x] Create `KdlWriter` implementation. - * [ ] Logic: Iterate AST, write `LeadingTrivia`, `TypeAnnotation`, `RawText`, `TrailingTrivia`. -* **4.2 Formatting Writer** - * [x] Create `KdlFormatter` (or options for `KdlWriter`). - * [ ] Logic to ignore stored trivia and re-indent based on settings. - -### Phase 5: Verification 🚧 - -*Goal: Ensure correctness.* - -* **5.1 Unit Tests** - * [x] **(In Progress)** AST Structural Tests. - * [x] Grammar Logic Tests (Strings, Numbers). -* **5.2 Integration Tests** - * [x] Run against the official `kdl-org/kdl` test suite. - -### Phase 6: Object Mapping (POCOs) & Source Gen - -*Goal: Map AST to strong C# types.* - -* **6.1 The "Sidecar" Source Generator** - * [ ] Create `[KdlAnnotate]` attribute. - * [ ] Generate partial classes for metadata storage. -* **6.2 The Mapper** - * [ ] Implement `KdlSerializer.Deserialize()`. - * [ ] Map Nodes to Classes, Entries to Properties. -* **6.3 Polymorphism** - * [ ] Implement Discriminator logic using Node Annotations.