2 // part of libkombilo, http://www.u-go.net/kombilo/
4 // Copyright (c) 2006-7 Ulrich Goertz <u@g0ertz.de>
6 // Permission is hereby granted, free of charge, to any person obtaining a copy of
7 // this software and associated documentation files (the "Software"), to deal in
8 // the Software without restriction, including without limitation the rights to
9 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10 // of the Software, and to permit persons to whom the Software is furnished to do
11 // so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in all
14 // copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 #include "sgfparser.h"
32 SGFError::SGFError() {}
34 ExtendedMoveNumber::ExtendedMoveNumber() {
39 ExtendedMoveNumber::ExtendedMoveNumber(int LENGTH
, int* DATA
) {
41 if (length
) data
= new int[length
];
43 for(int i
=0; i
<length
; i
++) data
[i
] = DATA
[i
];
46 ExtendedMoveNumber::ExtendedMoveNumber(int D
) {
52 ExtendedMoveNumber::ExtendedMoveNumber(const ExtendedMoveNumber
& emn
) {
54 if (length
) data
= new int[length
];
56 for(int i
=0; i
<length
; i
++) data
[i
] = emn
.data
[i
];
59 ExtendedMoveNumber::~ExtendedMoveNumber() {
60 if (data
) delete [] data
;
63 ExtendedMoveNumber
& ExtendedMoveNumber::operator=(const ExtendedMoveNumber
& emn
) {
66 if (data
) delete [] data
;
68 data
= new int[length
];
69 for(int i
=0; i
<length
; i
++) data
[i
] = emn
.data
[i
];
75 void ExtendedMoveNumber::next() {
79 void ExtendedMoveNumber::down() throw(SGFError
) {
80 if (length
==0) throw SGFError();
82 int* newdata
= new int[3];
91 int* newdata
= new int[length
+2];
92 for(int i
=0; i
<length
; i
++) newdata
[i
] = data
[i
];
94 newdata
[length
+1] = 0;
98 } else data
[length
-2]++;
102 int ExtendedMoveNumber::total_move_num() {
104 for(int i
=0; i
<(length
+1)/2; i
++) result
+= data
[2*i
];
108 char* SGFescape(const char* s
) {
109 char* t
= new char[2*strlen(s
)+1];
111 for(unsigned int i
= 0; i
<strlen(s
); i
++) {
112 if (s
[i
] == '\\' || s
[i
] == ']') t
[j
++]='\\';
118 char* result
= new char[j
];
124 vector
<string
>* parseRootNode(Node
* n
, vector
<string
>* tags
) throw(SGFError
) {
125 vector
<string
>* results
= new vector
<string
>(tags
->size());
126 string s
= n
->SGFstring
;
127 int lSGFstring
= s
.size();
130 while (i
< lSGFstring
&& s
[i
] != ';' && (s
[i
]==' ' || s
[i
]=='\n' || s
[i
]=='\r' || s
[i
]=='\t'))
133 if (i
>=lSGFstring
|| s
[i
] != ';') throw SGFError();
136 while (i
< lSGFstring
) {
137 while (i
< lSGFstring
&& (s
[i
]==' ' || s
[i
]=='\n' || s
[i
]=='\r' || s
[i
]=='\t'))
140 if (i
>= lSGFstring
) break;
145 while (i
< lSGFstring
&& s
[i
] != '[' && IDindex
< 30) {
146 if (65 <= s
[i
] && s
[i
] <= 90) {
147 ID
[IDindex
++] = s
[i
];
148 } else if (!(97 <= s
[i
] && s
[i
] <= 122) && (!(s
[i
]==' ' || s
[i
]=='\n' || s
[i
]=='\r' || s
[i
]=='\t'))) {
155 if (i
>= lSGFstring
|| IDindex
>= 30 || !IDindex
) {
160 char* propValue
= new char[lSGFstring
+1];
161 int propValueIndex
= 0;
163 while (i
< lSGFstring
) {
165 while (s
[i
] != ']') {
167 propValue
[propValueIndex
++] = ' ';
173 if ((s
[i
]=='\n' && s
[i
+1]=='\r') || (s
[i
]=='\r' && s
[i
+1]=='\n')) {
177 else if (s
[i
]=='\n' || s
[i
]=='\r') {
182 propValue
[propValueIndex
++] = s
[i
];
185 if (i
>= lSGFstring
) {
190 propValue
[propValueIndex
++] = ',';
191 propValue
[propValueIndex
++] = ' ';
194 while (i
< lSGFstring
&& (s
[i
]==' ' || s
[i
]=='\n' || s
[i
]=='\r' || s
[i
]=='\t'))
196 if (i
>= lSGFstring
|| s
[i
] != '[') {
197 propValue
[propValueIndex
-2] = 0;
198 string IDstring
= ID
;
200 for(vector
<string
>::iterator it
= tags
->begin(); it
!= tags
->end(); it
++) {
201 if (IDstring
== *it
) {
202 (*results
)[ctr
] = propValue
;
216 PropValue::PropValue(std::string IDC
, std::vector
<std::string
>* PV
) {
221 PropValue::~PropValue() {
226 Node::Node(Node
* prev
, char* SGFst
, int lvl
=0) throw(SGFError
) {
238 } else SGFstring
= "";
245 string
remove_lowercase(string s
) throw(SGFError
) {
248 for(unsigned int i
=0; i
<s
.size(); i
++) {
249 if (65 <= s
[i
] && s
[i
] <= 90) ID
[IDindex
++] = s
[i
];
250 else if (!(97 <= s
[i
] && s
[i
] <= 122)) {
258 vector
<string
> Node::gpv(const string
& prop
) {
259 vector
<string
>* result
= get_property_value(prop
);
260 if (result
) return *result
;
261 else return vector
<string
>();
264 vector
<string
>* Node::get_property_value(const string
& prop
) {
268 map
<string
, PropValue
>::iterator result
= data
.find(remove_lowercase(prop
));
269 if (result
== data
.end()) return 0;
270 else return result
->second
.pv
;
273 ExtendedMoveNumber
Node::get_move_number() {
278 while (n
->previous
) {
279 if (n
->level
) l
.push_back(n
->level
);
280 else l
[l
.size()-1]++;
284 int* result
= new int[l
.size()];
285 for(int i
= l
.size()-1; i
>= 0; i
--) {
286 result
[l
.size()-i
-1] = l
[i
];
288 ExtendedMoveNumber
emn(l
.size(), result
);
294 void Node::parseNode() throw(SGFError
) {
295 // printf("Parse node, %s\n", SGFstring);
297 string s
= SGFstring
;
298 int lSGFstring
= s
.size();
301 while (i
< lSGFstring
&& s
[i
] != ';' && (s
[i
]==' ' || s
[i
]=='\n' || s
[i
]=='\r' || s
[i
]=='\t'))
304 if (i
>=lSGFstring
|| s
[i
] != ';') throw SGFError();
307 while (i
< lSGFstring
) {
308 while (i
< lSGFstring
&& (s
[i
]==' ' || s
[i
]=='\n' || s
[i
]=='\r' || s
[i
]=='\t'))
311 if (i
>= lSGFstring
) break;
315 char IDcomplete
[100]; // store long property name here
316 int IDcompleteIndex
= 0;
318 while (i
< lSGFstring
&& s
[i
] != '[' && IDindex
< 30 && IDcompleteIndex
< 100) {
319 if (65 <= s
[i
] && s
[i
] <= 90) {
320 ID
[IDindex
++] = s
[i
];
321 IDcomplete
[IDcompleteIndex
++] = s
[i
];
322 } else if (97 <= s
[i
] && s
[i
] <= 122) {
323 IDcomplete
[IDcompleteIndex
++] = s
[i
];
324 } else if (!(s
[i
]==' ' || s
[i
]=='\n' || s
[i
]=='\r' || s
[i
]=='\t')) {
331 if (i
>= lSGFstring
|| IDindex
>= 30 || !IDindex
|| IDcompleteIndex
>= 100) {
335 IDcomplete
[IDcompleteIndex
] = 0;
336 vector
<string
>* propValueList
= new vector
<string
>;
338 while (i
< lSGFstring
) {
341 while (s
[i
] != ']') {
342 //printf("i, s[i]: %d, %c\n", i, s[i]);
351 if ((s
[i
]=='\n' && s
[i
+1]=='\r') || (s
[i
]=='\r' && s
[i
+1]=='\n')) {
355 else if (s
[i
]=='\n' || s
[i
]=='\r') {
360 if (Node::sloppy
&& (s
[i
] == '\n' || s
[i
] == '\r') && \
361 (!strcmp(ID
, "B") || !strcmp(ID
, "W") || !strcmp(ID
, "AW") || !strcmp(ID
, "AB"))) {
366 propValue
+= s
[i
]; // building propValue in this way could be a performance problem
367 // maybe we should use reserve before. FIXME
370 if (i
>= lSGFstring
) throw SGFError();
372 if ((!strcmp(ID
,"B") || !strcmp(ID
,"W")) && !(propValue
.size() == 2 || (propValue
.size() == 0))) {
376 propValueList
->push_back(propValue
);
379 while (i
< lSGFstring
&& (s
[i
]==' ' || s
[i
]=='\n' || s
[i
]=='\r' || s
[i
]=='\t'))
381 if (i
>= lSGFstring
|| s
[i
] != '[') break;
384 data
.insert(make_pair(string(ID
), PropValue(string(IDcomplete
), propValueList
)));
390 void Node::set_property_value(string
& IDcomplete
, vector
<string
>* propValue
) throw(SGFError
) {
391 string ID
= remove_lowercase(IDcomplete
);
392 map
<string
, PropValue
>::iterator it
= data
.find(ID
);
393 if (it
== data
.end()) data
.insert(make_pair(ID
, PropValue(IDcomplete
, propValue
)));
394 else it
->second
.pv
->insert(it
->second
.pv
->end(), propValue
->begin(), propValue
->end());
395 SGFstring
= nodeToString(data
);
400 int Node::sloppy
= 1;
402 Cursor::Cursor(const char* sgf
, int sloppy
) throw(SGFError
) {
403 Node::sloppy
= sloppy
;
410 root
= new Node(NULL
, NULL
, 0);
413 currentN
= root
->next
;
421 void Cursor::setFlags() {
422 if (currentN
->next
) atEnd
= 0;
424 if (currentN
->previous
) atStart
= 0;
428 void Cursor::parse(const char* s
) throw(SGFError
) {
431 int p
= -1; // start of the currently parsed node
432 stack
<Node
* > c
; // stack of nodes from which variations started
436 int height_previous
= 0;
437 int width_currentVar
= 0;
439 char last
= ')'; // type of last parsed bracked ('(' or ')')
440 bool inbrackets
= false; // are the currently parsed characters in []'s?
442 int i
= 0; // current parser position
443 int lSGFstring
= strlen(s
);
446 while (i
< lSGFstring
) {
452 if (found_par
&& s
[i
]==';') break;
454 if (found_par
&& !(s
[i
]==' ' || s
[i
]=='\n' || s
[i
]=='\r' || s
[i
]=='\t'))
459 if (i
>= lSGFstring
) throw SGFError();
461 i
= found_par
-1; // found beginning of SGF file
463 while (i
< lSGFstring
) {
464 while (i
< lSGFstring
&& !(s
[i
]=='(' || s
[i
]==')' || s
[i
]=='[' || s
[i
]==']' || s
[i
]==';')) i
++;
465 if (i
>= lSGFstring
) break;
469 int numberBackslashes
= 0;
471 while (s
[j
] == '\\') {
475 if (!(numberBackslashes
% 2))
482 if (s
[i
] == '[') inbrackets
= 1;
485 if (last
!= ')' && p
!= -1) curr
->SGFstring
= string(s
+p
, i
-p
);
487 Node
* nn
= new Node(0,0,0);
490 if (++width_currentVar
> width
) width
= width_currentVar
;
492 Node
* last
= curr
->next
;
493 while (last
->down
) last
= last
->down
;
496 nn
->level
= last
->level
+ 1;
498 nn
->posyD
= height
- height_previous
;
503 height_previous
= height
;
509 c_width
.push(width_currentVar
-1);
510 c_height
.push(height
);
519 if (last
!= ')' && p
!= -1) {
520 curr
->SGFstring
= string(s
+p
, i
-p
);
525 width_currentVar
= c_width
.top();
527 height_previous
= c_height
.top();
530 else throw SGFError();
536 curr
->SGFstring
= string(s
+p
, i
-p
);
538 Node
* nn
= new Node(0,0,0);
541 if (++width_currentVar
> width
) width
= width_currentVar
;
543 curr
->numChildren
= 1;
553 if (inbrackets
|| c
.size()) throw SGFError();
555 Node
* n
= curr
->next
;
565 void Cursor::game(int n
) throw(SGFError
) {
566 if (n
< root
->numChildren
) {
569 currentN
= root
->next
;
570 for(int i
=0; i
<n
; i
++) currentN
= currentN
->down
;
573 else throw SGFError();
576 void Cursor::deltree(Node
* node
) {
589 void Cursor::delVariation(Node
* c
) {
595 Node
* node
= c
->next
;
608 void Cursor::delVar(Node
* node
) {
610 if (node
->up
) node
->up
->down
= node
->down
;
612 node
->previous
->next
= node
->down
;
615 node
->down
->up
= node
->up
;
616 node
->down
->posyD
= node
->posyD
;
617 Node
* n
= node
->down
;
634 if (node
->up
|| node
->down
) h
++;
636 Node
* p
= node
->previous
;
640 if (p
->down
) p
->down
->posyD
-= h
;
652 void Cursor::add(char* st
) {
654 Node
* node
= new Node(currentN
,st
,0);
658 node
->numChildren
= 0;
660 if (!currentN
->next
) {
661 // printf("new %s at %s\n", node->SGFstring, currentN->SGFstring);
666 currentN
->next
= node
;
667 currentN
->numChildren
= 1;
670 // printf("adding %s at %s\n", node->SGFstring, currentN->SGFstring);
671 Node
* n
= currentN
->next
;
679 node
->level
= n
->level
+ 1;
681 currentN
->numChildren
++;
688 node
->posyD
+= n
->posyD
;
696 while (n
->previous
) {
698 if (n
->down
) n
->down
->posyD
++;
708 if (posx
> width
) width
++;
712 void Cursor::next(int n
) throw(SGFError
) {
714 if (n
>= currentN
->numChildren
) {
718 currentN
= currentN
->next
;
719 for (int i
=0; i
<n
; i
++) {
720 currentN
= currentN
->down
;
721 posy
+= currentN
->posyD
;
726 void Cursor::previous() throw(SGFError
) {
727 if (currentN
->previous
) {
728 while (currentN
->up
) {
729 posy
-= currentN
->posyD
;
730 currentN
= currentN
->up
;
732 currentN
= currentN
->previous
;
735 else throw SGFError();
739 Node
* Cursor::getRootNode(int n
) throw(SGFError
) {
742 if (n
>= root
->numChildren
) throw SGFError();
743 Node
* nn
= root
->next
;
744 for(int i
=0; i
<n
; i
++) nn
= nn
->down
;
746 if (!nn
->parsed
) nn
->parseNode();
751 // void Cursor::updateRootNode(PyObject* data, int n) throw(SGFError) {
752 // if (n >= root->numChildren) throw SGFError();
753 // Node* nn = root->next;
754 // for(int i=0; i<n; i++) nn = nn->down;
755 // delete[] nn->SGFstring;
756 // nn->SGFstring = rootNodeToString(data);
757 // Py_DECREF(nn->data);
758 // if (!(nn->data=PyDict_New())) throw SGFError();
764 // char* Cursor::rootNodeToString(PyObject* data) {
765 // char result[10000]; // FIXME check whether this is exceeded, on the way
767 // strcat(result, ";");
770 // if ((item=PyDict_GetItem(data, PyString_FromString("GM")))) {
771 // strcat(result, "GM[");
772 // char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
773 // strcat(result, t);
775 // strcat(result, "]\n");
777 // if ((item=PyDict_GetItem(data, PyString_FromString("FF")))) {
778 // strcat(result, "FF[");
779 // char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
780 // strcat(result, t);
782 // strcat(result, "]\n");
784 // if ((item=PyDict_GetItem(data, PyString_FromString("SZ")))) {
785 // strcat(result, "SZ[");
786 // char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
787 // strcat(result, t);
789 // strcat(result, "]\n");
791 // if ((item=PyDict_GetItem(data, PyString_FromString("PW")))) {
792 // strcat(result, "PW[");
793 // char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
794 // strcat(result, t);
796 // strcat(result, "]\n");
798 // if ((item=PyDict_GetItem(data, PyString_FromString("WR")))) {
799 // strcat(result, "WR[");
800 // char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
801 // strcat(result, t);
803 // strcat(result, "]\n");
805 // if ((item=PyDict_GetItem(data, PyString_FromString("PB")))) {
806 // strcat(result, "PB[");
807 // char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
808 // strcat(result, t);
810 // strcat(result, "]\n");
812 // if ((item=PyDict_GetItem(data, PyString_FromString("BR")))) {
813 // strcat(result, "BR[");
814 // char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
815 // strcat(result, t);
817 // strcat(result, "]\n");
819 // if ((item=PyDict_GetItem(data, PyString_FromString("EV")))) {
820 // strcat(result, "EV[");
821 // char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
822 // strcat(result, t);
824 // strcat(result, "]\n");
826 // if ((item=PyDict_GetItem(data, PyString_FromString("RO")))) {
827 // strcat(result, "RO[");
828 // char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
829 // strcat(result, t);
831 // strcat(result, "]\n");
833 // if ((item=PyDict_GetItem(data, PyString_FromString("DT")))) {
834 // strcat(result, "DT[");
835 // char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
836 // strcat(result, t);
838 // strcat(result, "]\n");
840 // if ((item=PyDict_GetItem(data, PyString_FromString("PC")))) {
841 // strcat(result, "PC[");
842 // char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
843 // strcat(result, t);
845 // strcat(result, "]\n");
847 // if ((item=PyDict_GetItem(data, PyString_FromString("KM")))) {
848 // strcat(result, "KM[");
849 // char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
850 // strcat(result, t);
852 // strcat(result, "]\n");
854 // if ((item=PyDict_GetItem(data, PyString_FromString("RE")))) {
855 // strcat(result, "RE[");
856 // char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
857 // strcat(result, t);
859 // strcat(result, "]\n");
861 // if ((item=PyDict_GetItem(data, PyString_FromString("US")))) {
862 // strcat(result, "US[");
863 // char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
864 // strcat(result, t);
866 // strcat(result, "]\n");
868 // if ((item=PyDict_GetItem(data, PyString_FromString("GC")))) {
869 // strcat(result, "GC[");
870 // char* t = SGFescape(PyString_AsString(PyList_GetItem(item, 0)));
871 // strcat(result, t);
873 // strcat(result, "]\n");
878 // PyObject *key, *value;
881 // while (PyDict_Next(data, &pos, &key, &value)) {
882 // char* s = PyString_AsString(key);
883 // if (strcmp(s, "GM") && strcmp(s, "FF") && strcmp(s, "SZ") && strcmp(s, "PW") && strcmp(s, "WR") &&
884 // strcmp(s, "PB") && strcmp(s, "BR") && strcmp(s, "EV") && strcmp(s, "RO") && strcmp(s, "DT") &&
885 // strcmp(s, "PC") && strcmp(s, "KM") && strcmp(s, "RE") && strcmp(s, "US") && strcmp(s, "GC")) {
887 // strcat(result, s);
889 // for(int k = 0; k < PyList_Size(value); k++) {
890 // PyObject* item = PyList_GetItem(value, k);
891 // char* t = SGFescape(PyString_AsString(item));
892 // strcat(result, "[");
893 // strcat(result, t);
894 // strcat(result, "]");
895 // l += strlen(t) + 2;
898 // strcat(result, "\n");
904 // strcat(result, "\n");
905 // char* t = new char[strlen(result)+1];
906 // strcpy(t, result);
910 string
nodeToString(map
<string
, PropValue
>& data
) throw(SGFError
) {
913 for(map
<string
, PropValue
>::iterator kv
= data
.begin(); kv
!= data
.end(); kv
++) {
914 if (!kv
->second
.pv
|| !kv
->second
.pv
->size()) continue;
916 result
+= kv
->second
.IDcomplete
;
917 for(vector
<string
>::iterator it
= kv
->second
.pv
->begin(); it
!= kv
->second
.pv
->end(); it
++) {
918 char* t
= SGFescape(it
->c_str());
935 char* Cursor::outputVar(Node
* node
) {
937 char* result
= new char[s
];
940 if ((int)(node
->SGFstring
.size() + strlen(result
)) >= s
-5) {
941 s
+= 1000 + node
->SGFstring
.size();
942 char* res
= new char[s
];
948 strcat(result
, node
->SGFstring
.c_str());
949 // printf("%s\n", node->SGFstring);
956 char* r
= outputVar(node
);
958 if ((int)(strlen(r
) + strlen(result
)) >= s
-5) {
959 s
+= 1000 + strlen(r
);
960 char* res
= new char[s
];
971 strcat(result
, ")(");
973 char* r
= outputVar(node
);
975 if ((int)(strlen(r
) + strlen(result
)) >= s
-5) {
976 s
+= 1000 + strlen(r
);
977 char* res
= new char[s
];
990 if ((int)(node
->SGFstring
.size() + strlen(result
)) >= s
) {
991 s
+= 1000 + node
->SGFstring
.size();
992 char* res
= new char[s
];
997 strcat(result
, node
->SGFstring
.c_str());
998 // printf("%s\n", node->SGFstring);
1002 // printf("r: %d \n", strlen(result));
1004 char* t
= new char[strlen(result
)+1];
1012 char* Cursor::output() {
1015 char* result
= new char[s
];
1018 Node
* n
= root
->next
;
1021 char* t
= outputVar(n
);
1023 if ((int)(strlen(t
) + strlen(result
)) >= s
-5) {
1024 s
+= 2000 + strlen(t
);
1025 char* res
= new char[s
];
1026 strcpy(res
, result
);
1031 strcat(result
, "(");
1033 strcat(result
, ")\n");
1038 char* t
= new char[strlen(result
)+1];