Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interpolation grammar: allow pipe | in unquoted strings #799

Merged
merged 8 commits into from
Oct 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/799.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Enable the use of the pipe symbol `|` in unquoted strings when parsing interpolations.
2 changes: 1 addition & 1 deletion omegaconf/grammar/OmegaConfGrammarLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ BOOL:

NULL: [Nn][Uu][Ll][Ll];

UNQUOTED_CHAR: [/\-\\+.$%*@?]; // other characters allowed in unquoted strings
UNQUOTED_CHAR: [/\-\\+.$%*@?|]; // other characters allowed in unquoted strings
ID: (CHAR|'_') (CHAR|DIGIT|'_')*;
ESC: (ESC_BACKSLASH | '\\(' | '\\)' | '\\[' | '\\]' | '\\{' | '\\}' |
'\\:' | '\\=' | '\\,' | '\\ ' | '\\\t')+;
Expand Down
4 changes: 2 additions & 2 deletions omegaconf/grammar/OmegaConfGrammarParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ primitive:
| INT // 0, 10, -20, 1_000_000
| FLOAT // 3.14, -20.0, 1e-1, -10e3
| BOOL // true, TrUe, false, False
| UNQUOTED_CHAR // /, -, \, +, ., $, %, *, @
| UNQUOTED_CHAR // /, -, \, +, ., $, %, *, @, ?, |
| COLON // :
| ESC // \\, \(, \), \[, \], \{, \}, \:, \=, \ , \\t, \,
| WS // whitespaces
Expand All @@ -84,7 +84,7 @@ dictKey:
| INT // 0, 10, -20, 1_000_000
| FLOAT // 3.14, -20.0, 1e-1, -10e3
| BOOL // true, TrUe, false, False
| UNQUOTED_CHAR // /, -, \, +, ., $, %, *, @
| UNQUOTED_CHAR // /, -, \, +, ., $, %, *, @, ?, |
| ESC // \\, \(, \), \[, \], \{, \}, \:, \=, \ , \\t, \,
| WS // whitespaces
)+;
5 changes: 4 additions & 1 deletion omegaconf/grammar_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,17 @@
_node_inter = f"\\${{\\s*{_node_path}\\s*}}" # node interpolation ${foo.bar}
_id = "[a-zA-Z_]\\w*" # foo, foo_bar, abc123
_resolver_name = f"({_id}(\\.{_id})*)?" # foo, ns.bar3, ns_1.ns_2.b0z
_arg = "[a-zA-Z_0-9/\\-\\+.$%*@]+" # string representing a resolver argument
_arg = "[a-zA-Z_0-9/\\-\\+.$%*@?|]+" # string representing a resolver argument
_args = f"{_arg}(\\s*,\\s*{_arg})*" # list of resolver arguments
_resolver_inter = f"\\${{\\s*{_resolver_name}\\s*:\\s*{_args}?\\s*}}" # ${foo:bar}
_inter = f"({_node_inter}|{_resolver_inter})" # any kind of interpolation
_outer = "([^$]|\\$(?!{))+" # any character except $ (unless not followed by {)
SIMPLE_INTERPOLATION_PATTERN = re.compile(
f"({_outer})?({_inter}({_outer})?)+$", flags=re.ASCII
)
# NOTE: SIMPLE_INTERPOLATION_PATTERN must not generate false positive matches:
# it must not accept anything that isn't a valid interpolation (per the
# interpolation grammar defined in `omegaconf/grammar/*.g4`).


class OmegaConfErrorListener(ErrorListener): # type: ignore
Expand Down
27 changes: 19 additions & 8 deletions tests/test_grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

# Characters that are not allowed by the grammar in config key names.
INVALID_CHARS_IN_KEY_NAMES = r"""\{}()[].:"' """
UNQUOTED_SPECIAL = r"/-\+.$%*@?|" # special characters allowed in unquoted strings

