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:
2025-03-07 17:14:18 +08:00
parent 81348b1f28
commit fc48162fcb

View File

@ -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}"
) )