This code is similar to things I do in an unpublished package of mine that I used for several documents, most of this should therefore be relatively tested; though I never really used these nested. Still, use at your own risk.
Just from looking at the code, nesting harray in htabular and vice versa will use the wrong specific hooks in the nested environment, so there is at least one known bug for nesting.
The following provides two environments (htabular and harray) which are patches of the tabular and array environments. It adds a few hooks to them (and keeps track of the current line):
begin is used before the alignment starts
start is used before the first line inside the alignment (might contain \noalign)
bol is used at the begin of every line (in the first line after start, might contain \noalign))
eol is used at the end of every line (inside \\ such that it's part of the last used column)
lastline is used at the same place eol would've been used if the last line of the alignment wasn't ended with \\
stop is used after the last line (might contain \noalign)
end is used after the alignment ends
Each hook is provided three times:
- common hooks
- hooks only used inside
htabular
- hooks only used inside
harray
The common hooks are always executed before the specific ones.
Hooks can be set and cleared via the macros:
\htabAddToHook[<specific>]{<hook>}{<code>} adds the <code> to the respective <hook>, if the optional argument is omitted the generic hook is used, else you can either specify tabular or array in it to set the specific one
\htabClearHook<*>[<specific>]{<hook>} clears the respective <hook>, if the optional star is given for each tabular, array and the generic ones, else if the optional argument is used for those specific one (either tabular or array) and if both is omitted the generic one
\htabClearAllHooks<*>[<specific>] clears all hooks of either tabular, array or the generic ones (or all if the star is given)
The row count can be accessed via htabrow, which is accessible via \value/\arabic/\roman/etc. (but is no real LaTeX-counter).
\documentclass{article}
\usepackage{array}
\usepackage{etoolbox}
\ExplSyntaxOn
\makeatletter
% row counter
\int_new:N \g_htab_row_int
\cs_new_eq:NN \c@htabrow \g_htab_row_int
\seq_new:N \g__htab_row_stack_seq
\cs_new_protected:Npn __htab_push_row:% >>=
{
\seq_gpush:NV \g__htab_row_stack_seq \g_htab_row_int
\int_gzero:N \g_htab_row_int
}% =<<
\cs_new_protected:Npn __htab_pop_row:% >>=
{
\group_begin:
\seq_gpop:NN \g__htab_row_stack_seq \l_tmpa_tl
\int_gset:Nn \g_htab_row_int \l_tmpa_tl
\group_end:
}% =<<
\seq_const_from_clist:Nn \c__htab_hook_names_seq% >>=
{ start, stop, bol, eol, begin, end, lastline }% =<<
% hooks
\seq_map_inline:Nn \c__htab_hook_names_seq % >>=
{
\clist_map_inline:nn { tabular, array }
{ \tl_new:c { l_htab_hook_ ##1/#1 tl } }
\tl_new:c { l_htab_hook #1 tl }
}% =<<
\cs_new:Npn \htab@hook@start@@% >>=
{
\l_htab_hook_start_tl
}% =<<
\cs_new:Npn \htab@hook@stop@@% >>=
{
\l_htab_hook_stop_tl
}% =<<
\cs_new:Npn \htab@hook@bol@@% >>=
{
\noalign { \int_gincr:N \g_htab_row_int }
\l_htab_hook_bol_tl
}% =<<
\cs_new:Npn \htab@hook@eol@@% >>=
{
\l_htab_hook_eol_tl
}% =<<
\cs_new:Npn \htab@hook@lastline@@% >>=
{
\l_htab_hook_lastline_tl
}% =<<
\cs_new:Npn \htab@hook@begin@@% >>=
{
\l_htab_hook_begin_tl
}% =<<
\cs_new:Npn \htab@hook@end@@% >>=
{
\l_htab_hook_end_tl
}% =<<
\cs_new_protected:Npn __htab_add_hooks:n #1% >>=
{
\seq_map_inline:Nn \c__htab_hook_names_seq
{ \tl_put_right:cv { l_htab_hook ##1 tl } { l_htab_hook #1/##1 _tl } }
}% =<<
\cs_generate_variant:Nn \tl_put_right:Nn { cv }
% patches
\msg_new:nnn { htab } { patch~failed } { Patching~ of~ #1 failed. }
\msg_new:nnn { htab } { unknown~hook } { Unknown~ hook~ #1. }
\cs_new_protected:Npn __htab_pretocmd:Nn #1 #2% >>=
{
\pretocmd #1 {#2} {} { \msg_error:nnn { htab } { patch~failed } {#1} }
}% =<<
\cs_new_protected:Npn __htab_apptocmd:Nn #1 #2% >>=
{
\apptocmd #1 {#2} {} { \msg_error:nnn { htab } { patch~failed } {#1} }
}% =<<
\cs_new_protected:Npn __htab_patchcmd:Nnn #1 #2 #3% >>=
{
\patchcmd #1 {#2} {#3} {} { \msg_error:nnn { htab } { patch~failed} {#1} }
}% =<<
\cs_new_protected:Npn __htab_backup:% >>=
{
\cs_set_eq:NN __htab_unpatched_@@array: @@array
\cs_set_eq:NN __htab_unpatched_@arraycr: @arraycr
\cs_set_eq:NN __htab_unpatched_@xarraycr: @xarraycr
\cs_set_eq:NN __htab_unpatched_@xargarraycr: @xargarraycr
\cs_set_eq:NN __htab_unpatched_@yargarraycr: @yargarraycr
\cs_set_eq:NN __htab_unpatched_tabular: \tabular
\cs_set_eq:NN __htab_unpatched_endtabular: \endtabular
\cs_set_eq:NN __htab_unpatched_array: \array
\cs_set_eq:NN __htab_unpatched_endarray: \endarray
}% =<<
\cs_new_protected:Npn \htab@unpatch@@% >>=
{
\cs_set_eq:NN @@array __htab_unpatched_@@array:
\cs_set_eq:NN @arraycr __htab_unpatched_@arraycr:
\cs_set_eq:NN @xarraycr __htab_unpatched_@xarraycr:
\cs_set_eq:NN @xargarraycr __htab_unpatched_@xargarraycr:
\cs_set_eq:NN @yargarraycr __htab_unpatched_@yargarraycr:
\cs_set_eq:NN \tabular __htab_unpatched_tabular:
\cs_set_eq:NN \endtabular __htab_unpatched_endtabular:
\cs_set_eq:NN \array __htab_unpatched_array:
\cs_set_eq:NN \endarray __htab_unpatched_endarray:
}% =<<
% due to etoolbox restrictions names of macros that are patched in don't follow
% the expl3 naming conventions
\cs_new_protected:Npn __htab_patch:n #1% >>=
{
\bool_lazy_or:nnT
{ \cs_if_eq_p:NN __htab_unpatched_tabular: \tabular }
{ !\cs_if_exist_p:N __htab_unpatched_tabular: }
{
__htab_backup:
__htab_pretocmd:Nn \tabular \htab@unpatch@@
__htab_pretocmd:Nn \array \htab@unpatch@@
__htab_pretocmd:Nn @arraycr \htab@hook@eol@@
__htab_apptocmd:Nn @yargarraycr \htab@hook@bol@@
__htab_apptocmd:Nn @xargarraycr \htab@hook@bol@@
__htab_patchcmd:Nnn @xarraycr \cr { \cr \htab@hook@bol@@ }
__htab_patchcmd:Nnn \endarray \crcr { \crcr \htab@hook@stop@@ }
__htab_patchcmd:Nnn @@array \cr
{ \cr \noexpand \htab@hook@start@@ \noexpand \htab@hook@bol@@ }
__htab_add_hooks:n {#1}
}
}% =<<
\cs_new_protected:Npn \htab_add_to_hook:nnn #1#2#3% >>=
{
\tl_if_exist:cTF { l_htab_hook_ #1#2 tl }
{ \tl_put_right:cn { l_htab_hook #1#2 tl } {#3} }
{ \msg_error:nnn { htab } { unknown~hook } {#1#2} }
}% =<<
\NewDocumentCommand \htabAddToHook { o m m }% >>=
{
\tl_if_novalue:nTF {#1}
{ \htab_add_to_hook:nnn {} }
{ \htab_add_to_hook:nnn { #1/ } }
{#2} {#3}
}% =<<
\cs_new_protected:Npn \htab_clear_hook:n #1% >>=
{ \tl_clear:c { l_htab_hook #1 _tl } }% =<<
\NewDocumentCommand \htabClearHook { s o m }% >>=
{
\IfBooleanTF {#1}
{
\htab_clear_hook:n { tabular/#3 }
\htab_clear_hook:n { array/#3 }
\htab_clear_hook:n {#3}
}
{
\tl_if_novalue:nTF {#2}
{ \htab_clear_hook:n {#3} }
{ \htab_clear_hook:n { #2/#3 } }
}
}% =<<
\cs_new_protected:Npn \htab_clear_all_hooks:n #1% >>=
{
\seq_map_inline:Nn \c__htab_hook_names_seq
{ \htab_clear_hook:n { #1##1 } }
}% =<<
\NewDocumentCommand \htabClearAllHooks { s o }% >>=
{
\IfBooleanTF {#1}
{
\htab_clear_all_hooks:n { tabular/ }
\htab_clear_all_hooks:n { array/ }
\htab_clear_all_hooks:n {}
}
{
\tl_if_novalue:nTF {#2}
{ \htab_clear_all_hooks:n {} }
{ \htab_clear_all_hooks:n { #2/ } }
}
}% =<<
\NewDocumentEnvironment { htabular } {}% >>=
{
__htab_push_row:
__htab_patch:n { tabular }
\htab@hook@begin@@
__htab_unpatched_tabular:
}
{
\ifvmode\else\expandafter\htab@hook@lastline@@\fi
\endtabular
\htab@hook@end@@
__htab_pop_row:
}% =<<
\NewDocumentEnvironment { harray } {}% >>=
{
__htab_push_row:
__htab_patch:n { array }
__htab_unpatched_array:
}
{
\ifvmode\else\expandafter\htab@hook@eol@@\fi
\endarray
__htab_pop_row:
}% =<<
\makeatother
\ExplSyntaxOff
% this might be a good idea, this way a htabular and harray always behave as
% if the last line was ended with \\ (so should give consistent results)
\htabAddToHook{lastline}{\}
\usepackage{booktabs}% just for an example
\newcommand\smartmidrule[1] % maybe this isn't really smart :P
{%
\ifnum\value{htabrow}=\numexpr#1\relax
\midrule
\fi
}
\newcolumntype\pseudoenum{@{\hskip\tabcolsep\arabic{htabrow}.\hskip2\tabcolsep}}
\begin{document}
\noindent
Putting \verb|\hline| in \texttt{bol}:\par
\htabAddToHook[tabular]{bol}{\hline}
\begin{htabular}{ll}
a & b \
c & d % would have no \hline without the \htabAddToHook{lastline}{\}
\end{htabular}
\noindent
array is not affected since \texttt{[tabular]} was used:\par
$\begin{harray}{ll}
a & b \
c & d
\end{harray}$
\bigskip
\noindent
Autostyling with \texttt{booktabs} macros and putting \texttt{end} in
\texttt{eol}:\par
\htabClearHook*{bol}
\htabAddToHook{start}{\toprule}
\htabAddToHook{bol}{\smartmidrule{2}}
\htabAddToHook{stop}{\bottomrule}
\htabAddToHook{eol}{\unskip end}
\begin{htabular}{ll}
my & headrow \
a & b \
c & d \
\end{htabular}
\bigskip
\noindent
Nested \texttt{tabular}s aren't affected:\par
\htabClearHook{eol}
\begin{htabular}{ll}
my & headrow \
\begin{tabular}{|c|}a\A\end{tabular} & b \
c & d
\end{htabular}
\bigskip
\noindent
Nested \texttt{htabular}s don't break the row count (but are affected):\par
\htabClearAllHooks*
\htabAddToHook[tabular]{start}{\hline}
\htabAddToHook[tabular]{stop}{\hline}
\begin{htabular}{\pseudoenum ll}
Some & tabular \
points & \begin{htabular}{\pseudoenum c@{}}making\absolutely\end{htabular} \
no & sense
\end{htabular}
\end{document}

\\at the end of a row for an unlined row, surely typing\nlat the end of a row for a lined row is no more of a burden? – David Carlisle May 01 '13 at 09:45\\\). – soandos May 01 '13 at 10:00