Skip to content

Commit

Permalink
Switch to custom dictionary parser
Browse files Browse the repository at this point in the history
  • Loading branch information
gerlero committed Mar 26, 2024
1 parent 57d70d7 commit d3da0de
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 40 deletions.
22 changes: 16 additions & 6 deletions foamlib/_dictionaries/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
cast,
)
from ._values import FoamDimensionSet, FoamDimensioned, FoamValue
from ._parsing import parse
from ._parsing import DICTIONARY, VALUE
from ._serialization import serialize
from .._subprocesses import run_process, CalledProcessError

Expand Down Expand Up @@ -67,13 +67,17 @@ def _cmd(self, args: Sequence[str], *, key: Optional[str] = None) -> str:
) from None

def __getitem__(self, key: str) -> Union[FoamValue, "FoamDictionary"]:
value = self._cmd(["-value"], key=key)
contents = self._file.path.read_text()
value = DICTIONARY.parse_string(contents, parse_all=True).as_dict()

if value.startswith("{"):
assert value.endswith("}")
for key in [*self._keywords, key]:
value = value[key]

if isinstance(value, dict):
return FoamDictionary(self._file, [*self._keywords, key])
else:
return parse(value)
start, end = value
return VALUE.parse_string(contents[start:end], parse_all=True).as_list()[0]

