/******************************************************************************* * Copyright (c) 2012, 2012 IBM Corporation and others. * 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 dtool.tests.utils; import static melnorme.utilbox.core.Assert.AssertNamespace.assertFail; import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull; /** * A very simple, unoptimized parser for simple languages. */ public class SimpleParser { public static final int EOF = -1; protected final String source; protected int pos; protected String lastToken; public SimpleParser(String source) { this.source = assertNotNull(source); this.pos = 0; } public final String getSource() { return source; } public final int getSourcePosition() { return pos; } @Override public String toString() { return source.substring(0, pos) + "<--parser-->" + source.substring(pos, source.length()); } public SimpleParser copyState() { SimpleParser newParser = new SimpleParser(source); newParser.pos = pos; newParser.lastToken = lastToken; return newParser; } /** Gets the character from absolute position index, or EOF if index exceeds source.length. */ public static int getCharacter(String source, int index) { if(index >= source.length()) { return EOF; } return source.charAt(index); } public final int lookAhead(int offset) { return getCharacter(source, pos + offset); } public final int lookAhead() { return getCharacter(source, pos); } public final boolean lookaheadIsEOF() { return pos >= source.length(); } public final String consumeAmount(int length) { lastToken = source.substring(pos, pos + length); pos += length; return lastToken; } public void resetToPosition(int sourcePosition) { pos = sourcePosition; lastToken = null; } public final boolean tryConsume(String string) { if(source.startsWith(string, pos)) { consumeAmount(string.length()); return true; } return false; } public final int tryConsume(String... strings) { for (int ix = 0; ix < strings.length; ix++) { String string = strings[ix]; if(tryConsume(string)) { return ix; } } return -1; } public final void consume(String string) { if(tryConsume(string) == false) { assertFail(); } lastToken = string; } public final String getLastConsumedString() { return lastToken; } public String getRestOfInput() { return source.substring(pos, source.length()); } public String consumeRestOfInput() { return consumeAmount(source.length() - pos); } public String consumeUntil(String string) { int startPos = pos; while(true) { if(lookaheadIsEOF()) { break; } if(source.startsWith(string, pos)) { break; } pos++; } lastToken = source.substring(startPos, pos); return lastToken; } public int consumeUntilAny(String strings[]) { int startPos = pos; while(true) { if(lookaheadIsEOF()) { lastToken = source.substring(startPos, pos); return EOF; } for (int i = 0; i < strings.length; i++) { String alt = strings[i]; if(source.startsWith(alt, pos)) { lastToken = source.substring(startPos, pos); return i; } } pos++; } } public SimpleParser seekSpaceChars() { lastToken = null; while(true) { if(lookaheadIsEOF() || !Character.isSpaceChar(source.charAt(pos))) { break; } pos++; } return this; } public SimpleParser seekWhiteSpace() { lastToken = null; while(true) { if(lookaheadIsEOF() || !Character.isWhitespace(source.charAt(pos))) { break; } pos++; } return this; } public boolean seekToNewLine() { int newPos = findNewLineEnd(source, pos); if(newPos == EOF) { return false; } consumeAmount(newPos - pos); return true; } public static int findNewLineEnd(String source, int offset) { while(true) { int ch = getCharacter(source, offset); if(ch == EOF) { return EOF; } offset++; if(ch == '\r') { if(getCharacter(source, offset) == '\n') { offset++; } return offset; } else if(ch == '\n') { return offset; } } } public boolean tryConsumeNewlineRule() { int ch = lookAhead(); if(ch == '\r') { if(lookAhead(1) == '\n') { consumeAmount(2); return true; } consumeAmount(1); return true; } else if(ch == '\n') { consumeAmount(1); return true; } return false; } public static String readNonWhiteSpace(String string, int offset) { int startPos = offset; while(true) { int ch = getCharacter(string, offset); if(ch == EOF || Character.isWhitespace(ch)) { break; } offset++; } return string.substring(startPos, offset); } public static String readAlphaNumericUS(String string, int offset) { int startPos = offset; while(true) { int ch = getCharacter(string, offset); if(ch == EOF || !(Character.isLetterOrDigit(ch) || ch == '_')) { break; } offset++; } return string.substring(startPos, offset); } public static String readInteger(String string, int offset) { int startPos = offset; while(true) { int ch = getCharacter(string, offset); if(ch == EOF || !(Character.isDigit(ch))) { break; } offset++; } return string.substring(startPos, offset); } public String consumeNonWhiteSpace(boolean skipSpaces) { if(skipSpaces) { seekSpaceChars(); } String str = readNonWhiteSpace(source, pos); return consumeAmount(str.length()); } public int consumeInteger(boolean skipSpaces) { if(skipSpaces) { seekSpaceChars(); } String numberStr = readInteger(source, pos); consumeAmount(numberStr.length()); return Integer.decode(numberStr); } public String consumeAlphaNumericUS(boolean skipSpaces) { if(skipSpaces) { seekSpaceChars(); } String str = readAlphaNumericUS(source, pos); consumeAmount(str.length()); return str; } public boolean tryConsumeKeyword(String string) { if(source.startsWith(string, pos)) { char charAfterString = source.charAt(pos + string.length()); if(!Character.isJavaIdentifierPart(charAfterString)) { consumeAmount(string.length()); return true; } } return false; } }