Skip to content
Closed
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
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[build-system]
requires = ["uv_build>=0.9,<0.10"]
build-backend = "uv_build"
requires = ["setuptools>=61", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "foamlib"
Expand Down Expand Up @@ -119,3 +119,6 @@ extend-ignore = [

[tool.ruff.lint.pydocstyle]
convention = "pep257"

[tool.setuptools.packages.find]
where = ["src"]
13 changes: 13 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Setup script for building C extension modules."""

from setuptools import Extension, setup

# Define the C extension module
skip_ext = Extension(
"foamlib._files._parsing._skip_ext",
sources=["src/foamlib/_files/_parsing/_skip_ext.c"],
)

setup(
ext_modules=[skip_ext],
)
94 changes: 9 additions & 85 deletions src/foamlib/_files/_parsing/_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
SubDict,
Tensor,
)
from ._skip_ext import ParseError as _CParseError
from ._skip_ext import _parse_number as _parse_number_c
from ._skip_ext import _skip
from .exceptions import FoamFileDecodeError

_DType = TypeVar("_DType", float, int)
Expand All @@ -46,21 +49,6 @@
for c in b"0123456789._<>#$:+-*/|^%&=!":
_IS_TOKEN_CONTINUATION[c] = True

_IS_WHITESPACE = [False] * 256
for c in b" \n\t\r\f\v":
_IS_WHITESPACE[c] = True

_IS_WHITESPACE_NO_NEWLINE = _IS_WHITESPACE.copy()
_IS_WHITESPACE_NO_NEWLINE[ord(b"\n")] = False

_IS_POSSIBLE_FLOAT = [False] * 256
for c in b"0123456789.-+eEinfnatyINFNATY":
_IS_POSSIBLE_FLOAT[c] = True

_IS_POSSIBLE_INTEGER = [False] * 256
for c in b"0123456789-+":
_IS_POSSIBLE_INTEGER[c] = True

_COMMENTS = re.compile(rb"(?:(?:/\*(?:[^*]|\*(?!/))*\*/)|(?://(?:\\\n|[^\n])*))+")
_SKIP = re.compile(rb"(?:\s+|" + _COMMENTS.pattern + rb")+")
_POSSIBLE_FLOAT = re.compile(rb"[0-9.\-+einfatyEINFATY]+", re.ASCII)
Expand All @@ -78,52 +66,6 @@ def make_fatal(self) -> FoamFileDecodeError:
return FoamFileDecodeError(self._contents, self.pos, expected=self._expected)


def _skip(
contents: bytes | bytearray,
pos: int,
*,
newline_ok: bool = True,
) -> int:
is_whitespace = _IS_WHITESPACE if newline_ok else _IS_WHITESPACE_NO_NEWLINE

with contextlib.suppress(IndexError):
while True:
while is_whitespace[contents[pos]]:
pos += 1

next1 = contents[pos]
next2 = contents[pos + 1]

if next1 == ord("/") and next2 == ord("/"):
pos += 2
while True:
if contents[pos] == ord("\n"):
if newline_ok:
pos += 1
break
if contents[pos] == ord("\\"):
with contextlib.suppress(IndexError):
if contents[pos + 1] == ord("\n"):
pos += 1
pos += 1
continue
pos += 1
continue

if next1 == ord("/") and next2 == ord("*"):
if (pos := contents.find(b"*/", pos + 2)) == -1:
raise FoamFileDecodeError(
contents,
len(contents),
expected="*/",
)
pos += 2
continue
break

return pos


def _expect(contents: bytes | bytearray, pos: int, expected: bytes | bytearray) -> int:
length = len(expected)
if contents[pos : pos + length] != expected:
Expand Down Expand Up @@ -216,31 +158,13 @@ def _parse_number(
*,
target: type[int] | type[float] | type[int | float] = int | float,
) -> tuple[int | float, int]:
is_numeric = _IS_POSSIBLE_INTEGER if target is int else _IS_POSSIBLE_FLOAT
end = pos
with contextlib.suppress(IndexError):
while is_numeric[contents[end]]:
end += 1

if _IS_TOKEN_CONTINUATION[contents[end]]:
raise ParseError(contents, pos, expected="number")

if pos == end:
raise ParseError(contents, pos, expected="number")

chars = contents[pos:end]
if target is not float:
try:
return int(chars), end
except ValueError as e:
if target is int:
raise ParseError(contents, pos, expected="integer") from e
"""Parse a number from contents and convert C ParseError to Python ParseError."""
try:
return float(chars), end
except ValueError as e:
if target is float:
raise ParseError(contents, pos, expected="float") from e
raise ParseError(contents, pos, expected="number") from e
return _parse_number_c(contents, pos, target=target)
except _CParseError as e:
# Convert C ParseError to Python ParseError
# The C exception has attributes _contents, pos, and _expected
raise ParseError(e._contents, e.pos, expected=e._expected) from None


class _ASCIINumericListParser(Generic[_DType, _ElShape]):
Expand Down
Loading
Loading