blob: 63fb589a28e303d0a63c6c1de0a52f995f776adc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 University of Illinois at Urbana-Champaign 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:
* UIUC - Initial API and implementation
* Greg Watson - GFortran 5.x support
*******************************************************************************/
package org.eclipse.photran.internal.cdtinterface.errorparsers;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.cdt.core.ErrorParserManager;
import org.eclipse.cdt.core.IErrorParser;
import org.eclipse.cdt.core.IMarkerGenerator;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
/**
* An error parser for GNU Fortran 4.x and 5.x
*
* @author Jeff Overbey
*/
public class GFortranErrorParser implements IErrorParser
{
private static final int MAX_LINES_IN_ERROR_MESSAGE = 10;
/*================================================================================
Gfortran 4.x
------------
cray-pointers.f90:56.21:
subroutine example3()
1
cray-pointers.f90:43.21:
subroutine example3()
2
Error: Global name 'example3' at (1) is already being used as a SUBROUTINE at (2)
cray-pointers.f90:58.22:
real :: pointee
1
Error: Array 'pointee' at (1) cannot have a deferred shape
GFortran 5.x:
-------------
delme.f90:4:2:
i = i + 1
1
Error: Named constant 'i' in variable definition context (assignment) at (1)
================================================================================*/
// Filename
// | Line Column
// | | |
// Regex group number 1 2 3 4 5
private static final Pattern startLine = Pattern.compile("^(In file )?([^:]+):([0-9]+)([\\.:]([0-9]+))?:$"); //$NON-NLS-1$
private static final Pattern errorLine = Pattern.compile("^(Fatal )?Error: .*"); //$NON-NLS-1$
private static final Pattern warningLine = Pattern.compile("^Warning: .*"); //$NON-NLS-1$
/*
* This error parser uses the GoF State pattern. It starts in the first of two states:
*
* (1) WaitForStartLine
* (2) AccumulateErrorMessageLines
*/
private IErrorParser currentState = new WaitForStartLine();
/*
* N.B. We don't return "true" (meaning we successfully matched an error line)
* until we match the final line and generate the marker: The startLine pattern
* (string:int.int:) is very general, so we might make a mistake. If this
* happens and we accumulate more than MAX_LINES_IN_ERROR_MESSAGE lines,
* we bail. Returning "false" on every line until the final one guarantees that
* another error parser will still have a chance to handle these lines if we
* do make a mistake.
*/
/**
* @see IErrorParser#processLine(String, ErrorParserManager)
*/
public boolean processLine(String line, ErrorParserManager eoParser)
{
return currentState.processLine(line, eoParser);
}
/**
* STATE 1: WAITING FOR A START LINE
* <p>
* This is the state of the error parser when it is waiting for a line like
* <pre>cray-pointers.f90:56.21:</pre>
*
* When it finds one, it switches to the next state (Accumulating an Error Message).
*/
private class WaitForStartLine implements IErrorParser
{
public boolean processLine(String line, ErrorParserManager eoParser)
{
Matcher startLineMatcher = startLine.matcher(line);
System.out.println("matching "+line);
if (startLineMatcher.matches())
{
String filename = startLineMatcher.group(2);
int lineNumber = Integer.parseInt(startLineMatcher.group(3));
currentState = new AccumulateErrorMessageLines(filename, lineNumber, line);
System.out.println("matched! " + filename + "#" + lineNumber);
}
return false;
}
}
/**
* STATE 2: ACCUMULATING ERROR MESSAGE
* <p>
* This is the state of the error parser after it has seen a line like
* <pre>cray-pointers.f90:56.21:</pre>.
* The next several lines contain a source pointer and error message, like
* <pre>
* subroutine example3()
* 2
* Error: Global name 'example3' at (1) is already being used as a SUBROUTINE at (2)
* </pre>
* In this state, we skip the first several lines (presumably the source pointer),
* and when we see a line starting with "Fatal Error:", "Error:", or "Warning:",
* we display that message in the Problems view, and the error parser returns to
* its initial state, waiting for the next line.
*/
private class AccumulateErrorMessageLines implements IErrorParser
{
private String filename;
private int lineNumber;
private StringBuffer errorMessage = new StringBuffer();
private int linesAccumulated = 1;
public AccumulateErrorMessageLines(String filename, int lineNumber, String line)
{
this.filename = filename;
this.lineNumber = lineNumber;
errorMessage.append(line);
}
public boolean processLine(String line, ErrorParserManager eoParser)
{
//errorMessage.append("\n");
//errorMessage.append(line);
linesAccumulated++;
Matcher errorMatcher = errorLine.matcher(line);
Matcher warningMatcher = warningLine.matcher(line);
if (errorMatcher.matches())
{
// Matched "Error: Description" or "Fatal Error: Description"
errorMessage.append(" "); //$NON-NLS-1$
errorMessage.append(line);
addMarker(eoParser, true);
return true;
}
else if (warningMatcher.matches())
{
// Matched "Warning: Description"
errorMessage.append(" "); //$NON-NLS-1$
errorMessage.append(line);
addMarker(eoParser, false);
return true;
}
else if (linesAccumulated > MAX_LINES_IN_ERROR_MESSAGE)
{
// We probably made a mistake matching the first line
currentState = new WaitForStartLine();
return false;
}
else
{
// Still accumulating an error message
return false;
}
}
private void addMarker(ErrorParserManager eoParser, boolean isError)
{
IResource file = findFile(eoParser);
eoParser.generateMarker(file,
lineNumber,
errorMessage.toString(),
isError ? IMarkerGenerator.SEVERITY_ERROR_RESOURCE : IMarkerGenerator.SEVERITY_WARNING,
null);
currentState = new WaitForStartLine();
}
private IFile findFile(ErrorParserManager eoParser)
{
IFile result = eoParser.findFileName(filename);
if (result != null) return result;
// The managed build system prefixes ../ to filenames.
// So (this is a hack) if the file can't be found and
// it starts with ../ try removing that and hope that
// maybe it will refer to a workspace location.
if (filename.startsWith("../") || filename.startsWith("..\\")) //$NON-NLS-1$ //$NON-NLS-2$
return eoParser.findFileName(filename.substring(3));
return null;
}
}
}