JSDOC/ScopeNamer.js
[app.jsdoc] / JSDOC / ScopeNamer.js
1 XObject = imports.XObject.XObject;
2
3  
4 console     = imports.console.console; 
5
6 Symbol = imports.Symbol.Symbol;   
7
8 /**
9  * @class ScopeNamer
10  * @namespace JSDOC
11  * The point of this class is to iterate through the Collapsed tree
12  * and add the property 'scopedName' to the tokens.
13  *
14  *
15  *
16  
17   After spending some time looking at this, a few conclusions...
18   
19   - this will have to be the base class of specific implementations.
20   - each implementation will have to decide how to declare a new scope
21   
22   scopes are normally changed when these things occur:
23
24
25   globalScope(stmts) => statementsScope($global$, stmts)
26   
27
28   'object'
29   objectScope(sname, props  )
30   a = { .... } << scope is a  $this$={a}
31   
32      // property scopes???
33     foo: function() {} $this$={object scope}
34     foo : ( xxx ? function()  { } ? function() { ...} << conditional properties types
35     foo : (function() { return x })() << a property or function??? << depends on notes
36     foo : [ ] << a property.. do not drill down?
37     foo : xxxxx << a property..
38   
39   
40   'function' 
41   functionScope(sname,  stmts  )
42   function a() { .... } << scope is a  $this$={a}
43   a = function() { } << scope might be a  $this$={a}
44   
45   // registers 'this.*'
46   
47   
48   
49   'call' scopes
50   XX.yy(a,b, { .... }) << scope might be a or b..  $this$={a}
51   aa = XX.yy(a,b, { .... }) << scope might aa   $this$={a}
52  
53   callscope(name, pref, args)
54  
55   can do losts ofthings
56  
57    ?? classify me...
58       foo = new (function() { }
59      (function() { }    
60     RETURN function(...) {
61
62     
63  
64     
65     
66     
67     
68  *
69  *
70  * usage  :
71  * sn = new ScopeName({
72     tokens : ... result from tokenizer,
73     
74  })
75    sn.buildSymbols();
76  *
77  * @param {Array} tokens array of tokens (from collapse)
78  * @param {String} srcFile the original file
79  * @param {Array} args Local variables which do not need to be added to scope.
80  */ 
81  
82 ScopeNamer = XObject.define(
83     function (cfg)
84     {
85         
86         ScopeNamer.superclass.constructor.call(this, cfg);
87         this.args = this.args ? this.args.slice(0)  : [];
88         Symbol.srcFile = this.filename;
89         this.createJSDOC = true;
90         
91        // console.dump(ar);
92         
93     }, 
94     imports.JSDOC.Collapse.Collapse, 
95     {
96         /**
97          * Call debugging
98          * add/remove ';' after return to configure at present..
99          * @param {String} str String to output
100          */
101         debugCall : function(str)  { return; print(str); }, 
102         
103         collapseTop : true,
104         
105         buildSymbols : function()
106         {
107             if (!this.statements) {
108                 this.statements = this.collapse(this.tokens);
109                 //print(JSON.stringify(this.s, null,4));
110             }
111             
112             //this.globalScope(this.statements);
113             this.debugCall("build Symbols");
114             //print (this.statements);
115             //print (JSON.stringify(this.statements, null,2));
116            
117             this.walkStatements('_global_', this.statements)
118             
119                 
120                 
121         },
122         
123         
124         ignore : false,
125          
126        
127          
128         
129         walkStatements: function(scope, statements)
130         {
131             this.debugCall("walkStatements :" + scope ) ;            
132             var _this = this;
133             var res = false;
134             
135             
136             statements.forEach(function(st ) {
137                 res = _this.walkStatement(scope, st);
138                 if (res === true) {
139                     return true;
140                 }
141                 return false;
142             });
143         },
144         
145         walkStatement: function(scope, stmt)
146         {
147             this.tokens = stmt;
148             this.rewind();
149             this.debugCall("walkStatement :" + scope + '@' + this.tokens[0].line );
150              
151             var name;
152             var sn;
153             
154             var isGlobal = scope == '_global_';
155             
156             
157             while (null != (token = this.next())) {
158             
159                 //'function' 
160                 //walkFunction(scope, name , args,  stmts  )
161                 //
162                 if (token.name == "FUNCTION") {
163                     // function a() { .... } << scope is a  $this$={a}
164                     if (this.lookTok(1).is('NAME')) {
165                         name = this.lookTok(2).data;
166                         this.walkFunctionDef(scope, name, this.lookTok(2).args, this.lookTok(3).items, token);
167                         continue;
168                     }
169                      //a = function() { } << scope might be a  $this$={a}
170                     if (this.lookTok(-1).data == '=' &&
171                         this.lookTok(-2).is('NAME')
172                     ) {
173                         name = this.lookTok(-2).data;
174                         this.walkFunctionDef(scope, name, this.lookTok(1).args, this.lookTok(2).items, this.lookTok(-2));
175                         continue;
176                     }
177                     
178                     
179                     print("+++ FUNCTION unusual context" + token.file + ':' + token.line);
180                     continue;
181                      
182                 }
183                 
184                 // control flow ...
185                 
186                 
187                  
188                 // 'call' scopes
189                 // XX.yy(a,b, { .... }) << scope might be a or b..  $this$={a}
190                 // aa = XX.yy(a,b, { .... }) << scope might aa   $this$={a}
191                 
192                 if (token.is('NAME') && this.lookTok(1).data =='(') {
193                     var assign = false;
194                     var jsdoc = token.jsdoc;
195                     if ((this.lookTok(-1).data == '=') && this.lookTok(-2).is('NAME')) {
196                         assign = this.lookTok(-2).data
197                         jsdoc = jsdoc || this.lookTok(-2).jsdoc;
198                     }
199                     this.walkCall(scope, assign, token.data, this.lookTok(1).items, jsdoc);
200                     continue;
201                 }
202                 
203                 //  'object'
204                 //   a = { .... } << scope is a  $this$={a}
205                 if (token.data == '{' && this.lookTok(-1).data == '=' && this.lookTok(-2).is('NAME')) {
206                     
207                     // could be var x = ..
208                     var jd = this.lookTok(-2).jsdoc ? this.lookTok(-2) : this.lookTok(-3);
209                     
210                     var isVar = this.lookTok(-3).name == 'VAR';
211                     
212                     // only register names of objects if 
213                     var name = this.lookTok(-2).data;
214                     name = !isGlobal && isVar ? false : name;
215                     name = !isGlobal && name && !name.match(/^this\./) ? false : name;
216                     //print(JSON.stringify(token,null,4));
217                     this.walkObject(scope, name, token.props, jd);
218                     continue;
219                 }
220                  
221                 // this.xxxx = (with jsdoc...)
222                 
223                 
224                 
225                  
226                 // standard flow....
227                 if (token.data == '{') { 
228                     sn = new ScopeNamer(this);
229                     //print("GOT { - walkings statuements;}");
230                     if (!token.items) {
231                         continue; // object..!?!?!? = ignore ???
232                         print(JSON.stringify(token,null,4));
233                     }
234                     sn.walkStatements(scope, token.items);
235                     continue;
236                 }
237             }
238         },
239         
240         walkFunctionDef : function (inscope, name, args, stmts, jsdocTok)
241         {
242             this.debugCall("wallkFuncDef: " + inscope + '@' + this.look(0).line );
243             var scope = inscope + '.' + name;
244             
245             
246             var symbol = new Symbol( scope , args || [] , "FUNCTION" ,  jsdocTok.jsdoc);
247             symbol._token = jsdocTok;
248             this.addSymbol(symbol, jsdocTok.jsdoc);
249             var sn = new ScopeNamer(this);
250             sn.walkStatements(scope, stmts);
251             
252         },            
253         
254         
255         
256         walkCall : function (inscope, assign, callname, items, jsdocTok)
257         {
258             this.debugCall("wallkCall : " + inscope  +':' + assign + '@' + this.look(0).line + ' ' + callname );
259             var scope = inscope + ( assign ? ('.' + assign) : '' );
260             //scope = scope.replace(/\^_global\$\./, '');
261             
262             
263             
264             
265             // add the handers for differnt methods in here....
266             switch (callname) {
267                 
268                 // somecall(BASE, { .. object ..})
269                 // or... x = somecall(BASE, { ... object..})
270                 case 'XObject.extend':
271                 case 'Roo.apply':
272                     //print(JSON.stringify(items,null,4));
273                     scope = items[0][0].data;
274                     // 2nd arg is a object def
275                     if (items[1][0].data != '{') {
276                         return;
277                     }
278                     
279                     if (assign != false   && !scope.match(/\.prototype$/)) { 
280                         var symbol = new Symbol( scope , false , "OBJECT" ,  jsdocTok);
281                         symbol._token = jsdocTok;
282                         this.addSymbol(symbol, jsdocTok.jsdoc);
283                         
284                     }
285                     
286                     
287                     var sn = new ScopeNamer(this);
288                     
289                     //print(JSON.stringify(items,null,4));
290                     sn.walkObject(scope , false, items[1][0].props, jsdocTok );
291                     
292                     return;
293                 
294                 
295                     
296                     
297                 case 'XObject.define':
298                     // func, extends, props.
299                     
300                     var symbol = new Symbol( scope , items[0][1].args || [] , "CONSTRUCTOR" ,  jsdocTok);
301                     symbol._token = jsdocTok;
302                     // extends = arg[1]
303                     
304                     this.addSymbol(symbol, jsdocTok.jsdoc);
305                     var sn = new ScopeNamer(this);
306                     
307                     
308                     sn.walkStatements(scope, items[0][2].items);
309                     sn.walkObject(scope + '.prototype', false, items[2].props );
310                     return;
311                 
312                 
313                 
314             }
315             
316             
317              
318         },
319                             
320         walkObject : function(inscope, name, items, jsdocTok)
321         {
322             var scope = inscope + (( name === false) ? '' : ('.' + name));
323            
324             if (name !== false) {
325                 
326                 var symbol = new Symbol( scope , false , "OBJECT" ,  jsdocTok.jsdoc);
327                 symbol._token = jsdocTok;
328                 this.addSymbol(symbol, jsdocTok.jsdoc);
329                  
330             }
331             // items can be false in this scenaro: a = {};
332             if (!items) {
333                 return;
334             }
335             print(typeof(items));
336             this.debugCall("wallkObject : " + scope + '@' + items[Object.keys(items)[0]].key.line);
337             for( var k in items) {
338                 var key = items[k].key;
339                 var val = items[k].val;
340                 
341                 
342                 // x : function(....)
343                 if (val[0].name == 'FUNCTION' ) {
344                     
345                     
346                     this.walkFunctionDef (scope, k, val[1].args, val[2].items, key)
347
348                     
349                     continue;
350                 }
351                 
352                 // x: ( .... ) conditional  properties? maybe detect function?
353                 
354                 
355                 // x : something else - not a function..
356                 
357                 
358                 var symbol = new Symbol( scope +'.'+ k , val[1].args || [] , "PROPERTY" ,  key.jsdoc);
359                 symbol._token = key;
360                    
361                 this.addSymbol(symbol, key.jsdoc);
362                 continue;
363                  
364                 
365             }
366             
367             
368         },
369         
370         
371         
372         
373         
374         
375         addSymbol: function(symbol, comment)
376         {
377               //print("ADD symbol: " + JSON.stringify( symbol, null, 4));
378               //print("PARSER addSYMBOL : " + symbol.alias);
379             ScopeNamer.prototype.debugCall("addSymbol : " + symbol.alias );       
380             // if a symbol alias is documented more than once the last one with the user docs wins
381             if (ScopeNamer.symbols.hasSymbol(symbol.alias)) {
382                 
383                 if (!comment) { // we do not have a comment, and it's registered.
384                     return;
385                 }
386                 var oldSymbol = ScopeNamer.symbols.getSymbol(symbol.alias);
387                 
388                 if (oldSymbol.comment && oldSymbol.comment.isUserComment && !oldSymbol.comment.hasTags) {
389                     if (symbol.comment.isUserComment) { // old and new are both documented
390                         this.LOG.warn("The symbol '"+symbol.alias+"' is documented more than once.");
391                     }
392                     else { // old is documented but new isn't
393                         return;
394                     }
395                 }
396             }
397             
398             // we don't document anonymous things
399             //if (this.conf.ignoreAnonymous && symbol.name.match(/\$anonymous\b/)) return;
400         
401             // uderscored things may be treated as if they were marked private, this cascades
402             //if (this.conf.treatUnderscoredAsPrivate && symbol.name.match(/[.#-]_[^.#-]+$/)) {
403             //    symbol.isPrivate = true;
404             //}
405             
406             // -p flag is required to document private things
407             if ((symbol.isInner || symbol.isPrivate) && !this.docPrivate) return;
408             
409             // ignored things are not documented, this doesn't cascade
410             if (symbol.isIgnored) return;
411             
412             
413             //print("ADD symbol: " + symbol.isa + ' => ' + symbol.alias );
414             //print("ADD symbol: " + JSON.stringify( symbol, null, 4));
415             
416             // add it to the file's list... (for dumping later..)
417             if (this.srcFile) {
418                 ScopeNamer.filesSymbols[Symbol.srcFile].addSymbol(symbol);
419             }
420             
421             ScopeNamer.symbols.addSymbol(symbol);
422         },
423         addBuiltin : function(name) {
424
425             var builtin = new Symbol(name, [], "CONSTRUCTOR", new imports.DocComment.DocComment(""));
426             builtin.isNamespace = false;
427             builtin.srcFile = "";
428             builtin.isPrivate = false;
429             this.addSymbol(builtin);
430             return builtin;
431         }
432         
433         
434          
435     }
436 );
437
438 ScopeNamer.symbols =  new  imports.SymbolSet.SymbolSet();
439
440
441
442
443     
444 ScopeNamer.addSymbol = ScopeNamer.prototype.addSymbol;
445 ScopeNamer.addBuiltin = ScopeNamer.prototype.addBuiltin;