Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/gjt/sp/jedit/syntax/FortranTokenMarker.java


1   /*
2    * FortranTokenMarker.java - Fortran token marker
3    * by Carl Smotricz
4    * carl@smotricz.com
5    * www.smotricz.com
6    *
7    * This program is free software; you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation; either version 2
10   * of the License, or any later version.
11   *
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Public License for more details.
16   *
17   * You should have received a copy of the GNU General Public License
18   * along with this program; if not, write to the Free Software
19   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20   */
21  
22  package org.gjt.sp.jedit.syntax;
23  
24  import javax.swing.text.Segment;
25  
26  /**
27   * Custom TokenMarker for UNISYS's <cite>ASCII FORTRAN 77</cite>.
28   * Characteristics of this dialect are:<ul>
29   *  <li>Fixed column format, with<ul>
30   *   <li>comment character ( 'C'|'c'|'*' ) in column 1,</li>
31   *   <li>labels (numeric) in column 1-5,</li>
32   *   <li>continuation character ( any nonblank ) in column 6,</li>
33   *   <li>logical end of line after column 72.</li>
34   *   </ul></li>
35   * <li>Nonstandard block comment character ( '@' ) in any column,</li>
36   * <li>Some nonstandard functions: <code>BITS</code>, <code>BOOL</code>,
37   *   <code>INDEX</code>, <code>TRMLEN</code></li>
38   * </ul>
39   * It should be easy enough to adapt this class for minor variations
40   * in the dialect so long as the format is the classic fixed column format.
41   * As this scanner is highly optimized for the fixed column format, it
42   * is probably not readily adaptable for freeform FORTRAN code.
43   */
44   
45  public class FortranTokenMarker extends TokenMarker
46  {
47    // private members
48  
49    private final static int MAYBE_KEYWORD_FIRST = Token.INTERNAL_FIRST;
50    private final static int MAYBE_KEYWORD_MORE = 1 + MAYBE_KEYWORD_FIRST;
51    private final static String S_E_P = "START EDIT PAGE";
52  
53    private static KeywordMap fortranKeywords;
54    private KeywordMap keywords;
55  
56    private int lastOffset;
57  
58    /**
59     * Constructor, with a wee bit of initialization.
60     */
61    public FortranTokenMarker()
62    {
63      this.keywords = getKeywords();
64    }
65  
66    /**
67     * Implementation of code to mark tokens.
68     */
69    public byte markTokensImpl(byte token, Segment line, int lineIndex)
70    {
71      byte lastLineToken = token;
72  
73      // --- Very quick check for empty line
74      if (line.count < 1) return lastLineToken; // EXIT METHOD!
75  
76      char[] array = line.array;
77      int offset = line.offset;
78      char c = array[offset];
79  
80      // --- Very quick check for 'C' comment line
81      if (c == 'C' || c == 'c' || c == '*') 
82      { 
83        addToken(line.count, Token.COMMENT1);
84        return lastLineToken; // EXIT METHOD!
85      }
86      
87      token = Token.NULL; // context usually ends on line boundary
88      int lineEnd = offset + line.count;
89  
90      // --- Check for a label
91      int limit = Math.min(lineEnd, offset + 5);
92      int i;
93      for (i=offset; i<limit; i++) 
94      {
95        c = array[i];
96        if (c == '@') 
97        { 
98          // comment to end of line
99          guardedAddToken(i - offset, token);
100         addToken(lineEnd - i, Token.COMMENT2);
101         return lastLineToken; // EXIT METHOD!
102       } 
103       else if (token == Token.NULL && '0' <= c && c <= '9') 
104       {
105         // numerics: Label.
106         token = Token.LABEL;
107       }
108     }
109     addToken(limit - offset, token);
110 
111     // --- End of line?
112     if (limit == lineEnd) return Token.NULL; // EXIT METHOD!
113       
114     // --- Check for line continuation
115     c = array[i];
116     if (c == '@')
117     {
118       // comment to end of line
119       addToken(lineEnd - i, Token.COMMENT2);
120       return Token.NULL; // EXIT METHOD!
121     } 
122     else if (c == ' ') 
123     {
124       // just a plain old blank
125       addToken(1, Token.NULL);
126       token = Token.NULL;
127     }
128     else
129     {
130       // line continuation: mark it as a label to make it stand out
131       addToken(1, Token.LABEL);
132       token = lastLineToken;
133     }
134 
135     // --- End of line?
136     if (lineEnd == offset + 6) return Token.NULL; // EXIT METHOD!
137 
138     limit = Math.min(offset + 72, lineEnd);
139     lastOffset = offset + 6;
140 
141     // --- Check for "START EDIT PAGE"
142     if (checkStartEditPage(line)) {
143       addToken(limit - lastOffset, Token.LABEL);
144       return Token.NULL; // EXIT METHOD!
145     }
146 
147     // --- 'normal' real, honest coding now
148     int i1;
149     for(i = lastOffset; i < limit; i++)
150     {
151       i1 = i + 1;
152       c = array[i];
153 
154       if (token == Token.LITERAL1)
155       {
156         // ignore anything but the end of literal
157         if (c == '\'')
158         {
159           addToken(i1 - lastOffset, Token.LITERAL1);
160           token = Token.NULL;
161           lastOffset = i1;
162         }
163       } 
164       else if (c == '@')
165       {
166         // comment to end of line
167         guardedAddToken(i - lastOffset, token);
168         addToken(lineEnd - i, Token.COMMENT2);
169         return token; // EXIT METHOD!
170       }
171       else if (token == Token.NULL) 
172       {
173         switch (c)
174         {
175           case '\'':
176             guardedAddToken(i - lastOffset, token);
177             token = Token.LITERAL1;
178             lastOffset = i;
179             break;
180           case '+':
181           case '-':
182           case '*':
183           case '/':
184           case '(':
185           case ')':
186           case ',':
187           case ':':
188           case '=':
189             guardedAddToken(i - lastOffset, token);
190             addToken(1, Token.OPERATOR);
191             lastOffset = i1;
192             break;
193           default:
194             if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))
195             {
196               guardedAddToken(i - lastOffset, token);
197               token = MAYBE_KEYWORD_FIRST;
198               lastOffset = i;
199               break;
200             }
201             else
202             {
203               // unknown special char - maintain state
204             }
205         }
206       }
207       else if (token == MAYBE_KEYWORD_FIRST ||
208                token == MAYBE_KEYWORD_MORE)
209       {
210         if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || 
211             ('0' <= c && c <= '9') || (c == '$'))
212         {
213           token = MAYBE_KEYWORD_MORE;
214         }
215         else
216         {
217           doKeyword(line, i);
218           c = array[i];
219           switch (c)
220           {
221             case '+':
222             case '-':
223             case '*':
224             case '/':
225             case '(':
226             case ')':
227             case ',':
228             case ':':
229             case '=':
230               guardedAddToken(i - lastOffset, token);
231               addToken(1, Token.OPERATOR);
232               lastOffset = i1;
233               break;
234           }
235           token = Token.NULL;
236         }
237       }
238       else 
239       {
240         throw new InternalError("Invalid state: " + token);
241       }  
242     } // end for
243 
244     // --- Finish up the coding part of the line
245     if (token == MAYBE_KEYWORD_FIRST || token == MAYBE_KEYWORD_MORE)
246     {
247       doKeyword(line, i);
248       token = Token.NULL;
249     }
250     else
251     {
252       guardedAddToken(i - lastOffset, token);
253     }
254 
255     // --- End of line?
256     if (limit == lineEnd) return token; // EXIT METHOD!
257 
258     // --- Anything beyond column 72 is comment
259     guardedAddToken(lineEnd - i, Token.COMMENT2);
260     //
261     return token;
262   }
263 
264   private boolean checkStartEditPage(Segment line)
265   {
266     if (line.count < 6+15) return false;
267     int limit = line.offset + Math.min(line.count, 72);
268     int i;
269     for (i=line.offset+6; i<limit-15; i++) if (line.array[i] != ' ') break;
270     if (!SyntaxUtilities.regionMatches(false, line, i, S_E_P)) return false;
271     for (i+=15; i<limit; i++) if (line.array[i] != ' ') return false;
272     return true;
273   }
274 
275   /**
276    * Add the latest token to the current list.
277    * Process 'START' as a special case.
278    */        
279   private void doKeyword(Segment line, int keywordEnd)
280   {
281     int len = keywordEnd - lastOffset;
282     if (len > 0)
283     {
284       byte id = keywords.lookup(line, lastOffset, len);
285       addToken(len, id);
286       lastOffset = keywordEnd;
287     }
288   }
289 
290   /**
291    * Call addToken only if the length of the token is not 0.
292    */
293   private void guardedAddToken(int len, byte token)
294   {
295     if (len > 0) addToken(len, token);
296   }
297   
298   /**
299    * Return the keyword map.
300    * It's lazily initialized on the first call.
301    */
302   public static KeywordMap getKeywords()
303   {
304     if (fortranKeywords == null)
305     {
306       fortranKeywords = new KeywordMap(false);
307       
308       // === Commands ===        
309       fortranKeywords.add("CALL", Token.KEYWORD1);
310       fortranKeywords.add("CLOSE", Token.KEYWORD1);
311       fortranKeywords.add("CONTINUE", Token.KEYWORD1);
312       fortranKeywords.add("DO", Token.KEYWORD1);
313       fortranKeywords.add("ELSE", Token.KEYWORD1);
314       fortranKeywords.add("ELSEIF", Token.KEYWORD1);
315       fortranKeywords.add("ENDIF", Token.KEYWORD1);
316       fortranKeywords.add("GOTO", Token.KEYWORD1);
317       fortranKeywords.add("GO TO", Token.KEYWORD1);
318       fortranKeywords.add("IF", Token.KEYWORD1);
319       fortranKeywords.add("INDEX", Token.KEYWORD1);
320       fortranKeywords.add("INQUIRE", Token.KEYWORD1);
321       fortranKeywords.add("OPEN", Token.KEYWORD1);
322       fortranKeywords.add("PRINT", Token.KEYWORD1);
323       fortranKeywords.add("READ", Token.KEYWORD1);
324       fortranKeywords.add("RETURN", Token.KEYWORD1);
325       fortranKeywords.add("THEN", Token.KEYWORD1);
326       fortranKeywords.add("WRITE", Token.KEYWORD1);
327 
328       // === Compiler directives ===
329       fortranKeywords.add("BLOCK DATA", Token.KEYWORD2);
330       fortranKeywords.add("COMPILER", Token.KEYWORD2);
331       fortranKeywords.add("END", Token.KEYWORD2);
332       fortranKeywords.add("ENTRY", Token.KEYWORD2);
333       fortranKeywords.add("FUNCTION", Token.KEYWORD2);
334       fortranKeywords.add("INCLUDE", Token.KEYWORD2);
335       fortranKeywords.add("SUBROUTINE", Token.KEYWORD2);
336 
337       // === Data types (etc.) ===
338       fortranKeywords.add("CHARACTER", Token.KEYWORD3);
339       fortranKeywords.add("DATA", Token.KEYWORD3);
340       fortranKeywords.add("DEFINE", Token.KEYWORD3);
341       fortranKeywords.add("EQUIVALENCE", Token.KEYWORD3);
342       fortranKeywords.add("IMPLICIT", Token.KEYWORD3);
343       fortranKeywords.add("INTEGER", Token.KEYWORD3);
344       fortranKeywords.add("LOGICAL", Token.KEYWORD3);
345       fortranKeywords.add("PARAMETER", Token.KEYWORD3);
346       fortranKeywords.add("REAL", Token.KEYWORD3);
347 
348       // === Operators ===        
349       fortranKeywords.add(".AND.", Token.OPERATOR);
350       fortranKeywords.add(".EQ.", Token.OPERATOR);
351       fortranKeywords.add(".NE.", Token.OPERATOR);
352       fortranKeywords.add(".NOT.", Token.OPERATOR);
353       fortranKeywords.add(".OR.", Token.OPERATOR);
354       fortranKeywords.add("+", Token.OPERATOR);
355       fortranKeywords.add("-", Token.OPERATOR);
356       fortranKeywords.add("*", Token.OPERATOR);
357       fortranKeywords.add("**", Token.OPERATOR);
358       fortranKeywords.add("/", Token.OPERATOR);
359 
360       // === Literals ===
361       fortranKeywords.add(".FALSE.", Token.LITERAL2);
362       fortranKeywords.add(".TRUE.", Token.LITERAL2);
363 
364     }
365     return fortranKeywords;
366   }
367 }
368 
369 // End of FortranTokenMarker.java