finished parser protoype

This commit is contained in:
Ulf Gebhardt 2023-04-10 14:30:28 +02:00
parent 6c63f25bcf
commit eaa0e0f74b
Signed by: ulfgebhardt
GPG Key ID: DA6B843E748679C9

View File

@ -1,89 +1,198 @@
// TODO file handle?
import { FileHandle, open } from 'fs/promises';
// const file = await fsPromise.open('./words.txt', 'r');
const TOC_PREFIX_TAG = '## '
const TOC_PREFIX_COMMENT = '#'
const TOC_TAG_SEPARATOR = ':'
const TOC_LINE_MAX_LENGTH = 1024
/*
https://wowwiki-archive.fandom.com/wiki/TOC_format
https://wowpedia.fandom.com/wiki/TOC_format
*/
import { FileHandle } from 'fs/promises';
export const TOC_PREFIX_TAG = '## '
export const TOC_PREFIX_COMMENT = '#'
export const TOC_TAG_DELIMITER = ':'
export const TOC_LINE_MAX_LENGTH = 1024
export type TOC = {/*interface: number, title: string, titleLocalized: Record<string,string>, xTags: Record<string, string>,*/ allTags: Record<string, string>,files: string[]}
export const TOC_TAG_NAME_INTERFACE = 'Interface'
export const TOC_TAG_NAME_TITLE = 'Title'
export const TOC_TAG_NAME_NOTES = 'Notes'
export const TOC_TAG_NAME_ICONTEXTURE = 'IconTexture'
export const TOC_TAG_NAME_ICONATLAS = 'IconAtlas'
export const TOC_TAG_NAME_ADDONCOMPARTMENTFUNC = 'AddonCompartmentFunc'
export const TOC_TAG_NAME_ADDONCOMPARTMENTFUNCONENTER = 'AddonCompartmentFuncOnEnter'
export const TOC_TAG_NAME_ADDONCOMPARTMENTFUNCONLEAVE = 'AddonCompartmentFuncOnLeave'
export const TOC_TAG_NAME_LOADONDEMAND = 'LoadOnDemand'
export const TOC_TAG_NAME_OPTIONALDEPS = 'OptionalDeps'
export const TOC_TAG_NAME_LOADWITH = 'LoadWith'
export const TOC_TAG_NAME_LOADMANAGERS = 'LoadManagers'
export const TOC_TAG_NAME_DEFAULTSTATE = 'DefaultState'
export const TOC_TAG_NAME_SAVEDVARIABLES = 'SavedVariables'
export const TOC_TAG_NAME_SAVEDVARIABESPERCHARACTER = 'SavedVariablesPerCharacter'
export const TOC_TAG_NAME_AUTHOR = 'Author'
export const TOC_TAG_NAME_VERSION = 'Version'
export const parse = async function(file: FileHandle, strict = true) {
const result: TOC = {
// interface: 0,
// title: '',
// titleLocalized: {},
// xTags: {},
allTags: {},
files: [],
export const TOC_TAG_PREFIX_TITLELOCALIZED = 'title-'
export const TOC_TAG_PREFIX_NOTESLOCALIZED = 'notes-'
export const TOC_TAG_PREFIX_DEP = 'dep'
export const TOC_TAG_PREFIX_X = 'x-'
export const TOC_TAG_LIST_DELIMITER = ','
export const TOC_TAG_VALUE_LOADONDEMAND = '1'
export const TOC_TAG_VALUE_DEFAULTSTATE_DISABLED = 'disabled'
export enum WOW_LOCALES {
frFR = 'frFR',
deDE = 'deDE',
enGB = 'enGB',
enUS = 'enUS',
itIT = 'itIT',
koKR = 'koKR',
zhCN = 'zhCN',
zhTW = 'zhTW',
ruRU = 'ruRU',
esES = 'esES',
esMX = 'esMX',
ptBR = 'ptBR',
}
export class TOC {
Interface: number | null = null
Title: string | null = null
TitleLocalized: Partial<Record<WOW_LOCALES,string>> = {}
Notes: string | null = null
NotesLocalized: Partial<Record<WOW_LOCALES,string>> = {}
IconTexture: string | null = null
IconAtlas: string | null = null
AddonCompartmentFunc: string | null = null
AddonCompartmentFuncOnEnter: string | null = null
AddonCompartmentFuncOnLeave: string | null = null
LoadOnDemand: boolean = false
Dependencies: string[] = []
OptionalDeps: string[] = []
LoadWith: string[] = []
LoadManagers: string[] = []
DefaultState: boolean = true
SavedVariables: string[] = []
SavedVariablesPerCharacter: string[] = []
Author: string | null = null
Version: string | null = null
xTags: Record<string, string> = {}
files: string[] = []
/*
toString(){
var result: string[] = [];
for(const tag in toc.allTags) {
result.push(`${TOC_PREFIX_TAG}${tag}${TOC_TAG_SEPARATOR} ${toc.allTags[tag]}`)
}
result.concat(this.files)
return result.join('\n');
};
*/
}
// import { open } from 'fs/promises';
// const file = await open('./words.txt', 'r');
export const parse = async function(file: FileHandle, strict = true) {
const result = new TOC();
const allTags: Record<string, string> = {}
for await (const line of file.readLines()) {
if (strict && line.length > TOC_LINE_MAX_LENGTH) {
throw new Error('Line is longer than TOC_LINE_MAX_LENGTH characters, WoW will truncate!');
}
if (line.slice(0, TOC_PREFIX_TAG.length) === TOC_PREFIX_TAG) {
const tagData = line.slice(TOC_PREFIX_TAG.length+1).split(TOC_TAG_SEPARATOR)
const tagData = line.slice(TOC_PREFIX_TAG.length+1).split(TOC_TAG_DELIMITER)
if(tagData.length !== 2){
throw new Error(`Tag could not be parsed: ${tagData}`)
if(strict){
throw new Error(`Tag could not be parsed: ${tagData}`)
} else {
continue
}
}
const tagName = tagData[0].trim()
const tagValue = tagData[1].trim()
if(strict && result.allTags[tagName]){
if(strict && allTags[tagName]){
throw new Error(`Duplicate value: ${tagName}`)
}
result.allTags[tagName] = tagValue;
allTags[tagName] = tagValue;
// specific tags
/*switch(tagName.toLowerCase()){
case 'interface':
result.interface = Number(tagValue)
switch(tagName.toLowerCase()){
case TOC_TAG_NAME_INTERFACE.toLowerCase():
result.Interface = Number(tagValue)
break;
case 'title':
result.title
case TOC_TAG_NAME_TITLE.toLowerCase():
result.Title = tagValue
break;
}*/
//
case TOC_TAG_NAME_NOTES.toLocaleLowerCase():
result.Notes = tagValue
break
case TOC_TAG_NAME_ICONTEXTURE.toLowerCase():
result.IconTexture = tagValue
break
case TOC_TAG_NAME_ICONATLAS.toLowerCase():
result.IconAtlas = tagValue
break
case TOC_TAG_NAME_ADDONCOMPARTMENTFUNC.toLowerCase():
result.AddonCompartmentFunc = tagValue
break
case TOC_TAG_NAME_ADDONCOMPARTMENTFUNCONENTER.toLowerCase():
result.AddonCompartmentFuncOnEnter = tagValue
break
case TOC_TAG_NAME_ADDONCOMPARTMENTFUNCONLEAVE.toLowerCase():
result.AddonCompartmentFuncOnLeave = tagValue
break
case TOC_TAG_NAME_LOADONDEMAND.toLowerCase():
result.LoadOnDemand = tagValue === TOC_TAG_VALUE_LOADONDEMAND
break
case TOC_TAG_NAME_OPTIONALDEPS.toLowerCase():
result.OptionalDeps = tagValue.split(TOC_TAG_LIST_DELIMITER).map((s) => s.trim())
break
case TOC_TAG_NAME_LOADWITH.toLowerCase():
result.LoadWith = tagValue.split(TOC_TAG_LIST_DELIMITER).map((s) => s.trim())
break
case TOC_TAG_NAME_LOADMANAGERS.toLowerCase():
result.LoadManagers = tagValue.split(TOC_TAG_LIST_DELIMITER).map((s) => s.trim())
break
case TOC_TAG_NAME_DEFAULTSTATE.toLowerCase():
result.DefaultState = tagValue.toLowerCase() !== TOC_TAG_VALUE_DEFAULTSTATE_DISABLED
break
case TOC_TAG_NAME_SAVEDVARIABLES.toLowerCase():
result.SavedVariables = tagValue.split(TOC_TAG_LIST_DELIMITER).map((s) => s.trim())
break
case TOC_TAG_NAME_SAVEDVARIABESPERCHARACTER.toLowerCase():
result.SavedVariablesPerCharacter = tagValue.split(TOC_TAG_LIST_DELIMITER).map((s) => s.trim())
break
case TOC_TAG_NAME_AUTHOR.toLowerCase():
result.Author = tagValue
break
case TOC_TAG_NAME_VERSION.toLowerCase():
result.Version = tagValue
break
default:
if(tagName.toLowerCase().startsWith(TOC_TAG_PREFIX_TITLELOCALIZED) && tagName.length === TOC_TAG_PREFIX_TITLELOCALIZED.length+4){
const locale = tagName.slice(TOC_TAG_PREFIX_TITLELOCALIZED.length,TOC_TAG_PREFIX_TITLELOCALIZED.length+4)
if(Object.keys(WOW_LOCALES).includes(locale)){
result.TitleLocalized[locale as WOW_LOCALES] = tagValue
} else if(strict){
throw new Error(`locale not found ${locale}`)
}
} else if(tagName.toLowerCase().startsWith(TOC_TAG_PREFIX_NOTESLOCALIZED) && tagName.length === TOC_TAG_PREFIX_NOTESLOCALIZED.length+4){
const locale = tagName.slice(TOC_TAG_PREFIX_NOTESLOCALIZED.length,TOC_TAG_PREFIX_NOTESLOCALIZED.length+4)
if(Object.keys(WOW_LOCALES).includes(locale)){
result.NotesLocalized[locale as WOW_LOCALES] = tagValue
} else if(strict){
throw new Error(`locale not found ${locale}`)
}
} else if(tagName.toLowerCase().startsWith(TOC_TAG_PREFIX_DEP)){
result.Dependencies = tagValue.split(TOC_TAG_LIST_DELIMITER).map((s) => s.trim())
} else if(tagName.toLowerCase().startsWith(TOC_TAG_PREFIX_X)){
result.xTags[tagName.slice(TOC_TAG_PREFIX_X.length)] = tagValue
} else if(strict){
throw new Error(`Unknown Tag name: ${tagName}`)
}
}
} else if (line.slice(0, 1) !== TOC_PREFIX_COMMENT && !line.trim()) {
result.files.push(line.trimEnd()));
result.files.push(line.trimEnd());
}
}
return result;
};
/*
https://wowpedia.fandom.com/wiki/TOC_format
## Interface: 100007
## Title: Waiting for Godot
## Title-frFR: En attendant Godot
## Notes: This word is |cFFFF0000red|r
## IconTexture: Interface\Icons\TEMP
## IconAtlas: TaskPOI-Icon
// ## AddonCompartmentFunc: MyAddon_OnAddonCompartmentClick
// ## AddonCompartmentFuncOnEnter: MyAddon_OnAddonCompartmentEnter
// ## AddonCompartmentFuncOnLeave: MyAddon_OnAddonCompartmentLeave
## LoadOnDemand: 1
## Dependencies: someAddOn, someOtherAddOn
## OptionalDeps: someAddOn, someOtherAddOn
## LoadWith: someAddOn, someOtherAddOn
// ## LoadManagers: someAddOn, someOtherAddOn
## DefaultState: disabled
## SavedVariables: MyAddOnNameFoo, MyAddOnNameBar
## SavedVariablesPerCharacter: MyAddOnNameAnotherVariable
## Author
## Version
## X-_____
*/
export const stringify = function(toc: TOC) {
var result: string[] = [];
for(const tag in toc.allTags) {
result.push(`${TOC_PREFIX_TAG}${tag}${TOC_TAG_SEPARATOR} ${toc.allTags[tag]}`)
}
result.concat(toc.files)
return result.join('\n');
};