source: trunk/ext/_syckmodule.c @ 4

Revision 4, 14.9 KB checked in by xi, 9 years ago (diff)

Add the module _syck and test suite.

Line 
1
2#include <Python.h>
3#include <syck.h>
4
5/* Global objects. */
6
7static PyObject *_syck_Error;
8
9static PyObject *_syck_ScalarKind;
10static PyObject *_syck_SeqKind;
11static PyObject *_syck_MapKind;
12
13/* Node type. */
14
15typedef struct {
16    PyObject_HEAD
17    PyObject *kind;
18    PyObject *type_id;
19    PyObject *value;
20} _syck_Node;
21
22static void
23_syck_Node_dealloc(_syck_Node *self)
24{
25    Py_XDECREF(self->kind);
26    Py_XDECREF(self->type_id);
27    Py_XDECREF(self->value);
28    PyObject_Del(self);
29}
30
31static PyObject *
32_syck_Node_getattr(_syck_Node *self, char *name)
33{
34    PyObject *value;
35
36    if (strcmp(name, "kind") == 0)
37        value = self->kind;
38    else if (strcmp(name, "type_id") == 0)
39        value = self->type_id;
40    else if (strcmp(name, "value") == 0)
41        value = self->value;
42    else {
43        PyErr_SetString(PyExc_AttributeError, name);
44        return NULL;
45    }
46
47    Py_INCREF(value);
48    return value;
49}
50
51static char _syck_Node_doc[] =
52    "Node object\n"
53    "\n"
54    "Attributes of the Node object:\n\n"
55    "kind -- 'scalar', 'seq', or 'map'.\n"
56    "type_id -- the tag of the node.\n"
57    "value -- the value of the node, a string, list or dict object.\n";
58
59static PyTypeObject _syck_NodeType = {
60    PyObject_HEAD_INIT(NULL)
61    0,                                  /* ob_size */
62    "_syck.Node",                       /* tp_name */
63    sizeof(_syck_Node),                 /* tp_basicsize */
64    0,                                  /* tp_itemsize */
65    (destructor)_syck_Node_dealloc,     /* tp_dealloc */
66    0,                                  /* tp_print */
67    (getattrfunc)_syck_Node_getattr,    /* tp_getattr */
68    0,                                  /* tp_setattr */
69    0,                                  /* tp_compare */
70    0,                                  /* tp_repr */
71    0,                                  /* tp_as_number */
72    0,                                  /* tp_as_sequence */
73    0,                                  /* tp_as_mapping */
74    0,                                  /* tp_hash */
75    0,                                  /* tp_call */
76    0,                                  /* tp_str */
77    0,                                  /* tp_getattro */
78    0,                                  /* tp_setattro */
79    0,                                  /* tp_as_buffer */
80    Py_TPFLAGS_DEFAULT,                 /* tp_flags */
81    _syck_Node_doc,                     /* tp_doc */
82};
83
84static PyObject *
85_syck_NewNode(PyObject *self, PyObject *args)
86{
87    if (!PyArg_ParseTuple(args, ":_syck.Node"))
88        return NULL;
89
90    PyErr_SetString(PyExc_TypeError, "Node object cannot be created explicitly. Use _syck.Parser.parse() instead.");
91    return NULL;
92}
93
94static char _syck_NewNode_doc[] =
95    "Node object cannot be created explicitly. Use _syck.Parser.parse() instead.";
96
97static PyObject *
98_syck_NewNode_FromValue(char *type_id, PyObject *value) /* Note: steals the reference to the value. */
99{
100    _syck_Node *self;
101    PyObject *kind;
102
103    self = PyObject_NEW(_syck_Node, &_syck_NodeType);
104    if (!self) {
105        Py_XDECREF(value);
106        return NULL;
107    }
108
109    self->value = value;
110
111    if (PyList_Check(value))
112        kind = _syck_SeqKind;
113    else if (PyDict_Check(value))
114        kind = _syck_MapKind;
115    else
116        kind = _syck_ScalarKind;
117    Py_INCREF(kind);
118    self->kind = kind;
119
120    if (type_id) {
121        self->type_id = PyString_FromString(type_id);
122        if (!self->type_id) {
123            Py_DECREF(self);
124            return NULL;
125        }
126    }
127    else {
128        Py_INCREF(Py_None);
129        self->type_id = Py_None;
130    }
131
132    return (PyObject *)self;
133}
134
135/* Parser type. */
136
137typedef struct {
138    PyObject_HEAD
139    PyObject *source;
140    PyObject *resolver;
141    PyObject *symbols;
142    int error_state;
143    int mark;
144    SyckParser *parser;
145} _syck_Parser;
146
147static PyObject *
148_syck_Parser_parse(_syck_Parser *self, PyObject *args)
149{
150    SYMID index;
151    PyObject *value;
152
153    if (!PyArg_ParseTuple(args, ":parse"))
154        return NULL;
155
156    if (!self->parser) {
157        PyErr_SetString(PyExc_TypeError, "Parser object is closed");
158        return NULL;
159    }
160
161    self->symbols = PyList_New(0);
162    if (!self->symbols) {
163        return NULL;
164    }
165
166    index = syck_parse(self->parser);
167    if (!self->error_state && !self->parser->eof) {
168        value = PyList_GetItem(self->symbols, index-1);
169    }
170
171    Py_DECREF(self->symbols);
172
173    if (self->error_state) {
174        self->error_state = 0;
175        return NULL;
176    }
177
178    if (self->parser->eof) {
179        Py_INCREF(Py_None);
180        return Py_None;
181    }
182   
183    return value;
184}
185
186static char _syck_Parser_parse_doc[] =
187    "Parses the next document in the YAML stream, return the root Node object or None on EOF.";
188
189static PyObject *
190_syck_Parser_parse_documents(_syck_Parser *self, PyObject *args)
191{
192    SYMID index;
193    PyObject *value = NULL;
194    PyObject *result = NULL;
195
196    if (!PyArg_ParseTuple(args, ":parse_document"))
197        return NULL;
198
199    if (!self->parser) {
200        PyErr_SetString(PyExc_TypeError, "Parser object is closed");
201        return NULL;
202    }
203
204    result = PyList_New(0);
205    if (!result) return NULL;
206
207    while (1) {
208
209        self->symbols = PyList_New(0);
210        if (!self->symbols) {
211            Py_DECREF(result);
212            return NULL;
213        };
214
215        index = syck_parse(self->parser);
216
217        if (!self->error_state && !self->parser->eof) {
218            value = PyList_GetItem(self->symbols, index-1);
219            if (!value) {
220                Py_DECREF(self->symbols);
221                Py_DECREF(result);
222                return NULL;
223            }
224            if (PyList_Append(result, value) < 0) {
225                Py_DECREF(self->symbols);
226                Py_DECREF(value);
227                Py_DECREF(result);
228                return NULL;
229            }
230            Py_DECREF(value);
231        }
232
233        Py_DECREF(self->symbols);
234
235        if (self->error_state) {
236            self->error_state = 0;
237            Py_DECREF(result);
238            return NULL;
239        }
240
241        if (self->parser->eof) break;
242    }
243
244    return result;
245}
246
247static char _syck_Parser_parse_documents_doc[] =
248    "Parses the entire YAML stream and returns list of documents.";
249
250static PyObject *
251_syck_Parser_close(_syck_Parser *self, PyObject *args)
252{
253    if (!PyArg_ParseTuple(args, ":close"))
254        return NULL;
255
256    Py_XDECREF(self->source);
257    self->source = NULL;
258
259    if (self->parser) {
260        syck_free_parser(self->parser);
261    }
262    self->parser = NULL;
263
264    Py_INCREF(Py_None);
265    return Py_None;
266}
267
268static char _syck_Parser_close_doc[] =
269    "Closes the parser and frees memory";
270
271static PyMethodDef _syck_Parser_methods[] = {
272    {"parse",  (PyCFunction)_syck_Parser_parse, METH_VARARGS, _syck_Parser_parse_doc},
273    {"parse_documents",  (PyCFunction)_syck_Parser_parse_documents, METH_VARARGS, _syck_Parser_parse_documents_doc},
274    {"close",  (PyCFunction)_syck_Parser_close, METH_VARARGS, _syck_Parser_close_doc},
275    {NULL}  /* Sentinel */
276};
277
278static void
279_syck_Parser_dealloc(_syck_Parser *self)
280{
281    Py_XDECREF(self->source);
282    if (self->parser) {
283        syck_free_parser(self->parser);
284    }
285    PyObject_Del(self);
286}
287
288static PyObject *
289_syck_Parser_getattr(_syck_Parser *self, char *name)
290{
291    return Py_FindMethod(_syck_Parser_methods, (PyObject *)self, name);
292}
293
294static char _syck_Parser_doc[] =
295    "_syck.Parser(yaml_string_or_file, implicit_typing=True, taguri_expansion=True) -> Parser object\n"
296    "\n"
297    "Methods of the Parser object:\n\n"
298    "parse() -- Parses the next document in the YAML stream, return the root Node object or None on EOF.\n"
299    "parse_documents() -- Parses the entire YAML stream and returns list of documents.\n"
300    "close() -- Closes the parser and frees memory.\n";
301
302static PyTypeObject _syck_ParserType = {
303    PyObject_HEAD_INIT(NULL)
304    0,                                  /* ob_size */
305    "_syck.Parser",                     /* tp_name */
306    sizeof(_syck_Parser),               /* tp_basicsize */
307    0,                                  /* tp_itemsize */
308    (destructor)_syck_Parser_dealloc,   /* tp_dealloc */
309    0,                                  /* tp_print */
310    (getattrfunc)_syck_Parser_getattr,  /* tp_getattr */
311    0,                                  /* tp_setattr */
312    0,                                  /* tp_compare */
313    0,                                  /* tp_repr */
314    0,                                  /* tp_as_number */
315    0,                                  /* tp_as_sequence */
316    0,                                  /* tp_as_mapping */
317    0,                                  /* tp_hash */
318    0,                                  /* tp_call */
319    0,                                  /* tp_str */
320    0,                                  /* tp_getattro */
321    0,                                  /* tp_setattro */
322    0,                                  /* tp_as_buffer */
323    Py_TPFLAGS_DEFAULT,                 /* tp_flags */
324    _syck_Parser_doc,                   /* tp_doc */
325};
326
327static long
328_syck_Parser_io_file_read(char *buf, SyckIoFile *file, long max_size, long skip)
329{
330    _syck_Parser *runtime = (_syck_Parser *)file->ptr;
331
332    PyObject *value;
333
334    char *str;
335    int length;
336
337    buf[skip] = '\0';
338
339    if (runtime->error_state) {
340        return skip;
341    }
342   
343    max_size -= skip;
344
345    value = PyObject_CallMethod(runtime->source, "read", "(i)", max_size);
346    if (!value) {
347        runtime->error_state = 1;
348        return skip;
349    }
350
351    if (!PyString_Check(value)) {
352        Py_DECREF(value);
353        PyErr_SetString(PyExc_TypeError, "file-like object should return a string");
354        runtime->error_state = 1;
355       
356        return skip;
357    }
358
359    str = PyString_AS_STRING(value);
360    length = PyString_GET_SIZE(value);
361    if (!length) {
362        Py_DECREF(value);
363        return skip;
364    }
365
366    if (length > max_size) {
367        Py_DECREF(value);
368        PyErr_SetString(PyExc_ValueError, "read returns an overly long string");
369        runtime->error_state = 1;
370        return skip;
371    }
372
373    memcpy(buf+skip, str, length);
374    length += skip;
375    buf[length] = '\0';
376
377    Py_DECREF(value);
378
379    return length;
380}
381
382static SYMID
383_syck_Parser_node_handler(SyckParser *parser, SyckNode *node)
384{
385    _syck_Parser *runtime = (_syck_Parser *)parser->bonus;
386
387    SYMID index;
388    PyObject *object = NULL;
389
390    PyObject *key, *value, *item;
391    int k;
392
393    if (runtime->error_state)
394        return 0;
395
396    switch (node->kind) {
397
398        case syck_str_kind:
399            object = PyString_FromStringAndSize(node->data.str->ptr,
400                    node->data.str->len);
401            if (!object) goto error;
402            break;
403
404        case syck_seq_kind:
405            object = PyList_New(node->data.list->idx);
406            if (!object) goto error;
407            for (k = 0; k < node->data.list->idx; k++) {
408                index = syck_seq_read(node, k);
409                item = PyList_GetItem(runtime->symbols, index-1);
410                if (!item) goto error;
411                Py_INCREF(item);
412                PyList_SET_ITEM(object, k, item);
413            }
414            break;
415
416        case syck_map_kind:
417            object = PyDict_New();
418            if (!object) goto error;
419            for (k = 0; k < node->data.pairs->idx; k++)
420            {
421                index = syck_map_read(node, map_key, k);
422                key = PyList_GetItem(runtime->symbols, index-1);
423                if (!key) goto error;
424                index = syck_map_read(node, map_value, k);
425                value = PyList_GetItem(runtime->symbols, index-1);
426                if (!value) goto error;
427                if (PyDict_SetItem(object, key, value) < 0)
428                    goto error;
429            }
430            break;
431    }
432
433    object = _syck_NewNode_FromValue(node->type_id, object);
434    if (!object) goto error;
435
436    if (PyList_Append(runtime->symbols, object) < 0)
437        goto error;
438
439    index = PyList_Size(runtime->symbols);
440    return index;
441
442error:
443    Py_XDECREF(object);
444    runtime->error_state = 1;
445    return 0;
446}
447
448static void
449_syck_Parser_error_handler(SyckParser *parser, char *str)
450{
451    _syck_Parser *runtime = (_syck_Parser *)parser->bonus;
452    PyObject *value;
453
454    if (runtime->error_state) return;
455
456    runtime->error_state = 1;
457
458    value = Py_BuildValue("(sii)", str, parser->linect, parser->cursor-parser->lineptr);
459    if (value) {
460        PyErr_SetObject(_syck_Error, value);
461    }
462}
463
464static PyObject *
465_syck_NewParser(PyObject *self, PyObject *args, PyObject *kwds)
466{
467    _syck_Parser *parser;
468    PyObject *source;
469    int implicit_typing = 1;
470    int taguri_expansion = 1;
471
472    static char *kwdlist[] = {"source", "implicit_typing", "taguri_expansion", NULL};
473
474    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|ii", kwdlist,
475                &source, &implicit_typing, &taguri_expansion))
476        return NULL;
477
478    parser = PyObject_NEW(_syck_Parser, &_syck_ParserType);
479    if (!parser)
480        return NULL;
481
482    Py_INCREF(source);
483    parser->source = source;
484    parser->error_state = 0;
485
486    parser->parser = syck_new_parser();
487    parser->parser->bonus = parser;
488
489    if (PyString_Check(source)) {
490        syck_parser_str_auto(parser->parser, PyString_AS_STRING(source), NULL);
491    }
492    else {
493        syck_parser_file(parser->parser, (FILE *)parser, _syck_Parser_io_file_read);
494    }
495    syck_parser_implicit_typing(parser->parser, implicit_typing);
496    syck_parser_taguri_expansion(parser->parser, taguri_expansion);
497
498    syck_parser_handler(parser->parser, _syck_Parser_node_handler);
499    syck_parser_error_handler(parser->parser, _syck_Parser_error_handler);
500    /*
501    syck_parser_bad_anchor_handler(parser, _syck_Parser_bad_anchor_handler);
502    */
503
504    return (PyObject *)parser;
505}
506
507static char _syck_NewParser_doc[] =
508    "Creates a new Parser object.";
509
510/* The module definitions. */
511
512static PyMethodDef _syck_methods[] = {
513    {"Node",  (PyCFunction)_syck_NewNode, METH_VARARGS, _syck_NewNode_doc},
514    {"Parser",  (PyCFunction)_syck_NewParser, METH_VARARGS|METH_KEYWORDS, _syck_NewParser_doc},
515    {NULL}  /* Sentinel */
516};
517
518static char _syck_doc[] =
519    "This module provides low-level access to the Syck parser and emitter.\n"
520    "Do not use this module directly, use the package 'syck' instead.\n";
521
522PyMODINIT_FUNC
523init_syck(void)
524{
525    PyObject *m;
526
527    _syck_NodeType.ob_type = &PyType_Type;
528    _syck_ParserType.ob_type = &PyType_Type;
529
530    _syck_Error = PyErr_NewException("_syck.error", NULL, NULL);
531    if (!_syck_Error)
532        return;
533
534    _syck_ScalarKind = PyString_FromString("scalar");
535    if (!_syck_ScalarKind)
536        return;
537    _syck_SeqKind = PyString_FromString("seq");
538    if (!_syck_SeqKind)
539        return;
540    _syck_MapKind = PyString_FromString("map");
541    if (!_syck_MapKind)
542        return;
543
544    m = Py_InitModule3("_syck", _syck_methods, _syck_doc);
545
546    Py_INCREF(_syck_Error);
547    if (!PyModule_AddObject(m, "error", _syck_Error) < 0)
548        return;
549
550    Py_INCREF(&_syck_NodeType);
551    if (PyModule_AddObject(m, "NodeType", (PyObject *)&_syck_NodeType) < 0)
552        return;
553
554    Py_INCREF(&_syck_ParserType);
555    if (PyModule_AddObject(m, "ParserType", (PyObject *)&_syck_ParserType) < 0)
556        return;
557}
558
Note: See TracBrowser for help on using the repository browser.