From 75edf1dff54d499e75c94330193dd66660a92eb1 Mon Sep 17 00:00:00 2001
From: Vanessa Stehr <vanessa.stehr@ptb.de>
Date: Wed, 20 Sep 2023 13:32:34 +0200
Subject: [PATCH] Implement default values, make the parser a class

---
 README.md          |  3 ++-
 src/dsiParser.py   | 48 ++++++++++++++++++++++++++++++++++++++++------
 tests/test_main.py | 44 ++++++++++++++++++++++++------------------
 3 files changed, 69 insertions(+), 26 deletions(-)

diff --git a/README.md b/README.md
index 406036c..d0ed432 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 d4eb5f6..313c839 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 d8b575a..6c2988a 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}@'
-- 
GitLab