You've already forked STARK
mirror of
https://github.com/MarkParker5/STARK.git
synced 2025-09-16 09:36:24 +02:00
WIP: update file structure
This commit is contained in:
@@ -1,2 +1,3 @@
|
|||||||
from . import expressions
|
from . import expressions
|
||||||
from .pattern import MatchResult, ParseError, Pattern
|
from .parsing import ParseError
|
||||||
|
from .pattern import MatchResult, Pattern
|
||||||
|
56
stark/core/patterns/parsing.py
Normal file
56
stark/core/patterns/parsing.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from ..types import Object
|
||||||
|
ObjectType = type[Object]
|
||||||
|
|
||||||
|
from typing_extensions import NamedTuple
|
||||||
|
|
||||||
|
|
||||||
|
class ParseResult(NamedTuple):
|
||||||
|
obj: Object
|
||||||
|
substring: str
|
||||||
|
|
||||||
|
class ParseError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ObjectParser:
|
||||||
|
async def did_parse(self, obj: Object, from_string: str) -> str:
|
||||||
|
'''
|
||||||
|
This method is called after parsing from string and setting parameters found in pattern.
|
||||||
|
You will very rarely, if ever, need to call this method directly.
|
||||||
|
|
||||||
|
Override this method for more complex parsing from string.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Minimal substring that is required to parse value.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ParseError: if parsing failed.
|
||||||
|
'''
|
||||||
|
return from_string
|
||||||
|
|
||||||
|
async def _parse_object(
|
||||||
|
object_type: type[Object],
|
||||||
|
parser: ObjectParser,
|
||||||
|
from_string: str,
|
||||||
|
parsed_parameters: dict[str, Object | None] | None = None
|
||||||
|
) -> ParseResult:
|
||||||
|
obj = object_type(from_string) # temp/default value, may be overridden by did_parse
|
||||||
|
parsed_parameters = parsed_parameters or {}
|
||||||
|
assert parsed_parameters.keys() <= {p.name for p in object_type.pattern.parameters.values() if not p.optional}
|
||||||
|
|
||||||
|
for name in object_type.pattern.parameters:
|
||||||
|
value = parsed_parameters[name]
|
||||||
|
setattr(obj, name, value)
|
||||||
|
|
||||||
|
substring = await parser.did_parse(obj, from_string)
|
||||||
|
substring = await obj.did_parse(substring)
|
||||||
|
|
||||||
|
assert substring.strip(), ValueError(f'Parsed substring must not be empty. Object: {obj}, Parser: {parser}')
|
||||||
|
assert substring in from_string, ValueError(f'Parsed substring must be a part of the original string. There is no {substring} in {from_string}. Object: {obj}, Parser: {parser}')
|
||||||
|
assert obj.value is not None, ValueError(f'Parsed object {obj} must have a `value` property set in did_parse method. Object: {obj}, Parser: {parser}')
|
||||||
|
|
||||||
|
return ParseResult(obj, substring)
|
@@ -23,29 +23,8 @@ class MatchResult:
|
|||||||
end: int
|
end: int
|
||||||
parameters: dict[str, Object | None]
|
parameters: dict[str, Object | None]
|
||||||
|
|
||||||
class ParseResult(NamedTuple):
|
from .parsing import ObjectParser, ParseError, _parse_object
|
||||||
obj: Object
|
|
||||||
substring: str
|
|
||||||
|
|
||||||
class ParseError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class ObjectParser:
|
|
||||||
|
|
||||||
async def did_parse(self, obj: Object, from_string: str) -> str:
|
|
||||||
'''
|
|
||||||
This method is called after parsing from string and setting parameters found in pattern.
|
|
||||||
You will very rarely, if ever, need to call this method directly.
|
|
||||||
|
|
||||||
Override this method for more complex parsing from string.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Minimal substring that is required to parse value.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ParseError: if parsing failed.
|
|
||||||
'''
|
|
||||||
return from_string
|
|
||||||
|
|
||||||
class PatternParameter(NamedTuple): # TODO: dataclass?
|
class PatternParameter(NamedTuple): # TODO: dataclass?
|
||||||
name: str
|
name: str
|
||||||
@@ -154,7 +133,7 @@ class Pattern:
|
|||||||
new_match.groupdict().items()
|
new_match.groupdict().items()
|
||||||
))
|
))
|
||||||
|
|
||||||
logger.debug(f'Found parameters: {[(new_match.start(name), name, match_str_groups[name]) for name in sorted(match_str_groups.keys(), key=lambda n: (int(self._parameter_types[self.parameters[parameter_name].type_name].type.greedy), new_match.start(n)))]}')
|
logger.debug(f'Found parameters: {[(new_match.start(name), name, match_str_groups[name]) for name in sorted(match_str_groups.keys(), key=lambda n: (int(Pattern._parameter_types[self.parameters[parameter_name].type_name].type.greedy), new_match.start(n)))]}')
|
||||||
|
|
||||||
if not match_str_groups:
|
if not match_str_groups:
|
||||||
break # everything's parsed (probably successfully)
|
break # everything's parsed (probably successfully)
|
||||||
@@ -164,11 +143,11 @@ class Pattern:
|
|||||||
parameter_name = min(
|
parameter_name = min(
|
||||||
match_str_groups.keys(),
|
match_str_groups.keys(),
|
||||||
key=lambda name: (
|
key=lambda name: (
|
||||||
int(self._parameter_types[self.parameters[parameter_name].type_name].type.greedy), # parse greedy the last so they don't absorb neighbours
|
int(Pattern._parameter_types[self.parameters[parameter_name].type_name].type.greedy), # parse greedy the last so they don't absorb neighbours
|
||||||
new_match.start(name) # parse left to right
|
new_match.start(name) # parse left to right
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
parameter_reg_type = self._parameter_types[self.parameters[parameter_name].type_name]
|
parameter_reg_type = Pattern._parameter_types[self.parameters[parameter_name].type_name]
|
||||||
parameter_type = parameter_reg_type.type
|
parameter_type = parameter_reg_type.type
|
||||||
raw_param_substr = match_str_groups[parameter_name].strip()
|
raw_param_substr = match_str_groups[parameter_name].strip()
|
||||||
|
|
||||||
@@ -193,7 +172,7 @@ class Pattern:
|
|||||||
if not object_matches:
|
if not object_matches:
|
||||||
raise ParseError(f"Failed to match object {parameter_type} from {raw_param_substr}")
|
raise ParseError(f"Failed to match object {parameter_type} from {raw_param_substr}")
|
||||||
object_pattern_match = object_matches[0]
|
object_pattern_match = object_matches[0]
|
||||||
parse_result = await self._parse_object(
|
parse_result = await _parse_object(
|
||||||
parameter_reg_type.type,
|
parameter_reg_type.type,
|
||||||
parameter_reg_type.parser,
|
parameter_reg_type.parser,
|
||||||
from_string=object_pattern_match.substring,
|
from_string=object_pattern_match.substring,
|
||||||
@@ -250,27 +229,6 @@ class Pattern:
|
|||||||
|
|
||||||
return sorted(matches, key = lambda m: len(m.substring), reverse = True)
|
return sorted(matches, key = lambda m: len(m.substring), reverse = True)
|
||||||
|
|
||||||
# parsing
|
|
||||||
|
|
||||||
async def _parse_object(self, object_type: type[Object], parser: ObjectParser, from_string: str, parsed_parameters: dict[str, Object | None] | None = None) -> ParseResult:
|
|
||||||
|
|
||||||
obj = object_type(from_string) # temp/default value, may be overridden by did_parse
|
|
||||||
parsed_parameters = parsed_parameters or {}
|
|
||||||
assert parsed_parameters.keys() <= {p.name for p in object_type.pattern.parameters.values() if not p.optional}
|
|
||||||
|
|
||||||
for name in object_type.pattern.parameters:
|
|
||||||
value = parsed_parameters[name]
|
|
||||||
setattr(obj, name, value)
|
|
||||||
|
|
||||||
substring = await parser.did_parse(obj, from_string)
|
|
||||||
substring = await obj.did_parse(substring)
|
|
||||||
|
|
||||||
assert substring.strip(), ValueError(f'Parsed substring must not be empty. Object: {obj}, Parser: {parser}')
|
|
||||||
assert substring in from_string, ValueError(f'Parsed substring must be a part of the original string. There is no {substring} in {from_string}. Object: {obj}, Parser: {parser}')
|
|
||||||
assert obj.value is not None, ValueError(f'Parsed object {obj} must have a `value` property set in did_parse method. Object: {obj}, Parser: {parser}')
|
|
||||||
|
|
||||||
return ParseResult(obj, substring)
|
|
||||||
|
|
||||||
# processing pattern
|
# processing pattern
|
||||||
|
|
||||||
def _get_pattern_parameter_annotation_regex(self) -> re.Pattern:
|
def _get_pattern_parameter_annotation_regex(self) -> re.Pattern:
|
||||||
@@ -305,7 +263,7 @@ class Pattern:
|
|||||||
# find and transform parameters like $name:Type
|
# find and transform parameters like $name:Type
|
||||||
|
|
||||||
for parameter in self.parameters.values():
|
for parameter in self.parameters.values():
|
||||||
parameter_type = self._parameter_types[parameter.type_name].type
|
parameter_type = Pattern._parameter_types[parameter.type_name].type
|
||||||
|
|
||||||
arg_declaration = f'\\${parameter.name}\\:{parameter_type.__name__}' # NOTE: special chars escaped for regex
|
arg_declaration = f'\\${parameter.name}\\:{parameter_type.__name__}' # NOTE: special chars escaped for regex
|
||||||
if parameter.name in prefill:
|
if parameter.name in prefill:
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
from .object import Object, ParseResult
|
from .object import Object
|
||||||
from .string import String
|
from .string import String
|
||||||
from .word import Word
|
from .word import Word
|
||||||
# from .Number import Number
|
# from .Number import Number
|
||||||
|
Reference in New Issue
Block a user