summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniil Baturin <daniil@baturin.org>2017-01-13 23:48:56 +0700
committerDaniil Baturin <daniil@baturin.org>2017-01-13 23:48:56 +0700
commit621d7bb43a9bb2c0941f91c7c3a5aa082aa87e1b (patch)
tree4f7eafda2c04c61e59640c1dd1f951cccef6b125
parent3de4790370ec8ee485c3f723c3471116dd094019 (diff)
downloadvyconf-621d7bb43a9bb2c0941f91c7c3a5aa082aa87e1b.tar.gz
vyconf-621d7bb43a9bb2c0941f91c7c3a5aa082aa87e1b.zip
T249: add support for inactive and ephemeral nodes to the curly config parser.
-rw-r--r--src/curly_lexer.mll4
-rw-r--r--src/curly_parser.mly31
-rw-r--r--test/curly_parser_test.ml66
3 files changed, 93 insertions, 8 deletions
diff --git a/src/curly_lexer.mll b/src/curly_lexer.mll
index 6d2bc87..32e566a 100644
--- a/src/curly_lexer.mll
+++ b/src/curly_lexer.mll
@@ -19,6 +19,10 @@ rule token = parse
{ Lexing.new_line lexbuf ; token lexbuf }
| "/*"
{ read_comment (Buffer.create 16) lexbuf }
+| "#INACTIVE"
+ { INACTIVE }
+| "#EPHEMERAL"
+ { EPHEMERAL }
| '{'
{ LEFT_BRACE }
| '}'
diff --git a/src/curly_parser.mly b/src/curly_parser.mly
index 7daa082..560d84c 100644
--- a/src/curly_parser.mly
+++ b/src/curly_parser.mly
@@ -25,6 +25,8 @@
%token <string> IDENTIFIER
%token <string> STRING
%token <string> COMMENT
+%token INACTIVE
+%token EPHEMERAL
%token LEFT_BRACE
%token RIGHT_BRACE
%token LEFT_BRACKET
@@ -35,9 +37,22 @@
%start <Config_tree.t> config
%%
+(* Shift-reduce conflicts are reduced to shift by default,
+ so it should be fine *)
opt_comment:
| (* empty *) { None }
| c = COMMENT { Some (String.trim c) }
+;
+
+opt_inactive:
+ | (* empty *) { false }
+ | INACTIVE { true }
+;
+
+opt_ephemeral:
+ | (* empty *) { false }
+ | EPHEMERAL { true }
+;
value:
| v = STRING
@@ -53,16 +68,16 @@ values:
;
leaf_node:
- | comment = opt_comment; name = IDENTIFIER; values = values; SEMI
- { Vytree.make_full {default_data with values=(List.rev values); comment=comment} name []}
- | comment = opt_comment; name = IDENTIFIER; SEMI (* valueless node *)
- { Vytree.make_full {default_data with comment=comment} name [] }
+ | comment = opt_comment; inactive = opt_inactive; ephemeral = opt_ephemeral; name = IDENTIFIER; values = values; SEMI
+ { Vytree.make_full {values=(List.rev values); comment=comment; inactive=inactive; ephemeral=ephemeral} name []}
+ | comment = opt_comment; inactive = opt_inactive; ephemeral = opt_ephemeral; name = IDENTIFIER; SEMI (* valueless node *)
+ { Vytree.make_full {default_data with comment=comment; inactive=inactive; ephemeral=ephemeral} name [] }
;
node:
- | comment = opt_comment; name = IDENTIFIER; LEFT_BRACE; children = list(node_content); RIGHT_BRACE
+ | comment = opt_comment; inactive = opt_inactive; ephemeral = opt_ephemeral; name = IDENTIFIER; LEFT_BRACE; children = list(node_content); RIGHT_BRACE
{
- let node = Vytree.make_full {default_data with comment=comment} name [] in
+ let node = Vytree.make_full {default_data with comment=comment; inactive=inactive; ephemeral=ephemeral} name [] in
let node = List.fold_left Vytree.adopt node (List.rev children) |> Vytree.merge_children merge_data in
try
List.iter find_duplicate_children (Vytree.children_of_node node);
@@ -74,10 +89,10 @@ node:
;
tag_node:
- | comment = opt_comment; name = IDENTIFIER; tag = IDENTIFIER; LEFT_BRACE; children = list(node_content); RIGHT_BRACE
+ | comment = opt_comment; inactive = opt_inactive; ephemeral = opt_ephemeral; name = IDENTIFIER; tag = IDENTIFIER; LEFT_BRACE; children = list(node_content); RIGHT_BRACE
{
let outer_node = Vytree.make_full default_data name [] in
- let inner_node = Vytree.make_full {default_data with comment=comment} tag [] in
+ let inner_node = Vytree.make_full {default_data with comment=comment; inactive=inactive; ephemeral=ephemeral} tag [] in
let inner_node = List.fold_left Vytree.adopt inner_node (List.rev children) |> Vytree.merge_children merge_data in
let node = Vytree.adopt outer_node inner_node in
try
diff --git a/test/curly_parser_test.ml b/test/curly_parser_test.ml
index b84e5f2..ba8100f 100644
--- a/test/curly_parser_test.ml
+++ b/test/curly_parser_test.ml
@@ -33,6 +33,17 @@ let config_with_duplicate_node = "foo { bar { baz {} } bar { baz {} } }"
let config_with_duplicate_tag_node = "foo { bar baz0 { } bar baz0 { } }"
let config_with_duplicate_leaf_node = "foo { bar baz; bar quux; }"
+let config_with_inactive_node = "#INACTIVE foo { bar baz; }"
+let config_with_inactive_leaf_node = "foo { #INACTIVE bar baz; quux xyzzy; }"
+let config_with_inactive_tag_node = "foo { #INACTIVE bar baz { quux xyzzy; } }"
+
+let config_with_ephemeral_node = "#EPHEMERAL foo { } bar { }"
+let config_with_ephemeral_leaf_node = "foo { #EPHEMERAL bar baz; quux xyzzy; } bar { }"
+let config_with_ephemeral_tag_node = "foo { #EPHEMERAL bar baz { #INACTIVE quux xyzzy; } }"
+
+let config_with_inactive_and_ephemeral_node = "#INACTIVE #EPHEMERAL foo { bar { } }"
+let config_with_inactive_node_and_comment = "/* comment */ #INACTIVE #EPHEMERAL foo { }"
+
let parse s = Curly_parser.config Curly_lexer.token (Lexing.from_string s)
(* Empty config is considered valid, creates just the root node *)
@@ -131,6 +142,53 @@ let test_parse_duplicate_leaf_node test_ctxt =
let config = parse config_with_duplicate_leaf_node in
assert_equal (CT.get_values config ["foo"; "bar"]) ["baz"; "quux"]
+(* Inactive nodes are parsed correctly *)
+let test_parse_inactive_node test_ctxt =
+ let config = parse config_with_inactive_node in
+ assert_equal (CT.is_inactive config ["foo"]) true
+
+(* Inactive leaf nodes are parsed correctly *)
+let test_parse_inactive_leaf_node test_ctxt =
+ let config = parse config_with_inactive_leaf_node in
+ assert_equal (CT.is_inactive config ["foo"; "bar"]) true
+
+(* Inactive leaf nodes are parsed correctly *)
+let test_parse_inactive_tag_node test_ctxt =
+ let config = parse config_with_inactive_tag_node in
+ assert_equal (CT.is_inactive config ["foo"; "bar"; "baz"]) true
+
+(* Normally ephemeral nodes shouldn't appear in config files, since it's the whole
+ point of ephemeral nodes that they are ignored when saving the config,
+ but they are a part of the format so we get to parse them.
+ Besides, HA scripts may want to use load or merge instead of executing set commands *)
+
+(* Ephemeral nodes are parsed correctly *)
+let test_parse_ephemeral_node test_ctxt =
+ let config = parse config_with_ephemeral_node in
+ assert_equal (CT.is_ephemeral config ["foo"]) true
+
+(* Ephemeral leaf nodes are parsed correctly *)
+let test_parse_ephemeral_leaf_node test_ctxt =
+ let config = parse config_with_ephemeral_leaf_node in
+ assert_equal (CT.is_ephemeral config ["foo"; "bar"]) true
+
+(* Ephemeral leaf nodes are parsed correctly *)
+let test_parse_ephemeral_tag_node test_ctxt =
+ let config = parse config_with_ephemeral_tag_node in
+ assert_equal (CT.is_ephemeral config ["foo"; "bar"; "baz"]) true
+
+(* Unusual but not impossible: a node that is both inactive and ephemeral *)
+let test_parse_inactive_and_ephemeral_node test_ctxt =
+ let config = parse config_with_inactive_and_ephemeral_node in
+ assert_equal (CT.is_ephemeral config ["foo"]) true;
+ assert_equal (CT.is_inactive config ["foo"]) true
+
+(* Comments and ephemeral/inactive properties mix well *)
+let test_parse_inactive_node_and_comment test_ctxt =
+ let config = parse config_with_inactive_node_and_comment in
+ assert_equal (CT.is_ephemeral config ["foo"]) true;
+ assert_equal (CT.is_inactive config ["foo"]) true;
+ assert_equal (CT.get_comment config ["foo"]) (Some "comment")
let suite =
"VyConf curly config parser tests" >::: [
@@ -153,6 +211,14 @@ let suite =
"test_parse_node_duplicate_child" >:: test_parse_node_duplicate_child;
"test_parse_tag_node_duplicate_child" >:: test_parse_tag_node_duplicate_child;
"test_parse_duplicate_leaf_node" >:: test_parse_duplicate_leaf_node;
+ "test_parse_inactive_node" >:: test_parse_inactive_node;
+ "test_parse_inactive_leaf_node" >:: test_parse_inactive_leaf_node;
+ "test_parse_inactive_tag_node" >:: test_parse_inactive_tag_node;
+ "test_parse_ephemeral_node" >:: test_parse_ephemeral_node;
+ "test_parse_ephemeral_leaf_node" >:: test_parse_ephemeral_leaf_node;
+ "test_parse_ephemeral_tag_node" >:: test_parse_ephemeral_tag_node;
+ "test_parse_inactive_and_ephemeral_node" >:: test_parse_inactive_and_ephemeral_node;
+ "test_parse_inactive_node_and_comment" >:: test_parse_inactive_node_and_comment;
]
let () =