~jasper/type_stack_calc

ref: 6aca66fb1a7cf1e10f7dd3263f04537c8ff37590 type_stack_calc/type_stack_calc/extractor.py -rw-r--r-- 4.4 KiB
6aca66fb — Jasper den Ouden Apparently `to_c` can already read ahead one, hopefully its this simple... 11 months ago
                                                                                
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
#  Copyright (C) 2021 Jasper den Ouden.
#
#  This is free software: you can redistribute it and/or modify
#  it under the terms of the Affero GNU General Public License as published
#  by the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.

from type_stack_calc.pe.tmpvar import TmpVar

def _ignore(x): pass

def getattr_or_parent(obj, key):
    return getattr(getattr(obj, 'parent', None), key, None) \
        or getattr(obj, key, None)

class Extractor(tuple):
    """Extracts things in arguments of functions to the body the function is called from.

For instance `c_function(int x=4);` isn't allowed and `int x=4; c_function(x);` is.

Derives from tuple; first is a function that puts it inside the function, and the second puts it outside."""

    # NOTE: needs to be true or side-effects may be incorrectly omitted.
    always_extract_set = True

    def _extract_1(self, gen, obj, ename='extract'):
        # It has a function that extracts it.
        if fun := getattr_or_parent(obj, ename):
            try:
                fun(self, gen, obj)  # NOTE may take things out of the generator!
            except StopIteration as si:
                assert False, f"Extractor of object {obj} went too far\n  ({si})"
        else:  # It just goes into the function.
            self[0](obj)

    def extract_1(self, gen, obj=None):
        return self._extract_1(gen, next(gen) if obj is None else obj)

    def extract_1_w_var(self, gen, var, obj=None):
        ext = ExtractorWVar((self[0], self[1], var))
        return ext._extract_1(gen, next(gen) if obj is None else obj)

    def sub_ignore_here(self):
        return Extractor((_ignore, self[1]))

    def tmp_var(self, name, tp):
        return TmpVar(name, tp)

class ExtractorWVar(Extractor):
    """Like extractor but has a variable attached to it, which it immediately loses if used."""
    def extract_1(self, gen, obj=None):
        ext = Extractor(self[:2])
        return ext._extract_1(gen, next(gen) if obj is None else obj)
        
    def tmp_var(self, name, tp):
        return self[2]

def _flatten_into_cb(got, into_cb):
    """Enters entries into the callback.
Flattening part: if list, it will do the elements one by one."""
    if type(got) == list:  # Recurse. (exact, some derive from lists)
        for el in got: _flatten_into_cb(el, into_cb)
    elif got is not None:
        into_cb(got)  # Use the callback.

def figure_destructive(val, been=None):
    """Figures out if a value is destructive. Memoizes."""
    cur = getattr(val, 'destructive', None)  # See if it says so.
    if cur is not None:
        return cur

    acs = getattr(val, 'affects_comp', None)
    if acs is None:  # Cannot be in connection with anything destructive.
        val.destructive = False
        return False

    if been is None: been = set()  # Avoid infinite recursion.

    for c in acs.values():
        if id(c) in been: continue  # Already done this one.
        been.add(id(c))
        if n := figure_destructive(c, been):
            n = (n + 1 if isinstance(n, int) else 2)  # Count distance.
            val.destructive = n  # One destructive, then this too.
            return n 
    val.destructive = False  # No reason found for it to be destructive.
    return False

# TODO Parts that are extracted may be up for removal entirely as wel?
def extract(gen):
    """Extract from whole generator, reconstituting after."""
    tot_ret = []
    for obj in gen:
        ilist, extlist = [], []
        ext = Extractor((ilist.append, extlist.append))
        # Extract, NOTE: also moves generator forward.
        ext._extract_1(gen, obj, 'extract_top')

        ret = []
        for el in extlist:  # The externalized bit goes first.
            _flatten_into_cb(el, ret.append)  # Also flatten so underlying code doesn't have to.
        for el in ilist:  # Non-externalized portion next.
            _flatten_into_cb(el, ret.append)
        # Drop:
        # * If any definitional parts => assumed comes back as variants.
        # * If entirely `is_const`, which does not do anything.
        # * TODO if it does not do anything, but less obviously?
        if len(ret) >0 \
            and not any(getattr(r, 'definitional', None) for r in ret) \
            and not all(getattr(r, 'is_const', None) for r in ret):
            # and (any(map(figure_destructive, ret)) or True)  (TODO?)
            tot_ret.insert(0, ret)
    return tot_ret