/******************************************************************************* * Copyright (c) 2015 Bruno Medeiros and other Contributors. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Bruno Medeiros - initial API and implementation *******************************************************************************/ package melnorme.lang.tooling.parser.lexer; import melnorme.lang.utils.parse.ICharacterReader; import melnorme.lang.utils.parse.LexingUtils; public class CharacterLexingRule extends LexingUtils implements IPredicateLexingRule { @Override public boolean doEvaluate(ICharacterReader reader) { if(!consumeStart(reader)) { return false; } if(!consumeBody(reader)) { return false; } if(!consumeEnd(reader)) { return false; } return true; } protected boolean consumeStart(ICharacterReader reader) { return reader.tryConsume('\''); } protected boolean consumeBody(ICharacterReader reader) { if(reader.tryConsume((char) 0x5c)) { return reader.tryConsume('\'') || reader.tryConsume('\"') || consumeCommonEscape(reader) || consumeUnicodeEscapeSequence(reader); }; if(reader.lookaheadIsEOS() || reader.lookahead() == '\'') { return false; } if(reader.lookahead() == 0) { return false; // Should the null character really not be allowed? } return reader.consumeAny(); } protected boolean consumeEnd(ICharacterReader reader) { return reader.tryConsume('\''); } protected static char[] COMMON_ESCAPES = { 0x5c, 'n' , 'r' , 't' , '0' }; protected boolean consumeCommonEscape(ICharacterReader reader) { for(char ch : COMMON_ESCAPES) { if(reader.tryConsume(ch)) { return true; } } if(consumeHexEscapeSequence(reader)) { return true; } return false; } protected boolean consumeHexEscapeSequence(ICharacterReader reader) { if(reader.lookahead(0) == 'x' && isHexDigit(reader.lookahead(1)) && isHexDigit(reader.lookahead(2))) { reader.consumeAmount(3); return true; } return false; } protected boolean consumeUnicodeEscapeSequence(ICharacterReader reader) { if(reader.tryConsume('u')) { while(true) { int la = reader.lookahead(); // This is not accurate to any spec, but is good enough. if(la == '{' || la == '}' || isHexDigit(la)) { reader.consume(); } else { break; } } return true; } return false; } public static boolean isHexDigit(int ch) { if(ch >= '0' && ch <= '9') { return true; } if((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) { return true; } return false; } }