summaryrefslogtreecommitdiff
path: root/htmtl/expression_parser.py
blob: fc3aa8de93d671fd099e7ce55f5f7537e52ddbbc (plain)
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
from typing import Any
from .modifier import Modifier


class ExpressionParser:
    __data: dict[str, Any]
    __modifiers: list[type[Modifier]]

    def __init__(self, data: dict[str, Any], modifiers: list[type[Modifier]]) -> None:
        self.__data = data
        self.__modifiers = modifiers

    def parse(self, expression: str) -> Any:
        # no curly brackets means that the whole thing is an interpolation
        if expression.count("{") == 0 and expression.count("}") == 0:
            parsed_interpolation = self.__parse_interpolation(expression)

            return parsed_interpolation

        # uneven curly brackets means invalid syntax
        if expression.count("{") != expression.count("}"):
            return expression

        # otherwise only parts of it are
        parsed_expression = ""
        interp_start = None
        interp_end = None

        for idx, char in enumerate(expression):
            parsed_expression += char

            if char == "{":
                interp_start = idx

            if char == "}":
                interp_end = idx + 1

            if interp_start is not None and interp_end is not None:
                interp = expression[interp_start:interp_end]
                parsed_expression = parsed_expression.replace(interp, self.__parse_interpolation(interp[1:-1]))
                interp_start = None
                interp_end = None

        return parsed_expression

    def __parse_interpolation(self, interpolation: str) -> Any:
        parts = interpolation.split("|")
        value = self.__var_to_val(parts[0].strip())
        modifiers = [x.strip() for x in parts[1:]] if len(parts) > 1 else []

        for modifier in modifiers:
            modifier_name = ""
            args_start = None
            args_end = None
            modifier_opts = []

            for idx, char in enumerate(modifier):
                if char == "(":
                    args_start = idx + 1

                if char == ")":
                    args_end = idx

                if args_start is None and args_end is None:
                    modifier_name += char

            if args_start and args_end:
                args_str = modifier[args_start:args_end]
                modifier_opts = self.__parse_args_str_to_args(args_str)

            value = self.__modify_value(value, modifier_name, modifier_opts)

        return value

    @staticmethod
    def __parse_args_str_to_args(args_str) -> list[str | int | float | bool]:
        args = []

        for idx, char in enumerate(args_str):
            if len(args) == 0:
                args.append("")

            if char == "," and args[-1].count('"') % 2 == 0:
                args.append("")
            else:
                args[-1] += char

        parsed_args = []

        for arg in args:
            if arg.startswith("'") and arg.endswith("'"):
                parsed_args.append(arg[1:-1])
                continue

            if arg.startswith('"') and arg.endswith('"'):
                parsed_args.append(arg[1:-1])
                continue

            if all([x in "1234567890" for x in arg.lstrip("-")]):
                parsed_args.append(int(arg))
                continue

            if all([x in "1234567890." for x in arg.lstrip("-")]):
                parsed_args.append(float(arg))
                continue

            if arg == "true" or arg == "false":
                parsed_args.append(True if arg == "true" else False)

        return parsed_args

    def __modify_value(self, value: Any, modifier_name: str, modifier_opts: list[Any]) -> Any:
        for modifier in self.__modifiers:
            if modifier.__name__ == modifier_name:
                return modifier().modify(value, modifier_opts)

        return value

    def __var_to_val(self, var: str) -> Any:
        parts = var.split(".")
        value = self.__data

        for part in parts:
            if part in value:
                value = value[part]
            else:
                return None

        return value