Repository: gerlero/foamlib

Issue:
Strings starting with ('inf') are marked as floats, making them invalid keywords for writing entries.
Hi, i've been using your library for my research, and came across a small bug in foamlib.FoamFile

Minimal example:

```python
import foamlib as fl

with fl.FoamFile("dict") as of_dict:
    of_dict["inference"] = "foo"
```

-> Resulting Exception:
```
Traceback (most recent call last):
  File "/home/jonas/Documents/Research/NLSS/nlss/bug.py", line 4, in <module>
    of_dict["inference"] = "foo"
    ~~~~~~~^^^^^^^^^^^^^
  File "/home/jonas/Documents/Research/NLSS/nlss/.venv/lib/python3.12/site-packages/foamlib/_files/_files.py", line 201, in __setitem__
    raise ValueError(msg)
ValueError: Invalid keyword: 'inference' <class 'tuple'>
```

In contrast, this works as expected:

```python

import foamlib as fl

with fl.FoamFile("dict") as of_dict:
    of_dict["ainference"] = "foo"
    
```

Result:

```
FoamFile
{
    version 2.0;
    format ascii;
    class dictionary;
    location "nlss";
    object dict;
}

barinference foo;
```

While this is probably not a high-priority issue, I just thought I'd let you know.

Cheers,
Jonas

Hints from issue comments:
@gerlero: @j-lthr Thanks. Good find! I'll try to come up with a fix.@gerlero: @j-lthr Thanks again. This should be fixed in `foamlib 0.8.6`.

Solution patch:
diff --git a/foamlib/_files/_parsing.py b/foamlib/_files/_parsing.py
index fcb290a..311d7f3 100644
--- a/foamlib/_files/_parsing.py
+++ b/foamlib/_files/_parsing.py
@@ -16,6 +16,7 @@ else:
 
 import numpy as np
 from pyparsing import (
+    CaselessKeyword,
     Combine,
     Dict,
     Forward,
@@ -267,7 +268,16 @@ _DICT = _dict_of(_TOKEN, _DATA)
 _DATA_ENTRY = Forward()
 _LIST_ENTRY = _DICT | _KEYWORD_ENTRY | _DATA_ENTRY
 _LIST = _list_of(_LIST_ENTRY)
-_NUMBER = common.signed_integer ^ common.ieee_float
+_NUMBER = (
+    common.number
+    | CaselessKeyword("nan").set_parse_action(lambda: np.nan)
+    | (CaselessKeyword("inf") | CaselessKeyword("infinity")).set_parse_action(
+        lambda: np.inf
+    )
+    | (CaselessKeyword("-inf") | CaselessKeyword("-infinity")).set_parse_action(
+        lambda: -np.inf
+    )
+)
 _DATA_ENTRY <<= _FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | _NUMBER | _SWITCH | _TOKEN
 
 _DATA <<= (


Test patch:
diff --git a/tests/test_files/test_parsing.py b/tests/test_files/test_parsing.py
index 7aa692e..0633ee2 100644
--- a/tests/test_files/test_parsing.py
+++ b/tests/test_files/test_parsing.py
@@ -10,6 +10,7 @@ def test_parse_value() -> None:
     assert Parsed(b"yes")[()] is True
     assert Parsed(b"no")[()] is False
     assert Parsed(b"word")[()] == "word"
+    assert Parsed(b"inference")[()] == "inference"
     assert Parsed(b"word word")[()] == ("word", "word")
     assert Parsed(b'"a string"')[()] == '"a string"'
     assert Parsed(b"uniform 1")[()] == 1