001: /*
002: * ParserRule.java - Parser rule for the token marker
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 1999 mike dillon
007: * Portions copyright (C) 2002 Slava Pestov
008: *
009: * This program is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU General Public License
011: * as published by the Free Software Foundation; either version 2
012: * of the License, or any later version.
013: *
014: * This program is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
017: * GNU General Public License for more details.
018: *
019: * You should have received a copy of the GNU General Public License
020: * along with this program; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
022: */
023:
024: package org.gjt.sp.jedit.syntax;
025:
026: import java.util.Arrays;
027: import java.util.HashSet;
028: import java.util.Set;
029: import java.util.regex.Pattern;
030: import java.util.regex.PatternSyntaxException;
031:
032: /**
033: * A parser rule.
034: * @author mike dillon, Slava Pestov
035: * @version $Id: ParserRule.java 11079 2007-11-16 03:20:00Z vanza $
036: */
037: public class ParserRule {
038:
039: //{{{ Major actions
040: public static final int MAJOR_ACTIONS = 0x000000FF;
041: public static final int SEQ = 0;
042: public static final int SPAN = 1 << 1;
043: public static final int MARK_PREVIOUS = 1 << 2;
044: public static final int MARK_FOLLOWING = 1 << 3;
045: public static final int EOL_SPAN = 1 << 4;
046: //}}}
047:
048: //{{{ Action hints
049: public static final int ACTION_HINTS = 0x0000FF00;
050:
051: @Deprecated
052: public static final int EXCLUDE_MATCH = 1 << 8;
053:
054: public static final int NO_LINE_BREAK = 1 << 9;
055: public static final int NO_WORD_BREAK = 1 << 10;
056: public static final int IS_ESCAPE = 1 << 11;
057:
058: @Deprecated
059: public static final int NO_ESCAPE = 1 << 12;
060:
061: public static final int REGEXP = 1 << 13;
062: //}}}
063:
064: //{{{ Special Match Token Types
065: public static final byte MATCH_TYPE_CONTEXT = -1;
066: public static final byte MATCH_TYPE_RULE = -2;
067: //}}}
068:
069: //{{{ Position match hints
070: public static final int AT_LINE_START = 1 << 1;
071: public static final int AT_WHITESPACE_END = 1 << 2;
072: public static final int AT_WORD_START = 1 << 3;
073: //}}}
074:
075: //{{{ Instance variables
076: public final String upHashChar;
077: public final char[] upHashChars;
078: public final int startPosMatch;
079: public final char[] start;
080: public final Pattern startRegexp;
081:
082: public final int endPosMatch;
083: public final char[] end;
084:
085: public final int action;
086: public final byte token;
087:
088: /**
089: * matchType is the type of the token for the matched region. Special
090: * values are: MATCH_TYPE_CONTEXT = default token for the context,
091: * MATCH_TYPE_RULE = same token as the rule itself.
092: *
093: * @since jEdit 4.3pre10
094: */
095: public final byte matchType;
096:
097: /**
098: * escapeRule is the rule-specific sequence used to escape other
099: * characters while the rule is in effect. If this character is
100: * non-zero, the character following the escape char will be skipped
101: * during parsing, and highlighted with the rule's token.
102: *
103: * @since jEdit 4.3pre12
104: */
105: public final ParserRule escapeRule;
106:
107: public ParserRuleSet delegate;
108:
109: /**
110: * @deprecated As the linking between rules is not anymore done within the rule but external. See {@link ParserRuleSet#getRules(Character)}
111: */
112: public ParserRule next;
113:
114: //}}}
115:
116: //{{{ createSequenceRule() method
117: public static final ParserRule createSequenceRule(int posMatch,
118: String seq, ParserRuleSet delegate, byte id) {
119: return new ParserRule(SEQ, seq.substring(0, 1), posMatch, seq
120: .toCharArray(), null, 0, null, delegate, id,
121: MATCH_TYPE_CONTEXT, null);
122: } //}}}
123:
124: //{{{ createRegexpSequenceRule() method
125: /**
126: * @deprecated Use {@link #createRegexpSequenceRule(String,int,String,ParserRuleSet,byte,boolean)} instead
127: */
128: public static final ParserRule createRegexpSequenceRule(
129: char hashChar, int posMatch, String seq,
130: ParserRuleSet delegate, byte id, boolean ignoreCase)
131: throws PatternSyntaxException {
132: return createRegexpSequenceRule(String.valueOf(hashChar),
133: posMatch, seq, delegate, id, ignoreCase);
134: } //}}}
135:
136: //{{{ createRegexpSequenceRule() method
137: public static final ParserRule createRegexpSequenceRule(
138: String hashChar, int posMatch, String seq,
139: ParserRuleSet delegate, byte id, boolean ignoreCase)
140: throws PatternSyntaxException {
141: return new ParserRule(SEQ | REGEXP, hashChar, posMatch, null,
142: Pattern.compile(seq,
143: (ignoreCase ? Pattern.CASE_INSENSITIVE : 0)),
144: 0, null, delegate, id, MATCH_TYPE_CONTEXT, null);
145: } //}}}
146:
147: //{{{ createRegexpSequenceRule() method
148: public static final ParserRule createRegexpSequenceRule(
149: int posMatch, char[] hashChars, String seq,
150: ParserRuleSet delegate, byte id, boolean ignoreCase)
151: throws PatternSyntaxException {
152: return new ParserRule(hashChars, SEQ | REGEXP, posMatch, null,
153: Pattern.compile(seq,
154: (ignoreCase ? Pattern.CASE_INSENSITIVE : 0)),
155: 0, null, delegate, id, MATCH_TYPE_CONTEXT, null);
156: } //}}}
157:
158: //{{{ createSpanRule() method
159: public static final ParserRule createSpanRule(int startPosMatch,
160: String start, int endPosMatch, String end,
161: ParserRuleSet delegate, byte id, byte matchType,
162: boolean noLineBreak, boolean noWordBreak, String escape) {
163: int ruleAction = SPAN | ((noLineBreak) ? NO_LINE_BREAK : 0)
164: | ((noWordBreak) ? NO_WORD_BREAK : 0);
165:
166: return new ParserRule(ruleAction, start.substring(0, 1),
167: startPosMatch, start.toCharArray(), null, endPosMatch,
168: end.toCharArray(), delegate, id, matchType, escape);
169: } //}}}
170:
171: //{{{ createRegexpSpanRule() method
172: public static final ParserRule createRegexpSpanRule(
173: String hashChar, int startPosMatch, String start,
174: int endPosMatch, String end, ParserRuleSet delegate,
175: byte id, byte matchType, boolean noLineBreak,
176: boolean noWordBreak, boolean ignoreCase, String escape)
177: throws PatternSyntaxException {
178: int ruleAction = SPAN | REGEXP
179: | ((noLineBreak) ? NO_LINE_BREAK : 0)
180: | ((noWordBreak) ? NO_WORD_BREAK : 0);
181:
182: return new ParserRule(ruleAction, hashChar, startPosMatch,
183: null, Pattern.compile(start,
184: (ignoreCase ? Pattern.CASE_INSENSITIVE : 0)),
185: endPosMatch, end.toCharArray(), delegate, id,
186: matchType, escape);
187: } //}}}
188:
189: //{{{ createRegexpSpanRule() method
190: public static final ParserRule createRegexpSpanRule(
191: int startPosMatch, char[] hashChars, String start,
192: int endPosMatch, String end, ParserRuleSet delegate,
193: byte id, byte matchType, boolean noLineBreak,
194: boolean noWordBreak, boolean ignoreCase, String escape)
195: throws PatternSyntaxException {
196: int ruleAction = SPAN | REGEXP
197: | ((noLineBreak) ? NO_LINE_BREAK : 0)
198: | ((noWordBreak) ? NO_WORD_BREAK : 0);
199:
200: return new ParserRule(hashChars, ruleAction, startPosMatch,
201: null, Pattern.compile(start,
202: (ignoreCase ? Pattern.CASE_INSENSITIVE : 0)),
203: endPosMatch, end.toCharArray(), delegate, id,
204: matchType, escape);
205: } //}}}
206:
207: //{{{ createEOLSpanRule() method
208: public static final ParserRule createEOLSpanRule(int posMatch,
209: String seq, ParserRuleSet delegate, byte id, byte matchType) {
210: int ruleAction = EOL_SPAN | NO_LINE_BREAK;
211:
212: return new ParserRule(ruleAction, seq.substring(0, 1),
213: posMatch, seq.toCharArray(), null, 0, null, delegate,
214: id, matchType, null);
215: } //}}}
216:
217: //{{{ createRegexpEOLSpanRule() method
218: /**
219: * @deprecated Use {@link #createRegexpEOLSpanRule(String,int,String,ParserRuleSet,byte,byte,boolean)} instead
220: */
221: public static final ParserRule createRegexpEOLSpanRule(
222: char hashChar, int posMatch, String seq,
223: ParserRuleSet delegate, byte id, byte matchType,
224: boolean ignoreCase) throws PatternSyntaxException {
225: return createRegexpEOLSpanRule(String.valueOf(hashChar),
226: posMatch, seq, delegate, id, matchType, ignoreCase);
227: } //}}}
228:
229: //{{{ createRegexpEOLSpanRule() method
230: public static final ParserRule createRegexpEOLSpanRule(
231: String hashChar, int posMatch, String seq,
232: ParserRuleSet delegate, byte id, byte matchType,
233: boolean ignoreCase) throws PatternSyntaxException {
234: int ruleAction = EOL_SPAN | REGEXP | NO_LINE_BREAK;
235:
236: return new ParserRule(ruleAction, hashChar, posMatch, null,
237: Pattern.compile(seq,
238: (ignoreCase ? Pattern.CASE_INSENSITIVE : 0)),
239: 0, null, delegate, id, matchType, null);
240: } //}}}
241:
242: //{{{ createRegexpEOLSpanRule() method
243: public static final ParserRule createRegexpEOLSpanRule(
244: int posMatch, char[] hashChars, String seq,
245: ParserRuleSet delegate, byte id, byte matchType,
246: boolean ignoreCase) throws PatternSyntaxException {
247: int ruleAction = EOL_SPAN | REGEXP | NO_LINE_BREAK;
248:
249: return new ParserRule(hashChars, ruleAction, posMatch, null,
250: Pattern.compile(seq,
251: (ignoreCase ? Pattern.CASE_INSENSITIVE : 0)),
252: 0, null, delegate, id, matchType, null);
253: } //}}}
254:
255: //{{{ createMarkFollowingRule() method
256: public static final ParserRule createMarkFollowingRule(
257: int posMatch, String seq, byte id, byte matchType) {
258: int ruleAction = MARK_FOLLOWING;
259:
260: return new ParserRule(ruleAction, seq.substring(0, 1),
261: posMatch, seq.toCharArray(), null, 0, null, null, id,
262: matchType, null);
263: } //}}}
264:
265: //{{{ createMarkPreviousRule() method
266: public static final ParserRule createMarkPreviousRule(int posMatch,
267: String seq, byte id, byte matchType) {
268: int ruleAction = MARK_PREVIOUS;
269:
270: return new ParserRule(ruleAction, seq.substring(0, 1),
271: posMatch, seq.toCharArray(), null, 0, null, null, id,
272: matchType, null);
273: } //}}}
274:
275: //{{{ createEscapeRule() method
276: public static final ParserRule createEscapeRule(String seq) {
277: int ruleAction = IS_ESCAPE;
278:
279: return new ParserRule(ruleAction, seq.substring(0, 1), 0, seq
280: .toCharArray(), null, 0, null, null, Token.NULL,
281: MATCH_TYPE_CONTEXT, null);
282: } //}}}
283:
284: //{{{ toString() method
285: public String toString() {
286: StringBuffer result = new StringBuffer();
287: result.append(getClass().getName()).append("[action=");
288: switch (action & MAJOR_ACTIONS) {
289: case SEQ:
290: result.append("SEQ");
291: break;
292: case SPAN:
293: result.append("SPAN");
294: break;
295: case MARK_PREVIOUS:
296: result.append("MARK_PREVIOUS");
297: break;
298: case MARK_FOLLOWING:
299: result.append("MARK_FOLLOWING");
300: break;
301: case EOL_SPAN:
302: result.append("EOL_SPAN");
303: break;
304: default:
305: result.append("UNKNOWN");
306: break;
307: }
308: int actionHints = action & ACTION_HINTS;
309: result
310: .append("[matchType=")
311: .append(
312: matchType == MATCH_TYPE_CONTEXT ? "MATCH_TYPE_CONTEXT"
313: : (matchType == MATCH_TYPE_RULE ? "MATCH_TYPE_RULE"
314: : Token
315: .tokenToString(matchType)));
316: result.append(",NO_LINE_BREAK=").append(
317: (actionHints & NO_LINE_BREAK) != 0);
318: result.append(",NO_WORD_BREAK=").append(
319: (actionHints & NO_WORD_BREAK) != 0);
320: result.append(",IS_ESCAPE=").append(
321: (actionHints & IS_ESCAPE) != 0);
322: result.append(",REGEXP=").append((actionHints & REGEXP) != 0);
323: result.append("],upHashChar=").append(upHashChar);
324: result.append(",upHashChars=").append(
325: Arrays.toString(upHashChars));
326: result.append(",startPosMatch=");
327: result.append("[AT_LINE_START=").append(
328: (startPosMatch & AT_LINE_START) != 0);
329: result.append(",AT_WHITESPACE_END=").append(
330: (startPosMatch & AT_WHITESPACE_END) != 0);
331: result.append(",AT_WORD_START=").append(
332: (startPosMatch & AT_WORD_START) != 0);
333: result.append("],start=").append(
334: null == start ? null : String.valueOf(start));
335: result.append(",startRegexp=").append(startRegexp);
336: result.append(",endPosMatch=");
337: result.append("[AT_LINE_START=").append(
338: (endPosMatch & AT_LINE_START) != 0);
339: result.append(",AT_WHITESPACE_END=").append(
340: (endPosMatch & AT_WHITESPACE_END) != 0);
341: result.append(",AT_WORD_START=").append(
342: (endPosMatch & AT_WORD_START) != 0);
343: result.append("],end=").append(
344: null == end ? null : String.valueOf(end));
345: result.append(",delegate=").append(delegate);
346: result.append(",escapeRule=").append(escapeRule);
347: result.append(",token=").append(Token.tokenToString(token))
348: .append(']');
349: return result.toString();
350: } //}}}
351:
352: //{{{ Private members
353: private ParserRule(int action, String hashChar, int startPosMatch,
354: char[] start, Pattern startRegexp, int endPosMatch,
355: char[] end, ParserRuleSet delegate, byte token,
356: byte matchType, String escape) {
357: this .action = action;
358: this .upHashChar = null == hashChar ? null : hashChar
359: .toUpperCase();
360: this .upHashChars = null;
361: this .startPosMatch = startPosMatch;
362: this .start = start;
363: this .startRegexp = startRegexp;
364: this .endPosMatch = endPosMatch;
365: this .end = end;
366: this .delegate = delegate;
367: this .token = token;
368: this .matchType = matchType;
369: this .escapeRule = (escape != null && escape.length() > 0) ? createEscapeRule(escape)
370: : null;
371:
372: if (this .delegate == null) {
373: if ((action & MAJOR_ACTIONS) != SEQ) {
374: this .delegate = ParserRuleSet.getStandardRuleSet(token);
375: }
376: }
377: }
378:
379: private ParserRule(char[] hashChars, int action, int startPosMatch,
380: char[] start, Pattern startRegexp, int endPosMatch,
381: char[] end, ParserRuleSet delegate, byte token,
382: byte matchType, String escape) {
383: this .action = action;
384: this .upHashChar = null;
385: Set<Character> hashCharsSet = new HashSet<Character>();
386: for (char c : hashChars) {
387: hashCharsSet.add(Character.toUpperCase(c));
388: }
389: this .upHashChars = new char[hashCharsSet.size()];
390: int i = 0;
391: for (Character c : hashCharsSet) {
392: this .upHashChars[i++] = c;
393: }
394: Arrays.sort(this .upHashChars);
395: this .startPosMatch = startPosMatch;
396: this .start = start;
397: this .startRegexp = startRegexp;
398: this .endPosMatch = endPosMatch;
399: this .end = end;
400: this .delegate = delegate;
401: this .token = token;
402: this .matchType = matchType;
403: this .escapeRule = (escape != null && escape.length() > 0) ? createEscapeRule(escape)
404: : null;
405:
406: if (this .delegate == null) {
407: if ((action & MAJOR_ACTIONS) != SEQ) {
408: this .delegate = ParserRuleSet.getStandardRuleSet(token);
409: }
410: }
411: } //}}}
412: }
|