diff --git a/Cargo.lock b/Cargo.lock index 14906c8..1932967 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -112,7 +112,7 @@ dependencies = [ [[package]] name = "corn-cli" -version = "0.5.0" +version = "0.6.1" dependencies = [ "clap", "colored", diff --git a/README.md b/README.md index 55ee16e..210aeb7 100644 --- a/README.md +++ b/README.md @@ -360,6 +360,50 @@ if you need more power you should use a full language. That said, they hopefully provide a way of quickly viewing/changing values without needing to trawl through the whole file. +#### Merging + +Somtimes you want to re-use an object or array to compose a larger object/array. +It is possible to achieve this by merging two together using the `..$input` spread operator. +This allows you to spread object inputs into other objects, and array inputs into other arrays. + +```corn +let { + $base = { foo = "bar"} +} in { + ..$base +} +``` + +Evaluates to: + +```json +{ + "foo": "bar" +} +``` + +And with arrays: + +```corn +let { + $low = [ 1 2 ] + $high = [ 3 4 ] +} in { + nums = [ ..$low ..$high ] +} +``` + +Evaluates to: + +```json +{ + "nums": ["1", "2", "3", "4"] +} +``` + +Object keys and spreads are evaulated in the order they are written, +which allows you to spread a base object and then manually overwrite specific keys. + ### Comments At any point you can start a comment using `//`. A comment is terminated by a newline `\n` character. @@ -510,4 +554,4 @@ Make sure to enable it when building: ```sh wasm-pack build -- --features wasm -``` \ No newline at end of file +``` diff --git a/assets/inputs/invalid_spread.corn b/assets/inputs/invalid_spread.corn new file mode 100644 index 0000000..491a29f --- /dev/null +++ b/assets/inputs/invalid_spread.corn @@ -0,0 +1,5 @@ +let { + foo = 23 +} in { + ..$foo +} diff --git a/assets/inputs/spread.corn b/assets/inputs/spread.corn new file mode 100644 index 0000000..966128c --- /dev/null +++ b/assets/inputs/spread.corn @@ -0,0 +1,10 @@ +let { + $foo = { bar = "baz" } + + $nums_low = [ 1 2 ] + $num_high = [ 3 4 ] +} in { + hello = "world" + ..$foo + nums = [ ..$nums_low ..$num_high ] +} diff --git a/assets/outputs/json/spread.json b/assets/outputs/json/spread.json new file mode 100644 index 0000000..c750a3d --- /dev/null +++ b/assets/outputs/json/spread.json @@ -0,0 +1,10 @@ +{ + "bar": "baz", + "hello": "world", + "nums": [ + 1, + 2, + 3, + 4 + ] +} diff --git a/assets/outputs/toml/spread.toml b/assets/outputs/toml/spread.toml new file mode 100644 index 0000000..ec0b6cf --- /dev/null +++ b/assets/outputs/toml/spread.toml @@ -0,0 +1,9 @@ +bar = 'baz' +hello = 'world' +nums = [ + 1, + 2, + 3, + 4, +] + diff --git a/assets/outputs/yaml/spread.yml b/assets/outputs/yaml/spread.yml new file mode 100644 index 0000000..24ff21f --- /dev/null +++ b/assets/outputs/yaml/spread.yml @@ -0,0 +1,8 @@ +bar: baz +hello: world +nums: +- 1 +- 2 +- 3 +- 4 + diff --git a/libcorn/src/grammar.pest b/libcorn/src/grammar.pest index c29a638..88f5c57 100644 --- a/libcorn/src/grammar.pest +++ b/libcorn/src/grammar.pest @@ -2,18 +2,34 @@ WHITESPACE = _{ " " | "\t" | "\r" | "\n" } COMMENT = _{ "//" ~ (!"\n" ~ ANY)* } object = { - "{" ~ pair* ~ "}" + "{" + ~ object_value* + ~ "}" +} + +object_value = _{ + pair | spread +} + +spread = { + ".." ~ input } array = { - "[" ~ value* ~ "]" + "[" + ~ array_value* + ~ "]" +} + +array_value = _{ + value | spread } pair = { path ~ "=" ~ value } path = ${ - path_seg - ~ ( "." ~ path_seg )* + path_seg + ~ ( "." ~ path_seg )* } path_seg = ${ path_char + } @@ -33,9 +49,9 @@ string_val = ${ char* } char = { // input - !("\"" | "\\") ~ ANY - | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") - | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) + !("\"" | "\\") ~ ANY + | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") + | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) } integer = @{ @@ -56,4 +72,4 @@ assignment = { input ~ "=" ~ value } assign_block = { "let" ~ "{" ~ assignment* ~ "}" ~ "in" } -config = _{ SOI ~ assign_block? ~ object ~ EOI } \ No newline at end of file +config = _{ SOI ~ assign_block? ~ object ~ EOI } diff --git a/libcorn/src/parser.rs b/libcorn/src/parser.rs index fbbf7f3..c82879e 100644 --- a/libcorn/src/parser.rs +++ b/libcorn/src/parser.rs @@ -89,10 +89,25 @@ impl<'a> CornParser<'a> { /// to form a vector of `Value`s. fn parse_array(&self, block: Pair<'a, Rule>) -> Result>> { assert_eq!(block.as_rule(), Rule::array); - block - .into_inner() - .map(|pair| self.parse_value(pair)) - .collect::>>() + + let mut arr = vec![]; + + for pair in block.into_inner() { + match pair.as_rule() { + Rule::spread => { + let input = pair.into_inner().next().unwrap(); + let value = self.parse_value(input)?; + + match value { + Value::Array(other) => arr.extend(other), + _ => unreachable!(), + } + } + _ => arr.push(self.parse_value(pair)?), + }; + } + + Ok(arr) } /// Parses each key/value pair in a `Rule::object` @@ -118,6 +133,15 @@ impl<'a> CornParser<'a> { value, ); } + Rule::spread => { + let input = pair.into_inner().next().unwrap(); + let value = self.parse_value(input)?; + + match value { + Value::Object(other) => obj.extend(other), + _ => unreachable!(), + } + } _ => unreachable!(), } } diff --git a/libcorn/tests/parser_tests.rs b/libcorn/tests/parser_tests.rs index 9b238d5..b094cc2 100644 --- a/libcorn/tests/parser_tests.rs +++ b/libcorn/tests/parser_tests.rs @@ -92,9 +92,10 @@ generate_eq_tests!( object, object_in_array, readme_example, + spread, string, value_after_table, very_compact ); -generate_invalid_tests!(invalid, invalid_input); +generate_invalid_tests!(invalid, invalid_input, invalid_spread);