├── .github
└── workflows
│ ├── docs.yml
│ └── tests.yml
├── .gitignore
├── LICENSE
├── README.md
├── kdl.nimble
├── src
├── kdl.nim
└── kdl
│ ├── decoder.nim
│ ├── encoder.nim
│ ├── jik.nim
│ ├── lexer.nim
│ ├── nodes.nim
│ ├── parser.nim
│ ├── prefs.nim
│ ├── query.nim
│ ├── schema.nim
│ ├── types.nim
│ ├── utils.nim
│ └── xik.nim
└── tests
├── README.md
├── config.nims
├── test_cases
├── examples
│ ├── Cargo.kdl
│ ├── ci.kdl
│ ├── kdl-schema.kdl
│ ├── nuget.kdl
│ └── website.kdl
├── expected_kdl
│ ├── all_escapes.kdl
│ ├── all_node_fields.kdl
│ ├── arg_and_prop_same_name.kdl
│ ├── arg_false_type.kdl
│ ├── arg_float_type.kdl
│ ├── arg_hex_type.kdl
│ ├── arg_null_type.kdl
│ ├── arg_raw_string_type.kdl
│ ├── arg_string_type.kdl
│ ├── arg_true_type.kdl
│ ├── arg_type.kdl
│ ├── arg_zero_type.kdl
│ ├── asterisk_in_block_comment.kdl
│ ├── bare_emoji.kdl
│ ├── binary.kdl
│ ├── binary_trailing_underscore.kdl
│ ├── binary_underscore.kdl
│ ├── blank_arg_type.kdl
│ ├── blank_node_type.kdl
│ ├── blank_prop_type.kdl
│ ├── block_comment.kdl
│ ├── block_comment_after_node.kdl
│ ├── block_comment_before_node.kdl
│ ├── block_comment_before_node_no_space.kdl
│ ├── block_comment_newline.kdl
│ ├── boolean_arg.kdl
│ ├── boolean_prop.kdl
│ ├── commented_arg.kdl
│ ├── commented_child.kdl
│ ├── commented_line.kdl
│ ├── commented_node.kdl
│ ├── commented_prop.kdl
│ ├── crlf_between_nodes.kdl
│ ├── emoji.kdl
│ ├── empty.kdl
│ ├── empty_child.kdl
│ ├── empty_child_different_lines.kdl
│ ├── empty_child_same_line.kdl
│ ├── empty_child_whitespace.kdl
│ ├── empty_quoted_node_id.kdl
│ ├── empty_quoted_prop_key.kdl
│ ├── empty_string_arg.kdl
│ ├── esc_newline_in_string.kdl
│ ├── esc_unicode_in_string.kdl
│ ├── escline.kdl
│ ├── escline_line_comment.kdl
│ ├── escline_node.kdl
│ ├── false_prefix_in_bare_id.kdl
│ ├── false_prefix_in_prop_key.kdl
│ ├── hex.kdl
│ ├── hex_int.kdl
│ ├── hex_int_underscores.kdl
│ ├── hex_leading_zero.kdl
│ ├── int_multiple_underscore.kdl
│ ├── just_block_comment.kdl
│ ├── just_child.kdl
│ ├── just_newline.kdl
│ ├── just_node_id.kdl
│ ├── just_space.kdl
│ ├── leading_newline.kdl
│ ├── leading_zero_binary.kdl
│ ├── leading_zero_int.kdl
│ ├── leading_zero_oct.kdl
│ ├── multiline_comment.kdl
│ ├── multiline_nodes.kdl
│ ├── multiline_string.kdl
│ ├── negative_exponent.kdl
│ ├── negative_float.kdl
│ ├── negative_int.kdl
│ ├── nested_block_comment.kdl
│ ├── nested_children.kdl
│ ├── nested_comments.kdl
│ ├── nested_multiline_block_comment.kdl
│ ├── newline_between_nodes.kdl
│ ├── newlines_in_block_comment.kdl
│ ├── no_decimal_exponent.kdl
│ ├── node_false.kdl
│ ├── node_true.kdl
│ ├── node_type.kdl
│ ├── null_arg.kdl
│ ├── null_prefix_in_bare_id.kdl
│ ├── null_prefix_in_prop_key.kdl
│ ├── null_prop.kdl
│ ├── numeric_arg.kdl
│ ├── numeric_prop.kdl
│ ├── octal.kdl
│ ├── only_cr.kdl
│ ├── only_line_comment.kdl
│ ├── only_line_comment_crlf.kdl
│ ├── only_line_comment_newline.kdl
│ ├── parse_all_arg_types.kdl
│ ├── positive_exponent.kdl
│ ├── positive_int.kdl
│ ├── preserve_duplicate_nodes.kdl
│ ├── preserve_node_order.kdl
│ ├── prop_false_type.kdl
│ ├── prop_float_type.kdl
│ ├── prop_hex_type.kdl
│ ├── prop_null_type.kdl
│ ├── prop_raw_string_type.kdl
│ ├── prop_string_type.kdl
│ ├── prop_true_type.kdl
│ ├── prop_type.kdl
│ ├── prop_zero_type.kdl
│ ├── quoted_arg_type.kdl
│ ├── quoted_node_name.kdl
│ ├── quoted_node_type.kdl
│ ├── quoted_numeric.kdl
│ ├── quoted_prop_name.kdl
│ ├── quoted_prop_type.kdl
│ ├── r_node.kdl
│ ├── raw_arg_type.kdl
│ ├── raw_node_name.kdl
│ ├── raw_node_type.kdl
│ ├── raw_prop_type.kdl
│ ├── raw_string_arg.kdl
│ ├── raw_string_backslash.kdl
│ ├── raw_string_hash_no_esc.kdl
│ ├── raw_string_just_backslash.kdl
│ ├── raw_string_just_quote.kdl
│ ├── raw_string_multiple_hash.kdl
│ ├── raw_string_newline.kdl
│ ├── raw_string_prop.kdl
│ ├── raw_string_quote.kdl
│ ├── repeated_arg.kdl
│ ├── repeated_prop.kdl
│ ├── same_args.kdl
│ ├── same_name_nodes.kdl
│ ├── sci_notation_large.kdl
│ ├── sci_notation_small.kdl
│ ├── semicolon_after_child.kdl
│ ├── semicolon_in_child.kdl
│ ├── semicolon_separated.kdl
│ ├── semicolon_separated_nodes.kdl
│ ├── semicolon_terminated.kdl
│ ├── single_arg.kdl
│ ├── single_prop.kdl
│ ├── slashdash_arg_after_newline_esc.kdl
│ ├── slashdash_arg_before_newline_esc.kdl
│ ├── slashdash_child.kdl
│ ├── slashdash_empty_child.kdl
│ ├── slashdash_full_node.kdl
│ ├── slashdash_in_slashdash.kdl
│ ├── slashdash_negative_number.kdl
│ ├── slashdash_node_in_child.kdl
│ ├── slashdash_node_with_child.kdl
│ ├── slashdash_only_node.kdl
│ ├── slashdash_only_node_with_space.kdl
│ ├── slashdash_prop.kdl
│ ├── slashdash_raw_prop_key.kdl
│ ├── slashdash_repeated_prop.kdl
│ ├── string_arg.kdl
│ ├── string_prop.kdl
│ ├── tab_space.kdl
│ ├── trailing_crlf.kdl
│ ├── trailing_underscore_hex.kdl
│ ├── trailing_underscore_octal.kdl
│ ├── true_prefix_in_bare_id.kdl
│ ├── true_prefix_in_prop_key.kdl
│ ├── two_nodes.kdl
│ ├── underscore_in_exponent.kdl
│ ├── underscore_in_float.kdl
│ ├── underscore_in_fraction.kdl
│ ├── underscore_in_int.kdl
│ ├── underscore_in_octal.kdl
│ ├── unusual_bare_id_chars_in_quoted_id.kdl
│ ├── unusual_chars_in_bare_id.kdl
│ ├── zero_arg.kdl
│ ├── zero_float.kdl
│ └── zero_int.kdl
├── input
│ ├── _negative_exponent.kdl
│ ├── _no_decimal_exponent.kdl
│ ├── _parse_all_arg_types.kdl
│ ├── _positive_exponent.kdl
│ ├── _prop_float_type.kdl
│ ├── _sci_notation_large.kdl
│ ├── _sci_notation_small.kdl
│ ├── _underscore_in_exponent.kdl
│ ├── all_escapes.kdl
│ ├── all_node_fields.kdl
│ ├── arg_and_prop_same_name.kdl
│ ├── arg_false_type.kdl
│ ├── arg_float_type.kdl
│ ├── arg_hex_type.kdl
│ ├── arg_null_type.kdl
│ ├── arg_raw_string_type.kdl
│ ├── arg_string_type.kdl
│ ├── arg_true_type.kdl
│ ├── arg_type.kdl
│ ├── arg_zero_type.kdl
│ ├── asterisk_in_block_comment.kdl
│ ├── backslash_in_bare_id.kdl
│ ├── bare_arg.kdl
│ ├── bare_emoji.kdl
│ ├── binary.kdl
│ ├── binary_trailing_underscore.kdl
│ ├── binary_underscore.kdl
│ ├── blank_arg_type.kdl
│ ├── blank_node_type.kdl
│ ├── blank_prop_type.kdl
│ ├── block_comment.kdl
│ ├── block_comment_after_node.kdl
│ ├── block_comment_before_node.kdl
│ ├── block_comment_before_node_no_space.kdl
│ ├── block_comment_newline.kdl
│ ├── boolean_arg.kdl
│ ├── boolean_prop.kdl
│ ├── brackets_in_bare_id.kdl
│ ├── chevrons_in_bare_id.kdl
│ ├── comma_in_bare_id.kdl
│ ├── comment_after_arg_type.kdl
│ ├── comment_after_node_type.kdl
│ ├── comment_after_prop_type.kdl
│ ├── comment_in_arg_type.kdl
│ ├── comment_in_node_type.kdl
│ ├── comment_in_prop_type.kdl
│ ├── commented_arg.kdl
│ ├── commented_child.kdl
│ ├── commented_line.kdl
│ ├── commented_node.kdl
│ ├── commented_prop.kdl
│ ├── crlf_between_nodes.kdl
│ ├── dash_dash.kdl
│ ├── dot_but_no_fraction.kdl
│ ├── dot_but_no_fraction_before_exponent.kdl
│ ├── dot_in_exponent.kdl
│ ├── dot_zero.kdl
│ ├── emoji.kdl
│ ├── empty.kdl
│ ├── empty_arg_type.kdl
│ ├── empty_child.kdl
│ ├── empty_child_different_lines.kdl
│ ├── empty_child_same_line.kdl
│ ├── empty_child_whitespace.kdl
│ ├── empty_node_type.kdl
│ ├── empty_prop_type.kdl
│ ├── empty_quoted_node_id.kdl
│ ├── empty_quoted_prop_key.kdl
│ ├── empty_string_arg.kdl
│ ├── esc_newline_in_string.kdl
│ ├── esc_unicode_in_string.kdl
│ ├── escline.kdl
│ ├── escline_comment_node.kdl
│ ├── escline_line_comment.kdl
│ ├── escline_node.kdl
│ ├── false_prefix_in_bare_id.kdl
│ ├── false_prefix_in_prop_key.kdl
│ ├── false_prop_key.kdl
│ ├── hex.kdl
│ ├── hex_int.kdl
│ ├── hex_int_underscores.kdl
│ ├── hex_leading_zero.kdl
│ ├── illegal_char_in_binary.kdl
│ ├── illegal_char_in_hex.kdl
│ ├── illegal_char_in_octal.kdl
│ ├── int_multiple_underscore.kdl
│ ├── just_block_comment.kdl
│ ├── just_child.kdl
│ ├── just_newline.kdl
│ ├── just_node_id.kdl
│ ├── just_space.kdl
│ ├── just_space_in_arg_type.kdl
│ ├── just_space_in_node_type.kdl
│ ├── just_space_in_prop_type.kdl
│ ├── just_type_no_arg.kdl
│ ├── just_type_no_node_id.kdl
│ ├── just_type_no_prop.kdl
│ ├── leading_newline.kdl
│ ├── leading_zero_binary.kdl
│ ├── leading_zero_int.kdl
│ ├── leading_zero_oct.kdl
│ ├── multiline_comment.kdl
│ ├── multiline_nodes.kdl
│ ├── multiline_string.kdl
│ ├── multiple_dots_in_float.kdl
│ ├── multiple_dots_in_float_before_exponent.kdl
│ ├── multiple_es_in_float.kdl
│ ├── multiple_x_in_hex.kdl
│ ├── negative_float.kdl
│ ├── negative_int.kdl
│ ├── nested_block_comment.kdl
│ ├── nested_children.kdl
│ ├── nested_comments.kdl
│ ├── nested_multiline_block_comment.kdl
│ ├── newline_between_nodes.kdl
│ ├── newlines_in_block_comment.kdl
│ ├── no_digits_in_hex.kdl
│ ├── node_false.kdl
│ ├── node_true.kdl
│ ├── node_type.kdl
│ ├── null_arg.kdl
│ ├── null_prefix_in_bare_id.kdl
│ ├── null_prefix_in_prop_key.kdl
│ ├── null_prop.kdl
│ ├── null_prop_key.kdl
│ ├── numeric_arg.kdl
│ ├── numeric_prop.kdl
│ ├── octal.kdl
│ ├── only_cr.kdl
│ ├── only_line_comment.kdl
│ ├── only_line_comment_crlf.kdl
│ ├── only_line_comment_newline.kdl
│ ├── parens_in_bare_id.kdl
│ ├── positive_int.kdl
│ ├── preserve_duplicate_nodes.kdl
│ ├── preserve_node_order.kdl
│ ├── prop_false_type.kdl
│ ├── prop_hex_type.kdl
│ ├── prop_null_type.kdl
│ ├── prop_raw_string_type.kdl
│ ├── prop_string_type.kdl
│ ├── prop_true_type.kdl
│ ├── prop_type.kdl
│ ├── prop_zero_type.kdl
│ ├── question_mark_at_start_of_int.kdl
│ ├── question_mark_before_number.kdl
│ ├── quote_in_bare_id.kdl
│ ├── quoted_arg_type.kdl
│ ├── quoted_node_name.kdl
│ ├── quoted_node_type.kdl
│ ├── quoted_numeric.kdl
│ ├── quoted_prop_name.kdl
│ ├── quoted_prop_type.kdl
│ ├── r_node.kdl
│ ├── raw_arg_type.kdl
│ ├── raw_node_name.kdl
│ ├── raw_node_type.kdl
│ ├── raw_prop_type.kdl
│ ├── raw_string_arg.kdl
│ ├── raw_string_backslash.kdl
│ ├── raw_string_hash_no_esc.kdl
│ ├── raw_string_just_backslash.kdl
│ ├── raw_string_just_quote.kdl
│ ├── raw_string_multiple_hash.kdl
│ ├── raw_string_newline.kdl
│ ├── raw_string_prop.kdl
│ ├── raw_string_quote.kdl
│ ├── repeated_arg.kdl
│ ├── repeated_prop.kdl
│ ├── same_args.kdl
│ ├── same_name_nodes.kdl
│ ├── semicolon_after_child.kdl
│ ├── semicolon_in_child.kdl
│ ├── semicolon_separated.kdl
│ ├── semicolon_separated_nodes.kdl
│ ├── semicolon_terminated.kdl
│ ├── single_arg.kdl
│ ├── single_prop.kdl
│ ├── slash_in_bare_id.kdl
│ ├── slashdash_arg_after_newline_esc.kdl
│ ├── slashdash_arg_before_newline_esc.kdl
│ ├── slashdash_child.kdl
│ ├── slashdash_empty_child.kdl
│ ├── slashdash_full_node.kdl
│ ├── slashdash_in_slashdash.kdl
│ ├── slashdash_negative_number.kdl
│ ├── slashdash_node_in_child.kdl
│ ├── slashdash_node_with_child.kdl
│ ├── slashdash_only_node.kdl
│ ├── slashdash_only_node_with_space.kdl
│ ├── slashdash_prop.kdl
│ ├── slashdash_raw_prop_key.kdl
│ ├── slashdash_repeated_prop.kdl
│ ├── space_after_arg_type.kdl
│ ├── space_after_node_type.kdl
│ ├── space_after_prop_type.kdl
│ ├── space_in_arg_type.kdl
│ ├── space_in_node_type.kdl
│ ├── space_in_prop_type.kdl
│ ├── square_bracket_in_bare_id.kdl
│ ├── string_arg.kdl
│ ├── string_prop.kdl
│ ├── tab_space.kdl
│ ├── trailing_crlf.kdl
│ ├── trailing_underscore_hex.kdl
│ ├── trailing_underscore_octal.kdl
│ ├── true_prefix_in_bare_id.kdl
│ ├── true_prefix_in_prop_key.kdl
│ ├── true_prop_key.kdl
│ ├── two_nodes.kdl
│ ├── type_before_prop_key.kdl
│ ├── unbalanced_raw_hashes.kdl
│ ├── underscore_at_start_of_fraction.kdl
│ ├── underscore_at_start_of_hex.kdl
│ ├── underscore_at_start_of_int.kdl
│ ├── underscore_before_number.kdl
│ ├── underscore_in_float.kdl
│ ├── underscore_in_fraction.kdl
│ ├── underscore_in_int.kdl
│ ├── underscore_in_octal.kdl
│ ├── unusual_bare_id_chars_in_quoted_id.kdl
│ ├── unusual_chars_in_bare_id.kdl
│ ├── zero_arg.kdl
│ ├── zero_float.kdl
│ └── zero_int.kdl
├── jik
│ ├── feed.json
│ ├── web-app.json
│ └── wordpress.json
└── xik
│ ├── website.html
│ └── xml2kdl.xsl
├── test_serializer.nim
└── tests.nim
/.github/workflows/docs.yml:
--------------------------------------------------------------------------------
1 | name: Generate docs
2 |
3 | on:
4 | release:
5 | types: [released]
6 |
7 | jobs:
8 | gen:
9 | name: Generate and deploy docs
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@v2
14 | - uses: iffy/install-nim@v4.1.1
15 | - name: Generate
16 | run: |
17 | nimble install -d -y
18 | nimble docs
19 | - name: Deploy
20 | uses: peaceiris/actions-gh-pages@v3
21 | with:
22 | github_token: ${{ secrets.GITHUB_TOKEN }}
23 | publish_dir: ./docs
24 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 | on: [push, pull_request]
3 | jobs:
4 | build:
5 | strategy:
6 | fail-fast: false
7 | matrix:
8 | os: [ubuntu-latest, windows-latest]
9 |
10 | runs-on: ${{ matrix.os }}
11 |
12 | steps:
13 | - uses: actions/checkout@v2
14 | - uses: iffy/install-nim@v4.5.0
15 | - run: nimble install -y --verbose
16 | - run: nimble test --verbose
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # ignore files with no extention:
2 | *
3 | !*/
4 | !*.*
5 | docs
6 |
7 | trial.*
8 | *.sublime-*
9 | *.out
10 | tests/a.nim
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Patitotective
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # kdl-nim
2 | [](https://nim-lang.org)
3 | [](https://github.com/Patitotective/kdl-nim/actions/workflows/tests.yml)
4 |
5 | [KDL](https://kdl.dev/) Nim implementation.
6 |
7 | ## Installation
8 | ```
9 | nimble install kdl
10 | ```
11 | Or directly from this repository:
12 | ```
13 | nimble install https://github.com/Patitotective/kdl-nim
14 | ```
15 |
16 | ## Features
17 | - Pure Nim with no dependencies.
18 | - Streams support
19 | - Compile-time parsing support
20 | - [Decoder/Desializer](https://patitotective.github.io/kdl-nim/kdl/decoder.html)
21 | - [Encoder/Serializer](https://patitotective.github.io/kdl-nim/kdl/encoder.html)
22 | - [JSON-in-KDL](https://github.com/kdl-org/kdl/blob/main/JSON-IN-KDL.md) ([JiK](https://patitotective.github.io/kdl-nim/kdl/jik.html))
23 | - [XML-in-KDL](https://github.com/kdl-org/kdl/blob/main/XML-IN-KDL.md) ([Xik](https://patitotective.github.io/kdl-nim/kdl/xik.html))
24 | - [Prefs](https://patitotective.github.io/kdl-nim/kdl/prefs.html)
25 |
26 | ## Overview
27 | ```nim
28 | import kdl
29 |
30 | var doc = parseKdl("""
31 | // Nodes can be separated into multiple lines
32 | title \
33 | "Some title"
34 |
35 | // Files must be utf8 encoded!
36 | smile (emoji)"😁" {
37 | upside-down (emoji)"🙃"
38 | }
39 |
40 | // Instead of anonymous nodes, nodes and properties can be wrapped
41 | // in "" for arbitrary node names.
42 | "!@#$@$%Q#$%~@!40" "1.2.3" "!!!!!"=true
43 |
44 | // The following is a legal bare identifier:
45 | foo123~!@#$%^&*.:'|?+ "weeee"
46 |
47 | // And you can also use unicode!
48 | ノード お名前="☜(゚ヮ゚☜)"
49 |
50 | // kdl specifically allows properties and values to be
51 | // interspersed with each other, much like CLI commands.
52 | foo bar=true "baz" quux=false 1 2 3
53 | """) # You can also read files using parseKdlFile("file.kdl")
54 |
55 | # Nodes are represented like:
56 | # type KdlNode* = object
57 | # tag*: Option[string]
58 | # name*: string
59 | # args*: seq[KdlVal]
60 | # props*: Table[string, KdlVal]
61 | # children*: seq[KdlNode]
62 |
63 | assert doc[0].args[0].isString() # title "Some title"
64 |
65 | assert doc[1].args[0] == "😁" # smile node
66 | assert doc[1].args[0].tag.isSome and doc[1].args[0].tag.get == "emoji" # Type annotation
67 | assert doc[1].children[0].args[0] == "🙃" # smile node's upside-down child
68 |
69 | assert doc[2].name == "!@#$@$%Q#$%~@!40"
70 |
71 | assert doc[^1]["quux"] == false
72 |
73 | doc[0].args[0].setString("New title")
74 |
75 | # toKdlNode is a macro that facilitates the creation of `KdlNode`s, there's also toKdl (to create documents) and toKdlVal
76 | doc[1].children[0] = toKdlNode: sunglasses("😎"[emoji], 3.14)
77 |
78 | assert $doc[1].children[0] == "\"sunglasses\" (\"emoji\")\"😎\" 3.14"
79 |
80 | assert doc[1].children[0].args[1].get(uint8) == 3u8 # Converts 3.14 into an uint8
81 |
82 | doc[^1]["bar"].setTo(false) # Same as setBool(false)
83 |
84 | writeFile("doc.kdl", doc)
85 | ```
86 |
87 | ## Docs
88 | Documentation is live at https://patitotective.github.io/kdl-nim/.
89 |
90 | ## TODO
91 | - Implement [KDL schema language](https://github.com/kdl-org/kdl/blob/main/SCHEMA-SPEC.md).
92 | - Implement [KDL query language](https://github.com/kdl-org/kdl/blob/main/QUERY-SPEC.md).
93 | - Support OrderedTables
94 |
95 | ## About
96 | - GitHub: https://github.com/Patitotective/kdl-nim.
97 | - Discord: https://discord.gg/U23ZQMsvwc.
98 | - Docs: https://patitotective.github.io/kdl-nim/.
99 |
100 | Contact me:
101 | - Discord: **Patitotective#0127**.
102 | - Twitter: [@patitotective](https://twitter.com/patitotective).
103 | - Email: **cristobalriaga@gmail.com**.
104 |
--------------------------------------------------------------------------------
/kdl.nimble:
--------------------------------------------------------------------------------
1 | # Package
2 |
3 | version = "2.0.3"
4 | author = "Patitotective"
5 | description = "KDL document language Nim implementation"
6 | license = "MIT"
7 | srcDir = "src"
8 | skipFiles = @["src/kdl/query.nim", "src/kdl/schema.nim"]
9 |
10 | # Dependencies
11 |
12 | requires "nim >= 1.6.0"
13 |
14 | task docs, "Generate documentation":
15 | # We create the prefs module documentation separately because it is not imported in the main kdl file as it's not backed:js friendly
16 | exec "nim doc --outdir:docs/kdl --index:on src/kdl/prefs.nim"
17 | exec "echo \"\" >> docs/prefs.html"
18 |
19 | # Here we make it so when you click 'Index' in the prefs.html file it redirects to theindex.html.
20 | exec "echo \"\" >> docs/kdl/theindex.html"
21 |
22 | exec "nim doc --git.url:https://github.com/Patitotective/kdl-nim --git.commit:main --outdir:docs --project src/kdl.nim"
23 | exec "echo \"\" >> docs/index.html"
24 |
--------------------------------------------------------------------------------
/src/kdl.nim:
--------------------------------------------------------------------------------
1 | ## # kdl-nim
2 | ## kdl-nim is an implementation of the [KDL document language](https://kdl.dev) v1.0.0 in the Nim programming language.
3 | ##
4 | ## ## Installation
5 | ## ```
6 | ## nimble install kdl
7 | ## ```
8 | ##
9 | ## ## Overview
10 | ## ### Parsing KDL
11 | ## kdl-nim parses strings, files or streams into a `KdlDoc` which is a sequence of `KdlNode`s.
12 | ##
13 | ## Each `KdlNode` holds a name, an optional type annotation (tag), zero ore more arguments, zero or more properties and optionally children nodes.
14 | ## Arguments are a sequence of values, while properties are an unordered table of string and values.
15 | ## Arguments and properties' values are represented by the object variant `KdlVal`. `KdlVal` can be of any kind `KString`, `KFloat`, `KBool`, `KNull` or `KInt`.
16 | runnableExamples:
17 | let doc = parseKdl("node (i8)1 null key=\"val\" {child \"abc\" true}")
18 | # You can also read files using parseKdlFile("file.kdl")
19 | assert doc ==
20 | @[
21 | initKNode(
22 | "node",
23 | args = @[initKVal(1, "i8".some), initKNull()],
24 | props = {"key": initKVal("val")}.toTable,
25 | children = @[initKNode("child", args = @[initKVal("abc"), initKVal(true)])],
26 | )
27 | ]
28 |
29 | ## ### Reading nodes
30 | runnableExamples:
31 | let doc = parseKdl("(tag)node 1 null key=\"val\" {child \"abc\" true}")
32 |
33 | assert doc[0].name == "node"
34 | assert doc[0].tag.isSome and doc[0].tag.get == "tag" # Tags are Option[string]
35 | assert doc[0]["key"] == "val" # Same as doc[0].props["key"]
36 | assert doc[0].children[0].args[0] == "abc" # Same as doc[0].children[0].args[0]
37 |
38 | ## ### Reading values
39 | ## Accessing to the inner value of any `KdlVal` can be achieved by using any of the following procedures:
40 | ## - `getString`
41 | ## - `getFloat`
42 | ## - `getBool`
43 | ## - `getInt`
44 | runnableExamples:
45 | let doc = parseKdl("node 1 3.14 {child \"abc\" true}")
46 |
47 | assert doc[0].args[0].getInt() == 1
48 | assert doc[0].args[1].getFloat() == 3.14
49 | assert doc[0].children[0].args[0].getString() == "abc"
50 | assert doc[0].children[0].args[1].getBool() == true
51 |
52 | ## There's also a generic procedure that converts `KdlValue` to the given type, consider this example:
53 | runnableExamples:
54 | let doc = parseKdl("node 1 3.14 255")
55 |
56 | assert doc[0].args[0].get(float32) == 1f
57 | assert doc[0].args[1].get(int) == 3
58 | assert doc[0].args[2].get(uint8) == 255u8
59 | assert doc[0].args[0].get(string) == "1"
60 |
61 | ## ### Setting values
62 | runnableExamples:
63 | var doc = parseKdl("node 1 3.14 {child \"abc\" true}")
64 |
65 | doc[0].args[0].setInt(10)
66 | assert doc[0].args[0] == 10
67 |
68 | doc[0].children[0].args[1].setBool(false)
69 | assert doc[0].children[0].args[1] == false
70 |
71 | # You can also use the generic procedure `setTo`
72 | doc[0].args[0].setTo(3.14)
73 | assert doc[0].args[0] == 3
74 |
75 | doc[0].children[0].args[0].setTo("def")
76 | assert doc[0].children[0].args[0] == "def"
77 |
78 | ## ### Creating KDL
79 | ## To create KDL documents, nodes or values without parsing or object constructors you can use the `toKdlDoc`, `toKdlNode` and`toKdlVal` macros which have a similar syntax to KDL:
80 | runnableExamples:
81 | let doc = toKdlDoc:
82 | node[tag](1, true, nil, key = "val"):
83 | child(3.14[pi])
84 |
85 | person(name = "pat")
86 |
87 | assert doc ==
88 | parseKdl("(tag)node 1 true null key=\"val\" {child (pi)3.14}; person name=\"pat\"")
89 |
90 | let node = toKdlNode:
91 | numbers(1, 2.13, 3.1e-10)
92 | assert node == parseKdl("numbers 1 2.13 3.1e-10")[0]
93 |
94 | assert toKdlVal("abc"[tag]) == parseKdl("node (tag)\"abc\"")[0].args[0]
95 |
96 | ## Furthermore there are the `toKdlArgs` and `toKdlProps` macros, they provide shortcuts for creating a sequence and a table of `KdlVal`:
97 | runnableExamples:
98 | assert toKdlArgs(1, 2[tag], "a") == [1.initKVal, 2.initKVal("tag".some), "a".initKVal]
99 | assert toKdlProps({"a": 1[tag], "b": 2}) ==
100 | {"a": 1.initKVal("tag".some), "b": 2.initKVal}.toTable
101 |
102 | ## ## Compile flags
103 | ## `-d:kdlDecoderAllowHoleyEnums`: to allow converting integers into holey enums.
104 | ## `-d:kdlDecoderNoCaseTransitionError`: to not get a compile error when trying to change a discriminator field from an object variant in an init hook.
105 |
106 | ## ## More
107 | ## Checkout these other useful modules:
108 | ## - [kdl/encoder](kdl/encoder.html) for KDL serializing (Nim objects to KDL)
109 | ## - [kdl/decoder](kdl/decoder.html) for KDL deserializing (KDL to Nim objects)
110 | ## - [kdl/xix](kdl/xik.html) for [XML-in-KDL](https://github.com/kdl-org/kdl/blob/main/XML-IN-KDL.md)
111 | ## - [kdl/jix](kdl/jix.html) for [JSON-in-KDL](https://github.com/kdl-org/kdl/blob/main/JSON-IN-KDL.md)
112 | ## - [kdl/prefs](kdl/prefs.html) for a simple preferences sytem.
113 |
114 | import std/[algorithm, enumerate, strformat, strutils, sequtils, options, tables]
115 |
116 | import kdl/[decoder, encoder, parser, lexer, nodes, types, utils, xik, jik]
117 |
118 | export decoder, encoder, parser, nodes, types
119 | export scanKdl, scanKdlFile, lexer.`$` # lexer
120 |
121 | func indent(s: string, count: Natural, padding = " ", newLine = "\n"): string =
122 | for e, line in enumerate(s.splitLines):
123 | if e > 0:
124 | result.add newLine
125 |
126 | for j in 1 .. count:
127 | result.add padding
128 |
129 | result.add line
130 |
131 | proc prettyIdent*(ident: string): string =
132 | if validToken(ident, tokenIdent):
133 | ident
134 | else:
135 | ident.quoted()
136 |
137 | proc pretty*(val: KdlVal): string =
138 | if val.tag.isSome:
139 | result = &"({val.tag.get.prettyIdent})"
140 |
141 | result.add:
142 | case val.kind
143 | of KFloat:
144 | $val.getFloat()
145 | of KString:
146 | val.getString().quoted()
147 | of KBool:
148 | $val.getBool()
149 | of KNull:
150 | "null"
151 | of KInt:
152 | $val.getInt()
153 | of KEmpty:
154 | "empty"
155 |
156 | proc pretty*(doc: KdlDoc, newLine = true): string
157 |
158 | proc pretty*(node: KdlNode): string =
159 | if node.tag.isSome:
160 | result = &"({node.tag.get.prettyIdent})"
161 |
162 | result.add node.name.prettyIdent()
163 |
164 | if node.args.len > 0:
165 | result.add " "
166 | for e, val in node.args:
167 | if e in 1 .. node.args.high:
168 | result.add " "
169 |
170 | result.add val.pretty()
171 |
172 | if node.props.len > 0:
173 | result.add " "
174 | for e, (key, val) in node.props.pairs.toSeq.sortedByIt(it[0]):
175 | if e in 1 ..< node.props.len:
176 | result.add " "
177 |
178 | result.add &"{key.prettyIdent}={val.pretty}"
179 |
180 | if node.children.len > 0:
181 | result.add " {\p"
182 | result.add indent(node.children.pretty(newLine = false), 4, newLine = "\p")
183 | result.add "\p}"
184 |
185 | proc pretty*(doc: KdlDoc, newLine = true): string =
186 | ## Pretty print a KDL document according to the [translation rules](https://github.com/kdl-org/kdl/tree/main/tests#translation-rules).
187 | ##
188 | ## If `newLine`, inserts a new line at the end.
189 | for e, node in doc:
190 | result.add node.pretty()
191 | if e < doc.high:
192 | result.add "\p"
193 |
194 | if newLine:
195 | result.add "\p"
196 |
197 | proc writeFile*(path: string, doc: KdlDoc, pretty = false) =
198 | ## Writes `doc` to path. Set `pretty` to true to use `pretty` instead of `$`.
199 | if pretty:
200 | writeFile(path, doc.pretty())
201 | else:
202 | writeFile(path, $doc & '\n')
203 |
--------------------------------------------------------------------------------
/src/kdl/encoder.nim:
--------------------------------------------------------------------------------
1 | ## ## Encoder
2 | ## This module implements a serializer for different types and objects into KDL documents, nodes and values:
3 | ## - `char`
4 | ## - `bool`
5 | ## - `Option[T]`
6 | ## - `SomeNumber`
7 | ## - `StringTableRef`
8 | ## - `enum` and `HoleyEnum`
9 | ## - `string` and `cstring`
10 | ## - `KdlVal` (object variant)
11 | ## - `seq[T]` and `array[I, T]`
12 | ## - `set[Ordinal],` `HashSet[A]` and `OrderedSet[A]`
13 | ## - `Table[string, T]` and `OrderedTable[string, T]`
14 | ## - `object`, `ref` and `tuple` (including object variants)
15 | ## - Plus any type you implement.
16 | ##
17 | ## Use `encodeKdlDoc`, `encodeKdlNode` and `encodeKdlVal` correspondingly.
18 | runnableExamples:
19 | import std/options
20 | import kdl
21 |
22 | type
23 | Package = object
24 | name*, version*: string
25 | authors*: Option[seq[string]]
26 | description*, licenseFile*, edition*: Option[string]
27 |
28 | const doc = parseKdl("""
29 | name "kdl"
30 | version "0.0.0"
31 | authors {
32 | - "Kat Marchán "
33 | }
34 | description "kats document language"
35 | licenseFile "LICENSE.md"
36 | edition "2018"
37 | """)
38 |
39 | const obj = Package(
40 | name: "kdl",
41 | version: "0.0.0",
42 | authors: @["Kat Marchán "].some,
43 | description: "kats document language".some,
44 | licenseFile: "LICENSE.md".some,
45 | edition: "2018".some
46 | )
47 |
48 | assert obj.encodeKdlDoc() == doc
49 |
50 | ## ### Custom Encode Hooks
51 | ## If you need to encode a specific type in a specific way you may create a custom encode hooks.
52 | ##
53 | ## To do so, you'll have to overload the `encodeKdl` procedure with the following signature:
54 | ## ```nim
55 | ## proc encodeKdl*(a: MyType, v: var KdlSome)
56 | ## ```
57 | ## Where `KdlSome` is one of `KdlDoc`, `KdlNode` or `KdlVal`.
58 | ## *Note: to understand it better think about it like encoding `a` into `v`, where `v` can be a KDL document, node or value.*
59 | runnableExamples:
60 | import std/times
61 | import kdl
62 |
63 | proc encodeKdl*(a: DateTime, v: var KdlDoc) =
64 | v = @[
65 | initKNode("year", args = @[encodeKdlVal(a.year)]),
66 | initKNode("month", args = @[encodeKdlVal(a.month)]),
67 | initKNode("day", args = @[encodeKdlVal(a.monthday)]),
68 | initKNode("hour", args = @[encodeKdlVal(a.hour)]),
69 | initKNode("minute", args = @[encodeKdlVal(a.minute)]),
70 | initKNode("second", args = @[encodeKdlVal(a.second)]),
71 | initKNode("nanosecond", args = @[encodeKdlVal(a.nanosecond)]),
72 | ]
73 |
74 | const doc = parseKdl("""
75 | "year" 2022
76 | "month" "October"
77 | "day" 15
78 | "hour" 12
79 | "minute" 4
80 | "second" 0
81 | "nanosecond" 0
82 | """)
83 |
84 | assert dateTime(2022, mOct, 15, 12, 04).encodeKdlDoc() == doc
85 |
86 | import std/[typetraits, strformat, enumerate, options, strtabs, tables, sets]
87 | import nodes, utils, types
88 |
89 | # TODO: provide custom encoding for a specific field in a specific object
90 |
91 | # ----- Index -----
92 |
93 | proc encodeKdlDoc*(a: auto): KdlDoc
94 | proc encodeKdlNode*(a: auto, name: string): KdlNode
95 | proc encodeKdlVal*(a: auto): KdlVal
96 | proc encodeKdl*(a: ref, v: var KdlSome)
97 |
98 | proc encodeKdl*(a: KdlDoc, v: var KdlDoc)
99 | proc encodeKdl*(a: List, v: var KdlDoc)
100 | proc encodeKdl*(a: SomeTable[string, auto] or StringTableRef, v: var KdlDoc)
101 | proc encodeKdl*(a: SomeSet[auto], v: var KdlDoc)
102 | proc encodeKdl*[T: Ordinal](a: set[T], v: var KdlDoc)
103 | proc encodeKdl*(a: Object, v: var KdlDoc)
104 |
105 | proc encodeKdl*(a: KdlNode, v: var KdlNode, name: string)
106 | proc encodeKdl*(a: KdlDoc, v: var KdlNode, name: string)
107 | proc encodeKdl*(a: KdlVal, v: var KdlNode, name: string)
108 | proc encodeKdl*(a: List, v: var KdlNode, name: string)
109 | proc encodeKdl*[T: Ordinal](a: set[T], v: var KdlNode, name: string)
110 | proc encodeKdl*(a: SomeTable[string, auto] or SomeSet[auto], v: var KdlNode, name: string)
111 | proc encodeKdl*(a: StringTableRef, v: var KdlNode, name: string)
112 | proc encodeKdl*(a: Option[auto], v: var KdlNode, name: string)
113 | proc encodeKdl*(a: Object, v: var KdlNode, name: string)
114 | proc encodeKdl*(a: auto, v: var KdlNode, name: string)
115 |
116 | proc encodeKdl*(a: KdlVal, v: var KdlVal)
117 | proc encodeKdl*(a: Value, v: var KdlVal)
118 | proc encodeKdl*(a: cstring, v: var KdlVal)
119 | proc encodeKdl*(a: char, v: var KdlVal)
120 | proc encodeKdl*(a: enum, v: var KdlVal)
121 | proc encodeKdl*(a: List, v: var KdlVal)
122 | proc encodeKdl*(a: Option[auto], v: var KdlVal)
123 | # proc encodeKdl*[T: KdlNode or KdlDoc](a: T, v: var KdlVal)
124 |
125 | # ----- KdlSome -----
126 |
127 | proc encodeKdlDoc*(a: auto): KdlDoc =
128 | mixin encodeKdl
129 | encodeKdl(a, result)
130 |
131 | proc encodeKdlNode*(a: auto, name: string): KdlNode =
132 | mixin encodeKdl
133 | encodeKdl(a, result, name)
134 |
135 | proc encodeKdlVal*(a: auto): KdlVal =
136 | mixin encodeKdl
137 | encodeKdl(a, result)
138 |
139 | proc encodeKdl*(a: ref, v: var KdlSome) =
140 | ## Short for `encodeKdl(a[], v)`
141 | encodeKdl(a[], v)
142 |
143 | # ----- KdlDoc -----
144 |
145 | proc encodeKdl*(a: KdlDoc, v: var KdlDoc) =
146 | v = a
147 |
148 | proc encodeKdl*(a: List, v: var KdlDoc) =
149 | v.setLen(a.len)
150 | for e, i in a:
151 | encodeKdl(i, v[e], "-")
152 |
153 | proc encodeKdl*(a: SomeTable[string, auto] or StringTableRef, v: var KdlDoc) =
154 | when a is StringTableRef:
155 | if a.isNil:
156 | v.setLen(0)
157 | return
158 |
159 | v.setLen(a.len)
160 | for e, (key, val) in enumerate(a.pairs):
161 | encodeKdl(val, v[e], key)
162 |
163 | proc encodeKdl*(a: SomeSet[auto], v: var KdlDoc) =
164 | v.setLen(a.len)
165 | for e, i in enumerate(a):
166 | encodeKdl(i, v[e], "-")
167 |
168 | proc encodeKdl*[T: Ordinal](a: set[T], v: var KdlDoc) =
169 | v.setLen(a.len)
170 | for e, i in a:
171 | encodeKdl(i, v[e], "-")
172 |
173 | proc encodeKdl*(a: Object, v: var KdlDoc) =
174 | v.setLen(0)
175 | type T = typeof(a)
176 | when T is tuple and not isNamedTuple(T): # Unnamed tuple
177 | for _, field in a.fieldPairs:
178 | v.add encodeKdlNode(field, "-")
179 | else:
180 | for fieldName, field in a.fieldPairs:
181 | v.add encodeKdlNode(field, fieldName)
182 |
183 | # ----- KdlNode -----
184 |
185 | proc encodeKdl*(a: KdlNode, v: var KdlNode, name: string) =
186 | v = a
187 | v.name = name
188 |
189 | proc encodeKdl*(a: KdlDoc, v: var KdlNode, name: string) =
190 | v = initKNode(name, children = a)
191 |
192 | proc encodeKdl*(a: KdlVal, v: var KdlNode, name: string) =
193 | v = initKNode(name, args = @[a])
194 |
195 | proc encodeKdl*(a: List, v: var KdlNode, name: string) =
196 | v = initKNode(name)
197 | v.children.setLen(a.len)
198 | for e, i in a:
199 | encodeKdl(i, v.children[e], "-")
200 |
201 | proc encodeKdl*[T: Ordinal](a: set[T], v: var KdlNode, name: string) =
202 | v = initKNode(name)
203 | v.args.setLen(a.len)
204 | for e, i in enumerate(a):
205 | v.args[e] = encodeKdlVal(i)
206 |
207 | proc encodeKdl*(a: SomeTable[string, auto] or SomeSet[auto], v: var KdlNode,
208 | name: string) =
209 | v = initKNode(name)
210 | encodeKdl(a, v.children)
211 |
212 | proc encodeKdl*(a: StringTableRef, v: var KdlNode, name: string) =
213 | v = initKNode(name)
214 | if a.isNil: return
215 |
216 | for key, val in a:
217 | v.props[key] = encodeKdlVal(val)
218 |
219 | proc encodeKdl*(a: Option[auto], v: var KdlNode, name: string) =
220 | if a.isNone:
221 | v = initKNode(name)
222 | else:
223 | encodeKdl(a.get, v, name)
224 |
225 | proc encodeKdl*(a: Object, v: var KdlNode, name: string) =
226 | ## Encode `a` fields as `v` children
227 | v = initKNode(name)
228 | encodeKdl(a, v.children)
229 |
230 | proc encodeKdl*(a: auto, v: var KdlNode, name: string) =
231 | ## Encodes a as the first argument of v, thus as a KdlVal
232 | v = initKNode(name, args = @[encodeKdlVal(a)])
233 |
234 | # ----- KdlVal -----
235 |
236 | proc encodeKdl*(a: KdlVal, v: var KdlVal) =
237 | v = a
238 |
239 | proc encodeKdl*(a: Value, v: var KdlVal) =
240 | ## Encodes it using initKVal
241 | v = initKVal(a)
242 |
243 | proc encodeKdl*(a: cstring, v: var KdlVal) =
244 | ## Encodes a as null if a is nil, otherwise encodes it as a string
245 | if a.isNil:
246 | v = initKNull()
247 | else:
248 | v = initKString($a)
249 |
250 | proc encodeKdl*(a: char, v: var KdlVal) =
251 | ## Encodes a as a string
252 | v = initKString($a)
253 |
254 | proc encodeKdl*(a: enum, v: var KdlVal) =
255 | ## Encodes a as a string
256 | v = initKString($a)
257 |
258 | proc encodeKdl*(a: List, v: var KdlVal) =
259 | ## Encodes the first element of a if a only has one element.
260 | check a.len == 1, &"cannot encode {$typeof(a)} to {$typeof(v)} in {a}"
261 | encodeKdl(a[0], v)
262 |
263 | proc encodeKdl*(a: Option[auto], v: var KdlVal) =
264 | ## Encodes a as null if a is none, otherwise encodes a.get
265 | if a.isNone:
266 | v = initKNull()
267 | else:
268 | encodeKdl(a.get, v)
269 |
270 | # proc encodeKdl*[T: KdlNode or KdlDoc](a: T, v: var KdlVal) =
271 | # fail &"{$typeof(a)} not implemented for {$typeof(v)} in {a}"
272 |
273 |
--------------------------------------------------------------------------------
/src/kdl/jik.nim:
--------------------------------------------------------------------------------
1 | ## # JiK
2 | ## This modules implements the JSON-in-KDL (JiK) specification to encode and decode JSON in KDL.
3 | ##
4 | ## Checkout the official specification: https://github.com/kdl-org/kdl/blob/main/JSON-IN-KDL.md.
5 | runnableExamples:
6 | import std/json
7 | import kdl
8 |
9 | let data = """
10 | {"widget": {
11 | "debug": "on",
12 | "window": {
13 | "title": "Sample Konfabulator Widget",
14 | "name": "main_window",
15 | "width": 500,
16 | "height": 500
17 | },
18 | "image": {
19 | "src": "Images/Sun.png",
20 | "name": "sun1",
21 | "hOffset": 250,
22 | "vOffset": 250,
23 | "alignment": "center"
24 | },
25 | "text": {
26 | "data": "Click Here",
27 | "size": 36,
28 | "style": "bold",
29 | "name": "text1",
30 | "hOffset": 250,
31 | "vOffset": 100,
32 | "alignment": "center",
33 | "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
34 | }
35 | }}""".parseJson()
36 | assert data.toKdl() == parseKdl("""
37 | (object)- {
38 | (object)widget {
39 | debug "on"
40 | (object)window {
41 | title "Sample Konfabulator Widget"
42 | name "main_window"
43 | width 500
44 | height 500
45 | }
46 | (object)image {
47 | src "Images/Sun.png"
48 | name "sun1"
49 | hOffset 250
50 | vOffset 250
51 | alignment "center"
52 | }
53 | (object)text {
54 | data "Click Here"
55 | size 36
56 | style "bold"
57 | name "text1"
58 | hOffset 250
59 | vOffset 100
60 | alignment "center"
61 | onMouseUp "sun1.opacity = (sun1.opacity / 100) * 90;"
62 | }
63 | }
64 | }""")[0]
65 |
66 | assert data == data.toKdl().toJson()
67 |
68 | {.used.}
69 |
70 | import std/[json, sets]
71 | import nodes, types
72 |
73 |
74 | proc toKVal(node: JsonNode): KdlVal =
75 | assert node.kind in {JString, JInt, JFloat, JBool, JNull}
76 |
77 | case node.kind
78 | of JString:
79 | result = initKVal(node.getStr)
80 | of JInt:
81 | result = initKVal(node.getInt)
82 | of JFloat:
83 | result = initKVal(node.getFloat)
84 | of JBool:
85 | result = initKVal(node.getBool)
86 | of JNull:
87 | result = initKNull()
88 | else: discard
89 |
90 | proc toKArray(node: JsonNode): KdlDoc
91 | proc toKdl*(node: JsonNode, name = "-"): KdlNode
92 |
93 | proc toKObject(node: JsonNode): KdlDoc =
94 | assert node.kind == JObject
95 |
96 | for key, val in node:
97 | result.add val.toKdl(key)
98 |
99 | proc toKArray(node: JsonNode): KdlDoc =
100 | assert node.kind == JArray
101 |
102 | for ele in node:
103 | result.add ele.toKdl()
104 |
105 | proc toKdl*(node: JsonNode, name = "-"): KdlNode =
106 | ## Converts node into a KDL node.
107 | case node.kind
108 | of JObject:
109 | initKNode(name, "object".some, children = node.toKObject)
110 | of JArray:
111 | initKNode(name, "array".some, children = node.toKArray)
112 | else:
113 | initKNode(name, args = [node.toKVal])
114 |
115 | proc toJVal(val: KdlVal): JsonNode =
116 | case val.kind
117 | of KString:
118 | newJString(val.getString)
119 | of KFloat:
120 | newJFloat(val.getFloat)
121 | of KBool:
122 | newJBool(val.getBool)
123 | of KNull:
124 | newJNull()
125 | of KInt:
126 | newJInt(val.getInt)
127 | of KEmpty:
128 | nil
129 |
130 | proc toJson*(node: KdlNode): JsonNode
131 |
132 | proc toJObject(node: KdlNode): JsonNode =
133 | result = newJObject()
134 |
135 | for key, val in node.props:
136 | result[key] = val.toJVal()
137 |
138 | for child in node.children:
139 | result[child.name] = child.toJson()
140 |
141 | proc toJArray(node: KdlNode): JsonNode =
142 | result = newJArray()
143 |
144 | for arg in node.args:
145 | result.add arg.toJVal()
146 |
147 | for child in node.children:
148 | result.add child.toJson()
149 |
150 | proc toJson*(node: KdlNode): JsonNode =
151 | ## Converts node into its JSON representation.
152 | let tag = node.tag.get("")
153 | if tag == "array":
154 | node.toJArray()
155 | elif tag == "object":
156 | node.toJObject()
157 | elif node.args.len == 1 and node.props.len == 0 and node.children.len == 0:
158 | node.args[0].toJVal()
159 | elif node.props.len == 0 and (node.args.len > 0 or node.children.len > 0):
160 | for child in node.children:
161 | if child.name != "-":
162 | raise newException(ValueError, "All node's children must be named - for a JiK Array")
163 |
164 | node.toJArray()
165 | elif node.args.len == 0 and (node.props.len > 0 or node.children.len > 0):
166 | var names = initHashSet[string]()
167 | for key, _ in node.props:
168 | names.incl key
169 |
170 | for child in node.children:
171 | if child.name in names:
172 | raise newException(ValueError, "All node's children must have different names for a JiK Object")
173 |
174 | names.incl child.name
175 |
176 | node.toJObject()
177 | else:
178 | raise newException(ValueError, "Invalid JiK node")
179 |
180 |
--------------------------------------------------------------------------------
/src/kdl/lexer.nim:
--------------------------------------------------------------------------------
1 | import std/[strformat, strutils, unicode, streams, tables, macros]
2 | import utils, types
3 |
4 | type
5 | TokenKind* = enum
6 | tkEmpty = "empty"
7 | tkNull = "null"
8 | tkStar = "star"
9 | tkPlus = "plus"
10 | tkBool = "bool"
11 | tkTilde = "tilde"
12 | tkComma = "comma"
13 | tkCaret = "caret"
14 | tkDollar = "dollar"
15 | tkIdent = "identifier"
16 | tkSemicolon = "semicolon"
17 | tkGreater = "greater_than"
18 | tkSlashDash = "slash_dash"
19 | tkDoublePipe = "double_pipe"
20 | tkLineCont = "line_continuation"
21 | tkEqual = "equal"
22 | tkNotEqual = "not_equal"
23 | tkString = "string"
24 | tkRawString = "raw_string"
25 | tkWhitespace = "whitespace"
26 | tkNewLine = "new_line"
27 | tkOpenPar = "open_parenthesis"
28 | tkClosePar = "close_parenthesis" # Type tagation
29 | tkOpenBra = "open_bracket"
30 | tkCloseBra = "close_bracket" # Children block
31 | tkOpenSqu = "open_square_bracket"
32 | tkCloseSqu = "close_square_bracket"
33 | tkNumFloat = "float_number"
34 | tkNumInt = "integer_number"
35 | tkNumHex = "hexadecimal_number"
36 | tkNumBin = "binary_number"
37 | tkNumOct = "octagonal_number"
38 |
39 | Token* = object
40 | lexeme*: string
41 | start*: int
42 | kind*: TokenKind
43 |
44 | Lexer* = object
45 | case isStream*: bool
46 | of true:
47 | stream*: Stream
48 | else:
49 | source*: string
50 | current*: int
51 | multilineStringsNewLines*: seq[tuple[idx, length: int]]
52 | # Indexes and length of new lines in multiline strings that have to be converted to a single \n
53 | stack*: seq[Token]
54 |
55 | const
56 | nonIdenChars = {'\\', '/', '(', ')', '{', '}', '<', '>', ';', '[', ']', '=', ',', '"'}
57 | nonInitialChars = Digits + nonIdenChars
58 | whitespaces = [
59 | 0x0009, 0x0020, 0x00A0, 0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005,
60 | 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000,
61 | ]
62 | newLines = ["\c\l", "\r", "\n", "\u0085", "\f", "\u2028", "\u2029"]
63 | litMatches = {
64 | "*": tkStar,
65 | "+": tkPlus,
66 | "~": tkTilde,
67 | "^": tkCaret,
68 | ",": tkComma,
69 | "$": tkDollar,
70 | ">": tkGreater,
71 | "null": tkNull,
72 | "true": tkBool,
73 | "false": tkBool,
74 | ";": tkSemicolon,
75 | "/-": tkSlashDash,
76 | "||": tkDoublePipe,
77 | "=": tkEqual,
78 | "!=": tkNotEqual,
79 | "(": tkOpenPar,
80 | ")": tkClosePar,
81 | "{": tkOpenBra,
82 | "}": tkCloseBra,
83 | "[": tkOpenSqu,
84 | "]": tkCloseSqu,
85 | }
86 |
87 | proc `$`*(lexer: Lexer): string =
88 | result =
89 | if lexer.isStream:
90 | &"{(if lexer.stream.atEnd: \"SUCCESS\" else: \"FAIL\")}\n\t"
91 | else:
92 | &"{(if lexer.current == lexer.source.len: \"SUCCESS\" else: \"FAIL\")} {lexer.current}/{lexer.source.len}\n\t"
93 |
94 | for token in lexer.stack:
95 | result.addQuoted(token.lexeme)
96 | result.add(&"({token.kind}) ")
97 |
98 | proc getPos*(lexer: Lexer): int =
99 | if lexer.isStream:
100 | lexer.stream.getPosition()
101 | else:
102 | lexer.current
103 |
104 | proc setPos(lexer: var Lexer, x: int) =
105 | if lexer.isStream:
106 | lexer.stream.setPosition(x)
107 | else:
108 | lexer.current = x
109 |
110 | proc inc(lexer: var Lexer, x = 1) =
111 | lexer.setPos(lexer.getPos() + x)
112 |
113 | proc dec(lexer: var Lexer, x = 1) =
114 | lexer.setPos(lexer.getPos() - x)
115 |
116 | macro lexing(token: TokenKind, body: untyped) =
117 | ## Converts a procedure definition like:
118 | ## ```nim
119 | ## proc foo() {.lexing: tkEmpty.} =
120 | ## echo "hi"
121 | ## ```
122 | ## Into
123 | ## ```nim
124 | ## proc foo(lexer: var Lexer, consume: bool = true, addToStack: bool = true): bool {.discardable.} =
125 | ## let before = getPos(lexer)
126 | ## echo "hi"
127 | ## result = before != getPos(lexer)
128 | ## if not consume:
129 | ## setPos(lexer, before)
130 | ## if result and addToStack: # Only when token is not tkEmpty
131 | ## lexer.add(token, before)
132 | ## ```
133 |
134 | body.expectKind(nnkProcDef)
135 |
136 | body.params[0] = ident"bool" # Return type
137 | body.params.add(newIdentDefs(ident"lexer", newNimNode(nnkVarTy).add(ident"Lexer")))
138 | body.params.add(newIdentDefs(ident"consume", ident"bool", newLit(true)))
139 | body.params.add(newIdentDefs(ident"addToStack", ident"bool", newLit(true)))
140 |
141 | body.addPragma(ident"discardable")
142 |
143 | # Modify the procedure statements list (body)
144 |
145 | body[^1].insert(
146 | 0,
147 | quote do:
148 | let before {.inject.} = getPos(lexer),
149 | )
150 | body[^1].add(
151 | quote do:
152 | result = before != getPos(lexer)
153 | )
154 | body[^1].add(
155 | quote do:
156 | if not consume:
157 | setPos(lexer, before)
158 | )
159 |
160 | if token != bindSym"tkEmpty":
161 | body[^1].add(
162 | quote do:
163 | if result and addToStack:
164 | lexer.add(`token`, before)
165 | )
166 |
167 | result = body
168 |
169 | proc eof(lexer: var Lexer, extra = 0): bool =
170 | let before = lexer.getPos
171 | inc lexer, extra
172 |
173 | result =
174 | if lexer.isStream:
175 | lexer.stream.atEnd
176 | else:
177 | lexer.current >= lexer.source.len
178 |
179 | lexer.setPos before
180 |
181 | proc peek(lexer: var Lexer, next = 0): char =
182 | if not lexer.eof(next):
183 | let before = lexer.getPos
184 | inc lexer, next
185 |
186 | result =
187 | if lexer.isStream:
188 | lexer.stream.peekChar()
189 | else:
190 | lexer.source[lexer.current]
191 |
192 | lexer.setPos before
193 |
194 | proc peekStr(lexer: var Lexer, until: int): string =
195 | if lexer.eof(until - 1):
196 | return
197 |
198 | if lexer.isStream:
199 | lexer.stream.peekStr(until)
200 | else:
201 | lexer.source[lexer.current ..< lexer.current + until]
202 |
203 | proc peek(lexer: var Lexer, x: string): bool =
204 | if not lexer.eof(x.high):
205 | result = lexer.peekStr(x.len) == x
206 |
207 | proc peekRune(lexer: var Lexer): Rune =
208 | if lexer.eof():
209 | return
210 |
211 | if lexer.isStream:
212 | lexer.stream.peekRune()
213 | else:
214 | lexer.source.runeAt(lexer.current)
215 |
216 | proc add(lexer: var Lexer, kind: TokenKind, start: int) =
217 | let before = lexer.getPos()
218 | lexer.setPos start
219 | lexer.stack.add(
220 | Token(kind: kind, lexeme: lexer.peekStr(before - start), start: start)
221 | )
222 | lexer.setPos before
223 |
224 | proc error(lexer: Lexer, msg: string) =
225 | let coord =
226 | if lexer.isStream:
227 | lexer.stream.getCoord(lexer.getPos)
228 | else:
229 | lexer.source.getCoord(lexer.getPos)
230 |
231 | let errorMsg =
232 | if lexer.isStream:
233 | lexer.stream.errorAt(coord)
234 | else:
235 | lexer.source.errorAt(coord)
236 |
237 | raise newException(
238 | KdlLexerError, &"{msg} at {coord.line + 1}:{coord.col + 1}\n{errorMsg.indent(2)}\n"
239 | )
240 |
241 | proc literal(lexer: var Lexer, lit: string, consume = true): bool {.discardable.} =
242 | result = lexer.peek(lit)
243 | if result and consume:
244 | lexer.inc lit.len
245 |
246 | proc skipWhile(lexer: var Lexer, x: set[char]): int {.discardable.} =
247 | while not lexer.eof() and lexer.peek() in x:
248 | inc result
249 | inc lexer
250 |
251 | proc tokenNewLine*() {.lexing: tkNewLine.} =
252 | for nl in newLines:
253 | if lexer.peek(nl):
254 | lexer.inc nl.len
255 | break
256 |
257 | proc tokenNumWhole() {.lexing: tkEmpty.} =
258 | if lexer.peek() in {'-', '+'}:
259 | inc lexer
260 |
261 | if lexer.peek() notin Digits:
262 | lexer.setPos before
263 | return
264 |
265 | inc lexer
266 |
267 | lexer.skipWhile(Digits + {'_'})
268 |
269 | proc tokenNumExp(lexer: var Lexer) =
270 | if lexer.peek().toLowerAscii() != 'e':
271 | return
272 |
273 | inc lexer
274 |
275 | if lexer.peek() in {'-', '+'}:
276 | inc lexer
277 |
278 | if lexer.peek() notin Digits:
279 | lexer.error "Expected one or more digits"
280 |
281 | lexer.skipWhile(Digits + {'_'})
282 |
283 | proc tokenNumFloat() {.lexing: tkNumFloat.} =
284 | if not lexer.tokenNumWhole():
285 | lexer.setPos before
286 | return
287 |
288 | if lexer.peek() == '.':
289 | inc lexer
290 |
291 | if lexer.peek() notin Digits:
292 | lexer.error "Expected one or more digits"
293 |
294 | lexer.skipWhile(Digits + {'_'})
295 |
296 | if lexer.peek().toLowerAscii() == 'e':
297 | lexer.tokenNumExp()
298 | elif lexer.peek().toLowerAscii() == 'e':
299 | lexer.tokenNumExp()
300 | else:
301 | lexer.setPos before
302 | return
303 |
304 | proc tokenNumInt*() {.lexing: tkNumInt.} =
305 | lexer.tokenNumWhole()
306 |
307 | proc tokenNumBin*() {.lexing: tkNumBin.} =
308 | if lexer.peek("0b"):
309 | lexer.inc 2
310 | if lexer.peek() notin {'0', '1'}:
311 | lexer.error "Expected one or more binary digits"
312 |
313 | lexer.skipWhile({'0', '1', '_'})
314 |
315 | proc tokenNumHex*() {.lexing: tkNumHex.} =
316 | if lexer.peek("0x"):
317 | lexer.inc 2
318 | if lexer.peek() notin HexDigits:
319 | lexer.error "Expected one or more octal digits"
320 |
321 | lexer.skipWhile(HexDigits + {'_'})
322 |
323 | proc tokenNumOct*() {.lexing: tkNumOct.} =
324 | if lexer.peek("0o"):
325 | lexer.inc 2
326 | if lexer.peek() notin {'0' .. '7'}:
327 | lexer.error "Expected one or more octal digits"
328 |
329 | lexer.skipWhile({'0' .. '7', '_'})
330 |
331 | proc tokenStringBody(lexer: var Lexer, raw = false) =
332 | let before = lexer.getPos()
333 |
334 | if raw:
335 | if lexer.peek() != 'r':
336 | return
337 |
338 | inc lexer
339 |
340 | let hashes = lexer.skipWhile({'#'})
341 |
342 | if lexer.peek() != '"':
343 | lexer.setPos before
344 | return
345 |
346 | inc lexer
347 |
348 | var terminated = false
349 |
350 | while not lexer.eof():
351 | let before = lexer.getPos()
352 | if lexer.tokenNewLine(addToStack = false):
353 | lexer.multilineStringsNewLines.add((before, lexer.getPos() - before))
354 | continue
355 |
356 | case lexer.peek()
357 | of '\\':
358 | if raw:
359 | inc lexer
360 | continue
361 |
362 | let next = lexer.peek(1)
363 | if next != 'u' and next notin escapeTable:
364 | lexer.error &"Invalid escape '{next}'"
365 |
366 | lexer.inc 2
367 |
368 | if next == 'u':
369 | if lexer.peek() != '{':
370 | lexer.error "Expected opening bracket '{'"
371 |
372 | inc lexer
373 |
374 | let digits = lexer.skipWhile(HexDigits)
375 | if digits notin 1 .. 6:
376 | lexer.error &"Expected 1-6 hexadecimal digits but found {digits}"
377 |
378 | if lexer.peek() != '}':
379 | lexer.error "Expected closing bracket '}'"
380 | of '"':
381 | inc lexer
382 | let endHashes = lexer.skipWhile({'#'})
383 | if not raw or hashes == 0 or endHashes == hashes:
384 | terminated = true
385 | break
386 | elif endHashes > hashes:
387 | lexer.error &"Expected {hashes} hashes but found {endHashes}"
388 | else:
389 | inc lexer
390 |
391 | if not terminated:
392 | lexer.error "Unterminated string"
393 |
394 | proc tokenString*() {.lexing: tkString.} =
395 | lexer.tokenStringBody()
396 |
397 | proc tokenRawString*() {.lexing: tkRawString.} =
398 | lexer.tokenStringBody(raw = true)
399 |
400 | proc tokenMultiLineComment*() {.lexing: tkEmpty.} =
401 | if not lexer.peek("/*"):
402 | return
403 |
404 | lexer.inc 2
405 |
406 | var nested = 1
407 |
408 | while not lexer.eof() and nested > 0:
409 | if lexer.peek("*/"):
410 | dec nested
411 | lexer.inc 2
412 | elif lexer.peek("/*"):
413 | inc nested
414 | lexer.inc 2
415 | else:
416 | inc lexer
417 |
418 | if nested > 0:
419 | lexer.error "Expected end of multi-line comment"
420 |
421 | proc tokenWhitespace*() {.lexing: tkWhitespace.} =
422 | if not lexer.eof() and (let rune = lexer.peekRune(); rune.int in whitespaces):
423 | lexer.inc rune.size
424 | else:
425 | lexer.tokenMultiLineComment()
426 |
427 | proc skipWhitespaces*() {.lexing: tkEmpty.} =
428 | while lexer.tokenWhitespace():
429 | discard
430 |
431 | proc tokenIdent*() {.lexing: tkIdent.} =
432 | if lexer.eof() or lexer.peek() in nonInitialChars:
433 | return
434 |
435 | # Check the identifier is similar to a boolean, null or number, and if it is it should follow the EOF, a whitespace, a new line or any non-ident char in order to be discarded.
436 | if (
437 | lexer.literal("true") or lexer.literal("false") or lexer.literal("null") or
438 | lexer.tokenNumHex(addToStack = false) or lexer.tokenNumBin(addToStack = false) or
439 | lexer.tokenNumOct(addToStack = false) or lexer.tokenNumFloat(addToStack = false) or
440 | lexer.tokenNumInt(addToStack = false)
441 | ):
442 | if (
443 | lexer.eof() or lexer.tokenWhitespace(addToStack = false) or
444 | lexer.tokenNewLine(addToStack = false) or lexer.peek() in nonIdenChars
445 | ):
446 | lexer.setPos before
447 | return
448 |
449 | block outer:
450 | while not lexer.eof() or not lexer.tokenWhitespace(consume = false) or
451 | not lexer.tokenNewLine(consume = false):
452 | let rune = lexer.peekRune()
453 | if rune.int <= 0x20 or rune.int > 0x10FFFF:
454 | break
455 |
456 | for c in nonIdenChars:
457 | if rune == Rune(c):
458 | break outer
459 |
460 | lexer.inc rune.size
461 |
462 | proc tokenSingleLineComment*() {.lexing: tkEmpty.} =
463 | if not lexer.peek("//"):
464 | return
465 |
466 | lexer.inc 2
467 |
468 | while not lexer.eof(): # Consume until a new line or EOF
469 | if lexer.tokenNewLine(addToStack = addToStack):
470 | break
471 |
472 | inc lexer
473 |
474 | proc tokenLineCont*() {.lexing: tkLineCont.} =
475 | if lexer.peek() != '\\':
476 | return
477 |
478 | inc lexer
479 |
480 | lexer.skipwhitespaces()
481 | if not lexer.tokenSingleLineComment(addToStack = false) and
482 | not lexer.tokenNewLine(addToStack = false):
483 | lexer.error "Expected a new line"
484 |
485 | proc tokenLitMatches() {.lexing: tkEmpty.} =
486 | ## Tries to match any of the litMatches literals.
487 | for (lit, kind) in litMatches:
488 | if lexer.literal(lit):
489 | lexer.add(kind, before)
490 | break
491 |
492 | proc validToken*(
493 | source: sink string,
494 | token: proc(lexer: var Lexer, consume = true, addToStack = true): bool,
495 | ): bool =
496 | var lexer = Lexer(isStream: true, stream: newStringStream(source))
497 |
498 | try:
499 | result = lexer.token() and lexer.eof()
500 | except KdlLexerError:
501 | return
502 |
503 | proc scanKdl*(lexer: var Lexer) =
504 | const choices = [
505 | tokenWhitespace, tokenNewLine, tokenLineCont, tokenSingleLineComment,
506 | tokenRawString, tokenString, tokenIdent, tokenNumHex, tokenNumBin, tokenNumOct,
507 | tokenNumFloat, tokenNumInt, tokenLitMatches,
508 | ]
509 |
510 | while not lexer.eof():
511 | var anyMatch = false
512 |
513 | for choice in choices:
514 | if lexer.choice():
515 | anyMatch = true
516 | break
517 |
518 | if not anyMatch:
519 | lexer.error "Could not match any pattern"
520 |
521 | proc scanKdl*(source: string, start = 0): Lexer =
522 | result = Lexer(isStream: false, source: source, current: start)
523 | result.scanKdl()
524 |
525 | proc scanKdlFile*(path: string): Lexer =
526 | scanKdl(readFile(path))
527 |
528 | proc scanKdl*(stream: sink Stream): Lexer =
529 | result = Lexer(isStream: true, stream: stream)
530 | defer:
531 | result.stream.close()
532 | result.scanKdl()
533 |
534 | proc scanKdlStream*(source: sink string): Lexer =
535 | scanKdl(newStringStream(source))
536 |
537 | proc scanKdlFileStream*(path: string): Lexer =
538 | scanKdl(openFileStream(path))
539 |
--------------------------------------------------------------------------------
/src/kdl/nodes.nim:
--------------------------------------------------------------------------------
1 | ## This module implements initializers, comparision, getters, setters, operatores and macros to manipilate `KdlVal`, `KdlNode` and `KdlDoc`.
2 | import std/[strformat, strutils, options, tables, macros]
3 |
4 | import types, utils
5 |
6 | export options, tables
7 |
8 | # ----- Initializers -----
9 |
10 | proc initKNode*(
11 | name: string,
12 | tag = string.none,
13 | args: openarray[KdlVal] = newSeq[KdlVal](),
14 | props = initTable[string, KdlVal](),
15 | children: openarray[KdlNode] = newSeq[KdlNode](),
16 | ): KdlNode =
17 | KdlNode(tag: tag, name: name, args: @args, props: props, children: @children)
18 |
19 | proc initKVal*(val: string, tag = string.none): KdlVal =
20 | KdlVal(tag: tag, kind: KString, str: val)
21 |
22 | proc initKVal*(val: SomeFloat, tag = string.none): KdlVal =
23 | KdlVal(tag: tag, kind: KFloat, fnum: val.float)
24 |
25 | proc initKVal*(val: bool, tag = string.none): KdlVal =
26 | KdlVal(tag: tag, kind: KBool, boolean: val)
27 |
28 | proc initKVal*(val: SomeInteger, tag = string.none): KdlVal =
29 | KdlVal(tag: tag, kind: KInt, num: val.int64)
30 |
31 | proc initKVal*(val: typeof(nil), tag = string.none): KdlVal =
32 | KdlVal(tag: tag, kind: KNull)
33 |
34 | proc initKVal*(val: KdlVal): KdlVal =
35 | val
36 |
37 | proc initKString*(val = string.default, tag = string.none): KdlVal =
38 | initKVal(val, tag)
39 |
40 | proc initKFloat*(val: SomeFloat = float.default, tag = string.none): KdlVal =
41 | initKVal(val.float, tag)
42 |
43 | proc initKBool*(val = bool.default, tag = string.none): KdlVal =
44 | initKVal(val, tag)
45 |
46 | proc initKNull*(tag = string.none): KdlVal =
47 | KdlVal(tag: tag, kind: KNull)
48 |
49 | proc initKInt*(val: SomeInteger = int64.default, tag = string.none): KdlVal =
50 | initKVal(val.int64, tag)
51 |
52 | # ----- Comparisions -----
53 |
54 | proc isString*(val: KdlVal): bool =
55 | val.kind == KString
56 |
57 | proc isFloat*(val: KdlVal): bool =
58 | val.kind == KFloat
59 |
60 | proc isBool*(val: KdlVal): bool =
61 | val.kind == KBool
62 |
63 | proc isInt*(val: KdlVal): bool =
64 | val.kind == KInt
65 |
66 | proc isNull*(val: KdlVal): bool =
67 | val.kind == KNull
68 |
69 | proc isEmpty*(val: KdlVal): bool =
70 | val.kind == KEmpty
71 |
72 | # ----- Getters -----
73 |
74 | proc getString*(val: KdlVal): string =
75 | check val.isString()
76 | val.str
77 |
78 | proc getFloat*(val: KdlVal): float =
79 | check val.isFloat()
80 | val.fnum
81 |
82 | proc getBool*(val: KdlVal): bool =
83 | check val.isBool()
84 | val.boolean
85 |
86 | proc getInt*(val: KdlVal): int64 =
87 | check val.isInt()
88 | val.num
89 |
90 | proc get*[T: Value](val: KdlVal, x: typedesc[T]): T =
91 | ## When x is string, stringifies val using `$`.
92 | ## when x is SomeNumber, converts val to x.
93 | runnableExamples:
94 | let val = initKFloat(3.14)
95 |
96 | assert val.get(int) == 3
97 | assert val.get(uint) == 3u
98 | assert val.get(float) == 3.14
99 | assert val.get(float32) == 3.14f
100 | assert val.get(range[0f .. 4f]) == 3.14f
101 | assert val.get(string) == "3.14"
102 |
103 | when T is string:
104 | result =
105 | case val.kind
106 | of KFloat:
107 | $val.getFloat()
108 | of KString:
109 | val.getString()
110 | of KBool:
111 | $val.getBool()
112 | of KNull:
113 | "null"
114 | of KInt:
115 | $val.getInt()
116 | of KEmpty:
117 | "empty"
118 | elif T is SomeNumber or T is range:
119 | check val.isFloat or val.isInt
120 |
121 | result =
122 | if val.isInt:
123 | T(val.getInt)
124 | else:
125 | T(val.getFloat)
126 | elif T is bool:
127 | check val.isBool
128 |
129 | result = val.getBool
130 | else:
131 | {.error: "get is not implemented for " & $typeof(T).}
132 |
133 | # ----- Setters -----
134 |
135 | proc setString*(val: var KdlVal, x: string) =
136 | check val.isString()
137 | val.str = x
138 |
139 | proc setFloat*(val: var KdlVal, x: SomeFloat) =
140 | check val.isFloat()
141 | val.fnum = x
142 |
143 | proc setBool*(val: var KdlVal, x: bool) =
144 | check val.isBool()
145 | val.boolean = x
146 |
147 | proc setInt*(val: var KdlVal, x: SomeInteger) =
148 | check val.isInt()
149 | val.num = x
150 |
151 | proc setTo*[T: Value](val: var KdlVal, x: T) =
152 | ## Tries to set val to x, raises an error when types are not compatible.
153 | runnableExamples:
154 | var val = initKFloat(3.14)
155 |
156 | val.setTo(100u8)
157 |
158 | assert val.getFloat() == 100
159 |
160 | val.setTo(20.12e2f)
161 |
162 | assert val.get(float32) == 20.12e2f
163 |
164 | when T is string:
165 | val.setString(x)
166 | elif T is SomeNumber:
167 | if val.isInt:
168 | val.setInt(x.int64)
169 | else:
170 | val.setFloat(x.float)
171 | elif T is bool:
172 | val.setBool(x)
173 |
174 | # ----- Operators -----
175 |
176 | proc `$`*(val: KdlVal): string =
177 | ## Returns "(tag)val"
178 | if val.tag.isSome:
179 | result = &"({val.tag.get.quoted})"
180 |
181 | result.add:
182 | case val.kind
183 | of KFloat:
184 | $val.getFloat()
185 | of KString:
186 | val.getString().quoted
187 | of KBool:
188 | $val.getBool()
189 | of KNull:
190 | "null"
191 | of KInt:
192 | $val.getInt()
193 | of KEmpty:
194 | "empty"
195 |
196 | proc inline*(doc: KdlDoc): string
197 |
198 | proc inline*(node: KdlNode): string =
199 | ## Returns node's single-line representation
200 | if node.tag.isSome:
201 | result = &"({node.tag.get.quoted})"
202 |
203 | result.add node.name.quoted()
204 |
205 | if node.args.len > 0:
206 | result.add " "
207 | for e, val in node.args:
208 | if e in 1 .. node.args.high:
209 | result.add " "
210 |
211 | result.add $val
212 |
213 | if node.props.len > 0:
214 | result.add " "
215 | var count = 0
216 | for key, val in node.props:
217 | if count in 1 ..< node.props.len:
218 | result.add " "
219 |
220 | result.add &"{key.quoted}={val}"
221 |
222 | inc count
223 |
224 | if node.children.len > 0:
225 | result.add " { "
226 | result.add inline(node.children)
227 | result.add " }"
228 |
229 | proc inline*(doc: KdlDoc): string =
230 | ## Returns doc's single-line representation
231 | for e, node in doc:
232 | result.add $node
233 | if e < doc.high:
234 | result.add "; "
235 |
236 | proc `$`*(doc: KdlDoc): string
237 |
238 | proc `$`*(node: KdlNode): string =
239 | ## The result is always valid KDL.
240 |
241 | if node.tag.isSome:
242 | result = &"({node.tag.get.quoted})"
243 |
244 | result.add node.name.quoted()
245 |
246 | if node.args.len > 0:
247 | result.add " "
248 | for e, val in node.args:
249 | if e in 1 .. node.args.high:
250 | result.add " "
251 |
252 | result.add $val
253 |
254 | if node.props.len > 0:
255 | result.add " "
256 | var count = 0
257 | for key, val in node.props:
258 | if count in 1 ..< node.props.len:
259 | result.add " "
260 |
261 | result.add &"{key.quoted}={val}"
262 |
263 | inc count
264 |
265 | if node.children.len > 0:
266 | result.add " {\n"
267 | result.add indent($node.children, 2)
268 | result.add "\n}"
269 |
270 | proc `$`*(doc: KdlDoc): string =
271 | ## The result is always valid KDL.
272 | for e, node in doc:
273 | result.add $node
274 | if e < doc.high:
275 | result.add "\n"
276 |
277 | proc `==`*(val1, val2: KdlVal): bool =
278 | ## Checks if val1 and val2 have the same value. They must be of the same kind.
279 |
280 | check val1.kind == val2.kind
281 |
282 | case val1.kind
283 | of KString:
284 | val1.getString() == val2.getString()
285 | of KFloat:
286 | val1.getFloat() == val2.getFloat()
287 | of KBool:
288 | val1.getBool() == val2.getBool()
289 | of KNull, KEmpty:
290 | true
291 | of KInt:
292 | val1.getInt() == val2.getInt()
293 |
294 | proc `==`*[T: Value](val: KdlVal, x: T): bool =
295 | ## Checks if val is x, raises an error when they are not comparable.
296 | runnableExamples:
297 | assert initKVal("a") == "a"
298 | assert initKVal(1) == 1
299 | assert initKVal(true) == true
300 |
301 | when T is string:
302 | check val.isString
303 | result = val.getString() == x
304 | elif T is SomeNumber:
305 | check val.isFloat or val.isInt
306 |
307 | result =
308 | if val.isInt:
309 | val.getInt() == x.int64
310 | else:
311 | val.getFloat() == x.float
312 | elif T is bool:
313 | check val.isBool
314 |
315 | result = val.getBool() == x
316 |
317 | func `==`*(node1, node2: KdlNode): bool =
318 | {.cast(noSideEffect).}:
319 | system.`==`(node1, node2)
320 |
321 | proc `[]`*(node: KdlNode, key: string): KdlVal =
322 | ## Gets the value of the key property.
323 | node.props[key]
324 |
325 | proc `[]`*(node: var KdlNode, key: string): var KdlVal = # TODO test
326 | ## Gets the value of the key property.
327 | node.props[key]
328 |
329 | proc `[]=`*(node: var KdlNode, key: string, val: KdlVal) =
330 | ## Sets the key property to val in node.
331 | node.props[key] = val
332 |
333 | proc contains*(node: KdlNode, key: string): bool =
334 | ## Checks if node has the key property.
335 | key in node.props
336 |
337 | proc contains*(node: KdlNode, val: KdlVal): bool =
338 | ## Checks if node has the val argument.
339 | val in node.args
340 |
341 | proc contains*(node: KdlNode, child: KdlNode): bool =
342 | ## Checks if node has the child children.
343 | child in node.children
344 |
345 | proc contains*(doc: KdlDoc, name: string): bool =
346 | ## Checks if doc has a node called name
347 | for node in doc:
348 | if node.name == name:
349 | return true
350 |
351 | proc add*(node: var KdlNode, val: KdlVal) =
352 | ## Adds val to node's arguments.
353 |
354 | node.args.add(val)
355 |
356 | proc add*(node: var KdlNode, child: KdlNode) =
357 | ## Adds child to node's children.
358 |
359 | node.children.add(child)
360 |
361 | proc findFirst*(doc: KdlDoc, name: string): int =
362 | ## Returns the index of the first node called name.
363 | ## Returns -1 when it doesn't exist
364 | result = -1
365 |
366 | for e, node in doc:
367 | if node.name == name:
368 | return e
369 |
370 | proc findLast*(doc: KdlDoc, name: string): int =
371 | ## Returns the index of the last node called name.
372 | ## Returns -1 when it doesn't exist
373 | result = -1
374 | for e in countdown(doc.high, 0):
375 | if doc[e].name == name:
376 | return e
377 |
378 | proc find*(doc: KdlDoc, name: string): seq[KdlNode] =
379 | ## Returns all the nodes called name.
380 | for node in doc:
381 | if node.name == name:
382 | result.add node
383 |
384 | # ----- Macros -----
385 |
386 | const identNodes = {nnkStrLit, nnkRStrLit, nnkTripleStrLit, nnkIdent}
387 |
388 | proc strIdent(node: NimNode): NimNode =
389 | node.expectKind(identNodes)
390 | newStrLitNode(node.strVal)
391 |
392 | proc withTag(body: NimNode): tuple[body, tag: NimNode] =
393 | result.tag = newCall("none", ident"string")
394 |
395 | if body.kind == nnkBracketExpr:
396 | result.body = body[0]
397 | result.tag = newCall("some", body[1].strIdent)
398 | else:
399 | result.body = body
400 |
401 | result.tag = newTree(nnkExprEqExpr, ident"tag", result.tag)
402 |
403 | proc toKdlValImpl(body: NimNode): NimNode =
404 | let (value, tag) = body.withTag()
405 |
406 | newCall("initKVal", value, tag)
407 |
408 | proc toKdlNodeImpl(body: NimNode): NimNode =
409 | var body = body
410 |
411 | if body.kind in identNodes + {nnkBracketExpr}:
412 | let (name, tag) = body.withTag()
413 | return newCall("initKNode", name.strIdent, tag)
414 | elif body.kind == nnkStmtList: # When a node has children it ends up being nnkStmtList
415 | body.expectLen(1)
416 | body = body[0]
417 |
418 | body.expectKind(nnkCall)
419 | body.expectMinLen(2)
420 |
421 | let (name, tag) = body[0].withTag()
422 |
423 | result = newCall("initKNode", name.strIdent, tag)
424 |
425 | var i = 1 # Index to start parsing args and props from (1 by default because )
426 |
427 | let args = newNimNode(nnkBracket)
428 | let props = newNimNode(nnkTableConstr)
429 |
430 | while i < body.len and body[i].kind != nnkStmtList:
431 | if body[i].kind == nnkExprEqExpr:
432 | props.add newTree(nnkExprColonExpr, body[i][0].strIdent, toKdlValImpl(body[i][1]))
433 | else:
434 | args.add newCall("initKVal", toKdlValImpl(body[i]))
435 |
436 | inc i
437 |
438 | result.add newTree(nnkExprEqExpr, ident"args", args)
439 |
440 | if props.len > 0:
441 | result.add newTree(nnkExprEqExpr, ident"props", newDotExpr(props, ident"toTable"))
442 |
443 | if i < body.len: # Children
444 | body[i].expectKind(nnkStmtList)
445 | result.add newTree(nnkExprEqExpr, ident"children", newCall("toKdlDoc", body[i]))
446 |
447 | macro toKdlVal*(body: untyped): KdlVal =
448 | ## Generate a KdlVal from Nim's AST that is somehat similar to KDL's syntax.
449 | ## - For type annotations use a bracket expresion: `node[tag]` instead of `(tag)node`.
450 |
451 | toKdlValImpl(body)
452 |
453 | macro toKdlNode*(body: untyped): KdlNode =
454 | ## Generate a KdlNode from Nim's AST that is somewhat similar to KDL's syntax.
455 | ## - For nodes use call syntax: `node(args, props)`.
456 | ## - For properties use an equal expression: `key=val`.
457 | ## - For children pass a block to a node: `node(args, props): ...`
458 | runnableExamples:
459 | let node = toKdlNode:
460 | numbers(10[u8], 20[i32], myfloat = 1.5[f32]):
461 | strings(
462 | "123e4567-e89b-12d3-a456-426614174000"[uuid],
463 | "2021-02-03"[date],
464 | filter = r"$\d+"[regex],
465 | )
466 | person[author](name = "Alex")
467 | # It is the same as:
468 | # numbers (u8)10 (i32)20 myfloat=(f32)1.5 {
469 | # strings (uuid)"123e4567-e89b-12d3-a456-426614174000" (date)"2021-02-03" filter=(regex)r"$\d+"
470 | # (author)person name="Alex"
471 | # }
472 |
473 | toKdlNodeImpl(body)
474 |
475 | macro toKdlDoc*(body: untyped): KdlDoc =
476 | ## Generate a KdlDoc from Nim's AST that is somewhat similar to KDL's syntax.
477 | ## body has to be an statement list
478 | ##
479 | ## See also [toKdlNode](#toKdlNode.m,untyped).
480 | runnableExamples:
481 | let doc = toKdlDoc:
482 | node
483 | numbers(10[u8], 20[i32], myfloat = 1.5[f32]):
484 | strings(
485 | "123e4567-e89b-12d3-a456-426614174000"[uuid],
486 | "2021-02-03"[date],
487 | filter = r"$\d+"[regex],
488 | )
489 | person[author](name = "Alex")
490 | "i am also a node"
491 | color[RGB](r = 200, b = 100, g = 100)
492 |
493 | body.expectKind nnkStmtList
494 |
495 | let doc = newNimNode(nnkBracket)
496 |
497 | for call in body:
498 | doc.add toKdlNodeImpl(call)
499 |
500 | result = prefix(doc, "@")
501 |
502 | macro toKdlArgs*(args: varargs[untyped]): untyped =
503 | ## Creates an array of `KdlVal`s by calling `initKVal` through `args`.
504 | runnableExamples:
505 | assert toKdlArgs(1, 2, "a"[tag]) ==
506 | [1.initKVal, 2.initKVal, "a".initKVal("tag".some)]
507 | assert initKNode("name", args = toKdlArgs(nil, true, "b")) ==
508 | initKNode("name", args = [initKNull(), true.initKVal, "b".initKVal])
509 |
510 | args.expectKind nnkArgList
511 | result = newNimNode(nnkBracket)
512 | for arg in args:
513 | result.add toKdlValImpl(arg)
514 |
515 | macro toKdlProps*(props: untyped): Table[string, KdlVal] =
516 | ## Creates a `Table[string, KdlVal]` from a array-of-tuples/table-constructor by calling `initKVal` through the values.
517 | runnableExamples:
518 | assert toKdlProps({"a": 1[i8], "b": 2}) ==
519 | {"a": 1.initKVal("i8".some), "b": 2.initKVal}.toTable
520 | assert initKNode("name", props = toKdlProps({"c": nil, "d": true})) ==
521 | initKNode("name", props = {"c": initKNull(), "d": true.initKVal}.toTable)
522 |
523 | props.expectKind nnkTableConstr
524 |
525 | result = newNimNode(nnkTableConstr)
526 | for i in props:
527 | i.expectKind nnkExprColonExpr
528 | result.add newTree(nnkExprColonExpr, i[0], toKdlValImpl(i[1]))
529 |
530 | result = newCall("toTable", result)
531 |
--------------------------------------------------------------------------------
/src/kdl/parser.nim:
--------------------------------------------------------------------------------
1 | import std/[parseutils, strformat, strutils, unicode, options, streams, tables, macros]
2 | import lexer, nodes, types, utils
3 | # TODO: add parseKdlVal
4 | type
5 | None = object
6 |
7 | Parser* = object
8 | case isStream*: bool
9 | of true:
10 | stream*: Stream
11 | else:
12 | source*: string
13 |
14 | multilineStringsNewLines*: seq[tuple[idx, length: int]] # Indexes and length of new lines in multiline strings that have to be converted to a single \n
15 | stack*: seq[Token]
16 | current*: int
17 |
18 | Match[T] = tuple[ok, ignore: bool, val: T]
19 |
20 | const
21 | integers = {tkNumInt, tkNumHex, tkNumBin, tkNumOct}
22 | numbers = integers + {tkNumFloat}
23 | strings = {tkString, tkRawString}
24 |
25 | macro parsing(x: typedesc, body: untyped): untyped =
26 | ## Converts a procedure definition like:
27 | ## ```nim
28 | ## proc foo() {.parsing[T].} =
29 | ## echo "hi"
30 | ## ```
31 | ## Into
32 | ## ```nim
33 | ## proc foo(parser: var Parser, required: bool = true): Match[T] {.discardable.} =
34 | ## let before = getPos(parser)
35 | ## echo "hi"
36 | ## ```
37 |
38 | body.expectKind(nnkProcDef)
39 |
40 | result = body.copyNimTree()
41 |
42 | result.params[0] = nnkBracketExpr.newTree(ident"Match", x) # Return type
43 | result.params.insert(1, newIdentDefs(ident"parser", newNimNode(nnkVarTy).add(ident"Parser")))
44 | result.params.add(newIdentDefs(ident"required", ident"bool", newLit(true)))
45 |
46 | result.addPragma(ident"discardable")
47 |
48 | if result[^1].kind == nnkStmtList:
49 | result[^1].insert(0, quote do:
50 | let before {.inject.} = parser.current
51 | )
52 |
53 | proc eof(parser: Parser, extra = 0): bool =
54 | parser.current + extra >= parser.stack.len
55 |
56 | proc peek(parser: Parser, next = 0): Token =
57 | if not parser.eof(next):
58 | result = parser.stack[parser.current + next]
59 | else:
60 | let token = parser.stack[parser.current - 1]
61 | result = Token(start: token.start + token.lexeme.len)
62 |
63 | proc error(parser: Parser, msg: string) =
64 | let coord =
65 | if parser.isStream:
66 | parser.stream.getCoord(parser.peek().start)
67 | else:
68 | parser.source.getCoord(parser.peek().start)
69 |
70 | let errorMsg =
71 | if parser.isStream:
72 | parser.stream.errorAt(coord)
73 | else:
74 | parser.source.errorAt(coord)
75 |
76 | raise newException(KdlParserError, &"{msg} at {coord.line + 1}:{coord.col + 1}\n{errorMsg.indent(2)}\n")
77 |
78 | proc consume(parser: var Parser, amount = 1) =
79 | parser.current += amount
80 |
81 | template invalid[T](x: Match[T]) =
82 | ## Returns if x is ok
83 | let val = x
84 |
85 | result.ok = val.ok
86 |
87 | if val.ok:
88 | return
89 |
90 | template valid[T](x: Match[T]): T =
91 | ## Returns if x is not ok and gives x.val back
92 | let val = x
93 |
94 | result.ok = val.ok
95 |
96 | if not val.ok:
97 | result.ignore = false
98 | parser.current = before
99 | return
100 |
101 | val.val
102 |
103 | template hasValue[T](match: Match[T]): bool =
104 | let (ok, ignore, val {.inject.}) = match; ok and not ignore
105 |
106 | template setValue[T](x: untyped, match: Match[T]) =
107 | if hasValue match:
108 | x = val
109 |
110 | proc match(x: TokenKind | set[TokenKind]) {.parsing: Token.} =
111 | let token = parser.peek()
112 | let matches =
113 | when x is TokenKind:
114 | token.kind == x
115 | else:
116 | token.kind in x
117 |
118 | if matches:
119 | result.ok = true
120 | result.val = token
121 | parser.consume()
122 | elif required:
123 | when x is TokenKind:
124 | parser.error &"Expected {x} but found {token.kind}"
125 | else:
126 | parser.error &"Expected one of {x} but found {token.kind}"
127 |
128 | proc skipWhile(parser: var Parser, kinds: set[TokenKind]): int {.discardable.} =
129 | while not parser.eof():
130 | if parser.peek().kind in kinds:
131 | parser.consume()
132 | inc result
133 | else:
134 | break
135 |
136 | proc more(kind: TokenKind) {.parsing: None.} =
137 | ## Matches one or more tokens of `kind`
138 | discard valid parser.match(kind, required)
139 | discard parser.skipWhile({kind})
140 |
141 | proc parseNumber(token: Token): KdlVal =
142 | assert token.kind in numbers
143 |
144 | if token.kind in integers:
145 | result = initKInt()
146 |
147 | result.num =
148 | case token.kind
149 | of tkNumInt:
150 | token.lexeme.parseBiggestInt()
151 | of tkNumBin:
152 | token.lexeme.parseBinInt()
153 | of tkNumHex:
154 | token.lexeme.parseHexInt()
155 | of tkNumOct:
156 | token.lexeme.parseOctInt()
157 | else:
158 | 0
159 |
160 | else:
161 | result = initKFloat()
162 | result.fnum = token.lexeme.parseFloat()
163 |
164 | proc escapeString(str: string, x = 0..str.high): string =
165 | var i = x.a
166 | while i <= x.b:
167 | if str[i] == '\\':
168 | inc i # Consume backslash
169 | if str[i] == 'u':
170 | inc i, 2 # Consume u and opening {
171 | var hex: string
172 | inc i, str.parseWhile(hex, HexDigits, i)
173 | result.add Rune(parseHexInt(hex))
174 | else:
175 | result.add escapeTable[str[i]]
176 | else:
177 | result.add str[i]
178 |
179 | inc i
180 |
181 | proc parseString(token: Token, multilineStringsNewLines: seq[(int, int)]): KdlVal =
182 | assert token.kind in strings
183 |
184 | result = initKString()
185 |
186 | var varToken = token
187 | varToken.lexeme = newStringOfCap(token.lexeme.len)
188 |
189 | var i = 0
190 | while i < token.lexeme.len:
191 | let before = i
192 | for (idx, length) in multilineStringsNewLines:
193 | if i + token.start == idx:
194 | varToken.lexeme.add '\n'
195 | i += length
196 |
197 | if i == before:
198 | varToken.lexeme.add token.lexeme[i]
199 | inc i
200 |
201 | if token.kind == tkString:
202 | result.str = escapeString(varToken.lexeme, 1.. `prefs.content.field`
124 | prefs.content.field
125 |
126 | template `[]=`*(prefs: KdlPrefs[auto], field, val): untyped =
127 | ## `prefs[field] = val` -> `prefs.content.field = val`
128 | prefs.content.field = val
129 |
130 | template `{}`*(prefs: KdlPrefs[auto], field): untyped =
131 | ## `prefs{field}` -> `prefs.default.field`
132 | prefs.default.field
133 |
134 |
--------------------------------------------------------------------------------
/src/kdl/query.nim:
--------------------------------------------------------------------------------
1 | import types
2 |
3 | type
4 | Query* = seq[Selector]
5 |
6 | Selector* = seq[NodeFilter]
7 |
8 | Operator* = enum
9 | opEqual # =
10 | opNoEqual # !=
11 | opDescend # >>
12 | opGreater # >
13 | opLess # <
14 | opGreaterEq # >=
15 | opLessEq # <=
16 | opStarts # ^
17 | opEnds # $
18 | opContains # *
19 |
20 | NodeFilter* = object
21 | matchers*: seq[Matcher]
22 | operator*: Operator
23 |
24 | Matcher* = object
25 | accessor*: Accessor
26 | operator*: Operator
27 | value*: KdlVal # Comparision value
28 |
29 | AccessorKind* = enum
30 | Name
31 | Prop
32 | Val
33 | Props
34 | Values
35 |
36 | Accessor* = object
37 | case kind*: AccessorKind
38 | of Prop:
39 | prop*: string
40 | of Val:
41 | index*: Natural
42 | else: discard
43 |
44 | Mapping*[T: Accessor or seq[Accessor]] = T
45 |
46 |
--------------------------------------------------------------------------------
/src/kdl/schema.nim:
--------------------------------------------------------------------------------
1 | # TODO: implement the KDL schema language specification https://github.com/kdl-org/kdl/blob/main/SCHEMA-SPEC.md
2 |
3 | import std/options
4 | import nodes, types
5 |
6 | type
7 | Document* = object
8 | info*: Info
9 | node*: seq[Node] # Zero or more
10 | defs*: Option[Defs]
11 | nodeNames*: Option[Validations]
12 | otherNodesAllowed*: bool
13 | tag*: seq[Tag]
14 | tagNames*: Option[Validations]
15 | otherTagsAllowed*: bool
16 |
17 | Info* = object
18 | title*: seq[Title] # Zero or more
19 | desc*: seq[Description] # Zero or more
20 | author*: seq[Author] # Zero or more
21 | contributor*: seq[Author] # Zero or more
22 | link*: seq[Link] # Zero or more
23 | license*: seq[License] # Zero or more
24 | published*: Option[Published]
25 | modified*: Option[Published]
26 | version*: Option[Version]
27 |
28 | Title* = object
29 | title*: string
30 | lang*: Option[string] # An IETF BCP 47 language tag
31 |
32 | Description* = object
33 | desc*: string
34 | lang*: Option[string] # An IETF BCP 47 language tag
35 |
36 | Author* = object
37 | name*: string
38 | orcid*: Option[string]
39 | links*: seq[Link] # Zero or more
40 |
41 | Link* = object
42 | uri*: string # URI/IRI
43 | rel*: string # "self" or "documentation"
44 | lang*: Option[string] # An IETF BCP 47 language tag
45 |
46 | License* = object
47 | name*: string
48 | spdx*: Option[string] # An SPDX license identifier
49 | links*: seq[Link] # One or more
50 |
51 | Published* = object
52 | date*: string # As a ISO8601 date
53 | time*: Option[string] # An ISO8601 Time to accompany the date
54 |
55 |
56 | Version* = string # SemVer https://github.com/euantorano/semver.nim
57 |
58 | Node* = object
59 | name*: Option[string]
60 | desc*: Option[string]
61 | id*: Option[string] # Unique
62 | refQuery*: Option[string] # KDL Query
63 |
64 | min*, max*: Option[int]
65 | propNames*: Option[Validations]
66 | otherPropsAllowed*: bool
67 | tag*: Validations
68 | prop*: seq[Prop] # Zero or more
69 | value*: seq[Value] # Zero or more
70 | children*: seq[Children] # Zero or more
71 |
72 | Tag* = object
73 | name*: Option[string]
74 | desc*: Option[string]
75 | id*: Option[string] # Unique
76 | refQuery*: Option[string] # KDL Query
77 |
78 | node*: seq[Node] # Zero or more
79 | nodeNames*: Option[Validations]
80 | otherNodesAllowed*: bool
81 |
82 | Prop* = object
83 | key*: Option[string]
84 | desc*: Option[string]
85 | id*: Option[string] # Unique
86 | refQuery*: Option[string] # KDL Query
87 |
88 | required*: Option[bool]
89 | # Any validation node
90 |
91 | Value* = object
92 | desc*: Option[string]
93 | id*: Option[string] # Unique
94 | refQuery*: Option[string] # KDL Query
95 |
96 | min*, max*: Option[int]
97 |
98 | # Any validation node
99 |
100 | Children* = object
101 | desc*: Option[string]
102 | id*: Option[string] # Unique
103 | refQuery*: Option[string] # KDL Query
104 |
105 | node*: seq[Node] # Zero or more
106 | nodeNames*: Option[Validations]
107 | otherNodesAllowed*: bool
108 |
109 | Format = enum
110 | DateTime # ISO8601 date/time format
111 | Time # Time section of ISO8601
112 | Date # Date section of ISO8601
113 | Duration # ISO8601 duration format
114 | Decimal # IEEE 754-2008 decimal string format
115 | Currency # ISO 4217 currency code
116 | Country2 # ISO 3166-1 alpha-2 country code
117 | Country3 # ISO 3166-1 alpha-3 country code
118 | CountrySubdivision # ISO 3166-2 country subdivison code
119 | Email # RFC5302 email address
120 | IdnEmail # RFC6531 internationalized email adress
121 | HostName # RFC1132 internet hostname
122 | IdnHostName # RFC5890 internationalized internet hostname
123 | Ipv4 # RFC2673 dotted-quad IPv4 address
124 | Ipv6 # RFC2373 IPv6 address
125 | Url # RFC3986 URI
126 | UrlReference # RFC3986 URI Reference
127 | Irl # RFC3987 Internationalized Resource Identifier
128 | IrlReference # RFC3987 Internationalized Resource Identifier Reference
129 | UrlTemplate # RFC6570 URI Template
130 | Uuid # RFC4122 UUID
131 | Regex # Regular expression. Specific patterns may be implementation-dependent
132 | Base64 # A Base64-encoded string, denoting arbitrary binary data
133 | KdlQuery # A KDL Query string
134 |
135 | Validations* = ref object
136 | tag*: Validations
137 | `enum`*: seq[KdlVal] # List of allowed values for this property
138 | case kind*: KValKind # Type of the property value
139 | of KString:
140 | pattern*: string # Regex
141 | minLength*, maxLength*: int
142 | format*: Format
143 | of KFloat, KInt:
144 | `%`*: string
145 |
146 | else: discard
147 |
148 | Defs* = object
149 | node*: seq[Node] # Zero or more
150 | tag*: seq[Tag] # Zero or more
151 | prop*: seq[Prop] # Zero or more
152 | value*: seq[Value] # Zero or more
153 | children*: seq[Children] # Zero or more
154 |
155 |
--------------------------------------------------------------------------------
/src/kdl/types.nim:
--------------------------------------------------------------------------------
1 | import std/[options, tables]
2 |
3 | type
4 | KdlError* = object of CatchableError
5 | KdlLexerError* = object of KdlError
6 | KdlParserError* = object of KdlError
7 |
8 | KValKind* = enum
9 | KEmpty,
10 | KString,
11 | KFloat,
12 | KBool,
13 | KNull
14 | KInt,
15 |
16 | KdlVal* = object
17 | tag*: Option[string] # Type annotation
18 |
19 | case kind*: KValKind
20 | of KString:
21 | str*: string
22 | of KFloat:
23 | fnum*: float
24 | of KBool:
25 | boolean*: bool
26 | of KNull, KEmpty:
27 | discard
28 | of KInt:
29 | num*: int64
30 |
31 | KdlProp* = tuple[key: string, val: KdlVal]
32 |
33 | KdlNode* = object
34 | tag*: Option[string]
35 | name*: string
36 | args*: seq[KdlVal]
37 | props*: Table[string, KdlVal]
38 | children*: seq[KdlNode]
39 |
40 | KdlDoc* = seq[KdlNode]
41 |
42 | KdlPrefs*[T] = object
43 | path*: string
44 | default*: T
45 | content*: T
46 |
--------------------------------------------------------------------------------
/src/kdl/utils.nim:
--------------------------------------------------------------------------------
1 | ## Various utilities for internal use in the library.
2 | import std/[strformat, strutils, unicode, streams, tables, macros, sets]
3 |
4 | import types
5 |
6 | type
7 | Coord* = object
8 | line*, col*, idx*: int
9 |
10 | Object* = (
11 | (object or tuple) and not KdlSome and not SomeTable and not List and not Value and
12 | not SomeSet
13 | )
14 | List* = (array or seq)
15 | Value* = (SomeNumber or string or bool) # or range
16 | KdlSome* = (KdlDoc or KdlNode or KdlVal)
17 | SomeTable*[K, V] = (Table[K, V] or OrderedTable[K, V])
18 |
19 | const escapeTable* = {
20 | 'n': "\u000A", # Line Feed
21 | 'r': "\u000D", # Carriage Return
22 | 't': "\u0009", # Character Tabulation (Tab)
23 | '\\': "\u005C", # Reverse Solidus (Backslash)
24 | '/': "\u002F", # Solidus (Forwardslash)
25 | '"': "\u0022", # Quotation Mark (Double Quote)
26 | 'b': "\u0008", # Backspace
27 | 'f': "\u000C", # Form Feed
28 | }.toTable
29 |
30 | template fail*(msg: string) =
31 | raise newException(KdlError, msg)
32 |
33 | template check*(cond: untyped, msg = "") =
34 | if not cond:
35 | let txt = msg
36 | fail astToStr(cond) & " failed" & (if txt.len > 0: ": " & txt else: "")
37 |
38 | proc quoted*(x: string): string =
39 | result.add '"'
40 | var i = 0
41 | while i < x.len:
42 | var isEscape = false
43 | for k, v in escapeTable:
44 | # Don't escape forward slash
45 | if k != '/' and x.continuesWith(v, i):
46 | result.add &"\\{k}"
47 | i.inc v.len
48 | isEscape = true
49 | if not isEscape:
50 | result.add x[i]
51 | i.inc
52 | result.add '"'
53 |
54 | proc cmpIgnoreStyle(a, b: openarray[char], ignoreChars = {'_', '-'}): int =
55 | let aLen = a.len
56 | let bLen = b.len
57 | var i = 0
58 | var j = 0
59 |
60 | while true:
61 | while i < aLen and a[i] in ignoreChars:
62 | inc i
63 | while j < bLen and b[j] in ignoreChars:
64 | inc j
65 | let aa =
66 | if i < aLen:
67 | toLowerAscii(a[i])
68 | else:
69 | '\0'
70 | let bb =
71 | if j < bLen:
72 | toLowerAscii(b[j])
73 | else:
74 | '\0'
75 | result = ord(aa) - ord(bb)
76 | if result != 0:
77 | return result
78 | # the characters are identical:
79 | if i >= aLen:
80 | # both cursors at the end:
81 | if j >= bLen:
82 | return 0
83 | # not yet at the end of 'b':
84 | return -1
85 | elif j >= bLen:
86 | return 1
87 | inc i
88 | inc j
89 |
90 | proc eqIdent*(v, a: openarray[char], ignoreChars = {'_', '-'}): bool =
91 | cmpIgnoreStyle(v, a, ignoreChars) == 0
92 |
93 | # ----- Streams -----
94 |
95 | proc peekRune*(s: Stream): Rune =
96 | let str = s.peekStr(4)
97 | if str.len > 0:
98 | result = str.runeAt(0)
99 |
100 | proc peekLineFromStart*(s: Stream): string =
101 | let before = s.getPosition()
102 | while s.getPosition() > 0:
103 | s.setPosition(s.getPosition() - 1)
104 | if s.peekChar() in Newlines:
105 | s.setPosition(s.getPosition() + 1)
106 | if s.atEnd:
107 | s.setPosition(s.getPosition() - 1)
108 |
109 | break
110 |
111 | result = s.peekLine()
112 | s.setPosition before
113 |
114 | proc peekLineFromStart*(s: string, at: int): string =
115 | let at = if at >= s.len: s.high else: at
116 |
117 | var idx = 0
118 | for i in countdown(at - 1, 0):
119 | if s[i] in Newlines:
120 | idx = i + 1
121 | if idx == s.high:
122 | dec idx
123 | break
124 |
125 | for i in idx .. s.high:
126 | if s[i] in Newlines:
127 | return s[idx ..< i]
128 |
129 | result = s[idx ..^ 1]
130 |
131 | proc getCoord*(s: Stream, i: int): Coord =
132 | let before = s.getPosition()
133 | s.setPosition 0
134 | while s.getPosition() < i:
135 | if (let str = s.peekStr(2); str == "\c\l" or str[0] == '\n'):
136 | inc result.line
137 | result.col = 0
138 | else:
139 | inc result.col
140 |
141 | s.setPosition(s.getPosition() + 1)
142 | inc result.idx
143 |
144 | s.setPosition before
145 |
146 | proc getCoord*(s: string, at: int): Coord =
147 | for i in 0 ..< at:
148 | if s.continuesWith("\c\l", i) or s[i] in Newlines:
149 | inc result.line
150 | result.col = 0
151 | else:
152 | inc result.col
153 |
154 | inc result.idx
155 |
156 | proc errorAt*(s: Stream, coord: Coord): string =
157 | let before = s.getPosition()
158 | s.setPosition coord.idx
159 | let line = s.peekLineFromStart()
160 | s.setPosition before
161 |
162 | let lineNum = &"{coord.line + 1} | "
163 | result.add(&"{lineNum}{line}\n")
164 | result.add(&"{repeat(' ', lineNum.len + coord.col)}^")
165 |
166 | proc errorAt*(s: string, coord: Coord): string =
167 | let line = s.peekLineFromStart(coord.idx)
168 |
169 | let lineNum = &"{coord.line + 1} | "
170 | result.add(&"{lineNum}{line}\n")
171 | result.add(&"{repeat(' ', lineNum.len + coord.col)}^")
172 |
173 | # ----- Object variants -----
174 |
175 | macro isObjVariant*(a: typedesc): bool =
176 | var a = a.getTypeImpl
177 | if a.kind != nnkBracketExpr:
178 | return ident("false")
179 |
180 | let sym = a[1]
181 | let t = sym.getTypeImpl
182 | if t.kind != nnkObjectTy:
183 | return ident("false")
184 |
185 | let t2 = t[2]
186 | if t2.kind != nnkRecList:
187 | return ident("false")
188 |
189 | result = ident("false")
190 |
191 | for ti in t2:
192 | if ti.kind == nnkRecCase:
193 | let key = ti[0][0]
194 | let typ = ti[0][1]
195 |
196 | return ident("true")
197 |
198 | macro getDiscriminants*(a: typedesc): seq[string] =
199 | ## return the discriminant keys
200 | # candidate for std/typetraits
201 | var a = a.getTypeImpl
202 | if a.kind != nnkBracketExpr:
203 | return quote:
204 | newSeq[string]()
205 |
206 | let sym = a[1]
207 | let t = sym.getTypeImpl
208 | if t.kind != nnkObjectTy:
209 | return quote:
210 | newSeq[string]()
211 |
212 | let t2 = t[2]
213 | if t2.kind != nnkRecList:
214 | return quote:
215 | newSeq[string]()
216 |
217 | result = newTree(nnkBracket)
218 |
219 | for ti in t2:
220 | if ti.kind == nnkRecCase:
221 | let key = ti[0][0]
222 | let typ = ti[0][1]
223 | result.add newLit key.strVal
224 |
225 | result =
226 | if result.len > 0:
227 | quote:
228 | @`result`
229 | else:
230 | quote:
231 | newSeq[string]()
232 |
233 | macro initCaseObject*(T: typedesc, discriminatorSetter): untyped =
234 | ## Does the minimum to construct a valid case object `T`.
235 | ## - `discriminatorSetter`: called passing two arguments `(key, typ)` (`key` being the field name and `typ` the field type), last expression should be the value for the field. Only for discriminator fields.
236 | # maybe candidate for std/typetraits
237 |
238 | var a = T.getTypeImpl
239 |
240 | doAssert a.kind == nnkBracketExpr
241 |
242 | let sym = a[1]
243 | let t = sym.getTypeImpl
244 | var t2: NimNode
245 |
246 | case t.kind
247 | of nnkObjectTy:
248 | t2 = t[2]
249 | of nnkRefTy:
250 | t2 = t[0].getTypeImpl[2]
251 | else:
252 | doAssert false, $t.kind
253 | # xxx `nnkPtrTy` could be handled too
254 |
255 | doAssert t2.kind == nnkRecList
256 |
257 | result = newTree(nnkObjConstr)
258 | result.add sym
259 |
260 | for ti in t2:
261 | if ti.kind == nnkRecCase:
262 | let key = ti[0][0]
263 | let typ = ti[0][1]
264 | let key2 = key.strVal
265 | let val = quote:
266 | `discriminatorSetter`(`key2`, typedesc[`typ`])
267 |
268 | result.add newTree(nnkExprColonExpr, key, val)
269 |
270 | template typeofdesc*[T](b: typedesc[T]): untyped =
271 | T
272 |
--------------------------------------------------------------------------------
/src/kdl/xik.nim:
--------------------------------------------------------------------------------
1 | ## # XiK
2 | ## This modules implements the XML-in-KDL (XiK) specification to encode and decode XML in KDL.
3 | ##
4 | ## Checkout the official specification: https://github.com/kdl-org/kdl/blob/main/XML-IN-KDL.md.
5 | runnableExamples:
6 | import std/[xmlparser, xmltree]
7 | import kdl
8 |
9 | const data = """
10 |
11 |
12 | Belgian Waffles
13 | $5.95
14 |
15 | Two of our famous Belgian Waffles with plenty of real maple syrup
16 |
17 | 650
18 |
19 |
20 | Strawberry Belgian Waffles
21 | $7.95
22 |
23 | Light Belgian waffles covered with strawberries and whipped cream
24 |
25 | 900
26 |
27 | """
28 | assert data.parseXml().toKdl() == parseKdl("""
29 | breakfast_menu {
30 | food {
31 | name "Belgian Waffles"
32 | price "$5.95"
33 | description "Two of our famous Belgian Waffles with plenty of real maple syrup\n"
34 | calories "650"
35 | }
36 | food {
37 | name "Strawberry Belgian Waffles"
38 | price "$7.95"
39 | description "Light Belgian waffles covered with strawberries and whipped cream\n"
40 | calories "900"
41 | }
42 | }""")[0]
43 |
44 | assert $data.parseXml() == $data.parseXml().toKdl().toXml()
45 |
46 | {.used.}
47 |
48 | import std/[strtabs, xmltree]
49 | import nodes, types
50 |
51 | proc toKdl*(node: XmlNode, addComments = false): KdlNode =
52 | ## Converts node into its KDL representation.
53 | ## Ignores CDATA nodes, i.e..
54 | ## If `addComments` preserves XML comments as KDL nodes named `!`.
55 |
56 | case node.kind
57 | of xnText, xnVerbatimText:
58 | result = initKNode("-", args = toKdlArgs(node.text))
59 | of xnEntity:
60 | result = initKNode("-", args = toKdlArgs('&' & node.text & ';'))
61 | of xnComment:
62 | if addComments:
63 | result = initKNode("!", args = toKdlArgs(node.text))
64 | of xnElement:
65 | result = initKNode(node.tag)
66 | if not node.attrs.isNil:
67 | for key, val in node.attrs:
68 | result.props[key] = initKVal(val)
69 |
70 | if node.len == 1 and node[0].kind in {xnText, xnEntity, xnVerbatimText}:
71 | result.args.add initKVal(node[0].text)
72 | else:
73 | for child in node:
74 | if addComments or child.kind != xnComment:
75 | result.children.add child.toKdl(addComments)
76 |
77 | of xnCData: discard # According to XiK spec CDATA is discarded
78 |
79 | proc toXmlSingle*(node: KdlNode): XmlNode =
80 | assert node.name.len == 1, "in " & $node
81 | assert node.args.len == 1, "single argument expected in " & $node
82 |
83 | case node.name[0]
84 | of '!':
85 | assert node.args.len == 1 and node.children.len == 0 and node.props.len == 0, "comments must have a single string argument in " & $node
86 | assert node.args[0].isString
87 | newComment(node.args[0].getString)
88 | of '-':
89 | let val = node.args[0].get(string)
90 | if val.len > 1 and val[0] == '&' and val[^1] == ';':
91 | newEntity(val[1..^2])
92 | else:
93 | newText(val)
94 | else:
95 | raise newException(ValueError, "Expected node named '!' or '-' in " & $node)
96 |
97 | proc toXml*(node: KdlNode, addComments = false): XmlNode =
98 | ## Converts node into its XML representation.
99 | ## - If `addComments` preserves comments in elements, if node is a comment ('! "something"') it DOES return it.
100 | runnableExamples:
101 | import std/xmltree
102 | import kdl
103 |
104 | assert parseKdl("! \"comment\"")[0].toXml().kind == xnComment
105 | assert parseKdl("tag { ! \"comment\"; - \"text\" }")[0].toXml().len == 1 # Ignored the comment
106 | assert parseKdl("tag { ! \"comment\"; - \"text\" }")[0].toXml(addComments = true).len == 2 # Added the comment
107 |
108 | assert node.name.len > 0
109 |
110 | if node.name == "!" or node.name == "-":
111 | return toXmlSingle(node)
112 |
113 | assert node.args.len == 0 or (node.args.len == 1 xor node.children.len > 0), "single-argument and children cannot be mixed in " & $node
114 | result = newElement(node.name)
115 |
116 | result.attrs = newStringTable()
117 | for key, val in node.props:
118 | result.attrs[key] = val.get(string) # Stringify the values
119 |
120 | if node.args.len == 1:
121 | result.add newText(node.args[0].get(string))
122 | else:
123 | for child in node.children:
124 | assert child.name.len > 0
125 | if child.name == "!":
126 | if addComments:
127 | result.add toXmlSingle(child)
128 | continue
129 | else:
130 | continue
131 |
132 | if child.name == "-":
133 | result.add toXmlSingle(child)
134 | else:
135 | result.add child.toXml(addComments)
136 |
137 |
--------------------------------------------------------------------------------
/tests/README.md:
--------------------------------------------------------------------------------
1 | # Tests
2 | ```
3 | nimble test
4 | ```
5 |
6 | Tests taken from https://github.com/kdl-org/kdl/tree/main/tests.
7 |
8 | ## Changes
9 | The following tests were modified from the original test suite:
10 | - `input/hex.kdl`: `node 0xabcdef1234567890` -> `node 0x1234567890abcdef`.
11 | - `input/hex_int.kdl`: `node 0xABCDEF0123456789abcdef` -> `node 0x1234567890ABCDEF`.
12 |
13 | The following tests are not compared with `expected_kdl` for float-formatting related casues:
14 | - `input/negative_exponent.kdl`
15 | - `input/no_decimal_exponent.kdl`
16 | - `input/parse_all_arg_types.kdl`
17 | - `input/positive_exponent.kdl`
18 | - `input/prop_float_type.kdl`
19 | - `input/sci_notation_large.kdl`
20 | - `input/sci_notation_small.kdl`
21 | - `input/underscore_in_exponent.kdl`
22 | They are instead prefixed with an underscore (`_`) and checked for successfull parsing.
23 |
24 | - New `examples` folder that is identical to https://github.com/kdl-org/kdl/tree/main/examples, all documents inside it must be parsed with no error.
25 | - New `jik` and `xik` folders that contain files that must successfully converted to KDL and then back to JSON/XML.
26 |
27 |
--------------------------------------------------------------------------------
/tests/config.nims:
--------------------------------------------------------------------------------
1 | switch("path", "$projectDir/../src")
--------------------------------------------------------------------------------
/tests/test_cases/examples/Cargo.kdl:
--------------------------------------------------------------------------------
1 | package {
2 | name "kdl"
3 | version "0.0.0"
4 | description "kat's document language"
5 | authors "Kat Marchán "
6 | license-file "LICENSE.md"
7 | edition "2018"
8 | }
9 |
10 | dependencies {
11 | nom "6.0.1"
12 | thiserror "1.0.22"
13 | }
14 |
--------------------------------------------------------------------------------
/tests/test_cases/examples/ci.kdl:
--------------------------------------------------------------------------------
1 | // This example is a GitHub Action if it used KDL syntax.
2 | // See .github/workflows/ci.yml for the file this was based on.
3 | name "CI"
4 |
5 | on "push" "pull_request"
6 |
7 | env {
8 | RUSTFLAGS "-Dwarnings"
9 | }
10 |
11 | jobs {
12 | fmt_and_docs "Check fmt & build docs" {
13 | runs-on "ubuntu-latest"
14 | steps {
15 | step uses="actions/checkout@v1"
16 | step "Install Rust" uses="actions-rs/toolchain@v1" {
17 | profile "minimal"
18 | toolchain "stable"
19 | components "rustfmt"
20 | override true
21 | }
22 | step "rustfmt" run="cargo fmt --all -- --check"
23 | step "docs" run="cargo doc --no-deps"
24 | }
25 | }
26 | build_and_test "Build & Test" {
27 | runs-on "${{ matrix.os }}"
28 | strategy {
29 | matrix {
30 | rust "1.46.0" "stable"
31 | os "ubuntu-latest" "macOS-latest" "windows-latest"
32 | }
33 | }
34 |
35 | steps {
36 | step uses="actions/checkout@v1"
37 | step "Install Rust" uses="actions-rs/toolchain@v1" {
38 | profile "minimal"
39 | toolchain "${{ matrix.rust }}"
40 | components "clippy"
41 | override true
42 | }
43 | step "Clippy" run="cargo clippy --all -- -D warnings"
44 | step "Run tests" run="cargo test --all --verbose"
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/tests/test_cases/examples/nuget.kdl:
--------------------------------------------------------------------------------
1 | // Based on https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Clients/NuGet.CommandLine/NuGet.CommandLine.csproj
2 | Project {
3 | PropertyGroup {
4 | IsCommandLinePackage true
5 | }
6 |
7 | Import Project=r"$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), 'README.md'))\build\common.props"
8 | Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk"
9 | Import Project="ilmerge.props"
10 |
11 | PropertyGroup {
12 | RootNamespace "NuGet.CommandLine"
13 | AssemblyName "NuGet"
14 | AssemblyTitle "NuGet Command Line"
15 | PackageId "NuGet.CommandLine"
16 | TargetFramework "$(NETFXTargetFramework)"
17 | GenerateDocumentationFile false
18 | Description "NuGet Command Line Interface."
19 | ApplicationManifest "app.manifest"
20 | Shipping true
21 | OutputType "Exe"
22 | ComVisible false
23 | // Pack properties
24 | PackProject true
25 | IncludeBuildOutput false
26 | TargetsForTfmSpecificContentInPackage "$(TargetsForTfmSpecificContentInPackage)" "CreateCommandlineNupkg"
27 | SuppressDependenciesWhenPacking true
28 | DevelopmentDependency true
29 | PackageRequireLicenseAcceptance false
30 | UsePublicApiAnalyzer false
31 | }
32 |
33 | Target Name="CreateCommandlineNupkg" {
34 | ItemGroup {
35 | TfmSpecificPackageFile Include=r"$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.exe" {
36 | PackagePath "tools/"
37 | }
38 | TfmSpecificPackageFile Include=r"$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.pdb" {
39 | PackagePath "tools/"
40 | }
41 | }
42 | }
43 |
44 | ItemGroup Condition="$(DefineConstants.Contains(SIGNED_BUILD))" {
45 | AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo" {
46 | _Parameter1 "NuGet.CommandLine.FuncTest, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293"
47 | }
48 | AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo" {
49 | _Parameter1 "NuGet.CommandLine.Test, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293"
50 | }
51 | }
52 |
53 | ItemGroup Condition="!$(DefineConstants.Contains(SIGNED_BUILD))" {
54 | AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo" {
55 | _Parameter1 "NuGet.CommandLine.FuncTest"
56 | }
57 | AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo" {
58 | _Parameter1 "NuGet.CommandLine.Test"
59 | }
60 | }
61 |
62 | ItemGroup Condition="$(DefineConstants.Contains(SIGNED_BUILD))" {
63 | AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo" {
64 | _Parameter1 "NuGet.CommandLine.Test, PublicKey=002400000480000094000000060200000024000052534131000400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9ad236132102900b723cf980957fc4e177108fc607774f29e8320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d622caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a12436518206dc093344d5ad293"
65 | }
66 | }
67 |
68 | ItemGroup Condition="!$(DefineConstants.Contains(SIGNED_BUILD))" {
69 | AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo" {
70 | _Parameter1 "NuGet.CommandLine.Test"
71 | }
72 | }
73 |
74 | ItemGroup {
75 | Reference Include="Microsoft.Build.Utilities.v4.0"
76 | Reference Include="Microsoft.CSharp"
77 | Reference Include="System"
78 | Reference Include="System.ComponentModel.Composition"
79 | Reference Include="System.ComponentModel.Composition.Registration"
80 | Reference Include="System.ComponentModel.DataAnnotations"
81 | Reference Include="System.IO.Compression"
82 | Reference Include="System.Net.Http"
83 | Reference Include="System.Xml"
84 | Reference Include="System.Xml.Linq"
85 | Reference Include="NuGet.Core" {
86 | HintPath r"$(SolutionPackagesFolder)nuget.core\2.14.0-rtm-832\lib\net40-Client\NuGet.Core.dll"
87 | Aliases "CoreV2"
88 | }
89 | }
90 | ItemGroup {
91 | PackageReference Include="Microsoft.VisualStudio.Setup.Configuration.Interop"
92 | ProjectReference Include=r"$(NuGetCoreSrcDirectory)NuGet.PackageManagement\NuGet.PackageManagement.csproj"
93 | ProjectReference Include=r"$(NuGetCoreSrcDirectory)NuGet.Build.Tasks\NuGet.Build.Tasks.csproj"
94 | }
95 |
96 | ItemGroup {
97 | EmbeddedResource Update="NuGetCommand.resx" {
98 | Generator "ResXFileCodeGenerator"
99 | LastGenOutput "NuGetCommand.Designer.cs"
100 | }
101 | Compile Update="NuGetCommand.Designer.cs" {
102 | DesignTime true
103 | AutoGen true
104 | DependentUpon "NuGetCommand.resx"
105 | }
106 | EmbeddedResource Update="NuGetResources.resx" {
107 | // Strings are shared by other projects, use public strings.
108 | Generator "PublicResXFileCodeGenerator"
109 | LastGenOutput "NuGetResources.Designer.cs"
110 | }
111 | Compile Update="NuGetResources.Designer.cs" {
112 | DesignTime true
113 | AutoGen true
114 | DependentUpon "NuGetResources.resx"
115 | }
116 | }
117 |
118 | ItemGroup {
119 | EmbeddedResource Include=r"$(NuGetCoreSrcDirectory)NuGet.Build.Tasks\NuGet.targets" {
120 | Link "NuGet.targets"
121 | SubType "Designer"
122 | }
123 | }
124 |
125 | // Since we are moving some code and strings from NuGet.CommandLine to NuGet.Commands, we opted to go through normal localization process (build .resources.dll) and then add them to the ILMerged nuget.exe
126 | // This will also be called from CI build, after assemblies are localized, since our test infra takes nuget.exe before Localization
127 | Target Name="ILMergeNuGetExe" \
128 | AfterTargets="Build" \
129 | Condition="'$(BuildingInsideVisualStudio)' != 'true' and '$(SkipILMergeOfNuGetExe)' != 'true'" \
130 | {
131 | PropertyGroup {
132 | // when done after build, no localizedartifacts are built yet, so expected localized artifact count is 0.
133 | ExpectedLocalizedArtifactCount 0 Condition="'$(ExpectedLocalizedArtifactCount)' == ''"
134 | }
135 | ItemGroup {
136 | BuildArtifacts Include=r"$(OutputPath)\*.dll" Exclude="@(MergeExclude)"
137 | // NuGet.exe needs all NuGet.Commands.resources.dll merged in
138 | LocalizedArtifacts Include=r"$(ArtifactsDirectory)\NuGet.Commands\**\$(NETFXTargetFramework)\**\*.resources.dll"
139 | }
140 | Error Text="Build dependencies are inconsistent with mergeinclude specified in ilmerge.props" \
141 | Condition="'@(BuildArtifacts->Count())' != '@(MergeInclude->Count())'"
142 | Error Text="Satellite assemblies count ILMerged into NuGet.exe should be $(ExpectedLocalizedArtifactCount), but was: @(LocalizedArtifacts->Count())" \
143 | Condition="'@(LocalizedArtifacts->Count())' != '$(ExpectedLocalizedArtifactCount)'"
144 | PropertyGroup {
145 | PathToBuiltNuGetExe "$(OutputPath)NuGet.exe"
146 | IlmergeCommand r"$(ILMergeExePath) /lib:$(OutputPath) /out:$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.exe @(MergeAllowDup -> '/allowdup:%(Identity)', ' ') /log:$(OutputPath)IlMergeLog.txt"
147 | IlmergeCommand Condition="Exists($(MS_PFX_PATH))" "$(IlmergeCommand) /delaysign /keyfile:$(MS_PFX_PATH)"
148 | // LocalizedArtifacts need fullpath, since there will be duplicate file names
149 | IlmergeCommand "$(IlmergeCommand) $(PathToBuiltNuGetExe) @(BuildArtifacts->'%(filename)%(extension)', ' ') @(LocalizedArtifacts->'%(fullpath)', ' ')"
150 | }
151 | MakeDir Directories="$(ArtifactsDirectory)$(VsixOutputDirName)"
152 | Exec Command="$(IlmergeCommand)" ContinueOnError="false"
153 | }
154 |
155 | Import Project="$(BuildCommonDirectory)common.targets"
156 | Import Project="$(BuildCommonDirectory)embedinterop.targets"
157 |
158 | // Do nothing. This basically strips away the framework assemblies from the resulting nuspec.
159 | Target Name="_GetFrameworkAssemblyReferences" DependsOnTargets="ResolveReferences"
160 |
161 | Target Name="GetSigningInputs" Returns="@(DllsToSign)" {
162 | ItemGroup {
163 | DllsToSign Include=r"$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.exe" {
164 | StrongName "MsSharedLib72"
165 | Authenticode "Microsoft400"
166 | }
167 | }
168 | }
169 |
170 | Target Name="GetSymbolsToIndex" Returns="@(SymbolsToIndex)" {
171 | ItemGroup {
172 | SymbolsToIndex Include=r"$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.exe"
173 | SymbolsToIndex Include=r"$(ArtifactsDirectory)$(VsixOutputDirName)\NuGet.pdb"
174 | }
175 | }
176 |
177 | Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk"
178 | }
179 |
--------------------------------------------------------------------------------
/tests/test_cases/examples/website.kdl:
--------------------------------------------------------------------------------
1 | !doctype "html"
2 | html lang="en" {
3 | head {
4 | meta charset="utf-8"
5 | meta name="viewport" content="width=device-width, initial-scale=1.0"
6 | meta \
7 | name="description" \
8 | content="kdl is a document language, mostly based on SDLang, with xml-like semantics that looks like you're invoking a bunch of CLI commands!"
9 | title "kdl - Kat's Document Language" // Comment
10 | link rel="stylesheet" href="/styles/global.css"
11 | }
12 | body {
13 | main {
14 | header class="py-10 bg-gray-300" {
15 | h1 class="text-4xl text-center" "kdl - Kat's Document Language"
16 | }
17 | section class="kdl-section" id="description" {
18 | p {
19 | - "kdl is a document language, mostly based on "
20 | a href="https://sdlang.org" "SDLang"
21 | - " with xml-like semantics that looks like you're invoking a bunch of CLI commands"
22 | }
23 | p "It's meant to be used both as a serialization format and a configuration language, and is relatively light on syntax compared to XML."
24 | }
25 | section class="kdl-section" id="design-and-discussion" {
26 | h2 "Design and Discussion"
27 | p {
28 | - "kdl is still extremely new, and discussion about the format should happen over on the "
29 | a href="https://github.com/kdoclang/kdl/discussions" {
30 | - "discussions"
31 | }
32 | - " page in the Github repo. Feel free to jump in and give us your 2 cents!"
33 | }
34 | }
35 | section class="kdl-section" id="design-principles" {
36 | h2 "Design Principles"
37 | ol {
38 | li "Maintainability"
39 | li "Flexibility"
40 | li "Cognitive simplicity and Learnability"
41 | li "Ease of de/serialization"
42 | li "Ease of implementation"
43 | }
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/all_escapes.kdl:
--------------------------------------------------------------------------------
1 | node "\"\\/\b\f\n\r\t"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/all_node_fields.kdl:
--------------------------------------------------------------------------------
1 | node "arg" prop="val" {
2 | inner_node
3 | }
4 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/arg_and_prop_same_name.kdl:
--------------------------------------------------------------------------------
1 | node "arg" arg="val"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/arg_false_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)false
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/arg_float_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)2.5
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/arg_hex_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)16
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/arg_null_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)null
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/arg_raw_string_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)"str"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/arg_string_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)"str"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/arg_true_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)true
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/arg_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)"arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/arg_zero_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/asterisk_in_block_comment.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/bare_emoji.kdl:
--------------------------------------------------------------------------------
1 | 😁 "happy!"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/binary.kdl:
--------------------------------------------------------------------------------
1 | node 2
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/binary_trailing_underscore.kdl:
--------------------------------------------------------------------------------
1 | node 2
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/binary_underscore.kdl:
--------------------------------------------------------------------------------
1 | node 2
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/blank_arg_type.kdl:
--------------------------------------------------------------------------------
1 | node ("")10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/blank_node_type.kdl:
--------------------------------------------------------------------------------
1 | ("")node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/blank_prop_type.kdl:
--------------------------------------------------------------------------------
1 | node key=("")true
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/block_comment.kdl:
--------------------------------------------------------------------------------
1 | node "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/block_comment_after_node.kdl:
--------------------------------------------------------------------------------
1 | node "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/block_comment_before_node.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/block_comment_before_node_no_space.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/block_comment_newline.kdl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/boolean_arg.kdl:
--------------------------------------------------------------------------------
1 | node false true
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/boolean_prop.kdl:
--------------------------------------------------------------------------------
1 | node prop1=true prop2=false
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/commented_arg.kdl:
--------------------------------------------------------------------------------
1 | node "arg2"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/commented_child.kdl:
--------------------------------------------------------------------------------
1 | node "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/commented_line.kdl:
--------------------------------------------------------------------------------
1 | node_2
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/commented_node.kdl:
--------------------------------------------------------------------------------
1 | node_2
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/commented_prop.kdl:
--------------------------------------------------------------------------------
1 | node "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/crlf_between_nodes.kdl:
--------------------------------------------------------------------------------
1 | node1
2 | node2
3 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/emoji.kdl:
--------------------------------------------------------------------------------
1 | node "😀"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/empty.kdl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/empty_child.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/empty_child_different_lines.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/empty_child_same_line.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/empty_child_whitespace.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/empty_quoted_node_id.kdl:
--------------------------------------------------------------------------------
1 | "" "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/empty_quoted_prop_key.kdl:
--------------------------------------------------------------------------------
1 | node ""="empty"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/empty_string_arg.kdl:
--------------------------------------------------------------------------------
1 | node ""
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/esc_newline_in_string.kdl:
--------------------------------------------------------------------------------
1 | node "hello\nworld"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/esc_unicode_in_string.kdl:
--------------------------------------------------------------------------------
1 | node "hello\nworld"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/escline.kdl:
--------------------------------------------------------------------------------
1 | node "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/escline_line_comment.kdl:
--------------------------------------------------------------------------------
1 | node "arg" "arg2\n"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/escline_node.kdl:
--------------------------------------------------------------------------------
1 | node1
2 | node2
3 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/false_prefix_in_bare_id.kdl:
--------------------------------------------------------------------------------
1 | false_id
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/false_prefix_in_prop_key.kdl:
--------------------------------------------------------------------------------
1 | node false_id=1
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/hex.kdl:
--------------------------------------------------------------------------------
1 | node 1311768467294899695
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/hex_int.kdl:
--------------------------------------------------------------------------------
1 | node 1311768467294899695
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/hex_int_underscores.kdl:
--------------------------------------------------------------------------------
1 | node 737894400291
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/hex_leading_zero.kdl:
--------------------------------------------------------------------------------
1 | node 1
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/int_multiple_underscore.kdl:
--------------------------------------------------------------------------------
1 | node 1234
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/just_block_comment.kdl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/just_child.kdl:
--------------------------------------------------------------------------------
1 | node {
2 | inner_node
3 | }
4 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/just_newline.kdl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/just_node_id.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/just_space.kdl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/leading_newline.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/leading_zero_binary.kdl:
--------------------------------------------------------------------------------
1 | node 1
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/leading_zero_int.kdl:
--------------------------------------------------------------------------------
1 | node 11
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/leading_zero_oct.kdl:
--------------------------------------------------------------------------------
1 | node 1
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/multiline_comment.kdl:
--------------------------------------------------------------------------------
1 | node "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/multiline_nodes.kdl:
--------------------------------------------------------------------------------
1 | node "arg1" "arg2"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/multiline_string.kdl:
--------------------------------------------------------------------------------
1 | node " hey\neveryone\nhow goes?\n"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/negative_exponent.kdl:
--------------------------------------------------------------------------------
1 | node 1.0E-10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/negative_float.kdl:
--------------------------------------------------------------------------------
1 | node -1.0 key=-10.0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/negative_int.kdl:
--------------------------------------------------------------------------------
1 | node -10 prop=-15
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/nested_block_comment.kdl:
--------------------------------------------------------------------------------
1 | node "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/nested_children.kdl:
--------------------------------------------------------------------------------
1 | node1 {
2 | node2 {
3 | node
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/nested_comments.kdl:
--------------------------------------------------------------------------------
1 | node "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/nested_multiline_block_comment.kdl:
--------------------------------------------------------------------------------
1 | node "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/newline_between_nodes.kdl:
--------------------------------------------------------------------------------
1 | node1
2 | node2
3 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/newlines_in_block_comment.kdl:
--------------------------------------------------------------------------------
1 | node "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/no_decimal_exponent.kdl:
--------------------------------------------------------------------------------
1 | node 1E+10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/node_false.kdl:
--------------------------------------------------------------------------------
1 | node false
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/node_true.kdl:
--------------------------------------------------------------------------------
1 | node true
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/node_type.kdl:
--------------------------------------------------------------------------------
1 | (type)node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/null_arg.kdl:
--------------------------------------------------------------------------------
1 | node null
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/null_prefix_in_bare_id.kdl:
--------------------------------------------------------------------------------
1 | null_id
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/null_prefix_in_prop_key.kdl:
--------------------------------------------------------------------------------
1 | node null_id=1
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/null_prop.kdl:
--------------------------------------------------------------------------------
1 | node prop=null
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/numeric_arg.kdl:
--------------------------------------------------------------------------------
1 | node 15.7
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/numeric_prop.kdl:
--------------------------------------------------------------------------------
1 | node prop=10.0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/octal.kdl:
--------------------------------------------------------------------------------
1 | node 16434824
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/only_cr.kdl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/only_line_comment.kdl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/only_line_comment_crlf.kdl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/only_line_comment_newline.kdl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/parse_all_arg_types.kdl:
--------------------------------------------------------------------------------
1 | node 1 1.0 1.0E+10 1.0E-10 1 7 2 "arg" "arg\\\\" true false null
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/positive_exponent.kdl:
--------------------------------------------------------------------------------
1 | node 1.0E+10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/positive_int.kdl:
--------------------------------------------------------------------------------
1 | node 10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/preserve_duplicate_nodes.kdl:
--------------------------------------------------------------------------------
1 | node
2 | node
3 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/preserve_node_order.kdl:
--------------------------------------------------------------------------------
1 | node2
2 | node5
3 | node1
4 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/prop_false_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)false
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/prop_float_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)2.5E+10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/prop_hex_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)16
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/prop_null_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)null
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/prop_raw_string_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)"str"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/prop_string_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)"str"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/prop_true_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)true
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/prop_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)true
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/prop_zero_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/quoted_arg_type.kdl:
--------------------------------------------------------------------------------
1 | node ("type/")10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/quoted_node_name.kdl:
--------------------------------------------------------------------------------
1 | "0node"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/quoted_node_type.kdl:
--------------------------------------------------------------------------------
1 | ("type/")node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/quoted_numeric.kdl:
--------------------------------------------------------------------------------
1 | node prop="10.0"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/quoted_prop_name.kdl:
--------------------------------------------------------------------------------
1 | node "0prop"="val"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/quoted_prop_type.kdl:
--------------------------------------------------------------------------------
1 | node key=("type/")true
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/r_node.kdl:
--------------------------------------------------------------------------------
1 | r "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/raw_arg_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)true
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/raw_node_name.kdl:
--------------------------------------------------------------------------------
1 | "\\node"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/raw_node_type.kdl:
--------------------------------------------------------------------------------
1 | (type)node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/raw_prop_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)true
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/raw_string_arg.kdl:
--------------------------------------------------------------------------------
1 | node_1 "arg\\n"
2 | node_2 "\"arg\\n\"and stuff"
3 | node_3 "#\"arg\\n\"#and stuff"
4 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/raw_string_backslash.kdl:
--------------------------------------------------------------------------------
1 | node "\\n"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/raw_string_hash_no_esc.kdl:
--------------------------------------------------------------------------------
1 | node "#"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/raw_string_just_backslash.kdl:
--------------------------------------------------------------------------------
1 | node "\\"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/raw_string_just_quote.kdl:
--------------------------------------------------------------------------------
1 | node "\""
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/raw_string_multiple_hash.kdl:
--------------------------------------------------------------------------------
1 | node "\"#\"##"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/raw_string_newline.kdl:
--------------------------------------------------------------------------------
1 | node "\nhello\nworld\n"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/raw_string_prop.kdl:
--------------------------------------------------------------------------------
1 | node_1 prop="arg\\n"
2 | node_2 prop="\"arg\"\\n"
3 | node_3 prop="#\"arg\"#\\n"
4 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/raw_string_quote.kdl:
--------------------------------------------------------------------------------
1 | node "a\"b"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/repeated_arg.kdl:
--------------------------------------------------------------------------------
1 | node "arg" "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/repeated_prop.kdl:
--------------------------------------------------------------------------------
1 | node prop=11
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/same_args.kdl:
--------------------------------------------------------------------------------
1 | node "whee" "whee"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/same_name_nodes.kdl:
--------------------------------------------------------------------------------
1 | node
2 | node
3 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/sci_notation_large.kdl:
--------------------------------------------------------------------------------
1 | node prop=1.23E+1000
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/sci_notation_small.kdl:
--------------------------------------------------------------------------------
1 | node prop=1.23E-1000
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/semicolon_after_child.kdl:
--------------------------------------------------------------------------------
1 | node {
2 | childnode
3 | }
4 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/semicolon_in_child.kdl:
--------------------------------------------------------------------------------
1 | node1 {
2 | node2
3 | }
4 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/semicolon_separated.kdl:
--------------------------------------------------------------------------------
1 | node1
2 | node2
3 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/semicolon_separated_nodes.kdl:
--------------------------------------------------------------------------------
1 | node1
2 | node2
3 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/semicolon_terminated.kdl:
--------------------------------------------------------------------------------
1 | node1
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/single_arg.kdl:
--------------------------------------------------------------------------------
1 | node "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/single_prop.kdl:
--------------------------------------------------------------------------------
1 | node prop="val"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/slashdash_arg_after_newline_esc.kdl:
--------------------------------------------------------------------------------
1 | node "arg2"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/slashdash_arg_before_newline_esc.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/slashdash_child.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/slashdash_empty_child.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/slashdash_full_node.kdl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/slashdash_in_slashdash.kdl:
--------------------------------------------------------------------------------
1 | node2
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/slashdash_negative_number.kdl:
--------------------------------------------------------------------------------
1 | node 2.0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/slashdash_node_in_child.kdl:
--------------------------------------------------------------------------------
1 | node1
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/slashdash_node_with_child.kdl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/slashdash_only_node.kdl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/slashdash_only_node_with_space.kdl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/slashdash_prop.kdl:
--------------------------------------------------------------------------------
1 | node "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/slashdash_raw_prop_key.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/slashdash_repeated_prop.kdl:
--------------------------------------------------------------------------------
1 | node arg="correct"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/string_arg.kdl:
--------------------------------------------------------------------------------
1 | node "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/string_prop.kdl:
--------------------------------------------------------------------------------
1 | node prop="val"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/tab_space.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/trailing_crlf.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/trailing_underscore_hex.kdl:
--------------------------------------------------------------------------------
1 | node 1194684
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/trailing_underscore_octal.kdl:
--------------------------------------------------------------------------------
1 | node 83
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/true_prefix_in_bare_id.kdl:
--------------------------------------------------------------------------------
1 | true_id
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/true_prefix_in_prop_key.kdl:
--------------------------------------------------------------------------------
1 | node true_id=1
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/two_nodes.kdl:
--------------------------------------------------------------------------------
1 | node1
2 | node2
3 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/underscore_in_exponent.kdl:
--------------------------------------------------------------------------------
1 | node 1.0E-100
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/underscore_in_float.kdl:
--------------------------------------------------------------------------------
1 | node 11.0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/underscore_in_fraction.kdl:
--------------------------------------------------------------------------------
1 | node 1.02
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/underscore_in_int.kdl:
--------------------------------------------------------------------------------
1 | node 10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/underscore_in_octal.kdl:
--------------------------------------------------------------------------------
1 | node 342391
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/unusual_bare_id_chars_in_quoted_id.kdl:
--------------------------------------------------------------------------------
1 | foo123~!@#$%^&*.:'|?+ "weeee"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/unusual_chars_in_bare_id.kdl:
--------------------------------------------------------------------------------
1 | foo123~!@#$%^&*.:'|?+ "weeee"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/zero_arg.kdl:
--------------------------------------------------------------------------------
1 | node 0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/zero_float.kdl:
--------------------------------------------------------------------------------
1 | node 0.0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/expected_kdl/zero_int.kdl:
--------------------------------------------------------------------------------
1 | node 0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/_negative_exponent.kdl:
--------------------------------------------------------------------------------
1 | node 1.0e-10
--------------------------------------------------------------------------------
/tests/test_cases/input/_no_decimal_exponent.kdl:
--------------------------------------------------------------------------------
1 | node 1e10
--------------------------------------------------------------------------------
/tests/test_cases/input/_parse_all_arg_types.kdl:
--------------------------------------------------------------------------------
1 | node 1 1.0 1.0e10 1.0e-10 0x01 0o07 0b10 "arg" r"arg\\" true false null
--------------------------------------------------------------------------------
/tests/test_cases/input/_positive_exponent.kdl:
--------------------------------------------------------------------------------
1 | node 1.0e+10
--------------------------------------------------------------------------------
/tests/test_cases/input/_prop_float_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)2.5E10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/_sci_notation_large.kdl:
--------------------------------------------------------------------------------
1 | node prop=1.23E+1000
--------------------------------------------------------------------------------
/tests/test_cases/input/_sci_notation_small.kdl:
--------------------------------------------------------------------------------
1 | node prop=1.23E-1000
--------------------------------------------------------------------------------
/tests/test_cases/input/_underscore_in_exponent.kdl:
--------------------------------------------------------------------------------
1 | node 1.0e-10_0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/all_escapes.kdl:
--------------------------------------------------------------------------------
1 | node "\"\\\/\b\f\n\r\t"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/all_node_fields.kdl:
--------------------------------------------------------------------------------
1 | node "arg" prop="val" {
2 | inner_node
3 | }
--------------------------------------------------------------------------------
/tests/test_cases/input/arg_and_prop_same_name.kdl:
--------------------------------------------------------------------------------
1 | node "arg" arg="val"
--------------------------------------------------------------------------------
/tests/test_cases/input/arg_false_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)false
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/arg_float_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)2.5
--------------------------------------------------------------------------------
/tests/test_cases/input/arg_hex_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)0x10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/arg_null_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)null
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/arg_raw_string_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)"str"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/arg_string_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)"str"
--------------------------------------------------------------------------------
/tests/test_cases/input/arg_true_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)true
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/arg_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)"arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/arg_zero_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/asterisk_in_block_comment.kdl:
--------------------------------------------------------------------------------
1 | node /* * */
--------------------------------------------------------------------------------
/tests/test_cases/input/backslash_in_bare_id.kdl:
--------------------------------------------------------------------------------
1 | foo123\bar "weeee"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/bare_arg.kdl:
--------------------------------------------------------------------------------
1 | node a
--------------------------------------------------------------------------------
/tests/test_cases/input/bare_emoji.kdl:
--------------------------------------------------------------------------------
1 | 😁 "happy!"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/binary.kdl:
--------------------------------------------------------------------------------
1 | node 0b10
--------------------------------------------------------------------------------
/tests/test_cases/input/binary_trailing_underscore.kdl:
--------------------------------------------------------------------------------
1 | node 0b10_
--------------------------------------------------------------------------------
/tests/test_cases/input/binary_underscore.kdl:
--------------------------------------------------------------------------------
1 | node 0b1_0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/blank_arg_type.kdl:
--------------------------------------------------------------------------------
1 | node ("")10
--------------------------------------------------------------------------------
/tests/test_cases/input/blank_node_type.kdl:
--------------------------------------------------------------------------------
1 | ("")node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/blank_prop_type.kdl:
--------------------------------------------------------------------------------
1 | node key=("")true
--------------------------------------------------------------------------------
/tests/test_cases/input/block_comment.kdl:
--------------------------------------------------------------------------------
1 | node /* comment */ "arg"
--------------------------------------------------------------------------------
/tests/test_cases/input/block_comment_after_node.kdl:
--------------------------------------------------------------------------------
1 | node /* hey */ "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/block_comment_before_node.kdl:
--------------------------------------------------------------------------------
1 | /* hey */ node
--------------------------------------------------------------------------------
/tests/test_cases/input/block_comment_before_node_no_space.kdl:
--------------------------------------------------------------------------------
1 | /* hey*/node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/block_comment_newline.kdl:
--------------------------------------------------------------------------------
1 | /* hey */
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/boolean_arg.kdl:
--------------------------------------------------------------------------------
1 | node false true
--------------------------------------------------------------------------------
/tests/test_cases/input/boolean_prop.kdl:
--------------------------------------------------------------------------------
1 | node prop1=true prop2=false
--------------------------------------------------------------------------------
/tests/test_cases/input/brackets_in_bare_id.kdl:
--------------------------------------------------------------------------------
1 | foo123{bar}foo "weeee"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/chevrons_in_bare_id.kdl:
--------------------------------------------------------------------------------
1 | foo123foo "weeee"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/comma_in_bare_id.kdl:
--------------------------------------------------------------------------------
1 | foo123,bar "weeee"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/comment_after_arg_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)/*huh*/10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/comment_after_node_type.kdl:
--------------------------------------------------------------------------------
1 | (type)/*huh*/node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/comment_after_prop_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)/*huh*/10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/comment_in_arg_type.kdl:
--------------------------------------------------------------------------------
1 | node (type/*huh*/)10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/comment_in_node_type.kdl:
--------------------------------------------------------------------------------
1 | (type/*huh*/)node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/comment_in_prop_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type/*huh*/)10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/commented_arg.kdl:
--------------------------------------------------------------------------------
1 | node /- "arg1" "arg2"
--------------------------------------------------------------------------------
/tests/test_cases/input/commented_child.kdl:
--------------------------------------------------------------------------------
1 | node "arg" /- {
2 | inner_node
3 | }
--------------------------------------------------------------------------------
/tests/test_cases/input/commented_line.kdl:
--------------------------------------------------------------------------------
1 | // node_1
2 | node_2
--------------------------------------------------------------------------------
/tests/test_cases/input/commented_node.kdl:
--------------------------------------------------------------------------------
1 | /- node_1
2 | node_2
3 |
--------------------------------------------------------------------------------
/tests/test_cases/input/commented_prop.kdl:
--------------------------------------------------------------------------------
1 | node /- prop="val" "arg"
--------------------------------------------------------------------------------
/tests/test_cases/input/crlf_between_nodes.kdl:
--------------------------------------------------------------------------------
1 | node1
2 | node2
--------------------------------------------------------------------------------
/tests/test_cases/input/dash_dash.kdl:
--------------------------------------------------------------------------------
1 | node --
--------------------------------------------------------------------------------
/tests/test_cases/input/dot_but_no_fraction.kdl:
--------------------------------------------------------------------------------
1 | node 1.
--------------------------------------------------------------------------------
/tests/test_cases/input/dot_but_no_fraction_before_exponent.kdl:
--------------------------------------------------------------------------------
1 | node 1.e7
--------------------------------------------------------------------------------
/tests/test_cases/input/dot_in_exponent.kdl:
--------------------------------------------------------------------------------
1 | node 1.0.0
--------------------------------------------------------------------------------
/tests/test_cases/input/dot_zero.kdl:
--------------------------------------------------------------------------------
1 | node .0
--------------------------------------------------------------------------------
/tests/test_cases/input/emoji.kdl:
--------------------------------------------------------------------------------
1 | node "😀"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/empty.kdl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Patitotective/kdl-nim/3a5d0b45bde5c3b75e08176cb660b33b0aae93ea/tests/test_cases/input/empty.kdl
--------------------------------------------------------------------------------
/tests/test_cases/input/empty_arg_type.kdl:
--------------------------------------------------------------------------------
1 | node ()10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/empty_child.kdl:
--------------------------------------------------------------------------------
1 | node {
2 | }
--------------------------------------------------------------------------------
/tests/test_cases/input/empty_child_different_lines.kdl:
--------------------------------------------------------------------------------
1 | node {
2 | }
--------------------------------------------------------------------------------
/tests/test_cases/input/empty_child_same_line.kdl:
--------------------------------------------------------------------------------
1 | node {}
--------------------------------------------------------------------------------
/tests/test_cases/input/empty_child_whitespace.kdl:
--------------------------------------------------------------------------------
1 | node {
2 |
3 | }
--------------------------------------------------------------------------------
/tests/test_cases/input/empty_node_type.kdl:
--------------------------------------------------------------------------------
1 | ()node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/empty_prop_type.kdl:
--------------------------------------------------------------------------------
1 | node key=()false
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/empty_quoted_node_id.kdl:
--------------------------------------------------------------------------------
1 | "" "arg"
--------------------------------------------------------------------------------
/tests/test_cases/input/empty_quoted_prop_key.kdl:
--------------------------------------------------------------------------------
1 | node ""="empty"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/empty_string_arg.kdl:
--------------------------------------------------------------------------------
1 | node ""
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/esc_newline_in_string.kdl:
--------------------------------------------------------------------------------
1 | node "hello\nworld"
--------------------------------------------------------------------------------
/tests/test_cases/input/esc_unicode_in_string.kdl:
--------------------------------------------------------------------------------
1 | node "hello\u{0a}world"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/escline.kdl:
--------------------------------------------------------------------------------
1 | node \
2 | "arg"
--------------------------------------------------------------------------------
/tests/test_cases/input/escline_comment_node.kdl:
--------------------------------------------------------------------------------
1 | node1
2 | \// hey
3 | node2
--------------------------------------------------------------------------------
/tests/test_cases/input/escline_line_comment.kdl:
--------------------------------------------------------------------------------
1 | node \ // comment
2 | "arg" \// comment
3 | "arg2
4 | "
--------------------------------------------------------------------------------
/tests/test_cases/input/escline_node.kdl:
--------------------------------------------------------------------------------
1 | node1
2 | node2
3 |
--------------------------------------------------------------------------------
/tests/test_cases/input/false_prefix_in_bare_id.kdl:
--------------------------------------------------------------------------------
1 | false_id
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/false_prefix_in_prop_key.kdl:
--------------------------------------------------------------------------------
1 | node false_id=1
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/false_prop_key.kdl:
--------------------------------------------------------------------------------
1 | node false=1
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/hex.kdl:
--------------------------------------------------------------------------------
1 | node 0x1234567890abcdef
--------------------------------------------------------------------------------
/tests/test_cases/input/hex_int.kdl:
--------------------------------------------------------------------------------
1 | node 0x1234567890ABCDEF
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/hex_int_underscores.kdl:
--------------------------------------------------------------------------------
1 | node 0xABC_def_0123
--------------------------------------------------------------------------------
/tests/test_cases/input/hex_leading_zero.kdl:
--------------------------------------------------------------------------------
1 | node 0x01
--------------------------------------------------------------------------------
/tests/test_cases/input/illegal_char_in_binary.kdl:
--------------------------------------------------------------------------------
1 | node 0bx01
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/illegal_char_in_hex.kdl:
--------------------------------------------------------------------------------
1 | node 0x10g10
--------------------------------------------------------------------------------
/tests/test_cases/input/illegal_char_in_octal.kdl:
--------------------------------------------------------------------------------
1 | node 0o45678
--------------------------------------------------------------------------------
/tests/test_cases/input/int_multiple_underscore.kdl:
--------------------------------------------------------------------------------
1 | node 1_2_3_4
--------------------------------------------------------------------------------
/tests/test_cases/input/just_block_comment.kdl:
--------------------------------------------------------------------------------
1 | /* hey */
--------------------------------------------------------------------------------
/tests/test_cases/input/just_child.kdl:
--------------------------------------------------------------------------------
1 | node {
2 | inner_node
3 | }
--------------------------------------------------------------------------------
/tests/test_cases/input/just_newline.kdl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/just_node_id.kdl:
--------------------------------------------------------------------------------
1 | node
--------------------------------------------------------------------------------
/tests/test_cases/input/just_space.kdl:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/test_cases/input/just_space_in_arg_type.kdl:
--------------------------------------------------------------------------------
1 | node ( )false
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/just_space_in_node_type.kdl:
--------------------------------------------------------------------------------
1 | ( )node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/just_space_in_prop_type.kdl:
--------------------------------------------------------------------------------
1 | node key=()0x10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/just_type_no_arg.kdl:
--------------------------------------------------------------------------------
1 | node (type)
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/just_type_no_node_id.kdl:
--------------------------------------------------------------------------------
1 | (type)
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/just_type_no_prop.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/leading_newline.kdl:
--------------------------------------------------------------------------------
1 |
2 | node
--------------------------------------------------------------------------------
/tests/test_cases/input/leading_zero_binary.kdl:
--------------------------------------------------------------------------------
1 | node 0b01
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/leading_zero_int.kdl:
--------------------------------------------------------------------------------
1 | node 011
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/leading_zero_oct.kdl:
--------------------------------------------------------------------------------
1 | node 0o01
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/multiline_comment.kdl:
--------------------------------------------------------------------------------
1 | node /*
2 | some
3 | comments
4 | */ "arg"
--------------------------------------------------------------------------------
/tests/test_cases/input/multiline_nodes.kdl:
--------------------------------------------------------------------------------
1 | node \
2 | "arg1" \// comment
3 | "arg2"
--------------------------------------------------------------------------------
/tests/test_cases/input/multiline_string.kdl:
--------------------------------------------------------------------------------
1 | node " hey
2 | everyone
3 | how goes?
4 | "
--------------------------------------------------------------------------------
/tests/test_cases/input/multiple_dots_in_float.kdl:
--------------------------------------------------------------------------------
1 | node 1.0.0
--------------------------------------------------------------------------------
/tests/test_cases/input/multiple_dots_in_float_before_exponent.kdl:
--------------------------------------------------------------------------------
1 | node 1.0.0e7
--------------------------------------------------------------------------------
/tests/test_cases/input/multiple_es_in_float.kdl:
--------------------------------------------------------------------------------
1 | node 1.0E10e10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/multiple_x_in_hex.kdl:
--------------------------------------------------------------------------------
1 | node 0xx10
--------------------------------------------------------------------------------
/tests/test_cases/input/negative_float.kdl:
--------------------------------------------------------------------------------
1 | node -1.0 key=-10.0
--------------------------------------------------------------------------------
/tests/test_cases/input/negative_int.kdl:
--------------------------------------------------------------------------------
1 | node -10 prop=-15
--------------------------------------------------------------------------------
/tests/test_cases/input/nested_block_comment.kdl:
--------------------------------------------------------------------------------
1 | node /* hi /* there */ everyone */ "arg"
--------------------------------------------------------------------------------
/tests/test_cases/input/nested_children.kdl:
--------------------------------------------------------------------------------
1 | node1 {
2 | node2 {
3 | node
4 | }
5 | }
--------------------------------------------------------------------------------
/tests/test_cases/input/nested_comments.kdl:
--------------------------------------------------------------------------------
1 | node /*/* nested */*/ "arg"
--------------------------------------------------------------------------------
/tests/test_cases/input/nested_multiline_block_comment.kdl:
--------------------------------------------------------------------------------
1 | node /*
2 | hey /*
3 | how's
4 | */
5 | it going
6 | */ "arg"
7 |
--------------------------------------------------------------------------------
/tests/test_cases/input/newline_between_nodes.kdl:
--------------------------------------------------------------------------------
1 | node1
2 | node2
3 |
--------------------------------------------------------------------------------
/tests/test_cases/input/newlines_in_block_comment.kdl:
--------------------------------------------------------------------------------
1 | node /* hey so
2 | I was thinking
3 | about newts */ "arg"
--------------------------------------------------------------------------------
/tests/test_cases/input/no_digits_in_hex.kdl:
--------------------------------------------------------------------------------
1 | node 0x
--------------------------------------------------------------------------------
/tests/test_cases/input/node_false.kdl:
--------------------------------------------------------------------------------
1 | node false
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/node_true.kdl:
--------------------------------------------------------------------------------
1 | node true
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/node_type.kdl:
--------------------------------------------------------------------------------
1 | (type)node
--------------------------------------------------------------------------------
/tests/test_cases/input/null_arg.kdl:
--------------------------------------------------------------------------------
1 | node null
--------------------------------------------------------------------------------
/tests/test_cases/input/null_prefix_in_bare_id.kdl:
--------------------------------------------------------------------------------
1 | null_id
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/null_prefix_in_prop_key.kdl:
--------------------------------------------------------------------------------
1 | node null_id=1
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/null_prop.kdl:
--------------------------------------------------------------------------------
1 | node prop=null
--------------------------------------------------------------------------------
/tests/test_cases/input/null_prop_key.kdl:
--------------------------------------------------------------------------------
1 | node null=1
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/numeric_arg.kdl:
--------------------------------------------------------------------------------
1 | node 15.7
--------------------------------------------------------------------------------
/tests/test_cases/input/numeric_prop.kdl:
--------------------------------------------------------------------------------
1 | node prop=10.0
--------------------------------------------------------------------------------
/tests/test_cases/input/octal.kdl:
--------------------------------------------------------------------------------
1 | node 0o76543210
--------------------------------------------------------------------------------
/tests/test_cases/input/only_cr.kdl:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/test_cases/input/only_line_comment.kdl:
--------------------------------------------------------------------------------
1 | // hi
--------------------------------------------------------------------------------
/tests/test_cases/input/only_line_comment_crlf.kdl:
--------------------------------------------------------------------------------
1 | // comment
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/only_line_comment_newline.kdl:
--------------------------------------------------------------------------------
1 | // hiiii
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/parens_in_bare_id.kdl:
--------------------------------------------------------------------------------
1 | foo123(bar)foo "weeee"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/positive_int.kdl:
--------------------------------------------------------------------------------
1 | node +10
--------------------------------------------------------------------------------
/tests/test_cases/input/preserve_duplicate_nodes.kdl:
--------------------------------------------------------------------------------
1 | node
2 | node
3 |
--------------------------------------------------------------------------------
/tests/test_cases/input/preserve_node_order.kdl:
--------------------------------------------------------------------------------
1 | node2
2 | node5
3 | node1
--------------------------------------------------------------------------------
/tests/test_cases/input/prop_false_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)false
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/prop_hex_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)0x10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/prop_null_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)null
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/prop_raw_string_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)r"str"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/prop_string_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)"str"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/prop_true_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)true
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/prop_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)true
--------------------------------------------------------------------------------
/tests/test_cases/input/prop_zero_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/question_mark_at_start_of_int.kdl:
--------------------------------------------------------------------------------
1 | node ?10
--------------------------------------------------------------------------------
/tests/test_cases/input/question_mark_before_number.kdl:
--------------------------------------------------------------------------------
1 | node ?15
--------------------------------------------------------------------------------
/tests/test_cases/input/quote_in_bare_id.kdl:
--------------------------------------------------------------------------------
1 | foo123"bar "weeee"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/quoted_arg_type.kdl:
--------------------------------------------------------------------------------
1 | node ("type/")10
--------------------------------------------------------------------------------
/tests/test_cases/input/quoted_node_name.kdl:
--------------------------------------------------------------------------------
1 | "0node"
--------------------------------------------------------------------------------
/tests/test_cases/input/quoted_node_type.kdl:
--------------------------------------------------------------------------------
1 | ("type/")node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/quoted_numeric.kdl:
--------------------------------------------------------------------------------
1 | node prop="10.0"
--------------------------------------------------------------------------------
/tests/test_cases/input/quoted_prop_name.kdl:
--------------------------------------------------------------------------------
1 | node "0prop"="val"
--------------------------------------------------------------------------------
/tests/test_cases/input/quoted_prop_type.kdl:
--------------------------------------------------------------------------------
1 | node key=("type/")true
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/r_node.kdl:
--------------------------------------------------------------------------------
1 | r "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/raw_arg_type.kdl:
--------------------------------------------------------------------------------
1 | node (type)true
--------------------------------------------------------------------------------
/tests/test_cases/input/raw_node_name.kdl:
--------------------------------------------------------------------------------
1 | r"\node"
--------------------------------------------------------------------------------
/tests/test_cases/input/raw_node_type.kdl:
--------------------------------------------------------------------------------
1 | (type)node
--------------------------------------------------------------------------------
/tests/test_cases/input/raw_prop_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type)true
--------------------------------------------------------------------------------
/tests/test_cases/input/raw_string_arg.kdl:
--------------------------------------------------------------------------------
1 | node_1 r"arg\n"
2 | node_2 r#""arg\n"and stuff"#
3 | node_3 r##"#"arg\n"#and stuff"##
--------------------------------------------------------------------------------
/tests/test_cases/input/raw_string_backslash.kdl:
--------------------------------------------------------------------------------
1 | node r"\n"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/raw_string_hash_no_esc.kdl:
--------------------------------------------------------------------------------
1 | node r"#"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/raw_string_just_backslash.kdl:
--------------------------------------------------------------------------------
1 | node r"\"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/raw_string_just_quote.kdl:
--------------------------------------------------------------------------------
1 | node r#"""#
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/raw_string_multiple_hash.kdl:
--------------------------------------------------------------------------------
1 | node r###""#"##"###
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/raw_string_newline.kdl:
--------------------------------------------------------------------------------
1 | node r"
2 | hello
3 | world
4 | "
5 |
--------------------------------------------------------------------------------
/tests/test_cases/input/raw_string_prop.kdl:
--------------------------------------------------------------------------------
1 | node_1 prop=r"arg\n"
2 | node_2 prop=r#""arg"\n"#
3 | node_3 prop=r##"#"arg"#\n"##
--------------------------------------------------------------------------------
/tests/test_cases/input/raw_string_quote.kdl:
--------------------------------------------------------------------------------
1 | node r#"a"b"#
--------------------------------------------------------------------------------
/tests/test_cases/input/repeated_arg.kdl:
--------------------------------------------------------------------------------
1 | node "arg" "arg"
--------------------------------------------------------------------------------
/tests/test_cases/input/repeated_prop.kdl:
--------------------------------------------------------------------------------
1 | node prop=10 prop=11
--------------------------------------------------------------------------------
/tests/test_cases/input/same_args.kdl:
--------------------------------------------------------------------------------
1 | node "whee" "whee"
--------------------------------------------------------------------------------
/tests/test_cases/input/same_name_nodes.kdl:
--------------------------------------------------------------------------------
1 | node
2 | node
3 |
--------------------------------------------------------------------------------
/tests/test_cases/input/semicolon_after_child.kdl:
--------------------------------------------------------------------------------
1 | node {
2 | childnode
3 | };
4 |
--------------------------------------------------------------------------------
/tests/test_cases/input/semicolon_in_child.kdl:
--------------------------------------------------------------------------------
1 | node1 {
2 | node2;
3 | }
--------------------------------------------------------------------------------
/tests/test_cases/input/semicolon_separated.kdl:
--------------------------------------------------------------------------------
1 | node1;node2
--------------------------------------------------------------------------------
/tests/test_cases/input/semicolon_separated_nodes.kdl:
--------------------------------------------------------------------------------
1 | node1; node2;
--------------------------------------------------------------------------------
/tests/test_cases/input/semicolon_terminated.kdl:
--------------------------------------------------------------------------------
1 | node1;
--------------------------------------------------------------------------------
/tests/test_cases/input/single_arg.kdl:
--------------------------------------------------------------------------------
1 | node "arg"
--------------------------------------------------------------------------------
/tests/test_cases/input/single_prop.kdl:
--------------------------------------------------------------------------------
1 | node prop="val"
--------------------------------------------------------------------------------
/tests/test_cases/input/slash_in_bare_id.kdl:
--------------------------------------------------------------------------------
1 | foo123/bar "weeee"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/slashdash_arg_after_newline_esc.kdl:
--------------------------------------------------------------------------------
1 | node \
2 | /- "arg" "arg2"
3 |
--------------------------------------------------------------------------------
/tests/test_cases/input/slashdash_arg_before_newline_esc.kdl:
--------------------------------------------------------------------------------
1 | node /- \
2 | "arg"
3 |
--------------------------------------------------------------------------------
/tests/test_cases/input/slashdash_child.kdl:
--------------------------------------------------------------------------------
1 | node /- {
2 | node2
3 | }
4 |
--------------------------------------------------------------------------------
/tests/test_cases/input/slashdash_empty_child.kdl:
--------------------------------------------------------------------------------
1 | node /- {
2 | }
3 |
--------------------------------------------------------------------------------
/tests/test_cases/input/slashdash_full_node.kdl:
--------------------------------------------------------------------------------
1 | /- node 1.0 "a" b="b
2 | "
--------------------------------------------------------------------------------
/tests/test_cases/input/slashdash_in_slashdash.kdl:
--------------------------------------------------------------------------------
1 | /- node1 /- 1.0
2 | node2
--------------------------------------------------------------------------------
/tests/test_cases/input/slashdash_negative_number.kdl:
--------------------------------------------------------------------------------
1 | node /--1.0 2.0
--------------------------------------------------------------------------------
/tests/test_cases/input/slashdash_node_in_child.kdl:
--------------------------------------------------------------------------------
1 | node1 {
2 | /- node2
3 | }
--------------------------------------------------------------------------------
/tests/test_cases/input/slashdash_node_with_child.kdl:
--------------------------------------------------------------------------------
1 | /- node {
2 | node2
3 | }
--------------------------------------------------------------------------------
/tests/test_cases/input/slashdash_only_node.kdl:
--------------------------------------------------------------------------------
1 | /-node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/slashdash_only_node_with_space.kdl:
--------------------------------------------------------------------------------
1 | /- node
--------------------------------------------------------------------------------
/tests/test_cases/input/slashdash_prop.kdl:
--------------------------------------------------------------------------------
1 | node /- key="value" "arg"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/slashdash_raw_prop_key.kdl:
--------------------------------------------------------------------------------
1 | node /- key="value"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/slashdash_repeated_prop.kdl:
--------------------------------------------------------------------------------
1 | node arg="correct" /- arg="wrong"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/space_after_arg_type.kdl:
--------------------------------------------------------------------------------
1 | node (type) 10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/space_after_node_type.kdl:
--------------------------------------------------------------------------------
1 | (type) node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/space_after_prop_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type) false
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/space_in_arg_type.kdl:
--------------------------------------------------------------------------------
1 | node (type )false
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/space_in_node_type.kdl:
--------------------------------------------------------------------------------
1 | ( type)node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/space_in_prop_type.kdl:
--------------------------------------------------------------------------------
1 | node key=(type )false
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/square_bracket_in_bare_id.kdl:
--------------------------------------------------------------------------------
1 | foo123[bar]foo "weeee"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/string_arg.kdl:
--------------------------------------------------------------------------------
1 | node "arg"
--------------------------------------------------------------------------------
/tests/test_cases/input/string_prop.kdl:
--------------------------------------------------------------------------------
1 | node prop="val"
--------------------------------------------------------------------------------
/tests/test_cases/input/tab_space.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/trailing_crlf.kdl:
--------------------------------------------------------------------------------
1 | node
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/trailing_underscore_hex.kdl:
--------------------------------------------------------------------------------
1 | node 0x123abc_
--------------------------------------------------------------------------------
/tests/test_cases/input/trailing_underscore_octal.kdl:
--------------------------------------------------------------------------------
1 | node 0o123_
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/true_prefix_in_bare_id.kdl:
--------------------------------------------------------------------------------
1 | true_id
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/true_prefix_in_prop_key.kdl:
--------------------------------------------------------------------------------
1 | node true_id=1
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/true_prop_key.kdl:
--------------------------------------------------------------------------------
1 | node true=1
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/two_nodes.kdl:
--------------------------------------------------------------------------------
1 | node1
2 | node2
3 |
--------------------------------------------------------------------------------
/tests/test_cases/input/type_before_prop_key.kdl:
--------------------------------------------------------------------------------
1 | node (type)key=10
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/unbalanced_raw_hashes.kdl:
--------------------------------------------------------------------------------
1 | node r##"foo"#
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/underscore_at_start_of_fraction.kdl:
--------------------------------------------------------------------------------
1 | node 1._7
--------------------------------------------------------------------------------
/tests/test_cases/input/underscore_at_start_of_hex.kdl:
--------------------------------------------------------------------------------
1 | node 0x_10
--------------------------------------------------------------------------------
/tests/test_cases/input/underscore_at_start_of_int.kdl:
--------------------------------------------------------------------------------
1 | node _15
--------------------------------------------------------------------------------
/tests/test_cases/input/underscore_before_number.kdl:
--------------------------------------------------------------------------------
1 | node _15
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/underscore_in_float.kdl:
--------------------------------------------------------------------------------
1 | node 1_1.0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/underscore_in_fraction.kdl:
--------------------------------------------------------------------------------
1 | node 1.0_2
--------------------------------------------------------------------------------
/tests/test_cases/input/underscore_in_int.kdl:
--------------------------------------------------------------------------------
1 | node 1_0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/underscore_in_octal.kdl:
--------------------------------------------------------------------------------
1 | node 0o012_3456_7
--------------------------------------------------------------------------------
/tests/test_cases/input/unusual_bare_id_chars_in_quoted_id.kdl:
--------------------------------------------------------------------------------
1 | "foo123~!@#$%^&*.:'|?+" "weeee"
--------------------------------------------------------------------------------
/tests/test_cases/input/unusual_chars_in_bare_id.kdl:
--------------------------------------------------------------------------------
1 | foo123~!@#$%^&*.:'|?+ "weeee"
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/zero_arg.kdl:
--------------------------------------------------------------------------------
1 | node 0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/zero_float.kdl:
--------------------------------------------------------------------------------
1 | node 0.0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/input/zero_int.kdl:
--------------------------------------------------------------------------------
1 | node 0
2 |
--------------------------------------------------------------------------------
/tests/test_cases/jik/web-app.json:
--------------------------------------------------------------------------------
1 | {"web-app": {
2 | "servlet": [
3 | {
4 | "servlet-name": "cofaxCDS",
5 | "servlet-class": "org.cofax.cds.CDSServlet",
6 | "init-param": {
7 | "configGlossary:installationAt": "Philadelphia, PA",
8 | "configGlossary:adminEmail": "ksm@pobox.com",
9 | "configGlossary:poweredBy": "Cofax",
10 | "configGlossary:poweredByIcon": "/images/cofax.gif",
11 | "configGlossary:staticPath": "/content/static",
12 | "templateProcessorClass": "org.cofax.WysiwygTemplate",
13 | "templateLoaderClass": "org.cofax.FilesTemplateLoader",
14 | "templatePath": "templates",
15 | "templateOverridePath": "",
16 | "defaultListTemplate": "listTemplate.htm",
17 | "defaultFileTemplate": "articleTemplate.htm",
18 | "useJSP": false,
19 | "jspListTemplate": "listTemplate.jsp",
20 | "jspFileTemplate": "articleTemplate.jsp",
21 | "cachePackageTagsTrack": 200,
22 | "cachePackageTagsStore": 200,
23 | "cachePackageTagsRefresh": 60,
24 | "cacheTemplatesTrack": 100,
25 | "cacheTemplatesStore": 50,
26 | "cacheTemplatesRefresh": 15,
27 | "cachePagesTrack": 200,
28 | "cachePagesStore": 100,
29 | "cachePagesRefresh": 10,
30 | "cachePagesDirtyRead": 10,
31 | "searchEngineListTemplate": "forSearchEnginesList.htm",
32 | "searchEngineFileTemplate": "forSearchEngines.htm",
33 | "searchEngineRobotsDb": "WEB-INF/robots.db",
34 | "useDataStore": true,
35 | "dataStoreClass": "org.cofax.SqlDataStore",
36 | "redirectionClass": "org.cofax.SqlRedirection",
37 | "dataStoreName": "cofax",
38 | "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
39 | "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
40 | "dataStoreUser": "sa",
41 | "dataStorePassword": "dataStoreTestQuery",
42 | "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
43 | "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
44 | "dataStoreInitConns": 10,
45 | "dataStoreMaxConns": 100,
46 | "dataStoreConnUsageLimit": 100,
47 | "dataStoreLogLevel": "debug",
48 | "maxUrlLength": 500}},
49 | {
50 | "servlet-name": "cofaxEmail",
51 | "servlet-class": "org.cofax.cds.EmailServlet",
52 | "init-param": {
53 | "mailHost": "mail1",
54 | "mailHostOverride": "mail2"}},
55 | {
56 | "servlet-name": "cofaxAdmin",
57 | "servlet-class": "org.cofax.cds.AdminServlet"},
58 |
59 | {
60 | "servlet-name": "fileServlet",
61 | "servlet-class": "org.cofax.cds.FileServlet"},
62 | {
63 | "servlet-name": "cofaxTools",
64 | "servlet-class": "org.cofax.cms.CofaxToolsServlet",
65 | "init-param": {
66 | "templatePath": "toolstemplates/",
67 | "log": 1,
68 | "logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
69 | "logMaxSize": "",
70 | "dataLog": 1,
71 | "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
72 | "dataLogMaxSize": "",
73 | "removePageCache": "/content/admin/remove?cache=pages&id=",
74 | "removeTemplateCache": "/content/admin/remove?cache=templates&id=",
75 | "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
76 | "lookInContext": 1,
77 | "adminGroupID": 4,
78 | "betaServer": true}}],
79 | "servlet-mapping": {
80 | "cofaxCDS": "/",
81 | "cofaxEmail": "/cofaxutil/aemail/*",
82 | "cofaxAdmin": "/admin/*",
83 | "fileServlet": "/static/*",
84 | "cofaxTools": "/tools/*"},
85 |
86 | "taglib": {
87 | "taglib-uri": "cofax.tld",
88 | "taglib-location": "/WEB-INF/tlds/cofax.tld"}}}
--------------------------------------------------------------------------------
/tests/test_cases/jik/wordpress.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 157538,
4 | "date": "2017-07-21T10:30:34",
5 | "date_gmt": "2017-07-21T17:30:34",
6 | "guid": {
7 | "rendered": "https://www.sitepoint.com/?p=157538"
8 | },
9 | "modified": "2017-07-23T21:56:35",
10 | "modified_gmt": "2017-07-24T04:56:35",
11 | "slug": "why-the-iot-threatens-your-wordpress-site-and-how-to-fix-it",
12 | "status": "publish",
13 | "type": "post",
14 | "link": "https://www.sitepoint.com/why-the-iot-threatens-your-wordpress-site-and-how-to-fix-it/",
15 | "title": {
16 | "rendered": "Why the IoT Threatens Your WordPress Site (and How to Fix It)"
17 | },
18 | "content": {},
19 | "excerpt": {},
20 | "author": 72546,
21 | "featured_media": 157542,
22 | "comment_status": "open",
23 | "ping_status": "closed",
24 | "sticky": false,
25 | "template": "",
26 | "format": "standard",
27 | "meta": [],
28 | "categories": [
29 | 6132
30 | ],
31 | "tags": [
32 | 1798,
33 | 6298
34 | ],
35 |
36 | }
37 | ]
38 |
--------------------------------------------------------------------------------
/tests/test_cases/xik/xml2kdl.xsl:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
15 |
16 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | \
54 | \\
55 |
56 |
57 |
58 | /
59 | \/
60 |
61 |
62 |
63 | "
64 | \"
65 |
66 |
67 |
68 |
69 | \t
70 |
71 |
72 |
73 |
74 | \r
75 |
76 |
77 |
78 |
79 | \n
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
112 | /*
113 |
114 | */
115 |
116 |
117 |
118 |
119 |
128 | ?
129 |
130 | r#"
131 |
132 | "#;
133 |
134 |
135 |
136 |
137 |
138 |
139 | =
140 | "
141 |
142 |
143 |
144 | "
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 | r#"
158 |
159 |
160 | "#
161 |
162 |
163 |
164 | "
165 |
166 |
167 |
168 |
172 |
173 |
174 |
175 | "
176 |
177 |
178 |
179 |
180 |
181 |
182 | ;
183 |
184 |
185 |
186 |
191 |
192 | {
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 | - "
202 |
203 |
204 |
205 | ";
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 | };
219 |
220 |
221 |
222 |
223 | {
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 | }
233 |
234 |
235 |
236 |
237 |
238 |
--------------------------------------------------------------------------------
/tests/tests.nim:
--------------------------------------------------------------------------------
1 | import std/[xmlparser, unittest, xmltree, json, os]
2 |
3 | import kdl
4 | import kdl/[schema, query, jik, xik]
5 |
6 | let testsDir = getAppDir() / "test_cases"
7 |
8 | suite "spec":
9 | for kind, path in walkDir(testsDir / "input"):
10 | if kind != pcFile:
11 | continue
12 | let filename = path.splitPath.tail
13 | let expectedPath = testsDir / "expected_kdl" / filename
14 |
15 | if filename[0] == '_':
16 | test "Ignore: " & filename:
17 | discard parseKdlFile(path)
18 | elif fileExists(expectedPath):
19 | test "Valid: " & filename:
20 | check readFile(expectedPath) == parseKdlFile(path).pretty()
21 | test "Valid: " & filename & " [Stream]":
22 | check readFile(expectedPath) == parseKdlFileStream(path).pretty()
23 | else:
24 | test "Invalid: " & filename:
25 | expect(KdlError):
26 | discard parseKdlFile(path)
27 | test "Invalid: " & filename & " [Stream]":
28 | expect(KdlError):
29 | discard parseKdlFileStream(path)
30 |
31 | suite "examples":
32 | for kind, path in walkDir(testsDir / "examples"):
33 | if kind != pcFile:
34 | continue
35 |
36 | let filename = path.splitPath.tail
37 |
38 | test "Example: " & filename:
39 | discard parseKdlFile(path)
40 |
41 | suite "XiK":
42 | for kind, path in walkDir(testsDir / "xik"):
43 | if kind != pcFile:
44 | continue
45 |
46 | let filename = path.splitPath.tail
47 |
48 | test "File: " & filename:
49 | let data = loadXml(path)
50 | # Here we compare strings otherwise it fails and I can't see why (maybe because they're refs)
51 | check $data == $data.toKdl(addComments = true).toXml(addComments = true)
52 |
53 | suite "JiK":
54 | for kind, path in walkDir(testsDir / "jik"):
55 | if kind != pcFile:
56 | continue
57 |
58 | let filename = path.splitPath.tail
59 |
60 | test "File: " & filename:
61 | let data = parseFile(path)
62 | check data == data.toKdl().toJson()
63 |
64 | suite "Other":
65 | test "Nodes":
66 | check toKdlArgs("abc", 123, 3.14, true, nil) ==
67 | ["abc".initKVal, 123.initKVal, 3.14.initKVal, true.initKVal, initKNull()]
68 | check toKdlProps({"a": "abc", "b": 123, "c": 3.14, "d": false, "e": nil}) == {
69 | "a": "abc".initKVal,
70 | "b": 123.initKVal,
71 | "c": 3.14.initKVal,
72 | "d": false.initKVal,
73 | "e": initKNull(),
74 | }.toTable
75 |
76 | test "Escaping":
77 | check ($toKdlVal("'")).parseKdl[0] == initKNode("'")
78 |
--------------------------------------------------------------------------------