%%
%% This is file `langsci-avm.sty',
%% generated with the docstrip utility.
%%
%% The original source files were:
%%
%% langsci-avm.dtx  (with options: `package')
%% ------------------------------------------------------------------------------
%% 
%% A package for typesetting feature structures, also known as attribute-value
%% matrices (AVMs), for use in linguistics.
%% 
%% The package provides a minimal and easy to read syntax. It depends only on the
%% array package and can be placed almost everywhere, in particular in footnotes or
%% graphs and tree structures. The package serves the same purpose as, Christopher
%% Manning’s avm package, but shares no code base with that package.
%% 
%% Copyright (C) 2023-2025 Language Science Press
%% 
%% This work consists of the file langsci-avm.dtx
%%           and the derived file langsci-avm.pdf.
%% 
%% It may be distributed and/or modified under the conditions of the
%% LaTeX Project Public License (LPPL), either version 1.3c of this
%% license or (at your option) any later version. The latest version
%% of this license is at <http://www.latex-project.org/lppl.txt>.
%% 
%% This work is ``maintained'' (per LPPL maintenance status) by
%% Language Science Press <support@langsci-press.org>.
%% 
%% The development version can be found at
%% 
%%    https://github.com/langsci/langsci-avm
%% 
%% for those people who are interested. Pull requests are welcome.
%% 
%% Please report any bugs or feature requests to
%% 
%%    https://github.com/langsci/langsci-avm/issues
%% 
%% ------------------------------------------------------------------------------
\RequirePackage{xparse}[2022/03/26]
\RequirePackage{array}
\ProvidesExplPackage {langsci-avm}
  {2023-02-20} {0.3.0}
  {AVMs and feature structures}

\msg_new:nnnn {avm} {lfgoptionmissing}
  {  Missing~package~option~lfg~at~line~\msg_line_number:  }
  {
    You~issued~a~command~in~line~\msg_line_number:~that~is~only~available~when~
    the~lfg~package~option~is~enabled.
  }

\msg_new:nnnn {avm} {idpositionunknown}
  {  Unkown~value~for~option~`id~position`~near~line~\msg_line_number:.  }
  {  You~specified~an~unknown~value~for~option~`id~position`.~The~content~of~
     the~id~could~not~be~output.~Please~see~the~manual~for~a~list~of~valid~
     settings.
  }
\bool_new:N \l__avm_lfg_bool
\bool_new:N \l__avm_tikz_bool
\DeclareOption{tikz}{ \bool_set_true:N \l__avm_tikz_bool }
\DeclareOption{lfg}{ \bool_set_true:N \l__avm_lfg_bool }
\ProcessOptions\relax
\bool_if:NT \l__avm_tikz_bool
  {
    \RequirePackage{tikz}
    \newcounter{l__avm_picture_counter}
    \tl_new:N \l__avm_picture_name_prefix_tl
  }
\bool_if:NT \l__avm_lfg_bool
  {
    \cs_if_exist:NF \lBrack
      {
        \RequirePackage{etoolbox}
        \AtEndPreamble { \RequirePackage{unicode-math} }
      }
  }
