143 lines
4.9 KiB
Python
143 lines
4.9 KiB
Python
from typing import Tuple, List, Dict, Any, Sequence, Optional
|
|
|
|
from . import _msgs as msgs
|
|
from ._commands import Int, Float
|
|
from ._helpers import SimpleError, null_terminate
|
|
|
|
|
|
def _count_params(s: str) -> int:
|
|
res = 0
|
|
while res < len(s) and s[res] in '.+*~':
|
|
res += 1
|
|
return res
|
|
|
|
|
|
def _encode_arg(s: str) -> bytes:
|
|
return s[_count_params(s):].encode()
|
|
|
|
|
|
def _default_value(s: str) -> Any:
|
|
if s[0] == '~':
|
|
return None
|
|
ind = _count_params(s)
|
|
if ind == 0:
|
|
return False
|
|
elif ind == 1:
|
|
return None
|
|
else:
|
|
return [None] * ind
|
|
|
|
|
|
def extract_args(
|
|
actual_args: Tuple[bytes, ...],
|
|
expected: Tuple[str, ...],
|
|
error_on_unexpected: bool = True,
|
|
left_from_first_unexpected: bool = True,
|
|
exception: Optional[str] = None
|
|
) -> Tuple[List[Any], Sequence[Any]]:
|
|
"""Parse argument values.
|
|
|
|
Extract from actual arguments which arguments exist and their value if relevant.
|
|
|
|
:param actual_args: The actual arguments to parse
|
|
:param expected: Arguments to look for, see below explanation.
|
|
:param error_on_unexpected: Should an error be raised when actual_args contain an unexpected argument?
|
|
:param left_from_first_unexpected: Once reaching an unexpected argument in actual_args, Should parsing stop?
|
|
:param exception: What exception msg to raise
|
|
:returns:
|
|
- List of values for expected arguments.
|
|
- List of remaining args.
|
|
|
|
An expected argument can have parameters:
|
|
- A numerical (Int) parameter is identified with '+'
|
|
- A float (Float) parameter is identified with '.'
|
|
- A non-numerical parameter is identified with a '*'
|
|
- An argument with potentially ~ or = between the
|
|
argument name and the value is identified with a '~'
|
|
- A numberical argument with potentially ~ or = between the
|
|
argument name and the value marked with a '~+'
|
|
|
|
E.g.
|
|
'++limit' will translate as an argument with 2 int parameters.
|
|
|
|
>>> extract_args((b'nx', b'ex', b'324', b'xx',), ('nx', 'xx', '+ex', 'keepttl'))
|
|
[True, True, 324, False], None
|
|
|
|
>>> extract_args(
|
|
(b'maxlen', b'10',b'nx', b'ex', b'324', b'xx',),
|
|
('~+maxlen', 'nx', 'xx', '+ex', 'keepttl'))
|
|
10, [True, True, 324, False], None
|
|
"""
|
|
args_info: Dict[bytes, Tuple[int, int]] = {
|
|
_encode_arg(k): (i, _count_params(k))
|
|
for (i, k) in enumerate(expected)
|
|
}
|
|
|
|
def _parse_params(key: bytes, ind: int, _actual_args: Tuple[bytes, ...]) -> Tuple[Any, int]:
|
|
"""Parse an argument from actual args.
|
|
:param key: Argument name to parse
|
|
:param ind: index of argument in actual_args
|
|
:param _actual_args: actual args
|
|
"""
|
|
pos, expected_following = args_info[key]
|
|
argument_name = expected[pos]
|
|
|
|
# Deal with parameters with optional ~/= before numerical value.
|
|
arg: Any
|
|
if argument_name[0] == '~':
|
|
if ind + 1 >= len(_actual_args):
|
|
raise SimpleError(msgs.SYNTAX_ERROR_MSG)
|
|
if _actual_args[ind + 1] != b'~' and _actual_args[ind + 1] != b'=':
|
|
arg, _parsed = _actual_args[ind + 1], 1
|
|
elif ind + 2 >= len(_actual_args):
|
|
raise SimpleError(msgs.SYNTAX_ERROR_MSG)
|
|
else:
|
|
arg, _parsed = _actual_args[ind + 2], 2
|
|
if argument_name[1] == '+':
|
|
arg = Int.decode(arg)
|
|
return arg, _parsed
|
|
# Boolean parameters
|
|
if expected_following == 0:
|
|
return True, 0
|
|
|
|
if ind + expected_following >= len(_actual_args):
|
|
raise SimpleError(msgs.SYNTAX_ERROR_MSG)
|
|
temp_res = []
|
|
for i in range(expected_following):
|
|
curr_arg: Any = _actual_args[ind + i + 1]
|
|
if argument_name[i] == '+':
|
|
curr_arg = Int.decode(curr_arg)
|
|
elif argument_name[i] == '.':
|
|
curr_arg = Float.decode(curr_arg)
|
|
temp_res.append(curr_arg)
|
|
|
|
if len(temp_res) == 1:
|
|
return temp_res[0], expected_following
|
|
else:
|
|
return temp_res, expected_following
|
|
|
|
results: List[Any] = [_default_value(key) for key in expected]
|
|
left_args = []
|
|
i = 0
|
|
while i < len(actual_args):
|
|
found = False
|
|
for key in args_info:
|
|
if null_terminate(actual_args[i]) == key:
|
|
arg_position, _ = args_info[key]
|
|
results[arg_position], parsed = _parse_params(key, i, actual_args)
|
|
i += parsed
|
|
found = True
|
|
break
|
|
|
|
if not found:
|
|
if error_on_unexpected:
|
|
raise (
|
|
SimpleError(msgs.SYNTAX_ERROR_MSG)
|
|
if exception is None
|
|
else SimpleError(exception.format(actual_args[i])))
|
|
if left_from_first_unexpected:
|
|
return results, actual_args[i:]
|
|
left_args.append(actual_args[i])
|
|
i += 1
|
|
return results, left_args
|