PageRenderTime 7ms CodeModel.GetById 1ms app.highlight 2ms RepoModel.GetById 1ms app.codeStats 0ms

/mordor/json.rl

http://github.com/mozy/mordor
Unknown | 465 lines | 419 code | 46 blank | 0 comment | 0 complexity | 91e6a643637aa52cdc8773ab4bff0c40 MD5 | raw file
  1// Copyright (c) 2009 - Mozy, Inc.
  2
  3#include "mordor/pch.h"
  4
  5#include "mordor/json.h"
  6
  7#include "mordor/assert.h"
  8#include "mordor/string.h"
  9
 10namespace Mordor {
 11namespace JSON {
 12
 13template<>
 14void
 15Value::set<bool>(const bool& value,
 16                 boost::enable_if<boost::is_arithmetic<bool> >::type *)
 17{
 18    (ValueBase &)*this = value;
 19}
 20
 21namespace {
 22class BoolVisitor : public boost::static_visitor<>
 23{
 24public:
 25    void operator()(const Array &array)
 26    {
 27        result = array.empty();
 28    }
 29
 30    void operator()(const Object &object)
 31    {
 32        result = object.empty();
 33    }
 34
 35    template <class T>
 36    void operator()(const T& value)
 37    {
 38        MORDOR_THROW_EXCEPTION(boost::bad_get());
 39    }
 40
 41    bool result;
 42};
 43
 44class SizeVisitor : public boost::static_visitor<>
 45{
 46public:
 47    void operator()(const Array &array)
 48    {
 49        result = array.size();
 50    }
 51
 52    void operator()(const Object &object)
 53    {
 54        result = object.size();
 55    }
 56
 57    template <class T>
 58    void operator()(const T& value)
 59    {
 60        MORDOR_THROW_EXCEPTION(boost::bad_get());
 61    }
 62
 63    size_t result;
 64};
 65}
 66
 67#if defined(GCC) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ > 6))
 68#pragma GCC diagnostic push
 69#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
 70#endif
 71
 72bool
 73Value::empty() const
 74{
 75    BoolVisitor visitor;
 76    boost::apply_visitor(visitor, *this);
 77    return visitor.result;
 78}
 79
 80size_t
 81Value::size() const
 82{
 83    SizeVisitor visitor;
 84    boost::apply_visitor(visitor, *this);
 85    return visitor.result;
 86}
 87
 88#if defined(GCC) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ > 6))
 89#pragma GCC diagnostic pop
 90#endif
 91
 92static Value g_blank;
 93
 94const Value &
 95Value::operator[](const std::string &key) const
 96{
 97    const Object &object = get<const Object>();
 98    const_iterator it = object.find(key);
 99    if (it == object.end())
100        return g_blank;
101    return it->second;
102}
103
104std::string unquote(const std::string &string)
105{
106    MORDOR_ASSERT(string.size() >= 2);
107    MORDOR_ASSERT(string[0] == '"');
108    MORDOR_ASSERT(string[string.size() - 1] == '"');
109    std::string result = string.substr(1, string.size() - 2);
110
111    const char *c = string.c_str() + 1;
112    const char *end = c + string.size() - 2;
113    bool differed = false;
114    utf16char utf16, priorUtf16 = 0;
115    while (c < end)
116    {
117        if (*c == '\\') {
118            MORDOR_ASSERT(c + 1 < end);
119            if (!differed) {
120                result.resize(c - (string.c_str() + 1));
121                differed = true;
122            }
123            ++c;
124            switch (*c) {
125                case '"':
126                case '\\':
127                case '/':
128                    result.append(1, *c);
129                    priorUtf16 = 0;
130                    break;
131                case 'b':
132                    result.append(1, '\b');
133                    priorUtf16 = 0;
134                    break;
135                case 'f':
136                    result.append(1, '\f');
137                    priorUtf16 = 0;
138                    break;
139                case 'n':
140                    result.append(1, '\n');
141                    priorUtf16 = 0;
142                    break;
143                case 'r':
144                    result.append(1, '\r');
145                    priorUtf16 = 0;
146                    break;
147                case 't':
148                    result.append(1, '\t');
149                    priorUtf16 = 0;
150                    break;
151                case 'u':
152                    MORDOR_ASSERT(c + 4 < end);
153                    utf16 = 0;
154                    ++c;
155                    for (int i = 0; i < 4; ++i) {
156                        utf16 *= 16;
157                        if (*c >= '0' && *c <= '9')
158                            utf16 += *c - '0';
159                        else if (*c >= 'a' && *c <= 'f')
160                            utf16 += *c - 'a' + 10;
161                        else if (*c >= 'A' && *c <= 'F')
162                            utf16 += *c - 'A' + 10;
163                        else
164                            MORDOR_NOTREACHED();
165                        ++c;
166                    }
167                    if (isHighSurrogate(priorUtf16) && isLowSurrogate(utf16)) {
168                        // Back out the incorrect UTF8 we previously saw
169                        result.resize(result.size() - 3);
170                        result.append(toUtf8(priorUtf16, utf16));
171                        priorUtf16 = 0;
172                    } else {
173                        result.append(toUtf8(utf16));
174                        priorUtf16 = utf16;
175                    }
176                    continue;
177                default:
178                    MORDOR_NOTREACHED();
179            }
180        } else if (differed) {
181            result.append(1, *c);
182            priorUtf16 = 0;
183        }
184        ++c;
185    }
186    return result;
187}
188
189%%{
190    machine json_parser;
191
192    action mark { mark = fpc;}
193    prepush {
194        prepush();
195    }
196    postpop {
197        postpop();
198    }
199
200    ws = ' ' | '\t' | '\r' | '\n';
201
202    unescaped = (any - ('"' | '\\') - cntrl);
203    char = unescaped | ('\\' ('"' | '\\' | '/' | 'b' | 'f' | 'n' | 'r' | 't' | ('u' [0-9A-Za-z]{4})));
204    string = '"' char* '"';
205
206    action begin_number
207    {
208        m_nonIntegral = false;
209    }
210    action set_non_integral
211    {
212        m_nonIntegral = true;
213    }
214    action parse_number
215    {
216        if (m_nonIntegral) {
217            *m_stack.top() = strtod(mark, NULL);
218        } else {
219            *m_stack.top() = strtoll(mark, NULL, 10);
220        }
221    }
222
223    int = (digit | ([1-9] digit*));
224    frac = '.' >set_non_integral digit+;
225    exp = 'e'i >set_non_integral ('+' | '-')? digit+;
226    number = ('-'? int frac? exp?) >mark >begin_number %parse_number;
227
228    action parse_string
229    {
230        *m_stack.top() = unquote(std::string(mark, fpc - mark));
231    }
232    action call_parse_object
233    {
234        *m_stack.top() = Object();
235        fcall *json_parser_en_parse_object;
236    }
237    action call_parse_array
238    {
239        *m_stack.top() = Array();
240        fcall *json_parser_en_parse_array;
241    }
242    action parse_true
243    {
244        *m_stack.top() = true;
245    }
246    action parse_false
247    {
248        *m_stack.top() = false;
249    }
250    action parse_null
251    {
252        *m_stack.top() = boost::blank();
253    }
254    value = string >mark %parse_string | number | '{' @call_parse_object |
255        '[' @call_parse_array | 'true' @parse_true | 'false' @parse_false |
256        'null' @parse_null;
257
258    object = '{' ws* (string ws* ':' ws* value ws* (',' ws* string ws* ':' ws* value ws*)*)? '}';
259    array = '[' ws* (value ws* (',' ws* value ws*)*)? ']';
260
261    action new_key
262
263    {
264        m_stack.push(&boost::get<Object>(*m_stack.top())[unquote(std::string(mark, fpc - mark))]);
265    }
266    action new_element
267    {
268        boost::get<Array>(*m_stack.top()).push_back(Value());
269        m_stack.push(&boost::get<Array>(*m_stack.top()).back());
270    }
271    action pop_stack
272    {
273        m_stack.pop();
274    }
275    action ret {
276        fret;
277    }
278
279    parse_object := parse_object_lbl: ws* (string >mark %new_key ws* ':' ws* value %pop_stack ws* (',' ws* string >mark %new_key ws* ':' ws* value %pop_stack ws*)*)? '}' @ret;
280    parse_array := parse_array_lbl: ws* (value >new_element %pop_stack ws* (',' ws* value >new_element %pop_stack ws*)*)? ']' @ret;
281
282    main := ws* value ws*;
283    write data;
284}%%
285
286void
287Parser::init()
288{
289    while (m_stack.size() > 1)
290        m_stack.pop();
291    *m_stack.top() = boost::blank();
292    RagelParserWithStack::init();
293    %% write init;
294}
295
296void
297Parser::exec()
298{
299#ifdef MSVC
300#pragma warning(push)
301#pragma warning(disable : 4244)
302#endif
303        %% write exec;
304#ifdef MSVC
305#pragma warning(pop)
306#endif
307}
308
309bool
310Parser::final() const
311{
312    return cs >= json_parser_first_final;
313}
314
315bool
316Parser::error() const
317{
318    return cs == json_parser_error;
319}
320
321std::string
322quote(const std::string &str)
323{
324    std::string result;
325    result.reserve(str.length() + 2);
326    result.append(1, '"');
327
328    static const char *escaped =
329        "\0\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
330        "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
331        "\\\"";
332
333    size_t lastEscape = 0;
334    size_t nextEscape = str.find_first_of(escaped, 0, 34);
335    std::ostringstream os;
336    os.fill('0');
337    os << std::hex << std::setw(4);
338    while (nextEscape != std::string::npos) {
339        result.append(str.substr(lastEscape, nextEscape - lastEscape));
340        result.append(1, '\\');
341        switch (str[nextEscape]) {
342            case '"':
343            case '\\':
344                result.append(1, str[nextEscape]);
345                break;
346            case '\b':
347                result.append(1, 'b');
348                break;
349            case '\f':
350                result.append(1, 'f');
351                break;
352            case '\n':
353                result.append(1, 'n');
354                break;
355            case '\r':
356                result.append(1, 'r');
357                break;
358            case '\t':
359                result.append(1, 't');
360                break;
361            default:
362                result.append(1, 'u');
363                os.str();
364                os << (int)str[nextEscape];
365                result.append(os.str());
366                break;
367        }
368        lastEscape = nextEscape + 1;
369        nextEscape = str.find_first_of(escaped, lastEscape, 34);
370    }
371    result.append(str.substr(lastEscape));
372    result.append(1, '"');
373    return result;
374}
375
376namespace {
377class JSONVisitor : public boost::static_visitor<>
378{
379public:
380    JSONVisitor(std::ostream &os)
381    : os(os),
382      depth(0)
383    {}
384
385    void operator()(const boost::blank &b)
386    {
387        os << "null";
388    }
389    void operator()(const bool &b)
390    {
391        os << (b ? "true" : "false");
392    }
393    template <class T>
394    void operator()(const T &t)
395    {
396        os << t;
397    }
398    void operator()(const std::string &str)
399    {
400        os << quote(str);
401    }
402    void operator()(const Object &object)
403    {
404        if (object.empty()) {
405            os << "{ }";
406        } else {
407            ++depth;
408            std::string prefix(depth * 4, ' ');
409            os << "{\n";
410            for (Object::const_iterator it(object.begin());
411                it != object.end();
412                ++it) {
413                if (it != object.begin())
414                    os << ",\n";
415                os << prefix << quote(it->first) << " : ";
416                boost::apply_visitor(*this, it->second);
417            }
418            --depth;
419            prefix.clear();
420            prefix.append(depth * 4, ' ');
421            os << '\n' << prefix << "}";
422        }
423    }
424    void operator()(const Array &array)
425    {
426        if (array.empty()) {
427            os << "[ ]";
428        } else {
429            ++depth;
430            std::string prefix(depth * 4, ' ');
431            os << "[\n";
432            for (Array::const_iterator it(array.begin());
433                it != array.end();
434                ++it) {
435                if (it != array.begin())
436                    os << ",\n";
437                os << prefix;
438                boost::apply_visitor(*this, *it);
439            }
440            --depth;
441            prefix.clear();
442            prefix.append(depth * 4, ' ');
443            os << '\n' << prefix << "]";
444        }
445    }
446
447    std::ostream &os;
448    int depth;
449};
450}
451
452std::ostream &operator <<(std::ostream &os, const Value &json)
453{
454    JSONVisitor visitor(os);
455    boost::apply_visitor(visitor, json);
456    return os;
457}
458
459bool isBlank(Value value)
460{
461    boost::blank *isItBlank = boost::get<boost::blank>(&value);
462    return  !!isItBlank;
463}
464
465}}