是否可以设置 listing 包来像 minted 那样突出显示 Prolog 代码?

是否可以设置 listing 包来像 minted 那样突出显示 Prolog 代码?

我有兴趣知道如何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}

相关内容