UNPKG

igir

Version:

🕹 A zero-setup ROM collection manager that sorts, filters, extracts or archives, patches, and reports on collections of any size on any OS.

122 lines (121 loc) • 3.76 kB
import IgirException from '../../exceptions/igirException.js'; /** * A parser for CMPRo schema DATs. * @see http://www.logiqx.com/DatFAQs/CMPro.php */ export default class CMProParser { static WHITESPACE_CHARS = new Set([' ', '\t', '\n', '\r', '\v']); contents; pos = 0; constructor(contents) { this.contents = contents; } /** * Parse the CMPro DAT's file contents. */ parse() { this.pos = 0; const result = {}; while (this.pos < this.contents.length) { const tag = this.parseTag(); const value = this.parseValue(); const existing = result[tag]; if (existing === undefined) { result[tag] = value; } else { if (Array.isArray(existing)) { result[tag] = [...existing, value]; } else { result[tag] = [existing, value]; } } this.skipWhitespace(); } return result; } skipWhitespace() { while (CMProParser.WHITESPACE_CHARS.has(this.contents.charAt(this.pos))) { this.pos += 1; } } parseObject() { if (this.contents.charAt(this.pos) === '(') { this.pos += 1; } this.skipWhitespace(); const result = {}; while (this.contents.charAt(this.pos) !== ')') { const tag = this.parseTag(); const value = this.parseValue(); const existing = result[tag]; if (existing === undefined) { result[tag] = value; } else { if (Array.isArray(existing)) { result[tag] = [...existing, value]; } else { result[tag] = [existing, value]; } } this.skipWhitespace(); } this.pos += 1; return result; } parseTag() { this.skipWhitespace(); const initialPos = this.pos; while (!CMProParser.WHITESPACE_CHARS.has(this.contents.charAt(this.pos))) { this.pos += 1; } return this.contents.slice(initialPos, this.pos); } parseValue() { this.skipWhitespace(); // Parse object if (this.contents.charAt(this.pos) === '(') { this.pos += 1; return this.parseObject(); } // Parse quoted string if (this.contents.charAt(this.pos) === '"') { return this.parseQuotedString(); } // Parse unquoted string return this.parseUnquotedString(); } parseQuotedString() { if (this.contents.charAt(this.pos) !== '"') { throw new IgirException('invalid quoted string'); } this.pos += 1; const initialPos = this.pos; while (this.pos < this.contents.length) { // String termination, return the value if (this.contents.charAt(this.pos) === '"') { const value = this.contents.slice(initialPos, this.pos); this.pos += 1; return value; } // Quoted character, skip it if (this.contents.charAt(this.pos) === '\\') { this.pos += 2; } else { this.pos += 1; } } throw new IgirException('invalid quoted string'); } parseUnquotedString() { const initialPos = this.pos; while (!CMProParser.WHITESPACE_CHARS.has(this.contents.charAt(this.pos))) { this.pos += 1; } return this.contents.slice(initialPos, this.pos); } }