+#include "Token.hpp"
#include "Tokenizer.hpp"
#include "TokenStreamReader.hpp"
#include <cctype>
#include <istream>
+#include <ostream>
+#include <sstream>
#include <stdexcept>
+#include <glm/gtc/quaternion.hpp>
using namespace std;
namespace blank {
+ostream &operator <<(ostream &out, Token::Type t) {
+ switch (t) {
+ case Token::ANGLE_BRACKET_OPEN:
+ return out << "ANGLE_BRACKET_OPEN";
+ case Token::ANGLE_BRACKET_CLOSE:
+ return out << "ANGLE_BRACKET_CLOSE";
+ case Token::CHEVRON_OPEN:
+ return out << "CHEVRON_OPEN";
+ case Token::CHEVRON_CLOSE:
+ return out << "CHEVRON_CLOSE";
+ case Token::BRACKET_OPEN:
+ return out << "BRACKET_OPEN";
+ case Token::BRACKET_CLOSE:
+ return out << "BRACKET_CLOSE";
+ case Token::PARENTHESIS_OPEN:
+ return out << "PARENTHESIS_OPEN";
+ case Token::PARENTHESIS_CLOSE:
+ return out << "PARENTHESIS_CLOSE";
+ case Token::COLON:
+ return out << "COLON";
+ case Token::SEMICOLON:
+ return out << "SEMICOLON";
+ case Token::COMMA:
+ return out << "COMMA";
+ case Token::EQUALS:
+ return out << "EQUALS";
+ case Token::NUMBER:
+ return out << "NUMBER";
+ case Token::STRING:
+ return out << "STRING";
+ case Token::IDENTIFIER:
+ return out << "IDENTIFIER";
+ case Token::COMMENT:
+ return out << "COMMENT";
+ default:
+ return out << "UNKNOWN";
+ }
+}
+
+ostream &operator <<(ostream &out, const Token &t) {
+ out << t.type;
+ switch (t.type) {
+ case Token::UNKNOWN:
+ case Token::NUMBER:
+ case Token::STRING:
+ case Token::IDENTIFIER:
+ case Token::COMMENT:
+ return out << '(' << t.value << ')';
+ default:
+ return out;
+ }
+}
+
Tokenizer::Tokenizer(istream &in)
: in(in)
, current() {
istream::sentry s(in);
if (!s) {
- // TODO: error?
+ throw runtime_error("read past the end of stream");
return;
}
case ',': case '=':
current.type = Token::Type(c);
break;
- case '+': case '-':
+ case '+': case '-': case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
in.putback(c);
namespace {
bool is_num_char(istream::char_type c) {
- return isdigit(c)
+ return isxdigit(c)
|| c == '.'
|| c == '-'
|| c == '+'
current.value += '\n';
break;
case 'r':
- current.value += '\t';
+ current.value += '\r';
break;
case 't':
current.value += '\t';
istream::char_type c;
while (in.get(c)) {
- if (isalnum(c) || c == '_') {
+ if (isalnum(c) || c == '_' || c == '.') {
current.value += c;
} else {
in.putback(c);
bool TokenStreamReader::HasMore() {
- while (in.HasMore()) {
- if (in.Next().type != Token::COMMENT) {
- cached = true;
- return true;
- }
+ if (cached) {
+ return true;
}
- return false;
+ SkipComments();
+ return cached;
}
const Token &TokenStreamReader::Next() {
+ SkipComments();
+ cached = false;
+ return in.Current();
+}
+
+void TokenStreamReader::SkipComments() {
if (cached) {
- cached = false;
- return in.Current();
- } else {
- return in.Next();
+ if (in.Current().type == Token::COMMENT) {
+ cached = false;
+ } else {
+ return;
+ }
+ }
+ while (in.HasMore()) {
+ if (in.Next().type != Token::COMMENT) {
+ cached = true;
+ return;
+ }
}
}
const Token &TokenStreamReader::Peek() {
if (!cached) {
- in.Next();
+ Next();
cached = true;
}
return in.Current();
}
-void TokenStreamReader::Assert(Token::Type t) {
+void TokenStreamReader::Assert(Token::Type t) const {
if (GetType() != t) {
- throw runtime_error("unexpected token in input stream");
+ stringstream s;
+ s << "unexpected token in input stream: expected " << t << ", but got " << in.Current();
+ throw runtime_error(s.str());
}
}
out = GetValue();
}
+void TokenStreamReader::ReadRelaxedString(string &out) {
+ out = GetString();
+}
+
void TokenStreamReader::ReadVec(glm::vec2 &v) {
Skip(Token::BRACKET_OPEN);
Skip(Token::BRACKET_CLOSE);
}
+void TokenStreamReader::ReadQuat(glm::quat &q) {
+ Skip(Token::BRACKET_OPEN);
+ ReadNumber(q.w);
+ Skip(Token::COMMA);
+ ReadNumber(q.x);
+ Skip(Token::COMMA);
+ ReadNumber(q.y);
+ Skip(Token::COMMA);
+ ReadNumber(q.z);
+ Skip(Token::BRACKET_CLOSE);
+}
+
bool TokenStreamReader::GetBool() {
Next();
+ return AsBool();
+}
+
+bool TokenStreamReader::AsBool() const {
switch (GetType()) {
case Token::NUMBER:
- return GetInt() != 0;
+ return AsInt() != 0;
case Token::IDENTIFIER:
case Token::STRING:
if (GetValue() == "true" || GetValue() == "yes" || GetValue() == "on") {
} else if (GetValue() == "false" || GetValue() == "no" || GetValue() == "off") {
return false;
} else {
- throw runtime_error("unexpected value in input stream");
+ throw runtime_error("unexpected value in input stream: cannot cast " + GetValue() + " to bool");
}
default:
- throw runtime_error("unexpected token in input stream");
+ {
+ stringstream s;
+ s << "unexpected token in input stream: cannot cast " << in.Current() << " to bool";
+ throw runtime_error(s.str());
+ }
}
}
float TokenStreamReader::GetFloat() {
Next();
+ return AsFloat();
+}
+
+float TokenStreamReader::AsFloat() const {
Assert(Token::NUMBER);
return stof(GetValue());
}
int TokenStreamReader::GetInt() {
Next();
+ return AsInt();
+}
+
+int TokenStreamReader::AsInt() const {
Assert(Token::NUMBER);
return stoi(GetValue());
}
unsigned long TokenStreamReader::GetULong() {
Next();
+ return AsULong();
+}
+
+unsigned long TokenStreamReader::AsULong() const {
Assert(Token::NUMBER);
return stoul(GetValue());
}
+const string &TokenStreamReader::GetString() {
+ Next();
+ return AsString();
+}
+
+const string &TokenStreamReader::AsString() const {
+ return GetValue();
+}
+
}