diff options
author | Daniil Baturin <daniil@baturin.org> | 2017-01-13 23:48:56 +0700 |
---|---|---|
committer | Daniil Baturin <daniil@baturin.org> | 2017-01-13 23:48:56 +0700 |
commit | 621d7bb43a9bb2c0941f91c7c3a5aa082aa87e1b (patch) | |
tree | 4f7eafda2c04c61e59640c1dd1f951cccef6b125 | |
parent | 3de4790370ec8ee485c3f723c3471116dd094019 (diff) | |
download | vyconf-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.mll | 4 | ||||
-rw-r--r-- | src/curly_parser.mly | 31 | ||||
-rw-r--r-- | test/curly_parser_test.ml | 66 |
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 () = |