You defined \basicMacro
such that it takes a single character as argument, then \chooseValue
does:
\str_case:nn {#1}
{
{ N } { 0.85 } % This value will be selected
{ A } { 0.62 } % This will not
}
to select one. However when you do, in \anotherUseMacro
:
\str_set:Nn \l_input_str { #1 }
\basicMacro{\l_input_str}
you are effectively calling \chooseValue
with the string \l_input_str
, and not with its value. You can easily see the problem if you change the definition of \chooseValue
to add a default case to the \str_case:nn
call:
\NewDocumentCommand \chooseValue { m }
{
\str_case:nnF {#1}
{
{ N } { 0.85 } % This value will be selected
{ A } { 0.62 } % This will not
}
{
\msg_expandable_error:nnn { 3isenheim } { wrong-case } {#1}
1.00 % default value
}
}
\msg_new:nnn { 3isenheim } { wrong-case }
{ Value~'#1'~invalid~for~\iow_char:N\\chooseValue. }
then you'll see the error message:
! Use of \??? doesn't match its definition.
<argument> \???
! Package 3isenheim Error: Value '\l_input_str ' invalid fo...
l.79 \anotherUseMacro{N}
?
You have to expand the string variable before comparing it. One option is to always exhaustively expand the argument, then you can use \str_case_e:nnF
instead of \str_case:nnF
(in the definition above). The other one is to expand \l_input_str
in \anotherUseMacro
before passing it on to \basicMacro
:
\NewDocumentCommand \anotherUseMacro { m }
{
\str_set:Nn \l_input_str {#1}
\exp_args:NV \basicMacro \l_input_str
}
Other poitns:
Your variables should be called \l__eisenheim_input_str
and so on to have a proper <module>
part. There's an explanation here;
\chooseValue
looks like an internal macro, so I'd call it \__eisenheim_choose_value:n
and define it with \cs_new:Npn
;
\computeValue
is not expandable (because \fp_set:Nn
is not expandable) so you should define it with \NewDocumentCommand
instead (or \cs_new_protected:Npn
if it's internal).
Complete code:
\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\fp_new:N \l_cvssAV_fp
\fp_new:N \l_argument_fp
\fp_new:N \l_value_fp
\fp_new:N \l_result_fp
\str_new:N \l_input_str
\NewDocumentCommand \chooseValue { m }
{
\str_case:nnF {#1}
{
{ N } { 0.85 } % This value will be selected
{ A } { 0.62 } % This will not
}
{
\msg_expandable_error:nnn { 3isenheim } { wrong-case } {#1}
1.00 % default value
}
}
\msg_new:nnn { 3isenheim } { wrong-case }
{ Value~'#1'~invalid~for~\iow_char:N\\chooseValue. }
\NewDocumentCommand \computeValue {m}{
\fp_set:Nn \l_argument_fp { \chooseValue{#1} }
\fp_show:N \l_argument_fp % \l_argument_fp=(). <<<< HERE IS THE ERROR
\fp_set:Nn \l_value_fp { 1 - (1 - \l_argument_fp )}
% 1 - (1 - 0.85 )
}
\NewDocumentCommand \factorScaling {m}{%
\fp_eval:n { 8.22 * (#1) }%
% 8.22 * 0.85
}%
% https://tex.stackexchange.com/a/615358/28926
\NewDocumentCommand \roundup {m}{
\fp_eval:n { ceil(#1,1) }
\fp_compare:nT { ceil(#1,1)=ceil(#1,0) } {.0}
}
\cs_new_protected:Npn \basicMacro #1 {
\computeValue{#1}
% \l_argument_fp=0.85
\fp_set:Nn \l_result_fp { \factorScaling{\chooseValue{#1}} }%
% \l_result_fp=8.22*0.85
% \l_result_fp=6.987
%
\fp_compare:nTF { \l_value_fp <= 0 }
% IF value <=0
{
% value <=0
0.0
}{
\fp_eval:n { round( min( 1.08 * (\l_value_fp + \l_result_fp), 10) ) }%
}%
}
\NewDocumentCommand \anotherUseMacro { m }{%
\str_set:Nn \l_input_str { #1 }
% CALLS basicMACRO
\exp_args:NV \basicMacro \l_input_str
}%
\ExplSyntaxOff
\begin{document}
\basicMacro{N}
\anotherUseMacro{N}
\end{document}
Here is a fully expandable implementation of \basicMacro
. Since you are doing basically only floating point computations, it is easy to do that. Roughly you want to replace every occurrence of:
\cs_new:Npn \some_macro:n #1
{
\fp_set:Nn \l_some_temp_fp { <computations> }
\fp_eval:n { <more computations with \l_some_temp_fp> }
}
by
\cs_new:Npn \some_macro:n #1
{
\exp_args:Ne \__some_macro_internal:n
{ \fp_eval:n { <computations> } }
}
\cs_new:Npn \__some_macro_internal:n #1
{ \fp_eval:n { <more computations with #1> } }
so that you remove the non-expandable \fp_set:Nn
and instead just use \fp_eval:n
(expandable) and pass its result around as arguments (expandable).
Here's the full code:
\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\cs_new:Npn \__eisenheim_choose_value:n #1
{
\str_case:nnF {#1}
{
{ N } { 0.85 } % This value will be selected
{ A } { 0.62 } % This will not
}
{
\msg_expandable_error:nnn { 3isenheim } { wrong-case } {#1}
1.00 % default value
}
}
\msg_new:nnn { 3isenheim } { wrong-case }
{ Value~'#1'~invalid~for~\iow_char:N\\chooseValue. }
\cs_new:Npn \__eisenheim_compute_value:n #1 { 1 - (1 - \__eisenheim_choose_value:n {#1} ) }
\cs_new:Npn \__eisenheim_factor_scaling:n #1 { 8.22 * (#1) }
\NewExpandableDocumentCommand \basicMacro { m }
{
\exp_args:Nee \__eisenheim_basic_aux:nn
{ \fp_eval:n { \__eisenheim_compute_value:n {#1} } }
{ \fp_eval:n { \__eisenheim_factor_scaling:n { \__eisenheim_choose_value:n {#1} } } }
}
\cs_new:Npn \__eisenheim_basic_aux:nn #1 #2
{
\fp_compare:nTF { #1 <= 0 }
{ 0.0 }
{ \fp_eval:n { round( min( 1.08 * (#1 + #2), 10) ) } }
}
\ExplSyntaxOff
\begin{document}
\edef\x{\basicMacro{N}}
\texttt{\meaning\x}
\end{document}
Best Answer
The situation is exactly the same as
\foo word
in classic TeX.You can use
\foo{} word
or\foo\space word
(or\foo\c_space_tl word
)Note in the first form the
{}
are not absorbed, they make two additional tokens. An empty group has no effect if typetting but these tokens would show up in a\typeout
or shell escape, or other non-typesetting contexts.