diff --git a/formulaic/parser/parser.py b/formulaic/parser/parser.py
index 34e8a8e..dab6bc2 100644
--- a/formulaic/parser/parser.py
+++ b/formulaic/parser/parser.py
@@ -36,7 +36,7 @@ class FormulaParser:
             terms = arg.to_terms()
             if len(terms) > 1 or list(terms)[0] != '0':
                 raise FormulaParsingError("Unary negation is only implemented for '0', where it is substituted for '1'.")
-            return {Term(factors=[Factor('1', eval_method='literal')])}
+            return {Term(factors=[Factor('1', eval_method='literal')])}  # pragma: no cover; All zero handling is currently done in the token pre-processor.
 
         return [
             Operator("~", arity=2, precedence=-100, associativity=None, to_terms=formula_separator_expansion),
@@ -86,11 +86,21 @@ class FormulaParser:
                     tokens.insert(0, one)
                     tokens.insert(1, plus)
 
-        # Replace "0" with "-1"
+        # Replace all "0"s with "-1"
+        zero_index = -1
         try:
-            zero_index = tokens.index('0')
-            if tokens[zero_index - 1] == '+':
-                tokens[zero_index - 1] = minus
+            while True:
+                zero_index = tokens.index('0', zero_index + 1)
+                if zero_index - 1 < 0 or tokens[zero_index - 1] == '~':
+                    tokens.pop(zero_index)
+                    zero_index -= 1
+                    continue
+                elif tokens[zero_index - 1] == '+':
+                    tokens[zero_index - 1] = minus
+                elif tokens[zero_index - 1] == '-':
+                    tokens[zero_index - 1] = plus
+                else:
+                    raise FormulaParsingError(f"Unrecognised use of `0` at index: {tokens[zero_index-1].source_start}.")
                 tokens[zero_index] = one
         except ValueError:
             pass

BUG: Repeated constant-like terms are incorrectly parsed
Formulas like `0 + 0 + 1 + x` are incorrectly parsed and result in a DataFrame that looks like:

```
from formulaic import model_matrix
from pandas import DataFrame 
df = DataFrame([[0],[1],[2],[3],[4]],columns=["x"])
dep = model_matrix("0 + 0 + 1 + x", df, ensure_full_rank=False)
print(DataFrame(dep))
```

```
   Intercept  Intercept  x
0        0.0        1.0  0
1        0.0        1.0  1
2        0.0        1.0  2
3        0.0        1.0  3
4        0.0        1.0  4
```

`0 + 0 + 1` should evaluate to just `1 +`
diff --git a/tests/parser/test_parser.py b/tests/parser/test_parser.py
index a5d4e2f..7b64ae9 100644
--- a/tests/parser/test_parser.py
+++ b/tests/parser/test_parser.py
@@ -18,7 +18,7 @@ FORMULA_TO_TOKENS = {
 
     # Test translation of '0'
     '0': ['1', '-', '1'],
-    '0 ~': ['0', '~', '1']
+    '0 ~': ['~', '1']
 }
 
 FORMULA_TO_TERMS = {
@@ -34,6 +34,13 @@ FORMULA_TO_TERMS = {
     '(a - a) + b': ['1', 'b'],
     'a + (b - a)': ['1', 'a', 'b'],
 
+    # Check that "0" -> "-1" substitution works as expected
+    '0 + 0': [],
+    '0 + 0 + 1': ['1'],
+    '0 + 0 + 1 + 0': [],
+    '0 - 0': ['1'],
+    '0 ~ 0': ([], ),
+
     # Formula separators
     '~ a + b': (['1', 'a', 'b'], ),
     'a ~ b + c': (['a'], ['1', 'b', 'c']),
@@ -80,4 +87,8 @@ class TestFormulaParser:
 
     def test_invalid_unary_negation(self):
         with pytest.raises(FormulaParsingError):
-            assert PARSER.get_terms('(-10)')
+            PARSER.get_terms('(-10)')
+
+    def test_invalid_use_of_zero(self):
+        with pytest.raises(FormulaParsingError):
+            PARSER.get_terms('a * 0')

pip install -e .[test]
parse_log_pytest
pytest
flake8 pytest-cov pytest
3.7
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
astor==0.8.1
attrs @ file:///croot/attrs_1668696182826/work
certifi @ file:///croot/certifi_1671487769961/work/certifi
coverage==7.2.7
flake8==5.0.4
flit_core @ file:///opt/conda/conda-bld/flit-core_1644941570762/work/source/flit_core
-e git+https://github.com/matthewwardrop/formulaic.git@88b985aaf4d3f7138e04c3258542253c5c4f3f62#egg=formulaic
importlib-metadata==4.2.0
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
interface-meta==1.3.0
mccabe==0.7.0
mpmath==1.3.0
numpy==1.21.6
packaging @ file:///croot/packaging_1671697413597/work
pandas==1.3.5
pluggy @ file:///tmp/build/80754af9/pluggy_1648042572264/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pycodestyle==2.9.1
pyflakes==2.5.0
pytest==7.1.2
pytest-cov==4.1.0
python-dateutil==2.9.0.post0
pytz==2025.2
scipy==1.7.3
six==1.17.0
sympy==1.10.1
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
typing_extensions @ file:///croot/typing_extensions_1669924550328/work
wrapt==1.16.0
zipp @ file:///croot/zipp_1672387121353/work

name: formulaic
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=22.1.0=py37h06a4308_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - flit-core=3.6.0=pyhd3eb1b0_0
  - importlib_metadata=4.11.3=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=22.0=py37h06a4308_0
  - pip=22.3.1=py37h06a4308_0
  - pluggy=1.0.0=py37h06a4308_1
  - py=1.11.0=pyhd3eb1b0_0
  - pytest=7.1.2=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py37h06a4308_0
  - typing_extensions=4.4.0=py37h06a4308_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.11.0=py37h06a4308_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - astor==0.8.1
      - coverage==7.2.7
      - flake8==5.0.4
      - importlib-metadata==4.2.0
      - interface-meta==1.3.0
      - mccabe==0.7.0
      - mpmath==1.3.0
      - numpy==1.21.6
      - pandas==1.3.5
      - pycodestyle==2.9.1
      - pyflakes==2.5.0
      - pytest-cov==4.1.0
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - scipy==1.7.3
      - six==1.17.0
      - sympy==1.10.1
      - wrapt==1.16.0
prefix: /opt/conda/envs/formulaic

tests/parser/test_parser.py::TestFormulaParser::test_get_tokens[0 tests/parser/test_parser.py::TestFormulaParser::test_to_terms[0 tests/parser/test_parser.py::TestFormulaParser::test_invalid_use_of_zero
tests/parser/test_parser.py::TestFormulaParser::test_get_tokens[-tokens0] tests/parser/test_parser.py::TestFormulaParser::test_get_tokens[ tests/parser/test_parser.py::TestFormulaParser::test_get_tokens[1-tokens3] tests/parser/test_parser.py::TestFormulaParser::test_get_tokens[a-tokens4] tests/parser/test_parser.py::TestFormulaParser::test_get_tokens[a tests/parser/test_parser.py::TestFormulaParser::test_get_tokens[~ tests/parser/test_parser.py::TestFormulaParser::test_get_tokens[0-tokens8] tests/parser/test_parser.py::TestFormulaParser::test_to_terms[-terms0] tests/parser/test_parser.py::TestFormulaParser::test_to_terms[a-terms1] tests/parser/test_parser.py::TestFormulaParser::test_to_terms[a tests/parser/test_parser.py::TestFormulaParser::test_to_terms[(a tests/parser/test_parser.py::TestFormulaParser::test_to_terms[~ tests/parser/test_parser.py::TestFormulaParser::test_to_terms[a:b-terms17] tests/parser/test_parser.py::TestFormulaParser::test_to_terms[(a+b):(c+d)-terms19] tests/parser/test_parser.py::TestFormulaParser::test_to_terms[(a+b)**2-terms20] tests/parser/test_parser.py::TestFormulaParser::test_to_terms[(a+b)**3-terms21] tests/parser/test_parser.py::TestFormulaParser::test_to_terms[a/b-terms22] tests/parser/test_parser.py::TestFormulaParser::test_to_terms[(a+b)/c-terms23] tests/parser/test_parser.py::TestFormulaParser::test_to_terms[a/(b+c)-terms24] tests/parser/test_parser.py::TestFormulaParser::test_to_terms[a/(b+c-b)-terms25] tests/parser/test_parser.py::TestFormulaParser::test_to_terms[+1-terms26] tests/parser/test_parser.py::TestFormulaParser::test_to_terms[-0-terms27] tests/parser/test_parser.py::TestFormulaParser::test_invalid_unary_negation
formulaic/parser/parser.py
formulaic/parser/parser.py