# A fixed config that may be used (but not modified!) by tests.
BASE_TEST_CFG = OmegaConf.create(
Expand Down Expand Up @@ -106,7 +107,11 @@
("float_minus_nan", "-nan", math.nan),
# Unquoted strings.
# Note: raw strings do not allow trailing \, adding a space and stripping it.
("str_legal", r" a/-\+.$*@?\\ ".strip(), r" a/-\+.$*@?\ ".strip()),
(
"str_legal",
(r" a" + UNQUOTED_SPECIAL + r"\\ ").strip(),
(r" a" + UNQUOTED_SPECIAL + r"\ ").strip(),
),
("str_illegal_1", "a,=b", GrammarParseError),
("str_illegal_2", f"{chr(200)}", GrammarParseError),
("str_illegal_3", f"{chr(129299)}", GrammarParseError),
Expand All @@ -128,8 +133,8 @@
("str_esc_illegal_1", r"\#", GrammarParseError),
("str_esc_illegal_2", r""" \'\" """.strip(), GrammarParseError),
# Quoted strings.
("str_quoted_single", "'!@#$%^&*()[]:.,\"'", '!@#$%^&*()[]:.,"'),
("str_quoted_double", '"!@#$%^&*()[]:.,\'"', "!@#$%^&*()[]:.,'"),
("str_quoted_single", "'!@#$%^&*|()[]:.,\"'", '!@#$%^&*|()[]:.,"'),
("str_quoted_double", '"!@#$%^&*|()[]:.,\'"', "!@#$%^&*|()[]:.,'"),
("str_quoted_outer_ws_single", "' a \t'", " a \t"),
("str_quoted_outer_ws_double", '" a \t"', " a \t"),
("str_quoted_int", "'123'", "123"),
Expand Down Expand Up @@ -181,8 +186,10 @@
),
(
"dict_unquoted_key",
fr"{{a0-null-1-3.14-NaN- {TAB}-true-False-/\+.$%*@\(\)\[\]\{{\}}\:\=\ \{TAB}\,:0}}",
{fr"a0-null-1-3.14-NaN- {TAB}-true-False-/\+.$%*@()[]{{}}:= {TAB},": 0},
fr"{{a0-null-1-3.14-NaN- {TAB}-true-False-{UNQUOTED_SPECIAL}\(\)\[\]\{{\}}\:\=\ \{TAB}\,:0}}",
{
fr"a0-null-1-3.14-NaN- {TAB}-true-False-{UNQUOTED_SPECIAL}()[]{{}}:= {TAB},": 0
},
),
(
"dict_quoted",
Expand Down Expand Up @@ -364,7 +371,11 @@
("str_top_middle_quote_double", 'I"d like ${str}', 'I"d like hi'),
("str_top_middle_quotes_single", "I like '${str}'", "I like 'hi'"),
("str_top_middle_quotes_double", 'I like "${str}"', 'I like "hi"'),
("str_top_any_char", r"${str} !@\#$%^&*})][({,/?;", r"hi !@\#$%^&*})][({,/?;"),
(
"str_top_any_char",
r"${str} " + UNQUOTED_SPECIAL + r"^!#&})][({,;",
r"hi " + UNQUOTED_SPECIAL + r"^!#&})][({,;",
),
("str_top_esc_inter", r"Esc: \${str}", "Esc: ${str}"),
("str_top_esc_inter_wrong_1", r"Wrong: $\{str\}", r"Wrong: $\{str\}"),
("str_top_esc_inter_wrong_2", r"Wrong: \${str\}", r"Wrong: ${str\}"),
Expand Down Expand Up @@ -602,7 +613,7 @@ def visit() -> Any:
"$ ${foo} ${bar} ${boz} $",
"${foo:bar}",
"${foo : bar, baz, boz}",
"${foo:bar,0,a-b+c*d/$.%@}",
"${foo:bar,0,a-b+c*d/$.%@?|}",
r"\${foo}",
"${foo.bar:boz}",
"${$foo.bar$.x$y}",
Expand Down Expand Up @@ -729,7 +740,7 @@ def callback(inter_key: Any, memo: Optional[Set[int]]) -> Any:


def test_custom_resolver_param_supported_chars() -> None:
supported_chars = r"abc123_/:-\+.$%*@"
supported_chars = r"abc123_:" + UNQUOTED_SPECIAL
c = OmegaConf.create({"dir1": "${copy:" + supported_chars + "}"})

OmegaConf.register_new_resolver("copy", lambda x: x)
Expand Down