1"""Functions."""
2
3from framer import template
4from framer.util import cstring, unindent
5
6METH_O = "METH_O"
7METH_NOARGS = "METH_NOARGS"
8METH_VARARGS = "METH_VARARGS"
9
10def parsefmt(fmt):
11    for c in fmt:
12        if c == '|':
13            continue
14        yield c
15
16class Argument:
17
18    def __init__(self, name):
19        self.name = name
20        self.ctype = "PyObject *"
21        self.default = None
22
23    def __str__(self):
24        return "%s%s" % (self.ctype, self.name)
25
26    def setfmt(self, code):
27        self.ctype = self._codes[code]
28        if self.ctype[-1] != "*":
29            self.ctype += " "
30
31    _codes = {"O": "PyObject *",
32              "i": "int",
33              }
34
35    def decl(self):
36        if self.default is None:
37            return str(self) + ";"
38        else:
39            return "%s = %s;" % (self, self.default)
40
41class _ArgumentList(object):
42
43    # these instance variables should be initialized by subclasses
44    ml_meth = None
45    fmt = None
46
47    def __init__(self, args):
48        self.args = map(Argument, args)
49
50    def __len__(self):
51        return len(self.args)
52
53    def __getitem__(self, i):
54        return self.args[i]
55
56    def dump_decls(self, f):
57        pass
58
59class NoArgs(_ArgumentList):
60
61    def __init__(self, args):
62        assert len(args) == 0
63        super(NoArgs, self).__init__(args)
64        self.ml_meth = METH_NOARGS
65
66    def c_args(self):
67        return "PyObject *self"
68
69class OneArg(_ArgumentList):
70
71    def __init__(self, args):
72        assert len(args) == 1
73        super(OneArg, self).__init__(args)
74        self.ml_meth = METH_O
75
76    def c_args(self):
77        return "PyObject *self, %s" % self.args[0]
78
79class VarArgs(_ArgumentList):
80
81    def __init__(self, args, fmt=None):
82        super(VarArgs, self).__init__(args)
83        self.ml_meth = METH_VARARGS
84        if fmt is not None:
85            self.fmt = fmt
86            i = 0
87            for code in parsefmt(fmt):
88                self.args[i].setfmt(code)
89                i += 1
90
91    def c_args(self):
92        return "PyObject *self, PyObject *args"
93
94    def targets(self):
95        return ", ".join(["&%s" % a.name for a in self.args])
96
97    def dump_decls(self, f):
98        for a in self.args:
99            print >> f, "        %s" % a.decl()
100
101def ArgumentList(func, method):
102    code = func.func_code
103    args = code.co_varnames[:code.co_argcount]
104    if method:
105        args = args[1:]
106    pyarg = getattr(func, "pyarg", None)
107    if pyarg is not None:
108        args = VarArgs(args, pyarg)
109        if func.func_defaults:
110            L = list(func.func_defaults)
111            ndefault = len(L)
112            i = len(args) - ndefault
113            while L:
114                args[i].default = L.pop(0)
115        return args
116    else:
117        if len(args) == 0:
118            return NoArgs(args)
119        elif len(args) == 1:
120            return OneArg(args)
121        else:
122            return VarArgs(args)
123
124class Function:
125
126    method = False
127
128    def __init__(self, func, parent):
129        self._func = func
130        self._parent = parent
131        self.analyze()
132        self.initvars()
133
134    def dump(self, f):
135        def p(templ, vars=None): # helper function to generate output
136            if vars is None:
137                vars = self.vars
138            print >> f, templ % vars
139
140        if self.__doc__:
141            p(template.docstring)
142
143        d = {"name" : self.vars["CName"],
144             "args" : self.args.c_args(),
145             }
146        p(template.funcdef_start, d)
147
148        self.args.dump_decls(f)
149
150        if self.args.ml_meth == METH_VARARGS:
151            p(template.varargs)
152
153        p(template.funcdef_end)
154
155    def analyze(self):
156        self.__doc__ = self._func.__doc__
157        self.args = ArgumentList(self._func, self.method)
158
159    def initvars(self):
160        v = self.vars = {}
161        v["PythonName"] = self._func.__name__
162        s = v["CName"] = "%s_%s" % (self._parent.name, self._func.__name__)
163        v["DocstringVar"] = s + "_doc"
164        v["MethType"] = self.args.ml_meth
165        if self.__doc__:
166            v["Docstring"] = cstring(unindent(self.__doc__))
167        if self.args.fmt is not None:
168            v["ArgParse"] = self.args.fmt
169            v["ArgTargets"] = self.args.targets()
170
171class Method(Function):
172
173    method = True
174