/tags/jsdoc_toolkit-2.0.1/jsdoc-toolkit/app/lib/JSDOC/Walker.js
http://jsdoc-toolkit.googlecode.com/ · JavaScript · 416 lines · 301 code · 90 blank · 25 comment · 173 complexity · 8d45962e909f435b03af467d216e7945 MD5 · raw file
- if (typeof JSDOC == "undefined") JSDOC = {};
- /** @constructor */
- JSDOC.Walker = function(/**JSDOC.TokenStream*/ts) {
- this.init();
- if (typeof ts != "undefined") {
- this.walk(ts);
- }
- }
- JSDOC.Walker.prototype.init = function() {
- this.ts = null;
- var globalSymbol = new JSDOC.Symbol("_global_", [], "GLOBAL", new JSDOC.DocComment(""));
- globalSymbol.isNamespace = true;
- globalSymbol.srcFile = "";
- globalSymbol.isPrivate = false;
- JSDOC.Parser.addSymbol(globalSymbol);
- this.lastDoc = null;
- this.token = null;
-
- /**
- The chain of symbols under which we are currently nested.
- @type Array
- */
- this.namescope = [globalSymbol];
- this.namescope.last = function(n){ if (!n) n = 0; return this[this.length-(1+n)] || "" };
- }
- JSDOC.Walker.prototype.walk = function(/**JSDOC.TokenStream*/ts) {
- this.ts = ts;
- while (this.token = this.ts.look()) {
- if (this.token.popNamescope) {
-
- var symbol = this.namescope.pop();
- if (symbol.is("FUNCTION")) {
- if (this.ts.look(1).is("LEFT_PAREN") && symbol.comment.getTag("function").length == 0) {
- symbol.isa = "OBJECT";
- }
- }
- }
- this.step();
- if (!this.ts.next()) break;
- }
- }
- JSDOC.Walker.prototype.step = function() {
- if (this.token.is("JSDOC")) { // it's a doc comment
-
- var doc = new JSDOC.DocComment(this.token.data);
-
- if (doc.getTag("lends").length > 0) { // it's a new namescope
- var lends = doc.getTag("lends")[0];
- var name = lends.desc
- if (!name) throw "@lends tag requires a value.";
-
- var symbol = new JSDOC.Symbol(name, [], "OBJECT", doc);
-
- this.namescope.push(symbol);
-
- var matching = this.ts.getMatchingToken("LEFT_CURLY");
- if (matching) matching.popNamescope = name;
- else LOG.warn("Mismatched } character. Can't parse code.");
-
- this.lastDoc = null;
- return true;
- }
- else if (doc.getTag("name").length > 0 && doc.getTag("overview").length == 0) { // it's a virtual symbol
- var virtualName = doc.getTag("name")[0].desc;
- if (!virtualName) throw "@name tag requires a value.";
-
- var symbol = new JSDOC.Symbol(virtualName, [], "VIRTUAL", doc);
-
- JSDOC.Parser.addSymbol(symbol);
-
- this.lastDoc = null;
- return true;
- }
- else if (doc.meta) { // it's a meta doclet
- if (doc.meta == "@+") JSDOC.DocComment.shared = doc.src;
- else if (doc.meta == "@-") JSDOC.DocComment.shared = "";
- else if (doc.meta == "nocode+") JSDOC.Parser.conf.ignoreCode = true;
- else if (doc.meta == "nocode-") JSDOC.Parser.conf.ignoreCode = JSDOC.opt.n;
- else throw "Unrecognized meta comment: "+doc.meta;
-
- this.lastDoc = null;
- return true;
- }
- else if (doc.getTag("overview").length > 0) { // it's a file overview
- symbol = new JSDOC.Symbol("", [], "FILE", doc);
-
- JSDOC.Parser.addSymbol(symbol);
-
- this.lastDoc = null;
- return true;
- }
- else {
- this.lastDoc = doc;
- return false;
- }
- }
- else if (!JSDOC.Parser.conf.ignoreCode) { // it's code
- if (this.token.is("NAME")) {
- var symbol;
- var name = this.token.data;
- var doc = null; if (this.lastDoc) doc = this.lastDoc;
- var params = [];
-
- // it's inside an anonymous object
- if (this.ts.look(1).is("COLON") && this.ts.look(-1).is("LEFT_CURLY") && !(this.ts.look(-2).is("JSDOC") || this.namescope.last().comment.getTag("lends").length || this.ts.look(-2).is("ASSIGN") || this.ts.look(-2).is("COLON"))) {
- name = "$anonymous";
- name = this.namescope.last().alias+"-"+name
-
- params = [];
-
- symbol = new JSDOC.Symbol(name, params, "OBJECT", doc);
- JSDOC.Parser.addSymbol(symbol);
-
- this.namescope.push(symbol);
-
- var matching = this.ts.getMatchingToken(null, "RIGHT_CURLY");
- if (matching) matching.popNamescope = name;
- else LOG.warn("Mismatched } character. Can't parse code.");
- }
- // function foo() {}
- else if (this.ts.look(-1).is("FUNCTION") && this.ts.look(1).is("LEFT_PAREN")) {
- var isInner;
-
- if (this.lastDoc) doc = this.lastDoc;
- name = this.namescope.last().alias+"-"+name;
- if (!this.namescope.last().is("GLOBAL")) isInner = true;
-
- params = JSDOC.Walker.onParamList(this.ts.balance("LEFT_PAREN"));
-
- symbol = new JSDOC.Symbol(name, params, "FUNCTION", doc);
- if (isInner) symbol.isInner = true;
-
-
- JSDOC.Parser.addSymbol(symbol);
-
- this.namescope.push(symbol);
-
- var matching = this.ts.getMatchingToken("LEFT_CURLY");
- if (matching) matching.popNamescope = name;
- else LOG.warn("Mismatched } character. Can't parse code.");
- }
- // foo = function() {}
- else if (this.ts.look(1).is("ASSIGN") && this.ts.look(2).is("FUNCTION")) {
- var isInner;
- if (this.ts.look(-1).is("VAR") || this.isInner) {
- name = this.namescope.last().alias+"-"+name
- if (!this.namescope.last().is("GLOBAL")) isInner = true;
- }
- else if (name.indexOf("this.") == 0) {
- name = this.resolveThis(name);
- }
-
- if (this.lastDoc) doc = this.lastDoc;
- params = JSDOC.Walker.onParamList(this.ts.balance("LEFT_PAREN"));
-
- symbol = new JSDOC.Symbol(name, params, "FUNCTION", doc);
- if (isInner) symbol.isInner = true;
-
- JSDOC.Parser.addSymbol(symbol);
-
- this.namescope.push(symbol);
-
- var matching = this.ts.getMatchingToken("LEFT_CURLY");
- if (matching) matching.popNamescope = name;
- else LOG.warn("Mismatched } character. Can't parse code.");
- }
- // foo = new function() {}
- else if (this.ts.look(1).is("ASSIGN") && this.ts.look(2).is("NEW") && this.ts.look(3).is("FUNCTION")) {
- var isInner;
- if (this.ts.look(-1).is("VAR") || this.isInner) {
- name = this.namescope.last().alias+"-"+name
- if (!this.namescope.last().is("GLOBAL")) isInner = true;
- }
- else if (name.indexOf("this.") == 0) {
- name = this.resolveThis(name);
- }
-
- if (this.lastDoc) doc = this.lastDoc;
- params = JSDOC.Walker.onParamList(this.ts.balance("LEFT_PAREN"));
-
- symbol = new JSDOC.Symbol(name, params, "OBJECT", doc);
- if (isInner) symbol.isInner = true;
-
-
- JSDOC.Parser.addSymbol(symbol);
-
- symbol.scopeType = "INSTANCE";
- this.namescope.push(symbol);
-
- var matching = this.ts.getMatchingToken("LEFT_CURLY");
- if (matching) matching.popNamescope = name;
- else LOG.warn("Mismatched } character. Can't parse code.");
- }
- // foo: function() {}
- else if (this.ts.look(1).is("COLON") && this.ts.look(2).is("FUNCTION")) {
- name = (this.namescope.last().alias+"."+name).replace("#.", "#");
-
- if (this.lastDoc) doc = this.lastDoc;
- params = JSDOC.Walker.onParamList(this.ts.balance("LEFT_PAREN"));
-
- if (doc && doc.getTag("constructs").length) {
- name = name.replace(/\.prototype(\.|$)/, "#");
-
- if (name.indexOf("#") > -1) name = name.match(/(^[^#]+)/)[0];
- else name = this.namescope.last().alias;
- symbol = new JSDOC.Symbol(name, params, "CONSTRUCTOR", doc);
- }
- else {
- symbol = new JSDOC.Symbol(name, params, "FUNCTION", doc);
- }
-
-
- JSDOC.Parser.addSymbol(symbol);
-
- this.namescope.push(symbol);
-
- var matching = this.ts.getMatchingToken("LEFT_CURLY");
- if (matching) matching.popNamescope = name;
- else LOG.warn("Mismatched } character. Can't parse code.");
- }
- // foo = {}
- else if (this.ts.look(1).is("ASSIGN") && this.ts.look(2).is("LEFT_CURLY")) {
- var isInner;
- if (this.ts.look(-1).is("VAR") || this.isInner) {
- name = this.namescope.last().alias+"-"+name
- if (!this.namescope.last().is("GLOBAL")) isInner = true;
- }
- else if (name.indexOf("this.") == 0) {
- name = this.resolveThis(name);
- }
-
- if (this.lastDoc) doc = this.lastDoc;
-
- symbol = new JSDOC.Symbol(name, params, "OBJECT", doc);
- if (isInner) symbol.isInner = true;
-
-
- if (doc) JSDOC.Parser.addSymbol(symbol);
- this.namescope.push(symbol);
-
- var matching = this.ts.getMatchingToken("LEFT_CURLY");
- if (matching) matching.popNamescope = name;
- else LOG.warn("Mismatched } character. Can't parse code.");
- }
- // foo = x
- else if (this.ts.look(1).is("ASSIGN")) {
-
- var isInner;
- if (this.ts.look(-1).is("VAR") || this.isInner) {
- name = this.namescope.last().alias+"-"+name
- if (!this.namescope.last().is("GLOBAL")) isInner = true;
- }
- else if (name.indexOf("this.") == 0) {
- name = this.resolveThis(name);
- }
-
- if (this.lastDoc) doc = this.lastDoc;
-
- symbol = new JSDOC.Symbol(name, params, "OBJECT", doc);
- if (isInner) symbol.isInner = true;
-
-
- if (doc) JSDOC.Parser.addSymbol(symbol);
- }
- // foo: {}
- else if (this.ts.look(1).is("COLON") && this.ts.look(2).is("LEFT_CURLY")) {
- name = (this.namescope.last().alias+"."+name).replace("#.", "#");
-
- if (this.lastDoc) doc = this.lastDoc;
-
- symbol = new JSDOC.Symbol(name, params, "OBJECT", doc);
-
-
- if (doc) JSDOC.Parser.addSymbol(symbol);
-
- this.namescope.push(symbol);
-
- var matching = this.ts.getMatchingToken("LEFT_CURLY");
- if (matching) matching.popNamescope = name;
- else LOG.warn("Mismatched } character. Can't parse code.");
- }
- // foo: x
- else if (this.ts.look(1).is("COLON")) {
- name = (this.namescope.last().alias+"."+name).replace("#.", "#");;
-
- if (this.lastDoc) doc = this.lastDoc;
-
- symbol = new JSDOC.Symbol(name, params, "OBJECT", doc);
-
-
- if (doc) JSDOC.Parser.addSymbol(symbol);
- }
- // foo(...)
- else if (this.ts.look(1).is("LEFT_PAREN")) {
- var functionCall = {name: name};
- if (!this.ts.look(2).is("RIGHT_PAREN")) functionCall.arg1 = this.ts.look(2).data;
-
- if (typeof JSDOC.PluginManager != "undefined") {
- JSDOC.PluginManager.run("onFunctionCall", functionCall);
- if (functionCall.doc) {
- this.ts.insertAhead(new JSDOC.Token(functionCall.doc, "COMM", "JSDOC"));
- }
- }
- }
- this.lastDoc = null;
- }
- else if (this.token.is("FUNCTION")) { // it's an anonymous function
- if (
- (!this.ts.look(-1).is("COLON") || !this.ts.look(-1).is("ASSIGN"))
- && !this.ts.look(1).is("NAME")
- ) {
- if (this.lastDoc) doc = this.lastDoc;
-
- name = "$anonymous";
- name = this.namescope.last().alias+"-"+name
-
- params = JSDOC.Walker.onParamList(this.ts.balance("LEFT_PAREN"));
-
- symbol = new JSDOC.Symbol(name, params, "FUNCTION", doc);
-
-
- JSDOC.Parser.addSymbol(symbol);
-
- this.namescope.push(symbol);
-
- var matching = this.ts.getMatchingToken("LEFT_CURLY");
- if (matching) matching.popNamescope = name;
- else LOG.warn("Mismatched } character. Can't parse code.");
- }
- }
- }
- return true;
- }
- /**
- Resolves what "this." means when it appears in a name.
- @param name The name that starts with "this.".
- @returns The name with "this." resolved.
- */
- JSDOC.Walker.prototype.resolveThis = function(name) {
- name.match(/^this\.(.+)$/)
- var nameFragment = RegExp.$1;
- if (!nameFragment) return name;
-
- var symbol = this.namescope.last();
- var scopeType = symbol.scopeType || symbol.isa;
- // if we are in a constructor function, `this` means the instance
- if (scopeType == "CONSTRUCTOR") {
- name = symbol.alias+"#"+nameFragment;
- }
-
- // if we are in an anonymous constructor function, `this` means the instance
- else if (scopeType == "INSTANCE") {
- name = symbol.alias+"."+nameFragment;
- }
-
- // if we are in a function, `this` means the container (possibly the global)
- else if (scopeType == "FUNCTION") {
- // in a method of a prototype, so `this` means the constructor
- if (symbol.alias.match(/(^.*)[#.-][^#.-]+/)) {
- var parentName = RegExp.$1;
- var parent = JSDOC.Parser.symbols.getSymbol(parentName);
- if (!parent) {
- if (JSDOC.Lang.isBuiltin(parentName)) parent = JSDOC.Parser.addBuiltin(parentName);
- else {
- if (symbol.alias.indexOf("$anonymous") < 0) // these will be ignored eventually
- LOG.warn("Can't document "+symbol.alias+" without first documenting "+parentName+".");
- }
- }
- if (parent) name = parentName+(parent.is("CONSTRUCTOR")?"#":".")+nameFragment;
- }
- else {
- parent = this.namescope.last(1);
- name = parent.alias+(parent.is("CONSTRUCTOR")?"#":".")+nameFragment;
- }
- }
- // otherwise it means the global
- else {
- name = nameFragment;
- }
-
- return name;
- }
- JSDOC.Walker.onParamList = function(/**Array*/paramTokens) {
- if (!paramTokens) {
- LOG.warn("Malformed parameter list. Can't parse code.");
- return [];
- }
- var params = [];
- for (var i = 0, l = paramTokens.length; i < l; i++) {
- if (paramTokens[i].is("JSDOC")) {
- var paramType = paramTokens[i].data.replace(/(^\/\*\* *| *\*\/$)/g, "");
-
- if (paramTokens[i+1] && paramTokens[i+1].is("NAME")) {
- i++;
- params.push({type: paramType, name: paramTokens[i].data});
- }
- }
- else if (paramTokens[i].is("NAME")) {
- params.push({name: paramTokens[i].data});
- }
- }
- return params;
- }