% Input: the executable registry and document-level setup declarations.
% Output: public setup commands, shared picture keys, and diagnostics.
% Owned state: registry records, atom descriptors, and language validation state.
% Invariants: public syntax is normalized before any model or geometry mutation.
% Next stage: tenkz-model.code.tex receives validated semantic records.
% SPDX-License-Identifier: Apache-2.0
% Copyright the TNLean project; see LICENSE for the full terms.

\ExplSyntaxOn
\seq_new:N \g__tenkz_language_preludes_seq
\tl_new:N \l__tenkz_language_atom_skin_tl
\tl_new:N \l__tenkz_language_atom_ports_tl
\tl_new:N \l__tenkz_language_atom_options_tl
\bool_new:N \l__tenkz_language_valid_bool

% Four of the five row kinds are read by the tools, which parse the registry
% source itself, and by nothing at load: the environment, command, key, and
% example rows were being collected into sequences no TeX code ever mapped.
% They take the same shape as the tombstone row below -- the row is a record
% for a reader, and the file it lives in is where a reader finds it.
\cs_new_protected:Npn \__tenkz_language_registry_environment:nnnn #1#2#3#4 { }
\cs_new_protected:Npn \__tenkz_language_registry_command:nnnnn #1#2#3#4#5 { }
\cs_new_protected:Npn \__tenkz_language_registry_key:nnnnnn #1#2#3#4#5#6 { }
\cs_new_protected:Npn \__tenkz_language_registry_example:nnn #1#2#3 { }
\cs_new_protected:Npn \__tenkz_language_registry_prelude:nnn #1#2#3
  { \seq_gput_right:Nn \g__tenkz_language_preludes_seq {{#1}{#2}{#3}} }
% A tombstone row is a record of a spelling that no longer runs, so the
% language stage keeps none of it: the parser installs its own refusals
% (tenkz-kernel.code.tex) and the tools read the rows from the registry
% source.  The rows live in the registry because a dead spelling belongs
% beside the live vocabulary it was struck from, and because one record is
% the only way the parser's refusals and the lint's cannot disagree.
\cs_new_protected:Npn \__tenkz_language_registry_tombstone:nnn #1#2#3 { }
\input{tenkz-language-registry.tex}

\msg_new:nnn {tenkz} {language-value}
  { [TKZ-LANGUAGE-INVALID-VALUE]~#1 }
\msg_new:nnn {tenkz} {atom-name}
  {
    [TKZ-ATOM-INVALID-NAME]~The~first~argument~of~
    \token_to_str:N \tndeclareatom~must~be~an~undefined~control~sequence.
  }
\msg_new:nnn {tenkz} {atom-port}
  {
    [TKZ-ATOM-INVALID-PORT]~got~'#1';~expected~west|east:virtual~or~
    up|down:physical.
  }

% \tnset is bound by \tenkzkernel; since the S4 surface swap tikz-tensor-networks.sty makes
% that binding at load, so setup keys are the kernel-setup registry rows.
% The one /tenkz pgf key left is the metric door the kernel-setup pitch row
% delegates to; the register lives in tenkz-core.
\pgfkeys{
  /tenkz/.is~family,
  /tenkz/pitch/.code={\tenkz@basepitch=#1\relax
                      \tenkz@pitch=\tenkz@basepitch},
}

\pgfkeys{
  /tenkz/declare~atom/.is~family,
  /tenkz/declare~atom/skin/.is~choice,
  /tenkz/declare~atom/skin/dot/.code={\tl_set:Nn \l__tenkz_language_atom_skin_tl {dot}},
  /tenkz/declare~atom/skin/box/.code={\tl_set:Nn \l__tenkz_language_atom_skin_tl {box}},
  /tenkz/declare~atom/skin/pill/.code={\tl_set:Nn \l__tenkz_language_atom_skin_tl {pill}},
  /tenkz/declare~atom/skin/mpo/.code={\tl_set:Nn \l__tenkz_language_atom_skin_tl {mpo}},
  /tenkz/declare~atom/ports/.store~in=\l__tenkz_language_atom_ports_tl,
  /tenkz/declare~atom/.unknown/.code={
    \bool_set_false:N \l__tenkz_language_valid_bool
    \msg_error:nnn {tenkz}{language-value}
      {atom~descriptor~key~'\pgfkeyscurrentname'}},
}
% The compatibility descriptor speaks compass faces (west:virtual,
% up:physical); the kernel's port grammar has the same compass shorthand
% (w:virtual, n:physical), so validation translates face words and the
% declaration is minted by the kernel's own door -- a declared command is an
% ordinary kernel atom.
\cs_new_protected:Npn \__tenkz_language_validate_port:n #1
  {
    \regex_extract_once:nnNTF
      { \A \s* (west|east|up|down) : (virtual|physical) \s* \Z }
      {#1} \l_tmpa_seq
      {
        \str_case_e:nnF
          { \seq_item:Nn \l_tmpa_seq {2} : \seq_item:Nn \l_tmpa_seq {3} }
          {
            {west:virtual} { \tl_set:Nn \l_tmpb_tl { w:virtual } }
            {east:virtual} { \tl_set:Nn \l_tmpb_tl { e:virtual } }
            {up:physical}  { \tl_set:Nn \l_tmpb_tl { n:physical } }
            {down:physical}{ \tl_set:Nn \l_tmpb_tl { s:physical } }
          }
          { \bool_set_false:N \l__tenkz_language_valid_bool
            \msg_error:nnn {tenkz}{atom-port}{#1} }
        \bool_if:NT \l__tenkz_language_valid_bool
          {
            \tl_if_empty:NF \l__tenkz_language_atom_options_tl
              { \tl_put_right:Nn \l__tenkz_language_atom_options_tl {,} }
            \tl_put_right:NV \l__tenkz_language_atom_options_tl \l_tmpb_tl
          }
      }
      { \bool_set_false:N \l__tenkz_language_valid_bool
        \msg_error:nnn {tenkz}{atom-port}{#1} }
  }
\cs_new_protected:Npn \__tenkz_language_declare_atom:Nn #1#2
  {
    \bool_set_true:N \l__tenkz_language_valid_bool
    \tl_set:Nn \l__tenkz_language_atom_skin_tl {dot}
    \tl_clear:N \l__tenkz_language_atom_ports_tl
    \tl_clear:N \l__tenkz_language_atom_options_tl
    \cs_if_exist:NT #1
      { \bool_set_false:N \l__tenkz_language_valid_bool
        \msg_error:nn {tenkz}{atom-name} }
    \pgfqkeys{/tenkz/declare~atom}{#2}
    \clist_map_inline:Vn \l__tenkz_language_atom_ports_tl
      { \__tenkz_language_validate_port:n {##1} }
    \bool_if:NT \l__tenkz_language_valid_bool
      {
        \use:e
          {
            \exp_not:N \__tenkz_kernel_declare_atom:nn
              { \exp_not:N #1 }
              {
                skin = \tl_use:N \l__tenkz_language_atom_skin_tl
                \tl_if_empty:NF \l__tenkz_language_atom_options_tl
                  { , ports = { \tl_use:N \l__tenkz_language_atom_options_tl } }
              }
          }
      }
  }
% A declaration is setup, wherever it is spelled: an equation places pictures
% around the mathematics between them and is no place to mint a document-wide
% name (LANGUAGE-1.0 section 7).
\NewDocumentCommand \tndeclareatom { m m }
  {
    \bool_if:NTF \l__tenkz_kernel_ineq_bool
      { \msg_error:nn {tenkz}{kernel-declare-equation} }
      { \__tenkz_language_declare_atom:Nn #1 {#2} }
  }
\ExplSyntaxOff
\endinput
