[Tex/LaTex] Define new language listing with comments ONLY in the beginning of a line

languageslistings

I am trying to create listing for a home-brew language (using \lstdefinelanguage). The problem is that the comments in the language are somewhat ambiguous. For example, the following code is valid:

* This is a comment
a = 4 /* This is a comment as well */
********** This is a comment ********
a = a + 1 /* So is this */
    * And Surprisingly This is a comment
  * And This
*      AND THIS!
a = a * 100500 /* Star here is not a comment! */

So I need to make sure that the '*' is treated as a comment iff in the beginning of the line (might have spaces before), but not in the middle, where it is a multiplication.

What I have so far is the following:

\lstdefinelanguage{HOMEBREW}{
  alsoletter = {\$},
  comment = [l]{*},
  keywords = {
    *,
    command,
    eval,
    if, elseif, else, endif,
    include,
    open, close,
    print, printf,
    set, reset,
    while, endwhile,
  },
  sensitive = false,
  string = [b]",
  % MORE:
  % morecomment = [l]{**},
  % moredelim = [l]{(}{)},
}

Is it even possible?

Best Answer

I had to do something similar in one of my packages. Instead of a detailed explanation here, I've left a few comments in my code, for you to follow how I did it.

Note: Because \lst@AddToHook modifies things globally, you will need additional precautions if you intend to have listings using a language other than HOMEBREW in the same document. Let me know.

Edit: Unfortunately, this solution is not perfect. Everything works fine with the sample code you posted, but if you start a block comment on a line that already contains a * and close that block comment on one of the following lines, that block comment won't be highlighted properly.

enter image description here

\documentclass{article}

\usepackage[dvipsnames]{xcolor}
\usepackage{listings}

\makeatletter

% --- a couple of switches to keep track of the context ---
% this switch will be set if we have encountered visible character on the current line
\newif\ifVisCharOccured@homebrew@
% this switch will be set when we're inside a one-line comment
\newif\ifinlcom@homebrew@

% flip the switch if visible characters occur on the line
\lst@AddToHook{PostOutput}
{%
  \lst@ifwhitespace%
  \else
    \global\VisCharOccured@homebrew@true%
  \fi
}

% reset switches at the end of each line
\lst@AddToHook{InitVarsEOL}
{%
  \global\inlcom@homebrew@false%
  \global\VisCharOccured@homebrew@false%
}

% reset switches at the beginning of each listing
\lst@AddToHook{PreInit}
{%
  \VisCharOccured@homebrew@false
  \inlcom@homebrew@false
}

% helper macro to handle instances of `*'
\newcommand\processlcom@homebrew
{%
  % if we're already inside a comment, we keep applying the comment style
  \ifinlcom@homebrew@%
    \lst@commentstyle%
  \else
    % Otherwise, we apply the comment style only if no visible characters have
    % been encountered before the `*' on the current line.
    \ifVisCharOccured@homebrew@%
    \else
      \global\inlcom@homebrew@true%
      \lst@commentstyle%
    \fi
  \fi
}


\lstdefinelanguage{HOMEBREW}{
  alsoletter = {\$},
  moredelim = *[l][\processlcom@homebrew]{*}, %<--- we handle to-end-of-line comments here
  keywords = {
    command,
    eval,
    if, elseif, else, endif,
    include,
    open, close,
    print, printf,
    set, reset,
    while, endwhile,
  },
  sensitive = false,
  string = [b]",
  morecomment = [s]{/*}{*/},
  % moredelim = [l]{(}{)},
}
\makeatother
\lstdefinestyle{myHOMEBREWstyle}
{
  language     = HOMEBREW,
  basicstyle   = \ttfamily,
  commentstyle = \color{ForestGreen},
}

\begin{document}
\begin{lstlisting}[style=myHOMEBREWstyle]
* This is a comment
a = 4 /* This is a comment as well */
********** This is a comment ********
a = a + 1 /* So is this */
    * And Surprisingly This is a comment
  * And This
*      AND THIS!
a = a * 100500 /* Star here is not a comment! */
\end{lstlisting}
\end{document}
Related Question