\RequirePackage{listings}
\RequirePackage{pgfextra}
\lstdefinelanguage{OCaml}{
  keywords={assert,asr,class,closed,constraint,external,false,%
    functor,include,inherit,land,lazy,lor,lsl,lsr,lxor,method,mod,%
    module,new,open,parser,private,sig,struct,true,val,virtual,when,%
    object,ref,% TH
    and,as,begin,do,done,downto,else,end,exception,for,%
    fun,function,if,in,let,match,mutable,not,of,or,prefix,rec,then,%
    to,try,type,where,while,with,\},\{,% remove value
    int,string,unit,float,bool,char,list,%
    rule,parse,%Ocamllex
    \%token,\%start,\%type,\%left,\%right,\%nonassoc,\%\%,\%\{,\%\}%Ocamlyacc
  },%
  alsoletter={\%,\{,\}},%
  otherkeywords={!=,\&,\&\&,\,,->,.,..,:,::,:=,:>,;,;;,<>,<,<-,=,>,>],>\},?,??,[,[<,[>,[|,],`,\{<,|,|],~,@,^,()},%
  sensitive,%
  morecomment=[n]{(*}{*)},%
  morecomment=[s]{/*}{*/},%
  morestring=[b]",%
  morestring=[b]'%
}[keywords,comments,strings]%


\lstdefinelanguage{OCamlII}{
  keywords={assert,asr,class,closed,constraint,external,false,%
    functor,include,inherit,land,lazy,lor,lsl,lsr,lxor,method,mod,%
    module,new,open,parser,private,sig,struct,true,val,virtual,when,%
    object,ref,% TH
    and,as,begin,do,done,downto,else,end,exception,for,%
    fun,function,if,in,let,match,mutable,not,of,or,prefix,rec,then,%
    to,try,type,where,while,with,\},\{,% remove value
    rule,parse,%Ocamllex
    \%token,\%start,\%type,\%left,\%right,\%nonassoc,\%\%,\%\{,\%\}%Ocamlyacc
  },%
  morekeywords=[2]{int,string,unit,float,bool,char,list,array,bytes},%
  alsoletter={\%,\{,\},'},%
  alsodigit={_},%
  otherkeywords={+,/,!=,\#,\&,\&\&,\,,-,->,.,..,:,::,:=,:>,;,;;,<>,<,<-,=,>,>],>\},?,??,[<,[>,[|,`,\{<,|,|],~,@,^,()},%
%  literate=*{_}{{\lst@keywordstyle \_}}{1},
  sensitive,%
  morecomment=[n]{(*}{*)},%
  morecomment=[s]{/*}{*/},%
  morestring=[b]"%
%%  morestring=[b]',%
}[keywords,comments,strings]%

\lstdefinelanguage{OCamlIII}{
  keywords={assert,asr,class,closed,constraint,external,false,%
    functor,include,inherit,land,lazy,lor,lsl,lsr,lxor,method,mod,%
    module,new,open,parser,private,sig,struct,true,val,virtual,when,%
    object,ref,% TH
    and,as,begin,do,done,downto,else,end,exception,for,%
    fun,function,if,in,let,match,mutable,not,of,or,prefix,rec,then,%
    to,try,type,where,while,with%,\},\{,% remove value
  },%
  morekeywords=[2]{int,string,unit,float,bool,char,list,array,bytes},%
  alsoletter={'},%
  alsodigit={_},%
%  otherkeywords={+,/,!=,\#,\&,\&\&,\,,-,->,.,..,:,::,:=,:>,;,;;,<>,<,<-,=,>,>],>\},?,??,[<,[>,[|,`,\{<,|,|],~,@,^,()},%
%  literate=*{_}{{\lst@keywordstyle \_}}{1},
  sensitive,%
  morecomment=[n]{(*}{*)},%
  morecomment=[s]{/*}{*/},%
  morestring=[b]"%
%%  morestring=[b]',%
}[keywords,comments,strings]%

\lstdefinelanguage{OCamlIV}{
  keywords={assert,asr,class,closed,constraint,external,false,%
    functor,include,inherit,land,lazy,lor,lsl,lsr,lxor,method,mod,%
    module,new,open,parser,private,sig,struct,true,val,virtual,when,%
    object,ref,% TH
    and,as,begin,do,done,downto,else,end,exception,for,%
    fun,function,if,in,let,match,mutable,not,of,or,prefix,rec,then,%
    to,try,type,where,while,with%,\},\{,% remove value
  },%
  morekeywords=[2]{int,string,unit,float,bool,char,list,array,bytes},%
%  otherkeywords={+,/,!=,\#,\&,\&\&,\,,-,->,.,..,:,::,:=,:>,;,;;,<>,<,<-,=,>,>],>\},?,??,[<,[>,[|,`,\{<,|,|],~,@,^,()},%
%  literate=*{_}{{\lst@keywordstyle \_}}{1},
  sensitive,%
  morecomment=[n]{(*}{*)},%
  morecomment=[s]{/*}{*/},%
  morestring=[b]",%
  morestring=[b]',%
}[keywords,comments,strings]%

\lstdefinelanguage{Ocamllex}{
  keywords={let,rule,and,parse,type,of,function,try,with,as},%
  morekeywords=[2]{int,string,unit,float,bool,char,list},%
  comment=[n]{(*}{*)},%
  otherkeywords={|,\{,\},?,^,=,[,],->},%
  literate=*{*}{{\lst@keywordstyle *}}{1} {+}{{\lst@keywordstyle +}}{1} {-}{{\lst@keywordstyle -}}{1} {!(}{{\lst@keywordstyle (}}{1} {!)}{{\lst@keywordstyle )}}{1} {![}{{\color{orange}\textrm{[}}}{1} {!]}{{\color{orange}\textrm{]}}}{1},
  %sensitive,%
  morecomment=[s]{/*}{*/},%
  morestring=[b]",%
  morestring=[b]',%
}[keywords,comments,strings]%

\lstdefinelanguage{Menhir}{
  keywords={\%token,\%start,\%type,\%left,\%right,\%nonassoc,\%inline,\%prec,\%\%,\%\},\%\{,\{,\},open},%
  alsoletter={\%,\{,\}},%
  otherkeywords={|,<,>,:},%
  sensitive,%
  morecomment=[n]{(*}{*)},%
  morecomment=[s]{/*}{*/},%
  morestring=[b]",%
  morestring=[b]',%
}[keywords,comments,strings]%

\lstdefinelanguage{OCamlType}{
  keywords={%
    int,string,unit,float,bool,char,list,%
  },%
  alsoletter={'},%
%  otherkeywords={->,*},%
  sensitive,%
}[keywords]%

\lstset{
  literate={à}{{\`a}}1 {À}{{\`A}}1 {ä}{{\"a}}1 {Ä}{{\"A}}1 {á}{{\'a}}1 {ã}{{\~a}}1 {é}{{\'e}}1 {è}{{\`e}}1 {ë}{{\"e}}1 {Ç}{{\c C}}1 {ç}{{\c c}}1 {€}{\euro}1 {ö}{{\"o}}1 {ü}{{\"u}} 1{Ö}{{\"O}}1 {Ü}{{\"U}}1 {µ}{\textmu}1 {ß}{{\ss}}1  {â}{{\^a}}1 {ê}{{\^e}}1 {ô}{{\^o}}1 {û}{{\^u}}1 {î}{{\^i}}1 {ï}{{\"i}}1,%TODO: add more possibilities
}


\newcommand{\ocamltype}[2][]{{\color{violet}\lstinline[language=OCamlType,texcl=false,keywordstyle=,identifierstyle=,basicstyle=\ttfamily,showspaces,#1]{#2}}}

\def\nespace{\hskip\fontdimen2\font\relax}
\newcommand*\prompt{{\color{black!60}\texttt{\# }}}
\newcommand*\eoe{{\color{black!60}\texttt{$\;$;$\!$;}}}
\newcommand*\ega{{\color{black!60}\texttt{$\;$=$\;$}}}
\newcommand*\eot{{\color{black!60}\texttt{$\;$:$\,$}}}
\newcommand*\pval{{\color{black!60}\texttt{val$\;$}}}
\newcommand*\pcol{{\color{black!60}\texttt{-}}}
\newcommand*\ptyp{{\color{black!60}\texttt{type$\;$}}}
\newcommand*\pexc{{\color{black!60}\texttt{exception$\;$}}}
\newcommand*\command[5][]{%
  \begin{tabular}{@{}l@{}}
    \prompt\marknode{expr#1}{\texttt{\lstinline{#2}}}\eoe\\
    \texttt{\pval\marknode{name#1}{\lstinline{#3}}\eot\marknode{type#1}{\ocamltype{#4}}\ega\marknode{value#1}{\color{brown}#5}}
  \end{tabular}}
\newcommand*\commandonly[2][]{%
  \begin{tabular}{@{}l@{}}
    \prompt\marknode{expr#1}{\lstinline{#2}}\eoe
  \end{tabular}}
\newcommand*\commandlarge[5][]{%
  \begin{tabular}{@{}l@{}}
    \prompt\marknode{expr#1}{\lstinline{#2}}\eoe\\
    \texttt{\pval\marknode{name#1}{\lstinline{#3}}\eot\marknode{type#1}{\ocamltype{#4}}\ega}\\
    \texttt{\marknode{value#1}{\color{brown}~~\begin{tabular}{@{}l@{}}#5\end{tabular}}}
  \end{tabular}}
\newcommand*\commandval[4][]{%
  \begin{tabular}[t]{@{}l@{}}
    \prompt\marknode{expr#1}{\texttt{\lstinline{#2}}}\eoe\\
    \texttt{\pcol\eot\marknode{type#1}{\ocamltype{#3}}\ega\marknode{value#1}{\color{brown}#4}}
  \end{tabular}}
\newcommand*\commandvalonly[4][]{%
  \begin{tabular}{@{}l@{}}
    \texttt{\pval\marknode{name#1}{\lstinline{#2}}\eot\marknode{type#1}{\ocamltype{#3}}\ega\marknode{value#1}{\color{brown}#4}}
  \end{tabular}}
\newcommand*\commandtype[4][]{%
  \begin{tabular}{@{}l@{}}
    \prompt\marknode{expr#1}{\lstinline{#2}}\eoe\\
    \texttt{\ptyp\marknode{name#1}{\lstinline{#3}}\ega\marknode{value#1}{\color{brown}#4}}
  \end{tabular}}
\newcommand*\commandlargetype[4][]{%
  \begin{tabular}{@{}l@{}}
    \prompt\marknode{expr#1}{\lstinline{#2}}\eoe\\
    \texttt{\ptyp\marknode{name#1}{\lstinline{#3}}\ega}\\
    \texttt{\marknode{value#1}{\color{brown}~~\begin{tabular}{@{}l@{}}#4\end{tabular}}}
  \end{tabular}}
\newcommand*\commandterr[4][10.5cm]{%
  \begin{tabular}{@{}p{#1}@{}}
    \prompt\lstinline{#2}\eoe\\
    \texttt{Error:$\;$This\nespace expression\nespace has\nespace type\nespace \ocamltype{#3}\nespace but\nespace an\nespace expression\nespace was\nespace expected\nespace of\nespace type\nespace \ocamltype{#4}}
  \end{tabular}}
\newcommand*\commandterrOld[4][10.5cm]{%
  \begin{tabular}{@{}p{#1}@{}}
    \prompt#2\eoe\\
    \texttt{Error:$\;$This\nespace expression\nespace has\nespace type\nespace \ocamltype{#3}\nespace but\nespace an\nespace expression\nespace was\nespace expected\nespace of\nespace type\nespace \ocamltype{#4}}
  \end{tabular}}

\def\lst@visiblespace{\lst@ttfamily{$\;$}{$\;$}}
% or
%\def\lst@visiblespace{\lst@ttfamily{\ }{\ }}

% comming from https://tex.stackexchange.com/questions/127010/how-can-i-make-lstinline-function-normally-inside-math-mode
\RequirePackage{letltxmacro}
\newcommand*{\SavedLstInline}{}
\LetLtxMacro\SavedLstInline\lstinline
\DeclareRobustCommand*{\lstinline}{%
  \ifmmode
    \let\SavedBGroup\bgroup
    \def\bgroup{%
      \let\bgroup\SavedBGroup
      \hbox\bgroup
    }%
  \fi
  \SavedLstInline
}

\lstnewenvironment{ocamllex}[1][]{\bgroup\lstset{language=Ocamllex,moredelim=**[is][\color{orange}]{(*!*)}{(*!*)}}}{\egroup}%
\newcommand{\olex}[2][]{\lstinline[language=Ocamllex,#1]{#2}}
\newcommand{\inputOlex}[2][]{%
  \lstinputlisting[language=Ocamllex,#1]{#2}%
}

\lstnewenvironment{menhir}[1][]{\bgroup\lstset{language=Menhir,moredelim=**[is][\color{orange}]{(*!*)}{(*!*)}}}{\egroup}%
\newcommand{\menh}[2][]{\lstinline[language=Menhir,#1]{#2}}
\newcommand{\inputMenhir}[2][]{%
  \lstinputlisting[language=Menhir,#1]{#2}%
}
