aboutsummaryrefslogtreecommitdiff
path: root/pyecsca/ec/op.py
blob: 72b06f313f44bc9ef5963a02542c8735ebdd19e0 (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
from ast import Module, walk, Name, BinOp, Constant, operator, Mult, Div, Add, Sub, Pow, Assign
from types import CodeType
from typing import FrozenSet, Optional, cast

from .mod import Mod


class CodeOp(object):
    result: str
    parameters: FrozenSet[str]
    variables: FrozenSet[str]
    code: Module
    operator: Optional[operator]
    compiled: CodeType

    def __init__(self, code: Module):
        self.code = code
        assign = cast(Assign, code.body[0])
        self.result = cast(Name, assign.targets[0]).id
        params = set()
        variables = set()
        constants = set()
        op = None
        for node in walk(assign.value):
            if isinstance(node, Name):
                name = node.id
                if name.isupper():
                    variables.add(name)
                else:
                    params.add(name)
            elif isinstance(node, Constant):
                constants.add(node.value)
            elif isinstance(node, BinOp):
                op = node.op
                self.left = self.__to_name(node.left)
                self.right = self.__to_name(node.right)
        if op is None and len(constants) == 1:
            self.left = next(iter(constants))
            self.right = None
        self.operator = op
        self.parameters = frozenset(params)
        self.variables = frozenset(variables)
        self.constants = frozenset(constants)
        self.compiled = compile(self.code, "", mode="exec")

    def __to_name(self, node):
        if isinstance(node, Name):
            return node.id
        elif isinstance(node, Constant):
            return node.value
        else:
            return None

    def __to_opsymbol(self, op):
        if isinstance(op, Mult):
            return "*"
        elif isinstance(op, Div):
            return "/"
        elif isinstance(op, Add):
            return "+"
        elif isinstance(op, Sub):
            return "-"
        elif isinstance(op, Pow):
            return "^"
        return ""

    def __str__(self):
        return f"{self.result} = {self.left}{self.__to_opsymbol(self.operator)}{self.right}"

    def __repr__(self):
        return f"CodeOp({self.result} = f(params={self.parameters}, vars={self.variables}, consts={self.constants}))"

    def __call__(self, *args, **kwargs: Mod) -> Mod:
        """Execute this operation with kwargs."""
        loc = dict(kwargs)
        exec(self.compiled, {}, loc)
        return loc[self.result]