1 // this walks through the code, and tries to find patterns that match documentable elements..
11 public class Walker : Object
14 Gee.ArrayList<string> warnings;
15 Gee.ArrayList<Scope> scopes;
16 Gee.HashMap<int,Scope> indexedScopes;
17 Gee.HashMap<string,Symbol> symbols;
18 Gee.HashMap<string,string> aliases;
22 WalkerMode mode = WalkerMode.XXX; //"BUILDING_SYMBOL_TREE",
25 DocComment? currentDoc = null;
29 public Walker(TokenStream ts)
32 this.warnings= new Gee.ArrayList<string>();
33 this.scopes = new Gee.ArrayList<Scope>();
34 this.indexedScopes = new Gee.HashMap<int,Scope>();
35 this.symbols = new Gee.HashMap<string,Symbol>();
36 this.aliases = new Gee.HashMap<string,string>();
37 this.braceNesting = 0;
41 //this.warnings.push(s);
42 // print("WARNING:" + htmlescape(s) + "<BR>");
44 // defaults should not be initialized here =- otherwise they get duped on new, rather than initalized..
51 public void buildSymbolTree()
56 this.braceNesting = 0;
57 this.scopes = new Gee.ArrayList<Scope>();;
58 this.aliases = new Gee.HashMap<string,string>();
60 this.globalScope = new Scope(-1, null, -1, "$global$", null);
61 this.indexedScopes = new Gee.HashMap<int,Scope>();
62 this.indexedScopes.set( 0, this.globalScope );
64 this.mode = WalkerMode.BUILDING_SYMBOL_TREE;
65 this.parseScope(this.globalScope,this.emptyAlias());
68 Gee.HashMap<string,string> emptyAlias()
70 return new Gee.HashMap<string,string> ();
75 string fixAlias (Gee.HashMap<string,string>aliases, string str, bool nomore = false)
77 var ar = str.split(".");
80 //print(str +"?=" +aliases.toSource());
81 if (!aliases.has_key(m)) {
84 ar[0] = aliases.get(m);
86 var ret = string.joinv(".", ar);
88 ret = this.fixAlias(aliases, ret, true);
97 void parseScope (Scope in_scope, Gee.HashMap<string,string> ealiases) // parse a token stream..
99 //this.timerPrint("parseScope EnterScope");
101 var scope = in_scope;
103 var aliases = new Gee.HashMap<string,string>();
105 foreach(var i in ealiases.keys) {
106 aliases.set(i, ealiases.get(i));
109 //print("STARTING SCOPE WITH: " + ealiases.toSource());
111 var expressionBraceNesting = this.braceNesting;
112 var bracketNesting = 0;
113 var parensNesting = 0;
116 var l1 = "", l2 = "";
120 var locBraceNest = 0;
121 // determines if we are in object literals...
123 var isObjectLitAr = new Gee.ArrayList<bool>();
124 isObjectLitAr.add(false);
125 //print("SCOPE: ------------------START ----------------");
127 this.scopesIn(scope);
128 var scopeLen = this.scopes.size;
130 if (this.ts.cursor < 1) {
131 // this.ts.cursor--; // hopeflly this kludge will work
135 //print(JSON.stringify(this.ts, null, 4)); Seed.quit();
137 while (null != ( token = this.ts.next())) {
138 //GLib.debug("TOK %s", token.asString());
139 // this.timerPrint("parseScope AFTER lookT: " + token.toString());
141 if (token.isType(TokenType.COMM)) {
144 if (!token.isName(TokenName.JSDOC)) {
145 //print("Walker2 : spce is not JSDOC");
148 if (this.currentDoc != null) {
149 // add it to the current scope????
151 this.addSymbol("", true);
152 //print ( "Unconsumed Doc: " + token.toString())
153 //throw "Unconsumed Doc (TOKwhitespace): " + this.currentDoc.toSource();
156 // print ( "NEW COMMENT: " + token.toString())
157 var newDoc = new DocComment(token.data);
159 // it"s a scope changer..
161 if (newDoc.getTag(DocTagTitle.SCOPE).size > 0) {
162 //print("Walker2 : doctag changes scope");
164 scope.ident = "$private$|" + newDoc.getTag(DocTagTitle.SCOPE).get(0).desc;
168 // it"s a scope changer..
169 if (newDoc.getTag(DocTagTitle.SCOPEALIAS).size > 0) {
170 //print(newDoc.getTag("scopeAlias").toSource());
172 //print("Walker2 : doctag changes scope (alias)");
173 var sal = newDoc.getTag(DocTagTitle.SCOPEALIAS).get(0).desc.split("=");
174 aliases[sal[0].strip()] = sal[1].strip();
180 /// got a doc comment..
181 //token.data might be this.??? (not sure though)
182 //print("Walker2 : setting currentDoc");
183 this.currentDoc = newDoc;
187 // catch the various issues .. - scoe changes or doc actions..
191 // things that stop comments carrying on...??
193 if (this.currentDoc != null && (
195 token.data == "}")) {
196 this.addSymbol("", true);
197 //throw "Unconsumed Doc ("+ token.toString() +"): " + this.currentDoc.toSource();
201 // the rest are scoping issues...
205 if (token.isName(TokenName.VAR) &&
207 this.ts.lookTok(1).isType(TokenType.NAME) &&
208 this.ts.lookTok(2).data == "=" &&
209 this.ts.lookTok(3).isType(TokenType.NAME) &&
210 this.ts.lookTok(4).data == ";"
214 //print("SET ALIAS:" + this.ts.lookTok(1).data +"=" + this.ts.lookTok(3).data);
216 aliases.set(this.ts.lookTok(1).data, this.ts.lookTok(3).data);
220 if ((token.data == "eval") || /\.eval$/.match(token.data)) {
221 this.currentDoc = null;
225 // extends scoping *** not sure if the can be x = Roo.apply(....)
226 // xxx.extends(a,b, {
227 // $this$=b|b.prototype
232 if (token.isType(TokenType.NAME) ) {
234 //print("TOK(ident)"+ token.toString());
236 if (/\.extend$/.match(token.data) &&
237 this.ts.lookTok(1).data == "(" &&
238 this.ts.lookTok(2).isType(TokenType.NAME) &&
239 this.ts.lookTok(3).data == "," &&
240 this.ts.lookTok(4).isType(TokenType.NAME) &&
241 this.ts.lookTok(5).data == "," &&
242 this.ts.lookTok(6).data == "{"
245 // ignore test for ( a and ,
246 this.ts.nextTok(); /// (
247 token = this.ts.nextTok(); // a
248 scopeName = token.data;
250 if (this.currentDoc != null) {
251 this.addSymbol(scopeName,false,"OBJECT");
254 this.ts.nextTok(); // ,
255 this.ts.nextTok(); // b
258 this.ts.nextTok(); // ,
259 token = this.ts.nextTok(); // {
261 scopeName = this.fixAlias(aliases, scopeName);
263 var fnScope = new Scope(this.braceNesting, scope, token.id, // was token.n?
264 "$this$=" + scopeName + "|"+scopeName+".prototype", null
268 this.indexedScopes.set(this.ts.cursor, fnScope);
270 this.scopesIn(fnScope);
273 //print(">>" +locBraceNest);
274 continue; // no more processing..
278 // a = Roo.extend(parentname, {
280 if (/\.extend$/.match(token.data) &&
281 this.ts.lookTok(-2).isType(TokenType.NAME) &&
282 this.ts.lookTok(-1).data == "=" &&
283 this.ts.lookTok(1).data == "(" &&
284 this.ts.lookTok(2).isType(TokenType.NAME) &&
285 this.ts.lookTok(3).data == "," &&
286 this.ts.lookTok(4).data == "{"
288 // ignore test for ( a and ,
289 token = this.ts.lookTok(-2);
290 scopeName = token.data;
291 if (this.currentDoc != null) {
292 this.addSymbol(scopeName,false,"OBJECT");
295 this.ts.nextTok(); /// (
296 this.ts.nextTok(); // parent
298 this.ts.nextTok(); // ,
299 token = this.ts.nextTok(); // {
302 scopeName = this.fixAlias(aliases,scopeName);
303 var fnScope = new Scope(this.braceNesting, scope, token.id, // was token.n?
304 "$this$=" + scopeName + "|"+scopeName+".prototype",
309 this.indexedScopes.set(this.ts.cursor, fnScope);
311 this.scopesIn(fnScope);
314 //print(">>" +locBraceNest);
315 continue; // no more processing..
322 print(JSON.stringify([
324 this.ts.lookTok(1).data ,
325 this.ts.lookTok(2).type ,
326 this.ts.lookTok(3).data ,
327 this.ts.lookTok(4).data
331 if (/\.(applyIf|apply)$/.match(token.data) &&
332 this.ts.lookTok(1).data == "(" &&
333 this.ts.lookTok(2).isType(TokenType.NAME) &&
334 this.ts.lookTok(3).data == "," &&
335 this.ts.lookTok(4).data == "{"
338 this.ts.nextTok(); /// (
340 //print("GOT : applyIF!");
342 token = this.ts.nextTok(); // b
343 scopeName = token.data;
346 if (this.currentDoc != null) {
347 this.addSymbol(scopeName,false,"OBJECT");
352 this.ts.nextTok(); /// ,
353 this.ts.nextTok(); // {
354 scopeName = this.fixAlias(aliases,scopeName);
355 var fnScope = new Scope(this.braceNesting, scope, token.id, // was token.n?
359 this.indexedScopes.set(this.ts.cursor, fnScope);
361 this.scopesIn(fnScope);
364 //print(">>" +locBraceNest);
365 continue; // no more processing..
371 // change scope to xxxx
373 print(JSON.stringify([
374 this.ts.lookTok(1).data ,
375 this.ts.lookTok(2).name ,
376 this.ts.lookTok(3).type ,
377 this.ts.lookTok(4).data ,
378 this.ts.lookTok(5).data
381 if ( this.ts.lookTok(1).data == "=" &&
382 this.ts.lookTok(2).isName(TokenName.NEW) &&
383 this.ts.lookTok(3).isType(TokenType.NAME)&&
384 this.ts.lookTok(4).data == "(" &&
385 this.ts.lookTok(5).data == "{"
387 scopeName = token.data;
388 if (this.currentDoc != null) {
389 this.addSymbol(scopeName,false,"OBJECT");
393 this.ts.nextTok(); /// =
394 this.ts.nextTok(); /// new
395 this.ts.nextTok(); /// yyy
396 this.ts.nextTok(); /// (
397 this.ts.nextTok(); /// {
399 scopeName = this.fixAlias(aliases,scopeName);
400 var fnScope = new Scope(this.braceNesting, scope, token.id, // was token.n?
403 this.indexedScopes.set(this.ts.cursor, fnScope);
405 this.scopesIn(fnScope);
408 //print(">>" +locBraceNest);
410 continue; // no more processing..
419 // eval can be prefixed with a hint hider for the compresser..
422 if (this.currentDoc != null) {
423 //print(token.toString());
425 // ident : function ()
426 // ident = function ()
427 var atype = "OBJECT";
429 if (((this.ts.lookTok(1).data == ":" )|| (this.ts.lookTok(1).data == "=")) &&
430 (this.ts.lookTok(2).isName(TokenName.FUNCTION))
432 // this.ts.nextTok();
433 // this.ts.nextTok();
437 //print("ADD SYM:" + atype + ":" + token.toString() + this.ts.lookTok(1).toString() + this.ts.lookTok(2).toString());
440 this.ts.lookTok(-1).data == "." ? token.data : this.fixAlias(aliases,token.data),
445 this.currentDoc = null;
455 continue; // dont care about other idents..
459 //print ("NOT NAME");
462 if (token.isType(TokenType.STRN)) { // THIS WILL NOT HAPPEN HERE?!!?
463 if (this.currentDoc != null) {
464 this.addSymbol(token.data.substring(1,token.data.length-1),false,"OBJECT");
468 // really we only have to deal with object constructs and function calls that change the scope...
471 if (token.isName(TokenName.FUNCTION)) {
472 //print("GOT FUNCTION");
473 // see if we have an unconsumed doc...
475 if (this.currentDoc != null) {
476 GLib.error("Unhandled doc (TOKfunction) %s", token.asString());
483 /// foo = function() {} << really it set"s the "this" scope to foo.prototype
484 //$this$=foo.prototype|$private$|foo.prototype
487 (this.ts.lookTok(-1).data == "=") &&
488 (this.ts.lookTok(-2).isType(TokenType.NAME))
490 scopeName = this.ts.lookTok(-2).data;
491 this.ts.balance(TokenName.LEFT_PAREN);
492 token = this.ts.nextTok(); // should be {
493 //print("FOO=FUNCITON() {}" + this.ts.context() + "\n" + token.toString());
496 scopeName = this.fixAlias(aliases, scopeName);
497 var fnScope = new Scope(this.braceNesting, scope, token.id, // was token.n?
498 "$this$="+scopeName+".prototype|$private$|"+scopeName+".prototype",
502 this.indexedScopes.set(this.ts.cursor, fnScope);
504 //this.scopesIn(fnScope);
505 this.parseScope(fnScope, aliases);
510 //print(">>" +locBraceNest);
511 continue; // no more processing..
517 // foo = new function() {}
518 // is this actually used much!?!?!
522 (this.ts.lookTok(-1).isName(TokenName.NEW)) &&
523 (this.ts.lookTok(-2).data == "=") &&
524 (this.ts.lookTok(-3).isName(TokenName.FUNCTION))
526 //scopeName = this.ts.look(-3).data;
527 this.ts.balance(TokenName.LEFT_PAREN);
528 token = this.ts.nextTok(); // should be {
529 scopeName = this.fixAlias(aliases, scopeName);
530 var fnScope = new Scope(this.braceNesting, scope, token.id, // was token.n?
535 this.indexedScopes.set(this.ts.cursor, fnScope);
537 //this.scopesIn(fnScope);
538 this.parseScope(fnScope, aliases);
541 //print(">>" +locBraceNest);
542 continue; // no more processing..
548 ///==== check/set isObjectLitAr ??
551 // foo: function() {}
552 // no change to scoping..
554 //print("checking for : function() {");
555 //print( [this.ts.lookTok(-3).type , this.ts.lookTok(-2).type , this.ts.lookTok(-1).type ].join(":"));
557 (this.ts.lookTok(-1).data == ":") &&
558 (this.ts.lookTok(-2).isType(TokenType.NAME)) &&
559 (this.ts.lookTok(-3).data == "(" || this.ts.lookTok(-3).data== ",")
561 //print("got for : function() {");
563 //scopeName = this.ts.look(-3).data;
564 this.ts.balance(TokenName.LEFT_PAREN);
565 //print(token.toString())
566 token = this.ts.nextTok(); // should be {
567 //print(token.toString())
569 scopeName = this.fixAlias(aliases, scopeName);
570 var fnScope = new Scope(this.braceNesting, scope, token.id, // was token.n?
574 this.indexedScopes.set(this.ts.cursor, fnScope);
576 //this.scopesIn(fnScope);
577 this.parseScope(fnScope, aliases);
579 //print(">>" +locBraceNest);
580 continue; // no more processing..
583 /// function foo() {} << really it set"s the "this" scope to foo.prototype
584 //$this$=foo|$private$
588 (this.ts.lookTok(1).isType(TokenType.NAME))
590 //scopeName = this.ts.look(-3).data;
591 this.ts.balance(TokenName.LEFT_PAREN);
592 token = this.ts.nextTok(); // should be {
593 var fnScope = new Scope(this.braceNesting, scope, token.id, // was token.n?
597 this.indexedScopes.set(this.ts.cursor, fnScope);
599 //this.scopesIn(fnScope);
600 this.parseScope(fnScope, aliases);
602 //print(">>" +locBraceNest);
603 continue; // no more processing..
608 // foo = new (function() { }
610 // RETURN function(...) {
613 // (this.ts.lookTok(-1).tokN == Script.TOKlparen) &&
614 (!this.ts.lookTok(1).isType(TokenType.NAME))
616 // (this.ts.lookTok(-2).tokN == Script.TOKnew) &&
617 // (this.ts.lookTok(-3).tokN == Script.TOKassign) &&
618 // (this.ts.lookTok(-4).tokN == Script.TOKidentifier)
620 //scopeName = this.ts.look(-3).data;
621 this.ts.balance(TokenName.LEFT_PAREN);
622 token = this.ts.nextTok(); // should be {
623 var fnScope = new Scope(this.braceNesting, scope, token.id, // was token.n?
627 this.indexedScopes.set(this.ts.cursor, fnScope);
629 //this.scopesIn(fnScope);
630 this.parseScope(fnScope, aliases);
632 //print(">>" +locBraceNest);
633 continue; // no more processing..
638 GLib.error( "dont know how to handle function syntax??\n %s" +
648 } // end checking for TOKfunction
650 if (token.data == "{") {
652 // foo = { // !var!!!
657 (this.ts.lookTok(-1).data == "=") &&
658 (this.ts.lookTok(-2).isType(TokenType.NAME)) &&
659 (!this.ts.lookTok(-3).isName(TokenName.VAR))
662 scopeName = this.ts.lookTok(-2).data;
664 scopeName = this.fixAlias(aliases, scopeName);
665 GLib.debug("got %s = {", scopeName);
667 //print(this.scopes.length);
668 var fnScope = new Scope(this.braceNesting, scope, token.id, // was token.n?
669 "$this$=" + scopeName +"|"+scopeName, null
672 this.indexedScopes.set(this.ts.cursor, fnScope);
674 // push the same scope onto the stack..
675 this.scopesIn(fnScope);
676 //this.scopesIn(this.scopes[this.scopes.length-1]);
680 //print(">>" +locBraceNest);
681 continue; // no more processing..
686 //print("GOT LBRACE : check for :");
688 (this.ts.lookTok(-1).data == ":") &&
689 (this.ts.lookTok(-2).isType(TokenType.NAME)) &&
690 (!this.ts.lookTok(-3).isName(TokenName.VAR))
693 scopeName = this.ts.lookTok(-2).data;
694 scopeName = this.fixAlias(aliases, scopeName);
695 var fnScope = new Scope(this.braceNesting, scope, token.id, // was token.n?
699 this.indexedScopes.set(this.ts.cursor, fnScope);
701 this.scopesIn(fnScope);
704 //print(">>" +locBraceNest);
705 continue; // no more processing..
707 var fnScope = new Scope(this.braceNesting, scope, token.id, // was token.n?
711 this.indexedScopes.set(this.ts.cursor, fnScope);
713 this.scopesIn(fnScope);
716 //print(">>" +locBraceNest);
720 if (token.data == "}") {
723 if (this.currentDoc != null) {
724 this.addSymbol("", true);
726 //throw "Unconsumed Doc: (TOKrbrace)" + this.currentDoc.toSource();
732 //assert braceNesting >= scope.getBraceNesting();
733 var closescope = this.scopeOut();
735 scope = this.scopes.get(this.scopes.size-1);
737 //print("<<:" + locBraceNest)
738 //print("<<<<<< " + locBraceNest );
739 if (locBraceNest < 0) {
740 // print("POPED OF END OF SCOPE!");
742 //var ls = this.scopeOut();
743 //ls.getUsedSymbols();
756 void addSymbol(string in_lastIdent, bool appendIt = false, string atype = "OBJECT")
759 GLib.debug("addSymbol %s", in_lastIdent);
760 var lastIdent = in_lastIdent;
761 if (this.currentDoc.getTag(DocTagTitle.PRIVATE).size > 0) {
764 //print(this.currentDoc.toSource());
765 this.currentDoc = null;
766 //print("SKIP ADD SYM: it"s private");
770 var token = this.ts.lookTok(0);
772 // print(this.currentDoc.toSource(););
773 if (this.currentDoc.getTag(DocTagTitle.EVENT).size > 0) {
774 //?? why does it end up in desc - and not name/...
775 //print(this.currentDoc.getTag("event")[0]);
776 lastIdent = "*" + this.currentDoc.getTag(DocTagTitle.EVENT).get(0).desc;
777 //lastIdent = "*" + lastIdent ;
779 if (lastIdent.length < 1 && this.currentDoc.getTag(DocTagTitle.PROPERTY).size > 0) {
780 lastIdent = this.currentDoc.getTag(DocTagTitle.PROPERTY).get(0).name;
781 //lastIdent = "*" + lastIdent ;
785 if (!/\./.match(_s)) {
787 //print("WALKER ADDsymbol: " + lastIdent);
790 GLib.debug("Checking Scopes %d", this.scopes.size);
791 for (var i = 0; i < this.scopes.size;i++) {
792 GLib.debug("Scope %s", this.scopes.get(i).ident);
793 var adds = this.scopes.get(i).ident;
799 GLib.debug("FULLSCOPE: '%s'" , string.joinv("', '", s));
802 s = string.joinv("|", s).split("|");
803 //print("FULLSCOPE: " + s);
804 // print("Walker:ADDSymbol: " + s.join("|") );
809 for (var i = 0; i < s.length;i++) {
811 if (s[i].length < 1) {
814 if ((s[i] == "$private$") || (s[i] == "$global$")) {
818 if (s[i].length > 5 && s[i].substring(0,6) == "$this$") {
819 var ts = s[i].split("=");
821 _s = ""; // ??? VERY QUESTIONABLE!!!
824 // when to use $this$ (probabl for events)
825 _s += _s.length > 0 ? "." : "";
828 GLib.debug("FULLSCOPE: _s=%s (append = %s)" , _s, appendIt? "YES": "no");
831 //print("ADDING SYMBOL: "+ s.join("|") +"\n"+ _s + "\n" +Script.prettyDump(this.currentDoc.toSource()));
832 //print("Walker.addsymbol - add : " + _s);
835 if (appendIt && lastIdent.length < 1) {
837 // append, and no symbol???
839 // see if it"s a @class
840 if (this.currentDoc.getTag(DocTagTitle.CLASS).size > 0) {
841 _s = this.currentDoc.getTag(DocTagTitle.CLASS).get(0).desc;
842 var symbol = new Symbol.new_populate_with_args(_s, new Gee.ArrayList<string>(),
843 "CONSTRUCTOR", this.currentDoc);
845 DocParser.addSymbol(symbol);
846 this.symbols[_s] = symbol;
850 // if (this.currentDoc.getTag("property").length) {
851 // print(Script.pretStringtyDump(this.currentDoc.toSource));
852 // throw "Add Prop?";
855 _s = /\.prototype.*$/.replace(_s, _s.length,0, "");
857 if (!this.symbols.has_key(_s)) {
858 //print("Symbol:" + _s);
859 //print(this.currentDoc.src);
862 // name: "ArgumentError",
863 // message: "Trying to append symbol "" + _s + "", but no doc available\n" +
864 // this.ts.lookTok(0).toString()
866 this.currentDoc = null;
870 GLib.debug("add to symbol _s=%s " , _s);
871 for (var i =0; i < this.currentDoc.tags.size;i++) {
872 this.symbols.get(_s).addDocTag(this.currentDoc.tags.get(i));
874 this.currentDoc = null;
878 //print("Walker.addsymbol - chkdup: " + _s);
879 if (this.symbols.has_key(_s)) {
881 if (this.symbols.get(_s).comment.hasTags) {
882 // then existing comment doesnt has tags
884 // name: "ArgumentError",
885 // message:"DUPLICATE Symbol " + _s + "\n" + token.toString()
889 // otherwise existing comment has tags - overwrite..
893 //print("Walker.addsymbol - ATYPE: " + _s);
898 //print("Walker.addsymbol - add : ");
899 var symbol = new Symbol.new_populate_with_args(
900 _s, new Gee.ArrayList<string>(), atype, this.currentDoc);
902 DocParser.addSymbol(symbol);
903 this.symbols[_s] = symbol;
905 this.currentDoc = null;
912 void scopesIn (Scope s)
915 //print(">>>" + this.ts.context() + "\n>>>"+this.scopes.length+":" +this.scopeListToStr());
921 // print("<<<" + this.ts.context() + "\n<<<"+this.scopes.length+":" +this.scopeListToStr());
922 return this.scopes.remove_at(this.scopes.size -1 );
926 string scopeListToStr ()
929 for (var i = 0; i < this.scopes.size;i++) {
930 s +=(this.scopes[i].ident);
932 return string.joinv("\n\t",s);