\NewDocumentCommand{\avm}{ O{} +m }
  {
    \c_group_begin_token
    \keys_set:nn { avm } { #1 }
    \__avm_initialise_document_commands:
    \__avm_initialise_custom_commands:
    \tl_use:N \l__avm_defined_commands_tl
    \__avm_mode_switch:
    \__avm_parse:n { #2 }
    \c_group_end_token
  }
\bool_new:N \l__avm_mode_bool
\seq_new:N \l__avm_parens_tracker
\tl_new:N \l__avm_defined_commands_tl
\box_new:N \l__avm_fillmore_kay_box
\tl_new:N \l__avm_parsed_tl
\int_new:N \l__avm_mode_switch_character_int
\NewDocumentCommand{\avmsetup}{ m }
  { \keys_set:nn { avm } { #1 } }

\keys_define:nn { avm }
  {
    align .bool_set:N      = \l__avm_align_bool,
    align .initial:n       = {true},
    stretch .tl_set:N      = \l__avm_arraystretch_tl,
    stretch .initial:n     = {0.9},
    columnsep .dim_set:N   = \l__avm_tabcolsep_dim,
    columnsep .initial:n   = {.5ex},
    vectorsep .dim_set:N   = \l__avm_singlesep_dim,
    vectorsep .initial:n   = {1em},
    delimfactor .int_set:N = \l__avm_delimfactor_int,
    delimfactor .initial:n = {1000},
    delimfall .dim_set:N   = \l__avm_delimshortfall_dim,
    delimfall .initial:n   = {0pt},
    framewidth .dim_set:N  = \l__avm_fillmore_kay_boxrule_dim,
    framewidth .initial:n  = {1pt},
    framesep .dim_set:N    = \l__avm_fillmore_kay_boxsep_dim,
    framesep .initial:n    = {3pt},
    attributes .code:n     = {\cs_set:Nn \__avm_font_attribute: {#1}},
    attributes .initial:n  = {\scshape},
    types .code:n          = {\cs_set:Nn \__avm_font_type: {#1}},
    types .initial:n       = {\itshape},
    values .code:n         = {\cs_set:Nn \__avm_font_value: {#1}},
    values .initial:n      = {\itshape},
    tags .code:n           = {\cs_set:Nn \__avm_font_tag: {#1}},
    tags .initial:n        = {\footnotesize},
    singleton .code:n      = {\cs_set:Nn \__avm_font_singleton: {#1}},
    singleton .initial:n   = {\normalfont},
    switch .code:n         =
      {
        \tl_set:Nn \l__avm_mode_switch_character {#1}
        \exp_args:NNx \int_set:Nn \l__avm_mode_switch_character_int
          {`\tl_use:N \l__avm_mode_switch_character}
      },
    switch .initial:n      = { ! },
    extraskip .dim_set:N   = \l__avm_extra_skip_dim,
    extraskip .initial:n   = {\smallskipamount},
    extraskip~in~every~row .bool_set:N = \l__avm_extraskip_bool,
    customise .code:n      = {\cs_set:Nn \__avm_initialise_custom_commands:
                                         {#1}},
    customise .initial:n   = { },
    pic .bool_set:N        = \l__avm_picture_bool,
    pic .default:n         = { true },
    picname .tl_set:N      = \l__avm_picture_name_tl,
    picname .initial:n    = {automatic},
    id~align .code:n       = { \newcolumntype{i}{#1} },
    id~align .initial:n    = {l},
    id~position .tl_set:N  = \l__avm_id_position_tl,
    id~position .initial:n = {south-west},
    style .choice:,
    style / narrow .code:n = {\int_set:Nn \l__avm_delimfactor_int {997}
                           \dim_set:Nn \l__avm_delimshortfall_dim {5pt}},
  }

\NewDocumentCommand{\avmdefinestyle}{ m m }
  {
    \keys_define:nn { avm }
      {
        style / #1 .code:n = { \keys_set:nn { avm } { #2 } }
      }
  }

\NewDocumentCommand{\avmdefinecommand}{ m O{} m }
  {
    \tl_put_right:Nn \l__avm_defined_commands_tl
      {
        \exp_args:Nc \DeclareDocumentCommand { #1 } { s }
          {
             #2 \IfBooleanF { ##1 } { & } \avmsetup{ #3 }
          }
      }
  }

\cs_generate_variant:Nn \tl_if_eq:nnTF {VnTF}

\bool_new:N \l__avm_in_first_column

\cs_new:Nn \__avm_init_first_column:
  {
    \bool_set_true:N \l__avm_in_first_column
    \normalfont\__avm_font_attribute:
  }

\cs_new:Nn \__avm_init_second_column:
  {
    \bool_set_false:N \l__avm_in_first_column
    \normalfont\__avm_font_value:
  }

\cs_new:Nn \__avm_init_single_column:
  {
    \normalfont\__avm_font_attribute:
  }



\tl_const:Nn \l__avm_italics_tl {it}

\cs_new:Nn \__avm_deinit_first_column:
  {
    \tl_if_eq:NNT \f@shape \l__avm_italics_tl {\/}
  }

\cs_new:Nn \__avm_deinit_second_column:
  {
    \tl_if_eq:NNT \f@shape \l__avm_italics_tl {\/}
  }

\cs_new:Nn \__avm_deinit_single_column:
  {
    \tl_if_eq:NNT \f@shape \l__avm_italics_tl {\/}
  }

\cs_new:Nn \__avm_kern_unused_columns:
  {
    \bool_if:NTF \l__avm_in_first_column
      { \span\hspace*{-2\tabcolsep} }
      {  }
  }

\cs_new:Nn \__avm_extra_skip:
  {
    \peek_meaning_ignore_spaces:NTF \\ {\vspace*{\l__avm_extra_skip_dim}} {}
  }

\cs_new:Nn \__avm_module_begin:
  {
    \bool_if:NTF \l__avm_align_bool
      {
        \begin{tabular}{@{}
                        >{\__avm_init_first_column:}l
                        <{\__avm_deinit_first_column:}
                        >{\__avm_init_second_column:}l
                        <{\__avm_deinit_second_column:}
                        @{}}
      }
      {
        \begin{tabular}{@{}
                       >{\__avm_init_single_column:}l
                       <{\__avm_deinit_single_column:}
                       @{}}
      }
  }
\cs_new:Nn \__avm_module_end:
  {
    \__avm_kern_unused_columns:
    \end{tabular}
  }

\cs_new:Nn \__avm_replace_ampersand:
  {
    \bool_if:NTF \l__avm_align_bool
      { \tl_build_put_right:Nn \l__avm_parsed_tl { & } }
      { \tl_build_put_right:Nn \l__avm_parsed_tl
        {
          \exp_not:n
            {
              \__avm_deinit_first_column:\skip_horizontal:N
              \dim_use:N \l__avm_singlesep_dim \__avm_init_second_column:
             }
         }
      }
  }
\cs_new:Nn \__avm_replace_lbrace:
  {
    \c_math_toggle_token\left\lbrace\__avm_module_begin:
  }
\cs_new:Nn \__avm_replace_rbrace:
  {
    \__avm_module_end:\right\rbrace\c_math_toggle_token\__avm_extra_skip:
  }
\cs_new:Nn \__avm_replace_lbrack:
  {
    \tl_build_put_right:Nn \l__avm_parsed_tl
      {
        \exp_not:n
          {
            \bool_if:NTF \l__avm_mode_bool
              {
                \c_math_toggle_token\left\lbrack\__avm_module_begin:
              }
              { [ }
          }
      }
  }
\cs_new:Nn \__avm_replace_rbrack:
  {
    \tl_build_put_right:Nn \l__avm_parsed_tl
      {
        \exp_not:n
          {
            \bool_if:NTF \l__avm_mode_bool
              {
                \__avm_module_end:\right\rbrack\c_math_toggle_token%
                \__avm_extra_skip:
              }
              { ] }
          }
      }
  }
\bool_if:NTF \l__avm_lfg_bool
  {
    \cs_new:Nn \__avm_replace_llbrack:
      {
        \c_math_toggle_token\left\lBrack\__avm_module_begin:
      }
    \cs_new:Nn \__avm_replace_rrbrack:
      {
        \__avm_module_end:\right\rBrack\c_math_toggle_token\__avm_extra_skip:
      }
  }
  {
    \cs_new:Nn \__avm_replace_llbrack:
      {
        \tl_build_put_right:Nn \l__avm_parsed_tl
          {
            \exp_not:n
              {
                \msg_warning:nn {avm}{lfgoptionmissing}
                \c_math_toggle_token\left.\__avm_module_begin:
              }
          }
      }
    \cs_new:Nn \__avm_replace_rrbrack:
      {
        \tl_build_put_right:Nn \l__avm_parsed_tl
          {
            \exp_not:n
            {
              \msg_warning:nn {avm}{lfgoptionmissing}
              \__avm_module_end:\right.\c_math_toggle_token\__avm_extra_skip:
            }
          }
      }
  }
\cs_new:Nn \__avm_replace_lparen:
  {
    \tl_build_put_right:Nn \l__avm_parsed_tl
      {
        \exp_not:n
          {
            \bool_if:NTF \l__avm_mode_bool
              {
                \c_math_toggle_token\left(\__avm_module_begin:
              }
              { ( }
          }
      }
  }
\cs_new:Nn \__avm_replace_rparen:
  {
    \tl_build_put_right:Nn \l__avm_parsed_tl
      {
        \exp_not:n
           {
             \bool_if:NTF \l__avm_mode_bool
               {
                 \__avm_module_end:\right)\c_math_toggle_token\__avm_extra_skip:
               }
               { ) }
           }
       }
  }
\cs_new:Nn \__avm_replace_langle:
  {
    \tl_build_put_right:Nn \l__avm_parsed_tl
      {
        \exp_not:n
          {
            \bool_if:NTF \l__avm_mode_bool
              {
                \c_math_toggle_token\left<\__avm_module_begin:
              }
              { < }
          }
      }
  }
\cs_new:Nn \__avm_replace_rangle:
  {
    \tl_build_put_right:Nn \l__avm_parsed_tl
      {
        \exp_not:n
          {
            \bool_if:NTF \l__avm_mode_bool
              {
                \__avm_module_end:\right>\c_math_toggle_token\__avm_extra_skip:
              }
              { > }
          }
      }
  }
\cs_new:Nn \__avm_replace_lframe:
  {
    \hbox_set:Nw \l__avm_fillmore_kay_box \group_begin:
    \c_math_toggle_token\__avm_module_begin:
  }
\cs_new:Nn \__avm_replace_rframe:
  {
    \__avm_module_end:\c_math_toggle_token\group_end:\hbox_set_end:
    \group_begin:
    \dim_set_eq:NN \fboxrule \l__avm_fillmore_kay_boxrule_dim
    \dim_set_eq:NN \fboxsep \l__avm_fillmore_kay_boxsep_dim
    \fbox{\box_use:N \l__avm_fillmore_kay_box}
    \group_end: \__avm_extra_skip:
  }
\cs_new:Nn \__avm_replace_plus:
  {
    \leavevmode\unskip\hbox{${}\oplus{}$}\ignorespaces
  }
\cs_new:Nn \__avm_replace_minus:
  {
    \leavevmode\unskip\hbox{${}\ominus{}$}\ignorespaces
  }
\cs_new:Nn \__avm_replace_circle:
  {
    \leavevmode\unskip\hbox{${}\bigcirc{}$}\ignorespaces
  }

\cs_new:Npn \__avm_controls_tag:n #1
  { \fboxsep.25ex\fboxrule.4pt\fbox{\normalfont\__avm_font_tag: #1} }
\cs_new:Npn \__avm_controls_type:n #1
  { \c_group_begin_token\normalfont\__avm_font_type: #1\c_group_end_token }
\cs_new_protected:Npn \__avm_controls_type_starred:n #1
  {
    \bool_set_false:N \l__avm_in_first_column
    \normalfont\__avm_font_type: #1
    \bool_if:NTF \l__avm_align_bool
      { \__avm_deinit_second_column:\span\hspace*{-2\tabcolsep} }
      { \__avm_deinit_single_column:}
    \peek_meaning_ignore_spaces:NTF \\ {} {\\}
  }
\cs_new_protected:Npn \__avm_controls_punk:nn #1 #2
  {
    \bool_set_false:N \l__avm_in_first_column
    \normalfont\c_group_begin_token\__avm_font_attribute:#1%
    \c_group_end_token\hspace{2\tabcolsep}%
    \c_group_begin_token\__avm_font_value: #2\c_group_end_token%
    \__avm_deinit_second_column:\span\hspace*{-2\tabcolsep}
    \peek_charcode_ignore_spaces:NTF \\ {} {\\}
  }

\cs_new:Nn \__avm_mode_switch:
  {
    \bool_set_inverse:N \l__avm_mode_bool
    \bool_if:NTF \l__avm_mode_bool
      {
        \DeclareDocumentCommand{\{}{}{ \__avm_replace_lbrace: }
        \DeclareDocumentCommand{\}}{}{ \__avm_replace_rbrace: }
        \DeclareDocumentCommand{\[}{}{ \__avm_replace_llbrack: }
        \DeclareDocumentCommand{\]}{}{ \__avm_replace_rrbrack: }
        \DeclareDocumentCommand{\+}{}{ \__avm_replace_plus: }
        \DeclareDocumentCommand{\-}{}{ \__avm_replace_minus: }
      }
      {
        \DeclareCommandCopy{\{}{\__avm_old_lbrace_store:}
        \DeclareCommandCopy{\}}{\__avm_old_rbrace_store:}
        \DeclareCommandCopy{\[}{\__avm_old_llbrack_store:}
        \DeclareCommandCopy{\]}{\__avm_old_rrbrack_store:}
        \DeclareCommandCopy{\+}{\__avm_old_plus_store:}
        \DeclareCommandCopy{\-}{\__avm_old_minus_store:}
      }
  }

\cs_new:Nn \__avm_initialise_document_commands:
  {
    \DeclareCommandCopy{\__avm_old_lbrace_store:}{\{}
    \DeclareCommandCopy{\__avm_old_rbrace_store:}{\}}
    \DeclareCommandCopy{\__avm_old_llbrack_store:}{\[}
    \DeclareCommandCopy{\__avm_old_rrbrack_store:}{\]}
    \DeclareCommandCopy{\__avm_old_plus_store:}{\+}
    \DeclareCommandCopy{\__avm_old_minus_store:}{\-}
    \def\arraystretch{\tl_use:N \l__avm_arraystretch_tl}
    \dim_set_eq:NN \tabcolsep \l__avm_tabcolsep_dim
    \int_set_eq:NN \delimiterfactor \l__avm_delimfactor_int
    \dim_set_eq:NN \delimitershortfall \l__avm_delimshortfall_dim
    \DeclareDocumentCommand{\shuffle}{}{ \__avm_replace_shuffle: }
    \DeclareDocumentCommand{\lframe}{}{ \__avm_replace_lframe: }
    \DeclareDocumentCommand{\rframe}{}{ \__avm_replace_rframe: }
    \DeclareDocumentCommand{\tag}{m}{ \__avm_controls_tag:n {##1} }
    \DeclareDocumentCommand{\0}{}{ \__avm_controls_tag:n {0} }
    \DeclareDocumentCommand{\1}{}{ \__avm_controls_tag:n {1} }
    \DeclareDocumentCommand{\2}{}{ \__avm_controls_tag:n {2} }
    \DeclareDocumentCommand{\3}{}{ \__avm_controls_tag:n {3} }
    \DeclareDocumentCommand{\4}{}{ \__avm_controls_tag:n {4} }
    \DeclareDocumentCommand{\5}{}{ \__avm_controls_tag:n {5} }
    \DeclareDocumentCommand{\6}{}{ \__avm_controls_tag:n {6} }
    \DeclareDocumentCommand{\7}{}{ \__avm_controls_tag:n {7} }
    \DeclareDocumentCommand{\8}{}{ \__avm_controls_tag:n {8} }
    \DeclareDocumentCommand{\9}{}{ \__avm_controls_tag:n {9} }
    \DeclareDocumentCommand{\type}{s m}
      {
        \IfBooleanTF { ##1 }
          { \__avm_controls_type_starred:n {##2} }
          { \__avm_controls_type:n {##2} }
       }
    \DeclareDocumentCommand{\punk}{m m}{ \__avm_controls_punk:nn {##1}{##2} }
    \DeclareDocumentCommand{\id}{m m}
      {%
        \hcoffin_set:Nw \l_tmpa_coffin
          \bgroup
          \def\arraystretch{.5}
          \begin{tabular}[b]{@{}>{$\scriptstyle}i<{$}@{}}
          ##1
          \end{tabular}
          \egroup
          \hcoffin_set_end:
          \hcoffin_set:Nw \l_tmpb_coffin ##2 \hcoffin_set_end:
          \tl_if_eq:VnTF \l__avm_id_position_tl {south-west}
            {%
              \coffin_join:NnnNnnnn \l_tmpb_coffin {l}{H}
              \l_tmpa_coffin {r}{H}{ 0pt }{ -\coffin_dp:N \l_tmpb_coffin }
            }
            {%
              \tl_if_eq:VnTF \l__avm_id_position_tl {south-east}
              {%
                \coffin_join:NnnNnnnn \l_tmpb_coffin {l}{H}
                \l_tmpa_coffin {l}{H}{ \coffin_wd:N \l_tmpb_coffin }
                                     { -\coffin_dp:N \l_tmpb_coffin }
              }
              {
                \msg_error:nn {avm}{idpositionunknown}
              }
            }
        \coffin_typeset:Nnnnn \l_tmpb_coffin {l}{vc}{0pt}{0pt}
      }
    \DeclareDocumentEnvironment{scope}{ +m }
      {
        \group_begin:
        \keys_set:nn { avm } { ##1 }
        \ignorespaces
      }
      {
        \group_end:
      }
    \bool_if:NT \l__avm_tikz_bool
      {
        \tl_if_eq:VnTF \l__avm_picture_name_tl {automatic}
          {
            \stepcounter{l__avm_picture_counter}
            \tl_set:Nn \l__avm_picture_name_prefix_tl
              {avm-\tl_use:N \thel__avm_picture_counter}
          }
          {
            \tl_set_eq:NN \l__avm_picture_name_prefix_tl \l__avm_picture_name_tl
          }
        \DeclareDocumentCommand{\node}{m m}
          {
            \tikz [remember~picture,
                   baseline=(\l__avm_picture_name_prefix_tl-##1.base)]
            \node [inner~sep=0pt] (\l__avm_picture_name_prefix_tl-##1)
                                  {\strut ##2};
          }
      }
  }

\cs_new:Npn \__avm_parse:n #1
  {
    \group_align_safe_begin:
    \tl_build_begin:N \l__avm_parsed_tl
    \tl_build_put_right:Nn \l__avm_parsed_tl { \exp_not:n {\ignorespaces} }
    \tl_analysis_map_inline:nn { #1 }
      {
        \int_case:nnF { ##2 }
          {
            { `&  }{ \__avm_replace_ampersand: }
            { `[  }{ \__avm_replace_lbrack: }
            { `]  }{ \__avm_replace_rbrack: }
            { `(  }{ \__avm_replace_lparen: }
            { `)  }{ \__avm_replace_rparen: }
            { `<  }{ \__avm_replace_langle: }
            { `>  }{ \__avm_replace_rangle: }
            { \l__avm_mode_switch_character_int }
              {
                \tl_build_put_right:Nn \l__avm_parsed_tl
                  { \exp_not:n { \__avm_mode_switch: } }
              }
          }
          {
            \tl_build_put_right:Nn \l__avm_parsed_tl { ##1 }
          }
      }
    \tl_build_end:N \l__avm_parsed_tl
    \tl_set:Nx \l__avm_parsed_tl {\l__avm_parsed_tl}
    \tl_use:N \l__avm_parsed_tl
    \group_align_safe_end:
  }


%% 
%%
%% End of file `langsci-avm.sty'.
