More commits to sync working copy
[gnome.introspection-doc-generator] / JSDOC / TokenReader.js
1 //<script type="text/javascript">
2
3 imports['Object.js'].load(Object);
4 console = imports['console.js'].console;
5
6 JSDOC   = imports['JSDOC.js'].JSDOC;
7 Token   = imports['JSDOC/Token.js'].Token;
8 Lang    = imports['JSDOC/Token.js'].Lang;
9
10 /**
11         @class Search a {@link JSDOC.TextStream} for language tokens.
12 */
13 TokenReader = Object.define(
14     function(o) {
15         
16         this.keepDocs = true;
17         this.keepWhite = false;
18         this.keepComments = false;
19         Roo.apply(this, o || {});
20         
21     },
22     Object,
23     {
24             
25
26         /**
27             @type {JSDOC.Token[]}
28          */
29
30
31         tokenize : function(/**JSDOC.TextStream*/stream) {
32             var tokens = [];
33             /**@ignore*/ tokens.last    = function() { return tokens[tokens.length-1]; }
34             /**@ignore*/ tokens.lastSym = function() {
35                 for (var i = tokens.length-1; i >= 0; i--) {
36                     if (!(tokens[i].is("WHIT") || tokens[i].is("COMM"))) return tokens[i];
37                 }
38             }
39
40             while (!stream.look().eof) {
41                 if (this.read_mlcomment(stream, tokens)) continue;
42                 if (this.read_slcomment(stream, tokens)) continue;
43                 if (this.read_dbquote(stream, tokens))   continue;
44                 if (this.read_snquote(stream, tokens))   continue;
45                 if (this.read_regx(stream, tokens))      continue;
46                 if (this.read_numb(stream, tokens))      continue;
47                 if (this.read_punc(stream, tokens))      continue;
48                 if (this.read_newline(stream, tokens))   continue;
49                 if (this.read_space(stream, tokens))     continue;
50                 if (this.read_word(stream, tokens))      continue;
51                 
52                 // if execution reaches here then an error has happened
53                 tokens.push(new Token(stream.next(), "TOKN", "UNKNOWN_TOKEN"));
54             }
55             
56             
57             
58             return tokens;
59         },
60
61         /**
62             @returns {Boolean} Was the token found?
63          */
64         read_word : function(/**JSDOC.TokenStream*/stream, tokens) {
65             var found = "";
66             while (!stream.look().eof && Lang.isWordChar(stream.look())) {
67                 found += stream.next();
68             }
69             
70             if (found === "") {
71                 return false;
72             }
73             else {
74                 var name;
75                 if ((name = Lang.keyword(found))) tokens.push(new Token(found, "KEYW", name));
76                 else tokens.push(new Token(found, "NAME", "NAME"));
77                 return true;
78             }
79         },
80
81         /**
82             @returns {Boolean} Was the token found?
83          */
84         read_punc : function(/**JSDOC.TokenStream*/stream, tokens) {
85             var found = "";
86             var name;
87             while (!stream.look().eof && Lang.punc(found+stream.look())) {
88                 found += stream.next();
89             }
90             
91             if (found === "") {
92                 return false;
93             }
94             else {
95                 tokens.push(new Token(found, "PUNC", Lang.punc(found)));
96                 return true;
97             }
98         },
99
100         /**
101             @returns {Boolean} Was the token found?
102          */
103         read_space : function(/**JSDOC.TokenStream*/stream, tokens) {
104             var found = "";
105             
106             while (!stream.look().eof && Lang.isSpace(stream.look())) {
107                 found += stream.next();
108             }
109             
110             if (found === "") {
111                 return false;
112             }
113             else {
114                 if (this.collapseWhite) found = " ";
115                 if (this.keepWhite) tokens.push(new Token(found, "WHIT", "SPACE"));
116                 return true;
117             }
118         },
119
120         /**
121             @returns {Boolean} Was the token found?
122          */
123         read_newline : function(/**JSDOC.TokenStream*/stream, tokens) {
124             var found = "";
125             
126             while (!stream.look().eof && Lang.isNewline(stream.look())) {
127                 found += stream.next();
128             }
129             
130             if (found === "") {
131                 return false;
132             }
133             else {
134                 if (this.collapseWhite) found = "\n";
135                 if (this.keepWhite) tokens.push(new Token(found, "WHIT", "NEWLINE"));
136                 return true;
137             }
138         },
139
140         /**
141             @returns {Boolean} Was the token found?
142          */
143         read_mlcomment : function(/**JSDOC.TokenStream*/stream, tokens) {
144             if (stream.look() == "/" && stream.look(1) == "*") {
145                 var found = stream.next(2);
146                 
147                 while (!stream.look().eof && !(stream.look(-1) == "/" && stream.look(-2) == "*")) {
148                     found += stream.next();
149                 }
150                 
151                 // to start doclet we allow /** or /*** but not /**/ or /****
152                 if (/^\/\*\*([^\/]|\*[^*])/.test(found) && this.keepDocs) tokens.push(new Token(found, "COMM", "JSDOC"));
153                 else if (this.keepComments) tokens.push(new Token(found, "COMM", "MULTI_LINE_COMM"));
154                 return true;
155             }
156             return false;
157         },
158
159         /**
160             @returns {Boolean} Was the token found?
161          */
162         read_slcomment : function(/**JSDOC.TokenStream*/stream, tokens) {
163             var found;
164             if (
165                 (stream.look() == "/" && stream.look(1) == "/" && (found=stream.next(2)))
166                 || 
167                 (stream.look() == "<" && stream.look(1) == "!" && stream.look(2) == "-" && stream.look(3) == "-" && (found=stream.next(4)))
168             ) {
169                 
170                 while (!stream.look().eof && !Lang.isNewline(stream.look())) {
171                     found += stream.next();
172                 }
173                 
174                 if (this.keepComments) {
175                     tokens.push(new Token(found, "COMM", "SINGLE_LINE_COMM"));
176                 }
177                 return true;
178             }
179             return false;
180         },
181
182         /**
183             @returns {Boolean} Was the token found?
184          */
185         read_dbquote : function(/**JSDOC.TokenStream*/stream, tokens) {
186             if (stream.look() == "\"") {
187                 // find terminator
188                 var string = stream.next();
189                 
190                 while (!stream.look().eof) {
191                     if (stream.look() == "\\") {
192                         if (Lang.isNewline(stream.look(1))) {
193                             do {
194                                 stream.next();
195                             } while (!stream.look().eof && Lang.isNewline(stream.look()));
196                             string += "\\\n";
197                         }
198                         else {
199                             string += stream.next(2);
200                         }
201                     }
202                     else if (stream.look() == "\"") {
203                         string += stream.next();
204                         tokens.push(new Token(string, "STRN", "DOUBLE_QUOTE"));
205                         return true;
206                     }
207                     else {
208                         string += stream.next();
209                     }
210                 }
211             }
212             return false; // error! unterminated string
213         },
214
215         /**
216             @returns {Boolean} Was the token found?
217          */
218         read_snquote : function(/**JSDOC.TokenStream*/stream, tokens) {
219             if (stream.look() == "'") {
220                 // find terminator
221                 var string = stream.next();
222                 
223                 while (!stream.look().eof) {
224                     if (stream.look() == "\\") { // escape sequence
225                         string += stream.next(2);
226                     }
227                     else if (stream.look() == "'") {
228                         string += stream.next();
229                         tokens.push(new Token(string, "STRN", "SINGLE_QUOTE"));
230                         return true;
231                     }
232                     else {
233                         string += stream.next();
234                     }
235                 }
236             }
237             return false; // error! unterminated string
238         },
239
240         /**
241             @returns {Boolean} Was the token found?
242          */
243         read_numb : function(/**JSDOC.TokenStream*/stream, tokens) {
244             if (stream.look() === "0" && stream.look(1) == "x") {
245                 return this.read_hex(stream, tokens);
246             }
247             
248             var found = "";
249             
250             while (!stream.look().eof && Lang.isNumber(found+stream.look())){
251                 found += stream.next();
252             }
253             
254             if (found === "") {
255                 return false;
256             }
257             else {
258                 if (/^0[0-7]/.test(found)) tokens.push(new Token(found, "NUMB", "OCTAL"));
259                 else tokens.push(new Token(found, "NUMB", "DECIMAL"));
260                 return true;
261             }
262         },
263         /*t:
264             requires("../lib/JSDOC/TextStream.js");
265             requires("../lib/JSDOC/Token.js");
266             requires("../lib/JSDOC/Lang.js");
267             
268             plan(3, "testing read_numb");
269             
270             //// setup
271             var src = "function foo(num){while (num+8.0 >= 0x20 && num < 0777){}}";
272             var tr = new TokenReader();
273             var tokens = tr.tokenize(new TextStream(src));
274             
275             var hexToken, octToken, decToken;
276             for (var i = 0; i < tokens.length; i++) {
277                 if (tokens[i].name == "HEX_DEC") hexToken = tokens[i];
278                 if (tokens[i].name == "OCTAL") octToken = tokens[i];
279                 if (tokens[i].name == "DECIMAL") decToken = tokens[i];
280             }
281             ////
282             
283             is(decToken.data, "8.0", "decimal number is found in source.");
284             is(hexToken.data, "0x20", "hexdec number is found in source (issue #99).");
285             is(octToken.data, "0777", "octal number is found in source.");
286         */
287
288         /**
289             @returns {Boolean} Was the token found?
290          */
291         read_hex : function(/**JSDOC.TokenStream*/stream, tokens) {
292             var found = stream.next(2);
293             
294             while (!stream.look().eof) {
295                 if (Lang.isHexDec(found) && !Lang.isHexDec(found+stream.look())) { // done
296                     tokens.push(new Token(found, "NUMB", "HEX_DEC"));
297                     return true;
298                 }
299                 else {
300                     found += stream.next();
301                 }
302             }
303             return false;
304         },
305
306         /**
307             @returns {Boolean} Was the token found?
308          */
309         read_regx : function(/**JSDOC.TokenStream*/stream, tokens) {
310             var last;
311             if (
312                 stream.look() == "/"
313                 && 
314                 (
315                     
316                     (
317                         !(last = tokens.lastSym()) // there is no last, the regex is the first symbol
318                         || 
319                         (
320                                !last.is("NUMB")
321                             && !last.is("NAME")
322                             && !last.is("RIGHT_PAREN")
323                             && !last.is("RIGHT_BRACKET")
324                         )
325                     )
326                 )
327             ) {
328                 var regex = stream.next();
329                 
330                 while (!stream.look().eof) {
331                     if (stream.look() == "\\") { // escape sequence
332                         regex += stream.next(2);
333                     }
334                     else if (stream.look() == "/") {
335                         regex += stream.next();
336                         
337                         while (/[gmi]/.test(stream.look())) {
338                             regex += stream.next();
339                         }
340                         
341                         tokens.push(new Token(regex, "REGX", "REGX"));
342                         return true;
343                     }
344                     else {
345                         regex += stream.next();
346                     }
347                 }
348                 // error: unterminated regex
349             }
350             return false;
351         }
352 });