def _setitem(
self,
Expand All @@ -88,6 +92,7 @@ def _setitem(
elif isinstance(value, Mapping):
self._cmd(["-set", "{}"], key=key)
subdict = self[key]
print(subdict)
assert isinstance(subdict, FoamDictionary)
for k, v in value.items():
subdict[k] = v
Expand All @@ -114,7 +119,12 @@ def __delitem__(self, key: str) -> None:
self._cmd(["-remove"], key=key)

def __iter__(self) -> Iterator[str]:
yield from self._cmd(["-keywords"]).splitlines()
value = DICTIONARY.parse_file(self._file.path, parse_all=True).as_dict()

for key in self._keywords:
value = value[key]

yield from value

def __len__(self) -> int:
return len(list(iter(self)))
Expand Down
48 changes: 38 additions & 10 deletions foamlib/_dictionaries/_parsing.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
from typing import cast

from pyparsing import (
Dict,
Forward,
Group,
Keyword,
LineEnd,
Literal,
Located,
Opt,
QuotedString,
Word,
c_style_comment,
common,
cpp_style_comment,
printables,
identchars,
identbodychars,
)

from ._values import FoamValue, FoamDimensionSet, FoamDimensioned
from ._values import FoamDimensionSet, FoamDimensioned


_YES = Keyword("yes").set_parse_action(lambda: True)
_NO = Keyword("no").set_parse_action(lambda: False)
_DIMENSIONS = (
Literal("[").suppress() + common.number * 7 + Literal("]").suppress()
).set_parse_action(lambda tks: FoamDimensionSet(*tks))
_TOKEN = common.identifier | QuotedString('"', unquote_results=False)
_TOKEN = QuotedString('"', unquote_results=False) | Word(
identchars + "$", identbodychars
)
_ITEM = Forward()
_LIST = Opt(
Literal("List") + Literal("<") + common.identifier + Literal(">")
Expand All @@ -45,12 +52,33 @@
_FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | common.number | _YES | _NO | _TOKEN
)

_TOKENS = (QuotedString('"', unquote_results=False) | Word(printables))[
1, ...
].set_parse_action(lambda tks: " ".join(tks))
_TOKENS = (
QuotedString('"', unquote_results=False)
| Word(printables.replace(";", "").replace("{", "").replace("}", ""))
)[2, ...].set_parse_action(lambda tks: " ".join(tks))

_VALUE = _ITEM ^ _TOKENS
VALUE = (_ITEM ^ _TOKENS).ignore(c_style_comment).ignore(cpp_style_comment)


def parse(value: str) -> FoamValue:
return cast(FoamValue, _VALUE.parse_string(value, parse_all=True).as_list()[0])
_UNPARSED_VALUE = (
QuotedString('"', unquote_results=False)
| Word(printables.replace(";", "").replace("{", "").replace("}", ""))
)[...]
_KEYWORD = QuotedString('"', unquote_results=False) | Word(
identchars + "$(,.)", identbodychars + "$(,.)"
)
DICTIONARY = Forward()
_ENTRY = _KEYWORD + (
(
Located(_UNPARSED_VALUE).set_parse_action(lambda tks: (tks[0], tks[2]))
+ Literal(";").suppress()
)
| (Literal("{").suppress() + DICTIONARY + Literal("}").suppress())
)
DICTIONARY <<= (
Dict(Group(_ENTRY)[...])
.set_parse_action(lambda tks: {} if not tks else tks)
.ignore(c_style_comment)
.ignore(cpp_style_comment)
.ignore(Literal("#include") + ... + LineEnd()) # type: ignore
)
60 changes: 36 additions & 24 deletions tests/test_dictionaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,52 @@
import numpy as np

from foamlib import *
from foamlib._dictionaries._parsing import parse


def test_parse() -> None:
assert parse("1") == 1
assert parse("1.0") == 1.0
assert parse("1.0e-3") == 1.0e-3
assert parse("yes") is True
assert parse("no") is False
assert parse("word") == "word"
assert parse("word word") == "word word"
assert parse('"a string"') == '"a string"'
assert parse("uniform 1") == 1
assert parse("uniform 1.0") == 1.0
assert parse("uniform 1.0e-3") == 1.0e-3
assert parse("(1.0 2.0 3.0)") == [1.0, 2.0, 3.0]
assert parse("nonuniform List<scalar> 2(1 2)") == [1, 2]
assert parse("3(1 2 3)") == [1, 2, 3]
assert parse("2((1 2 3) (4 5 6))") == [[1, 2, 3], [4, 5, 6]]
assert parse("nonuniform List<vector> 2((1 2 3) (4 5 6))") == [
from foamlib._dictionaries._parsing import VALUE


def test_parse_value() -> None:
assert VALUE.parse_string("1").as_list()[0] == 1
assert VALUE.parse_string("1.0").as_list()[0] == 1.0
assert VALUE.parse_string("1.0e-3").as_list()[0] == 1.0e-3
assert VALUE.parse_string("yes").as_list()[0] is True
assert VALUE.parse_string("no").as_list()[0] is False
assert VALUE.parse_string("word").as_list()[0] == "word"
assert VALUE.parse_string("word word").as_list()[0] == "word word"
assert VALUE.parse_string('"a string"').as_list()[0] == '"a string"'
assert VALUE.parse_string("uniform 1").as_list()[0] == 1
assert VALUE.parse_string("uniform 1.0").as_list()[0] == 1.0
assert VALUE.parse_string("uniform 1.0e-3").as_list()[0] == 1.0e-3
assert VALUE.parse_string("(1.0 2.0 3.0)").as_list()[0] == [1.0, 2.0, 3.0]
assert VALUE.parse_string("uniform (1 2 3)").as_list()[0] == [1, 2, 3]
assert VALUE.parse_string("nonuniform List<scalar> 2(1 2)").as_list()[0] == [1, 2]
assert VALUE.parse_string("3(1 2 3)").as_list()[0] == [1, 2, 3]
assert VALUE.parse_string("2((1 2 3) (4 5 6))").as_list()[0] == [
[1, 2, 3],
[4, 5, 6],
]
assert parse("[1 1 -2 0 0 0 0]") == FoamDimensionSet(mass=1, length=1, time=-2)
assert parse("g [1 1 -2 0 0 0 0] (0 0 -9.81)") == FoamDimensioned(
assert VALUE.parse_string("nonuniform List<vector> 2((1 2 3) (4 5 6))").as_list()[
0
] == [
[1, 2, 3],
[4, 5, 6],
]
assert VALUE.parse_string("[1 1 -2 0 0 0 0]").as_list()[0] == FoamDimensionSet(
mass=1, length=1, time=-2
)
assert VALUE.parse_string("g [1 1 -2 0 0 0 0] (0 0 -9.81)").as_list()[
0
] == FoamDimensioned(
name="g",
dimensions=FoamDimensionSet(mass=1, length=1, time=-2),
value=[0, 0, -9.81],
)
assert parse("[1 1 -2 0 0 0 0] 9.81") == FoamDimensioned(
assert VALUE.parse_string("[1 1 -2 0 0 0 0] 9.81").as_list()[0] == FoamDimensioned(
dimensions=FoamDimensionSet(mass=1, length=1, time=-2), value=9.81
)
assert (
parse("hex (0 1 2 3 4 5 6 7) (1 1 1) simpleGrading (1 1 1)")
VALUE.parse_string(
"hex (0 1 2 3 4 5 6 7) (1 1 1) simpleGrading (1 1 1)"
).as_list()[0]
== "hex (0 1 2 3 4 5 6 7) (1 1 1) simpleGrading (1 1 1)"
)

Expand Down

0 comments on commit d3da0de

Please sign in to comment.