FortranStream.java
/*******************************************************************************
* Copyright (c) 2005, 2006 Los Alamos National Security, LLC.
* This material was produced under U.S. Government contract DE-AC52-06NA25396
* for Los Alamos National Laboratory (LANL), which is operated by the Los Alamos
* National Security, LLC (LANS) for the U.S. Department of Energy. The U.S. Government has
* rights to use, reproduce, and distribute this software. NEITHER THE
* GOVERNMENT NOR LANS MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR
* ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified
* to produce derivative works, such modified software should be clearly marked,
* so as not to confuse it with the version available from LANL.
*
* Additionally, 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
*******************************************************************************/
package fortran.ofp.parser.java;
import java.io.*;
import org.antlr.runtime.*;
public class FortranStream extends ANTLRFileStream
{
private int sourceForm;
private String filename;
private String filepath;
public static final int UNKNOWN_SOURCE_FORM = -1;
public static final int FREE_FORM = 1;
public static final int FIXED_FORM = 2;
/**
* Create a new input buffer and use it to fix line continuation. This
* buffer will be used to strip out continuation characters, extra '\n'
* characters, and in fixed form, extra characters in columns 1-6,
* including TAB. It also moves comments and preprocesser commands if
* they are in the middle of continuation lines.
*
* Note that Holleriths in edit descriptors must be recognized, otherwise
* what looks like a comment will be processed incorrectly, consider the
* the very evil
*
* 100 format (1h1,58x,1h!,/,60x,/,59x,1h*,/)
*
* which has "1h!" as a hollerith, not a comment!
*
* Also consider
*
* 200 format (1h1,58x,2h!)
* &)
*/
public FortranStream(String filename) throws IOException
{
super(filename);
this.filename = filename;
File file = new File(filename);
this.filepath = file.getAbsolutePath();
this.sourceForm = determineSourceForm(filename);
convertInputBuffer();
}
public FortranStream(String filename, String path, int sourceForm) throws IOException
{
super(path);
this.filename = filename;
this.filepath = path;
this.sourceForm = sourceForm;
convertInputBuffer();
}
public int determineSourceForm(String fileName) {
if (fileName.endsWith(new String(".f")) == true ||
fileName.endsWith(new String(".F")) == true) {
this.sourceForm = FIXED_FORM;
return FIXED_FORM;
} else {
this.sourceForm = FREE_FORM;
return FREE_FORM;
}
} // end determineSourceForm()
public int getSourceForm()
{
return this.sourceForm;
}
public String getFileName()
{
return filename;
}
public String getAbsolutePath()
{
return filepath;
}
/**
* Convert characters to upper case. This is only for look ahead
* used in building tokens, in particular key words, the actually
* character buffer is unchanged so id tokens have original case.
*/
public int LA(int i)
{
int letter_value = super.LA(i);
// the letter is lower-case
if (Character.isLowerCase((char)letter_value)) {
// convert to upper-case
letter_value = (int) Character.toUpperCase((char)(letter_value));
}
return letter_value;
/****OBSOLETE*****
int char_pos = super.getCharPositionInLine();
if (this.sourceForm == FrontEnd.FIXED_FORM) {
System.out.println("FIXED_FORM: i=" + i + " pos=" + char_pos + " " + letter_value);
if (char_pos == 0) {
if (letter_value == 'C' || letter_value == '*') {
// return '!' to signify a line comment so the lexer won't try
// and parse this line.
letter_value = (int) '!';
}
}
else {
// Look for continuation character. The convention we use
// is for TAB + (WS | '0') acts as ';'. TAB + other char is a
// continuation line. This follows DEC (I believe) but is
// non standard Fortran.
//
// location of continuation character
int continue_pos = 5; // 6th column
// check for tab formatting, note, this seems to work because LA always
// called with i==1, so getCharPositionInLine is always as expected
if (super.LA(-char_pos) == (int) '\t') {
// first character in line is a tab
continue_pos = 1;
}
if (char_pos == continue_pos) {
// if neither '0' nor whitespace then a continuation character
if (! (letter_value == (int)'0' || Character.isWhitespace((char)letter_value))) {
letter_value = (int)'&';
}
}
}
}
return letter_value;
*****OBSOLETE*****/
} // end LA()
private void convertInputBuffer()
{
//
// Processing is a lot easier if we add a couple of '\n'
// chars to buffer, as file can terminate on '!', for example.
//
// IMPORTANT NOTE: In processing a buffer we assume we can always
// advance to character beyond a '\n'.
// TODO - I believe this means we don't have to check for end
// of buffer (as currently doing in many places) and these
// checks, for example,
//
// if (i < super.n && buf[i] == '!') {
//
// can be removed.
//
char[] newData = new char[super.n+2];
for (int i = 0; i < super.n; i++) {
newData[i] = super.data[i];
}
newData[super.n] = '\n';
newData[super.n+1] = '\n';
super.data = newData;
if (this.sourceForm == FIXED_FORM) {
convertFixedFormInputBuffer();
}
else {
convertFreeFormInputBuffer();
}
}
private void convertFreeFormInputBuffer()
{
// buffer for line comments and preprocessor lines
StringBuffer comments = new StringBuffer();
char[] newData = new char[super.n];
boolean continuation = false;
int count = 0;
int col = 1; // 1 based
int line = 1; // 1 based
for (int i = 0; i < super.n; i++) {
int ii;
// process column 1 special characters
if (col == 1) {
while ((ii = consumePreprocessLine(i, data, comments)) != i) {
// preprocess line can't be added immediately because
// could be in the middle of a continued line
line += 1;
i = ii;
}
while ((ii = consumeFreeFormCommentLine(i, data, comments)) != i) {
// comment line can't be added immediately because
// could be in the middle of a continued line
line += 1;
i = ii;
}
if (continuation) {
// '&' may be first nonblank character in a line,
// if so, skip over the continuation character
if ((ii = skipFreeFormContinuationAtBegin(i, data)) != i) {
col += ii - i;
i = ii;
}
// process a string if it exists
else if (matchFreeFormString(i, data)) {
ii = consumeFreeFormString(i, data, count, newData);
char quoteChar = data[i];
while (data[ii] == '&') {
// string is continued across multiple lines
line += 1;
count += ii - i;
col += ii - i;
i = ii;
ii = completeContinuedString(quoteChar, i, data, count, newData);
}
count += ii - i;
col += ii - i;
i = ii;
}
continuation = false;
}
else {
// add any comments and preprocess lines since not in
// the middle of a continued line
if (comments.length() > 0) {
count = consumeCommentLines(count, newData, comments);
if (i >= super.n) {
// this can occur if last line is a comment line
continue;
}
}
}
}
// process all columns > 1
else {
// consume comment if it exists but retain '\n'
if ((ii = consumeComment(i, data, comments)) != i) {
count = consumeCommentLines(count, newData, comments);
i = ii;
}
// remove continuation if it exists but retain '\n'
else if (matchFreeFormContinuationAtEnd(i, data)) {
ii = consumeFreeFormContinuationAtEnd(i, data, comments);
continuation = true;
i = ii;
}
// process a string if it exists but retain trailing quote char
else if (matchFreeFormString(i, data)) {
ii = consumeFreeFormString(i, data, count, newData);
char quoteChar = data[i];
while (data[ii] == '&') {
// string is continued across multiple lines
line += 1;
count += ii - i;
col += ii - i;
i = ii;
ii = completeContinuedString(quoteChar, i, data, count, newData);
}
count += ii - i;
col += ii - i;
i = ii;
}
// Holleriths are matched after strings so Hollerith matching doesn't have
// to worry about string, i.e, the string, "4HThis is a string".
else if ((ii = consumeHollerith(i, data, count, newData)) != i) {
//System.out.println("Found Hollerith character");
count += ii - i;
col += ii - i;
i = ii;
}
}
// copy current character
if (!continuation) {
if (data[i] == '\n') {
col = 1;
line += 1;
// copy comments that were caught up with continuation
count = consumeCommentLines(count, newData, comments);
}
else {
col += 1;
}
newData[count++] = data[i];
}
// this line is to be continued
else {
col = 1;
}
}
// switch to new data buffer
this.data = newData;
this.n = count;
}
/**
* All comments in the middle of continuation lines are moved to a location
* immediately AFTER the continued line.
*/
private void convertFixedFormInputBuffer()
{
// buffer for line comments and preprocessor lines
StringBuffer comments = new StringBuffer();
char[] newData = new char[super.n];
int count = 0;
int col = 1; // 1 based
int line = 1; // 1 based
for (int i = 0; i < super.n; i++) {
int ii;
// process column 1 special characters
if (col == 1) {
while ((ii = consumePreprocessLine(i, data, comments)) != i) {
count = consumeCommentLines(count, newData, comments);
line += 1;
i = ii;
}
while ((ii = consumeFixedFormCommentLine(i, data, comments)) != i) {
count = consumeCommentLines(count, newData, comments);
line += 1;
i = ii;
}
if (i >= super.n) {
// this can occur if last line is a comment line
continue;
}
// "expand" TABs by bumping to column 5
if (data[i] == '\t') {
col = 5; // column 5 will pick up TAB character
}
}
else if (col > 1 && col < 6) {
// consume a comment if it exists but retain '\n' or EOF
if (matchComment(i, data)) {
i = consumeComment(i, data, comments);
// can't add comments yet if the line is continued
if (!matchFixedFormContinuation(i, data)) {
count = consumeCommentLines(count, newData, comments);
}
}
}
else if (col == 6) {
// Continuation checked at '\n' so no need to here, just pass the character.
// If first line is a continuation it is an error so won't need to be
// caught here. TODO - what about included files with continuation, legal?
// but I think 0 in column 6 to start is legal (gfortran and ifort disagree)
if (data[i] == '0') data[i] = ' ';
}
else {
// consume a comment if it exists but retain '\n' or EOF
if (matchComment(i, data)) {
i = consumeComment(i, data, comments);
// can't add comments yet if the line is continued
if (!matchFixedFormContinuation(i, data)) {
count = consumeCommentLines(count, newData, comments);
}
}
// consume a string if it exists but retain trailing quote char
else if (matchFixedFormString(i, data)) {
ii = consumeFixedFormString(i, data, count, newData);
count += ii - i;
col += ii - i;
i = ii;
}
// Holleriths are matched after strings so Hollerith matching doesn't have
// to worry about strings, i.e, the string, "4HThis is a string".
else if ((ii = consumeHollerith(i, data, count, newData)) != i) {
//System.out.println("Found Hollerith character");
count += ii - i;
col += ii - i;
i = ii;
}
}
while (data[i] == '\n' && matchFixedFormContinuation(i, data)) {
i = consumeFixedFormContinuation(i, data, comments);
}
// copy current character
newData[count++] = data[i];
col += 1;
if (data[i] == '\n') {
col = 1;
line += 1;
// copy comments that were caught up with continuation
count = consumeCommentLines(count, newData, comments);
}
}
// switch to new data buffer
this.data = newData;
this.n = count;
}
/**
* Copy comment line and preprocessor lines to data buffer
*/
int consumeCommentLines(int i, char [] newData, StringBuffer comments)
{
for(int ii = 0; ii < comments.length(); ii++) {
newData[i++] = comments.charAt(ii);
}
comments.delete(0, comments.length());
return i;
}
/**
* Return true if this character starts a comment
*/
private boolean matchComment(int i, char buf[])
{
return (buf[i] == '!');
}
/**
* if a comment, copy comment to comments buffer excluding terminating '\n' character
*/
private int consumeComment(int i, char buf[], StringBuffer comments)
{
if (i < super.n && buf[i] == '!') {
// found comment character, copy characters up to '\n'
do {
comments.append(buf[i++]);
}
while (i < super.n && buf[i] != '\n');
}
return i;
}
/**
* Return true if a comment line beginning with '!' is found
*/
private boolean matchFreeFormCommentLine(int i, char buf[])
{
// skip over leading blank characters
// TODO - what about TABS
int i1 = i;
while(i1 < super.n && buf[i1] == ' ') i1 += 1;
if (i1 >= super.n) return false;
if (buf[i1] == '!' || buf[i1] == '\n') return true;
return false;
}
private int consumeFreeFormCommentLine(int i, char buf[], StringBuffer comments)
{
if (i >= super.n) return i;
// skip over leading blank characters
int i1 = i;
while(i1 < super.n && buf[i1] == ' ') i1 += 1;
// nothing to do if not a comment line
if (i1 < super.n && buf[i1] != '!' && buf[i1] != '\n') return i;
// copy leading blank characters
for (int ii = 0; ii < i1-i; ii++) comments.append(' ');
if (i1 == super.n) return super.n;
if (buf[i1] == '\n') {
// a comment line with only whitespace
comments.append('\n');
i = i1+1;
}
else {
i = processLineForCommentChar('!', i1, buf, comments);
}
return i;
}
/**
* If a comment, beginning with '!', copy the comment to comments buffer excluding
* the terminating '\n' character. Because the next line could be a fixed form
* continuation, we can't immediately consume the comment as all comments must
* come after all continued lines (comments can be interspersed between continued
* lines).
*
* Unused for now. In future could be used to shorten code in made section
* when processing comments.
*/
private int consumeFixedFormComments(int i, char buf[], StringBuffer comments)
{
if (i < super.n && buf[i] == '!') {
// found comment character, copy characters up to '\n'
do {
comments.append(buf[i++]);
}
while (i < super.n && buf[i] != '\n');
}
// consume all comment lines before looking for continuation
//
while (matchFixedFormCommentLine(i, buf)) {
// add '\n' so comments are not merged on a single line
comments.append('\n');
// TODO - bump line number and set column?
i = consumeFixedFormCommentLine(i, buf, comments);
}
return i;
}
/**
* Return true if a fixed form comment line is found.
*
* Check for comment characters, 'C', '*', and '!' at the start of
* a line. A blank line is also a comment line.
*/
private boolean matchFixedFormCommentLine(int i, char buf[])
{
if (i >= super.n) return false;
// first check for free form ('!' comments and blank character lines)
if (matchFreeFormCommentLine(i, buf)) return true;
// check for a normal comment line
if (buf[i] == '*' || buf[i] == 'C' || buf[i] == 'c') return true;
return false;
}
/**
* Check for comment characters, 'C', '*', and '!' at start of
* a line. A blank line is also a comment line. If a comment line is
* found, copy the line comment to the comments buffer (without trailing
* '\n', and return the position of the character after the '\n' character.
*/
private int consumeFixedFormCommentLine(int i, char buf[], StringBuffer comments)
{
if (i >= super.n) return i;
// first check for free form ('!' comments and blank character lines)
int ii = consumeFreeFormCommentLine(i, buf, comments);
if (ii != i) return ii;
// check for a normal comment line
if (buf[i] == '*') ii = processLineForCommentChar('*', i, buf, comments);
else if (buf[i] == 'C') ii = processLineForCommentChar('C', i, buf, comments);
else if (buf[i] == 'c') ii = processLineForCommentChar('c', i, buf, comments);
return ii;
}
/**
* If character at i == c, skip to next line advancing past '\n', while
* copying intervening characters to comments buffer.
*/
private int processLineForCommentChar(char c, int i, char buf[], StringBuffer comments)
{
if (i >= super.n) return i;
if (buf[i] == c) {
if (buf[i] == '*' || buf[i] == 'C' || buf[i] == 'c') {
// replace by free form comment character
comments.append('!');
} else {
comments.append(buf[i]);
}
i += 1;
// found character, copy rest of line
while (i < super.n && buf[i] != '\n') {
comments.append(buf[i++]);
}
if (i < super.n) {
comments.append(buf[i++]); // copy '\n' also
}
}
return i;
}
private boolean matchPreprocessLine(int i, char buf[])
{
return (buf[i] == '#');
}
private int consumePreprocessLine(int i, char buf[], StringBuffer comments)
{
return processLineForCommentChar('#', i, buf, comments);
}
/**
* Return true if the current character is '&'
*/
private boolean matchFreeFormContinuationAtEnd(int i, char buf[])
{
return (buf[i] == '&');
}
/**
* If the current character is '&', skip the '&' character and
* copy all remaining characters to the comments buffer, including
* '\n', to retain possible comments following the continuation character.
*/
private int consumeFreeFormContinuationAtEnd(int i, char buf[], StringBuffer comments)
{
if (i >= super.n || buf[i] != '&') return i;
i += 1; // skip the continuation character
while (i < super.n && buf[i] != '\n') {
comments.append(buf[i++]);
}
if (i < super.n) {
comments.append(buf[i++]); // copy '\n' also
}
return i-1; // retain the '\n'
}
/**
* Check to see if there is a continuation character as '&'
* the first non-blank character in a line. If there is, return
* the position after the '&' character.
*/
private int skipFreeFormContinuationAtBegin(int i, char buf[])
{
int ii = i;
while (ii < super.n && buf[ii] == ' ' && buf[ii] != '&') ii += 1;
if (buf[ii] == '&') i = ii + 1;
return i;
}
/**
* Called when at a '\n' character. Look ahead for continuation
* character at column 6. There could be comment or preprocess
* lines in between so have to skip over comment lines and if
* they exist.
*
* The convention for a TAB character in columns 1..5 followed
* by a digit 1..9 is a continuation line. If TAB + '0' the
* '0' is ignored in the input stream. This follows DEC convention
* (I believe) but is non standard Fortran.
*
* WARNING, don't go beyond length of stream, super.n
*/
private boolean matchFixedFormContinuation(int i, char buf[])
{
int ii;
// skip all preprocessor and comment lines
//
i += 1;
while (matchPreprocessLine(i, buf) || matchFixedFormCommentLine(i, buf)) {
i = findCharacter('\n', i, buf);
if (buf[i] != '\n') return false;
i += 1;
}
// search for TAB in columns 1..5, otherwise continued position will be ii
ii = i;
for (int j = 0; j < 5; j++) {
if (buf[ii] == '\n') return false;
if (buf[ii++] == '\t') {
if (buf[ii] >= '1' && buf[ii] <= '9') {
return true;
}
else {
return false;
}
}
}
if (buf[ii] != '0' && buf[ii] != ' ') {
return true;
}
return false;
}
/**
* Called when at a '\n' character. Look ahead for continuation
* character at column 6. There could be comment or preprocess
* lines in between so have to search for comment lines and remove
* them if they exist.
*
* The convention for a TAB character in columns 1..5 followed
* by a digit 1..9 is a continuation line. If TAB + '0' the
* '0' is ignored in the input stream. This follows DEC convention
* (I believe) but is non standard Fortran.
*
* WARNING, don't go beyond length of stream, super.n
*/
private int consumeFixedFormContinuation(int i, char buf[], StringBuffer comments)
{
int i0 = i; // save initial value of i
int ii = i + 1; // look ahead past the '\n'
// consume all preprocessor and comment lines
//
i += 1;
if (matchPreprocessLine(i, buf)) {
return (consumePreprocessLine(i, buf, comments) - 1); // retain the '\n'
}
if (matchFixedFormCommentLine(i, buf)) {
return (consumeFixedFormCommentLine(i, buf, comments) - 1); // retain the '\n'
}
// search for TAB in columns 1..5, otherwise continued position will be ii
for (int j = 0; j < 5; j++) {
if (buf[ii] == '\n') return i0;
if (buf[ii++] == '\t') {
if (buf[ii] >= '1' && buf[ii] <= '9') {
return ii+1;
}
else {
if (i == i0 + 1) return i0; // nothing found
else return i-1; // '\n' position from comment line
}
}
}
if (buf[ii] != '0' && buf[ii] != ' ') {
comments.append('\n');
return ii+1; // a continuation found
}
// if statement begins after '0', replace '0' with ' ' for parsing
if (buf[ii] == '0') {
buf[ii] = ' ';
}
return i0; // nothing found (expect possibly replacing '0' in column 6
}
/**
* Check for a Hollerith following the current character position. First must
* ensure that it's not an identifier, "var_2Hxx", so look for preceding
* character, ' ', '(', ',' (and perhaps more). Then look for digit string, n,
* followed by 'H'|'h' and then n characters (none of which is \'n').
* Perhaps we want to change Hollerith to a quoted string. In any case,
* copy string representation to newBuf.
*
* We would like to be conservative while matching Hollerith's. Examples showing
* characters that can precede a Hollerith constant:
* " 1Hx", "=1Hx", ".eq.1Hx", "(1Hx", "-1Hx", ",1Hx". Note Hollerith constants
* have no data type; they assume a numeric type based on the way they are used.
* They cannot assume a character data type and cannot be used where a character
* value is expected (from a DEC manual for F77, I believe). Not sure this
* applies to Hollerith edit descriptors.
*
* Assume that strings have been matched so a Hollerith-like constant
* within a string doesn't have to been considered.
*
* Return the last character position in the Hollerith constant if found.
*/
private int consumeHollerith(int i, char buf[], int count, char newBuf[])
{
int k;
if (i >= super.n) return i;
// Return i if the first character can be used in a name context, e.g.,
// "v1H" or "_1H" as this could have been the name "v_1H". A name is
// A name is a letter followed by an alphanumeric character
// (letter, digit, '_').
// it might be conservative and look for only what CAN precede Hollerith
if (buf[i] >= 'a' && buf[i] <= 'z') return i;
if (buf[i] >= 'A' && buf[i] <= 'Z') return i;
if (buf[i] == '_') return i;
// count digits preceding possible Hollerith
int ii = i + 1;
int numDigits = 0;
while (buf[ii] >= '0' && buf[ii] <= '9') {
ii += 1;
numDigits += 1;
}
if (numDigits == 0) return i;
if (buf[ii] != 'H' && buf[ii] != 'h') return i;
// found Hollerith
StringBuffer chars = new StringBuffer(numDigits);
for (k = 0; k < numDigits; k++) {
chars.append(buf[i+1+k]);
}
int numChars = Integer.parseInt(chars.toString());
// look for numChars printable characters
ii += 1;
for (k = 0; k < numChars; k++) {
if (buf[ii+k] < ' ' || buf[ii+k] > '-') break;
}
if (k != numChars) return i;
// number of characters to copy (includes preceding character)
int numTotal = 1 + numDigits + 1 + numChars;
// found a Hollerith constant, copy all but last character to newBuf
for (k = 0; k < numTotal-1; k++) {
newBuf[count++] = buf[i+k];
}
return i + numTotal - 1;
}
/**
* Complete the processing of a string that is continued across multiple lines.
*/
private int completeContinuedString(char quoteChar, int i, char buf[], int count, char newBuf[])
{
int i0 = i;
// skip initial '&'
if (++i >= super.n) return i0;
// skip characters after initial '&'
while (i < super.n && buf[i] != '\n') i += 1; // TODO - check for comment
i += 1; // skip '\n'
// skip ' ' characters on next line
// TODO - what about TABS?
while (i < super.n && buf[i] == ' ') i += 1;
//
// This should be removed in 0.8.3
//
// if (i >= super.n || buf[i] != '&') {
// // first non-blank character is part of the continued string so back up to get it
// i -= 1;
//
// //CER System.err.println("Terminating '&' not found in continued string at character position " + i);
// //CER return i0;
// }
if (i >= super.n) return i-1;
// skip trailing '&'
//
// NOTE: gfortran doesn't require the terminating character (warns with -Wall)
// so we also ignore the standard here if the trailing '&' is missing
//
if (buf[i] == '&') i += 1;
if (i >= super.n) return i-1;
do {
newBuf[count++] = buf[i++];
// look for two quote chars in a row, if found copy both
if (i < super.n - 1 && buf[i] == quoteChar && buf[i+1] == quoteChar) {
newBuf[count++] = buf[i++];
newBuf[count++] = buf[i++];
}
}
while (i < super.n && buf[i] != quoteChar && buf[i] != '&' && buf[i] != '\n');
return i;
}
/**
* Check for the beginning of a string at this character position.
*/
private boolean matchFreeFormString(int i, char buf[])
{
if (i >= super.n) return false;
char quote_char = buf[i];
if (quote_char == '"' || quote_char == '\'') return true;
else return false;
}
/**
* Check for the beginning of a string at this character position. If
* found copy the characters of the string into newBuf, except for the
* terminating quote character. A string may be continued, if so it
* continues after the '&' character on the next line; return the position
* of the trailing '&'. If a string doesn't terminate it's an error,
* return '\n' position.
*/
private int consumeFreeFormString(int i, char buf[], int count, char newBuf[])
{
if (i >= super.n) return i;
char quote_char = buf[i];
if (quote_char != '"' && quote_char != '\'') return i; // not the start of a string
do {
newBuf[count++] = buf[i++];
// look for two quote chars in a row, if found copy both
if (i < super.n - 1 && buf[i] == quote_char && buf[i+1] == quote_char) {
newBuf[count++] = buf[i++];
newBuf[count++] = buf[i++];
}
// look for continuation character as last non-blank character and
// if found, return the '&' position so caller can process continuation
if (buf[i] == '&') {
int ii = i;
while (buf[++ii] == ' ');
if (buf[ii] != '\n') {
// '&' not a continuation, just part of the string, so just copy it
newBuf[count++] = buf[i++];
}
}
}
while (i < super.n && buf[i] != quote_char && buf[i] != '&' && buf[i] != '\n');
return i;
}
/**
* Check for the beginning of a string at this character position.
*/
private boolean matchFixedFormString(int i, char buf[])
{
return matchFreeFormString(i, buf);
}
/**
* Check for the beginning of a string at this character position. If
* found copy the characters of the string into newBuf, except for the
* terminating quote character. If a string doesn't terminate it's an error,
* return '\n' position.
*/
private int consumeFixedFormString(int i, char buf[], int count, char newBuf[])
{
if (i >= super.n) return i;
char quote_char = buf[i];
if (quote_char != '"' && quote_char != '\'') return i; // not the start of a string
do {
newBuf[count++] = buf[i++];
// look for two quote chars in a row, if found copy both
if (i < super.n - 1 && buf[i] == quote_char && buf[i+1] == quote_char) {
newBuf[count++] = buf[i++];
newBuf[count++] = buf[i++];
}
}
while (i < super.n && buf[i] != quote_char && buf[i] != '\n');
return i;
}
private int findCharacter(char ch, int i, char buf[])
{
int i0 = i;
while (i < super.n && buf[i] != ch) i += 1;
if (buf[i] == ch) return i;
else return i0;
}
} // end class FortranStream