1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6  */
7 
8 #include "orcus/env.hpp"
9 #include "orcus/json_parser.hpp"
10 #include "orcus/json_document_tree.hpp"
11 #include "orcus/config.hpp"
12 #include "orcus/pstring.hpp"
13 
14 #include <algorithm>
15 #include <sstream>
16 #include <boost/current_function.hpp>
17 
18 #include <Python.h>
19 
20 #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
21 
22 using namespace std;
23 
24 namespace orcus { namespace python {
25 
26 namespace {
27 
28 class python_json_error : public general_error
29 {
30 public:
python_json_error(const std::string & msg)31     python_json_error(const std::string& msg) : general_error("python_json_error", msg) {}
32 };
33 
34 struct module_state
35 {
36     PyObject* error;
37 };
38 
orcus_traverse(PyObject * m,visitproc visit,void * arg)39 int orcus_traverse(PyObject* m, visitproc visit, void* arg)
40 {
41     Py_VISIT(GETSTATE(m)->error);
42     return 0;
43 }
44 
orcus_clear(PyObject * m)45 int orcus_clear(PyObject* m)
46 {
47     Py_CLEAR(GETSTATE(m)->error);
48     return 0;
49 }
50 
51 struct parser_stack
52 {
53     PyObject* key;
54     PyObject* node;
55     json::node_t type;
56 
parser_stackorcus::python::__anon67c32f910111::parser_stack57     parser_stack(PyObject* _node, json::node_t _type) : key(nullptr), node(_node), type(_type) {}
58 };
59 
60 class json_parser_handler
61 {
62     PyObject* m_root;
63     std::vector<parser_stack> m_stack;
64 
push_value(PyObject * value)65     PyObject* push_value(PyObject* value)
66     {
67         if (!value)
68         {
69             std::ostringstream os;
70             os << BOOST_CURRENT_FUNCTION << ": Empty value is passed.";
71             throw python_json_error(os.str());
72         }
73 
74         if (m_stack.empty())
75         {
76             std::ostringstream os;
77             os << BOOST_CURRENT_FUNCTION << ": Stack is unexpectedly empty.";
78             throw python_json_error(os.str());
79         }
80 
81         parser_stack& cur = m_stack.back();
82 
83         switch (cur.type)
84         {
85             case json::node_t::array:
86             {
87                 PyList_Append(cur.node, value);
88                 return value;
89             }
90             break;
91             case json::node_t::object:
92             {
93                 assert(cur.key);
94                 PyDict_SetItem(cur.node, cur.key, value);
95                 cur.key = nullptr;
96                 return value;
97             }
98             break;
99             default:
100                 Py_DECREF(value);
101         }
102 
103         std::ostringstream os;
104         os << BOOST_CURRENT_FUNCTION << ": unstackable JSON value type.";
105         throw python_json_error(os.str());
106     }
107 
108 public:
json_parser_handler()109     json_parser_handler() : m_root(nullptr) {}
110 
~json_parser_handler()111     ~json_parser_handler()
112     {
113         if (m_root)
114             Py_XDECREF(m_root);
115 
116         std::for_each(m_stack.begin(), m_stack.end(),
117             [](parser_stack& ps)
118             {
119                 if (ps.key)
120                 {
121                     Py_XDECREF(ps.key);
122                     ps.key = nullptr;
123                 }
124             }
125         );
126     }
127 
begin_parse()128     void begin_parse()
129     {
130         if (m_root)
131         {
132             std::ostringstream os;
133             os << BOOST_CURRENT_FUNCTION << ": Root JSON value already exists.";
134             throw python_json_error(os.str());
135         }
136     }
137 
end_parse()138     void end_parse() {}
139 
begin_array()140     void begin_array()
141     {
142         if (m_root)
143         {
144             PyObject* array = push_value(PyList_New(0));
145             m_stack.push_back(parser_stack(array, json::node_t::array));
146         }
147         else
148         {
149             m_root = PyList_New(0);
150             m_stack.push_back(parser_stack(m_root, json::node_t::array));
151         }
152     }
153 
end_array()154     void end_array()
155     {
156         if (m_stack.empty())
157         {
158             std::ostringstream os;
159             os << BOOST_CURRENT_FUNCTION << ": Stack is unexpectedly empty.";
160             throw python_json_error(os.str());
161         }
162 
163         m_stack.pop_back();
164     }
165 
begin_object()166     void begin_object()
167     {
168         if (m_root)
169         {
170             PyObject* dict = push_value(PyDict_New());
171             m_stack.push_back(parser_stack(dict, json::node_t::object));
172         }
173         else
174         {
175             m_root = PyDict_New();
176             m_stack.push_back(parser_stack(m_root, json::node_t::object));
177         }
178     }
179 
object_key(const char * p,size_t len,bool transient)180     void object_key(const char* p, size_t len, bool transient)
181     {
182         parser_stack& cur = m_stack.back();
183         cur.key = PyUnicode_FromStringAndSize(p, len);
184     }
185 
end_object()186     void end_object()
187     {
188         if (m_stack.empty())
189         {
190             std::ostringstream os;
191             os << BOOST_CURRENT_FUNCTION << ": Stack is unexpectedly empty.";
192             throw python_json_error(os.str());
193         }
194 
195         m_stack.pop_back();
196     }
197 
boolean_true()198     void boolean_true()
199     {
200         Py_INCREF(Py_True);
201         push_value(Py_True);
202     }
203 
boolean_false()204     void boolean_false()
205     {
206         Py_INCREF(Py_False);
207         push_value(Py_False);
208     }
209 
null()210     void null()
211     {
212         Py_INCREF(Py_None);
213         push_value(Py_None);
214     }
215 
string(const char * p,size_t len,bool transient)216     void string(const char* p, size_t len, bool transient)
217     {
218         push_value(PyUnicode_FromStringAndSize(p, len));
219     }
220 
number(double val)221     void number(double val)
222     {
223         push_value(PyFloat_FromDouble(val));
224     }
225 
get_root()226     PyObject* get_root()
227     {
228         PyObject* o = m_root;
229         m_root = nullptr;
230         return o;
231     }
232 };
233 
json_loads(PyObject *,PyObject * args,PyObject * kwargs)234 PyObject* json_loads(PyObject* /*module*/, PyObject* args, PyObject* kwargs)
235 {
236     char* stream = nullptr;
237     static const char* kwlist[] = { "s", nullptr };
238     if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", const_cast<char**>(kwlist), &stream))
239     {
240         PyErr_SetString(PyExc_TypeError, "The method must be given a string.");
241         return nullptr;
242     }
243 
244     json_parser_handler hdl;
245     orcus::json_parser<json_parser_handler> parser(stream, strlen(stream), hdl);
246     try
247     {
248         parser.parse();
249         return hdl.get_root();
250     }
251     catch (const orcus::json::parse_error& e)
252     {
253         PyErr_SetString(PyExc_TypeError, e.what());
254     }
255     return nullptr;
256 }
257 
258 PyMethodDef orcus_methods[] =
259 {
260     { "loads", (PyCFunction)json_loads, METH_VARARGS | METH_KEYWORDS, "Load JSON string into a Python object." },
261     { nullptr, nullptr, 0, nullptr }
262 };
263 
264 struct PyModuleDef moduledef =
265 {
266     PyModuleDef_HEAD_INIT,
267     "_orcus_json",
268     nullptr,
269     sizeof(struct module_state),
270     orcus_methods,
271     nullptr,
272     orcus_traverse,
273     orcus_clear,
274     nullptr
275 };
276 
277 }
278 
279 }}
280 
281 extern "C" {
282 
PyInit__orcus_json()283 ORCUS_DLLPUBLIC PyObject* PyInit__orcus_json()
284 {
285     PyObject* m = PyModule_Create(&orcus::python::moduledef);
286     return m;
287 }
288 
289 }
290 
291 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
292