# -*- mode: Perl -*-
# /=====================================================================\ #
# | natbib | #
# | Implementation for LaTeXML | #
# |=====================================================================| #
# | Part of LaTeXML: | #
# | Public domain software, produced as part of work done by the | #
# | United States Government & not subject to copyright in the US. | #
# |---------------------------------------------------------------------| #
# | Bruce Miller <bruce.miller@nist.gov> #_# | #
# | http://dlmf.nist.gov/LaTeXML/ (o o) | #
# \=========================================================ooo==U==ooo=/ #
package LaTeXML::Package::Pool;
use strict;
use warnings;
use LaTeXML::Package;
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# natbib
# following natbib.pdf document
# The basic support function for citation styles is in LaTeX.pool
# Other formatting support is in Post::MakeBibliography
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#======================================================================
# 5. Package Options
# The kind of citation
DeclareOption('numbers', sub {
setCitationStyle(numbers => 1);
ExecuteOptions('square', 'comma', 'nobibstyle'); });
DeclareOption('super', sub {
setCitationStyle(super => 1, open => Tokens(), close => Tokens());
ExecuteOptions('nobibstyle'); });
DeclareOption('authoryear', sub {
setCitationStyle(authoryear => 1);
ExecuteOptions('round', 'semicolon', 'bibstyle'); });
# # The kind of braces around citations
DeclareOption('round', sub {
setCitationStyle(round => 1);
ExecuteOptions('nobibstyle'); });
DeclareOption('curly', sub {
setCitationStyle(curly => 1);
ExecuteOptions('nobibstyle'); });
DeclareOption('square', sub {
setCitationStyle(square => 1);
ExecuteOptions('nobibstyle'); });
DeclareOption('angle', sub {
setCitationStyle(angle => 1);
ExecuteOptions('nobibstyle'); });
# The kind of separator between multiple citations
DeclareOption('comma', sub {
setCitationStyle(comma => 1);
ExecuteOptions('nobibstyle'); });
DeclareOption('semicolon', sub {
setCitationStyle(semicolon => 1);
ExecuteOptions('nobibstyle'); });
DeclareOption('colon', sub {
ExecuteOptions('semicolon'); }); # SIC!
# This disables any future \bibstyle found in the aux file (or from \bibliographystyle)
DeclareOption('nobibstyle', sub {
Let('\bibstyle', '\@gobble'); });
DeclareOption('bibstyle', sub {
Let('\bibstyle', '\@citestyle'); });
# sorting options
DeclareOption('sort', sub { });
DeclareOption('sort&compress', sub { });
DeclareOption('compress', sub { });
DeclareOption('longnamesfirst', sub { });
DeclareOption('openbib', sub { });
DeclareOption('sectionbib', sub {
AssignMapping('BACKMATTER_ELEMENT', 'ltx:bibliography' => 'ltx:section'); });
DeclareOption('nonamebreak', sub { });
# They _say_ round & semicolon but ...
#ExecuteOptions('round','semicolon','authoryear');
#ExecuteOptions('square','comma','authoryear');
setCitationStyle(round => 1, semicolon => 1);
# NOTE \bibliographystyle puts \bibstyle in the *.aux, read at the beginning!
# Normally, \bibliography appears at the end next to the bibliography.... it's TOO LATE!
# [FUTURE: Maybe this can be encoded as attributes on ltx:bibliography ???
# But then we need to put in stub elements to be filled in by CrossRef! Ugh]
# In any case, \bibstyle may get redefined by options, so define it now!
DefMacro('\bibstyle{}', sub {
my $style = T_CS('\bibstyle@' . ToString($_[1]));
(LookupDefinition($style) ? ($style) : (T_CS('\relax'))); });
#AtBeginDocument{\global\let\bibstyle=\@gobble}
Let('\@citestyle', '\bibstyle');
# # They _say_ round & semicolon but ...
ExecuteOptions('round', 'semicolon', 'authoryear');
# ExecuteOptions('square','comma','authoryear');
AssignValue(CITE_STYLE => 'authoryear'); # the default?
ProcessOptions();
#======================================================================
# 2.3 Basic Citation Commands
# Leverage the definitions in LaTeX.pool.
# Note that LaTeX's \cite command is treated almost equivalent to \citet in authoryear mode,
# but like \citep in numbers mode.
DefMacro('\cite OptionalMatch:* [][] Semiverbatim', sub {
my ($gullet, $star, $pre, $post, $keys) = @_;
my ($style, $open, $close, $ns, $ay)
= map { LookupValue($_) } qw(CITE_STYLE CITE_OPEN CITE_CLOSE CITE_NOTE_SEPARATOR CITE_AY_SEPARATOR);
if (!$post) { ($pre, $post) = (undef, $pre); }
$pre = undef if IsEmpty($pre);
$post = undef if IsEmpty($post);
my $author = ($star ? "FullAuthors" : "Authors");
if ($style eq 'numbers') {
Invocation(T_CS('\@@cite'),
Tokens(Explode('cite')),
Tokens($open, ($pre ? ($pre, T_SPACE) : ()),
Invocation(T_CS('\@@bibref'), Tokens(Explode("Number")), $keys, undef, undef),
($post ? ($ns, T_SPACE, $post) : ()),
$close)); }
elsif ($style eq 'super') {
Invocation(T_CS('\@@cite'),
Tokens(Explode('cite')),
Tokens(($pre ? ($pre, T_SPACE) : ()), Invocation(T_CS('\textsuperscript'),
Invocation(T_CS('\@@bibref'), Tokens(Explode("Number")), $keys,
undef, undef)),
($post ? (T_SPACE, $post) : ()))); }
else {
# Appears to be textual, unless pre or post phrase, then parenthetical (!)
if ($pre || $post) {
Invocation(T_CS('\@@cite'),
Tokens(Explode('cite')),
Tokens($open->unlist, ($pre ? ($pre, T_SPACE) : ()),
Invocation(T_CS('\@@bibref'),
Tokens(Explode($author . "Phrase1Year")),
$keys,
Invocation(T_CS('\@@citephrase'),
Tokens($ay->unlist, T_SPACE)), undef),
($post ? ($ns, T_SPACE, $post) : ()), $close)); }
else {
Invocation(T_CS('\@@cite'),
Tokens(Explode('cite')),
Invocation(T_CS('\@@bibref'),
Tokens(Explode($author . " Phrase1YearPhrase2")),
$keys,
Invocation(T_CS('\@@citephrase'), $open),
Invocation(T_CS('\@@citephrase'), $close))); } }
}, locked => 1);
DefMacro('\citet OptionalMatch:* [][] Semiverbatim', sub {
my ($gullet, $star, $pre, $post, $keys) = @_;
my ($style, $open, $close, $ns)
= map { LookupValue($_) } qw(CITE_STYLE CITE_OPEN CITE_CLOSE CITE_NOTE_SEPARATOR);
if (!$post) { ($pre, $post) = (undef, $pre); }
$pre = undef if IsEmpty($pre);
$post = undef if IsEmpty($post);
my $author = ($star ? "FullAuthors" : "Authors");
if ($style eq 'numbers') {
Invocation(T_CS('\@@cite'),
Tokens(Explode('citet')),
Tokens( #($pre ? ($pre, T_SPACE) : ()),
Invocation(T_CS('\@@bibref'),
Tokens(Explode("$author Phrase1NumberPhrase2")),
$keys,
Invocation(T_CS('\@@citephrase'),
Tokens($open, ($pre ? ($pre, T_SPACE) : ()))),
Invocation(T_CS('\@@citephrase'),
Tokens(($post ? ($ns->unlist, T_SPACE, $post->unlist) : ()), $close->unlist))
)))->unlist; }
elsif ($style eq 'super') {
Invocation(T_CS('\@@cite'),
Tokens(Explode('citet')),
Tokens(($pre ? ($pre, T_SPACE) : ()),
Invocation(T_CS('\@@bibref'),
Tokens(Explode("$author Phrase1SuperPhrase2")),
$keys, undef, undef)->unlist,
($post ? ($ns, T_SPACE, $post->unlist) : ()))); }
else {
Invocation(T_CS('\@@cite'),
Tokens(Explode('citet')),
Invocation(T_CS('\@@bibref'),
Tokens(Explode("$author Phrase1YearPhrase2")),
$keys,
Invocation(T_CS('\@@citephrase'),
Tokens($open, ($pre ? ($pre, T_SPACE) : ()))),
Invocation(T_CS('\@@citephrase'),
Tokens(($post ? ($ns, T_SPACE, $post) : ()), $close)))); }
}, locked => 1);
DefMacro('\citep OptionalMatch:* [][] Semiverbatim', sub {
my ($gullet, $star, $pre, $post, $keys) = @_;
my ($style, $open, $close, $ns, $ay)
= map { LookupValue($_) } qw(CITE_STYLE CITE_OPEN CITE_CLOSE
CITE_NOTE_SEPARATOR CITE_AY_SEPARATOR);
if (!$post) { ($pre, $post) = (undef, $pre); }
$pre = undef if IsEmpty($pre);
$post = undef if IsEmpty($post);
my $author = ($star ? "FullAuthors" : "Authors");
if ($style eq 'numbers') {
Invocation(T_CS('\@@cite'),
Tokens(Explode('citep')),
Tokens($open, ($pre ? ($pre, T_SPACE) : ()),
Invocation(T_CS('\@@bibref'), Tokens(Explode("Number")), $keys, undef, undef),
($post ? ($ns, T_SPACE, $post) : ()), $close)); }
elsif ($style eq 'super') {
Invocation(T_CS('\@@cite'),
Tokens(Explode('citep')),
Tokens(($pre ? ($pre, T_SPACE) : ()),
Invocation(T_CS('\@@bibref'), Tokens(Explode("Super")), $keys, undef, undef),
($post ? (T_SPACE, $post) : ()))); }
else {
Invocation(T_CS('\@@cite'),
Tokens(Explode('citep')),
Tokens($open->unlist, ($pre ? ($pre, T_SPACE) : ()),
Invocation(T_CS('\@@bibref'),
Tokens(Explode("${author}Phrase1Year")),
$keys,
Invocation(T_CS('\@@citephrase'), Tokens($ay->unlist, T_SPACE)),
undef),
($post ? ($ns, T_SPACE, $post) : ()), $close)); }
}, locked => 1);
#======================================================================
# 2.4 Extended Citation Commands
DefMacro('\@@cite@noparens', sub {
AssignValue(CITE_OPEN => Tokens());
AssignValue(CITE_CLOSE => Tokens()); });
# The next two are the same as \citet, \citep, but redefine open & close to empty.
DefMacro('\citealt OptionalMatch:* [][] Semiverbatim', sub {
my ($gullet, $star, $pre, $post, $keys) = @_;
(T_CS('\bgroup'), T_CS('\@@cite@noparens'),
Invocation(T_CS('\citet'), $star, $pre, $post, $keys)->unlist,
T_CS('\egroup')); });
DefMacro('\citealp OptionalMatch:* [][] Semiverbatim', sub {
my ($gullet, $star, $pre, $post, $keys) = @_;
(T_CS('\bgroup'), T_CS('\@@cite@noparens'),
Invocation(T_CS('\citep'), $star, $pre, $post, $keys)->unlist,
T_CS('\egroup')); });
DefMacro('\citenum Semiverbatim', sub { # No optional ?
my ($gullet, $keys) = @_;
Invocation(T_CS('\@@cite'),
Tokens(Explode('citenum')),
Tokens(Invocation(T_CS('\@@bibref'), Tokens(Explode("Number")), $keys, undef, undef))); });
# Sorta right, but would like to avoid the nested <ltx:cite>!
# maybe can neutralize \@@cite?
DefMacro('\citetext', '\@@cite');
DefMacro('\citeauthor OptionalMatch:* [][] Semiverbatim', sub {
my ($gullet, $star, $pre, $post, $keys) = @_;
my $author = ($star ? "FullAuthors" : "Authors");
if (!$post) { ($pre, $post) = (undef, $pre); }
$pre = undef if IsEmpty($pre);
$post = undef if IsEmpty($post);
my $ns = LookupValue('CITE_NOTE_SEPARATOR');
Invocation(T_CS('\@@cite'),
Tokens(Explode('citeauthor')),
Tokens( # ($pre ? ($pre, T_SPACE) : ()),
Invocation(T_CS('\@@bibref'), Tokens(Explode($author)), $keys, undef, undef),
($post ? ($ns, T_SPACE, $post) : ()))); });
DefMacro('\citefullauthor [][] Semiverbatim', sub {
my ($gullet, $pre, $post, $keys) = @_;
if (!$post) { ($pre, $post) = (undef, $pre); }
$pre = undef if IsEmpty($pre);
$post = undef if IsEmpty($post);
my $ns = LookupValue('CITE_NOTE_SEPARATOR');
Invocation(T_CS('\@@cite'),
Tokens(Explode('citefullauthor')),
Tokens( # ($pre ? ($pre, T_SPACE) : ()),
Invocation(T_CS('\@@bibref'), Tokens(Explode("FullAuthors")), $keys, undef, undef),
($post ? ($ns, T_SPACE, $post) : ()))); });
DefMacro('\citeyear [][] Semiverbatim', sub {
my ($gullet, $pre, $post, $keys) = @_;
if (!$post) { ($pre, $post) = (undef, $pre); }
$pre = undef if IsEmpty($pre);
$post = undef if IsEmpty($post);
my $ns = LookupValue('CITE_NOTE_SEPARATOR');
Invocation(T_CS('\@@cite'),
Tokens(Explode('citeyear')),
Tokens( # ($pre ? ($pre, T_SPACE) : ()),
Invocation(T_CS('\@@bibref'), Tokens(Explode("Year")), $keys, undef, undef),
($post ? ($ns, T_SPACE, $post) : ()))); });
DefMacro('\citeyearpar [][] Semiverbatim', sub {
my ($gullet, $pre, $post, $keys) = @_;
my ($open, $close) = map { LookupValue($_) } qw(CITE_OPEN CITE_CLOSE);
if (!$post) { ($pre, $post) = (undef, $pre); }
$pre = undef if IsEmpty($pre);
$post = undef if IsEmpty($post);
my $ns = LookupValue('CITE_NOTE_SEPARATOR');
Invocation(T_CS('\@@cite'),
Tokens(Explode('citeyearpar')),
Tokens($open,
($pre ? ($pre, T_SPACE) : ()),
Invocation(T_CS('\@@bibref'), Tokens(Explode("Year")), $keys, undef, undef),
($post ? ($ns, T_SPACE, $post) : ()),
$close)); });
#======================================================================
# 2.5 Forcing Upper Cased Name
# These are SUPPOSED to capitalize the first letter, .. but
DefMacro('\Citet', '\citet');
DefMacro('\Citep', '\citep');
DefMacro('\Citealt', '\citealt');
DefMacro('\Citealp', '\citealp');
DefMacro('\Citeauthor', '\citeauthor');
#======================================================================
# 2.6 Citation Aliasing
# Citation aliasing is achieved with
# \defcitealias{key}{text}
# \citetalias{key} ==>> text
# \citepalias{key} ==>> (text)
# should end the defined key with \@extra@b@citeb ???
DefPrimitive('\defcitealias Semiverbatim {}', sub {
my ($gullet, $key, $text) = @_;
DefMacroI(T_CS('\al@' . ToString($key)), undef, $text); });
# These use the above defined text to fill in the bibref
# (which will still be a link to the bibitem!)
DefMacro('\citetalias [][] Semiverbatim', sub {
my ($gullet, $pre, $post, $key) = @_;
my ($open, $close) = map { LookupValue($_) } qw(CITE_OPEN CITE_CLOSE);
if (!$post) { ($pre, $post) = (undef, $pre); }
$pre = undef if IsEmpty($pre);
$post = undef if IsEmpty($post);
Invocation(T_CS('\@@cite'),
Tokens(Explode('citealias')),
Tokens(($pre ? ($pre, T_SPACE) : ()),
Invocation(T_CS('\@@bibref'),
Tokens(Explode("Phrase1")),
$key,
Invocation(T_CS('\@@citephrase'), T_CS('\al@' . ToString($key)))),
($post ? (T_SPACE, $post) : ()))); });
DefMacro('\citepalias [][] Semiverbatim', sub {
my ($gullet, $pre, $post, $key) = @_;
my ($open, $close, $ns) = map { LookupValue($_) } qw(CITE_OPEN CITE_CLOSE CITE_NOTE_SEPARATOR);
Invocation(T_CS('\@@cite'),
Tokens(Explode('citepalias')),
Tokens($open,
($pre ? ($pre, T_SPACE) : ()),
Invocation(T_CS('\@@bibref'),
Tokens(Explode("Phrase1")),
$key,
Invocation(T_CS('\@@citephrase'), T_CS('\al@' . ToString($key)))),
($post ? ($ns, T_SPACE, $post) : ()),
$close)); });
#======================================================================
# 2.9 Selecting Citation Punctuation
DefKeyVal('natbib', 'authoryear', '', 'true');
DefKeyVal('natbib', 'numbers', '', 'true');
DefKeyVal('natbib', 'super', '', 'true');
DefKeyVal('natbib', 'round', '', 'true');
DefKeyVal('natbib', 'square', '', 'true');
DefKeyVal('natbib', 'open', '');
DefKeyVal('natbib', 'close', '');
DefKeyVal('natbib', 'semicolon', '');
DefKeyVal('natbib', 'comma', '');
DefKeyVal('natbib', 'citesep', '');
DefKeyVal('natbib', 'aysep', '');
DefKeyVal('natbib', 'yysep', '');
DefKeyVal('natbib', 'notesep', '');
AssignValue(CITE_AY_SEPARATOR => T_OTHER(','));
sub setCitationStyle {
my (@pairs) = @_;
while (@pairs) {
my ($key, $value) = (shift(@pairs), shift(@pairs));
$key = ToString(Digest($key)) if ref $key;
if ($key eq 'authoryear') { AssignValue(CITE_STYLE => 'authoryear'); }
elsif ($key eq 'numbers') { AssignValue(CITE_STYLE => 'numbers'); }
elsif ($key eq 'super') { AssignValue(CITE_STYLE => 'super'); }
elsif ($key eq 'round') { AssignValue(CITE_OPEN => T_OTHER('('));
AssignValue(CITE_CLOSE => T_OTHER(')')); }
elsif ($key eq 'square') { AssignValue(CITE_OPEN => T_OTHER('['));
AssignValue(CITE_CLOSE => T_OTHER(']')); }
elsif ($key eq 'curly') { AssignValue(CITE_OPEN => T_OTHER('{'));
AssignValue(CITE_CLOSE => T_OTHER('}')); }
elsif ($key eq 'angle') { AssignValue(CITE_OPEN => T_OTHER('<'));
AssignValue(CITE_CLOSE => T_OTHER('>')); }
elsif ($key eq 'open') { AssignValue(CITE_OPEN => $value); }
elsif ($key eq 'close') { AssignValue(CITE_CLOSE => $value); }
elsif ($key eq 'semicolon') { AssignValue(CITE_SEPARATOR => T_OTHER(';')); }
elsif ($key eq 'comma') { AssignValue(CITE_SEPARATOR => T_OTHER(',')); }
elsif ($key eq 'aysep') { AssignValue(CITE_AY_SEPARATOR => $value); }
elsif ($key eq 'yysep') { AssignValue(CITE_YY_SEPARATOR => $value); }
elsif ($key eq 'notesep') { AssignValue(CITE_NOTE_SEPARATOR => $value); }
else {
Warn('unexpected', $key, undef, "Unexpected Citation Style keyword '$key'"); } }
return; }
DefPrimitive('\setcitestyle RequiredKeyVals:natbib', sub {
setCitationStyle($_[1]->getPairs); });
DefPrimitive('\bibpunct[]{}{}{}{}{}{}', sub {
my ($stomach, $notesep, $open, $close, $sep, $style, $aysep, $yysep) = @_;
$style = ToString(Digest($style));
AssignValue(CITE_OPEN => $open);
AssignValue(CITE_CLOSE => $close);
AssignValue(CITE_SEPARATOR => $sep);
AssignValue(CITE_STYLE => ($style eq 'n' ? 'numbers' : ($style eq 's' ? 'super' : 'authoryear')));
AssignValue(CITE_AY_SEPARATOR => $aysep);
AssignValue(CITE_YY_SEPARATOR => $yysep);
AssignValue(CITE_NOTE_SEPARATOR => $notesep) if $notesep;
return; });
DefMacro('\citestyle{}', '\@citestyle{#1}\let\bibstyle\@gobble');
DefMacro('\bibstyle@chicago', '\bibpunct{(}{)}{;}{a}{,}{,}');
DefMacro('\bibstyle@named', '\bibpunct{[}{]}{;}{a}{,}{,}');
DefMacro('\bibstyle@agu', '\bibpunct{[}{]}{;}{a}{,}{,~}'); #Amer. Geophys. Union
DefMacro('\bibstyle@copernicus', '\bibpunct{(}{)}{;}{a}{,}{,}'); #Copernicus Publications
Let('\bibstyle@egu', '\bibstyle@copernicus');
Let('\bibstyle@egs', '\bibstyle@copernicus');
DefMacro('\bibstyle@agsm', '\bibpunct{(}{)}{,}{a}{}{,}\gdef\harvardand{\&}');
DefMacro('\bibstyle@kluwer', '\bibpunct{(}{)}{,}{a}{}{,}\gdef\harvardand{\&}');
DefMacro('\bibstyle@dcu', '\bibpunct{(}{)}{;}{a}{;}{,}\gdef\harvardand{and}');
DefMacro('\bibstyle@aa', '\bibpunct{(}{)}{;}{a}{}{,}'); # Astronomy & Astrophysics
DefMacro('\bibstyle@pass', '\bibpunct{(}{)}{;}{a}{,}{,}'); #Planet. & Space Sci
DefMacro('\bibstyle@anngeo', '\bibpunct{(}{)}{;}{a}{,}{,}'); #Annales Geophysicae
DefMacro('\bibstyle@nlinproc', '\bibpunct{(}{)}{;}{a}{,}{,}'); #Nonlin.Proc.Geophys.
DefMacro('\bibstyle@cospar', '\bibpunct{/}{/}{,}{n}{}{}');
DefMacro('\bibstyle@esa', '\bibpunct{(Ref.~}{)}{,}{n}{}{}');
DefMacro('\bibstyle@nature', '\bibpunct{}{}{,}{s}{}{\textsuperscript{,}}');
DefMacro('\bibstyle@plain', '\bibpunct{[}{]}{,}{n}{}{,}');
Let('\bibstyle@alpha', '\bibstyle@plain');
Let('\bibstyle@abbrv', '\bibstyle@plain');
Let('\bibstyle@unsrt', '\bibstyle@plain');
DefMacro('\bibstyle@plainnat', '\bibpunct{[}{]}{,}{a}{,}{,}');
Let('\bibstyle@abbrvnat', '\bibstyle@plainnat');
Let('\bibstyle@unsrtnat', '\bibstyle@plainnat');
#======================================================================
# 2.12 Other Formatting Options
# mostly ignored...
DefMacro('\bibname', 'Bibliography');
DefMacro('\refname', 'References');
DefMacro('\bibsection', '');
DefMacro('\bibpreamble', '');
DefMacro('\bibfont', '');
DefMacro('\citenumfont', '');
DefMacro('\bibnumfmt{}', '#1');
DefRegister('\bibhang', Dimension(0));
DefRegister('\bibsep', Dimension(0));
#======================================================================
# 2.13 Automatic Indexing of Citations
# Ignored, but could be done...
# However, it is basically equivalent to backrefs which are
# automatically handled in MakeBibliography, anyway...
RawTeX('\newif\ifciteindex');
DefMacro('\citeindextrue', '');
DefMacro('\citeindexfalse', '');
DefMacro('\citeindextype', '');
#======================================================================
# 2.17 Long Author List on First Citation
# Ignored (for now...)
DefMacro('\shortcites Semiverbatim', '');
#======================================================================
# Less Documented
# For manually formatted bibliographies, the following magical incantations
# will be recognized to deliniate the author and year:
# \bibitem[Jones et al.(1990)]{key}...
# \bibitem[Jones et al.(1990)Jones, Baker, and Williams]{key}...
# \bibitem[Jones et al., 1990]{key}...
# \bibitem[\protect\citeauthoryear{Jones, Baker, and Williams}{Jones et al.}{1990}]{key}...
# \bibitem[\protect\citeauthoryear{Jones et al.}{1990}]{key}...
# \bibitem[\protect\astroncite{Jones et al.}{1990}]{key}...
# \bibitem[\protect\citename{Jones et al., }1990]{key}...
# \harvarditem[Jones et al.]{Jones, Baker, and Williams}{1990}{key}...
DefMacro('\lx@nat@bibitem',
## '\reset@natbib@cites\refstepcounter{@bibitem}\@ifnextchar[{\@lbibitem}{\@lbibitem[\the@bibitem]}',
'\reset@natbib@cites\refstepcounter{@bibitem}\@ifnextchar[{\@lbibitem}{\@lbibitem[]}',
locked => 1);
# Careful: since OmniBus also defines \bibitem and LaTeX.pool has a save/restore mechanism,
# it is safer to \let to a constant natbib-specific macro definition. Test with arXiv:2007.09909
Let('\bibitem', '\lx@nat@bibitem');
RawTeX(<<'EOTeX');
%%%
\def\citeauthoryear#1#2#3(@)(@)\@nil#4{%
\if\relax#3\relax
\NAT@wrout{\the@bibitem}{#2}{#1}{}{#4}\else
\NAT@wrout{\the@bibitem}{#3}{#2}{#1}{#4}\fi}
\let\natbib@citeauthoryear\citeauthoryear
\def\astroncite#1#2(@)(@)\@nil#3{%
\NAT@wrout{\the@bibitem}{#2}{#1}{}{#3}}
\let\natbib@astroncite\astroncite
\def\citename#1#2(@)(@)\@nil#3{%
\expandafter\NAT@apalk#1#2, \@nil{#3}}
\let\natbib@citename\citename
\newcommand\harvarditem[4][]{%
\if\relax#1\relax\bibitem[#2(#3)]{#4}\else\bibitem[#1(#3)#2]{#4}\fi }
%%%%
\newcommand\NAT@ifcmd{\futurelet\NAT@temp\NAT@ifxcmd}
\newcommand\NAT@ifxcmd{\ifx\NAT@temp\relax\else\expandafter\NAT@bare\fi}
\def\NAT@bare#1(#2)#3(@)#4\@nil#5{%
\if @#2%
\expandafter\NAT@apalk#1, , \@nil{#5}\else
\NAT@wrout{\the@bibitem}{#2}{#1}{#3}{#5}\fi}
\def\NAT@apalk#1, #2, #3\@nil#4{%
% \if\relax#2\relax\NAT@wrout{#1}{}{}{}{#4}\else\NAT@wrout{\the@bibitem}{#2}{#1}{}{#4}\fi}
\NAT@wrout{\the@bibitem}{#2}{#1}{}{#4}}
%%%%
EOTeX
# Sometimes, perversely, redefined, so re-redefine them now...
DefPrimitiveI('\reset@natbib@cites', undef, sub {
Let('\citeauthoryear', '\natbib@citeauthoryear');
Let('\astroncite', '\natbib@astroncite');
Let('\citename', '\natbib@citename'); });
# Kinda rediculous simulating this here (we can almost pull it off in TeX)
# but the TeX tends to be quite brittle in the wilds of arXiv, so...
DefMacro('\lx@NAT@parselabel{}{}', sub {
my ($gullet, $label, $key) = @_;
my ($number, $year, $authors, $fullauthors);
my $bare = 1;
my @tokens = $label->unlist;
shift(@tokens) if Equals(T_CS('\protect'), $tokens[0]); # Ignore \protect
if ($tokens[0] && ($tokens[0]->getCatcode == CC_CS)) {
my $cs = $tokens[0];
my ($a1, $a2);
if (Equals(T_CS('\citeauthoryear'), $cs)) {
# \bibitem[\protect\citeauthoryear{Jones, Baker, and Williams}{Joneset al.}{1990}]{key}
# \bibitem[\protect\citeauthoryear{Jones et al.}{1990}]{key}...
# \bibitem[\protect\citeauthoryear{Jones} {1990}]{key}...
shift(@tokens);
($a1, @tokens) = NAT_peel_arg(@tokens);
($a2, @tokens) = NAT_peel_arg(@tokens);
if (@tokens) {
$fullauthors = $a1; $authors = $a2; $year = Tokens(@tokens); $bare = 0; }
else {
$authors = $a1; $year = $a2; $bare = 0; } }
elsif (Equals(T_CS('\astroncite'), $cs)) {
# \bibitem[\protect\astroncite{Jones et al.}{1990}]{key}...
shift(@tokens);
($a1, @tokens) = NAT_peel_arg(@tokens);
($a2, @tokens) = NAT_peel_arg(@tokens);
$authors = $a1; $year = $a2; $bare = 0; }
elsif (Equals(T_CS('\citename'), $cs)) {
# \bibitem[\protect\citename{Jones et al., }1990]{key}...
shift(@tokens);
($a1, @tokens) = NAT_peel_arg(@tokens);
$authors = $a1; $year = Tokens(@tokens); $bare = 0; } }
if ($bare) {
@tokens = Expand($label)->unlist;
# \bibitem[Jones et al.(1990)]{key}...
# \bibitem[Jones et al.(1990)Jones, Baker, and Williams]{key}...
# \bibitem[Jones et al., 1990]{key}...
my @arg = ();
while (@tokens && !T_OTHER('(')->equals($tokens[0])) {
push(@arg, shift(@tokens)); }
shift(@tokens);
my @year = ();
while (@tokens && !T_OTHER(')')->equals($tokens[0])) {
push(@year, shift(@tokens)); }
shift(@tokens);
if (!@year) {
while (@arg && ($arg[-1]->getCatcode == CC_OTHER)
&& ($arg[-1]->getString =~ /\d/)) { # or even digits only?
unshift(@year, pop(@arg)); }
#pop(@arg);
}
$authors = Tokens(@arg); $year = Tokens(@year); $fullauthors = Tokens(@tokens); }
## Debug("NATLABEL: ".ToString($label)." #=".ToString($number)." y=".ToString($year)
## ." a=".ToString($authors)." f=".ToString($fullauthors)." k=".ToString($key));
return Invocation(T_CS('\NAT@wrout'), ($number || T_CS('\the@bibitem')), $year,
$authors, $fullauthors, $key) });
sub NAT_peel_arg {
my (@tokens) = @_;
my @arg = ();
if (!@tokens) {
return; }
elsif (@tokens && ($tokens[0]->getCatcode != CC_BEGIN)) {
return (shift(@tokens), @tokens); }
else {
my $level = 1;
shift(@tokens);
while (my $t = shift(@tokens)) {
my $cc = $t->getCatcode;
$level++ if $cc == CC_BEGIN;
$level-- if $cc == CC_END;
last unless $level > 0;
push(@arg, $t); }
return (Tokens(@arg), @tokens); } }
# By this time, \NAT@wrout should look like:
# \NAT@wrout{number}{year}{authors}{fullauthors}{bibkey}
# So, we'll do one extra step, and format the refnum form
DefMacro('\NAT@wrout{}{}{}{} Semiverbatim', sub {
my ($gullet, $number, $year, $authors, $fullauthors, $key) = @_;
my ($style, $open, $close) = map { LookupValue($_) } qw(CITE_STYLE CITE_OPEN CITE_CLOSE);
$style = 'number' if IsEmpty($authors) || IsEmpty($year);
if ($style eq 'number') {
Invocation(T_CS('\NAT@@wrout'), $number, $year, $authors, $fullauthors,
Tokens($open, $number, $close),
$key)->unlist; }
else {
Invocation(T_CS('\NAT@@wrout'), $number, $year, $authors, $fullauthors,
Tokens($authors, T_SPACE, $open, $year, $close),
$key)->unlist; } });
DefConstructor('\NAT@@wrout{}{}{}{}{} Semiverbatim',
"<ltx:tags>"
. "?#1(<ltx:tag role='number'>#1</ltx:tag>)"
. "?#2(<ltx:tag role='year'>#2</ltx:tag>)"
. "?#3(<ltx:tag role='authors'>#3</ltx:tag>)"
. "?#4(<ltx:tag role='fullauthors'>#4</ltx:tag>)"
. "?#5(<ltx:tag role='refnum'>#5</ltx:tag>)"
. "?#6(<ltx:tag role='key'>#6</ltx:tag>)"
. "</ltx:tags>",
# Allow plain & in here ???
bounded => 1, beforeDigest => sub { Let(T_ALIGN, '\&'); }
);
# see arXiv:cond-mat/0003435 for
# an infinite loop we run into when this isn't locked
# \@@lbibitem is a DefConstructor, so the connection should be kept
DefMacro('\@lbibitem[]{}',
# '\@@lbibitem{#2}\NAT@ifcmd#1(@)(@)\@nil{#2}\newblock', locked => 1);
'\@@lbibitem{#2}\lx@NAT@parselabel{#1}{#2}\newblock', locked => 1);
# Similar to the one defined in LaTeX.pool, but the bibtag's have been setup above.
DefConstructor('\@@lbibitem Semiverbatim',
"<ltx:bibitem key='#key' xml:id='#id'>",
afterDigest => sub {
my $key = CleanBibKey($_[1]->getArg(1));
my $id = ToString(Expand(T_CS('\the@bibitem@ID')));
$_[1]->setProperties(key => $key, id => $id); });
#======================================================================
# These macros allow you to get the pieces used in the current style
# but don't seem to be used in natbib, so redefining them does nothing.
DefMacro('\citestarts', sub { LookupValue('CITE_OPEN')->unlist; });
DefMacro('\citeends', sub { LookupValue('CITE_CLOSE')->unlist; });
DefMacro('\betweenauthors', 'and');
DefMacro('\harvardleft', sub { LookupValue('CITE_OPEN')->unlist; });
DefMacro('\harvardright', sub { LookupValue('CITE_CLOSE')->unlist; });
DefMacro('\harvardyearleft', sub { LookupValue('CITE_OPEN')->unlist; });
DefMacro('\harvardyearright', sub { LookupValue('CITE_CLOSE')->unlist; });
DefMacro('\harvardand', 'and');
DefConstructor('\harvardurl Semiverbatim',
"<ltx:ref href='#href'>#1</ltx:ref>",
properties => sub { (href => CleanURL(ToString($_[1]))); });
Let('\citeN', '\cite');
Let('\shortcite', '\cite');
Let('\citeasnoun', '\cite');
DefMacro('\natexlab{}', '#1'); # ????
1;