Browse Source

Continue working on project.

Cixo Develop 9 months ago
parent
commit
be1936cc73
3 changed files with 246 additions and 60 deletions
  1. 3 0
      src/CxPini/exception.py
  2. 122 0
      src/CxPini/key.py
  3. 121 60
      src/CxPini/parser.py

+ 3 - 0
src/CxPini/exception.py

@@ -3,3 +3,6 @@ class pini_exception(Exception):
 
 class pini_type_exception(TypeError):
     pass
+
+class pini_syntax(SyntaxError):
+    pass

+ 122 - 0
src/CxPini/key.py

@@ -1,5 +1,6 @@
 from .exception import pini_exception
 from .exception import pini_type_exception
+from .exception import pini_syntax
 from .line import line
 
 class key(line):
@@ -81,3 +82,124 @@ class key(line):
         copy.comment = self.comment
 
         return copy
+
+class parse_key():
+    def __new__(cls, line: str) -> key:
+        target = key()
+
+        if type(line) is not str:
+            raise TypeError("Line must be in str type.")
+
+        cls.__init__(target, cls, line)
+
+        return target
+
+    def __init__(self, cls, line: str) -> None:
+        line = line.lstrip()
+
+        if len(line) == 0:
+            return
+
+        if line[0:3] == "###":
+            cls.__parse_blank(self, cls, line[3:])
+            return
+
+        cls.__parse_normal(self, cls, line)
+
+    def __split_comment(line: str) -> [str | None, str | None]:
+        line = line.lstrip()
+
+        if len(line) == 0:
+            return None, None
+
+        comment = None
+        content = line.rsplit()
+        comment_position = line.find("#")
+
+        if comment_position != -1:
+            comment = line[comment_position + 1:]
+
+            if comment[0] == " ":
+                comment = comment[1:]
+
+            if len(comment) == 0:
+                comment = None
+
+            content = line[:comment_position].rsplit()
+
+        if len(content) == 0:
+            content = None
+
+        return content, comment
+
+    def __parse_blank(self, cls, line: str) -> None:
+        content, comment = cls.__split_comment(line)
+
+        where = content.find("=")
+
+        if where != -1:
+            content = content[:where].rsplit()
+
+        self.name = content
+        self.value = None
+        self.comment = comment
+
+    def __split_comment(cls, line: str) -> [str | None, str | None]:
+        name = line
+        value = None
+        value_position = line.find("=")
+
+        if value_position == -1:
+            return name, value
+        
+        name = line[:value_position].rsplit()
+        value = line[value_position + 1:].lstrip()
+        value = cls.__parse_value(cls, value)
+
+        return name, value
+
+    def __parse_value(cls, line: str) -> str | None:
+        line = line.strip()
+
+        if len(line) == 0:
+            return None
+
+        if cls.is_number(line):
+            as_float = float(line)
+            as_integer = int(line)
+
+            if as_float == as_integer:
+                return as_integer
+
+            return as_float
+
+        if line[0] == "'" or line[0] == "\"":
+            start = line[0]
+
+            if line[-1] != start:
+                raise pini_syntax("String without stop.")
+
+            line = line[1:-1]
+            where = 0
+
+            while True:
+                where = line.find(start, where)
+
+                if where == -1:
+                    break
+
+                if line[where - 1] != "\\":
+                    raise pini_syntax("String stopper not expected.")
+
+        return line 
+
+    def is_number(value: str) -> bool:
+        if value[0] == "-":
+            value = value[1:]
+
+        value = value.replace(".", "")
+        return value.isnumeric()
+
+    def __parse_normal(self, cls, line: str) -> None:
+        content, comment = cls.__split_comment(line)
+        name, value = cls.__split_content(cls, content) 

+ 121 - 60
src/CxPini/parser.py

@@ -2,97 +2,158 @@ import pathlib
 import _io
 
 from .pini_exception import pini_exception
+from .pini_exception import pini_syntax
 from .pini import pini
 from .key import key
 from .section import section
 
-class parser:
-    def __init__(self, target: str | _io.TextIOWrapper | pathlib.Path):
+class parser():
+    def __init__(self, target: str | pathlib.Path):
+        if target isinstance str:
+            target = pathlib.Path(target)
+
+        if not target.is_file():
+            raise pini_exception("File " + str(target) + " not exists.")
+
+        self.__current = None
+        self.__path = target
         self.__config = pini()
-        self.__section = section()
 
-        if type(target) is str:
-            target = pathlib.Path(target)
+        self.__process()
 
-        if isinstance(target, pathlib.Path):
-            if not target.is_file():
-                raise pini_exception("File " + str(target) + "not exists.")
+    def __get_section(self) -> section:
+        if self.__current is None:
+            self.__current = section() 
+            return self.__get_section()
 
