New files:

no_terminator.js 
- Example of statements that do not have line terminators, which preceed a closing }.

secondary_fcn_name.js
- Example of a function having two names.
- var D1 = function other_name();

jsFunc_tutorial.js
- Created this off a website that lists the various syntax options available when
  declaring functions, classes, variables and so on.
- This signgicantly changed the jscript.js parser to handle these new cases and slight 
  twists.

New language elements:
var fcn_name = new Function(...);
global variables were not correctly parsed, or more specifically these interferred with 
correctly parsing functions of this format:
  var D1 = function ...



git-svn-id: svn://svn.code.sf.net/p/ctags/code/trunk@470 c5d04d22-be80-434c-894e-aa346cc9e8e8
This commit is contained in:
dfishburn 2006-10-11 01:16:16 +00:00
parent 49e76a8527
commit e5f0ee00ad
4 changed files with 800 additions and 89 deletions

524
Test/jsFunc_tutorial.js Normal file
View File

@ -0,0 +1,524 @@
/*
* These samples were retrieved from this website:
* http://www.permadi.com/tutorial/jsFunc/
*
* This the output you should see from running:
* ctags -f - Test/jsFunc_tutorial.js
* functions
* Ball1
* Ball3
* D1
* D2
* D2A
* D3
* D4
* D5
* DT1
* DT2
* DT2A
* DT3
* PT1
* calculate8
* getHalfOf7
* getHalfOf7.calculate
* getHalfOf8
* getSalaryFunctionDT9
* myFunction4
* myFunction5
* myFunction6
* myFunction6A
* myFunction6AE
* myFunction6B
* myFunction6E
* myObject.add
* savedFunc6B
* sayName4A
* theAdd
* classes
* DT4
* DT5
* DT6
* DT7
* DT7A
* DT8
* DT9
* PT2
* PT3
* addSalaryFunction
* addSalaryFunctionDT9
* methods
* DT7.getSalary
* DT7A.getSalary
* DT8.getSalary
* PT2.livesIn
* PT2.price
* PT3.addSalary
* PT3.getSalary
* variables
* my_global_var1
*/
// Example D1
function D1(a, b)
{
return a+b;
}
alert(D1(1,2)); // produces 3
var my_global_var1 = 'global';
// Example D2
var D2=function(a, b)
{
return a+b;
}
alert(D2(1,2)); // produces 3
// Example D2A
var D2A=function theAdd(a, b)
{
return a+b;
}
alert(D2A(1,2)); // produces 3
alert(theAdd(1,2)); // also produces 3
var myObject=new Object();
myObject.add=function(a,b){return a+b};
// myObject now has a property/a method named "add"
// and I can use it like below
myObject.add(1, 2);
// Example D3
var D3=new Function("a", "b", "return a+b;");
alert(D3(3,4)); // produces 7
// Example D4
var D4=new Function("a", "b",
"alert" + // chop string using "+"
"('adding '+a+' and ' +b);\ // separate string using "\"
return a+b;");
alert(D4(3,4)); // produces 7
// Example D5
function D5(myOperator)
{
return new Function("a", "b", "return a" + myOperator + "b;");
}
var add=D5("+"); // creates "add" function
var subtract=D5("-"); // creates "subtract" function
var multiply=D5("*"); // created "multiply" function
// test the functions
alert("result of add="+add(10,2)); // result is 12
alert("result of substract="+subtract(10,2)); // result is 8
alert("result of multiply="+multiply(10,2)); // result is 20
alert(add);
// Example 1
function Ball1() // it may seem odd, but this declaration
{ // creates a object named Ball
i=1;
}
alert(typeof Ball1); // produces "function"
// Example 3
function Ball3() // it may seem odd, but declaration
{ // creates an object named Ball, and you can
} // refer to it or add properties to it like below
Ball3.callsign="The Ball"; // add property to Ball
alert(Ball3.callsign); // produces "The Ball"
// Example 4
function myFunction4(message)
{
alert(message);
}
var ptr=myFunction4; // ptr points to myFunction
ptr("hello"); // executes myFunction which will prints "hello"
// Example 4A
function sayName4A(name)
{
alert(name);
}
var object1=new Object(); // creates 3 objects
var object2=new Object();
var object3=new Object();
object1.sayMyName4A=sayName; // assign the function to all objects
object2.sayMyName4A=sayName;
object3.sayMyName4A=sayName;
object1.sayMyName4A("object1"); // prints "object1"
object2.sayMyName4A("object2"); // prints "object2"
object3.sayMyName4A("object3"); // prints "object3"
// Example 5
function myFunction5()
{
alert(myFunction.message);
}
myFunction5.message="old";
var ptr1=myFunction5; // ptr1 points to myFunction
var ptr2=myFunction5; // ptr2 also points to myFunction
ptr1(); // prints "old"
ptr2(); // prints "old"
myFunction5.message="new";
ptr1(); // prints "new"
ptr2(); // prints "new"
//Example 6:
function myFunction6()
{
alert("Old");
}
myFunction6(); // prints "Old"
myFunction6E=function()
{
alert("New");
};
myFunction6E(); // prints "New"
//Example 6A:
function myFunction6A()
{
alert("Old");
}
var savedFunction=myFunction6A;
myFunction6AE=function()
{
alert("New");
};
myFunction6AE(); // prints "New"
savedFunction(); // printf "Old"
//Example 6B:
function myFunction6B()
{
alert("Old");
}
var savedFunc=myFunction6B;
savedFunc6B=function()
{
alert("New");
};
myFunction6B(); // prints "Old"
savedFunc6B(); // prints "New"
// Example 7
function getHalfOf7(num1, num2, num3)
{
function calculate(number)
{
return number/2;
}
var result="";
result+=calculate(num1)+" ";
result+=calculate(num2)+" ";
result+=calculate(num3);
}
var resultString=getHalfOf7(10,20,30);
alert(resultString); // prints "5 10 15"
// Example 8
function calculate8(number)
{
return number/3;
}
function getHalfOf8(num1, num2, num3)
{
function calculate(number)
{
return number/2;
}
var result="";
result+=calculate(num1)+" ";
result+=calculate(num2)+" ";
result+=calculate(num3);
}
var resultString=getHalfOf8(10,20,30);
alert(resultString); // prints "5 10 15"
// Example DT1
function DT1()
{
}
var ball0=new DT1(); // ball0 now points to a new object
alert(ball0); // prints "Object" because ball0 is now an Object
// Example DT2
function DT2(message)
{
alert(message);
}
var ball1=new DT2("creating new Ball"); // creates object &
// prints the message
ball1.name="ball-1"; // ball0 now has a "name" property
alert(ball1.name); // prints "ball-0"
// Example DT2A
function DT2A(message)
{
alert(message);
}
var ball2=new Object();
ball2.construct=DT2A;
ball2.construct("creating new ball2"); // executes ball0.Ball("creating..");
ball2.name="ball-2";
alert(ball2.name);
// Example DT3 (creates 3 ball objects)
function DT3()
{
}
var ball3=new DT3(); // ball0 now points to a new instance of type Ball
ball3.name="ball-3"; // ball0 now has a "name" property
var ball4=new DT3();
ball4.name="ball-4";
var ball5=new DT3();
alert(ball0.name); // prints "ball-0"
alert(ball1.name); // prints "ball-1"
alert(ball2.name); // oops, I forgot to add "name" to ball2!
// Example DT4
function DT4(message, specifiedName)
{
alert(message);
this.name=specifiedName;
}
var ball6=new DT4("creating new Ball", "Soccer Ball");
alert(ball6.name); // prints "Soccer Ball"
// Example DT5
function DT5(color, specifiedName, owner, weight)
{
this.name=specifiedName;
this.color=color;
this.owner=owner;
this.weight=weigth;
}
var ball7=new DT5("black/white", "Soccer Ball", "John", 20);
var ball8=new DT5("gray", "Bowling Ball", "John", 30);
var ball9=new DT5("yellow", "Golf Ball", "John", 55);
var balloon=new DT5("red", "Balloon", "Pete", 10);
alert(ball7.name); // prints "Soccer Ball"
alert(balloon.name); // prints "Balloon"
alert(ball9.weight); // prints "55"
// Example DT6
function DT6(name, salary, mySupervisor)
{
this.name=name;
this.salary=salary;
this.supervisor=mySupervisor;
}
var boss=new DT6("John", 200);
var manager=new DT6("Joan", 50, boss);
var teamLeader=new DT6("Rose", 50, boss);
alert(manager.supervisor.name+" is the supervisor of "+manager.name);
alert(manager.name+"\'s supervisor is "+manager.supervisor.name);
// Example DT7
function DT7(name, salary)
{
this.name=name;
this.salary=salary;
this.addSalary=addSalaryFunction;
this.getSalary=function()
{
return this.salary;
};
}
function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}
var boss=new DT7("John", 200000);
boss.addSalary(10000); // boss gets 10K raise
alert(boss.getSalary()); // print 210K
function DT7A(name, salary)
{
this.name=name;
this.salary=salary;
this.addSalary=addSalaryFunction;
this.getSalary=function()
{
return this.salary;
};
}
function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}
var boss=new DT7A("John", 200000);
var boss2=new DT7A("Joan", 200000);
var boss3=new DT7A("Kim", 200000);
// Example DT8
function DT8(name, salary)
{
this.name=name;
this.salary=salary;
this.addSalary=addSalaryFunction;
this.getSalary=function()
{
return this.salary;
};
}
function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}
var boss1=new DT8("John", 200000);
var boss2=new DT8("Joan", 200000);
// add properties to getSalary function object.
boss1.getSalary.owner="boss1";
boss2.getSalary.owner="boss2";
alert(boss1.getSalary.owner); // prints "boss1"
alert(boss2.getSalary.owner); // prints "boss2"
// if both objects are pointing to the same function object, then
// both output above should have printed "boss2".
// add properties to addSalary function object.
boss1.addSalary.owner="boss1";
boss1.addSalary.owner="boss2";
alert(boss1.addSalary.owner); // prints "boss2"
alert(boss2.addSalary.owner); // prints "boss2"
// since both objects are not pointing to the same function,
// then changes in one, affects all instances (so, both prints "boss2").
// Example DT9
function DT9(name, salary)
{
this.name=name;
this.salary=salary;
this.addSalary=addSalaryFunctionDT9;
this.getSalary=getSalaryFunctionDT9;
}
function getSalaryFunctionDT9()
{
return this.salary;
}
function addSalaryFunctionDT9(addition)
{
this.salary=this.salary+addition;
}
// Example PT1
function PT1()
{
}
alert(PT1.prototype); // prints "Object"
// Example PT2
function PT2(name, color)
{
this.name=name;
this.color=color;
}
PT2.prototype.livesIn="water";
PT2.prototype.price=20;
// Example PT3
function PT3(name, salary)
{
this.name=name;
this.salary=salary;
}
PT3.prototype.getSalary=function getSalaryFunction()
{
return this.salary;
}
PT3.prototype.addSalary=function addSalaryFunction(addition)
{
this.salary=this.salary+addition;
}

