Repository: sensein/cmixf

Issue:
fails on units like "ms"
Hey @satra thanks for such a clean package!

1. I installed the dev version as described in the README
1. I ran `cmixf` from the CLI to start the program
1. I entered `1ms` which should be 1 millisecond

Traceback:

```
cmixf > 1ms
type='REAL', value='1'
type='UNITC', value='m'
type='UNITC', value='s'
sly: Syntax error at line 1, token=UNITC
Traceback (most recent call last):
  File "/home/stefanappelhoff/miniconda3/bin/cmixf", line 11, in <module>
    load_entry_point('cmixf', 'console_scripts', 'cmixf')()
  File "/home/stefanappelhoff/Desktop/bids/cmixf/cmixf/parser.py", line 218, in main
    print(parser.parse(lexer.tokenize(text)))
  File "/home/stefanappelhoff/miniconda3/lib/python3.7/site-packages/sly/yacc.py", line 2119, in parse
    tok = self.error(errtoken)
  File "/home/stefanappelhoff/Desktop/bids/cmixf/cmixf/parser.py", line 204, in error
    raise RuntimeError(f"Could not parse {t}")
RuntimeError: Could not parse Token(type='UNITC', value='s', lineno=1, index=2)

```

Similar errors for:
- 1uV
- 1µV
- 1kBq

(and probably more)

Comments:

Patch:
diff --git a/cmixf/parser.py b/cmixf/parser.py
index db429d4..516a614 100644
--- a/cmixf/parser.py
+++ b/cmixf/parser.py
@@ -1,18 +1,19 @@
 from sly import Lexer, Parser
 
-decimalsubmultiple = r"(a|c|d|f|m|n|p|u|y|z)"
+decimalsubmultiple = r"(a|c|d|f|m|n|p|(u|µ)|y|z)"
 decimalmultiple = r"(E|G|M|P|T|Y|Z|da|h|k)"
 binary = r"(Ei|Gi|Ki|Mi|Pi|Ti)"
-unitc = [r"[A-Z][A-Z][A-Z]|A|Bq|C",
-         r"dB|d|h|min|u",
-         r"F|Gy|Hz|H|J|K|Np|N|(Ohm|Ω)|Pa|Sv|S|T|V|W|Wb",
-         r"L|(oC|°C)|o|rad|sr",
-         r"bit|cd|eV|g|kat|lm|lx|mol|m|s|µ",
-         r"Bd|B|r|t"]
-unitb = r"(" + r'|'.join([unitc[0], unitc[2], unitc[4]]) + r")"
-unitn = r"(" + unitc[3] + r")"
-unitp = r"(" + unitc[5] + r")"
-units = r"(" + unitc[2] + r")"
+unitc = {"unitb1": r"[A-Z][A-Z][A-Z]|A|Bq|C",
+         "units": r"dB|d|h|min|u",
+         "unitb2": r"F|Gy|Hz|H|J|K|Np|N|(Ohm|Ω)|Pa|Sv|S|T|V|W|Wb",
+         "unitn": r"L|(oC|°C)|(°|o)|rad|sr",
+         "unitb3": r"bit|cd|eV|g|kat|lm|lx|mol|m|s",
+         "unitp": r"Bd|B|r|t"}
+unitb = r"(" + r'|'.join([unitc["unitb1"], unitc["unitb2"],
+                          unitc["unitb3"]]) + r")"
+unitn = r"(" + unitc["unitn"] + r")"
+unitp = r"(" + unitc["unitp"] + r")"
+units = r"(" + unitc["units"] + r")"
 
 
 class CMIXFLexer(Lexer):
@@ -43,11 +44,11 @@ class CMIXFLexer(Lexer):
     DOT = r"\."
     BIT = binary + r"bit"
     BYTE = binary + r"B"
-    UNITC = r'|'.join(unitc)
-    MULTIP = decimalmultiple + unitp
-    SUBMULTIN = decimalsubmultiple + unitn
     MULTIB = decimalmultiple + unitb
     SUBMULTIB = decimalsubmultiple + unitb
+    MULTIP = decimalmultiple + unitp
+    UNITC = r'|'.join([val for val in unitc.values()])
+    SUBMULTIN = decimalsubmultiple + unitn
 
     def error(self, t):
         raise ValueError("Line %d: Bad character %r" % (self.lineno, t.value[0]))


Test patch:
diff --git a/cmixf/tests/test_cmixf.py b/cmixf/tests/test_cmixf.py
index 4541261..e519fa7 100644
--- a/cmixf/tests/test_cmixf.py
+++ b/cmixf/tests/test_cmixf.py
@@ -91,7 +91,7 @@ def test_cmixf_examples(text):
 @pytest.mark.parametrize(
     "text",
     [
-        "1µ",
+        "1µm",
         "1°C",
         "1Ω",
     ],
@@ -100,3 +100,18 @@ def test_bids_chars(text):
     lexer = CMIXFLexer()
     parser = CMIXFParser()
     assert isinstance(parser.parse(lexer.tokenize(text)), str)
+
+
+@pytest.mark.parametrize(
+    "text",
+    [
+        "1µV",
+        "1uV",
+        "1ms",
+        "1kBq",
+    ],
+)
+def test_new_errors(text):
+    lexer = CMIXFLexer()
+    parser = CMIXFParser()
+    assert isinstance(parser.parse(lexer.tokenize(text)), str)