grammar SGF; @header {package net.woodyfolsom.msproj.sgf;} @lexer::header {package net.woodyfolsom.msproj.sgf;} collection returns [SGFNodeCollection sgfNodeCollection] @init { $sgfNodeCollection = new SGFNodeCollection(); } : (gameTree{$sgfNodeCollection.add($gameTree.sgfGameTree);})+ ; gameTree returns [SGFGameTree sgfGameTree] @init { $sgfGameTree = new SGFGameTree(); } : LPAREN sequence{$sgfGameTree.setNodeSequence($sequence.nodeSequence);} (gameTree{$sgfGameTree.addSubTree(sgfGameTree);})* RPAREN ; sequence returns [List nodeSequence] @init { $nodeSequence = new ArrayList(); } : (node{$nodeSequence.add($node.sgfNode);})+; node returns [SGFNode sgfNode] @init { $sgfNode = new SGFNode(); } : SEMICOLON (property{$sgfNode.addProperty($property.sgfProperty);})*; property returns [SGFProperty sgfProperty] @init { $sgfProperty = new SGFProperty(); } : (numIdent{$sgfProperty.setIdentifier($numIdent.sgfIdent);}) ((LBRACKET numValue RBRACKET){$sgfProperty.addValue($numValue.sgfValue);})+ //an empty coord is a pass move | (playerIdent{$sgfProperty.setIdentifier($playerIdent.sgfPlayer);}) ((LBRACKET RBRACKET){sgfProperty.addValue(SGFValue.EMPTY);})+ | (playerIdent{$sgfProperty.setIdentifier($playerIdent.sgfPlayer);}) ((LBRACKET coordValue RBRACKET){sgfProperty.addValue(new SGFValue($coordValue.sgfCoord));})+ // 2 rules here because the disambiguating semantic pred. for 'player' would throw NPE | (strIdent{$sgfProperty.setIdentifier($strIdent.sgfIdent);}) ((LBRACKET RBRACKET){$sgfProperty.addValue(SGFValue.EMPTY);}) | (strIdent{$sgfProperty.setIdentifier($strIdent.sgfIdent);}) ((LBRACKET strValue RBRACKET){$sgfProperty.addValue(new SGFValue($strValue.text));})+ | (result{$sgfProperty.setIdentifier(SGFIdentifier.RESULT);}) ((LBRACKET strValue RBRACKET){$sgfProperty.addValue(new SGFValue(new SGFResult($strValue.text)));})+ | (komi{$sgfProperty.setIdentifier(SGFIdentifier.KOMI);}) ((LBRACKET realValue RBRACKET){$sgfProperty.addValue(new SGFValue(Double.parseDouble($realValue.text)));})+ | (coordIdent{$sgfProperty.setIdentifier($coordIdent.sgfIdent);})((LBRACKET coordValue RBRACKET){$sgfProperty.addValue(new SGFValue(new SGFCoord($coordValue.text)));})+ ; //playerIdent is a disambiguating rule playerIdent returns [SGFIdentifier sgfPlayer] : {(input.LT(1).getText().equals("W"))}? strIdent {$sgfPlayer = $strIdent.sgfIdent;} | {(input.LT(1).getText().equals("B"))}? strIdent {$sgfPlayer = $strIdent.sgfIdent;} | {(input.LT(1).getText().equals("White"))}? strIdent {$sgfPlayer = $strIdent.sgfIdent;} | {(input.LT(1).getText().equals("Black"))}? strIdent {$sgfPlayer = $strIdent.sgfIdent;} ; strIdent returns [SGFIdentifier sgfIdent] : charEnc{$sgfIdent = SGFIdentifier.CHARSET;} | source{$sgfIdent = SGFIdentifier.SOURCE;} | blackCountry{$sgfIdent = SGFIdentifier.COUNTRY_BLACK;} | whiteCountry{$sgfIdent = SGFIdentifier.COUNTRY_WHITE;} | event{$sgfIdent = SGFIdentifier.EVENT;} | playerBlack{$sgfIdent = SGFIdentifier.PLAYER_BLACK;} | playerWhite{$sgfIdent = SGFIdentifier.PLAYER_WHITE;} | blackRank{$sgfIdent = SGFIdentifier.RANK_BLACK;} | whiteRank{$sgfIdent = SGFIdentifier.RANK_WHITE;} | rules{$sgfIdent = SGFIdentifier.RULES;} | place{$sgfIdent = SGFIdentifier.PLACE;} | application{$sgfIdent = SGFIdentifier.APPLICATION;} | copyright | username | date{$sgfIdent = SGFIdentifier.DATE;} | 'Black'{$sgfIdent = SGFIdentifier.MOVE_BLACK;} | 'White'{$sgfIdent = SGFIdentifier.MOVE_WHITE;} | 'B'{$sgfIdent = SGFIdentifier.MOVE_BLACK;} | 'W'{$sgfIdent = SGFIdentifier.MOVE_WHITE;} | view{$sgfIdent = SGFIdentifier.VIEW;} //this is not really a strIdent, but is a placeholder since I don't use this info | comment{$sgfIdent = SGFIdentifier.COMMENT;} //SGF grammar proper allows extensions, but this reader does not ; //if this is matched from rule playerIdent, the value is thrown away - this rule alone //lack the context to disambiguate the single-character player movement IDs. coordIdent returns [SGFIdentifier sgfIdent] : addBlack{$sgfIdent = $addBlack.sgfIdent;} | addWhite{$sgfIdent = $addWhite.sgfIdent;} ; numIdent returns [SGFIdentifier sgfIdent] : fileFormat{$sgfIdent = SGFIdentifier.FILE_FORMAT;} | game{$sgfIdent = SGFIdentifier.GAME;} | size{$sgfIdent = SGFIdentifier.SIZE;} | time{$sgfIdent = SGFIdentifier.TIME;}; numValue returns [SGFValue sgfValue] : (STRVALUE){$sgfValue = new SGFValue(Integer.parseInt($numValue.text));}; realIdent : komi; realValue : STRVALUE; coordValue returns [SGFCoord sgfCoord] : STRVALUE {$sgfCoord = new SGFCoord($coordValue.text);} ; fileFormat : 'FF'; game : 'GM' | 'GaMe'; size : 'SZ' | 'SiZe'; view : 'VW' | 'VieW'; charEnc : 'CA'; source : 'SO'; blackCountry : 'BC'; whiteCountry : 'WC'; event : 'EV' | 'EVent'; playerBlack : 'PB' | 'PlayerBlack'; playerWhite : 'PW' | 'PlayerWhite'; blackRank : 'BR'; whiteRank : 'WR'; komi : 'KM' | 'KoMi'; result : 'RE' | 'REsult' ; rules : 'RU'; place : 'PC' | 'PlaCe'; application : 'AP'; time : 'TM'; date : 'DT' | 'DaTe'; addBlack returns [SGFIdentifier sgfIdent] : 'AB'{$sgfIdent = SGFIdentifier.ADD_BLACK;} ; addWhite returns [SGFIdentifier sgfIdent] : 'AW'{$sgfIdent = SGFIdentifier.ADD_WHITE;} ; copyright : 'CP'; username: 'US'; comment : 'C' | 'Comment'; strValue : (STRVALUE)+; SPACE : ' '; fragment DIGIT : '0'..'9'; COLON : ':'; MINUS : '-'; fragment PERIOD : '.'; COMMA : ','; PLUS : '+'; SLASH : '/'; LPAREN : '(' ; RPAREN : ')'; STRVALUE : (UCLETTER | LCLETTER | MINUS | DIGIT | SPACE | PERIOD | COMMA | PLUS | SLASH | COLON | LPAREN | RPAREN )+; SEMICOLON : ';' ; fragment UCLETTER : 'A'..'Z'; fragment LCLETTER : 'a'..'z'; LBRACKET : '['; RBRACKET : ']'; //CR : '\r'{$channel=HIDDEN;}; //NEWLINE : '\n'{$channel=HIDDEN;};