1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is Diavolo.
16 * The Initial Developer of the Original Code is
17 * Disruptive Innovations SARL.
18 * Portions created by the Initial Developer are Copyright (C) 2006-2008
19 * the Initial Developer. All Rights Reserved.
22 * Daniel Glazman <daniel.glazman@disruptive-innovations.com>, Original author
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 function DiavoloGrammar(aChromeURL)
40 this.init(aChromeURL);
43 DiavoloGrammar.prototype = {
45 kIOServiceCID : "@mozilla.org/network/io-service;1",
46 kFileInputStreamCID: "@mozilla.org/network/file-input-stream;1",
47 kScriptableInputCID: "@mozilla.org/scriptableinputstream;1",
48 kUnicodeConverterCID: "@mozilla.org/intl/scriptableunicodeconverter",
50 nsIIOService : Components.interfaces.nsIIOService,
51 nsIFileInputStream : Components.interfaces.nsIFileInputStream,
52 nsIScriptableInputStream: Components.interfaces.nsIScriptableInputStream,
53 nsIScriptableUnicodeConverter: Components.interfaces.nsIScriptableUnicodeConverter,
61 mGrammarDocument: null,
71 mFirstContextId: null,
74 mCaseInsensitive: false,
76 mResolver: new RegExp("\{[a-zA-Z][a-zA-Z0-9]*\}", "g"),
78 getGrammarName: function()
83 getDeftokens: function()
85 return this.mDeftokens;
88 isCaseInsensitive: function()
90 return this.mCaseInsensitive;
93 convertToUnicode: function(aCharset, aSrc )
95 // http://lxr.mozilla.org/mozilla/source/intl/uconv/idl/nsIScriptableUConv.idl
96 var unicodeConverter = Components.classes[this.kUnicodeConverterCID]
97 .createInstance(this.nsIScriptableUnicodeConverter);
98 unicodeConverter.charset = aCharset;
99 return unicodeConverter.ConvertToUnicode( aSrc );
102 init: function(aChromeURL)
104 this.mChromeURL = aChromeURL;
106 // Get the grammar file
107 // var urlspec="chrome://diavolo/content/css.xml";
108 var ioService = Components.classes[this.kIOServiceCID]
109 .getService(this.nsIIOService);
112 var url = ioService.newURI(aChromeURL, null, null);
114 var chann = ioService.newChannelFromURI(url);
115 var inputStream = Components.classes[this.kFileInputStreamCID]
116 .createInstance(this.nsIFileInputStream);
117 var sis = Components.classes[this.kScriptableInputCID]
118 .createInstance(this.nsIScriptableInputStream);
120 sis.init(chann.open());
121 var str = sis.read(sis.available());
123 str = this.convertToUnicode("UTF-8",str);
124 var parser = new DOMParser();
125 this.mGrammarDocument = parser.parseFromString(str, "text/xml");
127 this.mName = this.mGrammarDocument.documentElement.getAttribute("name");
134 this.mRegExps = null;
135 this.mGrammarDocument = null;
138 initRegExps: function()
140 var regexps = this.mGrammarDocument.getElementsByTagName("regexp");
141 var length = regexps.length;
142 for (var i = 0; i < length; i++)
144 var r = regexps.item(i);
145 var name = r.getAttribute("name");
146 var value = r.getAttribute("value");
148 this.mRegExps[name] = {value: value, resolved: false};
151 this.resolveAllRegexps();
153 this.resolveDeftokens();
156 resolveAllRegexps: function()
158 for (var i in this.mRegExps)
159 this.resolveRegexp(i);
162 resolveRegexp: function(aName)
164 if (!this.mRegExps[aName].resolved)
166 var value = this.mRegExps[aName].value;
167 var match = value.match(this.mResolver);
170 for (var j = 0; j < match.length; j++)
173 var subRegexp = m.substring(1, m.length - 1);
174 this.resolveRegexp(subRegexp);
176 var r = new RegExp( "\{" + subRegexp + "\}", "g" )
177 value = value.replace(r, "(" + this.mRegExps[subRegexp].value + ")");
179 this.mRegExps[aName].value = value;
181 this.mRegExps[aName].resolved = true;
185 getOptions: function()
187 var options = this.mGrammarDocument.getElementsByTagName("option");
188 var length = options.length;
189 for (var i = 0; i < length; i++)
191 var option = options.item(i);
192 var type = option.getAttribute("type");
195 case "case-insensitive":
196 this.mCaseInsensitive = true;
204 resolveDeftokens: function()
206 var deftokens = this.mGrammarDocument.getElementsByTagName("deftoken");
207 var length = deftokens.length;
208 for (var i = 0; i < length; i++)
210 var t = deftokens.item(i);
211 var name = t.getAttribute("name");
213 if (t.hasAttribute("string"))
215 type = this.kSTRING_TOKEN;
216 value = t.getAttribute("string");
220 type = this.kREGEXP_TOKEN;
221 value = new RegExp(this.mRegExps[t.getAttribute("regexp")].value,
222 this.mCaseInsensitive ? "iy" : "y");
224 this.mDeftokens[name] = { type: type, value: value};
228 getNextToken: function getNextToken(aSourceString, aLastIndex)
230 if (aLastIndex >= aSourceString.length)
232 var tokens = this.mDeftokens;
233 var tokenFound = false;
235 var tokenString = "";
236 for (var i in tokens)
238 var token = tokens[i];
241 case this.kSTRING_TOKEN:
245 if (aSourceString.substr(aLastIndex, l) == s)
253 case this.kREGEXP_TOKEN:
256 r.lastIndex = aLastIndex;
257 var result = r.exec(aSourceString);
261 tokenString = result[0];
266 default: // we should never reach this
273 return {name: tokenName, string: tokenString, error: false};
274 return {name: "", string: aSourceString[aLastIndex], error: true};
277 getContexts: function getContexts()
279 var contexts = this.mGrammarDocument.getElementsByTagName("context");
282 this.mFirstContextId = contexts.item(0).getAttribute("id");
284 for (var k = 0; k < contexts.length; k++)
286 var c = contexts.item(k);
287 var contextName = c.getAttribute("id");
288 this.mContexts[contextName] = {};
289 // get all error handlers
291 var errorsElts = c.getElementsByTagName("error");
292 for (var i = 0; i < errorsElts.length; i++)
294 var e = errorsElts.item(i);
295 skipuntils[e.getAttribute("skipuntil")] = e.getAttribute("expecting");
297 this.mContexts[contextName].skipuntils = skipuntils;
299 // now find all tokens to ignore in this context
301 var ignoreElts = c.getElementsByTagName("ignore");
303 if (ignoreElts && ignoreElts.length)
306 if (ignoreElts.length > 1)
307 dump("more than one <ignore> element found in context " + contextName + "\n")
308 var ignoreElt = ignoreElts.item(0);
309 var typeElts = ignoreElt.getElementsByTagName("type");
310 for (var i = 0; i < typeElts.length; i++)
312 var t = typeElts.item(i);
313 ignores[t.getAttribute("name")] = true;
316 this.mContexts[contextName].ignores = ignores;
318 // and now finally tokens
320 var tokenElts = c.getElementsByTagName("token");
321 for (var i = 0; i < tokenElts.length; i++)
323 t = tokenElts.item(i);
324 var ttype = t.getAttribute("type");
325 var tlookahead = t.hasAttribute("lookahead") ? t.getAttribute("lookahead") : null;
326 var trole = t.getAttribute("role");
327 var texpecting = t.getAttribute("expecting");
330 lookahead: tlookahead,
332 expecting: texpecting
334 // we need to keep track of all roles for color management
335 if (!(trole in this.mRoles))
336 this.mRoles[trole] = true;
338 this.mContexts[contextName].tokens = tokens;
342 initStyles: function initStyles()
344 var stylesets = this.mGrammarDocument.getElementsByTagName("styleset");
345 if (!stylesets || !stylesets.length)
347 var styles = stylesets[0].getElementsByTagName("style");
348 for (var i = 0; i < styles.length; i++)
350 var s = styles.item(i);
351 var role = s.getAttribute("role");
352 var forToken = s.hasAttribute("forToken")
353 ? s.getAttribute("forToken")
355 var value = s.getAttribute("value");
357 this.mStyles.push( { role: role,
362 var errorstyles = stylesets[0].getElementsByTagName("errorstyle");
363 if (errorstyles && errorstyles.length)
365 this.mErrorStyle = errorstyles[0].getAttribute("value");
368 var skipuntilstyles = stylesets[0].getElementsByTagName("skipuntilstyle");
369 if (skipuntilstyles && skipuntilstyles.length)
371 this.mSkipuntilStyle = skipuntilstyles[0].getAttribute("value");
375 buildStylesheet: function buildStylesheet()
379 cssText += "*[error='true'] {" + this.mErrorStyle + "}\n";
380 cssText += ".SKIPUNTIL {" + this.mSkipuntilStyle + "}\n";
382 for (var i = 0; i < this.mStyles.length; i++)
384 var s = this.mStyles[i];
385 cssText += "." + s.role
386 + (s.forToken ? "[token='" + s.forToken + "']"
396 getRoles: function getRoles()
399 for (var i in this.mRoles)
404 getTokens: function getTokens()
407 for (var i in this.mDeftokens)
412 getStyles: function getStyles(aRole, aForToken)
417 setStyles: function setStyles(aStyles)
419 this.mStyles = aStyles;
422 getErrorStyle: function getErrorStyle()
424 return this.mErrorStyle;
427 setErrorStyle: function setErrorStyle(aStyle)
429 this.mErrorStyle = aStyle;
432 getSkipuntilStyle: function getSkipuntilStyle()
434 return this.mSkipuntilStyle;
437 setSkipuntilStyle: function setSkipuntilStyle(aStyle)
439 this.mSkipuntilStyle = aStyle;