aboutsummaryrefslogtreecommitdiff
blob: c5a8d5f5a3e1566e183e012e8d26fffa864dd1a8 (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
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
180
181
182
183
184
185
186
187
188
189
190
import sys
from rpython.rlib import rstring


#- type name manipulations --------------------------------------------------
def remove_const(name):
    tmplt_start = name.find("<")
    tmplt_stop  = name.rfind(">")
    if 0 <= tmplt_start and 0 <= tmplt_stop:
        # only replace const qualifying the class name, not in the template parameters
        return "".join([x.strip(" ") for x in rstring.split(name[:tmplt_start], "const")])+\
                     name[tmplt_start:tmplt_stop]+\
                     "".join([x.strip(" ") for x in rstring.split(name[tmplt_stop:], "const")])
    else:
        return "".join([x.strip(" ") for x in rstring.split(name, "const")])

def compound(name):
    name = remove_const(name)
    if name.endswith("]"):                       # array type?
        return "[]"
    i = _find_qualifier_index(name)
    return "".join(name[i:].split(" "))

def array_size(name):
    name = remove_const(name)
    if name.endswith("]"):                       # array type?
        idx = name.rfind("[")
        if 0 < idx:
            end = len(name)-1                    # len rather than -1 for rpython
            if 0 < end and (idx+1) < end:        # guarantee non-neg for rpython
                return int(name[idx+1:end])
    return -1

def _find_qualifier_index(name):
    i = len(name)
    # search from the back; note len(name) > 0 (so rtyper can use uint)
    for i in range(len(name) - 1, 0, -1):
        c = name[i]
        if c.isalnum() or c == ">" or c == "]":
            break
    return i + 1

def clean_type(name):
    # can't strip const early b/c name could be a template ...
    i = _find_qualifier_index(name)
    name = name[:i].strip(" ")

    idx = -1
    if name.endswith("]"):                       # array type?
        idx = name.rfind("[")
        if 0 < idx:
            name = name[:idx]
    elif name.endswith(">"):                     # template type?
        idx = name.find("<")
        if 0 < idx:      # always true, but just so that the translater knows
            n1 = remove_const(name[:idx])
            name = "".join([n1, name[idx:]])
    else:
        name = remove_const(name)
        name = name[:_find_qualifier_index(name)]
    return name.strip(' ')


#- operator mappings --------------------------------------------------------
_operator_mappings = {}

def map_operator_name(space, cppname, nargs, result_type):
    from pypy.module._cppyy import capi

    if cppname[0:8] == "operator":
        op = cppname[8:].strip(' ')

        # look for known mapping
        try:
            return _operator_mappings[op]
        except KeyError:
            pass

        # return-type dependent mapping
        if op == "[]":
            if result_type.find("const") != 0:
                cpd = compound(result_type)
                if cpd and cpd[len(cpd)-1] == "&":
                    return "__setitem__"
            return "__getitem__"

        # a couple more cases that depend on whether args were given

        if op == "*":   # dereference (not python) vs. multiplication
            return nargs and "__mul__" or "__deref__"

        if op == "+":   # unary positive vs. binary addition
            return nargs and  "__add__" or "__pos__"

        if op == "-":   # unary negative vs. binary subtraction
            return nargs and "__sub__" or "__neg__"

        if op == "++":  # prefix v.s. postfix increment (not python)
            return nargs and "__postinc__" or "__preinc__"

        if op == "--":  # prefix v.s. postfix decrement (not python)
            return nargs and "__postdec__" or "__predec__"

        # operator could have been a conversion using a typedef (this lookup
        # is put at the end only as it is unlikely and may trigger unwanted
        # errors in class loaders in the backend, because a typical operator
        # name is illegal as a class name)
        true_op = capi.c_resolve_name(space, op)

        try:
            return _operator_mappings[true_op]
        except KeyError:
            pass

    # might get here, as not all operator methods handled (although some with
    # no python equivalent, such as new, delete, etc., are simply retained)
    # TODO: perhaps absorb or "pythonify" these operators?
    return cppname

CPPYY__div__  = "__div__"
CPPYY__idiv__ = "__idiv__"
CPPYY__long__ = "__long__"
CPPYY__bool__ = "__nonzero__"

# _operator_mappings["[]"]  = "__setitem__"      # depends on return type
# _operator_mappings["+"]   = "__add__"          # depends on # of args (see __pos__)
# _operator_mappings["-"]   = "__sub__"          # id. (eq. __neg__)
# _operator_mappings["*"]   = "__mul__"          # double meaning in C++

# _operator_mappings["[]"]  = "__getitem__"      # depends on return type
_operator_mappings["()"]  = "__call__"
_operator_mappings["/"]   = CPPYY__div__
_operator_mappings["%"]   = "__mod__"
_operator_mappings["**"]  = "__pow__"            # not C++
_operator_mappings["<<"]  = "__lshift__"
_operator_mappings[">>"]  = "__rshift__"
_operator_mappings["&"]   = "__and__"
_operator_mappings["|"]   = "__or__"
_operator_mappings["^"]   = "__xor__"
_operator_mappings["~"]   = "__inv__"
_operator_mappings["!"]   = "__nonzero__"
_operator_mappings["+="]  = "__iadd__"
_operator_mappings["-="]  = "__isub__"
_operator_mappings["*="]  = "__imul__"
_operator_mappings["/="]  = CPPYY__idiv__
_operator_mappings["%="]  = "__imod__"
_operator_mappings["**="] = "__ipow__"
_operator_mappings["<<="] = "__ilshift__"
_operator_mappings[">>="] = "__irshift__"
_operator_mappings["&="]  = "__iand__"
_operator_mappings["|="]  = "__ior__"
_operator_mappings["^="]  = "__ixor__"
_operator_mappings["=="]  = "__eq__"
_operator_mappings["!="]  = "__ne__"
_operator_mappings[">"]   = "__gt__"
_operator_mappings["<"]   = "__lt__"
_operator_mappings[">="]  = "__ge__"
_operator_mappings["<="]  = "__le__"

# the following type mappings are "exact"
_operator_mappings["const char*"] = "__str__"
_operator_mappings["int"]         = "__int__"
_operator_mappings["long"]        = CPPYY__long__
_operator_mappings["double"]      = "__float__"

# the following type mappings are "okay"; the assumption is that they
# are not mixed up with the ones above or between themselves (and if
# they are, that it is done consistently)
_operator_mappings["char*"]              = "__str__"
_operator_mappings["short"]              = "__int__"
_operator_mappings["unsigned short"]     = "__int__"
_operator_mappings["unsigned int"]       = CPPYY__long__
_operator_mappings["unsigned long"]      = CPPYY__long__
_operator_mappings["long long"]          = CPPYY__long__
_operator_mappings["unsigned long long"] = CPPYY__long__
_operator_mappings["float"]              = "__float__"

_operator_mappings["bool"] = CPPYY__bool__

# the following are not python, but useful to expose
_operator_mappings["->"]  = "__follow__"
_operator_mappings["="]   = "__assign__"

# a bundle of operators that have no equivalent and are left "as-is" for now:
_operator_mappings["&&"]       = "&&"
_operator_mappings["||"]       = "||"
_operator_mappings["new"]      = "new"
_operator_mappings["delete"]   = "delete"
_operator_mappings["new[]"]    = "new[]"
_operator_mappings["delete[]"] = "delete[]"