001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.languages;
043:
044: import java.beans.PropertyChangeListener;
045: import java.util.ArrayList;
046: import java.util.Arrays;
047: import java.util.Collections;
048: import java.util.HashMap;
049: import java.util.HashSet;
050: import java.util.Iterator;
051: import java.util.List;
052: import java.util.Map;
053: import java.util.Set;
054:
055: import org.netbeans.api.languages.ASTToken;
056: import org.netbeans.api.languages.ParseException;
057: import org.netbeans.modules.languages.parser.LLSyntaxAnalyser;
058: import org.netbeans.modules.languages.parser.Parser;
059: import org.netbeans.modules.languages.parser.Pattern;
060:
061: /**
062: *
063: * @author Jan Jancura
064: */
065: public class NBSLanguage extends Language {
066:
067: static final String NBS_MIME_TYPE = "text/x-nbs";
068:
069: public static int WHITESPACE_ID;
070: public static int COMMENT_ID;
071: public static int IDENTIFIER_ID;
072:
073: private static Language nbsLanguage;
074:
075: static Language getNBSLanguage() {
076: if (nbsLanguage == null)
077: nbsLanguage = new NBSLanguage();
078: return nbsLanguage;
079: }
080:
081: private static Rule rule(String nt, Object[] right) {
082: return Rule.create(nt, Arrays.asList(right));
083: }
084:
085: private Parser parser;
086: private LLSyntaxAnalyser analyser;
087:
088: /** Creates a new instance of Language */
089: private NBSLanguage() {
090:
091: // 1) init tokens ......................................................
092:
093: List<TokenType> tokenTypes = new ArrayList<TokenType>();
094: try {
095: tokenTypes
096: .add(new TokenType(null, Pattern
097: .create("'ACTION' |" + "'AST' |"
098: + "'BRACE' |" + "'BUNDLE' |"
099: + "'COLOR' |" + "'COMMENT_LINE' |"
100: + "'COMPLETE' |" + "'COMPLETION' |"
101: + "'FOLD' |" + "'FORMAT' |"
102: + "'HYPERLINK' |" + "'IMPORT' |"
103: + "'INDENT' |" + "'MARK' | "
104: + "'NAVIGATOR' |" + "'PARSE' |"
105: + "'PROPERTIES' |" + "'REFORMAT' |"
106: + "'SELECTION' | "
107: + "'SEMANTIC_CONTEXT' | "
108: + "'SEMANTIC_DECLARATION' | "
109: + "'SEMANTIC_USAGE' | "
110: + "'SKIP' |" + "'SYNTAX_ERROR' |"
111: + "'TOKEN' |" + "'TOOLTIP'"),
112: "keyword", 1, null, 1, null));
113: tokenTypes
114: .add(new TokenType(
115: null,
116: Pattern
117: .create("['a'-'z' 'A'-'Z'] ['a'-'z' 'A'-'Z' '0'-'9' '_']*"),
118: "identifier", 2, null, 2, null));
119: tokenTypes
120: .add(new TokenType(
121: null,
122: Pattern
123: .create("':' | '*' | '?' | '+' | '-' | '[' | ']' | '<' | "
124: + "'>' | '^' | '|' | '{' | '}' | '(' | ')' | ',' | "
125: + "'=' | ';' | '.' | '$'"),
126: "operator", 3, null, 3, null));
127: tokenTypes
128: .add(new TokenType(
129: null,
130: Pattern
131: .create("'\\\"'"
132: + "("
133: + "[^'\\\"' '\\\\' '\\r' '\\n'] |"
134: + "('\\\\' ['r' 'n' 't' '\\\\' '\\\'' '\\\"']) |"
135: + "('\\\\' 'u' ['0'-'9' 'a'-'f' 'A'-'F'] ['0'-'9' 'a'-'f' 'A'-'F'] ['0'-'9' 'a'-'f' 'A'-'F'] ['0'-'9' 'a'-'f' 'A'-'F'])"
136: + ")*" + "'\\\"'"),
137: "string", 4, null, 4, null));
138: tokenTypes
139: .add(new TokenType(
140: null,
141: Pattern
142: .create("'\\\''"
143: + "("
144: + "[^'\\\'' '\\\\' '\\r' '\\n'] |"
145: + "('\\\\' ['r' 'n' 't' '\\\\' '\\\'' '\\\"']) |"
146: + "('\\\\' 'u' ['0'-'9' 'a'-'f' 'A'-'F'] ['0'-'9' 'a'-'f' 'A'-'F'] ['0'-'9' 'a'-'f' 'A'-'F'] ['0'-'9' 'a'-'f' 'A'-'F'])"
147: + ")*" + "'\\\''"),
148: "string", 4, null, 4, null));
149: tokenTypes.add(new TokenType(null, Pattern
150: .create("'#' [^'\\n' '\\r']* ['\\n' '\\r']+"),
151: "comment", 5, null, 5, null));
152: tokenTypes
153: .add(new TokenType(null, Pattern
154: .create("'/#' - '#/'"), "comment", 5, null,
155: 5, null));
156: tokenTypes.add(new TokenType(null, Pattern
157: .create("['\\n' '\\r' ' ' '\\t']+"), "whitespace",
158: 6, null, 6, null));
159: } catch (ParseException ex) {
160: Utils.notify(ex);
161: }
162: tokenTypeToID.put("error", 0);
163: idToTokenType.put(0, "error");
164: tokenTypeCount = 1;
165: Iterator<TokenType> it = tokenTypes.iterator();
166: while (it.hasNext()) {
167: TokenType tokenType = it.next();
168: tokenTypeToID.put(tokenType.getType(), tokenType
169: .getTypeID());
170: idToTokenType.put(tokenType.getTypeID(), tokenType
171: .getType());
172: tokenTypeCount = Math.max(tokenTypeCount, tokenType
173: .getTypeID() + 1);
174: }
175: parser = Parser.create(tokenTypes);
176:
177: // 2) init grammar .....................................................
178:
179: int OPERATOR_ID = getTokenID("operator");
180: ASTToken COLON = ASTToken.create(this , OPERATOR_ID, ":", 0);
181: ASTToken PARENTHESIS = ASTToken.create(this , OPERATOR_ID, "(",
182: 0);
183: ASTToken PARENTHESIS2 = ASTToken.create(this , OPERATOR_ID, ")",
184: 0);
185: ASTToken BRACE = ASTToken.create(this , OPERATOR_ID, "{", 0);
186: ASTToken BRACE2 = ASTToken.create(this , OPERATOR_ID, "}", 0);
187: ASTToken LT = ASTToken.create(this , OPERATOR_ID, "<", 0);
188: ASTToken GT = ASTToken.create(this , OPERATOR_ID, ">", 0);
189: ASTToken DOT = ASTToken.create(this , OPERATOR_ID, ".", 0);
190: ASTToken PLUS = ASTToken.create(this , OPERATOR_ID, "+", 0);
191: ASTToken QUESTION = ASTToken.create(this , OPERATOR_ID, "?", 0);
192: ASTToken MULTIPLY = ASTToken.create(this , OPERATOR_ID, "*", 0);
193: ASTToken OR = ASTToken.create(this , OPERATOR_ID, "|", 0);
194: ASTToken MINUS = ASTToken.create(this , OPERATOR_ID, "-", 0);
195: ASTToken BRACKET = ASTToken.create(this , OPERATOR_ID, "[", 0);
196: ASTToken BRACKET2 = ASTToken.create(this , OPERATOR_ID, "]", 0);
197: ASTToken UPP = ASTToken.create(this , OPERATOR_ID, "^", 0);
198: ASTToken EQUAL = ASTToken.create(this , OPERATOR_ID, "=", 0);
199: ASTToken SEMICOLON = ASTToken.create(this , OPERATOR_ID, ";", 0);
200: ASTToken COMMA = ASTToken.create(this , OPERATOR_ID, ",", 0);
201: int KEYWORD_ID = getTokenID("keyword");
202: ASTToken KEYWORD = ASTToken.create(this , KEYWORD_ID, null, 0);
203: ASTToken KEYWORD_TOKEN = ASTToken.create(this , KEYWORD_ID,
204: "TOKEN", 0);
205: IDENTIFIER_ID = getTokenID("identifier");
206: ASTToken IDENTIFIER = ASTToken.create(this , IDENTIFIER_ID,
207: null, 0);
208: ASTToken IDENTIFIER_I = ASTToken.create(this , IDENTIFIER_ID,
209: "i", 0);
210: int STRING_ID = getTokenID("string");
211: ASTToken STRING = ASTToken.create(this , STRING_ID, null, 0);
212: WHITESPACE_ID = getTokenID("whitespace");
213: COMMENT_ID = getTokenID("comment");
214:
215: List<Rule> rules = new ArrayList<Rule>();
216: rules.add(rule("S", new Object[] { "token", "S" }));
217: rules.add(rule("S", new Object[] { "tokenState", "S" }));
218: rules.add(rule("S", new Object[] { "grammarRule", "S" }));
219: rules.add(rule("S", new Object[] { "command", "S" }));
220: rules.add(rule("S", new Object[] {}));
221:
222: rules.add(rule("tokenState", new Object[] { "state",
223: "tokenState1" }));
224: rules.add(rule("tokenState1", new Object[] { COLON, "token" }));
225: rules.add(rule("tokenState1", new Object[] { BRACE,
226: "tokenGroup" }));
227: rules.add(rule("token", new Object[] { KEYWORD_TOKEN, COLON,
228: IDENTIFIER, COLON, "token2" }));
229: rules.add(rule("token2", new Object[] { PARENTHESIS,
230: "regularExpression", PARENTHESIS2, "token3" }));
231: rules.add(rule("token2", new Object[] { BRACE, "properties",
232: BRACE2 }));
233: rules.add(rule("token3", new Object[] { COLON, "state" }));
234: rules.add(rule("token3", new Object[] {}));
235: rules.add(rule("state", new Object[] { LT, IDENTIFIER, GT }));
236: rules.add(rule("tokenGroup", new Object[] { "tokensInGroup",
237: BRACE2 }));
238: rules.add(rule("tokensInGroup", new Object[] { "token",
239: "tokensInGroup" }));
240: rules.add(rule("tokensInGroup", new Object[] {}));
241:
242: rules.add(rule("regularExpression", new Object[] { "reChoice",
243: "regularExpression1" }));
244: rules.add(rule("regularExpression1", new Object[] { OR,
245: "reChoice", "regularExpression1" }));
246: rules.add(rule("regularExpression1", new Object[] {}));
247: rules.add(rule("reChoice",
248: new Object[] { "rePart", "reChoice1" }));
249: rules.add(rule("reChoice1", new Object[] { "rePart",
250: "reChoice1" }));
251: rules.add(rule("reChoice1", new Object[] {}));
252: rules.add(rule("rePart", new Object[] { STRING,
253: "rePartOperatorOrMinus" }));
254: rules.add(rule("rePart", new Object[] { STRING, IDENTIFIER_I,
255: "rePartOperatorOrMinus" }));
256: rules
257: .add(rule("rePart", new Object[] { DOT,
258: "rePartOperator" }));
259: rules.add(rule("rePart", new Object[] { "reClass",
260: "rePartOperator" }));
261: rules.add(rule("rePart", new Object[] { PARENTHESIS,
262: "regularExpression", PARENTHESIS2, "rePartOperator" }));
263: rules.add(rule("rePartOperator", new Object[] {}));
264: rules.add(rule("rePartOperator", new Object[] { PLUS }));
265: rules.add(rule("rePartOperator", new Object[] { QUESTION }));
266: rules.add(rule("rePartOperator", new Object[] { MULTIPLY }));
267: rules.add(rule("rePartOperatorOrMinus", new Object[] { MINUS,
268: STRING }));
269: rules.add(rule("rePartOperatorOrMinus",
270: new Object[] { "rePartOperator" }));
271: rules.add(rule("reClass", new Object[] { BRACKET,
272: "reInClassNegation", "reInClass", BRACKET2 }));
273: rules.add(rule("reInClassNegation", new Object[] { UPP }));
274: rules.add(rule("reInClassNegation", new Object[] {}));
275: rules.add(rule("reInClass", new Object[] { STRING,
276: "reInClassMinus", "reInClass1" }));
277: rules.add(rule("reInClass1", new Object[] { STRING,
278: "reInClassMinus", "reInClass1" }));
279: rules.add(rule("reInClass1", new Object[] {}));
280: rules
281: .add(rule("reInClassMinus", new Object[] { MINUS,
282: STRING }));
283: rules.add(rule("reInClassMinus", new Object[] {}));
284:
285: rules.add(rule("grammarRule", new Object[] { IDENTIFIER, EQUAL,
286: "grRightSide", SEMICOLON }));
287: rules.add(rule("grRightSide", new Object[] { "grChoice",
288: "grRightSide1" }));
289: rules.add(rule("grRightSide1", new Object[] { OR, "grChoice",
290: "grRightSide1" }));
291: rules.add(rule("grRightSide1", new Object[] {}));
292: rules.add(rule("grChoice",
293: new Object[] { "grPart", "grChoice" }));
294: rules.add(rule("grChoice", new Object[] {}));
295: rules.add(rule("grPart", new Object[] { IDENTIFIER,
296: "grOperator" }));
297: rules.add(rule("grPart", new Object[] { "tokenDef",
298: "grOperator" }));
299: rules
300: .add(rule("grPart",
301: new Object[] { STRING, "grOperator" }));
302: rules.add(rule("grPart", new Object[] { BRACKET, "grRightSide",
303: BRACKET2 }));
304: rules.add(rule("grPart", new Object[] { PARENTHESIS,
305: "grRightSide", PARENTHESIS2, "grOperator" }));
306: rules.add(rule("grOperator", new Object[] { PLUS }));
307: rules.add(rule("grOperator", new Object[] { MULTIPLY }));
308: rules.add(rule("grOperator", new Object[] { QUESTION }));
309: rules.add(rule("grOperator", new Object[] {}));
310: rules.add(rule("tokenDef", new Object[] { LT, IDENTIFIER,
311: "tokenDef1", GT }));
312: rules.add(rule("tokenDef1", new Object[] { COMMA, STRING }));
313: rules.add(rule("tokenDef1", new Object[] {}));
314:
315: rules
316: .add(rule("command",
317: new Object[] { KEYWORD, "command0" }));
318: rules.add(rule("command0", new Object[] { COLON, "selector",
319: "command1" }));
320: rules.add(rule("command0", new Object[] { "value" }));
321: rules.add(rule("command1", new Object[] { COLON, "value" }));
322: rules.add(rule("command1", new Object[] {}));
323: rules.add(rule("value", new Object[] { "class" }));
324: rules.add(rule("value", new Object[] { STRING }));
325: rules.add(rule("value", new Object[] { BRACE, "properties",
326: BRACE2 }));
327: rules.add(rule("value", new Object[] { PARENTHESIS,
328: "regularExpression", PARENTHESIS2 }));
329: rules.add(rule("properties", new Object[] { "property",
330: "properties" }));
331: rules.add(rule("properties", new Object[] {}));
332: rules.add(rule("property", new Object[] { IDENTIFIER, COLON,
333: "propertyValue", SEMICOLON }));
334: rules.add(rule("propertyValue", new Object[] { STRING }));
335: rules.add(rule("propertyValue", new Object[] { "class" }));
336: rules.add(rule("propertyValue", new Object[] { PARENTHESIS,
337: "regularExpression", PARENTHESIS2 }));
338: rules.add(rule("selector",
339: new Object[] { "class", "selector1" }));
340: rules.add(rule("selector1", new Object[] { COMMA, "class",
341: "selector1" }));
342: rules.add(rule("selector1", new Object[] {}));
343: rules.add(rule("class", new Object[] { IDENTIFIER, "class1" }));
344: rules.add(rule("class1", new Object[] { DOT, IDENTIFIER,
345: "class1" }));
346: rules.add(rule("class1", new Object[] {}));
347:
348: Set<Integer> skipTokenIDs = new HashSet<Integer>();
349: skipTokenIDs.add(getTokenID("whitespace"));
350: skipTokenIDs.add(getTokenID("comment"));
351:
352: try {
353: analyser = LLSyntaxAnalyser.create(this , rules,
354: skipTokenIDs);
355: } catch (ParseException ex) {
356: Utils.notify(ex);
357: }
358: }
359:
360: public String getMimeType() {
361: return NBS_MIME_TYPE;
362: }
363:
364: public Parser getParser() {
365: return parser;
366: }
367:
368: public LLSyntaxAnalyser getAnalyser() {
369: return analyser;
370: }
371:
372: private FeatureList featureList = new FeatureList();
373:
374: public FeatureList getFeatureList() {
375: return featureList;
376: }
377:
378: public void addPropertyChangeListener(PropertyChangeListener l) {
379: }
380:
381: public void removePropertyChangeListener(PropertyChangeListener l) {
382: }
383:
384: // ids ...
385:
386: private Map<String, Integer> tokenTypeToID = new HashMap<String, Integer>();
387: private Map<Integer, String> idToTokenType = new HashMap<Integer, String>();
388: private int tokenTypeCount = 0;
389:
390: public int getTokenID(String tokenType) {
391: if (!tokenTypeToID.containsKey(tokenType))
392: System.err.println("unknown token type: " + tokenType);
393: return tokenTypeToID.get(tokenType);
394: }
395:
396: public int getTokenTypeCount() {
397: return tokenTypeCount;
398: }
399:
400: public String getTokenType(int tokenTypeID) {
401: return idToTokenType.get(tokenTypeID);
402: }
403:
404: private Map<String, Integer> ntToNTID;
405: private Map<Integer, String> ntidToNt;
406:
407: public int getNTID(String nt) {
408: if (ntidToNt == null)
409: ntidToNt = new HashMap<Integer, String>();
410: if (ntToNTID == null)
411: ntToNTID = new HashMap<String, Integer>();
412: if (!ntToNTID.containsKey(nt)) {
413: int id = ntToNTID.size();
414: ntToNTID.put(nt, id);
415: ntidToNt.put(id, nt);
416: }
417: return ntToNTID.get(nt);
418: }
419:
420: public int getNTCount() {
421: if (ntToNTID == null)
422: return 0;
423: return ntToNTID.size();
424: }
425:
426: public String getNT(int ntid) {
427: return ntidToNt.get(ntid);
428: }
429:
430: // imports ...
431:
432: public Feature getPreprocessorImport() {
433: return null;
434: }
435:
436: public Map<String, Feature> getTokenImports() {
437: return Collections.<String, Feature> emptyMap();
438: }
439:
440: public List<Language> getImportedLanguages() {
441: return Collections.<Language> emptyList();
442: }
443: }
|