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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions news/799.feature
@@ -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
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
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
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
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
},
odelalleau marked this conversation as resolved.
Show resolved Hide resolved
),
(
"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/$.%@?|}",
odelalleau marked this conversation as resolved.
Show resolved Hide resolved
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