自动二分乳胶文件以进行调试

自动二分乳胶文件以进行调试

如果我难以解释乳胶文件中的错误信息,我会注释掉一半的文件,进行编译,看看它是否有效,然后通过连续的二分法找到有问题的部分。

有没有自动的方法可以做到这一点?我知道这可能很难实现,因为你必须找到安全的二分点位置。不过我猜这在原则上是可以解决的,所以我想知道是否已经有解决方案了。

我正在使用 linux 和 emacs,因此我更喜欢尊重此环境的解决方案。

这个问题部分受到 emacs 包的启发漏洞猎手, 哪个 ”自动调试和二分你的初始化(.emacs)文件!“ (也可以看看这里)。

编辑

这是一个简单的例子,我觉得很难找到错误。在这种情况下,我会尝试二分法:

\documentclass{article}
\errorcontextlines=1000
\usepackage{exsheets}
\usepackage{tikz}
  
\begin{document}

\begin{question}
Test
\end{question}


\begin{solution}
\begin{tikzpicture}
\draw (0,0) -- (1,1)
\end{tikzpicture}
\end{solution}


\begin{question}
Test
\end{question}


\begin{solution}
Test
\end{solution}

\printsolutions
\end{document}

日志文件的输出:https://pastebin.com/kFfWu2zL

答案1

这是对特定示例的解释,以及为什么它难以调试。


堆栈跟踪发生了什么?

因为尾调用优化,缺少重要的错误上下文行。

比较:如果在错误的 tikzpicture 环境后添加\relax(或任何非空内容),则会打印相关的上下文行。

! Package tikz Error: Giving up on this path. Did you forget a semicolon?.

See the tikz package documentation for explanation.
Type  H <return>  for immediate help.
 ...ackage tikz Error: Giving up on this path. Did you forget a semicolon?.

See the tikz package documentation for explanation.
Type  H <return>  for immediate help\@err@                                                                   
                                                                                                                                                                                                                                              
\GenericError  ...                                                 \@empty \def \MessageBreak  
#1 \def   \errmessage  #2.

#3
Type  H <return>  for immediate help\@err@                                                                     
                                                                                                                                                                                                                                              \endgroup 
\pgfutil@next ->\advance \tikz@expandcount by -1 \ifnum \tikz@expandcount <0\relax \tikzerror {Giving up on this path. Did you forget a semicolon?}
                                                                                                                                                   \let \pgfutil@next =\tikz@finish \else \let \pgfutil@next =\tikz@@expand \fi \pgfutil@next 
\pgf@let@token ->\tikz@atend@picture \global 
                                             \let \pgf@shift@baseline@smuggle =\pgf@baseline \global \let \pgf@trimleft@final@smuggle =\pgf@trimleft \global \let \pgf@trimright@final@smuggle =\pgf@trimright \global \let \pgf@remember@smuggle =\ifpgfre...
