Skip to content

Instantly share code, notes, and snippets.

@jacoobes
Last active April 9, 2022 08:04
Show Gist options
  • Save jacoobes/f6e481e9011c0437829add88d7994f83 to your computer and use it in GitHub Desktop.
Save jacoobes/f6e481e9011c0437829add88d7994f83 to your computer and use it in GitHub Desktop.
Made a small dsl for parsing txt files
//an example txt fiie
const text =
`uuid=00000000-0000-0000-0009-01f2082a2089
lastOnline=1649034318641
registered=1648920816719
joinedTownAt=0
isNPC=false
title=
surname=
friends=
protectionStatus=residentBuild,s,residentSwitch,residentItemUse
metadata=
`;
// Create models to mirror txt files to a map
const model = {
uuid : "s",
lastOnline : "n",
registered : "n",
joinedTownAt : "n",
isNPC : "b?",
title : "s?",
surname : "s?",
friends : "s?",
protectionStatus : "a,s",
metadata : "s?"
}
// your valid types.
//
// s = string
// n = number
// b = boolean
// a = array
// all can have nullable types, where you append a ? to the end
// example types
// s? => nullable string
// a,s? => array with nullable strings
const validPrims = new Set(['s?','s','n?','n','b?','b']);
//use this function to compile .txt files
function compile(module : Record<string, string>, text: string) {
const kvs = keyVals(text);
return transformParse(module, kvs);
}
function isString(text : string) {
return text.match(/\.*/);
}
function isNull(text : string) {
return text === '';
}
function isNumber(text: string) {
return Number.parseInt(text) !== NaN;
}
function isBool(text : string) {
return text === 'false' || text === 'true';
}
const parseTable = {
['s?'] : parseNullableString,
['s'] : parseString,
['n?']: parseNullableNum,
['n'] : parseNum,
['b?']: parseNullableBool,
['b'] : parseBool
}
function parseNullableString(key : string, text : string) {
if(!isString(key)) {
throw TypeError(`${text} is not a ?string, reading ${key}`);
}
return [key, text || null] as [string, unknown];
}
function parseString(key : string, text : string) {
if (!isString(text) || isNull(text)) {
throw TypeError(`${text || null} is not a string, reading ${key}`);
}
return [key, text] as [string, unknown];
}
function parseNullableNum(key : string, text : string) {
if(!isNumber(key)) {
throw TypeError(`${key} is not a number`);
}
return [key, isNull(text) ? null : Number.parseInt(text)] as [string, unknown];
}
function parseNum(key : string, text : string ) {
if(!isNumber(text) || isNull(text) ) {
throw TypeError(`${text || null} is not a number`);
}
return [key, Number.parseInt(text)] as [string, unknown];
}
function parseNullableBool(key : string, text : string) {
if(!isBool(text)) {
throw TypeError(`${text} is not a ?boolean`)
}
return [key, isNull(text) ? null : text === 'true' ] as [string, unknown];
}
function parseBool ( key : string, text: string,) {
if(!isBool(text) || isNull(key)) {
throw TypeError(`${text || null} is not a boolean`)
}
return [key, text === 'true' ] as [string, unknown];
}
function transformParse(model : Record<string, string>, tokens : string[][]) {
verifySchema(model, tokens);
const parseVals : [string, unknown][] = [];
for(let curEntry = 0; curEntry < tokens.length; curEntry++)
{
const [ key, value ] = tokens[curEntry];
const curModelEntry = model[key];
if(!validPrims.has(curModelEntry)) {
const liType = curModelEntry.substring(2);
const arr = value.split(',').map( el => {
return parseTable[liType as keyof typeof parseTable](key, el)[1]
});
parseVals.push([key, arr]);
} else {
parseVals.push(parseTable[curModelEntry as keyof typeof parseTable](key, value))
}
}
return new Map(parseVals);
}
function verifySchema( model : Record<string,string>, tokens : string[][]) {
const keyVals = Object.entries(model);
for (let entryIndex = 0; entryIndex < keyVals.length; entryIndex++)
{
const curEntry = keyVals[entryIndex];
const curToken = tokens[entryIndex]
if(curEntry[0] !== curToken[0])
{
throw Error(`Mismatched keys ${curEntry[0]}, ${curToken[0]}`);
}
if(!validPrims.has(curEntry[1]))
{
const [posArrayType, delim, prim ] = curEntry[1];
if(posArrayType != 'a')
{
throw Error(`Unknown type ${curEntry[1]}`);
}
if(delim != ',')
{
throw Error('Array types expect a comma after primitive')
}
if(!validPrims.has(prim))
{
throw Error('After comma, expects a primitive')
}
}
}
}
function trimSplit(t: string) { return t.trim().split('=') }
function keyVals(t : string) {
return t.match(/\w+=.*\s+/g)?.map(trimSplit) ?? [];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment