Repository: SalihTasdelen/paco

Issue:
Seperating Atomic Parsers from combinators.py
The Atomic Parsers = [Char, Literal, Regex, Tok]
These parsers are usually the starting points for grammars so they are not actually combinators but pieces for combinators, like Choice, Sequence and Many to use. This issue proposes a goal to move Atomic parsers to atomic.py and change imports accoringly.

Discussion:

Patch:
diff --git a/examples/recursive_array.py b/examples/recursive_array.py
index 55b56df..46d5e5a 100644
--- a/examples/recursive_array.py
+++ b/examples/recursive_array.py
@@ -1,4 +1,5 @@
-from paco.combinators import (Char, Lazy)
+from paco.combinators import Lazy
+from paco.atomic import Char
 from paco.miscellaneous import (letters, numbers, optSpace)
 
 lbra = Char('[').then(optSpace)
@@ -12,7 +13,10 @@ array.p = optSpace >> lbra >> element.sepby(comm) << rbra << optSpace
 def main():
     test_str = ' [ [1, 3, 5], [hi, howdy, bye], 42, [[1,2], [4,5]]] '
     print('Running on: ' + test_str)
-    print(array(test_str))
+    _, ar = array(test_str)
+    print('(ar[0][2] == {}) ->'.format(ar[0][2]), ar[0][2] == '5')
+    print('(ar[1][1] == {}) ->'.format(ar[1][1]), ar[1][1] == 'howdy')
+    print('(ar[3][1][0] == {}) ->'.format(ar[3][1][0]), ar[3][1][0] == '4')
 
 if __name__ == '__main__':
     exit(main())
\ No newline at end of file
diff --git a/src/paco/__init__.py b/src/paco/__init__.py
index 0a0fd75..b2db0a3 100644
--- a/src/paco/__init__.py
+++ b/src/paco/__init__.py
@@ -1,1 +1,1 @@
-from . import combinators, miscellaneous, lexer
+from . import combinators, atomic, miscellaneous, lexer
diff --git a/src/paco/atomic.py b/src/paco/atomic.py
new file mode 100644
index 0000000..2468c3d
--- /dev/null
+++ b/src/paco/atomic.py
@@ -0,0 +1,67 @@
+
+import re
+from .combinators import Parser, ParseError
+
+class Char(Parser):
+    def __init__(self, char : str) -> None:
+        super().__init__()
+        self.name = 'char(\'{}\')'.format(char)
+        self.char = char
+    
+    def run(self, pos : int, tar : str):
+        if (len(tar) > pos) and (tar[pos] == self.char):
+            return (pos + 1, self.char)
+        
+        got = tar[pos] if len(tar) > pos else "EOF"
+        msg = f"Excpected '{self.char}' but got '{got}'"
+        raise ParseError(pos, pos + 1, msg, self)
+
+class Literal(Parser):
+
+    def __init__(self, literal : str) -> None:
+        super().__init__()
+        self.name = 'lit(\'{}\')'.format(literal)
+        self.literal = literal
+        self.length = len(literal)
+    
+    def run(self, pos : int, tar : str):
+        if tar.startswith(self.literal,pos):
+            return (pos + self.length, self.literal)
+        if len(tar) > (pos + self.length-1):
+            msg = f"Tried to match '{self.literal}' but got '{tar[pos:pos+self.length]}'"
+            raise ParseError(pos, pos + self.length, msg, self)
+        msg = f"Tried to match '{self.literal}' but got EOF"
+        raise ParseError(pos, pos + self.length, msg, self)
+        
+class Regex(Parser):
+
+    def __init__(self, rule : str) -> None:
+        super().__init__()
+        self.name = 'reg(r\'{}\')'.format(rule)
+        self.rule = re.compile(rule)
+    
+    def run(self, pos : int, tar : str):
+        m = self.rule.match(tar, pos)
+        if m is None:
+            msg = f"Couldn't match the rule: {self.rule}"
+            raise ParseError(pos, pos, msg, self)
+        return (m.end(), m.group())
+
+class Tok(Parser):
+
+    def __init__(self, tag : str, data = None):
+        self.tag, self.data = tag, data
+        if data:
+            self.condition = lambda t : (t.type == tag) and (t.data == data)
+        else:
+            self.condition = lambda t : (t.type == tag)
+    
+    def run(self, pos : int, tar : list):
+        if len(tar) > pos:
+            tok = tar[pos]
+            if self.condition(tok):
+                return (pos + 1, tok)
+            msg = 'Expected Token {} but got {}'.format((self.tag,self.data),tok)
+            raise ParseError(tok.start, tok.end, msg, self)
+        else:
+            raise ParseError(pos, pos, 'Got EOF', self)
diff --git a/src/paco/combinators.py b/src/paco/combinators.py
index 6ff2b26..7f2f721 100644
--- a/src/paco/combinators.py
+++ b/src/paco/combinators.py
@@ -1,4 +1,3 @@
-import re
 from typing import Iterable
 
 class Parser(object):
