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

support QUDT #1964

Closed
wants to merge 3 commits into from
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
5 changes: 5 additions & 0 deletions mytest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from __future__ import annotations

import pint

ureg = pint.UnitRegistry(r"E://test.txt", parser="qudt")
5 changes: 5 additions & 0 deletions origtest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from __future__ import annotations

import pint

ureg = pint.UnitRegistry()
10 changes: 8 additions & 2 deletions pint/delegates/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@
"""
from __future__ import annotations

from . import txt_defparser
from . import qudt_parser, txt_defparser
from .base_defparser import ParserConfig, build_disk_cache_class
from .formatter import Formatter

__all__ = ["txt_defparser", "ParserConfig", "build_disk_cache_class", "Formatter"]
__all__ = [
"txt_defparser",
"qudt_parser",
"ParserConfig",
"build_disk_cache_class",
"Formatter",
]
17 changes: 17 additions & 0 deletions pint/delegates/qudt_parser/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""
pint.delegates.qudt_parser
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Parser for QUDT files
https://www.qudt.org/pages/HomePage.html

:copyright: 2024 by Pint Authors, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
from __future__ import annotations

from .qudtparser import QudtParser

__all__ = [
"QudtParser",
]
75 changes: 75 additions & 0 deletions pint/delegates/qudt_parser/block.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""
pint.delegates.txt_defparser.block
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Classes for Pint Blocks, which are defined by:

@<block name>
<content>
@end

:copyright: 2022 by Pint Authors, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""


from __future__ import annotations

from dataclasses import dataclass
from typing import Generic, TypeVar

import flexparser as fp

from ..base_defparser import ParserConfig, PintParsedStatement


@dataclass(frozen=True)
class BeginDirectiveBlock(fp.ParsedStatement):
"""An BeginDirectiveBlock is simply a "." statement.
example statement: 'unit:A'
example _object_type: 'unit'

"""

_object_type = str

@classmethod
def from_string(cls, s):
if s[: len(cls._object_type)] == cls._object_type:
obj = cls()
obj.name = s[len(cls._object_type) + 1 :]
return obj

return None


@dataclass(frozen=True)
class EndDirectiveBlock(PintParsedStatement):
"""An EndDirectiveBlock is simply a "." statement."""

@classmethod
def from_string(cls, s: str) -> fp.NullableParsedResult[EndDirectiveBlock]:
if s == ".":
return cls()
return None


OPST = TypeVar("OPST", bound="PintParsedStatement") # Opening Parsed Statement Type
IPST = TypeVar("IPST", bound="PintParsedStatement") # ? Inner Parsed Statement Type

DefT = TypeVar("DefT") # Definition? Type


@dataclass(frozen=True)
class DirectiveBlock(
Generic[DefT, OPST, IPST], fp.Block[OPST, IPST, EndDirectiveBlock, ParserConfig]
):
"""Directive blocks have beginning statement Begining with a @ character.
and ending with a "@end" (captured using a EndDirectiveBlock).

Subclass this class for convenience.
"""

# is this needed below?
def derive_definition(self) -> DefT:
...
180 changes: 180 additions & 0 deletions pint/delegates/qudt_parser/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
"""
pint.delegates.txt_defparser.common
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Definitions for parsing an Import Statement

Also DefinitionSyntaxError

:copyright: 2022 by Pint Authors, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""

from __future__ import annotations

import re
from dataclasses import dataclass, field

import flexparser as fp

from ... import errors
from ...util import UnitsContainer
from ..base_defparser import ParserConfig
from . import block


@dataclass(frozen=True)
class DefinitionSyntaxError(errors.DefinitionSyntaxError, fp.ParsingError):
"""A syntax error was found in a definition. Combines:

DefinitionSyntaxError: which provides a message placeholder.
fp.ParsingError: which provides raw text, and start and end column and row

and an extra location attribute in which the filename or reseource is stored.
"""

location: str = field(init=False, default="")

def __str__(self) -> str:
msg = (
self.msg + "\n " + (self.format_position or "") + " " + (self.raw or "")
)
if self.location:
msg += "\n " + self.location
return msg

def set_location(self, value: str) -> None:
super().__setattr__("location", value)


@dataclass(frozen=True)
class ImportDefinition(fp.IncludeStatement[ParserConfig]):
value: str

@property
def target(self) -> str:
return self.value

@classmethod
def from_string(cls, s: str) -> fp.NullableParsedResult[ImportDefinition]:
if s.startswith("@import"):
return ImportDefinition(s[len("@import") :].strip())
return None


###########################################


@dataclass(frozen=True)
class Attribute(fp.ParsedStatement):
"""Parses the following `qudt:applicableSystem sou:CGS-EMU ;`"""

parent: str
attr: str
value: str

@classmethod
def from_string(cls, s):
if ":" not in s and ";" not in s:
# This means: I do not know how to parse it
# try with another ParsedStatement class.
return None
s = s[:-1].strip()
parent, s = s.split(":", 1)
if " " not in s:
attr, value = s, ""
else:
attr, value = s.split(" ", 1)

# if not str.isidentifier(lhs):
# return InvalidIdentifier(lhs)

return cls(parent, attr, value)


class BeginObject(fp.ParsedStatement):
_object_type = str

@classmethod
def from_string(cls, s):
if s[: len(cls._object_type)] == cls._object_type:
return cls()

return None


class BeginHeader(fp.ParsedStatement):
@classmethod
def from_string(cls, s):
if s[:9] == "# baseURI":
return cls()

return None


class End(fp.ParsedStatement):
@classmethod
def from_string(cls, s):
if s == ".":
return cls()

return None


class HeaderLine(fp.ParsedStatement):
# couldnt get BOF to work in place of this in HeaderBlock
def from_string(cls, s):
return cls()


@dataclass(frozen=True)
class HeaderBlock(fp.Block[BeginHeader, HeaderLine, End, ParserConfig]):
pass


DIMENSIONS = "substance, current, length, luminousity, mass, temperature, time, dimensionless".split(
", "
)
BASE_UNITS = "mol, A, m, cd, kg, K, s".split(", ")


class BeginVoag(block.BeginDirectiveBlock):
_object_type = "voag"


@dataclass(frozen=True)
class VoagDefinitionBlock(
fp.Block[BeginVoag, Attribute, block.EndDirectiveBlock, ParserConfig]
):
pass


class BeginVaem(block.BeginDirectiveBlock):
_object_type = "vaem"


@dataclass(frozen=True)
class VaemDefinitionBlock(
fp.Block[BeginVaem, Attribute, block.EndDirectiveBlock, ParserConfig]
):
pass


class BeginHttp(block.BeginDirectiveBlock):
_object_type = "<http"


@dataclass(frozen=True)
class HttpDefinitionBlock(
fp.Block[BeginHttp, Attribute, block.EndDirectiveBlock, ParserConfig]
):
pass


def make_units_container(dimensionvector: str) -> UnitsContainer:
# use regex to split the dimension vector 'A0E1L0I0M0H0T0D0' into a dictionary of units
if ":" in dimensionvector:
dimensionvector = dimensionvector.split(":")[1]
dimensionvector = re.sub(r"[a-zA-Z]", r" ", dimensionvector).split()
units = {unit: int(exponent) for unit, exponent in zip(BASE_UNITS, dimensionvector)}
return UnitsContainer(units)
Loading
Loading