-            with target.open(mode = "r", encoding = "utf-8") as file:
-                self.__read_file(file)
-                return
+        return self.__current
 
-        if type(target) is _io.TextIOWrapper:
-            self.__read_file(file)
-            return
+    def __new_section(self, name: str) -> section:
+        if type(name) is not str:
+            raise TypeError("New section name must be str.")
+
+        name = name.strip()
 
-        raise TypeError("Target to parse must be str file, path, or file.")
+        if len(name) == 0:
+            raise pini_syntax("New section name can not be empty.")
 
-    def __read_file(self, file: _io.TextIOWrapper) -> None:
-        while True:
-            line = file.readline()
-            
-            if len(line) == 0:
-                break
+        if self.__current is not None:
+            self.__config.add(self.__current)
 
-            self.__parse_line(line)
+        self.__current = section()
+        self.__current.name = name
+
+    def __process(self) -> None:
+        with self.__path.open() as file:
+            for line in file:
+                self.__parse_line(line)
 
     def __parse_line(self, line: str) -> None:
         line = line.lstrip()
 
-        if line[0:3] == "###":
-            self.__parse_key(line)
+        if len(line) == 0:
             return
 
-        if line[0] == "#":
-            self.__parse_comment(line)
-            return
+        first = line[0]
 
-        if line[0] == "[":
+        if first == "[":
             self.__parse_section(line)
             return
-        
-        if line.find("="):
-            self.__parse_key(line)
+
+        if first == "#":
+            if len(line) >= 3 and line[0:3] == "###":
+                self.__parse_unactive_key(line)
+                return
+
+            self.__parse_comment(line)
             return
 
-    def __parse_section(self, line: str) -> None:
-        name_position = line.find("=")
-        name = line[:name_position].strip()
-        value = line[name_position + 1:].lstrip()
-        value, comment = self.__parse_value(value)
-
-    def __parse_value(self, value: str) -> [str | int | float, str | None]:
-        if value[0] == "'" or value == "\"":
-            return self.__parse_string(value)
-        
-        comment_split = value.split("#")
-        
-        if len(comment_split) == 1:
-            pass
+        self.__parse_key(line)
 
-    def __parse_string(self, value: str) -> [str | int | float, str | None]:
-        tag = value[0]
-        end = value.find(tag, 1)
-        content = value[1:end]
+    def __split_comment(self, line: str) -> [str | None , str | None]:
+        position = line.find("#")
 
-        comment = value[end + 1:].lstrip()
-        
-        if comment[0] != "#" and len(comment) > 0:
-            raise pine_syntax("Chars after string close. " + comment)
+        if position == -1:
+            return line.strip(), None
 
-        if len(comment) == 0:
-            return content, None
+        content = line[:position].strip()
+        comment = line[position + 1:]
 
-        if comment[0] == "#":
+        if len(comment) > 0 and comment[0] == " ":
             comment = comment[1:]
         
-        if comment[0] == " ":
-            comment = comment[1:]
+        if len(content) == 0:
+            content = None
 
         if len(comment) == 0:
             comment = None
 
         return content, comment
-    
-     
+
+    def __parse_unactive_key(self, line: str) -> None:
+        line = line[3:]
+
+        if line[0] == " ":
+            line = line[1:]
+
+        content, comment = self.__split_comment(line)
+
+        if content is None:
+            if comment is None:
+                return
+
+            self.__get_section().add_comment(comment)
+
+        position = content.find("=")
+
+        target = key()
+        target.value = None
+        target.name = content.strip()
+        target.comment = comment
+
+    def __parse_key(self, line: str) -> None:
+        content, comment = self.__split_comment(line)
+
+        if content is None and comment is not None:
+            self.__parse_comment("#" + comment)
+            return
+
+        if content.count("=") != 1:
+            raise pini_syntax(""
+
+    def __parse_section(self, line: str) -> None:
+        content, comment = self.__split_comment(line)
+
+        if content.count("[") != 1 or content.count("]") != 1:
+            raise pini_syntax("Section name must start with [ and end with ].")
+
+        start = content.find("[") + 1
+        end = content.find("]")
+        name = content[start:end].strip()
+
+        if len(name) == 0:
+            raise pini_syntax("Section name can not be empty.")
+
+        if name.count(" ") > 0 or name.count("\t") > 0:
+            raise pini_syntax("Section name can not contain white chars.")
+
+        self.__new_section(name)
+
+        if comment is not None:
+            self.__get_section().add_comment(comment)
+
+    def __parse_comment(self, line: str) -> None:
+        if line[0] != "#":
+            return
+
+        line = line[1:]
+
+        if len(line) == 0:
+            self.__get_section().add_comment("")
+            return
+
+        if line[0] == " ":
+            line = line[1:]
+
+        self.__get_section().add_comment(line)
+