32
Test/no_terminator.js Normal file
View File

@ -0,0 +1,32 @@
function ts_resortTable(lnk) {
if (span.getAttribute("sortdir") == 'down') {
span.setAttribute('sortdir','up');
} else {
span.setAttribute('sortdir','down');
}
}
function getParent(el, pTagName) {
if (el == null) return null;
else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase()) // Gecko bug, supposed to be uppercase
return el;
else
return getParent(el.parentNode, pTagName);
}
function ts_sort_currency(a,b) {
aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g,'');
bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g,'');
return parseFloat(aa) - parseFloat(bb);
}
function checkForUpdate() {
if( 1==1 ) {
document.write("hello from checkForUpdate<br>")
}
return 1;
}
function checkForUpdate2() {
if( 1==1 ) {
document.write("hello from checkForUpdate<br>");
}
return 2;
}

View File

@ -0,0 +1,40 @@
/*
* ctags should return the following for parsing this file using:
* ctags -f - test.js
* functions
* D1
* D2
* D2A
* theAdd
* variables
* global
*/
function D1(a, b)
{
var my_local_var1 = 'local';
return a+b;
}
alert(D1(1,2)); // produces 3
// Example D2
var D2=function(a, b)
{
return a+b;
}
alert(D2(1,2)); // produces 3
var my_global_var1 = 'global';
// Example D2A
// Tags should be generated for both:
// D2A
// theAdd
var D2A=function theAdd(a, b)
{
return a+b;
}
alert(D2A(1,2)); // produces 3
alert(theAdd(1,2)); // also produces 3

