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:
parent
49e76a8527
commit
e5f0ee00ad
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
293
jscript.c
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue