About
I think using control+f to find the error, after you have done "show cell expression", is easy enough. However, if we ever collect code that can automatically check for various syntax errors, this may be a nice addition.
Easy Procedure
Make a new notebook and evaluate the following
nb = EvaluationNotebook[];
as well as
With[{sB = SuperscriptBox @@ {")", "2"}},
Cell[BoxData[RowBox[{"(", RowBox[{"k", "h"}], sB}]], "Input",
CellChangeTimes -> {3.536893041212217*^9},
EmphasizeSyntaxErrors -> True] // CellPrint;
]
so that this is the notebook in which you have the broken cell. Make sure to leave the generated (broken) cell in it's "fancy" form. Do not use "Show Cell Expression", as this somehow makes NotebookRead
unable to read the CellObject
later.
Then make a new notebook, in which to put the code below.
Finding the bad cell
First, let's consider some simple functions to find the cell where the error is.
badCellNumber[nb_] :=
LengthWhile[NotebookRead@Cells[nb],
Length[Cases[#, SuperscriptBox[")", _], Infinity]] == 0 &] + 1;
cellObjectByNumber[nb_, no_] :=
Part[Cells[nb], no]
and set
badNumber = badCellNumber[nb];
badCellObject = cellObjectByNumber[nb, badNumber];
Now we select all bad code in nb
as follows. The code below assumes that you are not showing the cell expression of the bad cell. Otherwise you can ommit the second and third line below. The first just serves to show you what is going on.
SetSelectedNotebook[nb]
SelectionMove[badCellObject, All, Cell]
FrontEndExecute[FrontEndToken@"ToggleShowExpression"]
NotebookFind[nb, "SuperscriptBox[\")\""]
This code looks for a very specific pattern. If you have a similar case where it does not work, please let me know.
Text file procedure
About
This part of the answer is based on Kuba's answer very much. All of this is not practical yet. Again, maybe somebody can someday use it to check for multiple syntax errors programatically. But it is really an overkill for now.
Make sure you have not generated any cell groupings in nb
, and in particular make sure you have not generated any output in that notebook.
Now we would like to get the string expression corresponding to the cell where the error is. Then we might be able to find out where exactly inside the cell the error is. However, getting this string is not so easy. Kuba's answer shows that we can also just replace code that causes errors by good code, but that seems a bit dangerous.
Below is a function that would tell us the position of the erroneous code in a string. You can play around with ToString
on the CellObject
, but the strings generated this way do not suit our purposes. Here is the function
With[{rNWC = RepeatedNull[WhitespaceCharacter]},
positionOfTroubleIn[str_] :=
StringPosition[str,
Shortest[
"SuperscriptBox[" ~~ rNWC ~~ "\")\"" ~~ rNWC ~~ "," ~~
rNWC ~~ ___ ~~ rNWC ~~ "]"]] // First
]
Probably the rNWC
additions do not help. I think most of time a space followed by a ")" will be made into a RowBox
anyway.
Before executing the code below, make sure you have saved the notebook (nb
) with bad cell in it. Also, evaluate (anywhere)
nFN = NotebookFileName[nb];
The code below will create a textFile version of that notebook in the directory where you have saved it. Unfortunately I did not see a way around copying the file here.
textName = StringJoin[StringDrop[nFN, -3], ".txt"];
CopyFile[nFN, textName]
fileText = Import[textName];
The code below finds out where the "real code" begins and ends
beginning =
StringPosition[fileText, "(* Beginning of Notebook Content *)"][[1,
2]]
end = StringPosition[fileText, "(* End of Notebook Content *)"][[1,
1]]
fileText2 = StringTake[fileText, {beginning + 2, end - 2}];
Parser
To find the string that corresponds the cell, we have to parse the text file. The parser I have made for this is quite alright I'd say, except that it does not consider comments. A nice feature is that cares about strings and does not consider brackets inside strings for parsing.
openingBrackets = {1, First@#} & /@ StringPosition[fileText2, "["];
closingBrackets = {-1, First@#} & /@ StringPosition[fileText2, "]"];
quotes = {2, First@#} & /@ StringPosition[fileText2, "\""];
orderedBracketPositions =
SortBy[Join[openingBrackets, closingBrackets, quotes ], Last];
Now we find the brackets that are not inside strings
quoteState = True;
(*commentState=True;*)
positionsOfRelevantBrackets =
Reap[
Block[{res},
Do[
res = orderedBracketPositions[[i]];
If[
res[[1]] == 2,
quoteState = ! quoteState,
If[
quoteState,
Sow[res]
]
]
,
{i, Length[orderedBracketPositions]}
]
]
][[2, 1]];
We are then ready to find the closing brackets of all the cells, by using the fact that when a Cell
bracket closes, all but one cell bracket are matched (the cell bracket corresponding to Notebook
is not matched).
positionsOfCellClosingBrackets =
Last /@ Part[positionsOfRelevantBrackets,
Flatten@Rest[
Position[Accumulate[First /@ positionsOfRelevantBrackets], 1]]];
With some magic numbers, we also find the starts of the cell :)
cellStarts = Prepend[Most[positionsOfCellClosingBrackets] + 4, 12];
Now lets put the starting positions and closing positions side by side
cellDelims = {cellStarts, positionsOfCellClosingBrackets} //
Transpose;
We can finally find the string
badCellString = StringTake[fileText2, cellDelims[[badNumber]]];
and the "position of trouble"
poT = positionOfTroubleIn[badCellString];
and the string causing the trouble
badString = StringTake[badCellString, poT];
Selecting the string inside the cell
Now we select all the bad code in the broken notebook as follows. The code below assumes that you are not showing the cell expression of the bad cell. Otherwise you can ommit the second and third line below. The first just serves to show you what is going on.
SetSelectedNotebook[nb]
SelectionMove[ badCellObject, All, Cell]
FrontEndExecute[FrontEndToken@"ToggleShowExpression"]
NotebookFind[nb, badString]
HoldForm
. $\endgroup$ – Simon Jan 30 '12 at 7:04ToExpression
code that your example reveals.ToExpression["(h k)^2"]
works fine, but the version with a superscript, $\text{ToExpression["}(h\ k)^2\text{"]}$, produces an error. For reasons very similar to the example that you gave: The string with a superscript is interpretted using the string representation of boxes"(k h\!\(\*SuperscriptBox[\()\), \(2\)]\)"
$\endgroup$ – Simon Jan 30 '12 at 7:11