/** * TGN Parser TypeScript Wrapper * * Wraps the jison-generated parser with TypeScript types * * Based on lotus project architecture: * - Use jison npm package for grammar compilation at build time * - Generate parser to .js file in build phase * - Use synchronous parsing (no async needed) * - Works in both browser and Node.js environments */ /** * Parsed move action - represents a single player's action in a round */ export interface ParsedMoveAction { type: "move" | "pass" | "resign"; position?: string; // ab0yz coordinate notation } /** * Parsed move round - contains both black and white moves */ export interface ParsedMoveRound { round: number; action_black: ParsedMoveAction; action_white?: ParsedMoveAction; } /** * Parsed game result */ export interface ParsedGameResult { Result: string; // "black win" | "white win" | "draw" | "unknown" Conquer?: { n: number; unit: string; // "points" | "stones" }; } /** * Parsed TGN tags (metadata) */ export interface ParsedTags { Event?: string; Site?: string; Date?: string; Round?: string; Black?: string; White?: string; Result?: string; Board?: number[]; // [x, y, z] or [x, y] Handicap?: string; Rules?: string; TimeControl?: string; Annotator?: string; Application?: string; [key: string]: string | number[] | ParsedGameResult | undefined; } /** * Parser output structure */ export interface TGNParseResult { tags: ParsedTags; moves: ParsedMoveRound[] | null; success: boolean; } /** * Parser error with position information */ export class TGNParseError extends Error { constructor( message: string, public line?: number, public column?: number, public hash?: any ) { super(message); this.name = "TGNParseError"; } } // Will be set by initialization code or build process let parserModule: any = null; /** * Set the parser module (called by initialization code) * This allows the pre-built parser to be used */ export function setParserModule(module: any): void { parserModule = module; } /** * Get the parser module * Throws error if parser not loaded */ function getParser() { if (!parserModule) { throw new Error( "TGN parser not loaded. Please ensure the parser has been built.\n" + "Run: npm run build:parsers" ); } return parserModule; } /** * Parse TGN string and return structured data * Synchronous parsing (no async needed) * * @param tgnString - TGN formatted game notation * @returns Parsed game data with tags and moves * @throws TGNParseError if parsing fails */ export function parseTGN(tgnString: string): TGNParseResult { const parser = getParser(); if (!parser.parse) { throw new Error("TGN parser parse method not available"); } try { const result = parser.parse(tgnString); return result as TGNParseResult; } catch (error: any) { // Wrap jison errors with our custom error type throw new TGNParseError( error.message || "Unknown parse error", error.hash?.line, error.hash?.loc?.first_column, error.hash ); } } /** * Validate TGN string without fully parsing * Synchronous validation (no async needed) * * @param tgnString - TGN formatted game notation * @returns Object with valid flag and error message if invalid */ export function validateTGN(tgnString: string): { valid: boolean; error?: string } { try { parseTGN(tgnString); return { valid: true }; } catch (error: any) { return { valid: false, error: error.message || "Unknown validation error" }; } }