@@ -65,67 +64,6 @@ class ParseError(Exception):
     def __str__(self):
         return self.msg
 
-class Char(Parser):
-    def __init__(self, char : str) -> None:
-        super().__init__()
-        self.name = 'char(\'{}\')'.format(char)
-        self.char = char
-    
-    def run(self, pos : int, tar : str):
-        if (len(tar) > pos) and (tar[pos] == self.char):
-            return (pos + 1, self.char)
-        
-        got = tar[pos] if len(tar) > pos else "EOF"
-        msg = f"Excpected '{self.char}' but got '{got}'"
-        raise ParseError(pos, pos + 1, msg, self)
-
-class Literal(Parser):
-
-    def __init__(self, literal : str) -> None:
-        super().__init__()
-        self.name = 'lit(\'{}\')'.format(literal)
-        self.literal = literal
-        self.length = len(literal)
-    
-    def run(self, pos : int, tar : str):
-        if tar.startswith(self.literal,pos):
-            return (pos + self.length, self.literal)
-        if len(tar) > (pos + self.length-1):
-            msg = f"Tried to match '{self.literal}' but got '{tar[pos:pos+self.length]}'"
-            raise ParseError(pos, pos + self.length, msg, self)
-        msg = f"Tried to match '{self.literal}' but got EOF"
-        raise ParseError(pos, pos + self.length, msg, self)
-        
-class Regex(Parser):
-
-    def __init__(self, rule : str) -> None:
-        super().__init__()
-        self.name = 'reg(r\'{}\')'.format(rule)
-        self.rule = re.compile(rule)
-    
-    def run(self, pos : int, tar : str):
-        m = self.rule.match(tar, pos)
-        if m is None:
-            msg = f"Couldn't match the rule: {self.rule}"
-            raise ParseError(pos, pos, msg, self)
-        return (m.end(), m.group())
-
-class Tok(Parser):
-
-    def __init__(self, tag : str, data = None):
-        self.tag, self.data = tag, data
-        if data:
-            self.condition = lambda t : (t.type == tag) and (t.data == data)
-        else:
-            self.condition = lambda t : (t.type == tag)
-    
-    def run(self, pos : int, tar : list):
-        tok = tar[pos]
-        if self.condition(tok):
-            return (pos + 1, tok)
-        msg = 'Expected Token was {} but got {}'.format((self.tag,self.data),tok)
-        raise ParseError(tok.start, tok.end, msg, self)
-
 class Sequence(Parser):
 
     def __init__(self, *parsers) -> None:
diff --git a/src/paco/miscellaneous.py b/src/paco/miscellaneous.py
index 3938293..c87c881 100644
--- a/src/paco/miscellaneous.py
+++ b/src/paco/miscellaneous.py
@@ -1,4 +1,4 @@
-from .combinators import (Regex)
+from .atomic import (Regex)
 
 letters = Regex("[a-zA-Z]+")
 letter = Regex("[a-zA-Z]")


Test patch:
diff --git a/tests/test_char_parser.py b/tests/test_char_parser.py
index e8737e1..861b054 100644
--- a/tests/test_char_parser.py
+++ b/tests/test_char_parser.py
@@ -1,5 +1,5 @@
 import unittest
-from paco.combinators import Char
+from paco.atomic import Char
 
 class TestCharParser(unittest.TestCase):
 
