77 "io"
88 "regexp"
99 "strconv"
10- "strings"
1110)
1211
1312type pgnToken int
@@ -33,7 +32,7 @@ const (
3332)
3433
3534const (
36- pgnSAN_REGEXP = "^(((O-O|O-O-O)|((P?|[RNBQK])[a-h]?[1-8]?x?[a-h][1-8](=[PRNBQK])?))(\\ +|#)?(!| \\ ?|!!| \\ ? \\ ?| \\ ?!|! \\ ?)? )$"
35+ pgnSAN_REGEXP = "^(((O-O|O-O-O)|((P?|[RNBQK])[a-h]?[1-8]?x?[a-h][1-8](=[PRNBQK])?))(\\ +|#)?)$"
3736 pgnTAG_REGEXP = `^\[?\s*([A-Za-z0-9_]+)\s+"(.*)"\s*\]`
3837)
3938
4342 whiteWins []byte = []byte ("1-0" )
4443 blackWins []byte = []byte ("0-1" )
4544 drawRes []byte = []byte ("1/2-1/2" )
45+
46+ specialAnnotations [][]byte = [][]byte {
47+ []byte ("??" ),
48+ []byte ("!!" ),
49+ []byte ("!?" ),
50+ []byte ("?!" ),
51+ []byte ("?" ),
52+ []byte ("!" ),
53+ }
4654)
4755
4856// Parser is a parser for the PGN chess games notation
@@ -251,6 +259,16 @@ func (t *tokenizer) next() token {
251259 return token {pgnSTRING , tok }
252260 }
253261
262+ // special handling for these six annotations
263+ // they can appear only in the import format
264+ // in export format they are always nags
265+ for _ , s := range specialAnnotations {
266+ if bytes .HasPrefix (t .text , s ) {
267+ t .text = t .text [len (s ):]
268+ return token {pgnTOKEN , string (s )}
269+ }
270+ }
271+
254272 if bytes .HasPrefix (t .text , whiteWins ) {
255273 t .text = t .text [len (whiteWins ):]
256274 return token {pgnRESULT , "1-0" }
@@ -329,7 +347,7 @@ func (t *tokenizer) next() token {
329347 break
330348 }
331349 }
332- tok := string (t .text [0 :pos ])
350+ tok := string (t .text [0 : pos ])
333351 t .text = t .text [pos :]
334352 if isSymbol {
335353 return token {pgnSYMBOL , tok }
@@ -338,6 +356,10 @@ func (t *tokenizer) next() token {
338356 } else if isInteger {
339357 return token {pgnINTEGER , tok }
340358 }
359+ if tok == "" {
360+ // quick and ugly hack to get error reporting right
361+ tok = string (t .text [0 : len (t .text )])
362+ }
341363 return token {pgnTOKEN , tok }
342364}
343365
@@ -417,25 +439,6 @@ func (t *tokenizer) generatePlies(variation *Variation, inRav bool, thisMoveNumb
417439 return fmt .Errorf ("mismatched SAN '%s'" , token .val )
418440 }
419441 SAN = token .val
420- if strings .HasSuffix (SAN , "!!" ) {
421- SAN = SAN [:len (SAN )- 2 ]
422- ply .Nags = append (ply .Nags , 3 )
423- } else if strings .HasSuffix (SAN , "??" ) {
424- SAN = SAN [:len (SAN )- 2 ]
425- ply .Nags = append (ply .Nags , 4 )
426- } else if strings .HasSuffix (SAN , "!?" ) {
427- SAN = SAN [:len (SAN )- 2 ]
428- ply .Nags = append (ply .Nags , 5 )
429- } else if strings .HasSuffix (SAN , "?!" ) {
430- SAN = SAN [:len (SAN )- 2 ]
431- ply .Nags = append (ply .Nags , 6 )
432- } else if strings .HasSuffix (SAN , "!" ) {
433- SAN = SAN [:len (SAN )- 1 ]
434- ply .Nags = append (ply .Nags , 1 )
435- } else if strings .HasSuffix (SAN , "?" ) {
436- SAN = SAN [:len (SAN )- 1 ]
437- ply .Nags = append (ply .Nags , 2 )
438- }
439442 }
440443 ply = & Ply {SAN : SAN }
441444 variation .Plies = append (variation .Plies , ply )
@@ -467,7 +470,34 @@ func (t *tokenizer) generatePlies(variation *Variation, inRav bool, thisMoveNumb
467470 }
468471
469472 default :
470- return fmt .Errorf ("unexpected token '%d:%s'" , token .typ , token .val )
473+ switch token .val {
474+ case "!" :
475+ if ply != nil {
476+ ply .Nags = append (ply .Nags , 1 )
477+ }
478+ case "?" :
479+ if ply != nil {
480+ ply .Nags = append (ply .Nags , 2 )
481+ }
482+ case "!!" :
483+ if ply != nil {
484+ ply .Nags = append (ply .Nags , 3 )
485+ }
486+ case "??" :
487+ if ply != nil {
488+ ply .Nags = append (ply .Nags , 4 )
489+ }
490+ case "!?" :
491+ if ply != nil {
492+ ply .Nags = append (ply .Nags , 5 )
493+ }
494+ case "?!" :
495+ if ply != nil {
496+ ply .Nags = append (ply .Nags , 6 )
497+ }
498+ default :
499+ return fmt .Errorf ("unexpected token '%s'" , token .val )
500+ }
471501 }
472502 }
473503 return nil
0 commit comments