1 /* This file is part of QJson
3 * Copyright (C) 2008 Flavio Castelli <flavio.castelli@gmail.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License version 2.1, as published by the Free Software Foundation.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
21 #include "qjson_debug.h"
22 #include "json_scanner.h"
23 #include "json_parser.hh"
27 #include <QtCore/QDebug>
28 #include <QtCore/QRegExp>
32 bool ishexnstring(const QString& string) {
33 for (int i = 0; i < string.length(); i++) {
34 if (isxdigit(string[i] == 0))
40 JSonScanner::JSonScanner(QIODevice* io)
41 : m_allowSpecialNumbers(false),
44 m_quotmarkClosed = true;
48 void JSonScanner::allowSpecialNumbers(bool allow) {
49 m_allowSpecialNumbers = allow;
52 static QString unescape( const QByteArray& ba, bool* ok ) {
58 for ( int i = 0, size = ba.size(); i < size; ++i ) {
59 const char ch = ba[i];
85 res += QString::fromUtf8( seg );
93 const QString hex_digit1 = QString::fromUtf8( ba.mid( i + 1, 2 ) );
94 const QString hex_digit2 = QString::fromUtf8( ba.mid( i + 3, 2 ) );
97 if ( !ishexnstring( hex_digit1 ) || !ishexnstring( hex_digit2 ) ) {
98 qCritical() << "Not an hex string:" << hex_digit1 << hex_digit2;
102 const ushort hex_code1 = hex_digit1.toShort( &hexOk, 16 );
104 qCritical() << "error converting hex value to short:" << hex_digit1;
107 const ushort hex_code2 = hex_digit2.toShort( &hexOk, 16 );
109 qCritical() << "error converting hex value to short:" << hex_digit2;
113 res += QChar(hex_code2, hex_code1);
125 res += QString::fromUtf8( seg );
130 int JSonScanner::yylex(YYSTYPE* yylval, yy::location *yylloc)
134 if (!m_io->isOpen()) {
135 qCritical() << "JSonScanner::yylex - io device is not open";
144 qjsonDebug() << "JSonScanner::yylex - yy::json_parser::token::END";
145 return yy::json_parser::token::END;
148 ret = m_io->getChar(&ch);
151 qCritical() << "JSonScanner::yylex - error reading from io device";
155 qjsonDebug() << "JSonScanner::yylex - got |" << ch << "|";
158 if (ch == '\n' || ch == '\r')
160 } while (m_quotmarkClosed && (isspace(ch) != 0));
162 if (m_quotmarkClosed && ((ch == 't') || (ch == 'T'))) {
163 const QByteArray buf = m_io->peek(3).toLower();
167 qjsonDebug() << "JSonScanner::yylex - TRUE_VAL";
168 return yy::json_parser::token::TRUE_VAL;
171 else if (m_quotmarkClosed && ((ch == 'n') || (ch == 'N'))) {
172 const QByteArray buf = m_io->peek(3).toLower();
176 qjsonDebug() << "JSonScanner::yylex - NULL_VAL";
177 return yy::json_parser::token::NULL_VAL;
178 } else if (buf.startsWith("an") && m_allowSpecialNumbers) {
181 qjsonDebug() << "JSonScanner::yylex - NAN_VAL";
182 return yy::json_parser::token::NAN_VAL;
186 else if (m_quotmarkClosed && ((ch == 'f') || (ch == 'F'))) {
188 const QByteArray buf = m_io->peek(4).toLower();
189 if (buf.length() == 4) {
193 qjsonDebug() << "JSonScanner::yylex - FALSE_VAL";
194 return yy::json_parser::token::FALSE_VAL;
198 else if (m_quotmarkClosed && ((ch == 'e') || (ch == 'E'))) {
199 QByteArray ret(1, ch);
200 const QByteArray buf = m_io->peek(1);
201 if (!buf.isEmpty()) {
202 if ((buf[0] == '+' ) || (buf[0] == '-' )) {
203 ret += m_io->read (1);
207 *yylval = QVariant(QString::fromUtf8(ret));
208 return yy::json_parser::token::E;
210 else if (m_allowSpecialNumbers && m_quotmarkClosed && ((ch == 'I') || (ch == 'i'))) {
211 QByteArray ret(1, ch);
212 const QByteArray buf = m_io->peek(7);
213 if (buf == "nfinity") {
216 qjsonDebug() << "JSonScanner::yylex - INFINITY_VAL";
217 return yy::json_parser::token::INFINITY_VAL;
221 if (ch != '"' && !m_quotmarkClosed) {
222 // we're inside a " " block
226 bool escape_on = (ch == '\\') ? true : false;
230 qint64 ret = m_io->peek(&nextCh, 1);
233 return yy::json_parser::token::END;
236 } else if ( !escape_on && nextCh == '\"' ) {
238 const QString str = unescape( raw, &ok );
239 *yylval = ok ? str : QString();
240 return ok ? yy::json_parser::token::STRING : -1;
243 if ( prevCh == '\\' && nextCh != '"' && nextCh != '\\' && nextCh != '/' &&
244 nextCh != 'b' && nextCh != 'f' && nextCh != 'n' &&
245 nextCh != 'r' && nextCh != 't' && nextCh != 'u') {
246 qjsonDebug() << "Just read" << nextCh;
247 qjsonDebug() << "JSonScanner::yylex - error decoding escaped sequence";
251 m_io->read(1); // consume
257 escape_on = (prevCh == '\\') ? true : false;
259 if (nextCh == '\\') {
261 if (m_io->getChar (&buf)) {
263 if (((buf != '"') && (buf != '\\') && (buf != '/') &&
264 (buf != 'b') && (buf != 'f') && (buf != 'n') &&
265 (buf != 'r') && (buf != 't') && (buf != 'u'))) {
266 qjsonDebug() << "Just read" << buf;
267 qjsonDebug() << "JSonScanner::yylex - error decoding escaped sequence";
271 qCritical() << "JSonScanner::yylex - error decoding escaped sequence : io error";
278 else if (isdigit(ch) != 0 && m_quotmarkClosed) {
280 QByteArray numArray = QByteArray::fromRawData( &ch, 1 * sizeof(char) );
281 qulonglong number = numArray.toULongLong(&ok);
283 //This shouldn't happen
284 qCritical() << "JSonScanner::yylex - error while converting char to ulonglong, returning -1";
288 // we have to return immediately otherwise numbers like
289 // 2.04 will be converted to 2.4
290 *yylval = QVariant(number);
291 qjsonDebug() << "JSonScanner::yylex - yy::json_parser::token::DIGIT";
292 return yy::json_parser::token::DIGIT;
296 qint64 ret = m_io->peek(&nextCh, 1);
297 while (ret == 1 && isdigit(nextCh)) {
298 m_io->read(1); //consume
300 numArray = QByteArray::fromRawData( &nextCh, 1 * sizeof(char) );
301 number = number * 10 + numArray.toULongLong(&ok);
303 //This shouldn't happen
304 qCritical() << "JSonScanner::yylex - error while converting char to ulonglong, returning -1";
307 ret = m_io->peek(&nextCh, 1);
310 *yylval = QVariant(number);
311 qjsonDebug() << "JSonScanner::yylex - yy::json_parser::token::DIGIT";
312 return yy::json_parser::token::DIGIT;
314 else if (isalnum(ch) != 0) {
315 *yylval = QVariant(QString(QChar::fromLatin1(ch)));
316 qjsonDebug() << "JSonScanner::yylex - yy::json_parser::token::WORD ("
318 return yy::json_parser::token::STRING;
320 else if (ch == ':') {
322 qjsonDebug() << "JSonScanner::yylex - yy::json_parser::token::COLON";
323 return yy::json_parser::token::COLON;
325 else if (ch == '"') {
326 // yy::json_parser::token::QUOTMARK (")
330 if (m_quotmarkCount %2 == 0) {
331 m_quotmarkClosed = true;
333 qjsonDebug() << "JSonScanner::yylex - yy::json_parser::token::QUOTMARKCLOSE";
334 return yy::json_parser::token::QUOTMARKCLOSE;
337 m_quotmarkClosed = false;
338 qjsonDebug() << "JSonScanner::yylex - yy::json_parser::token::QUOTMARKOPEN";
339 return yy::json_parser::token::QUOTMARKOPEN;
342 else if (ch == ',') {
343 qjsonDebug() << "JSonScanner::yylex - yy::json_parser::token::COMMA";
344 return yy::json_parser::token::COMMA;
346 else if (ch == '.') {
347 qjsonDebug() << "JSonScanner::yylex - yy::json_parser::token::DOT";
348 return yy::json_parser::token::DOT;
350 else if (ch == '-') {
351 qjsonDebug() << "JSonScanner::yylex - yy::json_parser::token::MINUS";
352 return yy::json_parser::token::MINUS;
354 else if (ch == '[') {
355 qjsonDebug() << "JSonScanner::yylex - yy::json_parser::token::SQUARE_BRACKET_OPEN";
356 return yy::json_parser::token::SQUARE_BRACKET_OPEN;
358 else if (ch == ']') {
359 qjsonDebug() << "JSonScanner::yylex - yy::json_parser::token::SQUARE_BRACKET_CLOSE";
360 return yy::json_parser::token::SQUARE_BRACKET_CLOSE;
362 else if (ch == '{') {
363 qjsonDebug() << "JSonScanner::yylex - yy::json_parser::token::CURLY_BRACKET_OPEN";
364 return yy::json_parser::token::CURLY_BRACKET_OPEN;
366 else if (ch == '}') {
367 qjsonDebug() << "JSonScanner::yylex - yy::json_parser::token::CURLY_BRACKET_CLOSE";
368 return yy::json_parser::token::CURLY_BRACKET_CLOSE;
373 qCritical() << "JSonScanner::yylex - unknown char, returning -1";