Repository: gerlero/foamlib

Issue:
Erronious space in 'functions'
I think I have found another issue. 

```py
from foamlib import FoamCase
from pathlib import Path

case_path = Path('./example_dir')
system_path = case_path / 'system'
system_path.mkdir(exist_ok=True, parents=True)
dict_file = system_path / 'controlDict'
dict_file.touch()

case = FoamCase(case_path)
with case.file(path=Path('system/controlDict')) as f:
    f['functions'] = {
        'runTimeControl1': {
            'conditions': {
                'stopOnUy': {
                    'type':  'equationInitialResidual',
                    'fields': "(U.component(1))",
                    'value':  1e-05,
                    'mode':  'minimum',
                    'conditionSatisfiedAction': 'writeNow',
                    'write':  True
                }
            }
        }
    }

```
produces 

```txt
FoamFile
{
    version 2.0;
    format ascii;
    class dictionary;
    location "system";
    object controlDict;
}

functions
{
    runTimeControl1
    {
    
        conditions
        {
        
            stopOnUy
            {
            
                type equationInitialResidual;
            
                fields (U.component (1));
            
                value 1e-05;
            
                mode minimum;
            
                conditionSatisfiedAction writeNow;
            
                write yes;
            }
        }
    }
}
```

Where `U.component(1)`  is converted `U.component (1)` and now has a space between `t` and `(`, which openFOAM can't seem to parse.

Hints from issue comments:
@gerlero: Should be fixed in v0.8.8. @bevanwsjones Thanks for reporting!@bevanwsjones: Thanks again, great library :)

Solution patch:
diff --git a/foamlib/_files/_parsing.py b/foamlib/_files/_parsing.py
index b9c9706..a7ec9c7 100644
--- a/foamlib/_files/_parsing.py
+++ b/foamlib/_files/_parsing.py
@@ -214,6 +214,8 @@ _COMMENT = Regex(r"(?:/\*(?:[^*]|\*(?!/))*\*/)|(?://(?:\\\n|[^\n])*)")
 _IDENTCHARS = identchars + "$"
 _IDENTBODYCHARS = (
     printables.replace(";", "")
+    .replace("(", "")
+    .replace(")", "")
     .replace("{", "")
     .replace("}", "")
     .replace("[", "")
@@ -243,10 +245,12 @@ _TENSOR = (
     | _tensor(TensorKind.TENSOR)
 )
 _PARENTHESIZED = Forward()
-_IDENTIFIER = Combine(
-    Word(_IDENTCHARS, _IDENTBODYCHARS, exclude_chars="()") + Opt(_PARENTHESIZED)
+_IDENTIFIER = Combine(Word(_IDENTCHARS, _IDENTBODYCHARS) + Opt(_PARENTHESIZED))
+_PARENTHESIZED <<= Combine(
+    Literal("(")
+    + (_PARENTHESIZED | Word(_IDENTBODYCHARS) + Opt(_PARENTHESIZED))
+    + Literal(")")
 )
-_PARENTHESIZED <<= Combine(Literal("(") + (_PARENTHESIZED | _IDENTIFIER) + Literal(")"))
 
 _DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
     lambda tks: Dimensioned(*reversed(tks.as_list()))
@@ -261,7 +265,7 @@ _FIELD = (Keyword("uniform", _IDENTBODYCHARS).suppress() + _TENSOR) | (
     )
 )
 _DIRECTIVE = Word("#", _IDENTBODYCHARS)
-_TOKEN = dbl_quoted_string | _IDENTIFIER | _DIRECTIVE
+_TOKEN = dbl_quoted_string | _DIRECTIVE | _IDENTIFIER
 _DATA = Forward()
 _KEYWORD_ENTRY = _keyword_entry_of(_TOKEN | _list_of(_IDENTIFIER), _DATA)
 _DICT = _dict_of(_TOKEN, _DATA)


Test patch:
diff --git a/tests/test_files/test_parsing.py b/tests/test_files/test_parsing.py
index 0633ee2..f37995b 100644
--- a/tests/test_files/test_parsing.py
+++ b/tests/test_files/test_parsing.py
@@ -93,6 +93,7 @@ def test_parse_value() -> None:
     assert Parsed(b"({a b; c d;} {e g;})")[()] == [{"a": "b", "c": "d"}, {"e": "g"}]
     assert Parsed(b"(water oil mercury air)")[()] == ["water", "oil", "mercury", "air"]
     assert Parsed(b"div(phi,U)")[()] == "div(phi,U)"
+    assert Parsed(b"U.component(1)")[()] == "U.component(1)"
     assert Parsed(b"div(nuEff*dev(T(grad(U))))")[()] == "div(nuEff*dev(T(grad(U))))"
     assert Parsed(b"div((nuEff*dev(T(grad(U)))))")[()] == "div((nuEff*dev(T(grad(U)))))"
     assert Parsed(b"((air and water) { type constant; sigma 0.07; })")[()] == [