export class TTCQueryParser { private data: QueryableObject; private relativeMap: Map; constructor(data: QueryableObject) { this.data = data; this.relativeMap = new Map(); this.buildRelativeMap(this.data, null); } public search( query: string, currentObject: QueryableObject = this.data ): any[] { // Normalize the query string by trimming whitespace query = query.trim(); // Determine the base structure to search let result: any[] = []; // Perform initial parsing based on the query syntax if (query.startsWith("^")) { result = this.queryCurrentObject(query, currentObject); } else if (query.startsWith("$")) { result = this.queryRelativeObject(query, currentObject); } else { result = this.searchInObject(this.data, query); } return result; } private queryCurrentObject( query: string, currentObject: QueryableObject ): any[] { // Example implementation for querying the current object const subQuery = query.substring(1); // Remove '^' return this.searchInObject(currentObject, subQuery); } private queryRelativeObject( query: string, currentObject: QueryableObject ): any[] { const relativeObject = this.relativeMap.get(currentObject); if (!relativeObject) { throw new Error("Relative object not found."); } const subQuery = query.substring(1); // Remove '$' return this.searchInObject(relativeObject, subQuery); } private buildRelativeMap( obj: QueryableObject, relative: QueryableObject | null ): void { if (obj.relative) { relative = obj; } this.relativeMap.set(obj, relative || obj); for (const key in obj) { if (typeof obj[key] === "object" && obj[key] !== null) { this.buildRelativeMap(obj[key], relative); } } } private searchInObject(obj: any, subQuery?: string): any[] { // Handle subqueries and search in the provided object if (!subQuery) { return [obj]; // Return the entire object if no subquery is present } // Split the subquery based on dot (.) to navigate through the object const keys = subQuery.split("."); let current: any = obj; for (const key of keys) { if (current && typeof current === "object") { if (key.includes("[") && key.includes("]")) { const [prop, selector] = key.split("["); const index = selector.slice(0, -1); if (Array.isArray(current[prop])) { if (!isNaN(Number(index))) { current = current[prop][Number(index)]; } else { const [k, comparator, value] = this.parseSelector(index); current = this.applySelector(current[prop], k, comparator, value); } } else { return []; } } else { current = Array.isArray(current) ? current.map((e: any) => e[key]) : current[key]; } } else { return []; } } return Array.isArray(current) ? current : [current]; } private parseSelector(selector: string): [string, string, string] { const match = selector.match(/(.+)(=|==|>|<|>=|<=|!!|!|!=)(.+)/); if (match) { return [match[1], match[2], match[3]]; } return ["", "=", selector]; } private applySelector( array: any[], key: string, comparator: string, value: string ): any[] { switch (comparator) { case "=": return array.filter((item) => (key ? item[key] : item).includes(value)); case "==": return array.filter((item) => (key ? item[key] : item) === value); case ">": return array.filter((item) => (key ? item[key] : item) > value); case "<": return array.filter((item) => (key ? item[key] : item) < value); case ">=": return array.filter((item) => (key ? item[key] : item) >= value); case "<=": return array.filter((item) => (key ? item[key] : item) <= value); case "!!": return array.filter((item) => (key ? item[key] : item)); case "!=": return array.filter( (item) => !(key ? item[key] : item).includes(value) ); case "!": return array.filter((item) => !(key ? item[key] : item)); default: return []; } } } // Example usage: // const // const parser = new TTCQueryParser(data); // // Example queries // console.log(parser.search("$weapon_abilities[name=Rapid Fire].body")); // Example output // // console.log(parser.search("^weapon_abilities[name=Rapid Fire]", data)); // Example output // console.log(parser.search("$weapon_abilities[name=Rapid Fire].body", data)); // Example output