293
jscript.c
View File

@ -44,22 +44,26 @@
typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
/*
* Tracks class names already created
* Tracks class and function names already created
*/
static stringList *ClassNames;
static stringList *FunctionNames;
/* Used to specify type of keyword.
*/
typedef enum eKeywordId {
KEYWORD_NONE = -1,
KEYWORD_function,
KEYWORD_capital_function,
KEYWORD_prototype,
KEYWORD_var,
KEYWORD_new,
KEYWORD_this,
KEYWORD_for,
KEYWORD_while,
KEYWORD_do,
KEYWORD_if,
KEYWORD_else,
KEYWORD_switch
} keywordId;
@ -97,6 +101,7 @@ typedef struct sTokenInfo {
vString * scope;
unsigned long lineNumber;
fpos_t filePosition;
int nestLevel;
} tokenInfo;
/*
@ -124,15 +129,18 @@ static kindOption JsKinds [] = {
static const keywordDesc JsKeywordTable [] = {
/* keyword keyword ID */
{ "function", KEYWORD_function },
{ "prototype", KEYWORD_prototype },
{ "var", KEYWORD_var },
{ "this", KEYWORD_this },
{ "for", KEYWORD_for },
{ "while", KEYWORD_while },
{ "do", KEYWORD_do },
{ "if", KEYWORD_if },
{ "switch", KEYWORD_switch }
{ "function", KEYWORD_function },
{ "Function", KEYWORD_capital_function },
{ "prototype", KEYWORD_prototype },
{ "var", KEYWORD_var },
{ "new", KEYWORD_new },
{ "this", KEYWORD_this },
{ "for", KEYWORD_for },
{ "while", KEYWORD_while },
{ "do", KEYWORD_do },
{ "if", KEYWORD_if },
{ "else", KEYWORD_else },
{ "switch", KEYWORD_switch }
};
/*
@ -141,10 +149,10 @@ static const keywordDesc JsKeywordTable [] = {
static void makeConstTag (tokenInfo *const token, const jsKind kind);
static void readToken (tokenInfo *const token);
static boolean parseBlock (tokenInfo *const token, tokenInfo *const parent);
static void parseFunction (tokenInfo *const token);
static void parseLine (tokenInfo *const token, boolean is_inside_class);
static void parseStatement (tokenInfo *const token, boolean is_inside_class);
static boolean parseStatement (tokenInfo *const token, boolean is_inside_class);
static boolean parseBlock (tokenInfo *const token, tokenInfo *const parent);
static boolean parseLine (tokenInfo *const token, boolean is_inside_class);
static boolean isIdentChar1 (const int c)
{
@ -218,6 +226,7 @@ static tokenInfo *newToken (void)
token->keyword = KEYWORD_NONE;
token->string = vStringNew ();
token->scope = vStringNew ();
token->nestLevel = 0;
return token;
}
@ -283,6 +292,15 @@ static void makeClassTag (tokenInfo *const token)
}
}
static void makeFunctionTag (tokenInfo *const token)
{
if ( ! stringListHas(FunctionNames, vStringValue (token->string)) )
{
stringListAdd (FunctionNames, vStringNewCopy (token->string));
makeJsTag (token, JSTAG_FUNCTION);
}
}
/*
* Parsing functions
*/
@ -343,6 +361,7 @@ static keywordId analyzeToken (vString *const name)
static void copyToken (tokenInfo *const dest, tokenInfo *const src)
{
dest->nestLevel = src->nestLevel;
dest->lineNumber = src->lineNumber;
dest->filePosition = src->filePosition;
dest->type = src->type;
@ -659,8 +678,10 @@ static void parseIf (tokenInfo *const token)
static boolean parseBlock (tokenInfo *const token, tokenInfo *const parent)
{
boolean is_class = FALSE;
boolean read_next_token = TRUE;
vString * saveScope = vStringNew ();
token->nestLevel++;
/*
* Make this routine a bit more forgiving.
* If called on an open_curly advance it
@ -677,6 +698,7 @@ static boolean parseBlock (tokenInfo *const token, tokenInfo *const parent)
*/
do
{
read_next_token = TRUE;
if (isKeyword (token, KEYWORD_this))
{
/*
@ -721,15 +743,31 @@ static boolean parseBlock (tokenInfo *const token, tokenInfo *const parent)
}
else
{
parseLine (token, is_class);
/*
* It is possible for a line to have no terminator
* if the following line is a closing brace.
* parseLine will detect this case and indicate
* whether we should read an additional token.
*/
read_next_token = parseLine (token, is_class);
}
readToken(token);
/*
* Always read a new token unless we find a statement without
* a ending terminator
*/
if( read_next_token )
readToken(token);
} while (! isType (token, TOKEN_CLOSE_CURLY));
/*
* If we find a statement without a terminator consider the
* block finished, otherwise the stack will be off by one.
*/
} while (! isType (token, TOKEN_CLOSE_CURLY) && read_next_token );
}
vStringDelete(saveScope);
token->nestLevel--;
return is_class;
}
@ -797,6 +835,7 @@ static void parseFunction (tokenInfo *const token)
readToken (name);
// Add scope in case this is an INNER function
addToScope(name, token->scope);
readToken (token);
if (isType (token, TOKEN_PERIOD))
{
@ -812,21 +851,15 @@ static void parseFunction (tokenInfo *const token)
}
if ( isType (token, TOKEN_OPEN_PAREN) )
{
skipArgumentList(token);
}
if ( isType (token, TOKEN_OPEN_CURLY) )
{
is_class = parseBlock (token, name);
if ( is_class )
{
makeClassTag (name);
}
else
{
makeJsTag (name, JSTAG_FUNCTION);
}
makeFunctionTag (name);
}
findCmdTerm (token);
@ -834,26 +867,17 @@ static void parseFunction (tokenInfo *const token)
deleteToken (name);
}
static void parseLine (tokenInfo *const token, boolean is_inside_class)
static boolean parseLine (tokenInfo *const token, boolean is_inside_class)
{
boolean is_terminated = TRUE;
/*
* Functions can be named or unnamed.
* This deals with these formats:
* Function
* validFunctionOne = function(a,b) {}
* testlib.validFunctionFive = function(a,b) {}
* var innerThree = function(a,b) {}
* var innerFour = (a,b) {}
* Class
* testlib.extras.ValidClassOne = function(a,b) {
* this.a = a;
* }
* Class Methods
* testlib.extras.ValidClassOne.prototype = {
* 'validMethodOne' : function(a,b) {},
* 'validMethodTwo' : function(a,b) {}
* }
* Database.prototype.getTodaysDate = Database_getTodaysDate;
* Detect the common statements, if, while, for, do, ...
* This is necessary since the last statement within a block "{}"
* can be optionally terminated.
*
* If the statement is not terminated, we need to tell
* the calling routine to prevent reading an additional token
* looking for the end of the statement.
*/
if (isType(token, TOKEN_KEYWORD))
@ -866,6 +890,7 @@ static void parseLine (tokenInfo *const token, boolean is_inside_class)
parseLoop (token);
break;
case KEYWORD_if:
case KEYWORD_else:
parseIf (token);
break;
case KEYWORD_switch:
@ -878,15 +903,24 @@ static void parseLine (tokenInfo *const token, boolean is_inside_class)
}
else
{
parseStatement (token, is_inside_class);
/*
* Special case where single line statements may not be
* SEMICOLON termianted. parseBlock needs to know this
* so that it does not read the next token.
*/
is_terminated = parseStatement (token, is_inside_class);
}
return is_terminated;
}
static void parseStatement (tokenInfo *const token, boolean is_inside_class)
static boolean parseStatement (tokenInfo *const token, boolean is_inside_class)
{
tokenInfo *const name = newToken ();
tokenInfo *const secondary_name = newToken ();
vString * saveScope = vStringNew ();
boolean is_class = FALSE;
boolean is_terminated = TRUE;
boolean is_global = FALSE;
vStringClear(saveScope);
/*
@ -897,6 +931,8 @@ static void parseStatement (tokenInfo *const token, boolean is_inside_class)
* testlib.validFunctionFive = function(a,b) {}
* var innerThree = function(a,b) {}
* var innerFour = (a,b) {}
* var D2 = secondary_fcn_name(a,b) {}
* var D3 = new Function("a", "b", "return a+b;");
* Class
* testlib.extras.ValidClassOne = function(a,b) {
* this.a = a;
@ -915,7 +951,16 @@ static void parseStatement (tokenInfo *const token, boolean is_inside_class)
* var can preceed an inner function
*/
if ( isKeyword(token, KEYWORD_var) )
{
/*
* Only create variables for global scope
*/
if ( token->nestLevel == 0 )
{
is_global = TRUE;
}
readToken(token);
}
copyToken(name, token);
@ -923,6 +968,10 @@ static void parseStatement (tokenInfo *const token, boolean is_inside_class)
readToken (token);
if (isType (token, TOKEN_PERIOD))
{
/*
* Cannot be a global variable is it has dot references in the name
*/
is_global = FALSE;
do
{
readToken (token);
@ -939,9 +988,7 @@ static void parseStatement (tokenInfo *const token, boolean is_inside_class)
goto cleanUp;
}
else
{
addContext (name, token);
}
}
else if ( isKeyword(token, KEYWORD_prototype) )
{
@ -956,13 +1003,36 @@ static void parseStatement (tokenInfo *const token, boolean is_inside_class)
{
readToken (token);
if (isKeyword (token, KEYWORD_function))
if ( isKeyword (token, KEYWORD_function) )
{
readToken (token);
if ( isKeyword (token, KEYWORD_NONE) &&
! isType (token, TOKEN_OPEN_PAREN) )
{
/*
* Functions of this format:
* var D2A=function theAdd(a, b)
* {
* return a+b;
* }
* Are really two separately defined functions and
* can be referenced in two ways:
* alert(D2A(1,2)); // produces 3
* alert(theAdd(1,2)); // also produces 3
* So it must have two tags:
* D2A
* theAdd
* Save the reference to the name for later use, once
* we have established this is a valid function we will
* create the secondary reference to it.
*/
copyToken(secondary_name, token);
readToken (token);
}
if ( isType (token, TOKEN_OPEN_PAREN) )
{
skipArgumentList(token);
}
if (isType (token, TOKEN_OPEN_CURLY))
{
@ -973,19 +1043,20 @@ static void parseStatement (tokenInfo *const token, boolean is_inside_class)
if ( is_inside_class )
{
makeJsTag (name, JSTAG_METHOD);
if ( vStringLength(secondary_name->string) > 0 )
makeFunctionTag (secondary_name);
parseBlock (token, name);
}
else
{
is_class = parseBlock (token, name);
if ( is_class )
{
makeClassTag (name);
}
else
{
makeJsTag (name, JSTAG_FUNCTION);
}
makeFunctionTag (name);
if ( vStringLength(secondary_name->string) > 0 )
makeFunctionTag (secondary_name);
}
}
}
@ -1008,49 +1079,91 @@ static void parseStatement (tokenInfo *const token, boolean is_inside_class)
else if (isType (token, TOKEN_OPEN_CURLY))
{
parseMethods(token, name);
}
else if (isKeyword (token, KEYWORD_new))
{
readToken (token);
if ( isKeyword (token, KEYWORD_function) ||
isKeyword (token, KEYWORD_capital_function) )
{
readToken (token);
if ( isType (token, TOKEN_OPEN_PAREN) )
skipArgumentList(token);
if (isType (token, TOKEN_SEMICOLON))
makeFunctionTag (name);
}
}
else if (isKeyword (token, KEYWORD_NONE))
{
/*
* Only create variables for global scope
*/
if ( token->nestLevel == 0 && is_global )
{
/*
* A pointer can be created to the function.
* If we recognize the function/class name ignore the variable.
* This format looks identical to a variable definition.
* A variable defined outside of a block is considered
* a global variable:
* var g_var1 = 1;
* var g_var2;
* This is not a global variable:
* var g_var = function;
* This is a global variable:
* var g_var = different_var_name;
*/
if ( ! stringListHas(FunctionNames, vStringValue (token->string)) &&
! stringListHas(ClassNames, vStringValue (token->string)) )
{
readToken (token);
if (isType (token, TOKEN_SEMICOLON))
makeJsTag (name, JSTAG_VARIABLE);
}
}
}
}
else
{
/*
* Only create variables for global scope
*/
if ( token->nestLevel == 0 && is_global )
{
/*
* Handles this syntax:
* var g_var2;
*/
if (isType (token, TOKEN_SEMICOLON))
makeJsTag (name, JSTAG_VARIABLE);
}
}
findCmdTerm (token);
/*
* Statements can be optionally terminated in the case of
* statement prior to a close curly brace as in the
* document.write line below:
*
* function checkForUpdate() {
* if( 1==1 ) {
* document.write("hello from checkForUpdate<br>")
* }
* return 1;
* }
*/
if (isType (token, TOKEN_CLOSE_CURLY))
is_terminated = FALSE;
cleanUp:
vStringCopy(token->scope, saveScope);
deleteToken (name);
deleteToken (secondary_name);
vStringDelete(saveScope);
}
static void parseGobalVar (tokenInfo *const token)
{
tokenInfo *const name = newToken ();
/*
* A variable defined outside of a block is considered
* a global variable:
* var g_var1 = 1;
* var g_var2;
* This is not a global variable:
* var g_var = function;
*/
// Potentially the name of the function
readToken (name);
readToken (token);
if ( isType (token, TOKEN_EQUAL_SIGN) )
{
readToken (token);
if (!isKeyword (token, KEYWORD_function))
{
makeJsTag (name, JSTAG_VARIABLE);
}
}
else if ( isType (token, TOKEN_SEMICOLON) )
{
makeJsTag (name, JSTAG_VARIABLE);
}
findCmdTerm (token);
deleteToken (name);
return is_terminated;
}
static void parseJsFile (tokenInfo *const token)
@ -1064,8 +1177,7 @@ static void parseJsFile (tokenInfo *const token)
switch (token->keyword)
{
case KEYWORD_function: parseFunction (token); break;
case KEYWORD_var: parseGobalVar (token); break;
default: break;
default: parseLine (token, FALSE); break;
}
}
else
@ -1087,12 +1199,15 @@ static void findJsTags (void)
tokenInfo *const token = newToken ();
exception_t exception = (exception_t) (setjmp (Exception));
ClassNames = stringListNew ();
FunctionNames = stringListNew ();
while (exception == ExceptionNone)
parseJsFile (token);
stringListDelete (ClassNames);
stringListDelete (FunctionNames);
ClassNames = NULL;
FunctionNames = NULL;
deleteToken (token);
}