fastparselite
Version:
Super simple & fast DSL parser for lightweight config-like data using custom syntax.
143 lines (116 loc) • 3.79 kB
JavaScript
const fs = require('fs');
function parseLiteData(text) {
// First, remove all comments
const cleanText = text.replace(/(#|\/\/).*$/gm, '');
const result = {};
// Split by blocks starting with @
const blocks = cleanText.split('@').slice(1);
blocks.forEach(block => {
const firstBrace = block.indexOf('{');
const lastBrace = block.lastIndexOf('}');
if (firstBrace === -1 || lastBrace === -1) return;
const name = block.substring(0, firstBrace).trim();
const content = block.substring(firstBrace + 1, lastBrace).trim();
result[name] = parseBlockContent(content);
});
return result;
}
function parseBlockContent(content) {
const obj = {};
let currentPos = 0;
const len = content.length;
while (currentPos < len) {
// Skip whitespace
while (currentPos < len && isWhitespace(content[currentPos])) {
currentPos++;
}
if (currentPos >= len) break;
// Find key
const keyMatch = content.slice(currentPos).match(/^([a-zA-Z_]\w*):/);
if (!keyMatch) break;
const key = keyMatch[1];
currentPos += key.length + 1; // Skip key and colon
// Skip whitespace after colon
while (currentPos < len && isWhitespace(content[currentPos])) {
currentPos++;
}
if (currentPos >= len) break;
// Determine value type
if (content[currentPos] === '{') {
// Nested object
let depth = 1;
let endPos = currentPos + 1;
while (endPos < len && depth > 0) {
if (content[endPos] === '{') depth++;
if (content[endPos] === '}') depth--;
endPos++;
}
const innerContent = content.substring(currentPos + 1, endPos - 1);
obj[key] = parseBlockContent(innerContent);
currentPos = endPos;
}
else if (content[currentPos] === '[') {
// Array
let endPos = currentPos + 1;
while (endPos < len && content[endPos] !== ']') {
endPos++;
}
const arrayContent = content.substring(currentPos + 1, endPos);
obj[key] = arrayContent.split(',').map(item => convertValue(item.trim()));
currentPos = endPos + 1;
}
else {
// Primitive value
let endPos = currentPos;
while (endPos < len && !isWhitespace(content[endPos]) && content[endPos] !== '}') {
endPos++;
}
const value = content.substring(currentPos, endPos).trim();
obj[key] = convertValue(value);
currentPos = endPos;
}
}
return obj;
}
function isWhitespace(char) {
return char === ' ' || char === '\n' || char === '\r' || char === '\t';
}
function convertValue(value) {
if (value === 'true') return true;
if (value === 'false') return false;
if (!isNaN(value)) return Number(value);
if ((value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))) {
return value.slice(1, -1);
}
return value;
}
// // Test with your exact sample
// const input = `
// @user {
// name: "Alice"
// age: 25
// address: {
// city: "Chennai"
// location: {
// lat: 13.08
// long: 80.27
// }
// description: "something"
// }
// skills: [Python, JavaScript, C++]
// active: true
// }
// @demo {
// name: "dinesh"
// }
// `;
// const parsed = parseLiteData(input);
// console.log(parsed.user)
// ======== Usage =========
const input = fs.readFileSync("sample.ld", "utf8");
const result = parseLiteData(input);
const flatUser = flatten(result);
// console.log("User name:", flatUser.name);
console.log("User active:", flatUser.active);
console.log("User address city:", flatUser.address_description);