Last active
April 9, 2022 08:04
-
-
Save jacoobes/f6e481e9011c0437829add88d7994f83 to your computer and use it in GitHub Desktop.
Made a small dsl for parsing txt files
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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