/* Trigo Game Notation Parser * Similar as PGN (Portable Game Notation) format */ /* ========== Lexical Grammar ========== */ %lex %% \s+ /* skip whitespace */ \n /* skip newlines */ ";"[^\n]* /* skip line comments */ "{"[^}]*"}" /* skip block comments */ /* Brackets */ "[" return '[' "]" return ']' /* String literals */ \"([^\\\"]|\\.)*\" return 'STRING' /* Tag names - Common PGN tags */ "Event" return 'TAG_EVENT' "Site" return 'TAG_SITE' "Date" return 'TAG_DATE' "Round" return 'TAG_ROUND' "Black" return 'TAG_BLACK' "White" return 'TAG_WHITE' "Result" return 'TAG_RESULT' /* Trigo-specific tags */ "Board" return 'TAG_BOARD' "Handicap" return 'TAG_HANDICAP' "Rules" return 'TAG_RULES' "TimeControl" return 'TAG_TIMECONTROL' "Annotator" return 'TAG_ANNOTATOR' "Application" return 'TAG_APPLICATION' /* Game result symbols */ "B+" return 'RESULT_BLACK' "W+" return 'RESULT_WHITE' "=" return '=' /* draw */ "*" return '*' /* unknown */ /* Move notation placeholders - will be expanded later */ [1-9][0-9]* return 'NUMBER' "." return 'DOT' "Pass" return 'PASS' "Resign" return 'RESIGN' "points" return 'POINTS' "stones" return 'STONES' [x](?=[1-9]) return 'TIMES' /* Coordinates for moves - will be refined */ [a-z0]+ return 'COORDINATE' /* Generic tag name for extensibility */ [A-Z][A-Za-z0-9_]* return 'TAG_NAME' /* End of file */ <> return 'EOF' /* Catch-all for unexpected characters */ . return 'INVALID' /lex /* ========== Grammar Rules ========== */ %% game : tag_section move_section EOF { return { tags: $1, moves: $2, success: true }; } | tag_section EOF { return { tags: $1, moves: null, success: true }; } | move_section EOF { return { tags: {}, moves: $1, success: true }; } | EOF { return { tags: {}, moves: null, success: true }; } ; /* ========== Tag Section (Metadata) ========== */ tag_section : tag_pair -> $1 | tag_section tag_pair -> Object.assign({}, $1, $2) ; tag_pair : "[" tag_name STRING "]" { const tagName = $2; const tagValue = $3.slice(1, -1); // Remove quotes $$ = { [tagName]: tagValue }; } | "[" TAG_RESULT game_result "]" -> $3 | "[" TAG_BOARD board_shape "]" -> ({[$2]: $3}) ; tag_name : TAG_EVENT | TAG_SITE | TAG_DATE | TAG_ROUND | TAG_BLACK | TAG_WHITE | TAG_HANDICAP | TAG_RULES | TAG_TIMECONTROL | TAG_ANNOTATOR | TAG_APPLICATION | TAG_NAME { $$ = yytext; } ; /* ========== Movetext Section (Game Body) ========== */ /* PLACEHOLDER: Will be implemented in future iterations */ move_section : move_sequence //| move_sequence game_termination ; move_sequence : move_sequence_intact -> $1 | move_sequence_truncated -> $1 ; move_sequence_intact : /* empty */ -> [] | move_sequence_intact move_round -> $1.concat([$2]) ; move_sequence_truncated : move_sequence_intact move_round_half -> $1.concat([$2]) ; move_round : number DOT move_action move_action -> ({ round: $1, action_black: $3, action_white: $4 }) ; move_round_half : number DOT move_action -> ({ round: $1, action_black: $3 }) ; move_action : PASS -> ({ type: 'pass' }) | RESIGN -> ({ type: 'resign' }) | COORDINATE { // Placeholder: Parse coordinate notation $$ = { type: 'move', position: yytext }; } ; game_result : win -> ({Result: $1}) | win conquer -> ({Result: $1, Conquer: $2}) | "=" -> ({Result: "draw"}) | "*" -> ({Result: "unknown"}) ; win : RESULT_BLACK -> "black win" | RESULT_WHITE -> "white win" ; conquer : number conquer_unit -> ({n: $1, unit: $2}) ; conquer_unit : POINTS | STONES ; board_shape : number -> [$1] | board_shape TIMES number -> $1.concat($3) ; number : NUMBER -> parseInt($1) ; %% /* ========== Additional JavaScript Code ========== */ // Parser configuration parser.yy = { // Helper functions can be added here parseError: function(str, hash) { throw new Error('Parse error: ' + str); } };