Могу ли я использовать «moredelim» в пакете «listings» для получения регулярного выражения?

Могу ли я использовать «moredelim» в пакете «listings» для получения регулярного выражения?

Я хотел бы узнать, возможно ли использовать пакет listingsдля подсветки регулярных выражений, чтобы они выглядели как обычный строковый объект. Примечание: синтаксис regexp не является контекстно-свободным, поэтому я не могу ожидать решения, которое распознает 100% случаев. В любом случае, например, в Ruby я хотел бы написать

(/[a-z]+/)

и сказать listingsраспознавать регулярное выражение, /[a-z]+/используя левую скобку (в качестве опорной точки (чтобы отделить его от арифметического деления):

\lstset{moredelim=[s][\color{red}]{(/}{/}}

Но это делает левую скобку цветной, а не только само регулярное выражение. Чтобы лучше понять парсер, я изменил оператор следующим образом:

 \lstset{moredelim=[s][\color{red}\textcolor{black}{:macro:}]{(/}{/}}

Однако, к моему удивлению, ":macro:" применяется ккаждыйиз токенов: (/, [, a, -, z, ], и /, обнаруженныхmoredelim . Я ошибочно ожидал, что ":macro:" будет применен квесьсовпавшее выражение (/[a-z]+/. Что, кстати, и произойдет, если я изменю выражение на его версию с двумя звездочками:

 \lstset{moredelim=**[s][\color{red}\textcolor{black}{:macro:}]{(/}{/}}

Но в этом случае регулярное выражение будет проанализировано с помощью listings- окрашивания ключевых слов внутри выражения. Это не то, что мне нужно. Я хочу использовать (только как якорь для поиска регулярного выражения, после чего я хочу обрабатывать якорь отдельно от самого регулярного выражения (придавая ему другой цвет).

Если говорить конкретно: я хочу знать, могу ли я взять только первый токен moredelim, содержащий (/, и обработать его отдельно от других токенов.

MWE для изучения этого вопроса представлен здесь:

\documentclass{article}  
\usepackage[english]{babel}   
\usepackage{listings}  
\usepackage{xcolor} 

\lstnewenvironment{lstRuby}{  
 \lstset{  
  language={},  
  moredelim=[s][\color{red}\textcolor{black}{:macro:}]{(/}{/}  
 }  
}{}  

\begin{document}  
\setlength{\parindent}{0pt}  

\ttfamily  
This is the code:  

(/[a-z]+/)

This is what I want to achieve:

(\textcolor{red}{/[a-z]+/})

This is what I get using listings:

\begin{lstRuby}  
(/[a-z]+/)  
\end{lstRuby}  

\end{document}

решение1

Этот ответ, возможно, больше не актуален для спрашивающего. Но поскольку эта проблема была гораздо сложнее в решении, чем должна была быть, и может быть актуальна для других, я все равно опубликую ответ.

Как упоминалось в вопросе и комментариях, moredelim=**[s]вариант рассматривает весь текст с разделителями как одну единицу/группу, что позволяет вставлять произвольный текст в начале группы с разделителями, а также в конце (через \aftergroup). Проблема в том, что другие стили применяются и к тексту между разделителями. С другой стороны, moredelim=[s]не применяет другие стили, а в свою очередь применяет стиль группы разделителей к каждой группе символов того же класса (буква, другой и т. д.). Это предотвращает вставку текста только непосредственно перед и после разделителей.

Кажется, нет простого способа достичь желаемого эффекта, поэтому нам придется зацепиться за некоторые внутренние механизмы. Макросы \lst@DelimOpenи \lst@DelimCloseуправляют действиями, когда в листинге обнаруживается новая пара разделителей. Поэтому мы переопределяем их, чтобы установить два хука \@delim@open@hookи \@delim@close@hook. В них мы можем проверить, какой стиль в данный момент активен (сравнив с \lst@currstyle), и на основе этого выбрать правильные действия. При таком подходе можно использовать несколько таких причудливых разделителей параллельно. Обратите внимание, что\@delim@close@hook выполняетсядовыводится последняя часть символов, поэтому нам снова нужно использовать трюк, \aftergroupчтобы переместить последнее действие после последних символов в разделенной группе.

Окончательная реализация использует moredelim=[is][\regexstyle]{(/}{/)}для определения стиля для регулярных выражений, где iполе удаляет исходные разделители из вывода. \regexstyle— это фактический стиль, который применяется ко всему внутреннему тексту регулярного выражения. Обязательно используйте здесь однозначно названный макрос-оболочку, в противном случае проверка \lst@currstyleможет привести к неправильным результатам. \regexstyle@startи \regexstyle@end— это макросы, которые вставляют код вместо исходного начального и конечного разделителя соответственно.

Вот полный пример:

\documentclass{article}
\usepackage{listings}
\usepackage{xcolor}

\lstnewenvironment{lstRuby}{%
    \lstset{
        language={},
        moredelim=[is][\color{green}]{*}{*},
        moredelim=[is][\regexstyle]{(/}{/)},
        emphstyle=\color{blue},
        emph={foo}
    }%
}{}

\makeatletter

\let\orig@lst@DelimOpen=\lst@DelimOpen
\def\lst@DelimOpen#1#2#3#4#5#6\@empty{%
    \orig@lst@DelimOpen{#1}{#2}{#3}{#4}{#5}{#6}\@empty
    \@delim@open@hook
}
\let\orig@lst@DelimClose=\lst@DelimClose
\def\lst@DelimClose{%
    \@delim@close@hook
    \orig@lst@DelimClose
}

\def\@delim@open@hook{%
    \def\@temp{\regexstyle}%
    \ifx\lst@currstyle\@temp
        \regexstyle@start
    \fi
}
\def\@delim@close@hook{%
    \def\@temp{\regexstyle}%
    \ifx\lst@currstyle\@temp
        \aftergroup\regexstyle@end
    \fi
}

\def\regexstyle{\color{red}}
\def\regexstyle@start{({\regexstyle /}}
\def\regexstyle@end{{\regexstyle /})}

\makeatother

\begin{document}
\setlength{\parindent}{0pt}

\ttfamily
This is the code:

(/[a-z]+/)

This is what I want to achieve:

(\textcolor{red}{/[a-z]+/})

This is what I get using listings:

\begin{lstRuby}
text (/[a-z]+/) /[a-z]+/ *foo*
text foo (/foo|\/|foo/) *bar*
\end{lstRuby}

\end{document}

введите описание изображения здесь

Связанный контент