summaryrefslogtreecommitdiff
path: root/pint/delegates/txt_defparser/system.py
diff options
context:
space:
mode:
Diffstat (limited to 'pint/delegates/txt_defparser/system.py')
-rw-r--r--pint/delegates/txt_defparser/system.py110
1 files changed, 110 insertions, 0 deletions
diff --git a/pint/delegates/txt_defparser/system.py b/pint/delegates/txt_defparser/system.py
new file mode 100644
index 0000000..b21fd7a
--- /dev/null
+++ b/pint/delegates/txt_defparser/system.py
@@ -0,0 +1,110 @@
+"""
+ pint.delegates.txt_defparser.system
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: 2022 by Pint Authors, see AUTHORS for more details.
+ :license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import annotations
+
+import re
+import typing as ty
+from dataclasses import dataclass
+
+from ..._vendor import flexparser as fp
+from ...facets.system import definitions
+from . import block, common, plain
+
+
+@dataclass(frozen=True)
+class BaseUnitRule(fp.ParsedStatement, definitions.BaseUnitRule):
+ @classmethod
+ def from_string(cls, s: str) -> fp.FromString[BaseUnitRule]:
+ if ":" not in s:
+ return cls(s.strip())
+ parts = [p.strip() for p in s.split(":")]
+ if len(parts) != 2:
+ return common.DefinitionSyntaxError(
+ f"Exactly two terms expected for rule, not {len(parts)} (`{s}`)"
+ )
+ return cls(*parts)
+
+
+@dataclass(frozen=True)
+class BeginSystem(fp.ParsedStatement):
+ """Being of a system directive.
+
+ @system <name> [using <group 1>, ..., <group N>]
+ """
+
+ #: Regex to match the header parts of a context.
+ _header_re = re.compile(r"@system\s+(?P<name>\w+)\s*(using\s(?P<used_groups>.*))*")
+
+ name: str
+ using_group_names: ty.Tuple[str, ...]
+
+ @classmethod
+ def from_string(cls, s: str) -> fp.FromString[BeginSystem]:
+ if not s.startswith("@system"):
+ return None
+
+ r = cls._header_re.search(s)
+
+ if r is None:
+ raise ValueError("Invalid System header syntax '%s'" % s)
+
+ name = r.groupdict()["name"].strip()
+ groups = r.groupdict()["used_groups"]
+
+ # If the systems has no group, it automatically uses the root group.
+ if groups:
+ group_names = tuple(a.strip() for a in groups.split(","))
+ else:
+ group_names = ("root",)
+
+ return cls(name, group_names)
+
+
+@dataclass(frozen=True)
+class SystemDefinition(block.DirectiveBlock):
+ """Definition of a System:
+
+ @system <name> [using <group 1>, ..., <group N>]
+ <rule 1>
+ ...
+ <rule N>
+ @end
+
+ See Rule and Comment for more parsing related information.
+
+ The syntax for the rule is:
+
+ new_unit_name : old_unit_name
+
+ where:
+ - old_unit_name: a root unit part which is going to be removed from the system.
+ - new_unit_name: a non root unit which is going to replace the old_unit.
+
+ If the new_unit_name and the old_unit_name, the later and the colon can be omitted.
+ """
+
+ opening: fp.Single[BeginSystem]
+ body: fp.Multi[ty.Union[plain.CommentDefinition, BaseUnitRule]]
+
+ def derive_definition(self):
+ return definitions.SystemDefinition(
+ self.name, self.using_group_names, self.rules
+ )
+
+ @property
+ def name(self):
+ return self.opening.name
+
+ @property
+ def using_group_names(self):
+ return self.opening.using_group_names
+
+ @property
+ def rules(self):
+ return tuple(el for el in self.body if isinstance(el, BaseUnitRule))