Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to custom dictionary parser #37

Merged
merged 1 commit into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading