fastparselite
Version:
Super simple & fast DSL parser for lightweight config-like data using custom syntax.
118 lines (97 loc) • 3.38 kB
JavaScript
const fs = require("fs");
function removeComments(text) {
return text
.split("\n")
.map(line => line.replace(/(#|\/\/).*/, "").trim())
.filter(line => line.length > 0)
.join("\n");
}
function parseObjectBlock(content) {
const obj = {};
let i = 0;
const len = content.length;
while (i < len) {
// Skip whitespace and newlines
while (i < len && (content[i] === ' ' || content[i] === '\n' || content[i] === '\r')) i++;
if (i >= len) break;
// Extract key
const keyMatch = content.slice(i).match(/^([a-zA-Z_]\w*):/);
if (!keyMatch) break;
const key = keyMatch[1];
i += key.length + 1; // Skip key and colon
// Skip whitespace after colon
while (i < len && (content[i] === ' ' || content[i] === '\n' || content[i] === '\r')) i++;
if (i >= len) break;
// Handle different value types
if (content[i] === '{') {
// Nested object
let depth = 1;
let j = i + 1;
while (j < len && depth > 0) {
if (content[j] === '{') depth++;
else if (content[j] === '}') depth--;
j++;
}
const innerContent = content.slice(i + 1, j - 1);
obj[key] = parseObjectBlock(innerContent);
i = j;
} else if (content[i] === '[') {
// Array
let j = i + 1;
while (j < len && content[j] !== ']') j++;
const elements = content.slice(i + 1, j).split(',').map(el => el.trim());
obj[key] = elements;
i = j + 1;
} else {
// Primitive value
let j = i;
while (j < len && content[j] !== '\n' && content[j] !== '\r' && content[j] !== ' ') j++;
let value = content.slice(i, j).trim();
// Value type conversion
if (value === 'true') value = true;
else if (value === 'false') value = false;
else if (!isNaN(value)) value = Number(value);
else if ((value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))) {
value = value.slice(1, -1);
}
obj[key] = value;
i = j;
}
}
// console.log(obj);
return obj;
}
function parseLiteData(text) {
const cleanText = removeComments(text);
const data = {};
const blocks = cleanText.match(/@(\w+)\s*{([\s\S]*?)}/g);
if (!blocks) return data;
for (const block of blocks) {
const [, name, content] = block.match(/@(\w+)\s*{([\s\S]*)}/);
data[name] = parseObjectBlock(content.trim());
}
return data;
}
function flatten(obj, prefix = "", result = {}) {
for (const key in obj) {
const value = obj[key];
const newKey = prefix ? `${prefix}_${key}` : key;
if (typeof value === "object" && !Array.isArray(value)) {
flatten(value, newKey, result);
} else {
result[newKey] = value;
}
}
return result;
}
// ======== Usage =========
const input = fs.readFileSync("sample.ld", "utf8");
const result = parseLiteData(input);
console.log(result.user)
// const flatUser = flatten(result.user);
// console.log("User name:", flatUser.name);
// console.log("User active:", flatUser.skills);
// console.log("User address city:", flatUser.skills);
// console.log("User address description:", flatUser.address_description);
// console.log("Latitude:", flatUser.address_location_lat);