diff --git a/README.md b/README.md index 96f3487..63fe00a 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ For questions and suggestions regarding The Art of C++ / Config, success or fail * use environment variables and the output of arbitrary shell commands. * The function [`tao::config::from_file()`](doc/Parsing-Config-Files.md) is all you need to get going. -Every JSON file with a top-level object can be used as config file. +Every JSON file with a top-level object can be used as [config file](doc/Writing-Config-Files.md). ``` { @@ -41,7 +41,7 @@ Every JSON file with a top-level object can be used as config file. } ``` -This small example can be rendered differently using some of the additional syntactic possibilities of the config file format. +This small example can be rendered differently using some of the additional syntactic possibilities of the [config file](doc/Writing-Config-Files.md) format. ``` #!/usr/local/bin/q3s @@ -51,11 +51,11 @@ port = 27960 maps = [ "ztn" "dm13" "t9" ] // Add dm6 or t4? ``` -Semantic features like deleting and referencing values, or including files and reading environment variables, usually only make sense with larger, non-trivial real-world examples. +Semantic features like [deleting](doc/Writing-Config-Files.md#delete) and [referencing](doc/Writing-Config-Files.md#references) values, or [including files](doc/Writing-Config-Filesmd#include-files) and [reading environment variables](doc/All-Config-Functions.md#env), usually only make sense with larger, non-trivial real-world examples. These features can be used to manage situations that go beyond single deployments with a single config, for example providing the tools to manage configuration templates that are adapted to different environments. -Parsing a config file generally entails nothing more than calling the appropriate `from_file()` function with the filename. +[Parsing](doc/Parsing-Config-Files.md) a [config file](doc/Writing-Config-Files.md) is as simple as calling the appropriate `from_file()` function with the filename. ``` #include @@ -63,8 +63,8 @@ Parsing a config file generally entails nothing more than calling the appropriat const tao::config::value config = tao::config::from_file( "foo.cfg" ); ``` -The resulting value is nothing other but a [JSON Value] from The Art of C++ / JSON with a custom traits class. -It can be inspected using all the facilities of that JSON library. +The resulting value is nothing other but a [JSON Value] from [taoJSON] with a custom traits class that annotates every sub-value with the filename and position it was parsed from. +It can be inspected -- and manipulated -- using all the facilities of that JSON library. ## License @@ -87,5 +87,5 @@ It is distributed under the terms of the [MIT license] reproduced here. [JSON Value]: https://github.com/taocpp/json/ [MIT license]: http://www.opensource.org/licenses/mit-license.html [Open Source]: http://www.opensource.org/docs/definition.html -[taocpp/json]: https://github.com/taocpp/json/ +[taoJSON]: https://github.com/taocpp/json/ [The Art of C++]: https://taocpp.github.io/ diff --git a/doc/Value-Extensions.md b/doc/All-Config-Functions.md similarity index 67% rename from doc/Value-Extensions.md rename to doc/All-Config-Functions.md index 10fcc96..5b93f2d 100644 --- a/doc/Value-Extensions.md +++ b/doc/All-Config-Functions.md @@ -1,7 +1,4 @@ -# Value Extensions - -Value extensions produce a [JAXN] value and can be used wherever a value is expected. -Note that whitespace is significant within value extensions, i.e. whitespace MUST be used as shown and comments are forbidden. +# All Config Functions * [binary](#binary) * [cbor](#cbor) @@ -17,12 +14,15 @@ Note that whitespace is significant within value extensions, i.e. whitespace MUS * [string](#string) * [ubjson](#ubjson) +This page is the reference documentation for all included config functions. + +The [general information on functions and how to use them can be found here.](Writing-Config-Files.md#functions) ## binary -The `binary` value extension explicitly transforms a string value into a binary value. -Only the type is changed, the represented sequence of bytes is not changed. +The `binary` function explicitly transforms a string value into a binary value. +Only the type is changed, the sequence of bytes stays the same. #### Example taoCONFIG Input File @@ -39,10 +39,9 @@ foo = (binary "Hello, world!") ``` - ## cbor -The `cbor` value extension parses binary data as [CBOR] and returns the resulting value. +The `cbor` function parses binary data as [CBOR] and returns the resulting value. #### Example taoCONFIG Input File @@ -66,14 +65,31 @@ Note that, in line with the JSON data model, only UTF-8 strings are supported as Note that `cbor` is frequently combined with `read` as in `foo = (cbor (read "filename.cbor"))`. - ## default +The `default` function takes one or more arguments and returns the first one that is not a JSON `null`. +It is an error if all arguments are `null`. + +#### Example taoCONFIG Input File + +``` +foo = (default 1 2) +bar = (default null false true) +``` + +#### Resulting JAXN Config Data + +```javascript +{ + bar: false, + foo: 1 +} +``` ## env -The `env` value extensions obtain the value of an environment variable as string. +The `env` functions obtain the value of an environment variable as string. For plain `env` it is an error when the environment variable does not exist, the `env?` alternative form returns a default value. #### Example taoCONFIG Input File @@ -93,10 +109,9 @@ bar = (env? "GRMBLFX" "default") ``` - ## jaxn -The `jaxn` value extension parses string (or binary) data as [JAXN] and returns the resulting value. +The `jaxn` function parses string (or binary) data as [JAXN] and returns the resulting value. In the case of binary data the input is automatically converted to a string, including a check for valid UTF-8. #### Example taoCONFIG Input File @@ -116,13 +131,12 @@ foo = (jaxn '[Infinity, $ff]') } ``` -Note that `jaxn` is frequently combined with `string` and `read` as in `foo = (jaxn (read "filename.jaxn"))`. - +Note that `jaxn` is frequently combined with `read` as in `foo = (jaxn (read "filename.jaxn"))`. ## json -The `json` value extension parses string (or binary) data as [JSON] and returns the resulting value. +The `json` function parses string (or binary) data as [JSON] and returns the resulting value. In the case of binary data the input is automatically converted to a string, including a check for valid UTF-8. #### Example taoCONFIG Input File @@ -145,7 +159,6 @@ foo = (json '["a","b"]') Note that `json` is frequently combined with `read` as in `foo = (json (read "filename.json"))`. - ## msgpack The `msgpack` value extension parses binary data as [MsgPack] and returns the resulting value. @@ -170,7 +183,6 @@ foo = (msgpack $82a161c3a162c2) Note that `msgpack` is frequently combined with `read` as in `foo = (msgpack (read "filename.msgpack"))`. - ## parse The `parse` value extension parses the given string as a single config value just "as if" the config file contained the string instead of the invocation of `parse`. @@ -189,13 +201,14 @@ foo = (parse "null") } ``` -Note that the value described in the string is *not* allowed to use addition/concatenation, however references and/or other value extensions *are* allowed. +This can be useful when combined with [`env`](#env) for environment variables that contain numeric values as in `foo = (parse (env "MYVAR"))`. +Note that the value described in the string is *not* allowed to use addition/concatenation, however references and/or other value extensions *are* allowed. ## read -The `read` file extension reads the contents of a file and returns them as binary data. +The `read` function returns the contents of a file as binary data. #### Example taoCONFIG Input File @@ -215,13 +228,13 @@ bar = (string (read "tests/doc_value_read.config")) Note that `read` can be combined with `string` to validate the data as UTF-8 and transform it into a string. -Note that the conversion from `binary` to `string` is automatic when the binary data is passed to an extension that expects a string. - +Note that the conversion from `binary` to `string` is automatic when the binary data is passed to a function that expects a string. +Like [`string`](#string), the automatic conversion checks whether the binary data is a valid UTF-8 sequence and throws an exception if that is not the case. ## shell -The `shell` value extension execute the given string as shell script and returns its output. +The `shell` function executes the given string as shell script and returns its output. #### Example taoCONFIG Input File @@ -238,13 +251,13 @@ foo = (shell "uname -s") ``` Note that availability and behaviour of the `shell` value extension are inherently system dependent. -Currently it is only supported on Unix-style operating system like Linux and macOS. - +Currently it is only supported on Unix-style operating systems that are sufficiently POSIX compliant, most prominently Linux and macOS. ## split -The `split` value extensions splits a string into its space-separated components and returns an array of them where "space" is a non-empty sequence of `' ', '\n', '\r', '\t', '\v' and/or '\f'` characters. +The `split` function splits a string into its space-separated components and returns an array of them. +Here "space" is a non-empty sequence of `' ', '\n', '\r', '\t', '\v' and/or '\f'` characters. #### Example taoCONFIG Input File @@ -265,10 +278,9 @@ foo = (split "a b c ") ``` - ## string -The `string` value extension transforms a binary value into a string value. +The `string` function transforms a binary value into a string value. It validates that the binary data is valid UTF-8 and produces an error if that is not the case. #### Example taoCONFIG Input File @@ -286,12 +298,12 @@ foo = (string $48656C6C6F2C20776F726C6421) ``` Note that the conversion from `binary` to `string` is automatic when the binary data is passed to an extension that expects a string. - +The automatic conversion, too, checks whether the binary data is a valid UTF-8 sequence and throws an exception if that is not the case. ## ubjson -The `ubjson` value extension parses binary data as [UBJSON] and returns the resulting value. +The `ubjson` value function parses binary data as [UBJSON] and returns the resulting value. #### Example taoCONFIG Input File diff --git a/doc/Changelog.md b/doc/Changelog.md index 623d660..5e3467f 100644 --- a/doc/Changelog.md +++ b/doc/Changelog.md @@ -8,6 +8,8 @@ Pre-1.0.0 milestones in rough reverse chronological order. +* Integrate value extensions with phase 2. +* Reduce member extensions to the essentials. * Remove the minus token from config keys. * Change the semantics of star and move evaluation to phase two. * Refactor everything in order to simplify the implementation. @@ -20,6 +22,8 @@ Pre-1.0.0 milestones in rough reverse chronological order. Development of taoCONFIG started in September 2018 to provide a more expressive config file syntax for [JSON] (or [JAXN]) config files. + + Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey [JAXN]: https://github.com/stand-art/jaxn diff --git a/doc/Member-Extensions.md b/doc/Member-Extensions.md deleted file mode 100644 index a85d622..0000000 --- a/doc/Member-Extensions.md +++ /dev/null @@ -1,240 +0,0 @@ -# Member Extensions - -Member extensions use the same syntax as value extensions, however they take the place of JSON object members. -Note that whitespace is significant within member extensions, i.e. whitespace must be used as shown and comments are forbidden. - - * [include](#include) - * [parse](#parse) - * [permanent](#permanent) - * [schema](#schema) - * [temporary](#temporary) - - - -## include - -The `include` member extension reads the named file relative to the current working directory and parses it "as if" the contents of that file were present instead of the `include`. -For plain `include` it is an error when the file does not exist, the `include?` alternative form ignores missing files. - -#### Example taoCONFIG Input File - -``` -// Include the file whose contents are shown below. -(include "tests/doc_member_include.inc") - -foo -{ - // Include the same file again, this time within an object. - (include "tests/doc_member_include.inc") -} - -(include? "tests/non_existing_file_is_no_error_with_include?") -``` - -#### Example taoCONFIG Include File - -``` -bar = 42 -baz = [ true, false ] -``` - -#### Resulting JAXN Config Data - -```javascript -{ - bar: 42, - baz: [ - true, - false - ], - foo: { - bar: 42, - baz: [ - true, - false - ] - } -} -``` - - - -## parse - -The `parse` member extension parses the given string as "as if" the contents of that string were present instead of the `parse`. - -#### Example taoCONFIG Input File - -``` -(parse "a = 42") - -foo -{ - b = true - c = false - (parse "b = delete c = true") -} -``` - -#### Resulting JAXN Config Data - -```javascript -{ - a: 42, - foo: { - c: true - } -} -``` - - - -## permanent - -The `permanent` member extension is used to undo the effects of [`temporary`](#temporary). - - - -## schema - -The `schema` member extension tells the config library which [schema file](Writing-Schema-Files.cfg) the config must adhere to. -After reading the config file(s), the schema is read, and the config, more precisely: the config (sub-)object of which the schema was set, is checked against it. - -#### Example taoCONFIG Input File - -``` -ip = "127.0.0.1" -port = 42 - -(schema "tests/doc_member_schema.schema") -``` - -#### Example taoCONFIG Schema File - -``` -definitions -{ - port - { - type: "std.unsigned" - minimum: 1 - maximum: 65535 - } -} - -properties -{ - required - { - ip: "std.net.ip_v4_address" - port: "port" - } -} -``` - -#### Resulting JAXN Config Data - -```javascript -{ - ip: "127.0.0.1", - port: 42 -} -``` - -For more information on taoCONFIG schemas [please consult the page on how to write schema files](Writing-Schema-Files.md). - -Schemas can also be applied to sub-sections of the config by putting the schema member in the appropriate sub-section. - -#### Example taoCONFIG Input File - -``` -foo.bar -{ - ip = "127.0.0.1" - port = 42 - - (schema "tests/doc_member_schema.schema") -} - -what = "ever" // Outside scope of schema. -``` - -#### Resulting JAXN Config Data - -```javascript -{ - foo: { - bar: { - ip: "127.0.0.1", - port: 42 - } - }, - what: "ever" -} -``` - -The schema member can occur multiple times at top-level or within a sub-section, however the last one "wins" and only a single schema will actually be used. -In order to "cancel" a schema checking the schema member can be used with `null`. - -``` -(schema null) // Remove any top-level schema checking. -``` - - - -## temporary - -The `temporary` member extension marks a sub-tree of the JSON as temporary, meaning that it will be removed from the final resulting JSON value. -This is frequently used for templates that are referenced somewhere else in the config. - -#### Example taoCONFIG Input File - -``` -(temporary template) // Can occur before... - -template -{ - host = "127.0.0.1" - port = 6000 - version = 42 -} - -foo = (template) + -{ - host = "127.0.0.2" -} - -bar = (template) + -{ - port = 6001 -} - -(temporary template) // ...and/or after. - -``` - -#### Resulting JAXN Config Data - -```javascript -{ - bar: { - host: "127.0.0.1", - port: 6001, - version: 42 - }, - foo: { - host: "127.0.0.2", - port: 6000, - version: 42 - } -} -``` - - - -Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey - -[JAXN]: https://github.com/stand-art/jaxn -[JSON]: https://tools.ietf.org/html/rfc8259 -[taoCONFIG]: https://github.com/taocpp/config -[taoJSON]: https://github.com/taocpp/json diff --git a/doc/Parsing-Config-Files.md b/doc/Parsing-Config-Files.md index 1614204..66476a1 100644 --- a/doc/Parsing-Config-Files.md +++ b/doc/Parsing-Config-Files.md @@ -6,10 +6,12 @@ * [Custom Traits](#custom-traits) * [Builtin Schema](#builtin-schema) -This library requires decent C++17 support, currently tested on GCC 8 and 9 and "recent" Clang. +This library requires decent C++17 support, currently supported are GCC 9 and Clang 11 (or newer). The following assumes that the [PEGTL], [taoJSON] and [taoCONFIG] are all available to your compiler, i.e. the include paths are set up correctly. -Then to access the facilities of this library please simply include `` in your source(s). +When checking out [taoCONFIG] via git this can be achieved by (recursively!) initialising and updating all git submodules. + +To access the facilities of this library simply include `` in your source(s). ## Parsing @@ -50,7 +52,7 @@ These public data members are: ```c++ tao::config::key key; - tao::json::position position; // tao/json/contrib/position.hpp + tao::json::position position; // From tao/json/contrib/position.hpp ``` ## Custom Traits @@ -79,13 +81,7 @@ The type `tao::config::annotation` implements the following non-static member fu However these functions are only called when they are available, so a custom traits class template can choose to omit the annotations by either not setting up a public base class for the [taoJSON] values or by not implementing either or both of these functions in the public base. -## Builtin Schema - -All of the config parsing functions actually have a further argument, a const-reference to an object of type `tao::config::schema::builtin`. -This argument is defaulted to a default-constructed object of that type. -The role of this `builtin` instance is to provide the built-in schema definitions that can then be extended by the schema files. -The built-in definitions can be programmatically extended by passing an appropriately prepared instance of `tao::config::schema::builtin` as final argument to the parsing functions. Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey diff --git a/doc/README.md b/doc/README.md index f0ac9b8..7e8285d 100644 --- a/doc/README.md +++ b/doc/README.md @@ -2,10 +2,10 @@ * [Project](https://github.com/taocpp/config) * [Writing Config Files](Writing-Config-Files.md) - * [Value Extensions](Value-Extensions.md) - * [Member Extensions](Member-Extensions.md) - * [Writing Schema Files](Writing-Schema-Files.md) + * [All Config Functions](All-Config-Functions.md) * [Parsing Config Files](Parsing-Config-Files.md) * [Changelog](Changelog.md) + + Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey diff --git a/doc/Writing-Config-Files.md b/doc/Writing-Config-Files.md index c09e8f1..7dc456c 100644 --- a/doc/Writing-Config-Files.md +++ b/doc/Writing-Config-Files.md @@ -1,10 +1,11 @@ # Writing Config Files The config file syntax is based on [JSON] and is completely backwards compatible: -**Every [JSON]** or [JAXN] **file** with a top-level [JSON] object **is a valid [taoCONFIG] file**. + +> **Every [JSON]** (or [JAXN]) **file** with a top-level [JSON] object **is a valid [taoCONFIG] file**. The data model is also based on [JSON] and corresponds exactly to the [JAXN] data model as implemented by the [taoJSON] library. -We assume that the reader is somewhat familiar with [JSON]. +We assume that the reader is somewhat familiar with [JSON] and its terminology. * [General Syntax](#general-syntax) - [Example](#example) @@ -23,19 +24,20 @@ We assume that the reader is somewhat familiar with [JSON]. - [Additions](#additions) - [Overwrite](#overwrite) - [Delete](#delete) - - [Asterisks](#asterisk) + - [Asterisks](#asterisks) - [References](#references) - - [Extensions](#extensions) + - [Functions](#functions) - [Dotted Names](#dotted-names) - -Please note that, for now, this document shows basic use cases for all features, but not much beyond that. -It also does not go into the details and complexities of how all the features (can) interact with each other. + - [Temporaries](#temporaries) + - [Include Files](#include-files) + * [Advanced Considerations](#advanced-considerations) # General Syntax This first section is in tutorial form. +It starts with a simple config file example in JSON form and successively introduces the main syntactic features of taoCONFIG by changing the example to use these features. ## Example @@ -287,7 +289,7 @@ Strings are like in [JAXN], i.e. [JSON] strings with [extensions](https://github ## Binary Values -[Binary data](https://github.com/stand-art/jaxn/blob/main/Specification.md#binary-data) is also like in [JAXN]. +[Binary data](https://github.com/stand-art/jaxn/blob/main/Specification.md#binary-data) is also exactly like in [JAXN]. @@ -328,7 +330,7 @@ foo += "c" // foo is "abc" ``` -Like in [JAXN] the same concatenations can be performed for binary data, and strings and binary data can not be mixed. +Like in [JAXN] the same concatenations can be performed for binary data, and strings and binary data can **not** be mixed. #### Arrays @@ -349,8 +351,8 @@ For objects the addition can be explicit or implicit and performs an object "mer For keys that exist only in one of the two objects being merged the result of the merge will contain that object member unchanged. -For keys that exist in both of the objects being merged the value of the member in the result will depend on whether the second objects used a `=` or a `+=` to define the member. -In the first case the member from the second object overwrites the member from the first, in the second case the corresponding values are in turn added. +For keys that exist in both of the objects being merged the value of the member in the result will depend on whether the second object used a `=` or a `+=` to define the member. +In the case of `=` the member from the second object overwrites the member of the first, in the case of `+=` the corresponding values are recursively merged. #### Example taoCONFIG Input File @@ -382,9 +384,10 @@ foo } ``` + ## Overwrite -A named object member can be assigned to multiple times in which case the last assignment "wins". +A named object member can be assigned-to multiple times with the final assignment "winning". #### Example taoCONFIG Input File @@ -440,18 +443,28 @@ maps = delete // Changed our minds, no maps. Deleted values can subsequently be assigned new values or added-to in which case they behave "as if" anything that was assigned or added prior to the `delete` never happened. +#### Example taoCONFIG Input File + ``` foo = 42 foo = delete foo += 44 ``` -Unlike the other pseudo-values `delete` is not allowed to be in a syntactic addition. +#### Resulting JSON Config Data + +```javascript +{ + foo: 44 +} +``` + +Note that `delete` may not be an operand of an addition, i.e. `42 + delete` or similar are **not** allowed. ## Asterisks -The asterisk can be used as special object member name to designate all array or object members. +The asterisk can be used as special name to designate all array or object members. It is frequently used in conjunction with [dotted names](#dotted-names) to form a pattern for an assignment or addition. #### Example taoCONFIG Input File @@ -486,12 +499,13 @@ servers.*.port = 7000 // Testing } ``` -Assignments using asterisks can be combined with the literal pseudo-value `delete`. +Assignments using asterisks **can** be combined with the literal pseudo-value `delete`. ## References -References are (nested) dotted names written in parentheses that refer to other parts of the config and copy the referenced value. +References are (nested) dotted names written in parentheses that refer to other parts of the config. +During processing of the config file they copy the referenced value. ``` foo = 42 @@ -499,8 +513,6 @@ bar = (foo) // bar is 42 ``` -To uniformly support forwards and backwards references, they and [additions](#additions) are resolved *after* the config has been parsed, i.e. after all assignments and [extensions](#extensions). - References are resolved relative to the array or object scope in which they syntactically occur, similar to how name lookups from within nested namespaces are handled in the C++ programming language. #### Example taoCONFIG Input File @@ -548,19 +560,19 @@ x = 1 } ``` -It is an error if there is no order in which all additions and references can be resolved due to some kind of cyclic references. -It is also an error when the first component of a reference matches an inner scope in which the other parts can not be resolved *even if* they could have been resolved in an enclosing scope. +It is an error if there is no order in which all additions, functions and references can be resolved due cyclic references! #### Example Broken Input Files -Cyclic reference is an error: +Cyclic references are an error for obvious reasons. ``` a = (b) b = (a) ``` -While resolving `(a.b)` within `a.a` the first time the first `a` of `(a.b)` matches while moving from inner to outer scopes is the inner `a` in which no `b` exists which is an error: +It is also an error when a reference can't be resolved due to the first component of the reference "anchoring" the lookup to the "wrong" place. +Consider the following example: ``` a @@ -573,8 +585,21 @@ a } ``` -References can be arbitrarily **nested** meaning that the parts ofa reference can themselves be references. -For this to work the inner references must refer to values that can occur as part of a reference, namely strings and integers. +The lookup of `a.b` starts by attempting to find `a` inside of `a.a`. +This fails because there is only `c` within `a.a`, not another `a` that could be matched to the `a` in `a.b`. +Next the lookup steps outside of `a.a` and attempts to find the `a` from `a.b` within `a`. +This succeeds, anchoring the lookup of `a.b` to `a.a`. +More precisely, the second component of `a.a` is matched with the first component of `a.b`. +Now the example generates an error because there is no `b` within `a.a`. + +The search is not continued inside the top-level `a` because once the lookup is anchored no backtracking is performed. +This is similar to the name lookup rules in the C++ programming language. + + +### Nested References + +References can be arbitrarily **nested**, that is parts of a reference can themselves be a reference! +For this to work the "inner" reference must refer to a value that can natuarlly occur as part of a reference, namely a string or an integer. #### Example taoCONFIG Input File @@ -600,8 +625,9 @@ baz = (foo.(bar)) } ``` -When referencing values marked as [temporary](Member-Extensions.md#temporary) the copied value will not be marked as temporary, however any sub-values will retain their temporary status. -Similarly schema definitions will be retained only for sub-values of the referenced value (TODO: Is this really what we want?) +### References vs. Temporary + +When referencing values marked as [temporary](Member-Extensions.md#temporary) the copied value will **not** be marked as [temporary](#temporaries), however any sub-values will retain their temporary status. #### Example taoCONFIG Input File @@ -637,60 +663,50 @@ ttt = (foo) ``` -## Extensions - -Extensions are expressions that, like references, are written in parentheses and give access to same *extended* features. - -[Value extensions](Value-Extensions.md) produce a [JAXN] value and can be used wherever a value is expected. +## Functions -For example the `split` value extension splits a string into its space-separated components. +This library contains [a set of config functions](All-Config-Functions.md). +These functions can be used in config files to perform operations on values. +Applications can also add custom functions. -``` -foo = (split "a 1 ?") // foo = [ "a", "a", "?" ] -``` +Functions are syntactically similar to references in taoCONFIG, and to lists or function calls in Scheme and LISP. +A function call has one or more arguments, wherefore `(foo)` is parsed as a reference rather than a call to a nullary function. -[Member extensions](Member-Extensions.md) use the same syntax as value extensions, however they take the place object members. - -For example the `include` member extensions takes a string, interprets it as filename, and reads and parses its contents in place of the `include` extension. +For example the following config file assigns the value of the environment (shell) variable `$USER` to the config key `foo`. ``` -(include "filename.cfg") +foo = (env "USER") ``` -For a list and description of all available extensions please read the pages on [value extensions](Value-Extensions.md) and [member extensions](Member-Extensions.md). - -Value extensions can be nested quite freely, however the inner-most arguments must be literals as extensions can *not* access config values or use references. +Functions can occur anywhere a value can occur. +A function's arguments can be literal values, other function calls, additions, references, or any combination or nesting of these. -For example the `split`, `read` and `env` extensions can be combined and nested like this. +The next example does the same as the previous one, albeit in a slightly contrived manner that shows a composed function argument with reference. ``` -foo = (split (read (env "IP_LIST_FILE"))) -``` - -On the other hand moving the inner-most argument into a separate definition and accessing it from inside the nested extensions does *not* work. +(temporary bar) -``` -bar = "IP_LIST_FILE -foo = (split (read (env (bar)))) // Parse error! +bar = "ER" +foo = (env "US" + (bar)) ``` -Member extensions can similarly contain nested value extensions. +The config functions supplied with this library are listed and explained on [a separate page](All-Config-Functions.md). -``` -(include (read (env "INCLUDE_FILE_NAME_FILE"))) -``` +To add custom functions to a config parser and make them available to your config files please consult `src/test/config/custom.cpp` or contact the developers of this library. ## Dotted Names Wherever the name of an object member is expected it is also possible to use a name with multiple dot-separated components. -Dotted names can contain kinds of components. +Dotted names can contain multiple kinds of components. 1. Strings, intended to index objects. 2. Unsigned integers, intended to index arrays. 4. The [asterisk](#asterisks), intended to index objects or arrays. +The asterisk indexes into all object members or array elements. + #### Example taoCONFIG Input File ``` @@ -720,11 +736,197 @@ foo.bar.baz += 1 Just as for single-component names, integers, and other strings that are not C-style identifiers can be used with (single or double) quotes. For example the dotted name `foo."1.2.3".bar` consists of three components, the strings `"foo"`, `"1.2.3"` and `"bar"`. -Dotted names can be thought of as strongly-typed JSON Pointers: there is no ambiguity about whether an integer is intended to index an array or an object. +Dotted names can be thought of as strongly-typed JSON Pointers; there is no ambiguity about whether an integer is intended to index an array or an object. + + +## Include Files + +The `include` feature reads the named file relative to the current working directory and parses it "as if" the contents of that file were present instead of the `include`. +For plain `include` it is an error when the file does not exist, the `include?` alternative form silently ignores this case. + +The filename has to be supplied as non-empty string in double quotes and can contain arbitrary characters except for the double quote itself. +Consequently, and unlike all other quoted strings in this library, *no escape sequences* are recognised. + +Files can be included anywhere an object member can be defined. +The top-level definitions of the included file will appear in the same place of the JSON hierarchy as the `include`. + +#### Example taoCONFIG Input File + +``` +// Include the file whose contents are shown below. +(include "tests/doc_include.inc") + +foo +{ + // Include the same file again, this time within an object. + (include "tests/doc_include.inc") +} + +// Use include? with a non-existing file, not an error. +(include? "tests/non_existing_file_is_no_error_with_include?") +``` + +#### Example taoCONFIG Include File + +``` +bar = 42 +baz = [ true, false ] +``` + +#### Resulting JAXN Config Data + +```javascript +{ + bar: 42, + baz: [ + true, + false + ], + foo: { + bar: 42, + baz: [ + true, + false + ] + } +} +``` + + +## Temporaries + +Sometimes parts of the configuration that are required during config file parsing, for example in order to be [referenced](#references), are not themselves considered part of the actual config. +In these cases the `temporary` feature can be used to exclude any part of the JSON hierarchy from the final config parsing result. + +Like `include` the `temporary` statement can occur anywhere an object member can be defined. +It takes a single config key, which can include [asterisks](#asterisks) and be any [dotted name](#dotted-names), as argument. + +The argument is always interpreted relative to the position of the `temporary` in the object hierarchy. +For example a `(temporary c.d)` within an object at `a.b` would mark `a.b.c.d` as temporary, independent of whether that node exists (yet). + +#### Example taoCONFIG Input File + +``` +(temporary template) // Can occur before... + +template +{ + host = "127.0.0.1" + port = 6000 + version = 42 +} + +foo = (template) + +{ + host = "127.0.0.2" +} + +bar = (template) + +{ + port = 6001 +} + +(temporary template) // ...and/or after. + +``` + +#### Resulting JAXN Config Data + +```javascript +{ + bar: { + host: "127.0.0.1", + port: 6001, + version: 42 + }, + foo: { + host: "127.0.0.2", + port: 6000, + version: 42 + } +} +``` + +Similarly the `permanent` feature can be used to restore any part of the JSON hierarchy that was marked as `temporary` to the default behaviour of being part of the final result data. + + + +# Advanced Considerations + +For technical reasons reading a configuration is performed in multiple steps. +Authors of config files need to be aware of the two main phases in order to fully understand the semantics of their configs. + +### Phase One + +The first phase does the reading and parsing of all configuration files. + +During this phase all assignments, additions and deletions are recorded in their order of occurrence. +The same goes for included files, their assignments, additions and deletions are performed when and where the include was encountered during parsing. + +### Phase Two + +The second phase successively eliminates all functions, references and additions. + +During this phase the order in which things were parsed is no longer relevant. + + * Functions are eliminated as soon as their arguments are "plain" values, i.e. they are free from functions, references and additions, by replacing the function with the result of calling said function. + * References are eliminated as soon as the referenced value is "plain", by replacing the reference with a (deep) copy of the referenced value. + * Additions are eliminated as soon as both operands are "plain", again by replacing the two operands with the result of the addition (which can be a concatenation, or, for objects, a merge). + +### Why Phases? + +The semantics of `delete`, `+=` and `=`, and, by extension, `include`, depends on the order in which they are processed. +This is due to the combination of features being non-monotonic, which requires linear processing for predictable behaviour. + +However for references this would be too limiting as only previously defined values could be referenced, and delta[^1] config files might not be able to make the required modifications. +With references that can "point" both forwards and backwards this problem does not arise. + +#### Example taoCONFIG Input File + +``` +hosts +{ + arctic + { + foo = 42 + } + mystic + { + foo = 23 + } +} +host = (env "HOST") + +(temporary host) +(temporary hosts) + +config +{ + mode = "FOBBLE" + size = (hosts.(host).foo) +} + +(include? "test_delta_config.cfg") +``` + +This example shows a configuration with parameters that depend on the name of the host machine. + +#### Example taoCONFIG Delta File + +``` +hosts.test01.foo = 3 +``` + +If references were resolved when encountered then the delta config would have no effect -- the `foo` value for the additional host `test01` would be available only *after* the refernce for `config.size` was resolved. + +Luckily this is not how taoCONFIG works, the references are resolved during the second phase, and everything will work as expected. + Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey +[^1]: A "delta" config is typically a configuration file that is optionally included at the end of a full configuration file. The full configuration file usually configures an application for an operational environment; the delta config can optionally modify the full configuration, e.g. for a test environment that might be funcionally identical to the operational environment but have minor differences like using different hostnames. + [CBOR]: http://cbor.io [JAXN]: https://github.com/stand-art/jaxn [JSON]: https://tools.ietf.org/html/rfc8259 diff --git a/include/tao/config.hpp b/include/tao/config.hpp index 9fb5f75..3b2d02d 100644 --- a/include/tao/config.hpp +++ b/include/tao/config.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2023 Dr. Colin Hirsch and Daniel Frey +// Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey // Please see LICENSE for license or visit https://github.com/taocpp/config/ #ifndef TAO_CONFIG_HPP @@ -17,6 +17,5 @@ #include "config/assign.hpp" #include "config/parser.hpp" -#include "config/schema.hpp" #endif diff --git a/include/tao/config/from_file.hpp b/include/tao/config/from_file.hpp index b28df0a..2c72a2c 100644 --- a/include/tao/config/from_file.hpp +++ b/include/tao/config/from_file.hpp @@ -8,22 +8,21 @@ #include #include "internal/config_parser.hpp" -#include "schema/builtin.hpp" #include "value.hpp" namespace tao::config { template< template< typename... > class Traits > - [[nodiscard]] json::basic_value< Traits > basic_from_file( const std::filesystem::path& path, const schema::builtin& b = schema::builtin() ) + [[nodiscard]] json::basic_value< Traits > basic_from_file( const std::filesystem::path& path ) { internal::config_parser c; c.parse( path ); - return c.finish< Traits >( b ); + return c.finish< Traits >(); } - [[nodiscard]] inline value from_file( const std::filesystem::path& path, const schema::builtin& b = schema::builtin() ) + [[nodiscard]] inline value from_file( const std::filesystem::path& path ) { - return basic_from_file< traits >( path, b ); + return basic_from_file< traits >( path ); } } // namespace tao::config diff --git a/include/tao/config/from_files.hpp b/include/tao/config/from_files.hpp index 9c43457..fba4fd0 100644 --- a/include/tao/config/from_files.hpp +++ b/include/tao/config/from_files.hpp @@ -9,24 +9,23 @@ #include #include "internal/config_parser.hpp" -#include "schema/builtin.hpp" #include "value.hpp" namespace tao::config { template< template< typename... > class Traits > - [[nodiscard]] json::basic_value< Traits > basic_from_files( const std::vector< std::filesystem::path >& paths, const schema::builtin& b = schema::builtin() ) + [[nodiscard]] json::basic_value< Traits > basic_from_files( const std::vector< std::filesystem::path >& paths ) { internal::config_parser c; for( const auto& path : paths ) { c.parse( path ); } - return c.finish< Traits >( b ); + return c.finish< Traits >(); } - [[nodiscard]] inline value from_files( const std::vector< std::filesystem::path >& paths, const schema::builtin& b = schema::builtin() ) + [[nodiscard]] inline value from_files( const std::vector< std::filesystem::path >& paths ) { - return basic_from_files< traits >( paths, b ); + return basic_from_files< traits >( paths ); } } // namespace tao::config diff --git a/include/tao/config/from_input.hpp b/include/tao/config/from_input.hpp index 337831b..4d405ba 100644 --- a/include/tao/config/from_input.hpp +++ b/include/tao/config/from_input.hpp @@ -8,22 +8,21 @@ #include "internal/config_parser.hpp" #include "internal/pegtl.hpp" -#include "schema/builtin.hpp" #include "value.hpp" namespace tao::config { template< template< typename... > class Traits > - [[nodiscard]] json::basic_value< Traits > basic_from_input( pegtl_input_t&& in, const schema::builtin& b = schema::builtin() ) + [[nodiscard]] json::basic_value< Traits > basic_from_input( pegtl_input_t&& in ) { internal::config_parser c; c.parse( std::move( in ) ); - return c.finish< Traits >( b ); + return c.finish< Traits >(); } - [[nodiscard]] inline value from_input( pegtl_input_t&& in, const schema::builtin& b = schema::builtin() ) + [[nodiscard]] inline value from_input( pegtl_input_t&& in ) { - return basic_from_input< traits >( std::move( in ), b ); + return basic_from_input< traits >( std::move( in ) ); } } // namespace tao::config diff --git a/include/tao/config/from_string.hpp b/include/tao/config/from_string.hpp index 318de57..6de2a10 100644 --- a/include/tao/config/from_string.hpp +++ b/include/tao/config/from_string.hpp @@ -9,33 +9,32 @@ #include #include "internal/config_parser.hpp" -#include "schema/builtin.hpp" #include "value.hpp" namespace tao::config { template< template< typename... > class Traits > - [[nodiscard]] json::basic_value< Traits > basic_from_string( const char* data, const std::size_t size, const std::string& source, const schema::builtin& b = schema::builtin() ) + [[nodiscard]] json::basic_value< Traits > basic_from_string( const char* data, const std::size_t size, const std::string& source ) { internal::config_parser c; c.parse( data, size, source ); - return c.finish< Traits >( b ); + return c.finish< Traits >(); } template< template< typename... > class Traits > - [[nodiscard]] json::basic_value< Traits > basic_from_string( const std::string_view data, const std::string& source, const schema::builtin& b = schema::builtin() ) + [[nodiscard]] json::basic_value< Traits > basic_from_string( const std::string_view data, const std::string& source ) { - return basic_from_string< Traits >( data.data(), data.size(), source, b ); + return basic_from_string< Traits >( data.data(), data.size(), source ); } - [[nodiscard]] inline value from_string( const char* data, const std::size_t size, const std::string& source, const schema::builtin& b = schema::builtin() ) + [[nodiscard]] inline value from_string( const char* data, const std::size_t size, const std::string& source ) { - return basic_from_string< traits >( data, size, source, b ); + return basic_from_string< traits >( data, size, source ); } - [[nodiscard]] inline value from_string( const std::string_view data, const std::string& source, const schema::builtin& b = schema::builtin() ) + [[nodiscard]] inline value from_string( const std::string_view data, const std::string& source ) { - return from_string( data.data(), data.size(), source, b ); + return from_string( data.data(), data.size(), source ); } } // namespace tao::config diff --git a/include/tao/config/internal/argument_traits.hpp b/include/tao/config/internal/argument_traits.hpp deleted file mode 100644 index 795a4e4..0000000 --- a/include/tao/config/internal/argument_traits.hpp +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright (c) 2020-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_ARGUMENT_TRAITS_HPP -#define TAO_CONFIG_INTERNAL_ARGUMENT_TRAITS_HPP - -#include -#include -#include -#include -#include - -#include "forward.hpp" -#include "inner_extensions.hpp" -#include "json.hpp" -#include "json_traits.hpp" -#include "key1.hpp" -#include "parse_utility.hpp" -#include "pegtl.hpp" - -namespace tao::config::internal -{ - template< typename T > - struct argument_traits - { - argument_traits( pegtl_input_t& in, state& st, const extension_maps& em ) - : m_j( do_inner_extension( in, st, em ) ) - {} - - [[nodiscard]] T convert() const - { - try { - return m_j.as< T >(); - } - catch( const std::logic_error& e ) { - throw std::runtime_error( e.what() ); - } - } - - private: - const json_t m_j; - }; - - template<> - struct argument_traits< std::string > - { - argument_traits( pegtl_input_t& in, state& st, const extension_maps& em ) - : m_p( in.position() ), - m_s( convert_impl( do_inner_extension( in, st, em ) ) ) - {} - - [[nodiscard]] const std::string& convert() const - { - return m_s; - } - - private: - const pegtl::position m_p; - const std::string m_s; - - [[nodiscard]] std::string convert_impl( json_t&& j ) - { - if( j.type() == json::type::STRING ) { - return std::move( j.get_string() ); - } - if( j.type() == json::type::BINARY ) { - const auto bv = j.as< tao::binary_view >(); - std::string s( reinterpret_cast< const char* >( bv.data() ), bv.size() ); - if( !json::internal::validate_utf8_nothrow( s ) ) { - throw pegtl::parse_error( "invalid utf-8 in extension result", m_p ); - } - return s; - } - throw pegtl::parse_error( "invalid type for string", m_p ); - } - }; - - template<> - struct argument_traits< std::string_view > - {}; - - template<> - struct argument_traits< json_t > - { - argument_traits( pegtl_input_t& in, state& st, const extension_maps& em ) - : m_j( do_inner_extension( in, st, em ) ) - {} - - [[nodiscard]] const json_t& convert() const - { - return m_j; - } - - private: - const json_t m_j; - }; - - template<> - struct argument_traits< key1 > - { - argument_traits( pegtl_input_t& in, state& /*unused*/, const extension_maps& /*unused*/ ) - : m_k( parse_key1( in ) ) - {} - - [[nodiscard]] const key1& convert() const noexcept - { - return m_k; - } - - private: - const key1 m_k; - }; - - template<> - struct argument_traits< pegtl::position > - { - argument_traits( pegtl_input_t& in, state& /*unused*/, const extension_maps& /*unused*/ ) - : m_p( in.position() ) - {} - - [[nodiscard]] const pegtl::position& convert() const noexcept - { - return m_p; - } - - private: - const pegtl::position m_p; - }; - - template<> - struct argument_traits< state > - { - argument_traits( pegtl_input_t& /*unused*/, state& st, const extension_maps& /*unused*/ ) - : m_st( st ) - {} - - [[nodiscard]] state& convert() const noexcept - { - return m_st; - } - - private: - state& m_st; - }; - - template<> - struct argument_traits< extension_maps > - { - argument_traits( pegtl_input_t& /*unused*/, state& /*unused*/, const extension_maps& em ) - : m_em( em ) - {} - - [[nodiscard]] const extension_maps& convert() const noexcept - { - return m_em; - } - - private: - const extension_maps& m_em; - }; - - template< typename T > - struct argument_traits< std::optional< T > > - { - argument_traits( pegtl_input_t& in, state& st, const extension_maps& em ) - : m_j( do_inner_extension( in, st, em ) ) - {} - - [[nodiscard]] std::optional< T > convert() const - { - try { - return m_j.optional< T >(); - } - catch( const std::logic_error& e ) { - throw std::runtime_error( e.what() ); - } - } - - private: - const json_t m_j; - }; - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/array.hpp b/include/tao/config/internal/array.hpp index 9bf2a49..80bbb28 100644 --- a/include/tao/config/internal/array.hpp +++ b/include/tao/config/internal/array.hpp @@ -4,8 +4,8 @@ #ifndef TAO_CONFIG_INTERNAL_ARRAY_HPP #define TAO_CONFIG_INTERNAL_ARRAY_HPP -#include #include +#include #include "forward.hpp" #include "pegtl.hpp" @@ -23,6 +23,11 @@ namespace tao::config::internal : position( p ) {} + explicit basic_array( const std::string& f, const pegtl::position& p ) + : function( f ), + position( p ) + {} + basic_array( basic_array&& ) = default; basic_array( const basic_array& ) = default; @@ -31,16 +36,7 @@ namespace tao::config::internal basic_array& operator=( basic_array&& ) = default; basic_array& operator=( const basic_array& ) = default; - [[nodiscard]] std::size_t count_references_recursive() const noexcept - { - std::size_t result = 0; - - for( const auto& c : array ) { - result += c.count_references_recursive(); - } - return result; - } - + std::string function; std::list< C > array; pegtl::position position; }; diff --git a/include/tao/config/internal/change_action_and_states.hpp b/include/tao/config/internal/change_action_and_states.hpp index b247fb2..e4326f1 100644 --- a/include/tao/config/internal/change_action_and_states.hpp +++ b/include/tao/config/internal/change_action_and_states.hpp @@ -5,7 +5,9 @@ #ifndef TAO_CONFIG_INTERNAL_CHANGE_ACTION_AND_STATES_HPP #define TAO_CONFIG_INTERNAL_CHANGE_ACTION_AND_STATES_HPP +#include #include +#include #include #include "pegtl.hpp" diff --git a/include/tao/config/internal/concat.hpp b/include/tao/config/internal/concat.hpp index 0603034..7001770 100644 --- a/include/tao/config/internal/concat.hpp +++ b/include/tao/config/internal/concat.hpp @@ -4,9 +4,12 @@ #ifndef TAO_CONFIG_INTERNAL_CONCAT_HPP #define TAO_CONFIG_INTERNAL_CONCAT_HPP +#include +#include #include #include #include +#include #include #include "entry_kind.hpp" @@ -42,16 +45,6 @@ namespace tao::config::internal return implicit || temporary || concat.empty(); } - [[nodiscard]] std::size_t count_references_recursive() const noexcept - { - std::size_t result = 0; - - for( const auto& e : concat ) { - result += e.count_references_recursive(); - } - return result; - } - [[nodiscard]] const json_t* get_value() const { const json_t* result = nullptr; @@ -80,18 +73,26 @@ namespace tao::config::internal void back_ensure_kind( const entry_kind k, const pegtl::position& p ) { - if( concat.empty() || ( concat.back().kind() != k ) ) { + if( concat.empty() ) { + concat.emplace_back( k, p ); + } + else if( concat.back().kind() != k ) { concat.emplace_back( k, p ); } } + void back_emplace_func( const std::string& name, const pegtl::position& p ) + { + back_ensure_kind( entry_kind::array, p ); + concat.back().get_array().function = name; + } + bool remove = false; // Whether generated by += (false) or by = (true), because in the latter case everything that comes before must be removed. bool implicit = false; // Whether implicitly generated by a delete, e.g. a.b.c = delete when a.b doesn't even exist, so it's implicitly generated to set the remove flag on a.b.c. bool temporary = false; // Whether flagged as temporary by the user, i.e. this is not to be included in the final result and will be omitted by phase3. std::uint64_t generation = 0; - std::string schema; std::list< E > concat; pegtl::position position; }; diff --git a/include/tao/config/internal/config_action.hpp b/include/tao/config/internal/config_action.hpp index 0b2fdd9..d322042 100644 --- a/include/tao/config/internal/config_action.hpp +++ b/include/tao/config/internal/config_action.hpp @@ -5,14 +5,20 @@ #define TAO_CONFIG_INTERNAL_CONFIG_ACTION_HPP #include +#include +#include +#include +#include +#include #include "concat.hpp" #include "config_grammar.hpp" #include "constants.hpp" #include "entry_kind.hpp" -#include "extension_maps.hpp" #include "forward.hpp" #include "json.hpp" +#include "key1.hpp" +#include "key1_action.hpp" #include "key1_guard.hpp" #include "pegtl.hpp" #include "phase1_append.hpp" @@ -28,7 +34,7 @@ namespace tao::config::internal struct config_action< rules::assign_head > { template< typename State > - static void apply0( State& st, const extension_maps& ) + static void apply0( State& st, const function_map& /*unused*/ ) { const auto f = []( concat& c ) { c.concat.clear(); c.remove = true; }; phase1_append( st.root, st.prefix + st.suffix, f, phase1_mode::implicit ); @@ -39,7 +45,7 @@ namespace tao::config::internal struct config_action< json::jaxn::internal::rules::begin_array > { template< typename Input, typename State > - static void apply( Input& in, State& st, const extension_maps& ) + static void apply( Input& in, State& st, const function_map& /*unused*/ ) { const auto p = in.position(); const auto f = [ & ]( concat& c ) { c.back_ensure_kind( entry_kind::array, p ); }; @@ -51,7 +57,7 @@ namespace tao::config::internal struct config_action< json::jaxn::internal::rules::begin_object > { template< typename Input, typename State > - static void apply( Input& in, State& st, const extension_maps& ) + static void apply( Input& in, State& st, const function_map& /*unused*/ ) { const auto p = in.position(); const auto f = [ & ]( concat& c ) { c.back_ensure_kind( entry_kind::object, p ); }; @@ -59,11 +65,24 @@ namespace tao::config::internal } }; + template<> + struct config_action< rules::function_name > + { + template< typename Input, typename State > + static void apply( Input& in, State& st, const function_map& /*unused*/ ) + { + const auto s = in.string(); + const auto p = in.position(); + const auto f = [ & ]( concat& c ) { c.back_emplace_func( s, p ); }; + phase1_append( st.root, st.prefix + st.suffix, f, phase1_mode::manifest ); + } + }; + template<> struct config_action< rules::value_list > { template< typename State > - static void apply0( State& st, const extension_maps& ) + static void apply0( State& st, const function_map& /*unused*/ ) { assert( !st.suffix.empty() ); @@ -76,6 +95,89 @@ namespace tao::config::internal : pegtl::instantiate< key1_guard > {}; + template<> + struct config_action< rules::extension_list > + : pegtl::instantiate< key1_guard > + {}; + + template<> + struct config_action< rules::key1_rule > + : pegtl::change_action_and_state< key1_action, std::vector< key1_part > > + { + template< typename Input, typename State > + static void success( const Input& /*unused*/, std::vector< key1_part >& s, State& st, const function_map& /*unused*/ ) + { + assert( st.member.empty() ); + std::swap( st.member.vector(), s ); + } + }; + + template<> + struct config_action< rules::temporary_extension > + { + template< typename State > + static void apply0( State& st, const function_map& /*unused*/ ) + { + assert( !st.member.empty() ); + const auto f = []( concat& c ) { c.temporary = true; }; + phase1_append( st.root, st.prefix + st.suffix + st.member, f, phase1_mode::implicit ); + st.member.vector().clear(); + } + }; + + template<> + struct config_action< rules::permanent_extension > + { + template< typename State > + static void apply0( State& st, const function_map& /*unused*/ ) + { + assert( !st.member.empty() ); + const auto f = []( concat& c ) { c.temporary = false; }; + phase1_append( st.root, st.prefix + st.suffix + st.member, f, phase1_mode::implicit ); + st.member.vector().clear(); + } + }; + + template<> + struct config_action< rules::include > + { + template< typename State > + static void apply0( State& st, const function_map& /*unused*/ ) + { + st.include_is_optional = false; + } + }; + + template<> + struct config_action< rules::inc_if > + { + template< typename State > + static void apply0( State& st, const function_map& /*unused*/ ) + { + st.include_is_optional = true; + } + }; + + template<> + struct config_action< rules::inc_fn > + { + template< typename Input, typename State > + static void apply( const Input& ai, State& st, const function_map& fm ) + { + try { + pegtl::file_input in( ai.string() ); + pegtl::parse_nested< rules::config_file, config_action >( ai.position(), static_cast< pegtl_input_t& >( in ), st, fm ); + } + catch( const std::system_error& e ) { + if( ( !st.include_is_optional ) || ( e.code().value() != ENOENT ) ) { + throw pegtl::parse_error( "include error", ai.position() ); + // throw pegtl::parse_error( format( __FILE__, __LINE__, "include failed", { { "filename", f }, { "error", e.what() }, { "errno", e.code().value() } } ), pos ); + } + // An optional include file does not exist -- silently ignore and continue with a smile :-) + } + } + }; + } // namespace tao::config::internal #endif diff --git a/include/tao/config/internal/config_grammar.hpp b/include/tao/config/internal/config_grammar.hpp index 7bf1bff..62afd2a 100644 --- a/include/tao/config/internal/config_grammar.hpp +++ b/include/tao/config/internal/config_grammar.hpp @@ -4,25 +4,21 @@ #ifndef TAO_CONFIG_INTERNAL_CONFIG_GRAMMAR_HPP #define TAO_CONFIG_INTERNAL_CONFIG_GRAMMAR_HPP -#include "extension_maps.hpp" #include "forward.hpp" #include "json.hpp" -#include "key1.hpp" #include "key1_grammar.hpp" #include "key1_guard.hpp" -#include "member_extensions.hpp" #include "parse_utility.hpp" #include "pegtl.hpp" #include "phase1_append.hpp" #include "reference2.hpp" -#include "value_extensions.hpp" namespace tao::config::internal::rules { - struct reference_value + struct jaxn_value { - using rule_t = reference_value; - using subs_t = pegtl::type_list< reference2_rest >; + using rule_t = jaxn_value; + using subs_t = pegtl::type_list< json::jaxn::internal::rules::sor_single_value >; template< pegtl::apply_mode A, pegtl::rewind_mode M, @@ -31,19 +27,19 @@ namespace tao::config::internal::rules template< typename... > class Control, typename State > - [[nodiscard]] static bool match( pegtl_input_t& in, State& st, const extension_maps& ) + [[nodiscard]] static bool match( pegtl_input_t& in, State& st, const function_map& /*unused*/ ) { - const auto r = parse_reference2( in ); - const auto f = [ & ]( concat& c ) { c.concat.emplace_back( r ); }; + const auto j = parse_jaxn( in ); + const auto f = [ & ]( concat& c ) { c.concat.emplace_back( j ); }; phase1_append( st.root, st.prefix + st.suffix, f, phase1_mode::manifest ); return true; } }; - struct jaxn_value + struct reference_value { - using rule_t = jaxn_value; - using subs_t = pegtl::type_list< json::jaxn::internal::rules::sor_single_value >; + using rule_t = reference_value; + using subs_t = pegtl::type_list< reference2_rest >; template< pegtl::apply_mode A, pegtl::rewind_mode M, @@ -52,24 +48,27 @@ namespace tao::config::internal::rules template< typename... > class Control, typename State > - [[nodiscard]] static bool match( pegtl_input_t& in, State& st, const extension_maps& ) + [[nodiscard]] static bool match( pegtl_input_t& in, State& st, const function_map& /*unused*/ ) { - const auto j = parse_jaxn( in ); - const auto f = [ & ]( concat& c ) { c.concat.emplace_back( j ); }; + const auto r = parse_reference2( in ); + const auto f = [ & ]( concat& c ) { c.concat.emplace_back( r ); }; phase1_append( st.root, st.prefix + st.suffix, f, phase1_mode::manifest ); return true; } }; - // clang-format off - struct extension_value : pegtl::must< pegtl::function< do_value_extension >, pegtl::one< ')' > > {}; - struct bracketed_value : pegtl::if_must< pegtl::one< '(' >, pegtl::if_must_else< pegtl::at< reference2_rest >, reference_value, extension_value > > {}; - struct array; struct object; + struct extension_list; + + // clang-format off struct remove : pegtl::keyword< 'd', 'e', 'l', 'e', 't', 'e' > {}; + struct function_name : pegtl::seq< ident, pegtl::opt< pegtl::one< '?' > > > {}; + struct extension_value : pegtl::seq< function_name, wsp, extension_list > {}; + struct bracketed_value : pegtl::if_must< pegtl::one< '(' >, wss, pegtl::if_must_else< pegtl::at< reference2_rest >, reference_value, extension_value > > {}; + struct value_part : pegtl::sor< array, object, bracketed_value, jaxn_value > {}; struct value_list : pegtl::list< value_part, pegtl::one< '+' >, jaxn::ws > {}; @@ -93,22 +92,38 @@ namespace tao::config::internal::rules template< typename... > class Control, typename State > - [[nodiscard]] static bool match( pegtl_input_t& in, State& st, const extension_maps& em ) + [[nodiscard]] static bool match( pegtl_input_t& in, State& st, const function_map& fm ) { const key1_guard kg( st, parse_key1( in ) ); - return Control< pegtl::must< wss, key_member > >::template match< A, M, Action, Control >( in, st, em ); + return Control< pegtl::must< wss, key_member > >::template match< A, M, Action, Control >( in, st, fm ); } }; - struct extension_member : pegtl::if_must< pegtl::one< '(' >, pegtl::function< do_member_extension >, pegtl::one< ')' > > {}; + struct inc_if : pegtl::one< '?' > {}; + struct opt_if : pegtl::opt< inc_if > {}; + struct inc_fn : pegtl::plus< pegtl::not_one< '"' > > {}; // TODO: Do we want/need to allow JSON escape sequences in filenames? + + struct include : pegtl::string< 'i', 'n', 'c', 'l', 'u', 'd', 'e' > {}; + struct permanent : pegtl::string< 'p', 'e', 'r', 'm', 'a', 'n', 'e', 'n', 't' > {}; + struct temporary : pegtl::string< 't', 'e', 'm', 'p', 'o', 'r', 'a', 'r', 'y' > {}; + + struct filename : pegtl::seq< pegtl::one< '"' >, inc_fn, pegtl::one< '"' > > {}; + + struct include_extension : pegtl::if_must< include, opt_if, wsp, filename > {}; + struct permanent_extension : pegtl::if_must< permanent, wsp, key1_rule > {}; + struct temporary_extension : pegtl::if_must< temporary, wsp, key1_rule > {}; + + struct extension_member : pegtl::if_must< pegtl::one< '(' >, wss, pegtl::sor< include_extension, temporary_extension, permanent_extension >, wss, pegtl::one< ')' > > {}; struct member : pegtl::sor< extension_member, member_key > {}; struct opt_comma : pegtl::opt< pegtl::one< ',' >, wss > {}; template< typename U > struct member_list_impl : pegtl::until< U, member, wss, opt_comma > {}; + template< typename U > struct element_list_impl : pegtl::until< U, value_list, wss, opt_comma > {}; - struct element_list : pegtl::until< jaxn::end_array, value_list, wss, opt_comma > {}; + struct element_list : element_list_impl< jaxn::end_array > {}; + struct extension_list : element_list_impl< pegtl::one< ')' > > {}; struct array : pegtl::if_must< jaxn::begin_array, element_list > {}; struct member_list : member_list_impl< jaxn::end_object > {}; struct object : pegtl::if_must< jaxn::begin_object, member_list > {}; diff --git a/include/tao/config/internal/config_parser.hpp b/include/tao/config/internal/config_parser.hpp index d7db676..606f878 100644 --- a/include/tao/config/internal/config_parser.hpp +++ b/include/tao/config/internal/config_parser.hpp @@ -12,34 +12,27 @@ #include "config_action.hpp" #include "config_grammar.hpp" -#include "extension_maps.hpp" -#include "inner_functions.hpp" +#include "forward.hpp" +#include "function_implementations.hpp" +#include "function_wrapper.hpp" #include "json.hpp" #include "json_traits.hpp" -#include "member_functions.hpp" #include "pegtl.hpp" -#include "phase2_combine.hpp" -#include "phase2_replace.hpp" -#include "phase2_resolve.hpp" +#include "phase2_everything.hpp" #include "phase3_remove.hpp" -#include "phase4_schema.hpp" #include "phase5_repack.hpp" #include "state.hpp" -#include "value_functions.hpp" - -#include "../schema/builtin.hpp" namespace tao::config::internal { struct config_parser { config_parser() - : em( { { "binary", wrap( binary_function ) }, + : fm( { { "binary", wrap( binary_function ) }, { "cbor", wrap( cbor_function ) }, { "default", wrap( default_function ) }, { "env", wrap( env_function ) }, { "env?", wrap( env_if_function ) }, - { "identity", wrap( identity_function ) }, { "jaxn", wrap( jaxn_function ) }, { "json", wrap( json_function ) }, { "msgpack", wrap( msgpack_function ) }, @@ -47,15 +40,7 @@ namespace tao::config::internal { "shell", wrap( shell_function ) }, { "split", wrap( split_function ) }, { "string", wrap( string_function ) }, - { "ubjson", wrap( ubjson_function ) } }, - { { "include", wrap( include_function ) }, - { "include?", wrap( include_if_function ) }, - { "parse", wrap( member_function ) }, - { "permanent", wrap( permanent_function ) }, - { "schema", wrap( schema_function ) }, - { "setenv", wrap( setenv_function ) }, - { "temporary", wrap( temporary_function ) } }, - { { "parse", wrap( value_function ) } } ) + { "ubjson", wrap( ubjson_function ) } } ) {} config_parser( config_parser&& ) = delete; @@ -65,11 +50,11 @@ namespace tao::config::internal void operator=( const config_parser& ) = delete; state st; - extension_maps em; + function_map fm; void parse( pegtl_input_t&& in ) { - pegtl::parse< rules::config_file, config_action >( in, st, em ); + pegtl::parse< rules::config_file, config_action >( in, st, fm ); } void parse( const std::filesystem::path& path ) @@ -87,23 +72,11 @@ namespace tao::config::internal parse( data.data(), data.size(), source ); } - [[nodiscard]] bool phase2_iteration() - { - return ( phase2_combine( st.root ) + phase2_resolve( st.root ) + phase2_replace( st.root ) ) > 0; - } - - void phase2_loop() - { - while( phase2_iteration() ) { - } - } - template< template< typename... > class Traits > - [[nodiscard]] json::basic_value< Traits > finish( const schema::builtin& b = schema::builtin() ) + [[nodiscard]] json::basic_value< Traits > finish() { - phase2_loop(); + phase2_everything( st, fm ); phase3_remove( st.root ); - phase4_schema( st.root, st.schema, b ); return phase5_repack< Traits >( st.root ); } }; diff --git a/include/tao/config/internal/debug_traits.hpp b/include/tao/config/internal/debug_traits.hpp index 3410c59..99b3faf 100644 --- a/include/tao/config/internal/debug_traits.hpp +++ b/include/tao/config/internal/debug_traits.hpp @@ -318,6 +318,7 @@ namespace tao::config::internal template<> struct debug_traits< array > : json::binding::object< TAO_JSON_BIND_REQUIRED( "position", &array::position ), + TAO_JSON_BIND_OPTIONAL( "function", &array::function ), TAO_JSON_BIND_REQUIRED( "array_data", &array::array ) > {}; diff --git a/include/tao/config/internal/entry.hpp b/include/tao/config/internal/entry.hpp index aa6b0ba..e548f2d 100644 --- a/include/tao/config/internal/entry.hpp +++ b/include/tao/config/internal/entry.hpp @@ -40,6 +40,10 @@ namespace tao::config::internal assert( !r.empty() ); } + entry( const std::string& n, const pegtl::position& p ) + : m_data( std::in_place_type_t< array >(), n, p ) + {} + entry( const entry_kind k, const pegtl::position& p ) : m_data( std::in_place_type_t< object >(), p ) { @@ -59,12 +63,12 @@ namespace tao::config::internal throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE } - entry( entry&& ) = delete; + entry( entry&& ) = default; entry( const entry& ) = default; ~entry() = default; - entry& operator=( entry&& ) = delete; + entry& operator=( entry&& ) = default; entry& operator=( const entry& ) = default; entry_kind kind() const noexcept @@ -97,6 +101,18 @@ namespace tao::config::internal return std::holds_alternative< concat >( m_data ); } + void set_value( json_t&& t ) + { + m_data = std::move( t ); + expand(); + } + + void set_value( const json_t& t ) + { + m_data = t; + expand(); + } + void set_array( pegtl::position p ) { m_data.emplace< std::size_t( entry_kind::array ) >( std::move( p ) ); @@ -182,23 +198,6 @@ namespace tao::config::internal return *s; } - [[nodiscard]] std::size_t count_references_recursive() const noexcept - { - switch( kind() ) { - case entry_kind::value: - return 0; - case entry_kind::reference: - return 1; - case entry_kind::array: - return get_array().count_references_recursive(); - case entry_kind::object: - return get_object().count_references_recursive(); - case entry_kind::concat: - return get_concat().count_references_recursive(); - } - std::abort(); // LCOV_EXCL_LINE - } - private: data_t m_data; diff --git a/include/tao/config/internal/extension_action.hpp b/include/tao/config/internal/extension_action.hpp deleted file mode 100644 index 4471049..0000000 --- a/include/tao/config/internal/extension_action.hpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2020-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_EXTENSION_ACTION_HPP -#define TAO_CONFIG_INTERNAL_EXTENSION_ACTION_HPP - -#include "extension_grammar.hpp" -#include "pegtl.hpp" - -namespace tao::config::internal -{ - template< typename Rule > - struct extension_action - : pegtl::nothing< Rule > - {}; - - template<> - struct extension_action< rules::extension_name > - { - template< typename Input > - static void apply( const Input& in, std::string& result ) - { - result = in.string(); - } - }; - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/extension_grammar.hpp b/include/tao/config/internal/extension_grammar.hpp deleted file mode 100644 index 6bcf52c..0000000 --- a/include/tao/config/internal/extension_grammar.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2020-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_EXTENSION_GRAMMAR_HPP -#define TAO_CONFIG_INTERNAL_EXTENSION_GRAMMAR_HPP - -#include "json.hpp" -#include "key_grammar.hpp" -#include "pegtl.hpp" - -namespace tao::config::internal::rules -{ - // clang-format off - struct extension_name : pegtl::seq< ident, pegtl::opt< pegtl::one< '?' > > > {}; - struct extension_rule : pegtl::must< extension_name, wsp > {}; - // clang-format on - -} // namespace tao::config::internal::rules - -#endif diff --git a/include/tao/config/internal/extension_maps.hpp b/include/tao/config/internal/extension_maps.hpp deleted file mode 100644 index 20e1977..0000000 --- a/include/tao/config/internal/extension_maps.hpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_EXTENSION_MAPS_HPP -#define TAO_CONFIG_INTERNAL_EXTENSION_MAPS_HPP - -#include - -#include "extension_types.hpp" - -namespace tao::config::internal -{ - struct extension_maps - { - extension_maps() = delete; - - extension_maps( inner_extension_map&& inner, outer_extension_map&& member, outer_extension_map&& value ) - : inner( std::move( inner ) ), - member( std::move( member ) ), - value( std::move( value ) ) - {} - - extension_maps( extension_maps&& ) = delete; - extension_maps( const extension_maps& ) = delete; - - ~extension_maps() = default; - - void operator=( extension_maps&& ) = delete; - void operator=( const extension_maps& ) = delete; - - inner_extension_map inner; - outer_extension_map member; - outer_extension_map value; - }; - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/extension_types.hpp b/include/tao/config/internal/extension_types.hpp deleted file mode 100644 index b669dc9..0000000 --- a/include/tao/config/internal/extension_types.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_EXTENSION_TYPES_HPP -#define TAO_CONFIG_INTERNAL_EXTENSION_TYPES_HPP - -#include -#include -#include - -#include "forward.hpp" -#include "json.hpp" -#include "json_traits.hpp" -#include "pegtl.hpp" - -namespace tao::config::internal -{ - using inner_extension = std::function< json_t( pegtl_input_t&, state&, const extension_maps& ) >; - using inner_extension_map = std::map< std::string, inner_extension >; - - using outer_extension = std::function< void( pegtl_input_t&, state&, const extension_maps& ) >; - using outer_extension_map = std::map< std::string, outer_extension >; - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/extension_wrapper.hpp b/include/tao/config/internal/extension_wrapper.hpp deleted file mode 100644 index 0bd14e5..0000000 --- a/include/tao/config/internal/extension_wrapper.hpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_EXTENSION_WRAPPER_HPP -#define TAO_CONFIG_INTERNAL_EXTENSION_WRAPPER_HPP - -#include -#include - -#include "argument_traits.hpp" -#include "extension_maps.hpp" -#include "extension_types.hpp" -#include "forward.hpp" -#include "json.hpp" -#include "json_traits.hpp" -#include "pegtl.hpp" -#include "result_traits.hpp" - -namespace tao::config::internal -{ - // NOTE: We can't just make wrap() variadic the straightforward way because that would look like - // return convert_result( p, f( argument_traits< std::decay_t< As > >( in, st, em ).convert()... ) ); - // and with the order of function parameter evaluation not being defined in C++ that doesn't work. - - template< typename R, typename A > - [[nodiscard]] inner_extension wrap( R ( *f )( A ) ) - { - static_assert( !std::is_pointer_v< R > ); - static_assert( !std::is_reference_v< R > ); - static_assert( !std::is_same_v< R, void > ); - - return [ = ]( pegtl_input_t& in, state& st, const extension_maps& em ) { - const auto p = in.position(); - const argument_traits< std::decay_t< A > > a( in, st, em ); - return convert_result( p, f( a.convert() ) ); - }; - } - - template< typename R, typename A, typename B > - [[nodiscard]] inner_extension wrap( R ( *f )( A, B ) ) - { - static_assert( !std::is_pointer_v< R > ); - static_assert( !std::is_reference_v< R > ); - static_assert( !std::is_same_v< R, void > ); - - return [ = ]( pegtl_input_t& in, state& st, const extension_maps& em ) { - const auto p = in.position(); - const argument_traits< std::decay_t< A > > a( in, st, em ); - const argument_traits< std::decay_t< B > > b( in, st, em ); - return convert_result( p, f( a.convert(), b.convert() ) ); - }; - } - - template< typename A, typename B > - [[nodiscard]] outer_extension wrap( void ( *f )( A, B ) ) - { - return [ = ]( pegtl_input_t& in, state& st, const extension_maps& em ) { - const argument_traits< std::decay_t< A > > a( in, st, em ); - const argument_traits< std::decay_t< B > > b( in, st, em ); - f( a.convert(), b.convert() ); - }; - } - - template< typename A, typename B, typename C > - [[nodiscard]] outer_extension wrap( void ( *f )( A, B, C ) ) - { - return [ = ]( pegtl_input_t& in, state& st, const extension_maps& em ) { - const argument_traits< std::decay_t< A > > a( in, st, em ); - const argument_traits< std::decay_t< B > > b( in, st, em ); - const argument_traits< std::decay_t< C > > c( in, st, em ); - f( a.convert(), b.convert(), c.convert() ); - }; - } - - template< typename A, typename B, typename C, typename D > - [[nodiscard]] outer_extension wrap( void ( *f )( A, B, C, D ) ) - { - return [ = ]( pegtl_input_t& in, state& st, const extension_maps& em ) { - const argument_traits< std::decay_t< A > > a( in, st, em ); - const argument_traits< std::decay_t< B > > b( in, st, em ); - const argument_traits< std::decay_t< C > > c( in, st, em ); - const argument_traits< std::decay_t< D > > d( in, st, em ); - f( a.convert(), b.convert(), c.convert(), d.convert() ); - }; - } - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/forward.hpp b/include/tao/config/internal/forward.hpp index 086dacf..369b0eb 100644 --- a/include/tao/config/internal/forward.hpp +++ b/include/tao/config/internal/forward.hpp @@ -4,6 +4,10 @@ #ifndef TAO_CONFIG_INTERNAL_FORWARD_HPP #define TAO_CONFIG_INTERNAL_FORWARD_HPP +#include +#include +#include + namespace tao::config::internal { struct entry; @@ -22,12 +26,11 @@ namespace tao::config::internal using array = basic_array< concat >; using object = basic_object< concat >; - template< typename T > - struct argument_traits; - template< typename T > - struct result_traits; + template< typename, typename > + struct function_traits; - struct extension_maps; + using function = std::function< bool( entry& ) >; + using function_map = std::map< std::string, function >; } // namespace tao::config::internal diff --git a/include/tao/config/internal/inner_functions.hpp b/include/tao/config/internal/function_implementations.hpp similarity index 72% rename from include/tao/config/internal/inner_functions.hpp rename to include/tao/config/internal/function_implementations.hpp index 885080d..ab64bc1 100644 --- a/include/tao/config/internal/inner_functions.hpp +++ b/include/tao/config/internal/function_implementations.hpp @@ -1,9 +1,15 @@ // Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey // Please see LICENSE for license or visit https://github.com/taocpp/config/ -#ifndef TAO_CONFIG_INTERNAL_INNER_FUNCTIONS_HPP -#define TAO_CONFIG_INTERNAL_INNER_FUNCTIONS_HPP +#ifndef TAO_CONFIG_INTERNAL_FUNCTION_IMPLEMENTATIONS_HPP +#define TAO_CONFIG_INTERNAL_FUNCTION_IMPLEMENTATIONS_HPP +#include +#include +#include +#include + +#include "forward.hpp" #include "jaxn_action.hpp" #include "json.hpp" #include "json_traits.hpp" @@ -23,28 +29,55 @@ namespace tao::config::internal return json::cbor::basic_from_binary< json_traits >( bv ); // TODO: Positions. } - [[nodiscard]] inline json_t default_function( const json_t& l, const json_t& r ) + [[nodiscard]] inline bool default_function( entry& e ) { - assert( !l.is_uninitialized() ); - return ( !l.is_null() ) ? l : r; + array& a = e.get_array(); + if( a.array.size() < 1 ) { + throw pegtl::parse_error( "default function requires at least one argument", a.position ); + } + for( concat& c : a.array ) { + if( c.concat.size() != 1 ) { + return false; + } + entry& f = c.concat.front(); + + switch( f.kind() ) { + case entry_kind::value: + if( f.get_value().is_null() ) { + continue; + } + break; + case entry_kind::reference: + return false; + case entry_kind::array: + if( !f.get_array().function.empty() ) { + return false; + } + break; + case entry_kind::object: + break; + case entry_kind::concat: + return false; + } + // Both f and a are sub-objects of e. + entry t = std::move( f ); + e = std::move( t ); + return true; + } + throw pegtl::parse_error( "default function requires at least one non-null argument", a.position ); } [[nodiscard]] inline std::string env_function( const pegtl::position& p, const std::string& s ) { - return get_env_throws( p, s ); + return getenv_throws( p, s ); } [[nodiscard]] inline std::string env_if_function( const std::string& s, const std::string& d ) { - const auto r = get_env_nothrow( s ); + const auto r = getenv_nothrow( s ); return r ? ( *r ) : d; } - [[nodiscard]] inline json_t identity_function( const json_t& j ) - { - return j; - } - [[nodiscard]] inline json_t jaxn_function( const pegtl::position& p, const std::string& s ) { json_to_value consumer( p ); diff --git a/include/tao/config/internal/function_traits.hpp b/include/tao/config/internal/function_traits.hpp new file mode 100644 index 0000000..e3442ef --- /dev/null +++ b/include/tao/config/internal/function_traits.hpp @@ -0,0 +1,123 @@ +// Copyright (c) 2020-2024 Dr. Colin Hirsch and Daniel Frey +// Please see LICENSE for license or visit https://github.com/taocpp/config/ + +#ifndef TAO_CONFIG_INTERNAL_FUNCTION_TRAITS_HPP +#define TAO_CONFIG_INTERNAL_FUNCTION_TRAITS_HPP + +#include +#include +#include +#include +#include +#include + +#include "array.hpp" +#include "entry.hpp" +#include "forward.hpp" +#include "json.hpp" +#include "json_traits.hpp" +#include "key1.hpp" +#include "parse_utility.hpp" +#include "pegtl.hpp" + +namespace tao::config::internal +{ + struct arguments_unready + {}; + + template< typename, typename = void > + struct function_traits; + + [[nodiscard]] inline decltype( auto ) function_traits_value( array& f, const std::size_t i ) + { + assert( f.array.size() > i ); + auto p = f.array.begin(); + std::advance( p, i ); + if( p->concat.size() > 1 ) { + throw arguments_unready(); + } + const entry& e = p->concat.front(); + if( e.kind() != entry_kind::value ) { + throw arguments_unready(); + } + return p->concat.front().get_value(); + } + + template<> + struct function_traits< json_t > + { + static void put( entry& e, const json_t& t ) + { + e.set_value( t ); + } + }; + + template<> + struct function_traits< std::string > + { + [[nodiscard]] static std::string get( array& f, std::size_t& i ) + { + const json_t& j = function_traits_value( f, i++ ); + if( j.type() == json::type::STRING ) { + return j.get_string(); + } + if( j.type() == json::type::BINARY ) { + const auto bv = j.as< tao::binary_view >(); + std::string s( reinterpret_cast< const char* >( bv.data() ), bv.size() ); + if( !json::internal::validate_utf8_nothrow( s ) ) { + throw pegtl::parse_error( "invalid utf-8 in binary data used as string argument", f.position ); + } + return s; + } + throw pegtl::parse_error( "invalid type for string argument", f.position ); + } + + static void put( entry& e, const std::string& s ) + { + e.set_value( json_t( s ) ); + } + }; + + template<> + struct function_traits< tao::binary_view > + { + [[nodiscard]] static tao::binary_view get( array& f, std::size_t& i ) + { + const json_t& j = function_traits_value( f, i++ ); + if( j.is_binary_type() ) { + return j.get_binary_type(); + } + throw pegtl::parse_error( "invalid type for binary argument", f.position ); + } + }; + + template<> + struct function_traits< std::vector< std::byte > > + { + static void put( entry& e, const std::vector< std::byte >& b ) + { + e.set_value( json_t( b ) ); + } + }; + + template<> + struct function_traits< std::vector< std::string > > + { + static void put( entry& e, const std::vector< std::string >& v ) + { + e.set_value( json_t( v ) ); + } + }; + + template<> + struct function_traits< pegtl::position > + { + [[nodiscard]] static pegtl::position get( array& f, const std::size_t /*unused*/ ) + { + return f.position; + } + }; + +} // namespace tao::config::internal + +#endif diff --git a/include/tao/config/internal/function_wrapper.hpp b/include/tao/config/internal/function_wrapper.hpp new file mode 100644 index 0000000..72adde1 --- /dev/null +++ b/include/tao/config/internal/function_wrapper.hpp @@ -0,0 +1,76 @@ +// Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey +// Please see LICENSE for license or visit https://github.com/taocpp/config/ + +#ifndef TAO_CONFIG_INTERNAL_FUNCTION_WRAPPER_HPP +#define TAO_CONFIG_INTERNAL_FUNCTION_WRAPPER_HPP + +#include +#include +#include + +#include "forward.hpp" +#include "function_traits.hpp" +#include "json.hpp" +#include "json_traits.hpp" +#include "pegtl.hpp" + +namespace tao::config::internal +{ + [[nodiscard]] function wrap( bool ( *x )( entry& e ) ) + { + return function( [ x ]( entry& e ) { + try { + return x( e ); + } + catch( const arguments_unready& ) { + return false; + } + } ); + } + + template< typename R, typename A > + [[nodiscard]] function wrap( R ( *x )( A ) ) + { + static_assert( !std::is_pointer_v< R > ); + static_assert( !std::is_reference_v< R > ); + static_assert( !std::is_same_v< R, void > ); + + return function( [ x ]( entry& e ) { + array& f = e.get_array(); + try { + std::size_t i = 0; + decltype( auto ) a = function_traits< std::decay_t< A > >::get( f, i ); + function_traits< std::decay_t< R > >::put( e, x( a ) ); + } + catch( const arguments_unready& ) { + return false; + } + return true; + } ); + } + + template< typename R, typename A, typename B > + [[nodiscard]] function wrap( R ( *x )( A, B ) ) + { + static_assert( !std::is_pointer_v< R > ); + static_assert( !std::is_reference_v< R > ); + static_assert( !std::is_same_v< R, void > ); + + return function( [ x ]( entry& e ) { + array& f = e.get_array(); + try { + std::size_t i = 0; + decltype( auto ) a = function_traits< std::decay_t< A > >::get( f, i ); + decltype( auto ) b = function_traits< std::decay_t< B > >::get( f, i ); + function_traits< std::decay_t< R > >::put( e, x( a, b ) ); + } + catch( const arguments_unready& ) { + return false; + } + return true; + } ); + } + +} // namespace tao::config::internal + +#endif diff --git a/include/tao/config/internal/inner_extensions.hpp b/include/tao/config/internal/inner_extensions.hpp deleted file mode 100644 index 79d575a..0000000 --- a/include/tao/config/internal/inner_extensions.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_INNER_EXTENSIONS_HPP -#define TAO_CONFIG_INTERNAL_INNER_EXTENSIONS_HPP - -#include "extension_maps.hpp" -#include "extension_types.hpp" -#include "extension_wrapper.hpp" -#include "forward.hpp" -#include "json.hpp" -#include "json_traits.hpp" -#include "parse_utility.hpp" -#include "pegtl.hpp" -#include "state.hpp" -#include "system_utility.hpp" - -namespace tao::config::internal -{ - [[nodiscard]] inline json_t do_inner_extension( pegtl_input_t& in, state& st, const extension_maps& em ) - { - if( parse_open( in ) ) { - const std::string name = parse_name( in ); - { - const auto i = em.inner.find( name ); - if( i != em.inner.end() ) { - json_t result = i->second( in, st, em ); - parse_close( in ); - return result; - } - } - throw pegtl::parse_error( "unknown inner extension " + name, in.position() ); - } - json_t result = parse_jaxn( in ); - skip_ws( in ); - return result; - } - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/jaxn_action.hpp b/include/tao/config/internal/jaxn_action.hpp index b37d718..d295343 100644 --- a/include/tao/config/internal/jaxn_action.hpp +++ b/include/tao/config/internal/jaxn_action.hpp @@ -4,6 +4,8 @@ #ifndef TAO_CONFIG_INTERNAL_JAXN_ACTION_HPP #define TAO_CONFIG_INTERNAL_JAXN_ACTION_HPP +#include +#include #include #include #include diff --git a/include/tao/config/internal/key1.hpp b/include/tao/config/internal/key1.hpp index 511fe4c..81c6cbd 100644 --- a/include/tao/config/internal/key1.hpp +++ b/include/tao/config/internal/key1.hpp @@ -4,6 +4,8 @@ #ifndef TAO_CONFIG_INTERNAL_KEY1_HPP #define TAO_CONFIG_INTERNAL_KEY1_HPP +#include +#include #include #include "key1_action.hpp" diff --git a/include/tao/config/internal/key1_action.hpp b/include/tao/config/internal/key1_action.hpp index 5a5a30b..ed7d237 100644 --- a/include/tao/config/internal/key1_action.hpp +++ b/include/tao/config/internal/key1_action.hpp @@ -4,6 +4,9 @@ #ifndef TAO_CONFIG_INTERNAL_KEY1_ACTION_HPP #define TAO_CONFIG_INTERNAL_KEY1_ACTION_HPP +#include +#include +#include #include #include "json.hpp" diff --git a/include/tao/config/internal/key1_guard.hpp b/include/tao/config/internal/key1_guard.hpp index 41f11c6..af16613 100644 --- a/include/tao/config/internal/key1_guard.hpp +++ b/include/tao/config/internal/key1_guard.hpp @@ -5,7 +5,10 @@ #define TAO_CONFIG_INTERNAL_KEY1_GUARD_HPP #include +#include +#include +#include "forward.hpp" #include "key1.hpp" #include "pegtl.hpp" @@ -26,8 +29,8 @@ namespace tao::config::internal m_suffix = std::move( suffix ); } - template< typename State, typename Arg > - key1_guard( const pegtl_input_t& in, State& st, const Arg& /*unused*/ ) + template< typename State > + key1_guard( const pegtl_input_t& in, State& st, const function_map& /*unused*/ ) : m_prefix( st.prefix ), m_suffix( st.suffix ), m_size( m_prefix.size() ) diff --git a/include/tao/config/internal/key1_part.hpp b/include/tao/config/internal/key1_part.hpp index 7579e5e..9457d15 100644 --- a/include/tao/config/internal/key1_part.hpp +++ b/include/tao/config/internal/key1_part.hpp @@ -5,6 +5,9 @@ #define TAO_CONFIG_INTERNAL_KEY1_PART_HPP #include +#include +#include +#include #include #include diff --git a/include/tao/config/internal/key_action.hpp b/include/tao/config/internal/key_action.hpp index 5e98388..a12b8bb 100644 --- a/include/tao/config/internal/key_action.hpp +++ b/include/tao/config/internal/key_action.hpp @@ -4,6 +4,9 @@ #ifndef TAO_CONFIG_INTERNAL_KEY_ACTION_HPP #define TAO_CONFIG_INTERNAL_KEY_ACTION_HPP +#include +#include +#include #include #include "json.hpp" diff --git a/include/tao/config/internal/limits.hpp b/include/tao/config/internal/limits.hpp index 0fc1283..9da6561 100644 --- a/include/tao/config/internal/limits.hpp +++ b/include/tao/config/internal/limits.hpp @@ -4,7 +4,7 @@ #ifndef TAO_CONFIG_INTERNAL_LIMITS_HPP #define TAO_CONFIG_INTERNAL_LIMITS_HPP -#include +#include namespace tao::config::internal { diff --git a/include/tao/config/internal/member_extensions.hpp b/include/tao/config/internal/member_extensions.hpp deleted file mode 100644 index 701abe6..0000000 --- a/include/tao/config/internal/member_extensions.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_MEMBER_EXTENSIONS_HPP -#define TAO_CONFIG_INTERNAL_MEMBER_EXTENSIONS_HPP - -#include "extension_maps.hpp" -#include "extension_types.hpp" -#include "extension_wrapper.hpp" -#include "key1_guard.hpp" -#include "parse_utility.hpp" -#include "state.hpp" - -namespace tao::config::internal -{ - [[nodiscard]] inline bool do_member_extension( pegtl_input_t& in, state& st, const extension_maps& em ) - { - const std::string name = parse_name( in ); - { - const auto i = em.member.find( name ); - if( i != em.member.end() ) { - const key1_guard kg( st, key1() ); - i->second( in, st, em ); - return true; - } - } - throw pegtl::parse_error( "unknown member extension " + name, in.position() ); - } - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/member_functions.hpp b/include/tao/config/internal/member_functions.hpp deleted file mode 100644 index aa9050e..0000000 --- a/include/tao/config/internal/member_functions.hpp +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_MEMBER_FUNCTIONS_HPP -#define TAO_CONFIG_INTERNAL_MEMBER_FUNCTIONS_HPP - -#include -#include -#include - -#include "concat.hpp" -#include "config_action.hpp" -#include "config_grammar.hpp" -#include "extension_maps.hpp" -#include "forward.hpp" -#include "key1.hpp" -#include "pegtl.hpp" -#include "phase1_append.hpp" -#include "state.hpp" -#include "system_utility.hpp" - -namespace tao::config::internal -{ - inline void include_function( state& st, const extension_maps& em, const pegtl::position& p, const std::string& f ) - { - try { - pegtl::file_input in( f ); - pegtl::parse_nested< rules::config_file, config_action >( p, static_cast< pegtl_input_t& >( in ), st, em ); - } - catch( const std::system_error& /*unused*/ ) { - throw pegtl::parse_error( "include error", p ); - // throw pegtl::parse_error( format( __FILE__, __LINE__, "include failed", { { "filename", f }, { "error", e.what() }, { "errno", e.code().value() } } ), pos ); - } - } - - inline void include_if_function( state& st, const extension_maps& em, const pegtl::position& p, const std::string& f ) - { - try { - pegtl::file_input in( f ); - pegtl::parse_nested< rules::config_file, config_action >( p, static_cast< pegtl_input_t& >( in ), st, em ); - } - catch( const std::system_error& e ) { - if( e.code().value() != ENOENT ) { - throw pegtl::parse_error( "include error", p ); - // throw pegtl::parse_error( format( __FILE__, __LINE__, "include failed", { { "filename", f }, { "error", e.what() }, { "errno", e.code().value() } } ), pos ); - } - } - } - - inline void member_function( state& st, const extension_maps& em, const pegtl::position& p, const std::string& c ) - { - pegtl::memory_input in( c, __FUNCTION__ ); - pegtl::parse_nested< rules::config_file, config_action >( p, static_cast< pegtl_input_t& >( in ), st, em ); - } - - inline void permanent_function( state& st, const key1& path ) - { - assert( st.suffix.empty() ); - - const auto f = []( concat& c ) { c.temporary = false; }; - phase1_append( st.root, st.prefix + path, f, phase1_mode::implicit ); - } - - inline void schema_function( state& st, const std::optional< std::string >& s ) - { - assert( st.suffix.empty() ); - - if( st.prefix.empty() ) { - st.schema = s ? ( *s ) : std::string(); - } - else { - const auto f = [ & ]( concat& c ) { c.schema = s ? ( *s ) : std::string(); }; - phase1_append( st.root, st.prefix, f, phase1_mode( !s.has_value() ) ); - } - } - - inline void setenv_function( const pegtl::position& p, const std::string& name, const std::string& value ) - { - set_env_throws( p, name, value ); - } - - inline void temporary_function( state& st, const key1& path ) - { - assert( st.suffix.empty() ); - - const auto f = []( concat& c ) { c.temporary = true; }; - phase1_append( st.root, st.prefix + path, f, phase1_mode::implicit ); - } - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/object.hpp b/include/tao/config/internal/object.hpp index 4a81dde..a868be6 100644 --- a/include/tao/config/internal/object.hpp +++ b/include/tao/config/internal/object.hpp @@ -44,16 +44,6 @@ namespace tao::config::internal return ( i == object.end() ) ? nullptr : ( &*i ); } - [[nodiscard]] std::size_t count_references_recursive() const noexcept - { - std::size_t result = 0; - - for( const auto& p : object ) { - result += p.second.count_references_recursive(); - } - return result; - } - std::map< std::string, C > object; pegtl::position position; }; diff --git a/include/tao/config/internal/parse_utility.hpp b/include/tao/config/internal/parse_utility.hpp index 34e1ed9..278fc9f 100644 --- a/include/tao/config/internal/parse_utility.hpp +++ b/include/tao/config/internal/parse_utility.hpp @@ -4,11 +4,8 @@ #ifndef TAO_CONFIG_INTERNAL_PARSE_UTILITY_HPP #define TAO_CONFIG_INTERNAL_PARSE_UTILITY_HPP -#include #include -#include "extension_action.hpp" -#include "extension_grammar.hpp" #include "forward.hpp" #include "jaxn_action.hpp" #include "json.hpp" @@ -24,21 +21,6 @@ namespace tao::config::internal { - inline void skip_ws( pegtl_input_t& in ) - { - pegtl::parse< rules::wss >( in ); - } - - [[nodiscard]] inline bool parse_open( pegtl_input_t& in ) - { - return pegtl::parse< pegtl::seq< pegtl::one< '(' >, rules::wss > >( in ); - } - - inline void parse_close( pegtl_input_t& in ) - { - pegtl::parse< pegtl::must< pegtl::one< ')' >, rules::wss > >( in ); - } - [[nodiscard]] inline key1 parse_key1( pegtl_input_t& in ) { key1 result; @@ -60,20 +42,6 @@ namespace tao::config::internal return std::move( consumer.value ); } - [[nodiscard]] inline std::string parse_string( pegtl_input_t& in ) - { - std::string result; - pegtl::parse< pegtl::must< json::jaxn::internal::rules::string_fragment >, json::jaxn::internal::unescape_action >( in, result ); - return result; - } - - [[nodiscard]] inline std::string parse_name( pegtl_input_t& in ) - { - std::string result; - pegtl::parse< pegtl::must< rules::wss, rules::extension_rule, rules::wss >, extension_action >( in, result ); - return result; - } - } // namespace tao::config::internal #endif diff --git a/include/tao/config/internal/pegtl.hpp b/include/tao/config/internal/pegtl.hpp index d5e2495..8bc4006 100644 --- a/include/tao/config/internal/pegtl.hpp +++ b/include/tao/config/internal/pegtl.hpp @@ -10,6 +10,8 @@ #include #include +#include + namespace tao::pegtl { template< typename P > diff --git a/include/tao/config/internal/phase1_append.hpp b/include/tao/config/internal/phase1_append.hpp index 73aa4d3..9c52e66 100644 --- a/include/tao/config/internal/phase1_append.hpp +++ b/include/tao/config/internal/phase1_append.hpp @@ -5,6 +5,7 @@ #define TAO_CONFIG_INTERNAL_PHASE1_APPEND_HPP #include +#include #include #include #include @@ -23,7 +24,6 @@ #include "object.hpp" #include "phase1_mode.hpp" #include "reference2.hpp" -#include "reverse.hpp" namespace tao::config::internal { diff --git a/include/tao/config/internal/phase2_access.hpp b/include/tao/config/internal/phase2_access.hpp index 708ada6..0e5ae87 100644 --- a/include/tao/config/internal/phase2_access.hpp +++ b/include/tao/config/internal/phase2_access.hpp @@ -5,6 +5,7 @@ #define TAO_CONFIG_INTERNAL_PHASE2_ACCESS_HPP #include +#include #include #include #include @@ -15,6 +16,7 @@ #include "entry.hpp" #include "key1.hpp" #include "object.hpp" +#include "statistics.hpp" namespace tao::config::internal { @@ -116,7 +118,7 @@ namespace tao::config::internal [[nodiscard]] inline const concat* phase2_access( const concat& c, const key1& suffix, const int down ) { if( suffix.empty() ) { - if( ( c.concat.size() < 2 ) && ( c.count_references_recursive() == 0 ) ) { + if( ( c.concat.size() < 2 ) && ( statistics( c ).references() == 0 ) ) { return &c; } throw phase2_access_return(); diff --git a/include/tao/config/internal/phase2_add.hpp b/include/tao/config/internal/phase2_additions.hpp similarity index 86% rename from include/tao/config/internal/phase2_add.hpp rename to include/tao/config/internal/phase2_additions.hpp index e567d95..af52b96 100644 --- a/include/tao/config/internal/phase2_add.hpp +++ b/include/tao/config/internal/phase2_additions.hpp @@ -1,9 +1,10 @@ // Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey // Please see LICENSE for license or visit https://github.com/taocpp/config/ -#ifndef TAO_CONFIG_INTERNAL_PHASE2_ADD_HH -#define TAO_CONFIG_INTERNAL_PHASE2_ADD_HH +#ifndef TAO_CONFIG_INTERNAL_PHASE2_ADDITIONS_HH +#define TAO_CONFIG_INTERNAL_PHASE2_ADDITIONS_HH +#include #include #include @@ -19,7 +20,7 @@ namespace tao::config::internal { - struct phase2_add_error + struct phase2_additions_error {}; // For numbers we currently use the following rules: @@ -36,7 +37,7 @@ namespace tao::config::internal r.assign( l.get_signed() + std::int64_t( r.get_unsigned() ) ); return; } - throw phase2_add_error(); + throw phase2_additions_error(); } inline void phase2_unsigned_add( const json_t& l, json_t& r ) @@ -49,7 +50,7 @@ namespace tao::config::internal r.assign( l.get_unsigned() + r.get_unsigned() ); return; } - throw phase2_add_error(); + throw phase2_additions_error(); } inline void phase2_double_add( const json_t& l, json_t& r ) @@ -58,7 +59,7 @@ namespace tao::config::internal r.assign( l.get_double() + r.get_double() ); return; } - throw phase2_add_error(); + throw phase2_additions_error(); } inline void phase2_string_add( const json_t& l, json_t& r ) @@ -67,7 +68,7 @@ namespace tao::config::internal r.get_string() = l.get_string() + r.get_string(); return; } - throw phase2_add_error(); + throw phase2_additions_error(); } inline void phase2_binary_add( const json_t& l, json_t& r ) @@ -76,7 +77,7 @@ namespace tao::config::internal r.get_binary().insert( r.get_binary().begin(), l.get_binary().begin(), l.get_binary().end() ); return; } - throw phase2_add_error(); + throw phase2_additions_error(); } inline void phase2_value_add( json_t&& l, json_t& r ) @@ -84,7 +85,7 @@ namespace tao::config::internal switch( l.type() ) { case json::type::NULL_: case json::type::BOOLEAN: - throw phase2_add_error(); + throw phase2_additions_error(); case json::type::SIGNED: phase2_signed_add( l, r ); return; @@ -114,12 +115,12 @@ namespace tao::config::internal throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE } - inline void phase2_add( json_t&& l, json_t& r ) + inline void phase2_additions( json_t&& l, json_t& r ) { try { - phase2_value_add( std::move( l ), r ); // l is unchanged when a phase2_add_error is thrown. + phase2_value_add( std::move( l ), r ); // l is unchanged when a phase2_additions_error is thrown. } - catch( const phase2_add_error& /*unused*/ ) { + catch( const phase2_additions_error& /*unused*/ ) { pegtl::parse_error p( strcat( "incompatible types ", l.type(), "@", l.position, " and ", r.type(), "@", r.position ), pegtl::position( 0, 1, 1, "(todo) location of '+'" ) ); throw p; } diff --git a/include/tao/config/internal/phase2_append.hpp b/include/tao/config/internal/phase2_append.hpp deleted file mode 100644 index 5943d7f..0000000 --- a/include/tao/config/internal/phase2_append.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_PHASE2_APPEND_HH -#define TAO_CONFIG_INTERNAL_PHASE2_APPEND_HH - -#include "array.hpp" -#include "forward.hpp" - -namespace tao::config::internal -{ - inline void phase2_append( array&& l, array& r ) - { - r.array.splice( r.array.begin(), l.array ); - } - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/phase2_replace.hpp b/include/tao/config/internal/phase2_asterisks.hpp similarity index 83% rename from include/tao/config/internal/phase2_replace.hpp rename to include/tao/config/internal/phase2_asterisks.hpp index f2fb646..991b9e8 100644 --- a/include/tao/config/internal/phase2_replace.hpp +++ b/include/tao/config/internal/phase2_asterisks.hpp @@ -1,11 +1,14 @@ // Copyright (c) 2020-2024 Dr. Colin Hirsch and Daniel Frey // Please see LICENSE for license or visit https://github.com/taocpp/config/ -#ifndef TAO_CONFIG_INTERNAL_PHASE2_REPLACE_HPP -#define TAO_CONFIG_INTERNAL_PHASE2_REPLACE_HPP +#ifndef TAO_CONFIG_INTERNAL_PHASE2_ASTERISKS_HPP +#define TAO_CONFIG_INTERNAL_PHASE2_ASTERISKS_HPP +#include +#include #include #include +#include #include "array.hpp" #include "concat.hpp" @@ -17,11 +20,10 @@ namespace tao::config::internal { - struct phase2_replace_impl + struct phase2_asterisks_impl { - explicit phase2_replace_impl( object& root ) - : m_root( root ), - m_changes( 0 ) + explicit phase2_asterisks_impl( object& root ) + : m_root( root ) {} [[nodiscard]] std::size_t process() @@ -34,7 +36,7 @@ namespace tao::config::internal private: object& m_root; - std::size_t m_changes; + std::size_t m_changes = 0; void process_concat( concat& c ) { @@ -88,9 +90,11 @@ namespace tao::config::internal case entry_kind::value: continue; case entry_kind::reference: - ++i; - return; + continue; case entry_kind::array: + if( !j->get_array().function.empty() ) { + throw pegtl::parse_error( "please do not use a star inside of a function", j->get_array().position ); + } process_array_concat_entry( j->get_array(), star ); continue; case entry_kind::object: @@ -107,9 +111,11 @@ namespace tao::config::internal case entry_kind::value: continue; case entry_kind::reference: - ++i; - return; + continue; case entry_kind::array: + if( !j->get_array().function.empty() ) { + throw pegtl::parse_error( "please do not use a star inside of a function", j->get_array().position ); + } process_concat_entry_array( star, j->get_array() ); continue; case entry_kind::object: @@ -161,9 +167,9 @@ namespace tao::config::internal } }; - [[nodiscard]] inline std::size_t phase2_replace( object& root ) + [[nodiscard]] inline std::size_t phase2_asterisks( object& root ) { - return phase2_replace_impl( root ).process(); + return phase2_asterisks_impl( root ).process(); } } // namespace tao::config::internal diff --git a/include/tao/config/internal/phase2_combine.hpp b/include/tao/config/internal/phase2_combinations.hpp similarity index 63% rename from include/tao/config/internal/phase2_combine.hpp rename to include/tao/config/internal/phase2_combinations.hpp index c7b47a7..3f9232e 100644 --- a/include/tao/config/internal/phase2_combine.hpp +++ b/include/tao/config/internal/phase2_combinations.hpp @@ -1,10 +1,12 @@ // Copyright (c) 2020-2024 Dr. Colin Hirsch and Daniel Frey // Please see LICENSE for license or visit https://github.com/taocpp/config/ -#ifndef TAO_CONFIG_INTERNAL_PHASE2_COMBINE_HPP -#define TAO_CONFIG_INTERNAL_PHASE2_COMBINE_HPP +#ifndef TAO_CONFIG_INTERNAL_PHASE2_COMBINATIONS_HPP +#define TAO_CONFIG_INTERNAL_PHASE2_COMBINATIONS_HPP #include +#include +#include #include #include "array.hpp" @@ -14,17 +16,14 @@ #include "json.hpp" #include "json_traits.hpp" #include "object.hpp" -#include "phase2_add.hpp" -#include "phase2_append.hpp" -#include "phase2_insert.hpp" +#include "phase2_additions.hpp" namespace tao::config::internal { - struct phase2_combine_impl + struct phase2_combinations_impl { - explicit phase2_combine_impl( object& root ) - : m_root( root ), - m_changes( 0 ) + explicit phase2_combinations_impl( object& root ) + : m_root( root ) {} [[nodiscard]] std::size_t process() @@ -37,7 +36,7 @@ namespace tao::config::internal private: object& m_root; - std::size_t m_changes; + std::size_t m_changes = 0; void process_concat( concat& c ) { @@ -50,14 +49,14 @@ namespace tao::config::internal if( c.concat.size() == 1 ) { return; } - assert( c.concat.size() >= 2 ); + // assert( c.concat.size() >= 2 ); for( auto r = ++c.concat.begin(); r != c.concat.end(); ++r ) { const auto l = std::prev( r ); switch( r->kind() ) { case entry_kind::value: if( l->kind() == entry_kind::value ) { - phase2_add( std::move( l->get_value() ), r->get_value() ); + phase2_additions( std::move( l->get_value() ), r->get_value() ); [[maybe_unused]] const auto t = c.concat.erase( l ); assert( t == r ); ++m_changes; @@ -66,8 +65,8 @@ namespace tao::config::internal case entry_kind::reference: continue; case entry_kind::array: - if( l->kind() == entry_kind::array ) { - phase2_append( std::move( l->get_array() ), r->get_array() ); + if( ( l->kind() == entry_kind::array ) && ( l->get_array().function.empty() ) && ( r->get_array().function.empty() ) ) { + process_array( std::move( l->get_array() ), r->get_array() ); [[maybe_unused]] const auto t = c.concat.erase( l ); assert( t == r ); ++m_changes; @@ -75,7 +74,7 @@ namespace tao::config::internal continue; case entry_kind::object: if( l->kind() == entry_kind::object ) { - phase2_insert( std::move( l->get_object() ), r->get_object() ); + process_object( std::move( l->get_object() ), r->get_object() ); [[maybe_unused]] const auto t = c.concat.erase( l ); assert( t == r ); ++m_changes; @@ -111,11 +110,35 @@ namespace tao::config::internal } throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE } + + static void process_array( array&& l, array& r ) + { + r.array.splice( r.array.begin(), l.array ); + } + + static void process_object( object&& l, object& r ) + { + for( std::pair< const std::string, concat >& m : l.object ) { + const auto pair = r.object.try_emplace( m.first, m.second ); + if( !pair.second ) { + if( pair.first->second.remove ) { + continue; + } + if( m.second.remove ) { + pair.first->second.remove = true; + } + if( m.second.temporary ) { + pair.first->second.temporary = true; + } + pair.first->second.concat.splice( pair.first->second.concat.begin(), m.second.concat ); + } + } + } }; - [[nodiscard]] inline std::size_t phase2_combine( object& root ) + [[nodiscard]] inline std::size_t phase2_combinations( object& root ) { - return phase2_combine_impl( root ).process(); + return phase2_combinations_impl( root ).process(); } } // namespace tao::config::internal diff --git a/include/tao/config/internal/phase2_everything.hpp b/include/tao/config/internal/phase2_everything.hpp new file mode 100644 index 0000000..0821e50 --- /dev/null +++ b/include/tao/config/internal/phase2_everything.hpp @@ -0,0 +1,31 @@ +// Copyright (c) 2020-2024 Dr. Colin Hirsch and Daniel Frey +// Please see LICENSE for license or visit https://github.com/taocpp/config/ + +#ifndef TAO_CONFIG_INTERNAL_PHASE2_EVERYTHING_HPP +#define TAO_CONFIG_INTERNAL_PHASE2_EVERYTHING_HPP + +#include "forward.hpp" +#include "phase2_asterisks.hpp" +#include "phase2_combinations.hpp" +#include "phase2_functions.hpp" +#include "phase2_references.hpp" +#include "state.hpp" + +namespace tao::config::internal +{ + [[nodiscard]] inline bool phase2_iteration( state& st, const function_map& fm ) + { + return ( phase2_functions( st, fm ) + phase2_combinations( st.root ) + phase2_references( st.root ) + phase2_asterisks( st.root ) ) > 0; + } + + inline void phase2_everything( state& st, const function_map& fm ) + { + while( phase2_iteration( st, fm ) ) { + // This loop could do with some major optimisations, though they would only be worth it + // if somebody reads some really large config files (or reads one 10^7 times per second). + } + } + +} // namespace tao::config::internal + +#endif diff --git a/include/tao/config/internal/phase2_functions.hpp b/include/tao/config/internal/phase2_functions.hpp new file mode 100644 index 0000000..473f680 --- /dev/null +++ b/include/tao/config/internal/phase2_functions.hpp @@ -0,0 +1,118 @@ +// Copyright (c) 2024 Dr. Colin Hirsch and Daniel Frey +// Please see LICENSE for license or visit https://github.com/taocpp/config/ + +#ifndef TAO_CONFIG_INTERNAL_PHASE2_FUNCTIONS_HPP +#define TAO_CONFIG_INTERNAL_PHASE2_FUNCTIONS_HPP + +#include +#include +#include + +#include "array.hpp" +#include "concat.hpp" +#include "config_action.hpp" +#include "config_grammar.hpp" +#include "entry.hpp" +#include "forward.hpp" +#include "function_traits.hpp" +#include "object.hpp" +#include "pegtl.hpp" +#include "state.hpp" + +namespace tao::config::internal +{ + struct phase2_functions_impl + { + phase2_functions_impl( state& st, const function_map& fm ) + : m_state( st ), + m_functions( fm ) + {} + + [[nodiscard]] std::size_t process() + { + for( auto& p : m_state.root.object ) { + process_concat( p.second ); + } + return m_changes; + } + + private: + state& m_state; + std::size_t m_changes = 0; + const function_map& m_functions; + + void process_concat( concat& c ) + { + for( auto& e : c.concat ) { + process_entry( e ); + } + } + + void process_entry( entry& e ) + { + switch( e.kind() ) { + case entry_kind::value: + return; + case entry_kind::reference: + return; + case entry_kind::array: + for( auto& c : e.get_array().array ) { + process_concat( c ); + } + if( !e.get_array().function.empty() ) { + process_function( e ); + } + return; + case entry_kind::object: + for( auto& p : e.get_object().object ) { + process_concat( p.second ); + } + return; + case entry_kind::concat: + return; + } + throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE + } + + void process_function( entry& e ) + { + array& a = e.get_array(); + + if( a.function == "parse" ) { + process_parse_function( e, a ); + ++m_changes; + return; + } + const auto i = m_functions.find( a.function ); + + if( i == m_functions.end() ) { + throw pegtl::parse_error( "unknown function name " + a.function, a.position ); + } + if( i->second( e ) ) { + ++m_changes; + } + } + + void process_parse_function( entry& e, array& a ) + { + state st; + std::size_t i = 0; + const std::string s = function_traits< std::string >::get( a, i ); + const key1 k = { key1_part( std::string( "\0", 1 ), a.position ) }; + const key1_guard kg( st, key1( k ) ); + pegtl::string_input< pegtl::tracking_mode::eager, pegtl_input_t::eol_t > in( s, __FUNCTION__ ); + pegtl::parse_nested< rules::value, config_action >( a.position, static_cast< pegtl_input_t& >( in ), st, m_functions ); + assert( st.root.object.size() == 1 ); + assert( st.root.object.begin()->second.concat.size() == 1 ); + e = st.root.object.begin()->second.concat.front(); // TODO: This is slightly hack-ish. + } + }; + + [[nodiscard]] inline std::size_t phase2_functions( state& st, const function_map& fm ) + { + return phase2_functions_impl( st, fm ).process(); + } + +} // namespace tao::config::internal + +#endif diff --git a/include/tao/config/internal/phase2_guard.hpp b/include/tao/config/internal/phase2_guard.hpp index 1baa5bb..a85a656 100644 --- a/include/tao/config/internal/phase2_guard.hpp +++ b/include/tao/config/internal/phase2_guard.hpp @@ -5,6 +5,7 @@ #define TAO_CONFIG_INTERNAL_PHASE2_GUARD_HPP #include +#include #include #include "pegtl.hpp" diff --git a/include/tao/config/internal/phase2_insert.hpp b/include/tao/config/internal/phase2_insert.hpp deleted file mode 100644 index 6d95bdc..0000000 --- a/include/tao/config/internal/phase2_insert.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_PHASE2_INSERT_HH -#define TAO_CONFIG_INTERNAL_PHASE2_INSERT_HH - -#include - -#include "concat.hpp" -#include "forward.hpp" -#include "object.hpp" - -namespace tao::config::internal -{ - inline void phase2_insert( object&& l, object& r ) - { - for( std::pair< const std::string, concat >& m : l.object ) { - const auto pair = r.object.try_emplace( m.first, m.second ); - if( !pair.second ) { - if( pair.first->second.remove ) { - continue; - } - if( m.second.remove ) { - pair.first->second.remove = true; - } - if( m.second.temporary ) { - pair.first->second.temporary = true; - } - pair.first->second.concat.splice( pair.first->second.concat.begin(), m.second.concat ); - } - } - } - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/phase2_resolve.hpp b/include/tao/config/internal/phase2_references.hpp similarity index 89% rename from include/tao/config/internal/phase2_resolve.hpp rename to include/tao/config/internal/phase2_references.hpp index 84d4473..230e61e 100644 --- a/include/tao/config/internal/phase2_resolve.hpp +++ b/include/tao/config/internal/phase2_references.hpp @@ -1,12 +1,15 @@ // Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey // Please see LICENSE for license or visit https://github.com/taocpp/config/ -#ifndef TAO_CONFIG_INTERNAL_PHASE2_RESOLVE_HPP -#define TAO_CONFIG_INTERNAL_PHASE2_RESOLVE_HPP +#ifndef TAO_CONFIG_INTERNAL_PHASE2_REFERENCES_HPP +#define TAO_CONFIG_INTERNAL_PHASE2_REFERENCES_HPP #include +#include +#include #include #include +#include #include "array.hpp" #include "concat.hpp" @@ -21,11 +24,10 @@ namespace tao::config::internal { - struct phase2_resolve_impl + struct phase2_references_impl { - explicit phase2_resolve_impl( object& root ) - : m_root( root ), - m_changes( 0 ) + explicit phase2_references_impl( object& root ) + : m_root( root ) {} [[nodiscard]] std::size_t process() @@ -41,7 +43,7 @@ namespace tao::config::internal private: object& m_root; - std::size_t m_changes; + std::size_t m_changes = 0; std::set< const void* > m_stack; void process_concat( const key1& prefix, concat& c ) @@ -56,7 +58,7 @@ namespace tao::config::internal assert( d->concat.size() == 1 ); e = d->concat.front(); - // TODO: Call phase2_combine( c ) to get things done quicker? + // TODO: Call phase2_combinations( c ) to get things done quicker? ++m_changes; } } @@ -138,9 +140,9 @@ namespace tao::config::internal } }; - [[nodiscard]] inline std::size_t phase2_resolve( object& root ) + [[nodiscard]] inline std::size_t phase2_references( object& root ) { - return phase2_resolve_impl( root ).process(); + return phase2_references_impl( root ).process(); } } // namespace tao::config::internal diff --git a/include/tao/config/internal/phase3_remove.hpp b/include/tao/config/internal/phase3_remove.hpp index e6c3db0..3947507 100644 --- a/include/tao/config/internal/phase3_remove.hpp +++ b/include/tao/config/internal/phase3_remove.hpp @@ -5,6 +5,7 @@ #define TAO_CONFIG_INTERNAL_PHASE3_REMOVE_HPP #include +#include #include "array.hpp" #include "concat.hpp" @@ -62,6 +63,9 @@ namespace tao::config::internal case entry_kind::reference: throw pegtl::parse_error( "unresolved reference '" + e.get_reference().to_string() + '\'', e.get_reference().at( 0 ).position ); case entry_kind::array: + if( !e.get_array().function.empty() ) { + throw pegtl::parse_error( "unresolved function '" + e.get_array().function + '\'', e.get_array().position ); + } phase3_remove( e.get_array() ); continue; case entry_kind::object: diff --git a/include/tao/config/internal/phase4_schema.hpp b/include/tao/config/internal/phase4_schema.hpp deleted file mode 100644 index e8f9110..0000000 --- a/include/tao/config/internal/phase4_schema.hpp +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_PHASE4_SCHEMA_HPP -#define TAO_CONFIG_INTERNAL_PHASE4_SCHEMA_HPP - -#include - -#include "array.hpp" -#include "concat.hpp" -#include "entry.hpp" -#include "forward.hpp" -#include "object.hpp" -#include "phase5_repack.hpp" - -#include "../schema/builtin.hpp" -#include "../schema/internal/forward.hpp" - -#include "../value.hpp" - -namespace tao::config::internal -{ - inline void phase4_schema( const value& v, const std::string& s, const schema::builtin& b ) - { - if( const auto error = schema::internal::phase2_from_file_and_validate( s, b, v ) ) { - throw std::runtime_error( json::to_string( error ) ); - } - } - -#if defined( _MSC_VER ) -#pragma warning( push ) -#pragma warning( disable : 4702 ) -#endif - - inline void phase4_schema( const concat& c, const schema::builtin& b ) - { - if( !c.schema.empty() ) { - phase4_schema( phase5_repack< traits >( c ), c.schema, b ); - } - for( const auto& e : c.concat ) { - switch( e.kind() ) { - case entry_kind::value: - return; - case entry_kind::reference: - throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE - case entry_kind::array: - for( const auto& i : e.get_array().array ) { - phase4_schema( i, b ); - } - return; - case entry_kind::object: - for( const auto& p : e.get_object().object ) { - phase4_schema( p.second, b ); - } - return; - case entry_kind::concat: - throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE - } - throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE - } - } - -#if defined( _MSC_VER ) -#pragma warning( pop ) -#endif - - inline void phase4_schema( const object& root, const std::string& s, const schema::builtin& b = schema::builtin() ) - { - if( !s.empty() ) { - phase4_schema( phase5_repack< traits >( root ), s, b ); - } - for( const auto& p : root.object ) { - phase4_schema( p.second, b ); - } - } - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/phase5_repack.hpp b/include/tao/config/internal/phase5_repack.hpp index f9ec71e..4b6596c 100644 --- a/include/tao/config/internal/phase5_repack.hpp +++ b/include/tao/config/internal/phase5_repack.hpp @@ -5,6 +5,7 @@ #define TAO_CONFIG_INTERNAL_PHASE5_REPACK_HPP #include +#include #include #include @@ -102,6 +103,9 @@ namespace tao::config::internal case entry_kind::reference: throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE -- must have been either eliminated or flagged as error earlier. case entry_kind::array: + if( !e.get_array().function.empty() ) { + throw std::logic_error( "code should be unreachable" ); // LCOV_EXCL_LINE -- must have been either eliminated or flagged as error earlier. + } phase5_repack( k, consumer, e.get_array() ); return; case entry_kind::object: diff --git a/include/tao/config/internal/reference2.hpp b/include/tao/config/internal/reference2.hpp index 797b4bc..fdd5121 100644 --- a/include/tao/config/internal/reference2.hpp +++ b/include/tao/config/internal/reference2.hpp @@ -4,6 +4,8 @@ #ifndef TAO_CONFIG_INTERNAL_REFERENCE2_HPP #define TAO_CONFIG_INTERNAL_REFERENCE2_HPP +#include +#include #include #include "reference2_action.hpp" diff --git a/include/tao/config/internal/reference2_action.hpp b/include/tao/config/internal/reference2_action.hpp index 0ec20b3..75019f8 100644 --- a/include/tao/config/internal/reference2_action.hpp +++ b/include/tao/config/internal/reference2_action.hpp @@ -4,6 +4,8 @@ #ifndef TAO_CONFIG_INTERNAL_REFERENCE2_ACTION_HPP #define TAO_CONFIG_INTERNAL_REFERENCE2_ACTION_HPP +#include +#include #include #include "json.hpp" diff --git a/include/tao/config/internal/reference2_part.hpp b/include/tao/config/internal/reference2_part.hpp index 3a216ac..a49bba8 100644 --- a/include/tao/config/internal/reference2_part.hpp +++ b/include/tao/config/internal/reference2_part.hpp @@ -1,10 +1,11 @@ // Copyright (c) 2020-2024 Dr. Colin Hirsch and Daniel Frey // Please see LICENSE for license or visit https://github.com/taocpp/config/ -#ifndef TAO_CONFIG_INTERNAL_PART2_HPP -#define TAO_CONFIG_INTERNAL_PART2_HPP +#ifndef TAO_CONFIG_INTERNAL_REFERENCE2_PART_HPP +#define TAO_CONFIG_INTERNAL_REFERENCE2_PART_HPP #include +#include #include #include #include diff --git a/include/tao/config/internal/repack_traits.hpp b/include/tao/config/internal/repack_traits.hpp index 579216e..da273fb 100644 --- a/include/tao/config/internal/repack_traits.hpp +++ b/include/tao/config/internal/repack_traits.hpp @@ -1,8 +1,8 @@ // Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey // Please see LICENSE for license or visit https://github.com/taocpp/config/ -#ifndef TAO_CONFIG_INTERNAL_REPACk_TRAITS_HPP -#define TAO_CONFIG_INTERNAL_REPACk_TRAITS_HPP +#ifndef TAO_CONFIG_INTERNAL_REPACK_TRAITS_HPP +#define TAO_CONFIG_INTERNAL_REPACK_TRAITS_HPP #include diff --git a/include/tao/config/internal/result_traits.hpp b/include/tao/config/internal/result_traits.hpp deleted file mode 100644 index eb95baf..0000000 --- a/include/tao/config/internal/result_traits.hpp +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2020-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_RESULT_TRAITS_HPP -#define TAO_CONFIG_INTERNAL_RESULT_TRAITS_HPP - -#include -#include -#include -#include - -#include "forward.hpp" -#include "json.hpp" -#include "json_traits.hpp" -#include "pegtl.hpp" - -namespace tao::config::internal -{ - template< typename T > - struct result_traits - { - static json_t convert( const pegtl::position& p, const T& t ) - { - return json_t( t, p ); - } - }; - - template<> - struct result_traits< json_t > - { - static json_t convert( const pegtl::position& /*unused*/, json_t&& t ) - { - return std::move( t ); - } - - static json_t convert( const pegtl::position& /*unused*/, const json_t& t ) - { - return t; - } - }; - - template<> - struct result_traits< std::string > - { - static json_t convert( const pegtl::position& p, const std::string& s ) - { - if( !json::internal::validate_utf8_nothrow( s ) ) { - throw pegtl::parse_error( "invalid utf-8 in extension result", p ); // TODO: Name of extension? - } - return json_t( s, p ); - } - }; - - template<> - struct result_traits< std::string_view > - { - static json_t convert( const pegtl::position& p, const std::string_view sv ) - { - return result_traits< std::string >::convert( p, std::string( sv ) ); - } - }; - - template< typename R > - [[nodiscard]] json_t convert_result( const pegtl::position& p, R&& r ) - { - return result_traits< std::decay_t< R > >::convert( p, std::forward< R >( r ) ); - } - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/reverse.hpp b/include/tao/config/internal/reverse.hpp deleted file mode 100644 index f31ac18..0000000 --- a/include/tao/config/internal/reverse.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_REVERSE_HPP -#define TAO_CONFIG_INTERNAL_REVERSE_HPP - -namespace tao::config::internal -{ - template< typename Container > - struct reverse - { - explicit reverse( Container& l ) - : m_c( l ) - {} - - reverse( reverse&& ) = delete; - reverse( const reverse& ) = delete; - - void operator=( reverse&& ) = delete; - void operator=( const reverse& ) = delete; - - auto begin() - { - return m_c.rbegin(); - } - - auto end() - { - return m_c.rend(); - } - - private: - Container& m_c; - }; - - template< typename T > - reverse( T& ) -> reverse< T >; - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/state.hpp b/include/tao/config/internal/state.hpp index fa4021c..ee93b69 100644 --- a/include/tao/config/internal/state.hpp +++ b/include/tao/config/internal/state.hpp @@ -5,13 +5,7 @@ #define TAO_CONFIG_INTERNAL_STATE_HPP #include -#include -#include -#include -#include "array.hpp" -#include "concat.hpp" -#include "entry.hpp" #include "key1.hpp" #include "object.hpp" #include "pegtl.hpp" @@ -34,10 +28,11 @@ namespace tao::config::internal key1 prefix; key1 suffix; + key1 member; object root; - std::string schema; + bool include_is_optional; std::uint64_t generation = 1; }; diff --git a/include/tao/config/internal/statistics.hpp b/include/tao/config/internal/statistics.hpp new file mode 100644 index 0000000..94966da --- /dev/null +++ b/include/tao/config/internal/statistics.hpp @@ -0,0 +1,114 @@ +// Copyright (c) 2024 Dr. Colin Hirsch and Daniel Frey +// Please see LICENSE for license or visit https://github.com/taocpp/config/ + +#ifndef TAO_CONFIG_INTERNAL_STATISTICS_HPP +#define TAO_CONFIG_INTERNAL_STATISTICS_HPP + +#include +#include + +#include "array.hpp" +#include "concat.hpp" +#include "entry.hpp" +#include "object.hpp" + +namespace tao::config::internal +{ + struct statistics + { + statistics() noexcept = default; + + explicit statistics( const entry& e ) + { + count( e ); + } + + explicit statistics( const concat& c ) + { + count( c ); + } + + void count( const entry& e ) + { + switch( e.kind() ) { + case entry_kind::value: + ++m_atoms; + return; + case entry_kind::reference: + ++m_references; + return; + case entry_kind::array: + count( e.get_array() ); + return; + case entry_kind::object: + count( e.get_object() ); + return; + case entry_kind::concat: + ++m_asterisks; + count( e.get_concat() ); + return; + } + std::abort(); // LCOV_EXCL_LINE + } + + void count( const array& a ) + { + if( a.function.empty() ) { + ++m_arrays; + } + else { + ++m_functions; + } + for( const auto& c : a.array ) { + count( c ); + } + } + + void count( const object& o ) + { + for( const auto& p : o.object ) { + count( p.second ); + } + ++m_objects; + } + + void count( const concat& c ) + { + if( c.concat.size() > 1 ) { + ++m_additions; + } + for( const auto& e : c.concat ) { + count( e ); + } + } + + [[nodiscard]] std::size_t nulls() const noexcept + { + return m_nulls; + } + + [[nodiscard]] std::size_t references() const noexcept + { + return m_references; + } + + [[nodiscard]] bool is_primitive() const noexcept + { + return ( m_functions == 0 ) && ( m_variables == 0 ) && ( m_additions == 0 ) && ( m_asterisks == 0 ) && ( m_references == 0 ); + } + + private: + std::size_t m_nulls = 0; + std::size_t m_atoms = 0; + std::size_t m_arrays = 0; + std::size_t m_objects = 0; + std::size_t m_functions = 0; + std::size_t m_variables = 0; + std::size_t m_additions = 0; + std::size_t m_asterisks = 0; + std::size_t m_references = 0; + }; + +} // namespace tao::config::internal + +#endif diff --git a/include/tao/config/internal/system_utility.hpp b/include/tao/config/internal/system_utility.hpp index c349cdd..8dc05dc 100644 --- a/include/tao/config/internal/system_utility.hpp +++ b/include/tao/config/internal/system_utility.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "pegtl.hpp" @@ -33,7 +34,7 @@ namespace tao::config::internal } } - [[nodiscard]] inline std::optional< std::string > get_env_nothrow( const std::string& name ) + [[nodiscard]] inline std::optional< std::string > getenv_nothrow( const std::string& name ) { #if defined( _MSC_VER ) char buffer[ 256 ]; @@ -52,30 +53,14 @@ namespace tao::config::internal return std::nullopt; } - [[nodiscard]] inline std::string get_env_throws( const pegtl::position& pos, const std::string& name ) + [[nodiscard]] inline std::string getenv_throws( const pegtl::position& pos, const std::string& name ) { - if( auto result = get_env_nothrow( name ) ) { + if( auto result = getenv_nothrow( name ) ) { return std::move( *result ); } throw pegtl::parse_error( "environment variable '" + name + "' not found", pos ); } - inline void set_env_throws( const pegtl::position& /*pos*/, const std::string& name, const std::string& value ) - { -#if defined( _MSC_VER ) - const auto e = ::_putenv_s( name.c_str(), value.c_str() ); - if( e != 0 ) { -#else - errno = 0; - if( ::setenv( name.c_str(), value.c_str(), 1 ) != 0 ) { - const auto e = errno; -#endif - (void)e; - throw std::string( "TODO" ); - // throw pegtl::parse_error( format( __FILE__, __LINE__, "failed to set environment variable", { { "name", name }, { "value", value }, { "errno", e } } ), pos ); - } - } - #if !defined( _MSC_VER ) [[nodiscard]] inline std::string shell_popen_throws( const pegtl::position& /*pos*/, const std::string& script ) { diff --git a/include/tao/config/internal/to_stream.hpp b/include/tao/config/internal/to_stream.hpp index c4e9b02..fc9c25b 100644 --- a/include/tao/config/internal/to_stream.hpp +++ b/include/tao/config/internal/to_stream.hpp @@ -4,6 +4,7 @@ #ifndef TAO_CONFIG_INTERNAL_TO_STREAM_HPP #define TAO_CONFIG_INTERNAL_TO_STREAM_HPP +#include #include #include "debug_traits.hpp" diff --git a/include/tao/config/internal/value_extensions.hpp b/include/tao/config/internal/value_extensions.hpp deleted file mode 100644 index fec8daf..0000000 --- a/include/tao/config/internal/value_extensions.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_VALUE_EXTENSIONS_HPP -#define TAO_CONFIG_INTERNAL_VALUE_EXTENSIONS_HPP - -#include - -#include "concat.hpp" -#include "config_action.hpp" -#include "extension_maps.hpp" -#include "extension_wrapper.hpp" -#include "forward.hpp" -#include "parse_utility.hpp" -#include "pegtl.hpp" -#include "phase1_append.hpp" -#include "state.hpp" - -namespace tao::config::internal -{ - [[nodiscard]] inline bool do_value_extension( pegtl_input_t& in, state& st, const extension_maps& em ) - { - const std::string name = parse_name( in ); - { - const auto i = em.value.find( name ); - if( i != em.value.end() ) { - i->second( in, st, em ); - return true; - } - } - { - const auto i = em.inner.find( name ); - if( i != em.inner.end() ) { - const auto j = i->second( in, st, em ); - const auto f = [ & ]( concat& c ) { c.concat.emplace_back( j ); }; - phase1_append( st.root, st.prefix + st.suffix, f, phase1_mode::manifest ); - return true; - } - } - throw pegtl::parse_error( "unknown value extension " + name, in.position() ); - } - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/internal/value_functions.hpp b/include/tao/config/internal/value_functions.hpp deleted file mode 100644 index ce1808a..0000000 --- a/include/tao/config/internal/value_functions.hpp +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2018-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_INTERNAL_VALUE_FUNCTIONS_HPP -#define TAO_CONFIG_INTERNAL_VALUE_FUNCTIONS_HPP - -#include "config_action.hpp" -#include "config_grammar.hpp" -#include "extension_maps.hpp" -#include "state.hpp" - -namespace tao::config::internal -{ - inline void value_function( const pegtl::position& p, state& st, const extension_maps& em, const std::string& s ) - { - pegtl::string_input< pegtl::tracking_mode::eager, pegtl_input_t::eol_t > in( s, __FUNCTION__ ); - pegtl::parse_nested< rules::value, config_action >( p, static_cast< pegtl_input_t& >( in ), st, em ); - } - -} // namespace tao::config::internal - -#endif diff --git a/include/tao/config/parser.hpp b/include/tao/config/parser.hpp index 912e328..50be23e 100644 --- a/include/tao/config/parser.hpp +++ b/include/tao/config/parser.hpp @@ -5,6 +5,7 @@ #define TAO_CONFIG_PARSER_HPP #include "internal/config_parser.hpp" +#include "internal/function_wrapper.hpp" namespace tao::config { @@ -26,13 +27,13 @@ namespace tao::config template< typename F > void set_inner_extension( const std::string& name, F& f ) { - m_parser.em.inner[ name ] = internal::wrap( f ); + m_parser.fm[ name ] = internal::wrap( f ); } template< template< typename... > class Traits > - [[nodiscard]] json::basic_value< Traits > result( const schema::builtin& b = schema::builtin() ) + [[nodiscard]] json::basic_value< Traits > result() { - return m_parser.finish< Traits >( b ); + return m_parser.finish< Traits >(); } protected: diff --git a/include/tao/config/schema.hpp b/include/tao/config/schema.hpp deleted file mode 100644 index c8ee62f..0000000 --- a/include/tao/config/schema.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_HPP -#define TAO_CONFIG_SCHEMA_HPP - -#include - -#include - -#include "value.hpp" - -#include "schema/builtin.hpp" -#include "schema/from_file.hpp" -#include "schema/from_input.hpp" - -namespace tao::config::schema::internal -{ - inline json::value phase2_from_file_and_validate( const std::filesystem::path& schema, const builtin& b, const value& config_value ) - { - return from_file( schema, b ).validate( config_value ); - } - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/builtin.hpp b/include/tao/config/schema/builtin.hpp deleted file mode 100644 index 070cb7f..0000000 --- a/include/tao/config/schema/builtin.hpp +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_BUILTIN_HPP -#define TAO_CONFIG_SCHEMA_BUILTIN_HPP - -#include -#include -#include - -#include "internal/array.hpp" -#include "internal/binary.hpp" -#include "internal/boolean.hpp" -#include "internal/grammar.hpp" -#include "internal/is_integer.hpp" -#include "internal/is_regex.hpp" -#include "internal/is_unsigned.hpp" -#include "internal/node.hpp" -#include "internal/null.hpp" -#include "internal/number.hpp" -#include "internal/object.hpp" -#include "internal/string.hpp" - -namespace tao::config::schema -{ - struct builtin - { - internal::node_map m_nodes; - - template< typename T > - void add( const char* name ) - { - value source; - source.key = name; - source.position = json::position( "", 0, 0 ); - m_nodes.emplace( name, std::make_shared< T >( source ) ); - } - - builtin() - { - add< internal::null >( "null" ); - add< internal::boolean >( "boolean" ); - add< internal::number >( "number" ); - add< internal::string >( "string" ); - add< internal::binary >( "binary" ); - add< internal::array >( "array" ); - add< internal::object >( "object" ); - - add< internal::is_integer >( "std.integer" ); - add< internal::is_unsigned >( "std.unsigned" ); - add< internal::is_regex >( "std.regex" ); - { - using namespace pegtl; - - add< internal::grammar< identifier > >( "std.identifier" ); - add< internal::grammar< pegtl::list< identifier, one< '.' > > > >( "std.key" ); - - add< internal::grammar< json_pointer::json_pointer > >( "std.json_pointer" ); - add< internal::grammar< json_pointer::relative_json_pointer > >( "std.relative_json_pointer" ); - - using namespace uri; - add< internal::grammar< URI > >( "std.net.uri" ); - add< internal::grammar< URI_reference > >( "std.net.uri_reference" ); - - add< internal::grammar< IPv4address > >( "std.net.ip_v4_address" ); - add< internal::grammar< IPv6address > >( "std.net.ip_v6_address" ); - add< internal::grammar< sor< IPv4address, IPv6address > > >( "std.net.ip_address" ); - - using ip_v4_cidr = seq< IPv4address, one< '/' >, maximum_rule< std::uint8_t, 32 > >; - using ip_v6_cidr = seq< IPv6address, one< '/' >, maximum_rule< std::uint8_t, 128 > >; - add< internal::grammar< ip_v4_cidr > >( "std.net.ip_v4_cidr" ); - add< internal::grammar< ip_v6_cidr > >( "std.net.ip_v6_cidr" ); - add< internal::grammar< sor< ip_v4_cidr, ip_v6_cidr > > >( "std.net.ip_cidr" ); - } - } - }; - -} // namespace tao::config::schema - -#endif diff --git a/include/tao/config/schema/from_file.hpp b/include/tao/config/schema/from_file.hpp deleted file mode 100644 index 5ef0c1d..0000000 --- a/include/tao/config/schema/from_file.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_FROM_FILE_HPP -#define TAO_CONFIG_SCHEMA_FROM_FILE_HPP - -#include - -#include "from_input.hpp" - -namespace tao::config::schema -{ - inline validator from_file( const std::filesystem::path& path, const builtin& b = builtin() ) - { - return from_input( pegtl::file_input( path ), b ); - } - -} // namespace tao::config::schema - -#endif diff --git a/include/tao/config/schema/from_input.hpp b/include/tao/config/schema/from_input.hpp deleted file mode 100644 index 09ad913..0000000 --- a/include/tao/config/schema/from_input.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_FROM_INPUT_HPP -#define TAO_CONFIG_SCHEMA_FROM_INPUT_HPP - -#include -#include - -#include "../from_input.hpp" - -#include "builtin.hpp" -#include "validator.hpp" - -#include "internal/validator.hpp" - -namespace tao::config::schema -{ - inline validator from_input( pegtl_input_t&& in, const builtin& b = builtin() ) - { - const config::value s = config::from_input( std::move( in ) ); - - if( const auto error = tao::config::schema::internal::validator.validate( s ) ) { - std::cerr << std::setw( 2 ) << error << std::endl; // TODO: Remove this line... - throw std::runtime_error( std::string( "invalid schema from '" ) + in.source() + "'" ); // TODO: ...and store error in exception. - } - return validator( s, b ); - } - -} // namespace tao::config::schema - -#endif diff --git a/include/tao/config/schema/internal/all_of.hpp b/include/tao/config/schema/internal/all_of.hpp deleted file mode 100644 index e8a82a6..0000000 --- a/include/tao/config/schema/internal/all_of.hpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_ALL_OF_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_ALL_OF_HPP - -#include "container.hpp" - -namespace tao::config::schema::internal -{ - struct all_of : container - { - using container::container; - - json::value validate( const value& v ) const override - { - const auto& vs = v.skip_value_ptr(); - for( const auto& p : m_properties ) { - if( auto e = p->validate( vs ) ) { - // short-circuit - return e; - } - } - return ok(); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/any_of.hpp b/include/tao/config/schema/internal/any_of.hpp deleted file mode 100644 index 0e5c82a..0000000 --- a/include/tao/config/schema/internal/any_of.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_ANY_OF_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_ANY_OF_HPP - -#include "container.hpp" - -namespace tao::config::schema::internal -{ - struct any_of : container - { - using container::container; - - json::value validate( const value& v ) const override - { - const auto& vs = v.skip_value_ptr(); - - json::value errors = json::empty_array; - for( const auto& p : m_properties ) { - if( auto e = p->validate( vs ) ) { - errors.emplace_back( std::move( e ) ); - } - else { - // short-circuit - return ok(); - } - } - // TODO: Pick "best" error - return error( v, "no match", { { "errors", std::move( errors ) } } ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/array.hpp b/include/tao/config/schema/internal/array.hpp deleted file mode 100644 index f89eb6b..0000000 --- a/include/tao/config/schema/internal/array.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_ARRAY_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_ARRAY_HPP - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - struct array : node - { - using node::node; - - json::value validate( const value& v ) const override - { - return v.is_array() ? ok() : error( v, "expected array" ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/binary.hpp b/include/tao/config/schema/internal/binary.hpp deleted file mode 100644 index 08d8197..0000000 --- a/include/tao/config/schema/internal/binary.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_BINARY_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_BINARY_HPP - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - struct binary : node - { - using node::node; - - json::value validate( const value& v ) const override - { - return v.is_binary_type() ? ok() : error( v, "expected binary" ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/boolean.hpp b/include/tao/config/schema/internal/boolean.hpp deleted file mode 100644 index e6040b9..0000000 --- a/include/tao/config/schema/internal/boolean.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_BOOLEAN_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_BOOLEAN_HPP - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - struct boolean : node - { - using node::node; - - json::value validate( const value& v ) const override - { - return v.is_boolean() ? ok() : error( v, "expected boolean" ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/compare.hpp b/include/tao/config/schema/internal/compare.hpp deleted file mode 100644 index f558269..0000000 --- a/include/tao/config/schema/internal/compare.hpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_COMPARE_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_COMPARE_HPP - -#include - -#include "node.hpp" -#include "number.hpp" - -namespace tao::config::schema::internal -{ - template< typename T > - struct compare : node - { - explicit compare( const value& v ) - : node( v ) - { - assert( m_source.is_number() ); - } - - json::value validate( const value& v ) const override - { - if( auto e = number( m_source ).validate( v ) ) { - return e; - } - return T()( m_source, v ) ? ok() : error( v, "value did not match" ); - } - }; - - using minimum = compare< std::less_equal<> >; - using maximum = compare< std::greater_equal<> >; - using exclusive_minimum = compare< std::less<> >; - using exclusive_maximum = compare< std::greater<> >; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/constant.hpp b/include/tao/config/schema/internal/constant.hpp deleted file mode 100644 index 50281d6..0000000 --- a/include/tao/config/schema/internal/constant.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_CONSTANT_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_CONSTANT_HPP - -#include "ref.hpp" - -namespace tao::config::schema::internal -{ - struct constant : node - { - using node::node; - - json::value validate( const value& v ) const override - { - return ( v == m_source ) ? ok() : error( v, "value did not match" ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/container.hpp b/include/tao/config/schema/internal/container.hpp deleted file mode 100644 index 36eda32..0000000 --- a/include/tao/config/schema/internal/container.hpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_CONTAINER_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_CONTAINER_HPP - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - struct container : node - { - using node::node; - - std::vector< std::shared_ptr< node > > m_properties; - - void resolve( const node_map& m ) override - { - for( const auto& p : m_properties ) { - p->resolve( m ); - } - } - - json::value pos() const override - { - if( m_properties.size() == 1 ) { - auto result = m_properties.front()->pos(); - append_via( result, node::pos() ); - return result; - } - else { - return node::pos(); - } - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/forward.hpp b/include/tao/config/schema/internal/forward.hpp deleted file mode 100644 index 0d5db79..0000000 --- a/include/tao/config/schema/internal/forward.hpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_FORWARD_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_FORWARD_HPP - -#include - -#include - -#include "../../value.hpp" - -#include "../builtin.hpp" - -namespace tao::config::schema::internal -{ - inline json::value phase2_from_file_and_validate( const std::filesystem::path& schema, const builtin& b, const value& config_value ); - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/grammar.hpp b/include/tao/config/schema/internal/grammar.hpp deleted file mode 100644 index e87b32e..0000000 --- a/include/tao/config/schema/internal/grammar.hpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_GRAMMAR_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_GRAMMAR_HPP - -#include "../../internal/pegtl.hpp" - -#include "node.hpp" -#include "string.hpp" - -namespace tao::config::schema::internal -{ - template< typename Grammar > - struct grammar : node - { - using node::node; - - json::value validate( const value& v ) const override - { - if( auto e = string( m_source ).validate( v ) ) { - return e; - } - const std::string_view sv = v.as< std::string_view >(); - if( pegtl::parse< pegtl::seq< Grammar, pegtl::eof > >( pegtl_input_t( sv, "" ) ) ) { - return ok(); - } - return error( v, "invalid format" ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/has_property.hpp b/include/tao/config/schema/internal/has_property.hpp deleted file mode 100644 index f993173..0000000 --- a/include/tao/config/schema/internal/has_property.hpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_HAS_PROPERTY_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_HAS_PROPERTY_HPP - -#include "node.hpp" -#include "object.hpp" - -namespace tao::config::schema::internal -{ - struct has_property : node - { - std::vector< std::string > m_keys; - - explicit has_property( const value& v ) - : node( v ) - { - if( v.is_string_type() ) { - m_keys.emplace_back( v.as< std::string_view >() ); - } - else { - for( const auto& e : v.get_array() ) { - m_keys.emplace_back( e.as< std::string_view >() ); - } - } - } - - json::value validate( const value& v ) const override - { - if( auto e = object( m_source ).validate( v ) ) { - return e; - } - const auto& o = v.get_object(); - for( const auto& k : m_keys ) { - if( o.find( k ) != o.end() ) { - return ok(); - } - } - return error( v, "missing property", { { "keys", m_keys } } ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/if_then_else.hpp b/include/tao/config/schema/internal/if_then_else.hpp deleted file mode 100644 index 4386125..0000000 --- a/include/tao/config/schema/internal/if_then_else.hpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_IF_THEN_ELSE_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_IF_THEN_ELSE_HPP - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - struct if_then_else : ref - { - std::shared_ptr< ref > m_then; - std::shared_ptr< ref > m_else; - - using ref::ref; - - void resolve( const node_map& m ) override - { - ref::resolve( m ); - if( m_then ) { - m_then->resolve( m ); - } - if( m_else ) { - m_else->resolve( m ); - } - } - - json::value validate( const value& v ) const override - { - if( !ref::validate( v ) ) { - if( m_then ) { - return m_then->validate( v ); - } - } - else { - if( m_else ) { - return m_else->validate( v ); - } - } - return ok(); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/iless.hpp b/include/tao/config/schema/internal/iless.hpp deleted file mode 100644 index 126b4e4..0000000 --- a/include/tao/config/schema/internal/iless.hpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_ILESS_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_ILESS_HPP - -#include - -#if defined( _MSC_VER ) -#include -#define strncasecmp _strnicmp -#else -#include -#endif - -namespace tao::config::schema::internal -{ - struct iless - { - // TODO: Compare international strings (Unicode, using ICU) - bool operator()( const std::string_view lhs, const std::string_view rhs ) const noexcept - { - const auto d = strncasecmp( &( *lhs.begin() ), &( *rhs.begin() ), ( std::min )( lhs.size(), rhs.size() ) ); - return ( d < 0 ) || ( ( d == 0 ) && ( lhs.size() < rhs.size() ) ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/is_integer.hpp b/include/tao/config/schema/internal/is_integer.hpp deleted file mode 100644 index ea46c97..0000000 --- a/include/tao/config/schema/internal/is_integer.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_IS_INTEGER_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_IS_INTEGER_HPP - -#include - -#include "node.hpp" -#include "number.hpp" - -namespace tao::config::schema::internal -{ - struct is_integer : node - { - using node::node; - - json::value validate( const value& v ) const override - { - if( auto e = number( m_source ).validate( v ) ) { - return e; - } - if( v.is_double() ) { - const double d = v.as< double >(); - double dummy; - if( std::modf( d, &dummy ) != 0 ) { - return error( v, "expected integer", { { "value", d } } ); - } - } - return ok(); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/is_regex.hpp b/include/tao/config/schema/internal/is_regex.hpp deleted file mode 100644 index 717a5b8..0000000 --- a/include/tao/config/schema/internal/is_regex.hpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_IS_REGEX_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_IS_REGEX_HPP - -#include - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - struct is_regex : node - { - using node::node; - - json::value validate( const value& v ) const override - { - if( auto e = string( m_source ).validate( v ) ) { - return e; - } - const std::string_view sv = v.as< std::string_view >(); - try { - std::regex( sv.begin(), sv.end() ); - return ok(); - } - catch( const std::regex_error& ) { - return error( v, "invalid regex" ); - } - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/is_unsigned.hpp b/include/tao/config/schema/internal/is_unsigned.hpp deleted file mode 100644 index 505e6e5..0000000 --- a/include/tao/config/schema/internal/is_unsigned.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_IS_UNSIGNED_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_IS_UNSIGNED_HPP - -#include "is_integer.hpp" - -namespace tao::config::schema::internal -{ - struct is_unsigned : is_integer - { - using is_integer::is_integer; - - json::value validate( const value& v ) const override - { - if( auto e = is_integer::validate( v ) ) { - return e; - } - return ( v >= 0 ) ? ok() : error( v, "expected positive integer" ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/istring.hpp b/include/tao/config/schema/internal/istring.hpp deleted file mode 100644 index d17bbf9..0000000 --- a/include/tao/config/schema/internal/istring.hpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_ISTRING_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_ISTRING_HPP - -#include "iless.hpp" -#include "node.hpp" - -#include -#include - -namespace tao::config::schema::internal -{ - struct istring : node - { - std::set< std::string_view, iless > m_values; - - explicit istring( const value& v ) - : node( v ) - { - if( m_source.is_array() ) { - for( const auto& e : m_source.get_array() ) { - m_values.emplace( e.as< std::string_view >() ); - } - } - else { - m_values.emplace( m_source.as< std::string_view >() ); - } - } - - json::value validate( const value& v ) const override - { - const auto s = v.as< std::string_view >(); - if( m_values.count( s ) == 1 ) { - return ok(); - } - - json::value candidates = json::empty_array; - for( const auto& sv : m_values ) { - candidates.emplace_back( sv ); - } - return error( v, "value did not match", { { "value", s }, { "candidates", std::move( candidates ) } } ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/items.hpp b/include/tao/config/schema/internal/items.hpp deleted file mode 100644 index c121b44..0000000 --- a/include/tao/config/schema/internal/items.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_ITEMS_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_ITEMS_HPP - -#include "array.hpp" -#include "ref.hpp" - -namespace tao::config::schema::internal -{ - struct items : ref - { - using ref::ref; - - json::value validate( const value& v ) const override - { - if( auto e = array( m_source ).validate( v ) ) { - return e; - } - for( const auto& e : v.get_array() ) { - if( const auto t = ref::validate( e ) ) { - return t; - } - } - return ok(); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/list.hpp b/include/tao/config/schema/internal/list.hpp deleted file mode 100644 index fe47e7d..0000000 --- a/include/tao/config/schema/internal/list.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_LIST_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_LIST_HPP - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - template< typename T, typename E > - struct list : T - { - template< typename... Ts > - explicit list( const value& v, Ts&&... ts ) - : T( v ) - { - for( const auto& e : v.get_array() ) { - this->m_properties.emplace_back( std::make_shared< E >( e, ts... ) ); - } - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/multiple_of.hpp b/include/tao/config/schema/internal/multiple_of.hpp deleted file mode 100644 index 91aab55..0000000 --- a/include/tao/config/schema/internal/multiple_of.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_MULTIPLE_OF_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_MULTIPLE_OF_HPP - -#include -#include - -#include "node.hpp" -#include "number.hpp" - -namespace tao::config::schema::internal -{ - struct multiple_of : node - { - explicit multiple_of( const value& v ) - : node( v ) - { - assert( m_source.is_number() ); - assert( m_source > 0 ); - } - - json::value validate( const value& v ) const override - { - if( auto e = number( m_source ).validate( v ) ) { - return e; - } - // TODO: enhance for large integer values (uint64_t, ...) - const auto x = v.as< double >(); - const auto d = m_source.as< double >(); - const auto r = std::fmod( x, d ); - if( std::fabs( r ) < std::numeric_limits< double >::epsilon() ) { - return ok(); - } - if( std::fabs( r - d ) < std::numeric_limits< double >::epsilon() ) { - return ok(); - } - return error( v, "value is not a multiple of given constant", { { "value", x }, { "multiplier", d } } ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/node.hpp b/include/tao/config/schema/internal/node.hpp deleted file mode 100644 index ac73a22..0000000 --- a/include/tao/config/schema/internal/node.hpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_NODE_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_NODE_HPP - -#include -#include -#include - -#include "../../value.hpp" - -#include "node_utility.hpp" - -namespace tao::config::schema::internal -{ - struct node; - - using node_map = std::map< std::string, std::shared_ptr< node > >; - - struct node - { - value m_source; - - explicit node( const value& source ) - : m_source( source ) - {} - - virtual ~node() = default; - - virtual void resolve( const node_map& /*unused*/ ) {} - virtual json::value validate( const value& v ) const = 0; - - virtual json::value pos() const - { - return internal::pos( m_source ); - } - - json::value error( const value& v, const char* message, json::value data = json::empty_object ) const - { - data.try_emplace( "_message", message ); - data.try_emplace( "_value", internal::pos( v ) ); - return data; - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/node_utility.hpp b/include/tao/config/schema/internal/node_utility.hpp deleted file mode 100644 index a8aae11..0000000 --- a/include/tao/config/schema/internal/node_utility.hpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_NODE_UTILITY_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_NODE_UTILITY_HPP - -#include -#include -#include - -#include "../../value.hpp" - -namespace tao::config::schema::internal -{ - inline const value& find( const value& v, const std::string_view k ) - { - static const value nope; - const auto& o = v.get_object(); - const auto it = o.find( k ); - return ( it != o.end() ) ? it->second : nope; - } - - inline std::string pos( const json::position& p ) - { - std::ostringstream oss; - p.append_message_extension( oss ); - return std::move( oss ).str(); - } - - inline json::value pos( const value& v ) - { - return { { "key", config::to_string( v.key ) }, { "pos", pos( v.position ) } }; - } - - inline json::value append_via( json::value& result, json::value&& via ) - { - auto& a = result[ "via" ]; - if( !a ) { - a.emplace_back( std::move( via ) ); - } - else { - auto& l = a.get_array().back(); - auto sv = via.at( "key" ).as< std::string_view >(); - if( l.at( "key" ).as< std::string_view >().substr( 0, sv.size() ) == sv ) { - l = std::move( via ); - } - else { - a.emplace_back( std::move( via ) ); - } - } - return result; - } - - inline json::value ok() - { - return json::value(); - } - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/not_ref.hpp b/include/tao/config/schema/internal/not_ref.hpp deleted file mode 100644 index 4eca484..0000000 --- a/include/tao/config/schema/internal/not_ref.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_NOT_REF_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_NOT_REF_HPP - -#include "ref.hpp" - -namespace tao::config::schema::internal -{ - struct not_ref : ref - { - using ref::ref; - - json::value validate( const value& v ) const override - { - return ref::validate( v ) ? ok() : error( v, "unexpected match" ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/null.hpp b/include/tao/config/schema/internal/null.hpp deleted file mode 100644 index 7ccba95..0000000 --- a/include/tao/config/schema/internal/null.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_NULL_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_NULL_HPP - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - struct null : node - { - using node::node; - - json::value validate( const value& v ) const override - { - return v.is_null() ? ok() : error( v, "expected null" ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/number.hpp b/include/tao/config/schema/internal/number.hpp deleted file mode 100644 index 93c25ea..0000000 --- a/include/tao/config/schema/internal/number.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_NUMBER_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_NUMBER_HPP - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - struct number : node - { - using node::node; - - json::value validate( const value& v ) const override - { - return v.is_number() ? ok() : error( v, "expected number" ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/object.hpp b/include/tao/config/schema/internal/object.hpp deleted file mode 100644 index 0a81c35..0000000 --- a/include/tao/config/schema/internal/object.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_OBJECT_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_OBJECT_HPP - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - struct object : node - { - using node::node; - - json::value validate( const value& v ) const override - { - return v.is_object() ? ok() : error( v, "expected object" ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/one_of.hpp b/include/tao/config/schema/internal/one_of.hpp deleted file mode 100644 index 5f4b789..0000000 --- a/include/tao/config/schema/internal/one_of.hpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_ONE_OF_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_ONE_OF_HPP - -#include "container.hpp" - -namespace tao::config::schema::internal -{ - struct one_of : container - { - using container::container; - - json::value validate( const value& v ) const override - { - const auto& vs = v.skip_value_ptr(); - - json::value errors = json::empty_array; - std::vector< node* > matched; - for( const auto& p : m_properties ) { - if( auto e = p->validate( vs ) ) { - errors.emplace_back( std::move( e ) ); - } - else { - matched.emplace_back( p.get() ); - } - } - if( matched.size() == 1 ) { - return ok(); - } - if( matched.empty() ) { - // TODO: Pick "best" error - return error( v, "no match", { { "errors", std::move( errors ) } } ); - } - json::value data = json::empty_array; - for( const auto& e : matched ) { - data.emplace_back( e->pos() ); - } - return error( v, "multiple matches found", { { "matched", std::move( data ) } } ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/pattern.hpp b/include/tao/config/schema/internal/pattern.hpp deleted file mode 100644 index 160fc5f..0000000 --- a/include/tao/config/schema/internal/pattern.hpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_PATTERN_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_PATTERN_HPP - -#include - -#include "node.hpp" -#include "string.hpp" - -namespace tao::config::schema::internal -{ - struct pattern : node - { - const std::regex m_regex; - - pattern( const value& v, const std::string_view sv ) - : node( v ), - m_regex( sv.begin(), sv.end() ) - {} - - explicit pattern( const value& v ) - : pattern( v, v.as< std::string_view >() ) - {} - - json::value validate( const value& v ) const override - { - if( auto e = string( m_source ).validate( v ) ) { - return e; - } - const std::string_view sv = v.as< std::string_view >(); - return std::regex_search( sv.begin(), sv.end(), m_regex ) ? ok() : error( v, "pattern did not match" ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/properties.hpp b/include/tao/config/schema/internal/properties.hpp deleted file mode 100644 index 7e61fc2..0000000 --- a/include/tao/config/schema/internal/properties.hpp +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_PROPERTIES_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_PROPERTIES_HPP - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - struct properties : node - { - std::map< std::string, std::shared_ptr< ref > > m_required; - std::map< std::string, std::shared_ptr< ref > > m_optional; - std::shared_ptr< ref > m_default; - - using node::node; - - void add_required( const value& v, node_map& m, const std::string& path ) - { - for( const auto& e : v.get_object() ) { - m_required.try_emplace( e.first, std::make_shared< ref >( e.second, m, path ) ); - } - } - - void add_optional( const value& v, node_map& m, const std::string& path ) - { - for( const auto& e : v.get_object() ) { - m_optional.try_emplace( e.first, std::make_shared< ref >( e.second, m, path ) ); - } - } - - void resolve( const node_map& m ) override - { - for( const auto& e : m_required ) { - e.second->resolve( m ); - } - for( const auto& e : m_optional ) { - e.second->resolve( m ); - } - if( m_default ) { - m_default->resolve( m ); - } - } - - json::value validate( const value& v ) const override - { - if( auto e = object( m_source ).validate( v ) ) { - return e; - } - - json::value errors = json::empty_array; - const auto& o = v.get_object(); - - for( const auto& e : m_required ) { - const auto it = o.find( e.first ); - if( it != o.end() ) { - if( auto r = e.second->validate( it->second ) ) { - errors.emplace_back( std::move( r ) ); - } - } - else { - errors.emplace_back( error( v, "missing property", { { "key", e.first } } ) ); - } - } - - for( const auto& e : o ) { - const auto it = m_optional.find( e.first ); - if( it != m_optional.end() ) { - if( auto r = it->second->validate( e.second ) ) { - errors.emplace_back( std::move( r ) ); - } - } - else if( m_required.find( e.first ) == m_required.end() ) { - if( m_default ) { - if( auto r = m_default->validate( e.second ) ) { - errors.emplace_back( std::move( r ) ); - } - } - else { - // TODO: The position will point to the value of the key/value-pair, - // but it should point to the key itself. - errors.emplace_back( error( e.second, "unexpected property" ) ); - } - } - } - - auto& a = errors.get_array(); - if( a.empty() ) { - return ok(); - } - if( a.size() == 1 ) { - return std::move( a.back() ); - } - return error( v, "multiple errors", { { "errors", std::move( errors ) } } ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/property.hpp b/include/tao/config/schema/internal/property.hpp deleted file mode 100644 index 8377286..0000000 --- a/include/tao/config/schema/internal/property.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_PROPERTY_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_PROPERTY_HPP - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - struct property : node - { - std::string m_key; - std::shared_ptr< ref > m_schema; - - property( const value& v, node_map& m, const std::string& path ) - : node( v ) - { - const auto& e = *v.get_object().begin(); - m_key = e.first; - m_schema = std::make_shared< ref >( e.second, m, path ); - } - - void resolve( const node_map& m ) override - { - m_schema->resolve( m ); - } - - json::value validate( const value& v ) const override - { - if( auto e = object( m_source ).validate( v ) ) { - return e; - } - const auto& o = v.get_object(); - const auto it = o.find( m_key ); - if( it == o.end() ) { - return error( v, "missing property", { { "key", m_key } } ); - } - return m_schema->validate( it->second ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/property_names.hpp b/include/tao/config/schema/internal/property_names.hpp deleted file mode 100644 index 64a0c21..0000000 --- a/include/tao/config/schema/internal/property_names.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_PROPERTY_NAMES_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_PROPERTY_NAMES_HPP - -#include "ref.hpp" - -namespace tao::config::schema::internal -{ - struct property_names : ref - { - using ref::ref; - - json::value validate( const value& v ) const override - { - if( auto e = object( m_source ).validate( v ) ) { - return e; - } - for( const auto& e : v.get_object() ) { - if( ref::validate( value( e.first ) ) ) { - return error( v.at( e.first ), "invalid key" ); - } - } - return ok(); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/ref.hpp b/include/tao/config/schema/internal/ref.hpp deleted file mode 100644 index 3426d81..0000000 --- a/include/tao/config/schema/internal/ref.hpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_REF_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_REF_HPP - -#include -#include -#include - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - struct ref : node - { - std::shared_ptr< node > m_node; - const node* m_ptr = nullptr; - - ref( const value& v, node_map& m, const std::string& path ); - - void resolve( const node_map& m ) override - { - if( m_node ) { - m_node->resolve( m ); - m_ptr = m_node.get(); - } - else { - const auto k = m_source.as< std::string >(); - const auto it = m.find( k ); - if( it == m.end() ) { - std::ostringstream oss; - oss << "can not resolve '" << k << "' defined here:"; - m_source.append_message_extension( oss ); - throw std::runtime_error( std::move( oss ).str() ); - } - m_ptr = it->second.get(); - } - } - - json::value validate( const value& v ) const override - { - return m_ptr->validate( v ); - } - - json::value pos() const override - { - auto result = m_ptr->pos(); - auto via = node::pos(); - if( result != via ) { - append_via( result, std::move( via ) ); - } - return result; - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/ref_impl.hpp b/include/tao/config/schema/internal/ref_impl.hpp deleted file mode 100644 index 8b61f77..0000000 --- a/include/tao/config/schema/internal/ref_impl.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_REF_IMPL_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_REF_IMPL_HPP - -#include "any_of.hpp" -#include "list.hpp" -#include "ref.hpp" -#include "schema.hpp" -#include "trivial.hpp" - -namespace tao::config::schema::internal -{ - inline ref::ref( const value& v, node_map& m, const std::string& path ) - : node( v ) - { - if( v.is_boolean() ) { - if( v.as< bool >() ) { - m_node = std::make_shared< trivial< true > >( v ); - } - else { - m_node = std::make_shared< trivial< false > >( v ); - } - } - else if( v.is_array() ) { - m_node = std::make_shared< list< any_of, ref > >( v, m, path ); - } - else if( v.is_object() ) { - m_node = std::make_shared< schema >( v, m, path ); - } - } - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/schema.hpp b/include/tao/config/schema/internal/schema.hpp deleted file mode 100644 index 5b9693a..0000000 --- a/include/tao/config/schema/internal/schema.hpp +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_SCHEMA_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_SCHEMA_HPP - -#include "all_of.hpp" -#include "any_of.hpp" -#include "compare.hpp" -#include "constant.hpp" -#include "has_property.hpp" -#include "if_then_else.hpp" -#include "istring.hpp" -#include "items.hpp" -#include "list.hpp" -#include "multiple_of.hpp" -#include "not_ref.hpp" -#include "one_of.hpp" -#include "pattern.hpp" -#include "properties.hpp" -#include "property.hpp" -#include "property_names.hpp" -#include "ref.hpp" -#include "size.hpp" -#include "switch_case.hpp" -#include "unique_items.hpp" - -namespace tao::config::schema::internal -{ - struct schema : all_of - { - template< typename P, typename T, typename... Ts > - void add( T&& t, Ts&&... ts ) - { - if( t ) { - m_properties.emplace_back( std::make_shared< P >( std::forward< T >( t ), std::forward< Ts >( ts )... ) ); - } - } - - schema( const value& v, node_map& m, const std::string& path = "" ) - : all_of( v ) - { - // definitions - if( const auto& d = internal::find( v, "definitions" ) ) { - for( const auto& e : d.get_object() ) { - assert( is_identifier( e.first ) ); - auto p = path.empty() ? e.first : ( path + '.' + e.first ); - auto n = std::make_shared< ref >( e.second, m, p ); - if( !m.emplace( p, std::move( n ) ).second ) { - std::ostringstream oss; - oss << "type '" << p << "' already defined, redefined here:"; - internal::find( d, e.first ).append_message_extension( oss ); - throw std::runtime_error( std::move( oss ).str() ); - } - } - } - - // structural - add< ref >( internal::find( v, "type" ), m, path ); - add< not_ref >( internal::find( v, "not" ), m, path ); - - add< list< all_of, ref > >( internal::find( v, "all_of" ), m, path ); - add< list< any_of, ref > >( internal::find( v, "any_of" ), m, path ); - add< list< one_of, ref > >( internal::find( v, "one_of" ), m, path ); - - if( const auto& n = internal::find( v, "if" ) ) { - auto p = std::make_shared< if_then_else >( n, m, path ); - if( const auto& t = internal::find( v, "then" ) ) { - p->m_then = std::make_shared< ref >( t, m, path ); - } - if( const auto& e = internal::find( v, "else" ) ) { - p->m_else = std::make_shared< ref >( e, m, path ); - } - m_properties.emplace_back( std::move( p ) ); - } - - // any - add< constant >( internal::find( v, "value" ) ); - add< list< any_of, constant > >( internal::find( v, "enum" ) ); - - // string/binary/array/object - add< size >( internal::find( v, "size" ) ); - add< min_size >( internal::find( v, "min_size" ) ); - add< max_size >( internal::find( v, "max_size" ) ); - - // string - add< istring >( internal::find( v, "istring" ) ); - add< pattern >( internal::find( v, "pattern" ) ); - add< switch_case >( internal::find( v, "switch" ), m, path ); - - // number - add< minimum >( internal::find( v, "minimum" ) ); - add< maximum >( internal::find( v, "maximum" ) ); - add< exclusive_minimum >( internal::find( v, "exclusive_minimum" ) ); - add< exclusive_maximum >( internal::find( v, "exclusive_maximum" ) ); - add< multiple_of >( internal::find( v, "multiple_of" ) ); - - // array - add< items >( internal::find( v, "items" ), m, path ); - if( const auto& n = internal::find( v, "unique_items" ) ) { - if( n.as< bool >() ) { - m_properties.emplace_back( std::make_shared< unique_items >( v ) ); - } - } - - // object - add< property_names >( internal::find( v, "property_names" ), m, path ); - add< has_property >( internal::find( v, "has_property" ) ); - add< property >( internal::find( v, "property" ), m, path ); - if( const auto& p = internal::find( v, "properties" ) ) { - auto n = std::make_shared< properties >( v ); - if( const auto& r = internal::find( p, "required" ) ) { - n->add_required( r, m, path ); - } - if( const auto& o = internal::find( p, "optional" ) ) { - n->add_optional( o, m, path ); - } - if( const auto& a = internal::find( p, "additional" ) ) { - n->m_default = std::make_shared< ref >( a, m, path ); - } - m_properties.emplace_back( std::move( n ) ); - } - } - }; - -} // namespace tao::config::schema::internal - -#include "ref_impl.hpp" - -#endif diff --git a/include/tao/config/schema/internal/size.hpp b/include/tao/config/schema/internal/size.hpp deleted file mode 100644 index efbcd54..0000000 --- a/include/tao/config/schema/internal/size.hpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_SIZE_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_SIZE_HPP - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - template< typename T > - struct compare_size : node - { - const std::size_t m_size; - - explicit compare_size( const value& v ) - : node( v ), - m_size( v.as< std::size_t >() ) - {} - - json::value validate( const value& v ) const override - { - if( v.is_string_type() ) { - return T()( m_size, v.as< std::string_view >().size() ) ? ok() : error( v, "size did not match" ); - } - if( v.is_binary_type() ) { - return T()( m_size, v.as< tao::binary_view >().size() ) ? ok() : error( v, "size did not match" ); - } - if( v.is_array() ) { - return T()( m_size, v.get_array().size() ) ? ok() : error( v, "size did not match" ); - } - if( v.is_object() ) { - return T()( m_size, v.get_object().size() ) ? ok() : error( v, "size did not match" ); - } - return error( v, "invalid type for size comparison" ); - } - }; - - using size = compare_size< std::equal_to<> >; - using min_size = compare_size< std::less_equal<> >; - using max_size = compare_size< std::greater_equal<> >; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/string.hpp b/include/tao/config/schema/internal/string.hpp deleted file mode 100644 index 668ba07..0000000 --- a/include/tao/config/schema/internal/string.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_STRING_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_STRING_HPP - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - struct string : node - { - using node::node; - - json::value validate( const value& v ) const override - { - return v.is_string_type() ? ok() : error( v, "expected string" ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/switch_case.hpp b/include/tao/config/schema/internal/switch_case.hpp deleted file mode 100644 index 0050d75..0000000 --- a/include/tao/config/schema/internal/switch_case.hpp +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_SWITCH_CASE_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_SWITCH_CASE_HPP - -#include "iless.hpp" -#include "node.hpp" -#include "ref.hpp" - -namespace tao::config::schema::internal -{ - struct switch_type : node - { - std::map< std::string_view, std::shared_ptr< node > > m_cases; - std::shared_ptr< node > m_default; - - switch_type( const value& v, node_map& m, const std::string& path ) - : node( v ) - { - if( const auto& c = internal::find( m_source, "case" ) ) { - for( const auto& p : c.get_object() ) { - m_cases.emplace( p.first, std::make_shared< ref >( p.second, m, path ) ); - } - } - if( const auto& d = internal::find( m_source, "default" ) ) { - if( d != false ) { - m_default = std::make_shared< ref >( d, m, path ); - } - } - } - - void resolve( const node_map& m ) override - { - for( const auto& p : m_cases ) { - p.second->resolve( m ); - } - if( m_default ) { - m_default->resolve( m ); - } - } - - json::value validate( const value& v ) const override - { - std::string_view key; - switch( v.skip_value_ptr().type() ) { - case json::type::NULL_: - key = "null"; - break; - case json::type::BOOLEAN: - key = "boolean"; - break; - case json::type::SIGNED: - case json::type::UNSIGNED: - case json::type::DOUBLE: - key = "number"; - break; - case json::type::STRING: - case json::type::STRING_VIEW: - key = "string"; - break; - case json::type::BINARY: - case json::type::BINARY_VIEW: - key = "binary"; - break; - case json::type::ARRAY: - key = "array"; - break; - case json::type::OBJECT: - key = "object"; - break; - default: - return error( v, "invalid type", { { "type", json::to_string( v.type() ) } } ); - } - const auto it = m_cases.find( key ); - if( it != m_cases.end() ) { - return it->second->validate( v ); - } - if( m_default ) { - return m_default->validate( v ); - } - return error( v, "invalid type", { { "type", json::to_string( v.type() ) } } ); - } - }; - - template< typename Compare > - struct switch_string : node - { - std::string_view m_key; - std::map< std::string_view, std::shared_ptr< node >, Compare > m_cases; - std::shared_ptr< node > m_default; - - switch_string( const value& v, node_map& m, const std::string& path ) - : node( v ) - { - const auto s = m_source.get_object().begin(); - m_key = s->first; - if( const auto& c = internal::find( s->second, "case" ) ) { - for( const auto& p : c.get_object() ) { - m_cases.emplace( p.first, std::make_shared< ref >( p.second, m, path ) ); - } - } - if( const auto& d = internal::find( s->second, "default" ) ) { - if( d != false ) { - m_default = std::make_shared< ref >( d, m, path ); - } - } - } - - void resolve( const node_map& m ) override - { - for( const auto& p : m_cases ) { - p.second->resolve( m ); - } - if( m_default ) { - m_default->resolve( m ); - } - } - - json::value validate( const value& v ) const override - { - if( auto e = object( m_source ).validate( v ) ) { - return e; - } - const auto& o = v.get_object(); - const auto it = o.find( m_key ); - if( it != o.end() ) { - if( auto e = string( m_source ).validate( it->second ) ) { - return e; - } - const auto k = it->second.template as< std::string_view >(); - const auto jt = m_cases.find( k ); - if( jt != m_cases.end() ) { - return jt->second->validate( v ); - } - if( m_default ) { - return m_default->validate( v ); - } - std::vector< std::string_view > keys; - for( const auto& e : m_cases ) { - keys.push_back( e.first ); - } - return error( it->second, "invalid property value", { { "value", k }, { "valid", keys } } ); - } - return error( v, "missing property", { { "key", m_key } } ); - } - }; - - struct switch_case : node - { - std::shared_ptr< node > m_impl; - - switch_case( const value& v, node_map& m, const std::string& path ) - : node( v ) - { - const auto s = m_source.get_object().begin(); - if( s->first == "type" ) { - m_impl = std::make_shared< switch_type >( s->second, m, path ); - } - else if( s->first == "string" ) { - m_impl = std::make_shared< switch_string< std::less< std::string_view > > >( s->second, m, path ); - } - else if( s->first == "istring" ) { - m_impl = std::make_shared< switch_string< iless > >( s->second, m, path ); - } - else { - // TODO: Throw proper exception - throw 42; - } - } - - void resolve( const node_map& m ) override - { - m_impl->resolve( m ); - } - - json::value validate( const value& v ) const override - { - return m_impl->validate( v ); - } - - json::value pos() const override - { - return m_impl->pos(); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/trivial.hpp b/include/tao/config/schema/internal/trivial.hpp deleted file mode 100644 index 6c871b1..0000000 --- a/include/tao/config/schema/internal/trivial.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_TRIVIAL_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_TRIVIAL_HPP - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - template< bool B > - struct trivial : node - { - using node::node; - - json::value validate( const value& v ) const override - { - return B ? ok() : error( v, "always fails" ); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/unique_items.hpp b/include/tao/config/schema/internal/unique_items.hpp deleted file mode 100644 index 4e7ec31..0000000 --- a/include/tao/config/schema/internal/unique_items.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_UNIQUE_ITEMS_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_UNIQUE_ITEMS_HPP - -#include "node.hpp" - -namespace tao::config::schema::internal -{ - struct unique_items : node - { - using node::node; - - json::value validate( const value& v ) const override - { - if( auto e = array( m_source ).validate( v ) ) { - return e; - } - std::set< value > s; - for( const auto& e : v.get_array() ) { - if( !s.emplace( &e ).second ) { - return error( v, "duplicate items detected" ); // TODO: Add indices/positions/...? - } - } - return ok(); - } - }; - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/internal/validator.hpp b/include/tao/config/schema/internal/validator.hpp deleted file mode 100644 index d54fa06..0000000 --- a/include/tao/config/schema/internal/validator.hpp +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_INTERNAL_VALIDATOR_HPP -#define TAO_CONFIG_SCHEMA_INTERNAL_VALIDATOR_HPP - -#include "../../from_input.hpp" -#include "../validator.hpp" - -namespace tao::config::schema::internal -{ - // clang-format off - const tao::config::schema::validator validator( config::from_input( pegtl_input_t( R"( - - // the schema for schemas: - - definitions - { - ref.switch.type - { - case.boolean: true - case.string: "std.key" - case.array: "ref_list" - case.object: "schema" - } - - ref_list: { min_size: 1, items: "ref" } - - type_cases - { - properties.optional - { - case.property_names.enum: [ "null", "boolean", "string", "binary", "number", "array", "object" ] - case.properties.additional: "ref" - default: "ref" - } - } - - string_cases - { - size: 1 - properties.additional - { - properties.optional - { - case.properties.additional: "ref" - default: "ref" - } - } - } - - - schema - { - properties.optional - { - definitions - { - property_names: "std.identifier" - properties.additional: "ref" - } - - // structural - type: "ref" - not: "ref" - - all_of: "ref_list" // short-circuit - any_of: "ref_list" // short-circuit - one_of: "ref_list" // exactly one - - if: "ref" - then: "ref" - else: "ref" - - // any - value: true - enum.items: true - - // string/binary/array/object - size: "std.unsigned" - min_size: "std.unsigned" - max_size: "std.unsigned" - - // string - istring.switch.type - { - case.string: true - case.array.items: "string" - } - pattern: "std.regex" - - // number - minimum: "number" - maximum: "number" - exclusive_minimum: "number" - exclusive_maximum: "number" - multiple_of.exclusive_minimum: 0 - - // array - items: "ref" - unique_items: "boolean" - - // object - property_names: "ref" - has_property - { - case.string: true - case.array.items: "string" - } - property - { - size: 1 - properties.additional: "ref" - } - properties.properties.optional - { - required.properties.additional: "ref" - optional.properties.additional: "ref" - additional: "ref" - } - - // special - switch { - size: 1 - one_of - [ - { if.has_property: "type" then.property.type: "type_cases" else: false } - { if.has_property: "string" then.property.string: "string_cases" else: false } - { if.has_property: "istring" then.property.istring: "string_cases" else: false } - ] - } - } - - definitions - { - has_size: [ - { property.type.enum: [ "string", "binary", "array", "object" ] } - { has_property: [ "size", "min_size", "max_size" ] } - ] - is_string: [ - { property.type.value: "string" } - { has_property: [ "istring", "pattern" ] } - ] - is_number: [ - { property.type.value: "number" } - { has_property: [ "minimum", "maximum", "exclusive_minimum", "exclusive_maximum", "multiple_of" ] } - ] - is_array: [ - { property.type.value: "array" } - { has_property: [ "items", "unique_items" ] } - ] - is_object: [ - { property.type.value: "object" } - { has_property: [ "property_names", "has_property", "property", "properties" ] } - ] - } - - all_of - [ - { if.has_property: "value" then.properties.optional: { value: true, definitions: true } } - { if.has_property: "if" then.has_property: "then" else.not.has_property: "then" } - { if.has_property: "else" then.has_property: "then" } - - { - if.property.type.enum: [ "null", "boolean", "binary" ] - then.not: [ "schema.is_string", "schema.is_number", "schema.is_array", "schema.is_object" ] - } - - { if: "schema.has_size" then.not: [ { property.type.enum: [ "null", "boolean" ] }, "schema.is_number" ] } - - { if: "schema.is_string" then.not: [ "schema.is_number", "schema.is_array", "schema.is_object" ] } - { if: "schema.is_number" then.not: [ "schema.is_array", "schema.is_object" ] } - { if: "schema.is_array" then.not: "schema.is_object" } - - { if.has_property: "size" then.not.has_property: [ "min_size", "max_size" ] } - { if.has_property: "minimum" then.not.has_property: "exclusive_minimum" } - { if.has_property: "maximum" then.not.has_property: "exclusive_maximum" } - ] - } - } - - type: "schema" - - )", "schema" ) ) ); - // clang-format on - -} // namespace tao::config::schema::internal - -#endif diff --git a/include/tao/config/schema/validator.hpp b/include/tao/config/schema/validator.hpp deleted file mode 100644 index 8dfa040..0000000 --- a/include/tao/config/schema/validator.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#ifndef TAO_CONFIG_SCHEMA_VALIDATOR_HPP -#define TAO_CONFIG_SCHEMA_VALIDATOR_HPP - -#include "internal/node.hpp" -#include "internal/schema.hpp" - -#include "builtin.hpp" - -namespace tao::config::schema -{ - struct validator - { - internal::node_map m_nodes; - - explicit validator( const value& v, const builtin& b = builtin() ) - : m_nodes( b.m_nodes ) - { - m_nodes.emplace( "", std::make_shared< internal::schema >( v, m_nodes ) ); - - for( const auto& e : m_nodes ) { - e.second->resolve( m_nodes ); - } - } - - json::value validate( const value& v ) const - { - return m_nodes.at( "" )->validate( v ); - } - }; - -} // namespace tao::config::schema - -#endif diff --git a/src/example/config/CMakeLists.txt b/src/example/config/CMakeLists.txt index dec218f..54615b5 100644 --- a/src/example/config/CMakeLists.txt +++ b/src/example/config/CMakeLists.txt @@ -3,7 +3,6 @@ cmake_minimum_required(VERSION 3.8...3.19) set(examplesources dump_all_phases.cpp dump_only_data.cpp - dump_phase_four.cpp dump_phase_one.cpp dump_phase_three.cpp dump_phase_two.cpp diff --git a/src/example/config/dump_all_phases.cpp b/src/example/config/dump_all_phases.cpp index 0cf67a5..ee1b36a 100644 --- a/src/example/config/dump_all_phases.cpp +++ b/src/example/config/dump_all_phases.cpp @@ -22,16 +22,13 @@ int main( int argc, char** argv ) std::cout << std::endl; } std::cout << "PHASE 2" << std::endl; - cfg.phase2_loop(); + tao::config::internal::phase2_everything( cfg.st, cfg.fm ); tao::config::internal::to_stream( std::cout, cfg.st.root, 3 ); std::cout << std::endl; std::cout << "PHASE 3" << std::endl; tao::config::internal::phase3_remove( cfg.st.root ); tao::config::internal::to_stream( std::cout, cfg.st.root, 3 ); std::cout << std::endl; - std::cout << "PHASE 4" << std::endl; - tao::config::internal::phase4_schema( cfg.st.root, cfg.st.schema ); - std::cout << " Schema validation does not change anything." << std::endl; std::cout << "RESULT" << std::endl; const tao::config::value j = tao::config::internal::phase5_repack< tao::config::traits >( cfg.st.root ); tao::config::to_stream( std::cout, j, 3 ); diff --git a/src/example/config/dump_only_data.cpp b/src/example/config/dump_only_data.cpp index 86afb99..a5dca2d 100644 --- a/src/example/config/dump_only_data.cpp +++ b/src/example/config/dump_only_data.cpp @@ -7,10 +7,10 @@ #include -#include - #include +#include "try_catch.hpp" + int main( int argc, char** argv ) { tao::config::internal::try_catch( [ = ]() { diff --git a/src/example/config/dump_phase_four.cpp b/src/example/config/dump_phase_four.cpp deleted file mode 100644 index 79ae1c7..0000000 --- a/src/example/config/dump_phase_four.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2020-2024 Dr. Colin Hirsch and Daniel Frey -// Please see LICENSE for license or visit https://github.com/taocpp/config/ - -#include -#include -#include - -#include - -#include - -int main( int argc, char** argv ) -{ - tao::config::internal::config_parser cfg; - - try { - for( int i = 1; i < argc; ++i ) { - const std::filesystem::path file( argv[ i ] ); - std::cout << "PARSE " << file << std::endl; - cfg.parse( file ); - } - std::cout << "PHASE 2" << std::endl; - cfg.phase2_loop(); - std::cout << "PHASE 3" << std::endl; - tao::config::internal::phase3_remove( cfg.st.root ); - std::cout << "PHASE 4" << std::endl; - tao::config::internal::phase4_schema( cfg.st.root, cfg.st.schema ); - tao::config::internal::to_stream( std::cout, cfg.st.root, 3 ); - std::cout << std::endl; - } - catch( const std::exception& e ) { - std::cerr << "ERROR " << e.what() << std::endl; - return 1; - } - catch( const std::string& e ) { - std::cerr << "STRING " << e << std::endl; - return 1; - } - return 0; -} diff --git a/src/example/config/dump_phase_three.cpp b/src/example/config/dump_phase_three.cpp index cf16ad8..e1ffc0c 100644 --- a/src/example/config/dump_phase_three.cpp +++ b/src/example/config/dump_phase_three.cpp @@ -20,7 +20,7 @@ int main( int argc, char** argv ) cfg.parse( file ); } std::cout << "PHASE 2" << std::endl; - cfg.phase2_loop(); + tao::config::internal::phase2_everything( cfg.st, cfg.fm ); std::cout << "PHASE 3" << std::endl; tao::config::internal::phase3_remove( cfg.st.root ); tao::config::internal::to_stream( std::cout, cfg.st.root, 3 ); diff --git a/src/example/config/dump_phase_two.cpp b/src/example/config/dump_phase_two.cpp index 2d5e5fb..5663e19 100644 --- a/src/example/config/dump_phase_two.cpp +++ b/src/example/config/dump_phase_two.cpp @@ -20,7 +20,7 @@ int main( int argc, char** argv ) cfg.parse( file ); } std::cout << "PHASE 2" << std::endl; - cfg.phase2_loop(); + tao::config::internal::phase2_everything( cfg.st, cfg.fm ); tao::config::internal::to_stream( std::cout, cfg.st.root, 3 ); std::cout << std::endl; } diff --git a/src/example/config/dump_with_meta.cpp b/src/example/config/dump_with_meta.cpp index 26d3341..43b2366 100644 --- a/src/example/config/dump_with_meta.cpp +++ b/src/example/config/dump_with_meta.cpp @@ -7,7 +7,7 @@ #include -#include +#include "try_catch.hpp" int main( int argc, char** argv ) { diff --git a/include/tao/config/internal/try_catch.hpp b/src/example/config/try_catch.hpp similarity index 87% rename from include/tao/config/internal/try_catch.hpp rename to src/example/config/try_catch.hpp index 04f4528..7785053 100644 --- a/include/tao/config/internal/try_catch.hpp +++ b/src/example/config/try_catch.hpp @@ -1,16 +1,16 @@ // Copyright (c) 2019-2024 Dr. Colin Hirsch and Daniel Frey // Please see LICENSE for license or visit https://github.com/taocpp/config/ -#ifndef TAO_CONFIG_INTERNAL_TRY_CATCH_HPP -#define TAO_CONFIG_INTERNAL_TRY_CATCH_HPP +#ifndef TAO_EXAMPLE_CONFIG_TRY_CATCH_HPP +#define TAO_EXAMPLE_CONFIG_TRY_CATCH_HPP #include #include +#include #include -#include "pegtl.hpp" -#include "reverse.hpp" +#include namespace tao::config::internal { diff --git a/src/test/config/enumerations.cpp b/src/test/config/enumerations.cpp index 45bdc73..c5361aa 100644 --- a/src/test/config/enumerations.cpp +++ b/src/test/config/enumerations.cpp @@ -31,7 +31,6 @@ namespace tao::config static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::key1_kind::name ), internal::key1_part::data_t >, std::string > ); static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::key1_kind::index ), internal::key1_part::data_t >, std::size_t > ); static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::key1_kind::star ), internal::key1_part::data_t >, internal::part_star_t > ); - static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::key1_kind::append ), internal::key1_part::data_t >, std::shared_ptr< std::uint64_t > > ); static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::reference2_kind::name ), internal::reference2_part::data_t >, std::string > ); static_assert( std::is_same_v< std::variant_alternative_t< std::size_t( internal::reference2_kind::index ), internal::reference2_part::data_t >, std::size_t > ); diff --git a/src/test/config/failure.cpp b/src/test/config/failure.cpp index 253d8d2..9d69cb3 100644 --- a/src/test/config/failure.cpp +++ b/src/test/config/failure.cpp @@ -7,8 +7,6 @@ #include -#include - const char* ansi_reset = "\033[0m"; const char* ansi_message = "\033[1;31m"; const char* ansi_source = "\033[36m"; @@ -31,10 +29,8 @@ namespace tao std::cerr << ccs << std::endl; std::cerr << ">>> Config parsed as config >>>" << std::endl; } - catch( const pegtl::parse_error& ) { - for( const auto& e : pegtl::nested::flatten() ) { - std::cout << ansi_text << "pegtl::parse_error: " << ansi_message << e.message() << ": " << ansi_source << e.position_string() << ansi_reset << std::endl; - } + catch( const pegtl::parse_error_base& e ) { + std::cout << ansi_text << "pegtl::parse_error: " << ansi_message << e.message() << ": " << ansi_source << e.position_string() << ansi_reset << std::endl; } catch( const std::exception& e ) { std::cout << "std::exception: " << e.what() << std::endl; diff --git a/src/test/config/success.cpp b/src/test/config/success.cpp index 9fc24ec..7e76e84 100644 --- a/src/test/config/success.cpp +++ b/src/test/config/success.cpp @@ -11,6 +11,21 @@ namespace tao { int failed = 0; + void setenv_throws( const std::string& name, const std::string& value ) + { +#if defined( _MSC_VER ) + const auto e = ::_putenv_s( name.c_str(), value.c_str() ); + if( e != 0 ) { +#else + errno = 0; + if( ::setenv( name.c_str(), value.c_str(), 1 ) != 0 ) { + const auto e = errno; +#endif + (void)e; + throw std::string( "setenv failed" ); + } + } + template< template< typename... > class Traits > void unit_test( const std::filesystem::path& path ) { @@ -71,6 +86,7 @@ int main() continue; } #endif + tao::setenv_throws( "TAO_CONFIG", "env_value" ); tao::unit_test< tao::json::traits >( path ); tao::unit_test< tao::config::traits >( path ); ++count; diff --git a/tests/combo_01.jaxn b/tests/combo_01.jaxn new file mode 100644 index 0000000..2cb5afd --- /dev/null +++ b/tests/combo_01.jaxn @@ -0,0 +1,11 @@ +{ + foo: { + a: { + baz: 2 + }, + b: { + bar: 42, + baz: 1 + } + } +} diff --git a/tests/combo_01.success b/tests/combo_01.success new file mode 100644 index 0000000..3cc0b24 --- /dev/null +++ b/tests/combo_01.success @@ -0,0 +1,4 @@ +foo.*.bar = 42 +foo.a.bar = delete +foo.b.baz = 1 +foo.a.baz = 2 diff --git a/tests/combo_02.jaxn b/tests/combo_02.jaxn new file mode 100644 index 0000000..43f5c14 --- /dev/null +++ b/tests/combo_02.jaxn @@ -0,0 +1,12 @@ +{ + foo: { + a: { + bar: 42, + baz: 2 + }, + b: { + bar: 42, + baz: 1 + } + } +} diff --git a/tests/combo_02.success b/tests/combo_02.success new file mode 100644 index 0000000..106cc44 --- /dev/null +++ b/tests/combo_02.success @@ -0,0 +1,4 @@ +foo.a.bar = delete +foo.*.bar = 42 +foo.b.baz = 1 +foo.a.baz = 2 diff --git a/tests/doc_default.jaxn b/tests/doc_default.jaxn new file mode 100644 index 0000000..148568f --- /dev/null +++ b/tests/doc_default.jaxn @@ -0,0 +1,4 @@ +{ + bar: false, + foo: 1 +} diff --git a/tests/doc_default.success b/tests/doc_default.success new file mode 100644 index 0000000..ebc2dfc --- /dev/null +++ b/tests/doc_default.success @@ -0,0 +1,2 @@ +foo = (default 1 2) +bar = (default null false true) diff --git a/tests/doc_delete_01.jaxn b/tests/doc_delete_01.jaxn new file mode 100644 index 0000000..1bb18f6 --- /dev/null +++ b/tests/doc_delete_01.jaxn @@ -0,0 +1,4 @@ +{ + ip: "127.0.0.2", + port: 27960 +} diff --git a/tests/doc_delete_01.success b/tests/doc_delete_01.success new file mode 100644 index 0000000..7223f11 --- /dev/null +++ b/tests/doc_delete_01.success @@ -0,0 +1,6 @@ +#!/usr/local/bin/qs + +ip = "127.0.0.2" +port = 27960 +maps = [ "ztn" "dm13" "t9" ] +maps = delete // Changed our minds, no maps. diff --git a/tests/doc_delete_02.jaxn b/tests/doc_delete_02.jaxn new file mode 100644 index 0000000..53f0652 --- /dev/null +++ b/tests/doc_delete_02.jaxn @@ -0,0 +1,3 @@ +{ + foo: 44 +} diff --git a/tests/doc_delete_02.success b/tests/doc_delete_02.success new file mode 100644 index 0000000..87c7afe --- /dev/null +++ b/tests/doc_delete_02.success @@ -0,0 +1,3 @@ +foo = 42 +foo = delete +foo += 44 diff --git a/tests/doc_member_include.inc b/tests/doc_include.inc similarity index 100% rename from tests/doc_member_include.inc rename to tests/doc_include.inc diff --git a/tests/doc_include.jaxn b/tests/doc_include.jaxn new file mode 100644 index 0000000..f46e399 --- /dev/null +++ b/tests/doc_include.jaxn @@ -0,0 +1,14 @@ +{ + bar: 42, + baz: [ + true, + false + ], + foo: { + bar: 42, + baz: [ + true, + false + ] + } +} diff --git a/tests/doc_member_include.cfg b/tests/doc_include.success similarity index 59% rename from tests/doc_member_include.cfg rename to tests/doc_include.success index 8cdd80d..a292cff 100644 --- a/tests/doc_member_include.cfg +++ b/tests/doc_include.success @@ -1,10 +1,11 @@ // Include the file whose contents are shown below. -(include "tests/doc_member_include.inc") +(include "tests/doc_include.inc") foo { // Include the same file again, this time within an object. - (include "tests/doc_member_include.inc") + (include "tests/doc_include.inc") } +// Use include? with a non-existing file, not an error. (include? "tests/non_existing_file_is_no_error_with_include?") diff --git a/tests/doc_member_parse.cfg b/tests/doc_member_parse.cfg deleted file mode 100644 index 6a1e7d8..0000000 --- a/tests/doc_member_parse.cfg +++ /dev/null @@ -1,8 +0,0 @@ -(parse "a = 42") - -foo -{ - b = true - c = false - (parse "b = delete c = true") -} \ No newline at end of file diff --git a/tests/doc_member_schema.schema b/tests/doc_member_schema.schema deleted file mode 100644 index 33781f7..0000000 --- a/tests/doc_member_schema.schema +++ /dev/null @@ -1,18 +0,0 @@ -definitions -{ - port - { - type: "std.unsigned" - minimum: 1 - maximum: 65535 - } -} - -properties -{ - required - { - ip: "std.net.ip_v4_address" - port: "port" - } -} diff --git a/tests/doc_member_schema_01.jaxn b/tests/doc_member_schema_01.jaxn deleted file mode 100644 index 85b45ba..0000000 --- a/tests/doc_member_schema_01.jaxn +++ /dev/null @@ -1,4 +0,0 @@ -{ - ip: "127.0.0.1", - port: 42 -} diff --git a/tests/doc_member_schema_01.success b/tests/doc_member_schema_01.success deleted file mode 100644 index c450735..0000000 --- a/tests/doc_member_schema_01.success +++ /dev/null @@ -1,4 +0,0 @@ -ip = "127.0.0.1" -port = 42 - -(schema "tests/doc_member_schema.schema") diff --git a/tests/doc_member_schema_02.jaxn b/tests/doc_member_schema_02.jaxn deleted file mode 100644 index a34803d..0000000 --- a/tests/doc_member_schema_02.jaxn +++ /dev/null @@ -1,9 +0,0 @@ -{ - foo: { - bar: { - ip: "127.0.0.1", - port: 42 - } - }, - what: "ever" -} diff --git a/tests/doc_member_schema_02.success b/tests/doc_member_schema_02.success deleted file mode 100644 index ead4aa5..0000000 --- a/tests/doc_member_schema_02.success +++ /dev/null @@ -1,9 +0,0 @@ -foo.bar -{ - ip = "127.0.0.1" - port = 42 - - (schema "tests/doc_member_schema.schema") -} - -what = "ever" // Outside scope of schema. diff --git a/tests/doc_member_temporary_01.jaxn b/tests/doc_temporary.jaxn similarity index 100% rename from tests/doc_member_temporary_01.jaxn rename to tests/doc_temporary.jaxn diff --git a/tests/doc_member_temporary_01.success b/tests/doc_temporary.success similarity index 100% rename from tests/doc_member_temporary_01.success rename to tests/doc_temporary.success diff --git a/tests/extensions.jaxn b/tests/extensions.jaxn index 87a72ff..860bf93 100644 --- a/tests/extensions.jaxn +++ b/tests/extensions.jaxn @@ -9,11 +9,6 @@ a: "env_value", b: "default_value" }, - identity: { - a: 42, - b: null, - c: $DEADBEAF - }, parse: { a: 42, b: null diff --git a/tests/extensions.success b/tests/extensions.success index 78b265f..38de6f8 100644 --- a/tests/extensions.success +++ b/tests/extensions.success @@ -1,26 +1,17 @@ default { - a = (default 1 2) - b = (default null 2) + a = (default 1 2 3 4 5) + b = (default null null 2) c = (default 1 null) - d = (default null null) + d = null // (default null null) } -(setenv "TAO_CONFIG" "env_value") - env { a = (env "TAO_CONFIG") b = (env? "a b c d e f g h i j k l m n o p q r s t u v w x y z" "default_value") } -identity -{ - a = (identity 42) - b = (identity null) - c = (identity $deadbeaf) -} - parse { a = (parse "42") diff --git a/tests/include.success b/tests/include.success index de13755..2afb61e 100644 --- a/tests/include.success +++ b/tests/include.success @@ -4,10 +4,8 @@ a = { (include "tests/simple.success") } -(setenv "CONFIGFILE" "tests/simple.success") - b = { - (include (env "CONFIGFILE")) + ( include "tests/simple.success" ) } b.simple += 1 diff --git a/tests/schema.schema b/tests/schema.schema deleted file mode 100644 index cce29e5..0000000 --- a/tests/schema.schema +++ /dev/null @@ -1,22 +0,0 @@ -//-*- mode: JavaScript -*- - -definitions -{ - foo.properties.required{ - a.istring : "foO" - b : "number" - } - - bar.properties.required{ - a.istring : "baR" - b : "string" - } - - baz.properties.required - { - a.istring : "baZ" b : "std.integer" c : "null" d : "std.net.ip_v4_cidr" - } -} - -all_of - [ { property.a.istring : [ "fOo", "bAr", "bAz" ] } { if.property.a.istring: "Foo" then: "foo" } { if.property.a.istring: "Bar" then: "bar" } { if.property.a.istring: "Baz" then: "baz" } ] diff --git a/tests/schema_01.failure b/tests/schema_01.failure deleted file mode 100644 index 4d9d557..0000000 --- a/tests/schema_01.failure +++ /dev/null @@ -1,6 +0,0 @@ -(schema "tests/schema.schema") - -a = "BAZ" -b = 42 -c = true -d = "255.255.255.255/32" diff --git a/tests/schema_01.jaxn b/tests/schema_01.jaxn deleted file mode 100644 index f317cb4..0000000 --- a/tests/schema_01.jaxn +++ /dev/null @@ -1,6 +0,0 @@ -{ - a: "BAZ", - b: 42, - c: null, - d: "255.255.255.255/32" -} diff --git a/tests/schema_01.success b/tests/schema_01.success deleted file mode 100644 index a72c930..0000000 --- a/tests/schema_01.success +++ /dev/null @@ -1,6 +0,0 @@ -(schema "tests/schema.schema") - -a = "BAZ" -b = 42 -c = null -d = "255.255.255.255/32" diff --git a/tests/schema_02.failure b/tests/schema_02.failure deleted file mode 100644 index d56bc6d..0000000 --- a/tests/schema_02.failure +++ /dev/null @@ -1,9 +0,0 @@ -foo.bar -{ - (schema "tests/schema.schema") - - a = "BAZ" - b = 42 - c = null - d = "not an ip network" -} diff --git a/tests/schema_02.jaxn b/tests/schema_02.jaxn deleted file mode 100644 index 344147c..0000000 --- a/tests/schema_02.jaxn +++ /dev/null @@ -1,10 +0,0 @@ -{ - foo: { - bar: { - a: "BAZ", - b: 42, - c: null, - d: "255.255.255.255/32" - } - } -} diff --git a/tests/schema_02.success b/tests/schema_02.success deleted file mode 100644 index b691784..0000000 --- a/tests/schema_02.success +++ /dev/null @@ -1,9 +0,0 @@ -foo.bar -{ - (schema "tests/schema.schema") - - a = "BAZ" - b = 42 - c = null - d = "255.255.255.255/32" -} diff --git a/tests/schema_03.failure b/tests/schema_03.failure deleted file mode 100644 index db1a66b..0000000 --- a/tests/schema_03.failure +++ /dev/null @@ -1,11 +0,0 @@ -foo.bar -{ - (schema "tests/schema.schema") - - a = "BAZ" - b = 42 - c = null -} -foo += temporary - -bar = (foo)