private static final String SHIELD_REGEX = "\\\\";
private static final String EMPTY = "";
+ private static final String TAB = "\t";
private static final String CRLF_REGEX = "\\r?\\n";
public static String match(String source, String regex) {
return pgn.replaceAll(CRLF_REGEX, PGN_SPACE);
}
+ public static String tabCrlf(String pgn) {
+ return pgn.replaceAll(TAB, PGN_SPACE).replaceAll(CRLF_REGEX, TAB);
+ }
+
private PGNUtils() {
throw new AssertionError(
String.format("No %s instances!", getClass().getName())
*/
public class CommentToken implements Token<Moves> {
- private static final String COMMENT_REGEX = "^\\s*\\{([^}]+)\\}";
- private static final Pattern COMMENT_PATTERN = Pattern.compile(COMMENT_REGEX);
+ enum Type {
+
+ BRACE ( BRACE_PATTERN ),
+ SEMICOLON ( SEMICOLON_PATTERN );
+
+ private Pattern pattern;
+
+ Type(Pattern pattern) {
+ this.pattern = pattern;
+ }
+
+ public Pattern pattern() {
+ return pattern;
+ }
+ }
+
+ private static final String BRACE_REGEX = "^\\s*\\{([^}]+)\\}";
+ private static final Pattern BRACE_PATTERN = Pattern.compile(BRACE_REGEX);
+ private static final String SEMICOLON_REGEX = "^\\s*;([^\t]+)\t";
+ private static final Pattern SEMICOLON_PATTERN = Pattern.compile(SEMICOLON_REGEX);
private static final int COMMENT_GROUP = 1;
+ private Type commentType;
+
+ public CommentToken(Type type) {
+ this.commentType = type;
+ }
+
@Override
public int token(Moves moves, String pgn) throws ParseException {
- Matcher matcher = COMMENT_PATTERN.matcher(pgn);
+ Matcher matcher = commentType.pattern().matcher(pgn);
if (!matcher.find()) {
throw new ParseException("parse.pgn.incorrect.comment", pgn);
} else {
import org.hedgecode.chess.pgn.entity.DetailMove;
import org.hedgecode.chess.pgn.entity.MoveNumber;
import org.hedgecode.chess.pgn.entity.Moves;
-import org.hedgecode.chess.pgn.nag.Glyph;
import org.hedgecode.chess.pgn.token.san.CommonSANToken;
import org.hedgecode.chess.pgn.token.san.SANToken;
*/
public class MoveToken implements Token<Moves> {
- public static final String MOVE_REGEX = "^\\s*([PNBRQKa-hx1-8=+#\\-O!?]+)(\\s+\\$([0-9]+))?";
+ public static final String MOVE_REGEX = "^\\s*([PNBRQKa-hx1-8=+#\\-O!?]+)";
private static final Pattern MOVE_PATTERN = Pattern.compile(MOVE_REGEX);
private static final int MOVE_GROUP = 1, NAG_GROUP = 3;
MoveNumber moveNumber = new MoveNumber(
moves.currentMove().ply()
);
- int numberLength = moveNumberToken.token(moveNumber, pgn);
- if (numberLength > 0) {
- pgn = pgn.substring(numberLength);
+ int numLength = moveNumberToken.token(moveNumber, pgn);
+ if (numLength > 0) {
+ pgn = pgn.substring(numLength);
}
Matcher matcher = MOVE_PATTERN.matcher(pgn);
if (!matcher.find()) {
moves.addMove(
detailMove
);
- String glyph = matcher.group(NAG_GROUP); // todo: more then one NAG
- if (glyph != null) {
- detailMove.addGlyph(
- Glyph.byNumber(
- Integer.parseInt(glyph)
- )
- );
- }
}
- return numberLength + matcher.group().length();
+ return numLength + matcher.group().length();
}
}
enum Type {
- MOVE ( new MoveToken() ),
- COMMENT ( new CommentToken() ),
- VARIATION ( new VariationToken() ),
- RESULT ( new ResultToken() );
+ MOVE ( new MoveToken() ),
+ NAG ( new NAGToken() ),
+ BRACE ( new CommentToken(CommentToken.Type.BRACE) ),
+ SEMICOLON ( new CommentToken(CommentToken.Type.SEMICOLON) ),
+ VARIATION ( new VariationToken() ),
+ RESULT ( new ResultToken() ),
+ RESERVED ( new ReservedToken() );
private Token<Moves> token;
private static final String MOVES_REGEX = "^\\s*([^\\s])";
private static final Pattern MOVES_PATTERN = Pattern.compile(MOVES_REGEX);
+ private static final int TYPE_GROUP = 1;
private static final String RESULT_REGEX = "^\\s*(1-0|0-1|1/2-1/2|\\*)";
private static final Pattern RESULT_PATTERN = Pattern.compile(RESULT_REGEX);
- private static final String COMMENT = "{";
+ private static final String NAG = "$";
+ private static final String BRACE = "{";
+ private static final String SEMICOLON = ";";
private static final String VARIATION = "(";
+ private static final String RESERVED = "<";
@Override
public int token(Entity entity, String pgn) throws ParseException {
int pgnLength = pgn.length();
- pgn = PGNUtils.stripCrlf(pgn); // todo: starting with ";" comments
+ pgn = PGNUtils.tabCrlf(pgn);
boolean isResulted = false;
Type tokenType = tokenType(pgn);
while (tokenType != null) {
} else {
Matcher moveMatcher = MOVES_PATTERN.matcher(pgn);
if (moveMatcher.find()) {
- switch (moveMatcher.group(1)) {
- case COMMENT:
- type = Type.COMMENT;
+ switch (moveMatcher.group(TYPE_GROUP)) {
+ case NAG:
+ type = Type.NAG;
+ break;
+ case BRACE:
+ type = Type.BRACE;
+ break;
+ case SEMICOLON:
+ type = Type.SEMICOLON;
break;
case VARIATION:
type = Type.VARIATION;
break;
+ case RESERVED:
+ type = Type.RESERVED;
+ break;
default:
type = Type.MOVE;
}
--- /dev/null
+/*
+ * Copyright (c) 2018-2020. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.pgn.token;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.hedgecode.chess.ParseException;
+import org.hedgecode.chess.pgn.entity.Moves;
+import org.hedgecode.chess.pgn.nag.Glyph;
+
+/**
+ * NAGToken
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class NAGToken implements Token<Moves> {
+
+ private static final String NAG_REGEX = "^\\s*\\$([0-9]{1,3})";
+ private static final Pattern NAG_PATTERN = Pattern.compile(NAG_REGEX);
+ private static final int NAG_GROUP = 1;
+
+ @Override
+ public int token(Moves moves, String pgn) throws ParseException {
+ Matcher matcher = NAG_PATTERN.matcher(pgn);
+ if (!matcher.find()) {
+ throw new ParseException("parse.pgn.incorrect.nag", pgn);
+ } else {
+ Glyph glyph = Glyph.byNumber(
+ Integer.parseInt(
+ matcher.group(NAG_GROUP)
+ )
+ );
+ if (glyph != null) {
+ moves.currentMove().addGlyph(glyph);
+ }
+ }
+ return matcher.group().length();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2018-2020. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.pgn.token;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.hedgecode.chess.ParseException;
+import org.hedgecode.chess.pgn.entity.Moves;
+
+/**
+ * ReservedToken
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class ReservedToken implements Token<Moves> {
+
+ private static final String RESERVED_REGEX = "^\\s*<([^>]+)>";
+ private static final Pattern RESERVED_PATTERN = Pattern.compile(RESERVED_REGEX);
+
+ @Override
+ public int token(Moves moves, String pgn) throws ParseException {
+ Matcher matcher = RESERVED_PATTERN.matcher(pgn);
+ if (!matcher.find()) {
+ throw new ParseException("parse.pgn.incorrect.reserved", pgn);
+ }
+ return matcher.group().length();
+ }
+
+}
parse.pgn.missed.empty=Missed PGN empty line
parse.pgn.incorrect.empty=Incorrect PGN empty line
parse.pgn.incorrect.move=Incorrect PGN move
+parse.pgn.incorrect.nag=Incorrect NAG of PGN move
parse.pgn.incorrect.comment=Incorrect PGN comment
parse.pgn.incorrect.variation=Incorrect PGN variation
parse.pgn.incorrect.result=Incorrect PGN result
parse.pgn.symbols.after.result=Incorrect symbols after PGN result
parse.pgn.result.not.match=Result in PGN tags and moves does not match
parse.pgn.variation.result=Variations cannot contain PGN result
+parse.pgn.incorrect.reserved=Incorrect PGN reserved token
parse.tcd.index.out.of.bounds=Array Index Out Of Bounds
parse.tcd.invalid.bytes=Invalid input bytes
parse.pgn.missed.empty=
parse.pgn.incorrect.empty=
parse.pgn.incorrect.move=
+parse.pgn.incorrect.nag=
parse.pgn.incorrect.comment=
parse.pgn.incorrect.variation=
parse.pgn.incorrect.result=
parse.pgn.symbols.after.result=
parse.pgn.result.not.match=
parse.pgn.variation.result=
+parse.pgn.incorrect.reserved=
parse.tcd.index.out.of.bounds=
parse.tcd.invalid.bytes=