diff --git a/src/tclscanner.l b/src/tclscanner.l index 7201088e0d4..48e8214aa8c 100644 --- a/src/tclscanner.l +++ b/src/tclscanner.l @@ -1700,6 +1700,122 @@ static void tcl_codify_link(QCString name) } +//! scan general argument for brackets +// +// parses (*tcl.list_commandwords.at(i)).utf8() and checks for brackets. +// Starts a new scan context if needed (*myScan==0 and brackets found). +// Returns NULL or the created scan context. +// +static tcl_scan *tcl_command_ARG(tcl_scan *myScan, unsigned int i, bool ignoreOutermostBraces) +{ + QCString myName; + bool insideQuotes=false; + unsigned int insideBrackets=0; + unsigned int insideBraces=0; + myName = (*tcl.list_commandwords.at(i)).utf8(); + if (i%2 != 0) + { + // handle white space + if (myScan!=NULL) + { + myScan->after << "NULL" << myName; + } + else + { + tcl_codify(NULL,myName); + } + } + else + { + QCString myStr = ""; + unsigned int j; + for (j=0;j0) + { + backslashed = myName[j-1]=='\\'; + } + // this is a state machine + // input is c + // internal state is myScan and insideXXX + // these are the transitions: + if (c=='[' && !backslashed && insideBraces==0) + { + insideBrackets++; + } + if (c==']' && !backslashed && insideBraces==0 && insideBrackets>0) + { + insideBrackets--; + } + if (c=='{' && !backslashed && !insideQuotes && !(ignoreOutermostBraces && j==0)) + { + insideBraces++; + } + if (c=='}' && !backslashed && !insideQuotes && insideBraces>0) + { + insideBraces--; + } + if (c=='"' && !backslashed && insideBraces==0) + { + insideQuotes=!insideQuotes; + } + // all output, depending on state and input + if (c=='[' && !backslashed && insideBrackets==1 && insideBraces==0) + { + // the first opening bracket, output what we have so far + myStr+=c; + if (myScan!=NULL) + { + myScan->after << "NULL" << myStr; + } + else + { + tcl_codify(NULL,myStr); + } + myStr=""; + } + else if (c==']' && !backslashed && insideBrackets==0 && insideBraces==0) + { + // the last closing bracket, start recursion, switch to deferred + if (myScan!=NULL) + { + myScan->after << "script" << myStr; + } + else + { + myScan=tcl.scan.at(0); + myScan = tcl_scan_start('?',myStr, + myScan->ns,myScan->entry_cl,myScan->entry_fn); + } + myStr=""; + myStr+=c; + } + else + { + myStr+=c; + } + } + if (myScan!=NULL) + { + myScan->after << "NULL" << myStr; + } + else + { + if (i==0) + { + tcl_codify_link(myStr); + } + else + { + tcl_codify(NULL,myStr); + } + } + } + return (myScan); +} + //! Handle internal tcl commands. // "if expr1 ?then? body1 elseif expr2 ?then? body2 elseif ... ?else? ?bodyN?" static void tcl_command_IF(QStringList type) @@ -1707,12 +1823,27 @@ static void tcl_command_IF(QStringList type) D tcl_codify_cmd("keyword",0); tcl_codify_cmd(NULL,1); - tcl_scan *myScan=tcl.scan.at(0); - myScan = tcl_scan_start('?',*tcl.list_commandwords.at(2), - myScan->ns,myScan->entry_cl,myScan->entry_fn); + tcl_scan *myScan = NULL; + myScan = tcl_command_ARG(myScan, 2, true); for (unsigned int i = 3;iafter << type[i] << tcl.list_commandwords[i]; + if (type[i] == "expr") + { + myScan = tcl_command_ARG(myScan, i, true); + } + else + { + if (myScan!=0) + { + myScan->after << type[i] << tcl.list_commandwords[i]; + } + else + { + myScan=tcl.scan.at(0); + myScan = tcl_scan_start('?',*tcl.list_commandwords.at(i), + myScan->ns,myScan->entry_cl,myScan->entry_fn); + } + } } } //! Handle internal tcl commands. @@ -1726,7 +1857,7 @@ D myScan = tcl_scan_start('?',*tcl.list_commandwords.at(2), myScan->ns,myScan->entry_cl,myScan->entry_fn); myScan->after << "NULL" << tcl.list_commandwords[3]; - myScan->after << "script" << tcl.list_commandwords[4]; + myScan = tcl_command_ARG(myScan, 4, true); myScan->after << "NULL" << tcl.list_commandwords[5]; myScan->after << "script" << tcl.list_commandwords[6]; myScan->after << "NULL" << tcl.list_commandwords[7]; @@ -1740,14 +1871,22 @@ static void tcl_command_FOREACH() { D unsigned int i; + tcl_scan *myScan=NULL; tcl_codify_cmd("keyword",0); for (i = 1;iafter << "script" << tcl.list_commandwords[tcl.list_commandwords.count()-1]; + } + else + { + myScan=tcl.scan.at(0); + myScan = tcl_scan_start('?',*tcl.list_commandwords.at(tcl.list_commandwords.count()-1), + myScan->ns,myScan->entry_cl,myScan->entry_fn); } - tcl_scan *myScan=tcl.scan.at(0); - myScan = tcl_scan_start('?',*tcl.list_commandwords.at(tcl.list_commandwords.count()-1), - myScan->ns,myScan->entry_cl,myScan->entry_fn); } ///! Handle internal tcl commands. @@ -1757,68 +1896,29 @@ static void tcl_command_WHILE() D tcl_codify_cmd("keyword",0); tcl_codify_cmd(NULL,1); - tcl_scan *myScan=tcl.scan.at(0); - myScan = tcl_scan_start('?',*tcl.list_commandwords.at(2), + tcl_scan *myScan = NULL; + myScan = tcl_command_ARG(myScan, 2, true); + myScan = tcl_command_ARG(myScan, 3, false); + if (myScan!=0) + { + myScan->after << "script" << tcl.list_commandwords[4]; + } + else + { + myScan=tcl.scan.at(0); + myScan = tcl_scan_start('?',*tcl.list_commandwords.at(4), myScan->ns,myScan->entry_cl,myScan->entry_fn); - myScan->after << "NULL" << tcl.list_commandwords[3]; - myScan->after << "script" << tcl.list_commandwords[4]; + } } //! Handle all other commands. // Create links of first command word or first command word inside []. static void tcl_command_OTHER() { - if (tcl.code == NULL) return; -D - QCString myName; + tcl_scan *myScan=NULL; for (unsigned int i=0; i< tcl.list_commandwords.count(); i++) { - myName = (*tcl.list_commandwords.at(i)).utf8(); - if (i==0) - { - tcl_codify_link(myName); - } - else if (i%2 != 0) - { - tcl_codify(NULL,myName); - } - else - { - QCString myStr=""; - int myCmd=0; - unsigned int i; - for (i=0;i\n"); // check command QCString myStr = (*tcl.list_commandwords.at(0)).utf8(); + tcl_scan *myScanBackup=tcl.scan.at(0); int myLevel = 0; Protection myProt = tcl.protection; if (tcl.list_commandwords.count() < 3) { tcl_command_OTHER(); - goto command_text; + goto command_end; } // remove leading "::" and apply TCL_SUBST if (myStr.left(2)=="::") myStr = myStr.mid(2); @@ -2294,7 +2395,7 @@ tcl_inf("->\n"); goto command_end; } tcl_command_OTHER(); - goto command_text; + goto command_end; } if (myStr=="itcl::class") { @@ -2317,7 +2418,7 @@ tcl_inf("->\n"); goto command_end; } tcl_command_OTHER(); - goto command_text; + goto command_end; } if (myStr=="oo::define") { @@ -2331,7 +2432,7 @@ tcl_inf("->\n"); if (tcl.scan.at(0)->entry_fn == NULL) {// only parsed outside functions tcl_command_VARIABLE(tcl.scan.at(0)->entry_cl && tcl.scan.at(0)->entry_cl->name!=""); - goto command_text; + goto command_end; } } if (myStr=="common") @@ -2340,7 +2441,7 @@ tcl_inf("->\n"); if (tcl.scan.at(0)->entry_fn == NULL) {// only parsed outside functions tcl_command_VARIABLE(0); - goto command_text; + goto command_end; } } if (myStr=="inherit" || myStr=="superclass") @@ -2378,7 +2479,7 @@ if expr1 ?then? body1 elseif expr2 ?then? body2 elseif ... ?else? ?bodyN? if (myStr=="if" && tcl.list_commandwords.count() > 4) { QStringList myType; - myType << "keyword" << "NULL" << "script" << "NULL"; + myType << "keyword" << "NULL" << "expr" << "NULL"; char myState='x';// last word: e'x'pr 't'hen 'b'ody 'e'lse else'i'f.. for (unsigned int i = 4; i < tcl.list_commandwords.count(); i = i + 2) { @@ -2427,7 +2528,7 @@ if expr1 ?then? body1 elseif expr2 ?then? body2 elseif ... ?else? ?bodyN? else if (myState=='i') { myState='x'; - myType << "script" << "NULL"; + myType << "expr" << "NULL"; } } if (myState != 'b') {myLine=__LINE__;goto command_warn;} @@ -2441,15 +2542,22 @@ if expr1 ?then? body1 elseif expr2 ?then? body2 elseif ... ?else? ?bodyN? goto command_end; } tcl_command_OTHER(); - goto command_text; + goto command_end; command_warn:// print warning message because of wrong used syntax tcl_war("%d count=%d: %s\n",myLine,tcl.list_commandwords.count(),tcl.list_commandwords.join(" ").ascii()); tcl_command_OTHER(); - command_text:// print remaining text as comment - if (!myText.isEmpty()) tcl_codify("comment",myText); - myText = ""; command_end:// add remaining text to current context - if (!myText.isEmpty()) tcl.scan.at(0)->after << "comment" << myText; + if (!myText.isEmpty()) + { + if(myScanBackup==tcl.scan.at(0)) + { + tcl_codify("comment",myText); + } + else + { + tcl.scan.at(0)->after << "comment" << myText; + } + } tcl.list_commandwords.clear(); tcl.command = 0; tcl.protection = myProt; diff --git a/testing/058/058__bracket__recursion_8tcl.xml b/testing/058/058__bracket__recursion_8tcl.xml new file mode 100644 index 00000000000..da0168d6526 --- /dev/null +++ b/testing/058/058__bracket__recursion_8tcl.xml @@ -0,0 +1,354 @@ + + + + 058_bracket_recursion.tcl + + + + Invoked + args + Invoked + + should be reference by every proc below + + + + + + + a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + + + + NotInvoked + args + NotInvoked + + must not be reference by every proc below + + + + + + + + + + a + args + a + + + + + + + + Invoked + + + + b + args + b + + + + + + + + Invoked + + + + c + args + c + + + + + + + + Invoked + + + + d + args + d + + + + + + + + Invoked + + + + e + args + e + + + + + + + + Invoked + + + + f + args + f + + + + + + + + Invoked + + + + g + args + g + + + + + + + + Invoked + + + + h + args + h + + + + + + + + Invoked + + + + i + args + i + + + + + + + + Invoked + + + + j + args + j + + + + + + + + Invoked + + + + k + args + k + + + + + + + + Invoked + + + + l + args + l + + + + + + + + Invoked + + + + m + args + m + + + + + + + + Invoked + + + + n + args + n + + + + + + + + Invoked + + + + o + args + o + + + + + + + + Invoked + + + + $NotInvoked + args + $NotInvoked + + + + + + + + + + + p + args + p + + + + + + + + Invoked + + + + q + args + q + + + + + + + + Invoked + + + + r + args + r + + + + + + + + Invoked + + + + s + args + s + + + + + + + + Invoked + + + + t + args + t + + + + + + + + Invoked + + + + + + + + + diff --git a/testing/058_bracket_recursion.tcl b/testing/058_bracket_recursion.tcl new file mode 100644 index 00000000000..0a070872a49 --- /dev/null +++ b/testing/058_bracket_recursion.tcl @@ -0,0 +1,141 @@ +#// objective: tests processing of commands inside brackets [], only references/referencedby relations are relevant +#// check: 058__bracket__recursion_8tcl.xml +#// config: REFERENCED_BY_RELATION = yes +#// config: REFERENCES_RELATION = yes +#// config: EXTRACT_ALL = yes +#// config: INLINE_SOURCES = yes + +## +# \brief should be reference by every proc below +proc Invoked args { + puts "Procedure \"Invoked\" is invoked indeed. Ok." + return $args +} +## +# \brief must not be reference by every proc below +proc NotInvoked args { + puts "Procedure \"NotInvoked\" is invoked. Not Ok!" + return $args +} +# +# check if call references work at all +proc a args { + Invoked NotInvoked + return +} +# +# check brackets with various quoting, bracing +proc b args { + set r [Invoked] + set r [list \[NotInvoked \]] + return +} +proc c args { + set r \{[Invoked]\} + set r {[NotInvoked]} + return +} +proc d args { + set r "[Invoked]" + set r "\[NotInvoked \]" + return +} +proc e args { + set r [list \[NotInvoked [Invoked]\]] + return +} +proc f args { + set r [list [Invoked \[NotInvoked \]]] + return +} +proc g args { + set r "{[Invoked]}" + set r "{\[NotInvoked \]}" + return +} +proc h args { + [Invoked set] r {[NotInvoked]} + return +} +# check brackets in tcl commands containing script arguments +# +# example generated according to +# https://groups.google.com/d/msg/comp.lang.tcl/G5-mc3GiIyY/e-AVD9t7xMkJ +proc i args { + foreach item [Invoked] { + return + } +} +proc j args { + foreach [Invoked item] [list one two three] { + } + return +} +proc k args { + while {[Invoked 0]} { + } +} +proc l args { + for {} {[Invoked 0]} {} { + } +} +proc m args { + if {[Invoked 1]} { + } +} +proc n args { + if [Invoked 1] { + } +} +proc o args { + if {0} { + } elseif {[Invoked 0]} { + } +} +# these are really nasty examples +# they shows, that the condition argument may not be parsed as a script +set NotInvoked \$NotInvoked +proc $NotInvoked args { + puts "Procedure \"\$NotInvoked\" is invoked. Not Ok!" + return $args +} +proc p args { + set NotInvoked \$NotInvoked + if {$NotInvoked eq [Invoked 1]} { + } + return +} +proc q args { + set NotInvoked \$NotInvoked + if {0} { + } elseif {$NotInvoked eq [Invoked 1]} { + } + return +} +proc r args { + set NotInvoked \$NotInvoked + while {$NotInvoked eq [Invoked 1]} { + } + return +} +proc s args { + set NotInvoked \$NotInvoked + for {} {$NotInvoked eq [Invoked 1]} {} { + } + return +} +# dangling open brackets should not confuse the scanner +proc t args { + set foo ]]]][Invoked] + return +} + +# +# call all single letter procs +# let tcl check what is called and what is not called +foreach p [info procs ?] { + puts "Check procedure \"$p\"" + $p +} +exit +