spec parser: precompile quoting-related regular expressions (#41657)
This adds a small (~5%) performance improvement to Spec parsing. Co-authored-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
This commit is contained in:
parent
a972314fa6
commit
0dc3fc2d21
@ -154,7 +154,7 @@ def quote_kvp(string: str) -> str:
|
|||||||
or ``name==``, and we assume the rest of the argument is the value. This covers the
|
or ``name==``, and we assume the rest of the argument is the value. This covers the
|
||||||
common cases of passign flags, e.g., ``cflags="-O2 -g"`` on the command line.
|
common cases of passign flags, e.g., ``cflags="-O2 -g"`` on the command line.
|
||||||
"""
|
"""
|
||||||
match = re.match(spack.parser.SPLIT_KVP, string)
|
match = spack.parser.SPLIT_KVP.match(string)
|
||||||
if not match:
|
if not match:
|
||||||
return string
|
return string
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@
|
|||||||
VALUE = r"(?:[a-zA-Z_0-9\-+\*.,:=\~\/\\]+)"
|
VALUE = r"(?:[a-zA-Z_0-9\-+\*.,:=\~\/\\]+)"
|
||||||
|
|
||||||
#: Variant/flag values that match this can be left unquoted in Spack output
|
#: Variant/flag values that match this can be left unquoted in Spack output
|
||||||
NO_QUOTES_NEEDED = r"^[a-zA-Z0-9,/_.-]+$"
|
NO_QUOTES_NEEDED = re.compile(r"^[a-zA-Z0-9,/_.-]+$")
|
||||||
|
|
||||||
#: Quoted values can be *anything* in between quotes, including escaped quotes.
|
#: Quoted values can be *anything* in between quotes, including escaped quotes.
|
||||||
QUOTED_VALUE = r"(?:'(?:[^']|(?<=\\)')*'|\"(?:[^\"]|(?<=\\)\")*\")"
|
QUOTED_VALUE = r"(?:'(?:[^']|(?<=\\)')*'|\"(?:[^\"]|(?<=\\)\")*\")"
|
||||||
@ -110,15 +110,15 @@
|
|||||||
VERSION_LIST = rf"(?:{VERSION_RANGE}|{VERSION})(?:\s*,\s*(?:{VERSION_RANGE}|{VERSION}))*"
|
VERSION_LIST = rf"(?:{VERSION_RANGE}|{VERSION})(?:\s*,\s*(?:{VERSION_RANGE}|{VERSION}))*"
|
||||||
|
|
||||||
#: Regex with groups to use for splitting (optionally propagated) key-value pairs
|
#: Regex with groups to use for splitting (optionally propagated) key-value pairs
|
||||||
SPLIT_KVP = rf"^({NAME})(==?)(.*)$"
|
SPLIT_KVP = re.compile(rf"^({NAME})(==?)(.*)$")
|
||||||
|
|
||||||
#: Regex to strip quotes. Group 2 will be the unquoted string.
|
#: Regex to strip quotes. Group 2 will be the unquoted string.
|
||||||
STRIP_QUOTES = r"^(['\"])(.*)\1$"
|
STRIP_QUOTES = re.compile(r"^(['\"])(.*)\1$")
|
||||||
|
|
||||||
|
|
||||||
def strip_quotes_and_unescape(string: str) -> str:
|
def strip_quotes_and_unescape(string: str) -> str:
|
||||||
"""Remove surrounding single or double quotes from string, if present."""
|
"""Remove surrounding single or double quotes from string, if present."""
|
||||||
match = re.match(STRIP_QUOTES, string)
|
match = STRIP_QUOTES.match(string)
|
||||||
if not match:
|
if not match:
|
||||||
return string
|
return string
|
||||||
|
|
||||||
@ -140,7 +140,7 @@ def quote_if_needed(value: str) -> str:
|
|||||||
``"``, and control codes.
|
``"``, and control codes.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if re.match(spack.parser.NO_QUOTES_NEEDED, value):
|
if NO_QUOTES_NEEDED.match(value):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
return json.dumps(value) if "'" in value else f"'{value}'"
|
return json.dumps(value) if "'" in value else f"'{value}'"
|
||||||
@ -456,14 +456,14 @@ def parse(
|
|||||||
)
|
)
|
||||||
|
|
||||||
elif self.ctx.accept(TokenType.KEY_VALUE_PAIR):
|
elif self.ctx.accept(TokenType.KEY_VALUE_PAIR):
|
||||||
match = re.match(SPLIT_KVP, self.ctx.current_token.value)
|
match = SPLIT_KVP.match(self.ctx.current_token.value)
|
||||||
assert match, "SPLIT_KVP and KEY_VALUE_PAIR do not agree."
|
assert match, "SPLIT_KVP and KEY_VALUE_PAIR do not agree."
|
||||||
|
|
||||||
name, delim, value = match.groups()
|
name, delim, value = match.groups()
|
||||||
initial_spec._add_flag(name, strip_quotes_and_unescape(value), propagate=False)
|
initial_spec._add_flag(name, strip_quotes_and_unescape(value), propagate=False)
|
||||||
|
|
||||||
elif self.ctx.accept(TokenType.PROPAGATED_KEY_VALUE_PAIR):
|
elif self.ctx.accept(TokenType.PROPAGATED_KEY_VALUE_PAIR):
|
||||||
match = re.match(SPLIT_KVP, self.ctx.current_token.value)
|
match = SPLIT_KVP.match(self.ctx.current_token.value)
|
||||||
assert match, "SPLIT_KVP and PROPAGATED_KEY_VALUE_PAIR do not agree."
|
assert match, "SPLIT_KVP and PROPAGATED_KEY_VALUE_PAIR do not agree."
|
||||||
|
|
||||||
name, delim, value = match.groups()
|
name, delim, value = match.groups()
|
||||||
|
Loading…
Reference in New Issue
Block a user