\end  #1->\romannumeral \IfHookEmptyTF {env/#1/end}{\expandafter \z@ }{\z@ \UseHook {env/#1/end}}\csname end#1\endcsname 
                                                                                                                         \@checkend {#1}\expandafter \endgroup \if@endpe \@doendpe \fi \UseHook {env/#1/after}\if@ignore \@ignorefalse \ignorespaces \fi 
<argument> \begin {tikzpicture} \draw (0,0) -- (1,1) \end {tikzpicture}
                                                             \relax 
\exsheetsprintsolution #1#2->#1#2
                                 
\__exsheets_surround_with:nnn #1#2#3->#2#1
                                          #3
\__exsheets_print_solution:nnn ...tl {#2}{#1}}{\exp_not:V \l__exsheets_solutions_pre_body_hook_tl \exp_not:n {#3}\exp_not:V \l__exsheets_solutions_post_body_hook_tl }}\l__exsheets_solutions_pre_hook_tl \l__exsheets_solutions_post_hook_tl 
                                                                                                                                                                                                                                              \exsheets_add...
\__prop_map_function:Nwwn #1#2\__prop_pair:wn #3\s__prop #4->#2#1{#3}{#4}
                                                                         \__prop_map_function:Nwwn #1
\g_exsheets_question_identification_prop ->\s__prop \__prop_pair:wn 1\s__prop {-0-1}
                                                                                    \__prop_pair:wn 2\s__prop {-0-2}
\prop_map_function:NN #1#2->\exp_after:wN \use_i_ii:nnn \exp_after:wN \__prop_map_function:Nwwn \exp_after:wN #2#1
                                                                                                                  \prg_break: \__prop_pair:wn \s__prop {}\prg_break_point: \prg_break_point:Nn \prop_map_break: {}
\__keys_set_keyval:nnn ...path_str \s__keys_stop \l__keys_module_str \l_keys_key_str \tl_set_eq:NN \l_keys_key_tl \l_keys_key_str \__keys_value_or_default:n {#3}\bool_if:NTF \l__keys_selective_bool \__keys_set_selective: \__keys_execute: 
                                                                                                                                                                                                                                              \str_set:Nn \...
\__keyval_key:nn #1#2->\__keyval_if_blank:w \s__keyval_mark #1\s__keyval_nil \s__keyval_stop \__keyval_blank_key_error:w \s__keyval_mark \s__keyval_stop \exp_not:n {#2{#1}}
                                                                                                                                                                            \__keyval_loop_other:nnw {#2}
\__keyval_loop_other:nnw ..._keyval_if_recursion_tail:w #3\__keyval_end_loop_other:w \s__keyval_tail \__keyval_split_active:w #3\s__keyval_nil \s__keyval_mark \__keyval_split_active_auxi:w =\s__keyval_mark \__keyval_clean_up_active:w {#1}
                                                                                                                                                                                                                                              {#2}\s__keyva...
\__keyval_loop_active:nnw #1#2#3,->\__keyval_if_recursion_tail:w #3\__keyval_end_loop_active:w \s__keyval_tail \__keyval_loop_other:nnw {#1}{#2}#3,
                                                                                                                                                   \s__keyval_tail ,
\keyval_parse:NNn #1#2#3->\__keyval_loop_active:nnw {#1}{#2}\s__keyval_mark #3,
                                                                               \s__keyval_tail ,
\__keys_set:nnn #1#2#3->\str_set:Nx \l__keys_module_str {\__keys_trim_spaces:n {#2}}\keyval_parse:NNn \__keys_set_keyval:n \__keys_set_keyval:nn {#3}
                                                                                                                                                     \str_set:Nn \l__keys_module_str {#1}
\l__exp_internal_tl ...\l__keys_only_known_bool \bool_set_false:N \l__keys_filtered_bool \bool_set_false:N \l__keys_selective_bool \tl_set:Nn \l__keys_relative_tl {\q__keys_no_value }\__keys_set:nn {exsheets/exsheets_print_solutions}{all}
                                                                                                                                                                                                                                              \tl_set:Nn \l...
\exsheets_print_solutions:n ...exsheets_solutions_print_bool \bool_set_true:N \l__exsheets_inside_solution_bool \cs_set:Npn \S ##1{\exref {exse:##1}}\cs_set:Npn \C ##1{\exref {exch:##1}}\keys_set:nn {exsheets/exsheets_print_solutions}{#1}
                                                                                                                                                                                                                                              \group_end: 
<to be read again> 
\end 
l.32 \end
       {document}

注意

<argument> \begin {tikzpicture} \draw (0,0) -- (1,1) \end {tikzpicture}
                                                             \relax 

这就是您输入的内容。

行号怎么了?

您在 TeX 中显示的代码大致相当于 Python 中的以下(伪)代码:

# ======== tikz library

def tikzPicture(s: str):
    if ";" not in s: raise RuntimeError("No semicolon")
    return "figure"

# ======== exsheets library

solutions=[]
def addSolution(s: str):
    solutions.append(s)

def printSolutions():
    for solution in solutions: print(eval(solution))

# ======== your code

addSolution("'Test'")
addSolution("tikzPicture('draw (0, 0) -- (1, 1);')")
addSolution("tikzPicture('draw (0, 0) -- (1, 1)')")  # line 21 here

printSolutions()

Python 中的回溯是

Traceback (most recent call last):
  File "FILE", line 23, in <module>
    printSolutions()
  File "FILE", line 15, in printSolutions
    for solution in solutions: print(eval(solution))
  File "<string>", line 1, in <module>
  File "FILE", line 5, in tikzPicture
    if ";" not in s: raise RuntimeError("No semicolon")
RuntimeError: No semicolon

如您所见,回溯仅指向第 23 行(printSolutions),而不是您实际添加代码的第 21 行。

(另请注意,在 TeX 中,“最近调用”是第一次,而不是最后一次)

Python:还能更好吗?

事实上 Python 甚至更差在这个特定的例子中!(这是可以修复的,参见stackoverflow 问题

您在“普通”编程语言中不会遇到它的唯一原因是您极少使用eval/ exec

理论上的 Python 库还能改进什么?

  • 在 上addSolution,将堆栈内容存储在某处,然后将其打印回来。这样您就可以得到“第 21 行”。
  • 出现printSolutions错误时打印出用户的源代码

明显的缺点是时间/内存/代码复杂性/维护难度开销。

TeX:还有更好的吗?

上述两种解决方案均适用,但都需要对exsheets包进行修改。

  • 可以将当前行号\begin{solution}与逐字解决方案文本一起定义,并在执行时打印它(在某处,例如在日志中)

  • 如果没有这个,包仍然可以在呈现每个解决方案之前打印出解决方案的内容。

    TeX 中没有try...catchortry...except

    对于尾部调用优化情况,如上面链接问题中提到的,可以查看输出来 \tracingmacros=1确定上下文。

    对于此特定版本的此特定软件包,省略的“用户层”位于该层的正上方(比该层更近) 。因此,在日志中搜索回溯之前exsheetsprintsolution的最后一次出现。在此特定情况下,它是\exsheetsprintsolution

    \exsheetsprintsolution #1#2->#1#2
    
    #1<-\exsheets_solutions_print_name:Vnn \l__exsheets_tmpa_tl {\GetTranslation {exsheets-solution-name}}{1}
    
    #2<-\begin {tikzpicture} \draw (0,0) -- (1,1) \end {tikzpicture}
    

    您可以清楚地看到这#2就是您的代码。

    或者(提醒:这是特定于此特定包实现/版本)可以I\ExplSyntaxOn\show\l__exsheets_tmpb_tl在 TeX 错误提示处输入来打印出你的环境内容(已经标记化)。

换行符发生了什么?

这是包的另一个问题exsheets:环境中的内容不是逐字存储的,而是直接解析的,解析会删除换行符。

您可以轻松看到exsheets没有正确存储逐字内容:

\begin{solution}
    \verb+%+
\end{solution}

(与该问题无关,但您可以\cprotEnvcprotect包中使用来修复该问题。)

为什么\scantokens( exec-equivalent) 在 TeX 中如此常见?

基本上,为了在一般情况下正确解析 TeX,您必须执行代码。

所以当你想存储内容而不执行代码, 你必须逐字存储它(大致“作为字符串”),然后稍后将其作为字符串执行。

尽管在这个特殊情况下,它被收集为一个参数。这也会破坏行号。

相关内容