/*******************************************************************************
* Copyright © 2011, 2013 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:
* IBM Corporation - initial API and implementation
*
*******************************************************************************/
package org.eclipse.edt.compiler.internal.core.validation.name;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Stack;
import org.eclipse.edt.compiler.internal.core.builder.IMarker;
import org.eclipse.edt.compiler.internal.core.builder.IProblemRequestor;
import org.eclipse.edt.compiler.internal.core.lookup.ICompilerOptions;
/**
* @author dollar
*
* Parse a name, collecting messages and breaking
* it into the respective parts.
*/
public class EGLNameParser {
private Reader inputReader;
private EGLNameLexer lexer;
private IProblemRequestor problemRequestor;
private EGLNameToken currentToken;
private EGLNameAndSubscripts nameAndSubscripts;
private Stack state = new Stack();
private String nameState = "NAME";
private String subScriptState = "SUBSCRIPT";
private String subStringState = "SUBSTRING";
private String subScriptNameState = "SubscriptName";
private String subStringNameState = "SubstringName";
private boolean alreadyConsumed = false;
private boolean hadSubstringState = false;
private String inputString = null;
private boolean parsingFileName = false;
public EGLNameParser(String input, boolean normalWhiteSpaceChecking, boolean filename, IProblemRequestor problemRequestor, ICompilerOptions compilerOptions) {
inputString = input;
this.problemRequestor = problemRequestor;
parsingFileName = filename;
inputReader = new StringReader(input);
lexer = new EGLNameLexer(inputReader, 0, parsingFileName, problemRequestor, compilerOptions);
currentToken = null;
nameAndSubscripts = new EGLNameAndSubscripts();
try {
// // System.out.println("Starting input = " + input);
parseName();
} catch (Exception iox) {
// end of file
}
}
/**
* Parse the name held by inputReader into tokens, placing them in a
* token list.
*
*/
private void parseName() throws IOException {
currentToken = lexer.getNextToken(parsingFileName);
state.push(nameState);
while (currentToken != null)
{
alreadyConsumed = false;
switch (currentToken.getType()) {
case (EGLNameToken.IDENTIFIER) :
{
updateState();
// // System.out.println("State = " + (String)state.peek() + " Good ident = " + currentToken.toString() );
if (((String)state.peek()).equalsIgnoreCase(nameState) ) {
ArrayList names = nameAndSubscripts.getNames();
if(names.isEmpty()) {
nameAndSubscripts.addName(currentToken);
}
else {
EGLNameToken lastName = (EGLNameToken) names.get(names.size()-1);
if(lastName.getText().endsWith(",")) {
names.remove(lastName);
names.add(new EGLNameToken(lastName.getType(), lastName.getText() + currentToken.getText(), lastName.getOffset(), lastName.getLine(), currentToken.getColumn()));
}
else {
nameAndSubscripts.addName(currentToken);
}
}
}
else if (((String)state.peek()).equalsIgnoreCase(subScriptNameState)) {
nameAndSubscripts.addSubscript(currentToken);
}
else if (((String)state.peek()).equalsIgnoreCase(subStringNameState)) {
nameAndSubscripts.addSubstring(currentToken);
}
break;
}
case (EGLNameToken.REAL_NUMBER) :
case (EGLNameToken.FLOAT_NUMBER) :
{
updateState();
// // System.out.println("State = " + (String)state.peek() + " real number = " + currentToken.toString() );
//error will be put out in the Name Validator
if (((String)state.peek()).equalsIgnoreCase(nameState) ) {
nameAndSubscripts.addName(currentToken);
}
else if (((String)state.peek()).equalsIgnoreCase(subScriptState)) {
nameAndSubscripts.addSubscript(currentToken);
}
else if (((String)state.peek()).equalsIgnoreCase(subScriptNameState)) {
nameAndSubscripts.addSubscript(currentToken);
}
else if (((String)state.peek()).equalsIgnoreCase(subStringState)) {
nameAndSubscripts.addSubstring(currentToken);
}
else if (((String)state.peek()).equalsIgnoreCase(subStringNameState)) {
nameAndSubscripts.addSubstring(currentToken);
}
break;
}
case (EGLNameToken.QUOTED_STRING) :
{
updateState();
// System.out.println("State = " + (String)state.peek() + " integer = " + currentToken.toString() );
if (((String)state.peek()).equalsIgnoreCase(nameState) ) {
// error will be put out in name validator
nameAndSubscripts.addName(currentToken);
}
else if (((String)state.peek()).equalsIgnoreCase(subScriptNameState)) {
nameAndSubscripts.addSubscript(currentToken);
}
else if (((String)state.peek()).equalsIgnoreCase(subStringNameState)) {
nameAndSubscripts.addSubstring(currentToken);
}
break;
}
case (EGLNameToken.INTEGER) :
{
updateState();
// System.out.println("State = " + (String)state.peek() + " integer = " + currentToken.toString() );
if (((String)state.peek()).equalsIgnoreCase(nameState) ) {
// error will be put out in name validator
nameAndSubscripts.addName(currentToken);
}
else if (((String)state.peek()).equalsIgnoreCase(subScriptNameState)) {
nameAndSubscripts.addSubscript(currentToken);
}
else if (((String)state.peek()).equalsIgnoreCase(subStringNameState)) {
nameAndSubscripts.addSubstring(currentToken);
}
break;
}
case (EGLNameToken.COMMENT) :
{
// System.out.println("State = " + (String)state.peek() + " comment = " + currentToken.toString() );
//FRIEDA - error if we want to restrict where comments can be
break;
}
case (EGLNameToken.L_SQUARE) :
{
if (((String)state.peek()).equalsIgnoreCase(subScriptState)||
((String)state.peek()).equalsIgnoreCase(subScriptNameState) ||
((String)state.peek()).equalsIgnoreCase(subStringState) ||
((String)state.peek()).equalsIgnoreCase(subStringNameState)) {
// error - nested subscripts
problemRequestor.acceptProblem(
currentToken.getOffset(),
currentToken.getOffset()+1,
IMarker.SEVERITY_ERROR,
IProblemRequestor.INVALID_SUBSCRIPT_NESTING,
new String[] { inputString });
}
updateState();
// System.out.println("State = " + (String)state.peek() + " left bracket = " + currentToken.toString() );
if (!(alreadyConsumed)) {
// errors issued elsewhere
nameAndSubscripts.newSubscript();
}
break;
}
case (EGLNameToken.R_SQUARE) :
{
if ((((String)state.peek()).equalsIgnoreCase(subScriptState) ||
((String)state.peek()).equalsIgnoreCase(subStringState) ) ) {
// //error - empty brackets
problemRequestor.acceptProblem(
currentToken.getOffset(),
currentToken.getOffset()+1,
IMarker.SEVERITY_ERROR,
IProblemRequestor.P_FOUND_EMPTY_BRACKETS,
new String[] { inputString });
}
if ( ((String)state.peek()).equalsIgnoreCase(nameState)) {
// error - right bracket when not in subscript or substing one
problemRequestor.acceptProblem(
currentToken.getOffset(),
currentToken.getOffset()+1,
IMarker.SEVERITY_ERROR,
IProblemRequestor.P_FOUND_RBRACKET_WRONG,
new String[] { inputString });
}
updateState();
// System.out.println("State = " + (String)state.peek() + " right bracket = " + currentToken.toString() );
break;
}
case (EGLNameToken.DOT) :
{
updateState();
// System.out.println("State = " + (String)state.peek() + " dot = " + currentToken.toString() );
//if name begins with dot, error is issued from name validator
if (((String)state.peek()).equalsIgnoreCase(nameState) ) {
nameAndSubscripts.addName(currentToken);
}
else if (((String)state.peek()).equalsIgnoreCase(subScriptState) ||
((String)state.peek()).equalsIgnoreCase(subScriptNameState)) {
nameAndSubscripts.addSubscript(currentToken);
}
else if (((String)state.peek()).equalsIgnoreCase(subStringState) ||
((String)state.peek()).equalsIgnoreCase(subStringNameState)) {
nameAndSubscripts.addSubstring(currentToken);
}
break;
}
case (EGLNameToken.COLON) :
{
if (state.peek().equals(subScriptState) || // name[:
state.peek().equals(subStringState) ) { // name[: shouldn't happen
// System.out.println("Missing from location");
// error - right bracket when not in subscript or substing one
problemRequestor.acceptProblem(
currentToken.getOffset(),
currentToken.getOffset()+1,
IMarker.SEVERITY_ERROR,
IProblemRequestor.P_MISSING_FROM_SUBSTRING,
new String[] { inputString });
}
else if (state.peek().equals(subStringNameState) ) { //name[name:name: or [name::
// System.out.println("too many substrings");
// error - right bracket when not in subscript or substing one
problemRequestor.acceptProblem(
currentToken.getOffset(),
currentToken.getOffset()+1,
IMarker.SEVERITY_ERROR,
IProblemRequestor.P_TOO_MANY_SUBSTRINGS,
new String[] { inputString });
}
// Need to remove from subscript list and put in substring list
updateState();
// System.out.println("State = " + (String)state.peek() + " colon = " + currentToken.toString() );
if (((String)state.peek()).equalsIgnoreCase(nameState) ) {
nameAndSubscripts.addName(currentToken);
//if name begins with dot, error is issued from name validator
}
else if (!(alreadyConsumed) ) {
nameAndSubscripts.newSubstring();
nameAndSubscripts.copySubscriptToSubstring();
nameAndSubscripts.newSubstring();
}
break;
}
case (EGLNameToken.COMMA) :
{
//comma anywhere in a name error issued from name validator
if (((String)state.peek()).equalsIgnoreCase(nameState) ) {
ArrayList names = nameAndSubscripts.getNames();
EGLNameToken lastName = (EGLNameToken) names.get(names.size()-1);
names.remove(lastName);
names.add(new EGLNameToken(lastName.getType(), lastName.getText() + ",", lastName.getOffset(), lastName.getLine(), lastName.getColumn()+1));
}
else {
updateState();
// System.out.println("State = " + (String)state.peek() + " comma = " + currentToken.toString() );
nameAndSubscripts.newSubscript();
}
break;
}
case (EGLNameToken.UNKNOWN_EGL) :
{
// System.out.println("State = " + (String)state.peek() + " invalid token = " + currentToken.toString() );
problemRequestor.acceptProblem(
currentToken.getOffset(),
currentToken.getOffset() + currentToken.getText().length(),
IMarker.SEVERITY_ERROR,
IProblemRequestor.P_UNRECOGNIZED_TOKEN,
new String[] { currentToken.getText(), inputString });
break;
}
default :
{
// System.out.println("Taking default - " + currentToken.toString() );
}
}
if (!(alreadyConsumed)) {
currentToken = lexer.getNextToken(parsingFileName);
}
}
if ( state.isEmpty() ||
!(state.peek().equals(nameState)) ) {
// error - didn't terminate name properly (subscript or substring not closed
// System.out.println("Didn't terminate properly.");
problemRequestor.acceptProblem(
inputString.lastIndexOf('['),
inputString.lastIndexOf('['),
IMarker.SEVERITY_ERROR,
IProblemRequestor.P_SUBSCRIPT_OR_SUBSTRING_NOT_CLOSED,
new String[] { inputString });
}
return ;
}
/**
* Parse the name held by inputReader into tokens, placing them in a
* token list.
*
*/
private void updateState() throws IOException {
if (hadSubstringState) {
//error - can't have anything after substring
problemRequestor.acceptProblem(
currentToken.getOffset(),
currentToken.getOffset() + currentToken.getText().length()+1,
IMarker.SEVERITY_ERROR,
IProblemRequestor.P_SUBSTRING_NOT_LAST,
new String[] { currentToken.getText(), inputString });
// System.out.println("Found token after substring. Token = " + currentToken.toString());
consumeRest();
return;
}
switch (currentToken.getType()) {
case (EGLNameToken.IDENTIFIER) :
case (EGLNameToken.REAL_NUMBER) :
case (EGLNameToken.DOT) :
case (EGLNameToken.COMMA) :
{
if (state.isEmpty())
state.push(nameState);
else if (state.peek().equals(subScriptState))
state.push(subScriptNameState);
else if (state.peek().equals(subStringState))
state.push(subStringNameState);
break;
}
case (EGLNameToken.INTEGER) :
case (EGLNameToken.QUOTED_STRING) :
{
if (state.isEmpty()) {
//error will be issued from the name validator
// System.out.println("State error - found int with empty stack " );
state.push(nameState);
}
else if (state.peek().equals(subScriptState))
state.push(subScriptNameState);
else if (state.peek().equals(subStringState))
state.push(subStringNameState);
break;
}
case (EGLNameToken.L_SQUARE) :
{
if (state.isEmpty()){
//error will be issued from the name validator
// System.out.println("State error - found [ with empty stack " );
alreadyConsumed = consumeBadSubscript();
}
else if ( !(state.peek().equals(nameState)) ) {
//nested subscript error - will be issued by parseName
// System.out.println("State error - found [ when not in main name (nested)" );
alreadyConsumed = consumeBadSubscript();
}
else
state.push(subScriptState);
break;
}
case (EGLNameToken.R_SQUARE) :
{
if (state.isEmpty()){
//error issued by parseName
// System.out.println("State error - found ] with empty stack " );
}
else if (state.peek().equals(subScriptState) ) {
//error issued by parseName
// System.out.println("State error - found ] in subscriptState " );
state.pop();
}
else if (state.peek().equals(subStringState) ) {
//error issued by ParseName
// System.out.println("State error - found ] in substringState " );
state.pop();
}
else if (state.peek().equals(nameState)) {
//error issued by ParseName
// System.out.println("State error - found ] in nameState " );
}
else {
String currentState = (String)state.pop();
if (currentState.equals(subStringNameState))
hadSubstringState = true;
while (currentState.equals(subScriptNameState) ||
currentState.equals(subStringNameState) ){
currentState = (String)state.pop();
}
}
break;
}
case (EGLNameToken.COLON) :
{
if (state.isEmpty()){
//error issued from nameValidator
// System.out.println("State error - found : with empty stack " );
state.push(nameState);
}
else if (state.peek().equals(subScriptState) || // name[:
state.peek().equals(subStringState) ) { // name[: shouldn't happen
// error issued from parseName
// System.out.println("Missing from location");
state.push(subStringNameState);
}
else if (state.peek().equals(subStringNameState) ) { //name[name:name: or [name::
// error issued from parseName
// System.out.println("too many substrings");
consumeRest();
}
else if (state.peek().equals(nameState)) { //name:
//error issued from nameValidator
// System.out.println("State error - found : in error location " );
state.push(nameState);
}
else if (state.peek().equals(subScriptNameState)){ // change to substringNameState
String currentState = (String)state.pop();
while (currentState.equals(subScriptNameState)){
currentState = (String)state.pop();
}
state.push(subStringState);
state.push(subStringNameState);
}
break;
}
case (EGLNameToken.COMMENT) :
default :
{
if (state.isEmpty()) {
state.push(nameState);
}
//else no state update to make
break;
}
}
return ;
}
public boolean consumeBadSubscript() throws IOException {
int bracketCount = 1;
currentToken = lexer.getNextToken(parsingFileName);
while (currentToken != null &&
bracketCount >0 )
{
switch (currentToken.getType()) {
case (EGLNameToken.L_SQUARE) :
{
bracketCount = bracketCount + 1;
}
case (EGLNameToken.R_SQUARE) :
{
bracketCount = bracketCount - 1;
}
}
// System.out.println("Consuming Token = " + currentToken.toString() );
currentToken = lexer.getNextToken(parsingFileName);
}
if (bracketCount != 0) {
//error issued in parseName - ran out of input before found matching brackets
// System.out.println("unmatched left bracket");
}
return true;
}
public void consumeRest() throws IOException {
while (currentToken != null )
{
// System.out.println("Consuming Token = " + currentToken.toString() );
currentToken = lexer.getNextToken(parsingFileName);
}
}
public EGLNameAndSubscripts getNamesAndSubscripts() {
return nameAndSubscripts;
}
public ArrayList getNames() {
return nameAndSubscripts.getNames();
}
public ArrayList getSubscripts() {
return nameAndSubscripts.getSubscripts();
}
public ArrayList getFirstSubscript() {
return ((ArrayList)(nameAndSubscripts.getSubscripts().get(0)));
}
public ArrayList getSubstrings() {
return nameAndSubscripts.getSubstrings();
}
public ArrayList getFirstSubstring() {
return ((ArrayList)(nameAndSubscripts.getSubstrings().get(0)));
}
}