178 lines
4.7 KiB
JavaScript
178 lines
4.7 KiB
JavaScript
/*
|
|
Copyright (c) 2012, Oracle and/or its affiliates. All rights
|
|
reserved.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; version 2 of
|
|
the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
|
02110-1301 USA
|
|
*/
|
|
|
|
var udebug = unified_debug.getLogger("doc_parser.js"),
|
|
fs = require("fs");
|
|
|
|
/* Returns a list of function definitions
|
|
*/
|
|
function scan(text) {
|
|
var i = 0; // the index of the current character
|
|
var c = text.charAt(i); // the current character
|
|
var list = []; // functions found in the file
|
|
var constructor = ""; // constructor function found in file
|
|
var tok; // the current token
|
|
|
|
function isUpper(c) { return (c >= 'A' && c <= 'Z'); }
|
|
function isLower(c) { return (c >= 'a' && c <= 'z'); }
|
|
function isAlpha(c) { return (isUpper(c) || isLower(c)); }
|
|
function isNumeric(c) { return (c >= '0' && c <= '9'); }
|
|
function isJsFunctionName(c) {
|
|
return( isAlpha(c) || isNumeric(c) || (c == '_'));
|
|
}
|
|
|
|
function peek() {
|
|
return text.charAt(i + 1);
|
|
}
|
|
|
|
function advance(n) { // Advance to next character
|
|
var amt = n || 1;
|
|
if(i + amt >= text.length) {
|
|
i = text.length;
|
|
c = '';
|
|
}
|
|
else {
|
|
i += amt;
|
|
c = text.charAt(i);
|
|
}
|
|
}
|
|
|
|
function Token() {
|
|
this.str = c;
|
|
advance();
|
|
}
|
|
|
|
Token.prototype.consume = function() {
|
|
this.str += c;
|
|
advance();
|
|
};
|
|
|
|
Token.prototype.commit = function() {
|
|
udebug.log("found function:", this.str);
|
|
list.push(this.str);
|
|
if(isUpper(this.str.charAt(0))) { constructor = this.str; }
|
|
};
|
|
|
|
// Start scanning
|
|
while(c) {
|
|
|
|
while(c != '' && c <= ' ') { advance(); } // whitespace
|
|
|
|
if(c == '/' && peek() == '/') { // comment to EOL
|
|
advance(2);
|
|
while(c !== '\n' && c !== '\r' && c !== '') {
|
|
advance();
|
|
}
|
|
}
|
|
|
|
else if (c === '/' && peek() === '*') { // comment to */
|
|
advance(2);
|
|
while(! (c == '*' && peek() == '/')) {
|
|
advance();
|
|
}
|
|
if(c === '') { throw Error("Unterminated comment"); }
|
|
advance(2);
|
|
}
|
|
|
|
else if(isAlpha(c)) { // candidate functions
|
|
tok = new Token();
|
|
while(isJsFunctionName(c)) {
|
|
tok.consume();
|
|
}
|
|
if(c == '(') { // IT WAS A FUNCTION
|
|
tok.commit();
|
|
advance();
|
|
/* Now, there may be more functions (callbacks) defined as arguments,
|
|
so we skip to the next semicolon */
|
|
while(c && c !== ';') {
|
|
advance();
|
|
}
|
|
}
|
|
delete tok;
|
|
}
|
|
|
|
else {
|
|
advance();
|
|
}
|
|
}
|
|
list._found_constructor = constructor;
|
|
return list;
|
|
}
|
|
|
|
|
|
function listFunctions(docFileName) {
|
|
var text = fs.readFileSync(docFileName, 'utf8');
|
|
return scan(text);
|
|
}
|
|
|
|
function ClassTester(obj, docClassName) {
|
|
this.class = obj;
|
|
this.file = docClassName;
|
|
}
|
|
|
|
ClassTester.prototype.test = function(functionList, testCase) {
|
|
var func, name;
|
|
var msg = "";
|
|
var missing = 0;
|
|
var firstMissing = null;
|
|
var documentedFunctions = {};
|
|
var i;
|
|
|
|
udebug.log("verifying",functionList.length,"functions");
|
|
|
|
// Test missing functions
|
|
for(i = 0 ; i < functionList.length ; i++) {
|
|
name = functionList[i];
|
|
func = this.class[name];
|
|
documentedFunctions[name] = func;
|
|
if(typeof func !== 'function' && functionList._found_constructor !== name) {
|
|
udebug.log("MISSING FUNCTION", this.file, name);
|
|
if(! firstMissing) { firstMissing = name; }
|
|
missing += 1;
|
|
}
|
|
}
|
|
if(missing) {
|
|
msg = "Missing " + firstMissing;
|
|
if(missing > 1) { msg += " and " + (missing-1) + " other function"; }
|
|
if(missing > 2) { msg += "s"; }
|
|
}
|
|
|
|
// Test undocumented functions
|
|
for(name in this.class) {
|
|
if(this.class.hasOwnProperty(name)) {
|
|
if(typeof this.class[name] === 'function') {
|
|
if(! documentedFunctions[name]) {
|
|
if(msg.length) msg += "; "
|
|
msg += name + " undocumented";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(msg) {
|
|
if(testCase) { testCase.appendErrorMessage(msg); }
|
|
else { throw new Error(msg); }
|
|
}
|
|
}
|
|
|
|
exports.listFunctions = listFunctions;
|
|
exports.ClassTester = ClassTester;
|
|
|