diff --git a/tests/test_choice_parser.py b/tests/test_choice_parser.py
index 1f4ff34..652ac7c 100644
--- a/tests/test_choice_parser.py
+++ b/tests/test_choice_parser.py
@@ -1,5 +1,6 @@
 import unittest
-from paco.combinators import (Char, Literal, Regex, Choice)
+from paco.combinators import Choice
+from paco.atomic import (Char, Literal, Regex)
 
 class TestChoiceParser(unittest.TestCase):
 
diff --git a/tests/test_keepleft_parser.py b/tests/test_keepleft_parser.py
index 4a27682..4c89b22 100644
--- a/tests/test_keepleft_parser.py
+++ b/tests/test_keepleft_parser.py
@@ -1,5 +1,6 @@
 import unittest
-from paco.combinators import (Char, Literal, Regex, KeepLeft)
+from paco.combinators import KeepLeft
+from paco.atomic import (Char, Literal, Regex)
 
 class TestKeepLeftParser(unittest.TestCase):
 
diff --git a/tests/test_keepright_parser.py b/tests/test_keepright_parser.py
index d90fbfe..3ff8807 100644
--- a/tests/test_keepright_parser.py
+++ b/tests/test_keepright_parser.py
@@ -1,5 +1,6 @@
 import unittest
-from paco.combinators import (Char, Literal, Regex, KeepRight)
+from paco.combinators import KeepRight
+from paco.atomic import (Char, Literal, Regex)
 
 class TestKeepRightParser(unittest.TestCase):
 
diff --git a/tests/test_lazy_parser.py b/tests/test_lazy_parser.py
index 4d902be..2207861 100644
--- a/tests/test_lazy_parser.py
+++ b/tests/test_lazy_parser.py
@@ -1,5 +1,6 @@
 import unittest
-from paco.combinators import (Char, Lazy)
+from paco.combinators import Lazy
+from paco.atomic import Char
 
 class TestLazyParser(unittest.TestCase):
 
diff --git a/tests/test_literal_parser.py b/tests/test_literal_parser.py
index 199e5b6..24e8cdc 100644
--- a/tests/test_literal_parser.py
+++ b/tests/test_literal_parser.py
@@ -1,5 +1,5 @@
 import unittest
-from paco.combinators import Literal
+from paco.atomic import Literal
 
 class TestLiteralParser(unittest.TestCase):
 
diff --git a/tests/test_many_parser.py b/tests/test_many_parser.py
index 1e6abba..75a0d12 100644
--- a/tests/test_many_parser.py
+++ b/tests/test_many_parser.py
@@ -1,5 +1,6 @@
 import unittest
-from paco.combinators import (Char, Regex, Many)
+from paco.combinators import Many
+from paco.atomic import (Char, Regex)
 
 class TestManyParser(unittest.TestCase):
 
diff --git a/tests/test_regex_parser.py b/tests/test_regex_parser.py
index 109fd50..57740f2 100644
--- a/tests/test_regex_parser.py
+++ b/tests/test_regex_parser.py
@@ -1,5 +1,5 @@
 import unittest
-from paco.combinators import Regex
+from paco.atomic import Regex
 
 class TestRegexParser(unittest.TestCase):
 
diff --git a/tests/test_sepby_parser.py b/tests/test_sepby_parser.py
index cfeb181..8e420ca 100644
--- a/tests/test_sepby_parser.py
+++ b/tests/test_sepby_parser.py
@@ -1,5 +1,6 @@
 import unittest
-from paco.combinators import (Char, Regex, SepBy)
+from paco.combinators import SepBy
+from paco.atomic import (Char, Regex)
 
 class TestSepByParser(unittest.TestCase):
 
diff --git a/tests/test_sequence_parser.py b/tests/test_sequence_parser.py
index 0dc25b6..70176ba 100644
--- a/tests/test_sequence_parser.py
+++ b/tests/test_sequence_parser.py
@@ -1,5 +1,6 @@
 import unittest
-from paco.combinators import (Char, Literal, Regex, Sequence)
+from paco.combinators import Sequence
+from paco.atomic import (Char, Literal, Regex)
 
 class TestSequenceParser(unittest.TestCase):