1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
import enum, typing
from .time import duration
from .types import try_int
class SpecArgumentContext(enum.IntFlag):
CHANNEL = 1
PRIVATE = 2
ALL = 3
class SpecArgumentType(object):
context = SpecArgumentContext.ALL
def __init__(self, type_name: str, name: typing.Optional[str],
exported: typing.Optional[str]):
self.type = type_name
self._name = name
self.exported = exported
def name(self) -> typing.Optional[str]:
return self._name
def simple(self, args: typing.List[str]) -> typing.Tuple[typing.Any, int]:
return None, -1
def error(self) -> typing.Optional[str]:
return None
class SpecArgumentTypeWord(SpecArgumentType):
def simple(self, args: typing.List[str]) -> typing.Tuple[typing.Any, int]:
if args:
return args[0], 1
return None, 1
class SpecArgumentTypeAdditionalWord(SpecArgumentType):
def simple(self, args: typing.List[str]) -> typing.Tuple[typing.Any, int]:
if len(args) > 1:
return args[0], 1
return None, 1
class SpecArgumentTypeWordLower(SpecArgumentTypeWord):
def simple(self, args: typing.List[str]) -> typing.Tuple[typing.Any, int]:
out = SpecArgumentTypeWord.simple(self, args)
if out[0]:
return out[0].lower(), out[1]
return out
class SpecArgumentTypeString(SpecArgumentType):
def name(self):
return "%s ..." % SpecArgumentType.name(self)
def simple(self, args: typing.List[str]) -> typing.Tuple[typing.Any, int]:
return " ".join(args), len(args)
class SpecArgumentTypeTrimString(SpecArgumentTypeString):
def simple(self, args: typing.List[str]):
return SpecArgumentTypeString.simple(self, list(filter(None, args)))
class SpecArgumentTypeInt(SpecArgumentType):
def simple(self, args):
if args:
return try_int(args[0]), 1
return None, 1
class SpecArgumentTypeDuration(SpecArgumentType):
def name(self):
return "+%s" % (SpecArgumentType.name(self) or "duration")
def simple(self, args: typing.List[str]) -> typing.Tuple[typing.Any, int]:
if args:
return duration(args[0]), 1
return None, 1
def error(self) -> typing.Optional[str]:
return "Invalid timeframe"
class SpecArgumentPrivateType(SpecArgumentType):
context = SpecArgumentContext.PRIVATE
SPEC_ARGUMENT_TYPES = {
"word": SpecArgumentTypeWord,
"aword": SpecArgumentTypeAdditionalWord,
"wordlower": SpecArgumentTypeWordLower,
"string": SpecArgumentTypeString,
"tstring": SpecArgumentTypeTrimString,
"int": SpecArgumentTypeInt,
"duration": SpecArgumentTypeDuration
}
class SpecArgument(object):
consume = True
optional: bool = False
types: typing.List[SpecArgumentType] = []
@staticmethod
def parse(optional: bool, argument_types: typing.List[str]):
out: typing.List[SpecArgumentType] = []
for argument_type in argument_types:
exported = None
if "~" in argument_type:
exported = argument_type.split("~", 1)[1]
argument_type = argument_type.replace("~", "", 1)
argument_type_name: typing.Optional[str] = None
name_end = argument_type.find(">")
if argument_type.startswith("<") and name_end > 0:
argument_type_name = argument_type[1:name_end]
argument_type = argument_type[name_end+1:]
argument_type_class = SpecArgumentType
if argument_type in SPEC_ARGUMENT_TYPES:
argument_type_class = SPEC_ARGUMENT_TYPES[argument_type]
elif exported:
argument_type_class = SpecArgumentPrivateType
out.append(argument_type_class(argument_type,
argument_type_name, exported))
spec_argument = SpecArgument()
spec_argument.optional = optional
spec_argument.types = out
return spec_argument
def format(self, context: SpecArgumentContext) -> typing.Optional[str]:
if self.optional:
format = "[%s]"
else:
format = "<%s>"
names: typing.List[str] = []
for argument_type in self.types:
if not (context&argument_type.context) == 0:
name = argument_type.name() or argument_type.type
if name:
names.append(name)
if names:
return format % "|".join(names)
return None
class SpecArgumentTypeLiteral(SpecArgumentType):
def simple(self, args: typing.List[str]) -> typing.Tuple[typing.Any, int]:
if args and args[0] == self.name():
return args[0], 1
return None, 1
def error(self) -> typing.Optional[str]:
return None
class SpecLiteralArgument(SpecArgument):
@staticmethod
def parse(optional: bool, literals: typing.List[str]) -> SpecArgument:
spec_argument = SpecLiteralArgument()
spec_argument.optional = optional
spec_argument.types = [
SpecArgumentTypeLiteral("literal", l, None) for l in literals]
return spec_argument
def format(self, context: SpecArgumentContext) -> typing.Optional[str]:
return "|".join(t.name() or "" for t in self.types)
def argument_spec(spec: str) -> typing.List[SpecArgument]:
out: typing.List[SpecArgument] = []
for spec_argument in spec.split(" "):
optional = spec_argument[0] == "?"
if spec_argument[1] == "'":
out.append(SpecLiteralArgument.parse(optional,
spec_argument[2:].split(",")))
else:
consume = True
if spec_argument[1] == "-":
consume = False
spec_argument = spec_argument[1:]
spec_argument_obj = SpecArgument.parse(optional,
spec_argument[1:].split("|"))
spec_argument_obj.consume = consume
out.append(spec_argument_obj)
return out
def argument_spec_human(spec: typing.List[SpecArgument],
context: SpecArgumentContext=SpecArgumentContext.ALL) -> str:
arguments: typing.List[str] = []
for spec_argument in spec:
if spec_argument.consume:
out = spec_argument.format(context)
if out:
arguments.append(out)
return " ".join(arguments)
|