blob: ec9efdbfe56b72492ba8ac43063bd9593f6f1c23 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2015 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
*******************************************************************************/
package org.eclipse.photran.internal.ui.editor;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.rules.ICharacterScanner;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.IWordDetector;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.jface.text.rules.WordRule;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.photran.internal.ui.FortranUIPlugin;
/**
* Syntax highlighting rule which determines whether a word should be a keyword or an identifier.
* <p>
* The logic is based on Sale's algorithm, extended to handle Fortran 90, 2003, and 2008.
* <p>
* This is only vaguely similar to FreeFormLexerPhase2; this version is much faster but less
* precise, as it is based on a character-by-character scan rather than a proper tokenization.
* It only scans one line at a time and is therefore oblivious to continuation lines; this
* makes it highlight continuation lines incorrectly (see Bugs 302318 and 301712).
*
* @see org.eclipse.jface.text.rules.WordRule
*
* @author Jeff Overbey (based on WordRule)
*/
public class SalesScanKeywordRule extends WordRule implements IRule
{
/** The word detector used by this rule. */
protected IWordDetector fDetector;
/** The default token to be returned on success and if nothing else has been specified. */
protected IToken fDefaultToken;
/** The source viewer on which this rule is being run. */
protected ISourceViewer fSourceViewer;
/** The table of predefined keywords and token for this rule. */
protected Map<String, IToken> fWords = new HashMap<String, IToken>();
/** The table of predefined identifiers and token for this rule. */
protected Map<String, IToken> fIdentifiers = new HashMap<String, IToken>();
/** Buffer used for pattern detection. */
private StringBuffer fBuffer = new StringBuffer();
/** Buffer used to store the entire line of text. */
private StringBuffer fLineBuffer = new StringBuffer();
/** The (1-based) column on which the scanned word starts. */
private int fWordCol = 0;
/**
* Creates a rule which, with the help of a word detector, will return the token associated with
* the detected word. If no token has been associated, the specified default token will be
* returned.
*
* @param detector the word detector to be used by this rule, may not be <code>null</code>
* @param defaultToken the default token to be returned on success if nothing else is specified,
* may not be <code>null</code>
* @param sourceViewer
* @see #addWord(String, IToken)
*/
public SalesScanKeywordRule(IWordDetector detector, IToken defaultToken, ISourceViewer sourceViewer)
{
super(detector);
Assert.isNotNull(detector);
Assert.isNotNull(defaultToken);
fDetector = detector;
fDefaultToken = defaultToken;
fSourceViewer = sourceViewer;
}
/**
* Adds a keyword and the token to be returned if it is used as a keyword
* rather than an identifier.
*
* @param word the word this rule will search for, may not be <code>null</code>
* @param token the token to be returned if the word has been found, may not be
* <code>null</code>
*/
@Override public void addWord(String word, IToken token)
{
Assert.isNotNull(word);
Assert.isNotNull(token);
fWords.put(word.toLowerCase(), token);
}
/**
* Adds an identifier and the token to be returned if it is not used as a keyword.
*
* @param word the word this rule will search for, may not be <code>null</code>
* @param token the token to be returned if the word has been found, may not be
* <code>null</code>
*/
public void addIdentifier(String word, IToken token)
{
Assert.isNotNull(word);
Assert.isNotNull(token);
fIdentifiers.put(word.toLowerCase(), token);
}
/*
* @see IRule#evaluate(ICharacterScanner)
*/
@Override public IToken evaluate(ICharacterScanner scanner)
{
if (!(scanner instanceof FortranKeywordRuleBasedScanner))
throw new IllegalStateException();
int c = scanner.read();
if (c != ICharacterScanner.EOF && fDetector.isWordStart((char)c))
{
scanner.unread();
populateBuffers(scanner);
String buffer = fBuffer.toString().toLowerCase();
IToken token = fWords.get(buffer);
if (token != null)
return salesScan(token, (IToken)fIdentifiers.get(buffer));
if (fDefaultToken.isUndefined())
for (int i = fBuffer.length() - 1; i >= 0; i--)
scanner.unread();
if (fIdentifiers.containsKey(buffer))
return fIdentifiers.get(buffer);
else
return fDefaultToken;
}
scanner.unread();
return Token.UNDEFINED;
}
private void populateBuffers(ICharacterScanner scanner)
{
fBuffer.setLength(0);
fLineBuffer.setLength(0);
readPrefix(scanner);
readWord(scanner);
readSuffix(scanner);
fixLineBuffer();
}
private void readPrefix(ICharacterScanner scanner)
{
fWordCol = scanner.getColumn();
try
{
FortranKeywordRuleBasedScanner fscanner = (FortranKeywordRuleBasedScanner)scanner;
int tokenOffset = fscanner.getTokenOffset();
int partitionStart = fSourceViewer.getDocument().getPartition(tokenOffset).getOffset();
for (int i = partitionStart; i < tokenOffset; i++)
scanner.unread();
for (int i = partitionStart; i < tokenOffset; i++)
fLineBuffer.append((char)scanner.read());
}
catch (BadLocationException e)
{
FortranUIPlugin.log(e);
for (int i = 0; i < fWordCol; i++)
scanner.unread();
for (int i = 0; i < fWordCol; i++)
fLineBuffer.append((char)scanner.read());
}
}
private void readWord(ICharacterScanner scanner)
{
int c = scanner.read();
do
{
fBuffer.append((char)c);
fLineBuffer.append((char)c);
c = scanner.read();
}
while (c != ICharacterScanner.EOF && fDetector.isWordPart((char)c));
scanner.unread();
}
// There is a better way to detect end-of-lines
// (see org.eclipse.jface.text.rules.PatternRule#endSequenceDetected)
private void readSuffix(ICharacterScanner scanner)
{
int startCol = scanner.getColumn();
int count = 1;
int c = scanner.read();
if (c == ICharacterScanner.EOF || scanner.getColumn() < startCol) return;
do
{
count++;
fLineBuffer.append((char)c);
c = scanner.read();
}
while (c != ICharacterScanner.EOF); // && scanner.getColumn() > startCol);
for (int i = 0; i < count; i++)
scanner.unread();
}
private void fixLineBuffer()
{
removeStringLiterals();
removeTrailingComment();
removeLineContinuations();
removeConcatenatedStatements();
}
private void removeStringLiterals()
{
boolean inString = false;
for (int i = 0, length = fLineBuffer.length(); i < length; i++)
{
char thisChar = fLineBuffer.charAt(i);
char nextChar = i+1 < length ? fLineBuffer.charAt(i+1) : '\0';
if ((thisChar == '\"' || thisChar == '\'') && !inString)
inString = true;
else if ((thisChar == '\"' || thisChar == '\'') && inString)
inString = (nextChar == '\"');
if (inString || thisChar == '\"' || thisChar == '\'')
fLineBuffer.setCharAt(i, ' ');
}
}
private void removeTrailingComment()
{
int excl = fLineBuffer.lastIndexOf("!"); //$NON-NLS-1$
if (excl >= 0)
{
for (int i = excl, length = fLineBuffer.length(); i < length; i++)
fLineBuffer.setCharAt(i, ' ');
}
}
private void removeLineContinuations()
{
for (int i = 0; i < fLineBuffer.length(); i++)
if (fLineBuffer.charAt(i) == '&')
fLineBuffer.setCharAt(i, ' ');
}
private void removeConcatenatedStatements()
{
int precedingSemicolon = fLineBuffer.indexOf(";"); //$NON-NLS-1$
while (precedingSemicolon > 0 && precedingSemicolon < fWordCol)
{
for (int i = 0; i <= precedingSemicolon; i++)
fLineBuffer.setCharAt(i, ' ');
precedingSemicolon = fLineBuffer.indexOf(";", precedingSemicolon+1); //$NON-NLS-1$
}
int followingSemicolon = fLineBuffer.indexOf(";", fWordCol); //$NON-NLS-1$
if (followingSemicolon > 0)
for (int i = followingSemicolon, length = fLineBuffer.length(); i < length; i++)
fLineBuffer.setCharAt(i, ' ');
}
private IToken salesScan(IToken tokenIfKeyword, IToken tokenIfIdentifier)
{
try {
SalesScanner salesScanner = new SalesScanner(fLineBuffer.toString(), fBuffer.toString());
boolean retainAsKeyword = salesScanner.retainAsKeyword(fWordCol);
// System.out.println();
// System.out.println("fLineBuffer: \"" + fLineBuffer + "\"");
// System.out.println("fBuffer: \"" + fBuffer + "\"");
// System.out.println("Position " + fWordCol);
// System.out.println("First token at column " + (salesScanner.firstTokenPos));
// System.out.println("First token following ) at " + (salesScanner.tokenFollowingParentheticalPos));
// System.out.println("Open context comma? " + salesScanner.openContextComma);
// System.out.println("Open context equals? " + salesScanner.openContextEquals);
// System.out.println("Letter follows paren? " + salesScanner.letterFollowsParenthetical);
if (retainAsKeyword)
return tokenIfKeyword;
else if (tokenIfIdentifier == null)
return fDefaultToken;
else
return tokenIfIdentifier;
} catch (Throwable e) {
FortranUIPlugin.log(e);
return fDefaultToken;
}
}
/**
* Sale's algorithm determines whether the first token is a keyword
* or an identifier as well as the first token after the first
* closing parenthesis at the top level.
* <p>
* To apply Sale's algorithm, we must identify whether there is
* <ul>
* <li>an open context comma,
* <li>an open context equals, and/or
* <li>a letter immediately following a right parenthesis
* </ul>
*/
private static class SalesScanner
{
private String line, keyword;
private int length;
private int pos;
private boolean lineContainsColonColon;
private boolean[] inParens;
private boolean openContextComma;
private boolean openContextEquals;
private boolean letterFollowsParenthetical;
private int firstTokenPos = -1;
private int tokenFollowingParentheticalPos = -1;
public SalesScanner(String line, String keyword)
{
this.line = line;
this.keyword = keyword;
this.length = line.length();
this.pos = 0;
this.inParens = new boolean[line.length()];
this.firstTokenPos = findFirstToken();
scan();
}
private int findFirstToken()
{
int start = 0;
int cc = line.indexOf("::"); //$NON-NLS-1$
lineContainsColonColon = cc > 0;
if (lineContainsColonColon) start = cc + 2;
int pos = skipWhitespace(start);
if (pos >= 0 && Character.isDigit(line.charAt(pos)))
{
// Line (possibly) starts with an integer label
pos = skipInteger(pos);
pos = skipWhitespace(pos);
}
pos = skipConstructLabel(pos);
pos = skipWhitespace(pos);
return pos;
}
private int skipWhitespace(int start)
{
for (pos = start; pos < length; pos++)
if (!Character.isWhitespace(line.charAt(pos)))
return pos;
return 0;
}
private int skipInteger(int start)
{
for (pos = start; pos < length; pos++)
if (!Character.isDigit(line.charAt(pos)))
return pos;
return 0;
}
private int skipConstructLabel(int start)
{
for (pos = start; pos < length; pos++)
{
char ch = line.charAt(pos);
if (Character.isLetterOrDigit(ch) || ch == '_')
continue;
else if (ch == ':')
return pos+1;
else
return start;
}
return start;
}
private void scan()
{
openContextComma = false;
openContextEquals = false;
letterFollowsParenthetical = false;
int currentParenDepth = 0; // Track nested parentheses
boolean justClosedParen = false; // Does this token immediately follow T_RPAREN?
boolean firstParenthetical = true; // Is this the first non-nested T_RPAREN?
boolean inArrayLiteral = false; // Are we inside an (/ array literal /)
boolean followingThen = false; // Have we seen a THEN token
for (pos = 0; pos < length; pos++)
{
if (currentParenDepth == 0)
{
if (match("(/")) //$NON-NLS-1$
inArrayLiteral = true;
else if (match("/)")) //$NON-NLS-1$
inArrayLiteral = false;
else if (match("then")) //$NON-NLS-1$
followingThen = true;
if (!inArrayLiteral)
{
// The pos test ensures that we don't match the = in "integer, kind=3 :: if"
if (match(",") && pos >= firstTokenPos) //$NON-NLS-1$
openContextComma = true;
else if (match("=") && pos >= firstTokenPos && !followingThen) //$NON-NLS-1$
openContextEquals = true;
if (justClosedParen && firstParenthetical)
{
firstParenthetical = false;
tokenFollowingParentheticalPos = nextPos();
letterFollowsParenthetical = letterIsNext();
}
}
}
justClosedParen = false;
if (match("(")) //$NON-NLS-1$
{
currentParenDepth++;
}
else if (match(")")) //$NON-NLS-1$
{
currentParenDepth--;
justClosedParen = true;
}
inParens[pos] = currentParenDepth > 0;
}
}
private boolean match(String pattern)
{
return match(pattern, pos);
}
private boolean match(String pattern, int matchAtPosition)
{
if (matchAtPosition < 0) return false;
int patternLength = pattern.length();
if (matchAtPosition + patternLength > length) return false;
for (int i = 0; i < patternLength; i++)
if (Character.toLowerCase(line.charAt(matchAtPosition+i)) != Character.toLowerCase(pattern.charAt(i)))
return false;
return true;
}
private boolean letterIsNext()
{
for (int i = pos; i < length; i++)
{
if (Character.isWhitespace(line.charAt(i)))
continue;
else if (Character.isLetter(line.charAt(i)))
return true;
else
return false;
}
return false;
}
private int nextPos()
{
for (int i = pos; i < length; i++)
{
if (Character.isWhitespace(line.charAt(i)))
continue;
else
return i;
}
return length-1;
}
public boolean retainAsKeyword(int column)
{
// System.out.println();
// System.out.println("Column " + column + ": " + line.substring(column));
// System.out.println("OC,: " + openContextComma + "\tOC=: " + openContextEquals + "\t)L: " + letterFollowsParenthetical);
// System.out.println("Token following parenthetical starts at column " + tokenFollowingParentheticalPos);
// int precedingKeywordOffset = findPrecedingKeyword(column);
// String precedingKeyword = precedingKeywordAsString(column, precedingKeywordOffset);
// System.out.println("Preceding token is " + precedingKeyword + " (column " + precedingKeywordOffset + ")");
// System.out.println("Retain preceding token as keyword? " + internalRetainAsKeyword(precedingKeywordOffset, precedingKeyword));
// System.out.println("Line starting at first token: " + line.substring(firstTokenPos));
// System.out.println("Retain as keyword? " + internalRetainAsKeyword(column, keyword));
return internalRetainAsKeyword(column, keyword);
}
private boolean internalRetainAsKeyword(int column, String keyword)
{
if (column < 0) return false;
if (salesRetainAsKeyword(column)) return true;
if (keyword.equals("")) return false; //$NON-NLS-1$
return applyKeywordRules(column, keyword);
}
private boolean applyKeywordRules(int column, String keyword)
{
// N.B. These rules apply regardless of the token preceding this one
if (keyword.equalsIgnoreCase("only")) //$NON-NLS-1$
return openContextComma && match("use", firstTokenPos); //$NON-NLS-1$
else if (keyword.equalsIgnoreCase("result")) //$NON-NLS-1$
return letterFollowsParenthetical;
else if (keyword.equalsIgnoreCase("then")) //$NON-NLS-1$
return !openContextEquals && !openContextComma && (match("if", firstTokenPos) || match("else", firstTokenPos) || match("forall", firstTokenPos)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
else if (keyword.equalsIgnoreCase("to")) //$NON-NLS-1$
return !openContextEquals && !openContextComma && match("go", firstTokenPos); //$NON-NLS-1$
// BEGIN FORTRAN 2003
else if (keyword.equalsIgnoreCase("bind")) //$NON-NLS-1$
return openContextComma && (match("enum", firstTokenPos) || match("type", firstTokenPos)) //$NON-NLS-1$ //$NON-NLS-2$
|| match("function", firstTokenPos) //$NON-NLS-1$
|| match("subroutine", firstTokenPos); //$NON-NLS-1$
else if (keyword.equalsIgnoreCase("procedure")) //$NON-NLS-1$
return match("procedure", firstTokenPos) || match("module", firstTokenPos) /* F08 */ || match("end", firstTokenPos); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
else if (keyword.equalsIgnoreCase("pointer")) //$NON-NLS-1$
return openContextComma;
else if (keyword.equalsIgnoreCase("operator") //$NON-NLS-1$
|| keyword.equalsIgnoreCase("assignment") //$NON-NLS-1$
|| keyword.equalsIgnoreCase("read") //$NON-NLS-1$
|| keyword.equalsIgnoreCase("write")) //$NON-NLS-1$
return lineContainsColonColon;
// END FORTRAN 2003
// BEGIN HP EXTENSIONS (%FILL)
else if (keyword.equalsIgnoreCase("fill")) //$NON-NLS-1$
return column > 0 && line.charAt(column-1) == '%' && isType(firstTokenLetterString());
// END HP EXTENSIONS
else
{
if (isType(keyword) && match("implicit", firstTokenPos)) //$NON-NLS-1$
return true;
int precedingKeywordOffset = findPrecedingKeyword(column);
if (precedingKeywordOffset == column) return false;
String precedingKeyword = precedingKeywordAsString(column, precedingKeywordOffset);
if (!internalRetainAsKeyword(precedingKeywordOffset, precedingKeyword)) return false;
return applyPrecedingKeywordRules(keyword, precedingKeyword);
}
}
private String firstTokenLetterString()
{
String lineFromFirstToken = line.substring(findFirstToken());
StringBuilder sb = new StringBuilder(16);
for (int pos = 0, len = lineFromFirstToken.length(); pos < len; pos++)
{
char ch = lineFromFirstToken.charAt(pos);
if (Character.isLetter(ch))
sb.append(ch);
else
break;
}
return sb.toString();
}
private boolean applyPrecedingKeywordRules(String keyword, String precedingKeyword)
{
// BEGIN FORTRAN 2008
if (keyword.equalsIgnoreCase("module")) //$NON-NLS-1$
return isPrefixSpec(precedingKeyword) && !precedingKeyword.equalsIgnoreCase("module") //$NON-NLS-1$
|| precedingKeyword.equalsIgnoreCase("end"); //$NON-NLS-1$
// END FORTRAN 2008
// N.B. These rules depend on the token preceding this one
else if (keyword.equalsIgnoreCase("complex") && precedingKeyword.equalsIgnoreCase("double")) //$NON-NLS-1$ //$NON-NLS-2$
return true;
else if (isType(keyword) && !keyword.equalsIgnoreCase("type")) //$NON-NLS-1$
return isPrefixSpec(precedingKeyword)|| precedingKeyword.equalsIgnoreCase("implicit"); //$NON-NLS-1$
else if (isPrefixSpec(keyword))
return isType(precedingKeyword);
else if (keyword.equalsIgnoreCase("case")) //$NON-NLS-1$
return precedingKeyword.equalsIgnoreCase("select"); //$NON-NLS-1$
else if (keyword.equalsIgnoreCase("data")) //$NON-NLS-1$
return precedingKeyword.equalsIgnoreCase("block"); //$NON-NLS-1$
else if (keyword.equalsIgnoreCase("subroutine")) //$NON-NLS-1$
return precedingKeyword.equalsIgnoreCase("end") || isPrefixSpec(precedingKeyword); //$NON-NLS-1$
else if (keyword.equalsIgnoreCase("function")) //$NON-NLS-1$
return precedingKeyword.equalsIgnoreCase("end") || isType(precedingKeyword) || isPrefixSpec(precedingKeyword) || match("type", firstTokenPos); //$NON-NLS-1$ //$NON-NLS-2$
else if (keyword.equalsIgnoreCase("if")) //$NON-NLS-1$
return precedingKeyword.equalsIgnoreCase("else") || precedingKeyword.equalsIgnoreCase("end"); //$NON-NLS-1$ //$NON-NLS-2$
else if (keyword.equalsIgnoreCase("none")) //$NON-NLS-1$
return precedingKeyword.equalsIgnoreCase("implicit"); //$NON-NLS-1$
else if (keyword.equalsIgnoreCase("complex")) //$NON-NLS-1$
return precedingKeyword.equalsIgnoreCase("double"); //$NON-NLS-1$
else if (keyword.equalsIgnoreCase("precision")) //$NON-NLS-1$
return precedingKeyword.equalsIgnoreCase("double"); //$NON-NLS-1$
else if (keyword.equalsIgnoreCase("where")) //$NON-NLS-1$
return precedingKeyword.equalsIgnoreCase("else") || precedingKeyword.equalsIgnoreCase("end"); //$NON-NLS-1$ //$NON-NLS-2$
else if (keyword.equalsIgnoreCase("while")) //$NON-NLS-1$
return precedingKeyword.equalsIgnoreCase("do"); //$NON-NLS-1$
// BEGIN FORTRAN 2003
else if (keyword.equalsIgnoreCase("type")) //$NON-NLS-1$
return isPrefixSpec(precedingKeyword)
|| precedingKeyword.equalsIgnoreCase("implicit") //$NON-NLS-1$
|| precedingKeyword.equalsIgnoreCase("end") //$NON-NLS-1$
|| precedingKeyword.equalsIgnoreCase("select"); //$NON-NLS-1$
else if (keyword.equalsIgnoreCase("interface")) //$NON-NLS-1$
return precedingKeyword.equalsIgnoreCase("abstract") || precedingKeyword.equalsIgnoreCase("end"); //$NON-NLS-1$ //$NON-NLS-2$
else if (keyword.equalsIgnoreCase("is")) //$NON-NLS-1$
return precedingKeyword.equalsIgnoreCase("type") || precedingKeyword.equalsIgnoreCase("class"); //$NON-NLS-1$ //$NON-NLS-2$
else if (keyword.equalsIgnoreCase("default")) //$NON-NLS-1$
return precedingKeyword.equalsIgnoreCase("case") || precedingKeyword.equalsIgnoreCase("class"); //$NON-NLS-1$ //$NON-NLS-2$
// END FORTRAN 2003
// BEGIN FORTRAN 2008
else if (keyword.equalsIgnoreCase("stop")) //$NON-NLS-1$
return precedingKeyword.equalsIgnoreCase("all"); //$NON-NLS-1$
else if (keyword.equalsIgnoreCase("all") //$NON-NLS-1$
|| keyword.equalsIgnoreCase("images") //$NON-NLS-1$
|| keyword.equalsIgnoreCase("memory")) //$NON-NLS-1$
return precedingKeyword.equalsIgnoreCase("sync"); //$NON-NLS-1$
else if (keyword.equalsIgnoreCase("concurrent")) //$NON-NLS-1$
return precedingKeyword.equalsIgnoreCase("do"); //$NON-NLS-1$
// END FORTRAN 2008
else
return precedingKeyword.equalsIgnoreCase("end"); //$NON-NLS-1$
}
private boolean salesRetainAsKeyword(int column)
{
if (column == firstTokenPos)
return retainFirstTokenAsKeyword();
else if (column == tokenFollowingParentheticalPos)
return retainTokenFollowingParentheticalAsKeyword();
else if (lineContainsColonColon)
return column < firstTokenPos && !inParens[column];
else
return false;
}
private boolean retainFirstTokenAsKeyword()
{
if (!openContextComma && !openContextEquals)
return !lineContainsColonColon;
else if (openContextEquals && !openContextComma)
return !lineContainsColonColon
&& letterFollowsParenthetical
&& (match("if", firstTokenPos) //$NON-NLS-1$
|| match("where", firstTokenPos) //$NON-NLS-1$
|| match("forall", firstTokenPos) //$NON-NLS-1$
|| match("structure") //$NON-NLS-1$
|| match("union") //$NON-NLS-1$
|| match("map")); //$NON-NLS-1$
else if (openContextComma)
return true;
else if (letterFollowsParenthetical)
return true;
else
return false;
}
private boolean retainTokenFollowingParentheticalAsKeyword()
{
return letterFollowsParenthetical
&& (match("if", firstTokenPos) || match("forall", firstTokenPos)) //$NON-NLS-1$ //$NON-NLS-2$
&& !openContextEquals;
}
private boolean isType(String kw)
{
return kw.equalsIgnoreCase("character") //$NON-NLS-1$
|| kw.equalsIgnoreCase("complex") //$NON-NLS-1$
|| kw.equalsIgnoreCase("double") //$NON-NLS-1$
|| kw.equalsIgnoreCase("doublecomplex") //$NON-NLS-1$
|| kw.equalsIgnoreCase("doubleprecision") //$NON-NLS-1$
|| kw.equalsIgnoreCase("integer") //$NON-NLS-1$
|| kw.equalsIgnoreCase("logical") //$NON-NLS-1$
|| kw.equalsIgnoreCase("real") //$NON-NLS-1$
|| kw.equalsIgnoreCase("type") //$NON-NLS-1$
|| kw.equalsIgnoreCase("byte"); //$NON-NLS-1$
}
private boolean isPrefixSpec(String kw)
{
return kw.equalsIgnoreCase("recursive") //$NON-NLS-1$
|| kw.equalsIgnoreCase("pure") //$NON-NLS-1$
|| kw.equalsIgnoreCase("elemental") //$NON-NLS-1$
|| kw.equalsIgnoreCase("impure") // F08 //$NON-NLS-1$
|| kw.equalsIgnoreCase("module"); // F08 //$NON-NLS-1$
}
private int findPrecedingKeyword(int wordCol)
{
return backOverIdentifier(backOverSpaces(wordCol-1))+1;
}
private int backOverSpaces(int offset)
{
while (offset >= 0 && Character.isWhitespace(line.charAt(offset)))
offset--;
return offset;
}
private int backOverIdentifier(int offset)
{
if (offset >= 0 && line.charAt(offset) == ')')
offset = backOverParens(offset);
while (offset >= 0 && Character.isJavaIdentifierPart(line.charAt(offset)))
offset--;
return offset;
}
private int backOverParens(int offset)
{
int depth = 0;
while (offset >= 0)
{
if (line.charAt(offset) == ')')
depth++;
else if (line.charAt(offset) == '(')
depth--;
offset--;
if (depth == 0) break;
}
return offset;
}
private String precedingKeywordAsString(int column, int precedingKeywordOffset)
{
String precedingKeyword = line.substring(precedingKeywordOffset, column);
if (precedingKeyword.indexOf("(") >= 0) precedingKeyword = precedingKeyword.substring(0, precedingKeyword.indexOf("(")); //$NON-NLS-1$ //$NON-NLS-2$
precedingKeyword = precedingKeyword.trim();
return precedingKeyword;
}
}
}