diff --git a/muppet/puppet/format/parser.py b/muppet/puppet/format/parser.py
index ad28c5299df409f2eaee0f7b76dae73666c603e0..552483b004e8c3fc6202b3828322e8f3e71feb2f 100644
--- a/muppet/puppet/format/parser.py
+++ b/muppet/puppet/format/parser.py
@@ -444,13 +444,38 @@ class ParserFormatter(Serializer[ParseDirective]):
 
     @override
     def _puppet_hash(self, it: PuppetHash) -> ParseDirective:
-        parser = ws & '{'
+        """
+        Parse a puppet hash literal.
+
+        The reason for the enclosing braces being optional is that
+        they are in some contexts.
+
+        For example:
+
+        .. code-block:: puppet
+
+            f("x",
+                a => 1,
+                b => 2)
+
+        is parsed as
+
+        .. code-block:: puppet
+
+            f("x", {
+                a => 1,
+                b => 2 })
+
+        This will however not give any false positives, since our
+        parser is built from the source.
+        """
+        parser = ws & optional(s('{'))
         for entry in it.entries:
             parser &= (ws & self.s(entry.k) &
                        ws & '=>' &
                        ws & self.s(entry.v) &
                        optional(ws & ','))
-        parser &= ws & '}'
+        parser &= ws & optional(s('}'))
         return parser
 
     @override
diff --git a/tests/test_parse_elsif.py b/tests/test_parse_elsif.py
index 02264153860141031a96640367f59feb815db72c..8de39f53e0a356ab878b242b115b71bbfdfa9aa8 100644
--- a/tests/test_parse_elsif.py
+++ b/tests/test_parse_elsif.py
@@ -29,19 +29,72 @@ error with the if block.
 
 import pytest
 from muppet.puppet.format.parser import ParserFormatter
-from muppet.puppet.ast import build_ast
+from muppet.puppet.ast import (
+    build_ast,
+    Puppet,
+    HashEntry,
+    PuppetHash,
+    PuppetNumber,
+    PuppetQn,
+    PuppetString,
+    PuppetInvoke,
+)
 from muppet.puppet.parser import puppet_parser
 from muppet.parser_combinator import ParserCombinator
 from pprint import pprint
-
-
-def parse_string(s: str):
-    ast = build_ast(puppet_parser(s))
-    pprint(ast)
-    parser = ParserFormatter(s, "s").serialize(ast)
+from typing import Any, Optional
+
+
+def parse_string(s: str, *,
+                 ast: Optional[Puppet] = None,
+                 matched: Optional[list[Any]] = None):
+    """
+    Parse and validate the given puppet fragment.
+
+    At it's core this procedure simply:
+    - Parses the puppet fragment into a well-behaved JSON structure
+      (note that this step is actually two steps, see documentation)
+    - Builds a Python AST from it
+    - Prints that AST to stdout
+    - Builds a parser combinator from that AST
+    - Prints that parser combinator to stdout
+      (TODO add rainbow parenthesis if module is available)
+    - Attempt to parse the original string with the generated parser
+      combinator
+    - Prints that result.
+
+    After the generation and printing of different data, there's an
+    option to compare it to a known correct value. This comparison is
+    done inside an `assert`, meaning that it works well with the
+    running test.
+
+    No comparison option exist for the parser object, since it may
+    contain functions, and the only function comparison is if it's the
+    exact same object, making it useless here.
+
+    :param s:
+        A self contained puppet fragment.
+    :param ast:
+        AST to compare the output of `build_ast` to.
+    :param matched:
+        List of match objects (or whatever the parser combinator
+        returned) to compare agains.
+    """
+    generated_ast = build_ast(puppet_parser(s))
+    pprint(generated_ast)
+    if ast:
+        assert generated_ast == ast
+
+    parser = ParserFormatter(s, "s").serialize(generated_ast)
+
+    print()
     pprint(parser)
     match_objects = ParserCombinator(s, "s").get(parser)
+
+    print()
     pprint(match_objects)
+    if matched:
+        assert matched == match_objects
 
 
 def test_parse_else_if():
@@ -121,6 +174,7 @@ def test_invoke_inside_if():
     }
     """)
 
+
 def test_funcall_with_block():
     parse_string("""
     assert_type(A::B, $name) |$_expected, $actual | {
@@ -210,3 +264,25 @@ def test_unless():
         }
     }
     """)
+
+
+def test_bare_hash():
+    # This will be parsed just as if there was a curly brace after
+    # `"String",`, with a matching end brace before the closing
+    # parenthesis.
+    s = """
+    f("String",
+        x => 1,
+        y => 2,
+    )
+    """
+    parse_string(
+        s,
+        ast=PuppetInvoke(
+            PuppetQn(name='f'),
+            [PuppetString('String'),
+             PuppetHash(entries=[HashEntry(PuppetQn('x'),
+                                           PuppetNumber(1)),
+                                 HashEntry(PuppetQn('y'),
+                                           PuppetNumber(2))])]))
+    assert False