CharacterScanner.java
package org.sterling.source.scanner;
import static java.util.regex.Pattern.compile;
import static org.sterling.source.scanner.ScannerUtil.invalidEscapeSequence;
import static org.sterling.source.scanner.ScannerUtil.unexpectedInput;
import static org.sterling.source.scanner.ScannerUtil.unterminatedCharacter;
import java.util.regex.Pattern;
import org.sterling.SterlingException;
import org.sterling.source.syntax.CharacterToken;
import org.sterling.source.syntax.NodeKind;
import org.sterling.source.syntax.Token;
public class CharacterScanner implements ScannerDelegate {
private final Pattern asciiEscape;
private final Pattern hexEscape;
private final Pattern octalEscape;
public CharacterScanner() {
asciiEscape = compile("^\\\\[btnfr\"'\\\\]");
hexEscape = compile("^\\\\[uU][a-fA-F0-9]{4}");
octalEscape = compile("^\\\\[0-3]?[0-7]{2}");
}
@Override
public boolean expect(NodeKind kind, InputReader reader) {
return reader.expect('\'');
}
@Override
public Token require(NodeKind kind, InputReader reader) throws SterlingException {
if (!expect(kind, reader)) {
throw unexpectedInput(reader, kind);
}
reader.store();
if (!reader.store(asciiEscape, hexEscape, octalEscape)) {
validateEscapeSequence(kind, reader);
reader.store();
}
if (reader.expect('\'')) {
reader.store();
} else {
throw unterminatedCharacter(reader, kind);
}
Token token = reader.accept(kind);
char charValue = EscapeUtil.unescapeString(token.getValue()).charAt(1);
return new CharacterToken(token, charValue);
}
private void validateEscapeSequence(NodeKind kind, InputReader reader) throws SterlingException {
if (reader.expect('\\')) {
throw invalidEscapeSequence(reader, kind);
} else if (reader.expect('\'')) {
throw unexpectedInput(reader, kind);
} else if (reader.expect('\n')) {
throw unterminatedCharacter(reader, kind);
}
}
}