Refactor ZonParser to use private attributes and improve encapsulation
- Change public attributes to private with leading underscore - Update method implementations to use new private attribute names - Maintain existing parsing logic and functionality - Improve code readability and follow Python naming conventions
This commit is contained in:
@ -14,6 +14,12 @@ class ZonParser:
|
|||||||
A parser for Zig Object Notation (ZON) files.
|
A parser for Zig Object Notation (ZON) files.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
_content: str
|
||||||
|
_pos: int
|
||||||
|
_line: int
|
||||||
|
_col: int
|
||||||
|
empty_tuple_as_dict: bool = False
|
||||||
|
|
||||||
def __init__(self, content: str, empty_tuple_as_dict: bool = False):
|
def __init__(self, content: str, empty_tuple_as_dict: bool = False):
|
||||||
"""
|
"""
|
||||||
Initialize the parser with ZON content.
|
Initialize the parser with ZON content.
|
||||||
@ -23,10 +29,10 @@ class ZonParser:
|
|||||||
empty_tuple_as_dict: If True, empty tuples (.{}) will be parsed as empty dictionaries ({})
|
empty_tuple_as_dict: If True, empty tuples (.{}) will be parsed as empty dictionaries ({})
|
||||||
If False, empty tuples will be parsed as empty lists ([])
|
If False, empty tuples will be parsed as empty lists ([])
|
||||||
"""
|
"""
|
||||||
self.content = content
|
self._content = content
|
||||||
self.pos = 0
|
self._pos = 0
|
||||||
self.line = 1
|
self._line = 1
|
||||||
self.col = 1
|
self._col = 1
|
||||||
self.empty_tuple_as_dict = empty_tuple_as_dict
|
self.empty_tuple_as_dict = empty_tuple_as_dict
|
||||||
|
|
||||||
def parse(self) -> Dict[str, Any]:
|
def parse(self) -> Dict[str, Any]:
|
||||||
@ -35,24 +41,24 @@ class ZonParser:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def _current_char(self) -> str:
|
def _current_char(self) -> str:
|
||||||
if self.pos >= len(self.content):
|
if self._pos >= len(self._content):
|
||||||
return ""
|
return ""
|
||||||
return self.content[self.pos]
|
return self._content[self._pos]
|
||||||
|
|
||||||
def _next_char(self) -> str:
|
def _next_char(self) -> str:
|
||||||
self.pos += 1
|
self._pos += 1
|
||||||
if self.pos - 1 < len(self.content):
|
if self._pos - 1 < len(self._content):
|
||||||
char = self.content[self.pos - 1]
|
char = self._content[self._pos - 1]
|
||||||
if char == "\n":
|
if char == "\n":
|
||||||
self.line += 1
|
self._line += 1
|
||||||
self.col = 1
|
self._col = 1
|
||||||
else:
|
else:
|
||||||
self.col += 1
|
self._col += 1
|
||||||
return char
|
return char
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def _skip_whitespace_and_comments(self):
|
def _skip_whitespace_and_comments(self):
|
||||||
while self.pos < len(self.content):
|
while self._pos < len(self._content):
|
||||||
char = self._current_char()
|
char = self._current_char()
|
||||||
|
|
||||||
# Skip whitespace
|
# Skip whitespace
|
||||||
@ -63,11 +69,11 @@ class ZonParser:
|
|||||||
# Skip comments
|
# Skip comments
|
||||||
if (
|
if (
|
||||||
char == "/"
|
char == "/"
|
||||||
and self.pos + 1 < len(self.content)
|
and self._pos + 1 < len(self._content)
|
||||||
and self.content[self.pos + 1] == "/"
|
and self._content[self._pos + 1] == "/"
|
||||||
):
|
):
|
||||||
# Skip to end of line
|
# Skip to end of line
|
||||||
while self.pos < len(self.content) and self._current_char() != "\n":
|
while self._pos < len(self._content) and self._current_char() != "\n":
|
||||||
self._next_char()
|
self._next_char()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -95,12 +101,12 @@ class ZonParser:
|
|||||||
return self._parse_number()
|
return self._parse_number()
|
||||||
elif char == "t" or char == "f":
|
elif char == "t" or char == "f":
|
||||||
return self._parse_boolean()
|
return self._parse_boolean()
|
||||||
elif char == "n" and self.content[self.pos : self.pos + 4] == "null":
|
elif char == "n" and self._content[self._pos : self._pos + 4] == "null":
|
||||||
self.pos += 4
|
self._pos += 4
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Unexpected character '{char}' at line {self.line}, column {self.col}"
|
f"Unexpected character '{char}' at line {self._line}, column {self._col}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def _parse_object(self) -> Union[Dict[str, Any], List[Any]]:
|
def _parse_object(self) -> Union[Dict[str, Any], List[Any]]:
|
||||||
@ -109,9 +115,9 @@ class ZonParser:
|
|||||||
self._next_char()
|
self._next_char()
|
||||||
|
|
||||||
# Look ahead to see if this is a tuple or an object
|
# Look ahead to see if this is a tuple or an object
|
||||||
pos_before = self.pos
|
pos_before = self._pos
|
||||||
line_before = self.line
|
line_before = self._line
|
||||||
col_before = self.col
|
col_before = self._col
|
||||||
|
|
||||||
self._skip_whitespace_and_comments()
|
self._skip_whitespace_and_comments()
|
||||||
|
|
||||||
@ -133,7 +139,7 @@ class ZonParser:
|
|||||||
if self._current_char() == "{":
|
if self._current_char() == "{":
|
||||||
# This is potentially a nested tuple starting with .{
|
# This is potentially a nested tuple starting with .{
|
||||||
# Go back to the dot and let the normal parsing decide
|
# Go back to the dot and let the normal parsing decide
|
||||||
self.pos -= 1
|
self._pos -= 1
|
||||||
elif (
|
elif (
|
||||||
self._current_char() == "@"
|
self._current_char() == "@"
|
||||||
or self._current_char().isalnum()
|
or self._current_char().isalnum()
|
||||||
@ -146,9 +152,9 @@ class ZonParser:
|
|||||||
is_tuple = False
|
is_tuple = False
|
||||||
|
|
||||||
# Reset position
|
# Reset position
|
||||||
self.pos = pos_before
|
self._pos = pos_before
|
||||||
self.line = line_before
|
self._line = line_before
|
||||||
self.col = col_before
|
self._col = col_before
|
||||||
|
|
||||||
if is_tuple:
|
if is_tuple:
|
||||||
return self._parse_tuple()
|
return self._parse_tuple()
|
||||||
@ -173,7 +179,7 @@ class ZonParser:
|
|||||||
key = self._parse_identifier()
|
key = self._parse_identifier()
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Expected '.' before key at line {self.line}, column {self.col}"
|
f"Expected '.' before key at line {self._line}, column {self._col}"
|
||||||
)
|
)
|
||||||
|
|
||||||
self._skip_whitespace_and_comments()
|
self._skip_whitespace_and_comments()
|
||||||
@ -196,7 +202,7 @@ class ZonParser:
|
|||||||
self._next_char()
|
self._next_char()
|
||||||
elif self._current_char() != "}":
|
elif self._current_char() != "}":
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Expected ',' or '}}' at line {self.line}, column {self.col}"
|
f"Expected ',' or '}}' at line {self._line}, column {self._col}"
|
||||||
)
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -231,9 +237,9 @@ class ZonParser:
|
|||||||
# Handle the special case of nested tuple/object with dot prefix
|
# Handle the special case of nested tuple/object with dot prefix
|
||||||
if self._current_char() == ".":
|
if self._current_char() == ".":
|
||||||
# Save position before the dot
|
# Save position before the dot
|
||||||
pos_before = self.pos
|
pos_before = self._pos
|
||||||
line_before = self.line
|
line_before = self._line
|
||||||
col_before = self.col
|
col_before = self._col
|
||||||
|
|
||||||
self._next_char() # Skip the dot
|
self._next_char() # Skip the dot
|
||||||
|
|
||||||
@ -244,9 +250,9 @@ class ZonParser:
|
|||||||
result.append(value)
|
result.append(value)
|
||||||
else:
|
else:
|
||||||
# Not a nested tuple/object, reset position and parse normally
|
# Not a nested tuple/object, reset position and parse normally
|
||||||
self.pos = pos_before
|
self._pos = pos_before
|
||||||
self.line = line_before
|
self._line = line_before
|
||||||
self.col = col_before
|
self._col = col_before
|
||||||
|
|
||||||
# Parse as normal value
|
# Parse as normal value
|
||||||
value = self._parse_value()
|
value = self._parse_value()
|
||||||
@ -263,35 +269,37 @@ class ZonParser:
|
|||||||
self._next_char()
|
self._next_char()
|
||||||
elif self._current_char() != "}":
|
elif self._current_char() != "}":
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Expected ',' or '}}' at line {self.line}, column {self.col}"
|
f"Expected ',' or '}}' at line {self._line}, column {self._col}"
|
||||||
)
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _parse_identifier(self) -> str:
|
def _parse_identifier(self) -> str:
|
||||||
start = self.pos
|
start = self._pos
|
||||||
|
|
||||||
# Handle quoted identifiers (like .@"lsp-codegen")
|
# Handle quoted identifiers (like .@"lsp-codegen")
|
||||||
if (
|
if (
|
||||||
self._current_char() == "@"
|
self._current_char() == "@"
|
||||||
and self.pos + 1 < len(self.content)
|
and self._pos + 1 < len(self._content)
|
||||||
and self.content[self.pos + 1] == '"'
|
and self._content[self._pos + 1] == '"'
|
||||||
):
|
):
|
||||||
self._next_char() # Skip @
|
self._next_char() # Skip @
|
||||||
return self._parse_string()
|
return self._parse_string()
|
||||||
|
|
||||||
# Regular identifier
|
# Regular identifier
|
||||||
while self.pos < len(self.content):
|
while self._pos < len(self._content):
|
||||||
char = self._current_char()
|
char = self._current_char()
|
||||||
if char.isalnum() or char == "_" or char == "-":
|
if char.isalnum() or char == "_" or char == "-":
|
||||||
self._next_char()
|
self._next_char()
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
if start == self.pos:
|
if start == self._pos:
|
||||||
raise ValueError(f"Empty identifier at line {self.line}, column {self.col}")
|
raise ValueError(
|
||||||
|
f"Empty identifier at line {self._line}, column {self._col}"
|
||||||
|
)
|
||||||
|
|
||||||
return self.content[start : self.pos]
|
return self._content[start : self._pos]
|
||||||
|
|
||||||
def _parse_string(self) -> str:
|
def _parse_string(self) -> str:
|
||||||
result = ""
|
result = ""
|
||||||
@ -299,7 +307,7 @@ class ZonParser:
|
|||||||
# Skip the opening quote
|
# Skip the opening quote
|
||||||
self._next_char()
|
self._next_char()
|
||||||
|
|
||||||
while self.pos < len(self.content) and self._current_char() != '"':
|
while self._pos < len(self._content) and self._current_char() != '"':
|
||||||
if self._current_char() == "\\":
|
if self._current_char() == "\\":
|
||||||
self._next_char()
|
self._next_char()
|
||||||
if self._current_char() == "n":
|
if self._current_char() == "n":
|
||||||
@ -320,32 +328,32 @@ class ZonParser:
|
|||||||
|
|
||||||
if self._current_char() != '"':
|
if self._current_char() != '"':
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Unterminated string at line {self.line}, column {self.col}"
|
f"Unterminated string at line {self._line}, column {self._col}"
|
||||||
)
|
)
|
||||||
|
|
||||||
self._next_char() # Skip the closing quote
|
self._next_char() # Skip the closing quote
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _parse_number(self) -> Union[int, float]:
|
def _parse_number(self) -> Union[int, float]:
|
||||||
start = self.pos
|
start = self._pos
|
||||||
|
|
||||||
# Handle hex numbers
|
# Handle hex numbers
|
||||||
if (
|
if (
|
||||||
self._current_char() == "0"
|
self._current_char() == "0"
|
||||||
and self.pos + 1 < len(self.content)
|
and self._pos + 1 < len(self._content)
|
||||||
and self.content[self.pos + 1].lower() == "x"
|
and self._content[self._pos + 1].lower() == "x"
|
||||||
):
|
):
|
||||||
self._next_char() # Skip 0
|
self._next_char() # Skip 0
|
||||||
self._next_char() # Skip x
|
self._next_char() # Skip x
|
||||||
|
|
||||||
hex_start = self.pos
|
hex_start = self._pos
|
||||||
while self.pos < len(self.content) and (
|
while self._pos < len(self._content) and (
|
||||||
self._current_char().isdigit()
|
self._current_char().isdigit()
|
||||||
or self._current_char().lower() in "abcdef"
|
or self._current_char().lower() in "abcdef"
|
||||||
):
|
):
|
||||||
self._next_char()
|
self._next_char()
|
||||||
|
|
||||||
hex_str = self.content[hex_start : self.pos]
|
hex_str = self._content[hex_start : self._pos]
|
||||||
return int(hex_str, 16)
|
return int(hex_str, 16)
|
||||||
|
|
||||||
# Regular number
|
# Regular number
|
||||||
@ -356,7 +364,7 @@ class ZonParser:
|
|||||||
self._next_char()
|
self._next_char()
|
||||||
|
|
||||||
# Handle digits before decimal point
|
# Handle digits before decimal point
|
||||||
while self.pos < len(self.content) and self._current_char().isdigit():
|
while self._pos < len(self._content) and self._current_char().isdigit():
|
||||||
self._next_char()
|
self._next_char()
|
||||||
|
|
||||||
# Handle decimal point
|
# Handle decimal point
|
||||||
@ -365,7 +373,7 @@ class ZonParser:
|
|||||||
self._next_char()
|
self._next_char()
|
||||||
|
|
||||||
# Handle digits after decimal point
|
# Handle digits after decimal point
|
||||||
while self.pos < len(self.content) and self._current_char().isdigit():
|
while self._pos < len(self._content) and self._current_char().isdigit():
|
||||||
self._next_char()
|
self._next_char()
|
||||||
|
|
||||||
# Handle exponent
|
# Handle exponent
|
||||||
@ -378,10 +386,10 @@ class ZonParser:
|
|||||||
self._next_char()
|
self._next_char()
|
||||||
|
|
||||||
# Handle exponent digits
|
# Handle exponent digits
|
||||||
while self.pos < len(self.content) and self._current_char().isdigit():
|
while self._pos < len(self._content) and self._current_char().isdigit():
|
||||||
self._next_char()
|
self._next_char()
|
||||||
|
|
||||||
num_str = self.content[start : self.pos]
|
num_str = self._content[start : self._pos]
|
||||||
|
|
||||||
if is_float:
|
if is_float:
|
||||||
return float(num_str)
|
return float(num_str)
|
||||||
@ -389,15 +397,15 @@ class ZonParser:
|
|||||||
return int(num_str)
|
return int(num_str)
|
||||||
|
|
||||||
def _parse_boolean(self) -> bool:
|
def _parse_boolean(self) -> bool:
|
||||||
if self.content[self.pos : self.pos + 4] == "true":
|
if self._content[self._pos : self._pos + 4] == "true":
|
||||||
self.pos += 4
|
self._pos += 4
|
||||||
return True
|
return True
|
||||||
elif self.content[self.pos : self.pos + 5] == "false":
|
elif self._content[self._pos : self._pos + 5] == "false":
|
||||||
self.pos += 5
|
self._pos += 5
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Expected 'true' or 'false' at line {self.line}, column {self.col}"
|
f"Expected 'true' or 'false' at line {self._line}, column {self._col}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user