001: /*
002: * VerilogFormatter.java
003: *
004: * Copyright (C) 2002 Peter Graves
005: * $Id: VerilogFormatter.java,v 1.1.1.1 2002/09/24 16:08:14 piso Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.j;
023:
024: import java.util.HashSet;
025:
026: public final class VerilogFormatter extends Formatter implements
027: Constants {
028: private static final int VERILOG_FORMAT_TEXT = 0;
029: private static final int VERILOG_FORMAT_COMMENT = 1;
030: private static final int VERILOG_FORMAT_STRING = 2;
031: private static final int VERILOG_FORMAT_IDENTIFIER = 3;
032: private static final int VERILOG_FORMAT_KEYWORD = 4;
033: private static final int VERILOG_FORMAT_COMPILER_DIRECTIVE = 5;
034: private static final int VERILOG_FORMAT_FUNCTION = 6;
035: private static final int VERILOG_FORMAT_OPERATOR = 7;
036: private static final int VERILOG_FORMAT_NUMBER = 8;
037:
038: private static final VerilogMode mode = VerilogMode.getMode();
039:
040: public VerilogFormatter(Buffer buffer) {
041: this .buffer = buffer;
042: }
043:
044: private int tokenBegin = 0;
045:
046: private void endToken(String text, int tokenEnd, int state) {
047: if (tokenEnd - tokenBegin > 0) {
048: int format = VERILOG_FORMAT_TEXT;
049: switch (state) {
050: case STATE_NEUTRAL:
051: format = VERILOG_FORMAT_TEXT;
052: break;
053: case STATE_QUOTE:
054: format = VERILOG_FORMAT_STRING;
055: break;
056: case STATE_IDENTIFIER:
057: format = VERILOG_FORMAT_IDENTIFIER;
058: break;
059: case STATE_COMMENT:
060: format = VERILOG_FORMAT_COMMENT;
061: break;
062: case STATE_OPERATOR:
063: format = VERILOG_FORMAT_OPERATOR;
064: break;
065: case STATE_NUMBER:
066: format = VERILOG_FORMAT_NUMBER;
067: break;
068: }
069: addSegment(text, tokenBegin, tokenEnd, format);
070: tokenBegin = tokenEnd;
071: }
072: }
073:
074: private void parseLine(Line line) {
075: if (line == null) {
076: addSegment("", VERILOG_FORMAT_TEXT);
077: return;
078: }
079: String text;
080: if (Editor.tabsAreVisible())
081: text = Utilities.makeTabsVisible(line.getText(), buffer
082: .getTabWidth());
083: else
084: text = Utilities
085: .detab(line.getText(), buffer.getTabWidth());
086: tokenBegin = 0;
087: int state = line.flags();
088: int i = 0;
089: final int limit = text.length();
090: // Skip whitespace at start of line.
091: while (i < limit) {
092: if (Character.isWhitespace(text.charAt(i))) {
093: ++i;
094: } else {
095: endToken(text, i, state);
096: break;
097: }
098: }
099: while (i < limit) {
100: char c = text.charAt(i);
101: if (state == STATE_COMMENT) {
102: if (i < limit - 1 && c == '*'
103: && text.charAt(i + 1) == '/') {
104: endToken(text, i + 2, state);
105: state = STATE_NEUTRAL;
106: i += 2;
107: } else
108: ++i;
109: continue;
110: }
111: if (state == STATE_QUOTE) {
112: if (c == '"') {
113: endToken(text, i + 1, state);
114: state = STATE_NEUTRAL;
115: } else if (c == '\\' && i < limit - 1) {
116: // Escape char.
117: ++i;
118: }
119: ++i;
120: continue;
121: }
122: // Reaching here, we're not in a comment or a quoted string.
123: if (c == '"') {
124: endToken(text, i, state);
125: state = STATE_QUOTE;
126: ++i;
127: continue;
128: }
129: if (c == '/') {
130: if (i < limit - 1) {
131: if (text.charAt(i + 1) == '*') {
132: endToken(text, i, state);
133: state = STATE_COMMENT;
134: i += 2;
135: } else if (text.charAt(i + 1) == '/') {
136: endToken(text, i, state);
137: endToken(text, limit, STATE_COMMENT);
138: return;
139: } else
140: ++i;
141: } else
142: ++i;
143: continue;
144: }
145: if (isOperatorChar(c)) {
146: if (state != STATE_OPERATOR) {
147: endToken(text, i, state);
148: // Check for keyword.
149: final LineSegment segment = getLastSegment();
150: if (segment != null) {
151: final String segmentText = segment.getText();
152: if (isKeyword(segmentText))
153: segment.setFormat(VERILOG_FORMAT_KEYWORD);
154: else if (isCompilerDirective(segmentText))
155: segment
156: .setFormat(VERILOG_FORMAT_COMPILER_DIRECTIVE);
157: }
158: state = STATE_OPERATOR;
159: }
160: ++i;
161: continue;
162: }
163: if (state == STATE_OPERATOR) {
164: if (c == '\'') {
165: if (i < limit - 1) {
166: c = text.charAt(i + 1);
167: if ("bBoOdDhH".indexOf(c) >= 0) {
168: endToken(text, i, state);
169: state = STATE_NUMBER;
170: i += 2;
171: continue;
172: }
173: }
174: } else if (mode.isIdentifierStart(c)) {
175: endToken(text, i, state);
176: state = STATE_IDENTIFIER;
177: } else if (Character.isDigit(c)) {
178: endToken(text, i, state);
179: state = STATE_NUMBER;
180: } else {
181: endToken(text, i, state);
182: state = STATE_NEUTRAL;
183: }
184: ++i;
185: continue;
186: }
187: if (state == STATE_IDENTIFIER) {
188: if (!mode.isIdentifierPart(c)) {
189: endToken(text, i, state);
190: // Check for keyword or function.
191: final LineSegment segment = getLastSegment();
192: if (segment != null) {
193: final String segmentText = segment.getText();
194: if (isKeyword(segmentText)) {
195: segment.setFormat(VERILOG_FORMAT_KEYWORD);
196: } else if (isCompilerDirective(segmentText)) {
197: segment
198: .setFormat(VERILOG_FORMAT_COMPILER_DIRECTIVE);
199: } else if (c == '(') {
200: segment.setFormat(VERILOG_FORMAT_FUNCTION);
201: } else if (Character.isWhitespace(c)) {
202: // Look ahead to see if next non-whitespace char is '('.
203: int j = i + 1;
204: while (j < limit
205: && Character.isWhitespace(c = text
206: .charAt(j)))
207: ++j;
208: if (c == '(')
209: segment
210: .setFormat(VERILOG_FORMAT_FUNCTION);
211: }
212: }
213: state = STATE_NEUTRAL;
214: }
215: ++i;
216: continue;
217: }
218: if (state == STATE_NUMBER) {
219: if (Character.isDigit(c))
220: ;
221: else if ((c >= 'a' && c <= 'f')
222: || (c >= 'A' && c <= 'F'))
223: ; // Hex digits are OK.
224: else if ("xXzZ?_".indexOf(c) >= 0)
225: ; // Other legal values.
226: else if (c == '\'') {
227: if (i < limit - 1) {
228: c = text.charAt(i + 1);
229: if ("bBoOdDhH".indexOf(c) >= 0) {
230: i += 2;
231: continue;
232: }
233: }
234: } else {
235: endToken(text, i, state);
236: if (mode.isIdentifierStart(c))
237: state = STATE_IDENTIFIER;
238: else
239: state = STATE_NEUTRAL;
240: }
241: ++i;
242: continue;
243: }
244: if (state == STATE_NEUTRAL) {
245: if (c == '\'') {
246: if (i < limit - 1) {
247: c = text.charAt(i + 1);
248: if ("bBoOdDhH".indexOf(c) >= 0) {
249: endToken(text, i, state);
250: state = STATE_NUMBER;
251: i += 2;
252: continue;
253: }
254: }
255: } else if (mode.isIdentifierStart(c)) {
256: endToken(text, i, state);
257: state = STATE_IDENTIFIER;
258: } else if (Character.isDigit(c)) {
259: endToken(text, i, state);
260: state = STATE_NUMBER;
261: }
262: }
263: ++i;
264: }
265: // Reached end of line.
266: endToken(text, i, state);
267: if (state == STATE_IDENTIFIER) {
268: // Last token might be a keyword.
269: final LineSegment segment = getLastSegment();
270: if (segment != null) {
271: final String segmentText = segment.getText();
272: if (isKeyword(segmentText))
273: segment.setFormat(VERILOG_FORMAT_KEYWORD);
274: else if (isCompilerDirective(segmentText))
275: segment
276: .setFormat(VERILOG_FORMAT_COMPILER_DIRECTIVE);
277: }
278: }
279: }
280:
281: public LineSegmentList formatLine(Line line) {
282: clearSegmentList();
283: parseLine(line);
284: return segmentList;
285: }
286:
287: public boolean parseBuffer() {
288: int state = STATE_NEUTRAL;
289: Line line = buffer.getFirstLine();
290: boolean changed = false;
291: while (line != null) {
292: int oldflags = line.flags();
293: // Quoted strings can't span lines.
294: if (state == STATE_QUOTE)
295: state = STATE_NEUTRAL;
296: if (state != oldflags) {
297: line.setFlags(state);
298: changed = true;
299: }
300: final int limit = line.length();
301: for (int i = 0; i < limit; i++) {
302: char c = line.charAt(i);
303: if (c == '\\' && i < limit - 1) {
304: // Escape.
305: ++i;
306: continue;
307: }
308: if (state == STATE_COMMENT) {
309: if (c == '*' && i < limit - 1) {
310: c = line.charAt(i + 1);
311: if (c == '/') {
312: ++i;
313: state = STATE_NEUTRAL;
314: }
315: }
316: continue;
317: }
318: if (state == STATE_QUOTE) {
319: if (c == '"')
320: state = STATE_NEUTRAL;
321: continue;
322: }
323: // Not in comment or quoted string.
324: if (c == '/' && i < limit - 1) {
325: c = line.charAt(++i);
326: if (c == '/') {
327: // Single-line comment beginning.
328: // Ignore rest of line.
329: break;
330: } else if (c == '*')
331: state = STATE_COMMENT;
332: } else if (c == '"')
333: state = STATE_QUOTE;
334: }
335: line = line.next();
336: }
337: buffer.setNeedsParsing(false);
338: return changed;
339: }
340:
341: private static final boolean isOperatorChar(char c) {
342: return "!&|<>=+/*-".indexOf(c) >= 0;
343: }
344:
345: public FormatTable getFormatTable() {
346: if (formatTable == null) {
347: formatTable = new FormatTable("VerilogMode");
348: formatTable.addEntryFromPrefs(VERILOG_FORMAT_TEXT, "text");
349: formatTable.addEntryFromPrefs(VERILOG_FORMAT_COMMENT,
350: "comment");
351: formatTable.addEntryFromPrefs(VERILOG_FORMAT_STRING,
352: "string");
353: formatTable.addEntryFromPrefs(VERILOG_FORMAT_IDENTIFIER,
354: "identifier", "text");
355: formatTable.addEntryFromPrefs(VERILOG_FORMAT_KEYWORD,
356: "keyword");
357: formatTable.addEntryFromPrefs(
358: VERILOG_FORMAT_COMPILER_DIRECTIVE, "preprocessor");
359: formatTable.addEntryFromPrefs(VERILOG_FORMAT_FUNCTION,
360: "function");
361: formatTable.addEntryFromPrefs(VERILOG_FORMAT_OPERATOR,
362: "operator");
363: formatTable.addEntryFromPrefs(VERILOG_FORMAT_NUMBER,
364: "number");
365: }
366: return formatTable;
367: }
368:
369: private static boolean isCompilerDirective(String s) {
370: if (s.length() > 0 && s.charAt(0) == '`')
371: return getCompilerDirectives().contains(s);
372: return false;
373: }
374:
375: private static HashSet compilerDirectiveHashSet;
376:
377: private static HashSet getCompilerDirectives() {
378: if (compilerDirectiveHashSet == null) {
379: String[] array = compilerDirectives;
380: int count = array.length;
381: compilerDirectiveHashSet = new HashSet(Math.max(2 * count,
382: 11));
383: for (int i = count - 1; i >= 0; i--)
384: compilerDirectiveHashSet.add(array[i]);
385: }
386: return compilerDirectiveHashSet;
387: }
388:
389: private static String[] compilerDirectives = {
390: "`autoexpand_vectornets", "`celldefine",
391: "`default_nettype", "`define", "`delay_mode_distributed",
392: "`delay_mode_path", "`delay_mode_unit", "`delay_mode_zero",
393: "`else", "`endcelldefine", "`endif", "`endprotect",
394: "`endprotected", "`expand_vectornets", "`ifdef", "`ifndef",
395: "`include", "`noexpand_vectornets", "`noremove_gatename",
396: "`noremove_netname", "`nounconnected_drive", "`protect",
397: "`protected", "`remove_gatename", "`remove_netname",
398: "`reset_all", "`signed", "`timescale",
399: "`unconnected_drive", "`undef", "`unsigned", "`uselib" };
400: }
|