%%
%% Package: spectralsequences v1.3.3 2023-01-28
%% Author: Hood Chatham
%% Email: hood@mit.edu
%% Date: 2023-01-28
%% License: Latex Project Public License
%%
%% File: sseqmacromakers.code.tex
%% Exposes: \DeclareSseqCommand, \NewSseqCommand, \DeclareSseqGroup, \NewSseqGroup
%%
%%   Set up commands to define other commands, both the internal macros, and the external ones.
%%   Also the internal macros \sseq@DeclareDocumentCommandAs and \sseq@DeclareDocumentCommand
%%   For the user commands, sets up call stack, thiscall, etc
%%

\ExplSyntaxOn

%%% Install user commands
% copy commands into user namespace by removing sseq@ prefixes
% given a list of commands, \let\thiscommand\sseq@thiscommand on each one
\def\sseq@installmacros{\@xp\sseq@installmacros@\sseq@macrolist\sseq@nil}
\def\sseq@installmacros@#1{%
    \ifx#1\sseq@nil\else % if so, that was the last command in the list
        \@xp\let\@xp#1\csname sseq@\sseq@macroname#1\endcsname
        \@xp\sseq@installmacros@
    \fi %
}

% Capital U argument type is "Until" but puts back the token when it's done.

% So xparse changed a bunch between the copy pushed to CTAN on February 7th and the copy pushed on February 9th
\newtoks\sseq@patchxparseUnewcode
\sseq@patchxparseUnewcode{
    \cs_new_protected:Npn \sseq__xparse_grab_U:w #1#2 \__xparse_run_code:
      { \sseq__xparse_grab_U_aux:nnN {#1} {#2} \cs_set_protected_nopar:Npn }
    \cs_new_protected:Npn \sseq__xparse_grab_U_aux:nnN #1#2#3
      {
        \tl_set:Nn \l__xparse_signature_tl {#2}
        \exp_after:wN #3 \l__xparse_fn_tl ##1 #1
          { \__xparse_add_arg:n {##1} #1 }
        \l__xparse_fn_tl
      }
    \ifx\__xparse_add_grabber_mandatory:N\undefined
        \let \__xparse_add_grabber_mandatory:N \__xparse_add_grabber:N
    \fi
    \cs_new_protected:Npn \sseq__xparse_add_type_U:w #1
      {
        \__xparse_flush_m_args:
        \__xparse_add_default:
        \__xparse_add_grabber_mandatory:N U
        \tl_put_right:Nn \l__xparse_signature_tl { {#1} }
        \__xparse_prepare_signature:N
      }
%
%
    \cs_new_protected:Npn \sseq__cmd_grab_U:w #1#2 \__cmd_run_code:
      { \sseq__cmd_grab_U_aux:nnN {#1} {#2} \cs_set_protected_nopar:Npn }
    \cs_new_protected:Npn \sseq__cmd_grab_U_aux:nnN #1#2#3
      {
        \tl_set:Nn \l__cmd_signature_tl {#2}
        \exp_after:wN #3 \l__cmd_fn_tl ##1 #1
          { \__cmd_add_arg:n {##1} #1 }
        \l__cmd_fn_tl
      }
    \cs_new_protected:Npn \sseq__cmd_add_type_U:w #1
      {
        \__cmd_flush_m_args:
        \__cmd_add_default:
        \__cmd_add_grabber:N U
        \tl_put_right:Nn \l__cmd_signature_tl { {#1} }
        \__cmd_prepare_signature:N
      }
}

\@ifpackagelater{xparse}{2017/02/08}{
    \@ifpackagelater{xparse}{2018/10/17}{
        \ifsseq@patchxparseU
            \the\sseq@patchxparseUnewcode
        \else
            % Assumptions failed, so just make U give an error (this isn't such a big deal since we don't use it for \d anymore).
            \cs_new_protected:Npn \sseq__cmd_add_type_U:w #1
              {
                \sseq@error{U-xparse-incompatible}
              }
            \cs_new_eq:NN \sseq__xparse_add_type_U:w \sseq__cmd_add_type_U
        \fi
    }{
        \the\sseq@patchxparseUnewcode
    }
}{
    % OLD CODE
    \cs_new_protected:Npn \sseq__xparse_grab_U:w #1#2 \l__xparse_args_tl
      { \sseq__xparse_grab_U_aux:nnN {#1} {#2} \cs_set_protected_nopar:Npn }
    \cs_new_protected:Npn \sseq__xparse_grab_U_aux:nnN #1#2#3
      {
        \exp_after:wN #3 \l__xparse_fn_tl ##1 #1
          {
            \__xparse_add_arg:n {##1}
            #2\l__xparse_args_tl #1
          }
        \l__xparse_fn_tl
      }
    \cs_new_protected:Npn \sseq__xparse_add_type_U:w #1
      {
        \__xparse_flush_m_args:
        \__xparse_add_grabber_mandatory:N U
        \tl_put_right:Nn \l__xparse_signature_tl { {#1} }
        \__xparse_prepare_signature:N
      }
}% END \@ifpackagelater

% On 2018/10/01, the commit "Reimplement the ignore_spaces peek functions in terms of peek_spaces" did what it says.
% Inside of \sseq@DeclareDocumentCommandAs we \let \peek_meaning to \peek_meaning_ignore_spaces
% which is bad news if \peek_meaning_ignore_spaces is defined in terms of \peek_meaning. This fixes this bug by
% redefining \peek_meaning_ignore_spaces in terms of a copy of \peek_meaning.
\@ifpackagelater{expl3}{2018/10/01}{
    \cs_set_eq:NN\sseq__copy_of_peek_meaning:NTF\peek_meaning:NTF
    \cs_set:Npn\sseq__peek_meaning_ignore_spaces:NTF#1#2#3{\peek_remove_spaces:n{\sseq__copy_of_peek_meaning:NTF#1{#2}{#3}}}
    \cs_set_eq:NN\sseq__copy_of_peek_meaning_remove:NTF\peek_meaning_remove:NTF
    \cs_set:Npn\sseq__peek_meaning_remove_ignore_spaces:NTF#1#2#3{\peek_remove_spaces:n{\sseq__copy_of_peek_meaning_remove:NTF#1{#2}{#3}}}
}{
    \cs_set_eq:NN\sseq__peek_meaning_ignore_spaces:NTF\peek_meaning_ignore_spaces:NTF
    \cs_set_eq:NN\sseq__peek_meaning_remove_ignore_spaces:NTF\peek_meaning_remove_ignore_spaces:NTF
}

\@ifpackagelater{xparse}{2018/10/17}{
    \cs_new_protected:Npn \sseq__xparse_normalize_type_U:w #1 {
        \quark_if_recursion_tail_stop_do:nn {#1} { \__xparse_bad_arg_spec:wn }
        \__xparse_normalize_check_lu:N U
        \__xparse_add_arg_spec_mandatory:n { U {#1} }
        \__xparse_normalize_arg_spec_loop:n
    }

    \cs_new_protected:Npn \sseq__cmd_normalize_type_U:w #1 {
        \quark_if_recursion_tail_stop_do:nn {#1} { \__cmd_bad_arg_spec:wn }
        \__cmd_normalize_check_lu:N U
        \__cmd_add_arg_spec_mandatory:n { U {#1} }
        \__cmd_normalize_arg_spec_loop:n
    }
}{
    \cs_new_protected:Npn \sseq__xparse_normalize_type_U:w #1 {
        \quark_if_recursion_tail_stop_do:nn {#1} { \__xparse_bad_arg_spec:wn }
        \__xparse_normalize_check_lu:N U
        \__xparse_add_arg_spec:n { U {#1} }
        \int_incr:N \l__xparse_mandatory_args_int
        \tl_clear:N \l__xparse_last_delimiters_tl
        \__xparse_normalize_arg_spec_loop:n
    }
}
% Expandable commands are a menace to us because they define a bunch of helper commands that we then have to keep track of.
% We are too lazy to do this, so force \l__cmd_grab_expandably_bool to be false.
% This is backwards compatible (I think) because \bool_set_false:N just performs a chardef (no existence check)
% so in old versions, \l__cmd_grab_expandably_bool will be created harmlessly.
\let\sseq__cmd_prepare_signature:n\__cmd_prepare_signature:n
\ifx\sseq__cmd_prepare_signature:n\undefined
    \let\sseq__cmd_prepare_signature:n\__xparse_prepare_signature:n
\fi
\pretocmd\sseq__cmd_prepare_signature:n {
    \bool_set_false:N \l__cmd_grab_expandably_bool
    \bool_set_false:N \l__xparse_grab_expandably_bool
}{}{\error}

% I would like to patch the \__xparse_grab_U:w's in my commands into \sseq__xparse_grab_U:w's but I can't because of -NoValue-.
\def\sseq@install@xparse@Uarggrabber{%
    \cs_set_eq:NN \__xparse_grab_U:w \sseq__xparse_grab_U:w
    \cs_set_eq:NN \__cmd_grab_U:w \sseq__cmd_grab_U:w
}

%%% \sseq@DeclareDocumentCommand
%
% \sseq@DeclareDocumentCommand\somecommand is shorthand for \sseq@DeclareDocumentCommandAs\sseq@somecommand\somecommand
%
% so the result is that it defines \sseq@somecommand intended to be \let to \somecommand later.

\cs_new_protected:Npn\sseq@DeclareDocumentCommand#1{%
    \exp_args:Nc \sseq@DeclareDocumentCommandAs@setinputline { sseq @ \cs_to_str:N #1 }#1
}

\cs_new_protected:Npn\sseq@DeclareDocumentCommandAs@setinputline#1{
   \protected\edef #1 { \@nx\sseq@setinputline \@xp\@nx\csname \cs_to_str:N #1 @@unique@@ start \endcsname }
    \exp_args:Nc \sseq@DeclareDocumentCommandAs { \cs_to_str:N #1 @@unique@@ start }
}

%%% \sseq@DeclareDocumentCommandAs
%
% #1 - the command to define
% #2 - the command to use in argument parsing errors
% #3 - parameters (\NewDocumentCommand style)
% #4 - code
%
% See my stack exchange question: https://tex.stackexchange.com/questions/350596/control-error-messages-made-by-commands-defined-with-newdocumentcommand

\cs_new_protected:Npn\sseq@DeclareDocumentCommandAs#1#2#3#4{
    \group_begin:
    \cs_set_eq:NN \__cmd_add_type_U:w \sseq__cmd_add_type_U:w
    \cs_set_eq:NN \__xparse_add_type_U:w \sseq__xparse_add_type_U:w
    \cs_set_eq:NN \__cmd_normalize_type_U:w \sseq__cmd_normalize_type_U:w
    \cs_set_eq:NN \__xparse_normalize_type_U:w \sseq__xparse_normalize_type_U:w
    \cs_set_eq:NN \__cmd_prepare_signature:n \sseq__cmd_prepare_signature:n
    \cs_set_eq:NN \__xparse_prepare_signature:n \sseq__cmd_prepare_signature:n
    %\cs_set:Npn   \__cmd_declare_cmd_code:Nnn {\bool_set_false:N \l__cmd_grab_expandably_bool\__cmd_declare_cmd_code_aux:Nnn}
    \sseq@installmsghooks
    \def\sseq@error@setup{}
    \def\sseq@error@cleanup{\sseq@errortrue}
    \let\sseq@error@annotation\empty
    \DeclareDocumentCommand #2 { #3 } { #4 }
    \ifsseq@error
        \group_end:
        \sseq@errortrue % This only happens if it came from user code, so don't set error to be false so it can break out too.
        \@xp\sseq@break
    \fi
    \cs_if_exist:cTF{ \cs_to_str:N #2 \c_space_tl code }{
        \cs_gset:Npx #1{
            \begingroup
            \exp_not:N \cs_set_eq:NN  % force xparse to always ignore spaces!
                \exp_not:N \peek_meaning:NTF
                \exp_not:N \sseq__peek_meaning_ignore_spaces:NTF
            \exp_not:N \cs_set_eq:NN  % force xparse to always ignore spaces!
                \exp_not:N \peek_meaning_remove:NTF
                \exp_not:N \sseq__peek_meaning_remove_ignore_spaces:NTF
            % Not sure why we use set_eq:cc and exp_not:n here but I'm concerned that something will break if I change it...
            \exp_not:N \cs_set_eq:cc
                \exp_not:n {{ \cs_to_str:N #2 \c_space_tl code }}
                \exp_not:n {{ \cs_to_str:N #1 \c_space_tl code }}
            \exp_not:N \cs_set_eq:NN
                \exp_not:c { \cs_to_str:N #2 \c_space_tl }
                \exp_not:c { \cs_to_str:N #1 \c_space_tl }
            \exp_not:c { \cs_to_str:N #1 \c_space_tl inner }
        }
        \pretocmd:cnnn { \cs_to_str:N #2 \c_space_tl code } { \endgroup{} } { } { \sseq@error@x { macro-patch-failed } { \string#1 } }
        \cs_gset_eq:cN { \cs_to_str:N #1 \c_space_tl inner } #2
        \cs_gset_eq:cc { \cs_to_str:N #1 \c_space_tl code } { \cs_to_str:N #2 \c_space_tl code }
        \cs_gset_eq:cc { \cs_to_str:N #1 \c_space_tl }{ \cs_to_str:N #2 \c_space_tl } % sometimes the new version of xparse stores stuff in #2<space>
    }{
        \cs_gset_eq:NN #1 #2
    }
    \group_end: % In this case, I want to pass \sseq@errortrue out of the block, so I close the group before I break.
    \sseq@breakpoint % So this breakpoint definitely should be after the \endgroup.
}

\cs_set_eq:NN \patchcmd:Nnnnn \patchcmd
\cs_set_eq:NN \pretocmd:Nnnn  \pretocmd
\cs_generate_variant:Nn \patchcmd:Nnnnn {cnfnn}
\cs_generate_variant:Nn \pretocmd:Nnnn  {cfnn}
\cs_generate_variant:Nn \pretocmd:Nnnn  {cnnn}

\cs_new_protected:Npn\NewSseqCommand#1{%
    \cs_if_free:cTF { sseq @ usermacro @ \cs_to_str:N #1 } { \DeclareSseqCommand#1 } { \sseq@error@x{usermacro-not-free}{\string#1} \use_none:nn }
}
\let\begingroupa\begingroup
\let\begingroupb\begingroup
\newtoks\sseq@macro@setthiscall@toks
\newtoks\sseq@macro@defaultarggetters@toks

\cs_new_protected:Npn\DeclareSseqCommand#1#2#3{%
    \cs_if_exist:cTF { sseq @ \cs_to_str:N #1 @@unique@@ start} {\sseq@error@x{wont-override-builtin}{\string#1}\sseq@break} {}
    \cs_if_free:cT { sseq @ usermacro @ \cs_to_str:N #1 } {
        \sseq@x@addto@macro { \sseq@installmacros } { \let \exp_not:N #1 \exp_not:c { sseq @ usermacro @ \cs_to_str:N #1 } }
    }
    % Make sure \cs_if_exist doesn't pass because there was a previously defined user macro with same name
    \cs_undefine:c { sseq @ usermacro @ \cs_to_str:N #1 \c_space_tl code }
    \exp_args:Nc \sseq@DeclareDocumentCommandAs@setinputline { sseq @ usermacro @ \cs_to_str:N #1 } #1 { #2 } {%
            \sseq@loadinputline
            \sseq@atbeginusermacro@msgsetup
                {}#3{} % prevent space patching space hazards with these {}'s
            \endgroup
        }%
    \ifsseq@error
        \global\sseq@errorfalse
        \@xp\sseq@break
    \fi
    \sseq@parseargspec{#1}{#2}%
    % You might think we could skip this patching, and it's probably possible.
    % However, this is responsible for turning the #'s of catcode other into #'s of catcode arg. I don't have as good of a way to do that.
    % Also, this allows me to use \sseq@parseargspec after defining the command, so I don't have to run any error checking inside of it
    % because if the argspec is invalid, \DeclareDocumentCommand will let me know.
    \cs_if_exist:cTF{ sseq @ usermacro @ \cs_to_str:N #1 @@unique@@ start \c_space_tl code }{
        \patchcmd:cnfnn{ sseq @ usermacro @ \cs_to_str:N #1 @@unique@@ start \c_space_tl code }{\endgroup{}}{
            \@xp\endgroup
            \@xp\begingroupa
            \the\sseq@macro@setthiscall@toks
        }{}{\sseq@error@x{usermacro-failed-patch}{\string#1}}
        \cs_gset_eq:cc { sseq @ usermacro @ \cs_to_str:N #1 @@unique@@ start \c_space_tl code } { sseq @ usermacro @ \cs_to_str:N #1 @@unique@@ start \c_space_tl code}
    }{
        \pretocmd:cfnn{ sseq @ usermacro @ \cs_to_str:N #1 @@unique@@ start }{
            \@xp\begingroupb
            \the\sseq@macro@setthiscall@toks
        }{}{\sseq@error@x{usermacro-failed-patch}{\string#1}}
    }
    \cs_gset_eq:cc { sseq @ usermacro @ \cs_to_str:N #1 } { sseq @ usermacro @ \cs_to_str:N #1 }
    \cs_gset_eq:cc { sseq @ usermacro @ \cs_to_str:N #1 @@unique@@ start } { sseq @ usermacro @ \cs_to_str:N #1 @@unique@@ start}
    \ifsseq@inprogress
        \cs_set_eq:Nc #1 { sseq @ usermacro @ \cs_to_str:N #1 }
    \fi
    \sseq@breakpoint
}

%%% Commands to help the user define "groups" of commands to be reused
% #1 -- command
% #2 -- argspec
% #3 -- body of function (long)
\NewDocumentCommand\NewSseqGroup{mm+m}{\DeclareSseqGroup@\NewSseqCommand{#1}{#2}{#3}}
\NewDocumentCommand\DeclareSseqGroup{mm+m}{\DeclareSseqGroup@\DeclareSseqCommand{#1}{#2}{#3}}

% #1 -- \NewSseqCommand or \DeclareSseqCommand
% #2 -- command
% #3 -- argspec
% #4 -- body of function (long)
\long\def\DeclareSseqGroup@#1#2#3#4{%
    \group_begin:
    \let\sseq@parseargspec\@gobbletwo % Get rid of argparse so we can change what gets added for setting up thiscall
% why are we doing these things here?
    \sseq@installmsghooks
    \def\sseq@error@setup{}
    \def\sseq@error@cleanup{\sseq@errortrue}
    \let\sseq@error@annotation\empty
%
% undefine so we can tell whether we need to patch \command<space>code or \command
    \cs_undefine:c { sseq @ usermacro @ \cs_to_str:N #2 @ helper  \c_space_tl code }
    \exp_args:Nc \sseq@DeclareDocumentCommandAs { sseq @ usermacro @ \cs_to_str:N #2 @ helper } #2 { #3 } {
        \sseq@loadinputline
        \sseq@usermacro@esetthiscall{\the\sseq@groupthiscalltoks}
        \sseq@atbeginusermacro@msgsetup % the stack push happens in here
        \sseq@scopecall
            {}#4{}
        \end{scope}
        \endgroup
        \sseq@breakpoint
    }%
    \ifsseq@error
        \@xp\sseq@break
    \fi
    \sseq@macro@setthiscall@toks\@xp{\sseq@SseqGroup@argspec} % This gets added to the command by DeclareSseqCommand
    #1#2{od()}{%
        %can't use \sseq@atbeginusermacro@msgsetup until next spot b/c don't know what the whole call looks like yet
        \IfNoValueTF{##1}{\def\sseq@options{}}{\def\sseq@options{##1}}%
        \IfNoValueTF{##2}{%
            \edef\sseq@scopecall{\@nx\begin{scope}[\unexpanded\@xp{\sseq@options}]}
        }{%
            \sseqnewgroup@splitcoord##2\sseq@nil
        }%
        \csname sseq @ usermacro @ \sseq@macroname #2 @ helper \@xp\endcsname\@gobbletwo % This gobble eats the endgroup added by sseqDeclareDocumentCommand
    }
    \sseq@parseargspec@newgroup{#2}{#3}%
    % You might think we could skip this patching, and it's probably possible.
    % However, this is responsible for turning the #'s of catcode other into #'s of catcode arg. I don't have as good of a way to do that.
    \cs_if_exist:cTF{ sseq @ usermacro @ \cs_to_str:N #2 @ helper \c_space_tl code }{
        \pretocmd:cfnn { sseq @ usermacro @ \cs_to_str:N #2 @ helper \c_space_tl code }{
            \the\sseq@macro@setthiscall@toks
        }{}{\sseq@error@x{usermacro-failed-patch}{\string#1}}
        % globalize definition:
        \cs_gset_eq:cc { sseq @ usermacro @ \cs_to_str:N #2 @ helper \c_space_tl code } { sseq @ usermacro @ \cs_to_str:N #2 @ helper \c_space_tl code }
    }{
        \pretocmd:cfnn { sseq @ usermacro @ \cs_to_str:N #2 @ helper }{
            \the\sseq@macro@setthiscall@toks
        }{}{\sseq@error@x{usermacro-failed-patch}{\string#1}}
    }
    \cs_gset_eq:cc { sseq @ usermacro @ \cs_to_str:N #2 @ helper } { sseq @ usermacro @ \cs_to_str:N #2 @ helper } % globalize definition
    \let\sseq@parseargspec\sseq@parseargspec@normal
    \sseq@breakpoint
    \group_end:
}

\def\sseqnewgroup@splitcoord#1,#2\sseq@nil{
    \sseq@ifintexpr{#1}{}{
        \def\sseq@scopecall{\sseq@error@n{invalid-coordinate}{x~}\sseq@break}
        \sseq@break
    }
    \sseq@ifintexpr{#2}{}{
        \def\sseq@scopecall{\sseq@error@n{invalid-coordinate}{y~}\sseq@break}
        \sseq@break
    }
    \edef\sseq@scopecall{
        \@nx\begin{scope}[xshift=\the\numexpr#1\relax,yshift=\the\numexpr#2\relax,\unexpanded\@xp{\sseq@options}]
    }
    \sseq@breakpoint
}


\newtoks\sseq@groupargspectoks
\newtoks\sseq@groupthiscalltoks
\bgroup\catcode`\#=12\relax
    \gdef\sseq@SseqGroup@argspec{
        \sseq@eval{\global\sseq@groupargspectoks{\IfNoValueF{#1}{\unexpanded{[#1]}}\IfNoValueF{#2}{\unexpanded{(#2)}}}}
        \@gobbletwo % What does this \@gobbletwo do?
    }
    \gdef\sseq@thearg{#\the\sseq@tempcount}
\egroup


% When there are arguments with default values (O, D, R, G), we need to put them
% into temporary macros to compare them and see if they are the default value
% that's what \sseq@macro@defaultarggetters@toks is for.
\def\sseq@parseargspec#1#2{%
    \sseq@tempcount=\z@
    \sseq@temptoks{\@nx#1}% Holds the stuff that goes in \esetthiscall (so most stuff)
    \sseq@macro@defaultarggetters@toks{}
    \sseq@parseargspec@#2\sseq@nil
    \sseq@eval{\sseq@macro@setthiscall@toks{\the\sseq@macro@defaultarggetters@toks\@nx\sseq@usermacro@esetthiscall{\the\sseq@temptoks}}}
}

% For NewGroup:
\let\sseq@parseargspec@normal\sseq@parseargspec
\def\sseq@parseargspec@newgroup#1#2{%
    \sseq@tempcount=\z@
    \sseq@temptoks{}
    \sseq@macro@defaultarggetters@toks{}%
    \sseq@parseargspec@#2\sseq@nil
    \sseq@eval{
        \sseq@macro@setthiscall@toks{
            \the\sseq@macro@defaultarggetters@toks
            \@nx\sseq@esetthiscall{
                \@nx\@nx\@nx#1
                \@nx\the\sseq@groupargspectoks
                \the\sseq@temptoks
            }
            \global\sseq@groupthiscalltoks\@nx\@xp{\@nx\the\sseq@thiscalltoks}
        }
    }
}


\def\sseq@parseargspec@#1{
    \advance\sseq@tempcount\@ne
    \ifx#1\sseq@nil\else
        \@xp\ifx\csname sseq@parseargspec@#1\@xp\endcsname\relax
            \sseq@error@n{usermacro-unsupported-argument-type}{#1}
        \else
            \csname sseq@parseargspec@#1\@xptwo\endcsname
        \fi
    \fi
}


% Have to use \@alph to convert the number to a letter. Using a number here doesn't work because the control sequence gets retokenized
% so we can only use letters in control sequences (another option would be to not expand the \csnames till later, but that would be harder).
\def\sseq@parseargspec@ifargisnotdefault{%
        \@nx\ifx
            \@xp\@nx\csname sseq@parseargspec@temparg\@alph\sseq@tempcount\endcsname
            \@xp\@nx\csname sseq@parseargspec@tempdefault\@alph\sseq@tempcount\endcsname
    \unexpanded{%
            \@xp\@gobble
        \else
            \@xp\@firstofone
        \fi
    }%
}

\def\sseq@parseargspec@setargdefault#1{%
    \sseq@e@addto@toks\sseq@macro@defaultarggetters@toks{%
        \def\@xp\@nx\csname sseq@parseargspec@temparg\@alph\sseq@tempcount\endcsname{\sseq@thearg}%
        \def\@xp\@nx\csname sseq@parseargspec@tempdefault\@alph\sseq@tempcount\endcsname{\unexpanded{#1}}%
    }
}

% v, E, and e are illegal

% Mandatory
\def\sseq@parseargspec@m{%
    \sseq@e@addto@temptoks{{\@nx\unexpanded{\sseq@thearg}}}
    \sseq@parseargspec@
}
\def\sseq@parseargspec@l{%
    \sseq@e@addto@temptoks{\@nx\unexpanded{\sseq@thearg}}
    \sseq@parseargspec@
}

\def\sseq@parseargspec@u#1{%
    \sseq@e@addto@temptoks{\@nx\unexpanded{\sseq@thearg}\unexpanded{\unexpanded{#1}}}
    \sseq@parseargspec@
}

\def\sseq@parseargspec@U#1{%
    \sseq@e@addto@temptoks{\@nx\unexpanded{\sseq@thearg}}
    \sseq@parseargspec@
}

% Optional no default
\def\sseq@parseargspec@o{%
    \sseq@e@addto@temptoks{\@nx\IfNoValueF{\sseq@thearg}{[\@nx\unexpanded{\sseq@thearg}]}}
    \sseq@parseargspec@
}

\def\sseq@parseargspec@d#1#2{%
    \sseq@e@addto@temptoks{\@nx\IfNoValueF{\sseq@thearg}{\@nx\unexpanded{#1\sseq@thearg#2}}}
    \sseq@parseargspec@
}

\def\sseq@parseargspec@r#1#2{ % technically required, but behaves like optional
    \sseq@e@addto@temptoks{#1\@nx\unexpanded{\sseq@thearg}#2}
    \sseq@parseargspec@
}


\def\sseq@parseargspec@g{%
    \sseq@e@addto@temptoks{\@nx\IfNoValueF{\sseq@thearg}{\@nx\unexpanded{{\sseq@thearg}}}}
    \sseq@parseargspec@
}

\def\sseq@parseargspec@s{%
    \sseq@e@addto@temptoks{\@nx\IfBooleanT{\sseq@thearg}{*}}
    \sseq@parseargspec@
}

\def\sseq@parseargspec@t#1{%
    \sseq@e@addto@temptoks{\@nx\IfBooleanT{\sseq@thearg}{#1}}
    \sseq@parseargspec@
}

% Optional with default

\def\sseq@parseargspec@O#1{
    \sseq@parseargspec@setargdefault{#1}%
    \sseq@e@addto@temptoks{\sseq@parseargspec@ifargisnotdefault{\@nx\unexpanded{[\sseq@thearg]}}}%
    \sseq@parseargspec@
}
\def\sseq@parseargspec@D#1#2#3{%
    \sseq@parseargspec@setargdefault{#3}%
    \sseq@e@addto@temptoks{\sseq@parseargspec@ifargisnotdefault{\@nx\unexpanded{#1\sseq@thearg#2}}}%
    \sseq@parseargspec@
}
\def\sseq@parseargspec@R#1#2#3{%
    \sseq@parseargspec@setargdefault{#3}%
    \sseq@e@addto@temptoks{\sseq@parseargspec@ifargisnotdefault{\@nx\unexpanded{#1\sseq@thearg#2}}}%
    \sseq@parseargspec@
}
\def\sseq@parseargspec@G#1{%
    \sseq@parseargspec@setargdefault{#1}%
    \sseq@e@addto@temptoks{\sseq@parseargspec@ifargisnotdefault{\@nx\unexpanded{{\sseq@thearg}}}}%
    \sseq@parseargspec@
}

\ExplSyntaxOff

% 