Danny Robson
e7c108c770
This mostly just complicates type manipulations. We can add it back in if performance actually warrants it, or if it becomes an official qualifier.
214 lines
5.7 KiB
Ragel
214 lines
5.7 KiB
Ragel
/*
|
|
* This file is part of libgim.
|
|
*
|
|
* libgim is free software: you can redistribute it and/or modify it under the
|
|
* terms of the GNU General Public License as published by the Free Software
|
|
* Foundation, either version 3 of the License, or (at your option) any later
|
|
* version.
|
|
*
|
|
* libgim is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with libgim. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Copyright 2010-2017 Danny Robson <danny@nerdcruft.net>
|
|
*/
|
|
|
|
#include "json/flat.hpp"
|
|
|
|
#include "debug.hpp"
|
|
#include "json/except.hpp"
|
|
#include "preprocessor.hpp"
|
|
|
|
#include <deque>
|
|
#include <iostream>
|
|
|
|
// We generate some really old style C code via ragel here, so we have to
|
|
// disable some noisy warnings (doubly so given -Werror)
|
|
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
%%{
|
|
# JSON (rfc7159)
|
|
machine json;
|
|
|
|
action trace { if (false) std::cerr << *p; }
|
|
action success { __success = true; }
|
|
action failure { }
|
|
|
|
action new_line { ++line; }
|
|
|
|
action first { parsed.push_back ({ type::UNKNOWN, p, p}); }
|
|
action last { parsed.back ().last = p; }
|
|
|
|
action tag_nul { parsed.back ().tag = type::NUL; }
|
|
action tag_boolean { parsed.back ().tag = type::BOOLEAN; }
|
|
action tag_string { parsed.back ().tag = type::STRING; }
|
|
action tag_integer { parsed.back ().tag = type::INTEGER; }
|
|
action tag_real { parsed.back ().tag = type::REAL; }
|
|
|
|
action tag_object_begin { parsed.push_back ({ type::OBJECT_BEGIN, p, p + 1 }); }
|
|
action tag_object_end { parsed.push_back ({ type::OBJECT_END, p, p + 1 }); }
|
|
action tag_array_begin { parsed.push_back ({ type::ARRAY_BEGIN, p, p + 1 }); }
|
|
action tag_array_end { parsed.push_back ({ type::ARRAY_END, p, p + 1 }); }
|
|
|
|
# Line counter
|
|
lines = (
|
|
any | '\n' @new_line
|
|
)*;
|
|
|
|
# UTF-8 (rfc3629)
|
|
utf8_tail = 0x80..0xbf;
|
|
|
|
utf8_1 = 0x00..0x7f;
|
|
utf8_2 = 0xc2..0xdf utf8_tail;
|
|
utf8_3 = 0xe0 0xa0..0xbf utf8_tail |
|
|
0xe1..0xec utf8_tail{2} |
|
|
0xed 0x80..0x9f utf8_tail |
|
|
0xee..0xef utf8_tail{2};
|
|
utf8_4 = 0xf0 0x90..0xbf utf8_tail{2} |
|
|
0xf1..0xf3 utf8_tail{3} |
|
|
0xf4 0x80..0x8f utf8_tail{2};
|
|
|
|
|
|
utf8 = utf8_1 | utf8_2 | utf8_3 | utf8_4;
|
|
|
|
# Utility
|
|
ws = 0x20 | 0x09 | 0x0A | 0x0D;
|
|
array_start = '[';
|
|
array_end = ']';
|
|
object_start = '{';
|
|
object_end = '}';
|
|
|
|
# Strings
|
|
char =
|
|
(utf8 - ["\\])
|
|
| "\\" (
|
|
[\\"/bfnrt]
|
|
| "u" xdigit{4}
|
|
)
|
|
;
|
|
|
|
string = ('"' char* '"') >first >tag_string %*last;
|
|
|
|
# numbers
|
|
int = '0' | [1-9] digit*;
|
|
|
|
frac = '.' digit+;
|
|
e = 'e'i[+\-]?;
|
|
exp = e digit+;
|
|
|
|
number = (
|
|
'-'?
|
|
int
|
|
(frac >tag_real)?
|
|
exp?
|
|
) >tag_integer;
|
|
|
|
# wrapper types
|
|
array = array_start @{ fhold; fcall array_members; } array_end;
|
|
object = object_start @{ fhold; fcall object_members; } object_end;
|
|
|
|
# simple types; case sensitive literals
|
|
bool = ("true" | "false") >tag_boolean;
|
|
nul = "null" >tag_nul;
|
|
literal = bool | nul;
|
|
|
|
value = object | array | (number | string | literal) >first %last;
|
|
|
|
# Complex
|
|
member = string ws* ':' ws* value;
|
|
|
|
array_members := ((
|
|
array_start >tag_array_begin ws* (value ws* (',' ws* value ws*)*)? array_end >tag_array_end
|
|
) & lines)
|
|
@{ fhold; fret; } $trace $!failure;
|
|
|
|
object_members := ((
|
|
object_start >tag_object_begin ws* (member ws* (',' ws* member ws*)*)? object_end >tag_object_end
|
|
) & lines)
|
|
@{ fhold; fret; } $trace $!failure;
|
|
|
|
# meta types
|
|
document := ((ws* value ws*) & lines)
|
|
%success
|
|
$!failure
|
|
$trace;
|
|
|
|
variable stack ragelstack;
|
|
prepush { ragelstack.push_back (0); }
|
|
postpop { ragelstack.pop_back (); }
|
|
|
|
write data;
|
|
}%%
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
template <typename T>
|
|
std::vector<json::flat::item<T>>
|
|
json::flat::parse (const util::view<T> src)
|
|
{
|
|
auto p = src.cbegin ();
|
|
auto pe = src.cend ();
|
|
auto eof = pe;
|
|
|
|
std::deque<int> ragelstack;
|
|
std::vector<item<T>> parsed;
|
|
|
|
size_t line = 0;
|
|
int cs, top;
|
|
bool __success = false;
|
|
|
|
%%write init;
|
|
%%write exec;
|
|
|
|
if (!__success)
|
|
throw json::parse_error ("parse error", line);
|
|
|
|
return parsed;
|
|
}
|
|
|
|
#define INSTANTIATE(KLASS) \
|
|
template \
|
|
std::vector<json::flat::item<KLASS>> \
|
|
json::flat::parse (util::view<KLASS>);
|
|
|
|
MAP0(INSTANTIATE,
|
|
std::string::iterator,
|
|
std::string::const_iterator,
|
|
const char*,
|
|
char *
|
|
)
|
|
|
|
#undef INSTANTIATE
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
std::ostream&
|
|
json::flat::operator<< (std::ostream &os, json::flat::type t)
|
|
{
|
|
using T = json::flat::type;
|
|
|
|
switch (t) {
|
|
case T::STRING: return os << "STRING";
|
|
case T::NUL: return os << "NUL";
|
|
case T::BOOLEAN: return os << "BOOLEAN";
|
|
case T::INTEGER: return os << "INTEGER";
|
|
case T::REAL: return os << "REAL";
|
|
|
|
case T::OBJECT_BEGIN: return os << "OBJECT_BEGIN";
|
|
case T::OBJECT_END: return os << "OBJECT_END";
|
|
case T::ARRAY_BEGIN: return os << "ARRAY_BEGIN";
|
|
case T::ARRAY_END: return os << "ARRAY_END";
|
|
|
|
case T::UNKNOWN: ;
|
|
// fall out
|
|
}
|
|
unreachable ();
|
|
}
|
|
|