valve-key-values
Version:
A simple parser of the KeyValues format from Valve
154 lines • 5.22 kB
JavaScript
var VKV;
(function (VKV) {
let TokenType;
(function (TokenType) {
TokenType["ObjectOpen"] = "object_open";
TokenType["ObjectClose"] = "object_close";
TokenType["String"] = "string";
TokenType["Word"] = "word";
})(TokenType || (TokenType = {}));
function tokenizer(input) {
let current = 0;
const tokens = [];
const length = input.length;
while (current < length) {
let char = input[current];
if (char === "{") {
tokens.push({ type: TokenType.ObjectOpen, value: char });
current++;
continue;
}
if (char === "}") {
tokens.push({ type: TokenType.ObjectClose, value: char });
current++;
continue;
}
if (char === '"') {
let value = "";
let escape = false;
char = input[++current];
while (char !== '"' || escape) {
if (char == "\\" && !escape) {
escape = true;
char = input[++current];
continue;
}
value += char;
char = input[++current];
escape = false;
}
current++;
tokens.push({ type: TokenType.String, value });
continue;
}
if (/[\d\w]/.test(char)) {
// if it's a number or a word character
let value = "";
while (/[\d\w]/.test(char)) {
value += char;
char = input[++current];
}
tokens.push({ type: TokenType.Word, value });
continue;
}
if (/\s/.test(char)) {
current++;
continue;
}
throw new Error("Unexpected character: " + char);
}
return tokens;
}
function parser(tokens) {
if (!tokens.length) {
throw new Error("Nothing to parse. Exiting!");
}
let current = 0;
function advance() {
return tokens[current++];
}
const root = {};
function parseKey() {
const token = advance();
switch (token.type) {
case TokenType.String:
case TokenType.Word:
return token.value.toLowerCase();
default:
throw new Error(`Unexpected token type: ${token.type}`);
}
}
function parseValue() {
const token = advance();
switch (token.type) {
case TokenType.String:
case TokenType.Word:
return token.value;
case TokenType.ObjectOpen:
const obj = {};
while (tokens[current].type != TokenType.ObjectClose) {
const [key, value] = parsePair();
obj[key] = value;
}
current++;
return obj;
default:
throw new Error(`Unexpected token type: ${token.type}`);
}
}
function parsePair() {
let key = parseKey();
let value = parseValue();
return [key, value];
}
while (tokens[current]) {
const [key, value] = parsePair();
root[key] = value;
}
return root;
}
function parse(input) {
const tokens = tokenizer(input);
return parser(tokens);
}
VKV.parse = parse;
const escape_chars = ["\"", "{", "}", "\\"];
function strigify(root) {
let output = "";
function stringifyString(value) {
let result = '"';
for (const char of value) {
if (escape_chars.indexOf(char) != -1)
result += "\\";
result += char;
}
result += '"';
return result;
}
function strigifyObject(root, nested = 1) {
let output = "".padStart(nested - 1, "\t") + "{\n";
for (const key in root) {
output += "".padStart(nested, "\t") + `${stringifyString(key)}`;
const value = root[key];
if (typeof value == "string")
output += `\t${stringifyString(value)}\n`;
else
output += `\n${strigifyObject(value, nested + 1)}\n`;
}
output += "".padStart(nested - 1, "\t") + "}";
return output;
}
for (const key in root) {
output += `${stringifyString(key)}`;
const value = root[key];
if (typeof value == "string")
output += `\t${stringifyString(value)}\n`;
else
output += `\n${strigifyObject(value)}\n`;
}
return output;
}
VKV.strigify = strigify;
})(VKV || (VKV = {}));
export default VKV;
//# sourceMappingURL=index.js.map