aboutsummaryrefslogtreecommitdiff
path: root/src/utils/parse/spec.py
blob: 2cde7410c3b0effc61f3cca984b3e46431a893e5 (about) (plain) (blame)
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
import enum, typing
from .time import duration

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: 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 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 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,
    "wordlower": SpecArgumentTypeWordLower,
    "string": SpecArgumentTypeString,
    "duration": SpecArgumentTypeDuration
}

class SpecArgument(object):
    def __init__(self, optional: bool, types: typing.List[SpecArgumentType]):
        self.optional = optional
        self.types = types

def argument_spec(spec: str) -> typing.List[SpecArgument]:
    out: typing.List[SpecArgument] = []
    for spec_argument in spec.split(" "):
        optional = spec_argument[0] == "?"

        argument_types: typing.List[SpecArgumentType] = []
        for argument_type in spec_argument[1:].split("|"):
            exported = ""
            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

            argument_types.append(argument_type_class(argument_type,
                argument_type_name, exported))
        out.append(SpecArgument(optional, argument_types))
    return out

def argument_spec_human(spec: typing.List[SpecArgument],
        context: SpecArgumentContext=SpecArgumentContext.ALL) -> str:
    out: typing.List[str] = []
    for spec_argument in spec:
        names: typing.List[str] = []
        for argument_type in spec_argument.types:
            if not (context&argument_type.context) == 0:
                name = argument_type.name() or argument_type.type
                if name:
                    names.append(name)

        if names:
            if spec_argument.optional:
                format = "[%s]"
            else:
                format = "<%s>"
            out.append(format % "|".join(names))
    return " ".join(out)