forked from mishoo/queen.lisp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pgn.lisp
90 lines (84 loc) · 3.57 KB
/
pgn.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
(in-package #:queen)
(defmethod parse-pgn ((in stream))
(with-parse-stream in
(labels
((read-sym ()
(read-while #'alnum?))
(read-header ()
(let (name value)
(skip #\[)
(setf name (read-sym))
(skip-whitespace)
(setf value (read-string))
(skip #\])
(cons name value)))
(read-result ()
(if (eql #\* (peek))
(progn (next) "*")
(look-ahead 3 (lambda (chars)
(unless (member nil chars)
(let ((str (coerce chars 'string)))
(cond
((string= "1-0" str)
"1-0")
((string= "0-1" str)
"0-1")
((string= "1/2" str)
(skip "-1/2")
"1/2-1/2"))))))))
(read-moves (game)
(let ((data '()))
(flet ((move ()
(let* ((movestr (read-while #'non-whitespace?))
(valid (game-parse-san game movestr)))
(skip-whitespace)
(cond
((null valid)
(error "Invalid move (~A)" movestr))
((< 1 (length valid))
(error "Ambiguous move (~A)" movestr)))
(game-move game (car valid))
(push (cons :move (car valid)) data)))
(comment1 ()
(skip #\;)
(read-while (lambda (ch)
(not (eql #\Newline ch)))))
(comment2 ()
(skip #\{)
(prog1
(read-while (lambda (ch)
(not (eql #\} ch))))
(skip #\}))))
(loop while (peek)
do (skip-whitespace)
(or (awhen (read-result)
(push (cons :result it) data)
(return (nreverse data)))
(when (eql (peek) #\;)
(push (cons :comment (comment1)) data))
(when (eql (peek) #\{)
(push (cons :comment (comment2)) data))
(progn
(when (read-number)
(skip #\.)
(when (eql #\. (peek))
(skip ".."))
(skip-whitespace))
(move)))
(skip-whitespace)
finally (return (nreverse data)))))))
(skip-whitespace)
(let* ((headers (loop while (eql #\[ (peek))
collect (prog1 (read-header)
(skip-whitespace))))
(game (make-instance 'game))
(start-fen (assoc "fen" headers :test #'string-equal)))
(reset-from-fen game (if start-fen
(cdr start-fen)
+FEN-START+))
`(:headers ,headers
:moves ,(read-moves game)
:game ,game)))))
(defmethod parse-pgn ((pgn string))
(with-input-from-string (in pgn)
(parse-pgn in)))