#include "argumentstore.h"
#include "binaryparser.h"
#include "binfloat.h"
+#include "except.h"
#include "input.h"
#include "loaderaction.h"
namespace Msp {
namespace DataFile {
-class bad_definition: public runtime_error
-{
-public:
- bad_definition(const std::string &w):
- runtime_error(w)
- { }
-
- virtual ~bad_definition() throw() { }
-};
-
-class nesting_error: public logic_error
-{
-public:
- nesting_error(const std::string &w):
- logic_error(w)
- { }
-
- virtual ~nesting_error() throw() { }
-};
-
-
BinaryParser::BinaryParser(Input &i, const string &s):
ParserMode(i, s),
float_precision(32),
+++ /dev/null
-#include <typeinfo>
-#include <msp/debug/demangle.h>
-#include <msp/strings/format.h>
-#include "dataerror.h"
-
-using namespace std;
-
-namespace Msp {
-namespace DataFile {
-
-data_error::data_error(const string &s, unsigned l, const string &w):
- runtime_error(make_what(s, l, w)),
- source(s),
- line(l)
-{ }
-
-data_error::data_error(const string &s, unsigned l, const exception &e):
- runtime_error(make_what(s, l, format("%s (%s)", Debug::demangle(typeid(e).name()), e.what()))),
- source(s),
- line(l)
-{ }
-
-string data_error::make_what(const string &s, unsigned l, const string &w)
-{
- if(l)
- return format("%s:%d: %s", s, l, w);
- else
- return format("%s: %s", s, w);
-}
-
-} // namespace DataFile
-} // namespace Msp
+++ /dev/null
-#ifndef MSP_DATAFILE_DATAERROR_H_
-#define MSP_DATAFILE_DATAERROR_H_
-
-#include <stdexcept>
-
-namespace Msp {
-namespace DataFile {
-
-class data_error: public std::runtime_error
-{
-private:
- std::string source;
- unsigned line;
-
-public:
- data_error(const std::string &, unsigned, const std::string &);
- data_error(const std::string &, unsigned, const std::exception &);
- virtual ~data_error() throw() { }
-
- const std::string &get_source() const { return source; }
- unsigned get_line() const { return line; }
-
-private:
- std::string make_what(const std::string &, unsigned, const std::string &);
-};
-
-} // namespace DataFile
-} // namespace Msp
-
-#endif
--- /dev/null
+#include <msp/debug/demangle.h>
+#include <msp/strings/format.h>
+#include "except.h"
+
+using namespace std;
+
+namespace Msp {
+namespace DataFile {
+
+data_error::data_error(const string &s, unsigned l, const string &w):
+ runtime_error(make_what(s, l, w)),
+ source(s),
+ line(l)
+{ }
+
+data_error::data_error(const string &s, unsigned l, const exception &e):
+ runtime_error(make_what(s, l, format("%s (%s)", Debug::demangle(typeid(e).name()), e.what()))),
+ source(s),
+ line(l)
+{ }
+
+string data_error::make_what(const string &s, unsigned l, const string &w)
+{
+ if(l)
+ return format("%s:%d: %s", s, l, w);
+ else
+ return format("%s: %s", s, w);
+}
+
+
+parse_error::parse_error(const string &t):
+ runtime_error(t.empty() ? "at end of input" : format("after '%s'", t))
+{ }
+
+
+syntax_error::syntax_error(const string &t):
+ runtime_error(t.empty() ? "at end of input" : format("at '%s'", t))
+{ }
+
+
+bad_definition::bad_definition(const string &w):
+ runtime_error(w)
+{ }
+
+
+nesting_error::nesting_error(const string &w):
+ logic_error(w)
+{ }
+
+
+unknown_keyword::unknown_keyword(const string &k):
+ runtime_error(k)
+{ }
+
+
+invalid_signature::invalid_signature(const string &k, const string &s):
+ runtime_error(format("%s %s", k, s))
+{ }
+
+
+no_collection::no_collection(const type_info &t):
+ runtime_error(Debug::demangle(t.name()))
+{ }
+
+} // namespace DataFile
+} // namespace Msp
--- /dev/null
+#ifndef MSP_DATAFILE_EXCEPT_H_
+#define MSP_DATAFILE_EXCEPT_H_
+
+#include <stdexcept>
+#include <typeinfo>
+
+namespace Msp {
+namespace DataFile {
+
+class data_error: public std::runtime_error
+{
+private:
+ std::string source;
+ unsigned line;
+
+public:
+ data_error(const std::string &, unsigned, const std::string &);
+ data_error(const std::string &, unsigned, const std::exception &);
+ virtual ~data_error() throw() { }
+
+ const std::string &get_source() const { return source; }
+ unsigned get_line() const { return line; }
+
+private:
+ std::string make_what(const std::string &, unsigned, const std::string &);
+};
+
+
+class parse_error: public std::runtime_error
+{
+public:
+ parse_error(const std::string &);
+ virtual ~parse_error() throw() { }
+};
+
+
+class syntax_error: public std::runtime_error
+{
+public:
+ syntax_error(const std::string &t);
+ virtual ~syntax_error() throw() { }
+};
+
+
+class bad_definition: public std::runtime_error
+{
+public:
+ bad_definition(const std::string &w);
+ virtual ~bad_definition() throw() { }
+};
+
+
+class nesting_error: public std::logic_error
+{
+public:
+ nesting_error(const std::string &);
+ virtual ~nesting_error() throw() { }
+};
+
+
+class unknown_keyword: public std::runtime_error
+{
+public:
+ unknown_keyword(const std::string &);
+ virtual ~unknown_keyword() throw() { }
+};
+
+
+class invalid_signature: public std::runtime_error
+{
+public:
+ invalid_signature(const std::string &, const std::string &);
+ virtual ~invalid_signature() throw() { }
+};
+
+
+class no_collection: public std::runtime_error
+{
+public:
+ no_collection(const std::type_info &);
+};
+
+} // namespace DataFile
+} // namespace Msp
+
+#endif
--- /dev/null
+#include <msp/stringcodec/utf8.h>
+#include "except.h"
+#include "input.h"
+#include "jsonparser.h"
+
+using namespace std;
+
+namespace Msp {
+namespace DataFile {
+
+JsonParser::JsonParser(Input &i, const string &s):
+ ParserMode(i, s),
+ toplevel_state(STATE_INIT)
+{ }
+
+Statement JsonParser::parse()
+{
+ if(toplevel_state==STATE_END)
+ return Statement();
+
+ bool was_init = (toplevel_state==STATE_INIT);
+ Token token = parse_token();
+ if(toplevel_state==STATE_INIT)
+ {
+ if(token.str=="[")
+ toplevel_state = STATE_ARRAY;
+ else if(token.str=="{")
+ toplevel_state = STATE_OBJECT;
+ else
+ {
+ // TODO Standalone simple values; does anyone use them?
+ toplevel_state = STATE_END;
+ throw syntax_error(token.str);
+ }
+
+ token = parse_token();
+ }
+
+ if((toplevel_state==STATE_ARRAY && token.str=="]") || (toplevel_state==STATE_OBJECT && token.str=="}"))
+ {
+ toplevel_state = STATE_END;
+ return Statement();
+ }
+ else if(!was_init)
+ {
+ if(token.str!=",")
+ throw syntax_error(token.str);
+
+ token = parse_token();
+ }
+
+ return parse_statement(&token, toplevel_state, string());
+}
+
+Statement JsonParser::parse_statement(const Token *t, State outer_state, const string &outer_kw)
+{
+ enum ParseState
+ {
+ INIT,
+ NAME,
+ VALUE,
+ ARRAY_INIT,
+ ARRAY,
+ ARRAY_ELEMENT,
+ OBJECT_INIT,
+ OBJECT,
+ OBJECT_MEMBER
+ };
+
+ Statement result;
+ ParseState state = INIT;
+
+ if(outer_state==STATE_ARRAY)
+ {
+ result.keyword = outer_kw+"[]";
+ state = VALUE;
+ }
+
+ while(in)
+ {
+ Token token;
+ if(t)
+ {
+ token = *t;
+ t = 0;
+ }
+ else
+ token = parse_token();
+
+ if(!result.valid)
+ {
+ result.valid = true;
+ result.source = src;
+ result.line = in.get_line_number();
+ }
+
+ if(state==INIT)
+ {
+ if(token.type!=Token::STRING)
+ throw syntax_error(token.str);
+
+ result.keyword = token.str;
+ state = NAME;
+ }
+ else if((state==ARRAY_INIT || state==ARRAY_ELEMENT) && token.str=="]")
+ break;
+ else if((state==ARRAY_INIT || state==ARRAY))
+ {
+ Statement ss = parse_statement(&token, STATE_ARRAY, result.keyword);
+ result.sub.push_back(ss);
+ state = ARRAY_ELEMENT;
+ }
+ else if(state==ARRAY_ELEMENT && token.str==",")
+ state = ARRAY;
+ else if((state==OBJECT_INIT || state==OBJECT_MEMBER) && token.str=="}")
+ break;
+ else if((state==OBJECT_INIT || state==OBJECT))
+ {
+ Statement ss = parse_statement(&token, STATE_OBJECT, result.keyword);
+ result.sub.push_back(ss);
+ state = OBJECT_MEMBER;
+ }
+ else if(state==OBJECT_MEMBER && token.str==",")
+ state = OBJECT;
+ else if(state==NAME && token.str==":")
+ state = VALUE;
+ else if(state==VALUE)
+ {
+ if(token.str=="[")
+ state = ARRAY_INIT;
+ else if(token.str=="{")
+ state = OBJECT_INIT;
+ else if(token.type!=Token::SPECIAL)
+ {
+ result.append_from_token(token);
+ break;
+ }
+ else
+ throw syntax_error(token.str);
+ }
+ else
+ throw syntax_error(token.str);
+ }
+
+ return result;
+}
+
+Token JsonParser::parse_token()
+{
+ int c = 0;
+
+ while(in)
+ {
+ c = in.get();
+ if(!isspace(c))
+ break;
+ }
+
+ if(!in)
+ return Token(Token::SPECIAL, "");
+
+ enum ParseState
+ {
+ INIT,
+ SIGN,
+ FLOATEXPINIT,
+ FLOATEXPSIGN,
+ STRING,
+ STRING_ESCAPE,
+ ACCEPT,
+ DECIMAL,
+ FLOAT,
+ FLOATEXP,
+ STRING_END,
+ IDENTIFIER
+ };
+
+ static Token::Type token_type[]=
+ {
+ Token::SPECIAL,
+ Token::SPECIAL,
+ Token::SPECIAL,
+ Token::SPECIAL,
+ Token::SPECIAL,
+ Token::SPECIAL,
+ Token::SPECIAL,
+ Token::INTEGER,
+ Token::FLOAT,
+ Token::FLOAT,
+ Token::STRING,
+ Token::IDENTIFIER
+ };
+
+ ParseState state = INIT;
+ string buf;
+
+ while(1)
+ {
+ if(state!=INIT)
+ c = in.get();
+ int next = in.peek();
+
+ buf += c;
+
+ switch(state)
+ {
+ case INIT:
+ if(c=='-' || c=='+')
+ state = SIGN;
+ else if(c=='.')
+ state = FLOAT;
+ else if(c=='"')
+ state = STRING;
+ else if(c=='{' || c=='}' || c=='[' || c==']' || c==':' || c==',')
+ return Token(Token::SPECIAL, string(1, c));
+ else if(isdigit(c))
+ state = DECIMAL;
+ else if(isalpha(c))
+ state = IDENTIFIER;
+ else
+ throw parse_error(buf);
+ break;
+
+ case SIGN:
+ if(isdigit(c))
+ state = DECIMAL;
+ else if(c=='.')
+ state = FLOAT;
+ else
+ throw parse_error(buf);
+ break;
+
+ case DECIMAL:
+ if(c=='.')
+ state = FLOAT;
+ else if(c=='e' || c=='E')
+ state = FLOATEXPINIT;
+ else if(!isdigit(c))
+ throw parse_error(buf);
+ break;
+
+ case FLOAT:
+ if(c=='e' || c=='E')
+ state = FLOATEXPINIT;
+ else if(!isdigit(c))
+ throw parse_error(buf);
+ break;
+
+ case FLOATEXPINIT:
+ if(c=='+' || c=='-')
+ state = FLOATEXPSIGN;
+ else if(isdigit(c))
+ state = FLOATEXP;
+ else
+ throw parse_error(buf);
+ break;
+
+ case FLOATEXPSIGN:
+ if(isdigit(c))
+ state = FLOATEXP;
+ else
+ throw parse_error(buf);
+ break;
+
+ case FLOATEXP:
+ if(!isdigit(c))
+ throw parse_error(buf);
+ break;
+
+ case STRING:
+ if(c=='\\')
+ state = STRING_ESCAPE;
+ else if(c=='"')
+ state = STRING_END;
+ break;
+
+ case STRING_ESCAPE:
+ state = STRING;
+ break;
+
+ case IDENTIFIER:
+ if(!isalpha(c))
+ throw parse_error(buf);
+ break;
+
+ case STRING_END:
+ throw parse_error(buf);
+
+ default:
+ throw logic_error("bad parser state");
+ }
+
+ if(is_delimiter(next) && state>=ACCEPT)
+ {
+ if(state==STRING_END)
+ return Token(Token::STRING, unescape(buf.substr(1, buf.size()-2)));
+ else
+ return Token(token_type[state], buf);
+ }
+ }
+}
+
+bool JsonParser::is_delimiter(int c)
+{
+ return (isspace(c) || c=='{' || c=='}' || c=='[' || c==']' || c==':' || c==',');
+}
+
+string JsonParser::unescape(const string &str)
+{
+ string result;
+ StringCodec::Utf8::Decoder dec;
+ StringCodec::Utf8::Encoder enc;
+ bool escape = false;
+
+ for(string::const_iterator i=str.begin(); i!=str.end(); )
+ {
+ StringCodec::unichar c = dec.decode_char(str, i);
+
+ if(escape)
+ {
+ if(c=='\"')
+ enc.encode_char('\"', result);
+ else if(c=='\\')
+ enc.encode_char('\\', result);
+ else if(c=='/')
+ enc.encode_char('/', result);
+ else if(c=='b')
+ enc.encode_char('\b', result);
+ else if(c=='f')
+ enc.encode_char('\f', result);
+ else if(c=='n')
+ enc.encode_char('\n', result);
+ else if(c=='r')
+ enc.encode_char('\r', result);
+ else if(c=='t')
+ enc.encode_char('\t', result);
+ else if(c=='u')
+ {
+ unsigned code = 0;
+ for(unsigned n=0; n<4; ++n)
+ {
+ if(i==str.end())
+ throw invalid_argument("JsonParser::unescape");
+
+ c = dec.decode_char(str, i);
+
+ unsigned digit = 0;
+ if(c>='0' && c<='9')
+ digit = c-'0';
+ else if(c>='a' && c<='f')
+ digit = c-'a'+10;
+ else if(c>='A' && c<='F')
+ digit = c-'A'+10;
+ else
+ throw invalid_argument("JsonParser::unescape");
+
+ code = (code<<4)+digit;
+ }
+
+ enc.encode_char(code, result);
+ }
+ else
+ throw invalid_argument("JsonParser::unescape");
+
+ escape = false;
+ }
+ else if(c=='\\')
+ escape = true;
+ else
+ enc.encode_char(c, result);
+ }
+
+ return result;
+}
+
+} // namespace DataFile
+} // namespace Msp
--- /dev/null
+#ifndef MSP_DATAFILE_JSONPARSER_H_
+#define MSP_DATAFILE_JSONPARSER_H_
+
+#include "parsermode.h"
+#include "token.h"
+
+namespace Msp {
+namespace DataFile {
+
+class JsonParser: public ParserMode
+{
+private:
+ enum State
+ {
+ STATE_INIT,
+ STATE_ARRAY,
+ STATE_OBJECT,
+ STATE_END
+ };
+
+ State toplevel_state;
+
+public:
+ JsonParser(Input &, const std::string &);
+
+ virtual Statement parse();
+private:
+ Statement parse_statement(const Token *, State, const std::string &);
+ Token parse_token();
+ bool is_delimiter(int);
+ std::string unescape(const std::string &);
+};
+
+} // namespace DataFile
+} // namespace Msp
+
+#endif
#include <msp/core/raii.h>
#include <msp/strings/format.h>
-#include "dataerror.h"
+#include "except.h"
#include "loader.h"
#include "type.h"
namespace Msp {
namespace DataFile {
-class unknown_keyword: public runtime_error
-{
-public:
- unknown_keyword(const std::string &k):
- runtime_error(k)
- { }
-
- virtual ~unknown_keyword() throw() { }
-};
-
-
-class invalid_signature: public runtime_error
-{
-public:
- invalid_signature(const std::string &k, const std::string &s):
- runtime_error(format("%s %s", k, s))
- { }
-
- virtual ~invalid_signature() throw() { }
-};
-
-
Loader::Loader():
cur_st(0),
direct(false),
+++ /dev/null
-#include <msp/debug/demangle.h>
-#include "objectloader.h"
-
-using namespace std;
-
-namespace Msp {
-namespace DataFile {
-
-no_collection::no_collection(const type_info &t):
- runtime_error(Debug::demangle(t.name()))
-{ }
-
-} // namespace DataFile
-} // namespace Msp
#ifndef MSP_DATAFILE_OBJECTLOADER_H_
#define MSP_DATAFILE_OBJECTLOADER_H_
-#include <typeinfo>
+#include "except.h"
#include "loader.h"
namespace Msp {
namespace DataFile {
-class no_collection: public std::runtime_error
-{
-public:
- no_collection(const std::type_info &);
-};
-
class Collection;
/**
#include <msp/strings/format.h>
#include "binaryparser.h"
-#include "dataerror.h"
+#include "except.h"
+#include "jsonparser.h"
#include "parser.h"
#include "statement.h"
#include "textparser.h"
in(i),
main_src(s),
src(s),
- good(true),
- mode(new TextParser(in, src))
-{ }
+ good(true)
+{
+ char c = in.peek();
+ if(c=='{' || c=='[')
+ mode = new JsonParser(in, src);
+ else
+ mode = new TextParser(in, src);
+}
Parser::~Parser()
{
#include <msp/strings/format.h>
#include "statement.h"
+#include "token.h"
#include "type.h"
using namespace std;
return result;
}
+Statement &Statement::append_from_token(const Token &token)
+{
+ if(token.type==Token::INTEGER)
+ return append(lexical_cast<IntType::Store>(token.str));
+ else if(token.type==Token::FLOAT)
+ return append(lexical_cast<FloatType::Store>(token.str));
+ else if(token.type==Token::STRING)
+ return append(token.str);
+ else if(token.type==Token::IDENTIFIER)
+ {
+ if(token.str=="true")
+ return append(true);
+ else if(token.str=="false")
+ return append(false);
+ else
+ return append(Symbol(token.str));
+ }
+ else
+ throw invalid_argument("Statement::append_from_token");
+}
+
StatementInfo::StatementInfo():
args_size(0)
namespace Msp {
namespace DataFile {
+struct Token;
+
class Statement
{
public:
return *this;
}
+ Statement &append_from_token(const Token &);
+
template<typename T>
Statement &operator,(const T &v)
{ return append(v); }
#include <msp/strings/format.h>
#include <msp/strings/utils.h>
+#include "except.h"
#include "input.h"
#include "textparser.h"
#include "token.h"
namespace Msp {
namespace DataFile {
-class parse_error: public runtime_error
-{
-public:
- parse_error(const std::string &t):
- runtime_error(t.empty() ? "at end of input" : format("after '%s'", t))
- { }
-
- virtual ~parse_error() throw() { }
-};
-
-
-class syntax_error: public runtime_error
-{
-public:
- syntax_error(const std::string &t):
- runtime_error(t.empty() ? "at end of input" : format("at '%s'", t))
- { }
-
- virtual ~syntax_error() throw() { }
-};
-
-
TextParser::TextParser(Input &i, const string &s):
ParserMode(i, s)
{ }
Statement TextParser::parse_statement(const Token *t)
{
Statement result;
- bool sub = false;
- bool finish = false;
+ unsigned sub = 0;
while(in)
{
result.source = src;
result.line = in.get_line_number();
}
- else if(sub)
+ else if(sub==1)
{
if(token.str=="}")
- {
- sub = false;
- finish = true;
- }
+ sub = 2;
else
{
Statement ss = parse_statement(&token);
result.sub.push_back(ss);
}
}
- else if(finish)
+ else if(sub==2)
{
if(token.str!=";")
throw syntax_error(token.str);
break;
}
else if(token.str=="{")
- sub = true;
+ sub = 1;
else if(token.str==";")
break;
- else if(token.type==Token::INTEGER)
- result.append(lexical_cast<IntType::Store>(token.str));
- else if(token.type==Token::FLOAT)
- result.append(lexical_cast<FloatType::Store>(token.str));
- else if(token.type==Token::STRING)
- result.append(token.str);
- else if(token.type==Token::IDENTIFIER)
- {
- if(token.str=="true")
- result.append(true);
- else if(token.str=="false")
- result.append(false);
- else
- result.append(Symbol(token.str));
- }
+ else if(token.type!=Token::SPECIAL)
+ result.append_from_token(token);
else
throw syntax_error(token.str);
}
FLOATEXPINIT,
FLOATEXPSIGN,
STRING,
+ STRING_ESCAPE,
ACCEPT,
ZERO,
DECIMAL,
Token::SPECIAL,
Token::SPECIAL,
Token::SPECIAL,
+ Token::SPECIAL,
Token::INTEGER,
Token::INTEGER,
Token::INTEGER,
ParseState state = INIT;
string buf;
- bool escape = false;
while(in || state==INIT)
{
case STRING:
if(c=='\\')
- escape = !escape;
- else if(c=='"' && !escape)
+ state = STRING_ESCAPE;
+ else if(c=='"')
state = STRING_END;
- else
- escape = false;
+ break;
+
+ case STRING_ESCAPE:
+ state = STRING;
break;
case IDENTIFIER: