Skip to content

Instantly share code, notes, and snippets.

@romgerman
Last active October 5, 2023 13:20
Show Gist options
  • Save romgerman/dd0ee6cf744ac8b6054623fd143200d2 to your computer and use it in GitHub Desktop.
Save romgerman/dd0ee6cf744ac8b6054623fd143200d2 to your computer and use it in GitHub Desktop.
Simple Valve Key Values parser in C++
#include "ValveKeyValue.h"
using namespace Parsers;
#include <exception>
#include <cctype>
ValveKeyValue::Token ValveKeyValue::LexNext()
{
bool readingValue = false;
bool readingRawValue = false;
bool readingComment = false;
std::string value = "";
while (m_lexPos < m_source.size())
{
if (readingComment)
{
if (m_source[m_lexPos] == '\n')
readingComment = false;
m_lexPos++;
continue;
}
if (readingValue || readingRawValue)
{
/*if (m_source[m_lexPos] != '"' &&
m_source[m_lexPos] != '{' &&
m_source[m_lexPos] != ' ' &&
m_source[m_lexPos] != '\t' &&
m_source[m_lexPos] != '\r' &&
m_source[m_lexPos] != '\n')*/
if ((readingRawValue && isalnum(m_source[m_lexPos])) || (readingValue && m_source[m_lexPos] != '"'))
{
value += m_source[m_lexPos];
m_lexPos++;
continue;
}
else
{
readingValue = false;
m_lexPos++;
return Token(TokenTypes::Value, value);
}
}
if (m_source[m_lexPos] == '"') // read value
{
readingValue = true;
value = "";
m_lexPos++;
}
else if (m_source[m_lexPos] == '{')
{
m_lexPos++;
return Token(TokenTypes::BraceOpen, "");
}
else if (m_source[m_lexPos] == '}')
{
m_lexPos++;
return Token(TokenTypes::BraceClose, "");
}
else if (m_source[m_lexPos] == '/')
{
if (readingValue)
{
value += m_source[m_lexPos];
m_lexPos++;
}
else
{
readingComment = true;
m_lexPos++;
}
}
else if (m_source[m_lexPos] == ' ' ||
m_source[m_lexPos] == '\t' ||
m_source[m_lexPos] == '\n' ||
m_source[m_lexPos] == '\r')
{
m_lexPos++;
}
else
{
if (!isdigit(m_source[m_lexPos]))
{
readingRawValue = true;
value = "";
}
}
}
return Token(TokenTypes::End, "");
}
void ValveKeyValue::Parse()
{
//m_current = &data;
Token tok = LexNext();
while (tok.type != TokenTypes::End)
{
switch (tok.type)
{
case TokenTypes::Value:
LexNext(); // Eat {
ParseBlock(tok, data);
tok = LexNext();
break;
default:
throw std::exception("Unexpected token. File is invalid");
break;
}
}
}
void Parsers::ValveKeyValue::ParseBlock(Token& name, KeyValue& container)
{
Token token = LexNext();
auto values = KeyValue();
while (token.type != TokenTypes::BraceClose)
{
auto key = token;
auto value = LexNext();
if (value.type != TokenTypes::BraceOpen)
{
values.values.emplace(key.value, value.value);
}
else
{
auto newdata = KeyValue();
ParseBlock(key, newdata);
values.keys.emplace(key.value, newdata);
}
token = LexNext();
}
container.keys.emplace(name.value, values);
}
#pragma once
#include <map>
namespace Parsers
{
class ValveKeyValue
{
public:
struct KeyValue
{
std::map<std::string, std::string> values;
std::map<std::string, KeyValue> keys;
//std::string GetValue();
//KeyValue GetKey();
};
public:
KeyValue data;
private:
enum class TokenTypes
{
Value, BraceOpen, BraceClose, End
};
struct Token
{
TokenTypes type;
std::string value;
Token(TokenTypes ttype, std::string tvalue)
{
this->type = ttype;
this->value = tvalue;
}
};
private:
Token LexNext(); // Do not support in-line comments
void Parse();
void ParseBlock(Token& name, KeyValue& container);
public:
ValveKeyValue() { }
~ValveKeyValue() { }
void Read(std::string& str)
{
m_source = str;
m_lexPos = 0;
Parse();
}
private:
std::string m_source;
size_t m_lexPos;
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment