我有兴趣知道如何listings
定制来模仿Pygments 的 Prolog 词法分析器(即,使所表示的 Prolog 代码listings
看起来与用 完成的相同minted
)
我以前问过这个问题:如何制作一个位于页面中心的铸造代码列表?
Jubobs 对该问题进行了评论并鼓励我发布这个问题。
egreg 回答了我的先前的问题,他提供了以下示例,这正是我希望我的代码列表在报告中显示的样子:
但是,这个解决方案需要很多额外的包。所以我想知道是否可以通过自定义来实现相同(或非常相似)的结果listings
。
Jubobs已经提供了以下lstdefinestyle
示例:
\lstdefinestyle{myPrologstyle}
{
language=Prolog,
basicstyle = \ttfamily\color{blue},
moredelim = [s][\color{black}]{(}{)},
literate =
{:-}{{\textcolor{black}{:-}}}2
{,}{{\textcolor{black}{,}}}1
{.}{{\textcolor{black}{.}}}1
}
但这对我来说还不够。例如,变量没有像在 中那样以深蓝色突出显示minted
。
下面是另一个 (更完整的) 关于如何使用 突出显示 Prolog 代码的示例minted
:
\begin{minted}{prolog}
somePredicate(A, B) :-
arbitraryPredicate(A, _, 1, 2),
predicateWithAtom(someAtom),
anotherPredicate(B, someAtom, myPredicate(A, _)),
findall(X, ('testString'(X), myPredicate(A, X)), L1),
member(A, L1),
!.
\end{minted}
上面的代码片段产生以下输出:
从这个例子中可以看出,原子以红色突出显示,匿名变量(下划线)以绿色突出显示,数字以灰色(或者是青色?)。
有人知道我如何按照listings
自己想要的方式进行定制吗?请记住,我希望我的列表是居中在页面上。(如果你想知道我如何使列表居中,请参阅我的另一个问题。)
答案1
几个月后,经过一番折腾,我终于想出了一个可行的(尽管不太好)解决方案……从而让自己成为骗子 -_-'
Prolog 谓词、原子和变量的自动识别
下面显示的解决方案自动区分(并以与 Pygments 非常相似的风格突出显示)Prolog谓词,原子和变量。无需手动输入长串的关键词!简而言之,该方法包括以下内容:
- 将
listings
开括号字符视为“字母”,以测试该字符是否出现在标识符的末尾;如果出现,则该标识符是谓词。 - 否则,测试标识符的第一个字符
[A-Z_]
;如果测试通过,则该标识符为多变的。 - 否则,标识符是原子。
为了进行比较,请参见下面的屏幕截图。
对 Pygments 的 Prolog 词法分析器的改进
我注意到 Pygments 会不加区别地用深绿色突出显示下划线,并且会突出显示紧跟该字符的任何单词作为原子...这对我来说没有意义,因为根据Prolog 语法的维基百科页面,以下划线开头的标识符是多变的,不是原子。
下面显示的方法改进了 Pygments Prolog 词法分析器,因为它在不同的上下文中以不同的方式突出显示下划线:
- 它正确地突出显示以下划线开头但至少由两个字符组成的标识符,如(非匿名)Prolog变量, 和
- 它突出显示单独的下划线,即序言匿名变量,风格独特(深绿色)。
如果我错了,请纠正我;我不是 Prolog 专家。
缺少功能
我没有费心复制数字的突出显示样式;正如有关该主题的其他问题所证明的那样,这是出了名的困难,所以至少现在我会放弃这一点。
代码
\documentclass{article}
\usepackage[top=1in]{geometry}
\usepackage{textcomp}
\usepackage{listings}
%\usepackage{minted} % (requires -shell-escape)
\usepackage{xcolor}
\usepackage{filecontents}
% --- ugly internals for language definition ---
%
\makeatletter
% initialisation of user macros
\newcommand\PrologPredicateStyle{}
\newcommand\PrologVarStyle{}
\newcommand\PrologAnonymVarStyle{}
\newcommand\PrologAtomStyle{}
\newcommand\PrologOtherStyle{}
\newcommand\PrologCommentStyle{}
% useful switches (to keep track of context)
\newif\ifpredicate@prolog@
\newif\ifwithinparens@prolog@
% save definition of underscore for test
\lst@SaveOutputDef{`_}\underscore@prolog
% local variables
\newcount\currentchar@prolog
\newcommand\@testChar@prolog%
{%
% if we're in processing mode...
\ifnum\lst@mode=\lst@Pmode%
\detectTypeAndHighlight@prolog%
\else
% ... or within parentheses
\ifwithinparens@prolog@%
\detectTypeAndHighlight@prolog%
\fi
\fi
% Some housekeeping...
\global\predicate@prolog@false%
}
% helper macros
\newcommand\detectTypeAndHighlight@prolog
{%
% First, assume that we have an atom.
\def\lst@thestyle{\PrologAtomStyle}%
% Test whether we have a predicate and modify the style accordingly.
\ifpredicate@prolog@%
\def\lst@thestyle{\PrologPredicateStyle}%
\else
% Test whether we have a predicate and modify the style accordingly.
\expandafter\splitfirstchar@prolog\expandafter{\the\lst@token}%
% Check whether the identifier starts by an underscore.
\expandafter\ifx\@testChar@prolog\underscore@prolog%
% Check whether the identifier is '_' (anonymous variable)
\ifnum\lst@length=1%
\let\lst@thestyle\PrologAnonymVarStyle%
\else
\let\lst@thestyle\PrologVarStyle%
\fi
\else
% Check whether the identifier starts by a capital letter.
\currentchar@prolog=65
\loop
\expandafter\ifnum\expandafter`\@testChar@prolog=\currentchar@prolog%
\let\lst@thestyle\PrologVarStyle%
\let\iterate\relax
\fi
\advance \currentchar@prolog by 1
\unless\ifnum\currentchar@prolog>90
\repeat
\fi
\fi
}
\newcommand\splitfirstchar@prolog{}
\def\splitfirstchar@prolog#1{\@splitfirstchar@prolog#1\relax}
\newcommand\@splitfirstchar@prolog{}
\def\@splitfirstchar@prolog#1#2\relax{\def\@testChar@prolog{#1}}
% helper macro for () delimiters
\def\beginlstdelim#1#2%
{%
\def\endlstdelim{\PrologOtherStyle #2\egroup}%
{\PrologOtherStyle #1}%
\global\predicate@prolog@false%
\withinparens@prolog@true%
\bgroup\aftergroup\endlstdelim%
}
% language name
\newcommand\lang@prolog{Prolog-pretty}
% ``normalised'' language name
\expandafter\lst@NormedDef\expandafter\normlang@prolog%
\expandafter{\lang@prolog}
% language definition
\expandafter\expandafter\expandafter\lstdefinelanguage\expandafter%
{\lang@prolog}
{%
language = Prolog,
keywords = {}, % reset all preset keywords
showstringspaces = false,
alsoletter = (,
alsoother = @$,
moredelim = **[is][\beginlstdelim{(}{)}]{(}{)},
MoreSelectCharTable =
\lst@DefSaveDef{`(}\opparen@prolog{\global\predicate@prolog@true\opparen@prolog},
}
% Hooking into listings to test each ``identifier''
\newcommand\@ddedToOutput@prolog\relax
\lst@AddToHook{Output}{\@ddedToOutput@prolog}
\lst@AddToHook{PreInit}
{%
\ifx\lst@language\normlang@prolog%
\let\@ddedToOutput@prolog\@testChar@prolog%
\fi
}
\lst@AddToHook{DeInit}{\renewcommand\@ddedToOutput@prolog{}}
\makeatother
%
% --- end of ugly internals ---
% --- definition of a custom style similar to that of Pygments ---
% custom colors
\definecolor{PrologPredicate}{RGB}{000,031,255}
\definecolor{PrologVar} {RGB}{024,021,125}
\definecolor{PrologAnonymVar}{RGB}{000,127,000}
\definecolor{PrologAtom} {RGB}{186,032,032}
\definecolor{PrologComment} {RGB}{063,128,127}
\definecolor{PrologOther} {RGB}{000,000,000}
% redefinition of user macros for Prolog style
\renewcommand\PrologPredicateStyle{\color{PrologPredicate}}
\renewcommand\PrologVarStyle{\color{PrologVar}}
\renewcommand\PrologAnonymVarStyle{\color{PrologAnonymVar}}
\renewcommand\PrologAtomStyle{\color{PrologAtom}}
\renewcommand\PrologCommentStyle{\itshape\color{PrologComment}}
\renewcommand\PrologOtherStyle{\color{PrologOther}}
% custom style definition
\lstdefinestyle{Prolog-pygsty}
{
language = Prolog-pretty,
upquote = true,
stringstyle = \PrologAtomStyle,
commentstyle = \PrologCommentStyle,
literate =
{:-}{{\PrologOtherStyle :-}}2
{,}{{\PrologOtherStyle ,}}1
{.}{{\PrologOtherStyle .}}1
}
% global settings
\lstset
{
captionpos = below,
frame = single,
columns = fullflexible,
basicstyle = \ttfamily,
}
% write some sample code to an external file
\begin{filecontents*}{sample.pl}
somePredicate(_, B) :-
arbitraryPredicate(A, _variable, 1, 2),
predicateWithAtom(someAtom),
anotherPredicate(B, someAtom, myPredicate(A, _)),
findall(X, ('testString'(X), myPredicate(A, X)), L1),
member(A, L1),
!.
/*
block comment: blah blah blah
*/
% to-end-of-line comment: blah blah blah
\end{filecontents*}
\begin{document}
\section{With \textsf{minted} / Pygments}
%\begin{listing}
%\inputminted[frame=single]{prolog}{sample.pl}
%\caption{Sample Prolog code}
%\end{listing}
\section{With \textsf{Listings}}
\setcounter{lstlisting}{1}
\lstinputlisting[
style = Prolog-pygsty,
caption = {Using my custom \textsf{listings} style},
]{sample.pl}
\lstinputlisting[
language = Prolog,
caption = {Using \textsf{listings}' default settings for Prolog},
]{sample.pl}
\end{document}