diff --git a/README.md b/README.md index 406036ce0b845b967afe99a83ba6bf1f189c6783..d0ed432feda0f682a8c465856155d60c83d7fcac 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ This library converts D-SI unit strings to Latex. ## Usage -Create a D-SI tree with the D-SI unit string as an argument: `tree = dsiParser.dsiTree("\mega\hertz")`. +Set up a parser (with optional arguments for default latex values): `parser = dsiParser(latexDefaultWrapper='$', latexDefaultPrefix=r'\mathrm{Prefix}', latexDefaultSuffix=r'\mathrm{Suffix}')`. +Create a D-SI tree with the D-SI unit string as an argument: `tree = parser.parse("\mega\hertz")`. - To validate the D-SI unit string, inspect the `valid` property: `print(tree.valid)` - To see any warning messages generated while parsing the string, inspect the `warnings` property: `print(tree.warnings)` diff --git a/src/dsiParser.py b/src/dsiParser.py index d4eb5f6a3418d3533b8eb36319e602df78807aba..313c8398e8a51272ce05edd97efb851c6a440077 100644 --- a/src/dsiParser.py +++ b/src/dsiParser.py @@ -2,10 +2,36 @@ from dataclasses import dataclass import re import warnings -class dsiTree: +class dsiParser: + """Parser to parse D-SI unit string into a tree + """ + def __init__(self, latexDefaultWrapper='$$', latexDefaultPrefix='', latexDefaultSuffix=''): + """ + Args: + latexDefaultWrapper (str, optional): String to be added both in the beginning and the end of the LaTeX string. Defaults to '$$'. + latexDefaultPrefix (str, optional): String to be added in the beginning of the LaTeX string, after the wrapper. Defaults to ''. + latexDefaultSuffix (str, optional): String to be added in the end of the LaTeX string, before the wrapper. Defaults to ''. + """ + self.latexDefaultWrapper = latexDefaultWrapper + self.latexDefaultPrefix = latexDefaultPrefix + self.latexDefaultSuffix = latexDefaultSuffix + + def parse(self, dsiString: str): + """Parse D-SI unit string into tree structure + + Args: + dsiString (str): The D-SI string to be parsed + + Returns: + _dsiTree: The generated tree + """ + return _dsiTree(dsiString, self.latexDefaultWrapper, self.latexDefaultPrefix, self.latexDefaultSuffix) + + +class _dsiTree: """D-SI representation in tree form, also includes validity check and warnings about D-SI string """ - def __init__(self, dsiString: str): + def __init__(self, dsiString: str, latexDefaultWrapper='$$', latexDefaultPrefix='', latexDefaultSuffix=''): """ Args: dsiString (str): the D-SI unit string to be parsed @@ -13,18 +39,28 @@ class dsiTree: self.dsiString = dsiString (self.tree, self.warnings) = _parseDsi(dsiString) self.valid = len(self.warnings) == 0 + self._latexDefaultWrapper = latexDefaultWrapper + self._latexDefaultPrefix = latexDefaultPrefix + self._latexDefaultSuffix = latexDefaultSuffix - def toLatex(self, wrapper='$$', prefix='', suffix=''): + + def toLatex(self, wrapper=None, prefix=None, suffix=None): """converts D-SI unit string to LaTeX Args: - wrapper (str, optional): String to be added both in the beginning and the end of the LaTeX string. Defaults to '$$'. - prefix (str, optional): String to be added in the beginning of the LaTeX string, after the wrapper. Defaults to ''. - suffix (str, optional): String to be added in the end of the LaTeX string, before the wrapper. Defaults to ''. + wrapper (str, optional): String to be added both in the beginning and the end of the LaTeX string. Defaults to the value set in the parser object. + prefix (str, optional): String to be added in the beginning of the LaTeX string, after the wrapper. Defaults to the value set in the parser object. + suffix (str, optional): String to be added in the end of the LaTeX string, before the wrapper. Defaults to the value set in the parser object. Returns: str: the corresponding LaTeX code """ + + # If no wrapper/prefix/suffix was given, set to the parser's default + wrapper = self._latexDefaultWrapper if wrapper == None else wrapper + prefix = self._latexDefaultPrefix if prefix == None else prefix + suffix = self._latexDefaultSuffix if suffix == None else suffix + if self.tree == []: if len(prefix)+len(suffix) > 0: return wrapper+prefix+suffix+wrapper diff --git a/tests/test_main.py b/tests/test_main.py index d8b575a4d562bcfc3a96c8751b27039f3094e2dd..6c2988a6b3a0db863ba88f5374f6fe7363e77279 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,16 +1,18 @@ import pytest -from dsiParser import dsiTree, _node +from dsiParser import dsiParser, _node + +p = dsiParser() def test_baseCase(): # Most basic case: one unit without prefix or exponent - tree = dsiTree(r'\metre') + tree = p.parse(r'\metre') assert tree.tree == [[_node('','metre','')]] assert tree.toLatex() == r'$$\mathrm{m}$$' assert tree.valid assert tree.warnings == [] # One longer unit - tree = dsiTree(r'\milli\metre\tothe{0.5}\kilogram\per\mega\second\tothe{3}\ampere\tothe{-2}') + tree = p.parse(r'\milli\metre\tothe{0.5}\kilogram\per\mega\second\tothe{3}\ampere\tothe{-2}') assert tree.toLatex() == r'$$\frac{\sqrt{\mathrm{m}\mathrm{m}}\,\mathrm{kg}}{\mathrm{M}\mathrm{s}^{3}\,\mathrm{A}^{-2}}$$' assert tree.valid assert tree.warnings == [] @@ -19,7 +21,7 @@ def test_baseCase(): def test_robustness(): # Unknown unit with pytest.warns(RuntimeWarning, match='The identifier \"foo\" does not match any D-SI units!'): - tree = dsiTree(r'\foo') + tree = p.parse(r'\foo') assert tree.toLatex() == r'$${\color{red}\mathrm{foo}}$$' assert not tree.valid assert len(tree.warnings) == 1 @@ -27,7 +29,7 @@ def test_robustness(): # Unknown string in the middle of input with pytest.warns(RuntimeWarning, match='The identifier \"mini\" does not match any D-SI units!'): - tree = dsiTree(r'\kilo\metre\per\mini\second') + tree = p.parse(r'\kilo\metre\per\mini\second') print(tree.toLatex()) assert tree.toLatex() == r'$$\frac{\mathrm{k}\mathrm{m}}{{\color{red}\mathrm{mini}}\,\mathrm{s}}$$' assert not tree.valid @@ -36,7 +38,7 @@ def test_robustness(): # Base unit missing with pytest.warns(RuntimeWarning, match=r'This D-SI unit seems to be missing the base unit! \"\\milli\\tothe\{2\}\"'): - tree = dsiTree(r'\milli\tothe{2}') + tree = p.parse(r'\milli\tothe{2}') print(tree.toLatex()) assert tree.toLatex() == r'$${\color{red}\mathrm{m}{\color{red}\mathrm{}}^{2}}$$' assert not tree.valid @@ -46,42 +48,42 @@ def test_robustness(): def test_power(): # power - powerTree = dsiTree(r'\metre\tothe{2}') + powerTree = p.parse(r'\metre\tothe{2}') assert powerTree.tree == [[_node('','metre','2')]] assert powerTree.toLatex() == r'$$\mathrm{m}^{2}$$' assert powerTree.valid assert powerTree.warnings == [] - assert dsiTree(r'\metre\tothe{0.5}').toLatex() == r'$$\sqrt{\mathrm{m}}$$' - assert dsiTree(r'\metre\tothe{-2}').toLatex() == r'$$\mathrm{m}^{-2}$$' - assert dsiTree(r'\metre\tothe{1337}').toLatex() == r'$$\mathrm{m}^{1337}$$' + assert p.parse(r'\metre\tothe{0.5}').toLatex() == r'$$\sqrt{\mathrm{m}}$$' + assert p.parse(r'\metre\tothe{-2}').toLatex() == r'$$\mathrm{m}^{-2}$$' + assert p.parse(r'\metre\tothe{1337}').toLatex() == r'$$\mathrm{m}^{1337}$$' # Non-numerical power with pytest.warns(RuntimeWarning, match='The exponent \"foo\" is not a number!'): - assert dsiTree(r'\metre\tothe{foo}').toLatex() == r'$$\mathrm{m}^{{\color{red}\mathrm{foo}}}$$' + assert p.parse(r'\metre\tothe{foo}').toLatex() == r'$$\mathrm{m}^{{\color{red}\mathrm{foo}}}$$' def test_prefix(): - # prefix - prefixTree = dsiTree(r'\kilo\metre') + # D-SI prefix + prefixTree = p.parse(r'\kilo\metre') assert prefixTree.tree == [[_node('kilo','metre','')]] assert prefixTree.toLatex() == r'$$\mathrm{k}\mathrm{m}$$' assert prefixTree.valid def test_node(): # full node - fullNodeTree = dsiTree(r'\kilo\metre\tothe{2}') + fullNodeTree = p.parse(r'\kilo\metre\tothe{2}') assert fullNodeTree.tree == [[_node('kilo','metre','2')]] assert fullNodeTree.toLatex() == r'$$\mathrm{k}\mathrm{m}^{2}$$' assert fullNodeTree.valid def test_fraction(): - fractionTree = dsiTree(r'\mega\metre\per\second\tothe{2}') + fractionTree = p.parse(r'\mega\metre\per\second\tothe{2}') assert fractionTree.tree == [[_node('mega','metre','')],[_node('','second','2')]] assert fractionTree.toLatex() == r'$$\frac{\mathrm{M}\mathrm{m}}{\mathrm{s}^{2}}$$' assert fractionTree.valid # double fraction with pytest.warns(RuntimeWarning, match=r'The dsi string contains more than one \\per, does not match specs! Given string: \\metre\\per\\metre\\per\\metre'): - tree = dsiTree(r'\metre\per\metre\per\metre') + tree = p.parse(r'\metre\per\metre\per\metre') assert tree.toLatex() == r'$$\mathrm{m}{\color{red}/}\mathrm{m}{\color{red}/}\mathrm{m}$$' assert not tree.valid assert len(tree.warnings) == 1 @@ -89,8 +91,12 @@ def test_fraction(): def test_empty(): with pytest.warns(RuntimeWarning, match='Given D-SI string is empty!'): - assert dsiTree('').toLatex() == '' + assert p.parse('').toLatex() == '' def test_wrappers(): - assert dsiTree(r'\metre').toLatex(wrapper='') == r'\mathrm{m}' - assert dsiTree(r'\metre').toLatex(wrapper='$',prefix=r'\mathrm{Unit: }',suffix=r'\mathrm{(generated from D-SI string)}') == r'$\mathrm{Unit: }\mathrm{m}\mathrm{(generated from D-SI string)}$' + assert p.parse(r'\metre').toLatex(wrapper='') == r'\mathrm{m}' + assert p.parse(r'\metre').toLatex(wrapper='$', prefix=r'\mathrm{Unit: }', suffix=r'\mathrm{(generated from D-SI string)}') == r'$\mathrm{Unit: }\mathrm{m}\mathrm{(generated from D-SI string)}$' + parserWrapper = dsiParser(latexDefaultWrapper="&") + assert parserWrapper.parse(r'\metre').toLatex() == r'&\mathrm{m}&' + parserFull = dsiParser(latexDefaultWrapper='@', latexDefaultPrefix=r'\mathrm{Prefix}', latexDefaultSuffix=r'\mathrm{Suffix}') + assert parserFull.parse(r'\metre').toLatex() == r'@\mathrm{Prefix}\mathrm{m}\mathrm{Suffix}@'