%% lua-placeholders.sty
%% Copyright 2024 E. Nijenhuis
%
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3c
% of this license or (at your option) any later version.
% The latest version of this license is in
% http://www.latex-project.org/lppl.txt
% and version 1.3c or later is part of all distributions of LaTeX
% version 2005/12/01 or later.
%
% This work has the LPPL maintenance status ‘maintained’.
%
% The Current Maintainer of this work is E. Nijenhuis.
%
% This work consists of the files lua-placeholders.sty
% lua-placeholders-manual.pdf lua-placeholders.lua
% lua-placeholders-common.lua lua-placeholders-namespace.lua
% lua-placeholders-parser.lua and lua-placeholders-types.lua

\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{lua-placeholders}[2026/05/19 2.0.2 Lua Placeholders Package]

\RequirePackage{textcomp}
\RequirePackage{xspace}

\newcommand\curnamespace{\jobname}
\newcommand\paramplaceholder[1]{\texttt{\textbf{[}#1\textbf{]}}}
\newcommand\paramnotfound[1]{\paramplaceholder{{\textnormal{\textlangle unknown\textrangle}} #1}}
\newcommand\paramlistconjunction{,~}

% --- Engine/format hooks the Lua core defers to ----------------------------
% The Lua side never references package-specific commands directly: it emits
% these wrappers and the .sty (or the plain-LuaTeX .tex) decides what they
% mean.  Each is \providecommand so a user can override before \usepackage.
%
% \paramnumberformat{N}     -- format a numeric value
% \paramdateformat{Y}{M}{D} -- format a YYYY-MM-DD date scalar
% \paramfieldterm           -- token appended after each \paramobject field
%                              binding (so \name without {} doesn't gobble
%                              the next space)
\providecommand\paramnumberformat[1]{%
    \@ifundefined{numprint}{#1}{\numprint{#1}}%
}
\providecommand\paramdateformat[3]{%
    \@ifundefined{printdateTeX}{#1/#2/#3}{\printdateTeX{#1/#2/#3}}%
}
\providecommand\paramfieldterm{\xspace}

% --- Boolean flag bridge ----------------------------------------------------
% Lua emits \paramnewbool{key} when a bool-typed parameter is declared and
% \paramsetbool{key}{true|false} when its value is loaded.  We \let the
% if-token directly to \iftrue / \iffalse (no \newif, whose internal
% \@if / \if@ machinery breaks for \csname-built names).  \paramnewbool
% routes through \@ifundefined so the literal \iffalse stays in a brace
% argument, hidden from TeX's skip-scanner -- otherwise a second call for
% the same key trips on it with "Incomplete \ifx".
\providecommand\paramnewbool[1]{%
    \@ifundefined{if#1}{%
        \expandafter\let\csname if#1\endcsname=\iffalse%
    }{}%
}
% Dispatch \paramsetbool{key}{true|false} to a per-value helper.  Putting
% \iftrue/\iffalse inside an \if-conditional branch trips TeX's skip scanner
% (which counts every \if* token as a nested conditional, even when it's an
% argument to \let), so we keep them at the top level of the helper bodies.
\providecommand\paramsetbool[2]{\csname paramsetbool#2\endcsname{#1}}
\providecommand\paramsetbooltrue[1]{\expandafter\let\csname if#1\endcsname=\iftrue}
\providecommand\paramsetboolfalse[1]{\expandafter\let\csname if#1\endcsname=\iffalse}

\directlua{lua_placeholders = require('lua-placeholders')}

\newcommand\setnamespace[1]{\renewcommand\curnamespace{#1}}
\newcommand\strictparams{\directlua{lua_placeholders.set_strict()}}
\newcommand%! suppress = NonMatchingIf
\ifparam[4][\curnamespace]{%
    \directlua{local p = lua_placeholders.param_object('#2','#1') if p then p:set_bool('#2') end}%
    \csname if#2\endcsname #3\else #4\fi}
\newcommand\param[2][\curnamespace]{\directlua{lua_placeholders.param('#2', '#1')}}
\newcommand\PARAM[2][\curnamespace]{\directlua{local p = lua_placeholders.param_object('#2', '#1') if p then tex.print(p:to_upper()) end}}
\def\rawparam#1#2{\directlua{local p = lua_placeholders.param_object('#2', '#1') if p then tex.print(p:raw_val() or p.default or p.placeholder) end}}
\newcommand\numparam[2][\curnamespace]{\directlua{local n = lua_placeholders.param_object('#2', '#1') if n then n:print_num() end}}
\newcommand\hasparam[4][\curnamespace]{%
    \def\paramhastrue{#3}%
    \def\paramhasfalse{#4}%
    \directlua{lua_placeholders.handle_param_is_set('#2', '#1')}}
\newcommand\paramfield[3][\curnamespace]{\directlua{lua_placeholders.field('#2','#3', '#1')}}
\newenvironment{paramobject}[2][\curnamespace]{\directlua{lua_placeholders.with_object('#2', '#1')}}{\directlua{lua_placeholders.exit_object()}}
\newcommand\forlistitem[3][\curnamespace]{\directlua{lua_placeholders.for_item('#2', '#1', '#3')}}
\newcommand\fortablerow[3][\curnamespace]{\directlua{lua_placeholders.with_rows('#2', '#1', '#3')}}
\newcommand\loadrecipe[2][]{\directlua{lua_placeholders.recipe('#2', '#1')}}
\newcommand\loadpayload[2][]{\directlua{lua_placeholders.payload('#2', '#1')}}
