我目前正在使用 listings 包编写语言定义。我的语言应按以下方式着色:
- 所有评论都应该是完全绿色的,无一例外
- 所有琴弦都应该是完全红色的,无一例外
- 所有关键字都应为蓝色(关键字可以包含数字)
- 标识符应该是黑色(标识符可以包含数字)
- 数字应该是洋红色(但关键字或标识符中的数字不是)
- 数学运算符、括号等应为青色
用这种方式给我的语言着色其实并不难。我通过将 basicstyle 设置为洋红色、keywordstyle 设置为蓝色、commentstyle 设置为绿色、stringstyle 设置为红色,然后使用 * 版本的 literate 将数学运算符设置为青色来实现这一点。一切都很顺利。但是,我的语言定义还必须允许在字符串或注释中使用像“ü”这样的德语变音符号。我们知道这有点棘手,因为列表和 utf8 编码会造成一些麻烦,但通常可以通过使用不带星号的 literate 版本来实现。
问题来了。正如你所见,我必须用 * 版本来标记一些内容,同时标记其他没有星号的内容。我发现理论上可以像这里一样做到这一点: 如何在列表环境中将星号(*)仅应用于少数识字者?
这有效,但并非在所有情况下都有效。我在尝试识读 = 符号和 时遇到了麻烦。问题似乎是我的注释的分隔符包含 =,而字符串本身中的 " 被 \ 转义了。有什么想法可以解决这个问题吗?
我创建了一个 MWE,并根据 MWE 中的评论做出了三项输出:
\documentclass{standalone}
\usepackage[utf8]{inputenc}
\usepackage{xcolor}
\usepackage{listings}
\lstdefinelanguage{testlang}{
morekeywords={int32,string},
morecomment=[l]{\#},
morecomment=[n]{\#=}{=\#},
morestring=[b]{"}
}[keywords,comments,strings]
\makeatletter
\newcommand{\jPm}[1]{%
\ifnum\lst@mode=\lst@Pmode\relax%
{\color{cyan}#1}%
\else%
#1%
\fi%
}
\begin{document}
% defining the colors
\lstset{basicstyle={\color{magenta}\ttfamily}, keywordstyle={\color{blue}\bfseries},
commentstyle={\color{green}\textit}, stringstyle={\color{red}}, identifierstyle={\color{black}}}
% defining the literals
\lstset{extendedchars=true}
\lstset{literate={ü}{{\"u}}{1} {+}{{{\jPm{+}}}}{1} % the first result does not use the next two lines
%{\\}{{{\jPm{\textbackslash{}}}}}{1} % the second result does not use the next line
%{=}{{{\jPm{=}}}}{1} % the third result also uses this line
}
\lstset{language=testlang}
\begin{lstlisting}
#= A comment that is longer than one line,
and that contains the German word "Blüte". =#
# Single comment: We will calculate 5 + 2 \ 8
string mystr = "An \"escaped string\", a Blüte and 1 + 1 = 2."
int32 my2ndvar = 8
int32 myres = 5 + 2 \ my2ndvar
\end{lstlisting}
\end{document}
答案1
在尝试了很多不同的方法、查看了 listings 包的文档和源代码以及搜索了互联网之后,我终于找到了一个解决方案,当然,我想与遇到同样问题的人分享。这可能不是最简单的解决方案,但它确实有效。
我所做的基本上是查看 listings 包的源代码,找出哪些命令定义了 literate 属性,将它们复制到我的源代码中并对其进行修改以创建一个新的 opliterate 属性,该属性的作用与 literate 属性相同。现在我可以使用 literate 属性来进行没有星号的文字识别,也可以使用 opliterate 属性来进行有星号的文字识别。
以下是代码:
\documentclass{standalone}
\usepackage[utf8]{inputenc}
\usepackage{xcolor}
\usepackage{listings}
\lstdefinelanguage{testlang}{
morekeywords={int32,string},
morecomment=[l]{\#},
morecomment=[n]{\#=}{=\#},
morestring=[b]{"}
}[keywords,comments,strings]
\makeatletter
\def\lst@OpLiteratekey#1\@nil@{\let\lst@ifxopliterate\lst@if
\def\lst@opliterate{#1}}
\lst@Key{opliterate}{}{\@ifstar{\lst@true \lst@OpLiteratekey}
{\lst@false\lst@OpLiteratekey}#1\@nil@}
\lst@AddToHook{SelectCharTable}
{\ifx\lst@opliterate\@empty\else
\expandafter\lst@OpLiterate\lst@opliterate{}\relax\z@
\fi}
\def\lst@OpLiterate#1#2#3{%
\ifx\relax#2\@empty\else
\lst@CArgX #1\relax\lst@CDef
{}
{\let\lst@next\@empty
\lst@ifxopliterate
\lst@ifmode \let\lst@next\lst@CArgEmpty \fi
\fi
\ifx\lst@next\@empty
\ifx\lst@OutputBox\@gobble\else
\lst@XPrintToken \let\lst@scanmode\lst@scan@m
\lst@token{#2}\lst@length#3\relax
\lst@XPrintToken
\fi
\let\lst@next\lst@CArgEmptyGobble
\fi
\lst@next}%
\@empty
\expandafter\lst@OpLiterate
\fi}
\begin{document}
% defining the colors
\lstset{basicstyle={\color{magenta}\ttfamily}, keywordstyle={\color{blue}\bfseries},
commentstyle={\color{green}\textit}, stringstyle={\color{red}}, identifierstyle={\color{black}}}
% defining the literals
\lstset{extendedchars=true}
\lstset{literate={ü}{{\"u}}{1}}
\lstset{opliterate=*{+}{{{\color{cyan}+}}}{1} {\\}{{{\color{cyan}\lstum@backslash}}}{1} {=}{{{\color{cyan}=}}}{1}}
\lstset{language=testlang}
\begin{lstlisting}
#= A comment that is longer than one line,
and that contains the German word "Blüte". =#
# Single comment: We will calculate 5 + 2 \ 8
string mystr = "An \"escaped string\", a Blüte and 1 + 1 = 2."
int32 my2ndvar = 8
int32 myres = 5 + 2 \ my2ndvar
\end{lstlisting}
\end{document}