├── .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 | [![Nim](https://img.shields.io/badge/Made%20with%3A-Nim-yellow?style=flat&logo=nim&logoColor=white)](https://nim-lang.org) 3 | [![Tests](https://github.com/Patitotective/kdl-nim/actions/workflows/tests.yml/badge.svg)](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 | --------------------------------------------------------------------------------