Skip to content

Commit 9933060

Browse files
committed
Update parsers
* Fix loop parsing in ScriptParser * Fix self closing tags that may have a body in TagParser
1 parent 1d5397e commit 9933060

File tree

7 files changed

+174
-25
lines changed

7 files changed

+174
-25
lines changed

ScriptParser.cfc

+107-19
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
component extends="AbstractParser" {
22

3-
this.STATE = {NONE=0,COMMENT=1, IF_STATEMENT=2, ELSE_IF_STATEMENT=3, ELSE_STATEMENT=4, SWITCH_STATEMENT=5, STATEMENT=6, COMPONENT_STATEMENT=7, FOR_LOOP=8,WHILE_LOOP=9,RETURN_STATEMENT=10,CLOSURE=11,FUNCTION_STATEMENT=12};
3+
this.STATE = {NONE=0,COMMENT=1, IF_STATEMENT=2, ELSE_IF_STATEMENT=3, ELSE_STATEMENT=4, SWITCH_STATEMENT=5, STATEMENT=6, COMPONENT_STATEMENT=7, FOR_LOOP=8,WHILE_LOOP=9,RETURN_STATEMENT=10,CLOSURE=11,FUNCTION_STATEMENT=12,DO_WHILE_LOOP=13,TRY_BLOCK=14,CATCH_BLOCK=15,FINALLY_BLOCK=16};
44

55

66
public function parse(file, startPosition=0, endPosition=0) {
@@ -10,6 +10,8 @@ component extends="AbstractParser" {
1010
var parent = "";
1111
var currentState = this.STATE.NONE;
1212
var c = "";
13+
var cCode = 0;
14+
var lowerC = "";
1315
var endPos = 0;
1416
var temp = "";
1517
var paren = 0;
@@ -89,6 +91,13 @@ component extends="AbstractParser" {
8991
if (currentState == this.STATE.CLOSURE) {
9092
currentState = this.STATE.STATEMENT;
9193
sb.append(c);
94+
} else if (!isSimpleValue(parent) && parent.getName() == "do" ) {
95+
//end of a do / while loop
96+
parent.setBodyClose(pos);
97+
currentStatement = parent;
98+
currentState = this.STATE.DO_WHILE_LOOP;
99+
parent = currentStatement.getParent();
100+
sb.setLength(0);
92101
} else {
93102
if (!isSimpleValue(parent)) {
94103
parent.setBodyClose(pos);
@@ -107,6 +116,12 @@ component extends="AbstractParser" {
107116
if (reFindNoCase("\s*cf(output|mail|savecontent|query|document|pdf|htmltopdf|htmltopdfitem|form|storedproc|chart|client|div|documentitem|documentsection|formgroup|grid|http|imap|invoke|layout|lock|login|map|menu|module|pod|presentation|thread\report|silent|table|textarea|timer|transaction|tree|zip|window|xml)\s*\([^{]+\)\s*$", sb.toString())) {
108117
//script tag that can have body
109118
currentStatement.setBodyOpen(pos);
119+
parent = currentStatement;
120+
currentState = this.STATE.NONE;
121+
sb.setLength(0);
122+
} else if (reFindNoCase("\s(transaction|)\s*$", sb.toString())) {
123+
currentStatement.setBodyOpen(pos);
124+
110125
parent = currentStatement;
111126
currentState = this.STATE.NONE;
112127
sb.setLength(0);
@@ -123,7 +138,7 @@ component extends="AbstractParser" {
123138
}
124139
} else if (c == ";") {
125140
//TODO handle case where if/else if/else/for/while does not use {}
126-
if (currentState == this.STATE.STATEMENT) {
141+
if (currentState == this.STATE.STATEMENT || currentState == this.STATE.DO_WHILE_LOOP) {
127142
currentState = this.STATE.NONE;
128143

129144
currentStatement.setEndPosition(pos);
@@ -138,7 +153,7 @@ component extends="AbstractParser" {
138153
sb.append(";");
139154
}
140155
} else if (c==chr(13)) {
141-
if (currentState == this.STATE.STATEMENT) {
156+
if (currentState == this.STATE.STATEMENT || currentState == this.STATE.DO_WHILE_LOOP) {
142157
if (isValidStatement(sb.toString())) {
143158
currentState = this.STATE.NONE;
144159
currentStatement.setEndPosition(pos);
@@ -150,24 +165,25 @@ component extends="AbstractParser" {
150165
sb.append(c);
151166
}
152167
} else if (currentState == this.STATE.NONE) {
153-
154-
if (reFind("[a-z_]", c)) {
155-
//some letter
168+
lowerC = lCase(c);
169+
cCode = asc(lowerC);
170+
if ( (cCode >= 97 && cCode <= 122) || cCode == 95 ) {
171+
//some letter reFind("[a-z_]", c)
156172

157173

158174

159175
sb.setLength(0);
160-
if (c == "c" && mid(content, pos, 9) == "component") {
176+
if (lowerC == "c" && mid(content, pos, 9) == "component") {
161177
currentStatement = new ScriptStatement(name="component",startPosition=pos, file=arguments.file, parent=parent);
162178
addStatement(currentStatement);
163179
parent = currentStatement;
180+
sb.append(mid(content, pos, 9));
164181
pos = pos+9;
165-
sb.append("component");
166182
currentState = this.STATE.COMPONENT_STATEMENT;
167183
continue;
168-
} else if (c == "f" && reFind("function[\t\r\n a-zA-Z_]", mid(content, pos, 9)) ) {
184+
} else if (lowerC == "f" && reFindNoCase("function[\t\r\n a-zA-Z_]", mid(content, pos, 9)) ) {
169185
//a function without access modifier or return type
170-
sb.append("function");
186+
sb.append(mid(content, pos, 8));
171187
currentState = this.STATE.FUNCTION_STATEMENT;
172188
currentStatement = new ScriptStatement(name="function",startPosition=pos, file=arguments.file, parent=parent);
173189
addStatement(currentStatement);
@@ -176,7 +192,7 @@ component extends="AbstractParser" {
176192
}
177193
pos = pos + 8;
178194
continue;
179-
} else if (c == "i" && reFind("if[\t\r\n (]", mid(content, pos, 3))) {
195+
} else if (lowerC == "i" && reFindNoCase("if[\t\r\n (]", mid(content, pos, 3))) {
180196
currentStatementStart = pos;
181197
currentStatement = new ScriptStatement(name="if", startPosition=pos, file=arguments.file, parent=parent);
182198
parent = currentStatement;
@@ -186,10 +202,10 @@ component extends="AbstractParser" {
186202
if (!isSimpleValue(parent)) {
187203
parent.addChild(currentStatement);
188204
}
189-
sb.append("if");
205+
sb.append(mid(content, pos, 2));
190206
pos = pos+2;
191207
continue;
192-
} else if (c == "e" && reFind("else[ \t\r\n]+if[\t\r\n (]", content, pos) == pos) {
208+
} else if (lowerC == "e" && reFindNoCase("else[ \t\r\n]+if[\t\r\n (]", content, pos) == pos) {
193209
currentStatementStart = pos;
194210
currentStatement = new ScriptStatement(name="else if", startPosition=pos, file=arguments.file, parent=parent);
195211
currentState = this.STATE.ELSE_IF_STATEMENT;
@@ -202,7 +218,7 @@ component extends="AbstractParser" {
202218
sb.append(mid(content, pos, paren-pos));
203219
pos = paren;
204220
continue;
205-
} else if (c == "e" && reFind("else[\t\r\n (]", content, pos) == pos) {
221+
} else if (lowerC == "e" && reFindNoCase("else[\t\r\n (]", content, pos) == pos) {
206222
currentStatementStart = pos;
207223
currentStatement = new ScriptStatement(name="else", startPosition=pos, file=arguments.file, parent=parent);
208224
parent = currentStatement;
@@ -211,10 +227,10 @@ component extends="AbstractParser" {
211227
if (!isSimpleValue(parent)) {
212228
parent.addChild(currentStatement);
213229
}
214-
sb.append("else");
230+
sb.append(mid(content, pos, 4));
215231
pos = pos+4;
216232
continue;
217-
} else if (c == "v" && trim(mid(content, pos, 4)) == "var") {
233+
} else if (lowerC == "v" && trim(mid(content, pos, 4)) == "var") {
218234
currentStatement = new ScriptStatement(name="var", startPosition=pos, file=arguments.file, parent=parent);
219235
currentState = this.STATE.STATEMENT;
220236
addStatement(currentStatement);
@@ -225,16 +241,88 @@ component extends="AbstractParser" {
225241
sb.append("var ");
226242
pos = pos + 4;
227243
continue;
228-
} else if (c == "r" && reFind("return[\t\r\n ;]", mid(content, pos, 7)) == pos) {
244+
} else if (lowerC == "r" && reFindNoCase("return[\t\r\n ;]", mid(content, pos, 7)) == pos) {
229245
currentStatement = new ScriptStatement(name="return", startPosition=pos, file=arguments.file, parent=parent);
230246
currentState = this.STATE.RETURN_STATEMENT;
231247
addStatement(currentStatement);
232248
if (!isSimpleValue(parent)) {
233249
parent.addChild(currentStatement);
234250
}
235-
sb.append("return");
251+
sb.append(mid(content, pos, 6));
236252
pos = pos + 6;
237253
continue;
254+
} else if (lowerC == "f" && reFindNoCase("for\s*\(", content, pos) == pos) {
255+
currentStatementStart = pos;
256+
currentStatement = new ScriptStatement(name="for", startPosition=pos, file=arguments.file, parent=parent);
257+
parent = currentStatement;
258+
currentState = this.STATE.FOR_LOOP;
259+
addStatement(currentStatement);
260+
if (!isSimpleValue(parent)) {
261+
parent.addChild(currentStatement);
262+
}
263+
sb.append(mid(content, pos, 3));
264+
pos = pos+3;
265+
continue;
266+
} else if (lowerC == "w" && reFindNoCase("while\s*\(", content, pos) == pos) {
267+
currentStatementStart = pos;
268+
currentStatement = new ScriptStatement(name="while", startPosition=pos, file=arguments.file, parent=parent);
269+
parent = currentStatement;
270+
currentState = this.STATE.WHILE_LOOP;
271+
addStatement(currentStatement);
272+
if (!isSimpleValue(parent)) {
273+
parent.addChild(currentStatement);
274+
}
275+
sb.append(mid(content, pos, 5));
276+
pos = pos+5;
277+
continue;
278+
} else if (lowerC == "d" && reFindNoCase("do\s*{", content, pos) == pos) {
279+
currentStatementStart = pos;
280+
currentStatement = new ScriptStatement(name="do", startPosition=pos, file=arguments.file, parent=parent);
281+
parent = currentStatement;
282+
currentState = this.STATE.DO_WHILE_LOOP;
283+
addStatement(currentStatement);
284+
if (!isSimpleValue(parent)) {
285+
parent.addChild(currentStatement);
286+
}
287+
sb.append(mid(content, pos, 2));
288+
pos = pos+2;
289+
continue;
290+
} else if (lowerC == "t" && reFindNoCase("try\s*{", content, pos) == pos) {
291+
currentStatementStart = pos;
292+
currentStatement = new ScriptStatement(name="try", startPosition=pos, file=arguments.file, parent=parent);
293+
parent = currentStatement;
294+
currentState = this.STATE.TRY_BLOCK;
295+
addStatement(currentStatement);
296+
if (!isSimpleValue(parent)) {
297+
parent.addChild(currentStatement);
298+
}
299+
sb.append(mid(content, pos, 3));
300+
pos = pos+3;
301+
continue;
302+
} else if (lowerC == "c" && reFindNoCase("catch\s*\(", content, pos) == pos) {
303+
currentStatementStart = pos;
304+
currentStatement = new ScriptStatement(name="catch", startPosition=pos, file=arguments.file, parent=parent);
305+
parent = currentStatement;
306+
currentState = this.STATE.CATCH_BLOCK;
307+
addStatement(currentStatement);
308+
if (!isSimpleValue(parent)) {
309+
parent.addChild(currentStatement);
310+
}
311+
sb.append(mid(content, pos, 5));
312+
pos = pos+5;
313+
continue;
314+
} else if (lowerC == "f" && reFindNoCase("finally\s*\{", content, pos) == pos) {
315+
currentStatementStart = pos;
316+
currentStatement = new ScriptStatement(name="finally", startPosition=pos, file=arguments.file, parent=parent);
317+
parent = currentStatement;
318+
currentState = this.STATE.CATCH_BLOCK;
319+
addStatement(currentStatement);
320+
if (!isSimpleValue(parent)) {
321+
parent.addChild(currentStatement);
322+
}
323+
sb.append(mid(content, pos, 7));
324+
pos = pos+7;
325+
continue;
238326
} else {
239327
//either a statement or a function
240328
/* cases to handle
@@ -252,7 +340,7 @@ component extends="AbstractParser" {
252340
semi = find(";", content, pos+1);
253341
paren = find("(", content, pos+1);
254342
quotePos = reFind("['""]", content, pos+1);
255-
temp = reFind("[^a-zA-Z0-9_.]*function[\t\r\n ]+[a-zA-Z_]", content, pos);
343+
temp = reFindNoCase("[^a-zA-Z0-9_.]*function[\t\r\n ]+[a-zA-Z_]", content, pos);
256344

257345

258346
if (temp == 0) {

ScriptStatement.cfc

+4-3
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,15 @@ component extends="Statement" accessors="false" {
2626
}
2727

2828
public string function getTagName() {
29-
if (getName() == "statement") {
29+
if (!structKeyExists(variables, "tagName")) {
30+
variables.tagName = "";
3031
if (left(trim(getText()), 2) == "cf") {
3132
if (reFindNoCase("^\s*cf[a-z]{4,26}\s*\(", getText())) {
32-
return reReplaceNoCase(getText(),"\s*(cf[a-z]{4,16})\s*\(.+" , "\1");
33+
variables.tagName = reReplaceNoCase(getText(),"\s*(cf[a-z]{4,16})\s*\(.+" , "\1");
3334
}
3435
}
3536
}
36-
return "";
37+
return variables.tagName;
3738
}
3839

3940
public boolean function isScriptModeTag() {

TagParser.cfc

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ component extends="AbstractParser" {
8383
//has a parent, so set as child
8484
parent.addChild(tag);
8585
}
86-
if (tag.couldHaveInnerContent()) {
86+
if (tag.couldHaveInnerContent() && gtPos > 1 && mid(content, gtPos-1, 2) != "/>") {
8787
//replaced these regex with java matcher for 5x performance
8888
//endTagPos = reFindNoCase("<[[:space:]]*/[[:space:]]*#reReplace(tagName, "[^[:alnum:]_]", "", "ALL")#[[:space:]]*>", content, gtPos);
8989
if (!endPosMap.keyExists(tagName)) {

box.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name":"cfmlparser",
3-
"version":"1.1.5",
3+
"version":"1.1.6",
44
"author":"Pete Freitag / Foundeo Inc.",
55
"location":"",
66
"directory":"",

tests/adhoc.cfm

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<cfset statements = codeFile.getStatements()>
1818
<cfloop array="#statements#" index="stmt">
1919
<cfif NOT len(form.name_filter) OR reFind(form.name_filter, stmt.getName())>
20-
<cfdump var="#{attr: stmt.getAttributes(), text:stmt.getText(), expr:stmt.getExpressions(), vars:stmt.getVariables(), startPosition:stmt.getStartPosition(), isFunc:stmt.isFunction(), endPos:stmt.getEndPosition()}#" expand="#expand#" label="#stmt.getName()#">
20+
<cfdump var="#{attr: stmt.getAttributes(), text:stmt.getText(), expr:stmt.getExpressions(), vars:stmt.getVariables(), startPosition:stmt.getStartPosition(), isFunc:stmt.isFunction(), endPos:stmt.getEndPosition(), name:stmt.getName()}#" expand="#expand#" label="#stmt.getName()#">
2121
</cfif>
2222

2323
</cfloop>

tests/tests/TestLoop.cfc

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
component extends="BaseTest" {
2+
3+
4+
5+
function testLoop() {
6+
var parser = getParser("script/loop.cfc");
7+
var statements = parser.getStatements();
8+
var stmt = "";
9+
10+
var foundUpperCase = false;
11+
var foundForLoop = false;
12+
var foundWhileLoop = false;
13+
var dbg = [];
14+
for (stmt in statements) {
15+
if (stmt.getName() == "statement") {
16+
if (find("THIS.UPPER_CASE", trim(stmt.getText())) == 1) {
17+
foundUpperCase = true;
18+
}
19+
}
20+
if (stmt.getName() == "for") {
21+
foundForLoop = true;
22+
}
23+
if (stmt.getName() == "while") {
24+
foundWhileLoop = true;
25+
}
26+
arrayAppend(dbg, stmt.getVariables());
27+
}
28+
debug(dbg);
29+
$assert.isTrue(foundUpperCase, "should find uppercase var");
30+
$assert.isTrue(foundForLoop, "should find for loop");
31+
$assert.isTrue(foundWhileLoop, "should find while loop");
32+
33+
34+
}
35+
36+
37+
38+
}

tests/tests/templates/script/loop.cfc

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
component {
2+
3+
THIS.UPPER_CASE = true;
4+
5+
public function forLoop() {
6+
var a = [1,2,3];
7+
var b = "";
8+
for (b in a) {
9+
return a;
10+
}
11+
}
12+
13+
public function whileLoop() {
14+
var i = 0;
15+
16+
while (i < 10) {
17+
i++;
18+
}
19+
return i;
20+
}
21+
22+
}

0 commit comments

Comments
 (0)