void Any::deserializeName(TextInput& ti, Token& token, std::string& name) { debugAssert(token.type() == Token::SYMBOL); std::string s = token.string(); while (!isOpen(s[0])) { name += s; // Skip newlines and comments token = ti.readSignificant(); if (token.type() != Token::SYMBOL) { throw ParseError(ti.filename(), token.line(), token.character(), "Expected symbol while parsing Any"); } s = token.string(); } }
void Any::readUntilCommaOrClose(TextInput& ti, Token& token) { while (!(((token.type() == Token::SYMBOL) && (isClose(token.string()[0]))) || isSeparator(token.string()[0]))) { switch (token.type()) { case Token::NEWLINE: case Token::COMMENT: // Consume token = ti.read(); break; default: throw ParseError(ti.filename(), token.line(), token.character(), "Expected a comma or close paren"); } } }
void Any::deserializeBody(TextInput& ti, Token& token) { char closeSymbol = '}'; m_type = TABLE; const char c = token.string()[0]; if (c != '{') { m_type = ARRAY; // Chose the appropriate close symbol closeSymbol = (c == '(') ? ')' : ']'; } // Allocate the underlying data structure ensureData(); m_data->source.set(ti, token); // Consume the open token token = ti.read(); while (!((token.type() == Token::SYMBOL) && (token.string()[0] == closeSymbol))) { // Read any leading comment. This must be done here (and not in the recursive deserialize // call) in case the body contains only a comment. std::string comment; deserializeComment(ti, token, comment); if ((token.type() == Token::SYMBOL) && (token.string()[0] == closeSymbol)) { // We're done; this catches the case where the array is empty break; } // Pointer the value being read Any a; std::string key; if (m_type == TABLE) { // Read the key if (token.type() != Token::SYMBOL && token.type() != Token::STRING) { throw ParseError(ti.filename(), token.line(), token.character(), "Expected a name"); } key = token.string(); // Consume everything up to the = sign token = ti.readSignificant(); if ((token.type() != Token::SYMBOL) || (token.string() != "=")) { throw ParseError(ti.filename(), token.line(), token.character(), "Expected ="); } else { // Consume (don't consume comments--we want the value pointed to by a to get those). token = ti.read(); } } a.deserialize(ti, token); if (!comment.empty()) { // Prepend the comment we read earlier a.ensureData(); a.m_data->comment = trimWhitespace(comment + "\n" + a.m_data->comment); } if (m_type == TABLE) { set(key, a); } else { append(a); } // Read until the comma or close paren, discarding trailing comments and newlines readUntilCommaOrClose(ti, token); // Consume the comma if (isSeparator(token.string()[0])) { token = ti.read(); } } // Consume the close paren (to match other deserialize methods) token = ti.read(); }
void Any::deserialize(TextInput& ti, Token& token) { // Deallocate old data dropReference(); m_type = NONE; m_simpleValue.b = false; // Skip leading newlines while (token.type() == Token::NEWLINE) { token = ti.read(); } std::string comment; if (token.type() == Token::COMMENT) { deserializeComment(ti, token, comment); } if (token.type() == Token::END) { // There should never be a comment without an Any following it; even // if the file ends with some commented out stuff, // that should not happen after a comma, so we'd never read that // far in a proper file. throw ParseError(ti.filename(), token.line(), token.character(), "File ended without a properly formed Any"); } switch (token.type()) { case Token::STRING: m_type = STRING; ensureData(); *(m_data->value.s) = token.string(); m_data->source.set(ti, token); break; case Token::NUMBER: m_type = NUMBER; m_simpleValue.n = token.number(); ensureData(); m_data->source.set(ti, token); break; case Token::BOOLEAN: m_type = BOOLEAN; m_simpleValue.b = token.boolean(); ensureData(); m_data->source.set(ti, token); break; case Token::SYMBOL: // Named Array, Named Table, Array, Table, or NONE if (toUpper(token.string()) == "NONE") { // Nothing left to do; we initialized to NONE originally ensureData(); m_data->source.set(ti, token); } else { // Array or Table // Parse the name // s must have at least one element or this would not have // been parsed as a symbol std::string name; deserializeName(ti, token, name); if (token.type() != Token::SYMBOL) { throw ParseError(ti.filename(), token.line(), token.character(), "Malformed Any TABLE or ARRAY; must start with [, (, or {"); } if (isOpen(token.string()[0])) { // Array or table deserializeBody(ti, token); } else { throw ParseError(ti.filename(), token.line(), token.character(), "Malformed Any TABLE or ARRAY; must start with [, (, or {"); } if (!name.empty()) { ensureData(); m_data->name = name; } } // if NONE break; default: throw ParseError(ti.filename(), token.line(), token.character(), "Unexpected token"); } // switch if (!comment.empty()) { ensureData(); m_data->comment = comment; } if (m_type != ARRAY && m_type != TABLE) { // Array and table already consumed their last token token = ti.read(); } }