|
|
6e237b |
|
|
|
6e237b |
#include <tvariant.h></tvariant.h>
|
|
|
6e237b |
|
|
|
9cf1a4 |
#include <sstream></sstream>
|
|
|
9cf1a4 |
#include <iostream></iostream>
|
|
|
9cf1a4 |
#include <cstdio></cstdio>
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
|
|
|
6e237b |
//---------------------------------------------------------
|
|
|
6e237b |
|
|
|
6e237b |
int
|
|
|
6e237b |
TVariantPath::compare(
|
|
|
6e237b |
const TVariantPath &a, int beginA,
|
|
|
6e237b |
const TVariantPath &b, int beginB, int count )
|
|
|
6e237b |
{
|
|
|
6e237b |
assert(beginA >= 0 && beginA <= (int)a.size());
|
|
|
6e237b |
assert(beginB >= 0 && beginB <= (int)b.size());
|
|
|
6e237b |
if (count == 0) return 0;
|
|
|
6e237b |
int countA = std::min(count, (int)a.size() - beginA);
|
|
|
6e237b |
int countB = std::min(count, (int)b.size() - beginB);
|
|
|
6e237b |
count = std::min(countA, countB);
|
|
|
6e237b |
|
|
|
6e237b |
TVariantPath::const_iterator ia = a.begin() + beginA;
|
|
|
6e237b |
TVariantPath::const_iterator ib = b.begin() + beginB;
|
|
|
6e237b |
for(int i = 0; i < count; ++i, ++ia, ++ib)
|
|
|
6e237b |
if ((*ia) < (*ib)) return -1; else
|
|
|
6e237b |
if ((*ib) < (*ia)) return 1;
|
|
|
6e237b |
return countA < countB ? -1
|
|
|
6e237b |
: countB < countA ? 1 : 0;
|
|
|
6e237b |
}
|
|
|
6e237b |
|
|
|
6e237b |
//---------------------------------------------------------
|
|
|
6e237b |
|
|
|
6e237b |
void
|
|
|
6e237b |
TVariant::setParentForChilds() {
|
|
|
6e237b |
if (m_type == List) {
|
|
|
6e237b |
for(TVariantList::iterator i = m_list.begin(); i != m_list.end(); ++i)
|
|
|
6e237b |
i->setParent(*this);
|
|
|
6e237b |
} else
|
|
|
6e237b |
if (m_type == Map) {
|
|
|
6e237b |
for(TVariantMap::iterator i = m_map.begin(); i != m_map.end(); ++i)
|
|
|
6e237b |
i->second.setParent(*this, i->first);
|
|
|
6e237b |
}
|
|
|
6e237b |
}
|
|
|
6e237b |
|
|
|
6e237b |
//---------------------------------------------------------
|
|
|
6e237b |
|
|
|
6e237b |
const TVariant&
|
|
|
6e237b |
TVariant::blank() {
|
|
|
6e237b |
static const TVariant blank;
|
|
|
6e237b |
return blank;
|
|
|
6e237b |
}
|
|
|
6e237b |
|
|
|
6e237b |
//---------------------------------------------------------
|
|
|
6e237b |
|
|
|
6e237b |
void
|
|
|
6e237b |
TVariant::resize(int size) {
|
|
|
6e237b |
setType(List);
|
|
|
6e237b |
int prevSize = (int)m_list.size();
|
|
|
6e237b |
if (prevSize == size) return;
|
|
|
6e237b |
m_list.resize(size);
|
|
|
6e237b |
if (prevSize < size)
|
|
|
6e237b |
for(TVariantList::iterator i = m_list.begin() + prevSize; i != m_list.end(); ++i)
|
|
|
6e237b |
i->setParent(*this);
|
|
|
6e237b |
touch();
|
|
|
6e237b |
}
|
|
|
6e237b |
|
|
|
6e237b |
//---------------------------------------------------------
|
|
|
6e237b |
|
|
|
6e237b |
void
|
|
|
6e237b |
TVariant::insert(int index, const TVariant &v) {
|
|
|
6e237b |
resize(std::max((int)m_list.size(), index));
|
|
|
6e237b |
m_list.insert(m_list.begin() + index, v);
|
|
|
6e237b |
m_list[index].setParent(*this);
|
|
|
6e237b |
touch();
|
|
|
6e237b |
}
|
|
|
6e237b |
|
|
|
6e237b |
//---------------------------------------------------------
|
|
|
6e237b |
|
|
|
6e237b |
void
|
|
|
6e237b |
TVariant::remove(int index) {
|
|
|
6e237b |
if (m_type == List && index >= 0 && index < (int)m_list.size())
|
|
|
6e237b |
{ m_list.erase(m_list.begin() + index); touch(); }
|
|
|
6e237b |
}
|
|
|
6e237b |
|
|
|
6e237b |
//---------------------------------------------------------
|
|
|
6e237b |
|
|
|
6e237b |
TVariant&
|
|
|
6e237b |
TVariant::operator[] (int index) {
|
|
|
6e237b |
setType(List);
|
|
|
6e237b |
assert(index >= 0);
|
|
|
6e237b |
int prevSize = (int)m_list.size();
|
|
|
6e237b |
if (index >= prevSize) {
|
|
|
6e237b |
m_list.resize(index + 1);
|
|
|
6e237b |
for(TVariantList::iterator i = m_list.begin() + prevSize; i != m_list.end(); ++i)
|
|
|
6e237b |
i->setParent(*this);
|
|
|
6e237b |
touch();
|
|
|
6e237b |
}
|
|
|
6e237b |
return m_list[index];
|
|
|
6e237b |
}
|
|
|
6e237b |
|
|
|
6e237b |
//---------------------------------------------------------
|
|
|
6e237b |
|
|
|
6e237b |
TVariant&
|
|
|
6e237b |
TVariant::operator[] (const TStringId &field) {
|
|
|
6e237b |
setType(Map);
|
|
|
6e237b |
TVariant &result = m_map[field];
|
|
|
ae9154 |
if (!result.m_parent) {
|
|
|
ae9154 |
result.setParent(*this, field);
|
|
|
ae9154 |
touch();
|
|
|
ae9154 |
}
|
|
|
6e237b |
return result;
|
|
|
6e237b |
}
|
|
|
6e237b |
|
|
|
6e237b |
//---------------------------------------------------------
|
|
|
6e237b |
|
|
|
6e237b |
bool
|
|
|
6e237b |
TVariant::remove(const TStringId &field) {
|
|
|
6e237b |
if (m_type == Map && m_map.erase(field))
|
|
|
6e237b |
{ touch(); return true; }
|
|
|
6e237b |
return false;
|
|
|
6e237b |
}
|
|
|
6e237b |
|
|
|
6e237b |
//---------------------------------------------------------
|
|
|
6e237b |
|
|
|
6e237b |
const TVariant&
|
|
|
6e237b |
TVariant::byPath(const TVariantPath &path, int begin, int end) const {
|
|
|
6e237b |
if ((int)path.size() <= begin || begin >= end) return *this;
|
|
|
6e237b |
if (isNone()) return blank();
|
|
|
9cf8be |
return (*this)[path[begin]].byPath(path, begin + 1, end);
|
|
|
6e237b |
}
|
|
|
6e237b |
|
|
|
6e237b |
//---------------------------------------------------------
|
|
|
6e237b |
|
|
|
6e237b |
TVariant&
|
|
|
6e237b |
TVariant::byPath(const TVariantPath &path, int begin, int end) {
|
|
|
6e237b |
if ((int)path.size() <= begin || begin >= end) return *this;
|
|
|
9cf8be |
return (*this)[path[begin]].byPath(path, begin + 1, end);
|
|
|
6e237b |
}
|
|
|
6e237b |
|
|
|
6e237b |
//---------------------------------------------------------
|
|
|
6e237b |
|
|
|
6e237b |
int
|
|
|
9cf8be |
TVariant::getParentPathSize(const TVariant &parent) const {
|
|
|
6e237b |
int ac = 0;
|
|
|
9cf8be |
for(const TVariant *a = this; a; a = a->parent(), ++ac)
|
|
|
9cf8be |
if (a == &parent) return ac;
|
|
|
9cf8be |
return -1;
|
|
|
6e237b |
}
|
|
|
6e237b |
|
|
|
6e237b |
//---------------------------------------------------------
|
|
|
6e237b |
|
|
|
9cf8be |
bool
|
|
|
9cf8be |
TVariant::getParentPath(TVariantPath &outPath, const TVariant &parent) const {
|
|
|
9cf8be |
if (!m_parent)
|
|
|
9cf8be |
{ outPath.clear(); return false; }
|
|
|
9cf8be |
if (m_parent == this)
|
|
|
9cf8be |
{ outPath.clear(); return true; }
|
|
|
9cf8be |
if (m_parent->getParentPath(outPath))
|
|
|
9cf8be |
{ outPath.push_back(parentPathEntry()); return true; }
|
|
|
9cf8be |
return false;
|
|
|
9cf8be |
}
|
|
|
9cf8be |
|
|
|
9cf8be |
//---------------------------------------------------------
|
|
|
9cf8be |
|
|
|
9cf8be |
bool
|
|
|
9cf8be |
TVariant::getChildPathEntry(const TVariant &child, TVariantPathEntry &outEntry) const {
|
|
|
9cf8be |
for(const TVariant *a = &child; a->parent(); a = a->parent())
|
|
|
9cf8be |
if (a->parent() == this)
|
|
|
9cf8be |
{ outEntry = a->parentPathEntry(); return true; }
|
|
|
9cf8be |
outEntry = TVariantPathEntry();
|
|
|
9cf8be |
return false;
|
|
|
6e237b |
}
|
|
|
6e237b |
|
|
|
6e237b |
//---------------------------------------------------------
|
|
|
6e237b |
|
|
|
6e237b |
bool
|
|
|
6e237b |
TVariant::isChildOf(const TVariant &other) const {
|
|
|
6e237b |
for(const TVariant *a = this->m_parent; a; a = a->m_parent)
|
|
|
6e237b |
if (a == &other) return true;
|
|
|
6e237b |
return false;
|
|
|
6e237b |
}
|
|
|
6e237b |
|
|
|
6e237b |
//---------------------------------------------------------
|
|
|
6e237b |
|
|
|
6e237b |
bool
|
|
|
6e237b |
TVariant::isChildOrEqual(const TVariant &other) const {
|
|
|
6e237b |
for(const TVariant *a = this; a; a = a->m_parent)
|
|
|
6e237b |
if (a == &other) return true;
|
|
|
6e237b |
return false;
|
|
|
6e237b |
}
|
|
|
6e237b |
|
|
|
6e237b |
//---------------------------------------------------------
|
|
|
6e237b |
|
|
|
6e237b |
const TVariant*
|
|
|
6e237b |
TVariant::findCommonParent(const TVariant &other) const {
|
|
|
6e237b |
if (m_root != other.m_root) return NULL;
|
|
|
6e237b |
const TVariant *a = this, *b = &other;
|
|
|
6e237b |
int ac = 0, bc = 0;
|
|
|
6e237b |
while(a->m_parent) a = a->m_parent, ++ac;
|
|
|
6e237b |
while(b->m_parent) b = b->m_parent, ++bc;
|
|
|
6e237b |
|
|
|
6e237b |
a = this, b = &other;
|
|
|
6e237b |
while(ac > bc) a = a->m_parent, --ac;
|
|
|
6e237b |
while(bc > ac) b = b->m_parent, --bc;
|
|
|
6e237b |
|
|
|
6e237b |
while(true) {
|
|
|
6e237b |
if (a == b) return a;
|
|
|
6e237b |
if (ac == 0) break;
|
|
|
6e237b |
--ac, a = a->m_parent, b = b->m_parent;
|
|
|
6e237b |
}
|
|
|
6e237b |
|
|
|
6e237b |
return NULL;
|
|
|
6e237b |
}
|
|
|
249386 |
|
|
|
249386 |
//---------------------------------------------------------
|
|
|
249386 |
|
|
|
249386 |
size_t
|
|
|
249386 |
TVariant::getMemSize() const {
|
|
|
249386 |
size_t s = sizeof(*this);
|
|
|
249386 |
for(TVariantList::const_iterator i = m_list.begin(); i != m_list.end(); ++i)
|
|
|
249386 |
s += i->getMemSize();
|
|
|
249386 |
for(TVariantMap::const_iterator i = m_map.begin(); i != m_map.end(); ++i)
|
|
|
249386 |
s += sizeof(*i) - sizeof(*this) + i->second.getMemSize();
|
|
|
249386 |
return s;
|
|
|
249386 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
//---------------------------------------------------------
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
void
|
|
|
9cf1a4 |
TVariant::toStream(std::ostream &stream, bool pretty, int level) const {
|
|
|
9cf1a4 |
struct Writer {
|
|
|
9cf1a4 |
const TVariant &data;
|
|
|
9cf1a4 |
std::ostream &stream;
|
|
|
9cf1a4 |
bool pretty;
|
|
|
9cf1a4 |
int level;
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
Writer(const TVariant &data, std::ostream &stream, bool pretty, int level):
|
|
|
9cf1a4 |
data(data), stream(stream), pretty(pretty), level(level) { }
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
void writeNewLine()
|
|
|
9cf1a4 |
{ if (pretty) stream << std::endl; }
|
|
|
9cf1a4 |
void writeSpace()
|
|
|
9cf1a4 |
{ if (pretty) stream << " "; }
|
|
|
9cf1a4 |
void writeTab(int level)
|
|
|
9cf1a4 |
{ if (pretty) for(int i = 2*level; i; --i) stream << " "; }
|
|
|
9cf1a4 |
void writeChar(char c)
|
|
|
9cf1a4 |
{ stream.put(c); }
|
|
|
9cf1a4 |
void writeWord(const char *word)
|
|
|
9cf1a4 |
{ stream << word; }
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
void writeString(const std::string &str) {
|
|
|
9cf1a4 |
writeWord("\"");
|
|
|
9cf1a4 |
for(const char *c = str.c_str(); *c; ++c) {
|
|
|
9cf1a4 |
switch (*c) {
|
|
|
9cf1a4 |
case '\"': writeWord("\\\""); break;
|
|
|
9cf1a4 |
case '\\': writeWord("\\\\"); break;
|
|
|
9cf1a4 |
case '\b': writeWord("\\b"); break;
|
|
|
9cf1a4 |
case '\f': writeWord("\\f"); break;
|
|
|
9cf1a4 |
case '\n': writeWord("\\n"); break;
|
|
|
9cf1a4 |
case '\r': writeWord("\\r"); break;
|
|
|
9cf1a4 |
case '\t': writeWord("\\t"); break;
|
|
|
9cf1a4 |
default: writeChar(*c); break;
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
writeWord("\"");
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
void writeDouble(double x) {
|
|
|
9cf1a4 |
char buf[256];
|
|
|
9cf1a4 |
snprintf(buf, sizeof(buf), "%.12lg", x);
|
|
|
9cf1a4 |
stream << buf;
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
void writeList(const TVariantList &list) {
|
|
|
9cf1a4 |
writeWord("[");
|
|
|
9cf1a4 |
if (!list.empty()) {
|
|
|
9cf1a4 |
writeNewLine();
|
|
|
9cf1a4 |
TVariantList::const_iterator i = list.begin();
|
|
|
9cf1a4 |
while(true) {
|
|
|
9cf1a4 |
writeTab(level + 1);
|
|
|
9cf1a4 |
i->toStream(stream, pretty, level + 1);
|
|
|
9cf1a4 |
if (++i == list.end()) { writeNewLine(); break; }
|
|
|
9cf1a4 |
writeWord(",");
|
|
|
9cf1a4 |
writeNewLine();
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
writeTab(level);
|
|
|
9cf1a4 |
} else writeSpace();
|
|
|
9cf1a4 |
writeWord("]");
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
void writeMap(const TVariantMap &map) {
|
|
|
9cf1a4 |
writeWord("{");
|
|
|
9cf1a4 |
if (!map.empty()) {
|
|
|
9cf1a4 |
writeNewLine();
|
|
|
9cf1a4 |
TVariantMap::const_iterator i = map.begin();
|
|
|
9cf1a4 |
while(true) {
|
|
|
9cf1a4 |
writeTab(level + 1);
|
|
|
9cf1a4 |
writeString(i->first.str());
|
|
|
9cf1a4 |
writeWord(":");
|
|
|
9cf1a4 |
writeSpace();
|
|
|
9cf1a4 |
i->second.toStream(stream, pretty, level + 1);
|
|
|
9cf1a4 |
if (++i == map.end()) { writeNewLine(); break; }
|
|
|
9cf1a4 |
writeWord(",");
|
|
|
9cf1a4 |
writeNewLine();
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
writeTab(level);
|
|
|
9cf1a4 |
} else writeSpace();
|
|
|
9cf1a4 |
writeWord("}");
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
void write() {
|
|
|
9cf1a4 |
switch(data.getType()) {
|
|
|
9cf1a4 |
case Bool: writeWord(data.getBool() ? "true" : "false"); break;
|
|
|
9cf1a4 |
case Double: writeDouble(data.getDouble()); break;
|
|
|
9cf1a4 |
case String: writeString(data.getString()); break;
|
|
|
9cf1a4 |
case List: writeList(data.getList()); break;
|
|
|
9cf1a4 |
case Map: writeMap(data.getMap()); break;
|
|
|
9cf1a4 |
case None:
|
|
|
9cf1a4 |
default: writeWord("null"); break;
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
if (!stream) throw TException("write to stream failed");
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
};
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
Writer(*this, stream, pretty, level).write();
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
//---------------------------------------------------------
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
void
|
|
|
9cf1a4 |
TVariant::fromStream(std::istream &stream, int *currentRow, int *currentCol) {
|
|
|
9cf1a4 |
struct Reader {
|
|
|
9cf1a4 |
TVariant &data;
|
|
|
9cf1a4 |
std::istream &stream;
|
|
|
9cf1a4 |
int &row;
|
|
|
9cf1a4 |
int &col;
|
|
|
9cf1a4 |
Reader(TVariant &data, std::istream &stream, int &row, int &col):
|
|
|
9cf1a4 |
data(data), stream(stream), row(row), col(col) { }
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
void warning(const std::string &msg)
|
|
|
9cf1a4 |
{ std::cerr << "TVariant load:" << row << ":" << col << ": " << msg << std::endl; }
|
|
|
9cf1a4 |
void error(const std::string &msg)
|
|
|
9cf1a4 |
{ throw TVariantSyntaxException(row, col, msg); }
|
|
|
9cf1a4 |
void error()
|
|
|
9cf1a4 |
{ error("cannot recognize type of data"); }
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
int peek()
|
|
|
9cf1a4 |
{ return stream.peek(); }
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
int get() {
|
|
|
9cf1a4 |
int c = stream.get();
|
|
|
9cf1a4 |
if (c == '\n') ++row, col = 1; else ++col;
|
|
|
9cf1a4 |
if (!stream) error("unexpected end of file");
|
|
|
9cf1a4 |
return c;
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
void skipSpaces()
|
|
|
9cf1a4 |
{ while(isspace(peek())) get(); }
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
bool readWord(const char *word) {
|
|
|
9cf1a4 |
if (peek() != *word) return false;
|
|
|
9cf1a4 |
while(*word)
|
|
|
9cf1a4 |
if (get() == *word) ++word; else error();
|
|
|
9cf1a4 |
return true;
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
bool readNull() {
|
|
|
9cf1a4 |
if (readWord("null") || peek() == EOF)
|
|
|
9cf1a4 |
{ data.reset(); return true; }
|
|
|
9cf1a4 |
return false;
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
bool readBool() {
|
|
|
9cf1a4 |
if (readWord("true")) { data.setBool(true); return true; }
|
|
|
9cf1a4 |
if (readWord("false")) { data.setBool(false); return true; }
|
|
|
9cf1a4 |
return false;
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
bool isdouble(int c)
|
|
|
9cf1a4 |
{ return isdigit(c) || c == '-' || c == '+' || c == '.'; }
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
bool readDouble() {
|
|
|
9cf1a4 |
if (!isdouble(peek())) return false;
|
|
|
9cf1a4 |
std::string str; str.reserve(20);
|
|
|
9cf1a4 |
while(isdouble(peek()) || isalpha(peek())) str.push_back((char)get());
|
|
|
9cf1a4 |
double d = 0.0;
|
|
|
9cf1a4 |
try { d = std::stod(str); }
|
|
|
9cf1a4 |
catch (const std::exception &e) { warning("wrong number: " + str); }
|
|
|
9cf1a4 |
data.setDouble(d);
|
|
|
9cf1a4 |
return true;
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
void readHexUnicode(std::string &str) {
|
|
|
9cf1a4 |
// read utf16 code
|
|
|
9cf1a4 |
// JSON standard requires exact four hex digits
|
|
|
9cf1a4 |
// but we're kind and allows 0-4 hex digits
|
|
|
9cf1a4 |
int code = 0;
|
|
|
9cf1a4 |
for(int i = 0; i < 4; ++i) {
|
|
|
9cf1a4 |
char c = peek();
|
|
|
9cf1a4 |
if (c >= '0' && c <= '9')
|
|
|
9cf1a4 |
code = 16*code + get() - '0';
|
|
|
9cf1a4 |
else if (c >= 'a' && c <= 'f')
|
|
|
9cf1a4 |
code = 16*code + get() - 'a';
|
|
|
9cf1a4 |
else if (c >= 'A' && c <= 'F')
|
|
|
9cf1a4 |
code = 16*code + get() - 'A';
|
|
|
9cf1a4 |
else break;
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
if (code == 0)
|
|
|
9cf1a4 |
{ warning("\\u token with zero code"); return; }
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
// 16 bits of utf16 character be encoded up to three utf8 bytes
|
|
|
9cf1a4 |
// in the following format:
|
|
|
9cf1a4 |
// 11000xxx 10xxxxxx 0xxxxxxx
|
|
|
9cf1a4 |
if (code >= 1 << 13) { // 11000xxx
|
|
|
9cf1a4 |
str.push_back((char)(192 | (code >> 13)));
|
|
|
9cf1a4 |
code &= (1 << 13) - 1;
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
if (code >= 1 << 6) { // 10xxxxxx
|
|
|
9cf1a4 |
str.push_back((char)(128 | (code >> 6)));
|
|
|
9cf1a4 |
code &= (1 << 6) - 1;
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
str.push_back((char)code); // 0xxxxxxx
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
void readString(std::string &str) {
|
|
|
9cf1a4 |
if (get() != '\"') error("expected quote");
|
|
|
9cf1a4 |
while(true) {
|
|
|
9cf1a4 |
int c = get();
|
|
|
9cf1a4 |
if (c == '\"') break;
|
|
|
9cf1a4 |
else
|
|
|
9cf1a4 |
if (c == '\\') {
|
|
|
9cf1a4 |
switch(int cc = get()) {
|
|
|
9cf1a4 |
case '\"': str.push_back('\"'); break;
|
|
|
9cf1a4 |
case '\\': str.push_back('\\'); break;
|
|
|
9cf1a4 |
case '/': str.push_back( '/'); break;
|
|
|
9cf1a4 |
case 'b': str.push_back('\b'); break;
|
|
|
9cf1a4 |
case 'f': str.push_back('\f'); break;
|
|
|
9cf1a4 |
case 'n': str.push_back('\n'); break;
|
|
|
9cf1a4 |
case 'r': str.push_back('\r'); break;
|
|
|
9cf1a4 |
case 't': str.push_back('\t'); break;
|
|
|
9cf1a4 |
case 'u': readHexUnicode(str); break;
|
|
|
9cf1a4 |
default: str.push_back((char)cc); break;
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
} else
|
|
|
9cf1a4 |
str.push_back((char)c);
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
bool readString() {
|
|
|
9cf1a4 |
if (peek() != '\"') return false;
|
|
|
9cf1a4 |
std::string str;
|
|
|
9cf1a4 |
readString(str);
|
|
|
9cf1a4 |
data.setString(str);
|
|
|
9cf1a4 |
return true;
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
bool readList() {
|
|
|
9cf1a4 |
if (peek() != '[') return false;
|
|
|
9cf1a4 |
get(); // skip bracket
|
|
|
9cf1a4 |
data.reset();
|
|
|
9cf1a4 |
data.setType(List);
|
|
|
9cf1a4 |
while(true) {
|
|
|
9cf1a4 |
skipSpaces();
|
|
|
9cf1a4 |
if (peek() == ']') { get(); break; }
|
|
|
9cf1a4 |
if (data.size() && get() != ',') error("expected comma or close bracket");
|
|
|
9cf1a4 |
skipSpaces();
|
|
|
9cf1a4 |
if (peek() == ']') { get(); break; } // to allow comma at the end
|
|
|
9cf1a4 |
data[data.size()].fromStream(stream, &row, &col);
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
return true;
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
bool readMap() {
|
|
|
9cf1a4 |
if (peek() != '{') return false;
|
|
|
9cf1a4 |
get(); // skip brace
|
|
|
9cf1a4 |
data.reset();
|
|
|
9cf1a4 |
data.setType(Map);
|
|
|
9cf1a4 |
while(true) {
|
|
|
9cf1a4 |
skipSpaces();
|
|
|
9cf1a4 |
if (peek() == '}') { get(); break; }
|
|
|
9cf1a4 |
if (data.size() && get() != ',') error("expected comma or close brace");
|
|
|
9cf1a4 |
skipSpaces();
|
|
|
9cf1a4 |
if (peek() == '}') { get(); break; } // to allow comma at the end
|
|
|
9cf1a4 |
std::string key;
|
|
|
9cf1a4 |
readString(key);
|
|
|
9cf1a4 |
skipSpaces();
|
|
|
9cf1a4 |
if (get() != ':') error("expected colon");
|
|
|
9cf1a4 |
if (data.contains(key)) warning("duplicate key: " + key);
|
|
|
9cf1a4 |
data[key].fromStream(stream, &row, &col);
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
return true;
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
void read() {
|
|
|
9cf1a4 |
skipSpaces();
|
|
|
9cf1a4 |
if ( !readNull()
|
|
|
9cf1a4 |
&& !readBool()
|
|
|
9cf1a4 |
&& !readDouble()
|
|
|
9cf1a4 |
&& !readString()
|
|
|
9cf1a4 |
&& !readList()
|
|
|
9cf1a4 |
&& !readMap() )
|
|
|
9cf1a4 |
error();
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
};
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
reset();
|
|
|
9cf1a4 |
int row = 1, col = 1;
|
|
|
9cf1a4 |
Reader(
|
|
|
9cf1a4 |
*this,
|
|
|
9cf1a4 |
stream,
|
|
|
9cf1a4 |
(currentRow ? *currentRow : row),
|
|
|
9cf1a4 |
(currentCol ? *currentCol : col) ).read();
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
//---------------------------------------------------------
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
std::string
|
|
|
9cf1a4 |
TVariant::toString(bool pretty, int level) const {
|
|
|
9cf1a4 |
std::stringstream stream(std::ios_base::out);
|
|
|
9cf1a4 |
toStream(stream, pretty, level);
|
|
|
9cf1a4 |
return stream.str();
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
//---------------------------------------------------------
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
void
|
|
|
9cf1a4 |
TVariant::fromString(const std::string &str, int *currentRow, int *currentCol) {
|
|
|
9cf1a4 |
std::stringstream stream(str, std::ios_base::in);
|
|
|
9cf1a4 |
fromStream(stream, currentRow, currentCol);
|
|
|
9cf1a4 |
}
|
|
|
9cf1a4 |
|
|
|
9cf1a4 |
//---------------------------------------------------------
|
|
|
9cf1a4 |
|