/** True if the next token begins the close tag */ static bool atClose(TextInput& t, const std::string name) { if ((t.peek().type() == Token::SYMBOL) && (t.peek().string() == "<")) { // Need to keep looking ahead Token p0 = t.read(); if ((t.peek().type() == Token::SYMBOL) && (t.peek().string() == "/")) { // Check the name on the close tag. It *must* match if // this is a well-formed document, but there might be a // tag error. Token p1 = t.read(); Token p2 = t.peek(); std::string s = p2.string(); debugAssertM(beginsWith(name, s), "Mismatched close tag"); // Put the tokens back t.push(p1); t.push(p0); return true; } else { // Put the read token back t.push(p0); return false; } } else { return false; } }
void Any::deserialize(TextInput& ti) { beforeRead(); Token token = ti.read(); deserialize(ti, token); // Restore the last token ti.push(token); }
GLenum Shader::ShaderProgram::getDeclarationType(TextInput& ti, bool& uniform) { uniform = false; if (ti.peek().type() == Token::SYMBOL) { bool readyForType = false; String s = ti.peek().string(); //Parse all qualifiers before the type: while (! readyForType) { if (isQualifier(s)) { uniform = (uniform || (s == "uniform")); ti.readSymbol(s); s = ti.peek().string(); } else if (s == "layout") { //This should properly parse through all possible layout inputs //http://www.opengl.org/registry/doc/GLSLangSpec.4.30.6.pdf (pg52) ti.readSymbol(s); ti.readSymbol("("); Token t = ti.read(); while ((t.type() != Token::SYMBOL) || (t.string() != ")")) { t = ti.read(); } s = ti.peek().string(); } else { // The next token is not a qualifier of any sort, so it is probably the type, if this is a declaration readyForType = true; } } // Read the type String variableSymbol = ti.readSymbol(); // check for unsigned int, short, long long, etc if (variableSymbol == "unsigned") { while (toGLType(variableSymbol + " " + ti.peek().string()) != GL_NONE) { variableSymbol += " " + ti.readSymbol(); } } // If the variableSymbol is not a valid type, then this is not a variable declaration, and GL_NONE will be returned // Checking for not being a declaration cannot be done earlier, as a declaration could have no qualifiers return toGLType(variableSymbol); } else { return GL_NONE; } }
void Any::deserializeComment(TextInput& ti, Token& token, std::string& comment) { // Parse comments while (token.type() == Token::COMMENT) { comment += trimWhitespace(token.string()) + "\n"; // Allow comments to contain newlines. do { token = ti.read(); comment += "\n"; } while (token.type() == Token::NEWLINE); } comment = trimWhitespace(comment); }
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(); } }
void XML::deserialize(TextInput& t) { begin: Token n = t.read(); m_attribute.clear(); m_child.clear(); m_name = ""; m_value = ""; if ((n.type() == Token::SYMBOL) && (n.string() == "<")) { // Beginning a tag // Read name n = t.read(); debugAssert(n.type() == Token::SYMBOL); bool isComment = (n.string() == "!") && (t.peek().type() == Token::SYMBOL) && (t.peek().string() == "--"); // ignored tag: <?xml> or <!xml> // comment tag: <!-- ... --> if ((n.string() == "?") || ((n.string() == "!") && ! isComment)) { // Ignore this tag while (t.hasMore() && ! ((n.type() == Token::SYMBOL) && (n.string() == ">"))) { n = t.read(); } goto begin; } else if (isComment) { // Ignore until "-->" bool prevWasDash = false; while (t.hasMore() && ! ((n.type() == Token::SYMBOL) && (n.string() == ">") && prevWasDash)) { prevWasDash = (n.type() == Token::SYMBOL) && (n.string() == "--"); n = t.read(); } goto begin; } // Keep reading until no colon m_name += n.string(); n = t.read(); while ((n.type() == Token::SYMBOL) && (n.string() == ":")) { // tag with namespace: <x:y> m_name += ":" + t.readSymbol(); n = t.read(); } // Read end of tag/close bool done = false; while (! done) { debugAssert(n.type() == Token::SYMBOL); if (n.string() == "/") { // empty-element tag: <foo/> // Consume the close tag t.readSymbol(">"); done = true; } else if (n.string() == ">") { // End of open tag: read children until close tag while (! atClose(t, m_name)) { m_child.next().deserialize(t); } // Read close tag (we wouldn't be here unless it parses correctly) while (t.hasMore() && ! (t.readSymbol() == ">")) {} done = true; } else { // Attribute pair std::string k = n.string(); t.readSymbol("="); std::string v = t.read().string(); m_attribute.set(k, v); // Advance to next n = t.read(); } } } else { // Beginning embedded content. Read until the end of file or the next tag. m_type = VALUE; m_value += n.string(); n = t.peek(); while (t.hasMore() && ! ((n.type() == Token::SYMBOL) && (n.string() == "<"))) { m_value += " " + t.read().string(); n = t.peek(); } } }