Repository: spack/spack

Issue:
Spack add parser: Unbounded version specifier followed by another spec 
### Steps to reproduce

An error occur when running this command:
```spack add opa-psm2^cuda@10.2: nccl
Error: 10.2:nccm is an empty range
```
You can reproduce this whenever you have 2 specs to add, use unbounded version specifier as last part of the first spec. 
It seems like the parser does not treat nccl in this case as a new spec but as the upper bound of the version.

Also, for some reason the last character of the second spec is incremented like in this example: nccl become nccm.
When I used package opa-psm2 instead of nccl, the error would be: Error: 10.2:opa-psm3 is an empty range

### Error message

_No response_

### Information on your system

* **Spack:** 0.21.0.dev0 (38dc3a6896f64b894adead3446e8f822f878860b)
* **Python:** 3.6.5
* **Platform:** linux-rhel8-cascadelake
* **Concretizer:** clingo

### General information

- [X] I have run `spack debug report` and reported the version of Spack/Python/Platform
- [X] I have searched the issues of this repo and believe this is not a duplicate
- [X] I have run the failing commands in debug mode and reported the output


Discussion:
@alalazo why are whitespaces supported in a version range/list? Has this always been the case? It's ambiguous, should be disallowed.

Resolving patch:
diff --git a/lib/spack/spack/parser.py b/lib/spack/spack/parser.py
--- a/lib/spack/spack/parser.py
+++ b/lib/spack/spack/parser.py
@@ -95,9 +95,9 @@
 VALUE = r"([a-zA-Z_0-9\-+\*.,:=\~\/\\]+)"
 QUOTED_VALUE = r"[\"']+([a-zA-Z_0-9\-+\*.,:=\~\/\\\s]+)[\"']+"
 
-VERSION = r"=?([a-zA-Z0-9_][a-zA-Z_0-9\-\.]*\b)"
-VERSION_RANGE = rf"({VERSION}\s*:\s*{VERSION}(?!\s*=)|:\s*{VERSION}(?!\s*=)|{VERSION}\s*:|:)"
-VERSION_LIST = rf"({VERSION_RANGE}|{VERSION})(\s*[,]\s*({VERSION_RANGE}|{VERSION}))*"
+VERSION = r"=?(?:[a-zA-Z0-9_][a-zA-Z_0-9\-\.]*\b)"
+VERSION_RANGE = rf"(?:(?:{VERSION})?:(?:{VERSION}(?!\s*=))?)"
+VERSION_LIST = rf"(?:{VERSION_RANGE}|{VERSION})(?:\s*,\s*(?:{VERSION_RANGE}|{VERSION}))*"
 
 
 class TokenBase(enum.Enum):


Test patch:
diff --git a/lib/spack/spack/test/spec_syntax.py b/lib/spack/spack/test/spec_syntax.py
--- a/lib/spack/spack/test/spec_syntax.py
+++ b/lib/spack/spack/test/spec_syntax.py
@@ -472,33 +472,46 @@ def _specfile_for(spec_str, filename):
             [Token(TokenType.PROPAGATED_KEY_VALUE_PAIR, value='cflags=="-O3 -g"')],
             'cflags=="-O3 -g"',
         ),
-        # Way too many spaces
+        # Whitespace is allowed in version lists
+        ("@1.2:1.4 , 1.6 ", [Token(TokenType.VERSION, value="@1.2:1.4 , 1.6")], "@1.2:1.4,1.6"),
+        # But not in ranges. `a@1:` and `b` are separate specs, not a single `a@1:b`.
         (
-            "@1.2 : 1.4 , 1.6 ",
-            [Token(TokenType.VERSION, value="@1.2 : 1.4 , 1.6")],
-            "@1.2:1.4,1.6",
+            "a@1: b",
+            [
+                Token(TokenType.UNQUALIFIED_PACKAGE_NAME, value="a"),
+                Token(TokenType.VERSION, value="@1:"),
+                Token(TokenType.UNQUALIFIED_PACKAGE_NAME, value="b"),
+            ],
+            "a@1:",
+        ),
+        (
+            "@1.2:   develop   = foo",
+            [
+                Token(TokenType.VERSION, value="@1.2:"),
+                Token(TokenType.KEY_VALUE_PAIR, value="develop   = foo"),
+            ],
+            "@1.2: develop=foo",
         ),
-        ("@1.2 :   develop", [Token(TokenType.VERSION, value="@1.2 :   develop")], "@1.2:develop"),
         (
-            "@1.2 :   develop   = foo",
+            "@1.2:develop   = foo",
             [
-                Token(TokenType.VERSION, value="@1.2 :"),
+                Token(TokenType.VERSION, value="@1.2:"),
                 Token(TokenType.KEY_VALUE_PAIR, value="develop   = foo"),
             ],
             "@1.2: develop=foo",
         ),
         (
-            "% intel @ 12.1 : 12.6 + debug",
+            "% intel @ 12.1:12.6 + debug",
             [
-                Token(TokenType.COMPILER_AND_VERSION, value="% intel @ 12.1 : 12.6"),
+                Token(TokenType.COMPILER_AND_VERSION, value="% intel @ 12.1:12.6"),
                 Token(TokenType.BOOL_VARIANT, value="+ debug"),
             ],
             "%intel@12.1:12.6+debug",
         ),
         (
-            "@ 12.1 : 12.6 + debug - qt_4",
+            "@ 12.1:12.6 + debug - qt_4",
             [
-                Token(TokenType.VERSION, value="@ 12.1 : 12.6"),
+                Token(TokenType.VERSION, value="@ 12.1:12.6"),
                 Token(TokenType.BOOL_VARIANT, value="+ debug"),
                 Token(TokenType.BOOL_VARIANT, value="- qt_4"),
             ],