2010-05-21 03:13:36 +02:00
|
|
|
/**********************************************
|
|
|
|
|
|
|
|
License: BSD
|
|
|
|
Project Webpage: http://cajun-jsonapi.sourceforge.net/
|
|
|
|
Author: Terry Caton
|
|
|
|
|
|
|
|
***********************************************/
|
|
|
|
|
2011-12-22 22:10:10 +01:00
|
|
|
#include "libaegisub/cajun/reader.h"
|
|
|
|
|
|
|
|
#ifndef LAGI_PRE
|
2010-05-21 03:13:36 +02:00
|
|
|
#include <cassert>
|
|
|
|
#include <set>
|
|
|
|
#include <sstream>
|
2011-12-22 22:10:10 +01:00
|
|
|
#endif
|
2010-05-21 03:13:36 +02:00
|
|
|
|
2011-12-22 22:10:00 +01:00
|
|
|
/*
|
2010-05-21 03:13:36 +02:00
|
|
|
|
|
|
|
TODO:
|
|
|
|
* better documentation
|
|
|
|
* unicode character decoding
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace json
|
|
|
|
{
|
|
|
|
|
2011-12-22 22:10:10 +01:00
|
|
|
std::istream& operator >> (std::istream& istr, UnknownElement& elementRoot) {
|
2010-05-21 03:13:36 +02:00
|
|
|
Reader::Read(elementRoot, istr);
|
|
|
|
return istr;
|
|
|
|
}
|
|
|
|
|
2011-12-22 22:10:10 +01:00
|
|
|
Reader::Location::Location() :
|
2010-05-21 03:13:36 +02:00
|
|
|
m_nLine(0),
|
|
|
|
m_nLineOffset(0),
|
|
|
|
m_nDocOffset(0)
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////
|
|
|
|
// Reader::InputStream
|
|
|
|
|
2011-12-22 22:10:00 +01:00
|
|
|
// wrapper around istream to keep track of document/line offsets
|
|
|
|
class Reader::InputStream
|
2010-05-21 03:13:36 +02:00
|
|
|
{
|
2011-12-22 22:10:00 +01:00
|
|
|
std::istream& m_iStr;
|
|
|
|
Location m_Location;
|
2010-05-21 03:13:36 +02:00
|
|
|
public:
|
2011-12-22 22:10:00 +01:00
|
|
|
InputStream(std::istream& iStr) : m_iStr(iStr) { }
|
|
|
|
|
|
|
|
int Get() {
|
|
|
|
assert(!m_iStr.eof());
|
|
|
|
int c = m_iStr.get();
|
|
|
|
|
|
|
|
++m_Location.m_nDocOffset;
|
|
|
|
if (c == '\n') {
|
|
|
|
++m_Location.m_nLine;
|
|
|
|
m_Location.m_nLineOffset = 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
++m_Location.m_nLineOffset;
|
|
|
|
}
|
2010-05-21 03:13:36 +02:00
|
|
|
|
2011-12-22 22:10:00 +01:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
int Peek() {
|
|
|
|
assert(!m_iStr.eof());
|
2010-05-21 03:13:36 +02:00
|
|
|
return m_iStr.peek();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EOS() {
|
|
|
|
m_iStr.peek(); // apparently eof flag isn't set until a character read is attempted. whatever.
|
|
|
|
return m_iStr.eof();
|
|
|
|
}
|
|
|
|
|
|
|
|
const Location& GetLocation() const { return m_Location; }
|
|
|
|
};
|
|
|
|
|
|
|
|
//////////////////////
|
|
|
|
// Reader::TokenStream
|
|
|
|
|
|
|
|
class Reader::TokenStream
|
|
|
|
{
|
|
|
|
const Tokens& m_Tokens;
|
|
|
|
Tokens::const_iterator m_itCurrent;
|
|
|
|
|
2011-10-18 00:00:38 +02:00
|
|
|
public:
|
|
|
|
TokenStream(const Tokens& tokens)
|
|
|
|
: m_Tokens(tokens), m_itCurrent(tokens.begin())
|
|
|
|
{ }
|
2010-05-21 03:13:36 +02:00
|
|
|
|
2011-10-18 00:00:38 +02:00
|
|
|
const Token& Peek() {
|
2011-12-22 22:10:00 +01:00
|
|
|
assert(!EOS());
|
|
|
|
return *m_itCurrent;
|
2011-10-18 00:00:38 +02:00
|
|
|
}
|
|
|
|
const Token& Get() {
|
2011-12-22 22:10:00 +01:00
|
|
|
assert(!EOS());
|
|
|
|
return *m_itCurrent++;
|
2011-10-18 00:00:38 +02:00
|
|
|
}
|
2010-05-21 03:13:36 +02:00
|
|
|
|
2011-10-18 00:00:38 +02:00
|
|
|
bool EOS() const {
|
2011-12-22 22:10:00 +01:00
|
|
|
return m_itCurrent == m_Tokens.end();
|
2011-10-18 00:00:38 +02:00
|
|
|
}
|
|
|
|
};
|
2010-05-21 03:13:36 +02:00
|
|
|
|
|
|
|
///////////////////
|
|
|
|
// Reader (finally)
|
2011-12-22 22:10:10 +01:00
|
|
|
void Reader::Read(Object& object, std::istream& istr) { Read_i(object, istr); }
|
|
|
|
void Reader::Read(Array& array, std::istream& istr) { Read_i(array, istr); }
|
|
|
|
void Reader::Read(String& string, std::istream& istr) { Read_i(string, istr); }
|
|
|
|
void Reader::Read(Number& number, std::istream& istr) { Read_i(number, istr); }
|
|
|
|
void Reader::Read(Boolean& boolean, std::istream& istr) { Read_i(boolean, istr); }
|
|
|
|
void Reader::Read(Null& null, std::istream& istr) { Read_i(null, istr); }
|
|
|
|
void Reader::Read(UnknownElement& unknown, std::istream& istr) { Read_i(unknown, istr); }
|
2010-05-21 03:13:36 +02:00
|
|
|
|
2011-12-22 22:10:00 +01:00
|
|
|
template <typename ElementTypeT>
|
2010-05-21 03:13:36 +02:00
|
|
|
void Reader::Read_i(ElementTypeT& element, std::istream& istr)
|
|
|
|
{
|
|
|
|
Reader reader;
|
|
|
|
|
|
|
|
Tokens tokens;
|
|
|
|
InputStream inputStream(istr);
|
|
|
|
reader.Scan(tokens, inputStream);
|
|
|
|
|
|
|
|
TokenStream tokenStream(tokens);
|
|
|
|
reader.Parse(element, tokenStream);
|
|
|
|
|
2011-12-22 22:10:00 +01:00
|
|
|
if (!tokenStream.EOS())
|
2010-05-21 03:13:36 +02:00
|
|
|
{
|
|
|
|
const Token& token = tokenStream.Peek();
|
2011-10-18 00:00:38 +02:00
|
|
|
throw ParseException("Expected End of token stream; found " + token.sValue, token.locBegin, token.locEnd);
|
2010-05-21 03:13:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-22 22:10:10 +01:00
|
|
|
void Reader::Scan(Tokens& tokens, InputStream& inputStream)
|
2010-05-21 03:13:36 +02:00
|
|
|
{
|
2011-12-22 22:10:00 +01:00
|
|
|
while (EatWhiteSpace(inputStream), !inputStream.EOS())
|
2010-05-21 03:13:36 +02:00
|
|
|
{
|
|
|
|
// if all goes well, we'll create a token each pass
|
|
|
|
Token token;
|
|
|
|
token.locBegin = inputStream.GetLocation();
|
|
|
|
|
|
|
|
// gives us null-terminated string
|
|
|
|
std::string sChar;
|
|
|
|
sChar.push_back(inputStream.Peek());
|
|
|
|
|
|
|
|
switch (sChar[0])
|
|
|
|
{
|
|
|
|
case '{':
|
|
|
|
token.sValue = sChar[0];
|
|
|
|
MatchExpectedString(sChar, inputStream);
|
|
|
|
token.nType = Token::TOKEN_OBJECT_BEGIN;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '}':
|
|
|
|
token.sValue = sChar[0];
|
|
|
|
MatchExpectedString(sChar, inputStream);
|
|
|
|
token.nType = Token::TOKEN_OBJECT_END;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '[':
|
|
|
|
token.sValue = sChar[0];
|
|
|
|
MatchExpectedString(sChar, inputStream);
|
|
|
|
token.nType = Token::TOKEN_ARRAY_BEGIN;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ']':
|
|
|
|
token.sValue = sChar[0];
|
|
|
|
MatchExpectedString(sChar, inputStream);
|
|
|
|
token.nType = Token::TOKEN_ARRAY_END;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ',':
|
|
|
|
token.sValue = sChar[0];
|
|
|
|
MatchExpectedString(sChar, inputStream);
|
|
|
|
token.nType = Token::TOKEN_NEXT_ELEMENT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ':':
|
|
|
|
token.sValue = sChar[0];
|
|
|
|
MatchExpectedString(sChar, inputStream);
|
|
|
|
token.nType = Token::TOKEN_MEMBER_ASSIGN;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '"':
|
|
|
|
MatchString(token.sValue, inputStream);
|
|
|
|
token.nType = Token::TOKEN_STRING;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '-':
|
|
|
|
case '0':
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
|
|
|
case '5':
|
|
|
|
case '6':
|
|
|
|
case '7':
|
|
|
|
case '8':
|
|
|
|
case '9':
|
|
|
|
MatchNumber(token.sValue, inputStream);
|
|
|
|
token.nType = Token::TOKEN_NUMBER;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 't':
|
|
|
|
token.sValue = "true";
|
|
|
|
MatchExpectedString(token.sValue, inputStream);
|
|
|
|
token.nType = Token::TOKEN_BOOLEAN;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'f':
|
|
|
|
token.sValue = "false";
|
|
|
|
MatchExpectedString(token.sValue, inputStream);
|
|
|
|
token.nType = Token::TOKEN_BOOLEAN;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'n':
|
|
|
|
token.sValue = "null";
|
|
|
|
MatchExpectedString(token.sValue, inputStream);
|
|
|
|
token.nType = Token::TOKEN_NULL;
|
|
|
|
break;
|
|
|
|
|
2011-12-22 22:10:00 +01:00
|
|
|
default:
|
2011-10-18 00:00:38 +02:00
|
|
|
throw ScanException("Unexpected character in stream: " + sChar, inputStream.GetLocation());
|
2010-05-21 03:13:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
token.locEnd = inputStream.GetLocation();
|
|
|
|
tokens.push_back(token);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-22 22:10:10 +01:00
|
|
|
void Reader::EatWhiteSpace(InputStream& inputStream)
|
2010-05-21 03:13:36 +02:00
|
|
|
{
|
2011-12-22 22:10:00 +01:00
|
|
|
while (!inputStream.EOS() && ::isspace(inputStream.Peek()))
|
2010-05-21 03:13:36 +02:00
|
|
|
inputStream.Get();
|
|
|
|
}
|
|
|
|
|
2011-12-22 22:10:10 +01:00
|
|
|
void Reader::MatchExpectedString(const std::string& sExpected, InputStream& inputStream)
|
2010-05-21 03:13:36 +02:00
|
|
|
{
|
|
|
|
std::string::const_iterator it(sExpected.begin()),
|
|
|
|
itEnd(sExpected.end());
|
|
|
|
for ( ; it != itEnd; ++it) {
|
|
|
|
if (inputStream.EOS() || // did we reach the end before finding what we're looking for...
|
|
|
|
inputStream.Get() != *it) // ...or did we find something different?
|
|
|
|
{
|
2011-10-18 00:00:38 +02:00
|
|
|
throw ScanException("Expected string: " + sExpected, inputStream.GetLocation());
|
2010-05-21 03:13:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// all's well if we made it here, return quietly
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-22 22:10:10 +01:00
|
|
|
void Reader::MatchString(std::string& string, InputStream& inputStream)
|
2010-05-21 03:13:36 +02:00
|
|
|
{
|
|
|
|
MatchExpectedString("\"", inputStream);
|
2011-12-22 22:10:00 +01:00
|
|
|
|
2010-05-21 03:13:36 +02:00
|
|
|
while (inputStream.EOS() == false &&
|
|
|
|
inputStream.Peek() != '"')
|
|
|
|
{
|
|
|
|
char c = inputStream.Get();
|
|
|
|
|
|
|
|
// escape?
|
|
|
|
if (c == '\\' &&
|
|
|
|
inputStream.EOS() == false) // shouldn't have reached the end yet
|
|
|
|
{
|
|
|
|
c = inputStream.Get();
|
|
|
|
switch (c) {
|
|
|
|
case '/': string.push_back('/'); break;
|
|
|
|
case '"': string.push_back('"'); break;
|
|
|
|
case '\\': string.push_back('\\'); break;
|
|
|
|
case 'b': string.push_back('\b'); break;
|
|
|
|
case 'f': string.push_back('\f'); break;
|
|
|
|
case 'n': string.push_back('\n'); break;
|
|
|
|
case 'r': string.push_back('\r'); break;
|
|
|
|
case 't': string.push_back('\t'); break;
|
|
|
|
case 'u': // TODO: what do we do with this?
|
2011-10-18 00:00:38 +02:00
|
|
|
default:
|
|
|
|
throw ScanException("Unrecognized escape sequence found in string: \\" + c, inputStream.GetLocation());
|
2010-05-21 03:13:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
string.push_back(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// eat the last '"' that we just peeked
|
|
|
|
MatchExpectedString("\"", inputStream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-22 22:10:10 +01:00
|
|
|
void Reader::MatchNumber(std::string& sNumber, InputStream& inputStream)
|
2010-05-21 03:13:36 +02:00
|
|
|
{
|
|
|
|
const char sNumericChars[] = "0123456789.eE-+";
|
|
|
|
std::set<char> numericChars;
|
|
|
|
numericChars.insert(sNumericChars, sNumericChars + sizeof(sNumericChars));
|
|
|
|
|
|
|
|
while (inputStream.EOS() == false &&
|
|
|
|
numericChars.find(inputStream.Peek()) != numericChars.end())
|
|
|
|
{
|
2011-10-18 00:00:38 +02:00
|
|
|
sNumber.push_back(inputStream.Get());
|
2010-05-21 03:13:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-22 22:10:10 +01:00
|
|
|
void Reader::Parse(UnknownElement& element, Reader::TokenStream& tokenStream)
|
2010-05-21 03:13:36 +02:00
|
|
|
{
|
|
|
|
if (tokenStream.EOS()) {
|
2011-10-18 00:00:38 +02:00
|
|
|
throw ParseException("Unexpected end of token stream", Location(), Location()); // nowhere to point to
|
2010-05-21 03:13:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const Token& token = tokenStream.Peek();
|
|
|
|
switch (token.nType) {
|
|
|
|
case Token::TOKEN_OBJECT_BEGIN:
|
|
|
|
{
|
|
|
|
// implicit non-const cast will perform conversion for us (if necessary)
|
|
|
|
Object& object = element;
|
|
|
|
Parse(object, tokenStream);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Token::TOKEN_ARRAY_BEGIN:
|
|
|
|
{
|
|
|
|
Array& array = element;
|
|
|
|
Parse(array, tokenStream);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Token::TOKEN_STRING:
|
|
|
|
{
|
|
|
|
String& string = element;
|
|
|
|
Parse(string, tokenStream);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Token::TOKEN_NUMBER:
|
|
|
|
{
|
|
|
|
Number& number = element;
|
|
|
|
Parse(number, tokenStream);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Token::TOKEN_BOOLEAN:
|
|
|
|
{
|
|
|
|
Boolean& boolean = element;
|
|
|
|
Parse(boolean, tokenStream);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Token::TOKEN_NULL:
|
|
|
|
{
|
|
|
|
Null& null = element;
|
|
|
|
Parse(null, tokenStream);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
{
|
2011-10-18 00:00:38 +02:00
|
|
|
throw ParseException("Unexpected token: " + token.sValue, token.locBegin, token.locEnd);
|
2010-05-21 03:13:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-22 22:10:10 +01:00
|
|
|
void Reader::Parse(Object& object, Reader::TokenStream& tokenStream)
|
2010-05-21 03:13:36 +02:00
|
|
|
{
|
|
|
|
MatchExpectedToken(Token::TOKEN_OBJECT_BEGIN, tokenStream);
|
|
|
|
|
|
|
|
bool bContinue = (tokenStream.EOS() == false &&
|
|
|
|
tokenStream.Peek().nType != Token::TOKEN_OBJECT_END);
|
|
|
|
while (bContinue)
|
|
|
|
{
|
|
|
|
// first the member name. save the token in case we have to throw an exception
|
|
|
|
const Token& tokenName = tokenStream.Peek();
|
2011-10-18 00:00:38 +02:00
|
|
|
std::string const& name = MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
|
|
|
|
|
|
|
|
if (object.count(name))
|
|
|
|
{
|
|
|
|
throw ParseException("Duplicate object member token: " + name, tokenName.locBegin, tokenName.locEnd);
|
|
|
|
}
|
2010-05-21 03:13:36 +02:00
|
|
|
|
|
|
|
// ...then the key/value separator...
|
|
|
|
MatchExpectedToken(Token::TOKEN_MEMBER_ASSIGN, tokenStream);
|
|
|
|
|
|
|
|
// ...then the value itself (can be anything).
|
2011-10-18 00:00:38 +02:00
|
|
|
UnknownElement value;
|
|
|
|
Parse(value, tokenStream);
|
2010-05-21 03:13:36 +02:00
|
|
|
|
2011-10-18 00:00:38 +02:00
|
|
|
object[name] = value;
|
2010-05-21 03:13:36 +02:00
|
|
|
|
|
|
|
bContinue = (tokenStream.EOS() == false &&
|
|
|
|
tokenStream.Peek().nType == Token::TOKEN_NEXT_ELEMENT);
|
|
|
|
if (bContinue)
|
|
|
|
MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
|
|
|
|
}
|
|
|
|
|
|
|
|
MatchExpectedToken(Token::TOKEN_OBJECT_END, tokenStream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-22 22:10:10 +01:00
|
|
|
void Reader::Parse(Array& array, Reader::TokenStream& tokenStream)
|
2010-05-21 03:13:36 +02:00
|
|
|
{
|
|
|
|
MatchExpectedToken(Token::TOKEN_ARRAY_BEGIN, tokenStream);
|
|
|
|
|
|
|
|
bool bContinue = (tokenStream.EOS() == false &&
|
|
|
|
tokenStream.Peek().nType != Token::TOKEN_ARRAY_END);
|
|
|
|
while (bContinue)
|
|
|
|
{
|
|
|
|
// ...what's next? could be anything
|
2011-10-17 23:59:35 +02:00
|
|
|
array.push_back(UnknownElement());
|
|
|
|
UnknownElement& element = array.back();
|
2010-05-21 03:13:36 +02:00
|
|
|
Parse(element, tokenStream);
|
|
|
|
|
|
|
|
bContinue = (tokenStream.EOS() == false &&
|
|
|
|
tokenStream.Peek().nType == Token::TOKEN_NEXT_ELEMENT);
|
|
|
|
if (bContinue)
|
|
|
|
MatchExpectedToken(Token::TOKEN_NEXT_ELEMENT, tokenStream);
|
|
|
|
}
|
|
|
|
|
|
|
|
MatchExpectedToken(Token::TOKEN_ARRAY_END, tokenStream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-22 22:10:10 +01:00
|
|
|
void Reader::Parse(String& string, Reader::TokenStream& tokenStream)
|
2010-05-21 03:13:36 +02:00
|
|
|
{
|
|
|
|
string = MatchExpectedToken(Token::TOKEN_STRING, tokenStream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-22 22:10:10 +01:00
|
|
|
void Reader::Parse(Number& number, Reader::TokenStream& tokenStream)
|
2010-05-21 03:13:36 +02:00
|
|
|
{
|
|
|
|
const Token& currentToken = tokenStream.Peek(); // might need this later for throwing exception
|
|
|
|
const std::string& sValue = MatchExpectedToken(Token::TOKEN_NUMBER, tokenStream);
|
|
|
|
|
|
|
|
std::istringstream iStr(sValue);
|
|
|
|
double dValue;
|
|
|
|
iStr >> dValue;
|
|
|
|
|
|
|
|
// did we consume all characters in the token?
|
|
|
|
if (iStr.eof() == false)
|
|
|
|
{
|
2011-10-18 00:00:38 +02:00
|
|
|
throw ParseException("Unexpected character in NUMBER token: " + iStr.peek(), currentToken.locBegin, currentToken.locEnd);
|
2010-05-21 03:13:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
number = dValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-22 22:10:10 +01:00
|
|
|
void Reader::Parse(Boolean& boolean, Reader::TokenStream& tokenStream)
|
2010-05-21 03:13:36 +02:00
|
|
|
{
|
|
|
|
const std::string& sValue = MatchExpectedToken(Token::TOKEN_BOOLEAN, tokenStream);
|
2011-10-18 00:00:38 +02:00
|
|
|
boolean = (sValue == "true");
|
2010-05-21 03:13:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-22 22:10:10 +01:00
|
|
|
void Reader::Parse(Null&, Reader::TokenStream& tokenStream)
|
2010-05-21 03:13:36 +02:00
|
|
|
{
|
|
|
|
MatchExpectedToken(Token::TOKEN_NULL, tokenStream);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-12-22 22:10:10 +01:00
|
|
|
const std::string& Reader::MatchExpectedToken(Token::Type nExpected, Reader::TokenStream& tokenStream)
|
2010-05-21 03:13:36 +02:00
|
|
|
{
|
|
|
|
if (tokenStream.EOS())
|
|
|
|
{
|
2011-10-18 00:00:38 +02:00
|
|
|
throw ParseException("Unexpected End of token stream", Location(), Location()); // nowhere to point to
|
2010-05-21 03:13:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const Token& token = tokenStream.Get();
|
|
|
|
if (token.nType != nExpected)
|
|
|
|
{
|
2011-10-18 00:00:38 +02:00
|
|
|
throw ParseException("Unexpected token: " + token.sValue, token.locBegin, token.locEnd);
|
2010-05-21 03:13:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return token.sValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // End namespace
|