Archived
1
0
Fork 0
This repository has been archived on 2024-04-26. You can view files and clone it, but cannot push or open issues or pull requests.
akari-bot/core/parser/args.py

309 lines
12 KiB
Python
Raw Normal View History

import re
import traceback
2022-08-07 17:56:38 +00:00
from typing import List, Dict
from core.exceptions import InvalidTemplatePattern, InvalidCommandFormatError
class Pattern:
pass
class Template:
def __init__(self, args: List[Pattern], priority: int = 1):
self.args = args
self.priority = priority
def __str__(self):
return 'Template({})'.format(self.args)
def __repr__(self):
return self.__str__()
class ArgumentPattern(Pattern):
def __init__(self, name):
self.name = name
def __str__(self):
return 'ArgumentPattern("{}")'.format(self.name)
def __repr__(self):
return self.__str__()
class OptionalPattern(Pattern):
def __init__(self, flag: str, args: List[Template]):
self.flag = flag
self.args = args
def __str__(self):
return 'OptionalPattern("{}", {})'.format(self.flag, self.args)
def __repr__(self):
return self.__str__()
2022-08-07 17:56:38 +00:00
class DescPattern(Pattern):
def __init__(self, text: str):
self.text = text
def __str__(self):
return 'DescPattern("{}")'.format(self.text)
def __repr__(self):
return self.__str__()
class Argument:
def __init__(self, value: str):
self.value = value
class Optional:
def __init__(self, args: Dict[str, dict], flagged=False):
self.flagged = flagged
self.args = args
class MatchedResult:
2022-08-06 13:39:24 +00:00
def __init__(self, args: dict, original_template, priority: int = 1):
self.args = args
2022-08-06 13:39:24 +00:00
self.original_template = original_template
self.priority = priority
def __str__(self):
return 'MatchedResult({}, {})'.format(self.args, self.priority)
def __repr__(self):
return self.__str__()
def split_multi_arguments(lst: list):
new_lst = []
for x in lst:
spl = list(filter(None, re.split(r"(\(.*?\))", x)))
if len(spl) > 1:
for y in spl:
index_y = spl.index(y)
mat = re.match(r"\((.*?)\)", y)
if mat:
spl1 = mat.group(1).split('|')
for s in spl1:
cspl = spl.copy()
cspl.insert(index_y, s)
del cspl[index_y + 1]
new_lst.append(''.join(cspl))
else:
mat = re.match(r"\((.*?)\)", spl[0])
if mat:
spl1 = mat.group(1).split('|')
for s in spl1:
new_lst.append(s)
else:
new_lst.append(spl[0])
split_more = False
for n in new_lst:
if re.match(r"\((.*?)\)", n):
split_more = True
if split_more:
return split_multi_arguments(new_lst)
else:
return list(set(new_lst))
2022-08-06 13:39:24 +00:00
def parse_template(argv: List[str]) -> List[Template]:
templates = []
argv_ = []
for a in argv:
if isinstance(a, str):
2022-08-06 13:39:24 +00:00
spl = split_multi_arguments([a])
for split in spl:
2022-08-06 13:39:24 +00:00
argv_.append(split)
for a in argv_:
template = Template([])
patterns = filter(None, re.split(r'(\[.*?])|(<.*?>)|(\{.*?})| ', a))
for p in patterns:
strip_pattern = p.strip()
if strip_pattern == '':
continue
if strip_pattern.startswith('['):
if not strip_pattern.endswith(']'):
raise InvalidTemplatePattern(p)
optional_patterns = strip_pattern[1:-1].split(' ')
2022-08-08 14:55:02 +00:00
flag = None
args = []
if optional_patterns[0][0] == '-':
flag = optional_patterns[0]
args += optional_patterns[1:]
else:
args += optional_patterns
template.args.append(OptionalPattern(flag=flag,
args=parse_template(
[' '.join(args).strip()]) if len(
args) > 0 else []))
elif strip_pattern.startswith('{'):
if not strip_pattern.endswith('}'):
raise InvalidTemplatePattern(p)
2022-08-07 17:56:38 +00:00
template.args.append(DescPattern(strip_pattern[1:-1]))
else:
if strip_pattern.startswith('<') and not strip_pattern.endswith('>'):
raise InvalidTemplatePattern(p)
template.args.append(ArgumentPattern(strip_pattern))
templates.append(template)
return templates
2022-08-07 17:56:38 +00:00
def templates_to_str(templates: List[Template], with_desc=False) -> List[str]:
text = []
for template in templates:
arg_text = []
for arg in template.args:
if isinstance(arg, ArgumentPattern):
arg_text.append(arg.name)
elif isinstance(arg, OptionalPattern):
t = '['
2022-08-08 17:02:25 +00:00
if arg.flag is not None:
t += arg.flag
2022-08-07 17:56:38 +00:00
if arg.args:
2022-08-08 08:53:35 +00:00
t += ' ' + ' '.join(templates_to_str(arg.args))
2022-08-07 17:56:38 +00:00
t += ']'
arg_text.append(t)
elif isinstance(arg, DescPattern):
if with_desc:
arg_text.append('- ' + arg.text)
text.append(" ".join(arg_text))
return text
2022-08-06 13:39:24 +00:00
def parse_argv(argv: List[str], templates: List[Template]) -> MatchedResult:
matched_result = []
for template in templates:
try:
argv_copy = argv.copy() # copy argv to avoid changing original argv
parsed_argv = {}
original_template = template
2022-08-08 11:29:02 +00:00
afters = []
2022-08-07 17:56:38 +00:00
template.args = [x for x in template.args if not isinstance(x, DescPattern)]
2022-08-08 11:29:02 +00:00
if not template.args:
continue
for a in template.args: # optional first
if isinstance(a, OptionalPattern):
2022-08-08 14:55:02 +00:00
if a.flag is None:
afters.append(a.args[0])
2022-08-08 11:29:02 +00:00
continue
2022-08-08 14:55:02 +00:00
parsed_argv[a.flag] = Optional({}, flagged=False)
if a.flag in argv_copy: # if flag is in argv
if not a.args: # no args
parsed_argv[a.flag] = Optional({}, flagged=True)
argv_copy.remove(a.flag)
else: # has args
index_flag = argv_copy.index(a.flag)
len_t_args = len(a.args[0].args)
if len(argv_copy[index_flag:]) >= len_t_args:
sub_argv = argv_copy[index_flag + 1: index_flag + len_t_args + 1]
2022-08-06 13:39:24 +00:00
parsed_argv[a.flag] = Optional(parse_argv(sub_argv, a.args).args, flagged=True)
del argv_copy[index_flag: index_flag + len_t_args + 1]
for a in template.args:
if isinstance(a, ArgumentPattern):
if a.name.startswith('<'):
if len(argv_copy) > 0:
parsed_argv[a.name] = Argument(argv_copy[0])
del argv_copy[0]
else:
parsed_argv[a.name] = False
2022-08-09 16:38:03 +00:00
elif a.name == '...':
if len(template.args) - 1 == template.args.index(a):
afters.append(Template([a]))
else:
raise InvalidTemplatePattern('... must be the last argument')
else:
parsed_argv[a.name] = a.name in argv_copy
if parsed_argv[a.name]:
argv_copy.remove(a.name)
if argv_copy: # if there are still some argv left
2022-08-08 11:29:02 +00:00
if afters:
2022-08-08 14:55:02 +00:00
for arg in afters:
for sub_args in arg.args:
if isinstance(sub_args, ArgumentPattern):
if sub_args.name.startswith('<'):
if len(argv_copy) > 0:
parsed_argv[sub_args.name] = Argument(argv_copy[0])
del argv_copy[0]
else:
parsed_argv[sub_args.name] = False
2022-08-09 16:38:03 +00:00
elif sub_args.name == '...':
parsed_argv[sub_args.name] = [Argument(x) for x in argv_copy]
del argv_copy[:]
2022-08-08 14:55:02 +00:00
else:
parsed_argv[sub_args.name] = sub_args.name in argv_copy
if parsed_argv[sub_args.name]:
argv_copy.remove(sub_args.name)
2022-08-08 11:29:02 +00:00
if argv_copy:
2022-08-11 12:14:27 +00:00
template_arguments = [arg for arg in template.args if isinstance(arg, ArgumentPattern)]
if template_arguments:
if isinstance(template_arguments[-1], ArgumentPattern):
if template_arguments[-1].name.startswith('<'): # if last arg is variable
argv_keys = list(parsed_argv.keys())
parsed_argv[argv_keys[argv_keys.index(template_arguments[-1].name)]]\
.value += ' ' + ' '.join(argv_copy)
del argv_copy[0]
2022-08-06 13:39:24 +00:00
matched_result.append(MatchedResult(parsed_argv, original_template, template.priority))
except TypeError:
traceback.print_exc()
continue
filtered_result = []
for m in matched_result: # convert to result dict
filtered = False
args_ = m.args
for keys in args_:
if isinstance(args_[keys], Optional):
if not args_[keys].flagged:
args_[keys] = False
else:
if not args_[keys].args:
args_[keys] = True
else:
args_[keys] = args_[keys].args
elif isinstance(args_[keys], Argument):
args_[keys] = args_[keys].value
2022-08-09 16:38:03 +00:00
elif isinstance(args_[keys], list):
args_[keys] = [v.value for v in args_[keys] if isinstance(v, Argument)]
elif isinstance(args_[keys], bool):
if not args_[keys]:
filtered = True
break
if not filtered:
filtered_result.append(m)
if (len_filtered_result := len(filtered_result)) > 1: # if multiple result, select one by priority
priority_result = {}
for f in filtered_result:
priority = f.priority # base priority
for keys in f.args: # add priority for each argument
if f.args[keys] is True: # if argument is not any else
priority += 1
if priority not in priority_result:
priority_result[priority] = [f]
else:
priority_result[priority].append(f)
2022-08-07 17:56:38 +00:00
max_ = max(priority_result.keys())
if len(priority_result[max_]) > 1: # if still...
new_priority_result = {}
for p in priority_result[max_]:
new_priority = p.priority
for keys in p.args:
if p.args[keys]:
new_priority += 1
if new_priority not in new_priority_result:
new_priority_result[new_priority] = [p]
else:
new_priority_result[new_priority].append(p)
max_ = max(new_priority_result.keys())
return new_priority_result[max_][0]
return priority_result[max_][0]
elif len_filtered_result == 0:
raise InvalidCommandFormatError
else:
2022-08-06 13:39:24 +00:00
return filtered_result[0]