% \iffalse meta-comment % arara: lualatex: { branch: developer, draft: true } % arara: lualatex: { branch: developer, interaction: batchmode, shell: false } % arara: lualatex: { branch: developer, interaction: batchmode, shell: false } % arara: clean: { extensions: [ aux, log, out, glo, gls, hd, ind, ilg, idx, toc, atfi ] } % arara: clean: { files: [ userdoc.idx, userdoc.ilg, userdoc.ind ] } % arara: halt % % File: spanish-mathml-intent.dtx % % Copyright (C) 2026 by Pablo González L % % 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 % % https://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 is "maintained" (as per the LPPL maintenance status) % by Pablo González L. % % This work consists of the files spanish-mathml-intent.dtx, spintent.lua, spitool.lua % and spanish-mathml-intent.sty. % % \fi % % \iffalse %<*driver> \documentclass[full]{l3doc} \usepackage[svgnames]{xcolor} \usepackage[sf,bf,compact,medium,pagestyles]{titlesec} \usepackage{lastpage,microtype,attachfile2,multicol,listings,titletoc,booktabs, longtable, fontawesome5} \usepackage{scalerel} % for \spmQ sample \usepackage[english]{babel} % \selectlanguage{english} \usepackage[top=0.5in,bottom=0.3in,left=1.70in,right=0.7in,footskip=0.2in,headheight=1cm,headsep=0.27cm]{geometry} % Set \parindent and \parskip \setlength{\parindent}{0pt} \setlength{\parskip}{\smallskipamount} % Estiramiento extra antes de desbordar la línea \setlength{\emergencystretch}{2em} \newlength{\mydpstrut} \setlength{\mydpstrut}{\dp\strutbox} % Only for arara... I like arara :) \usepackage[scale=0.85]{comfortaa} \definecolor{araracolor}{rgb}{0, 0.72, 0.28} \newcommand{\araratext}[1]{{\small\normalfont\comfortaa\color{araracolor}\bfseries#1}} \newcommand*\arara{\araratext{ar\kern-.03emar\kern-.03ema}} % Fonts \usepackage{luacode} % Para documentación y ejemplos \begin{luacode*} local SCALE = 0.85 local fallback_cache = {} local lookup_cache = {} luatexbase.add_to_callback("luaotfload.patch_font", function(fontdata, specification, id) if type(fontdata) ~= "table" then return end local meta = fontdata.shared and fontdata.shared.rawdata and fontdata.shared.rawdata.metadata local familyname = meta and meta.familyname if type(familyname) ~= "string" then return end if familyname:lower():find("libertinus serif", 1, true) then local size = fontdata.size local key = tostring(size) local fb_id = fallback_cache[key] if not fb_id then local path = kpse.find_file("Carlito-Regular.ttf", "truetype fonts") if path then local scaled_size = math.floor(size * SCALE) local ok, result = pcall(luaotfload.define_font, "[" .. path .. "]", scaled_size) if ok then fb_id = result fallback_cache[key] = fb_id end end end if fb_id then local lookup = lookup_cache[fb_id] if not lookup then local fb_font = font.getfont(fb_id) lookup = {} for uni, _ in next, fb_font.characters do lookup[uni] = fb_id end lookup_cache[fb_id] = lookup end fontdata.fallback_lookup = lookup end end end, "shekel_fallback_patch") \end{luacode*} \usepackage[osf,nomath,mono=false,ScaleSF=0.95,ScaleRM=0.95]{libertinus-otf} \begin{luacode*} local dejavu_path = kpse.find_file("DejaVuSansMono.ttf", "truetype fonts") luaotfload.add_fallback("shekelfallback", { "[" .. dejavu_path .. "]" }) \end{luacode*} \defaultfontfeatures[\ttfamily] { Numbers = OldStyle, Scale = 0.84249, Extension = .otf, } \setmonofont[ UprightFont = *-Regular, ItalicFont = *-RegularIt, BoldFont = *-Medium, BoldItalicFont = *-MediumIt, RawFeature = {+zero,+ss01,fallback=shekelfallback}]{SourceCodePro} \newfontfamily\mysourcecodeprolight{SourceCodePro-Regular.otf}[ Scale = 0.80,FakeStretch =0.75, FontFace = {m}{it}{Font =SourceCodePro-RegularIt.otf,Color=FF0000}] \RenewDocumentCommand{\MacroLongFont}{} { \mysourcecodeprolight\small } % Internal \newfontfamily\mylmrotf{lmroman10-regular.otf}[NFSSFamily=lmrotf,Scale = 0.95] % The character of visible space is now taken from Latin Modern Mono % to prevent fonts in T1. The original definition for xetex/luatex is % \def\verbvisiblespace{\usefont{OT1}{cmtt}{m}{n}\asciispace} \def\verbvisiblespace{{\fontfamily{lmrotf}\fontshape{n}\selectfont\char"2423}} % Math font % \typeout{ \fileversion } % \global\let\danger\relax % \usepackage[no-text,Scale = 0.95]{fourier-otf} \usepackage{unicode-math} \setmathfont{Erewhon-Math}[Scale = 0.95] \setmathfont{Erewhon-Math}[range={cal,bfcal},StylisticSet={1,4}] \setmathfont{NewCMMath-Regular.otf}[Scale = 0.95, range={"299C}] % \rightanglesqr \providecommand*{\widearc}{\Umathaccent 7 \symoperators "023DC} \newfontfamily\fetamontotf{ffmw10.otf}[ Scale = 0.95,% RawFeature = {+latn,+rand,+kern,+size},% FontFace = {b}{n}{ffmw10.otf},% fix replace font ] % Remove `"` and `|` for short verbatim defined in l3doc, then set short |...| \AtBeginDocument { \DeleteShortVerb \" \DeleteShortVerb \| \lstMakeShortInline[language=spintent-doc,basicstyle=\ttfamily]\| } % Index \usepackage{imakeidx} \indexsetup{level=\section,firstpagestyle=myheader,othercode=\pagestyle{myheader}} % imakeidx also opens a write stream for the .idx file, and that conflicts % with the one opened by l3doc. Here we close that write stream: \ExplSyntaxOn \iow_close:c { @indexfile } % and later we will copy the write stream opened by imakeidx into \@indexfile % so that entries written to both streams end up in the same file. \makeindex[name=userdoc,options={-q ~ -s ~ gind.ist},columnsep=15pt,title={Índice ~ de ~ la ~ Documentación}] \makeindex[options={-q ~ -s ~ gind.ist},columnsep=15pt,title={Índice ~ de ~ la ~ Implementación}] \EnableCrossrefs % \PageIndex, \CodelineIndex undoes what \PageIndex does \CodelineIndex % tries to open another write stream for the index file. We don't want % that, so we temporarily make \makeindex a no-op: \cs_set_eq:NN \l__mydoc_tmp_makeindex_cs \makeindex \cs_set_nopar:Npn \makeindex { } \CodelineIndex \cs_set_eq:NN \makeindex \l__mydoc_tmp_makeindex_cs \DoNotIndex{\ , \1,\^} \exp_args:Ne \DoNotIndex { \tl_to_str:n { \{ } } \exp_args:Ne \DoNotIndex { \tl_to_str:n { \} } } \exp_args:Ne \DoNotIndex { \tl_to_str:n { \begin } } % \StartImplementation \cs_new_protected:Npn \StartImplementation { \bool_set_true:N \l__codedoc_in_implementation_bool } \bool_set_false:N \l__codedoc_in_implementation_bool % Links and index \newcommand{\HP}[1]{\emph{\hyperpage{#1}}\normalsize} \cs_new_protected:Nn \__mydoc_sort_index:nn { \bool_if:NTF \l__codedoc_in_implementation_bool { \index{#1\actualchar#2|HP} } { \index[userdoc]{#1\actualchar#2|HP} } } % The text of the entry in Spanish (#2) for the user part and in English (#3) % for the implementation, chosen when the command is called. \cs_new_protected:Nn \__mydoc_sort_index:nnn { \bool_if:NTF \l__codedoc_in_implementation_bool { \__mydoc_sort_index:nn {#1} {#3} } { \__mydoc_sort_index:nn {#1} {#2} } } % Now, after imakeidx opens the write stream for the index file, we copy % the reference to \@indexfile: \cs_set_eq:cc { @indexfile } { \c_sys_jobname_str @idxfile } \ExplSyntaxOff % LuaLaTeX syntax highlighter for l3doc \usepackage{mylhmc} \setmylhmc { module = spintent, provide = { setspintent, spnum, spunit, spnewunit, spunitalias, spmoney, spshort, spsiglo, sptime, spdate, spread, spdsym, spmsym, spusep, spdiv, spmul, spnM, spnD, spmcd, spmcm, spnZ, spang, spmQ, spPoint, sprecta, sprayo, spside, spmside, spangle, spmangle, sparc, spmarc, spPoly, spAfig, spPfig, sprad, spdiam, spcirc, spAcirc, spPcirc, spcoord, spvec, sprazon, spProp, spnewread, spVol, spinterval, spfun, spfuncomp, spdom, sprec, spcod, % spProm, spmedia, spmediana, spmoda, spdesv, spvarz, % spcuartil, spdecil, spPerc, sprango, spriq, spmin, spmax, spFrec, spcase, spOmega, spPval, spProb, spcomp, spcomb, spfact, spabs, % } } % Colors \definecolor{clspkg}{HTML}{8DA0F5} % Azul claro \definecolor{rulecolor}{rgb}{0.96,0.96,0.96} \definecolor{nvdapurple}{rgb}{0.353, 0.176, 0.506} % Púrpura NVDA (#5A2D81) \definecolor{mathcatgreen}{rgb}{0.165, 0.478, 0.157} % Verde MathCAT (#2A7A28) \definecolor{catgray}{rgb}{0.4, 0.4, 0.4} % Gris para "CAT" o "ML" \definecolor{mathmlblue}{HTML}{1F5FBF} % Azul para "Math" de MathML % File info \def\fileversion{v1.0} \def\filedate{2026-10-20} \def\fileinfo{Spanish parse intents} % Config hyperref \definecolor{linkcolor}{rgb}{0.04,0.38,0.04} \definecolor{newlinkcolor}{HTML}{0A602C} % (0A6048 bonito Deep Teal) 029352 \hypersetup { allcolors = newlinkcolor, %%%araracolor,%%%;linkcolor, colorlinks = true, linktoc = all, pdftitle = {.:: The spanish-mathml-intent package --- \fileinfo{} ::.}, pdfauthor = {Pablo González L}, pdfsubject = {Documentation for \fileversion{} [\filedate] }, pdfkeywords = {MathCAT, NVDA, tagged PDF, spanish, accesibilidad}, bookmarksopenlevel = 2, } % User level commands \ExplSyntaxOn % Logo \NewDocumentCommand \pkglogo {} { \group_begin: \LibertinusSansOsF{\textcolor{module_name}{spanish}\textcolor{gray}{-}\textcolor{module_name}{mathml}\textcolor{gray}{-}\textcolor{OrangeRed}{intent}} \group_end: } % Custom \meta[...]{...}, \marg[...]{...} and \oarg[...]{...} with color \NewDocumentCommand \mymeta { O{} m } { \__mydoc_meta_generic:Nnn \__mydoc_meta:n { #1 } { #2 } } \NewDocumentCommand \mymarg { O{} m } { \__mydoc_meta_generic:Nnn \__mydoc_marg:n { #1 } { #2 } } \NewDocumentCommand \myoarg { O{} m } { \__mydoc_meta_generic:Nnn \__mydoc_oarg:n { #1 } { #2 } } \NewDocumentCommand \myparg { O{} m } { \__mydoc_meta_generic:Nnn \__mydoc_parg:n { #1 } { #2 } } % Rows with two columns inside the syntax environment \dim_new:N \l_userdoc_col_dim \NewDocumentCommand \mycolrow { m } { \mode_leave_vertical: \dim_set:Nn \l_userdoc_col_dim { #1 } \tex_hangindent:D \l_userdoc_col_dim \tex_hbox:D to \l_userdoc_col_dim \c_group_begin_token } \NewDocumentCommand \mycolsep { } { \tex_hfil:D \c_group_end_token } % Variables and keys \tl_new:N \l_userdoc_meta_font_tl \keys_define:nn { userdoc / mymeta } { type .choice:, type / tt .code:n = \tl_set:Nn \l_userdoc_meta_font_tl { \ttfamily }, type / rm .code:n = \tl_set:Nn \l_userdoc_meta_font_tl { \rmfamily }, type .initial:n = tt, cf .tl_set:N = \l_userdoc_meta_color_tl, cf .initial:n = keyname, ac .tl_set:N = \l_userdoc_meta_anglecolor_tl, ac .initial:n = lightgray, sbc .tl_set:N = \l_userdoc_meta_brackcolor_tl, sbc .initial:n = gray, cbc .tl_set:N = \l_userdoc_meta_bracecolor_tl, cbc .initial:n = gray, pbc .tl_set:N = \l_userdoc_meta_parencolor_tl, pbc .initial:n = gray, } % Internal commands \cs_new_protected:Npn \__mydoc_meta_generic:Nnn #1 #2 #3 { \group_begin: \keys_set:nn { userdoc / mymeta } { #2 } \color{ \l_userdoc_meta_color_tl } \l_userdoc_meta_font_tl #1 { #3 } % #1 is \__mydoc_meta:n, \__mydoc_marg:n or \__mydoc_oarg:n \group_end: } \cs_new_protected:Npn \__mydoc_meta:n #1 { \__mydoc_meta_angle:n { \textlangle } \__mydoc_meta_meta:n { #1 } \__mydoc_meta_angle:n { \textrangle } } \cs_new_protected:Npn \__mydoc_marg:n #1 { \__mydoc_meta_brace:n { \textbraceleft } \__mydoc_meta:n { #1 } \__mydoc_meta_brace:n { \textbraceright } } \cs_new_protected:Npn \__mydoc_oarg:n #1 { \__mydoc_meta_brack:n { [ } \__mydoc_meta:n { #1 } \__mydoc_meta_brack:n { ] } } \cs_new_protected:Npn \__mydoc_parg:n #1 { \__mydoc_meta_brace:n { ( } \__mydoc_meta:n { #1 } \__mydoc_meta_brace:n { ) } } \cs_new_protected:Npn \__mydoc_meta_meta:n #1 { \textnormal{\textit{#1}} } \cs_new_protected:Npn \__mydoc_meta_angle:n #1 { \group_begin: \fontfamily{lmrotf}\fontshape{n}\selectfont \textcolor{\l_userdoc_meta_anglecolor_tl}{#1} \group_end: } \cs_new_protected:Npn \__mydoc_meta_brace:n #1 { \group_begin: \color{\l_userdoc_meta_bracecolor_tl} #1 \group_end: } \cs_new_protected:Npn \__mydoc_meta_brack:n #1 { \textcolor{\l_userdoc_meta_brackcolor_tl}{#1} } % \keyexampcmd{s m m m o} \NewDocumentCommand \keyexampcmd { s m m m o } { \group_begin: \noindent \mode_leave_vertical: \hbox_to_wd:nn { -\marginparsep } { \tex_hss:D \textcolor { keyname } { \small \ttfamily {#2} } } \IfBooleanTF {#1} { \hphantom { \textcolor { white } { \, \bfseries \texttt{=} } \, {} } \mymeta [ type=tt, cbc=gray, ac=lightgray, cf=lightgray ] { \small {#3} } } { \textcolor { gray } { \, \bfseries \texttt{=} } \, {} \mymarg [ type=tt, cbc=gray, ac=lightgray, cf=keyname ] { \small {#3} } } \hfill \textcolor { gray } { \small \textsf { \bool_if:NTF \l__codedoc_in_implementation_bool { default } { por~defecto } } :~ \emph{#4} } \par \IfValueT {#5} { \clist_map_inline:nn {#5} { \__mydoc_sort_index:nnn {Keys} { Llaves~de~ \texttt { \textbackslash ##1 } ~ provistas~por~ \pkglogo :> \texttt {#2} } { Keys~for~ \texttt { \textbackslash ##1 } ~ provided~by~ \pkglogo :> \texttt {#2} } } } \group_end: } % \mykey{o m} \NewDocumentCommand \mykey { o m } { \group_begin: \textcolor{keyname}{\ttfamily #2} \IfValueT {#1} { \clist_map_inline:nn {#1} { \__mydoc_sort_index:nnn {Keys} {Llaves~de~\texttt{\textbackslash ##1}~provistas~por~\pkglogo :> \texttt {#2}} {Keys~for~\texttt{\textbackslash ##1}~provided~by~\pkglogo :> \texttt {#2}} } } \group_end: } % \mypkg{sm} \NewDocumentCommand \mypkg { s O{} m } { \group_begin: \IfBooleanTF {#1} { \pkglogo } { \textcolor{pkgname} { \textbf{\texttt{#3} } } %\texttt{#3} } \str_if_eq:nnF {#2} { no-index } { \__mydoc_sort_index:nnn { packages } { Paquetes~:>~\texttt{#3} } { Packages~:>~\texttt{#3} } } \group_end: } % \myclass{m} \NewDocumentCommand \myclass { m } { \textcolor{clsname} { \textbf{\texttt{#1} } } % \texttt{#3} \__mydoc_sort_index:nnn {class} {Clases~de~documento :>\texttt{#1}} {Document~class :>\texttt{#1}} } % \mynvmath{m} \tl_new:N \l__mynvmath_input_tl \NewDocumentCommand \mynvmath { m } { \group_begin: \tl_set:Nn \l__mynvmath_input_tl { #1 } \regex_replace_case_all:nN { { NVDA } { \c{textcolor}\cB\{percent\cE\}\cB\{NVDA\cE\} } { MathML } { \c{textcolor}\cB\{mathmlblue\cE\}\cB\{Math\cE\}\c{textcolor}\cB\{mathcatgreen\cE\}\cB\{ML\cE\} } { Math } { \c{textcolor}\cB\{percent\cE\}\cB\{Math\cE\} } { CAT|ML } { \c{textcolor}\cB\{comment\cE\}\cB\{\0\cE\} } { / } { \c{textcolor}\cB\{backtick\cE\}\cB\{/\cE\} } } \l__mynvmath_input_tl \textbf{\texttt{ \tl_use:N \l__mynvmath_input_tl } } \group_end: } % \myenv{sm} \NewDocumentCommand \myenv { s m O{}} { \IfBooleanTF{#1} { \tl_set:Nn \l_tmpa_tl { #2 } \regex_replace_once:nnN { (\*) } { \c{textcolor}\cB\{MediumOrchid\cE\}\cB\{\1\cE\} } \l_tmpa_tl \textcolor{module_name}{\ttfamily{\tl_use:N \l_tmpa_tl}}% \__mydoc_sort_index:nnn {environment} {Entornos~provistos~por~\pkglogo :>\texttt{\tl_use:N \l_tmpa_tl}} {Environments~provided~by~\pkglogo :>\texttt{\tl_use:N \l_tmpa_tl}} } { \textcolor{SlateBlue}{\ttfamily{#2}}% \__mydoc_sort_index:nnn {environment} {Entornos :>\texttt{#2}} {Environments :>\texttt{#2}} } } % \ics{sm} \NewDocumentCommand \ics {s m} { \IfBooleanTF{#1} { \tl_set:Nn \l_tmpa_tl { #2 } \regex_replace_once:nnN { (\*) } { \c{textcolor}\cB\{MediumOrchid\cE\}\cB\{\1\cE\} } \l_tmpa_tl \regex_replace_once:nnN { (!) } { \c{textcolor}\cB\{MediumOrchid\cE\}\cB\{\1\cE\} } \l_tmpa_tl \textcolor{module_name}{\ttfamily\textbackslash{\tl_use:N \l_tmpa_tl}} \__mydoc_sort_index:nnn {Commands} {Comandos~provistos~por~\pkglogo :>\texttt{\textbackslash#2}} {Commands~provided~by~\pkglogo :>\texttt{\textbackslash#2}} } { \textcolor{latex_cmd}{\ttfamily\textbackslash{#2}} \__mydoc_sort_index:nn {#2} {\texttt{\textbackslash#2}} } } % \mydim{m} \NewDocumentCommand \mydim { m } { \group_begin: \tl_set:Nn \l_tmpa_tl { #1 } \regex_replace_all:nnN { (\d) } { \c{textcolor}\cB\{number\cE\}\cB\{\1\cE\} } \l_tmpa_tl %\regex_replace_all:nnN { (\.) } { \c{textcolor}\cB\{unit\cE\}\cB\{\1\cE\} } \l_tmpa_tl \textcolor{unit}{ \ttfamily \tl_use:N \l_tmpa_tl } %%\texttt { \tl_use:N \l_tmpa_tl } \group_end: } % \myluamod{} \tl_new:N \l__mydoc_luamod_tl \NewDocumentCommand \myluamod { } { \group_begin: \tl_set:Nn \l__mydoc_luamod_tl { spintent.lua } \regex_replace_all:nnN { spintent } { \c{textcolor}\cB\{module_name\cE\}\cB\{sp\cE\} \c{textcolor}\cB\{OrangeRed\cE\}\cB\{intent\cE\} } \l__mydoc_luamod_tl \regex_replace_once:nnN { \. ( .* ) \Z } { \c{textcolor}\cB\{MediumOrchid\cE\}\cB\{\.\cE\} \c{textcolor}\cB\{gray\cE\}\cB\{\1\cE\} } \l__mydoc_luamod_tl \texttt{ \l__mydoc_luamod_tl } \__mydoc_sort_index:nnn {Module} {Módulo~Lua~provisto~por~\pkglogo :>\texttt{spintent.lua}} {Lua~module~provided~by~\pkglogo :>\texttt{spintent.lua}} \group_end: } % \myspitool{o} \NewDocumentCommand \myspitool { O{} } { \group_begin: \texttt { \textcolor { module_name } { sp } \textcolor { OrangeRed } { itool } } \str_if_eq:nnF {#1} { no-index } { \__mydoc_sort_index:nnn { Tool } { Herramienta~provista~por~\pkglogo :>\texttt{spitool} } { Tool~provided~by~\pkglogo :>\texttt{spitool} } } \group_end: } % \myfile{m} \NewDocumentCommand \myfile { m } { \group_begin: \tl_clear:N \l_tmpa_tl \tl_set:Nn \l_tmpa_tl { #1 } \regex_replace_all:nnN { (\d) } { \c{textcolor}\cB\{MediumOrchid\cE\}\cB\{\1\cE\} } \l_tmpa_tl \regex_replace_all:nnN { (\.) } { \c{textcolor}\cB\{MediumOrchid\cE\}\cB\{\1\cE\} } \l_tmpa_tl \textcolor{filename}{ \ttfamily \tl_use:N \l_tmpa_tl } \group_end: } % \myattachfile{m} \NewDocumentCommand \myattachfile{ m } { \group_begin: \IfFileExists{#1.pdf}{Archivo~fuente~ \textattachfile[color=newlinkcolor,print=false]{#1.tex}{\faFile*[regular]},~ \emph{tagged}~\texttt{PDF}~\texttt{mathml-SE}~ \textattachfile[color=newlinkcolor,print=false]{#1.pdf}{\faFilePdf[regular]} \ y~\texttt{mathml-AF}~\textattachfile[color=newlinkcolor,print=false]{#1-AF.pdf}{\faFilePdf[regular]}.}{} \group_end: } % Iconos al margen para entorno important (uso interno) \cs_new_protected:Npn \__mydoc_margin_icon:n #1 { \par \mode_leave_vertical: \skip_horizontal:n { -0.5 \marginparsep } \hbox_overlap_left:n { \hbox_to_wd:nn { \marginparsep } { \tex_hss:D \textcolor { module_at } { #1 } % privatefun } } \skip_horizontal:n { 0.5 \marginparsep } } % Espacio previo al entorno function \hook_gput_code:nnn { env/function/before } { mydoc } { \vspace* { -\baselineskip } } % Tablas largas del ancho de la línea \skip_set:Nn \LTleft { 0pt } \skip_set:Nn \LTright { 0pt } % Entorno de notas \NewDocumentEnvironment { important } { s } { \par\addvspace { \smallskipamount } \small\sffamily \IfBooleanTF { #1 } { \__mydoc_margin_icon:n { \footnotesize \faIcon { bomb } } } { \__mydoc_margin_icon:n { \scriptsize \reflectbox { \faIcon { tag } } } } \ignorespaces } { } % Análisis y formato de correo electrónico \seq_new:N \l__mydoc_email_seq \cs_new_protected:Npn \__mydoc_format_email:n #1 { \seq_set_split:Nnn \l__mydoc_email_seq { @ } { #1 } \href { mailto:#1 } { \ttfamily \seq_item:Nn \l__mydoc_email_seq { 1 } \textcolor { arroba } { \footnotesize @ } \seq_item:Nn \l__mydoc_email_seq { 2 } } } \NewDocumentCommand \email { m } { \__mydoc_format_email:n { #1 } } % Sobrescritura de la Tabla de Contenidos (TOC) \cs_new_eq:Nc \__mydoc_mkboth:nn { @mkboth } \cs_new_eq:Nc \__mydoc_starttoc:n { @starttoc } \RenewDocumentCommand \tableofcontents { } { \group_begin: % Restore the l3doc \parskip locally (only for the TOC) \skip_set:Nn \parskip { 0pt~plus~3pt~minus~0pt } \section* { \contentsname \quad { \color { gray } \tex_leaders:D \tex_hrule:D height 5pt depth -4.4pt \tex_hfill:D } } \__mydoc_mkboth:nn { \text_uppercase:n { \contentsname } } { \text_uppercase:n { \contentsname } } \vspace* { -14pt } \dim_set:Nn \columnsep { 10pt } \begin{multicols}{2} \__mydoc_starttoc:n { toc } \end{multicols} \vspace* { -3pt } { \color { gray } \tex_hrule:D height 0.6pt } \vspace* { 5pt } \group_end: } % \thanks \cs_new:Npn \__mydoc_fnsymbol:n #1 { \ensuremath { \int_case:nnF { #1 } { { 1 } { \textcolor { module_name } { * } } { 2 } { \textcolor { module_name } { \dagger } } { 3 } { \ddagger } { 4 } { \mathsection } { 5 } { \mathparagraph } { 6 } { \| } { 7 } { ** } { 8 } { \dagger \dagger } { 9 } { \ddagger \ddagger } } { \@ctrerr } } } \hook_gput_code:nnn { begindocument } { spintent } { \cs_set_eq:cN { @fnsymbol } \__mydoc_fnsymbol:n } % Don't copy numbers in code example \newcommand*{\noaccsupp}[1] { \__mylhmc_luafun_actual_text_begin: #1 \__mylhmc_luafun_actual_text_end: } \ExplSyntaxOff % Create a language for documentation \lstdefinelanguage{spintent-doc}{ texcsstyle = *,% escapechar =`,% extendedchars = true,% keepspaces = true,% stringstyle = {\color{red}},% basicstyle = \small\ttfamily,% \small alsoletter = {-,.},% @, % comments morecomment = [l]{\%},% commentstyle = \ttfamily\small\color{comment},% % Important \NeedsTeXFormat \ProvidesExplPackage \documentclass \DocumentMetadata keywordstyle = [1]{\bfseries\color{structure}},% keywords = [1]{documentclass, newcommand, makeatletter, makeatother, % begin, end, document, % },% % Other words \usepackage, keywordstyle = [2]{\color{pkgstruct}},% keywords = [2]{usepackage,},%, % Other words 3 MathML/tagpdf keywordstyle = [3]{\color{mathtag}},% keywords = [3]{MathMLintent,MathMLarg, invisibletimes, luamml_attribute, luamml_annotate, DocumentMetadata, mrow, msub, mo, mi, pre_mlist_to_hlist_filter, mathml_filter,},%, % Other words 4 (LaTeX2e) keywordstyle = [4]{\color{latex_cmd}},% keywords = [4]{frac, dfrac, section, input, listparindent, linewidth, emph, textbf, footnote, bigodot, % triangleright, diamond, circ, parleft, star, centering, ast, sptext, , href, label, ref, % mathord, mathbin, mathrel, mathopen, mathclose, parallel, mathstyle, text, textsuperscript, % Umathchardef,mathrm, mathbf, textstyle, addfontfeatures, mdlgwhtsquare, % left, right, Uleft, Uright, overleftrightarrow, overrightarrow, overline, triangle, rightangle,% rightanglesqr, operatorname, angle, measuredangle, measuredrightangle,alpha, beta, mathcal, pi, % attribute, vec, vphantom, widearc, cong, bigl, bigr, Bigl, Bigr,, biggl, biggr,Biggl, Biggr, % cup, cap, mid, setminus, infty, lvert, rvert, xcuad, Omega, % chardef, mathchardef, ell, lparen, rparen, textsc, },% % Other words 5 keywordstyle = [5]{\bfseries\color{pkgname}},% keywords = [5]{babel,spanish-mathml-intent,libertinus-otf,sourcecodepro,fourier-otf,Erewhon-Math,hyperref,},%, % Reserved words 6 (document class) keywordstyle = [6]{\bfseries\color{clsname}},% keywords = [6]{article, l3doc,},% % Reserved words 7 (spintent cmd) keywordstyle = [7]{\color{module_name}},% keywords = [7]{setspintent, spnum, spunit, spnewunit, spunitalias, spmoney, spshort, spsiglo, sptime, % spdate, spread, spdsym, spmsym, spusep, spdiv, spmul, spnM, spnD, spmcd, spmcm, spnZ, % spang, spmQ, spPoint, sprecta, sprayo, spside, spmside, spangle, spmangle, sparc, spmarc,% spPoly, spAfig, spPfig, sprad, spdiam, spcirc, spAcirc, spPcirc, spcoord, spvec, sprazon,% spProp, spnewread, spVol, spinterval, spfun, spfuncomp, spdom, sprec, spcod, % spProm, spmedia, spmediana, spmoda, spdesv, spvarz, % spcuartil, spdecil, spPerc, sprango, spriq, spmin, spmax, spFrec, spcase, spOmega, spPval, spProb, spcomp, spcomb, spfact, spabs, },% % Reserved words 8 (keyname) KEYS keywordstyle = [8]{\color{keyname}},% keywords = [8]{print-era, none, ac, dc, currency, sub-units, clear, terse, times, cdot, true, false, unknown, valid, % multislash, coma, punto, result, join-word, entero, enteros, y, error, periódico, postfix, infix, nofix,% es-paralela, es-paralelo, two-dots, old-style, slash, dividido, dividido-por, dividido-entre, por,% multiplicado-por, veces, cross, dot, asterisk, sample-arg, cifras-sep, read-cmd, % osf, nomath, mono, ScaleSF, ScaleRM, spanish, shorthands, no-text, semibold, colorlinks, pdftitle, pdfdisplaydoctitle, % pdfstartview, FitH, pdfinfo, Title, Subject, range, StylisticSet, small-caps, read-arg, píxeles, px, caudal,% read-period-sep, read-sign, notation-sym, partido-por, Numbers, Lining, OldStyle, size, read-sty, segmento,% lado, trazo, mathcat,school, rayo, semirrecta, print-sym, recto, silent, en-US, math, mathml-SE, es-CL, on, off, % setup, tagging, tagging-setup, lang, pdfversion, pdfstandard, arg, auto, read-sub, radio-sty, diameter, % circun, circulo, side, mside, usual, sub-index, odot, concept, upper-case, prefix, big, Big, bigg, Bigg, sigma, % use-dfrac, use-spnZ, wrap-parens, read-parens, read-cdot, read-slash, read-sym, set-sym, print-cdot, % money-math, autofit, suffix, result-num, alt, tag, AFghost, kind, repeat, circular, print-sty, close, card, bar, % inline, square, permutation, combination, variation, arrangement, binom, letter, mathml-AF, },% % Reserved words 9 (intent values) keywordstyle = [9]{\color{ltcmd_arg}},% keywords = [9]{number,intent,test,sample,above,below, ActualText, Span, E, cal,bfcal, silent, number, % intent, title, table, firstaid, on,XXI, setup, mathvariant, normal, },% % Reserved words 10 (signature) keywordstyle = [10]{\color{sigargs}},% keywords = [10]{een, },% % Reserved words 11 (support) keywordstyle = [11]{\color{support}},% keywords = [11]{hypersetup, setmathfont,},% % Reserved words 12 (AF ghost - MathML tags) keywordstyle = [12]{\color{other_np}},% keywords = [12]{sptmp, inner, outer, },% % Reserved words 13 (message names) keywordstyle = [13]{\color{red}},% keywords = [13]{cmd-key-unknown, cmd-key-value-unknown, env-success,% fatal-engine-requires-luatex, fatal-metadata-missing, mathcal-fallback,% mc-too-few-args, md-invalid-arg, need-math-mode, need-text-mode,% package-not-load, right-angle-sqr-fallback,% spAcirc-invalid-arg, spAfig-invalid-arg, spAfig-invalid-points,% spAfig-read-arg-key, spAfig-sym-cardinality-conflict,% spAfig-sym-letter-conflict, spFrec-invalid-sub-index, spFrec-invalid-symbol,% spPcirc-invalid-arg, spPfig-invalid-arg, spPfig-invalid-points,% spPfig-read-arg-key, spPfig-sym-cardinality-conflict,% spPfig-sym-letter-conflict, spPoint-invalid-point, spPoly-invalid-points,% spProb-invalid-arg, spabs-empty-arg, spang-invalid-arg, spangle-invalid-arg,% sparc-invalid-points, spcase-invalid-letter, spcirc-invalid-arg,% spcomb-extra-arg, spcomb-invalid-arg, spcomb-invalid-keys, spcomb-missing-arg,% spcomp-empty-arg, spcoord-invalid-arg, spdate-invalid-arg, spdiv-empty-arg,% spdom-invalid-arg, spfact-empty-arg, spfun-invalid-arg, spfuncomp-invalid-arg,% spinterval-invalid-arg, spmQ-not-integer, spmQ-signed-part,% spmQ-zero-denominator, spmangle-invalid-arg, spmarc-invalid-points,% spmoney-invalid-currency, spmoney-no-subunits, spmside-invalid-points,% spmul-empty-arg, spnZ-not-integer, spnewread-duplicate, spnewread-empty-args,% spnewunit-duplicate, spnum-has-units, spnum-no-digits, sprayo-invalid-points,% spread-empty-sym, spread-unknown-sym, sprecta-invalid-points,% spshort-unknown-abbreviation, spside-invalid-points, spsiglo-invalid-arg,% sptime-invalid-arg, spunit-inline-numeric-denominator, spunit-invalid-unit,% spunit-no-units, spunitalias-duplicate, spunitalias-invalid-expr,% spusep-empty-arg, spusep-invalid-arg, spvec-invalid-arg, stat-pos-invalid-index,% unknown-choice,% unknown-command-setup,% },% % literate {`}{{\textcolor{quote}{\textasciigrave}}}{1} literate =*{\{}{{{\color{brace}{\char`\{}}}}{1} {\}}{{{\color{brace}{\char`\}}}}}{1} {\|}{{{\color{gray}{\char`\|}}}}{1} {\$}{{{\color{math}{\char`\$}}}}{1} {\#}{{{\color{argument}{\char`\#}}}}{1} {(}{{\textcolor{gray}{(}}}{1} {)}{{\textcolor{gray}{)}}}{1} {[}{{\textcolor{bracket}{[}}}{1} {]}{{\textcolor{bracket}{]}}}{1} {,}{{\textcolor{gray}{,}}}{1} {;}{{\textcolor{gray}{;}}}{1} {.}{{\textcolor{signature}{.}}}{1} {:}{{\textcolor{signature}{:}}}{1} {\&}{{\textcolor{gray}{\&}}}{1} {>}{{\textcolor{percent}{>}}}{1} {<}{{\textcolor{percent}{<}}}{1} {*}{{\textcolor{star}{*}}}{1} {"}{{\textcolor{quote}{"}}}{1} {ua-2}{{\textcolor{keyname}{ua-2}}}{4} % UA-2 {!}{{\textcolor{MediumOrchid}{!}}}{1} {@}{{\textcolor{arroba}{@}}}{1}% {\^}{{\textcolor{keyname}{\textasciicircum}}}{1} {0}{{\textcolor{argnum}{0}}}{1} {1}{{\textcolor{argnum}{1}}}{1} {2}{{\textcolor{argnum}{2}}}{1} {3}{{\textcolor{argnum}{3}}}{1} {4}{{\textcolor{argnum}{4}}}{1} {5}{{\textcolor{argnum}{5}}}{1} {6}{{\textcolor{argnum}{6}}}{1} {7}{{\textcolor{argnum}{7}}}{1} {8}{{\textcolor{argnum}{8}}}{1} {9}{{\textcolor{argnum}{9}}}{1} {.0}{{\textcolor{argnum}{.0}}}{2}% Following is to ensure that only periods {.1}{{\textcolor{argnum}{.1}}}{2}% followed by a digit are changed. {.2}{{\textcolor{argnum}{.2}}}{2} {.3}{{\textcolor{argnum}{.3}}}{2} {.4}{{\textcolor{argnum}{.4}}}{2} {.5}{{\textcolor{argnum}{.5}}}{2} {.6}{{\textcolor{argnum}{.6}}}{2} {.7}{{\textcolor{argnum}{.7}}}{2} {.8}{{\textcolor{argnum}{.8}}}{2} {.9}{{\textcolor{argnum}{.9}}}{2} {=}{{\textcolor{module_eq}{=}}}{1},% }[keywords,tex,comments,strings]% end language % \begin{examplecode}[key=val]...\end{examplecode} \lstnewenvironment{examplecode}[1][]{% \lstset{ language = spintent-doc,% stringstyle = {\color{red}},% basicstyle = \ttfamily\small,% columns = fullflexible,% fontadjust = true,% aboveskip = 8pt,% belowskip = 5pt,% rulecolor = \color{gray!50},% framesep = \fboxsep,% framerule = \fboxrule,% xleftmargin = \dimexpr\fboxsep+\fboxrule\relax,% xrightmargin= \dimexpr\fboxsep+\fboxrule\relax,% #1,% }% close lstset }{\vspace{-\parskip}}% close examplecode % \lstinline[style=inline]|...| \lstdefinestyle{inline} { language = spintent-doc,% upquote = true,% numbersep = 1em,% escapechar =`,% basicstyle =\ttfamily,%\small numberstyle = \tiny\color{lightgray}\noaccsupp,% literate =*{\%}{{\textcolor{percent}{\%}}}{1} {i}{{\textcolor{keyname}{i}}}{1} {ii}{{\textcolor{keyname}{ii}}}{2} {iii}{{\textcolor{keyname}{iii}}}{3} {iv}{{\textcolor{keyname}{iv}}}{2} {v}{{\textcolor{keyname}{v}}}{1} {vi}{{\textcolor{keyname}{vi}}}{1} {>}{{\textcolor{mathtag}{>}}}{1} {<}{{\textcolor{mathtag}{<}}}{1} } % Set default \lstinline style \lstset{style=inline} % Configuration titleps and titlesec \settitlemarks{section} \renewpagestyle{plain}[\color{keyname}\small\sffamily]{% \setfoot{\rlap{\hskip\dimexpr-\oddsidemargin-1in\relax% \parbox{1.93\paperwidth}{\hfil\thepage\,/\,\pageref{LastPage}}}}% {}% {}% } \newpagestyle{myheader}[\color{keyname}\small\sffamily]{% \renewcommand\makeheadrule{% \rlap{\hskip\dimexpr-\oddsidemargin-1in\relax \color{rulecolor}\rule[0.3cm]{\paperwidth}{0.7cm}}\hss } \setfoot{\rlap{\hskip\dimexpr-\oddsidemargin-1in\relax% \parbox{1.93\paperwidth}{\hfil\thepage\,/\,\pageref{LastPage}}}}% {\parbox{\textwidth}{\raggedright \textcolor{gray}{\raisebox{-1pt}{\textcopyright}{}2026 by Pablo González L}}}% {}% \sethead{\llap{\raisebox{0.55cm}{\parbox{\dimexpr\oddsidemargin+1in\relax}{\makebox[0pt][l]{\hspace{15pt}\pkglogo\space\fileversion}}}}} {\raisebox{0.55cm}{\parbox{\textwidth}{\hspace*{-\oddsidemargin}\centering\small\S.\thesection\space\sectiontitle}}}% {}% } \titlecontents{section}[0mm]{}% {\bfseries\contentspush{\makebox[5mm][l]{\thecontentslabel\hfill}}}% {\hspace*{-5mm}}% numberless {\hspace{0.25em}\titlerule*[6pt]{.}\contentspage}% \titlecontents{subsection}[5mm]{}% {\contentspush{\makebox[6mm][l]{\thecontentslabel\hfill}}} {\hspace*{-11mm}}% numberless {\hspace{0.25em}\titlerule*[6pt]{.}\contentspage}% \titlecontents{subsubsection}[11mm]{}% {\contentspush{\makebox[8mm][l]{\thecontentslabel\hfill}}} {\hspace*{-19mm}}% numberless {\hspace{0.25em}\titlerule*[6pt]{.}\contentspage}% \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \selectlanguage{spanish} % \setlocalecaption{spanish}{table}{Tabla} % % \title{ % \scalebox{1.25}{\pkglogo}\\[-5pt] % {\Large\textcolor{guard_angle}{\fetamontotf{spanish parse intents}}}\\[-5pt] % \fileversion{} \quad \filedate\thanks{ % Este archivo describe la documentación para la versión \fileversion{} % revisada por última vez el % \filedate.}\\[25pt] % \author{ % \large % \raisebox{-1pt}{\textcolor{guard_angle}{\textcopyright{}2026 by Pablo González L}}\thanks{ % E-mail: \textcolor{guard_angle}{\textsf{\guillemotleft}}\email{pablgonz@educarchile.cl}\textcolor{guard_angle}{\textsf{\guillemotright}}. % }% % } % \small % \vspace*{-5pt} % \textsc{ctan}: \url{https://www.ctan.org/pkg/spanish-mathml-intent}\\ % \textcolor{gray}{\scriptsize\faIcon[regular]{github}}\,\,\,\url{https://github.com/pablgonz/spanish-mathml-intent} % \vspace*{-2cm} % } % \date{} % \maketitle % % \begin{abstract} % El paquete \mypkg*[no-index]{spanish-mathml-intent} agrega el atributo % \emph{intent} de \mynvmath{MathML} a las matemáticas escritas en español % con \hologo{LuaLaTeX}. El atributo le indica a un lector de pantalla qué % concepto es una expresión y cómo se debe leer, de modo que % \mynvmath{NVDA} con \mynvmath{MathCAT} lea correctamente las fórmulas en % documentos \texttt{PDF} accesibles (\emph{tagged} \texttt{PDF}). Sus % comandos escriben y etiquetan números, unidades, dinero, fechas y horas, % geometría, álgebra y estadística, cada uno con la lectura en español que le % corresponde. Está pensado para docentes de educación primaria y secundaria. % \end{abstract} % % \tableofcontents % % \section*{Motivación y agradecimientos} % % Desde que desarrollé \mypkg{scontents}\cite{scontents} en 2019 y, más % tarde, \mypkg{enumext}\cite{enumext} en 2024, me he ido interesando % cada vez más por la accesibilidad en los documentos \texttt{PDF}. Al % principio era un tema prácticamente desconocido para mí, pero a medida % que fui aprendiendo sobre él comprendí la importancia que tiene, % especialmente en el ámbito educativo. % % % El equipo del proyecto de \hologo{LaTeX}\cite{tagged-project} ha % realizado un trabajo extraordinario en este campo. Hoy es posible % generar documentos etiquetados, validarlos con herramientas como % \href{https://dev.verapdf-rest.duallab.com}{\texttt{veraPDF}}\cite{verapdf} % o inspeccionar su estructura con % \href{https://ngpdf.com}{\texttt{ngpdf}}\cite{ngpdf}. Sin embargo, % siempre me quedó una duda: cuando un estudiante utiliza \mynvmath{NVDA}\cite{nvda} % junto con \mynvmath{MathCAT}\cite{mathcat}, ¿realmente escucha lo que % el o la docente quiso expresar? % % Basta con generar un documento accesible siguiendo las recomendaciones % de \href{https://latex3.github.io/tagging-project/}{\texttt{The LaTeX % Tagged PDF Project}} y escucharlo con \mynvmath{NVDA/MathCAT} para darse % cuenta de que, en muchos casos, la \emph{verbalización} no coincide con lo % que uno espera. Esto no resulta extraño: las reglas del español, y en % particular el lenguaje matemático, dependen en gran medida del contexto % en que se utilizan. % % El objetivo de \mypkg*[no-index]{spintent} nace precisamente de esta necesidad: % proporcionar herramientas para que el o la docente pueda indicar % explícitamente el contexto de determinadas expresiones matemáticas % desde el propio código fuente de \hologo{LaTeX}, de modo que el % documento \texttt{PDF} resultante no solo sea \emph{accesible}, sino que % también produzca una \emph{verbalización más natural} y adecuada para el % estudiante al utilizar \mynvmath{NVDA/MathCAT}. % % El nuevo estándar \texttt{PDF} |2.0|\cite{pdf20,pdf20-impact,pdf20-errata}, % las especificaciones de \texttt{WTPDF}\cite{wtpdf} y la guía más reciente de la % \texttt{PDF} Association, «Best Practice Guide: Math in % \texttt{PDF}»\cite{bpg-math}, % coinciden en que el camino hacia una accesibilidad matemática de calidad % pasa por el uso de \mynvmath{MathML}. En ese contexto, % \hologo{LuaLaTeX} ofrece una plataforma especialmente adecuada para % incorporar esta tecnología. % % Debo agradecer a todo el equipo de \hologo{LaTeX} que desarrolló el proyecto % de \emph{tagged} \texttt{PDF} y a los diversos miembros de la comunidad % \href{https://tex.stackexchange.com}{TeX-SX} que han respondido mis muchas % dudas en el chat sobre este tema; destaco entre ellos a David Carlisle, % Ulrike Fischer, Clea F. Rees y Max Chernoff, quien me mostró el camino de Lua. % % Un agradecimiento especial a mi esposa Javiera, quien me acompañó % incontables horas en las escuchas de \mynvmath{NVDA/MathCAT} durante el % desarrollo de \mypkg*[no-index]{spintent}. % % % \thispagestyle{plain} % % \section*{Licencia y requerimientos} % % El paquete \mypkg*[no-index]{spintent} carga y requiere el paquete \mypkg{unicode-math}\cite{unicode-math} % y que se ejecute bajo \hologo{LuaLaTeX}, por lo que es necesario contar con % una distribución moderna de \hologo{TeX} como \hologo{TeX} Live 2026. Ha sido probado con la clase estándar % \myclass{article} proporcionada por \hologo{LaTeX} en tamaño \mydim{10pt}, usando % \mynvmath{NVDA} 2026.2 y Adobe Acrobat Reader\cite{acrobat} actualizado 2026; además se utilizó % \href{https://daisy.github.io/MathCATDemo/}{MathCATDemo}\cite{mathcatdemo}. % No se ha probado con otros lectores de pantalla. % % Para profundizar en el etiquetado de documentos, es decir, |\DocumentMetadata|, % \mykey{tagging} y \mykey{tagging-setup}, consulte la documentación de % \href{https://latex3.github.io/tagging-project/documentation/}{\texttt{The LaTeX % Tagged PDF Project}}\cite{tagged-project,userguide}, que está en inglés. % Quien da sus primeros pasos en \hologo{LaTeX} puede apoyarse en % \href{https://www.learnlatex.org/es/}{Learn \hologo{LaTeX}}\cite{learnlatex}, un sitio % respaldado por el equipo de \hologo{LaTeX} con lecciones breves y ejemplos que % se pueden probar en línea; como referencia general sirve el manual no oficial % de \hologo{LaTeX2e}\cite{latexref}. % % Se otorga permiso para copiar, distribuir y/o modificar este paquete bajo % los términos de \LaTeX{} Project Public License (\textsc{lppl}), versión % 1.3 o posterior (\url{https://www.latex-project.org/lppl.txt}). El paquete % tiene la condición de mantenido al estilo \emph{«A Work in Progress\footnote{% % \emph{A Work in Progress} es un álbum en vivo de Neil Peart % ($\ast$~1952--\textdied~2020), baterista y principal letrista de Rush. % El nombre no pretende formar parte de la terminología de la % \textsc{lppl}; simplemente describe el estado permanente de este paquete % mientras sigo aprendiendo sobre \emph{accesibilidad} en \texttt{PDF}.}»}. % % \begin{important}* % El requerimiento mínimo es \hologo{LaTeX}\cite{latex2e} release 2026-11-01. % \end{important} % % Este paquete solo se puede utilizar con \hologo{LuaLaTeX} y % \emph{tagged} \texttt{PDF} activo. Está presente en \hologo{TeX} Live, para % instalarlo utilice el gestor de paquetes con |tlmgr install| \texttt{spanish-mathml-intent}. % Si desea probar la versión de desarrollo, que puede contener cambios aún no % publicados, clone el repositorio y ejecute |l3build install|: % % \iffalse %<*example> % \fi \begin{examplecode}[frame=single,language={}] git clone https://github.com/pablgonz/spanish-mathml-intent.git cd spanish-mathml-intent l3build install \end{examplecode} % \iffalse % % \fi % % \pagestyle{myheader} % % \section{Descripción} % % El nombre \mypkg*[no-index]{spanish-mathml-intent} resume lo que hace el % paquete: \emph{«Spanish»} por el idioma, \emph{«MathML»} por el lenguaje en % que se escriben las fórmulas accesibles e \emph{«intent»} por el atributo % del mismo nombre que indica cómo se lee cada fórmula. El nombre corto % original, \emph{«Spanish Parse Intent»}, sobrevive en el prefijo \texttt{sp} % de los comandos y en el módulo interno \texttt{spintent}. El paquete está % pensado para docentes de educación primaria y secundaria. % % Un documento \emph{tagged} \texttt{PDF}, en español \emph{«PDF etiquetado»}, % es un \texttt{PDF} que, además de componer la página, guarda su estructura: % qué es un título, un párrafo, una lista, una tabla o una fórmula, y en qué % orden se leen. Los lectores de pantalla, como \mynvmath{NVDA}, usan esa % información para leer el documento a estudiantes ciegos o con baja visión; % sin ella solo disponen del texto suelto, sin saber qué es cada parte. % Cómo activarlo se explica en la~\S\ref{subsec:carga} y un documento % completo en la~\S\ref{subsec:primer-documento}. % % El paquete está pensado para crear \emph{«apuntes»}, \emph{«hojas de ejercicios»} % y \emph{«clases escritas»} enfocadas en el estudiante. Solo un docente sabe cuál es % el nivel de aprendizaje del estudiante. El paquete intenta sobrescribir % muchas de las reglas dictadas por la heurística de \mynvmath{NVDA/MathCAT} % teniendo esto presente. % % \begin{important}* % El paquete \mypkg*[no-index]{spintent} usa y abusa del atributo |:intent| de \mynvmath{MathML} % por medio del comando |\MathMLintent| definido en \mypkg{latex-lab-mathintent}\cite{latexlab}, % un encapsulado de |\luamml_attribute:een| provista por \mypkg{luamml}\cite{luamml}. ¡Gracias Marcel! % \end{important} % % \subsection{Carga del paquete}\label{subsec:carga} % % El paquete \mypkg*[no-index]{spintent} trabaja solo con \hologo{LuaLaTeX} y % \emph{tagged} \texttt{PDF} activo usando |tagging=on| dentro del comando % |\DocumentMetadata| al inicio del archivo. % Las llaves \mymeta{keys} aceptadas por |\DocumentMetadata| y en % general para el código de \emph{tagged} \texttt{PDF} están documentadas % en los paquetes \mypkg{tagpdf}\cite{tagpdf} y \mypkg{documentmetadata-support}\cite{latexlab}, % es deber del usuario revisar la documentación asociada a ellos. % % \iffalse %<*example> % \fi \begin{examplecode}[frame=single] \DocumentMetadata { lang=es-CL, pdfversion=2.0, pdfstandard=ua-2, tagging=on, tagging-setup={math/setup={mathml-SE}}, } \documentclass{article} \usepackage[spanish]{babel} \usepackage{spanish-mathml-intent} \end{examplecode} % \iffalse % % \fi % % \subsection{El paquete \texttt{babel}} % % El paquete \mypkg{babel-spanish}\cite{babel-spanish} está marcado como % «parcialmente compatible» para \emph{tagged} \texttt{PDF}, pero es % absolutamente necesario para la creación de documentos en español. % En caso de encontrar problemas de compatibilidad con otros paquetes % se puede desactivar usando: % % \iffalse %<*example> % \fi \begin{examplecode}[frame=single] \usepackage[spanish,shorthands=off]{babel} \end{examplecode} % \iffalse % % \fi % % Muchas de las «shorthands» provistas por \mypkg{babel-spanish} hoy no % son necesarias, más allá de |\sptext| para las abreviaturas y ordinales, % el resto son solo atajos de teclas para escritura que hoy en día no se ocupan. % El paquete \mypkg*[no-index]{spintent} provee el comando |\spshort| (\S\ref{subsec:spshort}) % que cubre estos casos, de esta manera podemos cargar \mypkg{babel}\cite{babel} % con todas sus funcionalidades sin problemas. % % \subsection{Fuentes tipográficas}\label{subsec:fuentes} % % Como \mypkg*[no-index]{spintent} es un paquete \hologo{LuaLaTeX} debemos % distinguir entre las fuentes tipográficas \texttt{OpenType/TrueType} % versus las fuentes de \texttt{8bit} estándar usadas por % \hologo{LaTeX} para el \emph{modo texto} y el \emph{modo matemático}. % Para evitar problemas con \emph{tagged} \texttt{PDF} preferiremos no % combinar fuentes \texttt{OpenType/TrueType} con fuentes \texttt{8bit} y % trabajaremos solo con fuentes \texttt{OpenType/TrueType} que tengan % soporte para matemáticas. % % La carga de las fuentes tipográficas para \hologo{LuaLaTeX} se % maneja por medio del paquete \mypkg{fontspec}\cite{fontspec}, si no se especifica % ninguna se usarán las fuentes \texttt{Latin Modern} para el \emph{modo texto} y % \texttt{Latin Modern Math} para el \emph{modo matemático}. % % El tipo de fuente tipográfica escogida es preferencia de cada usuario, en lo % personal utilizo la fuente \texttt{Libertinus} para el \emph{modo % texto} por medio del paquete % \mypkg{libertinus-otf}\cite{libertinus-otf} y la fuente % \texttt{Erewhon-Math} para el modo matemático cargada por el paquete % \mypkg{fourier-otf}\cite{erewhon-math}. Adicionalmente cargo la fuente \texttt{Source Code Pro} % para el texto tipo \texttt{«máquina de escribir»} por medio del paquete % \mypkg{sourcecodepro}\cite{sourcecodepro}. % % \iffalse %<*example> % \fi \begin{examplecode}[frame=single] % \DocumentMetadata{...} \documentclass{article} \usepackage[spanish]{babel} \usepackage[osf,nomath,mono=false]{libertinus-otf} \usepackage[osf,semibold]{sourcecodepro} \usepackage[no-text]{fourier-otf} \usepackage{spanish-mathml-intent} \end{examplecode} % \iffalse % % \fi % % \subsubsection{La llave \texttt{mathcal}} % % Algunos comandos, como |\spAfig| y |\spPfig|, usan % |\mathcal| en su salida. Por defecto, con \mykey[usepackage]{mathcal}|=true|, el paquete % \mypkg*[no-index]{spintent} busca la fuente \texttt{NewCMMath-Regular.otf}, % parte del paquete \mypkg[no-index]{newcomputermodern}\cite{newcomputermodern}, para imprimir esas % letras caligráficas con el estilo clásico; se recomienda tenerla instalada. % Si no la encuentra, avisa con un \enquote{warning} y usa el % |\mathcal| de la fuente matemática cargada. Con % \mykey{mathcal}|=false| se usa siempre el |\mathcal| de la % fuente matemática cargada, y la apariencia de la letra cambia. La misma % fuente se usa para el símbolo del ángulo recto de |\spangle| % (v.\,pág.\,\pageref{subsec:spangle}). La llave se pasa al cargar el % paquete: % % \iffalse %<*example> % \fi \begin{examplecode}[frame=single] \usepackage[mathcal=false]{spanish-mathml-intent} \end{examplecode} % \iffalse % % \fi % % \subsection{El paquete \texttt{hyperref}} % % El paquete \mypkg{hyperref}\cite{hyperref} es \emph{«casi obligatorio»} % cuando se crean documentos \emph{tagged} \texttt{PDF}, se encarga de las % referencias cruzadas |\label| y |\ref|, las notas al pie |\footnote|, % los hipervínculos |\href| y muchos otros elementos como marcadores, índices % y metadatos. % % \iffalse %<*example> % \fi \begin{examplecode}[frame=single] \DocumentMetadata { lang=es-CL, pdfversion=2.0, pdfstandard=ua-2, tagging=on, tagging-setup={math/setup={mathml-SE}}, } \documentclass{article} \usepackage[spanish]{babel} \usepackage[osf,nomath,mono=false]{libertinus-otf} \usepackage[osf,semibold]{sourcecodepro} \usepackage[no-text]{fourier-otf} \setmathfont{Erewhon-Math}[range={cal,bfcal},StylisticSet=1] \usepackage{spanish-mathml-intent, hyperref} \hypersetup { colorlinks = true, pdfstartview = FitH, pdfdisplaydoctitle = true, pdftitle = {Test para el paquete spintent}, pdfinfo = { Title = {Test}, Subject = {spintent}, }, } \end{examplecode} % \iffalse % % \fi % % \subsection{Interfaz de usuario}\label{subsec:interfaz} % % La interfaz de usuario de \mypkg*[no-index]{spintent} está formada solo por % \emph{comandos}: el comando de configuración |\setspintent| % (\S\ref{sec:setspintent}) y 73 utilidades para escribir y verbalizar % matemática escolar. El paquete no define entornos, contadores ni % dimensiones públicas. % % \subsubsection{Lo que ofrece el paquete} % % Cada utilidad hace dos cosas a la vez: imprime la expresión en la salida % \texttt{PDF} con el formato correcto y establece cómo la verbalizará % \mynvmath{NVDA/MathCAT}, para que la lea como la diría un docente y no % según la heurística general de \mynvmath{NVDA/MathCAT}. % Las utilidades que reciben llaves \myoarg{key \textnormal{\textcolor{gray}{=}} val} % las toman de tres niveles, de menor a mayor prioridad: los valores por % defecto del paquete, los establecidos de forma global con |\setspintent| y % los del argumento opcional de cada comando. Un argumento que no cumple la % forma documentada devuelve un \enquote{error} y se detiene la compilación % del documento. % % Los cuatro grandes (\S\ref{subsec:spnum}, \S\ref{subsec:spunit}, \S\ref{subsec:spmoney} y % \S\ref{subsec:spshort}) son la base del paquete: |\spnum| escribe % números, |\spunit| unidades de medida, |\spmoney| cifras con moneda y % |\spshort| abreviaturas, acrónimos y ordinales. Los comandos |\spnewunit| y % |\spunitalias| amplían el diccionario de |\spunit|. Muchas utilidades de los % demás ejes procesan sus argumentos numéricos bajo |\spnum| y |\spunit|, por % ejemplo |\spdiv|, |\spmul|, |\spcoord| y |\spang|. % % \subsubsection{Lo que ofrece cada eje} % % El resto de las utilidades se agrupan por eje temático, en el mismo orden en % que se describen en este documento: % % \begin{itemize}[left=0pt,itemsep=\parskip] % \item \emph{Generales} (\S\ref{sec:utilidades}): |\spsiglo| escribe % siglos en números arábigos o romanos, |\sptime| horas y |\spdate| fechas. % \item \emph{Símbolos} (\S\ref{sec:simbolos}): |\spnewread| y |\spread| % registran y usan la lectura de un símbolo, |\spusep| escribe paréntesis, % corchetes y llaves sueltos, y |\spdsym| y |\spmsym| fijan cómo se % imprime y se lee el símbolo de división y el de multiplicación. % \item \emph{Números} (\S\ref{sec:numeros}): operaciones con |\spdiv| y % |\spmul|, divisores y múltiplos con |\spnD| y |\spnM|, máximo común % divisor y mínimo común múltiplo con |\spmcd| y |\spmcm|, números enteros % con |\spnZ|, números mixtos con |\spmQ|, razones y proporciones con % |\sprazon| y |\spProp|. % \item \emph{Geometría} (\S\ref{sec:geometria}): puntos y coordenadas % con |\spPoint| y |\spcoord|, vectores con |\spvec|, rectas, rayos y lados % con |\sprecta|, |\sprayo|, |\spside| y |\spmside|, ángulos con |\spangle|, % |\spmangle| y |\spang|, arcos con |\sparc| y |\spmarc|, polígonos % con |\spPoly|, áreas y perímetros de figuras con |\spAfig| y |\spPfig|, % circunferencias con |\spcirc|, |\spAcirc| y |\spPcirc|, radio y diámetro % con |\sprad| y |\spdiam|, y volumen con |\spVol|. % \item \emph{Álgebra} (\S\ref{sec:algebra}): intervalos con |\spinterval|, % funciones con |\spfun|, dominio, recorrido y codominio con |\spdom|, % |\sprec| y |\spcod|, composición de funciones con |\spfuncomp| y valor % absoluto con |\spabs|. % \item \emph{Estadística} (\S\ref{sec:estadistica}): promedio con |\spProm|, % medidas de tendencia central y de dispersión con |\spmedia|, % |\spmediana|, |\spmoda|, |\spdesv|, |\spvarz|, |\sprango|, |\spriq|, % |\spmin| y |\spmax|, cuartiles, deciles y percentiles con |\spcuartil|, % |\spdecil| y |\spPerc|, frecuencias con |\spFrec|, probabilidad con % |\spcase|, |\spOmega|, |\spPval|, |\spProb| y |\spcomp|, y conteo con % |\spcomb| y |\spfact|. % \end{itemize} % % \subsubsection{Reglas para argumentos}\label{subsubsec:reglas-args} % % Casi todas las utilidades envían su argumento a \myluamod{}, que lo lee como % \emph{texto plano} y reconoce números, letras, signos y la sintaxis propia de % cada comando. Por eso, salvo lo que se documente en cada comando, el % \mymarg{argumento} NO puede contener comandos de formato o de estilo, con o % sin argumentos, como |\textstyle|, |\mathrm{#1}| o |\mathbf{#1}|, tal como % se advierte en |\spnum|, |\spunit| y |\spmoney|. Un argumento que no cumple % devuelve un \enquote{error} y se detiene la compilación del documento. % % Hacen excepción las utilidades que tipografían su argumento directamente, % sin procesamiento semántico adicional, y por eso aceptan cualquier expresión % matemática: |\sprazon|, |\spProp|, |\spPval|, |\spfact|, |\spProm| y % |\spabs|. En |\spinterval|, los extremos que no son numéricos, como una % letra, un subíndice, una fracción o una raíz, también se imprimen tal cual. % % \subsubsection{Consulta rápida}\label{subsubsec:consulta-rapida} % % La tabla~\ref{tab:consulta-rapida} reúne, para lo que un docente escribe con más % frecuencia, el comando que corresponde y la página donde se describe, con % su sintaxis completa y sus llaves. % % \begingroup\small % \setlength{\LTleft}{\fill}\setlength{\LTright}{\fill} % \begin{longtable}{@{}p{5cm} l r@{}} % \caption{Consulta rápida: qué quiero escribir y qué comando usar} \label{tab:consulta-rapida} \\ % \toprule % \textbf{Quiero escribir} & \textbf{Uso} & \multicolumn{1}{c}{\textbf{Página}} \\ % \midrule % \endfirsthead % \caption[]{\emph{(Continuación - Consulta rápida)}} \\ % \toprule % \textbf{Quiero escribir} & \textbf{Uso} & \multicolumn{1}{c}{\textbf{Página}} \\ % \midrule % \endhead % \midrule % \multicolumn{3}{r}{\emph{Continúa en la siguiente página...}} \\ % \endfoot % \bottomrule % \endlastfoot % \multicolumn{3}{c}{\textbf{Generales}} \\ % \midrule % un siglo & |\spsiglo{XXI}| & \pageref{subsec:spsiglo} \\ % una hora & |$\sptime{23:45}$| & \pageref{subsec:sptime} \\ % una fecha & |$\spdate{1981-01-02}$| & \pageref{subsec:spdate} \\ % \midrule % \multicolumn{3}{c}{\textbf{Los cuatro grandes}} \\ % \midrule % un número & |$\spnum{3,14}$| & \pageref{subsec:spnum} \\ % una medida & |$\spunit{23 m}$| & \pageref{subsec:spunit} \\ % una cantidad de dinero & |$\spmoney{500}$| & \pageref{subsec:spmoney} \\ % una abreviatura, sigla u ordinal & |\spshort{prof.a}| & \pageref{subsec:spshort} \\ % \midrule % \multicolumn{3}{c}{\textbf{Símbolos}} \\ % \midrule % la lectura de un símbolo & |$\spread{\cong}$| & \pageref{subsec:spread} \\ % \midrule % \multicolumn{3}{c}{\textbf{Números}} \\ % \midrule % una división & |$\spdiv{20}{26}$| & \pageref{subsec:spdiv} \\ % un máximo común divisor & |$\spmcd(2, 3, 6)$| & \pageref{subsec:spmcd} \\ % un número entero & |$\spnZ{-23}$| & \pageref{subsec:spnZ} \\ % una razón & |$\sprazon{3}{4}$| & \pageref{subsec:sprazon} \\ % una proporción & |$\spProp{3}{4}{6}{8}$| & \pageref{subsec:spProp} \\ % un número mixto & |$\spmQ{2}{5}{7}$| & \pageref{subsec:spmQ} \\ % \midrule % \multicolumn{3}{c}{\textbf{Geometría}} \\ % \midrule % un punto & |$\spPoint{A}$| & \pageref{subsec:spPoint} \\ % unas coordenadas & |$\spcoord{-3, 4}$| & \pageref{subsec:spcoord} \\ % un vector & |$\spvec{AB}$| & \pageref{subsec:spvec} \\ % una recta & |$\sprecta{AB}$| & \pageref{subsec:sprecta} \\ % un ángulo & |$\spangle{A}$| & \pageref{subsec:spangle} \\ % un arco & |$\sparc{AB}$| & \pageref{subsec:sparc} \\ % un polígono & |$\spPoly{ABC}$| & \pageref{subsec:spPoly} \\ % \midrule % \multicolumn{3}{c}{\textbf{Álgebra}} \\ % \midrule % un intervalo & |$\spinterval{(1,5)}$| & \pageref{subsec:spinterval} \\ % una función & |$\spfun{f(x)}$| & \pageref{subsec:spfun} \\ % un dominio & |$\spdom{f}$| & \pageref{subsec:spdom} \\ % un valor absoluto & |$\spabs{-5}$| & \pageref{subsec:spabs} \\ % \midrule % \multicolumn{3}{c}{\textbf{Estadística}} \\ % \midrule % un promedio & |$\spProm{x}$| & \pageref{subsec:spProm} \\ % un cuartil & |$\spcuartil{1}$| & \pageref{subsec:spcuartil} \\ % una probabilidad & |$\spProb{A \cap B}$| & \pageref{subsec:spProb} \\ % una combinatoria & |$\spcomb{5,2}$| & \pageref{subsec:spcomb} \\ % un factorial & |$\spfact{5}$| & \pageref{subsec:spfact} \\ % \end{longtable} % \endgroup % % \subsection{Un primer documento}\label{subsec:primer-documento} % % El siguiente documento está completo: se puede copiar, guardar con % extensión \texttt{.tex} y compilar con \hologo{LuaLaTeX}. Usa comandos de % varios ejes temáticos de este documento y los metadatos mínimos que necesita % un \emph{tagged} \texttt{PDF}. % % \iffalse %<*ignore> % \fi \begin{examplecode}[frame=single] \DocumentMetadata { lang=es-CL, pdfversion=2.0, pdfstandard=ua-2, tagging=on, tagging-setup={math/setup={mathml-SE}}, } \documentclass{article} \usepackage[spanish]{babel} \usepackage{spanish-mathml-intent, hyperref} \hypersetup { pdfdisplaydoctitle = true, pdftitle = {Guía de ejercicios}, } \begin{document} \section*{Guía de ejercicios} El número $\spnum{3,14}$ es una aproximación de $\pi$. El pasillo del colegio mide $\spunit{23 m}$ de largo y un cuaderno cuesta $\spmoney{500}$. Dibuje el triángulo $\spPoly{ABC}$ y marque el ángulo $\spangle{A}$. Sea $\spfun{f(x)}$ una función y sean $A$ y $B$ dos sucesos: calcule $\spProb{A \cap B}$. Material preparado por la \spshort{prof.a} Pérez. \end{document} \end{examplecode} % \iffalse % % \fi % % La tabla~\ref{tab:primer-documento} muestra qué se imprime en el \texttt{PDF} % y qué lee \mynvmath{NVDA/MathCAT} en cada caso; los valores salen de los % ejemplos documentados en las secciones de cada comando. % % \begingroup\small % \setlength{\LTleft}{\fill}\setlength{\LTright}{\fill} % \begin{longtable}{@{}l l p{5cm}@{}} % \caption{Lo que escribe el docente y lo que escucha el estudiante} \label{tab:primer-documento} \\ % \toprule % \textbf{Entrada} & \textbf{Salida PDF} & \multicolumn{1}{c}{\textbf{Lectura NVDA}} \\ % \midrule % \endfirsthead % \caption[]{\emph{(Continuación - Lo que escribe el docente y lo que escucha el estudiante)}} \\ % \toprule % \textbf{Entrada} & \textbf{Salida PDF} & \multicolumn{1}{c}{\textbf{Lectura NVDA}} \\ % \midrule % \endhead % \midrule % \multicolumn{3}{r}{\emph{Continúa en la siguiente página...}} \\ % \endfoot % \bottomrule % \endlastfoot % |$\spnum{3,14}$| & $3{,}14$ & \emph{«tres coma catorce»} \\ % |$\spunit{23 m}$| & $23\,\mathrm{m}$ & \emph{«veintitrés metros»} \\ % |$\spmoney{500}$| & $\$500$ & \emph{«quinientos pesos»} \\ % |\spshort{prof.a}| & Prof.$^{\text{a}}$ & \emph{«profesora»} \\ % |$\spPoly{ABC}$| & $\triangle ABC$ & \emph{«triángulo A B C»} \\ % |$\spfun{f(x)}$| & $f(x)$ & \emph{«f de x»} \\ % |$\spProb{A \cap B}$| & $P(A \cap B)$ & \emph{«probabilidad de A y B»} \\ % \end{longtable} % \endgroup % % \subsection{Cómo comprobar el resultado}\label{subsec:comprobar} % % Hay dos formas de comprobar un documento: \emph{escucharlo} y % \emph{validar su estructura}. La primera dice si la lectura es la que se % quiso; la segunda, si el \texttt{PDF} está bien etiquetado. % % \subsubsection{Escuchar la lectura} % % Abra el \texttt{PDF} con \mynvmath{NVDA}\cite{nvda} y % \mynvmath{MathCAT}\cite{mathcat} instalados, recorra el texto hasta la % fórmula y compare lo que escucha con la columna «Lectura NVDA» de la % tabla~\ref{tab:primer-documento} y de las secciones de cada comando. Si una % lectura no es la esperada, vea la~\S\ref{subsec:si-algo-falla}. % % \subsubsection{Validar la estructura} % % Las siguientes órdenes, que se escriben en una terminal y no dentro del % documento, extraen las etiquetas del \texttt{PDF} y las comprueban con % \texttt{show-pdf-tags} y \texttt{rnv-wrapp}, y validan la norma % \texttt{PDF/UA-2} con \texttt{veraPDF}\cite{verapdf}: % % \iffalse %<*ignore> % \fi \begin{examplecode}[frame=single,language={}] show-pdf-tags --xml archivo.pdf | rnv-wrapp verapdf --flavour ua2 --format text archivo.pdf \end{examplecode} % \iffalse % % \fi % % \texttt{show-pdf-tags} está en \href{https://ctan.org/pkg/show-pdf-tags}{\texttt{CTAN}}; si su % distribución no lo incluye, instálelo con el gestor de paquetes. % % Si tiene instaladas estas herramientas, la orden |spitool -c archivo.pdf| % ejecuta ambas comprobaciones de una vez (\S\ref{sec:af}). % % \subsubsection{Instalar \texttt{rnv-wrapp} y \texttt{veraPDF}} % % Ambas herramientas se instalan con los scripts del repositorio % \href{https://github.com/pablgonz/rnv-vera-pdf-cli}{\texttt{rnv-vera-pdf-cli}}. % \texttt{veraPDF} necesita tener \texttt{Java} instalado. En \texttt{Linux} se % ejecuta, en una terminal: % % \iffalse %<*ignore> % \fi \begin{examplecode}[frame=single,language={},breaklines=true] curl -sSL https://raw.githubusercontent.com/pablgonz/rnv-vera-pdf-cli/main/install-rnv.sh | bash -s -- --unattended curl -sSL https://raw.githubusercontent.com/pablgonz/rnv-vera-pdf-cli/main/install-verapdf.sh | bash -s -- --unattended \end{examplecode} % \iffalse % % \fi % % En \texttt{Windows}, en \texttt{PowerShell}: % % \iffalse %<*ignore> % \fi \begin{examplecode}[frame=single,language={},breaklines=true] Invoke-WebRequest -Uri "https://raw.githubusercontent.com/pablgonz/rnv-vera-pdf-cli/main/install-rnv.ps1" -OutFile install-rnv.ps1 .\install-rnv.ps1 --unattended Invoke-WebRequest -Uri "https://raw.githubusercontent.com/pablgonz/rnv-vera-pdf-cli/main/install-verapdf.ps1" -OutFile install-verapdf.ps1 .\install-verapdf.ps1 --unattended \end{examplecode} % \iffalse % % \fi % % La opción |--unattended| agrega las herramientas al \texttt{PATH}; después % de instalar, abra una terminal nueva para que el cambio tenga efecto. El % script de \texttt{veraPDF} también sirve en \texttt{macOS}; para % \texttt{rnv-wrapp} el repositorio ofrece solo \texttt{Linux} y \texttt{Windows}. % % % \subsection{Si algo no suena como esperaba}\label{subsec:si-algo-falla} % % Revise, en este orden: % % \begin{enumerate}[left=0pt,itemsep=\parskip] % \item \emph{El mensaje de error.} Si la compilación se detiene con un % \enquote{error}, el mensaje indica el comando y el argumento que no % cumple la forma documentada (\S\ref{sec:setspintent}). % \item \emph{La llave} \mykey{read-cmd}. Varios comandos ofrecen más de una % lectura; |$\spPoint[read-cmd=school]{A_{1}}$| verbaliza % «sub uno». Los valores posibles se describen en la sección de cada comando. % \item \emph{La llave} \mykey{read-arg}. Reemplaza la lectura del argumento, % |$\spread[read-arg={es-paralela-a}]{\parallel}$|; su valor siempre % va entre llaves. % \item \emph{La llave} \mykey{silent}. Con \mykey{silent}|=true| se silencia la lectura % sin cambiar lo que se imprime; la validación del argumento se mantiene. % \item \emph{La configuración global.} Se puede fijar para todo % el documento, |\setspintent{spPoint}{read-cmd=school}|; la llave dada % en el argumento opcional de un comando tiene prioridad sobre % ella (\S\ref{sec:setspintent}). % \end{enumerate} % % \section{El comando \cs[no-index]{setspintent}}\label{sec:setspintent} % % \begin{function}{\setspintent} % \begin{syntax} % |\setspintent|\mymarg{comando}\mymarg{key-uno \textnormal{\textcolor{gray}{=}} valor\textnormal{\textcolor{gray}{,}} key-dos \textnormal{\textcolor{gray}{=}} valor\textnormal{\textcolor{gray}{,}} key-tres \textnormal{\textcolor{gray}{=}} valor\textnormal{\textcolor{gray}{,}} \dots} % \end{syntax} % El comando \ics*{setspintent} establece las llaves \mymeta{keys} de % forma global para los comandos provistos por \mypkg*[no-index]{spintent}. Puede % utilizarse tanto en el preámbulo como en el cuerpo del documento % tantas veces como se desee. % \end{function} % % Las llaves \mymeta{keys} definidas en el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val} de los comandos % tienen prioridad sobre las establecidas por este, anulando las % configuraciones globales de |\setspintent|. % % Muchas de las utilidades que provee el paquete \mypkg*[no-index]{spintent} implementan % el sistema \myoarg{key \textnormal{\textcolor{gray}{=}} val} y están pensadas % para ser utilizadas en la generación de \emph{«hojas de ejercicios»} o \emph{«clases % escritas»}. Se recomienda usar |\setspintent| y establecerlas de manera % global para cada documento, para mantener la coherencia en la verbalización % de \mynvmath{NVDA/MathCAT} a lo largo del mismo. % % El sistema \myoarg{key \textnormal{\textcolor{gray}{=}} val} utilizado % por \mypkg*[no-index]{spintent} se implementa usando el módulo \mypkg{l3keys} % de \mypkg{expl3}\cite{expl3,inter3}. Por diseño, las llaves que reciben un % \emph{valor} después del signo `|=|' NO aceptan \emph{valores vacíos} % y es obligatorio pasar uno válido; las llaves documentadas como \mymeta{valor prohibido} % no reciben \emph{ningún valor}. % En caso de omitir un \emph{valor} válido o pasar un \emph{valor vacío} % a una llave que acepte \emph{valor} o pasar un \emph{valor} a una llave % del tipo \mymeta{valor prohibido} se devuelve un \enquote{error} % y se detiene la compilación del documento. % % \begin{important}* % En el modo \texttt{mathml-AF} (\S\ref{sec:af}), dos fórmulas con % \emph{exactamente la misma fuente} reciben el mismo \mynvmath{MathML} % adjunto, aunque una de ellas esté dentro de un grupo con otra configuración % de |\setspintent|. % % Por ejemplo, tras |{\setspintent{spmedia}{sub-index={x}}$\spmedia$}| la % fórmula siguiente |$\spmedia$| se verbaliza igual que la primera. Esto se % debe a que el núcleo de \hologo{LaTeX} asocia el \mynvmath{MathML} por el % \emph{hash} de la fuente y usa el primer fragmento que coincide; no ocurre % en el modo \texttt{mathml-SE}. Para evitarlo, haga distinta la fuente de % ambas fórmulas, por ejemplo pasando las llaves en el argumento opcional % de cada comando. % \end{important} % % \section{The big four} % % El paquete \mypkg*[no-index]{spintent} provee cuatro grandes comandos % \emph{«the big four»}\footnote{Los \emph{«cuatro grandes»} del % Thrash Metal: Metallica, Megadeth, Slayer y Anthrax, que me acompañan % mientras desarrollo \mypkg*[no-index]{spintent}.}: % |\spnum| para el manejo de números, |\spunit| para el manejo % de unidades, |\spmoney| para el manejo de dinero y |\spshort| % para el manejo de abreviaturas, acrónimos y números ordinales. % % \begin{important}* % También se incluyen las utilidades |\spnewunit| y |\spunitalias| asociadas % al comando |\spunit|. % \end{important} % % \subsection{El comando \cs[no-index]{spnum}}\label{subsec:spnum} % % \begin{function}{\spnum} % \begin{syntax} % |\spnum|\mymarg{número}\hfill|\spnum|\mymarg{número\textnormal{\textcolor{red}{{,}}}número}\hfill|\spnum|\mymarg{número\textnormal{\textcolor{red}{{.}}}número}\hfill|\spnum|\mymarg{número\textnormal{\textcolor{red}{{;}}}número}\par\noindent % |\spnum|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{número\textnormal{\textcolor{red}{{,}}}número\textnormal{\textcolor{red}{{;}}}número}\hfill|\spnum|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{número\textnormal{\textcolor{red}{{,}}}número\textnormal{\textcolor{red}{{;}}}número \textnormal{\textcolor{red}{{e}}} exponente}\par\noindent % |\spnum|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{número\textnormal{\textcolor{red}{{.}}}número\textnormal{\textcolor{red}{{;}}}número}\hfill|\spnum|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{número\textnormal{\textcolor{red}{{.}}}número\textnormal{\textcolor{red}{{;}}}número \textnormal{\textcolor{red}{{e}}} exponente} % \end{syntax} % El comando \ics*{spnum} trabaja en \emph{modo matemático} y recibe % el \emph{argumento obligatorio} \mymarg{número}, donde la \emph{«parte entera»} % puede iniciar con los signos `$\mathcolor{red}{+}$' o `$\mathcolor{red}{-}$', % la \emph{«parte decimal finita»} \textbf{debe} iniciar con una \emph{coma} % `\textcolor{red}{\texttt{,}}' o con un \emph{punto} `\textcolor{red}{\texttt{.}}', % y la \emph{«parte decimal infinita (periódica)»} \textbf{debe} iniciar % con un \emph{punto y coma} `\textcolor{red}{\texttt{;}}'. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % y se ejecuta |$\spnum{+23}$| se imprimirá `$+23$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«más veintitrés»}, si se % ejecuta |$\spnum{3,14}$| se imprimirá `$3{,}14$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«tres coma catorce»}, si % se ejecuta |$\spnum{3,1;4}$| se imprimirá `$3{,}1\overline{4}$' en la % salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«tres coma % uno con cuatro periódico»}. % % Además, el \emph{argumento obligatorio} \mymarg{número} admite al final % de este un sufijo de \emph{«notación científica»}. Este se indica mediante las % letras `\textcolor{red}{\texttt{e}}' o `\textcolor{red}{\texttt{E}}', % acompañadas de los signos opcionales `$\mathcolor{red}{+}$' o % `$\mathcolor{red}{-}$' seguidos por los dígitos del exponente. % Por ejemplo:~|$\spnum{3,14e3}$| imprimirá `$3{,}14\cdot 10^{3}$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«tres coma catorce por diez al cubo»}. % % \begin{important}* % La \emph{coma} `\textcolor{red}{\texttt{,}}' o el \emph{punto} `\textcolor{red}{\texttt{.}}' % son \emph{separadores decimales} y NO de millares, por lo tanto solo % pueden aparecer una «única vez» dentro del \mymarg{argumento}. % El \mymarg{argumento} puede contener espacios matemáticos válidos, pero % NO puede contener comandos con o sin argumentos como |\textstyle|, |\mathrm{#1}| o % |\mathbf{#1}| por ejemplo. Cualquier entrada fuera de esta sintaxis % devuelve un \enquote{error} y se detiene la compilación del documento. % \end{important} % % \subsubsection{Llaves para \cs[no-index]{spnum}}\label{subsubsec:key-spnum} % % \keyexampcmd{cifras-sep}{true \textnormal{\textcolor{lightgray}{\textbar}} false}{true}[spnum] % % Establece el \emph{espaciado} utilizado para agrupar los dígitos en % bloques de tres, tanto en la parte entera como en la parte decimal finita. % Si se establece en |true| se siguen las normas de la \textsc{rae} y se % utiliza un \emph{espacio fino} `\textcolor{blue!75}{\textbackslash}|,|' % entre las cifras; si se establece en |false|, se respetan los espacios % matemáticos introducidos en el argumento. Por ejemplo:~|$\spnum{100000}$| % imprimirá `$100\,000$' en la salida \texttt{PDF} y |$\spnum[cifras-sep=false]{10\,00\,00}$| % imprimirá `$10\,00\,00$' en la salida \texttt{PDF}. % % \keyexampcmd{read-sign}{terse \textnormal{\textcolor{lightgray}{\textbar}} clear}{terse}[spnum] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará los signos % `$\mathcolor{red}{+}$' o `$\mathcolor{red}{-}$'. % Si se establece en |terse|, \mynvmath{NVDA/MathCAT} verbalizará \emph{«más»} o % \emph{«menos»} \textbf{antes} de leer el número; si se establece en |clear|, \mynvmath{NVDA/MathCAT} % verbalizará \emph{«positivo»} o \emph{«negativo»} \textbf{después} de leer el número. % Por ejemplo:~|$\spnum{-23}$| imprimirá `$-23$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«menos veintitrés»} y |$\spnum[read-sign=clear]{-23}$| % imprimirá `$-23$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«veintitrés negativo»}. % % \keyexampcmd{read-period-sep}{coma \textnormal{\textcolor{lightgray}{\textbar}} punto}{coma}[spnum] % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará e imprimirá % el signo de separación `\textcolor{red}{\texttt{;}}' de la \emph{«parte % decimal infinita (periódica)»} cuando no se tiene la \emph{«parte % decimal finita»}. Si se establece en |coma|, se imprimirá `\textcolor{red}{\texttt{,}}' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«coma»}; % si se establece en |punto|, se imprimirá `\textcolor{red}{\texttt{.}}' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«punto»}. % Por ejemplo:~|$\spnum{3;4}$| imprimirá `$3{,}\overline{4}$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«tres coma cuatro periódico»} % y |$\spnum[read-period-sep=punto]{3;4}$| imprimirá `$3{.}\overline{4}$' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«tres punto cuatro periódico»}. % % \begin{important}* % Esta llave es necesaria SOLO cuando se utilizan \emph{«decimales periódicos puros»}, % es decir, cuando no se ha incluido en el \mymarg{argumento} una \emph{coma} % `\textcolor{red}{\texttt{,}}' o un \emph{punto} `\textcolor{red}{\texttt{.}}' % antes del signo de separación `\textcolor{red}{\texttt{;}}' de la parte periódica. % \end{important} % % \keyexampcmd{notation-sym}{times \textnormal{\textcolor{lightgray}{\textbar}} cdot}{cdot}[spnum] % % Establece el \emph{símbolo} que se utilizará para imprimir % la \emph{«notación científica»} al usar `\textcolor{red}{\texttt{e}}' o % `\textcolor{red}{\texttt{E}}'. Si se establece en |times| imprimirá % `$\mathcolor{red}{\times}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«por»}; si se establece en |cdot| imprimirá % `$\mathcolor{red}{\cdot}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«por»}. Por ejemplo:~|$\spnum{3,14e3}$| imprimirá `$3{,}14\cdot 10^{3}$' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«tres coma catorce por diez al cubo»} y |$\spnum[notation-sym=times]{3,14e3}$| % imprimirá `$3{,}14\times 10^{3}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«tres coma catorce por diez al cubo»}. % % \begin{important}* % Decir \emph{«notación científica»} aquí es un poco exagerado, en realidad % es \emph{«multiplicación por potencias de $10$»}, no se verifica si la % entrada cumple o no con la condición de estar escrita en \emph{«notación científica»}. % \end{important} % % \subsection{El comando \cs[no-index]{spunit}}\label{subsec:spunit} % % \begin{function}{\spunit} % \begin{syntax} % |\spunit|\mymarg{unidad}\hfill|\spunit|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{unidad}\hfill|\spunit|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{número unidad}\par\noindent % |\spunit|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{número unidad\textnormal{\textcolor{red}{{\textasteriskcentered}}}unidad}\hfill|\spunit|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{número unidad\textnormal{\textcolor{red}{{\textasteriskcentered}}}unidad\textnormal{\textcolor{red}{{/}}}unidad}\par\noindent % |\spunit|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{número unidad\textnormal{\textcolor{red}{{\textasteriskcentered}}}unidad\textnormal{\textcolor{red}{{/}}}unidad\textnormal{\textcolor{red}{{\textasteriskcentered}}}\dots}\hfill|\spunit|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{número \textnormal{\textcolor{red}{{e}}} exponente unidad \dots} % \end{syntax} % El comando \ics*{spunit} trabaja en \emph{modo matemático} y recibe % el \emph{argumento obligatorio} \mymarg{unidad}, el cual puede contener % una \emph{«parte numérica»} opcional la cual se procesa % internamente por el comando |\spnum| al inicio del \mymarg{argumento} seguido % de una \mymarg{unidad} o un \mymarg{alias} válido descritos en~\S\ref{subsubsec:arg-spunit} % o una \mymarg{unidad} creada por el comando |\spnewunit| (\S\ref{subsubsec:spnewunit}) % o un \mymarg{alias} creado por el comando |\spunitalias| (\S\ref{subsubsec:spunitalias}). % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spunit{23 m}$| imprimirá `$23\,\mathrm{m}$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«veintitrés metros»}. % % La multiplicación entre las \mymarg{unidades} se realiza mediante un % \emph{asterisco} `$\mathcolor{red}{\ast}$' y las potencias de estas % se indican con un \emph{circunflejo seguido del exponente entre llaves} % `\textcolor{red}{\texttt{\^{}}}\mymarg{exponente}'. Por ejemplo, si % se ejecuta |$\spunit{23 m*s^{2}}$| se imprimirá `$23\,\mathrm{m}\,\mathrm{s}^{2}$' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«veintitrés metros segundos cuadrado»}. % % Se pueden usar \emph{fracciones lineales} con las \mymarg{unidades} % utilizando una \emph{«única barra»} `$\mathcolor{red}{/}$' para la % separación entre las \mymarg{unidades} del numerador y el denominador, % teniendo presente que el denominador NO puede contener números de ningún % tipo. Por ejemplo:~|$\spunit{23 m/s^{2}}$| imprimirá `$23\,\mathrm{m}/\mathrm{s}^{2}$' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«veintitrés metros por segundos cuadrado»}. % % \begin{important}* % Solo imprimiremos \emph{fracciones lineales}, si se requiere otra forma % fraccionaria para explicaciones y otros usos se debe usar el comando % por separado. El \mymarg{argumento} puede contener espacios matemáticos % válidos, pero NO puede contener comandos con o sin argumentos como % |\textstyle|, |\mathrm{#1}| o |\mathbf{#1}| por ejemplo, NO puede contener % más de una \emph{«única barra»} `$\mathcolor{red}{/}$' y NO puede contener % números en el denominador. Cualquier entrada fuera de esta sintaxis % devuelve un \enquote{error} y se detiene la compilación del documento. % \end{important} % % \subsubsection{Llaves para \cs[no-index]{spunit}} % % El comando |\spunit| posee las \mymeta{meta-keys} \mykey[spunit]{cifras-sep}, % \mykey[spunit]{read-period-sep} y \mykey[spunit]{notation-sym} pasadas % al comando |\spnum| (\S\ref{subsubsec:key-spnum}). % % \keyexampcmd{print-cdot}{true \textnormal{\textcolor{lightgray}{\textbar}} false}{false}[spunit] % % Establece si se \emph{imprime} |\cdot| `$\mathcolor{red}{\cdot}$' o un \emph{espacio fino} % `\textcolor{blue!75}{\textbackslash}|,|' para la multiplicación de \mymarg{unidades}. % Si se establece en |true|, se imprimirá `$\mathcolor{red}{\cdot}$' entre las \mymarg{unidades} % en la salida \texttt{PDF}; si se establece en |false| se imprimirá un \emph{espacio fino} % `\textcolor{blue!75}{\textbackslash}|,|' entre las \mymarg{unidades} en % la salida \texttt{PDF}. % % \keyexampcmd{read-cdot}{true \textnormal{\textcolor{lightgray}{\textbar}} false}{false}[spunit] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará la % multiplicación de \mymarg{unidades}. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} verbalizará «por» entre las \mymarg{unidades}; si se % establece en |false|, \mynvmath{NVDA/MathCAT} no verbalizará nada entre las % \mymarg{unidades}. % % \keyexampcmd{read-slash}{por \textnormal{\textcolor{lightgray}{\textbar}} partido-por}{por}[spunit] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará la % \emph{«barra»} `$\mathcolor{red}{/}$' de separación entre el % numerador y el denominador. Si se establece en |por|, \mynvmath{NVDA/MathCAT} % verbalizará \emph{«por»}; si se establece en |partido-por|, \mynvmath{NVDA/MathCAT} % verbalizará \emph{«partido por»}. % % \subsubsection{Argumentos para \cs[no-index]{spunit}}\label{subsubsec:arg-spunit} % % La tabla~\ref{tab:spunit-unidades} resume las \mymarg{unidades} y los \mymarg{alias} % soportados por |\spunit|. % % \begingroup\small % \begin{longtable}{@{\extracolsep{\fill}}cllcl@{}} % \caption{Unidades soportadas} \label{tab:spunit-unidades} \\ % \toprule % \textbf{Entrada} & \textbf{Alias / Variantes} & \textbf{Nombre} & \textbf{Salida PDF} & \multicolumn{1}{c}{\textbf{Lectura NVDA}} \\ % \midrule % \endfirsthead % % % \caption[]{\emph{(Continuación - Unidades soportadas)}} \\ % \toprule % \textbf{Entrada} & \textbf{Alias / Variantes} & \textbf{Nombre} & \textbf{Salida PDF} & \multicolumn{1}{c}{\textbf{Lectura NVDA}} \\ % \midrule % \endhead % % % \midrule % \multicolumn{5}{r}{\emph{Continúa en la siguiente página...}} \\ % \endfoot % % % \bottomrule % \endlastfoot % % % m & metro, metros & Metro & $\mathrm{m}$ & \emph{«metro»} \\ % g & gramo, gramos & Gramo & $\mathrm{g}$ & \emph{«gramo»} \\ % s & segundo, segundos & Segundo (Tiempo) & $\mathrm{s}$ & \emph{«segundo»} \\ % l & litro, l-litro & Litro (minúscula) & $\mathrm{l}$ & \emph{«litro»} \\ % h & hora, horas & Hora & $\mathrm{h}$ & \emph{«hora»} \\ % d & dia, dias & Día & $\mathrm{d}$ & \emph{«día»} \\ % A & amperio, ampere & Amperio & $\mathrm{A}$ & \emph{«amperio»} \\ % V & voltio, volt & Voltio & $\mathrm{V}$ & \emph{«voltio»} \\ % W & vatio, watt & Vatio & $\mathrm{W}$ & \emph{«vatio»} \\ % J & julio, joule & Julio & $\mathrm{J}$ & \emph{«julio»} \\ % N & newton & Newton & $\mathrm{N}$ & \emph{«newton»} \\ % Pa & pascal & Pascal & $\mathrm{Pa}$ & \emph{«pascal»} \\ % Hz & hercio, hertz & Hercio & $\mathrm{Hz}$ & \emph{«hercio»} \\ % Ω & ohmio, ohm, Omega & Ohmio & $\mathrm{Ω}$ & \emph{«ohmio»} \\ % F & faradio & Faradio & $\mathrm{F}$ & \emph{«faradio»} \\ % C & culombio & Culombio & $\mathrm{C}$ & \emph{«culombio»} \\ % K & kelvin, Kelvin & Kelvin & $\mathrm{K}$ & \emph{«kelvin»} \\ % °C & celsius, degC, ℃ & Grados Celsius & $\mathrm{°C}$ & \emph{«grados celsius»} \\ % °F & fahrenheit, degF, ℉ & Grados Fahrenheit & $\mathrm{°F}$ & \emph{«grados fahrenheit»} \\ % cal & caloria, calorias & Caloría & $\mathrm{cal}$ & \emph{«caloría»} \\ % b & bit & Bit & $\mathrm{b}$ & \emph{«bit»} \\ % B & byte, bytes & Byte & $\mathrm{B}$ & \emph{«byte»} \\ % in & pulgada, pulgadas & Pulgada & $\mathrm{in}$ & \emph{«pulgada»} \\ % ft & pie, pies & Pie & $\mathrm{ft}$ & \emph{«pie»} \\ % yd & yarda, yardas & Yarda & $\mathrm{yd}$ & \emph{«yarda»} \\ % mi & milla, millas & Milla & $\mathrm{mi}$ & \emph{«milla»} \\ % lb & libra, libras & Libra & $\mathrm{lb}$ & \emph{«libra»} \\ % oz & onza, onzas & Onza & $\mathrm{oz}$ & \emph{«onza»} \\ % gal & galon, galones & Galón & $\mathrm{gal}$ & \emph{«galón»} \\ % Å & angstrom & Angstrom & $\mathrm{Å}$ & \emph{«angstrom»} \\ % µ & micro & Micro (Prefijo) & $\mathrm{µ}$ & \emph{«micro»} \\ % ℧ & mho & Mho / Siemens & $\mathrm{℧}$ & \emph{«mho / siemens»} \\ % ′ & minmin & Minuto (Arco) & $\mathrm{′}$ & \emph{«minuto»} \\ % ″ & segseg & Segundo (Arco) & $\mathrm{″}$ & \emph{«segundo»} \\ % u & u-masa & Unidad masa atómica & $\mathrm{u}$ & \emph{«unidad masa atómica»} \\ % \midrule % \multicolumn{5}{c}{\textbf{Unidades directas (sin alias)}} \\ % \midrule % L & --- & Litro (Mayúscula) & $\mathrm{L}$ & \emph{«litro»} \\ % ℓ & --- & Litro (Cursiva) & $\mathrm{ℓ}$ & \emph{«litro»} \\ % w & --- & Vatio (Minúscula) & $\mathrm{w}$ & \emph{«vatio»} \\ % °K & --- & Grado Kelvin & $\mathrm{°K}$ & \emph{«grado kelvin»} \\ % Bq & --- & Becquerel & $\mathrm{Bq}$ & \emph{«becquerel»} \\ % Da & --- & Dalton & $\mathrm{Da}$ & \emph{«dalton»} \\ % Gy & --- & Gray & $\mathrm{Gy}$ & \emph{«gray»} \\ % S & --- & Siemens & $\mathrm{S}$ & \emph{«siemens»} \\ % Sv & --- & Sievert & $\mathrm{Sv}$ & \emph{«sievert»} \\ % T & --- & Tesla & $\mathrm{T}$ & \emph{«tesla»} \\ % Wb & --- & Weber & $\mathrm{Wb}$ & \emph{«weber»} \\ % atm & --- & Atmósfera & $\mathrm{atm}$ & \emph{«atmósfera»} \\ % au & --- & Unidad astronómica & $\mathrm{au}$ & \emph{«unidad astronómica»} \\ % bar & --- & Bar & $\mathrm{bar}$ & \emph{«bar»} \\ % cd & --- & Candela & $\mathrm{cd}$ & \emph{«candela»} \\ % dB & --- & Decibelio & $\mathrm{dB}$ & \emph{«decibelio»} \\ % dyn & --- & Dina & $\mathrm{dyn}$ & \emph{«dina»} \\ % eV & --- & Electronvoltio & $\mathrm{eV}$ & \emph{«electronvoltio»} \\ % erg & --- & Ergio & $\mathrm{erg}$ & \emph{«ergio»} \\ % ha & --- & Hectárea & $\mathrm{ha}$ & \emph{«hectárea»} \\ % kat & --- & Katal & $\mathrm{kat}$ & \emph{«katal»} \\ % kg & --- & Kilogramo & $\mathrm{kg}$ & \emph{«kilogramo»} \\ % km & --- & Kilómetro & $\mathrm{km}$ & \emph{«kilómetro»} \\ % lm & --- & Lumen & $\mathrm{lm}$ & \emph{«lumen»} \\ % lx & --- & Lux & $\mathrm{lx}$ & \emph{«lux»} \\ % cm & --- & Centímetro & $\mathrm{cm}$ & \emph{«centímetro»} \\ % min & --- & Minuto (Tiempo) & $\mathrm{min}$ & \emph{«minuto»} \\ % mm & --- & Milímetro & $\mathrm{mm}$ & \emph{«milímetro»} \\ % mol & --- & Mol & $\mathrm{mol}$ & \emph{«mol»} \\ % pc & --- & Pársec & $\mathrm{pc}$ & \emph{«pársec»} \\ % rad & --- & Radián & $\mathrm{rad}$ & \emph{«radián»} \\ % rpm & --- & Rev. por minuto & $\mathrm{rpm}$ & \emph{«rev. por minuto»} \\ % sr & --- & Estereorradián & $\mathrm{sr}$ & \emph{«estereorradián»} \\ % t & --- & Tonelada & $\mathrm{t}$ & \emph{«tonelada»} \\ % a & --- & Año / Área (are) & $\mathrm{a}$ & \emph{«año / área»} \\ % y & --- & Año (Year) & $\mathrm{y}$ & \emph{«año»} \\ % ° & --- & Grado (Arco) & $\mathrm{°}$ & \emph{«grado»} \\ % \end{longtable} % \endgroup % % \subsubsection{El comando \cs[no-index]{spnewunit}}\label{subsubsec:spnewunit} % % \begin{function}{\spnewunit} % \begin{syntax} % \ics*{spnewunit}\mymarg{nueva unidad}\mymarg{verbalización \textnormal{NVDA}} % \end{syntax} % El comando \ics*{spnewunit} trabaja en \emph{modo texto} y recibe dos % \emph{argumentos obligatorios}: \mymarg{nueva unidad} que establece % lo que se imprimirá en la salida \texttt{PDF} y \mymarg{verbalización \textnormal{NVDA}} % que establece cómo \mynvmath{NVDA/MathCAT} \emph{verbalizará} la \mymarg{nueva unidad}. % \end{function} % % La \mymarg{nueva unidad} no debe estar registrada como \mymarg{unidad} % o \mymarg{alias} para |\spunit| descritos en~\S\ref{subsubsec:arg-spunit}. % Si la \mymarg{nueva unidad} ya está registrada se devuelve un \enquote{error} % y se detiene la compilación del documento. % % La declaración de la \mymarg{nueva unidad} es «global» y se almacena % en el módulo \myluamod{} como una \mymarg{unidad} válida para pasar como % \mymarg{argumento} a |\spunit|. Por ejemplo:~|\spnewunit{px}{píxeles}| % creará la \mymarg{nueva unidad} `|px|' y cuando se utilice |$\spunit{100 px}$| % se imprimirá `$100\,\mathrm{px}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«cien píxeles»}. % % \subsubsection{El comando \cs[no-index]{spunitalias}}\label{subsubsec:spunitalias} % % \begin{function}{\spunitalias} % \begin{syntax} % \ics*{spunitalias}\mymarg{nuevo alias}\mymarg{expresión de unidades} % \end{syntax} % El comando \ics*{spunitalias} trabaja en \emph{modo texto} y recibe % dos \emph{argumentos obligatorios}: \mymarg{nuevo alias} que es el nuevo \mymarg{alias} de \mymarg{argumento} para |\spunit| y % \mymarg{expresión de unidades} que puede contener multiplicación o % división de \mymarg{unidades} ya registradas que se imprimirán en la % salida \texttt{PDF}. % \end{function} % % El \mymarg{nuevo alias} no debe estar registrado como \mymarg{unidad} % o \mymarg{alias} para |\spunit| descritos en~\S\ref{subsubsec:arg-spunit} % o estar definido como \mymarg{unidad} por medio del comando |\spnewunit|. % Si el \mymarg{nuevo alias} ya está registrado se devuelve un \enquote{error} % y se detiene la compilación del documento. % % La declaración de un \mymarg{nuevo alias} es «global» y se almacena % en el módulo \myluamod{} como un \mymarg{alias} válido para pasar como % \mymarg{argumento} a |\spunit|. Por ejemplo:~|\spunitalias{caudal}{m^{3}/s}| % creará el \mymarg{alias} `|caudal|' y cuando se utilice |$\spunit{50 caudal}$| % se imprimirá `$50\,\mathrm{m}^{3} / \mathrm{s}$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«cincuenta metros al cubo por segundos»}. % % \subsection{El comando \cs[no-index]{spmoney}}\label{subsec:spmoney} % % \begin{function}{\spmoney} % \begin{syntax} % \mycolrow{19.7em}|\spmoney|\mymarg{cifra}\mycolsep |\spmoney|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{cifra} % \mycolrow{19.7em}|\spmoney|\mymarg{cifra símbolo moneda}\mycolsep |\spmoney|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{cifra símbolo moneda} % \mycolrow{19.7em}|\spmoney|\mymarg{cifra alias moneda}\mycolsep |\spmoney|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{cifra alias moneda} % \end{syntax} % El comando \ics*{spmoney} trabaja en \emph{modo matemático} y recibe % el \emph{argumento obligatorio} \mymarg{cifra} que contiene la % \emph{«cifra numérica»} positiva o negativa la cual se procesa internamente % por el comando |\spnum| acompañada de \emph{«símbolo moneda»} o % \emph{«alias moneda»} opcionales descritos en~\S\ref{subsubsec:arg-spmoney}. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spmoney{500}$| imprimirá `$\$500$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«quinientos pesos»}. % % El comportamiento de |\spmoney| depende de cómo se pase \emph{«símbolo moneda»} o % \emph{«alias moneda»} en el \emph{argumento obligatorio} donde tienen % prioridad por sobre los valores por defecto. Por ejemplo:~% % |$\spmoney{500 dolares}$| imprimirá `$\$500$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«quinientos dólares»} % aunque la moneda por defecto sean pesos. % % Si se pasa un \emph{«alias moneda»} del tipo \texttt{ISO}, el código de divisas, % como |CLP| o |USD| escrito en mayúsculas, se modifica la verbalización en \mynvmath{NVDA/MathCAT} % agregando el país y se ajusta la salida \texttt{PDF} al estándar \texttt{ISO}. Por ejemplo:~% % |$\spmoney{500 CLP}$| imprimirá `$\mathrm{CLP}500$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«quinientos pesos chilenos»}. % % Si se pasa un \emph{«alias moneda»} del tipo \texttt{ISO} % como |clp| o |usd| escrito en minúsculas, se modifica la verbalización en \mynvmath{NVDA/MathCAT} % agregando el país, pero NO se modifica la salida \texttt{PDF} imprimiendo % el \emph{«símbolo de moneda»} correspondiente. Por ejemplo:~|$\spmoney{500 clp}$| % imprimirá `$\$500$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«quinientos pesos chilenos»}. % % Pasar de manera directa en el \emph{argumento obligatorio} un \emph{«símbolo moneda»} % como `$\mathcolor{red}{\mathdollar}$', `$\mathcolor{red}{\euro}$', `$\mathcolor{red}{\mathsterling}$' % o un \emph{«alias moneda»} como |peso|, |euro| o |libra| produce % la salida \texttt{PDF} y verbalización estándar esperada para \mynvmath{NVDA/MathCAT}. % Los \emph{«alias moneda»} se pueden pasar con o sin tilde, por ejemplo |dolares| o |dólares|. % % \begin{important}* % Los \emph{«alias moneda»} se deben escribir en minúsculas y los códigos % \texttt{ISO} en minúsculas o todo en mayúsculas. Cualquier otra % escritura, por ejemplo |PESO| o |Usd|, devuelve un \enquote{error} y % se detiene la compilación del documento. % \end{important} % % \begin{important}* % La posición del \emph{«símbolo moneda»} a la izquierda o a la derecha % de la \emph{«cifra numérica»} y la verbalización para \mynvmath{NVDA/MathCAT} % se manejan desde el módulo Lua \myluamod{} y no se proveen llaves para % modificar este comportamiento. % \end{important} % % \begin{important}* % El \emph{«símbolo moneda»} es tomado de la fuente tipográfica definida % para el \emph{modo matemático} al momento de ejecutar el comando, o del % \emph{modo texto} si se establece \mykey{money-math}|=false|, se % debe tener presente en caso de querer usar un \emph{«símbolo moneda»} % poco habitual que esté soportado por \mypkg*[no-index]{spintent}. El \mymarg{argumento} % puede contener espacios matemáticos válidos, pero NO puede contener % comandos con o sin argumentos como |\textstyle|, |\mathrm{#1}| o % |\mathbf{#1}| por ejemplo, con la única excepción de |\$|. La \emph{coma} `\textcolor{red}{\texttt{,}}' % o el \emph{punto} `\textcolor{red}{\texttt{.}}' como \emph{separador decimal} solo % pueden aparecer una «única vez». Cualquier entrada fuera de esta sintaxis % devuelve un \enquote{error} y se detiene la compilación del documento. % \end{important} % % \subsubsection{Llaves para \cs[no-index]{spmoney}} % % El comando |\spmoney| posee la \mymeta{meta-key} \mykey[spmoney]{cifras-sep} % pasada al comando |\spnum| (\S\ref{subsubsec:key-spnum}). % % \keyexampcmd{currency}{moneda \textnormal{\textcolor{lightgray}{\textbar}} alias}{pesos}[spmoney] % % Establece el \emph{símbolo moneda} por defecto que se imprimirá en la % salida \texttt{PDF} y cómo lo verbalizará \mynvmath{NVDA/MathCAT} cuando % SOLO se pasa una \emph{«cifra numérica»} como \mymarg{argumento} al comando |\spmoney|. % El \emph{«símbolo moneda»} o el \emph{«alias moneda»} pasado a esta % llave debe ser alguno de los descritos en~\S\ref{subsubsec:arg-spmoney}. % % \keyexampcmd{sub-units}{true \textnormal{\textcolor{lightgray}{\textbar}} false}{false}[spmoney] % % Establece si se usarán \emph{subunidades} de la moneda en curso para la % verbalización en \mynvmath{NVDA/MathCAT}. La moneda en curso debe aceptar % \emph{subunidades}. Por ejemplo:~|$\spmoney[sub-units=true]{100,5 dolares}$| % imprimirá `$\$100{,}5$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«cien dólares con cincuenta centavos»}. % % \begin{important}* % Si la \emph{«cifra numérica»} no tiene parte decimal, la llave % \mykey{sub-units} no tiene efecto. Si la moneda en curso no admite % \emph{subunidades} se emite una advertencia y la llave tampoco tiene efecto. % \end{important} % % \keyexampcmd{money-math}{true \textnormal{\textcolor{lightgray}{\textbar}} false}{true}[spmoney] % % Establece si el \emph{símbolo moneda} se imprimirá en \emph{modo texto} % o en \emph{modo matemático}. Si se establece en |true|, se usará la % fuente tipográfica definida para el \emph{modo matemático} en curso % para imprimir el \emph{símbolo moneda} en la salida \texttt{PDF}; si se % establece en |false|, se usará la fuente tipográfica definida para el % \emph{modo texto} para imprimir el \emph{símbolo moneda} en la salida % \texttt{PDF}. % % \subsubsection{Monedas y alias para \cs[no-index]{spmoney}}\label{subsubsec:arg-spmoney} % % La tabla~\ref{tab:spmoney-divisas} resume los \emph{«símbolos moneda»} y los % \emph{«alias moneda»} soportados en el \mymarg{argumento} de |\spmoney|. % % \begingroup\small % \begin{longtable}{@{\extracolsep{\fill}}c >{\raggedright\arraybackslash}p{2.75cm} l c l@{}} % \caption{Monedas y alias soportados} \label{tab:spmoney-divisas} \\ % \toprule % \textbf{Entrada} & \textbf{Alias / Variantes} & \textbf{Nombre} & \textbf{Salida PDF} & \multicolumn{1}{c}{\textbf{Lectura NVDA}} \\ % \midrule % \endfirsthead % \caption[]{\emph{(Continuación - Monedas y alias soportados)}} \\ % \toprule % \textbf{Entrada} & \textbf{Alias / Variantes} & \textbf{Nombre} & \textbf{Salida PDF} & \multicolumn{1}{c}{\textbf{Lectura NVDA}} \\ % \midrule % \endhead % \midrule % \multicolumn{5}{r}{\emph{Continúa en la siguiente página...}} \\ % \endfoot % \bottomrule % \endlastfoot % \texttt{\$} & \texttt{peso}, \texttt{pesos} & peso & \$ & \emph{«peso / pesos»} \\ % \texttt{dolar} & \texttt{dolares}, \texttt{dólar}, \texttt{dólares} & dólar & \$ & \emph{«dólar / dólares»} \\ % \texttt{€} & \texttt{euro}, \texttt{euros} & euro & € & \emph{«euro / euros»} \\ % \texttt{£} & \texttt{libra}, \texttt{libras} & libra & £ & \emph{«libra / libras»} \\ % \texttt{¥} & \texttt{yen}, \texttt{yenes} & yen & ¥ & \emph{«yen / yenes»} \\ % \texttt{yuan} & \texttt{yuanes} & yuan & ¥ & \emph{«yuan / yuanes»} \\ % \texttt{₩} & \texttt{won}, \texttt{wons} & won & \texttt{₩} & \emph{«won / wons»} \\ % \texttt{₪} & \texttt{shekel}, \texttt{shekels}, \texttt{sequel}, \texttt{sequeles} & séquel & ₪ & \emph{«séquel / séqueles»} \\ % \texttt{₹} & \texttt{rupia}, \texttt{rupias} & rupia & ₹ & \emph{«rupia / rupias»} \\ % \texttt{₽} & \texttt{rublo}, \texttt{rublos} & rublo & \texttt{₽} & \emph{«rublo / rublos»} \\ % \texttt{₺} & \texttt{lira}, \texttt{liras} & lira & \texttt{₺} & \emph{«lira / liras»} \\ % \texttt{₴} & \texttt{grivna}, \texttt{grivnas} & grivna & \texttt{₴} & \emph{«grivna / grivnas»} \\ % \texttt{¢} & \texttt{centavo}, \texttt{centavos} & centavo & ¢ & \emph{«centavo / centavos»} \\ % \midrule % \multicolumn{5}{c}{\textbf{Divisas en formato ISO (Mayúsculas)}} \\ % \midrule % \texttt{CLP} & --- & peso chileno & \texttt{CLP} & \emph{«peso chileno / pesos chilenos»} \\ % \texttt{MXN} & --- & peso mexicano & \texttt{MXN} & \emph{«peso mexicano / pesos mexicanos»} \\ % \texttt{USD} & --- & dólar & \texttt{USD} & \emph{«dólar estadounidense / dólares estadounidenses»} \\ % \texttt{EUR} & --- & euro & \texttt{EUR} & \emph{«euro / euros»} \\ % \texttt{GBP} & --- & libra & \texttt{GBP} & \emph{«libra / libras»} \\ % \texttt{JPY} & --- & yen & \texttt{JPY} & \emph{«yen / yenes»} \\ % \texttt{CNY} & --- & yuan & \texttt{CNY} & \emph{«yuan / yuanes»} \\ % \texttt{KRW} & --- & won & \texttt{KRW} & \emph{«won / wons»} \\ % \texttt{ILS} & --- & séquel & \texttt{ILS} & \emph{«séquel / séqueles»} \\ % \texttt{INR} & --- & rupia & \texttt{INR} & \emph{«rupia / rupias»} \\ % \texttt{RUB} & --- & rublo & \texttt{RUB} & \emph{«rublo / rublos»} \\ % \texttt{TRY} & --- & lira & \texttt{TRY} & \emph{«lira / liras»} \\ % \texttt{UAH} & --- & grivna & \texttt{UAH} & \emph{«grivna / grivnas»} \\ % \midrule % \multicolumn{5}{c}{\textbf{Códigos ISO en minúsculas}} \\ % \midrule % \texttt{clp} & --- & peso chileno & \$ & \emph{«peso chileno / pesos chilenos»} \\ % \texttt{mxn} & --- & peso mexicano & \$ & \emph{«peso mexicano / pesos mexicanos»} \\ % \texttt{usd} & --- & dólar & \$ & \emph{«dólar estadounidense / dólares estadounidenses»} \\ % \texttt{eur} & --- & euro & € & \emph{«euro / euros»} \\ % \texttt{gbp} & --- & libra & £ & \emph{«libra / libras»} \\ % \texttt{jpy} & --- & yen & ¥ & \emph{«yen / yenes»} \\ % \texttt{cny} & --- & yuan & ¥ & \emph{«yuan / yuanes»} \\ % \texttt{krw} & --- & won & \texttt{₩} & \emph{«won / wons»} \\ % \texttt{ils} & --- & séquel & ₪ & \emph{«séquel / séqueles»} \\ % \texttt{inr} & --- & rupia & ₹ & \emph{«rupia / rupias»} \\ % \texttt{rub} & --- & rublo & \texttt{₽} & \emph{«rublo / rublos»} \\ % \texttt{try} & --- & lira & \texttt{₺} & \emph{«lira / liras»} \\ % \texttt{uah} & --- & grivna & \texttt{₴} & \emph{«grivna / grivnas»} \\ % \end{longtable} % \endgroup % % \subsection{El comando \cs[no-index]{spshort}}\label{subsec:spshort} % % \begin{function}{\spshort} % \begin{syntax} % |\spshort|\mymarg{abreviaturas \textnormal{\textcolor{lightgray}{\textbar}} acrónimos \textnormal{\textcolor{lightgray}{\textbar}} números ordinales} % |\spshort|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{abreviaturas \textnormal{\textcolor{lightgray}{\textbar}} acrónimos \textnormal{\textcolor{lightgray}{\textbar}} números ordinales} % |\spshort|\myoarg{read-arg\textnormal{\textcolor{gray}{=}}\mymarg{verbalización \textnormal{NVDA}}}\mymarg{comandos \textnormal{\hologo{LaTeX}}} % \end{syntax} % % El comando \ics*{spshort} trabaja \emph{solo en modo texto} y recibe % \emph{un argumento obligatorio} del tipo \mymarg{abreviaturas}, \mymarg{acrónimos} o \mymarg{números ordinales} % descritos en~\S\ref{subsubsec:arg-spshort}. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % y se ejecuta |\spshort{dr.a}| se imprimirá `Dr.\textsuperscript{a}' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«doctora»}; % si se ejecuta |\spshort{onu}| se imprimirá `ONU' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«organización de las naciones unidas»} % y si se ejecuta |\spshort{mineduc}| se imprimirá `Mineduc' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«ministerio de educación»}. % % Para la escritura de \mymarg{números ordinales} se puede ejecutar % |\spshort{1.a}| que imprimirá `{\addfontfeatures{Numbers=Lining}1.\textsuperscript{a}}' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«primera»}; % si se ejecuta |\spshort{1.o}| imprimirá `{\addfontfeatures{Numbers=Lining}1.\textsuperscript{o}}' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«primero»}. % % Para crear una nueva \mymarg{abreviatura}, \mymarg{acrónimo} o \mymarg{número ordinal} % que no se encuentre registrada por \mypkg*[no-index]{spintent} dentro del módulo Lua \myluamod{} % descritas en~\S\ref{subsubsec:arg-spshort} se debe utilizar la llave % \mykey{read-arg} y definir esta dentro del \emph{argumento obligatorio} % que puede contener \mymarg{comandos \hologo{LaTeX}} \emph{en modo texto} % los cuales se imprimirán en salida \texttt{PDF}. % % \begin{important}* % Para la escritura de \mymarg{números ordinales} se debe tener cuidado % y no confundir la \emph{«letra o volada»} `\textsuperscript{o}' con % el símbolo de grado `°'. Si la fuente tipográfica del \emph{modo texto} % está utilizando |Numbers=OldStyle|, los números pueden quedar muy separados % de la \emph{«letra volada»}, la solución para esto es agregar |\addfontfeatures{Numbers=Lining}| % dentro de un grupo junto al comando: |{\addfontfeatures{Numbers=Lining}\spshort{1.o}}|. % \end{important} % % \begin{important}* % El comando |\spshort| está pensado como un reemplazo para \emph{shorthands} % y el comando |\sptext| provistos por el paquete \mypkg{babel} en español % compatible con \emph{tagged} \texttt{PDF}. Es de las pocas utilidades que provee % el paquete \mypkg*[no-index]{spintent} que trabajan \emph{solo en modo texto}. Internamente % utiliza la llave \mykey{E} provista por el paquete % \mypkg{tagpdf}\cite{tagpdf} para el \emph{expansion text}. % \end{important} % % \subsubsection{Llaves para \cs[no-index]{spshort}} % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spshort] % % Establece la \emph{verbalización del argumento} \mymarg{comandos \textnormal{\hologo{LaTeX}}} % de la nueva \mymarg{abreviatura}, \mymarg{acrónimo} o \mymarg{número ordinal} para \mynvmath{NVDA/MathCAT}. % El valor de esta llave \emph{siempre} debe pasarse entre `|{ }|'. Por ejemplo:~% % |\spshort[read-arg={octavo}]{8.\textsuperscript{\emph{avo}}}| % creará una nueva \mymarg{abreviatura} que imprimirá `8.\textsuperscript{\emph{avo}}' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«octavo»}. % % \keyexampcmd{small-caps}{true \textnormal{\textcolor{lightgray}{\textbar}} false}{false}[spshort] % % Establece si se usarán \emph{versalitas} para imprimir \mymarg{acrónimos} % y para la \emph{«letra volada»} en \mymarg{abreviaturas} y \mymarg{números ordinales} % respetando las reglas de la \textsc{rae} para el uso de estas. Si se establece en |true|, % las abreviaturas con mayúscula como \texttt{EE.~UU.} y los acrónimos % de cinco o más letras como \texttt{Unicef} no se verán afectados; si se % establece en |false|, no se aplicarán \emph{versalitas}. Por ejemplo:~% % |\spshort[small-caps=true]{dr.a}| imprimirá `Dr.\textsuperscript{\textsc{a}}' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«doctora»}. % % \subsubsection{Argumentos para \cs[no-index]{spshort}}\label{subsubsec:arg-spshort} % % La tabla~\ref{tab:spshort-abreviaturas} resume las abreviaturas, % expresiones latinas y títulos registrados en el módulo Lua \myluamod{}. % % \begin{longtable}{@{\extracolsep{\fill}}l p{3.5cm} c p{5cm}@{}} % \caption{Abreviaturas soportadas} \label{tab:spshort-abreviaturas} \\ % \toprule % \textbf{Entrada} & \textbf{Alias / Variantes} & \textbf{Salida PDF} & \multicolumn{1}{c}{\textbf{Lectura NVDA}} \\ % \midrule % \endfirsthead % \caption[]{\emph{(Continuación - Abreviaturas soportadas)}} \\ % \toprule % \textbf{Entrada} & \textbf{Alias / Variantes} & \textbf{Salida PDF} & \multicolumn{1}{c}{\textbf{Lectura NVDA}} \\ % \midrule % \endhead % \midrule % \multicolumn{4}{r}{\emph{Continúa en la siguiente página...}} \\ % \endfoot % \bottomrule % \endlastfoot % \multicolumn{4}{c}{\textbf{Abreviaturas con letras voladas}} \\ % \midrule % \texttt{dr.a} & \texttt{dr.ª} & Dr.$^{\text{a}}$ & \emph{«doctora»} \\ % \texttt{dr.as} & --- & Dr.$^{\text{as}}$ & \emph{«doctoras»} \\ % \texttt{prof.a} & --- & Prof.$^{\text{a}}$ & \emph{«profesora»} \\ % \texttt{d.a} & \texttt{d.ª} & D.$^{\text{a}}$ & \emph{«doña»} \\ % \texttt{m.a} & --- & M.$^{\text{a}}$ & \emph{«maría»} \\ % \texttt{n.o} & \texttt{n.º} & N.$^{\text{o}}$ & \emph{«número»} \\ % \texttt{n.os} & --- & N.$^{\text{os}}$ & \emph{«números»} \\ % \texttt{c.ia} & --- & C.$^{\text{ia}}$ & \emph{«compañía»} \\ % \midrule % \multicolumn{4}{c}{\textbf{Abreviaturas comunes y expresiones latinas}} \\ % \midrule % \texttt{pág.} & \texttt{pag.} & pág. & \emph{«página»} \\ % \texttt{vol.} & --- & vol. & \emph{«volumen»} \\ % \texttt{etc.} & --- & etc. & \emph{«etcétera»} \\ % \texttt{et al.} & \texttt{et. al.} & et~al. & \emph{«y otros»} \\ % \texttt{ibíd.} & \texttt{ibid.} & ibíd. & \emph{«ibídem»} \\ % \texttt{op. cit.} & \texttt{op.cit.} & op.~cit. & \emph{«obra citada»} \\ % \texttt{loc. cit.} & \texttt{loc.cit.} & loc.~cit. & \emph{«lugar citado»} \\ % \texttt{v. gr.} & \texttt{v.gr.} & v.~gr. & \emph{«verbigracia»} \\ % \texttt{i. e.} & \texttt{i.e.} & i.~e. & \emph{«esto es»} \\ % \texttt{e. g.} & \texttt{e.g.} & e.~g. & \emph{«por ejemplo»} \\ % \texttt{p. ej.} & \texttt{p.ej.} & p.~ej. & \emph{«por ejemplo»} \\ % \midrule % \multicolumn{4}{c}{\textbf{Títulos, profesiones y cargos}} \\ % \midrule % \texttt{sr.} & --- & Sr. & \emph{«señor»} \\ % \texttt{sra.} & --- & Sra. & \emph{«señora»} \\ % \texttt{srta.} & --- & Srta. & \emph{«señorita»} \\ % \texttt{dr.} & --- & Dr. & \emph{«doctor»} \\ % \texttt{dra.} & --- & Dra. & \emph{«doctora»} \\ % \texttt{dres.} & --- & Dres. & \emph{«doctores»} \\ % \texttt{dras.} & --- & Dras. & \emph{«doctoras»} \\ % \texttt{prof.} & --- & Prof. & \emph{«profesor»} \\ % \texttt{profa.} & --- & Profa. & \emph{«profesora»} \\ % \texttt{profs.} & --- & Profs. & \emph{«profesores»} \\ % \texttt{ing.} & --- & Ing. & \emph{«ingeniero»} \\ % \texttt{ings.} & --- & Ings. & \emph{«ingenieros»} \\ % \texttt{lic.} & --- & Lic. & \emph{«licenciado»} \\ % \texttt{v. b.} & \texttt{v.b.} & V.~B. & \emph{«visto bueno»} \\ % \midrule % \multicolumn{4}{c}{\textbf{Abreviaturas compuestas e institucionales}} \\ % \midrule % \texttt{s. a.} & \texttt{s.a.} & S.~A. & \emph{«sociedad anónima»} \\ % \texttt{ee. uu.} & \texttt{ee.uu.} & EE.~UU. & \emph{«estados unidos»} \\ % \texttt{dd. hh.} & \texttt{dd.hh.} & DD.~HH. & \emph{«derechos humanos»} \\ % \texttt{jj. oo.} & \texttt{jj.oo.} & JJ.~OO. & \emph{«juegos olímpicos»} \\ % \texttt{ff. aa.} & \texttt{ff.aa.} & FF.~AA. & \emph{«fuerzas armadas»} \\ % \texttt{rr. ee.} & \texttt{rr.ee.} & RR.~EE. & \emph{«relaciones exteriores»} \\ % \texttt{cc. aa.} & \texttt{cc.aa.} & CC.~AA. & \emph{«comunidades autónomas»} \\ % \texttt{p. d.} & \texttt{p.d.} & P.~D. & \emph{«posdata»} \\ % \texttt{a. c.} & \texttt{a.c.} & a.~C. & \emph{«antes de Cristo»} \\ % \texttt{d. c.} & \texttt{d.c.} & d.~C. & \emph{«después de Cristo»} \\ % \texttt{d. n. i.} & \texttt{d.n.i.} & D.~N.~I. & \emph{«documento nacional de identidad»} \\ % \texttt{r. u. t.} & \texttt{r.u.t.} & R.~U.~T. & \emph{«rol único tributario»} \\ % \texttt{r. u. n.} & \texttt{r.u.n.} & R.~U.~N. & \emph{«rol único nacional»} \\ % \end{longtable} % % La tabla~\ref{tab:spshort-siglas} resume las siglas y acrónimos registrados en el módulo Lua \myluamod{}. % % \begin{longtable}{@{\extracolsep{\fill}}l p{3.5cm} c p{5cm}@{}} % \caption{Siglas y acrónimos soportados} \label{tab:spshort-siglas} \\ % \toprule % \textbf{Entrada} & \textbf{Alias / Variantes} & \textbf{Salida PDF} & \multicolumn{1}{c}{\textbf{Lectura NVDA}} \\ % \midrule % \endfirsthead % \caption[]{\emph{(Continuación - Siglas y acrónimos)}} \\ % \toprule % \textbf{Entrada} & \textbf{Alias / Variantes} & \textbf{Salida PDF} & \multicolumn{1}{c}{\textbf{Lectura NVDA}} \\ % \midrule % \endhead % \midrule % \multicolumn{4}{r}{\emph{Continúa en la siguiente página...}} \\ % \endfoot % \bottomrule % \endlastfoot % \multicolumn{4}{c}{\textbf{Siglas (Deletreo o lectura expandida)}} \\ % \midrule % \texttt{dni} & --- & DNI & \emph{«documento nacional de identidad»} \\ % \texttt{rut} & --- & RUT & \emph{«rol único tributario»} \\ % \texttt{run} & --- & RUN & \emph{«rol único nacional»} \\ % \texttt{onu} & --- & ONU & \emph{«organización de las naciones unidas»} \\ % \texttt{rae} & --- & RAE & \emph{«real academia española»} \\ % \texttt{ong} & --- & ONG & \emph{«organización no gubernamental»} \\ % \texttt{ongs} & --- & ONGs & \emph{«organizaciones no gubernamentales»} \\ % \texttt{urss} & --- & URSS & \emph{«unión de repúblicas socialistas soviéticas»} \\ % \texttt{oea} & --- & OEA & \emph{«organización de los estados americanos»} \\ % \texttt{oms} & --- & OMS & \emph{«organización mundial de la salud»} \\ % \texttt{fmi} & --- & FMI & \emph{«fondo monetario internacional»} \\ % \texttt{bid} & --- & BID & \emph{«banco interamericano de desarrollo»} \\ % \texttt{otan} & --- & OTAN & \emph{«organización del tratado del atlántico norte»} \\ % \texttt{ue} & --- & UE & \emph{«unión europea»} \\ % \texttt{ru} & --- & RU & \emph{«reino unido»} \\ % \midrule % \multicolumn{4}{c}{\textbf{Acrónimos silábicos}} \\ % \midrule % \texttt{unicef} & --- & Unicef & \emph{«fondo de las naciones unidas para la infancia»} \\ % \texttt{unesco} & --- & Unesco & \emph{«organización de las naciones unidas para la educación, la ciencia y la cultura»} \\ % \texttt{mercosur} & --- & Mercosur & \emph{«mercado común del sur»} \\ % \texttt{cepal} & --- & Cepal & \emph{«comisión económica para américa latina»} \\ % \texttt{mineduc} & --- & Mineduc & \emph{«ministerio de educación»} \\ % \texttt{conicet} & --- & Conicet & \emph{«consejo nacional de investigaciones»} \\ % \end{longtable} % % La tabla~\ref{tab:spshort-ordinales} resume ejemplos de números ordinales % registrados en el módulo Lua \myluamod{}. % % \begin{longtable}{@{\extracolsep{\fill}}l p{3.5cm} c p{5cm}@{}} % \caption{Números ordinales soportados} \label{tab:spshort-ordinales} \\ % \toprule % \textbf{Entrada} & \textbf{Alias / Variantes} & \textbf{Salida PDF} & \multicolumn{1}{c}{\textbf{Lectura NVDA}} \\ % \midrule % \endfirsthead % \caption[]{\emph{(Continuación - Números ordinales)}} \\ % \toprule % \textbf{Entrada} & \textbf{Alias / Variantes} & \textbf{Salida PDF} & \multicolumn{1}{c}{\textbf{Lectura NVDA}} \\ % \midrule % \endhead % \midrule % \multicolumn{4}{r}{\emph{Continúa en la siguiente página...}} \\ % \endfoot % \bottomrule % \endlastfoot % \multicolumn{4}{c}{\textbf{Sufijos para ordinales (Ejemplos)}} \\ % \midrule % \texttt{1.a} & \texttt{1.ª} & 1.$^{\text{a}}$ & \emph{«primera»} \\ % \texttt{2.o} & \texttt{2.º} & 2.$^{\text{o}}$ & \emph{«segundo»} \\ % \texttt{3.er} & --- & 3.$^{\text{er}}$ & \emph{«tercer»} \\ % \texttt{4.os} & --- & 4.$^{\text{os}}$ & \emph{«cuartos»} \\ % \texttt{5.as} & --- & 5.$^{\text{as}}$ & \emph{«quintas»} \\ % \end{longtable} % % \begin{important}* % Los números ordinales se verbalizan con palabras desde el |1| hasta % el |99|. Desde el |100| el ordinal no se convierte en palabras y se % entrega a \mynvmath{NVDA/MathCAT} tal como se imprime, por ejemplo % |100.o| como \emph{«100º»}. % \end{important} % % \section{Utilidades generales}\label{sec:utilidades} % % Además de los \emph{«cuatro grandes»} y sus asociados, el paquete \mypkg*[no-index]{spintent} % provee tres utilidades de uso general: |\spsiglo| para la escritura y % lectura de siglos, |\sptime| para la escritura y lectura de tiempo y % |\spdate| para la escritura y lectura de fechas. % % El comando |\spsiglo| trabaja en \emph{modo texto} y en \emph{modo % matemático}; |\sptime| y |\spdate| solo en \emph{modo matemático}. Los tres % reciben un \emph{argumento obligatorio}, y un argumento que no cumple alguna % de las formas descritas devuelve un \enquote{error} y se detiene la % compilación del documento. % % \subsection*{El comando \cs[no-index]{spsiglo}}\label{subsec:spsiglo} % % \begin{function}{\spsiglo} % \begin{syntax} % |\spsiglo|\mymarg{siglo} \qquad\qquad |\spsiglo|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{siglo} % \end{syntax} % El comando \ics*{spsiglo} trabaja en \emph{modo texto} y en \emph{modo matemático}, % recibe el \emph{argumento obligatorio} \mymarg{siglo}, el cual puede % ser un \emph{número arábigo} `|21|' o un \emph{número romano} `|XXI|' % mayor que `|0|' y menor que `|4000|'. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % y se ejecuta |\spsiglo{21}| o |\spsiglo{XXI}| se imprimirá `\textsc{xxi}' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«veintiuno»}. % % \begin{important}* % El \emph{argumento obligatorio} \mymarg{siglo} \emph{«siempre»} es % convertido a \emph{número romano en versalitas} y es verbalizado por % \mynvmath{NVDA/MathCAT} como \emph{número arábigo}. Esta es la única utilidad % que provee el paquete \mypkg*[no-index]{spintent} que trabaja en \emph{modo texto} % y en \emph{modo matemático}. Internamente utiliza la llave \mykey{alt} % provista por el paquete \mypkg{tagpdf} en \emph{modo texto} % y usa |\MathMLintent| para el \emph{modo matemático}. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spsiglo}} % % \keyexampcmd{print-era}{ac \textnormal{\textcolor{lightgray}{\textbar}} dc \textnormal{\textcolor{lightgray}{\textbar}} none}{none}[spsiglo] % % Establece si se \emph{imprime} o no la abreviatura después de imprimir el argumento \mymarg{siglo}. % Si se establece en |ac|, |\spsiglo[print-era=ac]{XXI}| imprimirá `\textsc{xxi} a.~C.' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«veintiuno antes de cristo»}; % si se establece en |dc|, |\spsiglo[print-era=dc]{XXI}| imprimirá `\textsc{xxi} d.~C.' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«veintiuno después de cristo»}; % si se establece en |none|, |\spsiglo[print-era=none]{XXI}| imprimirá `\textsc{xxi}' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«veintiuno»}. % % \subsection*{El comando \cs[no-index]{sptime}}\label{subsec:sptime} % % \begin{function}{\sptime} % \begin{syntax} % |\sptime|\mymarg{horas \textnormal{\textcolor{red}{:}} minutos} \qquad\qquad |\sptime|\mymarg{horas \textnormal{\textcolor{red}{:}} minutos \textnormal{\textcolor{red}{:}} segundos} % \end{syntax} % El comando \ics*{sptime} trabaja en \emph{modo matemático} y recibe % el \emph{argumento obligatorio} \mymarg{horas \textnormal{\textcolor{red}{:}} minutos} % o \mymarg{horas \textnormal{\textcolor{red}{:}} minutos \textnormal{\textcolor{red}{:}} segundos}. % \end{function} % % Por ejemplo:~|$\sptime{23:45}$| imprimirá en \emph{«modo texto»} la % salida \texttt{PDF} `23:45' y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«veintitrés cuarenta y cinco minutos»}. % % \subsection*{El comando \cs[no-index]{spdate}}\label{subsec:spdate} % % \begin{function}{\spdate} % \begin{syntax} % |\spdate|\mymarg{\textnormal{DD\textcolor{red}{/}MM\textcolor{red}{/}YYYY}} \hfill |\spdate|\mymarg{\textnormal{YYYY\textcolor{red}{/}MM\textcolor{red}{/}DD}} \hfill |\spdate|\mymarg{\textnormal{YYYY\textcolor{red}{-}MM\textcolor{red}{-}DD}} \hfill |\spdate|\mymarg{\textnormal{DD\textcolor{red}{-}MM\textcolor{red}{-}YYYY}} % \end{syntax} % El comando \ics*{spdate} trabaja en \emph{modo matemático} % y recibe el \emph{argumento} obligatorio % \mymarg{\textnormal{DD\textcolor{red}{/}MM\textcolor{red}{/}YYYY}}, % \mymarg{\textnormal{DD\textcolor{red}{-}MM\textcolor{red}{-}YYYY}} o % \mymarg{\textnormal{YYYY\textcolor{red}{-}MM\textcolor{red}{-}DD}}. % \end{function} % % Por ejemplo:~|$\spdate{1981-01-02}$| y |$\spdate{02/01/1981}$| % imprimirán en \emph{«modo texto»} la salida \texttt{PDF} `1981-01-02' % y `02/01/1981' respectivamente y en ambos casos, \mynvmath{NVDA/MathCAT} % verbalizará \emph{«dos de enero de mil novecientos ochenta y uno»}. % % \begin{important}* % Si se pasa el argumento % \mymarg{\textnormal{YYYY\textcolor{red}{/}MM\textcolor{red}{/}DD}}, % internamente se convertirá en % \mymarg{\textnormal{DD\textcolor{red}{/}MM\textcolor{red}{/}YYYY}} % y se imprimirá de esa forma por compatibilidad con \mynvmath{NVDA/MathCAT}. % \end{important} % % \section{Utilidades para símbolos}\label{sec:simbolos} % % El paquete \mypkg*[no-index]{spintent} provee cinco comandos |\spnewread|, % |\spread|, |\spusep|, |\spdsym| y |\spmsym| para trabajar con \emph{«símbolos matemáticos»} descritos % en esta sección. Están pensados desde el punto de vista docente, para % situaciones donde la lectura de un \emph{«símbolo»} % es relevante. % % El comando |\spnewread| registra la verbalización de un símbolo y trabaja en % \emph{modo texto}; |\spread| usa un símbolo registrado, |\spusep| escribe % paréntesis, corchetes y llaves sueltos, y |\spdsym| y |\spmsym| establecen % cómo se imprime y se verbaliza el símbolo de división y el de multiplicación. % Estos cuatro trabajan en \emph{modo matemático}. % % \begin{important}* % A diferencia de las otras utilidades implementadas por % \mypkg*[no-index]{spintent}, que analizan y dan formato a la salida, estos no % procesan los \emph{«símbolos matemáticos»}, solo afectan la \emph{forma} % semántica en que serán verbalizados por \mynvmath{NVDA/MathCAT}. % \end{important} % % \subsection*{El comando \cs[no-index]{spnewread}}\label{subsec:spnewread} % % \begin{function}{\spnewread} % \begin{syntax} % |\spnewread|\mymarg{símbolo}\mymarg{verbalización \textnormal{NVDA}} % \end{syntax} % El comando \ics*{spnewread} trabaja en \emph{modo texto} y recibe % dos \emph{argumentos obligatorios}: \mymarg{símbolo} y \mymarg{verbalización \textnormal{NVDA}} % los cuales se almacenan de forma interna para ser utilizados como % \mymarg{argumento} en |\spread|. Solo se establece como \mynvmath{NVDA/MathCAT} % verbalizará el \mymarg{símbolo} al utilizarlo con |\spread| sin modificar la impresión de este. % \end{function} % % El argumento \mymarg{símbolo} no debe estar registrado. Si ya está registrado % se devuelve un \enquote{error} y se detiene la compilación del % documento. La declaración es «global» y se almacena en el módulo \myluamod{}. % Por ejemplo:~|\spnewread{\cong}{es-congruente-con}| registrará % `|\cong|' y cuando se utilice |$\spread{\cong}$| se imprimirá % `$\mathcolor{red}{\cong}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«es congruente con»}. % % \begin{important}* % El \mymarg{símbolo} debe ser un único comando, incluso uno % definido por el usuario. Por ejemplo:~|\newcommand{\xcuad}{x^2}|, % |\spnewread{\xcuad}{x-al-cuadrado}| registrará `|\xcuad|' y cuando se % utilice |$\spread{\xcuad}$| se imprimirá `$x^2$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«x al cuadrado»}. % \end{important} % % \begin{important}* % Los espacios y los guiones bajos de \mymarg{verbalización % \textnormal{NVDA}} se convierten en guiones `|-|', por lo que % |{es congruente con}| equivale a |{es-congruente-con}|. Los guiones % repetidos se reducen a uno y se eliminan los del inicio y del final. % Si el resultado queda vacío se devuelve un \enquote{error} y se % detiene la compilación del documento. % \end{important} % % \subsection*{El comando \cs[no-index]{spread}}\label{subsec:spread} % % \begin{function}{\spread} % \begin{syntax} % |\spread|\mymarg{símbolo} \qquad\qquad |\spread|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{símbolo} % |\spread|\myoarg{read-arg\textnormal{\textcolor{gray}{=}}\mymarg{verbalización \textnormal{NVDA}}}\mymarg{comandos \textnormal{\hologo{LaTeX}}} % \end{syntax} % El comando \ics*{spread} trabaja en \emph{modo matemático} y % recibe el \emph{argumento obligatorio} de la forma \mymarg{símbolo} previamente % registrado por el comando |\spnewread| o \mymarg{comandos \textnormal{\hologo{LaTeX}}} % para ser usado junto a la llave \mykey{read-arg}. % \end{function} % % Si el argumento es \mymarg{símbolo}, |$\spread{\cong}$| imprimirá % `$\mathcolor{red}{\cong}$' en la salida \texttt{PDF} y el argumento \mymarg{verbalización \textnormal{NVDA}} % establecido al registrar \mymarg{símbolo} con |\spnewread| se verbalizará % en \mynvmath{NVDA/MathCAT}. % % Si el argumento es \mymarg{comandos \textnormal{\hologo{LaTeX}}} y no % se establece la llave \mykey{read-arg} se devuelve un \enquote{error} % y se detiene la compilación del documento. % % \subsubsection*{Llaves para \cs[no-index]{spread}} % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spread] % % Establece la \emph{verbalización} del argumento \mymarg{comandos \textnormal{\hologo{LaTeX}}}. % El valor de esta llave \emph{siempre} debe pasarse entre llaves `|{ }|' % y sobrescribe la verbalización del \mymarg{símbolo} si está registrado % previamente. Por ejemplo:~|$\spread[read-arg={es-paralela-a}]{\parallel}$| % imprimirá `$\mathcolor{red}{\parallel}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«es paralela a»}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spread] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del argumento \mymarg{símbolo} o \mymarg{comandos \textnormal{\hologo{LaTeX}}} % pasado al comando sin modificar la impresión de este. Si % se establece en |true|, \mynvmath{NVDA/MathCAT} NO verbalizará la % salida del comando; si se establece en |false|, \mynvmath{NVDA/MathCAT} % sí verbalizará la salida del comando. % % \begin{important}* % Al utilizar \mykey{silent} no es necesario que el argumento % \mymarg{símbolo} esté registrado o usar la llave \mykey{read-arg} con % \mymarg{comandos \textnormal{\hologo{LaTeX}}}. Dependiendo de lo que % contenga el \mymarg{argumento}, puede que \mykey{silent} no sea suficiente, % en ese caso utilice |read-arg={_-}|. % \end{important} % % \subsection*{El comando \cs[no-index]{spusep}}\label{subsec:spusep} % % \begin{function}{\spusep} % \begin{syntax} % |\spusep|\mymarg{\normalfont\ttfamily\textbackslash\{\textcolor{lightgray}{\textbar}[\textcolor{lightgray}{\textbar}(\textcolor{lightgray}{\textbar})\textcolor{lightgray}{\textbar}]\textcolor{lightgray}{\textbar}\textbackslash\}} \qquad\qquad |\spusep|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{\normalfont\ttfamily\textbackslash\{\textcolor{lightgray}{\textbar}[\textcolor{lightgray}{\textbar}(\textcolor{lightgray}{\textbar})\textcolor{lightgray}{\textbar}]\textcolor{lightgray}{\textbar}\textbackslash\}} % \end{syntax} % El comando \ics*{spusep} trabaja en \emph{modo matemático} y recibe el % \emph{argumento obligatorio} \mymarg{\normalfont\ttfamily\textbackslash\{\textcolor{lightgray}{\textbar}[\textcolor{lightgray}{\textbar}(\textcolor{lightgray}{\textbar})\textcolor{lightgray}{\textbar}]\textcolor{lightgray}{\textbar}\textbackslash\}}, % el cual debe ser \emph{«solo uno»} de los paréntesis soportados: `\texttt{\textcolor{keyname}{(}}', % `\texttt{\textcolor{keyname}{)}}', `\texttt{\textcolor{keyname}{[}}', `\texttt{\textcolor{keyname}{]}}', % `\texttt{\textcolor{keyname}{\textbackslash\{}}' o `\texttt{\textcolor{keyname}{\textbackslash\}}}'. % \end{function} % % Si no se proporciona el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val}, \myhlc|$\spusep{(}$| % imprimirá `$\mathcolor{red}{(}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«abrir paréntesis»} acorde a su propia heurística. % % \begin{important}* % El motivo de existencia de |\spusep| es que los comandos |\left|, % |\right|, |\Uleft| y |\Uright| provistos por \hologo{LuaTeX} crean % un \emph{grupo} implícito que dificulta el código para \emph{tagged} % \texttt{PDF} cuando se necesita interrumpir algo entre ellos, por % ejemplo al construir expresiones complejas con |\spnZ|, |\spmQ| o % |\spdiv|. El comando |\spusep| imprime el \emph{delimitador} sin crear % ningún grupo. % \end{important} % % \begin{important}* % Hay un segundo motivo para usar |\spusep|. Varios comandos aceptan un % \emph{argumento opcional} entre corchetes, \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % o delimitado por paréntesis, \myparg{número\textnormal{\textcolor{gray}{,}} número\textnormal{\textcolor{gray}{,}} \dots}, % o ambos, escrito justo después del nombre del comando. Si a continuación de % un comando necesita un `\texttt{\textcolor{keyname}{[}}' o un `\texttt{\textcolor{keyname}{(}}' % explícito que no sea ese argumento, escríbalo con |\spusep|; de lo contrario, % \hologo{LaTeX} lo interpretará como el comienzo del argumento opcional. Por % ejemplo, |$\spmedia\spusep{[}$| imprime el símbolo de |\spmedia| seguido de % `$\mathcolor{red}{[}$', mientras que |\spmedia[| haría que \hologo{LaTeX} lea lo que sigue como % llaves \myoarg{key \textnormal{\textcolor{gray}{=}} val}. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spusep}} % % \keyexampcmd{size}{none \textnormal{\textcolor{lightgray}{\textbar}} % big \textnormal{\textcolor{lightgray}{\textbar}} % Big \textnormal{\textcolor{lightgray}{\textbar}} % bigg \textnormal{\textcolor{lightgray}{\textbar}} % Bigg}{none}[spusep] % % Establece el \emph{tamaño del paréntesis} pasado como \mymarg{argumento} % que se imprimirá en la salida \texttt{PDF} al ejecutar el comando sin modificar % la verbalización de este. Si se establece en \mykey{none}, se imprime el \mymarg{argumento} % en el tamaño por defecto del modo matemático en curso; si se establece en \mykey{big}, % se usa |\bigl|/|\bigr| antes del \mymarg{argumento}; si se establece en \mykey{Big}, % se usa |\Bigl|/|\Bigr| antes del \mymarg{argumento}; si se establece en % \mykey{bigg}, se usa |\biggl|/|\biggr| antes del \mymarg{argumento}; y % si se establece en \mykey{Bigg}, se usa |\Biggl|/|\Biggr| antes del \mymarg{argumento}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spusep] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} del \mymarg{argumento} pasado al comando sin % modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se % establece en |false|, sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spdsym}}\label{subsec:spdsym} % % \begin{function}{\spdsym} % \begin{syntax} % |\spdsym| \qquad\qquad |\spdsym|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % \end{syntax} % El comando \ics*{spdsym} trabaja en \emph{modo matemático} y recibe % el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % mediante el cual se establece la \emph{«forma»} en la que se imprimirá el % \emph{símbolo de división} y cómo se verbalizará en \mynvmath{NVDA/MathCAT}. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spdsym$| imprimirá `$\mathcolor{red}{:}$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«dividido»}. % % \subsubsection*{Llaves para \cs[no-index]{spdsym}}\label{subsubsec:key-spdsym} % % \keyexampcmd{prefix}{prefijo}{no usado}[spdsym] % % Establece el \emph{prefijo} que será \emph{verbalizado} por % \mynvmath{NVDA/MathCAT} \emph{«antes»} de \emph{verbalizar} el \mymarg{valor} establecido % por la llave \mykey{read-sym}. El valor de esta llave \emph{siempre} % debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{suffix}{sufijo}{no usado}[spdsym] % % Establece el \emph{sufijo} que será \emph{verbalizado} por % \mynvmath{NVDA/MathCAT} \emph{«después»} de \emph{verbalizar} el \mymarg{valor} establecido % por la llave \mykey{read-sym}. El valor de esta llave \emph{siempre} % debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{set-sym}{old-style \textnormal{\textcolor{lightgray}{\textbar}} two-dots \textnormal{\textcolor{lightgray}{\textbar}} slash}{two-dots}[spdsym] % % Establece el \emph{símbolo de división} que se imprimirá en la salida % \texttt{PDF}. Si se establece en |old-style|, imprimirá % `$\mathcolor{red}{\div}$'; si se establece en |two-dots|, imprimirá % `$\mathcolor{red}{:}$' y si se establece en |slash|, imprimirá % `$\mathcolor{red}{/}$'. % % \keyexampcmd{read-sym}{dividido \textnormal{\textcolor{lightgray}{\textbar}} dividido-por \textnormal{\textcolor{lightgray}{\textbar}} dividido-entre}{dividido}[spdsym] % % Establece la \emph{forma} con la cual \mynvmath{NVDA/MathCAT} verbalizará el % \emph{símbolo de división}. Si se establece en |dividido|, \mynvmath{NVDA/MathCAT} % verbalizará \emph{«dividido»}; si se establece en |dividido-por|, \mynvmath{NVDA/MathCAT} % verbalizará \emph{«dividido por»} y si se establece en |dividido-entre|, % \mynvmath{NVDA/MathCAT} verbalizará \emph{«dividido entre»}. % % \subsection*{El comando \cs[no-index]{spmsym}}\label{subsec:spmsym} % % \begin{function}{\spmsym} % \begin{syntax} % |\spmsym| \qquad\qquad |\spmsym|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % \end{syntax} % El comando \ics*{spmsym} trabaja en \emph{modo matemático} y recibe % el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % mediante el cual se establece la \emph{«forma»} en la que se imprimirá el % \emph{símbolo de multiplicación} y cómo se verbalizará en \mynvmath{NVDA/MathCAT}. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spmsym$| imprimirá `$\mathcolor{red}{\cdot}$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«por»}. % % \subsubsection*{Llaves para \cs[no-index]{spmsym}}\label{subsubsec:key-spmsym} % % \keyexampcmd{prefix}{prefijo}{no usado}[spmsym] % % Establece el \emph{prefijo} que será \emph{verbalizado} por \mynvmath{NVDA/MathCAT} % \emph{«antes»} de \emph{verbalizar} el \mymarg{valor} establecido por % la llave \mykey{read-sym}. El valor de esta llave \emph{siempre} % debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{suffix}{sufijo}{no usado}[spmsym] % % Establece el \emph{sufijo} que será \emph{verbalizado} por \mynvmath{NVDA/MathCAT} % \emph{«después»} de \emph{verbalizar} el \mymarg{valor} establecido por % la llave \mykey{read-sym}. El valor de esta llave \emph{siempre} % debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{set-sym}{cross \textnormal{\textcolor{lightgray}{\textbar}} dot \textnormal{\textcolor{lightgray}{\textbar}} asterisk}{dot}[spmsym] % % Establece el \emph{símbolo de multiplicación} que se imprimirá en la salida % \texttt{PDF}. Si se establece en |cross|, imprimirá % `$\mathcolor{red}{\times}$'; si se establece en |dot|, imprimirá % `$\mathcolor{red}{\cdot}$' y si se establece en |asterisk|, imprimirá % `$\mathcolor{red}{\ast}$'. % % \keyexampcmd{read-sym}{por \textnormal{\textcolor{lightgray}{\textbar}} multiplicado-por \textnormal{\textcolor{lightgray}{\textbar}} veces}{por}[spmsym] % % Establece la \emph{forma} con la cual \mynvmath{NVDA/MathCAT} % verbalizará el \emph{símbolo de multiplicación}. Si se establece en |por|, % \mynvmath{NVDA/MathCAT} verbalizará \emph{«por»}; si se establece en % |multiplicado-por|, \mynvmath{NVDA/MathCAT} verbalizará \emph{«multiplicado por»} % y si se establece en |veces|, \mynvmath{NVDA/MathCAT} verbalizará \emph{«veces»}. % % \keyexampcmd{print-sym}{true \textnormal{\textcolor{lightgray}{\textbar}} false}{true}[spmsym] % % Establece si se \emph{imprime} el \emph{símbolo de multiplicación}. % Si se establece en |false|, \mynvmath{NVDA/MathCAT} % verbalizará el \emph{símbolo de multiplicación} acorde al valor establecido % por la llave \mykey{read-sym}, pero no se imprimirá este en % la salida \texttt{PDF}. % % \section{Utilidades para números}\label{sec:numeros} % % El paquete \mypkg*[no-index]{spintent} provee diez comandos para trabajar con % números, descritos en esta sección: |\spdiv| y |\spmul| para la división y % la multiplicación, |\spnD| y |\spnM| para los divisores y los múltiplos de un % número, |\spmcd| y |\spmcm| para el máximo común divisor y el mínimo común % múltiplo, |\spnZ| para los números enteros, |\spmQ| para los números mixtos % y |\sprazon| y |\spProp| para las razones y las proporciones. Todos trabajan % en \emph{modo matemático}. % % Los comandos |\spnD|, |\spnM|, |\spmcd| y |\spmcm| reciben un \emph{argumento % opcional delimitado por paréntesis}; los demás reciben \emph{argumentos % obligatorios}. Los argumentos numéricos de varios de ellos se procesan % internamente bajo |\spnum|, de modo que siguen sus reglas de escritura % (\S\ref{subsec:spnum}). % % \subsection*{El comando \cs[no-index]{spdiv}}\label{subsec:spdiv} % % \begin{function}{\spdiv} % \begin{syntax} % |\spdiv|\mymarg{dividendo}\mymarg{divisor} \qquad\qquad |\spdiv|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{dividendo}\mymarg{divisor} % \end{syntax} % El comando \ics*{spdiv} trabaja en \emph{modo matemático} y recibe % dos \emph{argumentos obligatorios} \mymarg{dividendo} y \mymarg{divisor}. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spdiv{20}{26}$| imprimirá `$20:26$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«veinte dividido veintiséis»}. % % \subsubsection*{Llaves para \cs[no-index]{spdiv}} % % El comando |\spdiv| posee las \mymeta{meta-keys} \mykey[spdiv]{cifras-sep}, % \mykey[spdiv]{read-sign}, \mykey[spdiv]{read-period-sep} y % \mykey[spdiv]{notation-sym} pasadas al comando |\spnum| (\S\ref{subsubsec:key-spnum}) % junto a las \mymeta{meta-keys} \mykey[spdiv]{set-sym} y \mykey[spdiv]{read-sym} % pasadas al comando |\spdsym| (v.\,pág.\,\pageref{subsubsec:key-spdsym}). % % \keyexampcmd{wrap-parens}{true \textnormal{\textcolor{lightgray}{\textbar}} false}{false}[spdiv] % % Establece si se \emph{imprimen} paréntesis redondos `|(|\dots|)|' % alrededor de los \emph{argumentos} \mymarg{dividendo} y \mymarg{divisor} % pasados a |\spdiv|. Si se establece en |true|, se imprimirán paréntesis % redondos alrededor de ambos \emph{argumentos}; si se establece en |false|, % no se imprimirán los paréntesis. % % \keyexampcmd{read-parens}{true \textnormal{\textcolor{lightgray}{\textbar}} false}{true}[spdiv] % % Establece si \mynvmath{NVDA/MathCAT} \emph{verbalizará} los paréntesis % redondos `|(|\dots|)|' alrededor de los \emph{argumentos} \mymarg{dividendo} % y \mymarg{divisor} pasados a |\spdiv|. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} verbalizará la lectura de los paréntesis; % si se establece en |false|, \mynvmath{NVDA/MathCAT} no los verbalizará. % % \subsection*{El comando \cs[no-index]{spmul}}\label{subsec:spmul} % % \begin{function}{\spmul} % \begin{syntax} % |\spmul|\mymarg{factor}\mymarg{factor} \qquad\qquad |\spmul|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{factor}\mymarg{factor} % \end{syntax} % El comando \ics*{spmul} trabaja en \emph{modo matemático} y recibe % dos \emph{argumentos obligatorios} \mymarg{factor} y \mymarg{factor}. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spmul{20}{26}$| imprimirá `$20 \cdot 26$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«veinte por veintiséis»}. % % \subsubsection*{Llaves para \cs[no-index]{spmul}} % % El comando |\spmul| posee las \mymeta{meta-keys} \mykey[spmul]{cifras-sep}, % \mykey[spmul]{read-sign}, \mykey[spmul]{read-period-sep} y % \mykey[spmul]{notation-sym} pasadas al comando |\spnum| (\S\ref{subsubsec:key-spnum}) % junto a las \mymeta{meta-keys} \mykey[spmul]{set-sym} y \mykey[spmul]{read-sym} % pasadas al comando |\spmsym| (v.\,pág.\,\pageref{subsubsec:key-spmsym}). % % \keyexampcmd{wrap-parens}{true \textnormal{\textcolor{lightgray}{\textbar}} false}{false}[spmul] % % Establece si se \emph{imprimen} paréntesis redondos `|(|\dots|)|' % alrededor de los \emph{argumentos} \mymarg{factor} y \mymarg{factor} % pasados a |\spmul|. Si se establece en |true|, se imprimirán paréntesis % redondos alrededor de ambos \emph{argumentos}; si se establece en |false|, % no se imprimirán los paréntesis. % % \keyexampcmd{read-parens}{true \textnormal{\textcolor{lightgray}{\textbar}} false}{true}[spmul] % % Establece si \mynvmath{NVDA/MathCAT} \emph{verbalizará} los paréntesis % redondos `|(|\dots|)|' alrededor de los \emph{argumentos} \mymarg{factor} % y \mymarg{factor} pasados a |\spmul|. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} verbalizará la lectura de los paréntesis; % si se establece en |false|, \mynvmath{NVDA/MathCAT} no los verbalizará. % % \subsection*{El comando \cs[no-index]{spnD}}\label{subsec:spnD} % % \begin{function}{\spnD} % \begin{syntax} % |\spnD| \hfill |\spnD|\myparg{número} \hfill |\spnD|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{número} \hfill |\spnD|\myparg{letra} \hfill |\spnD|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{letra} % \end{syntax} % El comando \ics*{spnD} trabaja en \emph{modo matemático} y recibe % el \emph{argumento opcional delimitado por paréntesis} de la forma % \myparg{número} que se procesa internamente por |\spnum| o \myparg{letra} % que se ejecuta de manera directa. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % ni el \emph{argumento opcional delimitado por paréntesis}, |$\spnD$| % imprimirá `$\mathrm{D}(n)$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«divisores de ene»}. % % Si se usa el argumento \myparg{número}, |$\spnD(6)$| imprimirá % `$\mathrm{D}(6)$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«divisores de seis»}. % % Si se usa el argumento \myparg{letra}, |$\spnD(m)$| imprimirá % `$\mathrm{D}(m)$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«divisores de eme»}. % % \begin{important}* % El número |0| no es válido: |$\spnD(0)$| devuelve un % \enquote{error} y se detiene la compilación del documento. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spnD}} % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spnD] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el concepto \emph{«divisores de»} al ejecutar el comando sin modificar % la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} verbalizará \emph{«divisores de»} y el \myparg{argumento}; % si se establece en |false|, \mynvmath{NVDA/MathCAT} solo verbalizará el % \myparg{argumento}. % % \keyexampcmd{prefix}{prefijo}{no usado}[spnD] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el concepto \emph{«divisores de»}. El valor % de esta llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{result}{true \textnormal{% % \textcolor{lightgray}{\textbar}} false}{false}[spnD] % % Establece si se \emph{imprime} el resultado de calcular los \emph{divisores} % del argumento \myparg{número}. Si se establece en |true|, |$\spnD[result=true](6)$| % imprimirá `$\mathrm{D}(6) = \{1, 2, 3, 6\}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«divisores de seis son uno, % dos, tres y seis»}; si se establece en |false|, no se imprimirá ni verbalizará % el resultado de calcular los \emph{divisores}. % % \keyexampcmd{result-num}{número natural}{todos}[spnD] % % Establece la \emph{cantidad} de divisores que se mostrarán en la salida % \texttt{PDF} al establecer \mykey{result}|=true|. Si se establece en |2|, solo % se imprimirán dos valores y luego se añadirá `\dots' en la salida % \texttt{PDF}; si no se establece, se imprimirán todos los divisores, % porque el conjunto es finito. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spnD] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del concepto \emph{«divisores de»} y del \myparg{argumento} pasado al comando % sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se establece % en |false|, \mynvmath{NVDA/MathCAT} sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spnM}}\label{subsec:spnM} % % \begin{function}{\spnM} % \begin{syntax} % |\spnM| \hfill |\spnM|\myparg{número} \hfill |\spnM|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{número} \hfill |\spnM|\myparg{letra} \hfill |\spnM|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{letra} % \end{syntax} % El comando \ics*{spnM} trabaja en \emph{modo matemático} y recibe % el \emph{argumento opcional delimitado por paréntesis} de la forma % \myparg{número} que se procesa internamente por |\spnum| o \myparg{letra} % que se ejecuta de manera directa. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % ni el \emph{argumento opcional delimitado por paréntesis}, |$\spnM$| % imprimirá `$\mathrm{M}(n)$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«múltiplos de ene»}. % % Si se usa el argumento \myparg{número}, |$\spnM(6)$| imprimirá % `$\mathrm{M}(6)$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«múltiplos de seis»}. % % Si se usa el argumento \myparg{letra}, |$\spnM(m)$| imprimirá % `$\mathrm{M}(m)$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«múltiplos de eme»}. % % \begin{important}* % El número |0| no es válido: |$\spnM(0)$| devuelve un % \enquote{error} y se detiene la compilación del documento. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spnM}} % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spnM] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el concepto \emph{«múltiplos de»} al ejecutar el comando sin modificar % la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} verbalizará \emph{«múltiplos de»} y el \myparg{argumento}; % si se establece en |false|, \mynvmath{NVDA/MathCAT} solo verbalizará el % \myparg{argumento}. % % \keyexampcmd{prefix}{prefijo}{no usado}[spnM] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el concepto \emph{«múltiplos de»}. El valor % de esta llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{result}{true \textnormal{% % \textcolor{lightgray}{\textbar}} false}{false}[spnM] % % Establece si se \emph{imprime} el resultado de calcular los \emph{múltiplos} % del argumento \myparg{número}. Si se establece en |true|, |$\spnM[result=true](6)$| % imprimirá `$\mathrm{M}(6) = \{6, 12, 18, 24, 30, \dots\}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«múltiplos de seis son seis, % doce, dieciocho, veinticuatro, treinta puntos suspensivos»}; si se % establece en |false|, no se imprimirá ni verbalizará el resultado de % calcular los \emph{múltiplos}. % % \keyexampcmd{result-num}{número natural}{5}[spnM] % % Establece la \emph{cantidad} de números que se mostrarán en la salida % \texttt{PDF} al establecer \mykey{result}|=true|. Si se establece en |2|, solo % se imprimirán dos valores en la salida \texttt{PDF}; si no se establece, % se imprimirán como máximo |5| valores y luego se añadirá `\dots' en la % salida \texttt{PDF}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spnM] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del concepto \emph{«múltiplos de»} y del \myparg{argumento} pasado al comando % sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se establece % en |false|, \mynvmath{NVDA/MathCAT} sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spmcd}}\label{subsec:spmcd} % % \begin{function}{\spmcd} % \begin{syntax} % |\spmcd| \hfill |\spmcd|\myparg{número\textnormal{\textcolor{gray}{,}} número\textnormal{\textcolor{gray}{,}} \dots} \hfill |\spmcd|\myparg{letra\textnormal{\textcolor{gray}{,}} letra\textnormal{\textcolor{gray}{,}} \dots} \hfill |\spmcd|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{número\textnormal{\textcolor{gray}{,}} número\textnormal{\textcolor{gray}{,}} \dots} % \end{syntax} % El comando \ics*{spmcd} trabaja en \emph{modo matemático} y recibe el % \emph{argumento opcional delimitado por paréntesis} de la forma % \myparg{número\textnormal{\textcolor{gray}{,}} número\textnormal{\textcolor{gray}{,}} \dots} % que se procesa internamente por |\spnum| o \myparg{letra\textnormal{\textcolor{gray}{,}} letra\textnormal{\textcolor{gray}{,}} \dots} % que se ejecuta de manera directa. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % ni el \emph{argumento opcional delimitado por paréntesis}, |$\spmcd$| % imprimirá `$\mathrm{mcd}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«máximo común divisor»}. % % Si se usa el argumento \myparg{número\textnormal{\textcolor{gray}{,}} número\textnormal{\textcolor{gray}{,}} \dots}, % |$\spmcd(2, 3, 6)$| imprimirá `$\mathrm{mcd}(2, 3, 6)$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«máximo común % divisor entre dos, tres y seis»}. % % Si se usa el argumento \myparg{letra\textnormal{\textcolor{gray}{,}} letra\textnormal{\textcolor{gray}{,}} \dots}, % |$\spmcd(m, n)$| imprimirá `$\mathrm{mcd}(m, n)$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«máximo común % divisor entre eme y ene»}. % % \begin{important}* % Los argumentos \myparg{número\textnormal{\textcolor{gray}{,}} número\textnormal{\textcolor{gray}{,}} \dots} % o \myparg{letra\textnormal{\textcolor{gray}{,}} letra\textnormal{\textcolor{gray}{,}} \dots} % requieren al menos dos elementos. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spmcd}} % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spmcd] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el concepto establecido por la llave \mykey{read-sty} al ejecutar el comando % sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} verbalizará \emph{«máximo común divisor \dots»} % o \emph{«m c d \dots»} y el \myparg{argumento}; si se establece en |false|, \mynvmath{NVDA/MathCAT} % solo verbalizará el \myparg{argumento}. % % \keyexampcmd{read-sty}{terse \textnormal{% % \textcolor{lightgray}{\textbar}} clear}{clear}[spmcd] % % Establece el \emph{concepto} que verbalizará \mynvmath{NVDA/MathCAT} % al ejecutar el comando sin modificar la impresión de este. Si se % establece en |terse|, \mynvmath{NVDA/MathCAT} verbalizará \emph{«m c d»}; % si se establece en |clear|, \mynvmath{NVDA/MathCAT} verbalizará % \emph{«máximo común divisor»}. % % \keyexampcmd{upper-case}{true \textnormal{% % \textcolor{lightgray}{\textbar}} false}{false}[spmcd] % % Establece si se usarán \emph{mayúsculas} en la salida \texttt{PDF} al % ejecutar el comando sin modificar la verbalización de este. Si se establece en |true|, se % imprimirá `$\mathrm{MCD}$' en la salida \texttt{PDF}; si se establece en % |false|, se imprimirá `$\mathrm{mcd}$' en la salida \texttt{PDF}. % % \keyexampcmd{prefix}{prefijo}{no usado}[spmcd] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el concepto establecido por la llave \mykey{read-sty}. % El valor de esta llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{result}{true \textnormal{% % \textcolor{lightgray}{\textbar}} false}{false}[spmcd] % % Establece si se \emph{imprime} el resultado de calcular el \emph{«máximo % común divisor»} del argumento \myparg{número\textnormal{\textcolor{gray}{,}} número\textnormal{\textcolor{gray}{,}} \dots}. % Si se establece en |true|: |$\spmcd[result=true](2, 3, 6)$| imprimirá % `$\mathrm{mcd}(2, 3, 6) = 1$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«máximo común divisor entre % dos, tres y seis es igual a uno»}; si se establece en |false| no se % imprimirá ni verbalizará el resultado de calcular el % \emph{«máximo común divisor»}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spmcd] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del concepto \emph{«máximo común divisor \dots»} y del \myparg{argumento} % pasado al comando sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se establece % en |false|, \mynvmath{NVDA/MathCAT} sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spmcm}}\label{subsec:spmcm} % % \begin{function}{\spmcm} % \begin{syntax} % |\spmcm| \hfill |\spmcm|\myparg{número\textnormal{\textcolor{gray}{,}} número\textnormal{\textcolor{gray}{,}} \dots} \hfill |\spmcm|\myparg{letra\textnormal{\textcolor{gray}{,}} letra\textnormal{\textcolor{gray}{,}} \dots} \hfill |\spmcm|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{número\textnormal{\textcolor{gray}{,}} número\textnormal{\textcolor{gray}{,}} \dots} % \end{syntax} % El comando \ics*{spmcm} trabaja en \emph{modo matemático} y recibe el % \emph{argumento opcional delimitado por paréntesis} de la forma % \myparg{número\textnormal{\textcolor{gray}{,}} número\textnormal{\textcolor{gray}{,}} \dots} % que se procesa internamente por |\spnum| o \myparg{letra\textnormal{\textcolor{gray}{,}} letra\textnormal{\textcolor{gray}{,}} \dots} % que se ejecuta de manera directa. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % ni el \emph{argumento opcional delimitado por paréntesis}, |$\spmcm$| % imprimirá `$\mathrm{mcm}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«mínimo común múltiplo»}. % % Si se usa el argumento \myparg{número\textnormal{\textcolor{gray}{,}} número\textnormal{\textcolor{gray}{,}} \dots}, % |$\spmcm(2, 3, 6)$| imprimirá `$\mathrm{mcm}(2, 3, 6)$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«mínimo común % múltiplo entre dos, tres y seis»}. % % Si se usa el argumento \myparg{letra\textnormal{\textcolor{gray}{,}} letra\textnormal{\textcolor{gray}{,}} \dots}, % |$\spmcm(m, n)$| imprimirá `$\mathrm{mcm}(m, n)$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«mínimo común % múltiplo entre eme y ene»}. % % \begin{important}* % Los argumentos \myparg{número\textnormal{\textcolor{gray}{,}} número\textnormal{\textcolor{gray}{,}} \dots} % o \myparg{letra\textnormal{\textcolor{gray}{,}} letra\textnormal{\textcolor{gray}{,}} \dots} % requieren al menos dos elementos. % \end{important} % \subsubsection*{Llaves para \cs[no-index]{spmcm}} % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spmcm] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el concepto establecido por la llave \mykey{read-sty} al ejecutar el comando % sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} verbalizará \emph{«mínimo común múltiplo \dots»} % o \emph{«m c m \dots»} y el \myparg{argumento}; si se establece en |false|, \mynvmath{NVDA/MathCAT} % solo verbalizará el \myparg{argumento}. % % \keyexampcmd{read-sty}{terse \textnormal{% % \textcolor{lightgray}{\textbar}} clear}{clear}[spmcm] % % Establece el \emph{concepto} que verbalizará \mynvmath{NVDA/MathCAT} % al ejecutar el comando sin modificar la impresión de este. Si se % establece en |terse|, \mynvmath{NVDA/MathCAT} verbalizará \emph{«m c m»}; % si se establece en |clear|, \mynvmath{NVDA/MathCAT} verbalizará % \emph{«mínimo común múltiplo»}. % % \keyexampcmd{upper-case}{true \textnormal{% % \textcolor{lightgray}{\textbar}} false}{false}[spmcm] % % Establece si se usarán \emph{mayúsculas} en la salida \texttt{PDF} al % ejecutar el comando sin modificar la verbalización de este. Si se establece en |true|, se % imprimirá `$\mathrm{MCM}$' en la salida \texttt{PDF}; si se establece en % |false|, se imprimirá `$\mathrm{mcm}$' en la salida \texttt{PDF}. % % \keyexampcmd{prefix}{prefijo}{no usado}[spmcm] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el concepto establecido por la llave \mykey{read-sty}. % El valor de esta llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{result}{true \textnormal{% % \textcolor{lightgray}{\textbar}} false}{false}[spmcm] % % Establece si se \emph{imprime} el resultado de calcular el \emph{«mínimo % común múltiplo»} del argumento \myparg{número\textnormal{\textcolor{gray}{,}} número\textnormal{\textcolor{gray}{,}} \dots}. % Si se establece en |true|: |$\spmcm[result=true](2, 3, 6)$| imprimirá % `$\mathrm{mcm}(2, 3, 6) = 6$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«mínimo común múltiplo entre % dos, tres y seis es igual a seis»}; si se establece en |false| no se % imprimirá ni verbalizará el resultado de calcular el % \emph{«mínimo común múltiplo»}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spmcm] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del concepto \emph{«mínimo común múltiplo \dots»} y del \myparg{argumento} % pasado al comando sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se establece % en |false|, \mynvmath{NVDA/MathCAT} sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spnZ}}\label{subsec:spnZ} % % \begin{function}{\spnZ} % \begin{syntax} % |\spnZ|\mymarg{número entero} \qquad\qquad |\spnZ|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{número entero} % \end{syntax} % El comando \ics*{spnZ} trabaja en \emph{modo matemático} y recibe % el \emph{argumento} obligatorio \mymarg{número entero}, el cual se procesa % internamente bajo el comando |\spnum|. % Este comando está dedicado exclusivamente al manejo de \emph{«números enteros»}, % y añade algunas utilidades prácticas para el demarcado e % interacción con \mynvmath{NVDA/MathCAT}. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spnZ{23}$| imprimirá `$23$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«veintitrés»}; |$\spnZ{-23}$| % imprimirá `$-23$' y verbalizará \emph{«menos veintitrés»}. El argumento % debe ser un entero: con un decimal o una unidad, el comando devuelve un % \enquote{error} y se detiene la compilación del documento. % % \subsubsection*{Llaves para \cs[no-index]{spnZ}} % % El comando |\spnZ| posee las \mymeta{meta-keys} \mykey[spnZ]{cifras-sep} % y \mykey[spnZ]{read-sign} pasadas al comando |\spnum| (\S\ref{subsubsec:key-spnum}). % % \keyexampcmd{wrap-parens}{true \textnormal{% % \textcolor{lightgray}{\textbar}} false}{false}[spnZ] % % Establece si se \emph{imprimen} paréntesis redondos `|(|\dots|)|' % alrededor del argumento \mymarg{número entero} pasado a |\spnZ|. Si se % establece en |true|, se imprimirán paréntesis redondos alrededor del % \mymarg{número entero}; si se establece en |false|, no se imprimirán % los paréntesis. Por ejemplo:~|$\spnZ[wrap-parens=true]{-23}$| imprimirá % `$(-23)$' en la salida \texttt{PDF}. % % \keyexampcmd{read-parens}{true \textnormal{% % \textcolor{lightgray}{\textbar}} false}{true}[spnZ] % % Establece si \mynvmath{NVDA/MathCAT} \emph{verbalizará} los paréntesis redondos % `|(|\dots|)|' alrededor del \emph{argumento} \mymarg{número entero} pasado a % |\spnZ|. Si se establece en |true|, \mynvmath{NVDA/MathCAT} verbalizará la lectura % de los paréntesis `|(|\dots|)|'; si se establece en |false|, \mynvmath{NVDA/MathCAT}\newline % no los verbalizará:~|$\spnZ[wrap-parens=true,read-parens=false]{-23}$| % verbalizará \emph{«menos veintitrés»}. % % \subsection*{El comando \cs[no-index]{spmQ}}\label{subsec:spmQ} % % \begin{function}{\spmQ} % \begin{syntax} % |\spmQ|\mymarg{entero}\mymarg{numerador}\mymarg{denominador} \hfill |\spmQ|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{entero}\mymarg{numerador}\mymarg{denominador} % \end{syntax} % El comando \ics*{spmQ} trabaja en \emph{modo matemático} y recibe % \emph{tres argumentos obligatorios}: \mymarg{número entero}, % \mymarg{numerador} y \mymarg{denominador}, los procesa internamente % y devuelve un \emph{«número mixto»}. Los tres deben ser \emph{enteros}; % el \mymarg{numerador} y el \mymarg{denominador} no pueden llevar signo % y el \mymarg{denominador} no puede ser cero. Cualquier otro valor % devuelve un \enquote{error} y se detiene la compilación del documento. % \end{function} % % Si no se proporciona el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spmQ{2}{5}{7}$| imprimirá `$\scalerel{2}{\frac{5}{7}}$' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«dos y cinco séptimos»}. % % \begin{important}* % En el caso de las fracciones, y en especial con los \emph{«números % mixtos»}, no se pueden manejar muchas cosas desde el lado de % \mypkg*[no-index]{spintent}. Por ejemplo, la entrada |$3\frac{5}{7}$| % con \emph{tagged} \texttt{PDF} se verbaliza por \mynvmath{NVDA/MathCAT} % como \emph{«tres más cinco séptimos»}, lo cual no está mal, pero % pedagógicamente hablando no es del todo correcto. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spmQ}} % % \keyexampcmd{join-word}{entero \textnormal{\textcolor{lightgray}{\textbar}} % enteros \textnormal{\textcolor{lightgray}{\textbar}} y}{y}[spmQ] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbaliza la % conexión entre el entero y la fracción que forman el \emph{número mixto}. % Si se establece en |entero|, |$\spmQ[join-word=entero]{1}{5}{7}$| % imprimirá `$\scalerel{1}{\frac{5}{7}}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará % \emph{«un entero cinco séptimos»}; % si se establece en |enteros|, |$\spmQ[join-word=enteros]{2}{5}{7}$| % imprimirá `$\scalerel{2}{\frac{5}{7}}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará % \emph{«dos enteros cinco séptimos»}; % si se establece en |y|, |$\spmQ[join-word=y]{2}{5}{7}$| % imprimirá `$\scalerel{2}{\frac{5}{7}}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará % \emph{«dos y cinco séptimos»}. % % \keyexampcmd{use-spnZ}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spmQ] % % Establece si el \mymarg{numerador} y el \mymarg{denominador} que forman % la fracción del \emph{número mixto} se procesan internamente bajo el % comando |\spnZ|, añadiendo así \emph{semántica de entero} en la % estructura \mynvmath{MathML} de la salida \texttt{PDF}. Si se establece % en |true|, \mymarg{numerador} y \mymarg{denominador} se pasarán a |\spnZ| % y, por ejemplo, |$\spmQ{2}{5}{7}$| se verbalizará % \emph{«dos y cinco séptimos»}; si se establece en |false|, los % argumentos se tipografían directamente sin procesamiento semántico % adicional y \mynvmath{NVDA/MathCAT} aplica sus propias reglas a la fracción: % |$\spmQ[use-spnZ=false]{2}{5}{7}$| también se verbalizará % \emph{«dos y cinco séptimos»}, de modo que la diferencia está solo en la % estructura \mynvmath{MathML} de la salida \texttt{PDF}. % % \keyexampcmd{use-dfrac}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spmQ] % % Establece si la fracción del \emph{número mixto} se imprime usando % |\dfrac| en lugar de |\frac|. Si se establece en |true|, % |$\spmQ[use-dfrac=true]{2}{5}{7}$| imprimirá la fracción en tamaño % \emph{display} `$\scalerel{2}{\dfrac{5}{7}}$' en la salida \texttt{PDF}; % si se establece en |false|, la fracción se imprime con |\frac| siguiendo % el tamaño matemático del contexto en curso. % % \keyexampcmd{wrap-parens}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spmQ] % % Establece si se \emph{imprimen} paréntesis redondos `|(|\dots|)|' % alrededor del \emph{número mixto} completo devuelto por |\spmQ|. Si se % establece en |true|, se imprimirán paréntesis redondos cuyo tamaño se % ajusta automáticamente al estilo matemático en curso; si se establece en % |false|, no se imprimirán los paréntesis. % % \keyexampcmd{read-parens}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spmQ] % % Establece si \mynvmath{NVDA/MathCAT} \emph{verbalizará} los paréntesis % redondos `|(|\dots|)|' alrededor del \emph{número mixto} cuando la llave % \mykey{wrap-parens} está activa. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} verbalizará la lectura de los paréntesis; si se % establece en |false|, \mynvmath{NVDA/MathCAT} no los verbalizará. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spmQ] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} del \emph{número mixto} sin modificar la % impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} NO % verbalizará la salida del comando; si se establece en |false|, sí % verbalizará la salida del comando. Un argumento inválido devuelve siempre un % \enquote{error} y se detiene la compilación del documento, % también con \mykey{silent}|=true|. % % \subsection*{El comando \cs[no-index]{sprazon}}\label{subsec:sprazon} % % \begin{function}{\sprazon} % \begin{syntax} % |\sprazon|\mymarg{antecedente}\mymarg{consecuente} \quad\quad |\sprazon|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{antecedente}\mymarg{consecuente} % \end{syntax} % El comando \ics*{sprazon} trabaja en \emph{modo matemático} y recibe % \emph{dos argumentos obligatorios}: \mymarg{antecedente} y % \mymarg{consecuente}, los tipografía como una fracción y devuelve una % \emph{«razón»}. % \end{function} % % Si no se proporciona el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\sprazon{3}{4}$| imprimirá `$\frac{3}{4}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«tres es a cuatro»}. % % \begin{important}* % El \mymarg{antecedente} y el \mymarg{consecuente} se tipografían % directamente, sin procesamiento semántico adicional. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{sprazon}} % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[sprazon] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} de la \emph{razón} pasada al comando sin % modificar la impresión de esta. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se % establece en |false|, sí verbalizará la salida del comando. % % \keyexampcmd{use-dfrac}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[sprazon] % % Establece si la fracción de la \emph{razón} se imprime usando |\dfrac| en % lugar de |\frac|. Si se establece en |true|, % |$\sprazon[use-dfrac=true]{3}{4}$| imprimirá la fracción en tamaño % \emph{display} `$\dfrac{3}{4}$' en la salida \texttt{PDF}; si se establece % en |false|, la fracción se imprime con |\frac| siguiendo el tamaño % matemático del contexto en curso. % % \keyexampcmd{inline}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[sprazon] % % Establece si la \emph{razón} se imprime en línea con dos puntos `|:|' en % lugar de una fracción. Si se establece en |true|, % |$\sprazon[inline=true]{3}{4}$| imprimirá `$3:4$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«tres es a cuatro»}; si se establece en |false|, la razón se % imprime como una fracción. % % \begin{important}* % La llave \mykey{use-dfrac} no tiene efecto cuando la llave % \mykey{inline} se establece en |true|, porque en ese caso no se % imprime una fracción. % \end{important} % % \subsection*{El comando \cs[no-index]{spProp}}\label{subsec:spProp} % % \begin{function}{\spProp} % \begin{syntax} % |\spProp|\mymarg{a}\mymarg{b}\mymarg{c}\mymarg{d} \quad\quad |\spProp|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{a}\mymarg{b}\mymarg{c}\mymarg{d} % \end{syntax} % El comando \ics*{spProp} trabaja en \emph{modo matemático} y recibe % \emph{cuatro argumentos obligatorios}: \mymarg{a} y \mymarg{b} forman % la primera \emph{razón}, \mymarg{c} y \mymarg{d} forman la segunda, y % devuelve una \emph{«proporción»} con las dos razones unidas por el signo % igual. % \end{function} % % Si no se proporciona el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spProp{3}{4}{6}{8}$| imprimirá % `$\frac{3}{4}=\frac{6}{8}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará % \emph{«tres es a cuatro como seis es a ocho»}. % % \begin{important}* % Los cuatro argumentos se tipografían directamente, sin procesamiento % semántico adicional. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spProp}} % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spProp] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} de la \emph{proporción} pasada al comando sin % modificar la impresión de esta. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se % establece en |false|, sí verbalizará la salida del comando. % % \keyexampcmd{use-dfrac}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spProp] % % Establece si las fracciones de la \emph{proporción} se imprimen usando % |\dfrac| en lugar de |\frac|. Si se establece en |true|, % |$\spProp[use-dfrac=true]{3}{4}{6}{8}$| imprimirá las fracciones en tamaño % \emph{display} `$\dfrac{3}{4}=\dfrac{6}{8}$' en la salida \texttt{PDF}; si se % establece en |false|, las fracciones se imprimen con |\frac| siguiendo el % tamaño matemático del contexto en curso. % % \keyexampcmd{inline}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spProp] % % Establece si las \emph{razones} de la \emph{proporción} se imprimen en línea % con dos puntos `|:|' en lugar de fracciones. Si se establece en |true|, % |$\spProp[inline=true]{3}{4}{6}{8}$| imprimirá `$3:4=6:8$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«tres es a cuatro como seis es a ocho»}; si se establece en |false|, % las razones se imprimen como fracciones. % % \begin{important}* % La llave \mykey{use-dfrac} no tiene efecto cuando la llave % \mykey{inline} se establece en |true|, porque en ese caso no se % imprimen fracciones. % \end{important} % % \section{Utilidades para geometría}\label{sec:geometria} % % El paquete \mypkg*[no-index]{spintent} provee comandos para trabajar % con \emph{geometría} descritos en esta sección. Todos los comandos % trabajan en \emph{modo matemático} y reciben como \mymarg{argumento} % base una \emph{única} letra, mayúscula en el caso de puntos, con un % modificador del tipo subíndice/prima, un número o comando. % % Algunos comandos reciben \mymarg{argumento} único, como una \enquote{\emph{letra}}, % o más de uno dentro del mismo, donde opcionalmente se pueden separar % por comas `|,|'. % % \begin{important}* % Las primas se deben pasar de forma directa `|'|' y los subíndices siempre % deben escribirse usando llaves `|A_{...}|' y nunca sin estas `|A_n|' % que funciona solo por coincidencia. % \end{important} % % \subsection*{El comando \cs[no-index]{spcoord}}\label{subsec:spcoord} % % \begin{function}{\spcoord} % \begin{syntax} % \mycolrow{17.9em}|\spcoord|\mymarg{abscisa\textnormal{\textcolor{red}{,}} ordenada}\mycolsep |\spcoord|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{abscisa\textnormal{\textcolor{red}{,}} ordenada} % \mycolrow{17.9em}|\spcoord|\mymarg{abscisa\textnormal{\textcolor{red}{;}} ordenada}\mycolsep |\spcoord|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{abscisa\textnormal{\textcolor{red}{;}} ordenada} % \end{syntax} % El comando \ics*{spcoord} trabaja en \emph{modo matemático} y recibe % el \emph{argumento obligatorio} \mymarg{abscisa\textnormal{\textcolor{red}{,}} ordenada} % o \mymarg{abscisa\textnormal{\textcolor{red}{;}} ordenada}, el cual % se procesa internamente bajo el comando |\spnum| cuando es numérico % no fraccionario. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spcoord{-3, 4}$| imprimirá `$(-3, 4)$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«menos tres coma cuatro»}; % |$\spcoord{x_{1},y_{1}}$| imprimirá `$(x_{1}, y_{1})$' y verbalizará % \emph{«x subíndice uno coma y subíndice uno»}. % La lectura del subíndice la decide \mynvmath{NVDA/MathCAT}, porque % |\spcoord| no tiene la llave \mykey{read-cmd}. % % \begin{important}* % El comando |\spcoord| solo acepta \emph{un separador} `\textcolor{red}{,}' % o `\textcolor{red}{;}' entre la abscisa y la ordenada,\newline fuera de llaves:~% % |$\spcoord{a_{1,2}, b}$| es válido porque la coma de las llaves no % separa. Con `\textcolor{red}{;}' las comas son decimales, por ejemplo % |$\spcoord{1,5; 2,5}$|. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spcoord}} % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spcoord] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del \mymarg{argumento} pasado al comando sin modificar la impresión de este. % Si se establece en |true|, \mynvmath{NVDA/MathCAT} NO verbalizará la salida % del comando; si se establece en |false|, \mynvmath{NVDA/MathCAT} % sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spPoint}}\label{subsec:spPoint} % % \begin{function}{\spPoint} % \begin{syntax} % \mycolrow{22.9em}|\spPoint|\mymarg{punto}\mycolsep |\spPoint|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto} % \mycolrow{22.9em}|\spPoint|\mymarg{punto}\myparg{abscisa\textnormal{\textcolor{red}{,}} ordenada}\mycolsep |\spPoint|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto}\myparg{abscisa\textnormal{\textcolor{red}{,}} ordenada} % \mycolrow{22.9em}|\spPoint|\mymarg{punto}\myparg{abscisa\textnormal{\textcolor{red}{;}} ordenada}\mycolsep |\spPoint|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto}\myparg{abscisa\textnormal{\textcolor{red}{;}} ordenada} % \end{syntax} % El comando \ics*{spPoint} trabaja en \emph{modo matemático} y % recibe el \emph{argumento obligatorio} \mymarg{punto}, el cual es una % \emph{única letra mayúscula} con prima `|'|' o subíndice % `|_{...}|' y el \emph{argumento opcional delimitado por paréntesis} % \myparg{abscisa\textnormal{\textcolor{red}{,}} ordenada}, el cual se % procesa internamente bajo el comando |\spcoord|. % \end{function} % % Si no se proporciona el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spPoint{A}$| imprimirá `$A$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«punto A»}; % |$\spPoint{A_{1}}$| imprimirá `$A_1$' y verbalizará % \emph{«punto A sub uno»}. % % Si se proporciona el \emph{argumento opcional delimitado por paréntesis}, % |$\spPoint{A}(-3,4)$| imprimirá `$A(-3,4)$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«punto A menos tres coma cuatro»}; % |$\spPoint{A_{1}}(-3,4)$| imprimirá `$A_{1}(-3,4)$' y verbalizará % \emph{«punto A sub uno menos tres coma cuatro»}. % % \subsubsection*{Llaves para \cs[no-index]{spPoint}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[spPoint] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará % el argumento \mymarg{punto}. Si se establece en |school|, % \mynvmath{NVDA/MathCAT} verbalizará el argumento \mymarg{punto} sin el prefijo % «mayúscula»: |$\spPoint{A}$| se verbaliza \emph{«punto A»}; si se establece % en |mathcat|, \mynvmath{NVDA/MathCAT} aplica sus propias reglas, que pueden % incluir el prefijo «mayúscula» dependiendo de la configuración del % usuario. % % \keyexampcmd{prefix}{prefijo}{no usado}[spPoint] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el concepto \emph{«punto»}. El valor de esta llave % \emph{siempre} debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spPoint] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el concepto \emph{«punto»} al ejecutar el comando sin modificar la impresión % de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} verbalizará % el concepto y el \mymarg{argumento}; si se establece en |false|, \mynvmath{NVDA/MathCAT} % solo verbalizará el \mymarg{argumento} acorde al valor establecido por % la llave \mykey{read-cmd}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spPoint] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del concepto \emph{«punto»} y del \mymarg{argumento} pasado al comando % sin modificar la impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} % NO verbalizará la salida del comando; si se establece en |false|, \mynvmath{NVDA/MathCAT} % sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spvec}}\label{subsec:spvec} % % \begin{function}{\spvec} % \begin{syntax} % \mycolrow{27.3em}|\spvec|\mymarg{letra}\mycolsep |\spvec|\mymarg{punto\textnormal{\textcolor{gray}{,}} punto} % \mycolrow{27.3em}|\spvec|\mymarg{letra}\myparg{abscisa\textnormal{\textcolor{red}{,}} ordenada}\mycolsep |\spvec|\mymarg{punto\textnormal{\textcolor{gray}{,}} punto}\myparg{abscisa\textnormal{\textcolor{red}{,}} ordenada} % \mycolrow{27.3em}|\spvec|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{letra}\myparg{abscisa\textnormal{\textcolor{red}{,}} ordenada}\mycolsep |\spvec|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto\textnormal{\textcolor{gray}{,}} punto}\myparg{abscisa\textnormal{\textcolor{red}{,}} ordenada} % \end{syntax} % El comando \ics*{spvec} trabaja en \emph{modo matemático} y recibe un % \emph{argumento obligatorio} de la forma \mymarg{letra} minúscula % o \mymarg{punto\textnormal{\textcolor{gray}{,}} punto} junto al % \emph{argumento opcional delimitado por paréntesis} \myparg{abscisa\textnormal{\textcolor{red}{,}} ordenada}, el cual se % procesa internamente bajo el comando |\spcoord|. % \end{function} % % Si el \emph{argumento} es de la forma \mymarg{punto\textnormal{\textcolor{gray}{,}} punto} % con prima(s) `|'|' o subíndice(s) `|_{...}|' opcionales se aplica % |\overrightarrow| en la impresión; si el \emph{argumento} es de % la forma \mymarg{letra} minúscula con subíndice `|_{...}|' opcional % se aplica |\vec| en la impresión. % % Si no se proporciona el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spvec{a}$| imprimirá `$\vec{a}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«vector a»}; % |$\spvec{AB}$| imprimirá `$\overrightarrow{AB}$' y verbalizará % \emph{«vector A B»}. % % Si se proporciona el \emph{argumento opcional delimitado por paréntesis}, % |$\spvec{a}(-3,4)$| imprimirá `$\vec{a}=(-3,4)$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«vector a es igual a menos tres coma cuatro»}; % |$\spvec{AB}(-3,4)$| imprimirá `$\overrightarrow{AB}=(-3,4)$' y verbalizará % \emph{«vector A B es igual a menos tres coma cuatro»}. % % \subsubsection*{Llaves para \cs[no-index]{spvec}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[spvec] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará el % argumento \mymarg{punto\textnormal{\textcolor{gray}{,}} punto}. Si se % establece en |school|, \mynvmath{NVDA/MathCAT} verbaliza las letras del % argumento sin el prefijo «mayúscula»: |$\spvec{AB}$| se verbaliza % \emph{«vector A B»}; si se establece en |mathcat|, \mynvmath{NVDA/MathCAT} % aplica sus propias reglas, que pueden incluir el prefijo «mayúscula» % dependiendo de la configuración del usuario. % % \keyexampcmd{prefix}{prefijo}{no usado}[spvec] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el concepto \emph{«vector»}. El valor de esta % llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{autofit}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spvec] % % Establece si se \emph{utiliza} |\vphantom{b}| al ejecutar el comando % cuando se utiliza el argumento \mymarg{letra}. Si se establece en |true|, % se aplicará |\vphantom{b}| al inicio del argumento \mymarg{letra}; si % se establece en |false|, no se aplicará |\vphantom{b}|. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spvec] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el concepto \emph{«vector»} al ejecutar el comando sin modificar la impresión % de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} verbalizará % el concepto y el \mymarg{argumento}; si se establece en |false|, \mynvmath{NVDA/MathCAT} % solo verbalizará el \mymarg{argumento} acorde al valor establecido por % la llave \mykey{read-cmd}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spvec] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del concepto \emph{«vector»} y del \mymarg{argumento} pasado al comando % sin modificar la impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} % NO verbalizará la salida del comando; si se establece en |false|, \mynvmath{NVDA/MathCAT} % sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{sprecta}}\label{subsec:sprecta} % % \begin{function}{\sprecta} % \begin{syntax} % \mycolrow{14.9em}|\sprecta|\mymarg{punto\textnormal{\textcolor{gray}{,}} punto}\mycolsep |\sprecta|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto\textnormal{\textcolor{gray}{,}} punto} % \mycolrow{14.9em}|\sprecta|\mymarg{letra}\mycolsep |\sprecta|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{letra} % \end{syntax} % El comando \ics*{sprecta} trabaja en \emph{modo matemático} y % recibe un \emph{argumento obligatorio} de la forma \mymarg{letra} o % \mymarg{punto\textnormal{\textcolor{gray}{,}} punto} con prima(s) `|'|' % o subíndice(s) `|_{...}|' opcionales. Si el \emph{argumento} es de % la forma \mymarg{punto\textnormal{\textcolor{gray}{,}} punto} % se aplica |\overleftrightarrow| en la impresión; si el \emph{argumento} % es de la forma \mymarg{letra}, mayúscula o minúscula, se imprimirá solo % la \mymarg{letra} sin aplicar |\overleftrightarrow|. % \end{function} % % Si no se proporciona el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val}, |$\sprecta{AB}$| % imprimirá `$\overleftrightarrow{AB}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«recta A B»}; |$\sprecta{m}$| % imprimirá `$m$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«recta m»}. % % \subsubsection*{Llaves para \cs[no-index]{sprecta}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[sprecta] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará el % argumento \mymarg{punto\textnormal{\textcolor{gray}{,}} punto} y el argumento % \mymarg{letra} cuando está en mayúscula. Si se establece en |school|, \mynvmath{NVDA/MathCAT} % verbaliza las letras del argumento sin el prefijo «mayúscula»: |$\sprecta{AB}$| % se verbaliza \emph{«recta A B»}; si se establece en |mathcat|, \mynvmath{NVDA/MathCAT} % aplica sus propias reglas, que pueden incluir el prefijo «mayúscula» % dependiendo de la configuración del usuario. % % \keyexampcmd{prefix}{prefijo}{no usado}[sprecta] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el concepto \emph{«recta»}. El valor de esta % llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[sprecta] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el concepto \emph{«recta»} al ejecutar el comando sin modificar la impresión % de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} verbalizará % el concepto y el \mymarg{argumento}; si se establece en |false|, \mynvmath{NVDA/MathCAT} % solo verbalizará el \mymarg{argumento} acorde al valor establecido por % la llave \mykey{read-cmd}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[sprecta] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del concepto \emph{«recta»} y del \mymarg{argumento} pasado al comando % sin modificar la impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} % NO verbalizará la salida del comando; si se establece en |false|, \mynvmath{NVDA/MathCAT} % sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{sprayo}}\label{subsec:sprayo} % % \begin{function}{\sprayo} % \begin{syntax} % |\sprayo|\mymarg{punto\textnormal{\textcolor{gray}{,}} punto} \quad\quad |\sprayo|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto\textnormal{\textcolor{gray}{,}} punto} % \end{syntax} % El comando \ics*{sprayo} trabaja en \emph{modo matemático} y % recibe el \emph{argumento obligatorio} \mymarg{punto\textnormal{\textcolor{gray}{,}} punto} % con prima(s) `|'|' o subíndice(s) `|_{...}|' opcionales. Representa % un \emph{rayo} o \emph{semirrecta} con origen en el primer % punto y dirección hacia el segundo, aplicando |\overrightarrow| en la impresión. % \end{function} % % Si no se proporciona el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\sprayo{AB}$| imprimirá `$\overrightarrow{AB}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«rayo A B»}. % % \subsubsection*{Llaves para \cs[no-index]{sprayo}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[sprayo] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará % el argumento \mymarg{punto\textnormal{\textcolor{gray}{,}} punto}. Si % se establece en |school|, \mynvmath{NVDA/MathCAT} verbaliza las letras % del argumento sin el prefijo «mayúscula»: |$\sprayo{AB}$| se verbaliza % \emph{«rayo A B»}; si se establece en |mathcat|, \mynvmath{NVDA/MathCAT} % aplica sus propias reglas, que pueden incluir el prefijo «mayúscula» % dependiendo de la configuración del usuario. % % \keyexampcmd{read-sty}{rayo \textnormal{\textcolor{lightgray}{\textbar}} % semirrecta}{rayo}[sprayo] % % Establece el \emph{concepto} que verbalizará \mynvmath{NVDA/MathCAT} % al ejecutar el comando sin modificar la impresión de este. Si se utiliza % |$\sprayo[read-sty=rayo]{AB}$|, \mynvmath{NVDA/MathCAT} verbalizará % \emph{«rayo A B»}; si se utiliza |$\sprayo[read-sty=semirrecta]{AB}$|, % \mynvmath{NVDA/MathCAT} verbalizará \emph{«semirrecta A B»}. % % \keyexampcmd{prefix}{prefijo}{no usado}[sprayo] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el \emph{«concepto»} establecido por la llave % \mykey{read-sty}. El valor de esta llave \emph{siempre} debe pasarse entre % llaves `|{ }|'. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[sprayo] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el \emph{«concepto»} establecido por la llave \mykey{read-sty} al ejecutar el % comando sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} verbalizará el concepto y el \mymarg{argumento}; % si se establece en |false|, \mynvmath{NVDA/MathCAT} solo verbalizará % el \mymarg{argumento} acorde al valor establecido por la llave \mykey{read-cmd}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[sprayo] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del \emph{«concepto»} establecido por la llave \mykey{read-sty} y del % \mymarg{argumento} pasado al comando sin modificar la impresión de este. % Si se establece en |true|, \mynvmath{NVDA/MathCAT} NO verbalizará la salida % del comando; si se establece en |false|, \mynvmath{NVDA/MathCAT} sí % verbalizará la salida del comando. % % \begin{important}* % La impresión `$\overrightarrow{AB}$' en la salida \texttt{PDF} % es idéntica en ambos casos. La elección depende de la terminología % del libro de texto o del currículo nacional: en Chile y España se % prefiere «semirrecta»; en México y otros países de América Latina se % usa «rayo». % \end{important} % % \subsection*{El comando \cs[no-index]{spside}}\label{subsec:spside} % % \begin{function}{\spside} % \begin{syntax} % |\spside|\mymarg{punto\textnormal{\textcolor{gray}{,}} punto} \quad\quad |\spside|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto\textnormal{\textcolor{gray}{,}} punto} % \end{syntax} % % El comando \ics*{spside} trabaja en \emph{modo matemático} y % recibe el \emph{argumento obligatorio} \mymarg{punto\textnormal{\textcolor{gray}{,}} punto} % con prima(s) `|'|' o subíndice(s) `|_{...}|' opcionales. Representa % un \emph{lado}, \emph{segmento} o \emph{trazo} determinado por dos puntos, % aplicando |\overline| en la impresión. % \end{function} % % Si no se proporciona el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spside{AB}$| imprimirá `$\overline{AB}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«lado A B»}. % % \subsubsection*{Llaves para \cs[no-index]{spside}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[spside] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará el % argumento \mymarg{punto\textnormal{\textcolor{gray}{,}} punto}. Si % se establece en |school|, \mynvmath{NVDA/MathCAT} verbaliza las letras % del argumento sin el prefijo «mayúscula»: |$\spside{AB}$| % se verbaliza \emph{«lado A B»}; si se establece en |mathcat|, \mynvmath{NVDA/MathCAT} % aplica sus propias reglas, que pueden incluir el prefijo «mayúscula» % dependiendo de la configuración del usuario. % % \keyexampcmd{read-sty}{lado \textnormal{\textcolor{lightgray}{\textbar}} % segmento \textnormal{\textcolor{lightgray}{\textbar}} % trazo}{lado}[spside] % % Establece el \emph{concepto} que verbalizará \mynvmath{NVDA/MathCAT} % al ejecutar el comando sin modificar la impresión de este. Si se utiliza % |$\spside[read-sty=lado]{AB}$|, \mynvmath{NVDA/MathCAT} verbalizará % \emph{«lado A B»}; si se utiliza |$\spside[read-sty=segmento]{AB}$|, % \mynvmath{NVDA/MathCAT} verbalizará \emph{«segmento A B»} y si se utiliza % |$\spside[read-sty=trazo]{AB}$|, \mynvmath{NVDA/MathCAT} verbalizará % \emph{«trazo A B»}. % % \keyexampcmd{prefix}{prefijo}{no usado}[spside] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el \emph{«concepto»} establecido por la llave % \mykey{read-sty}. El valor de esta llave \emph{siempre} debe pasarse % entre llaves `|{ }|'. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spside] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el \emph{«concepto»} establecido por la llave \mykey{read-sty} al ejecutar el % comando sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} verbalizará el concepto y el \mymarg{argumento}; % si se establece en |false|, \mynvmath{NVDA/MathCAT} solo verbalizará % el \mymarg{argumento} acorde al valor establecido por la llave \mykey{read-cmd}. % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spside] % % Sobrescribe el \emph{concepto} establecido por la llave \mykey{read-sty} % que será verbalizado por \mynvmath{NVDA/MathCAT} al ejecutar el comando % sin modificar la impresión de este. El valor de esta llave \emph{siempre} % debe pasarse entre llaves `|{ }|'. Por ejemplo:~|$\spside[read-arg={hipotenusa}]{AB}$| % imprimirá `$\overline{AB}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«hipotenusa A B»}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spside] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del \emph{«concepto»} establecido por la llave \mykey{read-sty} y del % \mymarg{argumento} pasado al comando sin modificar la impresión de este. % Si se establece en |true|, \mynvmath{NVDA/MathCAT} NO verbalizará la salida % del comando; si se establece en |false|, \mynvmath{NVDA/MathCAT} sí % verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spmside}}\label{subsec:spmside} % % \begin{function}{\spmside} % \begin{syntax} % |\spmside|\mymarg{punto\textnormal{\textcolor{gray}{,}} punto} \quad\quad |\spmside|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto\textnormal{\textcolor{gray}{,}} punto} % \end{syntax} % % El comando \ics*{spmside} trabaja en \emph{modo matemático} y % recibe el \emph{argumento obligatorio} \mymarg{punto\textnormal{\textcolor{gray}{,}} punto} % con prima(s) `|'|' o subíndice(s) `|_{...}|' opcionales. Representa % la \emph{medida} del \emph{lado}, \emph{segmento} o \emph{trazo} % determinado por dos puntos. La distinción entre |\spside| y |\spmside| es % semántica: |\spside| representa el \emph{objeto geométrico} % `$\overline{AB}$'; |\spmside| representa su \emph{medida}, que es un % número real positivo. % \end{function} % % Si no se proporciona el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spmside{AB}$| imprimirá `$AB$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«medida del lado A B»}. % % \subsubsection*{Llaves para \cs[no-index]{spmside}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[spmside] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará el % argumento \mymarg{punto\textnormal{\textcolor{gray}{,}} punto}. Si % se establece en |school|, \mynvmath{NVDA/MathCAT} verbaliza las letras % del argumento sin el prefijo «mayúscula»: |$\spmside{AB}$| % se verbaliza \emph{«medida del lado A B»}; si se establece en |mathcat|, % \mynvmath{NVDA/MathCAT} aplica sus propias reglas, que pueden incluir % el prefijo «mayúscula» dependiendo de la configuración del usuario. % % \keyexampcmd{read-sty}{lado \textnormal{\textcolor{lightgray}{\textbar}} % segmento \textnormal{\textcolor{lightgray}{\textbar}} % trazo}{lado}[spmside] % % Establece el \emph{concepto} que verbalizará \mynvmath{NVDA/MathCAT} % después de verbalizar el término \emph{«medida del»} al ejecutar el comando sin % modificar la impresión de este. Si se utiliza |$\spmside[read-sty=lado]{AB}$|, % \mynvmath{NVDA/MathCAT} verbalizará \emph{«medida del lado A B»}; si se % utiliza |$\spmside[read-sty=segmento]{AB}$|, \mynvmath{NVDA/MathCAT} % verbalizará \emph{«medida del segmento A B»} y si se utiliza % |$\spmside[read-sty=trazo]{AB}$|, \mynvmath{NVDA/MathCAT} verbalizará % \emph{«medida del trazo A B»}. % % \keyexampcmd{prefix}{prefijo}{no usado}[spmside] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el término \emph{«medida del»} seguido por % el \emph{«concepto»} establecido por la llave \mykey{read-sty}. El valor de esta llave % \emph{siempre} debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spmside] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el término \emph{«medida del»} y el \emph{«concepto»} establecido por % la llave \mykey{read-sty} al ejecutar el comando sin modificar la impresión % de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} verbalizará % el término \emph{«medida del»}, el concepto y el \mymarg{argumento}; % si se establece en |false|, \mynvmath{NVDA/MathCAT} solo verbalizará % el \mymarg{argumento} acorde al valor establecido por la llave \mykey{read-cmd}. % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spmside] % % Sobrescribe el \emph{concepto} establecido por la llave \mykey{read-sty} % que será verbalizado por \mynvmath{NVDA/MathCAT} al ejecutar el comando % sin modificar la impresión de este. El valor de esta llave \emph{siempre} % debe pasarse entre llaves `|{ }|' y se debe usar en conjunto con la llave % \mykey{concept}. % % Por ejemplo:~|$\spmside[concept=false,read-arg={medida de la cuerda}]{AB}$| % imprimirá `$AB$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«medida de la cuerda A B»}. % % \keyexampcmd{print-sym}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spmside] % % Establece \emph{si se imprime} la notación visual `$\mathrm{m}$' antes % de aplicar |\overline| al \mymarg{argumento}. Si se establece en |true|, % |$\spmside[print-sym=true]{AB}$| imprimirá `$\operatorname{m}\overline{AB}$' % en la salida \texttt{PDF}; si se establece en |false| imprimirá `$AB$' % en la salida \texttt{PDF}. En ambos casos \mynvmath{NVDA/MathCAT} % verbalizará \emph{«medida del lado A B»}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spmside] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del término \emph{«medida del»}, del \emph{«concepto»} establecido por % la llave \mykey{read-sty} y del \mymarg{argumento} pasado al comando % sin modificar la impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} % NO verbalizará la salida del comando; si se establece en |false|, \mynvmath{NVDA/MathCAT} % sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spangle}}\label{subsec:spangle} % % \begin{function}{\spangle} % \begin{syntax} % \mycolrow{32.5em}|\spangle|\mymarg{punto \textnormal{\textcolor{lightgray}{\textbar}} letra griega \textnormal{\textcolor{lightgray}{\textbar}} número}\mycolsep |\spangle|\mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto} % \mycolrow{32.5em}|\spangle|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto \textnormal{\textcolor{lightgray}{\textbar}} letra griega \textnormal{\textcolor{lightgray}{\textbar}} número}\mycolsep |\spangle|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto} % \end{syntax} % El comando \ics*{spangle} trabaja en \emph{modo matemático} y % recibe un \emph{argumento obligatorio} de la forma \mymarg{punto \textnormal{\textcolor{lightgray}{\textbar}} letra griega \textnormal{\textcolor{lightgray}{\textbar}} número} % o \mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto} % aplicando |\angle| o |\rightanglesqr| en la impresión. Solo los argumentos % de la forma \mymarg{punto} o \mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto} % admiten prima(s) `|'|' o subíndice(s) `|_{...}|' opcionales. % \end{function} % % Si no se proporciona el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val}, |$\spangle{A}$| % imprimirá `$\angle A$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«ángulo en A»}; |$\spangle{\alpha}$| imprimirá % `$\angle\alpha$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«ángulo alfa»}; |$\spangle{1}$| imprimirá % `$\angle 1$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«ángulo uno»} y |$\spangle{AOB}$| imprimirá % `$\angle AOB$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«ángulo A O B»}. % % \begin{important}* % Si no se tiene instalada la fuente matemática \texttt{NewCMMath-Regular.otf}, % parte del paquete \mypkg[no-index]{newcomputermodern}\cite{newcomputermodern}, en el sistema o % no se provee una fuente matemática con soporte para |\rightanglesqr|, % se usará |\rightangle| en su reemplazo. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spangle}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[spangle] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará el % argumento \mymarg{punto} o \mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto}. % Si se establece en |school|, \mynvmath{NVDA/MathCAT} verbaliza las letras % del argumento sin el prefijo «mayúscula»: |$\spangle{AOB}$| % se verbaliza \emph{«ángulo A O B»}; si se establece en |mathcat|, % \mynvmath{NVDA/MathCAT} aplica sus propias reglas, que pueden incluir % el prefijo «mayúscula» dependiendo de la configuración del usuario. % % \keyexampcmd{prefix}{prefijo}{no usado}[spangle] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el concepto \emph{«ángulo»}. El valor de esta % llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{recto}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spangle] % % Establece si se \emph{usará} |\rightanglesqr| en la impresión añadiendo % el término \emph{«recto»} a la verbalización en \mynvmath{NVDA/MathCAT}. % Si se establece en |true|, |$\spangle[recto=true]{A}$| imprimirá `$\rightanglesqr A$' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«ángulo recto en A»}, % |$\spangle[recto=true]{AOB}$| imprimirá `$\rightanglesqr AOB$' en la % salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«ángulo recto A O B»}; % si se establece en |false|, imprimirá usando |\angle| en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} omitirá el término \emph{«recto»}. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spangle] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el concepto \emph{«ángulo»} al ejecutar el comando sin modificar la impresión % de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} verbalizará % el concepto \emph{«ángulo»} y el \mymarg{argumento}; si se establece en |false|, % \mynvmath{NVDA/MathCAT} solo verbalizará el \mymarg{argumento} acorde % al valor establecido por la llave \mykey{read-cmd}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spangle] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del concepto \emph{«ángulo»} y del \mymarg{argumento} pasado al comando % sin modificar la impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} % NO verbalizará la salida del comando; si se establece en |false|, \mynvmath{NVDA/MathCAT} sí % verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spmangle}}\label{subsec:spmangle} % % \begin{function}{\spmangle} % \begin{syntax} % \mycolrow{26.4em}|\spmangle|\mymarg{punto \textnormal{\textcolor{lightgray}{\textbar}} letra griega \textnormal{\textcolor{lightgray}{\textbar}} número}\mycolsep |\spmangle|\mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto} % |\spmangle|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto \textnormal{\textcolor{lightgray}{\textbar}} letra griega \textnormal{\textcolor{lightgray}{\textbar}} número} % |\spmangle|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto} % \end{syntax} % El comando \ics*{spmangle} trabaja en \emph{modo matemático} y % recibe un \emph{argumento obligatorio} de la forma \mymarg{punto \textnormal{\textcolor{lightgray}{\textbar}} letra griega \textnormal{\textcolor{lightgray}{\textbar}} número} % o \mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto} % aplicando |\measuredangle| o |\measuredrightangle| en la impresión. % Solo los argumentos de la forma \mymarg{punto} o \mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto} % admiten prima(s) `|'|' o subíndice(s) `|_{...}|' opcionales. % \end{function} % % Si no se proporciona el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val}, |$\spmangle{A}$| % imprimirá `$\measuredangle A$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«medida del ángulo en A»}; |$\spmangle{\alpha}$| imprimirá % `$\measuredangle\alpha$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«medida del ángulo alfa»}; |$\spmangle{1}$| imprimirá % `$\measuredangle 1$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«medida del ángulo uno»} y |$\spmangle{AOB}$| imprimirá % `$\measuredangle AOB$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«medida del ángulo A O B»}. % % \begin{important}* % La distinción entre |\spangle| y |\spmangle| es semántica: |\spangle| % representa el \emph{objeto geométrico} `$\angle A O B$'; |\spmangle| % representa su \emph{medida}, que es un valor en grados. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spmangle}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[spmangle] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará el % argumento \mymarg{punto} o \mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto}. % Si se establece en |school|, \mynvmath{NVDA/MathCAT} verbaliza las letras % del argumento sin el prefijo «mayúscula»: |$\spmangle{AOB}$| % se verbaliza \emph{«medida del ángulo A O B»}; si se establece en |mathcat|, % \mynvmath{NVDA/MathCAT} aplica sus propias reglas, que pueden incluir % el prefijo «mayúscula» dependiendo de la configuración del usuario. % % \keyexampcmd{prefix}{prefijo}{no usado}[spmangle] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el término \emph{«medida del»} seguido por el % concepto \emph{«ángulo»}. El valor de esta llave \emph{siempre} debe % pasarse entre llaves `|{ }|'. % % \keyexampcmd{recto}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spmangle] % % Establece si se \emph{usará} |\measuredrightangle| en la impresión añadiendo % el término \emph{«recto»} a la verbalización en \mynvmath{NVDA/MathCAT}. % Si se establece en |true|, |$\spmangle[recto=true]{A}$| imprimirá % `$\measuredrightangle A$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«medida del ángulo recto en A»}, |$\spmangle[recto=true]{AOB}$| % imprimirá `$\measuredrightangle AOB$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«medida del ángulo recto A O B»}; si se establece en |false|, % imprimirá usando |\measuredangle| en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % omitirá el término \emph{«recto»}. % % \keyexampcmd{print-sym}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spmangle] % % Establece \emph{si se imprime} la notación visual `$\mathrm{m}$' antes % del argumento pasado al comando sin modificar la verbalización de este. % Si se establece en |true|, |$\spmangle[print-sym=true]{AOB}$| imprimirá % `$\operatorname{m}\angle AOB$' en la salida \texttt{PDF}; si se establece % en |false| imprimirá `$\measuredangle AOB$' en la salida \texttt{PDF}. % En ambos casos \mynvmath{NVDA/MathCAT} verbalizará \emph{«medida del ángulo A O B»}. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spmangle] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el término \emph{«medida del»} seguido por el concepto \emph{«ángulo»} al % ejecutar el comando sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} verbalizará \emph{«medida del ángulo»} y el \mymarg{argumento}; % si se establece en |false|, \mynvmath{NVDA/MathCAT} solo verbalizará el % \mymarg{argumento} acorde al valor establecido por la llave \mykey{read-cmd}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spmangle] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del término \emph{«medida del»}, del concepto \emph{«ángulo»} y del \mymarg{argumento} % pasado al comando sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se establece % en |false|, \mynvmath{NVDA/MathCAT} sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spang}}\label{subsec:spang} % % \begin{function}{\spang} % \begin{syntax} % |\spang|\mymarg{grados} \hfill |\spang|\mymarg{grados \textnormal{\textcolor{red}{:}} minutos} \hfill |\spang|\mymarg{grados \textnormal{\textcolor{red}{:}} minutos \textnormal{\textcolor{red}{:}} segundos} % \end{syntax} % El comando \ics*{spang} trabaja en \emph{modo matemático} y recibe el % \emph{argumento obligatorio} \mymarg{grados}, \mymarg{grados \textnormal{\textcolor{red}{:}} minutos} % o \mymarg{grados \textnormal{\textcolor{red}{:}} minutos % \textnormal{\textcolor{red}{:}} segundos}. Los procesa internamente bajo % los comandos |\spnum| y |\spunit| para imprimir y verbalizar. % \end{function} % % Por ejemplo:~|$\spang{45:30:15}$| imprimirá `$45°30′15″$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«cuarenta y cinco grados % treinta minutos quince segundos»}. % % \subsection*{El comando \cs[no-index]{sparc}}\label{subsec:sparc} % % \begin{function}{\sparc} % \begin{syntax} % \mycolrow{21.1em}|\sparc|\mymarg{punto\textnormal{\textcolor{gray}{,}} punto}\mycolsep |\sparc|\mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto} % \mycolrow{21.1em}|\sparc|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto\textnormal{\textcolor{gray}{,}} punto}\mycolsep |\sparc|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto} % \end{syntax} % El comando \ics*{sparc} trabaja en \emph{modo matemático} y recibe un % \emph{argumento obligatorio} de la forma \mymarg{punto\textnormal{\textcolor{gray}{,}} punto} % o \mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto} % con prima(s) `|'|' o subíndice(s) `|_{...}|' opcionales aplicando % |\widearc| en la impresión. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\sparc{AB}$| imprimirá `$\widearc{AB}$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«arco A B»}. % % \begin{important}* % El acento |\widearc| es proporcionado por algunos paquetes como % \mypkg[no-index]{fourier-otf} o \mypkg[no-index]{kpfonts-otf}, en caso % de no estar disponible se utilizará una versión interna para este. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{sparc}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[sparc] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará el % argumento \mymarg{punto\textnormal{\textcolor{gray}{,}} punto} o % \mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto}. % Si se establece en |school|, \mynvmath{NVDA/MathCAT} verbaliza las letras % del argumento sin el prefijo «mayúscula»: |$\sparc{AB}$| % se verbaliza \emph{«arco A B»}; si se establece en |mathcat|, % \mynvmath{NVDA/MathCAT} aplica sus propias reglas, que pueden incluir % el prefijo «mayúscula» dependiendo de la configuración del usuario. % % \keyexampcmd{prefix}{prefijo}{no usado}[sparc] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el concepto \emph{«arco»}. El valor de esta % llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[sparc] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el concepto \emph{«arco»} al ejecutar el comando sin modificar la impresión % de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} verbalizará % \emph{«arco»} y el \mymarg{argumento}; si se establece en |false|, % \mynvmath{NVDA/MathCAT} solo verbalizará el \mymarg{argumento} acorde % al valor establecido por la llave \mykey{read-cmd}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[sparc] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del concepto \emph{«arco»} y del \mymarg{argumento} pasado al comando % sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se establece % en |false|, \mynvmath{NVDA/MathCAT} sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spmarc}}\label{subsec:spmarc} % % \begin{function}{\spmarc} % \begin{syntax} % \mycolrow{21.7em}|\spmarc|\mymarg{punto\textnormal{\textcolor{gray}{,}} punto}\mycolsep |\spmarc|\mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto} % \mycolrow{21.7em}|\spmarc|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto\textnormal{\textcolor{gray}{,}} punto}\mycolsep |\spmarc|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto} % \end{syntax} % El comando \ics*{spmarc} trabaja en \emph{modo matemático} y % recibe un \emph{argumento obligatorio} de la forma \mymarg{punto\textnormal{\textcolor{gray}{,}} punto} % o \mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto} % con prima(s) `|'|' o subíndice(s) `|_{...}|' opcionales colocando % `$\operatorname{m}$' antes de aplicar |\widearc| en la impresión. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spmarc{AB}$| imprimirá `$\operatorname{m}\widearc{AB}$' en la % salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«medida del arco A B»}. % % \begin{important}* % La distinción entre |\sparc| y |\spmarc| es semántica: % |\sparc| representa el \emph{objeto geométrico} `$\widearc{AB}$'; % |\spmarc| representa su \emph{medida}, que es un valor en grados. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spmarc}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[spmarc] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará el % argumento \mymarg{punto\textnormal{\textcolor{gray}{,}} punto} o % \mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto}. % Si se establece en |school|, \mynvmath{NVDA/MathCAT} verbaliza las letras % del argumento sin el prefijo «mayúscula»: |$\spmarc{AB}$| % se verbaliza \emph{«medida del arco A B»}; si se establece en |mathcat|, % \mynvmath{NVDA/MathCAT} aplica sus propias reglas, que pueden incluir % el prefijo «mayúscula» dependiendo de la configuración del usuario. % % \keyexampcmd{prefix}{prefijo}{no usado}[spmarc] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el término \emph{«medida del»} seguido por % el concepto \emph{«arco»}. El valor de esta llave \emph{siempre} debe % pasarse entre llaves `|{ }|'. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spmarc] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el término \emph{«medida del»} y el concepto \emph{«arco»} al ejecutar % el comando sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} verbalizará \emph{«medida del arco»} y el \mymarg{argumento}; % si se establece en |false|, \mynvmath{NVDA/MathCAT} solo verbalizará el % \mymarg{argumento} acorde al valor establecido por la llave \mykey{read-cmd}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spmarc] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del término \emph{«medida del»}, el concepto \emph{«arco»} y el \mymarg{argumento} % pasado al comando sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se establece % en |false|, \mynvmath{NVDA/MathCAT} sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spPoly}}\label{subsec:spPoly} % % \begin{function}{\spPoly} % \begin{syntax} % |\spPoly|\mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots} \quad\quad |\spPoly|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots} % \end{syntax} % El comando \ics*{spPoly} trabaja en \emph{modo matemático} y % recibe el \emph{argumento obligatorio} \mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots}, % una lista de al menos tres \mymarg{puntos} con prima(s) `|'|' o % subíndice(s) `|_{...}|' opcionales. El \emph{símbolo} usado en la % impresión se maneja por medio de la llave \mykey{print-sym} y la % verbalización del concepto asociado se determina acorde al número % de puntos en el argumento y la llave \mykey{concept}. % \end{function} % % Si son tres puntos se aplica |\triangle| y añade el concepto \emph{«triángulo»} % a la verbalización; si son cuatro puntos se aplica |\mdlgwhtsquare| % y añade el concepto \emph{«cuadrado»} a la verbalización; si son cinco % o más puntos no se aplica ningún símbolo y se añade a la verbalización % el nombre del polígono: \emph{«pentágono»}, \emph{«hexágono»}, % \emph{«heptágono»} u \emph{«octágono»} de cinco a ocho puntos, y % \emph{«polígono»} desde nueve puntos. % % Si no se proporciona el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spPoly{ABC}$| imprimirá `$\triangle ABC$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«triángulo A B C»}; |$\spPoly{ABCD}$| imprimirá % `$\mdlgwhtsquare ABCD$' y verbalizará \emph{«cuadrado A B C D»}; % |$\spPoly{ABCDE}$| imprimirá `$ABCDE$' y verbalizará % \emph{«pentágono A B C D E»}. % % \subsubsection*{Llaves para \cs[no-index]{spPoly}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[spPoly] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará el % argumento \mymarg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots}. % Si se establece en |school|, \mynvmath{NVDA/MathCAT} verbaliza las letras % del argumento sin el prefijo «mayúscula»: |$\spPoly{ABC}$| % se verbaliza \emph{«triángulo A B C»}; si se establece en |mathcat|, % \mynvmath{NVDA/MathCAT} aplica sus propias reglas, que pueden incluir % el prefijo «mayúscula» dependiendo de la configuración del usuario. % % \keyexampcmd{prefix}{prefijo}{no usado}[spPoly] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el \mymarg{argumento} o el concepto \emph{«triángulo»}, % \emph{«cuadrado»}, \emph{«pentágono»}, \emph{«hexágono»}, \emph{«heptágono»}, % \emph{«octágono»} o \emph{«polígono»} establecido por la llave \mykey{concept}. % El valor de esta llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spPoly] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el concepto, que es \emph{«triángulo»}, \emph{«cuadrado»}, \emph{«pentágono»}, % \emph{«hexágono»}, \emph{«heptágono»}, \emph{«octágono»} o % \emph{«polígono»}, acorde al número de puntos en el \mymarg{argumento} al % ejecutar el comando sin modificar la impresión de este. Si se establece en % |true|, \mynvmath{NVDA/MathCAT} verbalizará el concepto y el % \mymarg{argumento}; % si se establece en |false|, \mynvmath{NVDA/MathCAT} solo verbalizará el % \mymarg{argumento} acorde al valor establecido por la llave \mykey{read-cmd}. % % \keyexampcmd{print-sym}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spPoly] % % Establece si se \emph{usará} |\triangle| o |\mdlgwhtsquare| en la impresión % sin modificar la verbalización de este. Si se establece en |true|, % |$\spPoly[print-sym=true]{ABC}$| imprimirá `$\triangle ABC$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«triángulo A B C»}; % |$\spPoly[print-sym=true]{ABCD}$| imprimirá `$\mdlgwhtsquare ABCD$' en % la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«cuadrado A B C D»}; % si se establece en |false|, solo se imprimirá el \mymarg{argumento} en % la salida \texttt{PDF} y la verbalización será la misma. % % \begin{important}* % Esta llave no tiene efecto con cinco o más puntos, porque no hay un % símbolo definido para ellos. % \end{important} % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spPoly] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del concepto establecido por la llave \mykey{concept} y del \mymarg{argumento} % pasado al comando sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se establece % en |false|, \mynvmath{NVDA/MathCAT} sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spAfig}}\label{subsec:spAfig} % % \begin{function}{\spAfig} % \begin{syntax} % \mycolrow{24.9em}|\spAfig|\mycolsep |\spAfig|\myparg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots} % \mycolrow{24.9em}|\spAfig|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mycolsep |\spAfig|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots} % \mycolrow{24.9em}|\spAfig|\myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n}\mycolsep |\spAfig|\myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{T}} % \mycolrow{24.9em}|\spAfig|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n}\mycolsep |\spAfig|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{T}} % \end{syntax} % El comando \ics*{spAfig} trabaja en \emph{modo matemático} y recibe % el \emph{argumento opcional delimitado por paréntesis} de la forma % \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n}, % \myparg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots} % o \myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{T}} % aplicando |\mathcal{A}| a la impresión. El \emph{símbolo} usado en % la impresión se maneja por medio de la llave \mykey{print-sym} y la % verbalización del concepto asociado se determina acorde al \myparg{argumento} % y la llave \mykey{concept}. % \end{function} % % Si se proporciona el \emph{argumento} \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n}, % |$\spAfig(1)$| imprimirá `$\mathcal{A}_{1}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«área uno»}; % |$\spAfig(n)$| imprimirá `$\mathcal{A}_{n}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«área ene»}. % % Si se proporciona el \emph{argumento} \myparg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots}, % |$\spAfig(ABC)$| imprimirá `$\mathcal{A}_{\triangle ABC}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«área del triángulo A B C»}; % |$\spAfig(ABCD)$| imprimirá `$\mathcal{A}_{\mdlgwhtsquare ABCD}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«área del cuadrado A B C D»}; % |$\spAfig(ABCDE)$| imprimirá `$\mathcal{A}_{ABCDE}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«área del pentágono A B C D E»}. % Con cinco o más puntos no se imprime ningún símbolo y se añade a la % verbalización el nombre del polígono: \emph{«pentágono»}, \emph{«hexágono»}, % \emph{«heptágono»} u \emph{«octágono»} de cinco a ocho puntos, y % \emph{«polígono»} desde nueve puntos. % % Si se proporciona el \emph{argumento} \myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{T}}, % |$\spAfig(F)$| imprimirá `$\mathcal{A}_{F}$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«área de la figura»}; % |$\spAfig(F_{1})$| imprimirá `$\mathcal{A}_{F_{1}}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«área de la figura uno»}. % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % ni el \emph{argumento opcional delimitado por paréntesis}, |$\spAfig$| imprimirá % `$\mathcal{A}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«área»}. % % \begin{important}* % El \emph{argumento} \myparg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots} % acepta prima(s) `|'|' o subíndice(s) `|_{...}|'. El \emph{argumento} % \myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{T}} % solo acepta subíndices `|_{...}|' y tiene una impresión y verbalización % fija para cada letra: |F| \emph{«figura»}, |P| \emph{«polígono»}, % |B| \emph{«basal»}, |L| \emph{«lateral»} y |T| \emph{«total»}. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spAfig}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[spAfig] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará el % argumento. Si se establece en |school|, \mynvmath{NVDA/MathCAT} verbaliza las letras % del argumento sin el prefijo «mayúscula»: |$\spAfig(ABC)$| % se verbaliza \emph{«área del triángulo A B C»}; si se establece en |mathcat|, % \mynvmath{NVDA/MathCAT} aplica sus propias reglas, que pueden incluir % el prefijo «mayúscula» dependiendo de la configuración del usuario. % % \keyexampcmd{prefix}{prefijo}{no usado}[spAfig] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el término \emph{«área del»} y el concepto % \emph{«nombre de la figura»} acorde al \myparg{argumento} pasado al comando. % El valor de esta llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spAfig] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el término \emph{«área del»} y el concepto \emph{«nombre de la figura»} % acorde al \myparg{argumento} pasado al comando sin modificar % la impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} % verbalizará el término \emph{«área del»}, el concepto \emph{«nombre de la figura»} % y el \myparg{argumento}; si se establece en |false|, \mynvmath{NVDA/MathCAT} % solo verbalizará el \myparg{argumento} acorde al valor establecido por % la llave \mykey{read-cmd}. % % \begin{important}* % Utilizar \mykey{concept}|=false| sin el \myparg{argumento} es equivalente a usar % \mykey{silent}|=true|. % \end{important} % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spAfig] % % Sobrescribe el \emph{concepto} \emph{«nombre de la figura»} que verbalizará % \mynvmath{NVDA/MathCAT} al ejecutar el comando sin modificar la impresión % de este, cuando se utiliza el argumento % \myparg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots}. % Con \mykey{concept}|=true| se mantiene el término % \emph{«área del»} antes del valor; con \mykey{concept}|=false| se verbaliza solo el valor % de esta llave. El valor de esta llave \emph{siempre} debe pasarse entre % llaves `|{ }|' y se debe usar en conjunto con la llave % \mykey{concept}. Por ejemplo:~|$\spAfig[read-arg={trapecio}](ABCD)$| imprimirá % `$\mathcal{A}_{\mdlgwhtsquare ABCD}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«área del trapecio A B C D»}. % % \begin{important}* % Esta llave no tiene efecto y emite un aviso con los argumentos % \myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} % \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} % \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} % \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} % \textnormal{T}} y \myparg{1 % \textnormal{\textcolor{lightgray}{\textbar}} 2 % \textnormal{\textcolor{lightgray}{\textbar}} 3 % \textnormal{\textcolor{lightgray}{\textbar}} \ldots % \textnormal{\textcolor{lightgray}{\textbar}} n}. % \end{important} % % \keyexampcmd*{concept\textcolor{MediumOrchid}{*}}{valor prohibido}{no usado}[spAfig] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del concepto \emph{«nombre de la figura»} acorde al \myparg{argumento} pasado % al comando sin modificar la impresión de este. Si se establece, \mynvmath{NVDA/MathCAT} % verbalizará el término \emph{«área»} y el \myparg{argumento} acorde al % valor establecido por la llave \mykey{read-cmd}. % % \keyexampcmd{read-sub}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spAfig] % % Establece si se \emph{antepone} la palabra «sub» al verbalizar los argumentos % \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % y \myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{T}} % para \mynvmath{NVDA/MathCAT}. Si se utiliza \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % y se establece en |true|, |$\spAfig[read-sub=true](1)$| imprimirá % `$\mathcal{A}_{1}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«área sub uno»}; si se establece en |false|, la impresión % será la misma y se verbaliza \emph{«área uno»}. % % Si se utiliza \myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{T}} % y se establece en |true|, |$\spAfig[read-sub=true](F_{1})$| imprimirá % `$\mathcal{A}_{F_{1}}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«área de la figura sub uno»}; si se establece en |false|, % la impresión será la misma y se verbaliza \emph{«área de la figura uno»}. % % \keyexampcmd{print-sym}{auto \textnormal{\textcolor{lightgray}{\textbar}} % triangle \textnormal{\textcolor{lightgray}{\textbar}} % square \textnormal{\textcolor{lightgray}{\textbar}} % none}{auto}[spAfig] % % Establece si se \emph{usará} |\triangle| o |\mdlgwhtsquare| en la impresión % para los argumentos \myparg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots} % y \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % pasado al comando sin modificar la verbalización de este. Si se utiliza % \myparg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots} % se infiere el \emph{símbolo} acorde al número de puntos; si se establece en % \mykey{auto} se imprime y si se establece en \mykey{none} se omite: % |$\spAfig[print-sym=auto](ABC)$| y |$\spAfig[print-sym=none](ABC)$| imprimirán % `$\mathcal{A}_{\triangle ABC}$' y `$\mathcal{A}_{ABC}$' en la salida % \texttt{PDF} y en ambos casos \mynvmath{NVDA/MathCAT} verbalizará \emph{«área del triángulo A B C»}. % % Si se utiliza \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % los valores \mykey{auto} y \mykey{none} quedan sin efecto; si se establece en \mykey{triangle} % o \mykey{square} anida el argumento bajo el \emph{símbolo}: % \lstinline[language=spintent-doc,emph={[4]{triangle}},emphstyle={[4]\color{keyname}}]|$\spAfig[print-sym=triangle](1)$| % imprimirá `$\mathcal{A}_{\triangle_{1}}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«área del triángulo uno»}; % \lstinline[language=spintent-doc,emph={[4]{square}},emphstyle={[4]\color{keyname}}]|$\spAfig[print-sym=square](1)$| % imprimirá `$\mathcal{A}_{\mdlgwhtsquare_{1}}$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«área del cuadrado uno»}. % % \begin{important}* % Esta llave no se puede utilizar con el argumento % \myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} % \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} % \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} % \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} % \textnormal{T}}. % \end{important} % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spAfig] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del término \emph{«área del»}, el concepto \emph{«nombre de la figura»} % y el \mymarg{argumento} pasado al comando sin modificar la impresión de este. % Si se establece en |true|, \mynvmath{NVDA/MathCAT} NO verbalizará la salida % del comando; si se establece en |false|, \mynvmath{NVDA/MathCAT} sí verbalizará % la salida del comando. % % \subsection*{El comando \cs[no-index]{spPfig}}\label{subsec:spPfig} % % \begin{function}{\spPfig} % \begin{syntax} % \mycolrow{24.9em}|\spPfig|\mycolsep |\spPfig|\myparg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots} % \mycolrow{24.9em}|\spPfig|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mycolsep |\spPfig|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots} % \mycolrow{24.9em}|\spPfig|\myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n}\mycolsep |\spPfig|\myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{T}} % \mycolrow{24.9em}|\spPfig|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n}\mycolsep |\spPfig|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{T}} % \end{syntax} % El comando \ics*{spPfig} trabaja en \emph{modo matemático} y recibe % el \emph{argumento opcional delimitado por paréntesis} de la forma % \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n}, % \myparg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots} % o \myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{T}} % aplicando |\mathcal{P}| a la impresión. El \emph{símbolo} usado en % la impresión se maneja por medio de la llave \mykey{print-sym} y la % verbalización del concepto asociado se determina acorde al \myparg{argumento} % y la llave \mykey{concept}. % \end{function} % % Si se proporciona el \emph{argumento} \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n}, % |$\spPfig(1)$| imprimirá `$\mathcal{P}_{1}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«perímetro uno»}; % |$\spPfig(n)$| imprimirá `$\mathcal{P}_{n}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«perímetro ene»}. % % Si se proporciona el \emph{argumento} \myparg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots}, % |$\spPfig(ABC)$| imprimirá `$\mathcal{P}_{\triangle ABC}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«perímetro del triángulo A B C»}; % |$\spPfig(ABCD)$| imprimirá `$\mathcal{P}_{\mdlgwhtsquare ABCD}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«perímetro del cuadrado A B C D»}; % |$\spPfig(ABCDE)$| imprimirá `$\mathcal{P}_{ABCDE}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«perímetro del pentágono A B C D E»}. % Con cinco o más puntos no se imprime ningún símbolo y se añade a la % verbalización el nombre del polígono: \emph{«pentágono»}, \emph{«hexágono»}, % \emph{«heptágono»} u \emph{«octágono»} de cinco a ocho puntos, y % \emph{«polígono»} desde nueve puntos. % % Si se proporciona el \emph{argumento} \myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{T}}, % |$\spPfig(F)$| imprimirá `$\mathcal{P}_{F}$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«perímetro de la figura»}; % |$\spPfig(F_{1})$| imprimirá `$\mathcal{P}_{F_{1}}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«perímetro de la figura uno»}. % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % ni el \emph{argumento opcional delimitado por paréntesis}, |$\spPfig$| imprimirá % `$\mathcal{P}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«perímetro»}. % % \begin{important}* % El \emph{argumento} \myparg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots} % acepta prima(s) `|'|' o subíndice(s) `|_{...}|'. El \emph{argumento} % \myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{T}} % solo acepta subíndices `|_{...}|' y tiene una impresión y verbalización % fija para cada letra: |F| \emph{«figura»}, |P| \emph{«polígono»}, % |B| \emph{«basal»}, |L| \emph{«lateral»} y |T| \emph{«total»}. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spPfig}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[spPfig] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará el % argumento. Si se establece en |school|, \mynvmath{NVDA/MathCAT} verbaliza las letras % del argumento sin el prefijo «mayúscula»: |$\spPfig(ABC)$| % se verbaliza \emph{«perímetro del triángulo A B C»}; si se establece en |mathcat|, % \mynvmath{NVDA/MathCAT} aplica sus propias reglas, que pueden incluir % el prefijo «mayúscula» dependiendo de la configuración del usuario. % % \keyexampcmd{prefix}{prefijo}{no usado}[spPfig] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el término \emph{«perímetro del»} y el concepto % \emph{«nombre de la figura»} acorde al \myparg{argumento} pasado al comando. % El valor de esta llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spPfig] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el término \emph{«perímetro del»} y el concepto \emph{«nombre de la figura»} % acorde al \myparg{argumento} pasado al comando sin modificar % la impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} % verbalizará el término \emph{«perímetro del»}, el concepto \emph{«nombre de la figura»} % y el \myparg{argumento}; si se establece en |false|, \mynvmath{NVDA/MathCAT} % solo verbalizará el \myparg{argumento} acorde al valor establecido por % la llave \mykey{read-cmd}. % % \begin{important}* % Utilizar \mykey{concept}|=false| sin el \myparg{argumento} es equivalente a usar % \mykey{silent}|=true|. % \end{important} % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spPfig] % % Sobrescribe el \emph{concepto} \emph{«nombre de la figura»} que verbalizará % \mynvmath{NVDA/MathCAT} al ejecutar el comando sin modificar la impresión % de este, cuando se utiliza el argumento % \myparg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots}. % Con \mykey{concept}|=true| se mantiene el término % \emph{«perímetro del»} antes del valor; con \mykey{concept}|=false| se verbaliza solo el valor % de esta llave. El valor de esta llave \emph{siempre} debe pasarse entre % llaves `|{ }|' y se debe usar en conjunto con la llave % \mykey{concept}. Por ejemplo:~|$\spPfig[read-arg={trapecio}](ABCD)$| imprimirá % `$\mathcal{P}_{\mdlgwhtsquare ABCD}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«perímetro del trapecio A B C D»}. % % \begin{important}* % Esta llave no tiene efecto y emite un aviso con los argumentos % \myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} % \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} % \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} % \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} % \textnormal{T}} y \myparg{1 % \textnormal{\textcolor{lightgray}{\textbar}} 2 % \textnormal{\textcolor{lightgray}{\textbar}} 3 % \textnormal{\textcolor{lightgray}{\textbar}} \ldots % \textnormal{\textcolor{lightgray}{\textbar}} n}. % \end{important} % % \keyexampcmd*{concept\textcolor{MediumOrchid}{*}}{valor prohibido}{no usado}[spPfig] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del concepto \emph{«nombre de la figura»} acorde al \myparg{argumento} pasado % al comando sin modificar la impresión de este. Si se establece, \mynvmath{NVDA/MathCAT} % verbalizará el término \emph{«perímetro»} y el \myparg{argumento} acorde al % valor establecido por la llave \mykey{read-cmd}. % % \keyexampcmd{read-sub}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spPfig] % % Establece si se \emph{antepone} la palabra «sub» al verbalizar los argumentos % \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % y \myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{T}} % para \mynvmath{NVDA/MathCAT}. Si se utiliza \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % y se establece en |true|, |$\spPfig[read-sub=true](1)$| imprimirá % `$\mathcal{P}_{1}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«perímetro sub uno»}; si se establece en |false|, la impresión % será la misma y se verbaliza \emph{«perímetro uno»}. % % Si se utiliza \myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} \textnormal{T}} % y se establece en |true|, |$\spPfig[read-sub=true](F_{1})$| imprimirá % `$\mathcal{P}_{F_{1}}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«perímetro de la figura sub uno»}; si se establece en |false|, % la impresión será la misma y se verbaliza \emph{«perímetro de la figura uno»}. % % \keyexampcmd{print-sym}{auto \textnormal{\textcolor{lightgray}{\textbar}} % triangle \textnormal{\textcolor{lightgray}{\textbar}} % square \textnormal{\textcolor{lightgray}{\textbar}} % none}{auto}[spPfig] % % Establece si se \emph{usará} |\triangle| o |\mdlgwhtsquare| en la impresión % para los argumentos \myparg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots} % y \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % pasado al comando sin modificar la verbalización de este. Si se utiliza % \myparg{punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} punto\textnormal{\textcolor{gray}{,}} \ldots} % se infiere el \emph{símbolo} acorde al número de puntos; si se establece en % \mykey{auto} se imprime y si se establece en \mykey{none} se omite: % |$\spPfig[print-sym=auto](ABC)$| y |$\spPfig[print-sym=none](ABC)$| imprimirán % `$\mathcal{P}_{\triangle ABC}$' y `$\mathcal{P}_{ABC}$' en la salida % \texttt{PDF} y en ambos casos \mynvmath{NVDA/MathCAT} verbalizará \emph{«perímetro del triángulo A B C»}. % % Si se utiliza \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % los valores \mykey{auto} y \mykey{none} quedan sin efecto; si se establece en \mykey{triangle} % o \mykey{square} anida el argumento bajo el \emph{símbolo}: % \lstinline[language=spintent-doc,emph={[4]{triangle}},emphstyle={[4]\color{keyname}}]|$\spPfig[print-sym=triangle](1)$| % imprimirá `$\mathcal{P}_{\triangle_{1}}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«perímetro del triángulo uno»}; % \lstinline[language=spintent-doc,emph={[4]{square}},emphstyle={[4]\color{keyname}}]|$\spPfig[print-sym=square](1)$| % imprimirá `$\mathcal{P}_{\mdlgwhtsquare_{1}}$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«perímetro del cuadrado uno»}. % % \begin{important}* % Esta llave no se puede utilizar con el argumento % \myparg{\textnormal{F} \textnormal{\textcolor{lightgray}{\textbar}} % \textnormal{P} \textnormal{\textcolor{lightgray}{\textbar}} % \textnormal{B} \textnormal{\textcolor{lightgray}{\textbar}} % \textnormal{L} \textnormal{\textcolor{lightgray}{\textbar}} % \textnormal{T}}. % \end{important} % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spPfig] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del término \emph{«perímetro del»}, el concepto \emph{«nombre de la figura»} % y el \mymarg{argumento} pasado al comando sin modificar la impresión de este. % Si se establece en |true|, \mynvmath{NVDA/MathCAT} NO verbalizará la salida % del comando; si se establece en |false|, \mynvmath{NVDA/MathCAT} sí verbalizará % la salida del comando. % % \subsection*{Los comandos \cs[no-index]{sprad} y \cs[no-index]{spdiam}}\label{subsec:sprad} % % \begin{function}{\sprad, \spdiam} % \begin{syntax} % |\sprad| \quad\quad |\sprad|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % |\spdiam| \quad\quad |\spdiam|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % \end{syntax} % Los comandos \ics*{sprad} y \ics*{spdiam} trabajan en \emph{modo % matemático} y reciben el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % mediante el cual se establece la \emph{«forma»} en la que se imprimirán % en la salida \texttt{PDF} las letras `$r$' y `$d$' y cómo se verbalizarán % en \mynvmath{NVDA/MathCAT}. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\sprad$| imprimirá `$r$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«radio»}; |$\spdiam$| imprimirá `$d$' en la % salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«diámetro»}. % % \subsubsection*{Llaves para \cs[no-index]{sprad} y \cs[no-index]{spdiam}} % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[sprad,spdiam] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % los conceptos \emph{«radio»} o \emph{«diámetro»} al ejecutar el comando % sin modificar la impresión de este. Si se establece en |true|, |$\sprad$| y |$\spdiam$| % se verbalizan como \emph{«radio»} y \emph{«diámetro»} en \mynvmath{NVDA/MathCAT}; % si se establece en |false|, se verbalizarán las letras `$r$', `$R$', `$d$' o `$D$' % acorde al valor establecido por la llave \mykey{read-cmd}. % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[sprad,spdiam] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará las % letras `$R$' o `$D$' cuando \mykey{concept}|=false| y \mykey{upper-case}|=true|. Si se % establece en |school|, \mynvmath{NVDA/MathCAT} verbalizará las letras `$R$' % o `$D$' sin el prefijo «mayúscula»; si se establece en |mathcat|, % \mynvmath{NVDA/MathCAT} aplica sus propias reglas, que pueden incluir % el prefijo «mayúscula» dependiendo de la configuración del usuario. % % \keyexampcmd{upper-case}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[sprad,spdiam] % % Establece si se \emph{imprime} usando mayúsculas. Si se establece en |true|, % |$\sprad[upper-case=true]$| y |$\spdiam[upper-case=true]$| imprimirán `$R$' y `$D$' % en la salida \texttt{PDF}; si se establece en |false| se imprimirá usando % minúsculas. % % \begin{important}* % La verbalización al establecer esta llave es controlada por la llave \mykey{read-cmd}. % \end{important} % % \keyexampcmd{print-sym}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spdiam] % % Establece si se \emph{usará} |\diameter| antes de la letra `$d$' o `$D$' % en la impresión del comando |\spdiam|. Si se establece en |true|, % |$\spdiam[print-sym=true]$| imprimirá `$\diameter d$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«diámetro»}. % % \begin{important}* % La llave \mykey{print-sym} no tiene ningún efecto en |\sprad|. % \end{important} % % \keyexampcmd{sub-index}{subíndice}{no usado}[sprad,spdiam] % % Establece un \emph{subíndice} sobre las letras `$r$', `$R$', `$d$' o `$D$'. % El valor de esta llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % Por ejemplo:~|$\sprad[sub-index={1}]$| imprimirá `$r_{1}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«radio uno»}. % % \keyexampcmd{read-sub}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[sprad,spdiam] % % Establece si se \emph{antepone} la palabra \emph{«sub»} al verbalizar % el subíndice establecido por la llave \mykey{sub-index} al ejecutar el % comando sin modificar la impresión de este. Si se establece en |true|,\newline % |$\sprad[sub-index={1},read-sub=true]$| se verbalizará \emph{«radio sub uno»} % en \mynvmath{NVDA/MathCAT}; si se establece en |false|, \mynvmath{NVDA/MathCAT} % verbalizará \emph{«radio uno»}. % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[sprad,spdiam] % % Sobrescribe el \emph{concepto} establecido por la llave \mykey{concept} que % será verbalizado por \mynvmath{NVDA/MathCAT} al ejecutar el comando sin % modificar la impresión de este. El valor de esta llave \emph{siempre} % debe pasarse entre llaves `|{ }|' y se debe usar en conjunto % con la llave \mykey{concept}. % % Por ejemplo:~|$\sprad[read-arg={medida exacta}]$| imprimirá `$r$' en % la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«radio medida exacta»}; |$\sprad[concept=false,read-arg={medida exacta}]$| % imprimirá `$r$' y \mynvmath{NVDA/MathCAT} verbalizará \emph{«medida % exacta»}. % % \begin{important}* % Las llaves \mykey{sub-index} y \mykey{read-sub} no tienen efecto al usar \mykey{read-arg}. % \end{important} % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[sprad,spdiam] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del \emph{concepto} establecido por la llave \mykey{concept} y de las % letras `$r$', `$R$', `$d$' o `$D$' al ejecutar el comando sin modificar % la impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} % NO verbalizará la salida del comando; si se establece en |false|, % \mynvmath{NVDA/MathCAT} sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spcirc}}\label{subsec:spcirc} % % \begin{function}{\spcirc} % \begin{syntax} % \mycolrow{14.9em}|\spcirc|\mycolsep |\spcirc|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % \mycolrow{14.9em}|\spcirc|\myparg{centro}\mycolsep |\spcirc|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{centro} % \mycolrow{14.9em}|\spcirc|\myparg{centro\textnormal{\textcolor{red}{,}} radio}\mycolsep |\spcirc|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{centro\textnormal{\textcolor{red}{,}} radio} % \end{syntax} % El comando \ics*{spcirc} trabaja en \emph{modo matemático} y recibe % el \emph{argumento opcional delimitado por paréntesis} de la forma % \myparg{centro} o \myparg{centro\textnormal{\textcolor{red}{,}} radio}, % donde \mymeta{radio} es opcional y debe pasarse junto al argumento % \myparg{centro} separado por una coma `\textcolor{red}{,}' obligatoria. % \end{function} % % La forma en la que se procesa el \mymeta{radio} junto a su impresión y % verbalización se establece de manera interna: si es un \mymeta{número} % entero o decimal se usa |\spnum|; si es una \mymeta{medida} se usa % |\spunit|; si es un segmento \mymeta{punto punto} se usa |\spside| o % |\spmside| acorde al valor establecido por la llave % \mykey{radio-sty}; y si es una letra se imprime tal cual. Cualquier otra % forma devuelve un \enquote{error} y se detiene la compilación del % documento. % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % ni el \emph{argumento opcional delimitado por paréntesis}, |$\spcirc$| % imprimirá `$\mathcolor{red}{\bigodot}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«circunferencia»}. % % Si se proporciona el \emph{argumento} \myparg{centro}, |$\spcirc(O)$| % imprimirá `$\bigodot(O)$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«circunferencia de centro en O»}. % % Si se proporciona el \emph{argumento} \myparg{centro\textnormal{\textcolor{red}{,}} radio}, % |$\spcirc(O,3)$| imprimirá `$\bigodot(O,3)$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«circunferencia de centro % en O y radio tres»}. % % \begin{important}* % El argumento \myparg{centro} y el argumento \mymeta{radio} cuando tiene % la forma \mymeta{punto punto} aceptan prima `|'|' o subíndice `|_{...}|'. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spcirc}} % % El comando |\spcirc| posee las \mymeta{meta-keys} \mykey[spcirc]{spside} % pasada al comando |\spside| (v.\,pág.\,\pageref{subsec:spside}) y \mykey[spcirc]{spmside} % pasada al comando |\spmside| (v.\,pág.\,\pageref{subsec:spmside}). % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[spcirc] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará % el argumento \myparg{centro}. Si se establece en |school|, \mynvmath{NVDA/MathCAT} % verbaliza el argumento \myparg{centro} sin el prefijo «mayúscula»; % si se establece en |mathcat|, \mynvmath{NVDA/MathCAT} aplica sus propias % reglas, que pueden incluir el prefijo «mayúscula» dependiendo de la % configuración del usuario. % % \begin{important}* % Esta llave tiene efecto sobre el argumento \mymeta{radio} cuando tiene % la forma \mymeta{punto punto}. % \end{important} % % \keyexampcmd{prefix}{prefijo}{no usado}[spcirc] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el \emph{«concepto»} establecido por la % llave \mykey{read-sty}. El valor de esta llave \emph{siempre} debe % pasarse entre llaves `|{ }|'. % % \keyexampcmd{print-sym}{usual \textnormal{\textcolor{lightgray}{\textbar}} % odot}{odot}[spcirc] % % Establece si se \emph{usará} |\bigodot| o la letra `$C$' en la impresión % del comando sin modificar la verbalización de este. Si se establece en % \mykey{usual}, |$\spcirc[print-sym=|\mykey{usual}|](O)$| imprimirá `$C(O)$' % en la salida \texttt{PDF}; si se establece en \mykey{odot}, % \lstinline[language=spintent-doc,emph={[4]{odot}},emphstyle={[4]\color{keyname}}]|$\spcirc[print-sym=odot](O)$| % imprimirá `$\bigodot(O)$' en la salida \texttt{PDF}. % % \keyexampcmd{read-sty}{circun \textnormal{\textcolor{lightgray}{\textbar}} % circulo}{circun}[spcirc] % % Establece el \emph{concepto} que verbalizará \mynvmath{NVDA/MathCAT} % al ejecutar el comando sin modificar la impresión de este. Si se % establece en \mykey{circun}, |$\spcirc[read-sty=circun](O)$| se verbalizará % \emph{«circunferencia de centro en O»} en \mynvmath{NVDA/MathCAT}; si % se establece en \mykey{circulo}, |$\spcirc[read-sty=circulo](O)$| se verbalizará % \emph{«círculo de centro en O»} en \mynvmath{NVDA/MathCAT}. % % \keyexampcmd{diameter}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spcirc] % % Establece \emph{cómo} se verbaliza el argumento \mymeta{radio} al ejecutar % el comando sin modificar la impresión de este. Si se establece en |true|, % |$\spcirc[diameter=true](O, 3)$| se verbalizará \emph{«circunferencia de centro en O y diámetro tres»} % en \mynvmath{NVDA/MathCAT}; si se establece en |false|, |$\spcirc[diameter=false](O, 3)$| % se verbalizará \emph{«circunferencia de centro en O y radio tres»} en % \mynvmath{NVDA/MathCAT}. % % \keyexampcmd{radio-sty}{side \textnormal{\textcolor{lightgray}{\textbar}} % mside}{mside}[spcirc] % % Establece el \emph{comando} que procesa internamente el argumento \mymeta{radio} % cuando tiene la forma \mymeta{punto punto}. Si se establece en \mykey{side} % se usará |\spside|\mymarg{punto punto}, |$\spcirc[radio-sty=side](O, AB)$| % imprimirá `$\bigodot(O, \overline{AB})$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«circunferencia de centro en O y radio lado A B»}; % si se establece en \mykey{mside} se usará |\spmside|\mymarg{punto punto}, % |$\spcirc[radio-sty=mside](O, AB)$| imprimirá `$\bigodot(O, AB)$' en la % salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«circunferencia % de centro en O y radio medida del lado A B»}. % % \begin{important}* % Las \mymeta{meta-keys} \mykey{spside} y \mykey{spmside} % permiten modificar la impresión y verbalización del argumento \mymeta{radio} % cuando tiene la forma \mymeta{punto punto}. % \end{important} % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spcirc] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el \emph{«concepto»} establecido por la llave \mykey{read-sty} al % ejecutar el comando sin modificar la impresión de este. Si se % establece en |true|, \mynvmath{NVDA/MathCAT} verbalizará el \emph{«concepto»} y % el argumento \myparg{centro} o \myparg{centro\textnormal{\textcolor{red}{,}} radio}; % si se establece en |false|, \mynvmath{NVDA/MathCAT} solo verbalizará el % argumento \myparg{centro} o \myparg{centro\textnormal{\textcolor{red}{,}} radio} % acorde al valor establecido por la llave \mykey{read-cmd}. % % \begin{important}* % Utilizar \mykey{concept}|=false| sin el argumento \myparg{centro} es equivalente a % usar \mykey{silent}|=true|. % \end{important} % % \keyexampcmd{sub-index}{subíndice}{no usado}[spcirc] % % Establece un \emph{subíndice} sobre |\bigodot| o la letra `$C$' establecida % por la llave \mykey{print-sym}. El valor de esta llave \emph{siempre} debe pasarse % entre llaves `|{ }|'.\newline Por ejemplo:~|$\spcirc[sub-index={1},radio-sty=mside](O, AB)$| % imprimirá `$\bigodot_{1}(O, AB)$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«circunferencia uno de centro en O y radio medida del lado A B»}. % % \keyexampcmd{read-sub}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spcirc] % % Establece si se \emph{antepone} la palabra \emph{«sub»} al verbalizar % el subíndice establecido por la llave \mykey{sub-index} al ejecutar el comando sin modificar la % impresión de este. Si se establece en |true|,\newline |$\spcirc[sub-index={1},read-sub=true](O)$| % se verbalizará \emph{«circunferencia sub uno de centro en O»} en \mynvmath{NVDA/MathCAT}; % si se establece en |false|, \mynvmath{NVDA/MathCAT} verbalizará \emph{«circunferencia uno de centro en O»}. % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spcirc] % % Sobrescribe el \emph{concepto} establecido por la llave \mykey{read-sty} % que será verbalizado por \mynvmath{NVDA/MathCAT} al ejecutar el % comando sin modificar la impresión de este. El valor de esta llave % \emph{siempre} debe pasarse entre llaves `|{ }|'. Por ejemplo:~|$\spcirc[read-arg={disco}](O)$| % imprimirá `$\bigodot(O)$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«disco de centro en O»}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spcirc] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del \emph{«concepto»} establecido por la llave \mykey{read-sty}, del % argumento \myparg{centro} o \myparg{centro\textnormal{\textcolor{red}{,}} radio} % pasados al comando sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se establece % en |false|, \mynvmath{NVDA/MathCAT} sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spAcirc}}\label{subsec:spAcirc} % % \begin{function}{\spAcirc} % \begin{syntax} % \mycolrow{18.2em}|\spAcirc|\mycolsep |\spAcirc|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % \mycolrow{18.2em}|\spAcirc|\myparg{centro}\mycolsep |\spAcirc|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{centro} % \mycolrow{18.2em}|\spAcirc|\myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n}\mycolsep |\spAcirc|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % \end{syntax} % El comando \ics*{spAcirc} trabaja en \emph{modo matemático} y % recibe el \emph{argumento opcional delimitado por paréntesis} de la % forma \myparg{centro} o \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n}, % aplicando |\mathcal{A}| a la impresión. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % ni el \emph{argumento opcional delimitado por paréntesis}, |$\spAcirc$| % imprimirá `$\mathcal{A}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«área de la circunferencia»}. % % Si se proporciona el \emph{argumento} \myparg{centro}, % |$\spAcirc(O)$| imprimirá `$\mathcal{A}_{\bigodot(O)}$' en la % salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«área % de la circunferencia con centro en O»}. % % Si se proporciona el \emph{argumento} \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n}, % |$\spAcirc(1)$| imprimirá `$\mathcal{A}_{\bigodot_{1}}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«área de la circunferencia uno»}. % % \begin{important}* % El \emph{argumento} \myparg{centro} acepta prima `|'|' o % subíndice `|_{...}|' y no acepta un \mymeta{radio}. Una coma `|,|' % en el argumento devuelve siempre un \enquote{error} y se detiene la compilación del documento. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spAcirc}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[spAcirc] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará % el argumento \myparg{centro}. Si se establece en |school|, \mynvmath{NVDA/MathCAT} % verbaliza el argumento \myparg{centro} sin el prefijo «mayúscula»; si se % establece en |mathcat|, \mynvmath{NVDA/MathCAT} aplica sus propias % reglas, que pueden incluir el prefijo «mayúscula» dependiendo de la % configuración del usuario. % % \keyexampcmd{prefix}{prefijo}{no usado}[spAcirc] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el término \emph{«área de \dots»} seguido por % el \emph{«concepto»} establecido por la llave \mykey{read-sty}. El % valor de esta llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{read-sty}{circun \textnormal{\textcolor{lightgray}{\textbar}} % circulo}{circun}[spAcirc] % % Establece el \emph{concepto} que verbalizará \mynvmath{NVDA/MathCAT} % después de verbalizar el término \emph{«área de \dots»} al ejecutar el % comando sin modificar la impresión de este. Si se establece en |circun|, % |$\spAcirc[read-sty=circun](O)$| se verbalizará \emph{«área de la circunferencia con centro en O»} % en \mynvmath{NVDA/MathCAT}; si se establece en |circulo|, |$\spAcirc[read-sty=circulo](O)$| % se verbalizará \emph{«área del círculo con centro en O»} en \mynvmath{NVDA/MathCAT}. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spAcirc] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el término \emph{«área de \dots»} y el \emph{«concepto»} establecido % por la llave \mykey{read-sty} al ejecutar el comando sin modificar la % impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} % verbalizará el término, el concepto y el argumento \myparg{centro} o % \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n}; % si se establece en |false|, \mynvmath{NVDA/MathCAT} solo verbalizará el % argumento \myparg{centro} acorde al valor establecido por la llave \mykey{read-cmd}. % % \begin{important}* % Utilizar \mykey{concept}|=false| sin el argumento \myparg{centro} o con el % argumento \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % es equivalente a usar \mykey{silent}|=true|. % \end{important} % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spAcirc] % % Sobrescribe el \emph{concepto} establecido por la llave \mykey{read-sty} % que será verbalizado por \mynvmath{NVDA/MathCAT} al ejecutar el % comando sin modificar la impresión de este. El valor de esta llave % \emph{siempre} debe pasarse entre llaves `|{ }|' y se debe usar en % conjunto con la llave \mykey{concept}. Por ejemplo:~|$\spAcirc[read-arg={esfera}](O)$| % se verbalizará \emph{«área de la esfera con centro en O»} en \mynvmath{NVDA/MathCAT};\newline % |$\spAcirc[concept=false,read-arg={esfera}](O)$| se verbalizará % \emph{«esfera con centro en O»} en \mynvmath{NVDA/MathCAT}. % % \begin{important}* % Utilizar esta llave con \mykey{concept}|=false| y el argumento % \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % es equivalente a usar \mykey{silent}|=true|. % \end{important} % % \keyexampcmd{print-sym}{usual \textnormal{\textcolor{lightgray}{\textbar}} % odot}{odot}[spAcirc] % % Establece si se \emph{usará} |\bigodot| o la letra `$C$' en la impresión % del comando sin modificar la verbalización de este. Si se establece en |usual|, % |$\spAcirc[print-sym=usual](O)$| imprimirá `$\mathcal{A}_{C(O)}$' % en la salida \texttt{PDF}; si se establece en \mykey{odot}, |$\spAcirc[print-sym=odot](O)$| % imprimirá `$\mathcal{A}_{\bigodot(O)}$' en la salida \texttt{PDF}. % % \keyexampcmd{sub-index}{subíndice}{no usado}[spAcirc] % % Establece un \emph{subíndice} sobre |\bigodot| o la letra `$C$' establecida % por la llave \mykey{print-sym} cuando se utiliza el argumento \myparg{centro}. % El valor de esta llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % Por ejemplo:~|$\spAcirc[sub-index={1}](O)$| imprimirá `$\mathcal{A}_{\bigodot_{1}(O)}$' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«área de la circunferencia uno con centro en O»}. % % \keyexampcmd{read-sub}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spAcirc] % % Establece si se \emph{antepone} la palabra \emph{«sub»} al verbalizar % el subíndice establecido por la llave \mykey{sub-index} o al usar el % argumento \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % al ejecutar el comando sin modificar la impresión de este. Si se establece % en |true|, |$\spAcirc[read-sub=true](1)$| se verbalizará % \emph{«área de la circunferencia sub uno»} en \mynvmath{NVDA/MathCAT}; % si se establece en |false|, \mynvmath{NVDA/MathCAT} verbalizará \emph{«área de la circunferencia uno»}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spAcirc] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del término \emph{«área de \dots»}, del concepto establecido por la % llave \mykey{read-sty} y del argumento \myparg{centro} o \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % pasado al comando sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se % establece en |false|, \mynvmath{NVDA/MathCAT} sí verbalizará la % salida del comando. % % \subsection*{El comando \cs[no-index]{spPcirc}}\label{subsec:spPcirc} % % \begin{function}{\spPcirc} % \begin{syntax} % \mycolrow{18.2em}|\spPcirc|\mycolsep |\spPcirc|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % \mycolrow{18.2em}|\spPcirc|\myparg{centro}\mycolsep |\spPcirc|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{centro} % \mycolrow{18.2em}|\spPcirc|\myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n}\mycolsep |\spPcirc|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % \end{syntax} % El comando \ics*{spPcirc} trabaja en \emph{modo matemático} y % recibe el \emph{argumento opcional delimitado por paréntesis} de la % forma \myparg{centro} o \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n}, % aplicando |\mathcal{P}| a la impresión. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % ni el \emph{argumento opcional delimitado por paréntesis}, |$\spPcirc$| % imprimirá `$\mathcal{P}$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«perímetro de la circunferencia»}. % % Si se proporciona el \emph{argumento} \myparg{centro}, % |$\spPcirc(O)$| imprimirá `$\mathcal{P}_{\bigodot(O)}$' en la % salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«perímetro % de la circunferencia con centro en O»}. % % Si se proporciona el \emph{argumento} \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n}, % |$\spPcirc(1)$| imprimirá `$\mathcal{P}_{\bigodot_{1}}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«perímetro de la circunferencia uno»}. % % \begin{important}* % El \emph{argumento} \myparg{centro} acepta prima `|'|' o % subíndice `|_{...}|' y no acepta un \mymeta{radio}. Una coma `|,|' % en el argumento devuelve siempre un \enquote{error} y se detiene la compilación del documento. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spPcirc}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[spPcirc] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará % el argumento \myparg{centro}. Si se establece en |school|, \mynvmath{NVDA/MathCAT} % verbaliza el argumento \myparg{centro} sin el prefijo «mayúscula»; si se % establece en |mathcat|, \mynvmath{NVDA/MathCAT} aplica sus propias % reglas, que pueden incluir el prefijo «mayúscula» dependiendo de la % configuración del usuario. % % \keyexampcmd{prefix}{prefijo}{no usado}[spPcirc] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el término \emph{«perímetro de \dots»} seguido por % el \emph{«concepto»} establecido por la llave \mykey{read-sty}. El % valor de esta llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % % \keyexampcmd{read-sty}{circun \textnormal{\textcolor{lightgray}{\textbar}} % circulo}{circun}[spPcirc] % % Establece el \emph{concepto} que verbalizará \mynvmath{NVDA/MathCAT} % después de verbalizar el término \emph{«perímetro de \dots»} al ejecutar el % comando sin modificar la impresión de este. Si se establece en |circun|,\newline % |$\spPcirc[read-sty=circun](O)$| se verbalizará \emph{«perímetro de la circunferencia con centro en O»} % en \mynvmath{NVDA/MathCAT}; si se establece en |circulo|, |$\spPcirc[read-sty=circulo](O)$| % se verbalizará \emph{«perímetro del círculo con centro en O»} en \mynvmath{NVDA/MathCAT}. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spPcirc] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el término \emph{«perímetro de \dots»} y el \emph{«concepto»} establecido % por la llave \mykey{read-sty} al ejecutar el comando sin modificar la % impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} % verbalizará el término, el concepto y el argumento \myparg{centro} o % \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n}; % si se establece en |false|, \mynvmath{NVDA/MathCAT} solo verbalizará el % argumento \myparg{centro} acorde al valor establecido por la llave \mykey{read-cmd}. % % \begin{important}* % Utilizar \mykey{concept}|=false| sin el argumento \myparg{centro} o con el % argumento \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % es equivalente a usar \mykey{silent}|=true|. % \end{important} % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spPcirc] % % Sobrescribe el \emph{concepto} establecido por la llave \mykey{read-sty} % que será verbalizado por \mynvmath{NVDA/MathCAT} al ejecutar el % comando sin modificar la impresión de este. El valor de esta llave % \emph{siempre} debe pasarse entre llaves `|{ }|' y se debe usar en % conjunto con la llave \mykey{concept}. % % Por ejemplo:~|$\spPcirc[read-sty=circulo,read-arg={disco}](O)$| % se verbalizará \emph{«perímetro del disco con centro en O»} en \mynvmath{NVDA/MathCAT}; % |$\spPcirc[concept=false,read-arg={disco}](O)$| se verbalizará % \emph{«disco con centro en O»} en \mynvmath{NVDA/MathCAT}. % % \begin{important}* % Utilizar esta llave con \mykey{concept}|=false| y el argumento % \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % es equivalente a usar \mykey{silent}|=true|. % \end{important} % % \keyexampcmd{print-sym}{usual \textnormal{\textcolor{lightgray}{\textbar}} % odot}{odot}[spPcirc] % % Establece si se \emph{usará} |\bigodot| o la letra `$C$' en la impresión % del comando sin modificar la verbalización de este. Si se establece en |usual|, % |$\spPcirc[print-sym=usual](O)$| imprimirá `$\mathcal{P}_{C(O)}$' % en la salida \texttt{PDF}; si se establece en \mykey{odot}, |$\spPcirc[print-sym=odot](O)$| % imprimirá `$\mathcal{P}_{\bigodot(O)}$' en la salida \texttt{PDF}. % % \keyexampcmd{sub-index}{subíndice}{no usado}[spPcirc] % % Establece un \emph{subíndice} sobre |\bigodot| o la letra `$C$' establecida % por la llave \mykey{print-sym} cuando se utiliza el argumento \myparg{centro}. % El valor de esta llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % Por ejemplo:~|$\spPcirc[sub-index={1}](O)$| imprimirá `$\mathcal{P}_{\bigodot_{1}(O)}$' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«perímetro de la circunferencia uno con centro en O»}. % % \keyexampcmd{read-sub}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spPcirc] % % Establece si se \emph{antepone} la palabra \emph{«sub»} al verbalizar % el subíndice establecido por la llave \mykey{sub-index} o al usar el % argumento \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % al ejecutar el comando sin modificar la impresión de este. Si se establece % en |true|, |$\spPcirc[read-sub=true](1)$| se verbalizará % \emph{«perímetro de la circunferencia sub uno»} en \mynvmath{NVDA/MathCAT}; % si se establece en |false|, \mynvmath{NVDA/MathCAT} verbalizará \emph{«perímetro de la circunferencia uno»}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spPcirc] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del término \emph{«perímetro de \dots»}, del concepto establecido por la % llave \mykey{read-sty} y del argumento \myparg{centro} o \myparg{1 \textnormal{\textcolor{lightgray}{\textbar}} 2 \textnormal{\textcolor{lightgray}{\textbar}} 3 \textnormal{\textcolor{lightgray}{\textbar}} \ldots \textnormal{\textcolor{lightgray}{\textbar}} n} % pasado al comando sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se % establece en |false|, \mynvmath{NVDA/MathCAT} sí verbalizará la % salida del comando. % % \subsection*{El comando \cs[no-index]{spVol}}\label{subsec:spVol} % % \begin{function}{\spVol} % \begin{syntax} % |\spVol| \qquad\qquad |\spVol|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % \end{syntax} % El comando \ics*{spVol} trabaja en \emph{modo matemático} y recibe % el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % mediante el cual se establece la \emph{«forma»} en la que se verbalizará % en \mynvmath{NVDA/MathCAT}. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spVol$| imprimirá `$\mathcal{V}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«volumen»}. % % \subsubsection*{Llaves para \cs[no-index]{spVol}} % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spVol] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el concepto \emph{«volumen»} al ejecutar el comando sin modificar la % impresión de este. Si se establece en |true|, |$\spVol$| se verbaliza % como \emph{«volumen»} en \mynvmath{NVDA/MathCAT}; si se establece en % |false|, se verbalizará la letra `$\mathcal{V}$' acorde al valor % establecido por la llave \mykey{read-cmd}. % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[spVol] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará la % letra `$\mathcal{V}$' cuando \mykey{concept}|=false|. Si se establece en |school|, % \mynvmath{NVDA/MathCAT} verbalizará únicamente la letra `$V$'; si se % establece en |mathcat|, \mynvmath{NVDA/MathCAT} aplica sus propias reglas % sobre `$\mathcal{V}$'. % % \keyexampcmd{sub-index}{subíndice}{no usado}[spVol] % % Establece un \emph{subíndice} sobre `$\mathcal{V}$'. El valor de esta % llave \emph{siempre} debe pasarse entre llaves `|{ }|'. Por ejemplo:~% % |$\spVol[sub-index={1}]$| imprimirá `$\mathcal{V}_{1}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«volumen uno»}. % % \keyexampcmd{read-sub}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spVol] % % Establece si se \emph{antepone} la palabra \emph{«sub»} al verbalizar % el subíndice establecido por la llave \mykey{sub-index} al ejecutar el % comando sin modificar la impresión de este. Si se establece en |true|,\newline % |$\spVol[sub-index={1},read-sub=true]$| se verbalizará \emph{«volumen sub % uno»} en \mynvmath{NVDA/MathCAT}; si se establece en |false|, % \mynvmath{NVDA/MathCAT} verbalizará \emph{«volumen uno»}. % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spVol] % % Sobrescribe el \emph{concepto} establecido por la llave \mykey{concept} que % será verbalizado por \mynvmath{NVDA/MathCAT} al ejecutar el comando sin % modificar la impresión de este. El valor de esta llave \emph{siempre} % debe pasarse entre llaves `|{ }|' y se debe usar en conjunto % con la llave \mykey{concept}. % % Por ejemplo:~|$\spVol[read-arg={medida exacta}]$| imprimirá `$\mathcal{V}$' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«volumen medida exacta»}; % |$\spVol[concept=false,read-arg={medida exacta}]$| imprimirá `$\mathcal{V}$' % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«medida exacta»}. % % \begin{important}* % Las llaves \mykey{sub-index} y \mykey{read-sub} no tienen efecto al usar \mykey{read-arg}. % \end{important} % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spVol] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del \emph{concepto} establecido por la llave \mykey{concept} y de % `$\mathcal{V}$' al ejecutar el comando sin modificar la impresión de % este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} NO verbalizará % la salida del comando; si se establece en |false|, \mynvmath{NVDA/MathCAT} % sí verbalizará la salida del comando. % % \section{Utilidades para álgebra}\label{sec:algebra} % % El paquete \mypkg*[no-index]{spintent} provee utilidades para trabajar con % álgebra, descritas en esta sección: |\spinterval| para los intervalos, % |\spfun| para las funciones, |\spdom|, |\sprec| y |\spcod| para el dominio, el % recorrido y el codominio de una función, |\spfuncomp| para la composición de % funciones y |\spabs| para el valor absoluto. Todos trabajan en \emph{modo % matemático} y reciben un \emph{argumento obligatorio}. % % El nombre de una función sigue la misma regla en |\spfun|, |\spdom|, |\sprec|, % |\spcod| y |\spfuncomp| (v.\,pág.\,\pageref{subsec:spfun}). Un argumento % que no cumple la forma documentada devuelve un \enquote{error} y se detiene % la compilación del documento. % % \subsection*{El comando \cs[no-index]{spinterval}}\label{subsec:spinterval} % % \begin{function}{\spinterval} % \begin{syntax} % |\spinterval|\mymarg{intervalo} \qquad\qquad |\spinterval|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{intervalo} % \end{syntax} % El comando \ics*{spinterval} trabaja en \emph{modo matemático} y recibe % el \emph{argumento} obligatorio \mymarg{intervalo} de la forma % \mymarg{(a\textnormal{\textcolor{red}{;}} b)}, donde `$a$' y `$b$' son los % \emph{extremos} del intervalo separados por un punto y coma % `\textcolor{red}{;}' o, si no hay punto y coma, por una única coma % `\textcolor{red}{,}'. El delimitador izquierdo puede ser `|(|', `|[|' o % `|]|' y el derecho `|)|', `|]|' o `|[|', en cualquier combinación. % Los extremos numéricos se procesan internamente bajo el comando % |\spnum|; cualquier otro extremo, como una letra, un subíndice, una % fracción o una raíz, se imprime tal cual y lo verbaliza % \mynvmath{NVDA/MathCAT}. % \end{function} % % Un delimitador `|(|' o `|]|' a la izquierda, y `|)|' o `|[|' a la derecha, % \emph{excluye} su extremo y el intervalo es \emph{abierto} en él; los demás % delimitadores lo \emph{incluyen} y el intervalo es \emph{cerrado} en él. % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % y el intervalo es abierto en sus dos extremos, |$\spinterval{(1,5)}$| % imprimirá `$(1,5)$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«intervalo abierto de uno a cinco»}; la notación % chilena |$\spinterval{]1,5[}$| imprimirá `$]1,5[$' y se verbalizará igual. % % Si el intervalo es cerrado en sus dos extremos, |$\spinterval{[1,5]}$| % imprimirá `$[1,5]$' y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«intervalo cerrado de uno a cinco»}. % % Si el intervalo es abierto en un extremo y cerrado en el otro, % |$\spinterval{]1,5]}$| imprimirá `$]1,5]$' y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«intervalo abierto en uno y cerrado en cinco»}; % |$\spinterval{[1,5[}$| imprimirá `$[1,5[$' y verbalizará % \emph{«intervalo cerrado en uno y abierto en cinco»}. % % Si un extremo es infinito, |$\spinterval{]-\infty,3]}$| imprimirá % `$]-\infty,3]$' y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«intervalo abierto en menos infinito y cerrado en tres»}. % % Si los extremos son números decimales con coma, el separador debe ser\newline % el punto y coma:~|$\spinterval{[3,5;4,2]}$| imprimirá `$[3{,}5;4{,}2]$' y % \mynvmath{NVDA/MathCAT} verbalizará % \emph{«intervalo cerrado de tres coma cinco a cuatro coma dos»}. % % El comando |\spinterval| devuelve un \enquote{error} y se detiene la % compilación del documento cuando el \mymarg{intervalo} no comienza con un % delimitador izquierdo y termina con uno derecho de los indicados, cuando un % extremo está vacío, cuando no hay separador, cuando hay más de una coma sin % punto y coma, cuando las llaves no están balanceadas o cuando el infinito % está mal ubicado o se incluye en el intervalo. % % \begin{important}* % El argumento \mymarg{intervalo} siempre debe pasarse entre llaves % `|{ }|', incluso cuando comienza con un corchete: sin ellas, % |$\spinterval[1,5]$| interpreta los corchetes como el argumento opcional % \myoarg{key \textnormal{\textcolor{gray}{=}} val}. % \end{important} % % \begin{important}* % El infinito `$\mathcolor{red}{\infty}$' nunca se incluye en el intervalo: `$-\infty$' solo % puede ir en el extremo izquierdo y `$\mathcolor{red}{\infty}$' en el derecho, ambos junto % a un delimitador que los excluya. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spinterval}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[spinterval] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará los % extremos del intervalo. Si se establece en |school|, % |$\spinterval[read-cmd=school]{]-2,+4[}$| se verbalizará % \emph{«intervalo abierto de menos dos a más cuatro»}; si se establece en % |mathcat|, \mynvmath{NVDA/MathCAT} aplica sus propias reglas y\newline % |$\spinterval[read-cmd=mathcat]{]-2,+4[}$| se verbalizará % \emph{«intervalo abierto de menos dos a positivo cuatro»}. % % \keyexampcmd{prefix}{prefijo}{no usado}[spinterval] % % Establece el \emph{prefijo} que será verbalizado por \mynvmath{NVDA/MathCAT} % \emph{antes} de verbalizar el \emph{«concepto»} intervalo. El valor de esta % llave \emph{siempre} debe pasarse entre llaves `|{ }|'. Por ejemplo:~% % |$\spinterval[prefix={el}]{(1,5)}$| imprimirá `$(1,5)$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«el intervalo abierto de uno a cinco»}. % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spinterval] % % Sobrescribe la verbalización del \emph{intervalo} completo que será % verbalizada por \mynvmath{NVDA/MathCAT} al ejecutar el comando sin modificar % la impresión de este. El valor de esta llave \emph{siempre} debe pasarse % entre llaves `|{ }|'. Por ejemplo:~|$\spinterval[read-arg={rango}]{[1,5]}$| % imprimirá `$[1,5]$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«rango»}. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spinterval] % % Establece si se \emph{verbaliza} en \mynvmath{NVDA/MathCAT} % el \emph{«concepto»} intervalo y el prefijo establecido por la llave % \mykey{prefix} al ejecutar el comando sin modificar la impresión de este. Si % se establece en |true|, \mynvmath{NVDA/MathCAT} verbalizará el % \emph{«concepto»} y los extremos; si se establece en |false|, % |$\spinterval[concept=false]{(1,5)}$| se verbalizará % \emph{«abierto de uno a cinco»}. % % \keyexampcmd{size}{auto \textnormal{\textcolor{lightgray}{\textbar}} % none \textnormal{\textcolor{lightgray}{\textbar}} % big \textnormal{\textcolor{lightgray}{\textbar}} % Big \textnormal{\textcolor{lightgray}{\textbar}} % bigg \textnormal{\textcolor{lightgray}{\textbar}} % Bigg}{auto}[spinterval] % % Establece el \emph{tamaño} de los delimitadores del intervalo sin modificar % la verbalización de este. Si se establece en \mykey{auto}, los delimitadores % conservan su tamaño normal salvo que algún extremo contenga una fracción % |\frac| o |\dfrac|, en cualquier posición del extremo: en ese caso se usan % |\bigl| y |\bigr| en estilo texto, |\Bigl| y |\Bigr| en estilo display, y % siempre |\Bigl| y |\Bigr| cuando la fracción es |\dfrac|. Si se establece en % \mykey{none}, los delimitadores nunca cambian de tamaño; si se establece en % \mykey{big}, \mykey{Big}, \mykey{bigg} o \mykey{Bigg}, se usan siempre % |\bigl| y |\bigr|, |\Bigl| y |\Bigr|, |\biggl| y |\biggr|, o |\Biggl| y % |\Biggr|, respectivamente. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spinterval] % % Establece si se \emph{silencia} la verbalización en \mynvmath{NVDA/MathCAT} % del \mymarg{intervalo} pasado al comando sin modificar la impresión de este. % Si se establece en |true|, \mynvmath{NVDA/MathCAT} NO verbalizará la salida % del comando; si se establece en |false|, \mynvmath{NVDA/MathCAT} sí % verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spfun}}\label{subsec:spfun} % % \begin{function}{\spfun} % \begin{syntax} % |\spfun|\mymarg{función} \qquad\qquad |\spfun|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{función} % \end{syntax} % El comando \ics*{spfun} trabaja en \emph{modo matemático} y recibe el % \emph{argumento} obligatorio \mymarg{función} de la forma % \mymarg{f(x)}, donde `$f$' es el \emph{nombre} de la función y lo que va % entre paréntesis son uno o más \emph{argumentos} separados por coma % `\textcolor{red}{,}'. El nombre es una letra, mayúscula o minúscula, con % prima(s) `|'|' o subíndice `|_{...}|' opcionales; el subíndice solo admite % letras y dígitos. Los argumentos pueden ser letras, números o % expresiones. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spfun{f(x)}$| imprimirá `$f(x)$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«f de x»}; % |$\spfun{f_{1}(x)}$| imprimirá `$f_{1}(x)$' y verbalizará % \emph{«f sub uno de x»}; |$\spfun{f'(x)}$| imprimirá `$f'(x)$' y % verbalizará \emph{«f prima de x»}; |$\spfun{f''(x)}$| imprimirá % `$f''(x)$' y verbalizará \emph{«f doble prima de x»}. % % Con dos o más argumentos, |$\spfun{f(x,y)}$| imprimirá `$f(x,y)$' y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«f de x coma y»}. Un argumento % puede ser una expresión: |$\spfun{f(x+1)}$| imprimirá `$f(x+1)$' y % verbalizará \emph{«f de x más 1»}. % % Un argumento puede ser a su vez una llamada, tantas veces como se quiera: % |$\spfun{f(g(x))}$| imprimirá `$f(g(x))$' y verbalizará % \emph{«f de g de x»}; |$\spfun{f(f^{-1}(y))}$| imprimirá `$f(f^{-1}(y))$' y % verbalizará \emph{«f de f inversa de y»}; |$\spfun{f(g(x),h(y))}$| imprimirá % `$f(g(x),h(y))$'. Una llamada anidada se valida igual que la exterior. Si % el argumento es otra cosa, como |g(x)+1|, se imprime y se verbaliza con % normalidad pero sin anotar la llamada que contiene. Las llaves % \mykey{read-cmd} y \mykey{silent} valen para todos los niveles; % \mykey{concept} y \mykey{prefix} solo para la llamada exterior. % % Las comas que quedan dentro de llaves, de paréntesis o de corchetes no % separan argumentos. Por eso, un número decimal con coma debe escribirse % entre llaves: |$\spfun{f(1{,}5)}$| tiene un solo argumento. % % El comando |\spfun| devuelve un \enquote{error} y se detiene la compilación % del documento cuando el \mymarg{función} no comienza con un nombre válido, % cuando el subíndice contiene algo distinto de letras o dígitos, cuando el % nombre no va seguido de paréntesis, cuando los paréntesis, las llaves o los % corchetes no están balanceados, cuando un argumento está vacío o cuando hay % algo después del paréntesis que cierra. Lo mismo vale para una llamada % anidada en un argumento. % % \subsubsection*{Llaves para \cs[no-index]{spfun}} % % \keyexampcmd{read-cmd}{school \textnormal{\textcolor{lightgray}{\textbar}} % mathcat}{school}[spfun] % % Establece la \emph{forma} en la que \mynvmath{NVDA/MathCAT} verbalizará el % nombre de la función. Si se establece en |school|, \mynvmath{NVDA/MathCAT} % verbaliza el nombre sin el prefijo «mayúscula»: |$\spfun{F''(x)}$| se % verbaliza \emph{«F doble prima de x»}; si se establece en |mathcat|, % \mynvmath{NVDA/MathCAT} aplica sus propias reglas, que pueden incluir el % prefijo «mayúscula» dependiendo de la configuración del usuario. % % \keyexampcmd{prefix}{prefijo}{no usado}[spfun] % % Establece el \emph{prefijo} que será verbalizado por % \mynvmath{NVDA/MathCAT} \emph{antes} de verbalizar el concepto % \emph{«función»}. El valor de esta llave \emph{siempre} debe pasarse entre % llaves `|{ }|'. Solo se verbaliza cuando \mykey{concept} es |true|. % % \keyexampcmd{concept}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spfun] % % Establece si se verbaliza el concepto \emph{«función»} antes de la % función. Si se establece en |true|, \mynvmath{NVDA/MathCAT} verbalizará el % concepto, el prefijo establecido por la llave \mykey{prefix} y la función; % si se establece en |false|, solo verbalizará la función. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spfun] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} de la \mymarg{función} pasada al comando sin % modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se % establece en |false|, sí verbalizará la salida del comando. El argumento % se valida igual en ambos casos. % % \subsection*{Los comandos \cs[no-index]{spdom}, \cs[no-index]{sprec} y \cs[no-index]{spcod}}\label{subsec:spdom} % % \begin{function}{\spdom, \sprec, \spcod} % \begin{syntax} % |\spdom|\mymarg{función} \quad\quad |\spdom|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{función} % |\sprec|\mymarg{función} \quad\quad |\sprec|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{función} % |\spcod|\mymarg{función} \quad\quad |\spcod|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{función} % \end{syntax} % Los comandos \ics*{spdom}, \ics*{sprec} y \ics*{spcod} trabajan en % \emph{modo matemático} y reciben el \emph{argumento} obligatorio % \mymarg{función}: el nombre de una función como en |\spfun|, esto es, una letra % con prima(s) o subíndice opcionales y, si se quiere, el exponente % `|^{-1}|', sin argumentos. Imprimen respectivamente $\operatorname{Dom}$, % $\operatorname{Rec}$ y $\operatorname{Cod}$ seguidos de la función entre % paréntesis. % \end{function} % % Si no se proporciona el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val}, |$\spdom{f}$| % imprimirá `$\operatorname{Dom}(f)$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«dominio de f»}; % |$\sprec{f}$| imprimirá `$\operatorname{Rec}(f)$' y verbalizará % \emph{«recorrido de f»}; |$\spcod{f}$| imprimirá % `$\operatorname{Cod}(f)$' y verbalizará \emph{«codominio de f»}. % % El nombre de la función admite prima(s) y subíndice, y se verbaliza sin % anunciar la mayúscula: |$\spdom{f'}$| imprimirá `$\operatorname{Dom}(f')$' % y verbalizará \emph{«dominio de f prima»}; |$\sprec{f_{1}}$| imprimirá % `$\operatorname{Rec}(f_{1})$' y verbalizará \emph{«recorrido de f sub % uno»}; |$\spcod{F''}$| imprimirá `$\operatorname{Cod}(F'')$' y verbalizará % \emph{«codominio de F doble prima»}. % % Con la inversa, |$\spdom{f^{-1}}$| imprimirá % `$\operatorname{Dom}(f^{-1})$' y verbalizará % \emph{«dominio de f inversa»};\newline |$\sprec{f^{-1}}$| verbalizará % \emph{«recorrido de f inversa»}. % % Los comandos devuelven un \enquote{error} y se detiene la compilación % del documento cuando el argumento no es el nombre de una función. % % \subsubsection*{Llaves para \cs[no-index]{spdom}, \cs[no-index]{sprec} y \cs[no-index]{spcod}} % % \keyexampcmd{prefix}{prefijo}{no usado}[spdom] % % Establece el \emph{prefijo} que será verbalizado por % \mynvmath{NVDA/MathCAT} \emph{antes} de la palabra del concepto. El valor % de esta llave \emph{siempre} debe pasarse entre llaves `|{ }|'. Con % |$\spdom[prefix={el}]{f}$|, \mynvmath{NVDA/MathCAT} verbalizará % \emph{«el dominio de f»}; con |$\sprec[prefix={el}]{f}$|, \emph{«el % recorrido de f»}; con |$\spcod[prefix={el}]{f}$|, \emph{«el codominio de % f»}. La impresión no cambia. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spdom] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} de la \mymarg{función} pasada al comando sin % modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando: % |$\spdom[silent=true]{f}$| imprimirá `$\operatorname{Dom}(f)$' y no se % verbalizará nada; si se establece en |false|, sí verbalizará la salida del % comando. El argumento se valida igual en ambos casos. % % \subsection*{El comando \cs[no-index]{spfuncomp}}\label{subsec:spfuncomp} % % \begin{function}{\spfuncomp} % \begin{syntax} % |\spfuncomp|\mymarg{composición} \qquad\qquad |\spfuncomp|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{composición} % \end{syntax} % El comando \ics*{spfuncomp} trabaja en \emph{modo matemático} y recibe el % \emph{argumento} obligatorio \mymarg{composición}: dos o más % \emph{funciones} separadas por |\circ|, como en \mymarg{f\circ g} o % \mymarg{f\circ g\circ h}. Cada función es un nombre como en |\spfun|: % una letra, mayúscula o minúscula, con prima(s) o subíndice opcionales, y el % exponente `|^{-1}|' de la función inversa. La composición puede ir entre % paréntesis, \mymarg{(f\circ g)}, y puede aplicarse a uno o más argumentos % entre paréntesis separados por coma, \mymarg{(f\circ g)(x)}. Los % argumentos siguen las reglas de |\spfun|: pueden ser letras, números, % expresiones o llamadas. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spfuncomp{f\circ g}$| imprimirá `$f\circ g$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«f compuesta con g»}; % |$\spfuncomp{f\circ g\circ h}$| imprimirá `$f\circ g\circ h$' y verbalizará % \emph{«f compuesta con g compuesta con h»}; % |$\spfuncomp{(f\circ g)(x)}$| imprimirá `$(f\circ g)(x)$' y verbalizará % \emph{«f compuesta con g de x»}; |$\spfuncomp{(f^{-1}\circ g)(x)}$| imprimirá % `$(f^{-1}\circ g)(x)$' y verbalizará \emph{«f inversa compuesta con g de x»}. % Un argumento puede ser una llamada: |$\spfuncomp{(f\circ g)(h(x))}$| % verbalizará \emph{«f compuesta con g de h de x»}. % % El comando |\spfuncomp| devuelve un \enquote{error} y se detiene la % compilación del documento cuando hay menos de dos funciones, cuando algo % entre los |\circ| no es el nombre de una función, cuando los paréntesis no % están balanceados, cuando lo que sigue a la composición no es una lista de % argumentos entre paréntesis o cuando un argumento está vacío. La forma % |f\circ g(x)|, sin paréntesis alrededor de la composición, no es válida % porque es ambigua. % % \subsubsection*{Llaves para \cs[no-index]{spfuncomp}} % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spfuncomp] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} de la \mymarg{composición} pasada al comando sin % modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se % establece en |false|, sí verbalizará la salida del comando. El argumento % se valida igual en ambos casos. % % La composición no admite \mykey{prefix} ni \mykey{read-cmd}: no lleva % palabra de concepto y su lectura es fija. Los nombres se verbalizan sin % anunciar la mayúscula. % % \subsection*{El comando \cs[no-index]{spabs}}\label{subsec:spabs} % % \begin{function}{\spabs} % \begin{syntax} % |\spabs|\mymarg{x} \qquad\qquad |\spabs|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{x} % \end{syntax} % El comando \ics*{spabs} trabaja en \emph{modo matemático} y recibe el % \emph{argumento obligatorio} \mymarg{x}: cualquier expresión matemática. % Imprime el valor absoluto de \mymarg{x} entre las barras |\lvert| y % |\rvert|. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spabs{x}$| imprimirá `$\lvert x\rvert$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«valor absoluto de x»}; % |$\spabs{-5}$| imprimirá `$\lvert -5\rvert$' y verbalizará \emph{«valor % absoluto de menos 5»}; |$\spabs{x-3}$| imprimirá `$\lvert x-3\rvert$' y % verbalizará \emph{«valor absoluto de x menos 3 fin valor absoluto»}. % % Cuando el argumento tiene más de un símbolo, sin contar un signo `|+|' o % `|-|' inicial, \mynvmath{NVDA/MathCAT} anuncia el final del valor absoluto % con \emph{«fin valor absoluto»}. Así se sabe dónde termina cuando la % expresión sigue: |$\spabs{x-3} - 1$| verbalizará \emph{«valor absoluto de x % menos 3 fin valor absoluto menos 1»}. La llave \mykey{close} controla % este cierre. % % El comando |\spabs| devuelve un \enquote{error} y se detiene la compilación % del documento cuando el argumento está vacío. % % \subsubsection*{Llaves para \cs[no-index]{spabs}} % % \keyexampcmd{close}{auto \textnormal{\textcolor{lightgray}{\textbar}} % true \textnormal{\textcolor{lightgray}{\textbar}} % false}{auto}[spabs] % % Establece si \mynvmath{NVDA/MathCAT} verbaliza el cierre \emph{«fin valor % absoluto»} después del argumento. Con |auto| el cierre se verbaliza solo % cuando \mymarg{x} tiene más de un símbolo, sin contar un signo |+| o |-| % inicial: |$\spabs{x}$| y |$\spabs{-5}$| no lo llevan y |$\spabs{x-3}$| sí. % Con |true| siempre se verbaliza: |$\spabs[close=true]{x}$| verbalizará % \emph{«valor absoluto de x fin valor absoluto»}. Con |false| nunca se % verbaliza: |$\spabs[close=false]{x-3}$| verbalizará \emph{«valor absoluto de % x menos 3»}. La llave no tiene efecto con \mykey{silent} ni con % \mykey{read-arg}. % % \keyexampcmd{size}{none \textnormal{\textcolor{lightgray}{\textbar}} % big \textnormal{\textcolor{lightgray}{\textbar}} Big % \textnormal{\textcolor{lightgray}{\textbar}} bigg % \textnormal{\textcolor{lightgray}{\textbar}} Bigg}{none}[spabs] % % Establece el \emph{tamaño} de las barras. Con |none| no se cambia el % tamaño; con |big|, |Big|, |bigg| y |Bigg| se fija con |\bigl|, |\Bigl|, % |\biggl| y |\Biggl|, y sus pares de cierre. Por ejemplo:~% % |$\spabs[size=Big]{\dfrac{x}{2}}$| imprime las barras más grandes que el % texto y no cambia la verbalización. % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spabs] % % Sobrescribe lo que verbalizará \mynvmath{NVDA/MathCAT} al ejecutar el % comando sin modificar la impresión de este. El valor de esta llave % \emph{siempre} debe pasarse entre llaves `|{ }|'. Por ejemplo:~% % |$\spabs[read-arg={módulo de x}]{x}$| imprimirá `$\lvert x\rvert$' en la % salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«módulo de % x»}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spabs] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} del valor absoluto al ejecutar el comando sin % modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se % establece en |false|, sí verbalizará la salida del comando. % % \section{Utilidades para estadística}\label{sec:estadistica} % % El paquete \mypkg*[no-index]{spintent} provee utilidades para trabajar con % estadística y probabilidad, descritas en esta sección. Todos los comandos % trabajan en \emph{modo matemático}. % % Los comandos |\spmedia|, |\spmediana|, |\spmoda|, |\spdesv|, |\spvarz|, % |\sprango|, |\spriq|, |\spmin|, |\spmax| y |\spOmega| escriben un símbolo y no % reciben argumento obligatorio, solo el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val} que establece cómo se % verbaliza. Los demás, que son |\spProm|, |\spcuartil|, |\spdecil|, |\spPerc|, % |\spFrec|, |\spcase|, |\spPval|, |\spProb|, |\spcomp|, |\spcomb| y |\spfact|, % reciben \emph{argumentos obligatorios}; si no cumplen la forma documentada % devuelven un \enquote{error} y se detiene la compilación del documento. % % \subsection*{El comando \cs[no-index]{spProm}}\label{subsec:spProm} % % \begin{function}{\spProm} % \begin{syntax} % |\spProm|\mymarg{argumento} \qquad\qquad |\spProm|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{argumento} % \end{syntax} % El comando \ics*{spProm} trabaja en \emph{modo matemático} y recibe el % \emph{argumento} obligatorio \mymarg{argumento}: cualquier expresión % matemática, como una letra, una letra con subíndice o una expresión. % Lo tipografía con una barra encima, la notación usual del % \emph{«promedio»}. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spProm{x}$| imprimirá `$\bar{x}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«promedio de x»}; % |$\spProm{x_{1}}$| imprimirá `$\bar{x_{1}}$' y verbalizará % \emph{«promedio de x subíndice uno»}. % % \begin{important}* % El \mymarg{argumento} se tipografía directamente, sin procesamiento % semántico adicional, y \mynvmath{NVDA/MathCAT} lo verbaliza con sus % propias reglas. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spProm}} % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spProm] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} del \mymarg{argumento} pasado al comando sin % modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se % establece en |false|, sí verbalizará la salida del comando. % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spProm] % % Sobrescribe las palabras \emph{«promedio de»} que verbaliza % \mynvmath{NVDA/MathCAT} antes del \mymarg{argumento}, sin modificar la % impresión del comando. El valor de esta llave \emph{siempre} debe pasarse % entre llaves `|{ }|'. Por ejemplo:~|$\spProm[read-arg={media de}]{x}$| % imprimirá `$\bar{x}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«media de x»}. % % \subsection*{Los comandos \cs[no-index]{spmedia}, \cs[no-index]{spmediana}, \cs[no-index]{spmoda}, \cs[no-index]{spdesv} y \cs[no-index]{spvarz}}\label{subsec:spmedia} % % \begin{function}{\spmedia, \spmediana, \spmoda, \spdesv, \spvarz} % \begin{syntax} % |\spmedia| \quad\quad |\spmedia|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % |\spmediana| \quad\quad |\spmediana|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % |\spmoda| \quad\quad |\spmoda|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % |\spdesv| \quad\quad |\spdesv|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % |\spvarz| \quad\quad |\spvarz|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % \end{syntax} % Los comandos \ics*{spmedia}, \ics*{spmediana}, \ics*{spmoda}, % \ics*{spdesv} y \ics*{spvarz} trabajan en \emph{modo matemático} y % reciben el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % mediante el cual se establece la \emph{«forma»} en la que se % verbalizarán en \mynvmath{NVDA/MathCAT} los símbolos de la estadística % descriptiva. % \end{function} % % Si no se proporciona el \emph{argumento opcional}, |$\spmedia$| % imprimirá `$\mathrm{MA}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«media aritmética»}; % |$\spmediana$| imprimirá `$\mathrm{MD}$' y verbalizará % \emph{«mediana»}; |$\spmoda$| imprimirá `$\mathrm{MO}$' y verbalizará % \emph{«moda»}; |$\spdesv$| imprimirá `$s$' y verbalizará % \emph{«desviación estándar s»}; |$\spvarz$| imprimirá `$s^{2}$' y % verbalizará \emph{«varianza s al cuadrado»}. % % \subsubsection*{Llaves para \cs[no-index]{spmedia}, \cs[no-index]{spmediana}, \cs[no-index]{spmoda}, \cs[no-index]{spdesv} y \cs[no-index]{spvarz}} % % \keyexampcmd{sigma}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spdesv,spvarz] % % Establece si se \emph{imprime} la letra `$\sigma$' en lugar de la letra % `$s$'. Si se establece en |true|, |$\spdesv[sigma=true]$| imprimirá % `$\sigma$' y |$\spvarz[sigma=true]$| imprimirá `$\sigma^{2}$' en la % salida \texttt{PDF}, y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«desviación estándar sigma»} y \emph{«varianza sigma al cuadrado»}. % Esta llave solo existe para |\spdesv| y |\spvarz|. % % \keyexampcmd{sub-index}{subíndice}{no usado}[spmedia,spmediana,spmoda,spdesv,spvarz] % % Establece un \emph{subíndice} sobre el símbolo del comando, que % \mynvmath{NVDA/MathCAT} verbaliza después del concepto con la palabra % \emph{«de»}, o \emph{«sub»} en |\spdesv| y |\spvarz| según la llave % \mykey{read-sub}. El valor de esta llave \emph{siempre} debe pasarse entre % llaves `|{ }|'. Por ejemplo:~|$\spmedia[sub-index={x}]$| imprimirá % `$\mathrm{MA}_{x}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«media aritmética de x»}; % |$\spdesv[sub-index={x}]$| imprimirá `$s_{x}$' y verbalizará % \emph{«desviación estándar s sub x»}; |$\spvarz[sub-index={x}]$| imprimirá % `$s_{x}^{2}$' y verbalizará \emph{«varianza s sub x al cuadrado»}. % % \keyexampcmd{read-sub}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spmedia,spmediana,spmoda,spdesv,spvarz] % % Establece si se \emph{usa} la palabra \emph{«sub»} en lugar de % \emph{«de»} al verbalizar el subíndice establecido por la llave % \mykey{sub-index}, sin modificar la impresión del comando. Si se % establece en |true|,\newline |$\spmedia[sub-index={x},read-sub=true]$| se % verbalizará \emph{«media aritmética sub x»} en \mynvmath{NVDA/MathCAT}; % si se establece en |false|, \mynvmath{NVDA/MathCAT} verbalizará % \emph{«media aritmética de x»}. El valor por defecto es |false|, salvo en % |\spdesv| y |\spvarz|, donde es |true| porque estos comandos % leen su letra y «s sub x» es más claro que «s de x»: en % \emph{«varianza s de x al cuadrado»}, la «x» podría entenderse como parte % del exponente. Así, |$\spvarz[sub-index={x},read-sub=false]$| verbalizará % \emph{«varianza s de x al cuadrado»}. % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spmedia,spmediana,spmoda,spdesv,spvarz] % % Sobrescribe el \emph{concepto} que verbalizará \mynvmath{NVDA/MathCAT} al % ejecutar el comando sin modificar la impresión de este. El valor de esta % llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % % Por ejemplo:~|$\spdesv[read-arg={desviación estándar poblacional}]$| % imprimirá `$s$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«desviación estándar poblacional»}. % % \begin{important}* % Las llaves \mykey{sub-index} y \mykey{read-sub} no tienen efecto al usar \mykey{read-arg}. % \end{important} % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spmedia,spmediana,spmoda,spdesv,spvarz] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} del símbolo al ejecutar el comando sin modificar % la impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} % NO verbalizará la salida del comando; si se establece en |false|, % \mynvmath{NVDA/MathCAT} sí verbalizará la salida del comando. % % \subsection*{Los comandos \cs[no-index]{sprango}, \cs[no-index]{spriq}, \cs[no-index]{spmin} y \cs[no-index]{spmax}}\label{subsec:sprango} % % \begin{function}{\sprango, \spriq, \spmin, \spmax} % \begin{syntax} % |\sprango| \quad\quad |\sprango|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % |\spriq| \quad\quad |\spriq|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % |\spmin| \quad\quad |\spmin|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % |\spmax| \quad\quad |\spmax|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % \end{syntax} % Los comandos \ics*{sprango}, \ics*{spriq}, \ics*{spmin} y \ics*{spmax} % trabajan en \emph{modo matemático} y reciben el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val} mediante el cual se % establece la \emph{«forma»} en la que se verbalizarán en % \mynvmath{NVDA/MathCAT} estas medidas de la estadística descriptiva. % \end{function} % % Si no se proporciona el \emph{argumento opcional}, |$\sprango$| % imprimirá `$R$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«rango»}; |$\spriq$| imprimirá `$\mathrm{RIQ}$' y % verbalizará \emph{«rango intercuartil»}; |$\spmin$| imprimirá % `$x_{\min}$' y verbalizará \emph{«mínimo»}; |$\spmax$| imprimirá % `$x_{\max}$' y verbalizará \emph{«máximo»}. % % \subsubsection*{Llaves para \cs[no-index]{sprango}, \cs[no-index]{spriq}, \cs[no-index]{spmin} y \cs[no-index]{spmax}} % % Las llaves \mykey{silent}, \mykey{read-arg}, \mykey{sub-index} y % \mykey{read-sub} de estos comandos funcionan como las de % |\spmedia| (v.\,pág.\,\pageref{subsec:spmedia}), con una % excepción: |\spmin| y |\spmax| solo tienen \mykey{silent} y % \mykey{read-arg}, porque su símbolo ya lleva un subíndice. % % \subsection*{Los comandos \cs[no-index]{spcuartil}, \cs[no-index]{spdecil} y \cs[no-index]{spPerc}}\label{subsec:spcuartil} % % \begin{function}{\spcuartil, \spdecil, \spPerc} % \begin{syntax} % |\spcuartil|\mymarg{k} \quad\quad |\spcuartil|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{k} % |\spdecil|\mymarg{k} \quad\quad |\spdecil|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{k} % |\spPerc|\mymarg{k} \quad\quad |\spPerc|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{k} % \end{syntax} % Los comandos \ics*{spcuartil}, \ics*{spdecil} y \ics*{spPerc} trabajan % en \emph{modo matemático} y reciben el \emph{argumento obligatorio} % \mymarg{k}: un número entero sin signo, de 1 a 3 en \ics*{spcuartil}, % de 1 a 9 en \ics*{spdecil} y de 1 a 99 en \ics*{spPerc}. Cualquier otro % valor devuelve un \enquote{error} y se detiene la compilación del documento. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spcuartil{1}$| imprimirá `$Q_{1}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«cuartil 1»}; % |$\spdecil{3}$| imprimirá `$D_{3}$' y verbalizará \emph{«decil 3»}; % |$\spPerc{25}$| imprimirá `$P_{25}$' y verbalizará % \emph{«percentil 25»}. % % \subsubsection*{Llaves para \cs[no-index]{spcuartil}, \cs[no-index]{spdecil} y \cs[no-index]{spPerc}} % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spcuartil,spdecil,spPerc] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} del símbolo al ejecutar el comando sin modificar % la impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} % NO verbalizará la salida del comando; si se establece en |false|, sí % verbalizará la salida del comando. Un índice inválido devuelve siempre un % \enquote{error} y se detiene la compilación del documento, % también con \mykey{silent}|=true|. % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spcuartil,spdecil,spPerc] % % Sobrescribe lo que verbalizará \mynvmath{NVDA/MathCAT} al ejecutar el % comando sin modificar la impresión de este. El valor de esta llave % \emph{siempre} debe pasarse entre llaves `|{ }|'.\newline Por ejemplo:~% % |$\spcuartil[read-arg={primer cuartil}]{1}$| imprimirá `$Q_{1}$' en la % salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«primer cuartil»}. % % \subsection*{El comando \cs[no-index]{spFrec}}\label{subsec:spFrec} % % \begin{function}{\spFrec} % \begin{syntax} % |\spFrec|\mymarg{símbolo} \qquad\qquad |\spFrec|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{símbolo} % \end{syntax} % El comando \ics*{spFrec} trabaja en \emph{modo matemático} y recibe el % \emph{argumento obligatorio} \mymarg{símbolo}: el símbolo de una % frecuencia, escrito tal como se imprime. Los símbolos válidos son % |f|, |F|, |f_{r}|, |F_{r}|, \myhlc|f_{r}\%| y \myhlc|F_{r}\%|; cualquier otro % devuelve un \enquote{error} y se detiene la compilación del documento, salvo % que se use la llave % \mykey{read-arg}. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spFrec{f}$| imprimirá `$f$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«frecuencia absoluta»}; % |$\spFrec{F}$| imprimirá `$F$' y verbalizará \emph{«frecuencia absoluta % acumulada»}; |$\spFrec{f_{r}}$| imprimirá `$f_{r}$' y verbalizará % \emph{«frecuencia relativa»}; |$\spFrec{F_{r}}$| imprimirá `$F_{r}$' y % verbalizará \emph{«frecuencia relativa acumulada»}; % \myhlc|$\spFrec{f_{r}\%}$| imprimirá `$f_{r}\%$' y verbalizará \emph{«frecuencia % porcentual»}; \myhlc|$\spFrec{F_{r}\%}$| imprimirá `$F_{r}\%$' y verbalizará % \emph{«frecuencia porcentual acumulada»}. % % \subsubsection*{Llaves para \cs[no-index]{spFrec}} % % \keyexampcmd{sub-index}{subíndice}{no usado}[spFrec] % % Establece un \emph{subíndice} sobre el símbolo, que % \mynvmath{NVDA/MathCAT} verbaliza después del nombre de la frecuencia % con la palabra \emph{«sub»}. Solo es válida con los símbolos |f| y |F|. % El valor de esta llave \emph{siempre} debe pasarse entre llaves % `|{ }|'. Por ejemplo:~|$\spFrec[sub-index={i}]{f}$| imprimirá `$f_{i}$' en % la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«frecuencia absoluta sub i»}. % % \keyexampcmd{read-sub}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{true}[spFrec] % % Establece si se \emph{usa} la palabra \emph{«sub»} en lugar de % \emph{«de»} al verbalizar el subíndice establecido por la llave % \mykey{sub-index}, sin modificar la impresión del comando. Con % |$\spFrec[sub-index={i},read-sub=false]{f}$|, % \mynvmath{NVDA/MathCAT} verbalizará \emph{«frecuencia absoluta de i»}. % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spFrec] % % Sobrescribe el \emph{nombre de la frecuencia} que verbalizará % \mynvmath{NVDA/MathCAT} al ejecutar el comando sin modificar la % impresión de este. Es la única forma de usar un símbolo que no está en % la tabla anterior: |$\spFrec[read-arg={frecuencia absoluta}]{n_{i}}$| % imprimirá `$n_{i}$' y verbalizará \emph{«frecuencia absoluta»}. El valor % de esta llave \emph{siempre} debe pasarse entre llaves `|{ }|'. % % \begin{important}* % Las llaves \mykey{sub-index} y \mykey{read-sub} no tienen efecto al usar \mykey{read-arg}. % \end{important} % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spFrec] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} del símbolo al ejecutar el comando sin modificar % la impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} % NO verbalizará la salida del comando; si se establece en |false|, sí % verbalizará la salida del comando. Un símbolo inválido devuelve siempre un % \enquote{error} y se detiene la compilación del documento, % también con \mykey{silent}|=true|. % % \subsection*{El comando \cs[no-index]{spcase}}\label{subsec:spcase} % % \begin{function}{\spcase} % \begin{syntax} % |\spcase|\mymarg{letra} \qquad\qquad |\spcase|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{letra} % \end{syntax} % El comando \ics*{spcase} trabaja en \emph{modo matemático} y recibe el % \emph{argumento obligatorio} \mymarg{letra}: la letra que acompaña a los % casos de una probabilidad. Las letras válidas son |f|, |p| y |t|; % cualquier otra devuelve un \enquote{error} y se detiene la compilación del % documento. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spcase{f}$| imprimirá `$C_{f}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«casos favorables»}; % |$\spcase{p}$| imprimirá `$C_{p}$' y verbalizará \emph{«casos % posibles»}; |$\spcase{t}$| imprimirá `$C_{t}$' y verbalizará % \emph{«casos totales»}. % % \subsubsection*{Llaves para \cs[no-index]{spcase}} % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spcase] % % Sobrescribe el \emph{nombre de los casos} que verbalizará % \mynvmath{NVDA/MathCAT} al ejecutar el comando sin modificar la % impresión de este. El valor de esta llave \emph{siempre} debe pasarse % entre llaves `|{ }|'. Por ejemplo:~% % |$\spcase[read-arg={casos que cumplen}]{f}$| imprimirá `$C_{f}$' en la % salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«casos % que cumplen»}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spcase] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} del símbolo al ejecutar el comando sin modificar % la impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} % NO verbalizará la salida del comando; si se establece en |false|, sí % verbalizará la salida del comando. Una letra inválida devuelve siempre un % \enquote{error} y se detiene la compilación del documento, % también con \mykey{silent}|=true|. % % \subsection*{El comando \cs[no-index]{spOmega}}\label{subsec:spOmega} % % \begin{function}{\spOmega} % \begin{syntax} % |\spOmega| \qquad\qquad |\spOmega|\myoarg{key \textnormal{\textcolor{gray}{=}} val} % \end{syntax} % El comando \ics*{spOmega} trabaja en \emph{modo matemático} y recibe el % \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val} % mediante el cual se establece la \emph{«forma»} en la que se % verbalizará en \mynvmath{NVDA/MathCAT} el espacio muestral. % \end{function} % % Si no se proporciona el \emph{argumento opcional}, |$\spOmega$| % imprimirá `$\Omega$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«espacio muestral»}. % % \subsubsection*{Llaves para \cs[no-index]{spOmega}} % % \keyexampcmd{card}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spOmega] % % Establece si se \emph{imprime} el símbolo `$\#$' antes de la letra, para % representar la cardinalidad del espacio muestral. Si se establece en |true|, % |$\spOmega[card=true]$| imprimirá `$\#\Omega$' en la salida \texttt{PDF} % y \mynvmath{NVDA/MathCAT} verbalizará \emph{«cardinalidad del espacio % muestral»}. % % \keyexampcmd{sub-index}{subíndice}{no usado}[spOmega] % % Establece un \emph{subíndice} sobre la letra, que % \mynvmath{NVDA/MathCAT} verbaliza después del nombre con la palabra % \emph{«sub»}. El valor de esta llave % \emph{siempre} debe pasarse entre llaves `|{ }|'. % % Por ejemplo:~|$\spOmega[sub-index={1}]$| imprimirá `$\Omega_{1}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«espacio muestral % sub 1»}; |$\spOmega[card=true,sub-index={1}]$| imprimirá `$\#\Omega_{1}$' % y verbalizará \emph{«cardinalidad del espacio muestral sub 1»}. % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spOmega] % % Sobrescribe el \emph{nombre} que verbalizará \mynvmath{NVDA/MathCAT} al % ejecutar el comando sin modificar la impresión de este. El valor de esta % llave \emph{siempre} debe pasarse entre llaves `|{ }|'.\newline Por ejemplo:~% % |$\spOmega[read-arg={universo}]$| imprimirá `$\Omega$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«universo»}. % % \begin{important}* % La llave \mykey{sub-index} no tiene efecto al usar \mykey{read-arg}. % \end{important} % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spOmega] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} del símbolo al ejecutar el comando sin modificar % la impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} % NO verbalizará la salida del comando; si se establece en |false|, sí % verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spPval}}\label{subsec:spPval} % % \begin{function}{\spPval} % \begin{syntax} % |\spPval|\mymarg{casos}\mymarg{total} \qquad\qquad |\spPval|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{casos}\mymarg{total} % \end{syntax} % El comando \ics*{spPval} trabaja en \emph{modo matemático} y recibe % \emph{dos argumentos obligatorios}: \mymarg{casos} y \mymarg{total}, los % tipografía como una fracción y devuelve el \emph{«valor de una % probabilidad»}. % \end{function} % % Si no se proporciona el \emph{argumento opcional} % \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spPval{1}{5}$| imprimirá `$\frac{1}{5}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«1 de 5»}. % % \begin{important}* % Los argumentos \mymarg{casos} y \mymarg{total} se tipografían % directamente, sin procesamiento semántico adicional. % \end{important} % % \subsubsection*{Llaves para \cs[no-index]{spPval}} % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spPval] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} del \emph{valor de la probabilidad} pasado al % comando sin modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se % establece en |false|, sí verbalizará la salida del comando. % % \keyexampcmd{use-dfrac}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spPval] % % Establece si la fracción del \emph{valor de la probabilidad} se imprime % usando |\dfrac| en lugar de |\frac|. Si se establece en |true|, % |$\spPval[use-dfrac=true]{1}{5}$| imprimirá la fracción en tamaño % \emph{display} `$\dfrac{1}{5}$' en la salida \texttt{PDF}; si se establece % en |false|, la fracción se imprime con |\frac| siguiendo el tamaño % matemático del contexto en curso. % % \subsection*{El comando \cs[no-index]{spProb}}\label{subsec:spProb} % % \begin{function}{\spProb} % \begin{syntax} % |\spProb|\mymarg{evento} \qquad\qquad |\spProb|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{evento} % \end{syntax} % El comando \ics*{spProb} trabaja en \emph{modo matemático} y recibe el % \emph{argumento obligatorio} \mymarg{evento}: un evento, o dos eventos % unidos por \emph{exactamente uno} de los operadores |\cap|, |\cup|, % |\mid| o el carácter `\texttt{\textbar}', escritos tal como se imprimen. El % comando imprime la probabilidad entre paréntesis precedida de la letra % `$P$'. Cualquier otro argumento, como uno vacío, con llaves o paréntesis sin % cerrar, con un evento vacío o con más de un operador a la vez, % devuelve un \enquote{error} y se detiene la compilación del documento. Lo % que va entre llaves, % paréntesis o corchetes no se examina, de modo que % |$\spProb{A \cap (B \cup C)}$| es válido. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spProb{A}$| imprimirá `$P(A)$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«probabilidad de A»}; % |$\spProb{A \cap B}$| imprimirá `$P(A \cap B)$' y verbalizará % \emph{«probabilidad de A y B»}; |$\spProb{A \cup B}$| imprimirá % `$P(A \cup B)$' y verbalizará \emph{«probabilidad de A o B»}; % |$\spProb{A \mid B}$|, o \myhlc+$\spProb{A | B}$+, imprimirá `$P(A \mid B)$' y % verbalizará \emph{«probabilidad de A dado B»}. % % \subsubsection*{Llaves para \cs[no-index]{spProb}} % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spProb] % % Sobrescribe lo que verbalizará \mynvmath{NVDA/MathCAT} al ejecutar el % comando sin modificar la impresión de este. El valor de esta llave % \emph{siempre} debe pasarse entre llaves `|{ }|'. % % Por ejemplo:~|$\spProb[read-arg={probabilidad del suceso}]{A}$| % imprimirá `$P(A)$' en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} % verbalizará \emph{«probabilidad del suceso»}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spProb] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} de la probabilidad al ejecutar el comando sin % modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se % establece en |false|, sí verbalizará la salida del comando. Un argumento % inválido devuelve siempre un \enquote{error} y se detiene la compilación del % documento, % también con \mykey{silent}|=true|. % % \begin{important}* % Dentro del argumento de |\spProb|, los operadores |\cap|, |\cup| y % |\mid| que quedan entre llaves o paréntesis, por ejemplo en % |$\spProb{A \cap (B \cup C)}$|, también se verbalizan como «y», «o» y % «dado», respectivamente. % \end{important} % % \subsection*{El comando \cs[no-index]{spcomp}}\label{subsec:spcomp} % % \begin{function}{\spcomp} % \begin{syntax} % |\spcomp|\mymarg{evento} \qquad\qquad |\spcomp|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{evento} % \end{syntax} % El comando \ics*{spcomp} trabaja en \emph{modo matemático} y recibe el % \emph{argumento obligatorio} \mymarg{evento}: el evento del que se % quiere el complemento, escrito tal como se imprime. El comando imprime el % complemento con el exponente `$c$' y se puede usar solo o dentro del % argumento de |\spProb|. Si el evento contiene una operación entre % conjuntos, que es |\cap|, |\cup|, |\setminus| o |\mid|, se imprime entre % paréntesis y se verbaliza entre paréntesis para no confundir el % complemento de toda la operación con el de su primer evento. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spcomp{A}$| imprimirá `$A^{c}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«complemento de A»}; % |$\spProb{A \cap \spcomp{B}}$| imprimirá `$P(A \cap B^{c})$' y % verbalizará \emph{«probabilidad de A y complemento de B»}; % |$\spcomp{A \cup B}$| imprimirá `$(A \cup B)^{c}$' y verbalizará % \emph{«complemento de se abren paréntesis A o B se cierran % paréntesis»}. % % \subsubsection*{Llaves para \cs[no-index]{spcomp}} % % \keyexampcmd{bar}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spcomp] % % Establece si el complemento se imprime con una \emph{barra horizontal} % sobre el evento en lugar del exponente `$c$'. Si se establece en |true|, % |$\spcomp[bar=true]{A}$| imprimirá `$\overline{A}$' en la salida % \texttt{PDF}; la verbalización es la misma en ambos casos. % % \keyexampcmd{wrap-parens}{auto \textnormal{\textcolor{lightgray}{\textbar}} % true \textnormal{\textcolor{lightgray}{\textbar}} false}{auto}[spcomp] % % Establece si se \emph{imprimen} paréntesis redondos `|(|\dots|)|' alrededor % del evento cuando se usa el exponente `$c$'. Esta llave admite % |true|, |false| y |auto|. Con |auto| se agregan solo si el evento contiene % |\cap|, |\cup|, |\setminus| o |\mid|; con |true| siempre, y con |false| % nunca. Con \mykey{bar}|=true| la barra cubre el evento y no se agregan % paréntesis. Como % \mynvmath{NVDA/MathCAT} no verbaliza la barra, cuando el evento es compuesto % y se usa \mykey{bar}|=true| el complemento se verbaliza \emph{«complemento de la % expresión»}; por ejemplo, |$\spcomp[bar=true]{A \cup B}$| se verbaliza % \emph{«complemento de la expresión A o B»}, para dejar claro que abarca % todo el evento. % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spcomp] % % Sobrescribe lo que verbalizará \mynvmath{NVDA/MathCAT} al ejecutar el % comando sin modificar la impresión de este. El valor de esta llave % \emph{siempre} debe pasarse entre llaves `|{ }|'.\newline Por ejemplo:~% % |$\spcomp[read-arg={no ocurre A}]{A}$| imprimirá `$A^{c}$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«no ocurre A»}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spcomp] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} del complemento al ejecutar el comando sin % modificar la impresión de este. Si se establece en |true|, % \mynvmath{NVDA/MathCAT} NO verbalizará la salida del comando; si se % establece en |false|, sí verbalizará la salida del comando. % % \subsection*{El comando \cs[no-index]{spcomb}}\label{subsec:spcomb} % % \begin{function}{\spcomb} % \begin{syntax} % |\spcomb|\mymarg{n\textnormal{\textcolor{gray}{,}} k} \qquad\qquad |\spcomb|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{n\textnormal{\textcolor{gray}{,}} k} % \end{syntax} % El comando \ics*{spcomb} trabaja en \emph{modo matemático} y escribe los % símbolos de conteo: combinaciones, variaciones, arreglos y permutaciones, % con o sin repetición, y las permutaciones circulares. Recibe un único % \emph{argumento obligatorio} con elementos separados por comas: \mymarg{n}, % el total de elementos, y, después de la primera coma, \mymarg{k}, los % elementos que se toman. Según el símbolo, lo que sigue a la primera coma % puede ser \emph{la lista de repeticiones}, en las permutaciones con repetición, % o puede no escribirse, en las permutaciones sin repetición y circulares, que % solo llevan \mymarg{n}. % \end{function} % % Los argumentos aceptan números o símbolos; si ambos son números enteros, % el comando comprueba que $k \leq n$, salvo cuando hay repetición, y que % las repeticiones no sumen más de $n$. % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spcomb{5,2}$| imprimirá `$\binom{5}{2}$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«5 sobre 2»}; % |$\spcomb[kind=variation]{5,2}$| imprimirá `$V_{2}^{5}$' y verbalizará % \emph{«variación de 5 elementos tomados de a 2»}; % |$\spcomb[kind=permutation]{n}$| imprimirá `$P_{n}$' y verbalizará % \emph{«permutación de n elementos»}. % % \subsubsection*{Llaves para \cs[no-index]{spcomb}} % % \keyexampcmd{kind}{combination \textnormal{\textcolor{lightgray}{\textbar}} % variation \textnormal{\textcolor{lightgray}{\textbar}} arrangement % \textnormal{\textcolor{lightgray}{\textbar}} permutation}{combination}[spcomb] % % Establece el tipo de símbolo: combinaciones, que son $C$ o $\binom{n}{k}$, % variaciones, que son $V$, arreglos, que son $A$, o permutaciones, que son $P$. Las variaciones y % los arreglos se escriben distinto pero se verbalizan según su nombre, % \emph{«variación»} y \emph{«arreglo»}. Con \mykey{kind}|=permutation| el % \mymarg{k} es opcional: sin él, con |$\spcomb[kind=permutation]{n}$|, se imprime % $P_{n}$ y se verbaliza \emph{«permutación de n elementos»}, y con él, % con |$\spcomb[kind=permutation]{n,k}$|, se imprime $P_{k}^{n}$ y se verbaliza % \emph{«permutación de n elementos tomados de a k»}. % % \keyexampcmd{repeat}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spcomb] % % Establece si los elementos pueden \emph{repetirse}. Si se establece en % |true|, se agrega una \emph{R} a la letra y \emph{«con repetición»} a la % verbalización: |$\spcomb[kind=variation,repeat=true]{5,2}$| imprimirá % `$VR_{2}^{5}$' y verbalizará % \emph{«variación con repetición de 5 elementos tomados de a 2»}; si se % establece en |false|, no se agrega nada. Con \mykey{kind}|=permutation| y % \mykey{repeat}|=true| lo que sigue a la primera coma es la \emph{lista de repeticiones}, % que también va separada por comas y tiene al menos dos elementos.\newline Por ejemplo:~% % |$\spcomb[kind=permutation,repeat=true]{10,3,3,4}$| % imprimirá `$PR_{10}^{3,3,4}$' y verbalizará \emph{«permutación con % repetición de 10 elementos con repeticiones 3 3 y 4»}. % % \keyexampcmd{circular}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spcomb] % % Establece si se trata de una \emph{permutación circular}. Si se establece en % |true|,\newline |$\spcomb[kind=permutation,circular=true]{n}$| imprimirá `$PC_{n}$' % en la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará % \emph{«permutación circular de n elementos»}; si se establece en |false|, la % permutación es la usual. Solo es válida con \mykey{kind}|=permutation| y % \mykey{repeat}|=false|, y entonces el comando solo recibe \mymarg{n}. % % \keyexampcmd{print-sty}{auto \textnormal{\textcolor{lightgray}{\textbar}} % binom \textnormal{\textcolor{lightgray}{\textbar}} letter}{auto}[spcomb] % % Establece cómo se imprimen las combinaciones sin repetición. Si se establece % en |auto| o |binom|, se imprime el coeficiente binomial $\binom{n}{k}$ y % \mynvmath{NVDA/MathCAT} lo verbaliza \emph{«n sobre k»}; si se establece en % |letter|, |$\spcomb[print-sty=letter]{5,2}$| imprimirá `$C_{2}^{5}$' y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«combinación de 5 elementos % tomados de a 2»}. El valor |binom| solo es válido para combinaciones sin % repetición. % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spcomb] % % Sobrescribe lo que verbalizará \mynvmath{NVDA/MathCAT} al ejecutar el % comando sin modificar la impresión de este. El valor de esta llave % \emph{siempre} debe pasarse entre llaves `|{ }|'.\newline Por ejemplo:~% % |$\spcomb[read-arg={cinco sobre dos}]{5,2}$| imprimirá `$\binom{5}{2}$' en % la salida \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«cinco % sobre dos»}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spcomb] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} del símbolo al ejecutar el comando sin modificar la % impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} NO % verbalizará la salida del comando; si se establece en |false|, sí % verbalizará la salida del comando. % % \begin{important}* % Un argumento inválido devuelve siempre un \enquote{error} y se detiene la % compilación del documento, % también con \mykey{silent}|=true|: argumentos vacíos, una lista de % repeticiones con menos de dos elementos o con un elemento vacío, $k > n$ % sin repetición, repeticiones que suman más de $n$, \mymarg{k} % ausente cuando el símbolo lo necesita o presente cuando no lo admite, y % combinaciones de llaves que no existen: \mykey{circular} sin \mykey{kind}|=permutation| o % con \mykey{repeat}, o \mykey{print-sty}|=binom| fuera de las combinaciones sin repetición. % \end{important} % % \subsection*{El comando \cs[no-index]{spfact}}\label{subsec:spfact} % % \begin{function}{\spfact} % \begin{syntax} % |\spfact|\mymarg{n} \qquad\qquad |\spfact|\myoarg{key \textnormal{\textcolor{gray}{=}} val}\mymarg{n} % \end{syntax} % El comando \ics*{spfact} trabaja en \emph{modo matemático} y recibe el % \emph{argumento obligatorio} \mymarg{n}. Imprime % el factorial de \mymarg{n}. Si \mymarg{n} es una expresión, los paréntesis % se escriben en el argumento: |$\spfact{(n-1)}$| imprimirá `$(n-1)!$'. % \end{function} % % Si no se proporciona el \emph{argumento opcional} \myoarg{key \textnormal{\textcolor{gray}{=}} val}, % |$\spfact{5}$| imprimirá `$5!$' en la salida \texttt{PDF} y % \mynvmath{NVDA/MathCAT} verbalizará \emph{«5 factorial»}. % % El comando |\spfact| devuelve un \enquote{error} y se detiene la compilación % del documento cuando el argumento está vacío. % % \subsubsection*{Llaves para \cs[no-index]{spfact}} % % \keyexampcmd{read-arg}{verbalización \textnormal{NVDA}}{no usado}[spfact] % % Sobrescribe lo que verbalizará \mynvmath{NVDA/MathCAT} al ejecutar el % comando sin modificar la impresión de este. El valor de esta llave % \emph{siempre} debe pasarse entre llaves `|{ }|'.\newline Por ejemplo:~% % |$\spfact[read-arg={cinco factorial}]{5}$| imprimirá `$5!$' en la salida % \texttt{PDF} y \mynvmath{NVDA/MathCAT} verbalizará \emph{«cinco factorial»}. % % \keyexampcmd{silent}{true \textnormal{\textcolor{lightgray}{\textbar}} % false}{false}[spfact] % % Establece si se \emph{silencia} la verbalización en % \mynvmath{NVDA/MathCAT} del factorial al ejecutar el comando sin modificar % la impresión de este. Si se establece en |true|, \mynvmath{NVDA/MathCAT} NO % verbalizará la salida del comando; si se establece en |false|, sí % verbalizará la salida del comando. % % \section{Modo \texttt{mathml-AF} y \texttt{spitool}}\label{sec:af} % % Las matemáticas de un documento \emph{tagged} \texttt{PDF} se configuran con % la llave |math/setup| de |\DocumentMetadata|. Todos los ejemplos de este % documento usan \texttt{mathml-SE}, que significa \emph{Structure Elements}: el % \mynvmath{MathML} de cada fórmula se inserta en el árbol de etiquetas del % \texttt{PDF}. Existe además el modo \texttt{mathml-AF}, que significa \emph{Associated Files} % o \emph{«archivos asociados»}, en el que las fórmulas se leen desde un % archivo externo que contiene un \mynvmath{MathML} por cada fórmula, en % lugar de generarse al compilar. % % \subsection{Cuál usar y cuándo} % % Como \texttt{mathml-AF} lee las fórmulas desde un archivo fijo, los cambios % que haga en las matemáticas \emph{no se reflejan} en él mientras no lo % genere de nuevo: si agrega, borra o modifica fórmulas con frecuencia, el % \texttt{PDF} seguirá leyendo el contenido anterior. Por eso se recomienda: % % \begin{itemize}[nosep,itemsep=\parskip] % \item usar \texttt{mathml-SE} mientras el documento está en desarrollo, y % \item usar \texttt{mathml-AF} solo en documentos terminados. % \end{itemize} % % La ventaja de \texttt{mathml-AF} es que el archivo asociado contiene el % \mynvmath{MathML} de cada fórmula y puede editarse: cambiar su contenido % cambia la lectura, e incluso se puede escribir \mynvmath{MathML} directamente. % % \begin{important}* % Editar el archivo asociado es una opción para quien sabe lo que hace: % \mypkg*[no-index]{spintent} no revisa lo que se escriba en él. % \end{important} % % \subsection{De \texttt{mathml-SE} a \texttt{mathml-AF}} % % \begin{enumerate}[nosep,itemsep=\parskip] % \item Escriba y revise el documento con \texttt{mathml-SE}. % \item Cuando esté terminado, ejecute en una terminal |spitool archivo.tex|. % Se genera el archivo \myfile{archivo-mathml.html}. % \item En |\DocumentMetadata|, cambie \texttt{mathml-SE} por \texttt{mathml-AF} y compile % de nuevo. % \item Si después modifica alguna fórmula, repita desde el segundo paso con % la opción |-o|, que reemplaza el archivo existente. % \end{enumerate} % % La línea del tercer paso queda así: % % \iffalse %<*ignore> % \fi \begin{examplecode}[frame=single] \DocumentMetadata { lang=es-CL, pdfversion=2.0, pdfstandard=ua-2, tagging=on, tagging-setup={math/setup={mathml-AF}}, } \end{examplecode} % \iffalse % % \fi % % \subsection{La herramienta \texttt{spitool}} % % El script \myspitool{} se instala junto con el paquete. Prepara los % archivos de \mynvmath{MathML} que genera \mypkg{luamml}\cite{luamml} para el % modo \texttt{mathml-AF}: elimina de ellos unos caracteres invisibles, % \texttt{U+2063}, los \emph{«fantasmas»}, y escribe el resultado en % \myfile{archivo-mathml.html}. También valida el \texttt{PDF}, siempre que las % herramientas de la~\S\ref{subsec:comprobar} estén instaladas. Según el % archivo que reciba, hace lo siguiente: % % \begingroup\small % \begin{longtable}{@{\extracolsep{\fill}}l p{8.5cm}@{}} % \caption{Modos de \texttt{spitool}} \label{tab:spitool-modos} \\ % \toprule % \textbf{Archivo} & \textbf{Qué hace} \\ % \midrule % \endfirsthead % \caption[]{\emph{(Continuación - Modos de \texttt{spitool})}} \\ % \toprule % \textbf{Archivo} & \textbf{Qué hace} \\ % \midrule % \endhead % \midrule % \multicolumn{2}{r}{\emph{Continúa en la siguiente página...}} \\ % \endfoot % \bottomrule % \endlastfoot % \texttt{archivo.tex} & Compila una vez en un directorio temporal con % |AFghost=true|, obtiene el archivo de \mynvmath{MathML} de \mypkg{luamml}, % lo limpia y escribe \myfile{archivo-mathml.html}. El \texttt{PDF} temporal se % descarta y no se valida. \\ % \texttt{archivo-luamml-mathml.html} & Solo limpia un archivo ya generado. \\ % \texttt{archivo.pdf} & Solo valida el \texttt{PDF} con \texttt{rnv-wrapp} y % \texttt{veraPDF} para \texttt{PDF/UA-2}, si están en el \texttt{PATH}. \\ % \end{longtable} % \endgroup % % Las opciones son: % % \begingroup\small % \begin{longtable}{@{\extracolsep{\fill}}l p{8.5cm}@{}} % \caption{Opciones de \texttt{spitool}} \label{tab:spitool-opciones} \\ % \toprule % \textbf{Opción} & \textbf{Efecto} \\ % \midrule % \endfirsthead % \caption[]{\emph{(Continuación - Opciones de \texttt{spitool})}} \\ % \toprule % \textbf{Opción} & \textbf{Efecto} \\ % \midrule % \endhead % \midrule % \multicolumn{2}{r}{\emph{Continúa en la siguiente página...}} \\ % \endfoot % \bottomrule % \endlastfoot % |-c|, |--check-only| & Solo valida; el archivo debe ser un \texttt{PDF}. \\ % |-o|, |--overwrite| & Reemplaza los archivos |-mathml.html| existentes. \\ % |-p|, |--path=|\myparg{carpeta} & Carpeta donde se escriben los archivos, que se crea si no existe. \\ % |-e|, |--engine=|\myparg{motor} & Motor con que se compila; por defecto \texttt{lualatex-dev} si existe y, si no, |lualatex|. \\ % |-s|, |--strict| & Se detiene en la primera falla; sin ella procesa todos los archivos. \\ % |-h|, |--help| & Muestra la ayuda. \\ % |-v|, |--version| & Muestra la versión. \\ % \end{longtable} % \endgroup % % Si algún paso falla, el código de salida de \myspitool[no-index]{} es distinto de % cero. Pueden indicarse varios archivos a la vez y la extensión puede % omitirse. Algunos ejemplos: % % \iffalse %<*ignore> % \fi \begin{examplecode}[frame=single,language={}] spitool -c ejemplo.pdf spitool -c -s ejemplo spitool ejemplo-luamml-mathml.html spitool -o -p build/ ejemplo.tex \end{examplecode} % \iffalse % % \fi % % \section{Ejemplos de \texttt{PDF} accesible y etiquetado}\label{sec:ejemplos} % % Esta sección muestra el uso de \mypkg*[no-index]{spintent} en documentos % \emph{tagged} \texttt{PDF} con \hologo{LuaLaTeX}. Los archivos están en el % directorio |sources/test-pkg/|: los cinco primeros prueban \emph{todos} los % comandos del paquete, agrupados por eje, y los tres últimos son ejemplos % reales, del tipo de documento que haría un docente de aula. Cada uno se % compila con \texttt{lualatex-dev} en los dos modos, \texttt{mathml-SE} y \texttt{mathml-AF} % (\S\ref{sec:af}), y los \texttt{PDF} resultantes se validan con % \texttt{rnv-wrapp} y \texttt{veraPDF} (\S\ref{subsec:comprobar}). % % \begin{important}* % This section is just to show the compatibility of \mypkg*[no-index]{spintent} % with tagged \texttt{PDF} using \hologo{LuaLaTeX}. The attached files are % intended as examples and, in a way, to save time for Matthew Bertucci % (@mbertucci), who runs the tests on the \hologo{LaTeX} side, when he sees % this package and adds it to The \hologo{LaTeX} Tagged \texttt{PDF} repository. % \end{important} % % Para compilar los ejemplos con \texttt{lualatex-dev} se requieren los % paquetes \mypkg{babel} con la opción \texttt{spanish}, \mypkg{spintent} % y \mypkg{hyperref}, junto con la línea de |\DocumentMetadata| (\S\ref{subsec:carga}). % Las versiones \texttt{mathml-AF} se obtienen cambiando \texttt{mathml-SE} % por \texttt{mathml-AF} después de ejecutar \myspitool[no-index]{}. % % Los archivos \texttt{.tex} van adjuntos a este \texttt{PDF}, junto con % los \texttt{PDF} ya compilados en cada modo: al final de cada descripción % están el archivo fuente y los \texttt{PDF} en \texttt{mathml-SE} y en % \texttt{mathml-AF}. % % \begin{itemize}[left=0pt,itemsep=\parskip] % \item El archivo \myfile{spintent-01.tex} contiene las pruebas de |\spnum|, % |\spunit|, |\spmoney| y |\spshort|, junto con |\spnewunit| y |\spunitalias|. % \myattachfile{spintent-01} % \item El archivo \myfile{spintent-02.tex} contiene las pruebas de las utilidades % generales, que son |\spsiglo|, |\sptime| y |\spdate|, y de las utilidades para % símbolos, que son |\spnewread|, |\spread|, |\spusep|, |\spdsym| y |\spmsym|. % \myattachfile{spintent-02} % \item El archivo \myfile{spintent-03.tex} contiene las pruebas de las utilidades % para números: |\spdiv|, |\spmul|, |\spnD|, |\spnM|, |\spmcd|, |\spmcm|, % |\spnZ|, |\spmQ|, |\sprazon| y |\spProp|. % \myattachfile{spintent-03} % \item El archivo \myfile{spintent-04.tex} contiene las pruebas de las utilidades % para geometría: |\spcoord|, |\spPoint|, |\spvec|, |\sprecta|, |\sprayo|, % |\spside|, |\spmside|, |\spangle|, |\spmangle|, |\spang|, |\sparc|, % |\spmarc|, |\spPoly|, |\spAfig|, |\spPfig|, |\sprad|, |\spdiam|, % |\spcirc|, |\spAcirc|, |\spPcirc| y |\spVol|. % \myattachfile{spintent-04} % \item El archivo \myfile{spintent-05.tex} contiene las pruebas de las utilidades % para álgebra: |\spinterval|, |\spfun|, |\spdom|, |\sprec|, |\spcod|, % |\spfuncomp| y |\spabs|; y para estadística: |\spProm|, |\spmedia|, % |\spmediana|, |\spmoda|, |\spdesv|, |\spvarz|, |\sprango|, |\spriq|, % |\spmin|, |\spmax|, |\spcuartil|, |\spdecil|, |\spPerc|, |\spFrec|, % |\spcase|, |\spOmega|, |\spPval|, |\spProb|, |\spcomp|, |\spcomb| y |\spfact|. % \myattachfile{spintent-05} % \item El archivo \myfile{spintent-06.tex} es un ejemplo real: una guía de % ejercicios de divisibilidad, fracciones, razones y enteros. % \myattachfile{spintent-06} % \item El archivo \myfile{spintent-07.tex} es un ejemplo real: una guía de % ejercicios de geometría con ángulos, áreas y perímetros. % \myattachfile{spintent-07} % \item El archivo \myfile{spintent-08.tex} es un ejemplo real: una guía de % ejercicios de estadística y probabilidad. % \myattachfile{spintent-08} % \end{itemize} % % \newpage % % \section*{A Work in Progress} % % Todo comenzó cuando salió la versión de desarrollo de \hologo{LaTeX} % release 2025-11-01 \cite{ltnews}, la cual implementó |\MathMLintent| y |\MathMLarg|. % Mi idea inicial era modesta: un paquete llamado \texttt{spunit}, pensado para las % unidades de medida. Pero una unidad casi nunca viene sola, y de ahí nació % |\spnum|; luego vinieron |\spmoney| y |\spshort| en reemplazo de |\sptext|, % hasta formar \emph{«los cuatro grandes»}, que cubren lo esencial de un % documento en español con \mypkg{babel}. % % Durante un tiempo fui desarrollando pequeñas utilidades, una a una, pero % pronto quedaron cortas para lo que necesita un docente de enseñanza básica y % media. Lo que partió como un conjunto de utilidades sueltas se transformó en % un trabajo por ejes: números, geometría, álgebra y estadística. El nombre % también cambió. Después de leer mucho sobre el tema, vi una y otra vez la % palabra \emph{intent}, y lo que yo \emph{intentaba} hacer sonaba mejor en mi % cabeza como \mypkg*[no-index]{spintent} que como \texttt{spunit}. % % En el camino aprendí bastante más que Lua y \hologo{LaTeX}: a validar un % \texttt{PDF} etiquetado con \texttt{rnv} y \texttt{veraPDF}\cite{verapdf}, a % construir baterías de tests de regresión, a comprender los modos \texttt{mathml-SE} y % \texttt{mathml-AF}, a escuchar con \mynvmath{NVDA/MathCAT}\cite{nvda,mathcat} % una y otra vez, a revisar los \emph{intents} del núcleo antes de crear uno % propio, a definir una interfaz de usuario amigable y, sobre todo, a % documentar, y documentar otra vez. % % Esta sección es para mí, el de hoy y el de mañana. Hoy el proyecto está % terminado, pero esto no termina aquí: debo seguirle el paso al proyecto de % etiquetado de \hologo{LaTeX}\cite{tagged-project} y a % \mypkg{luamml}\cite{luamml}, y pensar en las utilidades que hoy quedaron % fuera. Por eso esta sección se llama \emph{A Work in Progress}: porque un % trabajo en progreso es el corazón de \mypkg*[no-index]{spintent}. % % \let\stdsection\section % \def\section*#1{\stdsection{#1}} % \expandafter\let\expandafter\thebibliography\csname HDorg@thebibliography\endcsname % % \begin{thebibliography}{10} % % \bibitem{unicode-math} \textsc{Robertson, Will}. \enquote{unicode-math % - Unicode mathematics support for \hologo{XeTeX} and \hologo{LuaTeX}}. Available from % \textsc{ctan}, \url{https://www.ctan.org/pkg/unicode-math}, 2023. % % \bibitem{luamml} \textsc{Krueger, Marcel}. \enquote{LuaMML% % - Automatically generate MathML from \hologo{LuaTeX} math mode material}. % Available from \textsc{ctan}, \url{https://ctan.org/pkg/luamml}, 2026. % % \bibitem{fontspec} The \hologo{LaTeX} Project. \enquote{fontspec% % - Advanced font selection for \hologo{XeLaTeX} and \hologo{LuaTeX}}. % Available from \textsc{ctan}, \url{https://www.ctan.org/pkg/fontspec},% % 2025. % % \bibitem{libertinus-otf} \textsc{Voß, Herbert}. \enquote{libertinus-otf% % - Support for Libertinus OpenType}. Available from \textsc{ctan},% % \url{https://www.ctan.org/pkg/libertinus-otf}, 2025. % % \bibitem{erewhon-math} \textsc{Flipo, Daniel}. \enquote{erewhon-math% % - Utopia based OpenType Math font}. Available from% % \textsc{ctan}, \url{https://ctan.org/pkg/erewhon-math}, 2026. % % \bibitem{sourcecodepro} \textsc{Hofstra, Silke}. \enquote{sourcecodepro% % - Use SourceCodePro with \hologo{TeX}(-alike) systems}. Available from% % \textsc{ctan}, \url{https://ctan.org/pkg/sourcecodepro}, 2025. % % \bibitem{newcomputermodern} \textsc{Tsolomitis, Antonis}. \enquote{newcomputermodern% % - The New Computer Modern family of fonts}. Available from \textsc{ctan}, % \url{https://ctan.org/pkg/newcomputermodern}, 2026. % % \bibitem{scontents} \textsc{González, Pablo}. \enquote{\textsf{scontents} - Stores % \hologo{LaTeX} contents in memory or files}. Available from % \textsc{ctan}, \url{https://www.ctan.org/pkg/scontents}, 2026. % % \bibitem{enumext} \textsc{González, Pablo}. \enquote{\textsf{enumext} - % Enumerate exercise sheets}. Available from \textsc{ctan}, % \url{https://www.ctan.org/pkg/enumext}, 2026. % % \bibitem{babel} \textsc{Braams, Johannes and Bezos, Javier}. % \enquote{\textsf{babel} - Multilingual support for \hologo{LaTeX}, % \hologo{LuaLaTeX}, \hologo{XeLaTeX}, and Plain \TeX}. Available from % \textsc{ctan}, \url{https://www.ctan.org/pkg/babel}, 2026. % % \bibitem{babel-spanish} \textsc{Bezos, Javier}. % \enquote{\textsf{babel-spanish} - Babel support for Spanish}. % Available from \textsc{ctan}, \url{https://www.ctan.org/pkg/babel-spanish}, % 2026. % % \bibitem{nvda} \textsc{NV Access}. \enquote{\textsf{NVDA} - NonVisual % Desktop Access screen reader}. Available from \textsc{nv access}, % \url{https://www.nvaccess.org/}, 2026. % % \bibitem{mathcat} \textsc{Soiffer, Neil}. \enquote{\textsf{MathCAT} - % Math to speech and braille translation}. Available from \textsc{github}, % \url{https://github.com/daisy/MathCAT}, 2026. % % \bibitem{mathcatdemo} \textsc{Soiffer, Neil}. \enquote{\textsf{MathCAT Demo} - % Online demonstration of \textsf{MathCAT}}. Available from \textsc{github}, % \url{https://daisy.github.io/MathCATDemo/}, 2026. % % \bibitem{acrobat} \textsc{Adobe Inc.} \enquote{\textsf{Adobe Acrobat % Reader} - Reliable \texttt{PDF} viewer}. Available from \textsc{adobe}, % \url{https://get.adobe.com/reader/}, 2026. % % \bibitem{pdf20} \textsc{ISO}. \enquote{\textsf{ISO 32000-2:2020} - Portable % Document Format (\texttt{PDF} 2.0)}. Available from \texttt{PDF} \textsc{association}, % \url{https://pdfa.org/resource/iso-32000-2/}, 2020. % % \bibitem{wtpdf} \texttt{PDF} \textsc{association}. \enquote{\textsf{WTPDF} - % Well-Tagged \texttt{PDF} specification}. Available from \textsc{pdfa}, % \url{https://pdfa.org/wtpdf/}, 2024. % % \bibitem{pdf20-errata} \texttt{PDF} \textsc{association}. \enquote{\textsf{\texttt{PDF} 2.0 % Errata Collection 3}}. Available from \textsc{pdfa}, \newline % \url{https://pdfa.org/PDF-2-0-errata-collection-3-now-available/}, 2026. % % \bibitem{bpg-math} \textsc{LaTeX Project LWG}. \enquote{\textsf{Best % Practice Guide: Math in PDF}}. Available from \textsc{pdfa}, % \url{https://pdfa.org/resource/best-practice-guide-math-in-PDF/}, % 2026. % % \bibitem{pdf20-impact} \textsc{Johnson, Duff}. \enquote{\textsf{\texttt{PDF} 2.0: % New Features, Real-World Impact}}. Available from \textsc{pdfa}, % \url{https://pdfa.org/PDF-2-0-new-features-real-world-impact/}, 2026. % % \bibitem{tagged-project} The \hologo{LaTeX} Project. \enquote{\textsf{LaTeX % Tagged \texttt{PDF} Project} - Documentation and resources}. Available from % \textsc{latex project}, \url{https://latex3.github.io/tagging-project/documentation/}, % 2026. % % \bibitem{verapdf} \textsc{veraPDF Consortium}. \enquote{\textsf{veraPDF} - % Industry supported \texttt{PDF}/A and \texttt{PDF/UA} validator}. Available from % \textsc{verapdf}, \url{https://verapdf.org/}, 2026. % % \bibitem{ngpdf} \textsc{Dual Lab}. \enquote{\textsf{ngPDF} - % Next Generation \texttt{PDF} demonstrator and validator}. Available from % \textsc{ngpdf}, \url{https://ngpdf.com/}, 2026. % % \bibitem{latexref} \textsc{Berry, Karl}. \enquote{\hologo{LaTeX2e}: An % Unofficial Reference Manual}. Available from \textsc{ctan}, % \url{https://ctan.org/pkg/latex2e-help-texinfo}, 2026. % % \bibitem{hyperref} The \hologo{LaTeX} Project. \enquote{Extensive support % for hypertext in \hologo{LaTeX}}. Available from % \textsc{ctan}, \url{https://www.ctan.org/pkg/hyperref}, 2026. % % \bibitem{expl3} The \hologo{LaTeX} Project. \enquote{The \textsf{expl3} % package}. Available from % \textsc{ctan}, \url{https://www.ctan.org/pkg/l3kernel}, 2026. % % \bibitem{inter3} The \hologo{LaTeX} Project. \enquote{The \hologo{LaTeX}3 % Interfaces}. Available from % \textsc{ctan}, \newline \url{https://www.ctan.org/pkg/l3kernel}, 2026. % % \bibitem{latex2e} The \hologo{LaTeX} Project. \enquote{The \hologo{LaTeX2e} % sources}. Available from % \textsc{ctan}, \url{https://ctan.org/tex-archive/macros/latex/base}, 2026. % % \bibitem{ltnews} The \hologo{LaTeX} Project. \enquote{\hologo{LaTeX} News, % Issue 42, November 2025}. Available from % \textsc{ctan}, \url{https://ctan.org/tex-archive/macros/latex/base}, 2025. % % \bibitem{userguide} The \hologo{LaTeX} Project. \enquote{\hologo{LaTeX} for authors % current version}. Available from % \textsc{ctan}, \url{https://ctan.org/pkg/latex-base}, 2026. % % \bibitem{learnlatex} \textsc{learnlatex.org}. \enquote{\textsf{Learn \hologo{LaTeX}} - % Your first steps in \hologo{LaTeX}, in short lessons}. Available from % \textsc{learnlatex.org}, \url{https://www.learnlatex.org/es/}, 2026. % % \bibitem{tagpdf} \textsc{Fischer, Ulrike}. \enquote{\textsf{tagpdf} – \hologo{LaTeX} % kernel code for \texttt{PDF} tagging}. Available from % \textsc{ctan}, \url{https://www.ctan.org/pkg/tagpdf}, 2026. % % \bibitem{latexlab} The \hologo{LaTeX} Project. \enquote{\textsf{latex-lab} – \hologo{LaTeX} % laboratory}. Available from % \textsc{ctan}, \url{https://www.ctan.org/pkg/latex-lab}, 2026. % % \end{thebibliography} % % \let\section\stdsection % % \section{Change history} % \label{sec:changes} % % \setlist[itemize,1]{label=\textendash,wide=0.5em,nosep,noitemsep,leftmargin=10pt} % \newlength\descrwidth % \settowidth{\descrwidth}{\textsf{v1.0, (ctan), 2026-06-20} } % \begin{description}[font=\small\sffamily,wide=0pt,style=multiline,leftmargin=1.1\descrwidth,nosep,noitemsep] % \item [\fileversion{} (ctan), \filedate] % \begin{itemize} % \item First public release. % \end{itemize} % \end{description} % % \newpage % % \newgeometry{top=0.5in,bottom=0.3in,left=1.0in,right=0.5in,footskip=0.2in,headheight=1cm,headsep=0.27cm} % % \indexprologue{ % Los números en cursiva indican las páginas donde se describe la entrada % correspondiente.} % % \printindex[userdoc] % % \newpage % % \selectlanguage{english} % % \StartImplementation % % \StopEventually{^^A % \newgeometry{top=0.5in,bottom=0.3in,left=1.0in,right=0.5in,footskip=0.2in,headheight=1cm,headsep=0.27cm} % \addtocontents{toc}{\protect\setcounter{tocdepth}{2}} % \cleardoublepage % \phantomsection % \indexprologue{ % The italic numbers denote the pages where the corresponding entry is % described, the numbers underlined and all others indicate the line on % which they are implemented in the package code. % } % \printindex % } % % \newgeometry{top=0.5in,bottom=0.3in,left=1.95in,right=0.5in,footskip=0.2in,headheight=1cm,headsep=0.27cm} % % \section{Implementation} % \label{sec:implementation} % \addtocontents{toc}{\protect\setcounter{tocdepth}{0}} % % The most recent publicly released version of \mypkg*[no-index]{spintent} is % available at \textsc{ctan}: \url{https://www.ctan.org/pkg/spanish-mathml-intent}. % While general feedback via email is welcomed, specific bugs or % feature requests should be reported through the issue % tracker: \textcolor{gray}{\scriptsize\faIcon[regular]{github}} \url{https://github.com/pablgonz/spanish-mathml-intent/issues}. % % \begin{important}* % The documentation presented here is far from professional, it contains % a lot of obvious information that to the eye of a \hologo{TeX}pert are % superfluous, but, after so many years developing this project is the % only way to remember what does what. % \end{important} % % \subsection{General conventions} % % Functions of the form \myhlc|\@@_luafun_some:n| or \myhlc|\@@_luafun_some:nn| % are created in the Lua module \myluamod{} and only their variants % are declared in \mypkg[no-index]{expl3}. % % In the same way, variables of the form \myhlc|\l_@@_luaset_some_tl|, % \myhlc|\l_@@_luaset_some_str| or \myhlc|\l_@@_luaset_some_clist|, also % with the prefix of a command, such as \myhlc|\l_@@_spcomb_luaset_n_str|, are only % declared in \mypkg[no-index]{expl3}; their values are set from Lua. % % \begin{important}* % All variables and functions defined in this package are private and % are NOT intended to work or be used by another package or module. % \end{important} % % \subsection{Initial set up} % % Start the \pkg{DocStrip} guards. % \begin{macrocode} %<*package> % \end{macrocode} % Identify the internal prefix, following the \LaTeX3 \pkg{DocStrip} convention, for % \myclass{l3doc} class. % \begin{macrocode} %<@@=spintent> % \end{macrocode} % % \subsection{Declaration of the package} % % First we will make sure we have a minimum release version % of \hologo{LaTeX} to work correctly, otherwise, we will return an \enquote{error}. % \begin{macrocode} \NeedsTeXFormat{LaTeX2e}[2026-11-01] \IfFormatAtLeastF { 2026-11-01 } { \PackageError { spanish-mathml-intent } { LaTeX~kernel~too~old~need~2026-11-01~or~later } { spanish-mathml-intent~requires~a~LaTeX2e~release~2026-11-01~or~ later.~Update~your~TeX~distribution. } } % \end{macrocode} % Now declare the \mypkg*[no-index]{spintent} package and add only two % keys: \mykey[usepackage]{mathcal} and \mykey[usepackage]{AFghost}, which can be passed as package % options. % \begin{macrocode} \ProvidesExplPackage {spanish-mathml-intent} {2026-10-20} {1.0} {Spanish parse intents} \keys_define:nn { spintent } { mathcal .usage:n = load, mathcal .bool_set:N = \l_@@_mathcal_bool, mathcal .initial:n = true, mathcal .value_required:n = true, AFghost .usage:n = load, AFghost .bool_set:N = \l_@@_debug_ghost_bool, AFghost .initial:n = false, AFghost .value_required:n = true, } \ProcessKeyOptions [ spintent ] % \end{macrocode} % We check if the requirements for running the \mypkg*[no-index]{spintent} package % are met; that is: |\DocumentMetadata| must be active, \hologo{LuaTeX} % must be running, and the \mypkg{unicode-math} or \mypkg{lua-unicode-math} % package must be loaded. If neither is present, we will load \mypkg{unicode-math}. % If the conditions cannot be met, we will return a \enquote{fatal error}. % \begin{macrocode} \IfDocumentMetadataTF { \sys_if_engine_luatex:TF { \msg_new:nnn { spintent } { package-not-load } { The~'#1'~package~will~be~loaded~as~a~dependency. } \IfPackageLoadedF { unicode-math } { \IfPackageLoadedF { lua-unicode-math } { \msg_info:nnn { spintent } { package-not-load } { unicode-math } \RequirePackage{unicode-math} } } \msg_new:nnn { spintent } { env-success } { Everything~looks~correct,~spanish-mathml-intent~will~make~the~intent! } \msg_info:nn { spintent } { env-success } } { \msg_new:nnnn { spintent } { fatal-engine-requires-luatex } { The~spanish-mathml-intent~package~requires~LuaLaTeX. } { This~package~relies~on~Lua~module.~Compile~with~lualatex. } \msg_fatal:nn { spintent } { fatal-engine-requires-luatex } } } { \msg_new:nnnn { spintent } { fatal-metadata-missing } { '\token_to_str:N \DocumentMetadata'~is~missing. } { The~spanish-mathml-intent~package~requires~tagged~PDF~active.\\ You~must~declare~'\token_to_str:N \DocumentMetadata { tagging=on }'. } \msg_fatal:nn { spintent } { fatal-metadata-missing } } % \end{macrocode} % % \subsection{Some kernel variants} % % \begin{macro}[int]{\str_lowercase:V, \seq_set_from_clist:Ne, \int_set:NV} % Non-standard kernel variants. % \begin{macrocode} \cs_generate_variant:Nn \str_lowercase:n { V } \cs_generate_variant:Nn \seq_set_from_clist:Nn { Ne } \cs_generate_variant:Nn \int_set:Nn { NV } % \end{macrocode} % \end{macro} % % \subsection{Variables for the key \texttt{mathcal}, \texttt{\textbackslash rightanglesqr} and \texttt{\textbackslash attribute}} % % \begin{macro}[int] % { % \l_@@_luaset_cal_fam_ok_str, % \l_@@_luaset_sym_fam_ok_str, % \l_@@_luaset_scope_attr_str, % } % First, we declare the string variable \myhlc|\l_@@_luaset_cal_fam_ok_str| % used by the function \myhlc|\@@_luafun_classic_mathcal:nn|, controlled % by the key \mykey{mathcal}, the string variable \myhlc|\l_@@_luaset_sym_fam_ok_str| % used by the function \myhlc|\@@_luafun_right_angle_sqr:| to copy |\rightanglesqr| % alongside the string variable \myhlc|\l_@@_luaset_scope_attr_str| % used by |\attribute| before loading the Lua module \myluamod{}. % This is necessary due to the implementation of the functions within % the Lua module; otherwise, \mypkg[no-index]{expl3} would issue a warning % regarding duplicate variable declarations. % \begin{macrocode} \str_new:N \l_@@_luaset_cal_fam_ok_str \str_new:N \l_@@_luaset_sym_fam_ok_str \str_new:N \l_@@_luaset_scope_attr_str % \end{macrocode} % \end{macro} % % \subsection{Loading the Lua module and functions} % % \begin{macro}[int]{spintent.lua} % Loads the Lua module \myluamod{}, where every |_luafun_| function % used throughout the package is registered. % \begin{macrocode} \lua_load_module:n { spintent.lua } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_luafun_set_debug_ghost:n} % Passes \mykey{AFghost} to Lua as a plain string. If it is |true|, the two % ghost characters are marked with |sptmp="outer"| or |sptmp="inner"| in the % file |-luamml-mathml.html|, so that the Lua script \myspitool{} can % remove them before generating the |Associated~Files| structure of the % \emph{tagged} \texttt{PDF} for \texttt{mathml-AF} mode. % \begin{macrocode} \bool_if:NTF \l_@@_debug_ghost_bool { \@@_luafun_set_debug_ghost:n { true } } { \@@_luafun_set_debug_ghost:n { false } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\l_@@_scope_attr_int} % A \hologo{LuaTeX} attribute number allocated from Lua with % |luatexbase.new_attribute| and exposed as an integer. Every command that % uses the function \myhlc|\@@_luafun_mrow_intent_arg:nn| sets |\attribute| % \myhlc|\l_@@_scope_attr_int| |= 1| inside its own group, which marks the % nodes typeset within it as eligible for the queue. % \begin{macrocode} \int_new:N \l_@@_scope_attr_int \int_set:NV \l_@@_scope_attr_int \l_@@_luaset_scope_attr_str % \end{macrocode} % \end{macro} % % \begin{macro}[int] % { % \@@_luafun_spunit_parse_product:nV, % \@@_luafun_spmoney_lookup_data:V, % \@@_luafun_spmQ_scale_int:Vn, % \@@_luafun_spfun_parse_and_set:v, % } % Variants generated for \myhlc|\@@_luafun_spunit_parse_product:nn|, % used by |\spunit|; \myhlc|\@@_luafun_spmoney_lookup_data:n|, used % by |\spmoney|; \myhlc|\@@_luafun_spmQ_scale_int:nn|, used by % |\spmQ|; and \myhlc|\@@_luafun_spfun_parse_and_set:n|, used by % |\spfun|. % \begin{macrocode} \cs_generate_variant:Nn \@@_luafun_spunit_parse_product:nn { nV } \cs_generate_variant:Nn \@@_luafun_spmoney_lookup_data:n { V } \cs_generate_variant:Nn \@@_luafun_spmQ_scale_int:nn { Vn } \cs_generate_variant:Nn \@@_luafun_spfun_parse_and_set:n { v } % \end{macrocode} % \end{macro} % % \subsection{Internal implementation for \texttt{\textbackslash mathcal} and \texttt{\textbackslash rightanglesqr}} % % To simplify user configuration and obtain the expected output style % \hologo{pdfTeX} for |\mathcal|, we will provide a fallback to the font % \texttt{NewCMMath-Regular.otf}, which contains the glyphs for these % characters. Using this same font, we will obtain the symbol % |\rightanglesqr| for the key \mykey{recto}. % % \begin{macro}[int]{mathcal-fallback, right-angle-sqr-fallback} % First, we define two warnings in case our fallback fails: the % \enquote{warning} |mathcal-fallback| is raised when no suitable font or % free math family is available, and the \enquote{warning} % |right-angle-sqr-fallback| is raised when the right-angle glyph is % replaced by |\rightangle|. % \begin{macrocode} \hook_gput_code:nnn { begindocument } { spintent } { \@@_luafun_init_math_fallbacks: } \msg_new:nnn { spintent } { mathcal-fallback } { Using~classic~'\token_to_str:N \mathcal'~as~fallback~ (font~not~found~or~no~free~math~family~available)~\msg_line_context:. } \msg_new:nnn { spintent } { right-angle-sqr-fallback } { Using~'\token_to_str:N \rightangle'~as~fallback~ for~the~right-angle~glyph~\msg_line_context:. } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_mathcal:n} % Now we define the internal function \myhlc|\@@_mathcal:n| for % |\mathcal| which checks the state of the variable \myhlc|\l_@@_mathcal_bool| % set by the package key \mykey{mathcal}; if its state is true, we check % the string variable \myhlc|\l_@@_luaset_cal_fam_ok_str| and if the latter has the value % true, we call the function \myhlc|\@@_luafun_classic_mathcal:n| and % otherwise we return a warning % message and use |\mathcal|. Otherwise, that is, % \mykey{mathcal}|=false| we use |\mathcal|. % \begin{macrocode} \cs_new_protected:Nn \@@_mathcal:n { \bool_if:NTF \l_@@_mathcal_bool { \str_if_eq:VnTF \l_@@_luaset_cal_fam_ok_str { true } { \@@_luafun_classic_mathcal:n {#1} } { \msg_warning:nn { spintent } { mathcal-fallback } \mathcal {#1} } } { \mathcal {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_right_angle_sqr:} % The internal function \myhlc|\@@_right_angle_sqr:| establishes the % internal copy of the command |\rightanglesqr|. First, we check % the string variable \myhlc|\l_@@_luaset_sym_fam_ok_str|. If it % is true, we call the function \myhlc|\@@_luafun_right_angle_sqr:|; % otherwise we return a warning % message and use |\rightangle| instead. % \begin{macrocode} \cs_new_protected:Nn \@@_right_angle_sqr: { \str_if_eq:VnTF \l_@@_luaset_sym_fam_ok_str { true } { \@@_luafun_right_angle_sqr: } { \msg_warning:nn { spintent } { right-angle-sqr-fallback } \rightangle } } % \end{macrocode} % \end{macro} % % \subsection{Shared annotation mechanisms: the queue and the ghosts} % % Almost all commands in the \mypkg*[no-index]{spintent} package share % three annotation functions, the mechanism of which is defined in the % Lua module \myluamod{}. % % \begin{macro}[int]{\@@_luafun_mrow_intent_arg:nn} % The internal function \myhlc|\@@_luafun_mrow_intent_arg:nn| is called % directly, not through an \mypkg[no-index]{expl3} function. It pushes onto a queue % one entry formed by a concept |intent| in `|#1|', which may be empty, and a Lua % table literal |arg=| or |intent=| in `|#2|'. A |pre_mlist_to_hlist_filter| % callback, installed before the one of \mypkg{luamml}\cite{luamml}, walks the % top-level noads of the formula in order. Each noad whose nucleus or subscript % was typeset inside the group of a \mypkg*[no-index]{spintent} command, marked % with the \hologo{LuaTeX} |\attribute| activated by every command that uses % this queue, takes the next pending entry: |arg| or the literal |intent| is % assigned to that leaf and, if the concept |intent| is not empty, the leaf is % wrapped in a native || carrying it. The entries are consumed in order % and those left when the formula ends are discarded. The queue does not see the % claims of a native |\MathMLarg| or |\MathMLintent|. % \end{macro} % % The other two are the \enquote{ghost} characters, an invisible |U+2063| tagged % |:silent| through their own |mathml_filter|, which the queue never claims. A % ghost is added as an extra sibling of the || so that it is not the only % top-level content of the formula; otherwise \mypkg{luamml} would promote it to % ||, as in issue % \href{https://github.com/latex3/luamml/issues/26}{\#26}. % % \begin{macro}[int]{\@@_invisible_sep:} % Places the ghost before the outer |\MathMLintent| of the formula. Only the % first call in the document has effect; the following ones, in any command, % do nothing. % \begin{macrocode} \cs_new_protected:Nn \@@_invisible_sep: { \@@_luafun_invisible_sep: } % \end{macrocode} % \begin{important}* % Executing this function writes the following line to the % output file |-luamml-mathml.html|: % \iffalse %<*example> % \fi \begin{examplecode} ⁣ \end{examplecode} % \iffalse % % \fi % and if the package key \mykey{AFghost}|=true| is used, the output is: % \iffalse %<*example> % \fi \begin{examplecode} ⁣ \end{examplecode} % \iffalse % % \fi % If you wish to use \texttt{mathml-AF} mode, you can remove the line generated by % \mykey{AFghost}|=true| using the Lua script \myspitool{}. % \end{important} % \end{macro} % % \begin{macro}[int]{\@@_inner_sep:} % Places the ghost just inside a native |\MathMLintent| group. Every call has % effect, one for each such group of the formula, nested or sibling. % \begin{macrocode} \cs_new_protected:Nn \@@_inner_sep: { \@@_luafun_inner_sep: } % \end{macrocode} % \begin{important}* % Executing this function writes the following line to the % output file |-luamml-mathml.html|: % \iffalse %<*example> % \fi \begin{examplecode} ⁣ \end{examplecode} % \iffalse % % \fi % and if the package key \mykey{AFghost}|=true| is used, the output is: % \iffalse %<*example> % \fi \begin{examplecode} ⁣ \end{examplecode} % \iffalse % % \fi % If you wish to use \texttt{mathml-AF} mode, you can remove the line generated by % \mykey{AFghost}|=true| using the Lua script \myspitool{}. % \end{important} % \end{macro} % % \begin{macro}[int]{\c_@@_invisible_sep_int} % A raw, untagged copy of the character |U+2063| defined with the % \hologo{LuaTeX} primitive |\Umathchardef|. It is used as content inside an % explicit |\MathMLintent|\mymarg{intent}, typically |:silent|. % \begin{macrocode} \hook_gput_code:nnn { begindocument } { spintent } { \Umathchardef \c_@@_invisible_sep_int = "0 "0 "2063 } % \end{macrocode} % \begin{important}* % Documented in \mypkg[no-index]{source3} on page 887 for \myhlc|\int_const:Nn|: % ``As stated, most constants can be defined as |\chardef| or |\mathchardef| % but that’s engine dependent.'' We follow this recommendation. % \end{important} % \end{macro} % % \subsection{Normalize argument and affixes} % % We normalize the \mymarg{argument} to support prefixes and suffixes % by trimming leading and trailing spaces, converting internal spaces to % hyphens, collapsing consecutive hyphens, and removing leading or % trailing hyphens. % % \begin{macro}[int]{\@@_collapse_hyphens:N} % A recursive function to collapse multiple hyphens into a single one. % \begin{macrocode} \cs_new_protected:Nn \@@_collapse_hyphens:N { \tl_if_in:NnT #1 { -- } { \tl_replace_all:Nnn #1 { -- } { - } \@@_collapse_hyphens:N #1 } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_sanitize_affix:Nn, \@@_sanitize_affix:cn} % The function \myhlc|\@@_sanitize_affix:Nn| checks if the \mymarg{argument} % passed in `|#2|' is \emph{blank}. If so, it clears the target token list % variable passed in `|#1|'. Otherwise, it converts the \mymarg{argument} % into a string with every character in category code~12, so it no longer % matters how an underscore `|_|' was typed, trims the bounds, converts % spaces `\verb*+ +' and underscores `|_|' to hyphens `|-|', collapses % multiple hyphens, and strips leading/trailing hyphens. Because % underscores are converted \emph{before} collapsing, any run mixing % hyphens and underscores is reduced to a single hyphen, and the stored % value never starts or ends with `|-|' or `|_|': the command that uses the % value is the one that places the leading `|_|' of the concept name. For % example, if you pass a target variable and raw text: % % \begin{center} % \myhlc|\@@_sanitize_affix:Nn| \myhlc|\l_@@_spsomesym_prefix_tl||{ foo bar baz }| % \end{center} % % The function will \emph{\enquote{store}} the normalized string % |foo-bar-baz| inside \myhlc|\l_@@_spsomesym_prefix_tl|, ready to be % conditionally appended. In the same way, |{ __foo___bar__ }| is stored % as |foo-bar|. % % The only exception is the exact value `|_-|', once the bounds are % trimmed, which is stored as a single hyphen `|-|'. After the command % prepends its own leading `|_|', this yields the \emph{silent} literal % `|_-|' documented for |\spread|. Any other value made only of hyphens, % underscores and spaces is stored as \emph{empty}. % \begin{macrocode} \cs_new_protected:Nn \@@_sanitize_affix:Nn { \tl_if_blank:nTF {#2} { \tl_clear:N #1 } { \tl_set:Ne #1 { \tl_to_str:e {#2} } \tl_trim_spaces:N #1 \str_if_eq:VnTF #1 { _- } { \tl_set:Nn #1 { - } } { \tl_replace_all:Nnn #1 { ~ } { - } \tl_replace_all:Nee #1 { \tl_to_str:n { _ } } { - } \@@_collapse_hyphens:N #1 \tl_put_left:Nn #1 { \q_mark } \tl_put_right:Nn #1 { \q_stop } \tl_replace_all:Nnn #1 { \q_mark - } { \q_mark } \tl_replace_all:Nnn #1 { - \q_stop } { \q_stop } \tl_remove_all:Nn #1 { \q_mark } \tl_remove_all:Nn #1 { \q_stop } } } } \cs_generate_variant:Nn \@@_sanitize_affix:Nn { cn } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{setspintent}}\label{cmd:setspintent} % % The |\setspintent| command is used to configure \mymeta{keys} % \emph{\enquote{globally}} for the utilities provided by \mypkg*[no-index]{spintent}. % % \subsubsection{Definition of variables and error messages for \cs[no-index]{setspintent}} % % \begin{macro}[int]{\l_@@_setspintent_input_arg_cmd_tl, unknown-command-setup} % The variable \myhlc|\l_@@_setspintent_input_arg_cmd_tl| will \emph{store} the % command or command name passed to the \emph{\enquote{first}} \mymarg{argument} `|#1|' % of the command |\setspintent|. % \begin{macrocode} \tl_new:N \l_@@_setspintent_input_arg_cmd_tl % \end{macrocode} % The \enquote{error} |unknown-command-setup| is raised when the % \mymarg{argument} `|#1|' passed to |\setspintent| is a % \emph{\enquote{non-existent}} command. % \begin{macrocode} \msg_new:nnnn { spintent } { unknown-command-setup } { Cannot~configure~unknown~command~'\c_backslash_str #1'~\msg_line_context:. } { The~command~'\c_backslash_str #1'~is~not~defined.~Check~the~spelling~of~the~command~ name. } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{setspintent}} % % \begin{macro}[int]{\@@_setspintent_set_keys:nn, \@@_setspintent_set_keys:en} % The function \myhlc|\@@_setspintent_set_keys:nn| will check if the % command passed as \mymarg{argument} exists in `|#1|' and set the % \mymeta{keys} passed as \mymarg{argument} in `|#2|' to the correct % path; otherwise, it will return an error. % \begin{macrocode} \cs_new_protected:Nn \@@_setspintent_set_keys:nn { \cs_if_exist:cTF {#1} { \keys_set:nn { spintent / #1 } {#2} } { \msg_error:nnn { spintent } { unknown-command-setup } {#1} } } \cs_generate_variant:Nn \@@_setspintent_set_keys:nn { en } % \end{macrocode} % \end{macro} % \begin{macro}[int]{\@@_setspintent_parse_args:nn, \@@_setspintent_parse_args:Vn} % The function \myhlc|\@@_setspintent_parse_args:nn| will check if the % argument passed in |#1| begins with `|\|' or is just a \emph{command name}. If it % begins with `|\|', it will remove it using \myhlc|\cs_to_str:N| and then % execute \myhlc|\@@_setspintent_set_keys:en|. Otherwise, i.e., if `|\|' % was not passed, it will execute \myhlc|\@@_setspintent_set_keys:nn|. % \begin{macrocode} \cs_new_protected:Nn \@@_setspintent_parse_args:nn { \tl_if_single:nTF {#1} { \token_if_cs:NTF #1 { \@@_setspintent_set_keys:en { \cs_to_str:N #1 } {#2} } { \@@_setspintent_set_keys:nn {#1} {#2} } } { \@@_setspintent_set_keys:nn {#1} {#2} } } \cs_generate_variant:Nn \@@_setspintent_parse_args:nn { Vn } % \end{macrocode} % \end{macro} % \begin{macro}{\setspintent} % Now we define the user command \ics*{setspintent}. % \begin{macrocode} \NewDocumentCommand \setspintent { m m } { \tl_if_blank:nTF {#1} { \msg_error:nnn { spintent } { unknown-command-setup } { } } { \tl_set:Nn \l_@@_setspintent_input_arg_cmd_tl {#1} \tl_trim_spaces:N \l_@@_setspintent_input_arg_cmd_tl \@@_setspintent_parse_args:Vn \l_@@_setspintent_input_arg_cmd_tl {#2} } } % \end{macrocode} % \end{macro} % % \subsection{Handling unknown keys}\label{key:unknown-keys} % % \begin{macro}[int] % { % \l_@@_command_name_str, % unknown-choice, % cmd-key-unknown, % cmd-key-value-unknown, % } % We define the variable \myhlc|\l_@@_command_name_str| which will \emph{store} % the \emph{\enquote{name}} of each command within the implementation along with the % messages |unknown-choice| used by \mymeta{keys} that use \myhlc|.choice:n| in % their implementation and the messages |cmd-key-unknown|, |cmd-key-value-unknown| % used by the \mymeta{key} |unknown| in commands that use \myoarg{key\textnormal{\textcolor{gray}{=}}val}. % \begin{macrocode} \str_new:N \l_@@_command_name_str \msg_new:nnnn { spintent } { unknown-choice } { Invalid~value~'#3'~for~key~'#1';~use~one~of:~#2~\msg_line_context:. } { The~key~'#1'~only~accepts~one~of:~#2. } \msg_new:nnnn { spintent } { cmd-key-unknown } { The~key~'#1'~is~unknown~by~command~ '\c_backslash_str \l_@@_command_name_str'~and~is~being~ignored~ \msg_line_context:. } { Check~the~spelling~of~the~key~name. } \msg_new:nnnn { spintent } { cmd-key-value-unknown } { The~key~'#1=#2'~is~unknown~by~command~ '\c_backslash_str \l_@@_command_name_str'~and~is~being~ignored~ \msg_line_context:. } { Check~the~spelling~of~the~key~name. } % \end{macrocode} % \end{macro} % \begin{macro}[int] % { % \@@_unknown_keys_cmd:nn, % \@@_unknown_keys_cmd:Vn, % \@@_unknown_keys_cmd:n, % } % The internal function \myhlc|\@@_unknown_keys_cmd:nn| raises the % \enquote{error} for an unknown \mymeta{key}. Its first \mymarg{argument} % is the name of the key and the second one its value. If the value is blank % it issues the message |cmd-key-unknown|; otherwise it issues % |cmd-key-value-unknown|. The variant \myhlc|\@@_unknown_keys_cmd:Vn| takes % the name of the key from a variable. The function % \myhlc|\@@_unknown_keys_cmd:n| is the one used by the \mymeta{key} % |unknown| of each command: it receives the value and takes the name of the % key from \myhlc|\l_keys_key_str|. The name of the command shown in the % message is stored in \myhlc|\l_@@_command_name_str|. % \begin{macrocode} \cs_new_protected:Nn \@@_unknown_keys_cmd:nn { \tl_if_blank:nTF {#2} { \msg_error:nnn { spintent } { cmd-key-unknown } {#1} } { \msg_error:nnnn { spintent } { cmd-key-value-unknown } {#1} {#2} } } \cs_generate_variant:Nn \@@_unknown_keys_cmd:nn { Vn } \cs_new_protected:Nn \@@_unknown_keys_cmd:n { \@@_unknown_keys_cmd:Vn \l_keys_key_str {#1} } % \end{macrocode} % \end{macro} % % \subsection{Definition of core base variables}\label{subsec:core-base-vars} % % \begin{macro}[int]{\@@_luafun_clean_split_and_set:n} % The function \myhlc|\@@_luafun_clean_split_and_set:n| defined in the % Lua module \myluamod{}, is common to several commands in this % implementation. It analyzes, processes, cleans and splits the % \mymarg{argument} passed to commands using internal functions defined % in the Lua module \myluamod. It isolates signs, integer components, decimal % separators, decimal part, decimal periodic part, exponents and final physical % \emph{\enquote{units}}, assigning them to \mypkg{expl3} variables. % % This function assigns the states of the variables `|_str|' or `|_tl|' % described below to |true|, |false|, |error| or \emph{stores} % information according to the role it fulfills within the % implementation. % \end{macro} % % \begin{variable}[int]{ % \l_@@_luaset_sign_tl, % \l_@@_luaset_part_int_tl, % \l_@@_luaset_dec_sep_tl, % \l_@@_luaset_part_dec_tl, % \l_@@_luaset_part_period_tl, % \l_@@_luaset_has_integer_str, % } % The variable \myhlc|\l_@@_luaset_sign_tl| \emph{stores} the explicit % `$\mathcolor{red}{+}$' or `$\mathcolor{red}{-}$' sign if present. % The variable \myhlc|\l_@@_luaset_part_int_tl| \emph{stores} the raw digit % sequence of the \emph{\enquote{integer part}}. The variable % \myhlc|\l_@@_luaset_dec_sep_tl| \emph{stores} the matched % `\textcolor{red}{\texttt{,}}' or `\textcolor{red}{\texttt{.}}' used as % the decimal separator. The variable \myhlc|\l_@@_luaset_part_dec_tl| % \emph{stores} the raw digits of the \emph{\enquote{finite decimal part}} % that follow the separator, and the variable \myhlc|\l_@@_luaset_part_period_tl| % \emph{stores} the digits of the \emph{\enquote{infinite periodic decimal part}} % that follow a semicolon `\textcolor{red}{\texttt{;}}'. % % The variable \myhlc|\l_@@_luaset_has_integer_str| \emph{store} the state % |true| when it is \emph{only} an \emph{\enquote{integer}}. % \iffalse %% Core base variables set in Lua module spintent.lua by \__spintent_luafun_clean_split_and_set:n % \fi % \begin{macrocode} \tl_new:N \l_@@_luaset_sign_tl \tl_new:N \l_@@_luaset_part_int_tl \tl_new:N \l_@@_luaset_dec_sep_tl \tl_new:N \l_@@_luaset_part_dec_tl \tl_new:N \l_@@_luaset_part_period_tl \str_new:N \l_@@_luaset_has_integer_str % \end{macrocode} % \end{variable} % % \begin{variable}[int]{ % \l_@@_luaset_only_part_int_str, % \l_@@_luaset_only_part_dec_str, % \l_@@_luaset_only_part_period_str, % \l_@@_luaset_format_part_int_str, % \l_@@_luaset_format_part_dec_str, % } % The literal entries of the numbers that we will pass to |\MathMLintent| % with the \emph{intent} \emph{property} |:number| are \emph{stored} % in \myhlc|\l_@@_luaset_only_part_int_str| for the % \emph{\enquote{integer part}}, in \myhlc|\l_@@_luaset_only_part_dec_str| for the % \emph{\enquote{finite decimal part}}, and in \myhlc|\l_@@_luaset_only_part_period_str| % for the \emph{\enquote{infinite periodic decimal part}}. % % Formatted versions, \mykey[spnum]{cifras-sep}|=true|, are \emph{stored} % in \myhlc|\l_@@_luaset_format_part_int_str| for the % \emph{\enquote{integer part}} and \myhlc|\l_@@_luaset_format_part_dec_str| for % the \emph{\enquote{finite decimal part}}. % \begin{macrocode} \str_new:N \l_@@_luaset_only_part_int_str \str_new:N \l_@@_luaset_only_part_dec_str \str_new:N \l_@@_luaset_only_part_period_str \str_new:N \l_@@_luaset_format_part_int_str \str_new:N \l_@@_luaset_format_part_dec_str % \end{macrocode} % \end{variable} % % \begin{variable}[int]{ % \l_@@_luaset_millons_str, % \l_@@_luaset_decimal_period_str, % \l_@@_luaset_not_decimal_period_str, % \l_@@_luaset_not_decimal_not_period_str, % \l_@@_luaset_dec_is_all_zeros_str, % \l_@@_luaset_has_exponent_str, % \l_@@_luaset_exponent_tl, % } % % The flag variable \myhlc|\l_@@_luaset_millons_str| indicates whether the % integer part translates exactly to a multiple of one million when % passed to |\MathMLintent|. To properly structure the reading and writing of numbers in % \mynvmath{MathML}, we use the flag variables \myhlc|\l_@@_luaset_decimal_period_str|, % \myhlc|\l_@@_luaset_not_decimal_period_str|, and \myhlc|\l_@@_luaset_not_decimal_not_period_str|. % The flag variable \myhlc|\l_@@_luaset_dec_is_all_zeros_str| monitors whether the \emph{\enquote{finite % decimal part}} is completely zero, useful for \emph{\enquote{pure periodic decimals}}. % % The flag variable \myhlc|\l_@@_luaset_has_exponent_str| indicates % whether active \emph{\enquote{scientific notation}} was detected at the end of the % entered number, passed with `\textcolor{red}{\texttt{e}}' or % `\textcolor{red}{\texttt{E}}'. If true, it will store the digits of the % exponent in \myhlc|\l_@@_luaset_exponent_tl|. % \begin{macrocode} \str_new:N \l_@@_luaset_millons_str \str_new:N \l_@@_luaset_decimal_period_str \str_new:N \l_@@_luaset_not_decimal_period_str \str_new:N \l_@@_luaset_not_decimal_not_period_str \str_new:N \l_@@_luaset_dec_is_all_zeros_str \str_new:N \l_@@_luaset_has_exponent_str \tl_new:N \l_@@_luaset_exponent_tl % \end{macrocode} % \end{variable} % % \begin{variable}[int]{ % \l_@@_luaset_has_units_str, % \l_@@_luaset_status_str, % \l_@@_luaset_arg_above_tl, % \l_@@_luaset_arg_below_tl, % \l_@@_luaset_denom_has_number_str, % } % % Since the function \myhlc|\@@_luafun_clean_split_and_set:n| is common to % |\spnum| (\S\ref{cmd:spnum}) and |\spunit| (\S\ref{cmd:spunit}), it determines the following % unit-related variables after processing the \mymarg{arguments} via the Lua module \myluamod. % % First, it checks if there is any residual text \emph{after} the processed number % that is a candidate for physical \emph{\enquote{units}}. It sets the flag variable % \myhlc|\l_@@_luaset_has_units_str| accordingly. Following this, it % verifies if this text contains more than one \enquote{/}. It then sets % the variable \myhlc|\l_@@_luaset_status_str| to |valid| or |multislash|. % If the structure is |valid|, it splits the unit into % \myhlc|\l_@@_luaset_arg_above_tl| and \myhlc|\l_@@_luaset_arg_below_tl|. % Finally, it analyzes \myhlc|\l_@@_luaset_arg_below_tl|; if the denominator % starts with a number, it sets \myhlc|\l_@@_luaset_denom_has_number_str| to % |true|, otherwise to |false|. % \begin{macrocode} \str_new:N \l_@@_luaset_has_units_str \str_new:N \l_@@_luaset_status_str \tl_new:N \l_@@_luaset_arg_above_tl \tl_new:N \l_@@_luaset_arg_below_tl \str_new:N \l_@@_luaset_denom_has_number_str % \end{macrocode} % \end{variable} % % \subsection{Common messages for text mode and math mode} % % The \enquote{error}s |need-math-mode| and |need-text-mode| are raised when a % command that requires math mode or text mode is used in the other one. % \begin{macrocode} \msg_new:nnnn { spintent } { need-math-mode } { Math~mode~required~for~'\tl_trim_spaces:n {#1}'~\msg_line_context:. } { Use~the~command~inside~'$...$'~or~in~a~math~environment. } \msg_new:nnnn { spintent } { need-text-mode } { Text~mode~required~for~'\tl_trim_spaces:n {#1}'~\msg_line_context:. } { Use~the~command~outside~of~'$...$'~and~math~environments. } % \end{macrocode} % % \subsection{The command \cs{spnum}}\label{cmd:spnum} % % The user command |\spnum| is \emph{first} of \emph{\enquote{the big four}} % provided by the \mypkg*[no-index]{spintent} package and is fundamental to the % implementation of other commands. It handles the processing of numerical % input, scientific notation, and units of measurement, using the Lua module \myluamod, % which is responsible for separating and assigning variables defined in % \mypkg{expl3}. % % From the \mypkg{expl3} side, we handle the printing logic and the % intents passed to |\MathMLintent| to achieve a valid % \mynvmath{MathML} structure that is accepted by \mynvmath{MathCAT}. % % \subsubsection{Definition of variables for \cs[no-index]{spnum}} % % \begin{variable}[int] % { % \l_@@_spnum_read_terse_bool, % \l_@@_spnum_read_period_sep_str, % \l_@@_spnum_notation_sym_tl, % \l_@@_spnum_intent_read_sign_str, % \l_@@_spnum_intent_read_dec_sep_str, % } % The variable \myhlc|\l_@@_spnum_read_terse_bool| handled by the key \mykey[spnum]{read-sign}, controls the % |intent| passed to |\MathMLintent| to speak the `$\mathcolor{red}{+}$' % or `$\mathcolor{red}{-}$'. If set to |clear|, it will say % \emph{\enquote{positivo}} or \emph{\enquote{negativo}} \emph{after} reading the % number; if set to |terse|, it will say \emph{\enquote{más}} or \emph{\enquote{menos}} % \emph{before} reading the number. % % The variable \myhlc|\l_@@_spnum_read_period_sep_str| handled by the key \mykey[spnum]{read-period-sep}, controls how the % decimal separator `\textcolor{red}{\texttt{;}}', periodic part, is printed % and spoken when the \mymarg{argument} is a \emph{pure decimal periodic} and % is passed the |intent| to |\MathMLintent|. If set to |coma|, it will say % \emph{\enquote{coma}} and print `\textcolor{red}{,}', if set to |punto| it % will say \emph{\enquote{punto}} and print `\textcolor{red}{.}'. % % The variable \myhlc|\l_@@_spnum_notation_sym_tl|, handled by the key % \mykey[spnum]{notation-sym}, determines whether `$\mathcolor{red}{\times}$' % will be printed when set to |times| or `$\mathcolor{red}{\cdot}$' % when set to |cdot|. % % The variable \myhlc|\l_@@_spnum_intent_read_sign_str| will \emph{store} the % spoken translation used in the |intent| function passed to % |\MathMLintent| for the `$\mathcolor{red}{+}$' or % `$\mathcolor{red}{-}$' according to the value passed to the key % \mykey[spnum]{read-sign}. % % The variable \myhlc|\l_@@_spnum_intent_read_dec_sep_str| \emph{stores} the % spoken translation that will be used in the |intent| function passed to % |\MathMLintent| for reading the decimal separation as a % \emph{\enquote{coma}} or a \emph{\enquote{punto}} according to the % value found in the variable \myhlc|\l_@@_luaset_dec_sep_tl|. % \begin{macrocode} \bool_new:N \l_@@_spnum_read_terse_bool \str_new:N \l_@@_spnum_read_period_sep_str \tl_new:N \l_@@_spnum_notation_sym_tl \str_new:N \l_@@_spnum_intent_read_sign_str \str_new:N \l_@@_spnum_intent_read_dec_sep_str % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spnum}} % % \begin{macro}[int]{spnum-has-units, spnum-no-digits} % The \enquote{error} |spnum-has-units| is raised when the user includes % \emph{\enquote{units}} in the \mymarg{argument} passed in `|#1|', advising % them to use the |\spunit| command instead. The \enquote{error} % |spnum-no-digits| is raised when the provided \mymarg{argument} contains no % numerical digits. Since other commands, such as |\spdiv| and |\spmul|, % delegate to |\spnum|, these messages do not mention the command name. % \begin{macrocode} \msg_new:nnnn { spintent } { spnum-has-units } { Units~are~not~allowed~in~a~numerical~value~('#1')~\msg_line_context:. } { Use~'\token_to_str:N \spunit'~to~print~numbers~with~units. } \msg_new:nnnn { spintent } { spnum-no-digits } { Invalid~numerical~value~'#1'~\msg_line_context:. } { A~numerical~value~must~contain~at~least~one~digit. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spnum}} % % \begin{macro}[int]{cifras-sep, read-sign, read-period-sep, notation-sym, unknown} % We define the \mymeta{keys} \mykey[spnum]{cifras-sep}, \mykey[spnum]{read-sign}, % \mykey[spnum]{read-period-sep} and \mykey[spnum]{notation-sym}. % \begin{macrocode} \keys_define:nn { spintent / spnum } { cifras-sep .bool_set:N = \l_@@_spnum_cifras_sep_bool, cifras-sep .initial:n = true, cifras-sep .value_required:n = true, read-sign .choices:nn = { terse , clear } { \int_case:nn { \l_keys_choice_int } { {1} { \bool_set_true:N \l_@@_spnum_read_terse_bool } {2} { \bool_set_false:N \l_@@_spnum_read_terse_bool } } }, read-sign .initial:n = terse, read-sign .value_required:n = true, read-sign / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-sign } { terse , ~clear } { \exp_not:n {#1} }, read-period-sep .choices:nn = { coma , punto } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spnum_read_period_sep_str { coma } } {2} { \str_set:Nn \l_@@_spnum_read_period_sep_str { punto } } } }, read-period-sep .initial:n = coma, read-period-sep .value_required:n = true, read-period-sep / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-period-sep } { coma, ~punto } { \exp_not:n {#1} }, notation-sym .choices:nn = { times , cdot } { \int_case:nn { \l_keys_choice_int } { {1} { \tl_set:Nn \l_@@_spnum_notation_sym_tl { \times } } {2} { \tl_set:Nn \l_@@_spnum_notation_sym_tl { \cdot } } } }, notation-sym .initial:n = cdot, notation-sym .value_required:n = true, notation-sym / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { notation-sym } { times , ~ cdot } { \exp_not:n {#1} }, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spnum } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spnum}} % % \begin{macro}[int]{\@@_spnum_render_part:n, \@@_spnum_render_int:, \@@_spnum_render_dec:} % The function \myhlc|\@@_spnum_render_part:n| processes the % \mymarg{argument} passed in `|#1|' used by the functions \myhlc|\@@_spnum_render_int:| and % \myhlc|\@@_spnum_render_dec:| to define the \emph{intent property} |:number| for % the integer and decimal part passed to |\MathMLintent|. It evaluates % whether the key \mykey[spnum]{cifras-sep} is active or not. % \begin{macrocode} \cs_new_protected:Nn \@@_spnum_render_part:n { \MathMLintent { \str_use:c { l_@@_luaset_only_part_#1_str } :number } { \bool_if:NTF \l_@@_spnum_cifras_sep_bool { \str_use:c { l_@@_luaset_format_part_#1_str } } { \tl_use:c { l_@@_luaset_part_#1_tl } } } } \cs_new_protected:Nn \@@_spnum_render_int: { \@@_spnum_render_part:n { int } } \cs_new_protected:Nn \@@_spnum_render_dec: { \@@_spnum_render_part:n { dec } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spnum_render_only_period:, \@@_spnum_print_period_bar:,\@@_spnum_render_period_sep:} % The function \myhlc|\@@_spnum_render_only_period:| isolates the % \emph{\enquote{infinite periodic decimal part}} from the % \emph{\enquote{finite decimal part}}, assigning the % \emph{intent property} |:number| passed to |\MathMLintent| which will be used % by |\MathMLarg| in the function \myhlc|\@@_spnum_print_period_bar:|. % \begin{macrocode} \cs_new_protected:Nn \@@_spnum_render_only_period: { \MathMLintent { \l_@@_luaset_only_part_period_str :number } { \l_@@_luaset_part_period_tl } } % \end{macrocode} % The function \myhlc|\@@_spnum_print_period_bar:| inserts the function % \myhlc|\@@_spnum_render_only_period:| as an argument within the % \emph{intent} |_periódico:postfix($dp)| passed to |\MathMLintent|, generating % the correct spoken for the \emph{\enquote{infinite periodic decimal % part}}. % \begin{macrocode} \cs_new_protected:Nn \@@_spnum_print_period_bar: { \MathMLintent { _periódico:postfix($dp) } { { \overline { \MathMLarg { dp } { \@@_spnum_render_only_period: } } } } } % \end{macrocode} % The function \myhlc|\@@_spnum_render_period_sep:| sets how the decimal % separator handled by the key \mykey[spnum]{read-period-sep} will be % printed and spoken when a \emph{\enquote{pure periodic decimal}} is passed. % \begin{macrocode} \cs_new_protected:Nn \@@_spnum_render_period_sep: { \str_if_eq:VnTF \l_@@_spnum_read_period_sep_str { coma } { \MathMLintent { _coma:prefix($pp) } { \mathord{,} \MathMLarg { pp } { \@@_spnum_print_period_bar: } } } { \MathMLintent { _punto:prefix($pp) } { \mathord{.} \MathMLarg { pp } { \@@_spnum_print_period_bar: } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spnum_render_decimal_sep:} % The function \myhlc|\@@_spnum_render_decimal_sep:| sets the variable % \myhlc|\l_@@_spnum_intent_read_dec_sep_str| with the function % |_coma:prefix($dp)| for reading a \emph{\enquote{coma}} or |_punto:prefix($dp)| for % reading a \emph{\enquote{punto}} to |\MathMLintent|, after parsing the value of % \myhlc|\l_@@_luaset_dec_sep_tl|. We print the decimal separator % stored in \myhlc|\l_@@_luaset_dec_sep_tl| using |\mathord| to remove % spaces around it and then use the command |\MathMLarg| where we insert % \myhlc|\@@_invisible_sep:| for compatibility with \mynvmath{MathML} and % finally call the function \myhlc|\@@_spnum_render_dec:| to print the % finite decimal part. % \begin{macrocode} \cs_new_protected:Nn \@@_spnum_render_decimal_sep: { \str_if_eq:VnTF \l_@@_luaset_dec_sep_tl { , } { \str_set:Nn \l_@@_spnum_intent_read_dec_sep_str { _coma } } { \str_set:Nn \l_@@_spnum_intent_read_dec_sep_str { _punto } } \MathMLintent { \l_@@_spnum_intent_read_dec_sep_str :prefix($dp) } { \mathord { \l_@@_luaset_dec_sep_tl } \MathMLarg { dp } { % keep "dp" in mathml-AF \MathMLintent { :silent } { \c_@@_invisible_sep_int } \@@_spnum_render_dec: } } % period part \str_if_eq:VnT \l_@@_luaset_decimal_period_str { true } { \str_if_eq:VnTF \l_@@_luaset_dec_is_all_zeros_str { true } { \@@_inner_sep: \@@_spnum_print_period_bar: } { \MathMLintent { _con:prefix($pnum) } { \@@_inner_sep: \MathMLarg { pnum } { \@@_spnum_print_period_bar: } } } } } % \end{macrocode} % \end{macro} % % % \begin{macro}[int]{\@@_spnum_print_part_dec:, \@@_spnum_print_part_period:} % The function \myhlc|\@@_spnum_print_part_dec:| checks if a decimal part exists. % If it does, it determines whether the decimal separator is a comma or a period % to set the correct speech intent, \emph{\enquote{coma}} or \emph{\enquote{punto}}. % It then prints the separator, renders the decimal digits, and if a periodic % sequence follows, it decides how to attach the periodic bar. % % The function \myhlc|\@@_spnum_print_part_period:| handles cases where a % periodic part exists. It evaluates if it should render the periodic separator, % particularly when the number has a periodic part but lacks a standard % decimal part. % \begin{macrocode} \cs_new_protected:Nn \@@_spnum_print_part_dec: { \tl_if_empty:NF \l_@@_luaset_part_dec_tl { \@@_spnum_render_decimal_sep: } } \cs_new_protected:Nn \@@_spnum_print_part_period: { \tl_if_empty:NF \l_@@_luaset_part_period_tl { \str_if_eq:VnTF \l_@@_luaset_not_decimal_period_str { true } { \@@_spnum_render_period_sep: } { \tl_if_empty:NT \l_@@_luaset_part_dec_tl { \@@_spnum_render_period_sep: } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spnum_print_integer:, \@@_spnum_print_num_start:, \@@_spnum_mathml:} % The function \myhlc|\@@_spnum_print_integer:| checks if the number consists % only of an integer. If so, it renders it. If a decimal or periodic % part exists, it sets up an intent to link the integer part, respecting % digit separators if enabled, to the subsequent decimal or periodic functions. % % The function \myhlc|\@@_spnum_print_num_start:| checks if an explicit sign % is present. If it finds one, it evaluates the terse reading flag to decide % whether the sign should be read as a prefix, \emph{\enquote{más}} or % \emph{\enquote{menos}}, or as a postfix, \emph{\enquote{positivo}} or % \emph{\enquote{negativo}}, and wraps the intent around the rest of the number. % % The function \myhlc|\@@_spnum_mathml:| is the main function. % It triggers the sign and base number evaluation, and then checks if a scientific % exponent is present. If it is, it appends the correct base-10 notation symbol % and exponent to complete the sequence. % % \begin{important}* % Note for \myhlc|\@@_invisible_sep:|. % \end{important} % \begin{macrocode} \cs_new_protected:Nn \@@_spnum_print_integer: { \str_if_eq:VnTF \l_@@_luaset_not_decimal_not_period_str { true } { \@@_spnum_render_int: } { \MathMLintent { \l_@@_luaset_only_part_int_str :number:prefix($next) } { \bool_if:NTF \l_@@_spnum_cifras_sep_bool { \l_@@_luaset_format_part_int_str } { \l_@@_luaset_part_int_tl } \MathMLarg { next } { \tl_if_empty:NTF \l_@@_luaset_part_dec_tl { \@@_spnum_print_part_period: } { \@@_spnum_print_part_dec: } } } } } \cs_new_protected:Nn \@@_spnum_print_num_start: { \tl_if_empty:NTF \l_@@_luaset_sign_tl { \@@_invisible_sep: % need \@@_spnum_print_integer: } { \bool_if:NTF \l_@@_spnum_read_terse_bool { \str_if_eq:VnTF \l_@@_luaset_sign_tl { + } { \str_set:Nn \l_@@_spnum_intent_read_sign_str { _más:prefix($n)} } { \str_set:Nn \l_@@_spnum_intent_read_sign_str { _menos:prefix($n)} } } { \str_if_eq:VnTF \l_@@_luaset_sign_tl { + } { \str_set:Nn \l_@@_spnum_intent_read_sign_str { _positivo:postfix($n) } } { \str_set:Nn \l_@@_spnum_intent_read_sign_str { _negativo:postfix($n) } } } \@@_invisible_sep: % need \MathMLintent { \l_@@_spnum_intent_read_sign_str } { \l_@@_luaset_sign_tl \MathMLarg { n } { \@@_spnum_print_integer: } } } } \cs_new_protected:Nn \@@_spnum_mathml: { \@@_spnum_print_num_start: \str_if_eq:VnT \l_@@_luaset_has_exponent_str { true } { \MathMLintent { _por } { \l_@@_spnum_notation_sym_tl } 10^{ \l_@@_luaset_exponent_tl } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spnum} % The public user command \ics*{spnum} accepts an optional \myoarg{key% % \textnormal{\textcolor{gray}{=}}val} argument in `|#1|' and a mandatory % \mymarg{rational number} in `|#2|'. It first checks if it is executed inside % math mode, raising an error if it is not. If correct, it opens a local group % to apply the options and passes the input to Lua for parsing via % \myhlc|\@@_luafun_clean_split_and_set:n|. % % Before printing, it validates the extracted data. It throws an error if it % finds trailing units in the input or if the integer part is completely empty, % that is, it has no valid digits. If all checks pass, it calls \myhlc|\@@_spnum_mathml:| % to render the final number, and finally closes the group. % \begin{macrocode} \NewDocumentCommand \spnum { O{} m } { \mode_if_math:TF { \group_begin: \keys_set:nn { spintent / spnum } {#1} \@@_luafun_clean_split_and_set:n { \exp_not:n {#2} } \str_if_eq:VnTF \l_@@_luaset_has_units_str { true } { \msg_error:nnn { spintent } { spnum-has-units } {#2} } { \tl_if_empty:NTF \l_@@_luaset_part_int_tl { \msg_error:nnn { spintent } { spnum-no-digits } {#2} } { \@@_spnum_mathml: } } \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spnum } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spunit}}\label{cmd:spunit} % % The user command |\spunit| is the \emph{second} of \emph{\enquote{the big four}} % provided by the \mypkg*[no-index]{spintent} package and is used to parse and print physical units % and angular systems. It sends the \mymarg{argument} to Lua module \myluamod{} via % \myhlc|\@@_luafun_clean_split_and_set:n| to separate and clean the \mymarg{argument}. % % This ensures that all \emph{\enquote{units}}, including SÍ fractions, products, and sexagesimal % angles, are processed consistently by the \myluamod{}. % % \subsubsection{Definition of variables for \cs[no-index]{spunit}} % % \begin{variable}[int]{ % \l_@@_spunit_luaset_above_n_str, % \l_@@_spunit_luaset_above_found_str, % \l_@@_spunit_luaset_above_sexa_str, % \l_@@_spunit_luaset_below_n_str, % \l_@@_spunit_luaset_below_found_str % } % These string variables store the summary returned by the Lua module \myluamod{} % for each of the two products of a \emph{\enquote{unit expression}}, the one % before the slash, |above|, and the one after it, |below|: the number of % \emph{\enquote{units}} in the product, whether all of them exist in the dictionary % and, for |above|, whether it is a single sexagesimal symbol. For each unit % number || of a product |

|, Lua also leaves the canonical symbol, the % speech text and the exponent. The canonical symbol is stored in % \myhlc|\l_@@_spunit_luaset_

__canonical_str|, the speech text in % \myhlc|\l_@@_spunit_luaset_

__read_str| and the exponent, which is % empty when there is none, in \myhlc|\l_@@_spunit_luaset_

__exp_tl|. % \begin{macrocode} \str_new:N \l_@@_spunit_luaset_above_n_str \str_new:N \l_@@_spunit_luaset_above_found_str \str_new:N \l_@@_spunit_luaset_above_sexa_str \str_new:N \l_@@_spunit_luaset_below_n_str \str_new:N \l_@@_spunit_luaset_below_found_str % \end{macrocode} % \end{variable} % % \begin{variable}[int]{ % \l_@@_spunit_luaset_canonical_str, % \l_@@_spunit_luaset_read_str, % \l_@@_spunit_luaset_status_str % } % The first two hold the data of the \emph{\enquote{unit}} being printed, a copy % of the one stored by Lua for it: the canonical \emph{\enquote{unit symbol}} and % the correct speech text for \mynvmath{NVDA/MathCAT}. The last one stores the % result of defining a new \emph{\enquote{unit}} or alias in Lua. % \begin{macrocode} \str_new:N \l_@@_spunit_luaset_canonical_str \str_new:N \l_@@_spunit_luaset_read_str \str_new:N \l_@@_spunit_luaset_status_str % \end{macrocode} % \end{variable} % % \begin{variable}[int]{ % \l_@@_spunit_exponent_tl, % \l_@@_spunit_read_slash_str % } % These variables are used to format the \emph{\enquote{unit}}: its exponent, % a copy of the one stored by Lua, and the speech text of the slash. % \begin{macrocode} \tl_new:N \l_@@_spunit_exponent_tl \str_new:N \l_@@_spunit_read_slash_str % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spunit}} % % \begin{macro}[int]{spunit-invalid-unit, spunit-no-units, spunit-inline-numeric-denominator} % The \enquote{error} |spunit-invalid-unit| is raised when the % \emph{\enquote{unit expression}} provided in `|#1|' is unknown or % malformed. The \enquote{error} |spunit-no-units| is raised when no % \emph{\enquote{units}} are found in the \mymarg{argument}, advising the % user to use |\spnum| for pure numbers. The \enquote{error} % |spunit-inline-numeric-denominator| is raised when a numeric coefficient is % illegally placed in the denominator of an inline fraction. % \begin{macrocode} \msg_new:nnnn { spintent } { spunit-invalid-unit } { Invalid~unit~expression~'#1'~\msg_line_context:. } { The~expression~contains~an~unknown~or~malformed~unit.~Check~the~spelling~or~verify~ that~the~unit~is~defined. } \msg_new:nnnn { spintent } { spunit-no-units } { No~unit~found~in~'#1'~\msg_line_context:. } { At~least~one~unit~is~required;~use~'\token_to_str:N \spnum'~for~pure~ numbers. } \msg_new:nnnn { spintent } { spunit-inline-numeric-denominator } { Invalid~numeric~coefficient~in~the~inline~denominator~'#1'~\msg_line_context:. } { Numbers~are~not~allowed~after~the~slash~'/'~in~the~inline~mode~of~units. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spunit}} % % \begin{macro}[int]{print-cdot, read-cdot, read-slash, cifras-sep, read-period-sep, notation-sym, unknown} % We define the \mymeta{keys} \mykey[spunit]{print-cdot}, \mykey[spunit]{read-cdot}, \mykey[spunit]{read-slash}, % \mykey[spunit]{cifras-sep}, \mykey[spunit]{read-period-sep} and \mykey[spunit]{notation-sym}. % \begin{macrocode} \keys_define:nn { spintent / spunit } { print-cdot .bool_set:N = \l_@@_spunit_print_cdot_bool, print-cdot .initial:n = false, print-cdot .value_required:n = true, read-cdot .bool_set:N = \l_@@_spunit_read_cdot_bool, read-cdot .initial:n = false, read-cdot .value_required:n = true, read-slash .choices:nn = { por , partido-por } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spunit_read_slash_str { _por } } {2} { \str_set:Nn \l_@@_spunit_read_slash_str { _partido-por } } } }, read-slash .initial:n = por, read-slash .value_required:n = true, read-slash / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-slash } { por, ~partido-por } { \exp_not:n {#1} }, cifras-sep .meta:nn = { spintent / spnum } { cifras-sep = {#1} }, cifras-sep .value_required:n = true, read-period-sep .meta:nn = { spintent / spnum } { read-period-sep = {#1} }, read-period-sep .value_required:n = true, notation-sym .meta:nn = { spintent / spnum } { notation-sym = {#1} }, notation-sym .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spunit } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spunit}} % % \begin{macro}[int]{\@@_spunit_format_canonical:} % The function \myhlc|\@@_spunit_format_canonical:| handles the visual formatting % of the \emph{\enquote{unit symbol}}. It specifically checks for the \emph{liter symbol} % `ℓ' to print it as |\ell|, and formats all other valid \emph{\enquote{units}} % in standard upright text using |\mathrm|. % \begin{macrocode} \cs_new_protected:Nn \@@_spunit_format_canonical: { \str_case:VnF \l_@@_spunit_luaset_canonical_str { { ℓ } { \ell } } { \mathrm { \l_@@_spunit_luaset_canonical_str } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spunit_load_term:nn} % The function \myhlc|\@@_spunit_load_term:nn| copies the data that Lua stored % for the unit number `|#2|' of the product `|#1|', which is |above| or |below|, into % the variables used to print it. % \begin{macrocode} \cs_new_protected:Nn \@@_spunit_load_term:nn { \str_set_eq:Nc \l_@@_spunit_luaset_canonical_str { l_@@_spunit_luaset_ #1 _ #2 _canonical_str } \str_set_eq:Nc \l_@@_spunit_luaset_read_str { l_@@_spunit_luaset_ #1 _ #2 _read_str } \tl_set_eq:Nc \l_@@_spunit_exponent_tl { l_@@_spunit_luaset_ #1 _ #2 _exp_tl } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spunit_print_term:nn} % The function \myhlc|\@@_spunit_print_term:nn| prints the unit number `|#2|' % of the product `|#1|', along with its exponent. Unless this is the first % unit of the product, it handles the spacing and multiplication dot: % |\cdot| when \mykey{print-cdot} is |true|, or a thin space |\,| % when it is |false|. In both cases it is read as \emph{\enquote{por}} when % \mykey{read-cdot} is |true|, and it is silent otherwise. % \begin{macrocode} \cs_new_protected:Nn \@@_spunit_print_term:nn { \int_compare:nNnT {#2} > { 1 } { \bool_if:NTF \l_@@_spunit_print_cdot_bool { \bool_if:NTF \l_@@_spunit_read_cdot_bool { \MathMLintent { _por } { \cdot } } { \MathMLintent { :silent } { \cdot } } } { \, \bool_if:NT \l_@@_spunit_read_cdot_bool { \MathMLintent { _por } { \mathopen { \c_@@_invisible_sep_int } } } } } % \end{macrocode} % Then it renders the base unit and, if it has one, its exponent. % \begin{macrocode} \@@_spunit_load_term:nn {#1} {#2} \tl_if_empty:NTF \l_@@_spunit_exponent_tl { \MathMLintent { \l_@@_spunit_luaset_read_str } { \@@_spunit_format_canonical: } } { { \MathMLintent { \l_@@_spunit_luaset_read_str } { \@@_spunit_format_canonical: } } ^ { \l_@@_spunit_exponent_tl } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spunit_print_product:n} % The function \myhlc|\@@_spunit_print_product:n| prints all the units of % the product `|#1|', |above| or |below|, in the order in which Lua found them. % \begin{macrocode} \cs_new_protected:Nn \@@_spunit_print_product:n { \int_step_inline:nn { \str_use:c { l_@@_spunit_luaset_ #1 _n_str } } { \@@_spunit_print_term:nn {#1} {##1} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_print_spunit_hierarchy:} % The function \myhlc|\@@_print_spunit_hierarchy:| handles the layout of inline % \emph{\enquote{unit}} fractions. It first prints the \emph{\enquote{units}} % before the slash. If a denominator exists, it prints the slash |/| `$\mathcolor{red}{/}$' % with its corresponding speech intent, and then prints the remaining \emph{\enquote{units}}. % \begin{macrocode} \cs_new_protected:Nn \@@_print_spunit_hierarchy: { \tl_if_empty:NF \l_@@_luaset_arg_above_tl { \@@_spunit_print_product:n { above } \tl_if_empty:NF \l_@@_luaset_arg_below_tl { \MathMLintent { \l_@@_spunit_read_slash_str } { / } \@@_spunit_print_product:n { below } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spunit_exec:n} % The function \myhlc|\@@_spunit_exec:n| performs the main validation % and layout for \emph{\enquote{units}}. It first checks if the \mymarg{argument} % has units, is a valid expression, and does not contain illegal numbers % in the denominator, throwing the appropriate \enquote{error}s if any check fails. % % Then it asks Lua to split each product of the expression into its units % and to look them up; if any of them is unknown, it raises the % \enquote{error} for an invalid expression. % % If the units are valid, it calls \myhlc|\@@_spunit_mathml:|. % \begin{macrocode} \cs_new_protected:Nn \@@_spunit_exec:n { \str_if_eq:VnTF \l_@@_luaset_has_units_str { false } { \msg_error:nnn { spintent } { spunit-no-units } {#1} } { \str_if_eq:VnTF \l_@@_luaset_status_str { valid } { \str_if_eq:VnTF \l_@@_luaset_denom_has_number_str { true } { \msg_error:nnn { spintent } { spunit-inline-numeric-denominator } {#1} } { \@@_luafun_spunit_parse_product:nV { above } \l_@@_luaset_arg_above_tl \tl_if_empty:NF \l_@@_luaset_arg_below_tl { \@@_luafun_spunit_parse_product:nV { below } \l_@@_luaset_arg_below_tl } \bool_lazy_and:nnTF { \str_if_eq_p:ee { \l_@@_spunit_luaset_above_found_str } { true } } { \bool_lazy_or_p:nn { \tl_if_empty_p:N \l_@@_luaset_arg_below_tl } { \str_if_eq_p:ee { \l_@@_spunit_luaset_below_found_str } { true } } } { \@@_spunit_mathml: } { \msg_error:nnn { spintent } { spunit-invalid-unit } {#1} } } } { \msg_error:nnn { spintent } { spunit-invalid-unit } {#1} } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spunit_mathml:} % The function \myhlc|\@@_spunit_mathml:| builds the \mynvmath{MathML} % structure of a validated expression. If it is a sexagesimal angle in % degrees, minutes, or seconds, it prints the number and the symbol % \emph{\enquote{without any space in between}}, applying a slight negative % space `|\!|' for |arcseconds|. When the number is exactly |1|, with no % decimal part, the reading becomes the singular literal |_grado:unit|, % |_minuto:unit| or |_segundo:unit|, since neither the fixed plural names nor % \mynvmath{MathCAT} can see the number. For standard units, it prints the % number followed by a thin space `|\,|' and then processes the unit % sequence. % \begin{macrocode} \cs_new_protected:Nn \@@_spunit_mathml: { \str_if_eq:VnTF \l_@@_spunit_luaset_above_sexa_str { true } { \@@_spunit_load_term:nn { above } { 1 } \tl_if_empty:NF \l_@@_luaset_part_int_tl { \@@_spnum_mathml: } \bool_lazy_and:nnT { \str_if_eq_p:ee { \l_@@_luaset_part_int_tl } { 1 } } { \tl_if_empty_p:N \l_@@_luaset_part_dec_tl } { \str_set:Ne \l_@@_spunit_luaset_read_str { \str_case:Vn \l_@@_spunit_luaset_canonical_str { { ° } { _grado:unit } { ′ } { _minuto:unit } { ″ } { _segundo:unit } } } } \MathMLintent { \l_@@_spunit_luaset_read_str } { \mathord { \str_case:Vn \l_@@_spunit_luaset_canonical_str { { ″ } { \prime \! \prime } { ′ } { \prime } { ° } { ° } } } } } { \tl_if_empty:NF \l_@@_luaset_part_int_tl { \@@_spnum_mathml: \, } \@@_print_spunit_hierarchy: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spunit} % The public user command \ics*{spunit} accepts an optional \myoarg{key% % \textnormal{\textcolor{gray}{=}}val} argument in `|#1|' and a mandatory % \mymarg{unit expression} in `|#2|'. It first checks if it is executed inside % \emph{math mode}, raising an \enquote{error} if it is not. If correct, it opens a local group % to apply the options and passes the \mymarg{argument} to Lua module for parsing via % \myhlc|\@@_luafun_clean_split_and_set:n| function. % % Finally, it delegates the execution to the function \myhlc|\@@_spunit_exec:n| % to perform the safety checks and render the parsed data, before closing the group. % \begin{macrocode} \NewDocumentCommand \spunit { O{} m } { \mode_if_math:TF { \group_begin: \keys_set:nn { spintent / spunit } {#1} \@@_luafun_clean_split_and_set:n { \exp_not:n {#2} } \@@_spunit_exec:n {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spunit } } } % \end{macrocode} % \end{macro} % % \subsection{The commands \cs{spnewunit} and \cs{spunitalias}}\label{cmd:spnewunit} % % The user commands |\spnewunit| and |\spunitalias| allow users to add % new custom \emph{\enquote{units}} or \emph{\enquote{aliases}} to the % package's dictionary. Similar to |\spunit|, these commands delegate % the processing and validation to the \myluamod{} via % \myhlc|\@@_luafun_define_custom_unit:nn| and \myhlc|\@@_luafun_define_spunit_alias:nn|. % % During the definition, the Lua module checks if the new \emph{\enquote{unit}} % or \emph{\enquote{alias}} is valid and does not conflict with existing \emph{\enquote{units}}. % It returns the result of this operation to \TeX{} using the \myhlc|\l_@@_spunit_luaset_status_str| variable. % If a collision is detected or the \mymarg{argument} is invalid, the package throws the % corresponding \enquote{error} message. % % \subsubsection{Definition of error messages for \cs[no-index]{spnewunit} and \cs[no-index]{spunitalias}} % % \begin{macro}[int]{spnewunit-duplicate, spunitalias-duplicate, spunitalias-invalid-expr} % The \enquote{error}s |spnewunit-duplicate| and |spunitalias-duplicate| are % raised when the user attempts to define a \emph{\enquote{new unit}} or % alias passed in `|#1|' that already exists in the dictionary. The % \enquote{error} |spunitalias-invalid-expr| is raised by |\spunitalias| when % the target expression provided in `|#2|' is invalid, for example, because it % contains unknown \emph{\enquote{units}} or incorrect syntax. % \begin{macrocode} \msg_new:nnnn { spintent } { spnewunit-duplicate } { The~unit~symbol~or~alias~'#1'~is~already~defined~\msg_line_context:. } { Existing~units~and~aliases~cannot~be~redefined~with~ '\token_to_str:N \spnewunit'. } \msg_new:nnnn { spintent } { spunitalias-duplicate } { The~alias~'#1'~is~already~defined~or~reserved~\msg_line_context:. } { Existing~units~and~aliases~cannot~be~redefined. } \msg_new:nnnn { spintent } { spunitalias-invalid-expr } { Invalid~expression~'#2'~for~alias~'#1'~\msg_line_context:. } { The~expression~must~contain~only~defined~units,~exponents~and~one~'/'. } % \end{macrocode} % \end{macro} % % \begin{macro}{\spnewunit} % The public command \ics*{spnewunit} defines a new custom unit. It passes % the new unit symbol `|#1|' and its speech text `|#2|' to Lua via % \myhlc|\@@_luafun_define_custom_unit:nn|. If the module reports that the % unit already exists, it throws the corresponding duplicate error. % \begin{macrocode} \NewDocumentCommand \spnewunit { m m } { \mode_if_math:TF { \msg_error:nnn { spintent } { need-text-mode } { \spnewunit } } { \@@_luafun_define_custom_unit:nn {#1} {#2} \str_if_eq:VnT \l_@@_spunit_luaset_status_str { duplicate } { \msg_error:nnn { spintent } { spnewunit-duplicate } {#1} } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spunitalias} % The public command \ics*{spunitalias} creates a shortcut for a complex % unit expression. It passes the new alias name `|#1|' and the target % expression `|#2|' to Lua via \myhlc|\@@_luafun_define_spunit_alias:nn|. % It throws an error if the alias already exists, or if the provided target % expression contains invalid syntax or unknown units. % \begin{macrocode} \NewDocumentCommand \spunitalias { m m } { \mode_if_math:TF { \msg_error:nnn { spintent } { need-text-mode } { \spunitalias } } { \@@_luafun_define_spunit_alias:nn {#1} {#2} \str_if_eq:VnT \l_@@_spunit_luaset_status_str { duplicate } { \msg_error:nnn { spintent } { spunitalias-duplicate } {#1} } \str_if_eq:VnT \l_@@_spunit_luaset_status_str { invalid-expr } { \msg_error:nnnn { spintent } { spunitalias-invalid-expr } {#1} {#2} } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spmoney}}\label{cmd:spmoney} % % The user command |\spmoney| is the \emph{third} of % \emph{\enquote{the big four}} provided by the \mypkg*[no-index]{spintent} package, % designed to format monetary values and currencies. It delegates % \mymarg{argument} parsing to the Lua module \myluamod{} via % \myhlc|\@@_luafun_clean_split_and_set:n|. The module then retrieves the % correct \emph{\enquote{currency symbol}}, pluralization rules, and layout % structures from the database. % % The command accepts \emph{optional arguments} to override the default % \emph{\enquote{currency symbol}} or enable \emph{\enquote{subunit}} rendering. % Finally, it constructs the intent for \mynvmath{MathCAT}. % % \begin{variable}[int]{ % \l_@@_spmoney_luaset_symbol_str, % \l_@@_spmoney_luaset_position_str, % \l_@@_spmoney_luaset_sing_str, % \l_@@_spmoney_luaset_plur_str, % \l_@@_spmoney_luaset_conde_str, % \l_@@_spmoney_luaset_subsing_str, % \l_@@_spmoney_luaset_subplur_str, % \l_@@_spmoney_luaset_print_iso_str, % \l_@@_spmoney_luaset_currency_arg_str, % \l_@@_spmoney_luaset_status_str % } % These string variables store the currency data returned by the Lua module % \myluamod{}. Upon a successful database lookup, the module populates these % variables with the \emph{\enquote{currency symbol}}, its layout position, % |pre| or |post|, the appropriate singular and plural names for both the % main unit and subunits, and the execution status flags. \TeX{} then uses % this information to render the visual layout and construct the correct % \mynvmath{MathML} speech intents. % \begin{macrocode} \str_new:N \l_@@_spmoney_luaset_symbol_str \str_new:N \l_@@_spmoney_luaset_position_str \str_new:N \l_@@_spmoney_luaset_sing_str \str_new:N \l_@@_spmoney_luaset_plur_str \str_new:N \l_@@_spmoney_luaset_conde_str \str_new:N \l_@@_spmoney_luaset_subsing_str \str_new:N \l_@@_spmoney_luaset_subplur_str \str_new:N \l_@@_spmoney_luaset_print_iso_str \str_new:N \l_@@_spmoney_luaset_currency_arg_str \str_new:N \l_@@_spmoney_luaset_status_str % \end{macrocode} % \end{variable} % % \begin{variable}[int]{ % \l_@@_spmoney_sub_units_bool, % \l_@@_spmoney_intent_money_str, % \l_@@_spmoney_intent_sign_str, % \l_@@_spmoney_intent_sub_unit_str, % \l_@@_spmoney_res_main_voice_str, % \l_@@_spmoney_subnum_int, % \l_@@_spmoney_extracted_curr_str, % \l_@@_spmoney_extracted_num_tl % } % The boolean variable tracks whether subunit rendering % is enabled. The string variables assemble the specific speech % intents for \mynvmath{NVDA/MathCAT}, including the sign, the main currency, % and the subunits. Finally, the remaining variables temporarily store % the numeric value and currency code extracted from the user's input. % \begin{macrocode} \bool_new:N \l_@@_spmoney_sub_units_bool \str_new:N \l_@@_spmoney_intent_money_str \str_new:N \l_@@_spmoney_intent_sign_str \str_new:N \l_@@_spmoney_intent_sub_unit_str \str_new:N \l_@@_spmoney_res_main_voice_str \int_new:N \l_@@_spmoney_subnum_int \str_new:N \l_@@_spmoney_extracted_curr_str \tl_new:N \l_@@_spmoney_extracted_num_tl % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spmoney}} % % \begin{macro}[int]{spmoney-no-subunits, spmoney-invalid-currency} % The \enquote{warning} |spmoney-no-subunits| is raised when subunit rendering % is requested for a currency that does not support fractional divisions, % such as JPY or CLP, ignoring the flag. The \enquote{error} % |spmoney-invalid-currency| is raised when the provided currency identifier % is not registered in the Lua database or is not written validly: an alias % or symbol with any uppercase letter, or an \texttt{ISO} code that is % neither all lowercase nor all uppercase. % \begin{macrocode} \msg_new:nnnn { spintent } { spmoney-no-subunits } { The~option~'sub-units=true'~is~ignored~for~the~currency~'#1'~\msg_line_context:. } { Currencies~like~'JPY'~or~'CLP'~have~no~subunits~in~the~Lua~module. } \msg_new:nnnn { spintent } { spmoney-invalid-currency } { Invalid~currency~'#1'~for~'\token_to_str:N \spmoney'~\msg_line_context:. } { The~symbol,~alias~or~ISO~code~does~not~exist~in~the~Lua~module. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spmoney}} % % \begin{macro}[int] % { % currency, % sub-units, % money-math, % cifras-sep, % unknown, % } % We define the \mymeta{keys} \mykey[spmoney]{currency}, \mykey[spmoney]{sub-units}, % \mykey[spmoney]{money-math} and \mykey[spmoney]{cifras-sep}. % \begin{macrocode} \keys_define:nn { spintent / spmoney } { currency .code:n = { \@@_spmoney_set_currency:n {#1} }, currency .value_required:n = true, sub-units .bool_set:N = \l_@@_spmoney_sub_units_bool, sub-units .initial:n = { false }, sub-units .value_required:n = true, money-math .bool_set:N = \l_@@_spmoney_money_math_bool, money-math .initial:n = { true }, money-math .value_required:n = true, cifras-sep .meta:nn = { spintent / spnum } { cifras-sep = {#1} }, cifras-sep .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spmoney } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spmoney}} % % \begin{macro}[int]{\@@_spmoney_set_currency:n, \@@_spmoney_normalize_key:n, \@@_spmoney_normalize_key:V} % The function \myhlc|\@@_spmoney_set_currency:n| validates the currency % identifier provided in `|#1|' against the database. It delegates the % lookup to the function \myhlc|\@@_spmoney_normalize_key:n|, which % calls the Lua module \myluamod{}. If the module returns a % |notfound| status, an \enquote{error} is triggered. Otherwise, it stores the valid % currency identifier in \myhlc|\l_@@_spmoney_luaset_currency_arg_str| for % subsequent processing. % \begin{macrocode} \cs_new_protected:Nn \@@_spmoney_set_currency:n { \@@_spmoney_normalize_key:n {#1} \str_if_eq:VnTF \l_@@_spmoney_luaset_status_str { notfound } { \msg_error:nnn { spintent } { spmoney-invalid-currency } {#1} } { \str_set:Nn \l_@@_spmoney_luaset_currency_arg_str {#1} } } \cs_new_protected:Nn \@@_spmoney_normalize_key:n { \@@_luafun_spmoney_normalize_key:n { \exp_not:n {#1} } } \cs_generate_variant:Nn \@@_spmoney_normalize_key:n { V } % \end{macrocode} % Since the key \mykey[spmoney]{currency} calls % \myhlc|\@@_spmoney_set_currency:n|, its default value is set once the % function exists. % \begin{macrocode} \keys_set:nn { spintent / spmoney } { currency = pesos } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmoney_validate_money:} % The function \myhlc|\@@_spmoney_validate_money:| verifies if the parsed % input contains an embedded currency identifier by checking % \myhlc|\l_@@_luaset_has_units_str|. If a currency is detected, it extracts % and normalizes the key from \myhlc|\l_@@_luaset_arg_above_tl|. It throws % an \enquote{error} if the currency is not found in the database. If valid, it % updates \myhlc|\l_@@_spmoney_luaset_currency_arg_str| and retrieves the % full currency metadata from the \myluamod{} via \myhlc|\@@_luafun_spmoney_lookup_data:V|. % \begin{macrocode} \cs_new_protected:Nn \@@_spmoney_validate_money: { \str_if_eq:VnT \l_@@_luaset_has_units_str { true } { \@@_spmoney_normalize_key:V \l_@@_luaset_arg_above_tl \str_if_eq:VnTF \l_@@_spmoney_luaset_status_str { notfound } { \msg_error:nne { spintent } { spmoney-invalid-currency } { \l_@@_luaset_arg_above_tl } } { \str_set:NV \l_@@_spmoney_luaset_currency_arg_str \l_@@_luaset_arg_above_tl } } \str_set:Ne \l_@@_spmoney_luaset_status_str { found } \@@_luafun_spmoney_lookup_data:V \l_@@_spmoney_luaset_currency_arg_str } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmoney_print_symbol:} % The function \myhlc|\@@_spmoney_print_symbol:| renders the \emph{\enquote{currency symbol}}. % If \myhlc|\l_@@_spmoney_luaset_print_iso_str| is |true|, it outputs the ISO % code using |\mathrm|. Otherwise, it prints the \emph{\enquote{standard symbol}}, % directly in math mode when \myhlc|\l_@@_spmoney_money_math_bool| is |true|, % or wrapped inside |\text| when it is |false|. For dollar signs `\$', the % symbol is typeset through the control sequence |\$| in both cases. % \begin{macrocode} \cs_new_protected:Nn \@@_spmoney_print_symbol: { \str_if_eq:VnTF \l_@@_spmoney_luaset_print_iso_str { true } { \mathrm { \l_@@_spmoney_luaset_currency_arg_str } } { \str_if_eq:VnTF \l_@@_spmoney_luaset_symbol_str { $ } { \bool_if:NTF \l_@@_spmoney_money_math_bool { \$ } { \text { \$ } } } { \bool_if:NTF \l_@@_spmoney_money_math_bool { \l_@@_spmoney_luaset_symbol_str } { \text { \l_@@_spmoney_luaset_symbol_str } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmoney_render_layout:} % The function \myhlc|\@@_spmoney_render_layout:| determines the layout % order of the \emph{\enquote{currency symbol}} relative to the numeric % value. It evaluates \myhlc|\l_@@_spmoney_luaset_position_str|; if set to |pre|, % it renders the \emph{\enquote{currency symbol}} \emph{before} the number % node. Otherwise, it places the number first, followed by the % \emph{\enquote{currency symbol}}. % \begin{macrocode} \cs_new_protected:Nn \@@_spmoney_render_layout: { \str_if_eq:VnTF \l_@@_spmoney_luaset_position_str { pre } { \@@_spmoney_print_symbol: \MathMLarg { n } { \@@_spmoney_render_number_node: } } { \MathMLarg { n } { \@@_spmoney_render_number_node: } \@@_spmoney_print_symbol: } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmoney_render_number_node:, \@@_spmoney_render_only_integer_node:} % The function \myhlc|\@@_spmoney_render_number_node:| renders the full % numeric value, encompassing both the integer and decimal components. % Conversely, the function \myhlc|\@@_spmoney_render_only_integer_node:| % outputs exclusively the integer portion, omitting any decimal data. % \begin{macrocode} \cs_new_protected:Nn \@@_spmoney_render_number_node: { \@@_spnum_render_int: \@@_spnum_print_part_dec: } \cs_new_protected:Nn \@@_spmoney_render_only_integer_node: { \@@_spnum_render_int: } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmoney_mathml:} % The function \myhlc|\@@_spmoney_mathml:| evaluates the presence of an % explicit sign in \myhlc|\l_@@_luaset_sign_tl|. If a sign is detected, it % assigns a prefix speech intent `|más|' or `|menos|' and embeds the sign % and subsequent layout inside a |\MathMLintent|. If no sign is present, % it routes execution based on whether subunits are enabled. % \begin{macrocode} \cs_new_protected:Nn \@@_spmoney_mathml: { \tl_if_empty:NTF \l_@@_luaset_sign_tl { \bool_if:NTF \l_@@_spmoney_sub_units_bool { \@@_spmoney_set_and_print_sub_units: } { \@@_spmoney_intent_int: } } { \str_if_eq:VnTF \l_@@_luaset_sign_tl { + } { \str_set:Nn \l_@@_spmoney_intent_sign_str { _más:prefix ($n) } } { \str_set:Nn \l_@@_spmoney_intent_sign_str { _menos:prefix ($n) } } \@@_invisible_sep: \MathMLintent { \l_@@_spmoney_intent_sign_str } { \l_@@_luaset_sign_tl \MathMLarg { n } { \bool_if:NTF \l_@@_spmoney_sub_units_bool { \@@_spmoney_set_and_print_sub_units: } { \@@_spmoney_intent_int: } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmoney_intent_int:} % The function \myhlc|\@@_spmoney_intent_int:| determines the correct % grammatical inflection for the currency string: singular, plural, or the % prepositional `|de|' variant for exact millions. It evaluates the integer % component, decimal boundaries, and zero-fills to select the appropriate % string register, applying it as a postfix intent over % the layout rendered by \myhlc|\@@_spmoney_render_layout:|. % \begin{macrocode} \cs_new_protected:Nn \@@_spmoney_intent_int: { \tl_if_empty:NTF \l_@@_luaset_dec_sep_tl { \str_if_eq:VnTF \l_@@_luaset_only_part_int_str { 1 } { \str_set:NV \l_@@_spmoney_intent_money_str \l_@@_spmoney_luaset_sing_str } { \str_set:NV \l_@@_spmoney_intent_money_str \l_@@_spmoney_luaset_plur_str } \str_if_eq:VnT \l_@@_luaset_millons_str { true } { \str_set:NV \l_@@_spmoney_intent_money_str \l_@@_spmoney_luaset_conde_str } } { \str_set:NV \l_@@_spmoney_intent_money_str \l_@@_spmoney_luaset_plur_str \str_if_eq:VnT \l_@@_luaset_millons_str { true } { \str_if_eq:VnTF \l_@@_luaset_dec_is_all_zeros_str { true } { \str_set:NV \l_@@_spmoney_intent_money_str \l_@@_spmoney_luaset_conde_str } { \str_set:NV \l_@@_spmoney_intent_money_str \l_@@_spmoney_luaset_plur_str } } } \@@_invisible_sep: \MathMLintent { _ \l_@@_spmoney_intent_money_str :postfix($n) } { \@@_spmoney_render_layout: } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmoney_check_sub_units:, \@@_spmoney_set_and_print_sub_units:} % The function \myhlc|\@@_spmoney_check_sub_units:| verifies whether the % selected currency supports \emph{\enquote{subunits}}. If the database returns empty % subunit data, it triggers a warning and disables the % \emph{\enquote{subunit}} rendering flag. The flag is also disabled, % without a warning, when the amount has no decimal part, since there % are no \emph{\enquote{subunits}} to voice. % % The function \myhlc|\@@_spmoney_set_and_print_sub_units:| processes % and formats the speech intents when \emph{\enquote{subunits}} are active. It normalizes % the decimal component, padding a single-digit decimal % like `.5` to `50`, evaluates the correct singular or plural inflections % for both the main \emph{\enquote{currency}} and the \emph{\enquote{subunits}}, % and delegates the final \mynvmath{MathML} tree assembly to \myhlc|\@@_spmoney_build_tree:|. % \begin{macrocode} \cs_new_protected:Nn \@@_spmoney_check_sub_units: { \str_if_empty:NT \l_@@_spmoney_luaset_subsing_str { \msg_warning:nne { spintent } { spmoney-no-subunits } { \l_@@_spmoney_luaset_currency_arg_str } \bool_set_false:N \l_@@_spmoney_sub_units_bool } \tl_if_empty:NT \l_@@_luaset_dec_sep_tl { \bool_set_false:N \l_@@_spmoney_sub_units_bool } } \cs_new_protected:Nn \@@_spmoney_set_and_print_sub_units: { \int_compare:nNnTF { \str_count:N \l_@@_luaset_only_part_dec_str } = { 1 } { \int_set:Nn \l_@@_spmoney_subnum_int { \l_@@_luaset_only_part_dec_str * 10 } \str_set:Ne \l_@@_luaset_only_part_dec_str { \l_@@_luaset_only_part_dec_str 0 } } { \int_set:Nn \l_@@_spmoney_subnum_int { \l_@@_luaset_only_part_dec_str } } \int_compare:nNnTF { \l_@@_spmoney_subnum_int } = { 1 } { \str_set:Ne \l_@@_spmoney_intent_sub_unit_str { _ \l_@@_spmoney_luaset_subsing_str :postfix($sub) } } { \str_set:Ne \l_@@_spmoney_intent_sub_unit_str { _ \l_@@_spmoney_luaset_subplur_str :postfix($sub) } } \str_if_eq:VnTF \l_@@_luaset_only_part_int_str { 1 } { \str_set:NV \l_@@_spmoney_res_main_voice_str \l_@@_spmoney_luaset_sing_str } { \str_set:NV \l_@@_spmoney_res_main_voice_str \l_@@_spmoney_luaset_plur_str } \str_if_eq:VnT \l_@@_luaset_millons_str { true } { \str_set:NV \l_@@_spmoney_res_main_voice_str \l_@@_spmoney_luaset_conde_str } \@@_spmoney_build_tree: } % \end{macrocode} % \end{macro} % % \begin{macro}[int] % { % \@@_spmoney_build_tree:, % \@@_spmoney_print_sep_con:, % \@@_spmoney_print_dec_sub_unit:, % } % The function \myhlc|\@@_spmoney_build_tree:| structures the % final \mynvmath{MathML} tree assembly for fractional \emph{\enquote{currencies}}. It arranges the % integer component, the decimal separator, and the \emph{\enquote{subunit}} component % based on the layout position, |pre| or |post|, of the \emph{\enquote{currency symbol}}. % % \myhlc|\@@_spmoney_print_sep_con:| also applies the specific % speech intent `|_con|' to the decimal separator, while % \myhlc|\@@_spmoney_print_dec_sub_unit:| binds the computed \emph{\enquote{subunit}} % intent to the decimal digits. % \begin{macrocode} \cs_new_protected:Nn \@@_spmoney_build_tree: { \str_if_eq:VnTF \l_@@_spmoney_luaset_position_str { pre } { \MathMLintent { _ \l_@@_spmoney_res_main_voice_str :postfix($n) } { \@@_spmoney_print_symbol: \MathMLarg { n } { \@@_spmoney_render_only_integer_node: } } \@@_spmoney_print_sep_con: \@@_spmoney_print_dec_sub_unit: } { \MathMLintent { _ \l_@@_spmoney_res_main_voice_str :postfix($n) } { \@@_inner_sep: \MathMLarg { n } { \@@_spmoney_render_only_integer_node: } } \@@_spmoney_print_sep_con: \@@_spmoney_print_dec_sub_unit: \MathMLintent { :silent } { \@@_spmoney_print_symbol: } } } \cs_new_protected:Nn \@@_spmoney_print_sep_con: { \MathMLintent { _con } { \mathord { \l_@@_luaset_dec_sep_tl } } } \cs_new_protected:Nn \@@_spmoney_print_dec_sub_unit: { \MathMLintent { \l_@@_spmoney_intent_sub_unit_str } { \@@_inner_sep: \MathMLarg { sub } { \@@_spnum_render_dec: } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmoney_parse_and_route:n} % The internal function \myhlc|\@@_spmoney_parse_and_route:n| processes % the \mymarg{argument} passed in `|#1|` using Lua module to extract % the numeric value and any \emph{\enquote{currency}} identifier. If a % \emph{\enquote{currency}} is found in \myhlc|\l_@@_spmoney_extracted_curr_str|, % it sets the corresponding variables. It then calls \myhlc|\@@_spmoney_validate_money:| % to verify the \emph{\enquote{currency}}. If \emph{\enquote{subunit}} % rendering is enabled in \myhlc|\l_@@_spmoney_sub_units_bool|, % it calls \myhlc|\@@_spmoney_check_sub_units:|. Finally, it calls % \myhlc|\@@_spmoney_mathml:| to format the output. % \begin{macrocode} \cs_new_protected:Nn \@@_spmoney_parse_and_route:n { \@@_luafun_spmoney_prepare_input:n { \exp_not:n {#1} } \@@_luafun_clean_split_and_set:n { \exp_not:V \l_@@_spmoney_extracted_num_tl } \str_if_empty:NF \l_@@_spmoney_extracted_curr_str { \str_set:Ne \l_@@_luaset_has_units_str { true } \str_set:NV \l_@@_luaset_arg_above_tl \l_@@_spmoney_extracted_curr_str } \@@_spmoney_validate_money: \bool_if:NT \l_@@_spmoney_sub_units_bool { \@@_spmoney_check_sub_units: } \@@_spmoney_mathml: } % \end{macrocode} % \end{macro} % % \begin{macro}{\spmoney} % The public document command \ics*{spmoney} formats monetary values % with accessible speech intents. It accepts an optional argument `|#1|' % for local key-value configurations and a mandatory argument `|#2|' % containing the numeric value and optional embedded currency identifier. % It first evaluates the typesetting context, triggering a fatal error if % executed outside of math mode. Upon validation, it establishes a local % \TeX{} group to isolate the key assignments, delegates the raw % input string to \myhlc|\@@_spmoney_parse_and_route:n| for parsing and % rendering, and subsequently closes the group to prevent scope leakage. % \begin{macrocode} \NewDocumentCommand \spmoney { O{} m } { \mode_if_math:TF { \group_begin: \keys_set:nn { spintent / spmoney } {#1} \@@_spmoney_parse_and_route:n {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spmoney } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spshort}}\label{cmd:spshort} % % The user command |\spshort| is the \emph{fourth} and \emph{final} of % \emph{\enquote{the big four}}. It manages the semantic injection and % typographical formatting of \emph{text mode} abbreviations, ordinals, and % acronyms. Unlike \emph{math mode} commands, which rely on \mynvmath{MathML} % attributes, this command uses the \texttt{PDF} \emph{tagging tools} % provided by the \mypkg{tagpdf}\cite{tagpdf} package. Specifically, it uses % the expanded key \mykey{E} to ensure that \mynvmath{NVDA} interpret the full % form of the abbreviation, ordinal, or acronym in \emph{text mode}. % % \begin{variable}[int]{ % \l_@@_spshort_luaset_status_str, % \l_@@_spshort_luaset_layout_str, % \l_@@_spshort_luaset_expand_str, % \l_@@_spshort_luaset_output_str, % \l_@@_spshort_luaset_base_str, % \l_@@_spshort_luaset_suffix_str % } % These internal string variables serve as the primary data interface with % the Lua module \myluamod{}. During processing, the Lua module % \emph{\enquote{stores}} within these registers the execution status, the % targeted typographical layout parameters, the phonetic replacement text % for the \mykey{E} key intended for \mynvmath{NVDA}, the final % sanitized output string, and the isolated structural components for % ordinals, base and suffix. % \begin{macrocode} \str_new:N \l_@@_spshort_luaset_status_str \str_new:N \l_@@_spshort_luaset_layout_str \str_new:N \l_@@_spshort_luaset_expand_str \str_new:N \l_@@_spshort_luaset_output_str \str_new:N \l_@@_spshort_luaset_base_str \str_new:N \l_@@_spshort_luaset_suffix_str % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spshort}} % % \begin{macro}[int]{spshort-unknown-abbreviation} % The \enquote{error} |spshort-unknown-abbreviation| is raised when the % command |\spshort| encounters an invalid lookup state from the Lua module. % It acts as a strict fallback exception when the provided \mymarg{argument} % cannot be mapped to any registered database entry in \myluamod{} nor % successfully resolved through the grammatical ordinal expansion rules. % \begin{macrocode} \msg_new:nnnn { spintent } { spshort-unknown-abbreviation } { The~abbreviation~or~ordinal~'#1'~is~invalid~or~not~recognized~\msg_line_context:. } { Check~the~spelling~or~the~grammar~rules~for~ordinals. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spshort}} % % \begin{macro}[int]{small-caps, read-arg, unknown} % We define the \mymeta{keys} \mykey[spshort]{small-caps} and \mykey[spshort]{read-arg}. % \begin{macrocode} \keys_define:nn { spintent / spshort } { small-caps .bool_set:N = \l_@@_spshort_smallcaps_bool, small-caps .initial:n = false, small-caps .value_required:n = true, read-arg .str_set:N = \l_@@_spshort_read_arg_str, read-arg .initial:n = {}, read-arg .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spshort } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spshort}} % % \begin{macro}[int]{\@@_spshort_tag_span:nn, \@@_spshort_tag_span:en} % The internal function \myhlc|\@@_spshort_tag_span:nn| processes the % \mymarg{argument} passed in `|#2|' using the tools provided by \mypkg{tagpdf}. % By utilizing the |tag| and \mykey{E} keys, it creates a % native \texttt{PDF} |/E (#1)| structure and expands the \mymarg{argument} % passed in `|#1|' for proper reading with \mynvmath{NVDA}. % \begin{macrocode} \cs_new_protected:Nn \@@_spshort_tag_span:nn { \tag_mc_end_push: \tag_struct_begin:n { tag=Span, E={#1} } \tag_mc_begin:n { tag=Span } \tag_suspend:n { \spshort } #2 \tag_resume:n { \spshort } \tag_mc_end: \tag_struct_end: \tag_mc_begin_pop:n {} } \cs_generate_variant:Nn \@@_spshort_tag_span:nn { en } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spshort_print:nn} % The internal function \myhlc|\@@_spshort_print:nn| acts as the primary % formatter for text abbreviations. It clears the string variable % \myhlc|\l_@@_spshort_read_arg_str| and evaluates the % optional \mymeta{keys} passed in `|#1|'. It then checks if a manual % reading override was provided via those keys. If that string variable % remains empty, it calls \myhlc|\@@_spshort_exec:n| to % process the \mymarg{argument} passed in `|#2|'. Otherwise, if an % override is detected, it delegates the execution to % \myhlc|\@@_spshort_render_bypass:n|. % \begin{macrocode} \cs_new_protected:Nn \@@_spshort_print:nn { \str_clear:N \l_@@_spshort_read_arg_str \keys_set:nn { spintent / spshort } {#1} \str_if_empty:NTF \l_@@_spshort_read_arg_str { \@@_spshort_exec:n {#2} } { \@@_spshort_render_bypass:n {#2} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spshort_exec:n} % The internal function \myhlc|\@@_spshort_exec:n| interfaces % with the Lua module \myluamod{} function \myhlc|\@@_luafun_spshort_process:n| % to resolve the abbreviation or ordinal \mymarg{argument} passed in `|#1|'. % % It subsequently evaluates the execution state stored within the string % variable \myhlc|\l_@@_spshort_luaset_status_str| and dynamically routes % execution to the corresponding rendering function |success|, % |fallback| or |error|. Any unrecognized status defaults % to the \enquote{error} case. % \begin{macrocode} \cs_new_protected:Nn \@@_spshort_exec:n { \@@_luafun_spshort_process:n {#1} \str_case:VnF \l_@@_spshort_luaset_status_str { { success } { \@@_spshort_render_success: } { fallback } { \@@_spshort_render_fallback: } { error } { \@@_spshort_render_error:n {#1} } } { \@@_spshort_render_error:n {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spshort_render_success:, \@@_spshort_render_fallback:, % \@@_spshort_render_bypass:n, \@@_spshort_render_error:n} % The function \myhlc|\@@_spshort_render_success:| injects the key % \mykey{E} and executes the targeted typographical layout. % The \myhlc|\@@_spshort_render_fallback:| function handles default cases, % applying optional formatting such as \textsc{small caps} to unrecognized % or partially resolved terms. % % Conversely, the function \myhlc|\@@_spshort_render_bypass:n| maps % the user-defined \emph{\enquote{speech}} to the \mymarg{argument}, % bypassing the automated lookup Lua module. Finally, the function % \myhlc|\@@_spshort_render_error:n| triggers a structural exception while % yielding the unformatted \mymarg{argument} to prevent fatal compilation. % \begin{macrocode} \cs_new_protected:Nn \@@_spshort_render_success: { \@@_spshort_tag_span:en { \l_@@_spshort_luaset_expand_str } { \@@_spshort_execute_layout: } } \cs_new_protected:Nn \@@_spshort_render_fallback: { \@@_spshort_tag_span:en { \l_@@_spshort_luaset_expand_str } { \bool_if:NTF \l_@@_spshort_smallcaps_bool { \textsc{ \l_@@_spshort_luaset_output_str } } { \l_@@_spshort_luaset_output_str } } } \cs_new_protected:Nn \@@_spshort_render_bypass:n { \@@_spshort_tag_span:en { \l_@@_spshort_read_arg_str } {#1} } \cs_new_protected:Nn \@@_spshort_render_error:n { \msg_error:nnn { spintent } { spshort-unknown-abbreviation } {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spshort_execute_layout:} % The internal function \myhlc|\@@_spshort_execute_layout:| executes the % typographical layout. It evaluates the descriptor \emph{\enquote{stored}} % within the string variable \myhlc|\l_@@_spshort_luaset_layout_str|, which holds a value % such as |linear_regular|, |superscript|, or |small_caps_pure|, and % maps it to the corresponding \LaTeX{} formatting. It also % manages complex nested configurations, dynamically applying % combinations such as superscripts integrated with % conditional \textsc{small caps} based on the active boolean variables. % \begin{macrocode} \cs_new_protected:Nn \@@_spshort_execute_layout: { \str_case:VnF \l_@@_spshort_luaset_layout_str { { linear_regular } { \bool_if:NTF \l_@@_spshort_smallcaps_bool { \textsc{ \l_@@_spshort_luaset_output_str } } { \l_@@_spshort_luaset_output_str } } { linear_caps } { \l_@@_spshort_luaset_output_str } { linear_mixed } { \l_@@_spshort_luaset_output_str } { superscript } { \l_@@_spshort_luaset_base_str \bool_if:NTF \l_@@_spshort_smallcaps_bool { \textsuperscript{ \textsc{ \str_lowercase:V \l_@@_spshort_luaset_suffix_str } } } { \textsuperscript{ \l_@@_spshort_luaset_suffix_str } } } { small_caps_pure } { \bool_if:NTF \l_@@_spshort_smallcaps_bool { \textsc{ \str_lowercase:V \l_@@_spshort_luaset_output_str } } { \l_@@_spshort_luaset_output_str } } { acronym_long } { \l_@@_spshort_luaset_output_str } } { \l_@@_spshort_luaset_output_str } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spshort} % The public document command \ics*{spshort} manages the accessible % formatting of \emph{text mode} abbreviations, acronyms, and ordinals. % It first triggers an error if invoked within a \emph{math mode}, % upon validation, puts \myhlc|\mode_leave_vertical:| and establishes a local % group to call the function \myhlc|\@@_spshort_print:nn| for processing % and printing. % \begin{macrocode} \NewDocumentCommand \spshort { O{} m } { \mode_if_math:TF { \msg_error:nnn { spintent } { need-text-mode } { \spshort } } { \mode_leave_vertical: \group_begin: \@@_spshort_print:nn {#1} {#2} \group_end: } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spsiglo}}\label{cmd:spsiglo} % % The public document command |\spsiglo| provides a semantic interface % for typesetting century designations such as siglo XXI. It accepts both % Arabic and Roman numerals as \mymarg{argument}, interfacing with the Lua % module \myluamod{} to validate, parse, and normalize chronological values. % % This command work in dual-mode context awareness, operating uniformly across % both \emph{text mode} and \emph{math mode}. When executed within \emph{math % mode}, it constructs an accessible \mynvmath{MathML} \texttt{intent} % expression tree. Conversely, when invoked in standard \emph{text mode}, % it encapsulates the rendered output within a native \texttt{PDF} tagging % structure, such as |/Alt (#1)|, containing an |alt| key. % % \begin{variable}[int]{ % \l_@@_spsiglo_luaset_error_str, % \l_@@_spsiglo_luaset_arabic_str, % \l_@@_spsiglo_luaset_roman_min_str, % \l_@@_spsiglo_print_era_str % } % These internal string variables handle data transfer and synchronization % with the Lua module \myluamod{}. They isolate the exception evaluation % states, the Arabic numeral required for speech synthesis, the lowercase % Roman numeral designated for small caps typesetting via |\textsc|, and % the localized chronological era modifier. % \begin{macrocode} \str_new:N \l_@@_spsiglo_luaset_error_str \str_new:N \l_@@_spsiglo_luaset_arabic_str \str_new:N \l_@@_spsiglo_luaset_roman_min_str \str_new:N \l_@@_spsiglo_print_era_str % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spsiglo}} % % \begin{macro}[int]{spsiglo-invalid-arg} % The \enquote{error} |spsiglo-invalid-arg| is raised when the % Lua module \myluamod{} cannot map the \mymarg{argument} to an % integer within the valid range of |1| to |4000|, preventing incorrect % speech synthesis. % \begin{macrocode} \msg_new:nnnn { spintent } { spsiglo-invalid-arg } { Invalid~argument~'#2'~for~'\tl_trim_spaces:n {#1}'~\msg_line_context:. } { The~argument~must~be~an~Arabic~or~Roman~numeral~between~'1'~and~'4000'. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spsiglo}} % % \begin{macro}[int]{print-era, unknown} % We define the \mymeta{key} \mykey[spsiglo]{print-era}. % \begin{macrocode} \keys_define:nn { spintent / spsiglo } { print-era .choices:nn = { none , ac , dc } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spsiglo_print_era_str { none } } {2} { \str_set:Nn \l_@@_spsiglo_print_era_str { ac } } {3} { \str_set:Nn \l_@@_spsiglo_print_era_str { dc } } } }, print-era .initial:n = none, print-era .value_required:n = true, print-era / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { print-era } { none, ~ac, ~dc } { \exp_not:n {#1} }, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spsiglo } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spsiglo}} % % \begin{macro}[int]{\@@_spsiglo_render_simple:nn, \@@_spsiglo_render_simple:VV, % \@@_spsiglo_render_with_era:nnn, \@@_spsiglo_render_with_era:VVV} % The functions \myhlc|\@@_spsiglo_render_simple:nn| and % \myhlc|\@@_spsiglo_render_with_era:nnn| generate \mynvmath{MathML} structures % when century designations are evaluated within \emph{math mode}. They % construct \mynvmath{MathML} \texttt{intent} structures, mapping the Arabic % numerals to their phonetic equivalents. % \myhlc|\@@_spsiglo_render_with_era:nnn| also integrates the era modifiers % |ac| or |dc|. It adds invisible spacing and postfix % intents to ensure correct \mynvmath{NVDA/MathCAT} pronunciation. % \begin{macrocode} \cs_new_protected:Nn \@@_spsiglo_render_simple:nn { \MathMLintent { #1:number } { \text { \textsc {#2} } } } \cs_generate_variant:Nn \@@_spsiglo_render_simple:nn { VV } \cs_new_protected:Nn \@@_spsiglo_render_with_era:nnn { \str_if_eq:nnTF {#1} { ac } { \MathMLintent { _antes-de-cristo:postfix($z) } { \@@_inner_sep: \MathMLarg { z } { \MathMLintent { #2:number } { \text { \textsc {#3}~a.~C. } } } } } { \MathMLintent { _después-de-cristo:postfix($z) } { \@@_inner_sep: \MathMLarg { z } { \MathMLintent { #2:number } { \text { \textsc {#3}~d.~C. } } } } } } \cs_generate_variant:Nn \@@_spsiglo_render_with_era:nnn { VVV } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spsiglo_tag_span:nn, \@@_spsiglo_tag_span:en} % The function \myhlc|\@@_spsiglo_tag_span:nn| handles century formatting % within \emph{text mode}. It utilizes the \mypkg{tagpdf} package to % enclose the content inside a \texttt{PDF} |/Alt (#1)| structure. By passing % the \mymarg{argument} to the |alt| key, it embeds the % chronological value into the \texttt{PDF}, so that % \mynvmath{NVDA/MathCAT} pronounce it correctly without relying on intents. % \begin{macrocode} \cs_new_protected:Nn \@@_spsiglo_tag_span:nn { \tag_mc_end_push: \tag_struct_begin:n { tag=Span, alt={#1} } \tag_mc_begin:n { tag=Span } \tag_suspend:n { \spsiglo } #2 \tag_resume:n { \spsiglo } \tag_mc_end: \tag_struct_end: \tag_mc_begin_pop:n {} } \cs_generate_variant:Nn \@@_spsiglo_tag_span:nn { en } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spsiglo_exec:n, \@@_spsiglo_mathml:, % \@@_spsiglo_text:} % The function \myhlc|\@@_spsiglo_exec:n| formats century designations % based on the active mode. It first checks the \enquote{error} status in % the variable \myhlc|\l_@@_spsiglo_luaset_error_str| returned by the Lua % module \myluamod{} and issues an \enquote{error} if validation fails. % % If the \mymarg{argument} is valid, in \emph{math mode} it calls % \myhlc|\@@_spsiglo_mathml:|, which applies the \mynvmath{MathML} % structures depending on whether an era modifier is present. In % \emph{text mode} it leaves vertical mode first and calls % \myhlc|\@@_spsiglo_text:|, which utilizes the |tag| and |alt| keys to % expand the value of the \mykey[spsiglo]{print-era} option into full text % such as \enquote{antes de cristo} for \mynvmath{NVDA/MathCAT}. % \begin{macrocode} \cs_new_protected:Nn \@@_spsiglo_exec:n { \mode_if_math:F { \mode_leave_vertical: } \str_if_eq:VnTF \l_@@_spsiglo_luaset_error_str { true } { \msg_error:nnnn { spintent } { spsiglo-invalid-arg } { \spsiglo } {#1} } { \mode_if_math:TF { \@@_spsiglo_mathml: } { \@@_spsiglo_text: } } } \cs_new_protected:Nn \@@_spsiglo_mathml: { \str_if_eq:VnTF \l_@@_spsiglo_print_era_str { none } { \@@_spsiglo_render_simple:VV \l_@@_spsiglo_luaset_arabic_str \l_@@_spsiglo_luaset_roman_min_str } { \@@_spsiglo_render_with_era:VVV \l_@@_spsiglo_print_era_str \l_@@_spsiglo_luaset_arabic_str \l_@@_spsiglo_luaset_roman_min_str } } \cs_new_protected:Nn \@@_spsiglo_text: { \str_case:Vn \l_@@_spsiglo_print_era_str { { none } { \@@_spsiglo_tag_span:en { \l_@@_spsiglo_luaset_arabic_str } { \textsc { \l_@@_spsiglo_luaset_roman_min_str } } } { ac } { \@@_spsiglo_tag_span:en { \l_@@_spsiglo_luaset_arabic_str ~ antes ~ de ~ cristo } { \textsc { \l_@@_spsiglo_luaset_roman_min_str } ~ a.~C. } } { dc } { \@@_spsiglo_tag_span:en { \l_@@_spsiglo_luaset_arabic_str ~ después ~ de ~ cristo } { \textsc { \l_@@_spsiglo_luaset_roman_min_str } ~ d.~C. } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spsiglo} % The public command \ics*{spsiglo} is the main interface for century % typesetting. It opens a local group and evaluates any optional % \mymeta{keys} passed in `|#1|', then executes the function % \myhlc|\@@_luafun_spsiglo_parse:n| defined in the Lua module \myluamod{} % passed in `|#2|', and delegates the output for \emph{math mode} or % \emph{text mode} to \myhlc|\@@_spsiglo_exec:n|. % \begin{macrocode} \NewDocumentCommand \spsiglo { O{} m } { \group_begin: \keys_set:nn { spintent / spsiglo } {#1} \@@_luafun_spsiglo_parse:n {#2} \@@_spsiglo_exec:n {#2} \group_end: } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{sptime}}\label{cmd:sptime} % % The command |\sptime| is the main public interface for typesetting and % tagging time expressions. It parses time string formats, such as |HH:MM| % or |HH:MM:SS|, using the Lua function \myhlc|\@@_luafun_sptime_parse:n|. % This separates the hours, minutes, seconds, and fractions to construct % a \mynvmath{MathML} |intent| tree for \mynvmath{NVDA/MathCAT}. % % \begin{variable}[int]{ % \l_@@_sptime_luaset_seconds_str, % \l_@@_sptime_luaset_has_seconds_str, % \l_@@_sptime_luaset_error_str, % \l_@@_sptime_luaset_is_fraction_str, % \l_@@_sptime_luaset_base_str % } % These internal string variables store the values returned from the Lua % module \myluamod{}. They hold the extracted time units, error flags, and % boolean indicators, such as the presence of seconds or decimal fractions, % used during formatting. % \begin{macrocode} \str_new:N \l_@@_sptime_luaset_seconds_str \str_new:N \l_@@_sptime_luaset_has_seconds_str \str_new:N \l_@@_sptime_luaset_error_str \str_new:N \l_@@_sptime_luaset_is_fraction_str \str_new:N \l_@@_sptime_luaset_base_str % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{sptime}} % % \begin{macro}[int]{sptime-invalid-arg} % The \enquote{error} |sptime-invalid-arg| is raised when an argument % fails the format checks or contains values outside normal ranges, such as % hours exceeding 23 or minutes and seconds exceeding 59. % \begin{macrocode} \msg_new:nnnn { spintent } { sptime-invalid-arg } { Invalid~argument~'#2'~for~'\tl_trim_spaces:n {#1}'~\msg_line_context:. } { Allowed~formats:~'HH:MM',~'HH:MM:SS'~or~'HH:MM:SS.ss'.~ Hours~must~be~'0-23'~and~minutes/seconds~'0-59'. } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{sptime}} % % \begin{macro}[int]{\@@_sptime_build_seconds:nn} % The internal function \myhlc|\@@_sptime_build_seconds:nn| builds the \mynvmath{MathML} % structure for the seconds component of a time expression. It wraps the % numeric value `|#2|' in a speech intent, applying a connector prefix, % passed in `|#1|', and the "segundos" postfix so it is read correctly by % \mynvmath{NVDA/MathCAT}. % \begin{macrocode} \cs_new_protected:Nn \@@_sptime_build_seconds:nn { \MathMLintent { #1:prefix($z) } { \text { : } \MathMLarg { z } { \MathMLintent { _segundos:postfix($m) } { \@@_inner_sep: \MathMLarg { m } { \text {#2} } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_sptime_mathml:} % The internal function \myhlc|\@@_sptime_mathml:| builds the complete % \mynvmath{MathML} structure for the time expression. It checks the string variables % \myhlc|\l_@@_sptime_luaset_has_seconds_str| and % \myhlc|\l_@@_sptime_luaset_is_fraction_str| to determine if the time includes % seconds or decimal fractions. Based on these values, it inserts the base % time string and either calls \myhlc|\@@_sptime_build_seconds:nn| with the % appropriate prefix, 'con' or 'minutos-con', or appends the 'minutos' % intent if no seconds are present. % \begin{macrocode} \cs_new_protected:Nn \@@_sptime_mathml: { \str_if_eq:VnTF \l_@@_sptime_luaset_has_seconds_str { true } { \MathMLintent { :time } { \text { \l_@@_sptime_luaset_base_str } } \str_if_eq:VnTF \l_@@_sptime_luaset_is_fraction_str { true } { \@@_sptime_build_seconds:nn { con } { \l_@@_sptime_luaset_seconds_str } } { \@@_sptime_build_seconds:nn { _minutos-con } { \l_@@_sptime_luaset_seconds_str } } } { \MathMLintent { :time } { \text { \l_@@_sptime_luaset_base_str } } \str_if_eq:VnT \l_@@_sptime_luaset_is_fraction_str { false } { \MathMLintent { _minutos } { \c_@@_invisible_sep_int } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\sptime} % The command \ics*{sptime} is the public interface for formatting time % expressions. It first checks if it is being used in math mode, issuing % an error if not. Inside a local group, it parses the input using % \myhlc|\@@_luafun_sptime_parse:n| and checks the error status in % \myhlc|\l_@@_sptime_luaset_error_str|. If an invalid format is detected, % it issues the |sptime-invalid-arg| error; otherwise, it calls % \myhlc|\@@_sptime_mathml:| to assemble the \mynvmath{MathML} output. % \begin{macrocode} \NewDocumentCommand \sptime { m } { \mode_if_math:TF { \group_begin: \@@_luafun_sptime_parse:n {#1} \str_if_eq:VnTF \l_@@_sptime_luaset_error_str { true } { \msg_error:nnnn { spintent } { sptime-invalid-arg } { \sptime } {#1} } { \@@_sptime_mathml: } \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \sptime } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spdate}}\label{cmd:spdate} % % The command |\spdate| is the main public command for typesetting and % tagging date expressions. It parses date string formats, such as |DD/MM/YYYY| % or |YYYY-MM-DD|, using the Lua function \myhlc|\@@_luafun_spdate_parse:n|. % This verifies the input and constructs a \mynvmath{MathML} |intent| tree for % \mynvmath{NVDA/MathCAT}. % % \begin{variable}[int]{ % \l_@@_spdate_luaset_output_str, % \l_@@_spdate_luaset_error_str % } % These internal string variables store the values returned from the Lua % module \myluamod{}. They hold the formatted date string and the error % status used during formatting. % \begin{macrocode} \str_new:N \l_@@_spdate_luaset_output_str \str_new:N \l_@@_spdate_luaset_error_str % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spdate}} % % \begin{macro}[int]{spdate-invalid-arg} % The \enquote{error} |spdate-invalid-arg| is raised when an argument % fails the format checks or represents an invalid calendar date, such as % |31/02/2024| or using an unsupported pattern like |YYYY/MM/DD|. % \begin{macrocode} \msg_new:nnnn { spintent } { spdate-invalid-arg } { Invalid~argument~'#2'~for~'\tl_trim_spaces:n {#1}'~\msg_line_context:. } { Use~'DD/MM/YYYY',~'DD-MM-YYYY'~or~'YYYY-MM-DD',~with~a~valid~day~ and~month. } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spdate}} % % \begin{macro}[int]{\@@_spdate_exec:n} % The internal function \myhlc|\@@_spdate_exec:n| parses the input using % \myhlc|\@@_luafun_spdate_parse:n| and checks the error status in % \myhlc|\l_@@_spdate_luaset_error_str|. If an invalid format is detected, % it issues the |spdate-invalid-arg| error. Otherwise, it calls % \myhlc|\@@_spdate_mathml:|. % \begin{macrocode} \cs_new_protected:Nn \@@_spdate_exec:n { \@@_luafun_spdate_parse:n {#1} \str_if_eq:VnTF \l_@@_spdate_luaset_error_str { true } { \msg_error:nnnn { spintent } { spdate-invalid-arg } { \spdate } {#1} } { \@@_spdate_mathml: } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spdate_mathml:} % The internal function \myhlc|\@@_spdate_mathml:| builds the % \mynvmath{MathML} |intent| structure using the formatted date stored in % \myhlc|\l_@@_spdate_luaset_output_str|. % \begin{macrocode} \cs_new_protected:Nn \@@_spdate_mathml: { \MathMLintent { :date } { \text { \l_@@_spdate_luaset_output_str } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spdate} % The command \ics*{spdate} is the public interface for formatting date % expressions. It first checks if it is being used in math mode, issuing % an error if not. Inside a local group, it calls \myhlc|\@@_spdate_exec:n| % to process the input. % \begin{macrocode} \NewDocumentCommand \spdate { m } { \mode_if_math:TF { \group_begin: \@@_spdate_exec:n {#1} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spdate } } } % \end{macrocode} % \end{macro} % % % \subsection{Semantic Operators} % % This section provides semantic operators. Unlike their formatting % counterparts, these commands do not % process mandatory arguments. Instead, they only inject a mathematical % glyph wrapped in its corresponding regional intent. This % grants teachers absolute control when building complex algebraic % expressions, mixed numbers, or fraction divisions. % % \subsection{The command \cs{spusep}}\label{cmd:spusep} % % For the use of parentheses, we will provide the command |\spusep| with % only two \mymeta{keys} \mykey{size} and \mykey{silent}. Most of the time we can use % |\left| and |\right| or |\Uleft| and |\Uright|, but both forms create a % group, and this hinders the code for \emph{tagged} \texttt{PDF} when we % want to interrupt something between them. % % \begin{macro}[int]{spusep-empty-arg, spusep-invalid-arg} % The \enquote{error} |spusep-empty-arg| is raised when |\spusep| receives an % empty or blank \mymarg{argument}. The \enquote{error} |spusep-invalid-arg| % is raised when the input is invalid. % \begin{macrocode} \msg_new:nnnn { spintent } { spusep-empty-arg } { Empty~argument~for~'\token_to_str:N \spusep'~\msg_line_context:. } { A~non-empty~argument~is~required. } \msg_new:nnnn { spintent } { spusep-invalid-arg } { Invalid~argument~'#1'~for~'\token_to_str:N \spusep'~\msg_line_context:. } { Only~the~delimiters~listed~in~the~documentation~of~'\token_to_str:N \spusep'~are~ accepted. } % \end{macrocode} % \end{macro} % % \begin{variable}[int]{\l_@@_spusep_arg_tl, \l_@@_spusep_left_tl, \l_@@_spusep_right_tl} % The variable \myhlc|\l_@@_spusep_arg_tl| stores the \mymarg{argument}, % the variable \myhlc|\l_@@_spusep_left_tl| and \myhlc|\l_@@_spusep_right_tl| % stores the parentheses. % \begin{macrocode} \tl_new:N \l_@@_spusep_arg_tl \tl_new:N \l_@@_spusep_left_tl \tl_new:N \l_@@_spusep_right_tl % \end{macrocode} % \end{variable} % % \begin{macro}[int]{size, silent, unknown} % We define the \mymeta{keys} \mykey[spusep]{size} and \mykey[spusep]{silent}. % \begin{macrocode} \keys_define:nn { spintent / spusep } { size .choices:nn = { none , big , Big, bigg, Bigg } { \int_case:nn { \l_keys_choice_int } { { 1 } { \tl_clear:N \l_@@_spusep_left_tl \tl_clear:N \l_@@_spusep_right_tl } { 2 } { \tl_set:Nn \l_@@_spusep_left_tl { \bigl } \tl_set:Nn \l_@@_spusep_right_tl { \bigr } } { 3 } { \tl_set:Nn \l_@@_spusep_left_tl { \Bigl } \tl_set:Nn \l_@@_spusep_right_tl { \Bigr } } { 4 } { \tl_set:Nn \l_@@_spusep_left_tl { \biggl } \tl_set:Nn \l_@@_spusep_right_tl { \biggr } } { 5 } { \tl_set:Nn \l_@@_spusep_left_tl { \Biggl } \tl_set:Nn \l_@@_spusep_right_tl { \Biggr } } } }, size .initial:n = none, size .value_required:n = true, size / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { size } { none,~big,~Big,~bigg,~Bigg } { \exp_not:n {#1} }, silent .bool_set:N = \l_@@_spusep_silent_bool, silent .initial:n = false, silent .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spusep } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % % \begin{macro}[int]{\@@_spusep_mathml:nn,} % The function \myhlc|\@@_spusep_mathml:nn| will place the argument % passed in `|#2|' to the right of the variables \myhlc|\l_@@_spusep_left_tl| % and \myhlc|\l_@@_spusep_right_tl| and then check the state of the variable % \myhlc|\l_@@_spusep_silent_bool| handled by the key \mykey{silent} to % print. % \begin{macrocode} \cs_new_protected:Nn \@@_spusep_mathml:nn { \tl_put_right:cn { l_@@_spusep_ #1 _tl } {#2} \bool_if:NTF \l_@@_spusep_silent_bool { \MathMLintent { :silent } { \tl_use:c { l_@@_spusep_ #1 _tl } } } { \tl_use:c { l_@@_spusep_ #1 _tl } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spusep_exec:n,} % The function \myhlc|\@@_spusep_exec:n| captures and stores the % \mymarg{argument} passed in `|#1|` in the variable \myhlc|\l_@@_spusep_arg_tl|, % validates the cases by converting them to the macros provided by % \mypkg{unicode-math}, and passes them to the function \myhlc|\@@_spusep_mathml:nn| % for printing. If the input is not supported or is empty, we return an \enquote{error}. % \begin{macrocode} \cs_new_protected:Nn \@@_spusep_exec:n { \tl_set:Ne \l_@@_spusep_arg_tl { \tl_trim_spaces:n {#1} } \tl_if_empty:NT \l_@@_spusep_arg_tl { \msg_error:nn { spintent } { spusep-empty-arg } } \str_case:VnF \l_@@_spusep_arg_tl { { ( } { \@@_spusep_mathml:nn { left } { \lparen } } { ) } { \@@_spusep_mathml:nn { right } { \rparen } } { [ } { \@@_spusep_mathml:nn { left } { \lbrack } } { ] } { \@@_spusep_mathml:nn { right } { \rbrack } } { \{ } { \@@_spusep_mathml:nn { left } { \lbrace } } { \} } { \@@_spusep_mathml:nn { right } { \rbrace } } } { \tl_if_empty:NF \l_@@_spusep_arg_tl { \msg_error:nnn { spintent } { spusep-invalid-arg } {#1} } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spusep} % The command \ics*{spusep} checks that it is running in math mode, opens a % local group, sets the keys passed in `|#1|' and processes the % \mymarg{argument} passed in `|#2|' by means of the function % \myhlc|\@@_spusep_exec:n|. % \begin{macrocode} \NewDocumentCommand \spusep { O{} m } { \mode_if_math:TF { \group_begin: \keys_set:nn { spintent / spusep } {#1} \@@_spusep_exec:n {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spusep } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spread}}\label{cmd:spread} % % The command |\spread| provides a semantic read for \mynvmath{MathCAT} % of mathematical symbols for situations in which it is necessary to % specify gender or plurality in Spanish. % % \begin{macro}[int]{spnewread-empty-args, spnewread-duplicate, spread-empty-sym, spread-unknown-sym} % The \enquote{error} |spnewread-empty-args| is raised when % |\spnewread| receives a blank symbol or a reading that is empty % once normalized. The % \enquote{error} |spnewread-duplicate| is raised when it registers a % symbol already in the dictionary. The \enquote{error} % |spread-empty-sym| is raised when |\spread| receives a blank symbol. % The \enquote{error} |spread-unknown-sym| is raised when |\spread| is % given a symbol that is neither registered nor accompanied by % \mykey{read-arg}. % \begin{macrocode} \msg_new:nnnn { spintent } { spnewread-empty-args } { Empty~argument~for~'\token_to_str:N \spnewread'~\msg_line_context:. } { Two~non-empty~arguments~are~required. } \msg_new:nnnn { spintent } { spnewread-duplicate } { The~symbol~'#1'~is~already~defined~\msg_line_context:. } { Existing~symbols~cannot~be~redefined~with~ '\token_to_str:N \spnewread'. } \msg_new:nnnn { spintent } { spread-empty-sym } { Empty~argument~for~'\token_to_str:N \spread'~\msg_line_context:. } { A~non-empty~symbol~is~required. } \msg_new:nnnn { spintent } { spread-unknown-sym } { The~symbol~'#1'~is~not~registered~and~no~'read-arg'~was~given~\msg_line_context:. } { Register~the~symbol~first~with~'\token_to_str:N \spnewread'~or~pass~'read-arg'. } % \end{macrocode} % \end{macro} % % \begin{variable}[int] % { % \l_@@_spread_luaset_status_str, % \l_@@_spread_luaset_reading_str, % \l_@@_spread_read_arg_tl, % \l_@@_spread_read_tmp_tl, % } % The variables \myhlc|\l_@@_spread_luaset_status_str| and % \myhlc|\l_@@_spread_luaset_reading_str| are set by % \myhlc|\@@_luafun_spread_new:nn|/\myhlc|\@@_luafun_spread_lookup:n|. % The variable \myhlc|\l_@@_spread_read_arg_tl| stores \mykey{read-arg}. % \begin{macrocode} \str_new:N \l_@@_spread_luaset_status_str \str_new:N \l_@@_spread_luaset_reading_str \tl_new:N \l_@@_spread_read_arg_tl \tl_new:N \l_@@_spread_read_tmp_tl % \end{macrocode} % \end{variable} % % \begin{macro}[int]{read-arg, silent, unknown} % We define the \mymeta{keys} \mykey[spread]{read-arg} and % \mykey[spread]{silent}. % \begin{macrocode} \keys_define:nn { spintent / spread } { read-arg .code:n = { \@@_sanitize_affix:Nn \l_@@_spread_read_arg_tl {#1} }, read-arg .initial:n = , read-arg .value_required:n = true, silent .bool_set:N = \l_@@_spread_silent_bool, silent .initial:n = false, silent .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spread } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spread_exec:nn} % The function \myhlc|\@@_spread_exec:nn| receives the optional \mymeta{keys} % in `|#1|' and the \mymarg{symbol} in `|#2|'. With \mykey{silent}|=true|, % it passes the literal reading `|-|' to \myhlc|\@@_spread_mathml:nn|. % Otherwise, with \mykey{read-arg} given, that reading is used for this call, % overriding any registered reading. With \mykey{read-arg} empty, it calls % \myhlc|\@@_luafun_spread_lookup:n|, using the registered reading when % found and reporting |spread-unknown-sym| otherwise. % \begin{macrocode} \cs_new_protected:Nn \@@_spread_exec:nn { \keys_set:nn { spintent / spread } {#1} \tl_if_blank:nTF {#2} { \msg_error:nn { spintent } { spread-empty-sym } } { \bool_if:NTF \l_@@_spread_silent_bool { \@@_spread_mathml:nn {#2} { - } } { \tl_if_empty:NTF \l_@@_spread_read_arg_tl { \@@_luafun_spread_lookup:n { \exp_not:n {#2} } \str_if_eq:VnTF \l_@@_spread_luaset_status_str { found } { \@@_spread_mathml:nV {#2} \l_@@_spread_luaset_reading_str } { \msg_error:nne { spintent } { spread-unknown-sym } { \exp_not:n {#2} } } } { \@@_spread_mathml:nV {#2} \l_@@_spread_read_arg_tl } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spread_mathml:nn, \@@_spread_mathml:nV} % The function \myhlc|\@@_spread_mathml:nn| receives the \mymarg{symbol} % in `|#1|' and the reading in `|#2|'. It typesets the symbol, preceded by % \myhlc|\@@_inner_sep:|, inside a |\MathMLintent| whose literal is the % reading preceded by `|_|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spread_mathml:nn { \@@_invisible_sep: \MathMLintent { _#2 } { \@@_inner_sep: #1 } } \cs_generate_variant:Nn \@@_spread_mathml:nn { nV } % \end{macrocode} % \end{macro} % % \begin{macro}{\spread} % The public command \ics*{spread} enforces \emph{math mode}, opens a % local group, and delegates execution to \myhlc|\@@_spread_exec:nn|. % \begin{macrocode} \NewDocumentCommand \spread { O{} m } { \mode_if_math:TF { \group_begin: \@@_spread_exec:nn {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spread } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spnewread} % The public command \ics*{spnewread} registers a new symbol/reading % pair. It first normalizes the reading `|#2|' with % \myhlc|\@@_sanitize_affix:Nn|, storing the result in the scratch % variable \myhlc|\l_@@_spread_read_tmp_tl|, and reports |spnewread-empty-args| % when the symbol `|#1|' is blank or the normalized reading is empty. % Otherwise it passes the symbol and the normalized reading to Lua via % \myhlc|\@@_luafun_spread_new:nn|, reporting % |spnewread-duplicate| when the symbol is already registered. % \begin{macrocode} \NewDocumentCommand \spnewread { m m } { \mode_if_math:TF { \msg_error:nnn { spintent } { need-text-mode } { \spnewread } } { \@@_sanitize_affix:Nn \l_@@_spread_read_tmp_tl {#2} \bool_lazy_or:nnTF { \tl_if_blank_p:n {#1} } { \tl_if_empty_p:N \l_@@_spread_read_tmp_tl } { \msg_error:nn { spintent } { spnewread-empty-args } } { \@@_luafun_spread_new:nn { \exp_not:n {#1} } { \exp_not:V \l_@@_spread_read_tmp_tl } \str_if_eq:VnT \l_@@_spread_luaset_status_str { duplicate } { \msg_error:nnn { spintent } { spnewread-duplicate } {#1} } } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spdsym}} % % \begin{variable}[int]{\l_@@_spdsym_read_sym_tl, % \l_@@_spdsym_symbol_tl, % \l_@@_spdsym_prefix_tl, % \l_@@_spdsym_suffix_tl} % Variables to store the regional reading string, the visual operator, % and the optional semantic affixes specifically for |\spdsym|. % \begin{macrocode} \tl_new:N \l_@@_spdsym_read_sym_tl \tl_new:N \l_@@_spdsym_symbol_tl \tl_new:N \l_@@_spdsym_prefix_tl \tl_new:N \l_@@_spdsym_suffix_tl % \end{macrocode} % \end{variable} % % \begin{macro}[int]{prefix, suffix, set-sym, read-sym, unknown} % We define the \mymeta{keys} \mykey[spdsym]{prefix}, \mykey[spdsym]{suffix}, % \mykey[spdsym]{set-sym} and \mykey[spdsym]{read-sym} for |\spdsym|. % \begin{macrocode} \keys_define:nn { spintent / spdsym } { prefix .code:n = { \@@_sanitize_affix:Nn \l_@@_spdsym_prefix_tl {#1} }, prefix .initial:n = , prefix .value_required:n = true, suffix .code:n = { \@@_sanitize_affix:Nn \l_@@_spdsym_suffix_tl {#1} }, suffix .initial:n = , suffix .value_required:n = true, set-sym .choices:nn = { old-style , two-dots , slash } { \int_case:nn { \l_keys_choice_int } { {1} { \tl_set:Nn \l_@@_spdsym_symbol_tl { \div } } {2} { \tl_set:Nn \l_@@_spdsym_symbol_tl { : } } {3} { \tl_set:Nn \l_@@_spdsym_symbol_tl { / } } } }, set-sym .initial:n = two-dots, set-sym .value_required:n = true, set-sym / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { set-sym } { old-style, ~two-dots, ~slash } { \exp_not:n {#1} }, read-sym .choices:nn = { dividido , dividido-por , dividido-entre } { \int_case:nn { \l_keys_choice_int } { {1} { \tl_set:Nn \l_@@_spdsym_read_sym_tl { dividido } } {2} { \tl_set:Nn \l_@@_spdsym_read_sym_tl { dividido-por } } {3} { \tl_set:Nn \l_@@_spdsym_read_sym_tl { dividido-entre } } } }, read-sym .initial:n = dividido, read-sym .value_required:n = true, read-sym / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-sym } { dividido, ~dividido-por, ~dividido-entre } { \exp_not:n {#1} }, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spdsym } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spdsym_mathml:} % The internal function \myhlc|\@@_spdsym_mathml:| outputs the configured % intent structure, attaching prefixes and suffixes if present. % \begin{macrocode} \cs_new_protected:Nn \@@_spdsym_mathml: { \MathMLintent { _ \tl_if_empty:NF \l_@@_spdsym_prefix_tl { \l_@@_spdsym_prefix_tl - } \l_@@_spdsym_read_sym_tl \tl_if_empty:NF \l_@@_spdsym_suffix_tl { - \l_@@_spdsym_suffix_tl } } { \l_@@_spdsym_symbol_tl } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spdsym} % Defines the \ics*{spdsym} operator. It takes a single optional argument % for local setup and delegates the output to \myhlc|\@@_spdsym_mathml:|. % \begin{macrocode} \NewDocumentCommand \spdsym { O{} } { \mode_if_math:TF { \group_begin: \keys_set:nn { spintent / spdsym } {#1} \@@_spdsym_mathml: \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spdsym } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spmsym}} % % \begin{variable}[int]{\l_@@_spmsym_read_sym_tl, % \l_@@_spmsym_symbol_tl, % \l_@@_spmsym_prefix_tl, % \l_@@_spmsym_suffix_tl} % Variables to store the regional reading string, the visual operator, % and the optional semantic affixes specifically for |\spmsym|. % \begin{macrocode} \tl_new:N \l_@@_spmsym_read_sym_tl \tl_new:N \l_@@_spmsym_symbol_tl \tl_new:N \l_@@_spmsym_prefix_tl \tl_new:N \l_@@_spmsym_suffix_tl % \end{macrocode} % \end{variable} % % \begin{macro}[int]{prefix, suffix, set-sym, read-sym, print-sym, unknown} % We define the \mymeta{keys} \mykey[spmsym]{prefix}, \mykey[spmsym]{suffix}, % \mykey[spmsym]{set-sym}, \mykey[spmsym]{read-sym} and \mykey[spmsym]{print-sym} % for |\spmsym|. % \begin{macrocode} \keys_define:nn { spintent / spmsym } { prefix .code:n = { \@@_sanitize_affix:Nn \l_@@_spmsym_prefix_tl {#1} }, prefix .initial:n = , prefix .value_required:n = true, suffix .code:n = { \@@_sanitize_affix:Nn \l_@@_spmsym_suffix_tl {#1} }, suffix .initial:n = , suffix .value_required:n = true, set-sym .choices:nn = { cross , dot , asterisk } { \int_case:nn { \l_keys_choice_int } { {1} { \tl_set:Nn \l_@@_spmsym_symbol_tl { \times } } {2} { \tl_set:Nn \l_@@_spmsym_symbol_tl { \cdot } } {3} { \tl_set:Nn \l_@@_spmsym_symbol_tl { \ast } } } }, set-sym .initial:n = dot, set-sym .value_required:n = true, set-sym / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { set-sym } { cross, ~dot, ~asterisk } { \exp_not:n {#1} }, read-sym .choices:nn = { por , multiplicado-por , veces } { \int_case:nn { \l_keys_choice_int } { {1} { \tl_set:Nn \l_@@_spmsym_read_sym_tl { por } } {2} { \tl_set:Nn \l_@@_spmsym_read_sym_tl { multiplicado-por } } {3} { \tl_set:Nn \l_@@_spmsym_read_sym_tl { veces } } } }, read-sym .initial:n = por, read-sym .value_required:n = true, read-sym / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-sym } { por, ~multiplicado-por, ~veces } { \exp_not:n {#1} }, print-sym .bool_set:N = \l_@@_spmsym_print_sym_bool, print-sym .initial:n = true, print-sym .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spmsym } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmsym_mathml:} % The internal function \myhlc|\@@_spmsym_mathml:| outputs the correct % mathematical glyph based on the local \mymeta{keys}, attaching prefixes and % suffixes if present. With \mykey[spmsym]{print-sym} false, it prints an % invisible separator instead of the glyph. % \begin{macrocode} \cs_new_protected:Nn \@@_spmsym_mathml: { \MathMLintent { _ \tl_if_empty:NF \l_@@_spmsym_prefix_tl { \l_@@_spmsym_prefix_tl - } \l_@@_spmsym_read_sym_tl \tl_if_empty:NF \l_@@_spmsym_suffix_tl { - \l_@@_spmsym_suffix_tl } } { \bool_if:NTF \l_@@_spmsym_print_sym_bool { \l_@@_spmsym_symbol_tl } { \mathopen { \c_@@_invisible_sep_int } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spmsym} % Defines the \ics*{spmsym} operator. Similar to |\spdsym|, it takes a single % optional argument for local setup and delegates the output to % \myhlc|\@@_spmsym_mathml:|. % \begin{macrocode} \NewDocumentCommand \spmsym { O{} } { \mode_if_math:TF { \group_begin: \keys_set:nn { spintent / spmsym } {#1} \@@_spmsym_mathml: \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spmsym } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spdiv}}\label{cmd:spdiv} % % The user-level command |\spdiv| formats division representations. It % takes two mandatory arguments: the \mymarg{dividend} and the % \mymarg{divisor}. Both \mymarg{arguments} are internally processed by the % |\spnum| command to ensure proper digit separation. This command checks % that neither the \mymarg{dividend} nor % the \mymarg{divisor} is \emph{blank}. % % \subsubsection{Definition of error messages for \cs[no-index]{spdiv}} % % \begin{macro}[int]{spdiv-empty-arg} % The \enquote{error} |spdiv-empty-arg| is raised when either the % \mymarg{dividend} or the \mymarg{divisor} is missing or \emph{blank}. % \begin{macrocode} \msg_new:nnnn { spintent } { spdiv-empty-arg } { Empty~argument~for~'\token_to_str:N \spdiv'~\msg_line_context:. } { A~dividend~and~a~divisor~are~required. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spdiv}} % % \begin{macro}[int]{cifras-sep, read-sign, read-period-sep, notation-sym, % set-sym, read-sym, wrap-parens, read-parens, unknown} % We define the \mymeta{keys} \mykey[spdiv]{cifras-sep}, % \mykey[spdiv]{read-sign}, \mykey[spdiv]{read-period-sep}, % \mykey[spdiv]{notation-sym}, \mykey[spdiv]{set-sym}, % \mykey[spdiv]{read-sym}, \mykey[spdiv]{wrap-parens} and % \mykey[spdiv]{read-parens}. % \begin{macrocode} \keys_define:nn { spintent / spdiv } { cifras-sep .meta:nn = { spintent / spnum } { cifras-sep = {#1} }, read-sign .meta:nn = { spintent / spnum } { read-sign = {#1} }, read-period-sep .meta:nn = { spintent / spnum } { read-period-sep = {#1} }, notation-sym .meta:nn = { spintent / spnum } { notation-sym = {#1} }, set-sym .meta:nn = { spintent / spdsym } { set-sym = {#1} }, read-sym .meta:nn = { spintent / spdsym } { read-sym = {#1} }, wrap-parens .bool_set:N = \l_@@_spdiv_wrap_parens_bool, wrap-parens .initial:n = false, wrap-parens .value_required:n = true, read-parens .bool_set:N = \l_@@_spdiv_read_parens_bool, read-parens .initial:n = true, read-parens .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spdiv } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spdiv}} % % \begin{macro}[int]{\@@_spdiv_render_op:nn} % The function \myhlc|\@@_spdiv_render_op:nn| parses both \mymarg{arguments} % through the |\spnum| command and embeds the semantic operator from % |\spdsym|. % \begin{macrocode} \cs_new_protected:Nn \@@_spdiv_render_op:nn { \spnum {#1} \MathMLintent { _ \l_@@_spdsym_read_sym_tl } { \l_@@_spdsym_symbol_tl } \spnum {#2} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spdiv_mathml:nn} % The function \myhlc|\@@_spdiv_mathml:nn| builds the \mynvmath{MathML} % structure of the division. If \mykey{wrap-parens} is true, it adds the % structural parentheses around \myhlc|\@@_spdiv_render_op:nn|, read or % silenced according to \mykey{read-parens}. % \begin{macrocode} \cs_new_protected:Nn \@@_spdiv_mathml:nn { \bool_if:NTF \l_@@_spdiv_wrap_parens_bool { \bool_if:NTF \l_@@_spdiv_read_parens_bool { \lparen \@@_spdiv_render_op:nn {#1} {#2} \rparen } { \MathMLintent { :silent } { \lparen } \@@_spdiv_render_op:nn {#1} {#2} \MathMLintent { :silent } { \rparen } } } { \@@_spdiv_render_op:nn {#1} {#2} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spdiv_exec:nnn} % The function \myhlc|\@@_spdiv_exec:nnn| sets \mymeta{keys}, validates % \mymarg{arguments}, and calls \myhlc|\@@_spdiv_mathml:nn|. % \begin{macrocode} \cs_new_protected:Nn \@@_spdiv_exec:nnn { \keys_set:nn { spintent / spdiv } {#1} \tl_if_blank:nTF {#2} { \msg_error:nn { spintent } { spdiv-empty-arg } } { \tl_if_blank:nTF {#3} { \msg_error:nn { spintent } { spdiv-empty-arg } } { \@@_spdiv_mathml:nn {#2} {#3} } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spdiv} % Defines the user command \ics*{spdiv}. It requires \emph{math mode}. % \begin{macrocode} \NewDocumentCommand \spdiv { O{} m m } { \mode_if_math:TF { \group_begin: \@@_spdiv_exec:nnn {#1} {#2} {#3} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spdiv } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spmul}}\label{cmd:spmul} % % The user-level command |\spmul| formats multiplication representations. It % takes two mandatory arguments: the \mymarg{first factor} and the % \mymarg{second factor}. Both \mymarg{arguments} are internally processed by the % |\spnum| command to ensure proper digit separation. This command checks % that neither the \mymarg{first factor} nor % the \mymarg{second factor} is \emph{blank}. % % \subsubsection{Definition of error messages for \cs[no-index]{spmul}} % % \begin{macro}[int]{spmul-empty-arg} % The \enquote{error} |spmul-empty-arg| is raised when either the % \mymarg{first factor} or the \mymarg{second factor} is missing or % \emph{blank}. % \begin{macrocode} \msg_new:nnnn { spintent } { spmul-empty-arg } { Empty~argument~for~'\token_to_str:N \spmul'~\msg_line_context:. } { A~first~factor~and~a~second~factor~are~required. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spmul}} % % \begin{macro}[int]{cifras-sep, read-sign, read-period-sep, notation-sym, % set-sym, read-sym, wrap-parens, read-parens, unknown} % We define the \mymeta{keys} \mykey[spmul]{cifras-sep}, % \mykey[spmul]{read-sign}, \mykey[spmul]{read-period-sep}, % \mykey[spmul]{notation-sym}, \mykey[spmul]{set-sym}, % \mykey[spmul]{read-sym}, \mykey[spmul]{wrap-parens} and % \mykey[spmul]{read-parens}. % \begin{macrocode} \keys_define:nn { spintent / spmul } { cifras-sep .meta:nn = { spintent / spnum } { cifras-sep = {#1} }, read-sign .meta:nn = { spintent / spnum } { read-sign = {#1} }, read-period-sep .meta:nn = { spintent / spnum } { read-period-sep = {#1} }, notation-sym .meta:nn = { spintent / spnum } { notation-sym = {#1} }, set-sym .meta:nn = { spintent / spmsym } { set-sym = {#1} }, read-sym .meta:nn = { spintent / spmsym } { read-sym = {#1} }, wrap-parens .bool_set:N = \l_@@_spmul_wrap_parens_bool, wrap-parens .initial:n = false, wrap-parens .value_required:n = true, read-parens .bool_set:N = \l_@@_spmul_read_parens_bool, read-parens .initial:n = true, read-parens .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spmul } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spmul}} % % \begin{macro}[int]{\@@_spmul_render_op:nn} % The function \myhlc|\@@_spmul_render_op:nn| parses both \mymarg{arguments} % through the |\spnum| command and embeds the semantic operator from % |\spmsym|. % \begin{macrocode} \cs_new_protected:Nn \@@_spmul_render_op:nn { \spnum {#1} \MathMLintent { _ \l_@@_spmsym_read_sym_tl } { \l_@@_spmsym_symbol_tl } \spnum {#2} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmul_mathml:nn} % The function \myhlc|\@@_spmul_mathml:nn| builds the \mynvmath{MathML} % structure of the multiplication. If \mykey{wrap-parens} is true, it adds the % structural parentheses around \myhlc|\@@_spmul_render_op:nn|, read or % silenced according to \mykey{read-parens}. % \begin{macrocode} \cs_new_protected:Nn \@@_spmul_mathml:nn { \bool_if:NTF \l_@@_spmul_wrap_parens_bool { \bool_if:NTF \l_@@_spmul_read_parens_bool { \lparen \@@_spmul_render_op:nn {#1} {#2} \rparen } { \MathMLintent { :silent } { \lparen } \@@_spmul_render_op:nn {#1} {#2} \MathMLintent { :silent } { \rparen } } } { \@@_spmul_render_op:nn {#1} {#2} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmul_exec:nnn} % The function \myhlc|\@@_spmul_exec:nnn| sets \mymeta{keys}, validates % \mymarg{arguments}, and calls \myhlc|\@@_spmul_mathml:nn|. % \begin{macrocode} \cs_new_protected:Nn \@@_spmul_exec:nnn { \keys_set:nn { spintent / spmul } {#1} \tl_if_blank:nTF {#2} { \msg_error:nn { spintent } { spmul-empty-arg } } { \tl_if_blank:nTF {#3} { \msg_error:nn { spintent } { spmul-empty-arg } } { \@@_spmul_mathml:nn {#2} {#3} } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spmul} % Defines the user command \ics*{spmul}. It requires \emph{math mode}. % \begin{macrocode} \NewDocumentCommand \spmul { O{} m m } { \mode_if_math:TF { \group_begin: \@@_spmul_exec:nnn {#1} {#2} {#3} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spmul } } } % \end{macrocode} % \end{macro} % % \subsection{The commands \cs{spmcd} and \cs{spmcm}}\label{cmd:spmcd} % % The user-level commands |\spmcd| and |\spmcm| provide a semantic interface % for the Greatest Common Divisor, MCD in Spanish, and the Least Common % Multiple, MCM in Spanish. They delegate argument validation and calculation % to the Lua module \myluamod{}, constructing a valid intent. % % \subsubsection{Definition of variables for \cs[no-index]{spmcd} and \cs[no-index]{spmcm}} % \begin{variable}[int]{ % \l_@@_mc_luaset_all_numeric_str, \l_@@_mc_args_seq, % \l_@@_mc_raw_args_seq, \l_@@_mc_types_seq, % \l_@@_mc_luaset_types_clist, \l_@@_mc_idx_int, % \l_@@_spmcd_print_result_bool, \l_@@_spmcm_print_result_bool, % \l_@@_spmcd_read_terse_bool, \l_@@_spmcm_read_terse_bool, % \l_@@_spmcd_print_upper_bool, \l_@@_spmcm_print_upper_bool, % \l_@@_spmcd_prefix_tl, \l_@@_spmcm_prefix_tl, % \l_@@_spmcd_suffix_tl, \l_@@_spmcm_suffix_tl, % } % Internal variables generated dynamically via a comma-separated list % to avoid code duplication for both symmetric commands. % \begin{macrocode} \clist_new:N \l_@@_mc_luaset_types_clist \str_new:N \l_@@_mc_luaset_all_numeric_str \seq_new:N \l_@@_mc_args_seq \seq_new:N \l_@@_mc_raw_args_seq \seq_new:N \l_@@_mc_types_seq \int_new:N \l_@@_mc_idx_int \clist_map_inline:nn { mcd , mcm } { \tl_new:c { l_@@_sp #1 _luaset_ #1 _value_tl } \bool_new:c { l_@@_sp #1 _read_terse_bool } \tl_new:c { l_@@_sp #1 _prefix_tl } } % \end{macrocode} % \end{variable} % % \subsubsection{Definition of keys for \cs[no-index]{spmcd} and \cs[no-index]{spmcm}} % % \begin{macro}[int] % { % result, % prefix, % read-sty, % upper-case, % concept, % silent, % unknown, % } % We define the shared \mymeta{keys} \mykey[spmcd,spmcm]{result}, % \mykey[spmcd,spmcm]{prefix}, \mykey[spmcd,spmcm]{read-sty}, % \mykey[spmcd,spmcm]{upper-case}, \mykey[spmcd,spmcm]{concept} and % \mykey[spmcd,spmcm]{silent}. % \begin{macrocode} \clist_map_inline:nn { mcd , mcm } { \keys_define:nn { spintent / sp #1 } { result .bool_set:c = { l_@@_sp #1 _print_result_bool }, result .initial:n = false, result .value_required:n = true, prefix .code:n = { \@@_sanitize_affix:cn { l_@@_sp #1 _prefix_tl } {##1} }, prefix .initial:n = , prefix .value_required:n = true, read-sty .choices:nn = { terse , clear } { \int_case:nn { \l_keys_choice_int } { {1} { \bool_set_true:c { l_@@_sp #1 _read_terse_bool } } {2} { \bool_set_false:c { l_@@_sp #1 _read_terse_bool } } } }, read-sty .initial:n = clear, read-sty .value_required:n = true, read-sty / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-sty } { terse , ~clear } { \exp_not:n {##1} }, upper-case .bool_set:c = { l_@@_sp #1 _print_upper_bool }, upper-case .initial:n = false, upper-case .value_required:n = true, concept .bool_set:c = { l_@@_sp #1 _concept_bool }, concept .initial:n = true, concept .value_required:n = true, silent .bool_set:c = { l_@@_sp #1 _silent_bool }, silent .initial:n = false, silent .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { sp #1 } \@@_unknown_keys_cmd:n {##1} }, } } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spmcd} and \cs[no-index]{spmcm}} % % \begin{macro}[int]{\@@_mc_process_args:nn} % The function \myhlc|\@@_mc_process_args:nn| parses the operation `|#1|' % and its raw argument list `|#2|'. Calls \myhlc|\@@_luafun_mc_classify_args:n|, % splits `|#2|' into \myhlc|\l_@@_mc_raw_args_seq| and \myhlc|\l_@@_mc_luaset_types_clist| into % \myhlc|\l_@@_mc_types_seq|, then walks the former by position, % fetching the matching type from the latter through % \myhlc|\seq_item:Nn|, to build \myhlc|\l_@@_mc_args_seq|: each % argument wrapped through |\spnum| when its type is |number|, % verbatim otherwise. Then it calls \myhlc|\@@_mc_mathml_args:n|. % \begin{macrocode} \cs_new_protected:Nn \@@_mc_process_args:nn { \@@_luafun_mc_classify_args:n {#2} \seq_set_from_clist:Nn \l_@@_mc_raw_args_seq {#2} \seq_set_from_clist:NN \l_@@_mc_types_seq \l_@@_mc_luaset_types_clist \seq_clear:N \l_@@_mc_args_seq \int_zero:N \l_@@_mc_idx_int \seq_map_inline:Nn \l_@@_mc_raw_args_seq { \int_incr:N \l_@@_mc_idx_int \str_if_eq:eeTF { \seq_item:Nn \l_@@_mc_types_seq { \l_@@_mc_idx_int } } { number } { \seq_put_right:Nn \l_@@_mc_args_seq { \spnum {##1} } } { \seq_put_right:Nn \l_@@_mc_args_seq {##1} } } \@@_mc_mathml_args:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_mc_mathml_args:n} % The function \myhlc|\@@_mc_mathml_args:n| receives the short identifier % in `|#1|' and injects the intents around the % already-formatted \myhlc|\l_@@_mc_args_seq|. % \begin{macrocode} \cs_new_protected:Nn \@@_mc_mathml_args:n { \bool_if:cTF { l_@@_sp #1 _silent_bool } { \seq_set_map:NNn \l_@@_mc_args_seq \l_@@_mc_args_seq { \MathMLintent { _- } { \@@_inner_sep: ##1 } } \MathMLintent { :silent } { \c_@@_invisible_sep_int } % prevent "por" \MathMLintent { :silent } { \lparen } % prevent "intervalo" \MathMLintent { :silent } { \c_@@_invisible_sep_int } \seq_use:Nnnn \l_@@_mc_args_seq { \MathMLintent { :silent } { , } } { \MathMLintent { :silent } { , } } { \MathMLintent { :silent } { , } } } { \MathMLintent { :silent } { \c_@@_invisible_sep_int } % prevent doble "de" \bool_if:cTF { l_@@_sp #1 _concept_bool } { \MathMLintent { _entre } { \lparen } } { \MathMLintent { :silent } { \lparen } } % prevent "intervalo" \MathMLintent { :silent } { \c_@@_invisible_sep_int } \seq_use:Nnnn \l_@@_mc_args_seq { \MathMLintent { :silent } { , } \MathMLintent { _y- } { \c_@@_invisible_sep_int } % "y" medio } { \MathMLintent { :silent } { , } } { \MathMLintent { :silent } { , } \MathMLintent { _y- } { \c_@@_invisible_sep_int } % "y" final } } \MathMLintent { :silent } { \rparen } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmc_exec:nnnn} % The internal function \myhlc|\@@_spmc_exec:nnnn| is the shared % function for both |\spmcd| and |\spmcm|. Takes the short identifier % in `|#1|', its full reading string in `|#2|', the local % \mymeta{keys} in `|#3|', and the optional arguments in `|#4|'. % % After setting the \mymeta{keys}, it calls \myhlc|\@@_spmc_mathml:nn| to % print the operator name. % % With `|#4|' given, classifies it through \myhlc|\@@_luafun_mc_classify_args:n|. % Only when every argument is a number does it call the Lua calculation % |calculate_|\myhlc|#1| and, when \mykey{result} is |true|, print the % computed value with \myhlc|\@@_spmc_result_mathml:n|. % \myhlc|\@@_mc_process_args:nn| prints the argument list itself either way. % With \mykey{silent}|=true|, it wraps each argument in a literal `|_-|' % intent and the printed result is silenced as well. % % \begin{important}* % We should use |\mathrm| instead of |\operatorname|, which generates % unwanted spacing when \myhlc|\c__spintent_invisible_sep_int| is % applied. % \end{important} % \begin{macrocode} \cs_new_protected:Nn \@@_spmc_exec:nnnn { \keys_set:nn { spintent / sp #1 } {#3} \@@_spmc_mathml:nn {#1} {#2} % \end{macrocode} % With `|#4|' given, it is classified with % \myhlc|\@@_luafun_mc_classify_args:n|. If every argument is a number, the % Lua calculation |calculate_|\myhlc|#1| is called, and the computed value is % printed when \mykey{result} is |true|. If any argument is not a number, no calculation is % attempted. In both cases \myhlc|\@@_mc_process_args:n| prints the argument % list. % \begin{macrocode} \tl_if_novalue:nF {#4} { \seq_set_from_clist:Nn \l_@@_mc_raw_args_seq {#4} \int_compare:nNnTF { \seq_count:N \l_@@_mc_raw_args_seq } < { 2 } { \msg_error:nne { spintent } { mc-too-few-args } { \exp_not:c { sp #1 } } } { \@@_luafun_mc_classify_args:n {#4} \str_if_eq:VnT \l_@@_mc_luaset_all_numeric_str { true } { \use:c { @@_luafun_calculate_ #1 :n } {#4} } \@@_mc_process_args:nn {#1} {#4} \@@_spmc_result_mathml:n {#1} } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmc_mathml:nn} % The function \myhlc|\@@_spmc_mathml:nn| receives the short identifier in % `|#1|' and its full reading string in `|#2|', and builds the % \mynvmath{MathML} structure of the operator name. With \mykey{silent}|=true| % or \mykey{concept}|=false|, the operator name is silenced. With % \mykey{concept}|=true|, the literal |intent| is |#1| or |#2| according to % \mykey{read-sty}, prefixed with \mykey{prefix} when given. % \begin{macrocode} \cs_new_protected:Nn \@@_spmc_mathml:nn { \bool_if:cTF { l_@@_sp #1 _silent_bool } { \MathMLintent { :silent } { \mathrm { \bool_if:cTF { l_@@_sp #1 _print_upper_bool } { \text_uppercase:n {#1} } {#1} } } } { \bool_if:cTF { l_@@_sp #1 _concept_bool } { \MathMLintent { _ \tl_if_empty:cF { l_@@_sp #1 _prefix_tl } { \tl_use:c { l_@@_sp #1 _prefix_tl } - } \bool_if:cTF { l_@@_sp #1 _read_terse_bool } {#1} {#2} } { \mathrm { \bool_if:cTF { l_@@_sp #1 _print_upper_bool } { \text_uppercase:n {#1} } {#1} } } } { \MathMLintent { :silent } { \mathrm { \bool_if:cTF { l_@@_sp #1 _print_upper_bool } { \text_uppercase:n {#1} } {#1} } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmc_result_mathml:n} % The function \myhlc|\@@_spmc_result_mathml:n| receives the short % identifier in `|#1|' and builds the \mynvmath{MathML} structure of the % computed value, when \mykey{result} is |true|. With \mykey{silent}|=true|, % the result is silenced as well. % \begin{macrocode} \cs_new_protected:Nn \@@_spmc_result_mathml:n { \str_if_eq:VnT \l_@@_mc_luaset_all_numeric_str { true } { \bool_if:cT { l_@@_sp #1 _print_result_bool } { \bool_if:cTF { l_@@_sp #1 _silent_bool } { \MathMLintent { :silent } { = } \MathMLintent { _- } { \@@_inner_sep: \tl_use:c { l_@@_sp #1 _luaset_ #1 _value_tl } } } { \MathMLintent { _es-igual-a } { = } \tl_use:c { l_@@_sp #1 _luaset_ #1 _value_tl } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spmcd, \spmcm} % The commands \ics*{spmcd} and \ics*{spmcm} are the public commands % for typesetting the greatest common divisor and least common % multiple. They enforce math mode, open a local group, and defer % execution to the main processing macro \myhlc|\@@_spmc_exec:nnnn|, % passing along the appropriate reading strings and arguments. % \begin{macrocode} \NewDocumentCommand \spmcd { O{} d() } { \mode_if_math:TF { \group_begin: \@@_spmc_exec:nnnn { mcd } { máximo-común-divisor } {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spmcd } } } \NewDocumentCommand \spmcm { O{} d() } { \mode_if_math:TF { \group_begin: \@@_spmc_exec:nnnn { mcm } { mínimo-común-múltiplo } {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spmcm } } } % \end{macrocode} % \end{macro} % % \subsection{The commands \cs{spnM} and \cs{spnD}}\label{cmd:spnM} % % The user-level commands |\spnM| and |\spnD| provide a semantic interface % for the Multiples of a number, \emph{\enquote{múltiplos}} in Spanish, % and the Divisors of a number, \emph{\enquote{divisores}} in Spanish. % They delegate argument validation and calculation to the Lua module \myluamod{}, % constructing a valid intent. % % \subsubsection{Definition of variables for \cs[no-index]{spnM} and \cs[no-index]{spnD}} % % \begin{variable}[int] % { % \l_@@_md_out_seq, % \l_@@_spnmd_luaset_is_number_str, % \l_@@_spnM_spnD_luaset_error_str, % \l_@@_spnM_luaset_result_tl, % \l_@@_spnD_luaset_result_tl, % \l_@@_spnM_luaset_is_truncated_str, % \l_@@_spnD_luaset_is_truncated_str, % \l_@@_spnM_prefix_tl, % \l_@@_spnD_prefix_tl, % \l_@@_spnM_print_result_num_tl, % \l_@@_spnD_print_result_num_tl % } % Internal variables generated dynamically for multiples and divisors. % \begin{macrocode} \seq_new:N \l_@@_md_out_seq \str_new:N \l_@@_spnM_spnD_luaset_error_str \str_new:N \l_@@_spnmd_luaset_is_number_str \clist_map_inline:nn { nM , nD } { \tl_new:c { l_@@_sp #1 _luaset_result_tl } \str_new:c { l_@@_sp #1 _luaset_is_truncated_str } \tl_new:c { l_@@_sp #1 _prefix_tl } } % \end{macrocode} % \end{variable} % % % \subsubsection{Definition of error messages for \cs[no-index]{spnM} and \cs[no-index]{spnD}} % % \begin{macro}[int] % { % md-invalid-arg, % mc-too-few-args, % } % The \enquote{error} |md-invalid-arg| is raised when the argument for % multiples or divisors is invalid. % \begin{macrocode} \msg_new:nnnn { spintent } { md-invalid-arg } { Invalid~argument~for~'\tl_trim_spaces:n {#1}'~\msg_line_context:. } { The~argument~must~be~a~single~positive~integer. } % \end{macrocode} % The \enquote{error} |mc-too-few-args| is raised when fewer than two % arguments are provided. % \begin{macrocode} \msg_new:nnnn { spintent } { mc-too-few-args } { Invalid~arguments~for~'\tl_trim_spaces:n {#1}'~\msg_line_context:. } { At~least~two~arguments~are~required. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spnM} and \cs[no-index]{spnD}} % % \begin{macro}[int] % { % result, % silent, % result-num, % prefix, % concept, % unknown, % } % We define the shared \mymeta{keys} \mykey[spnM,spnD]{result}, % \mykey[spnM,spnD]{silent}, \mykey[spnM,spnD]{result-num}, % \mykey[spnM,spnD]{prefix} and \mykey[spnM,spnD]{concept}. % \begin{macrocode} \clist_map_inline:nn { nM , nD } { \keys_define:nn { spintent / sp #1 } { result .bool_set:c = { l_@@_sp #1 _print_result_bool }, result .initial:n = false, result .value_required:n = true, silent .bool_set:c = { l_@@_sp #1 _silent_bool }, silent .initial:n = false, silent .value_required:n = true, result-num .tl_set:c = { l_@@_sp #1 _print_result_num_tl }, result-num .initial:n = , result-num .value_required:n = true, prefix .code:n = { \@@_sanitize_affix:cn { l_@@_sp #1 _prefix_tl } {##1} }, prefix .initial:n = , prefix .value_required:n = true, concept .bool_set:c = { l_@@_sp #1 _concept_bool }, concept .initial:n = true, concept .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { sp #1 } \@@_unknown_keys_cmd:n {##1} }, } } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spnM} and \cs[no-index]{spnD}} % % \begin{macro}[int]{\@@_spnM_spnD_intent:nn} % The internal function \myhlc|\@@_spnM_spnD_intent:nn| resolves the main % reading intent. It takes the command identifier `|#1|', such as % ``nM'' or ``nD'', and the base reading string `|#2|'. It % prepends the localized prefix if defined, normalizes accentuation for % \emph{\enquote{múltiplos}}, and appends `|-de|'. % \begin{macrocode} \cs_new:Nn \@@_spnM_spnD_intent:nn { _ \tl_if_empty:cF { l_@@_sp #1 _prefix_tl } { \tl_use:c { l_@@_sp #1 _prefix_tl } - } \str_case:nnF {#2} { { multiplos } { múltiplos } } {#2} -de } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spnM_spnD_silent:nn} % The internal function \myhlc|\@@_spnM_spnD_silent:nn| prints the atomized % notation structure in full silent mode. It takes the mathematical symbol % `|#1|', such as ``D'' or ``M'', and the variable or explicit argument `|#2|'. % Every component is wrapped in a silent intent, and an invisible separator % is injected between the parentheses to prevent \mynvmath{NVDA/MathCAT} from % applying default contextual reading rules. The argument is wrapped in a % literal `|_-|' intent, not in |:silent|, so that intents of its own, such as % those of |\spnum|, are silenced too. % \begin{macrocode} \cs_new_protected:Nn \@@_spnM_spnD_silent:nn { \MathMLintent { :silent } { \operatorname {#1} } \MathMLintent { :silent } { \lparen } \MathMLintent { :silent } { \c_@@_invisible_sep_int } % prevent doble "de" \MathMLintent { _- } { \@@_inner_sep: #2 } \MathMLintent { :silent } { \rparen } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spnM_spnD_result:nn} % The internal function \myhlc|\@@_spnM_spnD_result:nn| calculates and formats % the computed set. It takes the command identifier `|#1|' and the classified % numeric argument `|#2|'. It invokes the Lua calculation, reporting % |md-invalid-arg| if it fails. On success, it maps the resulting clist into a % sequence and formats the printed output with custom intents for the % separators and the conjunctions. % \begin{macrocode} \cs_new_protected:Nn \@@_spnM_spnD_result:nn { \use:c { @@_luafun_calcular_ #1 :nn } {#2} { \tl_use:c { l_@@_sp #1 _print_result_num_tl } } \str_if_eq:VnTF \l_@@_spnM_spnD_luaset_error_str { true } { \msg_error:nne { spintent } { md-invalid-arg } { \exp_not:c { sp #1 } } } { \MathMLintent { :silent } { \c_@@_invisible_sep_int } % prevent "por" \MathMLintent { _son } { = \{ } \seq_set_from_clist:Ne \l_@@_md_out_seq { \tl_use:c { l_@@_sp #1 _luaset_result_tl } } \seq_use:Nnnn \l_@@_md_out_seq { \MathMLintent { :silent } { , } \str_if_eq:vnT { l_@@_sp #1 _luaset_is_truncated_str } { false } { \MathMLintent { _y- } { \c_@@_invisible_sep_int } % "y" (solo dos) } } { \MathMLintent { :silent } { , } } { \MathMLintent { :silent } { , } \str_if_eq:vnT { l_@@_sp #1 _luaset_is_truncated_str } { false } { \MathMLintent { _y- } { \c_@@_invisible_sep_int } % "y" final } } \str_if_eq:vnT { l_@@_sp #1 _luaset_is_truncated_str } { true } { \MathMLintent { :silent } { , } ~ \dots } \MathMLintent { :silent } { \} } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spnM_spnD_exec:nnnnn} % The internal function \myhlc|\@@_spnM_spnD_exec:nnnnn| acts as the % primary entry point for the multiples and divisors commands. It takes % the command identifier `|#1|', its printed mathematical symbol `|#2|', % its base reading string `|#3|', the local key-value options `|#4|', and % the optional argument `|#5|'. It first sets the provided keys within the % current scope. With `|#5|' given, % \myhlc|\@@_luafun_spnmd_classify:n| classifies it, and a zero is rejected % with |md-invalid-arg|. Then it calls \myhlc|\@@_spnM_spnD_mathml:nnnn|. % \begin{macrocode} \cs_new_protected:Nn \@@_spnM_spnD_exec:nnnnn { \keys_set:nn { spintent / sp #1 } {#4} \tl_if_novalue:nF {#5} { \@@_luafun_spnmd_classify:n { \exp_not:n {#5} } \str_if_eq:VnT \l_@@_spnM_spnD_luaset_error_str { true } { \msg_error:nne { spintent } { md-invalid-arg } { \exp_not:c { sp #1 } } } } \@@_spnM_spnD_mathml:nnnn {#1} {#2} {#3} {#5} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spnM_spnD_mathml:nnnn} % The function \myhlc|\@@_spnM_spnD_mathml:nnnn| takes the command % identifier in `|#1|', the symbol in `|#2|', the base reading string in % `|#3|', and the optional argument in `|#4|', and builds the % \mynvmath{MathML} structure. The parentheses are always silenced. % % With `|#4|' absent, the placeholder |n| stands in for the argument. With % `|#4|' given, the classification stored in % \myhlc|\l_@@_spnmd_luaset_is_number_str| decides whether it is wrapped % with |\spnum|, and then \mykey{result} decides whether % \myhlc|\@@_spnM_spnD_result:nn| prints the computed set; otherwise the % argument is typeset verbatim and no result is attempted. With % \mykey{silent}|=true| the printed result is wrapped in a literal `|_-|' % intent. % % Either way, \mykey{silent} silences the whole thing through % \myhlc|\@@_spnM_spnD_silent:nn|; otherwise \mykey{concept}|=true| % carries the literal |intent| from \myhlc|\@@_spnM_spnD_intent:nn|, % |false| silences only the operator symbol, leaving the argument to % read normally. % \begin{macrocode} \cs_new_protected:Nn \@@_spnM_spnD_mathml:nnnn { \tl_if_novalue:nTF {#4} { \bool_if:cTF { l_@@_sp #1 _silent_bool } { \@@_spnM_spnD_silent:nn {#2} { n } } { \bool_if:cTF { l_@@_sp #1 _concept_bool } { \MathMLintent { \@@_spnM_spnD_intent:nn {#1} {#3} } { \operatorname {#2} } } { \MathMLintent { :silent } { \operatorname {#2} } } \MathMLintent { :silent } { \lparen } % prevent doble "de" \MathMLintent { :silent } { \c_@@_invisible_sep_int } n \MathMLintent { :silent } { \rparen } } } { \bool_if:cTF { l_@@_sp #1 _silent_bool } { \str_if_eq:VnTF \l_@@_spnmd_luaset_is_number_str { true } { \@@_spnM_spnD_silent:nn {#2} { \spnum {#4} } } { \@@_spnM_spnD_silent:nn {#2} {#4} } } { \bool_if:cTF { l_@@_sp #1 _concept_bool } { \MathMLintent { \@@_spnM_spnD_intent:nn {#1} {#3} } { \operatorname {#2} } } { \MathMLintent { :silent } { \operatorname {#2} } } \MathMLintent { :silent } { \lparen } % prevent doble "de" \MathMLintent { :silent } { \c_@@_invisible_sep_int } \str_if_eq:VnTF \l_@@_spnmd_luaset_is_number_str { true } { \spnum {#4} } {#4} \MathMLintent { :silent } { \rparen } } \str_if_eq:VnT \l_@@_spnmd_luaset_is_number_str { true } { \bool_if:cT { l_@@_sp #1 _print_result_bool } { \bool_if:cTF { l_@@_sp #1 _silent_bool } { % prevent "por" \MathMLintent { :silent } { \c_@@_invisible_sep_int } \MathMLintent { _- } { \@@_inner_sep: \@@_spnM_spnD_result:nn {#1} {#4} } } { \@@_spnM_spnD_result:nn {#1} {#4} } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spnM, \spnD} % The commands \ics*{spnM} and \ics*{spnD} are the public commands % for typesetting multiples and divisors, respectively. They % enforce math mode, open a local group, and defer execution to the % function \myhlc|\@@_spnM_spnD_exec:nnnnn|, passing along % the optional \mymeta{keys} `|#1|' and the numeric \mymarg{argument} `|#2|'. % \begin{macrocode} \NewDocumentCommand \spnM { O{} d() } { \mode_if_math:TF { \group_begin: \@@_spnM_spnD_exec:nnnnn { nM } { M } { multiplos } {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spnM } } } \NewDocumentCommand \spnD { O{} d() } { \mode_if_math:TF { \group_begin: \@@_spnM_spnD_exec:nnnnn { nD } { D } { divisores } {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spnD } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{sprazon}}\label{cmd:sprazon} % % The user command |\sprazon| typesets a ratio |a|:|b| as a fraction, using |\dfrac| % or |\frac| according to \mykey{use-dfrac}, read natively, without parsing in % Lua. % % \subsubsection{Definition of variables for \cs[no-index]{sprazon}} % % \begin{variable}[int]{\l_@@_sprazon_cmd_str} % The string variable \myhlc|\l_@@_sprazon_cmd_str| holds the name of the % command that the message of an unknown key reports: |sprazon|, or % |spProp|, which reuses the keys of |\sprazon|. % \begin{macrocode} \str_new:N \l_@@_sprazon_cmd_str \str_set:Nn \l_@@_sprazon_cmd_str { sprazon } % \end{macrocode} % \end{variable} % % \subsubsection{Definition of keys for \cs[no-index]{sprazon}} % % \begin{macro}[int]{silent, use-dfrac, inline, unknown} % We define the \mymeta{keys} \mykey[sprazon]{silent}, % \mykey[sprazon]{use-dfrac} and \mykey[sprazon]{inline}. % \begin{macrocode} \keys_define:nn { spintent / sprazon } { silent .bool_set:N = \l_@@_sprazon_silent_bool, silent .initial:n = false, silent .value_required:n = true, use-dfrac .bool_set:N = \l_@@_sprazon_use_dfrac_bool, use-dfrac .initial:n = false, use-dfrac .value_required:n = true, inline .bool_set:N = \l_@@_sprazon_inline_bool, inline .initial:n = false, inline .value_required:n = true, unknown .code:n = { \str_set_eq:NN \l_@@_command_name_str \l_@@_sprazon_cmd_str \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_sprazon_exec:nnn} % The function \myhlc|\@@_sprazon_exec:nnn| receives the optional % \mymeta{keys} in `|#1|', the numerator in `|#2|' and the denominator in % `|#3|'. It processes the keys and passes the numerator and the denominator % to \myhlc|\@@_sprazon_mathml:nn|. % \begin{macrocode} \cs_new_protected:Nn \@@_sprazon_exec:nnn { \keys_set:nn { spintent / sprazon } {#1} \@@_sprazon_mathml:nn {#2} {#3} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_sprazon_mathml:nn} % The function \myhlc|\@@_sprazon_mathml:nn| receives the numerator in % `|#1|' and the denominator in `|#2|'. With \mykey{silent}, the whole % expression is silenced as one unit: |\frac| or |\dfrac|, according to % \mykey{use-dfrac}, or |#1 : #2| with \mykey{inline}. % % Otherwise, with \mykey{inline}|=false|, the default, the numerator and the % denominator are tagged with a native |\MathMLarg|, as |num| and |denom|, inside % |\frac| or |\dfrac|, and the outer |\MathMLintent| reads % \myhlc|_es-a:infix($num,$denom)|. With \mykey{inline}|=true|, the two % references are tagged side by side and the |:| between them carries the % \myhlc|_es-a:infix($num,$denom)| intent. % \begin{macrocode} \cs_new_protected:Nn \@@_sprazon_mathml:nn { \@@_invisible_sep: \bool_if:NTF \l_@@_sprazon_silent_bool { \MathMLintent { _- } { \@@_inner_sep: \bool_if:NTF \l_@@_sprazon_inline_bool { #1 : #2 } { \bool_if:NTF \l_@@_sprazon_use_dfrac_bool { \dfrac {#1} {#2} } { \frac {#1} {#2} } } } } { \bool_if:NTF \l_@@_sprazon_inline_bool { \MathMLintent { _es-a:infix($num,$denom) } { \MathMLarg { num } {#1} : \MathMLarg { denom } {#2} } } { \MathMLintent { _es-a:infix($num,$denom) } { \@@_inner_sep: \bool_if:NTF \l_@@_sprazon_use_dfrac_bool { \dfrac { \MathMLarg { num } {#1} } { \MathMLarg { denom } {#2} } } { \frac { \MathMLarg { num } {#1} } { \MathMLarg { denom } {#2} } } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\sprazon} % The public command \ics*{sprazon} enforces \emph{math mode} and % delegates execution to \myhlc|\@@_sprazon_exec:nnn|. % \begin{macrocode} \NewDocumentCommand \sprazon { O{} m m } { \mode_if_math:TF { \group_begin: \@@_sprazon_exec:nnn {#1} {#2} {#3} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \sprazon } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spProp}}\label{cmd:spProp} % % The user command |\spProp| typesets a proportion |a|:|b| |=| |c|:|d| % as two fractions joined by |=|, reusing % \myhlc|\@@_sprazon_exec:nnn| directly, not the public |\sprazon| % command, for each ratio. % % \begin{macro}{\spProp} % The public command \ics*{spProp} enforces \emph{math mode}, and % forwards the same \mymeta{keys} in `|#1|' to both ratios: |a/b|, % built from `|#2|' and `|#3|', and |c/d|, built from `|#4|' and `|#5|', joined by a native % |\MathMLintent| reading |_como| over the |=| sign. % \begin{macrocode} \NewDocumentCommand \spProp { O{} m m m m } { \mode_if_math:TF { \group_begin: \str_set:Nn \l_@@_sprazon_cmd_str { spProp } \@@_sprazon_exec:nnn {#1} {#2} {#3} \bool_if:NTF \l_@@_sprazon_silent_bool { \MathMLintent { :silent } { = } } { \MathMLintent { _como } { = } } \@@_sprazon_exec:nnn {#1} {#4} {#5} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spProp } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spang}}\label{cmd:spang} % % The command |\spang| is the main public command for typesetting and % tagging sexagesimal angle expressions. % It parses the input using the Lua function \myhlc|\@@_luafun_spang_parse:n|. % This separates the degrees, minutes, and seconds to construct a \mynvmath{MathML} % |intent| tree with the correct postfixes for \mynvmath{NVDA/MathCAT}. % % \begin{variable}[int]{ % \l_@@_spang_luaset_grado_str, % \l_@@_spang_luaset_minuto_str, % \l_@@_spang_luaset_segundo_str, % \l_@@_spang_luaset_error_str % } % These internal string variables store the values returned from the Lua % module \myluamod{}. They hold the extracted numerical values for degrees, % minutes, and seconds, as well as the error status used during formatting. % \begin{macrocode} \str_new:N \l_@@_spang_luaset_grado_str \str_new:N \l_@@_spang_luaset_minuto_str \str_new:N \l_@@_spang_luaset_segundo_str \str_new:N \l_@@_spang_luaset_error_str % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spang}} % % \begin{macro}[int]{spang-invalid-arg} % The \enquote{error} |spang-invalid-arg| is raised when an argument % fails the format checks, does not match the expected sexagesimal pattern, % or contains invalid measurement symbols. % \begin{macrocode} \msg_new:nnnn { spintent } { spang-invalid-arg } { Invalid~argument~'#2'~for~'\tl_trim_spaces:n {#1}'~\msg_line_context:. } { The~argument~must~have~the~form~'degrees',~'degrees:minutes'~or~ 'degrees:minutes:seconds'. } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spang}} % % \begin{macro}[int]{\@@_spang_exec:n} % The internal function \myhlc|\@@_spang_exec:n| parses the angle input using % \myhlc|\@@_luafun_spang_parse:n| after resetting the error flag in % \myhlc|\l_@@_spang_luaset_error_str|. If an invalid format is detected, it % issues the |spang-invalid-arg| error. Otherwise, it calls % \myhlc|\@@_spang_mathml:|. % \begin{macrocode} \cs_new_protected:Nn \@@_spang_exec:n { \str_set:Nn \l_@@_spang_luaset_error_str { false } \@@_luafun_spang_parse:n {#1} \str_if_eq:VnTF \l_@@_spang_luaset_error_str { true } { \msg_error:nnnn { spintent } { spang-invalid-arg } { \spang } {#1} } { \@@_spang_mathml: } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spang_mathml:} % The internal function \myhlc|\@@_spang_mathml:| checks the extracted % string variables \myhlc|\l_@@_spang_luaset_grado_str|, % \myhlc|\l_@@_spang_luaset_minuto_str|, and % \myhlc|\l_@@_spang_luaset_segundo_str|. For each non-empty variable, it % constructs the corresponding \mynvmath{MathML} |intent| structure with the % correct measurement symbols and postfixes for \mynvmath{NVDA/MathCAT}. % Each value is expanded with \myhlc|\exp_args:Ne| before it reaches |\spunit|, % which protects its \mymarg{argument} from expansion. % \begin{macrocode} \cs_new_protected:Nn \@@_spang_mathml: { \str_if_empty:NF \l_@@_spang_luaset_grado_str { \exp_args:Ne \spunit { \l_@@_spang_luaset_grado_str ° } } \str_if_empty:NF \l_@@_spang_luaset_minuto_str { \MathMLintent { _minutos:postfix($m) } { \MathMLarg { m } { \exp_args:Ne \spunit { \l_@@_spang_luaset_minuto_str ′ } } } } \str_if_empty:NF \l_@@_spang_luaset_segundo_str { \MathMLintent { _segundos:postfix($s) } { \MathMLarg { s } { \exp_args:Ne \spunit { \l_@@_spang_luaset_segundo_str ″ } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spang} % The command \ics*{spang} is the public interface for formatting % sexagesimal angle expressions. It first checks if it is being used in % math mode, issuing an error if not. Inside a local group, it calls % \myhlc|\@@_spang_exec:n| to process the \mymarg{argument}. % \begin{macrocode} \NewDocumentCommand \spang { m } { \mode_if_math:TF { \group_begin: \@@_spang_exec:n {#1} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spang } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spnZ}}\label{cmd:spnZ} % % The user-level command |\spnZ| formats integers, the set % $\mathbb{Z}$. It includes specific options to enclose % numbers, particularly negative ones, in visual parentheses and allows % you to control whether these parentheses are read by \mynvmath{MathCAT}. % % This command verifies that the \mymarg{argument} is indeed an \emph{integer}, % checking for the complete absence of decimal separators or attached % units of measurement. % % \subsubsection{Definition of error messages for \cs[no-index]{spnZ}} % % \begin{macro}[int]{spnZ-not-integer} % The \enquote{error} |spnZ-not-integer| is raised when the % \mymarg{argument} contains decimal separators or attached units, which % conflict with the constraints of a pure integer representation, or when it % is empty. % \begin{macrocode} \msg_new:nnnn { spintent } { spnZ-not-integer } { Invalid~argument~'#1'~for~'\token_to_str:N \spnZ'~\msg_line_context:. } { Only~integers~are~accepted;~decimal~separators~and~units~are~not~allowed. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spnZ}} % % \begin{macro}[int]{cifras-sep, read-sign, wrap-parens, read-parens, unknown} % We define the \mymeta{keys} \mykey[spnZ]{cifras-sep}, % \mykey[spnZ]{read-sign}, \mykey[spnZ]{wrap-parens} and % \mykey[spnZ]{read-parens}. % \begin{macrocode} \keys_define:nn { spintent / spnZ } { cifras-sep .meta:nn = { spintent / spnum } { cifras-sep = {#1} }, read-sign .meta:nn = { spintent / spnum } { read-sign = {#1} }, wrap-parens .bool_set:N = \l_@@_spnz_wrap_parens_bool, wrap-parens .initial:n = false, wrap-parens .value_required:n = true, read-parens .bool_set:N = \l_@@_spnz_read_parens_bool, read-parens .initial:n = true, read-parens .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spnZ } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spnZ}} % % \begin{macro}[int]{\@@_spnz_mathml:} % The function \myhlc|\@@_spnz_mathml:| builds the \mynvmath{MathML} % structure of the integer. If \mykey[spnZ]{wrap-parens} is true, it prints % the number surrounded by parentheses, checking the state of % \myhlc|\l_@@_spnz_read_parens_bool|, which the % \mykey[spnZ]{read-parens} key controls: if it is |true|, the parentheses are read; % otherwise it applies the |:silent| \emph{intent property} to them. If % \mykey[spnZ]{wrap-parens} is false, it prints the number alone with % \myhlc|\@@_spnum_print_num_start:|. % \begin{macrocode} \cs_new_protected:Nn \@@_spnz_mathml: { \bool_if:NTF \l_@@_spnz_wrap_parens_bool { \bool_if:NTF \l_@@_spnz_read_parens_bool { \lparen \@@_spnum_print_num_start: \rparen } { \MathMLintent { :silent } { \lparen } \@@_spnum_print_num_start: \MathMLintent { :silent } { \rparen } } } { \@@_spnum_print_num_start: } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spnz_exec:nn} % The function \myhlc|\@@_spnz_exec:nn| first processes the % \mymeta{keys} passed in `|#1|' and then calls % \myhlc|\@@_luafun_clean_split_and_set:n| on `|#2|'. It verifies % that the input is a valid integer by checking if the variable % \myhlc|\l_@@_luaset_has_integer_str| is |true|. If not |true|, % an \enquote{error} is raised. % % If it is |true|, it calls \myhlc|\@@_spnz_mathml:|. % \begin{macrocode} \cs_new_protected:Nn \@@_spnz_exec:nn { \keys_set:nn { spintent / spnZ } {#1} \@@_luafun_clean_split_and_set:n { \exp_not:n {#2} } \str_if_eq:VnTF \l_@@_luaset_has_integer_str { true } { \@@_spnz_mathml: } { \msg_error:nnn { spintent } { spnZ-not-integer } {#2} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spnZ} % Defines the user command \ics*{spnZ}, which is executed within a % \emph{group} and enforces \emph{math mode}. % \begin{macrocode} \NewDocumentCommand \spnZ { O{} m } { \mode_if_math:TF { \group_begin: \@@_spnz_exec:nn {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spnZ } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spmQ}}\label{cmd:spmQ} % % The command |\spmQ| typesets \emph{\enquote{mixed numbers}} within Spanish % mathematical contexts such as $3\frac{1}{2}$. It delegates the scaling % of the integer part to the Lua module \myluamod{} via % \myhlc|\@@_luafun_spmQ_scale_int:Vn|, which measures both the integer % glyph and the fraction box at the node level and computes a scaling factor % to align their heights visually. Finally, it constructs the correct % intent for \mynvmath{MathCAT}. % % \subsubsection{Definition of variables for \cs[no-index]{spmQ}} % % \begin{variable}[int]{ % \l_@@_spmQ_join_word_str, % \l_@@_spmQ_intent_read_sign_str, % \l_@@_spmQ_wrap_parens_l_tl, % \l_@@_spmQ_wrap_parens_r_tl, % \l_@@_spmQ_error_bool, % \l_@@_saved_luamml_flag_int} % The variable \myhlc|\l_@@_spmQ_join_word_str| stores the value set by % the key \mykey[spmQ]{join-word} which will be verbalized by % \mynvmath{MathCAT} as a grammatical connector between the integer and % fractional parts. % % The variable \myhlc|\l_@@_spmQ_intent_read_sign_str| stores the % |:intent| passed to |\MathMLintent| when the signs % `$\mathcolor{red}{+}$' or `$\mathcolor{red}{-}$' are present in the % integer part, used by the function \myhlc|\@@_spmQ_print_scale_int:n|. % % The variables \myhlc|\l_@@_spmQ_wrap_parens_l_tl| and % \myhlc|\l_@@_spmQ_wrap_parens_r_tl| store the sized parentheses set % by the function \myhlc|\@@_spmQ_set_wrap_parens_size:| and used by the % key \mykey[spmQ]{wrap-parens}. % % The boolean variable \myhlc|\l_@@_spmQ_error_bool| is set to |true| % by \myhlc|\@@_spmQ_check_part:nn| when any of the three arguments is % rejected, so that nothing is typeset. % % The integer variable \myhlc|\l_@@_saved_luamml_flag_int| temporarily % stores the current value of \myhlc|\l__luamml_flag_int| before disabling it % during the injection of the scaled integer node, and restores it % afterwards. This prevents \mypkg{luamml} from generating a spurious % \lstinline++ element around the |sub_box| processed in isolation, % before |\frac| has expanded. % \begin{macrocode} \str_new:N \l_@@_spmQ_join_word_str \str_new:N \l_@@_spmQ_intent_read_sign_str \tl_new:N \l_@@_spmQ_wrap_parens_l_tl \tl_new:N \l_@@_spmQ_wrap_parens_r_tl \bool_new:N \l_@@_spmQ_error_bool \int_new:N \l_@@_saved_luamml_flag_int % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spmQ}} % % \begin{macro}[int]{spmQ-zero-denominator, spmQ-not-integer, spmQ-signed-part} % The \enquote{error} |spmQ-zero-denominator| is raised when a zero % denominator is passed to |\spmQ|. The \enquote{error} |spmQ-not-integer| is % raised when any of the three arguments is not actually an integer. The % \enquote{error} |spmQ-signed-part| is raised when the numerator or the % denominator carries a sign: only the integer part may be signed. % \begin{macrocode} \msg_new:nnnn { spintent } { spmQ-zero-denominator } { Invalid~denominator~for~'\token_to_str:N \spmQ'~\msg_line_context:. } { The~denominator~of~a~fraction~cannot~be~zero. } \msg_new:nnnn { spintent } { spmQ-not-integer } { Invalid~argument~'#1'~for~'\token_to_str:N \spmQ'~\msg_line_context:. } { Only~integers~are~accepted. } \msg_new:nnnn { spintent } { spmQ-signed-part } { Invalid~argument~'#1'~for~'\token_to_str:N \spmQ'~\msg_line_context:. } { The~numerator~and~the~denominator~must~be~integers~without~sign;~only~the~integer~ part~may~have~one. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spmQ}} % % \begin{macro}[int]{join-word, use-spnZ, wrap-parens, read-parens, % use-dfrac, silent, unknown} % We define the \mymeta{keys} \mykey[spmQ]{join-word}, \mykey[spmQ]{use-spnZ}, % \mykey[spmQ]{wrap-parens}, \mykey[spmQ]{read-parens}, % \mykey[spmQ]{use-dfrac} and \mykey[spmQ]{silent}. % \begin{macrocode} \keys_define:nn { spintent / spmQ } { join-word .choices:nn = { entero , enteros , y } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spmQ_join_word_str { entero } } {2} { \str_set:Nn \l_@@_spmQ_join_word_str { enteros } } {3} { \str_set:Nn \l_@@_spmQ_join_word_str { y } } } }, join-word .initial:n = y, join-word .value_required:n = true, join-word / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { join-word } { entero, ~enteros, ~y } { \exp_not:n {#1} }, use-spnZ .bool_set:N = \l_@@_spmQ_use_spnZ_bool, use-spnZ .initial:n = true, use-spnZ .value_required:n = true, use-dfrac .bool_set:N = \l_@@_spmQ_use_dfrac_bool, use-dfrac .initial:n = false, use-dfrac .value_required:n = true, wrap-parens .bool_set:N = \l_@@_spmQ_wrap_parens_bool, wrap-parens .initial:n = false, wrap-parens .value_required:n = true, read-parens .bool_set:N = \l_@@_spmQ_read_parens_bool, read-parens .initial:n = true, read-parens .value_required:n = true, silent .bool_set:N = \l_@@_spmQ_silent_bool, silent .initial:n = false, silent .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spmQ } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spmQ}} % % \begin{macro}[int]{\@@_spmQ_set_wrap_parens_size:} % The function \myhlc|\@@_spmQ_set_wrap_parens_size:| captures the % current mathematical style using the primitive |\mathstyle| from % \hologo{LuaTeX} and sets the variables \myhlc|\l_@@_spmQ_wrap_parens_l_tl| % and \myhlc|\l_@@_spmQ_wrap_parens_r_tl| with the appropriate size for the % parentheses. Display styles |0| and |1| receive |\Bigl\lparen| and % |\Bigr\rparen|; text styles |2| and |3| receive |\bigl\lparen| and % |\bigr\rparen|; any other style falls back to unsized |\lparen| and % |\rparen|. % % If the key \mykey[spmQ]{use-dfrac} is active, the size is always % forced to |\Bigl|/|\Bigr| regardless of the current mathematical style, % since |\dfrac| always produces a display-height fraction. % \begin{macrocode} \cs_new_protected:Nn \@@_spmQ_set_wrap_parens_size: { \int_case:nnF { \mathstyle } { { 0 } { \tl_set:Nn \l_@@_spmQ_wrap_parens_l_tl { \Bigl \lparen } \tl_set:Nn \l_@@_spmQ_wrap_parens_r_tl { \Bigr \rparen } } { 1 } { \tl_set:Nn \l_@@_spmQ_wrap_parens_l_tl { \Bigl \lparen } \tl_set:Nn \l_@@_spmQ_wrap_parens_r_tl { \Bigr \rparen } } { 2 } { \tl_set:Nn \l_@@_spmQ_wrap_parens_l_tl { \bigl \lparen } \tl_set:Nn \l_@@_spmQ_wrap_parens_r_tl { \bigr \rparen } } { 3 } { \tl_set:Nn \l_@@_spmQ_wrap_parens_l_tl { \bigl \lparen } \tl_set:Nn \l_@@_spmQ_wrap_parens_r_tl { \bigr \rparen } } } { \tl_set:Nn \l_@@_spmQ_wrap_parens_l_tl { \lparen } \tl_set:Nn \l_@@_spmQ_wrap_parens_r_tl { \rparen } } % \end{macrocode} % If the key \mykey[spmQ]{use-dfrac} is active, we must reset the % variables \myhlc|\l_@@_spmQ_wrap_parens_l_tl| and % \myhlc|\l_@@_spmQ_wrap_parens_r_tl| to |\Bigl|/|\Bigr| unconditionally. % \begin{macrocode} \bool_if:NT \l_@@_spmQ_use_dfrac_bool { \tl_set:Nn \l_@@_spmQ_wrap_parens_l_tl { \Bigl \lparen } \tl_set:Nn \l_@@_spmQ_wrap_parens_r_tl { \Bigr \rparen } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmQ_start_wrap_parens:, \@@_spmQ_stop_wrap_parens:} % The functions \myhlc|\@@_spmQ_start_wrap_parens:| and % \myhlc|\@@_spmQ_stop_wrap_parens:| open and close the parentheses % around the \emph{\enquote{mixed number}} when the key % \mykey[spmQ]{wrap-parens} is active. They first call % \myhlc|\@@_spmQ_set_wrap_parens_size:| to determine the correct size % for the current mathematical style, then print the appropriate % parenthesis stored in \myhlc|\l_@@_spmQ_wrap_parens_l_tl| or % \myhlc|\l_@@_spmQ_wrap_parens_r_tl|. % % If the variable \myhlc|\l_@@_spmQ_read_parens_bool|, controlled by the % key \mykey[spmQ]{read-parens}, is \emph{false}, each parenthesis is % wrapped inside |\MathMLintent{:silent}| so that % \mynvmath{NVDA/MathCAT} does not verbalize them. % \begin{macrocode} \cs_new_protected:Nn \@@_spmQ_start_wrap_parens: { \bool_if:NT \l_@@_spmQ_wrap_parens_bool { \@@_spmQ_set_wrap_parens_size: \bool_if:NTF \l_@@_spmQ_read_parens_bool { \l_@@_spmQ_wrap_parens_l_tl } { \MathMLintent { :silent } { \l_@@_spmQ_wrap_parens_l_tl } } } } \cs_new_protected:Nn \@@_spmQ_stop_wrap_parens: { \bool_if:NT \l_@@_spmQ_wrap_parens_bool { \@@_spmQ_set_wrap_parens_size: \bool_if:NTF \l_@@_spmQ_read_parens_bool { \l_@@_spmQ_wrap_parens_r_tl } { \MathMLintent { :silent } { \l_@@_spmQ_wrap_parens_r_tl } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmQ_set_join_word:} % The function \myhlc|\@@_spmQ_set_join_word:| injects the grammatical % connector between the integer part and the fraction by branching on % the value stored in \myhlc|\l_@@_spmQ_join_word_str|, set by the key % \mykey[spmQ]{join-word}. In all three cases the visual output is the % Unicode invisible separator |U+2063| via \myhlc|\c_@@_invisible_sep_tl|, % which produces a \lstinline++ node in the \mynvmath{MathML} % structure; what differs is the |:intent| text attached to it via % |\MathMLintent|, which \mynvmath{NVDA/MathCAT} will verbalize as % \emph{«entero»}, \emph{«enteros»} or \emph{«y»} respectively. % \begin{macrocode} \cs_new_protected:Nn \@@_spmQ_set_join_word: { \str_case:Vn \l_@@_spmQ_join_word_str { { entero } { \MathMLintent { _entero } { \c_@@_invisible_sep_int } } { enteros } { \MathMLintent { _enteros } { \c_@@_invisible_sep_int } } { y } { \MathMLintent { _y- } { \c_@@_invisible_sep_int } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmQ_print_scale_int:} % The function \myhlc|\@@_spmQ_print_scale_int:| prints the integer part % of the \emph{\enquote{mixed number}} scaled to match the visual height % of the accompanying fraction. Before calling the Lua function % \myhlc|\@@_luafun_spmQ_scale_int:Vn|, it temporarily disables % \mypkg{luamml} by saving the current value of \myhlc|\l__luamml_flag_int| % into \myhlc|\l_@@_saved_luamml_flag_int| and setting it to |0|, which skips it % completely. This prevents \mypkg{luamml} from generating a spurious % \lstinline++ wrapper around the |sub_box| node that holds the % scaled integer, which is processed in isolation before |\frac| has % expanded. Once the Lua function returns, the original flag value is % restored via \myhlc|\l_@@_saved_luamml_flag_int|. % % The Lua function receives the integer digits from % \myhlc|\l_@@_luaset_part_int_tl| and the fraction command name, % determined by the key \mykey[spmQ]{use-dfrac}, % as its two arguments, and inserts the appropriately scaled |sub_box| % node into the current horizontal list. % \begin{macrocode} \cs_new_protected:Nn \@@_spmQ_print_scale_int: { \int_set_eq:Nc \l_@@_saved_luamml_flag_int { l__luamml_flag_int } \int_set:Nn \l__luamml_flag_int { 0 } \bool_if:NTF \l_@@_spmQ_use_dfrac_bool { \@@_luafun_spmQ_scale_int:Vn \l_@@_luaset_part_int_tl { dfrac } } { \@@_luafun_spmQ_scale_int:Vn \l_@@_luaset_part_int_tl { frac } } \int_set_eq:cN { l__luamml_flag_int } \l_@@_saved_luamml_flag_int } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmQ_print_scale_int:n} % The function \myhlc|\@@_spmQ_print_scale_int:n| takes the integer part % of the \emph{\enquote{mixed number}} passed in `|#1|'. It first calls % \myhlc|\@@_luafun_clean_split_and_set:n| on `|#1|' to parse and % populate the shared Lua variables, then checks the status of % \myhlc|\l_@@_luaset_has_integer_str|. % % If the status is |true|, it inspects \myhlc|\l_@@_luaset_sign_tl|. When % the sign is \emph{empty}, it delegates to % \myhlc|\@@_spmQ_print_scale_int:| to render the scaled integer. When % a sign is present, it sets \myhlc|\l_@@_spmQ_intent_read_sign_str| to % either |_más:prefix($e)| or |_menos:prefix($e)| and wraps the sign % glyph together with a \mynvmath{MathML} argument |($e)| containing the % scaled integer inside a |\MathMLintent|, so that % \mynvmath{NVDA/MathCAT} verbalizes the sign as a prefix before the % integer value. An \myhlc|\@@_invisible_sep:| call between the sign and % the |MathMLarg| forces the opening of a new \lstinline++ in the % \mynvmath{MathML} structure, which is required for the intent tree to % be well-formed. % % If the status is |false|, the |spmQ-not-integer| \enquote{error} is % raised immediately. % \begin{macrocode} \cs_new_protected:Nn \@@_spmQ_print_scale_int:n { \@@_luafun_clean_split_and_set:n { \exp_not:n {#1} } \str_if_eq:VnTF \l_@@_luaset_has_integer_str { true } { \tl_if_empty:NTF \l_@@_luaset_sign_tl { \@@_spmQ_print_scale_int: } { \str_if_eq:VnTF \l_@@_luaset_sign_tl { + } { \str_set:Nn \l_@@_spmQ_intent_read_sign_str { _más:prefix($e) } } { \str_set:Nn \l_@@_spmQ_intent_read_sign_str { _menos:prefix($e) } } \MathMLintent { \l_@@_spmQ_intent_read_sign_str } { % \@@_invisible_sep: \l_@@_luaset_sign_tl \c_@@_invisible_sep_int \MathMLarg { e } { \@@_spmQ_print_scale_int: } } } } { \msg_error:nnn { spintent } { spmQ-not-integer } {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmQ_print_spnZ_dfrac:nn} % The function \myhlc|\@@_spmQ_print_spnZ_dfrac:nn| typesets the % fractional part of the \emph{\enquote{mixed number}}. It takes the % numerator in `|#1|' and the denominator in `|#2|', both already % validated by \myhlc|\@@_spmQ_check_part:nn|. The function branches % on the two boolean variables \myhlc|\l_@@_spmQ_use_spnZ_bool| and % \myhlc|\l_@@_spmQ_use_dfrac_bool|, controlled respectively by the keys % \mykey[spmQ]{use-spnZ} and \mykey[spmQ]{use-dfrac}. When % \mykey[spmQ]{use-spnZ} is |true|, both the numerator and denominator % are passed through |\spnZ| to gain \mynvmath{MathML} integer semantics; % otherwise, the raw token lists `|#1|' and `|#2|' are used as given. % In both branches, the fraction command is selected dynamically via % |\use:c|: if \mykey[spmQ]{use-dfrac} is |true|, |dfrac| is % assembled; otherwise, |frac| is used. % Inside the fraction the outer invisible separator is not typeset, only % recorded as already placed, since the |\spnZ| of the numerator would put % it there as the first element, and \mynvmath{MathCAT} would no longer % read the fraction as a simple one. % \begin{macrocode} \cs_new_protected:Nn \@@_spmQ_print_spnZ_dfrac:nn { \bool_if:NTF \l_@@_spmQ_use_spnZ_bool { \group_begin: \cs_set_protected:Nn \@@_invisible_sep: { \@@_luafun_invisible_sep_mark: } \use:c { \bool_if:NT \l_@@_spmQ_use_dfrac_bool { d } frac } { \spnZ {#1} } { \spnZ {#2} } \group_end: } { \use:c { \bool_if:NT \l_@@_spmQ_use_dfrac_bool { d } frac } {#1} {#2} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmQ_print:nnn} % The function \myhlc|\@@_spmQ_print:nnn| is the main rendering % orchestrator for |\spmQ|. It takes three arguments: the integer part % `|#1|', the numerator `|#2|', and the denominator `|#3|'. It opens % the optional parentheses via \myhlc|\@@_spmQ_start_wrap_parens:|, % then calls \myhlc|\@@_spmQ_print_scale_int:n| to scale and render the % integer part. Next, it injects the grammatical connector between the % integer and the fraction via \myhlc|\@@_spmQ_set_join_word:|. Then it % calls \myhlc|\@@_spmQ_print_spnZ_dfrac:nn| to typeset the fraction % using the keys \mykey[spmQ]{use-dfrac} and \mykey[spmQ]{use-spnZ}. % Finally, it closes the optional parentheses via % \myhlc|\@@_spmQ_stop_wrap_parens:|. % \begin{macrocode} \cs_new_protected:Nn \@@_spmQ_print:nnn { \@@_spmQ_start_wrap_parens: \@@_spmQ_print_scale_int:n {#1} \@@_spmQ_set_join_word: \@@_spmQ_print_spnZ_dfrac:nn {#2} {#3} \@@_spmQ_stop_wrap_parens: } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmQ_check_part:nn} % The function \myhlc|\@@_spmQ_check_part:nn| validates one argument of % |\spmQ|. It receives the role of the argument in `|#1|', which is |int|, % |num| or |den|, and the argument itself in `|#2|', and calls % \myhlc|\@@_luafun_clean_split_and_set:n| on it. If % \myhlc|\l_@@_luaset_has_integer_str| is |false|, the |spmQ-not-integer| % \enquote{error} is raised. If the argument is an integer but carries a % sign and its role is not |int|, the |spmQ-signed-part| \enquote{error} % is raised. If the role is |den| and the value is zero, the % |spmQ-zero-denominator| \enquote{error} is raised. Every rejected % argument sets \myhlc|\l_@@_spmQ_error_bool| to |true|; the arguments are % rejected whatever the value of the key \mykey[spmQ]{silent}. % \begin{macrocode} \cs_new_protected:Nn \@@_spmQ_check_part:nn { \@@_luafun_clean_split_and_set:n { \exp_not:n {#2} } \str_if_eq:VnTF \l_@@_luaset_has_integer_str { true } { \bool_lazy_or:nnTF { \tl_if_empty_p:N \l_@@_luaset_sign_tl } { \str_if_eq_p:nn {#1} { int } } { \str_if_eq:nnT {#1} { den } { \int_compare:nNnT { \l_@@_luaset_part_int_tl } = { 0 } { \msg_error:nn { spintent } { spmQ-zero-denominator } \bool_set_true:N \l_@@_spmQ_error_bool } } } { \msg_error:nnn { spintent } { spmQ-signed-part } {#2} \bool_set_true:N \l_@@_spmQ_error_bool } } { \msg_error:nnn { spintent } { spmQ-not-integer } {#2} \bool_set_true:N \l_@@_spmQ_error_bool } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmQ_exec:nnnn} % The internal function \myhlc|\@@_spmQ_exec:nnnn| takes four arguments: % the optional \mymeta{keys} `|#1|', the integer `|#2|', the numerator % `|#3|', and the denominator `|#4|'. First sets the optional \mymeta{keys} % from `|#1|', then validates the three arguments with % \myhlc|\@@_spmQ_check_part:nn|. If any of them is rejected, nothing is % typeset. % % Otherwise, it calls \myhlc|\@@_spmQ_mathml:nnn|. % \begin{macrocode} \cs_new_protected:Nn \@@_spmQ_exec:nnnn { \keys_set:nn { spintent / spmQ } {#1} \bool_set_false:N \l_@@_spmQ_error_bool \@@_spmQ_check_part:nn { int } {#2} \@@_spmQ_check_part:nn { num } {#3} \@@_spmQ_check_part:nn { den } {#4} \bool_if:NF \l_@@_spmQ_error_bool { \@@_spmQ_mathml:nnn {#2} {#3} {#4} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spmQ_mathml:nnn} % The internal function \myhlc|\@@_spmQ_mathml:nnn| takes the integer % `|#1|', the numerator `|#2|', and the denominator `|#3|', and calls % \myhlc|\@@_spmQ_print:nnn| to format the \emph{\enquote{mixed number}}. % With the key \mykey[spmQ]{silent} set to |true|, the whole % \emph{\enquote{mixed number}} is wrapped in a single % |\MathMLintent{_-}|, preceded by the outer invisible separator and with % the inner one first inside it, so \mynvmath{MathCAT} verbalizes nothing. % \begin{macrocode} \cs_new_protected:Nn \@@_spmQ_mathml:nnn { \bool_if:NTF \l_@@_spmQ_silent_bool { \@@_invisible_sep: \MathMLintent { _- } { \@@_inner_sep: \@@_spmQ_print:nnn {#1} {#2} {#3} } } { \@@_spmQ_print:nnn {#1} {#2} {#3} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spmQ} % The command \ics*{spmQ} is the public interface for formatting % \emph{\enquote{mixed numbers}}. It first checks if it is being used in % \emph{math mode}, issuing an \enquote{error} if not. Inside a local % group, it calls \myhlc|\@@_spmQ_exec:nnnn| to process the optional % \mymeta{keys} and the \mymarg{arguments}. % \begin{macrocode} \NewDocumentCommand \spmQ { O{} m m m } { \mode_if_math:TF { \group_begin: \@@_spmQ_exec:nnnn {#1} {#2} {#3} {#4} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spmQ } } } % \end{macrocode} % \end{macro} % % \subsection{The commands of the \texttt{geo} family}\label{cmd:geo} % % The commands described in this section provide a semantic interface for % geometric objects in Spanish mathematical contexts. They delegate the parsing % to \myluamod{} and share the variables \myhlc{\l_@@_geo_luaset_error_str}, % \myhlc{\l_@@_geo_luaset_intent_str}, \myhlc{\l_@@_geo_luaset_print_tl}, % \myhlc{\l_@@_geo_luaset_letra_str}, \myhlc{\l_@@_geo_luaset_sub_str} and % \myhlc{\l_@@_geo_luaset_tipo_str}, with which the intent is % built. A single point is always annotated with % \myhlc|\@@_mathmlarg_point:nnn|. Every command activates % \myhlc|\l_@@_scope_attr_int| inside its own group, so that the shared queue % only claims content typeset by a \texttt{geo} command. % % \subsubsection{Definition of shared variables} % % \begin{variable}[int] % { % \l_@@_geo_luaset_error_str, % \l_@@_geo_luaset_intent_str, % \l_@@_geo_luaset_print_tl, % \l_@@_geo_luaset_points_clist, % \l_@@_geo_luaset_is_name_str, % \l_@@_geo_luaset_poly_sym_str, % \l_@@_geo_luaset_angulo_case_str, % \l_@@_geo_luaset_concept_str, % \l_@@_geo_luaset_letra_word_str, % \l_@@_geo_luaset_sub_spoken_str, % \l_@@_geo_luaset_nombre_es_num_str, % \l_@@_geo_luaset_letra_str, % \l_@@_geo_luaset_sub_str, % \l_@@_geo_luaset_tipo_str, % } % Internal variables shared across all commands of the family to % avoid repeated calls into Lua. % \begin{macrocode} \str_new:N \l_@@_geo_luaset_error_str \str_new:N \l_@@_geo_luaset_intent_str \tl_new:N \l_@@_geo_luaset_print_tl \clist_new:N \l_@@_geo_luaset_points_clist \str_new:N \l_@@_geo_luaset_is_name_str \str_new:N \l_@@_geo_luaset_poly_sym_str \str_new:N \l_@@_geo_luaset_angulo_case_str \str_new:N \l_@@_geo_luaset_concept_str \str_new:N \l_@@_geo_luaset_letra_word_str \str_new:N \l_@@_geo_luaset_sub_spoken_str \str_new:N \l_@@_geo_luaset_nombre_es_num_str \str_new:N \l_@@_geo_luaset_letra_str \str_new:N \l_@@_geo_luaset_sub_str \str_new:N \l_@@_geo_luaset_tipo_str % \end{macrocode} % \end{variable} % % \subsection{The command \cs{spcoord}}\label{cmd:spcoord} % % The user command |\spcoord| typesets an ordered pair |(x,y)|. The comma or % semicolon between the coordinates carries its own |:infix| intent, and each % coordinate that starts with |+| or |-| is wrapped with |:prefix($vp)| or % |:prefix($vn)|, so that the sign is read as \emph{\enquote{más}} or % \emph{\enquote{menos}}. % % \subsubsection{Definition of variables for \cs[no-index]{spcoord}} % % \begin{variable}[int] % { % \l_@@_spcoord_luaset_error_str, % \l_@@_spcoord_luaset_sep_str, % \l_@@_spcoord_luaset_x_sign_str, % \l_@@_spcoord_luaset_x_type_str, % \l_@@_spcoord_luaset_x_value_str, % \l_@@_spcoord_luaset_x_has_frac_str, % \l_@@_spcoord_luaset_x_composite_str, % \l_@@_spcoord_luaset_y_sign_str, % \l_@@_spcoord_luaset_y_type_str, % \l_@@_spcoord_luaset_y_value_str, % \l_@@_spcoord_luaset_y_has_frac_str, % \l_@@_spcoord_luaset_y_composite_str, % } % Variables set by the function \myhlc|\@@_luafun_spcoord_parse_and_set:n|. % The string variable \myhlc|\l_@@_spcoord_luaset_sep_str| is |,| or |;|, whichever the % argument actually used. The variables for the abscissa |x| and the ordinate % |y| have the same meaning: \myhlc|_sign_str| is |+|, |-|, or empty; % \myhlc|_type_str| is |number| or |raw|; \myhlc|_value_str| is the value with % the sign already removed; \myhlc|_has_frac_str| is |none|, |frac|, or |dfrac|; % and \myhlc|_composite_str| is |true| when the value is a sole subscripted, % superscripted or radical node, |false| otherwise. % \begin{macrocode} \str_new:N \l_@@_spcoord_luaset_error_str \str_new:N \l_@@_spcoord_luaset_sep_str \clist_map_inline:nn { x , y } { \str_new:c { l_@@_spcoord_luaset_ #1 _sign_str } \str_new:c { l_@@_spcoord_luaset_ #1 _type_str } \str_new:c { l_@@_spcoord_luaset_ #1 _value_str } \str_new:c { l_@@_spcoord_luaset_ #1 _has_frac_str } \str_new:c { l_@@_spcoord_luaset_ #1 _composite_str } } % \end{macrocode} % \end{variable} % % \begin{variable}[int] % { % \l_@@_spcoord_raw_arg_tl, % \l_@@_spcoord_intent_str, % \l_@@_spcoord_wrap_parens_l_tl, % \l_@@_spcoord_wrap_parens_r_tl, % } % The variable \myhlc|\l_@@_spcoord_raw_arg_tl| holds the \mymarg{argument} exactly as % given. The variable \myhlc|\l_@@_spcoord_intent_str| holds the |:infix| % word for the separator |_coma:infix| or |_punto-y-coma:infix|. The variables % \myhlc|\l_@@_spcoord_wrap_parens_l_tl|/\myhlc|\l_@@_spcoord_wrap_parens_r_tl| % hold the opening/closing delimiter, sized by the function \myhlc|\@@_spcoord_set_wrap_parens_size:|. % \begin{macrocode} \tl_new:N \l_@@_spcoord_raw_arg_tl \str_new:N \l_@@_spcoord_intent_str \tl_new:N \l_@@_spcoord_wrap_parens_l_tl \tl_new:N \l_@@_spcoord_wrap_parens_r_tl % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spcoord}} % % \begin{macro}[int]{spcoord-invalid-arg} % The \enquote{error} |spcoord-invalid-arg| is raised when the % \mymarg{argument} does not have the form \emph{abscissa,ordinate} or % \emph{abscissa;ordinate}. % \begin{macrocode} \msg_new:nnnn { spintent } { spcoord-invalid-arg } { Invalid~argument~'#1'~for~'\token_to_str:N \spcoord'~\msg_line_context:. } { The~argument~must~have~the~form~'abscissa,~ordinate'~or~'abscissa;~ordinate'. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spcoord}} % % \begin{macro}[int]{silent, unknown} % We define the \mymeta{key} \mykey[spcoord]{silent}. % \begin{macrocode} \keys_define:nn { spintent / spcoord } { silent .bool_set:N = \l_@@_spcoord_silent_bool, silent .initial:n = false, silent .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spcoord } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spcoord}} % % \begin{macro}[int]{\@@_spcoord_set_wrap_parens_size:} % With neither side carrying a fraction, the parentheses stay % unsized. A fraction counts wherever it appears in the value, % be it |\frac|, |\dfrac| or a |\spmQ|, which prints one, except inside % a subscript or superscript, where it does not make the line taller. % Otherwise, |\mathstyle| picks |\Bigl|/|\Bigr| for % display, |\bigl|/|\bigr| for text. When either side is % |dfrac|, the parentheses are forced to |\Bigl|/|\Bigr| regardless % of |\mathstyle|, since |\dfrac| always renders at display % height. % \begin{macrocode} \cs_new_protected:Nn \@@_spcoord_set_wrap_parens_size: { \tl_set:Nn \l_@@_spcoord_wrap_parens_l_tl { \lparen } \tl_set:Nn \l_@@_spcoord_wrap_parens_r_tl { \rparen } \bool_if:nT { \bool_lazy_or_p:nn { \bool_not_p:n { \str_if_eq_p:Vn \l_@@_spcoord_luaset_x_has_frac_str { none } } } { \bool_not_p:n { \str_if_eq_p:Vn \l_@@_spcoord_luaset_y_has_frac_str { none } } } } { \int_case:nnF { \mathstyle } { { 0 } { \tl_set:Nn \l_@@_spcoord_wrap_parens_l_tl { \Bigl \lparen } \tl_set:Nn \l_@@_spcoord_wrap_parens_r_tl { \Bigr \rparen } } { 1 } { \tl_set:Nn \l_@@_spcoord_wrap_parens_l_tl { \Bigl \lparen } \tl_set:Nn \l_@@_spcoord_wrap_parens_r_tl { \Bigr \rparen } } { 2 } { \tl_set:Nn \l_@@_spcoord_wrap_parens_l_tl { \bigl \lparen } \tl_set:Nn \l_@@_spcoord_wrap_parens_r_tl { \bigr \rparen } } { 3 } { \tl_set:Nn \l_@@_spcoord_wrap_parens_l_tl { \bigl \lparen } \tl_set:Nn \l_@@_spcoord_wrap_parens_r_tl { \bigr \rparen } } } { } \bool_if:nT { \bool_lazy_or_p:nn { \str_if_eq_p:Vn \l_@@_spcoord_luaset_x_has_frac_str { dfrac } } { \str_if_eq_p:Vn \l_@@_spcoord_luaset_y_has_frac_str { dfrac } } } { \tl_set:Nn \l_@@_spcoord_wrap_parens_l_tl { \Bigl \lparen } \tl_set:Nn \l_@@_spcoord_wrap_parens_r_tl { \Bigr \rparen } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcoord_value:n} % With the variable \myhlc|_type_str| of the endpoint in `|#1|' equal to % |number|, it calls |\spnum| on its variable \myhlc|_value_str|. Otherwise, % it typesets it as given, a |\frac|, a |\dfrac| or a letter expression. % Receives |x| or |y| in `|#1|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spcoord_value:n { \str_if_eq:eeTF { \str_use:c { l_@@_spcoord_luaset_ #1 _type_str } } { number } { \exp_args:Ne \spnum { \str_use:c { l_@@_spcoord_luaset_ #1 _value_str } } } { \str_use:c { l_@@_spcoord_luaset_ #1 _value_str } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcoord_print:n} % With the variable \myhlc|_sign_str| of the endpoint in `|#1|' equal to |+| % or |-|, it wraps the sign and \myhlc|\@@_spcoord_value:n| with % |_más:prefix($vp)| or |_menos:prefix($vn)|, referencing the value with % \MathMLarg. With no sign, it calls \myhlc|\@@_spcoord_value:n| directly. % Receives |x| or |y| in `|#1|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spcoord_print:n { \str_case:enF { \str_use:c { l_@@_spcoord_luaset_ #1 _sign_str } } { { + } { \MathMLintent { _más:prefix($vp) } { \str_use:c { l_@@_spcoord_luaset_ #1 _sign_str } \MathMLarg{ vp } { \@@_spcoord_value:n {#1} } } } { - } { \MathMLintent { _menos:prefix($vn) } { \str_use:c { l_@@_spcoord_luaset_ #1 _sign_str } \MathMLarg{ vn } { \@@_spcoord_value:n {#1} } } } } { \@@_spcoord_value:n {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcoord_inner_sep:n} % Receives |x| or |y| in `|#1|'. With the variable \myhlc|_composite_str| of that % endpoint equal to |true|, places % \myhlc|\@@_inner_sep:| first, so that the group |\MathMLarg| wraps holds % two children and keeps its |arg|: luamml drops it from a sole node that % carries a subscript, a superscript or a radical. % \begin{macrocode} \cs_new_protected:Nn \@@_spcoord_inner_sep:n { \str_if_eq:eeT { \str_use:c { l_@@_spcoord_luaset_ #1 _composite_str } } { true } { \@@_inner_sep: } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcoord_intent_infix:} % Sets \myhlc|\l_@@_spcoord_intent_str| to |_coma:infix| or % |_punto-y-coma:infix|, according to \myhlc|\l_@@_spcoord_luaset_sep_str|, % and wraps both sides, each referenced with \MathMLarg, with it. The names % |$x| and |$y| stand for the output of \myhlc|\@@_spcoord_print:n|. % \begin{macrocode} \cs_new_protected:Nn \@@_spcoord_intent_infix: { \str_if_eq:VnTF \l_@@_spcoord_luaset_sep_str { ; } { \str_set:Nn \l_@@_spcoord_intent_str { _punto-y-coma:infix } } { \str_set:Nn \l_@@_spcoord_intent_str { _coma:infix } } \MathMLintent { \l_@@_spcoord_intent_str ($x, $y) } { \MathMLarg{x} { \@@_spcoord_inner_sep:n { x } \@@_spcoord_print:n { x } } \MathMLintent { :silent } { \l_@@_spcoord_luaset_sep_str } \MathMLarg{y} { \@@_spcoord_inner_sep:n { y } \@@_spcoord_print:n { y } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcoord_mathml:N} % The argument is the boolean variable that holds the value of \mykey{silent} % of the command that calls it. With \mykey{silent}|=true|, the parentheses % and the argument are wrapped in a single `|_-|' intent, and % \myhlc|\l_@@_spcoord_raw_arg_tl| is typeset as given. Otherwise, the % parentheses are silenced individually and % \myhlc|\@@_spcoord_intent_infix:| provides the reading between them. % \begin{macrocode} \cs_new_protected:Nn \@@_spcoord_mathml:N { \bool_if:NTF #1 { \MathMLintent { _- } { \l_@@_spcoord_wrap_parens_l_tl \l_@@_spcoord_raw_arg_tl \l_@@_spcoord_wrap_parens_r_tl } } { \MathMLintent { :silent } { \l_@@_spcoord_wrap_parens_l_tl } \@@_spcoord_intent_infix: \MathMLintent { :silent } { \l_@@_spcoord_wrap_parens_r_tl } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcoord_exec:nn} % Sets the \mymeta{keys} in `|#1|' and \myhlc|\l_@@_spcoord_raw_arg_tl| to % `|#2|'. Calls \myhlc|\@@_luafun_spcoord_parse_and_set:n| and reports % |spcoord-invalid-arg| on error, whatever the value of \mykey{silent}. Then % it sizes the parentheses and, if there was no error, calls % \myhlc|\@@_spcoord_mathml:N| preceded by an invisible separator placed % \emph{before} the opening parenthesis. The whole pair is wrapped in % |\mathord|, which prevents \mynvmath{MathCAT} from reading |(1,2)| as an % interval when an equals sign or |\in| follows it. % \begin{macrocode} \cs_new_protected:Nn \@@_spcoord_exec:nn { \keys_set:nn { spintent / spcoord } {#1} \tl_set:Nn \l_@@_spcoord_raw_arg_tl {#2} \@@_luafun_spcoord_parse_and_set:n { \exp_not:n {#2} } \str_if_eq:VnT \l_@@_spcoord_luaset_error_str { true } { \msg_error:nne { spintent } { spcoord-invalid-arg } { \exp_not:n {#2} } } \@@_spcoord_set_wrap_parens_size: \str_if_eq:VnF \l_@@_spcoord_luaset_error_str { true } { \mathord { % prevent "intervalo" \MathMLintent { :silent } { \c_@@_invisible_sep_int } \@@_spcoord_mathml:N \l_@@_spcoord_silent_bool } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spcoord} % The public command \ics*{spcoord} % enforces \emph{math mode}, opens a local group, % and delegates to \myhlc|\@@_spcoord_exec:nn|. % \begin{macrocode} \NewDocumentCommand \spcoord { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_spcoord_exec:nn {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spcoord } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spinterval}}\label{cmd:spinterval} % % The user command |\spinterval| receives the \mymarg{argument} written as a % left delimiter |(|, |[| or |]|, two endpoints separated by a semicolon or by % a single comma, and a right delimiter |)|, |]| or |[|. A delimiter |(| or |]| % on the left, and |)| or |[| on the right, excludes its endpoint; the others % include it. The type of interval, the endpoints and the separator are % detected by the Lua module. % % \subsubsection{Definition of variables for \cs[no-index]{spinterval}} % % \begin{variable}[int] % { % \l_@@_spinterval_luaset_error_str, % \l_@@_spinterval_luaset_error_code_str, % \l_@@_spinterval_luaset_left_sym_str, % \l_@@_spinterval_luaset_right_sym_str, % \l_@@_spinterval_luaset_type_str, % \l_@@_spinterval_luaset_sep_str, % \l_@@_spinterval_luaset_tall_str, % } % Variables set by the function \myhlc|\@@_luafun_spinterval_parse_and_set:n|. % \begin{macrocode} \str_new:N \l_@@_spinterval_luaset_error_str \str_new:N \l_@@_spinterval_luaset_error_code_str \str_new:N \l_@@_spinterval_luaset_left_sym_str \str_new:N \l_@@_spinterval_luaset_right_sym_str \str_new:N \l_@@_spinterval_luaset_type_str \str_new:N \l_@@_spinterval_luaset_sep_str \str_new:N \l_@@_spinterval_luaset_tall_str % \end{macrocode} % \end{variable} % % \begin{variable}[int] % { % \l_@@_spinterval_luaset_a_sign_str, % \l_@@_spinterval_luaset_a_type_str, % \l_@@_spinterval_luaset_a_value_str, % \l_@@_spinterval_luaset_a_inf_str, % \l_@@_spinterval_luaset_a_tall_str, % \l_@@_spinterval_luaset_a_composite_str, % \l_@@_spinterval_luaset_a_integer_str, % \l_@@_spinterval_luaset_a_short_str, % \l_@@_spinterval_luaset_a_braced_str, % \l_@@_spinterval_luaset_b_sign_str, % \l_@@_spinterval_luaset_b_type_str, % \l_@@_spinterval_luaset_b_value_str, % \l_@@_spinterval_luaset_b_inf_str, % \l_@@_spinterval_luaset_b_tall_str, % \l_@@_spinterval_luaset_b_composite_str, % \l_@@_spinterval_luaset_b_integer_str, % \l_@@_spinterval_luaset_b_short_str, % \l_@@_spinterval_luaset_b_braced_str, % } % The same variables for each endpoint, |a| and |b|. The |integer| and % |short| ones are |true| when the value is an integer and when it is an % integer of at most three digits, respectively; |braced| holds the value % with each comma protected by braces. % \begin{macrocode} \clist_map_inline:nn { a , b } { \str_new:c { l_@@_spinterval_luaset_ #1 _sign_str } \str_new:c { l_@@_spinterval_luaset_ #1 _type_str } \str_new:c { l_@@_spinterval_luaset_ #1 _value_str } \str_new:c { l_@@_spinterval_luaset_ #1 _inf_str } \str_new:c { l_@@_spinterval_luaset_ #1 _tall_str } \str_new:c { l_@@_spinterval_luaset_ #1 _composite_str } \str_new:c { l_@@_spinterval_luaset_ #1 _integer_str } \str_new:c { l_@@_spinterval_luaset_ #1 _short_str } \str_new:c { l_@@_spinterval_luaset_ #1 _braced_str } } % \end{macrocode} % \end{variable} % % \begin{variable}[int] % { % \l_@@_spinterval_prefix_tl, % \l_@@_spinterval_read_arg_tl, % \l_@@_spinterval_mode_str, % \l_@@_spinterval_size_str, % \l_@@_spinterval_reason_str, % \l_@@_spinterval_word_w_tl, % \l_@@_spinterval_word_g_tl, % \l_@@_spinterval_intent_arg_tl, % \l_@@_spinterval_intent_group_tl, % \l_@@_spinterval_sym_l_tl, % \l_@@_spinterval_sym_r_tl, % \l_@@_spinterval_size_l_tl, % \l_@@_spinterval_size_r_tl, % \l_@@_spinterval_delim_l_tl, % \l_@@_spinterval_delim_r_tl, % \l_@@_spinterval_tmp_tl, % } % The variables \myhlc|\l_@@_spinterval_mode_str|, % \myhlc|\l_@@_spinterval_prefix_tl|, \myhlc|\l_@@_spinterval_read_arg_tl| and % \myhlc|\l_@@_spinterval_size_str| store the values set by the keys % \mykey[spinterval]{read-cmd}, \mykey[spinterval]{prefix}, % \mykey[spinterval]{read-arg} and \mykey[spinterval]{size}, and % \myhlc|\l_@@_spinterval_reason_str| the text of the error reason. The variables % \myhlc|\l_@@_spinterval_word_w_tl| and \myhlc|\l_@@_spinterval_word_g_tl| store % the word that names the interval and the word that joins its endpoints, and % \myhlc|\l_@@_spinterval_intent_arg_tl| and \myhlc|\l_@@_spinterval_intent_group_tl| % the intents of the whole interval and of the group of endpoints. The variables % \myhlc|\l_@@_spinterval_sym_l_tl|, \myhlc|\l_@@_spinterval_sym_r_tl|, % \myhlc|\l_@@_spinterval_size_l_tl|, \myhlc|\l_@@_spinterval_size_r_tl|, % \myhlc|\l_@@_spinterval_delim_l_tl| and \myhlc|\l_@@_spinterval_delim_r_tl| store % the delimiter symbols, their size commands and the delimiters ready to be % typeset; \myhlc|\l_@@_spinterval_tmp_tl| is a scratch variable. % \begin{macrocode} \tl_new:N \l_@@_spinterval_prefix_tl \tl_new:N \l_@@_spinterval_read_arg_tl \str_new:N \l_@@_spinterval_mode_str \str_new:N \l_@@_spinterval_size_str \str_new:N \l_@@_spinterval_reason_str \tl_new:N \l_@@_spinterval_word_w_tl \tl_new:N \l_@@_spinterval_word_g_tl \tl_new:N \l_@@_spinterval_intent_arg_tl \tl_new:N \l_@@_spinterval_intent_group_tl \tl_new:N \l_@@_spinterval_sym_l_tl \tl_new:N \l_@@_spinterval_sym_r_tl \tl_new:N \l_@@_spinterval_size_l_tl \tl_new:N \l_@@_spinterval_size_r_tl \tl_new:N \l_@@_spinterval_delim_l_tl \tl_new:N \l_@@_spinterval_delim_r_tl \tl_new:N \l_@@_spinterval_tmp_tl % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spinterval}} % % \begin{macro}[int]{spinterval-invalid-arg} % The \enquote{error} |spinterval-invalid-arg| is raised when the Lua module % rejects the \mymarg{argument}; it reports the argument and the reason set by % \myhlc|\@@_spinterval_set_reason:|. % \begin{macrocode} \msg_new:nnnn { spintent } { spinterval-invalid-arg } { Invalid~argument~'#1'~for~'\token_to_str:N \spinterval'~(#2)~ \msg_line_context:. } { Use~'(a;~b)'~or~'(a,~b)',~with~'(',~'['~or~']'~on~the~left~and~')',~']'~or~'['~ on~the~right. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spinterval}} % % \begin{macro}[int]{read-cmd, prefix, read-arg, size, silent, concept, unknown} % We define the \mymeta{keys} \mykey[spinterval]{read-cmd}, \mykey[spinterval]{prefix}, % \mykey[spinterval]{read-arg}, \mykey[spinterval]{size}, \mykey[spinterval]{silent} % and \mykey[spinterval]{concept}. % \begin{macrocode} \keys_define:nn { spintent / spinterval } { read-cmd .choices:nn = { school , mathcat } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spinterval_mode_str { school } } {2} { \str_set:Nn \l_@@_spinterval_mode_str { mathcat } } } }, read-cmd .initial:n = school, read-cmd .value_required:n = true, read-cmd / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-cmd } { school,~mathcat } { \exp_not:n {#1} }, prefix .code:n = { \@@_sanitize_affix:Nn \l_@@_spinterval_prefix_tl {#1} }, prefix .initial:n = , prefix .value_required:n = true, read-arg .code:n = { \@@_sanitize_affix:Nn \l_@@_spinterval_read_arg_tl {#1} }, read-arg .initial:n = , read-arg .value_required:n = true, size .choices:nn = { auto , none , big , Big , bigg , Bigg } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spinterval_size_str { auto } } {2} { \str_set:Nn \l_@@_spinterval_size_str { none } } {3} { \str_set:Nn \l_@@_spinterval_size_str { big } } {4} { \str_set:Nn \l_@@_spinterval_size_str { Big } } {5} { \str_set:Nn \l_@@_spinterval_size_str { bigg } } {6} { \str_set:Nn \l_@@_spinterval_size_str { Bigg } } } }, size .initial:n = auto, size .value_required:n = true, size / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { size } { auto,~none,~big,~Big,~bigg,~Bigg } { \exp_not:n {#1} }, silent .bool_set:N = \l_@@_spinterval_silent_bool, silent .initial:n = false, silent .value_required:n = true, concept .bool_set:N = \l_@@_spinterval_concept_bool, concept .initial:n = true, concept .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spinterval } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spinterval}} % % \begin{macro}[int]{\@@_spinterval_set_reason:} % The internal function \myhlc|\@@_spinterval_set_reason:| stores in % \myhlc|\l_@@_spinterval_reason_str| the text that describes the error code % returned by the Lua module, or |invalid argument| when the code is not known. % \begin{macrocode} \cs_new_protected:Nn \@@_spinterval_set_reason: { \str_case:VnF \l_@@_spinterval_luaset_error_code_str { { delim } { \str_set:Nn \l_@@_spinterval_reason_str { it~must~start~with~'(',~'['~or~']'~and~end~with~')',~']'~or~'[' } } { vacio } { \str_set:Nn \l_@@_spinterval_reason_str { an~endpoint~is~empty } } { sep } { \str_set:Nn \l_@@_spinterval_reason_str { there~is~no~separator:~use~a~comma~or~a~semicolon } } { ambiguo } { \str_set:Nn \l_@@_spinterval_reason_str { more~than~one~separator:~use~a~single~semicolon~and~the~commas~are~ decimal } } { llaves } { \str_set:Nn \l_@@_spinterval_reason_str { unbalanced~braces } } { inf-orden } { \str_set:Nn \l_@@_spinterval_reason_str { minus~infinity~goes~on~the~left~and~infinity~on~the~right } } { inf-cerrado } { \str_set:Nn \l_@@_spinterval_reason_str { an~infinite~endpoint~cannot~be~included:~use~'('~or~']'~on~the~left,~ ')'~or~'['~on~the~right } } } { \str_set:Nn \l_@@_spinterval_reason_str { invalid~argument } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spinterval_set_delims:} % The internal function \myhlc|\@@_spinterval_set_delims:| sets the symbols of the % left and right delimiters from the characters returned by the Lua module, and % their size commands from the key \mykey[spinterval]{size}. With |big|, |Big|, % |bigg| and |Bigg| the size is fixed, and with |none| there is no size command. % With |auto| there is none unless an endpoint contains a fraction, in which case it % is |\Bigl|/|\Bigr| in display style and |\bigl|/|\bigr| in text style, and always % |\Bigl|/|\Bigr| when the fraction is a |\dfrac|. Without a size command each % delimiter is wrapped in |\mathopen| or |\mathclose|; with one, the delimiter % follows the size command. % \begin{macrocode} \cs_new_protected:Nn \@@_spinterval_set_delims: { \str_case:Vn \l_@@_spinterval_luaset_left_sym_str { { ( } { \tl_set:Nn \l_@@_spinterval_sym_l_tl { \lparen } } { [ } { \tl_set:Nn \l_@@_spinterval_sym_l_tl { \lbrack } } { ] } { \tl_set:Nn \l_@@_spinterval_sym_l_tl { \rbrack } } } \str_case:Vn \l_@@_spinterval_luaset_right_sym_str { { ) } { \tl_set:Nn \l_@@_spinterval_sym_r_tl { \rparen } } { [ } { \tl_set:Nn \l_@@_spinterval_sym_r_tl { \lbrack } } { ] } { \tl_set:Nn \l_@@_spinterval_sym_r_tl { \rbrack } } } \tl_clear:N \l_@@_spinterval_size_l_tl \tl_clear:N \l_@@_spinterval_size_r_tl \str_case:Vn \l_@@_spinterval_size_str { { big } { \tl_set:Nn \l_@@_spinterval_size_l_tl { \bigl } \tl_set:Nn \l_@@_spinterval_size_r_tl { \bigr } } { Big } { \tl_set:Nn \l_@@_spinterval_size_l_tl { \Bigl } \tl_set:Nn \l_@@_spinterval_size_r_tl { \Bigr } } { bigg } { \tl_set:Nn \l_@@_spinterval_size_l_tl { \biggl } \tl_set:Nn \l_@@_spinterval_size_r_tl { \biggr } } { Bigg } { \tl_set:Nn \l_@@_spinterval_size_l_tl { \Biggl } \tl_set:Nn \l_@@_spinterval_size_r_tl { \Biggr } } { auto } { \str_if_eq:VnF \l_@@_spinterval_luaset_tall_str { none } { \int_case:nnF { \mathstyle } { { 0 } { \tl_set:Nn \l_@@_spinterval_size_l_tl { \Bigl } \tl_set:Nn \l_@@_spinterval_size_r_tl { \Bigr } } { 1 } { \tl_set:Nn \l_@@_spinterval_size_l_tl { \Bigl } \tl_set:Nn \l_@@_spinterval_size_r_tl { \Bigr } } { 2 } { \tl_set:Nn \l_@@_spinterval_size_l_tl { \bigl } \tl_set:Nn \l_@@_spinterval_size_r_tl { \bigr } } { 3 } { \tl_set:Nn \l_@@_spinterval_size_l_tl { \bigl } \tl_set:Nn \l_@@_spinterval_size_r_tl { \bigr } } } { } \str_if_eq:VnT \l_@@_spinterval_luaset_tall_str { dfrac } { \tl_set:Nn \l_@@_spinterval_size_l_tl { \Bigl } \tl_set:Nn \l_@@_spinterval_size_r_tl { \Bigr } } } } } \tl_if_empty:NTF \l_@@_spinterval_size_l_tl { \tl_set:Ne \l_@@_spinterval_delim_l_tl { \exp_not:N \mathopen { \exp_not:V \l_@@_spinterval_sym_l_tl } } \tl_set:Ne \l_@@_spinterval_delim_r_tl { \exp_not:N \mathclose { \exp_not:V \l_@@_spinterval_sym_r_tl } } } { \tl_set:Ne \l_@@_spinterval_delim_l_tl { \exp_not:V \l_@@_spinterval_size_l_tl \exp_not:V \l_@@_spinterval_sym_l_tl } \tl_set:Ne \l_@@_spinterval_delim_r_tl { \exp_not:V \l_@@_spinterval_size_r_tl \exp_not:V \l_@@_spinterval_sym_r_tl } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spinterval_set_intents:} % The internal function \myhlc|\@@_spinterval_set_intents:| sets the two words used % in the intents from the type returned by the Lua module: |abierto-de| and |a| for % an open interval, |cerrado-de| and |a| for a closed one, |abierto-en| and % |y-cerrado-en| for an open-closed one, and |cerrado-en| and |y-abierto-en| for a % closed-open one. The intent of the group of endpoints is |_:infix($a,$b)| % with the second word. The intent of the whole interval is `|_-|' when % \mykey[spinterval]{silent} is |true|, |_| followed by the value of % \mykey[spinterval]{read-arg} when it is not empty, and otherwise |_:prefix($ab)| % with the first word, preceded by the \mykey[spinterval]{prefix}, if any, and % |intervalo-| when \mykey[spinterval]{concept} is |true|. % \begin{macrocode} \cs_new_protected:Nn \@@_spinterval_set_intents: { \str_case:Vn \l_@@_spinterval_luaset_type_str { { open } { \tl_set:Nn \l_@@_spinterval_word_w_tl { abierto-de } \tl_set:Nn \l_@@_spinterval_word_g_tl { a } } { closed } { \tl_set:Nn \l_@@_spinterval_word_w_tl { cerrado-de } \tl_set:Nn \l_@@_spinterval_word_g_tl { a } } { open-closed } { \tl_set:Nn \l_@@_spinterval_word_w_tl { abierto-en } \tl_set:Nn \l_@@_spinterval_word_g_tl { y-cerrado-en } } { closed-open } { \tl_set:Nn \l_@@_spinterval_word_w_tl { cerrado-en } \tl_set:Nn \l_@@_spinterval_word_g_tl { y-abierto-en } } } \tl_set:Ne \l_@@_spinterval_intent_group_tl { _ \l_@@_spinterval_word_g_tl :infix($a,$b) } \bool_if:NTF \l_@@_spinterval_silent_bool { \tl_set:Nn \l_@@_spinterval_intent_arg_tl { _- } } { \tl_if_empty:NTF \l_@@_spinterval_read_arg_tl { \tl_set:Ne \l_@@_spinterval_intent_arg_tl { _ \bool_if:NT \l_@@_spinterval_concept_bool { \tl_if_empty:NF \l_@@_spinterval_prefix_tl { \tl_use:N \l_@@_spinterval_prefix_tl - } intervalo- } \l_@@_spinterval_word_w_tl :prefix($ab) } } { \tl_set:Ne \l_@@_spinterval_intent_arg_tl { _ \l_@@_spinterval_read_arg_tl } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spinterval_inner_sep:n} % The internal function \myhlc|\@@_spinterval_inner_sep:n| calls % \myhlc|\@@_inner_sep:| when the endpoint |a| or |b| passed in `|#1|' is composite. % \begin{macrocode} \cs_new_protected:Nn \@@_spinterval_inner_sep:n { \str_if_eq:eeT { \str_use:c { l_@@_spinterval_luaset_ #1 _composite_str } } { true } { \@@_inner_sep: } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spinterval_plain_number:n} % The internal function \myhlc|\@@_spinterval_plain_number:n| typesets the value of % the endpoint passed in `|#1|' as a number, with each comma protected by braces. % Unless the value is an integer it is preceded by \myhlc|\@@_inner_sep:|. An % integer is preceded by a permanent silent ghost instead, which survives in mathml-AF % and keeps MathCAT from merging |1,5| into a single decimal number. % \begin{macrocode} \cs_new_protected:Nn \@@_spinterval_plain_number:n { \str_if_eq:eeTF { \str_use:c { l_@@_spinterval_luaset_ #1 _integer_str } } { true } % keep "a,b" apart in mathml-AF { \MathMLintent { :silent } { \c_@@_invisible_sep_int } } { \@@_inner_sep: } \str_use:c { l_@@_spinterval_luaset_ #1 _braced_str } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spinterval_value:n} % The internal function \myhlc|\@@_spinterval_value:n| typesets the value of the % endpoint passed in `|#1|' according to its type: |\infty| for infinity; for a number, % \myhlc|\@@_inner_sep:| followed by |\spnum| when \mykey[spinterval]{read-cmd} is % |school| and the value is not an integer of at most three digits, or % \myhlc|\@@_spinterval_plain_number:n| otherwise, since |\spnum| reads such an % integer as MathCAT does; and the value as written for any other type. % \begin{macrocode} \cs_new_protected:Nn \@@_spinterval_value:n { \str_case:enF { \str_use:c { l_@@_spinterval_luaset_ #1 _type_str } } { { infinity } { \infty } { number } { \str_if_eq:VnTF \l_@@_spinterval_mode_str { school } { \tl_set:Ne \l_@@_spinterval_tmp_tl { \str_use:c { l_@@_spinterval_luaset_ #1 _value_str } } \str_if_eq:eeTF { \str_use:c { l_@@_spinterval_luaset_ #1 _short_str } } { true } { \@@_spinterval_plain_number:n {#1} } { \@@_inner_sep: \exp_args:NV \spnum \l_@@_spinterval_tmp_tl } } { \@@_spinterval_plain_number:n {#1} } } } { \str_use:c { l_@@_spinterval_luaset_ #1 _value_str } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spinterval_print:n} % The internal function \myhlc|\@@_spinterval_print:n| typesets the endpoint passed % in `|#1|' with its sign. With \mykey{read-cmd}|=school| a sign |+| or |-| is wrapped, with % its value, in a native |\MathMLintent| with the intent |_más:prefix($vp)| or % |_menos:prefix($vn)|, the value tagged with |\MathMLarg| and preceded by % \myhlc|\@@_spinterval_inner_sep:n|; an endpoint without sign is typeset through % \myhlc|\@@_spinterval_value:n|. With \mykey{read-cmd}|=mathcat| the sign is typeset as is, % followed by the value. % \begin{macrocode} \cs_new_protected:Nn \@@_spinterval_print:n { \str_if_eq:VnTF \l_@@_spinterval_mode_str { school } { \str_case:enF { \str_use:c { l_@@_spinterval_luaset_ #1 _sign_str } } { { + } { \MathMLintent { _más:prefix($vp) } { + \MathMLarg { vp } { \@@_spinterval_inner_sep:n {#1} \@@_spinterval_value:n {#1} } } } { - } { \MathMLintent { _menos:prefix($vn) } { - \MathMLarg { vn } { \@@_spinterval_inner_sep:n {#1} \@@_spinterval_value:n {#1} } } } } { \@@_spinterval_value:n {#1} } } { \str_use:c { l_@@_spinterval_luaset_ #1 _sign_str } \@@_spinterval_value:n {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spinterval_mathml:} % The internal function \myhlc|\@@_spinterval_mathml:| builds the annotated interval % with a native |\MathMLintent| that carries the intent of the whole interval. It % contains the left delimiter silenced, a |\MathMLarg| named |ab| that wraps % \myhlc|\@@_inner_sep:| and a |\MathMLintent| with the intent of the group of % endpoints, and the right delimiter silenced. The group holds the endpoints |a| and % |b|, each tagged with |\MathMLarg| and typeset by \myhlc|\@@_spinterval_print:n|, % separated by the silenced separator. % \begin{macrocode} \cs_new_protected:Nn \@@_spinterval_mathml: { \MathMLintent { \l_@@_spinterval_intent_arg_tl } { \MathMLintent { :silent } { \l_@@_spinterval_delim_l_tl } \MathMLarg { ab } { \@@_inner_sep: \MathMLintent { \l_@@_spinterval_intent_group_tl } { \MathMLarg { a } { \@@_spinterval_inner_sep:n { a } \@@_spinterval_print:n { a } } \MathMLintent { :silent } { \l_@@_spinterval_luaset_sep_str } \MathMLarg { b } { \@@_spinterval_inner_sep:n { b } \@@_spinterval_print:n { b } } } } \MathMLintent { :silent } { \l_@@_spinterval_delim_r_tl } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spinterval_exec:nn} % The internal function \myhlc|\@@_spinterval_exec:nn| receives the optional % \mymeta{keys} passed in `|#1|' and the \mymarg{argument} passed in `|#2|'. It calls % \myhlc|\@@_luafun_spinterval_parse_and_set:n|; if the Lua module reports an error, % it reports |spinterval-invalid-arg| whatever the value of % \mykey[spinterval]{silent}: an invalid \mymarg{argument} is always % rejected. When there is no error, it calls \myhlc|\@@_spinterval_set_delims:| and % \myhlc|\@@_spinterval_set_intents:|, places \myhlc|\@@_invisible_sep:| and % delegates the rendering to \myhlc|\@@_spinterval_mathml:|. % \begin{macrocode} \cs_new_protected:Nn \@@_spinterval_exec:nn { \keys_set:nn { spintent / spinterval } {#1} \@@_luafun_spinterval_parse_and_set:n { \exp_not:n {#2} } \str_if_eq:VnTF \l_@@_spinterval_luaset_error_str { true } { \@@_spinterval_set_reason: \msg_error:nnee { spintent } { spinterval-invalid-arg } { \exp_not:n {#2} } { \l_@@_spinterval_reason_str } } { \@@_spinterval_set_delims: \@@_spinterval_set_intents: \@@_invisible_sep: \@@_spinterval_mathml: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spinterval} % The public command \ics*{spinterval} % enforces \emph{math mode}, opens a local group, % and delegates to \myhlc|\@@_spinterval_exec:nn|. % \begin{macrocode} \NewDocumentCommand \spinterval { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_spinterval_exec:nn {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spinterval } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spfun}}\label{cmd:spfun} % % The user command |\spfun| typesets a function call such as |f(x)|, % |f_{1}(x)|, |f''(x,y)|. The Lua function % \myhlc|\@@_luafun_spfun_parse_and_set:n| splits the \mymarg{argument} % into the function name, which is a letter with an optional subscript or primes % left in the shared \myhlc|\l_@@_geo_luaset_*| variables, and the list of % arguments, cut at the top-level commas, each one stored in % \myhlc|\l_@@_spfun_luaset_arg__tl| together with a flag % \myhlc|\l_@@_spfun_luaset_simple__str| telling whether it is a single % node. The whole call is tagged with a native |\MathMLintent| whose % |intent| is |$f($a1,$a2,...)|; the name is tagged |arg="f"| and each % argument |arg="a"|, while the parentheses and the commas are silenced. % The name may carry the inverse exponent |^{-1}|, which sets the flag % \myhlc|\l_@@_spfun_luaset_inv_str|; in that case the literal |intent| of the % name ends in |-inversa|, the exponent is typeset silenced and the % parentheses may be omitted, with no arguments and |$f| as the whole |intent|. % An argument that is itself a call, |f(g(x))| or |f(g^{-1}(y))|, is analysed % recursively by Lua, kind |call|, and typeset by % \myhlc|\@@_spfun_nested:n|, which runs the same code in a group, one level % deeper; the |arg| names carry the depth. % % \subsubsection{Definition of variables for \cs[no-index]{spfun}} % % \begin{variable}[int] % { % \l_@@_spfun_mode_str, % \l_@@_spfun_prefix_tl, % \l_@@_spfun_intent_arg_tl, % \l_@@_spfun_kv_tl, % \l_@@_spfun_refs_tl, % \l_@@_spfun_tmp_str, % \l_@@_spfun_nargs_int, % \l_@@_spfun_depth_int, % \l_@@_spfun_call_tl, % \l_@@_spfun_silent_bool, % \l_@@_spfun_concept_bool, % \l_@@_spfun_luaset_error_str, % \l_@@_spfun_luaset_nargs_str, % \l_@@_spfun_luaset_inv_str, % } % The variable \myhlc|\l_@@_spfun_mode_str| stores the value set by the % key \mykey[spfun]{read-cmd} and \myhlc|\l_@@_spfun_prefix_tl| the one % set by \mykey[spfun]{prefix}. The boolean variables % \myhlc|\l_@@_spfun_silent_bool| and \myhlc|\l_@@_spfun_concept_bool| % store the keys \mykey[spfun]{silent} and \mykey[spfun]{concept}. The % variable \myhlc|\l_@@_spfun_call_tl| stores the |$f($a1,...)| text of the % |intent| and \myhlc|\l_@@_spfun_depth_int| the nesting level of % \myhlc|\@@_spfun_nested:n|. The three |luaset| variables are set from % Lua. % \begin{macrocode} \str_new:N \l_@@_spfun_mode_str \tl_new:N \l_@@_spfun_prefix_tl \tl_new:N \l_@@_spfun_intent_arg_tl \tl_new:N \l_@@_spfun_kv_tl \tl_new:N \l_@@_spfun_refs_tl \str_new:N \l_@@_spfun_tmp_str \int_new:N \l_@@_spfun_nargs_int \bool_new:N \l_@@_spfun_silent_bool \bool_new:N \l_@@_spfun_concept_bool \str_new:N \l_@@_spfun_luaset_error_str \str_new:N \l_@@_spfun_luaset_nargs_str \str_new:N \l_@@_spfun_luaset_inv_str \tl_new:N \l_@@_spfun_call_tl \int_new:N \l_@@_spfun_depth_int % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spfun}} % % \begin{macro}[int]{spfun-invalid-arg} % The \enquote{error} |spfun-invalid-arg| is raised when the argument % fails to parse: no valid name, no parentheses unless it is the inverse % function, a malformed or misplaced inverse exponent, unbalanced % delimiters, an empty argument or text after the closing parenthesis. It % is raised whatever the value of \mykey{silent}. % \begin{macrocode} \msg_new:nnnn { spintent } { spfun-invalid-arg } { Invalid~argument~'#1'~for~'\token_to_str:N \spfun'~\msg_line_context:. } { The~argument~must~have~the~form~'f(x)'~or~'f(x,y,...)',~where~'f'~is~a~letter~with~ optional~primes~or~subscript. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spfun}} % % \begin{macro}[int]{read-cmd, prefix, concept, silent, unknown} % We define the \mymeta{keys} \mykey[spfun]{read-cmd}, \mykey[spfun]{prefix}, % \mykey[spfun]{concept} and \mykey[spfun]{silent}. % \begin{macrocode} \keys_define:nn { spintent / spfun } { read-cmd .choices:nn = { school , mathcat } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spfun_mode_str { school } } {2} { \str_set:Nn \l_@@_spfun_mode_str { mathcat } } } }, read-cmd .initial:n = school, read-cmd .value_required:n = true, read-cmd / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-cmd } { school,~mathcat } { \exp_not:n {#1} }, prefix .code:n = { \@@_sanitize_affix:Nn \l_@@_spfun_prefix_tl {#1} }, prefix .initial:n = , prefix .value_required:n = true, silent .bool_set:N = \l_@@_spfun_silent_bool, silent .initial:n = false, silent .value_required:n = true, concept .bool_set:N = \l_@@_spfun_concept_bool, concept .initial:n = false, concept .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spfun } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spfun}} % % \begin{macro}[int]{\@@_spfun_fname:, \@@_spfun_aname:n} % Names of the |arg| attributes at the current nesting depth % \myhlc|\l_@@_spfun_depth_int|. At depth~1 they are |f| and |a|; at a % deeper level, |f| and |ax|. % \begin{macrocode} \cs_new:Nn \@@_spfun_fname: { f \int_compare:nNnF { \l_@@_spfun_depth_int } = { 1 } { \int_use:N \l_@@_spfun_depth_int } } \cs_new:Nn \@@_spfun_aname:n { a \int_compare:nNnF { \l_@@_spfun_depth_int } = { 1 } { \int_use:N \l_@@_spfun_depth_int x } #1 } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfun_name_plain:} % Typesets the name without any annotation: the letter, followed by the % subscript or by the primes when there are any. % \begin{macrocode} \cs_new_protected:Nn \@@_spfun_name_plain: { \l_@@_geo_luaset_letra_str \str_case:VnF \l_@@_geo_luaset_tipo_str { { sub } { \c_math_subscript_token { \l_@@_geo_luaset_sub_str } } { prima } { \l_@@_geo_luaset_sub_str } } { } \str_if_eq:VnT \l_@@_spfun_luaset_inv_str { true } { \c_math_superscript_token { -1 } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfun_name_school:} % For \mykey{read-cmd}|=school| the name is tagged |arg="f"| with a literal % |intent| such as |_f-sub-uno-| or |_F-doble-prima-| through the shared % queue, so \mynvmath{MathCAT} neither announces a capital letter nor reads % the Unicode double prime as seconds. A subscript is typeset silenced; a % prime is not, since it is not spoken on the base. The inverse exponent % |^{-1}|, when present, is typeset silenced too: the literal |intent| % |_f-inversa-| already says everything. % \begin{macrocode} \cs_new_protected:Nn \@@_spfun_name_school: { \tl_set:Ne \l_@@_spfun_kv_tl { arg = " \@@_spfun_fname: ", intent = "_ \l_@@_geo_luaset_intent_str -" } \@@_luafun_mrow_intent_arg:nn { } { \l_@@_spfun_kv_tl } \str_case:VnF \l_@@_geo_luaset_tipo_str { { sub } { \l_@@_geo_luaset_letra_str \c_math_subscript_token { \MathMLintent { :silent } { \l_@@_geo_luaset_sub_str } } } { prima } { \l_@@_geo_luaset_letra_str \l_@@_geo_luaset_sub_str } } { \l_@@_geo_luaset_letra_str } \str_if_eq:VnT \l_@@_spfun_luaset_inv_str { true } { \c_math_superscript_token { \MathMLintent { :silent } { -1 } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfun_name_mathcat:} % For \mykey{read-cmd}|=mathcat| the name is tagged |arg="f"| with a native % |\MathMLarg|, without using the shared queue. With a subscript or primes, % the inner separator keeps the |arg| on a wrapping ||. % \begin{macrocode} \cs_new_protected:Nn \@@_spfun_name_mathcat: { \bool_lazy_and:nnTF { \str_if_eq_p:Vn \l_@@_geo_luaset_tipo_str { } } { \str_if_eq_p:Vn \l_@@_spfun_luaset_inv_str { false } } { \MathMLarg { \@@_spfun_fname: } { \@@_spfun_name_plain: } } { \MathMLarg { \@@_spfun_fname: } { \@@_inner_sep: \@@_spfun_name_plain: } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfun_arg:n} % Tags argument number |#1| with |arg="a"|, named by \myhlc|\@@_spfun_aname:n|. % A single node, which is a letter, a number or a command such as |\alpha|, carries its % |arg| directly; any other argument starts with the inner separator, which % forces an || to hold the |arg|. An argument that is itself a call, % such as |g(x)| or |g^{-1}(y)|, is typeset by \myhlc|\@@_spfun_nested:n|. % \begin{macrocode} \cs_new_protected:Nn \@@_spfun_arg:n { \str_set_eq:Nc \l_@@_spfun_tmp_str { l_@@_spfun_luaset_simple_ #1 _str } \str_case:VnF \l_@@_spfun_tmp_str { { true } { \MathMLarg { \@@_spfun_aname:n {#1} } { \tl_use:c { l_@@_spfun_luaset_arg_ #1 _tl } } } { call } { \MathMLarg { \@@_spfun_aname:n {#1} } { \@@_inner_sep: \@@_spfun_nested:n {#1} } } } { \MathMLarg { \@@_spfun_aname:n {#1} } { \@@_inner_sep: \tl_use:c { l_@@_spfun_luaset_arg_ #1 _tl } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfun_args:} % Walks the arguments, silencing the comma between them. % \begin{macrocode} \cs_new_protected:Nn \@@_spfun_args: { \int_step_inline:nn { \l_@@_spfun_nargs_int } { \int_compare:nNnF { ##1 } = { 1 } { \MathMLintent { :silent } { , } } \@@_spfun_arg:n {##1} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfun_args_plain:} % Typesets the arguments with no annotation for \mykey{silent}. % \begin{macrocode} \cs_new_protected:Nn \@@_spfun_args_plain: { \int_step_inline:nn { \l_@@_spfun_nargs_int } { \int_compare:nNnF { ##1 } = { 1 } { , } \tl_use:c { l_@@_spfun_luaset_arg_ ##1 _tl } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfun_set_intent:} % Builds the concept |intent| in \myhlc|\l_@@_spfun_intent_arg_tl|: % |$f($a1,$a2,...)| when \mykey{concept} is |false|, and % |_prefix-función:prefix($f($a1,$a2,...))| otherwise. Without arguments, % as for the inverse function written bare, the call is just |$f|. % \begin{macrocode} \cs_new_protected:Nn \@@_spfun_set_intent: { \tl_clear:N \l_@@_spfun_refs_tl \int_step_inline:nn { \l_@@_spfun_nargs_int } { \tl_put_right:Ne \l_@@_spfun_refs_tl { \int_compare:nNnF { ##1 } = { 1 } { , } $ \@@_spfun_aname:n {##1} } } \int_compare:nNnTF { \l_@@_spfun_nargs_int } = { 0 } { \tl_set:Ne \l_@@_spfun_call_tl { $ \@@_spfun_fname: } } { \tl_set:Ne \l_@@_spfun_call_tl { $ \@@_spfun_fname: ( \l_@@_spfun_refs_tl ) } } \bool_if:NTF \l_@@_spfun_concept_bool { \tl_set:Ne \l_@@_spfun_intent_arg_tl { _ \tl_if_empty:NF \l_@@_spfun_prefix_tl { \tl_use:N \l_@@_spfun_prefix_tl - } función:prefix( \l_@@_spfun_call_tl ) } } { \tl_set_eq:NN \l_@@_spfun_intent_arg_tl \l_@@_spfun_call_tl } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfun_mathml:} % Calls \myhlc|\@@_invisible_sep:| first, then wraps name, parentheses and % arguments in a native |\MathMLintent|. With \mykey{silent} set to |true| % the intent is `|_-|' and nothing inside is annotated. % \begin{macrocode} \cs_new_protected:Nn \@@_spfun_mathml: { \@@_invisible_sep: \@@_spfun_body: } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfun_body:} % The call itself, without the outer separator: used by the top-level call % and by every call nested in an argument. % \begin{macrocode} \cs_new_protected:Nn \@@_spfun_body: { \bool_if:NTF \l_@@_spfun_silent_bool { \MathMLintent { _- } { \@@_inner_sep: \@@_spfun_name_plain: \int_compare:nNnF { \l_@@_spfun_nargs_int } = { 0 } { \lparen \@@_spfun_args_plain: \rparen } } } { \@@_spfun_set_intent: \MathMLintent { \l_@@_spfun_intent_arg_tl } { \int_compare:nNnT { \l_@@_spfun_nargs_int } = { 0 } { \@@_inner_sep: } \str_if_eq:VnTF \l_@@_spfun_mode_str { school } { \@@_spfun_name_school: } { \@@_spfun_name_mathcat: } \int_compare:nNnF { \l_@@_spfun_nargs_int } = { 0 } { \MathMLintent { :silent } { \lparen } \@@_spfun_args: \MathMLintent { :silent } { \rparen } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfun_nested:n} % Typesets argument number |#1| as a call of its own. It opens a group, so the % values that Lua leaves in the variables for the nested call, namely the name, the % arguments and their number, are discarded when it ends. The nested call % raises the depth, does not apply \mykey{concept} and goes through % \myhlc|\@@_spfun_body:| with no outer separator. It cannot fail to parse, % since the first analysis already validated every level. % \begin{macrocode} \cs_new_protected:Nn \@@_spfun_nested:n { \group_begin: \@@_luafun_spfun_parse_and_set:v { l_@@_spfun_luaset_arg_ #1 _tl } \int_incr:N \l_@@_spfun_depth_int \bool_set_false:N \l_@@_spfun_concept_bool \int_set:NV \l_@@_spfun_nargs_int \l_@@_spfun_luaset_nargs_str \@@_spfun_body: \group_end: } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfun_exec:nn} % Receives the optional \mymeta{keys} in `|#1|' and the \mymarg{argument} % in `|#2|'. Parses first, reporting |spfun-invalid-arg| on error whatever % \mykey{silent} is. % \begin{macrocode} \cs_new_protected:Nn \@@_spfun_exec:nn { \keys_set:nn { spintent / spfun } {#1} \@@_luafun_spfun_parse_and_set:n { \exp_not:n {#2} } \str_if_eq:VnTF \l_@@_spfun_luaset_error_str { true } { \msg_error:nne { spintent } { spfun-invalid-arg } { \exp_not:n {#2} } } { \int_set:NV \l_@@_spfun_nargs_int \l_@@_spfun_luaset_nargs_str \int_set:Nn \l_@@_spfun_depth_int { 1 } \@@_spfun_mathml: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spfun} % The public command \ics*{spfun} % enforces \emph{math mode}, opens a local group, % and delegates to \myhlc|\@@_spfun_exec:nn|. % \begin{macrocode} \NewDocumentCommand \spfun { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_spfun_exec:nn {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spfun } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spfuncomp}}\label{cmd:spfuncomp} % % The user command |\spfuncomp| typesets the linear composition of % functions: |f\circ g|, |f\circ g\circ h|, |(f\circ g)| and the % composition applied to arguments, |(f\circ g)(x)|. The Lua function % \myhlc|\@@_luafun_spfuncomp_parse_and_set:n| splits the \mymarg{argument} % at the top-level |\circ|, or the Unicode character U+2218; every function is a name as in % |\spfun| and gets its literal |intent|, so % \mynvmath{MathCAT} neither announces a capital letter nor reads the letter h, U+210E, as % Planck's constant. The arguments of the application, if any, are left in % the |\spfun| variables and typeset with its own functions, so an % argument may be a call. The whole is tagged with a native % |\MathMLintent| whose |intent| is |_compuesta-con:infix($k1,$k2,...)|; the % application adds |$c($a1,...)| around it, with the composition tagged % |arg="c"|. With two or more arguments they are grouped under their own % |intent|, |_coma:infix($a1,$a2,...)|, tagged |arg="l"|, because % \mynvmath{MathCAT} drops the commas of a call whose head is not a single % letter. The |\circ| and the parentheses are silenced. % % \subsubsection{Definition of variables for \cs[no-index]{spfuncomp}} % % \begin{variable}[int] % { % \l_@@_spfuncomp_n_int, % \l_@@_spfuncomp_refs_tl, % \l_@@_spfuncomp_intent_tl, % \l_@@_spfuncomp_kv_tl, % \l_@@_spfuncomp_tipo_str, % \l_@@_spfuncomp_luaset_error_str, % \l_@@_spfuncomp_luaset_n_str, % \l_@@_spfuncomp_luaset_paren_str, % \l_@@_spfuncomp_luaset_apply_str, % } % Number of functions, the references and the |intent| being built, the % key-value list and the kind of the function being typeset, and the % values that Lua leaves: error flag, % number of functions, whether the % composition is in parentheses and whether it is applied to arguments. % \begin{macrocode} \int_new:N \l_@@_spfuncomp_n_int \tl_new:N \l_@@_spfuncomp_refs_tl \tl_new:N \l_@@_spfuncomp_intent_tl \tl_new:N \l_@@_spfuncomp_kv_tl \str_new:N \l_@@_spfuncomp_tipo_str \str_new:N \l_@@_spfuncomp_luaset_error_str \str_new:N \l_@@_spfuncomp_luaset_n_str \str_new:N \l_@@_spfuncomp_luaset_paren_str \str_new:N \l_@@_spfuncomp_luaset_apply_str % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spfuncomp}} % % \begin{macro}[int]{spfuncomp-invalid-arg} % The \enquote{error} |spfuncomp-invalid-arg| is raised when the argument is % not a composition of at least two function names, optionally in parentheses % and optionally applied to arguments written in parentheses, whatever the % value of \mykey{silent}. % \begin{macrocode} \msg_new:nnnn { spintent } { spfuncomp-invalid-arg } { Invalid~argument~'#1'~for~'\token_to_str:N \spfuncomp'~\msg_line_context:. } { The~argument~must~be~two~or~more~functions~joined~by~'\token_to_str:N \circ'. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spfuncomp}} % % \begin{macro}[int]{silent, unknown} % We define the \mymeta{key} \mykey[spfuncomp]{silent}. % \begin{macrocode} \keys_define:nn { spintent / spfuncomp } { silent .bool_set:N = \l_@@_spfuncomp_silent_bool, silent .initial:n = false, silent .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spfuncomp } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spfuncomp}} % % \begin{macro}[int]{\@@_spfuncomp_fun:n} % Function number |#1| of the chain. Its first letter is tagged |arg="k"| % with the literal |intent| of the name through % the shared queue. A subscript is typeset silenced; a prime is not; the % inverse exponent |^{-1}| is silenced as well. % \begin{macrocode} \cs_new_protected:Nn \@@_spfuncomp_fun:n { \tl_set:Ne \l_@@_spfuncomp_kv_tl { arg = " k #1 ", intent = "_ \use:c { l_@@_spfuncomp_luaset_intent_ #1 _str } -" } \@@_luafun_mrow_intent_arg:nn { } { \l_@@_spfuncomp_kv_tl } \str_set_eq:Nc \l_@@_spfuncomp_tipo_str { l_@@_spfuncomp_luaset_tipo_ #1 _str } \str_case:VnF \l_@@_spfuncomp_tipo_str { { sub } { \use:c { l_@@_spfuncomp_luaset_letra_ #1 _str } \c_math_subscript_token { \MathMLintent { :silent } { \use:c { l_@@_spfuncomp_luaset_sub_ #1 _str } } } } { prima } { \use:c { l_@@_spfuncomp_luaset_letra_ #1 _str } \use:c { l_@@_spfuncomp_luaset_sub_ #1 _str } } } { \use:c { l_@@_spfuncomp_luaset_letra_ #1 _str } } \str_set_eq:Nc \l_@@_spfuncomp_tipo_str { l_@@_spfuncomp_luaset_inv_ #1 _str } \str_if_eq:VnT \l_@@_spfuncomp_tipo_str { true } { \c_math_superscript_token { \MathMLintent { :silent } { -1 } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfuncomp_group:} % The composition, with its own |intent|, in parentheses when the argument % was written in parentheses. % \begin{macrocode} \cs_new_protected:Nn \@@_spfuncomp_group: { \tl_clear:N \l_@@_spfuncomp_refs_tl \int_step_inline:nn { \l_@@_spfuncomp_n_int } { \tl_put_right:Ne \l_@@_spfuncomp_refs_tl { \int_compare:nNnF { ##1 } = { 1 } { , } $k ##1 } } \tl_set:Ne \l_@@_spfuncomp_intent_tl { _compuesta-con:infix( \l_@@_spfuncomp_refs_tl ) } \MathMLintent { \l_@@_spfuncomp_intent_tl } { \str_if_eq:VnT \l_@@_spfuncomp_luaset_paren_str { true } { \MathMLintent { :silent } { \lparen } } \int_step_inline:nn { \l_@@_spfuncomp_n_int } { \int_compare:nNnF { ##1 } = { 1 } { \MathMLintent { :silent } { \circ } } \@@_spfuncomp_fun:n {##1} } \str_if_eq:VnT \l_@@_spfuncomp_luaset_paren_str { true } { \MathMLintent { :silent } { \rparen } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfuncomp_plain:} % Typesets the whole with no annotation for \mykey{silent}. % \begin{macrocode} \cs_new_protected:Nn \@@_spfuncomp_plain: { \str_if_eq:VnT \l_@@_spfuncomp_luaset_paren_str { true } { \lparen } \int_step_inline:nn { \l_@@_spfuncomp_n_int } { \int_compare:nNnF { ##1 } = { 1 } { \circ } \tl_use:c { l_@@_spfuncomp_luaset_print_ ##1 _tl } } \str_if_eq:VnT \l_@@_spfuncomp_luaset_paren_str { true } { \rparen } \str_if_eq:VnT \l_@@_spfuncomp_luaset_apply_str { true } { \lparen \@@_spfun_args_plain: \rparen } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfuncomp_mathml:} % Calls \myhlc|\@@_invisible_sep:| first. With \mykey{silent} the |intent| % is `|_-|'. Otherwise an application wraps the composition with |arg="c"|, and % the arguments in |$c($a1,...)|; the arguments are processed as in |\spfun|, % including calls nested in them. % \begin{macrocode} \cs_new_protected:Nn \@@_spfuncomp_mathml: { \@@_invisible_sep: \str_if_eq:VnT \l_@@_spfuncomp_luaset_apply_str { true } { \int_set:NV \l_@@_spfun_nargs_int \l_@@_spfun_luaset_nargs_str } \int_set:Nn \l_@@_spfun_depth_int { 1 } \str_set:Nn \l_@@_spfun_mode_str { school } \bool_set_false:N \l_@@_spfun_silent_bool \bool_set_false:N \l_@@_spfun_concept_bool \bool_if:NTF \l_@@_spfuncomp_silent_bool { \MathMLintent { _- } { \@@_inner_sep: \@@_spfuncomp_plain: } } { \str_if_eq:VnTF \l_@@_spfuncomp_luaset_apply_str { true } { \tl_clear:N \l_@@_spfun_refs_tl \int_step_inline:nn { \l_@@_spfun_nargs_int } { \tl_put_right:Ne \l_@@_spfun_refs_tl { \int_compare:nNnF { ##1 } = { 1 } { , } $ \@@_spfun_aname:n {##1} } } \int_compare:nNnTF { \l_@@_spfun_nargs_int } > { 1 } { \tl_set:Ne \l_@@_spfun_call_tl { $c ( $l ) } \MathMLintent { \l_@@_spfun_call_tl } { \MathMLarg { c } { \@@_inner_sep: \@@_spfuncomp_group: } \MathMLintent { :silent } { \lparen } \MathMLarg { l } { \@@_inner_sep: \MathMLintent { _coma:infix ( \l_@@_spfun_refs_tl ) } { \@@_spfun_args: } } \MathMLintent { :silent } { \rparen } } } { \tl_set:Ne \l_@@_spfun_call_tl { $c ( \l_@@_spfun_refs_tl ) } \MathMLintent { \l_@@_spfun_call_tl } { \MathMLarg { c } { \@@_inner_sep: \@@_spfuncomp_group: } \MathMLintent { :silent } { \lparen } \@@_spfun_args: \MathMLintent { :silent } { \rparen } } } } { \@@_spfuncomp_group: } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfuncomp_exec:nn} % Receives the optional \mymeta{keys} in `|#1|' and the \mymarg{argument} % in `|#2|'. Parses first, reporting |spfuncomp-invalid-arg| on error % whatever \mykey{silent} is. % \begin{macrocode} \cs_new_protected:Nn \@@_spfuncomp_exec:nn { \keys_set:nn { spintent / spfuncomp } {#1} \@@_luafun_spfuncomp_parse_and_set:n { \exp_not:n {#2} } \str_if_eq:VnTF \l_@@_spfuncomp_luaset_error_str { true } { \msg_error:nne { spintent } { spfuncomp-invalid-arg } { \exp_not:n {#2} } } { \int_set:NV \l_@@_spfuncomp_n_int \l_@@_spfuncomp_luaset_n_str \@@_spfuncomp_mathml: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spfuncomp} % The public command \ics*{spfuncomp} enforces \emph{math mode}, opens a % local group with the scope attribute, for the calls that an argument may contain, % and delegates to \myhlc|\@@_spfuncomp_exec:nn|. % \begin{macrocode} \NewDocumentCommand \spfuncomp { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_spfuncomp_exec:nn {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spfuncomp } } } % \end{macrocode} % \end{macro} % % \subsection{The commands \cs{spdom}, \cs{sprec} and \cs{spcod}}\label{cmd:spdom} % % The user commands |\spdom|, |\sprec| and |\spcod| typeset the domain % |\operatorname{Dom}(f)|, the range |\operatorname{Rec}(f)| and the % codomain |\operatorname{Cod}(f)| of a function. They share one % implementation, \myhlc|\@@_spdom_exec:nnnnn|, which receives the command % name, the operator, the spoken word, the \mymeta{keys} and the % \mymarg{argument}. The Lua function \myhlc|\@@_luafun_spdom_parse_and_set:n| % accepts exactly a function name, as |\spfun| does: a letter with an % optional subscript or primes, and the optional exponent |^{-1}|. It % leaves it in the shared \myhlc|\l_@@_geo_luaset_*| variables. The whole is tagged % with a native |\MathMLintent| whose |intent| is % |_[prefix-]dominio-de:prefix($f)|, with |recorrido| or |codominio| in place of |dominio| for the other two commands; the operator, % the parentheses are silenced and the name carries |arg="f"| wrapped in its % own literal |intent|, such as |_f-|, |_F-doble-prima-| or |_f-inversa-|, so % \mynvmath{MathCAT} neither announces a capital letter nor reads the % inverse as a power. No \mykey{read-cmd} nor \mykey{concept} keys exist: % the native reading of |Dom(f)| is not useful, and the concept is already % the spoken word. % % \subsubsection{Definition of variables for \cs[no-index]{spdom}} % % \begin{variable}[int] % { % \l_@@_spdom_cmd_str, % \l_@@_spdom_op_tl, % \l_@@_spdom_word_tl, % \l_@@_spdom_prefix_tl, % \l_@@_spdom_intent_arg_tl, % \l_@@_spdom_luaset_error_str, % } % Command name, operator and word, which each public % command sets, the value of the key \mykey[spdom]{prefix}, the |intent| % being built and the error flag set from Lua. % \begin{macrocode} \str_new:N \l_@@_spdom_cmd_str \tl_new:N \l_@@_spdom_op_tl \tl_new:N \l_@@_spdom_word_tl \tl_new:N \l_@@_spdom_prefix_tl \tl_new:N \l_@@_spdom_intent_arg_tl \str_new:N \l_@@_spdom_luaset_error_str % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spdom}} % % \begin{macro}[int]{spdom-invalid-arg} % The \enquote{error} |spdom-invalid-arg| is raised when the argument is % not a function name. It receives the command name and the argument, and % is raised whatever the value of \mykey{silent}. % \begin{macrocode} \msg_new:nnnn { spintent } { spdom-invalid-arg } { Invalid~argument~'#2'~for~'\c_backslash_str #1'~\msg_line_context:. } { The~argument~must~be~a~function~name. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spdom}} % % \begin{macro}[int]{prefix, silent, unknown} % We define the \mymeta{keys} \mykey[spdom]{prefix} and % \mykey[spdom]{silent}. % \begin{macrocode} \keys_define:nn { spintent / spdom } { prefix .code:n = { \@@_sanitize_affix:Nn \l_@@_spdom_prefix_tl {#1} }, prefix .initial:n = , prefix .value_required:n = true, silent .bool_set:N = \l_@@_spdom_silent_bool, silent .initial:n = false, silent .value_required:n = true, unknown .code:n = { \str_set_eq:NN \l_@@_command_name_str \l_@@_spdom_cmd_str \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spdom}} % % \begin{macro}[int]{\@@_spdom_name:} % Tags the name with |arg="f"| through a native |\MathMLarg|; inside it, a % native |\MathMLintent| gives the name its literal |intent|. Both groups % start with the inner separator, so each one gets its own || even % when the name is a single node. % \begin{macrocode} \cs_new_protected:Nn \@@_spdom_name: { \MathMLarg { f } { \@@_inner_sep: \MathMLintent { _ \l_@@_geo_luaset_intent_str - } { \@@_inner_sep: \@@_spfun_name_plain: } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spdom_mathml:} % Calls \myhlc|\@@_invisible_sep:| first, then wraps operator, % parentheses and name in a native |\MathMLintent|. With \mykey{silent} % set to |true| the |intent| is `|_-|' and nothing inside is annotated. % \begin{macrocode} \cs_new_protected:Nn \@@_spdom_mathml: { \@@_invisible_sep: \bool_if:NTF \l_@@_spdom_silent_bool { \MathMLintent { _- } { \@@_inner_sep: \operatorname { \l_@@_spdom_op_tl } \lparen \@@_spfun_name_plain: \rparen } } { \tl_set:Ne \l_@@_spdom_intent_arg_tl { _ \tl_if_empty:NF \l_@@_spdom_prefix_tl { \tl_use:N \l_@@_spdom_prefix_tl - } \l_@@_spdom_word_tl -de:prefix( $f ) } \MathMLintent { \l_@@_spdom_intent_arg_tl } { \MathMLintent { :silent } { \operatorname { \l_@@_spdom_op_tl } } \MathMLintent { :silent } { \lparen } \@@_spdom_name: \MathMLintent { :silent } { \rparen } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spdom_exec:nnnnn} % Receives the command name in `|#1|', the operator in `|#2|', the spoken % word in `|#3|', the optional \mymeta{keys} in `|#4|' and the % \mymarg{argument} in `|#5|'. Parses first, reporting % |spdom-invalid-arg| on error whatever \mykey{silent} is. % \begin{macrocode} \cs_new_protected:Nn \@@_spdom_exec:nnnnn { \str_set:Nn \l_@@_spdom_cmd_str {#1} \tl_set:Nn \l_@@_spdom_op_tl {#2} \tl_set:Nn \l_@@_spdom_word_tl {#3} \keys_set:nn { spintent / spdom } {#4} \@@_luafun_spdom_parse_and_set:n { \exp_not:n {#5} } \str_if_eq:VnTF \l_@@_spdom_luaset_error_str { true } { \msg_error:nnee { spintent } { spdom-invalid-arg } {#1} { \exp_not:n {#5} } } { \@@_spdom_mathml: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spdom, \sprec, \spcod} % The public commands \ics*{spdom}, \ics*{sprec} and \ics*{spcod} enforce % \emph{math mode}, open a local group, and delegate to \myhlc|\@@_spdom_exec:nnnnn|. % \begin{macrocode} \NewDocumentCommand \spdom { O{} m } { \mode_if_math:TF { \group_begin: \@@_spdom_exec:nnnnn { spdom } { Dom } { dominio } {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spdom } } } \NewDocumentCommand \sprec { O{} m } { \mode_if_math:TF { \group_begin: \@@_spdom_exec:nnnnn { sprec } { Rec } { recorrido } {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \sprec } } } \NewDocumentCommand \spcod { O{} m } { \mode_if_math:TF { \group_begin: \@@_spdom_exec:nnnnn { spcod } { Cod } { codominio } {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spcod } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spabs}}\label{cmd:spabs} % % The user command |\spabs| typesets the absolute value |\lvert x\rvert| and % reads it with the literal intent |valor-absoluto($x)|. Optionally, a second % literal intent |fin-valor-absoluto| announces where it ends. It has the keys % \mykey[spabs]{silent}, \mykey[spabs]{read-arg}, \mykey[spabs]{size} and % \mykey[spabs]{close}. % % \subsubsection{Definition of variables for \cs[no-index]{spabs}} % % \begin{macro}[int] % { % \l_@@_spabs_read_arg_tl, % \l_@@_spabs_size_str, % \l_@@_spabs_close_mode_str, % } % The token list variable \myhlc|\l_@@_spabs_read_arg_tl| stores the key % \mykey[spabs]{read-arg}, and the string variables % \myhlc|\l_@@_spabs_size_str| and \myhlc|\l_@@_spabs_close_mode_str| the % keys \mykey[spabs]{size} and \mykey[spabs]{close}. % \begin{macrocode} \tl_new:N \l_@@_spabs_read_arg_tl \str_new:N \l_@@_spabs_size_str \str_new:N \l_@@_spabs_close_mode_str % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\l_@@_spabs_delim_l_tl, \l_@@_spabs_delim_r_tl} % The opening and closing bars, with their size commands. % \begin{macrocode} \tl_new:N \l_@@_spabs_delim_l_tl \tl_new:N \l_@@_spabs_delim_r_tl % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\l_@@_spabs_luaset_close_str, \l_@@_spabs_close_bool} % The string variable \myhlc|\l_@@_spabs_luaset_close_str| is set by the % function \myhlc|\@@_luafun_spabs_close:n| and is |true| when the % argument, without spaces and without a leading sign, has more than one % token; the boolean variable % \myhlc|\l_@@_spabs_close_bool| tells whether the closing is read. % \begin{macrocode} \str_new:N \l_@@_spabs_luaset_close_str \bool_new:N \l_@@_spabs_close_bool % \end{macrocode} % \end{macro} % % \subsubsection{Definition of error messages for \cs[no-index]{spabs}} % % \begin{macro}[int]{spabs-empty-arg} % The \enquote{error} |spabs-empty-arg| is raised when the argument of % |\spabs| is missing or \emph{blank}. % \begin{macrocode} \msg_new:nnnn { spintent } { spabs-empty-arg } { Empty~argument~for~'\token_to_str:N \spabs'~\msg_line_context:. } { The~expression~inside~the~absolute~value~is~required. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spabs}} % % \begin{macro}[int]{silent, read-arg, size, close, unknown} % We define the \mymeta{keys} \mykey[spabs]{silent}, \mykey[spabs]{read-arg}, % \mykey[spabs]{size} and \mykey[spabs]{close}. % \begin{macrocode} \keys_define:nn { spintent / spabs } { silent .bool_set:N = \l_@@_spabs_silent_bool, silent .initial:n = false, silent .value_required:n = true, read-arg .code:n = { \@@_sanitize_affix:Nn \l_@@_spabs_read_arg_tl {#1} }, read-arg .initial:n = , read-arg .value_required:n = true, size .choices:nn = { none , big , Big , bigg , Bigg } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spabs_size_str { none } } {2} { \str_set:Nn \l_@@_spabs_size_str { big } } {3} { \str_set:Nn \l_@@_spabs_size_str { Big } } {4} { \str_set:Nn \l_@@_spabs_size_str { bigg } } {5} { \str_set:Nn \l_@@_spabs_size_str { Bigg } } } }, size .initial:n = none, size .value_required:n = true, size / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { size } { none,~big,~Big,~bigg,~Bigg } { \exp_not:n {#1} }, close .choices:nn = { auto , true , false } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spabs_close_mode_str { auto } } {2} { \str_set:Nn \l_@@_spabs_close_mode_str { true } } {3} { \str_set:Nn \l_@@_spabs_close_mode_str { false } } } }, close .initial:n = auto, close .value_required:n = true, close / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { close } { auto,~true,~false } { \exp_not:n {#1} }, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spabs } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spabs}} % % \begin{macro}[int]{\@@_spabs_set_delims:} % The function \myhlc|\@@_spabs_set_delims:| sets the bars from the key % \mykey[spabs]{size}: the bare |\lvert| and |\rvert| for |none|, and % |\bigl|, |\Bigl|, |\biggl| or |\Biggl|, and the closing versions, % before the bars otherwise. % \begin{macrocode} \cs_new_protected:Nn \@@_spabs_set_delims: { \str_case:Vn \l_@@_spabs_size_str { { none } { \tl_set:Nn \l_@@_spabs_delim_l_tl { \lvert } \tl_set:Nn \l_@@_spabs_delim_r_tl { \rvert } } { big } { \tl_set:Nn \l_@@_spabs_delim_l_tl { \bigl \lvert } \tl_set:Nn \l_@@_spabs_delim_r_tl { \bigr \rvert } } { Big } { \tl_set:Nn \l_@@_spabs_delim_l_tl { \Bigl \lvert } \tl_set:Nn \l_@@_spabs_delim_r_tl { \Bigr \rvert } } { bigg } { \tl_set:Nn \l_@@_spabs_delim_l_tl { \biggl \lvert } \tl_set:Nn \l_@@_spabs_delim_r_tl { \biggr \rvert } } { Bigg } { \tl_set:Nn \l_@@_spabs_delim_l_tl { \Biggl \lvert } \tl_set:Nn \l_@@_spabs_delim_r_tl { \Biggr \rvert } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spabs_set_close:n} % The function \myhlc|\@@_spabs_set_close:n| sets % \myhlc|\l_@@_spabs_close_bool| from \mykey[spabs]{close}: always % |true| with |true|, always |false| with |false|, and with |auto| |true| % when Lua reports that the argument `|#1|', without spaces and without a % leading |+| or |-|, has more than one token. % \begin{macrocode} \cs_new_protected:Nn \@@_spabs_set_close:n { \bool_set_false:N \l_@@_spabs_close_bool \str_case:Vn \l_@@_spabs_close_mode_str { { true } { \bool_set_true:N \l_@@_spabs_close_bool } { auto } { \@@_luafun_spabs_close:n { \exp_not:n {#1} } \str_if_eq:VnT \l_@@_spabs_luaset_close_str { true } { \bool_set_true:N \l_@@_spabs_close_bool } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spabs_exec:nn} % The function \myhlc|\@@_spabs_exec:nn| sets the \mymeta{keys} in `|#1|', % sets the delimiters with \myhlc|\@@_spabs_set_delims:| and calls % \myhlc|\@@_spabs_mathml:n| with the argument in `|#2|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spabs_exec:nn { \keys_set:nn { spintent / spabs } {#1} \@@_spabs_set_delims: \@@_spabs_mathml:n {#2} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spabs_mathml:n} % The function \myhlc|\@@_spabs_mathml:n| prints the absolute value of `|#1|' % inside one native |\MathMLintent|, preceded by the outer ghost % \myhlc|\@@_invisible_sep:| and followed by the inner ghost % \myhlc|\@@_inner_sep:|. With \mykey{silent}|=true| the % intent is `|_-|'; with \mykey{read-arg} given it is % |_|\mymeta{read-arg}|:nofix|; otherwise it is |_valor-absoluto($x)|. In this % last case, when \myhlc|\@@_spabs_set_close:n| sets the closing, a native % |\MathMLintent| with the literal intent |_fin-valor-absoluto| follows the bars % as a sibling, holding \myhlc|\@@_inner_sep:| and an invisible character, and % the outer ghost is omitted because the formula already has two top-level % nodes. % \begin{macrocode} \cs_new_protected:Nn \@@_spabs_mathml:n { \bool_if:NTF \l_@@_spabs_silent_bool { \@@_invisible_sep: \MathMLintent { _- } { \l_@@_spabs_delim_l_tl \@@_inner_sep: #1 \l_@@_spabs_delim_r_tl } } { \tl_if_empty:NTF \l_@@_spabs_read_arg_tl { \@@_spabs_set_close:n {#1} \bool_if:NF \l_@@_spabs_close_bool { \@@_invisible_sep: } \MathMLintent { _valor-absoluto($x) } { \l_@@_spabs_delim_l_tl \MathMLarg { x } { \@@_inner_sep: #1 } \l_@@_spabs_delim_r_tl } \bool_if:NT \l_@@_spabs_close_bool { \MathMLintent { _fin-valor-absoluto } { \@@_inner_sep: \c_@@_invisible_sep_int } } } { \@@_invisible_sep: \MathMLintent { _ \tl_use:N \l_@@_spabs_read_arg_tl :nofix } { \l_@@_spabs_delim_l_tl \@@_inner_sep: #1 \l_@@_spabs_delim_r_tl } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spabs} % The public command \ics*{spabs} enforces \emph{math mode}, opens a local % group and delegates execution to \myhlc|\@@_spabs_exec:nn|. % \begin{macrocode} \NewDocumentCommand \spabs { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \tl_if_blank:nTF {#2} { \msg_error:nn { spintent } { spabs-empty-arg } } { \@@_spabs_exec:nn {#1} {#2} } \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spabs } } } % \end{macrocode} % \end{macro} % % \subsection{The function \cs[no-index]{__spintent_mathmlarg_point:nnn}}\label{fun:mathmlarg-point} % % \begin{variable}[int] % { % \l_@@_mathmlarg_point_ref_tl, % \l_@@_mathmlarg_point_mode_str, % \l_@@_mathmlarg_point_kv_tl, % \l_@@_mathmlarg_point_silent_bool, % } % Scratch variables for \myhlc|\@@_mathmlarg_point:nnn|: the % reference name in `|#1|', and the calling command's own % \mykey{read-cmd}/\mykey{silent} state, looked up by the name in `|#2|'. % \begin{macrocode} \tl_new:N \l_@@_mathmlarg_point_ref_tl \str_new:N \l_@@_mathmlarg_point_mode_str \tl_new:N \l_@@_mathmlarg_point_kv_tl \bool_new:N \l_@@_mathmlarg_point_silent_bool % \end{macrocode} % \end{variable} % % \begin{macro}[int]{\@@_mathmlarg_point:nnn, \@@_mathmlarg_point:nnV} % Annotates the point in `|#3|' with |arg| and, when applicable, a literal % |intent|, without wrapping it in its own ||. Concept |intent| % is passed empty on purpose: the caller applies it externally, via a % native |\MathMLintent|. % % With \myhlc|\l_@@_geo_luaset_tipo_str| equal to |sub|, for % \mykey{read-cmd}|=school| the letter is tagged with % \myhlc|\@@_luafun_mrow_intent_arg:nn| and the subscript is appended % separately, silenced. A subscript of more than one digit is typeset plain, % with one extra silent claim per digit. For \mykey{read-cmd}|=mathcat|, the % letter and the subscript are typeset together and tagged as a whole with a % native |\MathMLarg|. % \begin{macrocode} \cs_new_protected:Nn \@@_mathmlarg_point:nnn { \tl_set:Nn \l_@@_mathmlarg_point_ref_tl {#1} \@@_luafun_geo_point_parse_and_set:n { \exp_not:n {#3} } \bool_set_eq:Nc \l_@@_mathmlarg_point_silent_bool { l_@@_ #2 _silent_bool } \str_set_eq:Nc \l_@@_mathmlarg_point_mode_str { l_@@_ #2 _mode_str } \@@_invisible_sep: \str_case:VnF \l_@@_geo_luaset_tipo_str { { sub } { \str_if_eq:VnTF \l_@@_mathmlarg_point_mode_str { school } { \tl_set:Ne \l_@@_mathmlarg_point_kv_tl { arg = "\l_@@_mathmlarg_point_ref_tl", intent = "_ \l_@@_geo_luaset_intent_str -" } \@@_luafun_mrow_intent_arg:nn { } { \l_@@_mathmlarg_point_kv_tl } \int_compare:nNnTF { \str_count:N \l_@@_geo_luaset_sub_str } > { 1 } { \int_step_inline:nnnn { 1 } { 1 } { \str_count:N \l_@@_geo_luaset_sub_str } { \@@_luafun_mrow_intent_arg:nn { } { intent = ":silent" } } { \l_@@_geo_luaset_letra_str \c_math_subscript_token { \l_@@_geo_luaset_sub_str } } } { { \l_@@_geo_luaset_letra_str \c_math_subscript_token { \MathMLintent { :silent } { \l_@@_geo_luaset_sub_str } } } } } { { \@@_inner_sep: \MathMLarg { \l_@@_mathmlarg_point_ref_tl } { { \l_@@_geo_luaset_letra_str \c_math_subscript_token { \l_@@_geo_luaset_sub_str } } } } } } % \end{macrocode} % % With \myhlc|\l_@@_geo_luaset_tipo_str| equal to |prima|, the same % distinction applies: \mykey{read-cmd}|=school| tags the letter with a % literal |intent| and appends the mark unsilenced, since a prime is not % spoken on the base and there is nothing to repeat; \mykey{read-cmd}|=mathcat| tags % letter and mark together through a native |\MathMLarg|. % \begin{macrocode} { prima } { \str_if_eq:VnTF \l_@@_mathmlarg_point_mode_str { school } { \tl_set:Ne \l_@@_mathmlarg_point_kv_tl { arg = "\l_@@_mathmlarg_point_ref_tl", intent = "_ \l_@@_geo_luaset_intent_str -" } \@@_luafun_mrow_intent_arg:nn { } { \l_@@_mathmlarg_point_kv_tl } { \l_@@_geo_luaset_letra_str \l_@@_geo_luaset_sub_str } } { { \@@_inner_sep: \MathMLarg { \l_@@_mathmlarg_point_ref_tl } { { \l_@@_geo_luaset_letra_str \l_@@_geo_luaset_sub_str } } } } } } % \end{macrocode} % % Otherwise, the same distinction applies to the % combined \myhlc|\l_@@_geo_luaset_print_tl|: the shared queue for |school| % or when \mykey{silent} is |true|, and a native |\MathMLarg| for |mathcat|. % The two mechanisms are never mixed in the same formula. % \begin{macrocode} { \bool_if:NTF \l_@@_mathmlarg_point_silent_bool { \tl_set:Ne \l_@@_mathmlarg_point_kv_tl { arg = "\l_@@_mathmlarg_point_ref_tl", intent = "_-" } \@@_luafun_mrow_intent_arg:nn { } % must be empty { \l_@@_mathmlarg_point_kv_tl } { \l_@@_geo_luaset_print_tl } } { \str_if_eq:VnTF \l_@@_mathmlarg_point_mode_str { school } { \tl_set:Ne \l_@@_mathmlarg_point_kv_tl { arg = "\l_@@_mathmlarg_point_ref_tl", intent = "_ \l_@@_geo_luaset_intent_str -" } \@@_luafun_mrow_intent_arg:nn { } % must be empty { \l_@@_mathmlarg_point_kv_tl } { \l_@@_geo_luaset_print_tl } } { \MathMLarg { \l_@@_mathmlarg_point_ref_tl } { \l_@@_geo_luaset_print_tl } } } } } \cs_generate_variant:Nn \@@_mathmlarg_point:nnn { nnV } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spPoint}}\label{cmd:spPoint} % % The user-level command |\spPoint| provides a semantic interface for % a single geometric point. It delegates argument validation to the % Lua module \myluamod{}, constructing a valid \mynvmath{MathCAT} % intent. % % \subsubsection{Definition of variables for \cs[no-index]{spPoint}} % % \begin{variable}[int] % { % \l_@@_spPoint_mode_str, % \l_@@_spPoint_prefix_tl, % \l_@@_spPoint_intent_arg_tl, % } % Internal variables holding the values set by % \mykey[spPoint]{read-cmd} and \mykey[spPoint]{prefix}. % \begin{macrocode} \str_new:N \l_@@_spPoint_mode_str \tl_new:N \l_@@_spPoint_prefix_tl \tl_new:N \l_@@_spPoint_intent_arg_tl % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spPoint}} % % \begin{macro}[int]{spPoint-invalid-point} % The \enquote{error} |spPoint-invalid-point| is raised when the % argument passed in `|#1|' is not a single uppercase letter with an % optional subscript or prime modifier. % \begin{macrocode} \msg_new:nnnn { spintent } { spPoint-invalid-point } { Invalid~argument~'#1'~for~'\token_to_str:N \spPoint'~\msg_line_context:. } { The~argument~must~be~a~single~uppercase~letter~with~an~optional~subscript~(\_{...})~ or~prime~('). } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spPoint}} % % \begin{macro}[int]{read-cmd, prefix, silent, concept, unknown} % We define the \mymeta{keys} \mykey[spPoint]{read-cmd}, % \mykey[spPoint]{prefix}, \mykey[spPoint]{silent} and % \mykey[spPoint]{concept}. % \begin{macrocode} \keys_define:nn { spintent / spPoint } { read-cmd .choices:nn = { school , mathcat } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spPoint_mode_str { school } } {2} { \str_set:Nn \l_@@_spPoint_mode_str { mathcat } } } }, read-cmd .initial:n = school, read-cmd .value_required:n = true, read-cmd / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-cmd } { school,~mathcat } { \exp_not:n {#1} }, prefix .code:n = { \@@_sanitize_affix:Nn \l_@@_spPoint_prefix_tl {#1} }, prefix .initial:n = , prefix .value_required:n = true, silent .bool_set:N = \l_@@_spPoint_silent_bool, silent .initial:n = false, silent .value_required:n = true, concept .bool_set:N = \l_@@_spPoint_concept_bool, concept .initial:n = true, concept .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spPoint } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spPoint}} % % \begin{macro}[int]{\@@_spPoint_exec:nnn} % The internal function \myhlc|\@@_spPoint_exec:nnn| processes the % \mymarg{argument} passed in `|#2|'. It calls % \myhlc|\@@_luafun_geo_point_parse_and_set:n|, reporting % |spPoint-invalid-point| on error. If \mykey{silent} is |true| or % \mykey{concept} is |false|, it sets \myhlc|\l_@@_spPoint_intent_arg_tl| to % |_($pt)|, or to `|_-|' when \mykey{silent} is |true|, whatever % \mykey{read-cmd} is. Otherwise it sets it to |_punto:prefix($pt)|, with % \mykey{prefix} placed after the leading |_| when given. Finally it calls % \myhlc|\@@_spPoint_mathml:n|. % % Before processing the \mymarg{argument}, it sets the optional \mymeta{keys} passed in `|#1|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spPoint_exec:nnn { \keys_set:nn { spintent / spPoint } {#1} \@@_luafun_geo_point_parse_and_set:n { \exp_not:n {#2} } \str_if_eq:VnT \l_@@_geo_luaset_error_str { true } { \msg_error:nne { spintent } { spPoint-invalid-point } { \exp_not:n {#2} } } \bool_if:nTF { \bool_lazy_or_p:nn { \l_@@_spPoint_silent_bool } { ! \l_@@_spPoint_concept_bool } } { \tl_set:Nn \l_@@_spPoint_intent_arg_tl { _($pt) } \bool_if:NT \l_@@_spPoint_silent_bool { \tl_set:Nn \l_@@_spPoint_intent_arg_tl { _- } } } { \tl_set:Ne \l_@@_spPoint_intent_arg_tl { _ \tl_if_empty:NF \l_@@_spPoint_prefix_tl { \tl_use:N \l_@@_spPoint_prefix_tl - } punto:prefix($pt) } } \@@_spPoint_mathml:n {#2} \tl_if_novalue:nF {#3} { \MathMLintent { :silent } { \c_@@_invisible_sep_int } % prevent "por" \tl_set:Nn \l_@@_spcoord_raw_arg_tl {#3} \@@_luafun_spcoord_parse_and_set:n { \exp_not:n {#3} } \str_if_eq:VnT \l_@@_spcoord_luaset_error_str { true } { \msg_error:nne { spintent } { spcoord-invalid-arg } { \exp_not:n {#3} } } \@@_spcoord_set_wrap_parens_size: \str_if_eq:VnF \l_@@_spcoord_luaset_error_str { true } { \@@_spcoord_mathml:N \l_@@_spPoint_silent_bool } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spPoint_mathml:n} % The function \myhlc|\@@_spPoint_mathml:n| calls \myhlc|\@@_invisible_sep:| % and then wraps \myhlc|\@@_mathmlarg_point:nnn|, which parses `|#1|', tags it % |arg="pt"| and silences a subscript or prime, inside a native % |\MathMLintent| carrying \myhlc|\l_@@_spPoint_intent_arg_tl| as the concept % |intent|. Inside that group it calls \myhlc|\@@_inner_sep:| first. % \begin{macrocode} \cs_new_protected:Nn \@@_spPoint_mathml:n { \@@_invisible_sep: \MathMLintent { \l_@@_spPoint_intent_arg_tl } { \@@_inner_sep: \@@_mathmlarg_point:nnn { pt } { spPoint } {#1} } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spPoint} % The public command \ics*{spPoint} enforces \emph{math mode}, opens a local % group, and delegates execution to \myhlc|\@@_spPoint_exec:nnn|. % \begin{macrocode} \NewDocumentCommand \spPoint { O{} m d() } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_spPoint_exec:nnn {#1} {#2} {#3} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spPoint } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spvec}}\label{cmd:spvec} % % The user command |\spvec| supports two types of notation for the % input \mymarg{argument}: a pair of points, which gets |\overrightarrow|, % or a one-letter name, which gets |\vec| instead. Both % notations are detected by the Lua module. An optional % coordinate, in parentheses, may follow the \mymarg{argument}. % % \subsubsection{Definition of variables for \cs[no-index]{spvec}} % % \begin{variable}[int] % { % \l_@@_spvec_mode_str, % \l_@@_spvec_prefix_tl, % \l_@@_spvec_intent_arg_tl, % \l_@@_spvec_kv_tl, % \l_@@_spvec_pt_seq, % \l_@@_spvec_refs_tl, % \l_@@_spvec_pt_idx_int, % } % The variable \myhlc|\l_@@_spvec_mode_str| stores the value set by % the key \mykey[spvec]{read-cmd} and the variable % \myhlc|\l_@@_spvec_prefix_tl| stores the value set by the key % \mykey[spvec]{prefix}. % \begin{macrocode} \str_new:N \l_@@_spvec_mode_str \tl_new:N \l_@@_spvec_prefix_tl \tl_new:N \l_@@_spvec_intent_arg_tl \tl_new:N \l_@@_spvec_kv_tl \seq_new:N \l_@@_spvec_pt_seq \tl_new:N \l_@@_spvec_refs_tl \int_new:N \l_@@_spvec_pt_idx_int % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spvec}} % % \begin{macro}[int]{spvec-invalid-arg} % The \enquote{error} |spvec-invalid-arg| is raised when the % argument fails to parse, when a one-letter name carries a prime, % or when the point list is not exactly two points. % \begin{macrocode} \msg_new:nnnn { spintent } { spvec-invalid-arg } { Invalid~argument~'#1'~for~'\token_to_str:N \spvec'~\msg_line_context:. } { The~argument~must~be~a~lowercase~letter~or~two~points~separated~by~a~comma. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spvec}} % % \begin{macro}[int]{silent, concept, autofit, read-cmd, prefix, unknown} % We define the \mymeta{keys} \mykey[spvec]{silent}, % \mykey[spvec]{concept}, \mykey[spvec]{autofit}, % \mykey[spvec]{read-cmd} and \mykey[spvec]{prefix}. % \begin{macrocode} \keys_define:nn { spintent / spvec } { silent .bool_set:N = \l_@@_spvec_silent_bool, silent .initial:n = false, silent .value_required:n = true, concept .bool_set:N = \l_@@_spvec_concept_bool, concept .initial:n = true, concept .value_required:n = true, autofit .bool_set:N = \l_@@_spvec_autofit_bool, autofit .initial:n = false, autofit .value_required:n = true, read-cmd .choices:nn = { school , mathcat } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spvec_mode_str { school } } {2} { \str_set:Nn \l_@@_spvec_mode_str { mathcat } } } }, read-cmd .initial:n = school, read-cmd .value_required:n = true, read-cmd / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-cmd } { school,~mathcat } { \exp_not:n {##1} }, prefix .code:n = { \@@_sanitize_affix:Nn \l_@@_spvec_prefix_tl {#1} }, prefix .initial:n = { }, prefix .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spvec } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spvec}} % % \begin{macro}[int]{\@@_spvec_exec:nnn} % The internal function \myhlc|\@@_spvec_exec:nnn| processes the % \mymarg{argument} passed in `|#2|'. Calls % \myhlc|\@@_luafun_geo_parse_and_set:n|, reporting % |spvec-invalid-arg| on error, on a proper name with % \myhlc|\l_@@_geo_luaset_tipo_str| equal to |prima|, since a one-letter % vector cannot carry a prime, or on a point list that is not % exactly two points. Otherwise calls \myhlc|\@@_spvec_mathml:n|. % % Before processing the \mymarg{argument}, it sets the optional \mymeta{keys} passed in `|#1|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spvec_exec:nnn { \keys_set:nn { spintent / spvec } {#1} \@@_luafun_geo_parse_and_set:n { \exp_not:n {#2} } \str_if_eq:VnT \l_@@_geo_luaset_is_name_str { true } { \str_if_eq:VnT \l_@@_geo_luaset_tipo_str { prima } { \str_set:Nn \l_@@_geo_luaset_error_str { true } } } \str_if_eq:VnT \l_@@_geo_luaset_is_name_str { false } { \int_compare:nNnF { \clist_count:N \l_@@_geo_luaset_points_clist } = { 2 } { \str_set:Nn \l_@@_geo_luaset_error_str { true } } } \str_if_eq:VnTF \l_@@_geo_luaset_error_str { true } { \msg_error:nne { spintent } { spvec-invalid-arg } { \exp_not:n {#2} } } { \@@_spvec_mathml:n {#2} } \tl_if_novalue:nF {#3} { \bool_if:NTF \l_@@_spvec_silent_bool { \MathMLintent { :silent } { \equal } } { \equal } % vector notation =( , ) % prevent "intervalo" \MathMLintent { :silent } { \c_@@_invisible_sep_int } \tl_set:Nn \l_@@_spcoord_raw_arg_tl {#3} \@@_luafun_spcoord_parse_and_set:n { \exp_not:n {#3} } \str_if_eq:VnT \l_@@_spcoord_luaset_error_str { true } { \msg_error:nne { spintent } { spcoord-invalid-arg } { \exp_not:n {#3} } } \@@_spcoord_set_wrap_parens_size: \str_if_eq:VnF \l_@@_spcoord_luaset_error_str { true } { \@@_spcoord_mathml:N \l_@@_spvec_silent_bool } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spvec_mathml:n} % The function \myhlc|\@@_spvec_mathml:n| branches on % \myhlc|\l_@@_geo_luaset_is_name_str|. % % For a proper name that is a single letter with no prime, the concept |intent| is `|_-|' % when \mykey{silent} is |true|, whatever \mykey{read-cmd} is, |_($pt)| when % \mykey{concept} is |false|, and otherwise |_vector:prefix($pt)|, with % \mykey{prefix} placed after the leading |_| when given. With % \myhlc|\l_@@_geo_luaset_tipo_str| equal to |sub|, \mykey{read-cmd}|=school| % tags |\vec{letra}| with \myhlc|\@@_luafun_mrow_intent_arg:nn| and a literal % |kv|, and appends the subscript silenced; \mykey{read-cmd}|=mathcat| tags % |\vec{...}| with a native |\MathMLarg| and silences the subscript only under % \mykey{silent}. The key \mykey{autofit} inserts a |\vphantom{b}| inside % |\vec{...}| to normalize its height. Otherwise, the same |kv| % logic wraps |\vec{print\_tl}| as a whole. % \begin{macrocode} \cs_new_protected:Nn \@@_spvec_mathml:n { \str_if_eq:VnTF \l_@@_geo_luaset_is_name_str { true } { \bool_if:nTF { \bool_lazy_or_p:nn { \l_@@_spvec_silent_bool } { ! \l_@@_spvec_concept_bool } } { \tl_set:Nn \l_@@_spvec_intent_arg_tl { _($pt) } \bool_if:NT \l_@@_spvec_silent_bool { \tl_set:Nn \l_@@_spvec_intent_arg_tl { _- } } } { \tl_set:Ne \l_@@_spvec_intent_arg_tl { _ \tl_if_empty:NF \l_@@_spvec_prefix_tl { \tl_use:N \l_@@_spvec_prefix_tl - } vector:prefix($pt) } } \str_case:VnF \l_@@_geo_luaset_tipo_str { { sub } { \str_if_eq:VnTF \l_@@_spvec_mode_str { school } { \bool_if:NTF \l_@@_spvec_silent_bool { \tl_set:Nn \l_@@_spvec_kv_tl { arg = "pt", intent = "_-" } } { \tl_set:Ne \l_@@_spvec_kv_tl { arg = "pt", intent = "_ \l_@@_geo_luaset_intent_str -" } } \@@_invisible_sep: \MathMLintent { \l_@@_spvec_intent_arg_tl } { \@@_inner_sep: \@@_luafun_mrow_intent_arg:nn { } { \l_@@_spvec_kv_tl } { \vec { \bool_if:NT \l_@@_spvec_autofit_bool { \vphantom{ b } } \l_@@_geo_luaset_letra_str } \c_math_subscript_token { \MathMLintent { :silent } { \l_@@_geo_luaset_sub_str } } } } } { \@@_invisible_sep: \MathMLintent { \l_@@_spvec_intent_arg_tl } { \@@_inner_sep: \vec { \bool_if:NT \l_@@_spvec_autofit_bool { \vphantom{ b } } \MathMLarg{ pt } { \l_@@_geo_luaset_letra_str } } \bool_if:NT \l_@@_spvec_silent_bool { \c_math_subscript_token { \l_@@_geo_luaset_sub_str } } } \bool_if:NF \l_@@_spvec_silent_bool { \c_math_subscript_token { \l_@@_geo_luaset_sub_str } } } } } { \bool_if:NTF \l_@@_spvec_silent_bool { \tl_set:Nn \l_@@_spvec_kv_tl { arg = "pt", intent = "_-" } } { \tl_set:Ne \l_@@_spvec_kv_tl { arg = "pt", intent = "_ \l_@@_geo_luaset_intent_str -" } } \@@_invisible_sep: \MathMLintent { \l_@@_spvec_intent_arg_tl } { \@@_inner_sep: \@@_luafun_mrow_intent_arg:nn { } { \l_@@_spvec_kv_tl } { \vec { \bool_if:NT \l_@@_spvec_autofit_bool { \vphantom{ b } } \l_@@_geo_luaset_print_tl } } } } } % \end{macrocode} % For a pair of points, \myhlc|\l_@@_geo_luaset_points_clist| is % split into \myhlc|\l_@@_spvec_pt_seq| and walked to build % \myhlc|\l_@@_spvec_refs_tl|, the |$pts1,$pts2| reference list. % The concept |intent| is `|_-|' when \mykey{silent} is |true|, % |_(refs)| when \mykey{concept} is |false| or % |_vector:prefix(refs)| otherwise, applied with a native |\MathMLintent| wrapping % |\overrightarrow|. The sequence is walked again, calling % \myhlc|\@@_mathmlarg_point:nnn| once per point. % \begin{macrocode} { \seq_set_from_clist:NN \l_@@_spvec_pt_seq \l_@@_geo_luaset_points_clist \tl_clear:N \l_@@_spvec_refs_tl \int_zero:N \l_@@_spvec_pt_idx_int \seq_map_inline:Nn \l_@@_spvec_pt_seq { \int_incr:N \l_@@_spvec_pt_idx_int \tl_if_empty:NF \l_@@_spvec_refs_tl { \tl_put_right:Nn \l_@@_spvec_refs_tl { , } } \tl_put_right:Ne \l_@@_spvec_refs_tl { $ pts \int_use:N \l_@@_spvec_pt_idx_int } } \bool_if:nTF { \bool_lazy_or_p:nn { \l_@@_spvec_silent_bool } { ! \l_@@_spvec_concept_bool } } { \tl_set:Ne \l_@@_spvec_intent_arg_tl { _( \l_@@_spvec_refs_tl ) } \bool_if:NT \l_@@_spvec_silent_bool { \tl_set:Nn \l_@@_spvec_intent_arg_tl { _- } } } { \tl_set:Ne \l_@@_spvec_intent_arg_tl { _ \tl_if_empty:NF \l_@@_spvec_prefix_tl { \tl_use:N \l_@@_spvec_prefix_tl - } vector:prefix( \l_@@_spvec_refs_tl ) } } \@@_invisible_sep: \MathMLintent { \l_@@_spvec_intent_arg_tl } { \@@_inner_sep: \overrightarrow { \int_zero:N \l_@@_spvec_pt_idx_int \seq_map_inline:Nn \l_@@_spvec_pt_seq { \int_incr:N \l_@@_spvec_pt_idx_int \@@_mathmlarg_point:nnn { pts \int_use:N \l_@@_spvec_pt_idx_int } { spvec } { ##1 } } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spvec} % The public command \ics*{spvec} enforces \emph{math mode}, opens a % local group, and delegates execution to \myhlc|\@@_spvec_exec:nnn|. % \begin{macrocode} \NewDocumentCommand \spvec { O{} m d() } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_spvec_exec:nnn {#1} {#2} {#3} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spvec } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{sprecta}}\label{cmd:sprecta} % % The user command |\sprecta| supports two types of notation for the % input \mymarg{argument}: when the argument contains two points separated by % an optional comma, |\overleftrightarrow| is applied; when the % argument is a single point without a comma or a lowercase letter, it is % formatted without any visual decorator. Both notations are % detected by the Lua module. % % \subsubsection{Definition of variables for \cs[no-index]{sprecta}} % % \begin{variable}[int] % { % \l_@@_sprecta_mode_str, % \l_@@_sprecta_prefix_tl, % \l_@@_sprecta_pt_seq, % \l_@@_sprecta_refs_tl, % \l_@@_sprecta_intent_arg_tl, % \l_@@_sprecta_kv_tl, % \l_@@_sprecta_pt_idx_int, % } % The variable \myhlc|\l_@@_sprecta_mode_str| store the values set by % the key \mykey[sprecta]{read-cmd} and the variable \myhlc|\l_@@_sprayo_prefix_tl| % store the value set by the key \mykey[sprecta]{prefix}. % \begin{macrocode} \str_new:N \l_@@_sprecta_mode_str \tl_new:N \l_@@_sprecta_prefix_tl \seq_new:N \l_@@_sprecta_pt_seq \tl_new:N \l_@@_sprecta_refs_tl \tl_new:N \l_@@_sprecta_intent_arg_tl \tl_new:N \l_@@_sprecta_kv_tl \int_new:N \l_@@_sprecta_pt_idx_int % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{sprecta}} % % \begin{macro}[int]{sprecta-invalid-points} % The \enquote{error} |sprecta-invalid-points| is raised when the % argument matches neither the two-point notation nor the % name notation. % \begin{macrocode} \msg_new:nnnn { spintent } { sprecta-invalid-points } { Invalid~argument~'#1'~for~'\token_to_str:N \sprecta'~\msg_line_context:. } { The~argument~must~be~a~letter~or~two~points~separated~by~a~comma. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{sprecta}} % % \begin{macro}[int]{read-cmd, prefix, silent, concept, unknown} % We define the \mymeta{keys} \mykey[sprecta]{read-cmd}, \mykey[sprecta]{prefix}, % \mykey[sprecta]{silent} and \mykey[sprecta]{concept}. % \begin{macrocode} \keys_define:nn { spintent / sprecta } { read-cmd .choices:nn = { school , mathcat } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_sprecta_mode_str { school } } {2} { \str_set:Nn \l_@@_sprecta_mode_str { mathcat } } } }, read-cmd .initial:n = school, read-cmd .value_required:n = true, read-cmd / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-cmd } { school,~mathcat } { \exp_not:n {#1} }, prefix .code:n = { \@@_sanitize_affix:Nn \l_@@_sprecta_prefix_tl {#1} }, prefix .initial:n = , prefix .value_required:n = true, silent .bool_set:N = \l_@@_sprecta_silent_bool, silent .initial:n = false, silent .value_required:n = true, concept .bool_set:N = \l_@@_sprecta_concept_bool, concept .initial:n = true, concept .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { sprecta } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{sprecta}} % % \begin{macro}[int]{\@@_sprecta_exec:nn} % The internal function \myhlc|\@@_sprecta_exec:nn| processes the % \mymarg{argument} passed in `|#2|'. Calls % \myhlc|\@@_luafun_geo_parse_and_set:n|, reporting % |sprecta-invalid-points| on error, then % \myhlc|\@@_sprecta_mathml:n|. % % Before processing the \mymarg{argument}, it sets the optional \mymeta{keys} passed in `|#1|'. % \begin{macrocode} \cs_new_protected:Nn \@@_sprecta_exec:nn { \keys_set:nn { spintent / sprecta } {#1} \@@_luafun_geo_parse_and_set:n { \exp_not:n {#2} } \str_if_eq:VnT \l_@@_geo_luaset_error_str { true } { \msg_error:nne { spintent } { sprecta-invalid-points } { \exp_not:n {#2} } } \@@_sprecta_mathml:n {#2} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_sprecta_mathml:n} % The function \myhlc|\@@_sprecta_mathml:n| branches on % \myhlc|\l_@@_geo_luaset_is_name_str|. For a proper name, such as |m| or |l|, % the concept |intent| is |_($pt)| when \mykey{silent} is |true| \emph{or} % \mykey{concept} is |false|, and |_recta:prefix($pt)| otherwise, with % \mykey{prefix} placed after the leading |_| when given; with \mykey{silent}% % |=true| and \mykey{read-cmd} not |school| it is `|_-|'. With % \mykey{read-cmd}|=school|, the |sub| and |prima| cases tag the letter with a % literal |kv| that is `|_-|' when \mykey{silent} is |true|. % % With \myhlc|\l_@@_geo_luaset_tipo_str| equal to |sub|, % \mykey{read-cmd}|=school| tags the base through % \myhlc|\@@_luafun_mrow_intent_arg:nn| with a literal |kv|, % the subscript appended and silenced; \mykey{read-cmd}|=mathcat| tags base % and subscript together through a native |\MathMLarg| instead. % \begin{macrocode} \cs_new_protected:Nn \@@_sprecta_mathml:n { \str_if_eq:VnTF \l_@@_geo_luaset_is_name_str { true } { \@@_invisible_sep: \bool_if:nTF { \bool_lazy_or_p:nn { \l_@@_sprecta_silent_bool } { ! \l_@@_sprecta_concept_bool } } { \tl_set:Nn \l_@@_sprecta_intent_arg_tl { _($pt) } \bool_if:NT \l_@@_sprecta_silent_bool { \str_if_eq:VnF \l_@@_sprecta_mode_str { school } { \tl_set:Nn \l_@@_sprecta_intent_arg_tl { _- } } } } { \tl_set:Ne \l_@@_sprecta_intent_arg_tl { _ \tl_if_empty:NF \l_@@_sprecta_prefix_tl { \tl_use:N \l_@@_sprecta_prefix_tl - } recta:prefix($pt) } } \str_case:VnF \l_@@_geo_luaset_tipo_str { { sub } { \str_if_eq:VnTF \l_@@_sprecta_mode_str { school } { \bool_if:NTF \l_@@_sprecta_silent_bool { \tl_set:Nn \l_@@_sprecta_kv_tl { arg = "pt", intent = "_-" } } { \tl_set:Ne \l_@@_sprecta_kv_tl { arg = "pt", intent = "_ \l_@@_geo_luaset_intent_str -" } } \MathMLintent { \l_@@_sprecta_intent_arg_tl } { \@@_inner_sep: \@@_luafun_mrow_intent_arg:nn { } { \l_@@_sprecta_kv_tl } { \l_@@_geo_luaset_letra_str \c_math_subscript_token { \MathMLintent { :silent } { \l_@@_geo_luaset_sub_str } } } } } % \end{macrocode} % For \mykey{read-cmd}|=mathcat|, the same |sub| case tags the base and the % subscript together with a native |\MathMLarg|, without using the shared % queue. % \begin{macrocode} { \MathMLintent { \l_@@_sprecta_intent_arg_tl } { { \@@_inner_sep: \MathMLarg { pt } { { \l_@@_geo_luaset_letra_str \c_math_subscript_token { \l_@@_geo_luaset_sub_str } } } } } } } % \end{macrocode} % With \myhlc|\l_@@_geo_luaset_tipo_str| equal to |prima|, the same % distinction applies: \mykey{read-cmd}|=school| tags the base with the % literal |kv| and appends the mark, unsilenced, since a prime is not % spelled out on the base and there is nothing to repeat; \mykey{read-cmd}|=mathcat| % tags base and mark together through a native |\MathMLarg|. % \begin{macrocode} { prima } { \str_if_eq:VnTF \l_@@_sprecta_mode_str { school } { \bool_if:NTF \l_@@_sprecta_silent_bool { \tl_set:Nn \l_@@_sprecta_kv_tl { arg = "pt", intent = "_-" } } { \tl_set:Ne \l_@@_sprecta_kv_tl { arg = "pt", intent = "_ \l_@@_geo_luaset_intent_str -" } } \MathMLintent { \l_@@_sprecta_intent_arg_tl } { \@@_inner_sep: \@@_luafun_mrow_intent_arg:nn { } { \l_@@_sprecta_kv_tl } { \l_@@_geo_luaset_letra_str \l_@@_geo_luaset_sub_str } } } { \MathMLintent { \l_@@_sprecta_intent_arg_tl } { { \@@_inner_sep: \MathMLarg { pt } { { \l_@@_geo_luaset_letra_str \l_@@_geo_luaset_sub_str } } } } } } } % \end{macrocode} % Otherwise, the same |kv| logic wraps the combined % \myhlc|\l_@@_geo_luaset_print_tl| as a whole: with the shared queue for % |school| and with its own |arg| alone for |mathcat|. % \begin{macrocode} { \bool_if:NTF \l_@@_sprecta_silent_bool { \tl_set:Nn \l_@@_sprecta_kv_tl { arg = "pt", intent = "_-" } } { \str_if_eq:VnTF \l_@@_sprecta_mode_str { school } { \tl_set:Ne \l_@@_sprecta_kv_tl { arg = "pt", intent = "_ \l_@@_geo_luaset_intent_str -" } } { \tl_set:Nn \l_@@_sprecta_kv_tl { arg = "pt" } } } \@@_luafun_mrow_intent_arg:nn { \l_@@_sprecta_intent_arg_tl } { \l_@@_sprecta_kv_tl } { \l_@@_geo_luaset_print_tl } } } % \end{macrocode} % For a list of points, \myhlc|\l_@@_geo_luaset_points_clist| is % split into \myhlc|\l_@@_sprecta_pt_seq| and walked to build % \myhlc|\l_@@_sprecta_refs_tl|, the |$pt1,$pt2,...| reference list. % The concept |intent| is `|_-|' when \mykey{silent} is |true|, % |_(refs)| when \mykey{concept} is |false| or |_recta:prefix(refs)| % otherwise, applied with a native |\MathMLintent| % wrapping |\overleftrightarrow|. The sequence is walked again, % calling \myhlc|\@@_mathmlarg_point:nnn| once per point. % \begin{macrocode} { \seq_set_from_clist:NN \l_@@_sprecta_pt_seq \l_@@_geo_luaset_points_clist \tl_clear:N \l_@@_sprecta_refs_tl \int_zero:N \l_@@_sprecta_pt_idx_int \seq_map_inline:Nn \l_@@_sprecta_pt_seq { \int_incr:N \l_@@_sprecta_pt_idx_int \tl_if_empty:NF \l_@@_sprecta_refs_tl { \tl_put_right:Nn \l_@@_sprecta_refs_tl { , } } \tl_put_right:Ne \l_@@_sprecta_refs_tl { $ pt \int_use:N \l_@@_sprecta_pt_idx_int } } \bool_if:nTF { \bool_lazy_or_p:nn { \l_@@_sprecta_silent_bool } { ! \l_@@_sprecta_concept_bool } } { \tl_set:Ne \l_@@_sprecta_intent_arg_tl { _( \l_@@_sprecta_refs_tl ) } \bool_if:NT \l_@@_sprecta_silent_bool { \tl_set:Nn \l_@@_sprecta_intent_arg_tl { _- } } } { \tl_set:Ne \l_@@_sprecta_intent_arg_tl { _ \tl_if_empty:NF \l_@@_sprecta_prefix_tl { \tl_use:N \l_@@_sprecta_prefix_tl - } recta:prefix( \l_@@_sprecta_refs_tl ) } } \@@_invisible_sep: \MathMLintent { \l_@@_sprecta_intent_arg_tl } { \@@_inner_sep: \overleftrightarrow { \int_zero:N \l_@@_sprecta_pt_idx_int \seq_map_inline:Nn \l_@@_sprecta_pt_seq { \int_incr:N \l_@@_sprecta_pt_idx_int \@@_mathmlarg_point:nnn { pt \int_use:N \l_@@_sprecta_pt_idx_int } { sprecta } { ##1 } } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\sprecta} % The public command \ics*{sprecta} enforces \emph{math mode}, opens a local % group, and delegates execution to \myhlc|\@@_sprecta_exec:nn|. % \begin{macrocode} \NewDocumentCommand \sprecta { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_sprecta_exec:nn {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \sprecta } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{sprayo}}\label{cmd:sprayo} % % The user command |\sprayo| provides a semantic interface for a ray. % The key \mykey[sprayo]{read-sty} selects the verbalized term, % |rayo| or |semirrecta|, both rendered identically with |\overrightarrow|. % % \subsubsection{Definition of variables for \cs[no-index]{sprayo}} % % \begin{variable}[int] % { % \l_@@_sprayo_mode_str, % \l_@@_sprayo_prefix_tl, % \l_@@_sprayo_read_sty_str, % } % The variable \myhlc|\l_@@_sprayo_mode_str| store the values set by % the key \mykey[sprayo]{read-cmd}; the variable \myhlc|\l_@@_sprayo_prefix_tl| % store the value set by the key \mykey[sprayo]{prefix} and the variable % \myhlc|\l_@@_sprayo_read_sty_str| stores the value set by the key % \mykey[sprayo]{read-sty}. % % \begin{important}* % The variables defined here are assigned using \myhlc|.choices:nn| % or \myhlc|.code:n| within \myhlc|\keys_define:nn|. % \end{important} % \begin{macrocode} \str_new:N \l_@@_sprayo_mode_str \tl_new:N \l_@@_sprayo_prefix_tl \str_new:N \l_@@_sprayo_read_sty_str % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{sprayo}} % % \begin{macro}[int]{sprayo-invalid-points} % The \enquote{error} |sprayo-invalid-points| is raised when the argument is % not a comma-separated pair of uppercase letters. % \begin{macrocode} \msg_new:nnnn { spintent } { sprayo-invalid-points } { Invalid~argument~'#1'~for~'\token_to_str:N \sprayo'~\msg_line_context:. } { The~argument~must~be~two~points~separated~by~a~comma. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{sprayo}} % % \begin{macro}[int]{read-cmd, prefix, silent, concept, read-sty, unknown} % We define the \mymeta{keys} \mykey[sprayo]{read-cmd}, % \mykey[sprayo]{prefix}, \mykey[sprayo]{silent}, \mykey[sprayo]{concept} % and \mykey[sprayo]{read-sty}. % \begin{macrocode} \keys_define:nn { spintent / sprayo } { read-cmd .choices:nn = { school , mathcat } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_sprayo_mode_str { school } } {2} { \str_set:Nn \l_@@_sprayo_mode_str { mathcat } } } }, read-cmd .initial:n = school, read-cmd .value_required:n = true, read-cmd / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-cmd } { school,~mathcat } { \exp_not:n {#1} }, prefix .code:n = { \@@_sanitize_affix:Nn \l_@@_sprayo_prefix_tl {#1} }, prefix .initial:n = , prefix .value_required:n = true, silent .bool_set:N = \l_@@_sprayo_silent_bool, silent .initial:n = false, silent .value_required:n = true, concept .bool_set:N = \l_@@_sprayo_concept_bool, concept .initial:n = true, concept .value_required:n = true, read-sty .choices:nn = { rayo , semirrecta } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_sprayo_read_sty_str { rayo } } {2} { \str_set:Nn \l_@@_sprayo_read_sty_str { semirrecta } } } }, read-sty .initial:n = rayo, read-sty .value_required:n = true, read-sty / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-sty } { rayo,~semirrecta } { \exp_not:n {#1} }, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { sprayo } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{sprayo}} % % \begin{variable}[int] % { % \l_@@_sprayo_pt_seq, % \l_@@_sprayo_refs_tl, % \l_@@_sprayo_intent_arg_tl, % \l_@@_sprayo_pt_idx_int, % } % Auxiliary variables used by \myhlc|\@@_sprayo_mathml:| to split % \myhlc|\l_@@_geo_luaset_points_clist| and build the |$pts1,$pts2,...| % reference list. % \begin{macrocode} \seq_new:N \l_@@_sprayo_pt_seq \tl_new:N \l_@@_sprayo_refs_tl \tl_new:N \l_@@_sprayo_intent_arg_tl \int_new:N \l_@@_sprayo_pt_idx_int % \end{macrocode} % \end{variable} % % \begin{macro}[int]{\@@_sprayo_exec:nn} % The internal function \myhlc|\@@_sprayo_exec:nn| processes the % \mymarg{argument} passed in `|#2|'. It calls % \myhlc|\@@_luafun_geo_parse_and_set:n| to process the \mymarg{argument}, % returning an \enquote{error} if the input is invalid, then calls % \myhlc|\@@_sprayo_mathml:|. The command |\sprayo| always takes a list of % points, so no |intent| is built here. % % Before processing the \mymarg{argument}, it sets the optional \mymeta{keys} passed in `|#1|'. % \begin{macrocode} \cs_new_protected:Nn \@@_sprayo_exec:nn { \keys_set:nn { spintent / sprayo } {#1} \@@_luafun_geo_parse_and_set:n { \exp_not:n {#2} } \bool_if:nT { \str_if_eq_p:Vn \l_@@_geo_luaset_error_str { true } || \str_if_eq_p:Vn \l_@@_geo_luaset_is_name_str { true } } { \msg_error:nne { spintent } { sprayo-invalid-points } { \exp_not:n {#2} } } \@@_sprayo_mathml: } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_sprayo_mathml:} % The function \myhlc|\@@_sprayo_mathml:| splits % \myhlc|\l_@@_geo_luaset_points_clist|, which Lua sets with one raw point per % element, into \myhlc|\l_@@_sprayo_pt_seq| and walks it once to % build \myhlc|\l_@@_sprayo_refs_tl|, the comma-separated % |$pts1,$pts2,...| reference list, naming each point by position. % % The concept |intent| is `|_-|' when \mykey{silent} is |true|, |_(refs)| when % \mykey{concept} is |false|, and otherwise % \myhlc|_\l_@@_sprayo_read_sty_str|\myhlc|:prefix(refs)|, where the word % |rayo| or |semirrecta| comes from \mykey{read-sty}, with \mykey{prefix} % placed after the leading |_| when given. It is applied with a native % |\MathMLintent| wrapping |\overrightarrow|, preceded by % \myhlc|\@@_inner_sep:|. The sequence is walked a second time, calling % \myhlc|\@@_mathmlarg_point:nnn| once per point with its |pts|$n$ reference % name and the values of \mykey[sprayo]{silent} and \mykey[sprayo]{read-cmd} % looked up under |sprayo|. This call decides |arg| or the literal |intent| % per point, so \mykey{concept} plays no role at that level. % \begin{macrocode} \cs_new_protected:Nn \@@_sprayo_mathml: { \seq_set_from_clist:NN \l_@@_sprayo_pt_seq \l_@@_geo_luaset_points_clist \tl_clear:N \l_@@_sprayo_refs_tl \int_zero:N \l_@@_sprayo_pt_idx_int \seq_map_inline:Nn \l_@@_sprayo_pt_seq { \int_incr:N \l_@@_sprayo_pt_idx_int \tl_if_empty:NF \l_@@_sprayo_refs_tl { \tl_put_right:Nn \l_@@_sprayo_refs_tl { , } } \tl_put_right:Ne \l_@@_sprayo_refs_tl { $ pts \int_use:N \l_@@_sprayo_pt_idx_int } } \bool_if:nTF { \bool_lazy_or_p:nn { \l_@@_sprayo_silent_bool } { ! \l_@@_sprayo_concept_bool } } { \tl_set:Ne \l_@@_sprayo_intent_arg_tl { _( \l_@@_sprayo_refs_tl ) } \bool_if:NT \l_@@_sprayo_silent_bool { \tl_set:Nn \l_@@_sprayo_intent_arg_tl { _- } } } { \tl_set:Ne \l_@@_sprayo_intent_arg_tl { _ \tl_if_empty:NF \l_@@_sprayo_prefix_tl { \tl_use:N \l_@@_sprayo_prefix_tl - } \l_@@_sprayo_read_sty_str :prefix( \l_@@_sprayo_refs_tl ) } } \@@_invisible_sep: \MathMLintent { \l_@@_sprayo_intent_arg_tl } { \@@_inner_sep: \overrightarrow { \int_zero:N \l_@@_sprayo_pt_idx_int \seq_map_inline:Nn \l_@@_sprayo_pt_seq { \int_incr:N \l_@@_sprayo_pt_idx_int \@@_mathmlarg_point:nnn { pts \int_use:N \l_@@_sprayo_pt_idx_int } { sprayo } { ##1 } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\sprayo} % The public command \ics*{sprayo} enforces \emph{math mode}, opens a local % group, and delegates execution to \myhlc|\@@_sprayo_exec:nn|. % \begin{macrocode} \NewDocumentCommand \sprayo { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_sprayo_exec:nn {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \sprayo } } } % \end{macrocode} % \end{macro} % % \subsection{The commands \cs{spside} and \cs{spmside}}\label{cmd:spside} % % The user commands |\spside| and |\spmside| provide a semantic interface for a % side, segment or trace determined by two points. The key % \mykey[spside]{read-sty} selects the verbalized term among |lado|, |segmento| and % |trazo|, and the key \mykey[spside]{read-arg} overrides that term with a % free-form string. The command |\spmside| also provides the key % \mykey[spmside]{print-sym}, which controls whether |\operatorname{m}| is % typeset before the argument. % % \subsubsection{Definition of variables for \cs[no-index]{spside} and % \cs[no-index]{spmside}} % % \begin{variable}[int] % { % \l_@@_spside_mode_str, % \l_@@_spmside_mode_str, % \l_@@_spside_prefix_tl, % \l_@@_spmside_prefix_tl, % \l_@@_spside_read_sty_str, % \l_@@_spmside_read_sty_str, % \l_@@_spside_read_arg_tl, % \l_@@_spmside_read_arg_tl, % } % Each pair of variables is declared once for both |\spside| and % |\spmside| via \myhlc|\clist_map_inline:nn|. The variables \myhlc|\l_@@_spside_mode_str| % and \myhlc|\l_@@_spmside_mode_str| store the values set by the key \mykey[spside,spmside]{read-cmd}; % the variables \myhlc|\l_@@_spside_prefix_tl| and \myhlc|\l_@@_spmside_prefix_tl| % store the values set by the key \mykey[spside,spmside]{prefix}. % % The variables \myhlc|\l_@@_spside_read_sty_str| and \myhlc|\l_@@_spmside_read_sty_str| % store the value set by the key \mykey[spside,spmside]{read-sty}. % % The variables \myhlc|\l_@@_spside_read_arg_tl| and \myhlc|\l_@@_spmside_read_arg_tl| % store the value set by the key \mykey[spside,spmside]{read-arg}. % % \begin{important}* % The variables are defined here as they are assigned using \myhlc|.choices:nn| % or \myhlc|.code:n| rather than a direct property within \myhlc|\keys_define:nn|. % \end{important} % \begin{macrocode} \clist_map_inline:nn { side , mside } { \str_new:c { l_@@_sp #1 _mode_str } \tl_new:c { l_@@_sp #1 _prefix_tl } \str_new:c { l_@@_sp #1 _read_sty_str } \tl_new:c { l_@@_sp #1 _read_arg_tl } } % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spside} % and \cs[no-index]{spmside}} % % \begin{macro}[int]{spside-invalid-points, spmside-invalid-points} % The \enquote{error}s |spside-invalid-points| and |spmside-invalid-points| % are raised when the user includes an invalid \emph{\enquote{point}} in the % \mymarg{argument} passed in `|#1|' to |\spside| or |\spmside|. % \begin{macrocode} \clist_map_inline:nn { side , mside } { \msg_new:nnnn { spintent } { sp #1 -invalid-points } { Invalid~argument~'##2'~for~'\tl_trim_spaces:n {##1}'~\msg_line_context:. } { The~argument~must~be~two~points~separated~by~a~comma. } } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spside} and % \cs[no-index]{spmside}} % % \begin{macro}[int] % { % read-cmd, % prefix, % silent, % concept, % read-sty, % read-arg, % print-sym, % unknown, % } % We define the shared \mymeta{keys} \mykey[spside,spmside]{read-cmd}, % \mykey[spside,spmside]{prefix}, \mykey[spside,spmside]{silent}, % \mykey[spside,spmside]{concept}, \mykey[spside,spmside]{read-sty}, % \mykey[spside,spmside]{read-arg} and \mykey[spmside]{print-sym}. % \begin{macrocode} \clist_map_inline:nn { side , mside } { \keys_define:nn { spintent / sp #1 } { read-cmd .choices:nn = { school , mathcat } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:cn { l_@@_sp #1 _mode_str } { school } } {2} { \str_set:cn { l_@@_sp #1 _mode_str } { mathcat } } } }, read-cmd .initial:n = school, read-cmd .value_required:n = true, read-cmd / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-cmd } { school,~mathcat } { \exp_not:n {##1} }, prefix .code:n = { \@@_sanitize_affix:cn { l_@@_sp #1 _prefix_tl } {##1} }, prefix .initial:n = , prefix .value_required:n = true, silent .bool_set:c = { l_@@_sp #1 _silent_bool }, silent .initial:n = false, silent .value_required:n = true, concept .bool_set:c = { l_@@_sp #1 _concept_bool }, concept .initial:n = true, concept .value_required:n = true, read-sty .choices:nn = { lado , segmento , trazo } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:cn { l_@@_sp #1 _read_sty_str } { lado } } {2} { \str_set:cn { l_@@_sp #1 _read_sty_str } { segmento } } {3} { \str_set:cn { l_@@_sp #1 _read_sty_str } { trazo } } } }, read-sty .initial:n = lado, read-sty .value_required:n = true, read-sty / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-sty } { lado,~segmento,~trazo } { \exp_not:n {##1} }, read-arg .code:n = { \@@_sanitize_affix:cn { l_@@_sp #1 _read_arg_tl } {##1} }, read-arg .initial:n = , read-arg .value_required:n = true, print-sym .bool_set:c = { l_@@_sp #1 _print_sym_bool }, print-sym .initial:n = false, print-sym .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { sp #1 } \@@_unknown_keys_cmd:n {##1} }, } } % \end{macrocode} % \begin{important}* % The key \mykey{print-sym} is defined for both commands for % simplicity, but has no effect on |\spside|. % \end{important} % \end{macro} % % \begin{variable}[int] % { % \l_@@_spside_intent_tl, % \l_@@_spside_pt_seq, % \l_@@_spside_refs_tl, % \l_@@_spside_intent_arg_tl, % \l_@@_spside_pt_idx_int, % } % The variable \myhlc|\l_@@_spside_intent_tl| holds the concept word, % taken from \mykey{read-arg} or \mykey{read-sty}. The rest are scratch % variables shared by both commands % to split \myhlc|\l_@@_geo_luaset_points_clist| and build the % |$pts1,$pts2,...| reference list. % \begin{macrocode} \tl_new:N \l_@@_spside_intent_tl \seq_new:N \l_@@_spside_pt_seq \tl_new:N \l_@@_spside_refs_tl \tl_new:N \l_@@_spside_intent_arg_tl \int_new:N \l_@@_spside_pt_idx_int % \end{macrocode} % \end{variable} % % \begin{macro}[int]{\@@_spside_exec:nnn} % The internal function \myhlc|\@@_spside_exec:nnn| receives the % short name of the command, |side| or |mside|, in `|#1|' and the % \mymarg{argument} in `|#3|'. It calls % \myhlc|\@@_luafun_geo_parse_and_set:n| to process `|#3|', returning % an \enquote{error} if invalid, then builds \myhlc|\l_@@_spside_intent_tl|. % % When \mykey{read-arg} is empty, it is set from \mykey{read-sty}, % prefixed with `|medida-del-|' for |\spmside|. When \mykey{read-arg} % is given, it is used as is, and `|medida-del-|' is prepended only % if \mykey{concept} is |true|: with \mykey{concept}|=false|, \mykey{read-arg} is % the verbatim escape hatch for a full user-written phrase such as % |read-arg={medida de la hipotenusa}|, with gender and article left to % the user. No |intent| is built here: it needs the reference list, % which \myhlc|\@@_spside_mathml:n| builds after splitting the % points. % % Before processing the \mymarg{argument}, it sets the optional \mymeta{keys} passed in `|#2|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spside_exec:nnn { \keys_set:nn { spintent / sp #1 } {#2} \@@_luafun_geo_parse_and_set:n { \exp_not:n {#3} } \bool_if:nT { \str_if_eq_p:Vn \l_@@_geo_luaset_error_str { true } || \str_if_eq_p:Vn \l_@@_geo_luaset_is_name_str { true } } { \msg_error:nnee { spintent } { sp #1 -invalid-points } { \c_backslash_str sp #1 } { \exp_not:n {#3} } } \tl_if_empty:cTF { l_@@_sp #1 _read_arg_tl } { \tl_set:Ne \l_@@_spside_intent_tl { \str_use:c { l_@@_sp #1 _read_sty_str } } \str_if_eq:eeT {#1} { mside } { \tl_put_left:Nn \l_@@_spside_intent_tl { medida-del- } } } { \tl_set:Ne \l_@@_spside_intent_tl { \tl_use:c { l_@@_sp #1 _read_arg_tl } } \bool_if:cT { l_@@_sp #1 _concept_bool } { \str_if_eq:eeT {#1} { mside } { \tl_put_left:Nn \l_@@_spside_intent_tl { medida-del- } } } } \@@_spside_mathml:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spside_mathml:n} % The function \myhlc|\@@_spside_mathml:n| receives the short command % name in `|#1|'. It splits \myhlc|\l_@@_geo_luaset_points_clist| into % \myhlc|\l_@@_spside_pt_seq| and walks it once to build % \myhlc|\l_@@_spside_refs_tl|, the comma-separated |$pts1,$pts2,...| % reference list, naming each point by position. % \begin{macrocode} \cs_new_protected:Nn \@@_spside_mathml:n { \seq_set_from_clist:NN \l_@@_spside_pt_seq \l_@@_geo_luaset_points_clist \tl_clear:N \l_@@_spside_refs_tl \int_zero:N \l_@@_spside_pt_idx_int \seq_map_inline:Nn \l_@@_spside_pt_seq { \int_incr:N \l_@@_spside_pt_idx_int \tl_if_empty:NF \l_@@_spside_refs_tl { \tl_put_right:Nn \l_@@_spside_refs_tl { , } } \tl_put_right:Ne \l_@@_spside_refs_tl { $ pts \int_use:N \l_@@_spside_pt_idx_int } } % \end{macrocode} % % The concept |intent| is `|_-|' when \mykey{silent} is |true|. Otherwise, if % \mykey{read-arg} is empty, it is |_(refs)| when \mykey{concept} is |false| % and \myhlc|_\l_@@_spside_intent_tl|\myhlc|:prefix(refs)| when |true|; if % \mykey{read-arg} is given, it is always % \myhlc|_\l_@@_spside_intent_tl|\myhlc|:prefix(refs)|. The |:prefix(...)| % form is prefixed with \mykey{prefix} when given. % \begin{macrocode} \bool_if:cTF { l_@@_sp #1 _silent_bool } { \tl_set:Nn \l_@@_spside_intent_arg_tl { _- } } { \tl_if_empty:cTF { l_@@_sp #1 _read_arg_tl } { \bool_if:cTF { l_@@_sp #1 _concept_bool } { \tl_set:Ne \l_@@_spside_intent_arg_tl { _ \tl_if_empty:cF { l_@@_sp #1 _prefix_tl } { \tl_use:c { l_@@_sp #1 _prefix_tl } - } \l_@@_spside_intent_tl :prefix( \l_@@_spside_refs_tl ) } } { \tl_set:Ne \l_@@_spside_intent_arg_tl { _( \l_@@_spside_refs_tl ) } } } { \tl_set:Ne \l_@@_spside_intent_arg_tl { _ \tl_if_empty:cF { l_@@_sp #1 _prefix_tl } { \tl_use:c { l_@@_sp #1 _prefix_tl } - } \l_@@_spside_intent_tl :prefix( \l_@@_spside_refs_tl ) } } } % \end{macrocode} % It is applied with a native |\MathMLintent|, preceded by % \myhlc|\@@_inner_sep:|. Inside, for |#1=side| the points are wrapped in % |\overline|; for |#1=mside|, a silent |\operatorname{m}| precedes the % |\overline|-wrapped points when \mykey{print-sym} is |true|, and the points % are typeset bare, without |\overline|, when it is |false|. In every case the % sequence is walked a second time, calling \myhlc|\@@_mathmlarg_point:nnn| % once per point with its |pts|$n$ reference name and the values of % \mykey[spside,spmside]{silent} and \mykey[spside,spmside]{read-cmd} looked % up under \texttt{spside} or \texttt{spmside}. This call decides |arg| or the % literal |intent| per point, so \mykey{concept} plays no role at that level. % \begin{macrocode} \@@_invisible_sep: % (luamml issues #26) \MathMLintent { \l_@@_spside_intent_arg_tl } { \@@_inner_sep: \str_if_eq:eeTF {#1} { side } { \overline { \@@_spside_points_loop:n {#1} } } { \bool_if:cTF { l_@@_sp #1 _print_sym_bool } { \@@_luafun_mrow_intent_arg:nn { } { intent = ":silent" } { \operatorname { m } } \overline { \@@_spside_points_loop:n {#1} } } { \@@_spside_points_loop:n {#1} } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spside_points_loop:n} % Walks \myhlc|\l_@@_spside_pt_seq| a second time, calling % \myhlc|\@@_mathmlarg_point:nnn| once per point. Factored out of % \myhlc|\@@_spside_mathml:n| since it is needed identically in three % places there: |side|, and |mside| with and without \mykey{print-sym}; it % takes the short command name in `|#1|' again since a separate % macro does not inherit its caller's own |#1|. % \begin{macrocode} \cs_new_protected:Nn \@@_spside_points_loop:n { \int_zero:N \l_@@_spside_pt_idx_int \seq_map_inline:Nn \l_@@_spside_pt_seq { \int_incr:N \l_@@_spside_pt_idx_int \@@_mathmlarg_point:nnn { pts \int_use:N \l_@@_spside_pt_idx_int } { sp #1 } { ##1 } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spside, \spmside} % Both public commands \ics*{spside} and \ics*{spmside} enforces \emph{math mode}, % opens a local group, and delegates execution to \myhlc|\@@_spside_exec:nnn|. % \begin{macrocode} \NewDocumentCommand \spside { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_spside_exec:nnn { side } {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spside } } } \NewDocumentCommand \spmside { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_spside_exec:nnn { mside } {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spmside } } } % \end{macrocode} % \end{macro} % % \subsection{The commands \cs{spangle} and \cs{spmangle}}\label{cmd:spangle} % % The user commands |\spangle| and |\spmangle| provide a semantic interface for % angles. The key \mykey[spmangle]{print-sym} chooses between the % |\angle|/|\rightanglesqr| pair, typeset together with an explicit % |\operatorname{m}|, and the |\measuredangle|/|\measuredrightangle| pair, % which already conveys the notion of measure; the key % \mykey[spangle,spmangle]{recto} chooses the right-angle variant of whichever % pair is active. % % \subsubsection{Definition of variables for \cs[no-index]{spangle} and % \cs[no-index]{spmangle}} % % \begin{variable}[int] % { % \l_@@_spangle_mode_str, % \l_@@_spmangle_mode_str, % \l_@@_spangle_prefix_tl, % \l_@@_spmangle_prefix_tl, % } % Each pair of variables is declared once for both |\spangle| and % |\spmangle| via \myhlc|\clist_map_inline:nn|. The variables \myhlc|\l_@@_spangle_mode_str| % and \myhlc|\l_@@_spmangle_mode_str| store the values set by the key % \mykey[spangle,spmangle]{read-cmd}; the variables \myhlc|\l_@@_spangle_prefix_tl| % and \myhlc|\l_@@_spmangle_prefix_tl| store the values set by the key % \mykey[spangle,spmangle]{prefix}. % % \begin{important}* % The variables defined here are assigned using \myhlc|.choices:nn| % or \myhlc|.code:n| within \myhlc|\keys_define:nn|. % \end{important} % \begin{macrocode} \clist_map_inline:nn { angle , mangle } { \str_new:c { l_@@_sp #1 _mode_str } \tl_new:c { l_@@_sp #1 _prefix_tl } } % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spangle} % and \cs[no-index]{spmangle}} % % \begin{macro}[int]{spangle-invalid-arg, spmangle-invalid-arg} % The \enquote{error}s |spangle-invalid-arg| and |spmangle-invalid-arg| are % raised when the user includes an invalid \mymarg{argument} passed in % `|#1|' to |\spangle| or |\spmangle|. % \begin{macrocode} \clist_map_inline:nn { angle , mangle } { \msg_new:nnnn { spintent } { sp #1 -invalid-arg } { Invalid~argument~'##2'~for~'\tl_trim_spaces:n {##1}'~\msg_line_context:. } { Use~a~point,~a~Greek~letter,~a~number~or~three~points~separated~by~ commas. } } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spangle} and % \cs[no-index]{spmangle}} % % \begin{macro}[int] % { % read-cmd, % prefix, % recto, % print-sym, % silent, % concept, % unknown, % } % We define the shared \mymeta{keys} \mykey[spangle,spmangle]{read-cmd}, % \mykey[spangle,spmangle]{prefix}, \mykey[spangle,spmangle]{recto}, % \mykey[spangle,spmangle]{print-sym}, \mykey[spangle,spmangle]{silent} % and \mykey[spangle,spmangle]{concept}, % \begin{macrocode} \clist_map_inline:nn { angle , mangle } { \keys_define:nn { spintent / sp #1 } { read-cmd .choices:nn = { school , mathcat } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:cn { l_@@_sp #1 _mode_str } { school } } {2} { \str_set:cn { l_@@_sp #1 _mode_str } { mathcat } } } }, read-cmd .initial:n = school, read-cmd .value_required:n = true, read-cmd / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-cmd } { school,~mathcat } { \exp_not:n {##1} }, prefix .code:n = { \@@_sanitize_affix:cn { l_@@_sp #1 _prefix_tl } {##1} }, prefix .initial:n = , prefix .value_required:n = true, recto .bool_set:c = { l_@@_sp #1 _recto_bool }, recto .initial:n = false, recto .value_required:n = true, print-sym .bool_set:c = { l_@@_sp #1 _print_sym_bool }, print-sym .initial:n = false, print-sym .value_required:n = true, silent .bool_set:c = { l_@@_sp #1 _silent_bool }, silent .initial:n = false, silent .value_required:n = true, concept .bool_set:c = { l_@@_sp #1 _concept_bool }, concept .initial:n = true, concept .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { sp #1 } \@@_unknown_keys_cmd:n {##1} }, } } % \end{macrocode} % \begin{important}* % The key \mykey{print-sym} is defined for both commands for % simplicity, but has no effect on |\spangle|. % \end{important} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spangle} and \cs[no-index]{spmangle}} % % \begin{macro}[int]{\@@_spangle_sym:n} % The internal function \myhlc|\@@_spangle_sym:n| branches on % |#1|, \mykey{print-sym} and \mykey{recto} to select one of four % symbols: |\angle|/|\rightanglesqr| when \mykey{print-sym} is % |true|; |\measuredangle|/|\measuredrightangle| otherwise. When % |#1| is |angle|, only \mykey{recto} applies, choosing between % |\angle| and |\rightangle|. % \begin{macrocode} \cs_new_protected:Nn \@@_spangle_sym:n { \str_if_eq:eeTF {#1} { mangle } { \bool_if:cTF { l_@@_sp #1 _print_sym_bool } { \bool_if:cTF { l_@@_sp #1 _recto_bool } { \@@_right_angle_sqr: } { \angle } } { \bool_if:cTF { l_@@_sp #1 _recto_bool } { \measuredrightangle } { \measuredangle } } } { \bool_if:cTF { l_@@_sp #1 _recto_bool } { \@@_right_angle_sqr: } { \angle } } } % \end{macrocode} % \end{macro} % % \begin{variable}[int] % { % \l_@@_spangle_intent_tl, % \l_@@_spangle_pt_seq, % \l_@@_spangle_refs_tl, % \l_@@_spangle_intent_arg_tl, % \l_@@_spangle_pt_idx_int, % \l_@@_spangle_kv_tl, % \l_@@_spangle_print_sym_now_bool, % } % The variable \myhlc|\l_@@_spangle_intent_tl| holds the concept word, % |ángulo| or |medida-del-ángulo|, with the suffix |-recto| when % \mykey{recto} is |true|. The other variables are used by both commands to % split \myhlc|\l_@@_geo_luaset_points_clist| and build the reference list, in % the |tres-puntos| and |un-punto| cases of \myhlc|\@@_spangle_mathml:n|. % The variable \myhlc|\l_@@_spangle_kv_tl| stores the key-value list passed % to \myhlc|\@@_luafun_mrow_intent_arg:nn|. % \begin{macrocode} \tl_new:N \l_@@_spangle_intent_tl \seq_new:N \l_@@_spangle_pt_seq \tl_new:N \l_@@_spangle_refs_tl \tl_new:N \l_@@_spangle_intent_arg_tl \int_new:N \l_@@_spangle_pt_idx_int \tl_new:N \l_@@_spangle_kv_tl \bool_new:N \l_@@_spangle_print_sym_now_bool % \end{macrocode} % \end{variable} % % \begin{macro}[int]{\@@_spangle_exec:nnn} % The internal function \myhlc|\@@_spangle_exec:nnn| receives the short name % of the command, |angle| or |mangle|, in `|#1|' and the \mymarg{argument} in % `|#3|'. It calls \myhlc|\@@_luafun_geo_angulo_parse_and_set:n|, reporting % |spangle-invalid-arg| or |spmangle-invalid-arg| on error. For a proper name % that is a single letter, not numeric, it reports the same error if the % letter is lowercase, since an angle name takes an uppercase letter, a number % or a Greek letter; the length check excludes a Greek letter command such as % |\alpha| from this comparison. Then it sets \myhlc|\l_@@_spangle_intent_tl| % to |ángulo|, or |medida-del-ángulo| for |\spmangle|, suffixed with |-recto| % when \mykey{recto} is |true|, and calls \myhlc|\@@_spangle_mathml:n|. % % Before processing the \mymarg{argument}, it sets the optional \mymeta{keys} passed in `|#2|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spangle_exec:nnn { \keys_set:nn { spintent / sp #1 } {#2} \@@_luafun_geo_angulo_parse_and_set:n { \exp_not:n {#3} } \str_if_eq:VnT \l_@@_geo_luaset_error_str { true } { \msg_error:nnee { spintent } { sp #1 -invalid-arg } { \c_backslash_str sp #1 } { \exp_not:n {#3} } } \str_if_eq:VnT \l_@@_geo_luaset_angulo_case_str { nombre } { \str_if_eq:VnF \l_@@_geo_luaset_nombre_es_num_str { true } { \int_compare:nNnT { \str_count:N \l_@@_geo_luaset_letra_str } = { 1 } { \str_if_eq:eeF { \l_@@_geo_luaset_letra_str } { \str_uppercase:n { \l_@@_geo_luaset_letra_str } } { \msg_error:nnee { spintent } { sp #1 -invalid-arg } { \c_backslash_str sp #1 } { \exp_not:n {#3} } } } } } \str_if_eq:eeTF {#1} { mangle } { \tl_set:Nn \l_@@_spangle_intent_tl { medida-del-ángulo } } { \tl_set:Nn \l_@@_spangle_intent_tl { ángulo } } \bool_if:cT { l_@@_sp #1 _recto_bool } { \tl_put_right:Nn \l_@@_spangle_intent_tl { -recto } } \@@_spangle_mathml:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spangle_mathml:n} % The function \myhlc|\@@_spangle_mathml:n| branches on % \myhlc|\l_@@_geo_luaset_angulo_case_str|. % % For |nombre|, the symbol \myhlc|\@@_spangle_sym:n| is tagged with % \myhlc|\@@_luafun_mrow_intent_arg:nn| on its own, with % \myhlc|_\l_@@_spangle_intent_tl| alone, without a |$|-reference, or with % `|_-|' when \mykey{silent} is |true| \emph{or} \mykey{concept} is |false|. % With \mykey{silent}|=true|, the name that follows is silenced as a whole. % \begin{macrocode} \cs_new_protected:Nn \@@_spangle_mathml:n { \@@_invisible_sep: % (luamml issues #26) \str_if_eq:VnTF \l_@@_geo_luaset_angulo_case_str { nombre } { \bool_if:cTF { l_@@_sp #1 _silent_bool } { \tl_set:Nn \l_@@_spangle_intent_arg_tl { _- } } { \bool_if:cTF { l_@@_sp #1 _concept_bool } { \tl_set:Ne \l_@@_spangle_intent_arg_tl { _ \tl_if_empty:cF { l_@@_sp #1 _prefix_tl } { \tl_use:c { l_@@_sp #1 _prefix_tl } - } \l_@@_spangle_intent_tl } } { \tl_set:Nn \l_@@_spangle_intent_arg_tl { _- } } } \tl_set:Ne \l_@@_spangle_kv_tl { intent = "\l_@@_spangle_intent_arg_tl" } \@@_luafun_mrow_intent_arg:nn { } { \l_@@_spangle_kv_tl } { \@@_spangle_sym:n {#1} } \bool_if:cTF { l_@@_sp #1 _silent_bool } { \@@_luafun_mrow_intent_arg:nn { } { intent = "_-" } { \l_@@_geo_luaset_print_tl } } % \end{macrocode} % Otherwise, a numeric name is tagged as a whole, through % \myhlc|\@@_luafun_mrow_intent_arg:nn|, with a literal % |intent| built from \myhlc|\l_@@_geo_luaset_intent_str|. % \begin{macrocode} { \str_if_eq:VnTF \l_@@_geo_luaset_nombre_es_num_str { true } { \tl_set:Ne \l_@@_spangle_kv_tl { intent = "_ \l_@@_geo_luaset_intent_str -" } \@@_luafun_mrow_intent_arg:nn { } { \l_@@_spangle_kv_tl } { \l_@@_geo_luaset_print_tl } } % \end{macrocode} % With \myhlc|\l_@@_geo_luaset_tipo_str| equal to |sub|, % \mykey{read-cmd}|=school| tags the letter through the same function, with % a literal |intent|, and the subscript is appended and silenced; % \mykey{read-cmd}|=mathcat| leaves both letter and subscript untouched, for % \mynvmath{MathCAT} to read the whole || natively. No |arg| % is set in either case, since there is no |$|-reference to resolve. % \begin{macrocode} { \str_case:VnF \l_@@_geo_luaset_tipo_str { { sub } { \str_if_eq:eeTF { \use:c { l_@@_sp #1 _mode_str } } { school } { \tl_set:Ne \l_@@_spangle_kv_tl { intent = "_ \l_@@_geo_luaset_intent_str -" } \@@_luafun_mrow_intent_arg:nn { } { \l_@@_spangle_kv_tl } { \l_@@_geo_luaset_letra_str \c_math_subscript_token { \MathMLintent { :silent } { \l_@@_geo_luaset_sub_str } } } } { \l_@@_geo_luaset_letra_str \c_math_subscript_token { \l_@@_geo_luaset_sub_str } } } % \end{macrocode} % With \myhlc|\l_@@_geo_luaset_tipo_str| equal to |prima|, the prime is always % read: it is spoken as part of the literal for \mykey{read-cmd}|=school| and % natively for \mykey{read-cmd}|=mathcat|. % \begin{macrocode} { prima } { \str_if_eq:eeTF { \use:c { l_@@_sp #1 _mode_str } } { school } { \tl_set:Ne \l_@@_spangle_kv_tl { intent = "_ \l_@@_geo_luaset_intent_str -" } \@@_luafun_mrow_intent_arg:nn { } { \l_@@_spangle_kv_tl } { \l_@@_geo_luaset_letra_str \l_@@_geo_luaset_sub_str } } { \l_@@_geo_luaset_letra_str \l_@@_geo_luaset_sub_str } } } { \l_@@_geo_luaset_print_tl } } } } % \end{macrocode} % For |tres-puntos| and |un-punto|, with the suffix |-en| added to the % concept word in the latter, where a single point reads \emph{\enquote{ángulo en A}}, % \myhlc|\l_@@_geo_luaset_points_clist| is % split into \myhlc|\l_@@_spangle_pt_seq| and walked to build % \myhlc|\l_@@_spangle_refs_tl|, the reference list |$pt1,$pt2,...|. A single % native |\MathMLintent| wraps the symbol and the points together, with % \myhlc|_\l_@@_spangle_intent_tl|\myhlc|:prefix(refs)|, or `|_-|' when % \mykey{silent} is |true| and |_(refs)| when \mykey{concept} is |false|. For % |\spmangle| with \mykey{print-sym}|=true|, a silent |\operatorname{m}| % precedes \myhlc|\@@_spangle_sym:n| inside the same |\MathMLintent|. The % sequence is walked again, calling \myhlc|\@@_mathmlarg_point:nnn| once per % point. % \begin{macrocode} { \str_if_eq:VnT \l_@@_geo_luaset_angulo_case_str { un-punto } { \tl_put_right:Nn \l_@@_spangle_intent_tl { -en } } \seq_set_from_clist:NN \l_@@_spangle_pt_seq \l_@@_geo_luaset_points_clist \tl_clear:N \l_@@_spangle_refs_tl \int_zero:N \l_@@_spangle_pt_idx_int \seq_map_inline:Nn \l_@@_spangle_pt_seq { \int_incr:N \l_@@_spangle_pt_idx_int \tl_if_empty:NF \l_@@_spangle_refs_tl { \tl_put_right:Nn \l_@@_spangle_refs_tl { , } } \tl_put_right:Ne \l_@@_spangle_refs_tl { $ pt \int_use:N \l_@@_spangle_pt_idx_int } } \bool_if:cTF { l_@@_sp #1 _silent_bool } { \tl_set:Nn \l_@@_spangle_intent_arg_tl { _- } } { \bool_if:cTF { l_@@_sp #1 _concept_bool } { \tl_set:Ne \l_@@_spangle_intent_arg_tl { _ \tl_if_empty:cF { l_@@_sp #1 _prefix_tl } { \tl_use:c { l_@@_sp #1 _prefix_tl } - } \l_@@_spangle_intent_tl :prefix( \l_@@_spangle_refs_tl ) } } { \tl_set:Ne \l_@@_spangle_intent_arg_tl { _( \l_@@_spangle_refs_tl ) } } } \bool_set_false:N \l_@@_spangle_print_sym_now_bool \str_if_eq:eeT {#1} { mangle } { \bool_if:cT { l_@@_sp #1 _print_sym_bool } { \bool_set_true:N \l_@@_spangle_print_sym_now_bool } } \MathMLintent { \l_@@_spangle_intent_arg_tl } { \@@_inner_sep: \bool_if:NT \l_@@_spangle_print_sym_now_bool { \@@_luafun_mrow_intent_arg:nn { } { intent = ":silent" } { \operatorname { m } } } \@@_luafun_mrow_intent_arg:nn { } { intent = ":silent" } { \@@_spangle_sym:n {#1} } \int_zero:N \l_@@_spangle_pt_idx_int \seq_map_inline:Nn \l_@@_spangle_pt_seq { \int_incr:N \l_@@_spangle_pt_idx_int \@@_mathmlarg_point:nnn { pt \int_use:N \l_@@_spangle_pt_idx_int } { sp#1 } { ##1 } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spangle, \spmangle} % Both public commands \ics*{spangle} and \ics*{spmangle} enforces \emph{math mode}, % opens a local group, and delegates execution to \myhlc|\@@_spangle_exec:nnn|. % \begin{macrocode} \NewDocumentCommand \spangle { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_spangle_exec:nnn { angle } {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spangle } } } \NewDocumentCommand \spmangle { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_spangle_exec:nnn { mangle } {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spmangle } } } % \end{macrocode} % \end{macro} % % \subsection{The commands \cs{sparc} and \cs{spmarc}}\label{cmd:sparc} % % The user commands |\sparc| and |\spmarc| provide a semantic interface for % arcs. The command |\spmarc| has no \mykey{print-sym} key: |\operatorname{m}| % is always present, since $\operatorname{m}\widearc{AB}$ is the standard % notation for the measure of an arc. % % \subsubsection{Definition of variables for \cs[no-index]{sparc} and % \cs[no-index]{spmarc}} % % \begin{variable}[int] % { % \l_@@_sparc_mode_str, % \l_@@_spmarc_mode_str, % \l_@@_sparc_prefix_tl, % \l_@@_spmarc_prefix_tl, % } % Each pair of variables is declared once for both |\sparc| and % |\spmarc| via \myhlc|\clist_map_inline:nn|. The variables \myhlc|\l_@@_sparc_mode_str| % and \myhlc|\l_@@_spmarc_mode_str| store the values set by the key % \mykey[sparc,spmarc]{read-cmd}; the variables \myhlc|\l_@@_sparc_prefix_tl| % and \myhlc|\l_@@_spmarc_prefix_tl| store the values set by the key % \mykey[sparc,spmarc]{prefix}. % \begin{macrocode} \clist_map_inline:nn { arc , marc } { \str_new:c { l_@@_sp #1 _mode_str } \tl_new:c { l_@@_sp #1 _prefix_tl } } % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{sparc} % and \cs[no-index]{spmarc}} % % \begin{macro}[int]{sparc-invalid-points, spmarc-invalid-points} % The \enquote{error}s |sparc-invalid-points| and |spmarc-invalid-points| are % raised when the user includes an invalid \mymarg{argument} passed in % `|#1|' to |\sparc| or |\spmarc|. % \begin{macrocode} \clist_map_inline:nn { arc , marc } { \msg_new:nnnn { spintent } { sp #1 -invalid-points } { Invalid~argument~'##2'~for~'\tl_trim_spaces:n {##1}'~\msg_line_context:. } { The~argument~must~be~two~or~three~points~separated~by~commas. } } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{sparc} and % \cs[no-index]{spmarc}} % % \begin{macro}[int]{read-cmd, prefix, silent, concept, unknown} % We define the shared \mymeta{keys} \mykey[sparc,spmarc]{read-cmd}, % \mykey[sparc,spmarc]{prefix}, \mykey[sparc,spmarc]{silent} and % \mykey[sparc,spmarc]{concept}. % \begin{macrocode} \clist_map_inline:nn { arc , marc } { \keys_define:nn { spintent / sp #1 } { read-cmd .choices:nn = { school , mathcat } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:cn { l_@@_sp #1 _mode_str } { school } } {2} { \str_set:cn { l_@@_sp #1 _mode_str } { mathcat } } } }, read-cmd .initial:n = school, read-cmd .value_required:n = true, read-cmd / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-cmd } { school,~mathcat } { \exp_not:n {##1} }, prefix .code:n = { \@@_sanitize_affix:cn { l_@@_sp #1 _prefix_tl } {##1} }, prefix .initial:n = , prefix .value_required:n = true, silent .bool_set:c = { l_@@_sp #1 _silent_bool }, silent .initial:n = false, silent .value_required:n = true, concept .bool_set:c = { l_@@_sp #1 _concept_bool }, concept .initial:n = true, concept .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { sp #1 } \@@_unknown_keys_cmd:n {##1} }, } } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{sparc} and % \cs[no-index]{spmarc}} % % \begin{macro}[int]{\@@_sparc_wide_arc:n} % The function \myhlc|\@@_sparc_wide_arc:n| acts as a fallback when the % mathematical font used in the document does not provide the command % |\widearc|. The definition of this function is a direct adaptation of a % response given by Clea F. Rees (\texttt{@cfr}) to a query in the chat room of % \href{https://chat.stackexchange.com/transcript/message/69167926#69167926}{TeX-SX}. % \begin{macrocode} \cs_new_protected:Nn \@@_sparc_wide_arc:n { \Umathaccent 7 \symoperators "023DC \scan_stop: {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_sparc_print_arc:n} % The function \myhlc|\@@_sparc_print_arc:n| applies the command % |\widearc| to the argument passed to |\sparc| or % |\spmarc|. If the mathematical font set in the document does not % provide the command |\widearc|, we will use the built-in function % \myhlc|\@@_sparc_wide_arc:n|. % \begin{macrocode} \cs_new_protected:Nn \@@_sparc_print_arc:n { \cs_if_exist:NTF \widearc { \widearc {#1} } { \@@_sparc_wide_arc:n {#1} } } % \end{macrocode} % \end{macro} % % \begin{variable}[int] % { % \l_@@_sparc_intent_tl, % \l_@@_sparc_pt_seq, % \l_@@_sparc_refs_tl, % \l_@@_sparc_intent_arg_tl, % \l_@@_sparc_pt_idx_int, % } % The variable \myhlc|\l_@@_sparc_intent_tl| holds the concept word, % |arco| or |medida-del-arco|. The rest are scratch variables to split % \myhlc|\l_@@_geo_luaset_points_clist| and build the |$pt1,$pt2,...| % reference list used in the concept |intent|. % \begin{macrocode} \tl_new:N \l_@@_sparc_intent_tl \seq_new:N \l_@@_sparc_pt_seq \tl_new:N \l_@@_sparc_refs_tl \tl_new:N \l_@@_sparc_intent_arg_tl \int_new:N \l_@@_sparc_pt_idx_int % \end{macrocode} % \end{variable} % % \begin{macro}[int]{\@@_sparc_exec:nnn} % The internal function \myhlc|\@@_sparc_exec:nnn| receives the % short name of the command, |arc| or |marc|, in `|#1|' and the % \mymarg{argument} in `|#3|'. It calls % \myhlc|\@@_luafun_geo_parse_and_set:n| to process `|#3|', returning % an \enquote{error} if invalid, then sets \myhlc|\l_@@_sparc_intent_tl| % to |arco|, or |medida-del-arco| for |\spmarc|. No |intent| is % built here: it needs the reference list, which % \myhlc|\@@_sparc_mathml:n| builds after splitting the points. % % Before processing the \mymarg{argument}, it sets the optional \mymeta{keys} passed in `|#2|'. % \begin{macrocode} \cs_new_protected:Nn \@@_sparc_exec:nnn { \keys_set:nn { spintent / sp #1 } {#2} \@@_luafun_geo_parse_and_set:n { \exp_not:n {#3} } \bool_if:nT { \str_if_eq_p:Vn \l_@@_geo_luaset_error_str { true } || \str_if_eq_p:Vn \l_@@_geo_luaset_is_name_str { true } } { \msg_error:nnee { spintent } { sp #1 -invalid-points } { \c_backslash_str sp #1 } { \exp_not:n {#3} } } \str_if_eq:eeTF {#1} { marc } { \tl_set:Nn \l_@@_sparc_intent_tl { medida-del-arco } } { \tl_set:Nn \l_@@_sparc_intent_tl { arco } } \@@_sparc_mathml:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_sparc_mathml:n} % The function \myhlc|\@@_sparc_mathml:n| receives the short command % name in `|#1|'. It splits \myhlc|\l_@@_geo_luaset_points_clist| into % \myhlc|\l_@@_sparc_pt_seq| and walks it once to build % \myhlc|\l_@@_sparc_refs_tl|, the comma-separated |$pt1,$pt2,...| % reference list, naming each point by position. % \begin{macrocode} \cs_new_protected:Nn \@@_sparc_mathml:n { \seq_set_from_clist:NN \l_@@_sparc_pt_seq \l_@@_geo_luaset_points_clist \tl_clear:N \l_@@_sparc_refs_tl \int_zero:N \l_@@_sparc_pt_idx_int \seq_map_inline:Nn \l_@@_sparc_pt_seq { \int_incr:N \l_@@_sparc_pt_idx_int \tl_if_empty:NF \l_@@_sparc_refs_tl { \tl_put_right:Nn \l_@@_sparc_refs_tl { , } } \tl_put_right:Ne \l_@@_sparc_refs_tl { $ pt \int_use:N \l_@@_sparc_pt_idx_int } } % \end{macrocode} % % The concept |intent| is `|_-|' when \mykey{silent} is |true|, % |_(refs)| when \mykey{concept} is |false|; otherwise % \myhlc|_\l_@@_sparc_intent_tl|\myhlc|:prefix(refs)|, with % \mykey{prefix} placed after the leading |_| when given. % \begin{macrocode} \bool_if:cTF { l_@@_sp #1 _silent_bool } { \tl_set:Nn \l_@@_sparc_intent_arg_tl { _- } } { \bool_if:cTF { l_@@_sp #1 _concept_bool } { \tl_set:Ne \l_@@_sparc_intent_arg_tl { _ \tl_if_empty:cF { l_@@_sp #1 _prefix_tl } { \tl_use:c { l_@@_sp #1 _prefix_tl } - } \l_@@_sparc_intent_tl :prefix( \l_@@_sparc_refs_tl ) } } { \tl_set:Ne \l_@@_sparc_intent_arg_tl { _( \l_@@_sparc_refs_tl ) } } } % \end{macrocode} % It is applied with a single native |\MathMLintent| wrapping % \myhlc|\@@_sparc_print_arc:n| directly, with the same shape as % \myhlc|\@@_spside_mathml:n|, preceded by % \myhlc|\@@_inner_sep:|, a second sibling so the sole % || does not collapse into ||, \mypkg{luamml} issue % \href{https://github.com/latex3/luamml/issues/26}{\#26}. A silent % |\operatorname{m}| always precedes it for |\spmarc|, since there is no % \mykey{print-sym} key here. The sequence is walked a second time, % calling \myhlc|\@@_mathmlarg_point:nnn| once per point with its % |pt|$n$ reference name, looked up under \texttt{sparc} or \texttt{spmarc}. % \begin{macrocode} \@@_invisible_sep: % need by MathCAT (bug in luamml issues #26) \MathMLintent { \l_@@_sparc_intent_arg_tl } { \@@_inner_sep: \str_if_eq:eeT {#1} { marc } { \@@_luafun_mrow_intent_arg:nn { } { intent = ":silent" } { \operatorname { m } } } \@@_sparc_print_arc:n { \int_zero:N \l_@@_sparc_pt_idx_int \seq_map_inline:Nn \l_@@_sparc_pt_seq { \int_incr:N \l_@@_sparc_pt_idx_int \@@_mathmlarg_point:nnn { pt \int_use:N \l_@@_sparc_pt_idx_int } { sp #1 } { ##1 } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\sparc, \spmarc} % Both public commands \ics*{sparc} and \ics*{spmarc} enforces \emph{math mode}, % opens a local group, and delegates execution to \myhlc|\@@_sparc_exec:nnn|. % \begin{macrocode} \NewDocumentCommand \sparc { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_sparc_exec:nnn { arc } {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \sparc } } } \NewDocumentCommand \spmarc { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_sparc_exec:nnn { marc } {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spmarc } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spPoly}}\label{cmd:spPoly} % % The user command |\spPoly| typesets a polygon defined by its % vertices. The figure type and its visual symbol are inferred from % the vertex count: three points produce a triangle, four a square, % five or more a generic polygon with no symbol. It never sends a % boolean to Lua: \myhlc|\l_@@_geo_luaset_intent_str|, which holds the full % |intent| with the figure name. % % \subsubsection{Definition of variables for \cs[no-index]{spPoly}} % % \begin{variable}[int] % { % \l_@@_spPoly_mode_str, % \l_@@_spPoly_prefix_tl, % } % The variable \myhlc|\l_@@_spPoly_mode_str| stores the value set by % the key \mykey[spPoly]{read-cmd}; the variable % \myhlc|\l_@@_spPoly_prefix_tl| stores the value set by the key % \mykey[spPoly]{prefix}. % \begin{macrocode} \str_new:N \l_@@_spPoly_mode_str \tl_new:N \l_@@_spPoly_prefix_tl % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spPoly}} % % \begin{macro}[int]{spPoly-invalid-points} % The \enquote{error} |spPoly-invalid-points| is raised when the argument is % not a comma-separated list of at least three uppercase letters. % \begin{macrocode} \msg_new:nnnn { spintent } { spPoly-invalid-points } { Invalid~argument~'#1'~for~'\token_to_str:N \spPoly'~\msg_line_context:. } { The~argument~must~be~a~list~of~at~least~three~points~separated~by~commas. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spPoly}} % % \begin{macro}[int] % { % read-cmd, % prefix, % print-sym, % silent, % concept, % unknown, % } % We define the \mymeta{keys} \mykey[spPoly]{read-cmd}, \mykey[spPoly]{prefix}, % \mykey[spPoly]{print-sym}, \mykey[spPoly]{silent} and \mykey[spPoly]{concept}. % \begin{macrocode} \keys_define:nn { spintent / spPoly } { read-cmd .choices:nn = { school , mathcat } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spPoly_mode_str { school } } {2} { \str_set:Nn \l_@@_spPoly_mode_str { mathcat } } } }, read-cmd .initial:n = school, read-cmd .value_required:n = true, read-cmd / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-cmd } { school,~mathcat } { \exp_not:n {#1} }, prefix .code:n = { \@@_sanitize_affix:Nn \l_@@_spPoly_prefix_tl {#1} }, prefix .initial:n = , prefix .value_required:n = true, print-sym .bool_set:N = \l_@@_spPoly_print_sym_bool, print-sym .initial:n = true, print-sym .value_required:n = true, silent .bool_set:N = \l_@@_spPoly_silent_bool, silent .initial:n = false, silent .value_required:n = true, concept .bool_set:N = \l_@@_spPoly_concept_bool, concept .initial:n = true, concept .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spPoly } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spPoly}} % % \begin{variable}[int] % { % \l_@@_spPoly_pt_seq, % \l_@@_spPoly_refs_tl, % \l_@@_spPoly_intent_arg_tl, % \l_@@_spPoly_pt_idx_int, % } % Scratch variables to split \myhlc|\l_@@_geo_luaset_points_clist| and % build the |$pt1,$pt2,...| reference list, and to hold the final % concept |intent| once built. % \begin{macrocode} \seq_new:N \l_@@_spPoly_pt_seq \tl_new:N \l_@@_spPoly_refs_tl \tl_new:N \l_@@_spPoly_intent_arg_tl \int_new:N \l_@@_spPoly_pt_idx_int % \end{macrocode} % \end{variable} % % \begin{macro}[int]{\@@_spPoly_sym:} % Unchanged: reads \myhlc|\l_@@_geo_luaset_poly_sym_str| and % typesets the corresponding symbol. % \begin{macrocode} \cs_new_protected:Nn \@@_spPoly_sym: { \str_case:Vn \l_@@_geo_luaset_poly_sym_str { { triangle } { \triangle } { square } { \mdlgwhtsquare } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spPoly_build_refs:} % Splits \myhlc|\l_@@_geo_luaset_points_clist| into % \myhlc|\l_@@_spPoly_pt_seq| and walks it once to build % \myhlc|\l_@@_spPoly_refs_tl|, the comma-separated |$pt1,$pt2,...| % reference list, naming each vertex by position. % \begin{macrocode} \cs_new_protected:Nn \@@_spPoly_build_refs: { \seq_set_from_clist:NN \l_@@_spPoly_pt_seq \l_@@_geo_luaset_points_clist \tl_clear:N \l_@@_spPoly_refs_tl \int_zero:N \l_@@_spPoly_pt_idx_int \seq_map_inline:Nn \l_@@_spPoly_pt_seq { \int_incr:N \l_@@_spPoly_pt_idx_int \tl_if_empty:NF \l_@@_spPoly_refs_tl { \tl_put_right:Nn \l_@@_spPoly_refs_tl { , } } \tl_put_right:Ne \l_@@_spPoly_refs_tl { $ pt \int_use:N \l_@@_spPoly_pt_idx_int } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spPoly_build_concept:} % Builds \myhlc|\l_@@_spPoly_intent_arg_tl|, independently of % \mykey{print-sym}: `|_-|' when \mykey{silent} is |true|, % |_(refs)| when \mykey{concept} is |false|; otherwise % \myhlc|_\l_@@_geo_luaset_concept_str|\myhlc|:prefix(refs)|, with % \mykey{prefix} placed after the leading |_| when given. Must be % called after \myhlc|\@@_spPoly_build_refs:|. % \begin{macrocode} \cs_new_protected:Nn \@@_spPoly_build_concept: { \bool_if:NTF \l_@@_spPoly_silent_bool { \tl_set:Nn \l_@@_spPoly_intent_arg_tl { _- } } { \bool_if:NTF \l_@@_spPoly_concept_bool { \tl_set:Ne \l_@@_spPoly_intent_arg_tl { _ \tl_if_empty:NF \l_@@_spPoly_prefix_tl { \tl_use:N \l_@@_spPoly_prefix_tl - } \l_@@_geo_luaset_concept_str :prefix( \l_@@_spPoly_refs_tl ) } } { \tl_set:Ne \l_@@_spPoly_intent_arg_tl { _( \l_@@_spPoly_refs_tl ) } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spPoly_points_loop:} % It walks \myhlc|\l_@@_spPoly_pt_seq| a second time, calling % \myhlc|\@@_mathmlarg_point:nnn| once per vertex with its |pt|$n$ reference % name, looked up under |spPoly|. This call decides |arg| or the literal % |intent| per vertex. % \begin{macrocode} \cs_new_protected:Nn \@@_spPoly_points_loop: { \int_zero:N \l_@@_spPoly_pt_idx_int \seq_map_inline:Nn \l_@@_spPoly_pt_seq { \int_incr:N \l_@@_spPoly_pt_idx_int \@@_mathmlarg_point:nnn { pt \int_use:N \l_@@_spPoly_pt_idx_int } { spPoly } { ##1 } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spPoly_exec:nn} % Calls \myhlc|\@@_luafun_geo_poly_parse_and_set:n|, reports % |spPoly-invalid-points| on failure, then delegates to % \myhlc|\@@_spPoly_mathml:|. No |intent| is built here: it needs % the reference list, which \myhlc|\@@_spPoly_mathml:| builds after % splitting the vertices. % % Before processing the \mymarg{argument}, it sets the optional \mymeta{keys} passed in `|#1|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spPoly_exec:nn { \keys_set:nn { spintent / spPoly } {#1} \@@_luafun_geo_poly_parse_and_set:n { \exp_not:n {#2} } \str_if_eq:VnT \l_@@_geo_luaset_error_str { true } { \msg_error:nne { spintent } { spPoly-invalid-points } { \exp_not:n {#2} } } \@@_spPoly_mathml: } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spPoly_mathml:} % Builds the reference list with \myhlc|\@@_spPoly_build_refs:| and the % concept |intent| with \myhlc|\@@_spPoly_build_concept:|, then wraps % everything in a single native |\MathMLintent|, preceded by % \myhlc|\@@_inner_sep:|, a second sibling so the sole % || does not collapse into ||, \mypkg{luamml} issue % \href{https://github.com/latex3/luamml/issues/26}{\#26}. % % The keys \mykey{print-sym} and \mykey{concept}/\mykey{silent} are % independent. The key \mykey{print-sym} only decides whether the glyph % from \myhlc|\@@_spPoly_sym:|, silenced with % \myhlc|\@@_luafun_mrow_intent_arg:nn|, is attempted, and the glyph is % skipped when \myhlc|\l_@@_geo_luaset_poly_sym_str| is empty, which happens % for a pentagon and above because they have no symbol. It does not affect % whether the concept word is spoken. % \begin{macrocode} \cs_new_protected:Nn \@@_spPoly_mathml: { \@@_invisible_sep: % (luamml issues #26) \@@_spPoly_build_refs: \@@_spPoly_build_concept: \MathMLintent { \l_@@_spPoly_intent_arg_tl } { \@@_inner_sep: \bool_if:NT \l_@@_spPoly_print_sym_bool { \str_if_empty:NF \l_@@_geo_luaset_poly_sym_str { \@@_luafun_mrow_intent_arg:nn { } { intent = ":silent" } { \@@_spPoly_sym: } } } \@@_spPoly_points_loop: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spPoly} % The public command \ics*{spPoly} enforces \emph{math mode}, opens a local % group, and delegates execution to \myhlc|\@@_spPoly_exec:nn|. % \begin{macrocode} \NewDocumentCommand \spPoly { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_spPoly_exec:nn {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spPoly } } } % \end{macrocode} % \end{macro} % % \subsection{The commands \cs{spAfig} and \cs{spPfig}}\label{cmd:spAfig} % % The user commands |\spAfig| and |\spPfig| typeset the area and the % perimeter of a polygonal figure, using the delimited-optional % argument syntax |d()|. The key \mykey[spAfig,spPfig]{print-sym} is % never sent to Lua: the symbol type is returned unconditionally in % \myhlc|\l_@@_geo_luaset_poly_sym_str|, and expl3 decides whether to % wrap it in |\MathMLintent|\marg{:silent}. % % % \subsubsection{Definition of variables for \cs[no-index]{spAfig} and % \cs[no-index]{spPfig}} % % \begin{variable}[int] % { % \l_@@_geo_final_print_tl, % } % Scratch variables used by \myhlc|\@@_spfig_exec:nnn| to assemble % the visual and the |intent|. % The variable \myhlc|\l_@@_geo_final_print_tl| holds the final visual placed % under |\mathcal|'s subscript. The final literal |intent| and the % concept forwarded when \mykey[spAfig,spPfig]{read-cmd} is |mathcat|. % \begin{macrocode} \tl_new:N \l_@@_geo_final_print_tl % \end{macrocode} % \end{variable} % % \begin{variable}[int] % { % \l_@@_spAfig_mode_str, % \l_@@_spPfig_mode_str, % \l_@@_spAfig_print_sym_str, % \l_@@_spPfig_print_sym_str, % \l_@@_spAfig_prefix_tl, % \l_@@_spPfig_prefix_tl, % \l_@@_spAfig_read_arg_tl, % \l_@@_spPfig_read_arg_tl, % } % These variables store the value set by each corresponding key: % \mykey[spAfig,spPfig]{read-cmd}, \mykey[spAfig,spPfig]{print-sym}, % \mykey[spAfig,spPfig]{prefix} and \mykey[spAfig,spPfig]{read-arg}, % respectively. The value set by \mykey[spAfig,spPfig]{read-arg} % overrides the figure name inferred from the vertex count, % independently of \mykey[spAfig,spPfig]{print-sym}. % \begin{macrocode} \clist_map_inline:nn { Afig , Pfig } { \str_new:c { l_@@_sp #1 _mode_str } \str_new:c { l_@@_sp #1 _print_sym_str } \tl_new:c { l_@@_sp #1 _prefix_tl } \tl_new:c { l_@@_sp #1 _read_arg_tl } } % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spAfig} % and \cs[no-index]{spPfig}} % % \begin{macro}[int] % { % spAfig-invalid-points, spPfig-invalid-points, % spAfig-invalid-arg, spPfig-invalid-arg, % spAfig-sym-letter-conflict, spPfig-sym-letter-conflict, % spAfig-sym-cardinality-conflict, spPfig-sym-cardinality-conflict, % spAfig-read-arg-key, spPfig-read-arg-key, % } % The ten \enquote{error}s are generated by the same map, using the same % |##1|/|##2| doubling convention as |\spside|/|\spmside|. % |spAfig-invalid-points| and |spPfig-invalid-points| are currently unused by % \myhlc|\@@_spfig_exec:nnn|, which raises only % |spAfig-invalid-arg| or |spPfig-invalid-arg| for any parse failure, % regardless of whether a comma was present. |spAfig-sym-letter-conflict|, % |spPfig-sym-letter-conflict|, |spAfig-sym-cardinality-conflict| and % |spPfig-sym-cardinality-conflict| are raised when % \mykey[spAfig,spPfig]{print-sym} is forced to a value incompatible % with the actual argument. % \begin{macrocode} \clist_map_inline:nn { Afig , Pfig } { \msg_new:nnnn { spintent } { sp #1 -invalid-points } { Invalid~argument~'##2'~for~'\tl_trim_spaces:n {##1}'~\msg_line_context:. } { The~argument~must~be~a~list~of~points~separated~by~commas. } \msg_new:nnnn { spintent } { sp #1 -invalid-arg } { Invalid~argument~'##2'~for~'\tl_trim_spaces:n {##1}'~\msg_line_context:. } { See~the~manual~for~the~accepted~argument~forms. } \msg_new:nnnn { spintent } { sp #1 -sym-letter-conflict } { The~key~'print-sym'~cannot~be~combined~with~a~lettered~argument~in~'##1'~ \msg_line_context:. } { See~the~documentation~of~'##1'~for~details~on~the~key~'print-sym'. } \msg_new:nnnn { spintent } { sp #1 -sym-cardinality-conflict } { The~key~'print-sym'~does~not~match~the~vertex~count~in~'##1'~\msg_line_context:. } { See~the~documentation~of~'##1'~for~details~on~the~key~'print-sym'. } \msg_new:nnnn { spintent } { sp #1 -read-arg-key } { The~key~'read-arg'~has~no~effect~with~a~single~argument~in~'##1'~ \msg_line_context:. } { See~the~documentation~of~'##1'~for~details~on~the~key~'read-arg'. } } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spAfig} and \cs[no-index]{spPfig}} % % \begin{macro}[int] % { % read-cmd, % prefix, % print-sym, % read-arg, % read-sub, % silent, % concept, % concept*, % unknown, % } % We define the shared \mymeta{keys} \mykey[spAfig,spPfig]{read-cmd}, % \mykey[spAfig,spPfig]{prefix}, \mykey[spAfig,spPfig]{print-sym}, % \mykey[spAfig,spPfig]{read-arg}, \mykey[spAfig,spPfig]{read-sub}, % \mykey[spAfig,spPfig]{silent} and \mykey[spAfig,spPfig]{concept}. % \begin{macrocode} \clist_map_inline:nn { Afig , Pfig } { \keys_define:nn { spintent / sp #1 } { read-cmd .choices:nn = { school , mathcat } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:cn { l_@@_sp #1 _mode_str } { school } } {2} { \str_set:cn { l_@@_sp #1 _mode_str } { mathcat } } } }, read-cmd .initial:n = school, read-cmd .value_required:n = true, read-cmd / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-cmd } { school,~mathcat } { \exp_not:n {##1} }, prefix .code:n = { \@@_sanitize_affix:cn { l_@@_sp #1 _prefix_tl } {##1} }, prefix .initial:n = , prefix .value_required:n = true, print-sym .choices:nn = { auto , triangle , square , none } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:cn { l_@@_sp #1 _print_sym_str } { auto } } {2} { \str_set:cn { l_@@_sp #1 _print_sym_str } { triangle } } {3} { \str_set:cn { l_@@_sp #1 _print_sym_str } { square } } {4} { \str_set:cn { l_@@_sp #1 _print_sym_str } { none } } } }, print-sym .initial:n = auto, print-sym .value_required:n = true, print-sym / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { print-sym } { auto,~triangle,~square,~none } { \exp_not:n {##1} }, read-arg .code:n = { \@@_sanitize_affix:cn { l_@@_sp #1 _read_arg_tl } {##1} }, read-arg .initial:n = , read-arg .value_required:n = true, read-sub .bool_set:c = { l_@@_sp #1 _read_sub_bool }, read-sub .initial:n = false, read-sub .value_required:n = true, silent .bool_set:c = { l_@@_sp #1 _silent_bool }, silent .initial:n = false, silent .value_required:n = true, concept .bool_set:c = { l_@@_sp #1 _concept_bool }, concept .initial:n = true, concept .value_required:n = true, concept* .meta:n = { concept = false, read-arg = { \str_if_eq:eeTF {#1} { Afig } { _área } { _perímetro } }, }, concept* .value_forbidden:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { sp #1 } \@@_unknown_keys_cmd:n {##1} }, } } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spAfig} and % \cs[no-index]{spPfig}} % % \begin{macro}[int]{\@@_spfig_sym:} % The function \myhlc|\@@_spfig_sym:| reads % \myhlc|\l_@@_geo_print_sym_str| and typesets the corresponding % symbol: |\triangle| for the value |triangle|, |\mdlgwhtsquare| for % the value |square|. % \begin{macrocode} \cs_new_protected:Nn \@@_spfig_sym: { \str_case:Vn \l_@@_geo_print_sym_str { { triangle } { \triangle } { square } { \mdlgwhtsquare } } } % \end{macrocode} % \end{macro} % % \begin{variable}[int] % { % \l_@@_spfig_op_tl, % \l_@@_spfig_intent_tl, % \l_@@_spfig_desc_tl, % \l_@@_spfig_symbol_tl, % \l_@@_spfig_pt_seq, % \l_@@_spfig_refs_tl, % \l_@@_spfig_intent_arg_tl, % \l_@@_spfig_pt_idx_int, % \l_@@_geo_print_sym_str, % \l_@@_geo_temp_tl, % } % The variable \myhlc|\l_@@_spfig_op_tl| holds \emph{«área»} or \emph{«perímetro»}. % The variable \myhlc|\l_@@_spfig_intent_tl| holds the concept word for the % vertices case, taken from \mykey{read-arg} or the auto-detected figure name. % The rest are scratch variables to split % \myhlc|\l_@@_geo_luaset_points_clist| and build the % |$pt1,$pt2,...| reference list. % \begin{macrocode} \tl_new:N \l_@@_spfig_op_tl \tl_new:N \l_@@_spfig_intent_tl \tl_new:N \l_@@_spfig_desc_tl \tl_new:N \l_@@_spfig_symbol_tl \seq_new:N \l_@@_spfig_pt_seq \tl_new:N \l_@@_spfig_refs_tl \tl_new:N \l_@@_spfig_intent_arg_tl \int_new:N \l_@@_spfig_pt_idx_int \str_new:N \l_@@_geo_print_sym_str \tl_new:N \l_@@_geo_temp_tl % \end{macrocode} % \end{variable} % % \begin{macro}[int]{\@@_spfig_exec:nnn} % Receives the short command name, |Afig| or |Pfig|, in `|#1|' and % the \mymarg{argument} in `|#3|'. Calls % \myhlc|\@@_luafun_geo_afig_parse_and_set:n| or % \myhlc|\@@_luafun_geo_pfig_parse_and_set:n|, reports % |spAfig-invalid-arg| or |spPfig-invalid-arg| on failure, and warns with % |spAfig-read-arg-key| or |spPfig-read-arg-key| when % \mykey[spAfig,spPfig]{read-arg} is given for a bare number/letter % argument that has no vertices, where it has no effect and is ignored. Sets % \myhlc|\l_@@_spfig_op_tl| to \emph{«área»}/\emph{«perímetro»}, then delegates to % \myhlc|\@@_spfig_mathml:n|. % % Before processing the \mymarg{argument}, it sets the optional \mymeta{keys} passed in `|#2|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spfig_exec:nnn { \keys_set:nn { spintent / sp #1 } {#2} \str_if_eq:eeTF {#1} { Afig } { \@@_luafun_geo_afig_parse_and_set:n { \exp_not:n {#3} } } { \@@_luafun_geo_pfig_parse_and_set:n { \exp_not:n {#3} } } \str_if_eq:VnT \l_@@_geo_luaset_error_str { true } { \msg_error:nnee { spintent } { sp #1 -invalid-arg } { \c_backslash_str sp #1 } { \exp_not:n {#3} } } \clist_if_empty:NT \l_@@_geo_luaset_points_clist { \tl_if_empty:cF { l_@@_sp #1 _read_arg_tl } { \msg_warning:nne { spintent } { sp #1 -read-arg-key } { \c_backslash_str sp #1 } } } \str_if_eq:eeTF {#1} { Afig } { \tl_set:Nn \l_@@_spfig_op_tl { área } } { \tl_set:Nn \l_@@_spfig_op_tl { perímetro } } \str_set:Ne \l_@@_geo_print_sym_str { \str_use:c { l_@@_sp #1 _print_sym_str } } \@@_spfig_mathml:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfig_build_simple:n} % Handles the argument-less, table-letter and bare number/letter % cases, none of which has vertices. Builds \myhlc|\l_@@_geo_final_print_tl|, the % visual placed under |\mathcal|'s subscript, and % \myhlc|\l_@@_spfig_desc_tl|, the descriptive part without % \emph{«área»}/\emph{«perímetro»}. Receives the short command name in `|#1|'. % % With an empty argument, both are cleared for \mykey{print-sym}|=auto|/ % |none|; a forced \mykey{print-sym} instead places the symbol alone, % with its own name as the description. % \begin{macrocode} \cs_new_protected:Nn \@@_spfig_build_simple:n { \tl_if_empty:NTF \l_@@_geo_luaset_print_tl { \str_case:VnF \l_@@_geo_print_sym_str { { auto } { \tl_clear:N \l_@@_geo_final_print_tl \tl_clear:N \l_@@_spfig_desc_tl } { none } { \tl_clear:N \l_@@_geo_final_print_tl \tl_clear:N \l_@@_spfig_desc_tl } } { \tl_set:Nn \l_@@_geo_final_print_tl { \@@_spfig_sym: } \tl_set:Ne \l_@@_spfig_desc_tl { \str_if_eq:VnTF \l_@@_geo_print_sym_str { triangle } { triángulo } { cuadrado } } } } % \end{macrocode} % % With a bare number/letter, for which % \myhlc|\l_@@_geo_luaset_letra_word_str| is empty, the value itself becomes % the visual for \mykey{print-sym}|=auto|/|none|, or its own subscript % under a forced symbol otherwise; the description is the spoken % value, prefixed with «sub-» when \mykey{read-sub} is |true|, % further prefixed with the symbol's own name for a forced % \mykey{print-sym}. % \begin{macrocode} { \str_if_empty:NTF \l_@@_geo_luaset_letra_word_str { \str_case:VnF \l_@@_geo_print_sym_str { { auto } { \tl_set:NV \l_@@_geo_final_print_tl \l_@@_geo_luaset_print_tl } { none } { \tl_set:NV \l_@@_geo_final_print_tl \l_@@_geo_luaset_print_tl } } { \tl_set:Nn \l_@@_geo_final_print_tl { \@@_spfig_sym: \c_math_subscript_token { \l_@@_geo_luaset_print_tl } } } \str_case:VnF \l_@@_geo_print_sym_str { { auto } { \tl_set:Ne \l_@@_spfig_desc_tl { \bool_if:cT { l_@@_sp #1 _read_sub_bool } { sub - } \l_@@_geo_luaset_sub_spoken_str } } { none } { \tl_set:Ne \l_@@_spfig_desc_tl { \bool_if:cT { l_@@_sp #1 _read_sub_bool } { sub - } \l_@@_geo_luaset_sub_spoken_str } } } { \tl_set:Ne \l_@@_spfig_desc_tl { del- \str_if_eq:VnTF \l_@@_geo_print_sym_str { triangle } { triángulo } { cuadrado } - \bool_if:cT { l_@@_sp #1 _read_sub_bool } { sub - } \l_@@_geo_luaset_sub_spoken_str } } } % \end{macrocode} % A forced \mykey{print-sym} raises |spAfig-sym-letter-conflict| or % |spPfig-sym-letter-conflict| when \myhlc|\l_@@_geo_luaset_letra_word_str| % is non-empty, which happens with a table letter, that is |F|, |P|, |B|, |L| % or |T|. The visual is the value as given. The description is its own word, % connected as \myhlc|\l_@@_geo_luaset_letra_conector_str| requires, with the % same spoken subscript as above when present. % \begin{macrocode} { \str_case:VnF \l_@@_geo_print_sym_str { { auto } { } { none } { } } { \msg_error:nne { spintent } { sp #1 -sym-letter-conflict } { \c_backslash_str sp #1 } } \tl_set:NV \l_@@_geo_final_print_tl \l_@@_geo_luaset_print_tl \tl_set:Ne \l_@@_spfig_desc_tl { \str_if_empty:NF \l_@@_geo_luaset_letra_conector_str { \l_@@_geo_luaset_letra_conector_str - } \l_@@_geo_luaset_letra_word_str \str_if_empty:NF \l_@@_geo_luaset_sub_spoken_str { - \bool_if:cT { l_@@_sp #1 _read_sub_bool } { sub - } \l_@@_geo_luaset_sub_spoken_str } } } } % \end{macrocode} % The variable \myhlc|\l_@@_spfig_intent_tl| is always the |word-| shape, % without the leading |_|: % \myhlc|\l_@@_spfig_op_tl| alone when \myhlc|\l_@@_spfig_desc_tl| is % empty, both joined by a dash otherwise. \mykey{silent} % overrides \myhlc|\l_@@_spfig_intent_arg_tl| with `|_-|'; with % \mykey{silent}|=false| and \mykey{concept}|=false|, it becomes % |_|\myhlc|\l_@@_spfig_desc_tl|\myhlc|-|, where the descriptive part % is alone, without \emph{«área»}/\emph{«perímetro»} nor, for a table letter, its % connector |de-la|/|del|; with \mykey{concept}|=true| it % is |_|, then \mykey{prefix} when given, and then % \myhlc|\l_@@_spfig_intent_tl|. % \begin{macrocode} \tl_set:Ne \l_@@_spfig_intent_tl { \tl_if_empty:NTF \l_@@_spfig_desc_tl { \l_@@_spfig_op_tl - } { \l_@@_spfig_op_tl - \l_@@_spfig_desc_tl - } } \bool_if:cTF { l_@@_sp #1 _silent_bool } { \tl_set:Nn \l_@@_spfig_intent_arg_tl { _- } } { \bool_if:cTF { l_@@_sp #1 _concept_bool } { \tl_set:Ne \l_@@_spfig_intent_arg_tl { _ \tl_if_empty:cF { l_@@_sp #1 _prefix_tl } { \tl_use:c { l_@@_sp #1 _prefix_tl } - } \l_@@_spfig_intent_tl } } { \tl_set_eq:NN \l_@@_geo_temp_tl \l_@@_spfig_desc_tl \str_if_empty:NF \l_@@_geo_luaset_letra_conector_str { \tl_replace_once:Nee \l_@@_geo_temp_tl { \l_@@_geo_luaset_letra_conector_str - } { } } \tl_set:Ne \l_@@_spfig_intent_arg_tl { _ \l_@@_geo_temp_tl - } } } } % \end{macrocode} % \end{macro} % \begin{macro}[int]{\@@_spfig_build_refs:} % Splits \myhlc|\l_@@_geo_luaset_points_clist| into % \myhlc|\l_@@_spfig_pt_seq| and walks it once to build % \myhlc|\l_@@_spfig_refs_tl|, the comma-separated |$pt1,$pt2,...| % reference list, naming each vertex by position. % \begin{macrocode} \cs_new_protected:Nn \@@_spfig_build_refs: { \seq_set_from_clist:NN \l_@@_spfig_pt_seq \l_@@_geo_luaset_points_clist \tl_clear:N \l_@@_spfig_refs_tl \int_zero:N \l_@@_spfig_pt_idx_int \seq_map_inline:Nn \l_@@_spfig_pt_seq { \int_incr:N \l_@@_spfig_pt_idx_int \tl_if_empty:NF \l_@@_spfig_refs_tl { \tl_put_right:Nn \l_@@_spfig_refs_tl { , } } \tl_put_right:Ne \l_@@_spfig_refs_tl { $ pt \int_use:N \l_@@_spfig_pt_idx_int } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfig_build_concept_vertices:n} % Handles the vertices case. Receives the short command name in % `|#1|'. Must be called after \myhlc|\@@_spfig_build_refs:|. % % The key \mykey{read-arg}, when given, overrides the auto-detected figure % name in \myhlc|\l_@@_spfig_intent_tl|: with \mykey{concept}|=true| % it is still prefixed with |área-del-|/|perímetro-del-|, with % \mykey{concept}|=false| it is used verbatim. With \mykey{read-arg} % empty, \myhlc|\l_@@_spfig_intent_tl| is % \myhlc|\l_@@_geo_luaset_concept_str|. % \begin{macrocode} \cs_new_protected:Nn \@@_spfig_build_concept_vertices:n { \tl_if_empty:cTF { l_@@_sp #1 _read_arg_tl } { \tl_set:Ne \l_@@_spfig_intent_tl { \l_@@_geo_luaset_concept_str } } { \bool_if:cTF { l_@@_sp #1 _concept_bool } { \tl_set:Ne \l_@@_spfig_intent_tl { \l_@@_spfig_op_tl -del- \tl_use:c { l_@@_sp #1 _read_arg_tl } } } { \tl_set:Ne \l_@@_spfig_intent_tl { \tl_use:c { l_@@_sp #1 _read_arg_tl } } } } % \end{macrocode} % Determines \myhlc|\l_@@_spfig_symbol_tl|, which is empty when none % applies: for \mykey{print-sym}|=auto|, the symbol matching % \myhlc|\l_@@_geo_luaset_poly_sym_str|, |\triangle|/ % |\mdlgwhtsquare|, or none when that is empty; \mykey{print-sym}|=none| % always clears it; a forced \mykey{print-sym} requires it to match the % actual vertex count, reporting |spAfig-sym-cardinality-conflict| or % |spPfig-sym-cardinality-conflict| % otherwise. % \begin{macrocode} \str_case:VnF \l_@@_geo_print_sym_str { { auto } { \str_case:Vn \l_@@_geo_luaset_poly_sym_str { { triangle } { \tl_set:Nn \l_@@_spfig_symbol_tl { \triangle } } { square } { \tl_set:Nn \l_@@_spfig_symbol_tl { \mdlgwhtsquare } } } \str_if_empty:NT \l_@@_geo_luaset_poly_sym_str { \tl_clear:N \l_@@_spfig_symbol_tl } } { none } { \tl_clear:N \l_@@_spfig_symbol_tl } } { \str_if_eq:VVTF \l_@@_geo_print_sym_str \l_@@_geo_luaset_poly_sym_str { \tl_set:Nn \l_@@_spfig_symbol_tl { \@@_spfig_sym: } } { \msg_error:nne { spintent } { sp #1 -sym-cardinality-conflict } { \c_backslash_str sp #1 } } } % \end{macrocode} % The key \mykey{silent} sets \myhlc|\l_@@_spfig_intent_arg_tl| to `|_-|'. % Otherwise, with \mykey{read-arg} empty, \mykey{concept} chooses between % \myhlc|_\l_@@_spfig_intent_tl|\myhlc|:prefix(refs)|, with \mykey{prefix} % placed after the leading |_| when given, and |_(refs)| alone; with % \mykey{read-arg} given, it is always % \myhlc|_\l_@@_spfig_intent_tl|\myhlc|:prefix(refs)|. % \begin{macrocode} \bool_if:cTF { l_@@_sp #1 _silent_bool } { \tl_set:Nn \l_@@_spfig_intent_arg_tl { _- } } { \tl_if_empty:cTF { l_@@_sp #1 _read_arg_tl } { \bool_if:cTF { l_@@_sp #1 _concept_bool } { \tl_set:Ne \l_@@_spfig_intent_arg_tl { _ \tl_if_empty:cF { l_@@_sp #1 _prefix_tl } { \tl_use:c { l_@@_sp #1 _prefix_tl } - } \l_@@_spfig_intent_tl :prefix( \l_@@_spfig_refs_tl ) } } { \tl_set:Ne \l_@@_spfig_intent_arg_tl { _( \l_@@_spfig_refs_tl ) } } } { \tl_set:Ne \l_@@_spfig_intent_arg_tl { _ \tl_if_empty:cF { l_@@_sp #1 _prefix_tl } { \tl_use:c { l_@@_sp #1 _prefix_tl } - } \l_@@_spfig_intent_tl :prefix( \l_@@_spfig_refs_tl ) } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfig_points_loop:n} % Walks \myhlc|\l_@@_spfig_pt_seq| a second time, calling % \myhlc|\@@_mathmlarg_point:nnn| once per vertex with its |pt|$n$ % reference name, looked up under \texttt{spAfig} or \texttt{spPfig}. Receives the short command % name in `|#1|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spfig_points_loop:n { \int_zero:N \l_@@_spfig_pt_idx_int \seq_map_inline:Nn \l_@@_spfig_pt_seq { \int_incr:N \l_@@_spfig_pt_idx_int \@@_mathmlarg_point:nnn { pt \int_use:N \l_@@_spfig_pt_idx_int } { sp #1 } { ##1 } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfig_mathml:n} % Receives the short command name in `|#1|'. Branches on whether % \myhlc|\l_@@_geo_luaset_points_clist| is empty. % % Without vertices, \myhlc|\@@_spfig_build_simple:n| builds the content. A % |\MathMLintent| wraps it only when \mykey{concept} is |true|; with % \mykey{concept}|=false| the content is typeset with no |intent|. % % With vertices, \myhlc|\@@_spfig_build_refs:| builds the reference list and % \myhlc|\@@_spfig_build_concept_vertices:n| builds the concept |intent|, % applied with a single native |\MathMLintent|. Inside, % \myhlc|\@@_mathcal:n|\mymarg{A \textnormal{\textcolor{lightgray}{\textbar}} P} % is silenced with \myhlc|\@@_luafun_mrow_intent_arg:nn|; the same is done % with \myhlc|\l_@@_spfig_symbol_tl| when it is non-empty; the % sequence is then walked by \myhlc|\@@_spfig_points_loop:n|. All of this is % nested inside a subscript, \myhlc|\c_math_subscript_token|, which the % recursive lookup of the shared |mathml_filter| callback reaches through the % |sub| field of the noad. % % The function \myhlc|\@@_invisible_sep:| precedes both branches. % It adds a second sibling so that the sole || does not collapse into % ||. % \begin{macrocode} \cs_new_protected:Nn \@@_spfig_mathml:n { \@@_invisible_sep: % (luamml issues #26) \clist_if_empty:NTF \l_@@_geo_luaset_points_clist { \@@_spfig_build_simple:n {#1} \MathMLintent { \l_@@_spfig_intent_arg_tl } { \@@_inner_sep: \str_if_eq:eeTF {#1} { Afig } { \@@_mathcal:n { A } } { \@@_mathcal:n { P } } \tl_if_empty:NF \l_@@_geo_final_print_tl { \c_math_subscript_token { \l_@@_geo_final_print_tl } } } } { \@@_spfig_build_refs: \@@_spfig_build_concept_vertices:n {#1} \MathMLintent { \l_@@_spfig_intent_arg_tl } { \@@_inner_sep: \@@_luafun_mrow_intent_arg:nn { } { intent = ":silent" } { \str_if_eq:eeTF {#1} { Afig } { \@@_mathcal:n { A } } { \@@_mathcal:n { P } } } \c_math_subscript_token { \tl_if_empty:NF \l_@@_spfig_symbol_tl { \@@_luafun_mrow_intent_arg:nn { } { intent = ":silent" } { \tl_use:N \l_@@_spfig_symbol_tl } } \@@_spfig_points_loop:n {#1} } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spAfig, \spPfig} % Both public commands \ics*{spAfig} and \ics*{spPfig} enforce % math mode, open a local group, and delegate execution to % \myhlc|\@@_spfig_exec:nnn|. % \begin{macrocode} \NewDocumentCommand \spAfig { O{} d() } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \tl_if_novalue:nTF {#2} { \@@_spfig_exec:nnn { Afig } {#1} { } } { \@@_spfig_exec:nnn { Afig } {#1} {#2} } \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spAfig } } } \NewDocumentCommand \spPfig { O{} d() } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \tl_if_novalue:nTF {#2} { \@@_spfig_exec:nnn { Pfig } {#1} { } } { \@@_spfig_exec:nnn { Pfig } {#1} {#2} } \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spPfig } } } % \end{macrocode} % \end{macro} % % % \subsection{The commands \cs{sprad} and \cs{spdiam}}\label{cmd:sprad} % % The user-level commands |\sprad| and |\spdiam| provide a semantic % interface for the radius and diameter of a circle. % % \subsubsection{Definition of variables for \cs[no-index]{sprad} and % \cs[no-index]{spdiam}} % % \begin{variable}[int] % { % \l_@@_sp_rad_diam_letra_tl, % \l_@@_sp_rad_diam_word_tl, % \l_@@_sp_rad_diam_read_arg_full_tl, % } % The variables \myhlc|\l_@@_sp_rad_diam_letra_tl| and % \myhlc|\l_@@_sp_rad_diam_word_tl| are scratch variables used by % \myhlc|\@@_sp_rad_diam_mathml:n|. \myhlc|\l_@@_sp_rad_diam_read_arg_full_tl| % is used by \myhlc|\@@_sp_rad_diam_read_arg_print:n| to hold the % text to voice when \mykey{read-arg} is given. % \begin{macrocode} \tl_new:N \l_@@_sp_rad_diam_letra_tl \tl_new:N \l_@@_sp_rad_diam_word_tl \tl_new:N \l_@@_sp_rad_diam_read_arg_full_tl % \end{macrocode} % \end{variable} % % \begin{variable}[int] % { % \l_@@_sprad_mode_str, % \l_@@_sprad_sub_index_str, % \l_@@_sprad_read_arg_tl, % \l_@@_spdiam_mode_str, % \l_@@_spdiam_sub_index_str, % \l_@@_spdiam_read_arg_tl, % } % Each command has its own copy of the string variables % \myhlc|_mode_str| and \myhlc|_sub_index_str| and of the token list % variable \myhlc|_read_arg_tl|, which hold the values of the keys % \mykey[sprad,spdiam]{read-cmd}, \mykey[sprad,spdiam]{sub-index} and % \mykey[sprad,spdiam]{read-arg}. % \begin{macrocode} \clist_map_inline:nn { rad , diam } { \str_new:c { l_@@_sp #1 _mode_str } \str_new:c { l_@@_sp #1 _sub_index_str } \tl_new:c { l_@@_sp #1 _read_arg_tl } } % \end{macrocode} % \end{variable} % % \subsubsection{Definition of keys for \cs[no-index]{sprad} and % \cs[no-index]{spdiam}} % % \begin{macro}[int] % { % read-cmd, % upper-case, % sub-index, % read-sub, % read-arg, % concept, % silent, % print-sym, % unknown, % } % We define the shared \mymeta{keys} \mykey[sprad,spdiam]{read-cmd}, % \mykey[sprad,spdiam]{upper-case}, \mykey[sprad,spdiam]{sub-index}, % \mykey[sprad,spdiam]{read-sub}, \mykey[sprad,spdiam]{read-arg}, % \mykey[sprad,spdiam]{concept}, \mykey[sprad,spdiam]{silent} and % \mykey[spmside]{print-sym}. % \begin{macrocode} \clist_map_inline:nn { rad , diam } { \keys_define:nn { spintent / sp #1 } { read-cmd .choices:nn = { school , mathcat } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:cn { l_@@_sp #1 _mode_str } { school } } {2} { \str_set:cn { l_@@_sp #1 _mode_str } { mathcat } } } }, read-cmd .initial:n = school, read-cmd .value_required:n = true, read-cmd / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-cmd } { school,~mathcat } { \exp_not:n {##1} }, upper-case .bool_set:c = { l_@@_sp #1 _upper_case_bool }, upper-case .initial:n = false, upper-case .value_required:n = true, sub-index .code:n = { \str_set:cn { l_@@_sp #1 _sub_index_str } {##1} }, sub-index .initial:n = , sub-index .value_required:n = true, read-sub .bool_set:c = { l_@@_sp #1 _read_sub_bool }, read-sub .initial:n = false, read-sub .value_required:n = true, read-arg .code:n = { \@@_sanitize_affix:cn { l_@@_sp #1 _read_arg_tl } {##1} }, read-arg .initial:n = , read-arg .value_required:n = true, concept .bool_set:c = { l_@@_sp #1 _concept_bool }, concept .initial:n = true, concept .value_required:n = true, silent .bool_set:c = { l_@@_sp #1 _silent_bool }, silent .initial:n = false, silent .value_required:n = true, print-sym .bool_set:c = { l_@@_sp #1 _print_sym_bool }, print-sym .initial:n = false, print-sym .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { sp #1 } \@@_unknown_keys_cmd:n {##1} }, } } % \end{macrocode} % \begin{important}* % The key \mykey{print-sym} is defined for both commands for % simplicity, but has no effect on |\sprad|. % \end{important} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{sprad} and % \cs[no-index]{spdiam}} % % \begin{macro}[int]{\@@_sp_rad_diam_mathml:n} % The function \myhlc|\@@_sp_rad_diam_mathml:n| sets % \myhlc|\l_@@_sp_rad_diam_letra_tl| to the bare letter, |r| or |R| for % |\sprad| and |d| or |D| for |\spdiam|, according to \mykey{upper-case}; for % |\spdiam| with \mykey{print-sym}|=true|, it silently typesets |\diameter| % first. Receives the short command name in `|#1|': |rad| or |diam|. % \begin{macrocode} \cs_new_protected:Nn \@@_sp_rad_diam_mathml:n { \bool_if:cTF { l_@@_sp #1 _upper_case_bool } { \str_if_eq:eeTF {#1} { rad } { \tl_set:Nn \l_@@_sp_rad_diam_letra_tl { R } } { \tl_set:Nn \l_@@_sp_rad_diam_letra_tl { D } } } { \str_if_eq:eeTF {#1} { rad } { \tl_set:Nn \l_@@_sp_rad_diam_letra_tl { r } } { \tl_set:Nn \l_@@_sp_rad_diam_letra_tl { d } } } \@@_invisible_sep: \str_if_eq:eeT {#1} { diam } { \bool_if:NT \l_@@_spdiam_print_sym_bool { \MathMLintent { :silent } { \diameter } } } % \end{macrocode} % With \mykey{silent}|=true|, the intent is `|_-|': the letter alone with the % shared queue when there is no \mykey{sub-index}, or the letter and the % subscript together in one native |\MathMLintent|, with the subscript % silenced, when there is. % \begin{macrocode} \bool_if:cTF { l_@@_sp #1 _silent_bool } { \str_if_empty:cTF { l_@@_sp #1 _sub_index_str } { \@@_luafun_mrow_intent_arg:nn { } { intent = "_-" } { \l_@@_sp_rad_diam_letra_tl } } { \MathMLintent { _- } { \@@_inner_sep: \l_@@_sp_rad_diam_letra_tl \c_math_subscript_token { \MathMLintent { :silent } { \str_use:c { l_@@_sp #1 _sub_index_str } } } } } } % \end{macrocode} % Otherwise, with \mykey{concept}|=true|, the default, it verbalizes % \emph{\enquote{radio}} or \emph{\enquote{diámetro}}, or the text of % \mykey{read-arg} instead of that word when given, followed by the \mykey{sub-index} if % present, as |sub-|\mymeta{value} or \mymeta{value} alone according to % \mykey{read-sub}; the letter is not read. With no \mykey{sub-index}, the % shared queue tags the letter alone. With one, for \mykey{read-cmd}|=school| % the letter and the subscript are wrapped together in one native % |\MathMLintent| with the subscript silenced, and for % \mykey{read-cmd}|=mathcat| the shared queue tags the letter alone and the % subscript is left bare. % \begin{macrocode} { \tl_if_empty:cTF { l_@@_sp #1 _read_arg_tl } { \bool_if:cTF { l_@@_sp #1 _concept_bool } { \str_if_eq:eeTF {#1} { rad } { \tl_set:Nn \l_@@_sp_rad_diam_word_tl { radio } } { \tl_set:Nn \l_@@_sp_rad_diam_word_tl { diámetro } } \str_if_empty:cTF { l_@@_sp #1 _sub_index_str } { \@@_luafun_mrow_intent_arg:nn { } { intent = "_ \l_@@_sp_rad_diam_word_tl :nofix" } { \l_@@_sp_rad_diam_letra_tl } } { \str_if_eq:eeTF { \str_use:c { l_@@_sp #1 _mode_str } } { school } { \MathMLintent { _ \l_@@_sp_rad_diam_word_tl - \bool_if:cT { l_@@_sp #1 _read_sub_bool } { sub- } \str_use:c { l_@@_sp #1 _sub_index_str } } { \@@_inner_sep: \l_@@_sp_rad_diam_letra_tl \c_math_subscript_token { \MathMLintent { :silent } { \str_use:c { l_@@_sp #1 _sub_index_str } } } } } { \@@_luafun_mrow_intent_arg:nn { } { intent = "_ \l_@@_sp_rad_diam_word_tl :nofix" } { \l_@@_sp_rad_diam_letra_tl } \c_math_subscript_token { \str_use:c { l_@@_sp #1 _sub_index_str } } } } } % \end{macrocode} % With \mykey{concept}|=false|, the letter itself is read, but only when % \mykey{read-cmd} is |school|, built in the same way from the letter. With % \mykey{sub-index}, the letter and the subscript are wrapped together and the % subscript is silenced. With \mykey{read-cmd}|=mathcat|, the intent is left % empty and \mynvmath{MathCAT} applies its own rules to the letter and its % subscript. % \begin{macrocode} { \str_if_empty:cTF { l_@@_sp #1 _sub_index_str } { \str_if_eq:eeTF { \str_use:c { l_@@_sp #1 _mode_str } } { school } { \@@_luafun_mrow_intent_arg:nn { } { intent = "_ \l_@@_sp_rad_diam_letra_tl -" } { \l_@@_sp_rad_diam_letra_tl } } { \l_@@_sp_rad_diam_letra_tl } } { \str_if_eq:eeTF { \str_use:c { l_@@_sp #1 _mode_str } } { school } { \MathMLintent { _ \l_@@_sp_rad_diam_letra_tl - \bool_if:cT { l_@@_sp #1 _read_sub_bool } { sub- } \str_use:c { l_@@_sp #1 _sub_index_str } } { \@@_inner_sep: \l_@@_sp_rad_diam_letra_tl \c_math_subscript_token { \MathMLintent { :silent } { \str_use:c { l_@@_sp #1 _sub_index_str } } } } } { \l_@@_sp_rad_diam_letra_tl \c_math_subscript_token { \str_use:c { l_@@_sp #1 _sub_index_str } } } } } } { \@@_sp_rad_diam_read_arg_print:n {#1} } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_sp_rad_diam_read_arg_print:n} % With \mykey{read-arg} given, \mykey{sub-index}/\mykey{read-sub} % are ignored for the \emph{spoken} content; the subscript, if any, % still displays, silenced. With \mykey{concept}|=true|, the % default, \myhlc|\l_@@_sp_rad_diam_read_arg_full_tl| becomes % \emph{\enquote{radio}}/\emph{\enquote{diámetro}} followed by \mykey{read-arg}'s text; with % |false|, \mykey{read-arg}'s text alone. Combined natively, % regardless of \mykey{read-cmd}: with no \mykey{sub-index}, the % shared queue tags the letter alone; with one, letter and % subscript are wrapped together in one native |\MathMLintent|. % \begin{macrocode} \cs_new_protected:Nn \@@_sp_rad_diam_read_arg_print:n { \bool_if:cTF { l_@@_sp #1 _concept_bool } { \str_if_eq:eeTF {#1} { rad } { \tl_set:Ne \l_@@_sp_rad_diam_read_arg_full_tl { radio- \tl_use:c { l_@@_sp #1 _read_arg_tl } } } { \tl_set:Ne \l_@@_sp_rad_diam_read_arg_full_tl { diámetro- \tl_use:c { l_@@_sp #1 _read_arg_tl } } } } { \tl_set_eq:Nc \l_@@_sp_rad_diam_read_arg_full_tl { l_@@_sp #1 _read_arg_tl } } \str_if_empty:cTF { l_@@_sp #1 _sub_index_str } { \@@_luafun_mrow_intent_arg:nn { } { intent = "_ \l_@@_sp_rad_diam_read_arg_full_tl :nofix" } { \l_@@_sp_rad_diam_letra_tl } } { \MathMLintent { _ \l_@@_sp_rad_diam_read_arg_full_tl :nofix } { \@@_inner_sep: \l_@@_sp_rad_diam_letra_tl \c_math_subscript_token { \MathMLintent { :silent } { \str_use:c { l_@@_sp #1 _sub_index_str } } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_sp_rad_diam_exec:nn} % The function \myhlc|\@@_sp_rad_diam_exec:nn| receives the \emph{short name} % of the command passed in `|#1|' and the optional \mymeta{keys} passed in `|#2|' % then delegating the rendering to \myhlc|\@@_sp_rad_diam_mathml:n|. % \begin{macrocode} \cs_new_protected:Nn \@@_sp_rad_diam_exec:nn { \keys_set:nn { spintent / sp #1 } {#2} \@@_sp_rad_diam_mathml:n {#1} } % \end{macrocode} % \end{macro} % % \begin{macro}{\sprad, \spdiam} % Both public commands \ics*{sprad} and \ics*{spdiam} enforces \emph{math mode}, % opens a local group, and delegates execution to \myhlc|\@@_sp_rad_diam_exec:nn|. % \begin{macrocode} \NewDocumentCommand \sprad { O{} } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_sp_rad_diam_exec:nn { rad } {#1} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \sprad } } } \NewDocumentCommand \spdiam { O{} } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_sp_rad_diam_exec:nn { diam } {#1} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spdiam } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spcirc}}\label{cmd:spcirc} % % The user command |\spcirc| provides a semantic interface for a circle or a % circumference, using the delimited-optional argument syntax |d()|. The % argument holds a required center point and an optional radius, separated by a % comma. The shape of the radius decides whether it is a pure number, a measure, % number~+~unit, a bare letter or a two-letter segment, and it is processed % respectively with |\spnum|, |\spunit| and |\spside|/|\spmside|. % % \subsubsection{Definition of variables for \cs[no-index]{spcirc}} % % \begin{variable}[int]{ % \l_@@_geo_luaset_circ_radio_type_str, % \l_@@_geo_luaset_circ_radio_raw_str} % Shared outputs of \myhlc|\@@_luafun_geo_circ_parse_and_set:n|. % The variable \myhlc|\l_@@_geo_luaset_circ_radio_type_str| holds one of % |number|, |measure|, |label|, |segment|, or an empty string when % no radius was given. \myhlc|\l_@@_geo_luaset_circ_radio_raw_str| % holds the corresponding raw text, ready to be forwarded to the % subcommand that renders it. % \begin{macrocode} \str_new:N \l_@@_geo_luaset_circ_radio_type_str \str_new:N \l_@@_geo_luaset_circ_radio_raw_str % \end{macrocode} % \end{variable} % % \begin{variable}[int]{ % \l_@@_spcirc_mode_str, \l_@@_spcirc_prefix_tl, % \l_@@_spcirc_print_sym_str, \l_@@_spcirc_read_sty_str, % \l_@@_spcirc_radio_sty_str, \l_@@_spcirc_sub_index_str, % \l_@@_spcirc_articulo_str, \l_@@_spcirc_read_arg_tl} % The variable \myhlc|\l_@@_spcirc_mode_str| stores the value set by % the key \mykey[spcirc]{read-cmd}; the variable % \myhlc|\l_@@_spcirc_prefix_tl| stores the value set by the key % \mykey[spcirc]{prefix}. The variable % \myhlc|\l_@@_spcirc_print_sym_str| stores the value set by the key % \mykey[spcirc]{print-sym}, either |usual| or |odot|. The variable % \myhlc|\l_@@_spcirc_read_sty_str| stores the value set by the key % \mykey[spcirc]{read-sty}, either |circun| or |circulo|. The variable % \myhlc|\l_@@_spcirc_radio_sty_str| stores the value set by the key % \mykey[spcirc]{radio-sty}, only meaningful when the radius is a % segment. The variable \myhlc|\l_@@_spcirc_sub_index_str| stores % the value set by the key \mykey[spcirc]{sub-index}. The variable % \myhlc|\l_@@_spcirc_articulo_str| is set internally by % \myhlc|\@@_spcirc_word_and_article:| to |la| or |el|, matching the % grammatical gender of the chosen word. The variable % \myhlc|\l_@@_spcirc_read_arg_tl| stores the value set by the key % \mykey[spcirc]{read-arg}. % \begin{macrocode} \str_new:N \l_@@_spcirc_mode_str \tl_new:N \l_@@_spcirc_prefix_tl \str_new:N \l_@@_spcirc_print_sym_str \str_new:N \l_@@_spcirc_read_sty_str \str_new:N \l_@@_spcirc_radio_sty_str \str_new:N \l_@@_spcirc_sub_index_str \str_new:N \l_@@_spcirc_articulo_str \tl_new:N \l_@@_spcirc_read_arg_tl % \end{macrocode} % \end{variable} % % \begin{variable}[int] % { % \l_@@_spcirc_word_tl, % } % Scratch variables used by \myhlc|\@@_spcirc_exec:nn| to assemble % the chosen word, which is |circunferencia|/|círculo| or the % \mykey[spcirc]{read-arg} override, and the final literal |intent| % string. % \begin{macrocode} \tl_new:N \l_@@_spcirc_word_tl % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spcirc}} % % \begin{macro}[int]{spcirc-invalid-arg} % The \enquote{error} |spcirc-invalid-arg| is raised when the argument does % not match a single center point, optionally followed by a comma and a valid % radius shape. % \begin{macrocode} \msg_new:nnnn { spintent } { spcirc-invalid-arg } { Invalid~argument~'#1'~for~'\token_to_str:N \spcirc'~\msg_line_context:. } { The~argument~must~have~the~form~'(center)'~or~'(center,~radius)'. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spcirc}} % % \begin{macro}[int] % { % read-cmd, % prefix, % print-sym, % read-sty, % diameter, % radio-sty, % sub-index, % read-sub, % read-arg, % silent, % concept, % spside, % spmside, % unknown, % } % We define the \mymeta{keys} \mykey[spcirc]{read-cmd}, % \mykey[spcirc]{prefix}, \mykey[spcirc]{print-sym}, % \mykey[spcirc]{read-sty}, \mykey[spcirc]{diameter}, % \mykey[spcirc]{radio-sty}, \mykey[spcirc]{sub-index}, % \mykey[spcirc]{read-sub}, \mykey[spcirc]{read-arg}, % \mykey[spcirc]{silent} and \mykey[spcirc]{concept}. % \begin{macrocode} \keys_define:nn { spintent / spcirc } { read-cmd .choices:nn = { school , mathcat } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spcirc_mode_str { school } \keys_set:nn { spintent / spside } { read-cmd = school } \keys_set:nn { spintent / spmside } { read-cmd = school } } {2} { \str_set:Nn \l_@@_spcirc_mode_str { mathcat } \keys_set:nn { spintent / spside } { read-cmd = mathcat } \keys_set:nn { spintent / spmside } { read-cmd = mathcat } } } }, read-cmd .initial:n = school, read-cmd .value_required:n = true, read-cmd / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-cmd } { school,~mathcat } { \exp_not:n {#1} }, prefix .code:n = { \@@_sanitize_affix:Nn \l_@@_spcirc_prefix_tl {#1} }, prefix .initial:n = , prefix .value_required:n = true, print-sym .choices:nn = { usual , odot } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spcirc_print_sym_str { usual } } {2} { \str_set:Nn \l_@@_spcirc_print_sym_str { odot } } } }, print-sym .initial:n = odot, print-sym .value_required:n = true, print-sym / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { print-sym } { usual,~odot } { \exp_not:n {#1} }, read-sty .choices:nn = { circun , circulo } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spcirc_read_sty_str { circun } } {2} { \str_set:Nn \l_@@_spcirc_read_sty_str { circulo } } } }, read-sty .initial:n = circun, read-sty .value_required:n = true, read-sty / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-sty } { circun,~circulo } { \exp_not:n {#1} }, diameter .bool_set:N = \l_@@_spcirc_diameter_bool, diameter .initial:n = false, diameter .value_required:n = true, radio-sty .choices:nn = { side , mside } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spcirc_radio_sty_str { side } } {2} { \str_set:Nn \l_@@_spcirc_radio_sty_str { mside } } } }, radio-sty .initial:n = mside, radio-sty .value_required:n = true, radio-sty / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { radio-sty } { side,~mside } { \exp_not:n {#1} }, sub-index .code:n = { \str_set:Nn \l_@@_spcirc_sub_index_str {#1} }, sub-index .initial:n = , sub-index .value_required:n = true, read-sub .bool_set:N = \l_@@_spcirc_read_sub_bool, read-sub .initial:n = false, read-sub .value_required:n = true, read-arg .code:n = { \@@_sanitize_affix:Nn \l_@@_spcirc_read_arg_tl {#1} }, read-arg .initial:n = , read-arg .value_required:n = true, silent .bool_set:N = \l_@@_spcirc_silent_bool, silent .initial:n = false, silent .value_required:n = true, concept .bool_set:N = \l_@@_spcirc_concept_bool, concept .initial:n = true, concept .value_required:n = true, spside .meta:nn = { spintent / spside } {#1}, spside .value_required:n = true, spmside .meta:nn = { spintent / spmside } {#1}, spmside .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spcirc } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spcirc}} % % \begin{variable}[int] % { % \l_@@_spcirc_has_arg_bool, % \l_@@_spcirc_intent_tl, % \l_@@_spcirc_intent_arg_tl, % \l_@@_spcirc_connector_tl, % \l_@@_spcirc_connector_arg_tl, % \l_@@_spcirc_radio_arg_tl, % } % The variable \myhlc|\l_@@_spcirc_has_arg_bool| stores whether the optional % argument |d()| was given. The variable \myhlc|\l_@@_spcirc_intent_tl| stores % the word |circunferencia| or |círculo|, followed by the \mykey{sub-index} % suffix if present. The variable \myhlc|\l_@@_spcirc_connector_tl| stores % |de-centro-en|. The variables \myhlc|\l_@@_spcirc_intent_arg_tl| and % \myhlc|\l_@@_spcirc_connector_arg_tl| store the |intent| passed to % |\MathMLintent| for the word and for the connector, where the center is % referenced as |$pt|. The variable \myhlc|\l_@@_spcirc_radio_arg_tl| stores % the |intent| of the radius, spoken on the delimiter |,|. % \begin{macrocode} \bool_new:N \l_@@_spcirc_has_arg_bool \tl_new:N \l_@@_spcirc_intent_tl \tl_new:N \l_@@_spcirc_intent_arg_tl \tl_new:N \l_@@_spcirc_connector_tl \tl_new:N \l_@@_spcirc_connector_arg_tl \tl_new:N \l_@@_spcirc_radio_arg_tl % \end{macrocode} % \end{variable} % % \begin{macro}[int]{\@@_spcirc_sym:} % Typesets |\bigodot| or the plain letter |C|, according to % \mykey[spcirc]{print-sym}. % \begin{macrocode} \cs_new_protected:Nn \@@_spcirc_sym: { \str_if_eq:VnTF \l_@@_spcirc_print_sym_str { odot } { \group_begin: \textstyle\bigodot \group_end: } { C } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcirc_word_and_article:} % Sets \myhlc|\l_@@_spcirc_word_tl| to \mykey{read-arg} or % |circunferencia|/|círculo| from \mykey{read-sty}, and % \myhlc|\l_@@_spcirc_articulo_str| to the matching article, which % always follows \mykey{read-sty} even when \mykey{read-arg} % overrides the word. % \begin{macrocode} \cs_new_protected:Nn \@@_spcirc_word_and_article: { \tl_if_empty:NTF \l_@@_spcirc_read_arg_tl { \str_if_eq:VnTF \l_@@_spcirc_read_sty_str { circun } { \tl_set:Nn \l_@@_spcirc_word_tl { circunferencia } \str_set:Nn \l_@@_spcirc_articulo_str { la } } { \tl_set:Nn \l_@@_spcirc_word_tl { círculo } \str_set:Nn \l_@@_spcirc_articulo_str { el } } } { \tl_set:NV \l_@@_spcirc_word_tl \l_@@_spcirc_read_arg_tl \str_if_eq:VnTF \l_@@_spcirc_read_sty_str { circun } { \str_set:Nn \l_@@_spcirc_articulo_str { la } } { \str_set:Nn \l_@@_spcirc_articulo_str { el } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcirc_radio_print:} % Branches on \myhlc|\l_@@_geo_luaset_circ_radio_type_str|, % reusing |\spnum|/|\spunit|/|\spside|/|\spmside| directly rather % than reimplementing any parsing. % \begin{macrocode} \cs_new_protected:Nn \@@_spcirc_radio_print: { \str_case:Vn \l_@@_geo_luaset_circ_radio_type_str { { number } { \exp_args:Ne \spnum { \l_@@_geo_luaset_circ_radio_raw_str } } { measure } { \exp_args:Ne \spunit { \l_@@_geo_luaset_circ_radio_raw_str } } { label } { \l_@@_geo_luaset_circ_radio_raw_str } { segment } { \str_if_eq:VnTF \l_@@_spcirc_radio_sty_str { side } { \exp_args:Ne \spside { \l_@@_geo_luaset_circ_radio_raw_str } } { \exp_args:Ne \spmside { \l_@@_geo_luaset_circ_radio_raw_str } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcirc_build_concept:} % Builds the words \myhlc|\l_@@_spcirc_intent_tl|, with % \myhlc|\@@_spcirc_word_and_article:| and an optional \mykey{sub-index} % suffix, and \myhlc|\l_@@_spcirc_connector_tl|, |de-centro-en|. Both are % built even when \myhlc|\l_@@_spcirc_has_arg_bool| is |false|, although % \myhlc|\@@_spcirc_mathml:| does not use them in that case. The center is not % concatenated into them: it is read by reference with % \myhlc|\@@_mathmlarg_point:nnn|. % \begin{macrocode} \cs_new_protected:Nn \@@_spcirc_build_concept: { \@@_spcirc_word_and_article: \tl_set:Ne \l_@@_spcirc_intent_tl { \l_@@_spcirc_word_tl } \str_if_empty:NF \l_@@_spcirc_sub_index_str { \tl_put_right:Ne \l_@@_spcirc_intent_tl { - \bool_if:NT \l_@@_spcirc_read_sub_bool { sub - } \l_@@_spcirc_sub_index_str } } \tl_set:Nn \l_@@_spcirc_connector_tl { de-centro-en } % \end{macrocode} % % The key \mykey{silent}, when |true|, overrides % \myhlc|\l_@@_spcirc_intent_arg_tl|, \myhlc|\l_@@_spcirc_connector_arg_tl| % and \myhlc|\l_@@_spcirc_radio_arg_tl| with `|_-|', regardless of % \mykey{read-arg}. % \begin{macrocode} \bool_if:NTF \l_@@_spcirc_silent_bool { \tl_set:Nn \l_@@_spcirc_intent_arg_tl { _- } \tl_set:Nn \l_@@_spcirc_connector_arg_tl { _- } \tl_set:Nn \l_@@_spcirc_radio_arg_tl { _- } } % \end{macrocode} % % With \mykey{concept}|=true|, \myhlc|\l_@@_spcirc_intent_arg_tl| is % \myhlc|_\l_@@_spcirc_intent_tl| alone, with \mykey{prefix} placed after the % leading |_| when given; \myhlc|\l_@@_spcirc_connector_arg_tl| is % \myhlc|_\l_@@_spcirc_connector_tl|\myhlc|:prefix($pt)|; and % \myhlc|\l_@@_spcirc_radio_arg_tl| is the literal |_y-radio-|, or % |_y-diámetro-| when \mykey{diameter} is |true|, spoken on the delimiter |,| % before the reading of \myhlc|\@@_spcirc_radio_print:|. % \begin{macrocode} { \bool_if:NTF \l_@@_spcirc_concept_bool { \tl_set:Ne \l_@@_spcirc_intent_arg_tl { _ \tl_if_empty:NF \l_@@_spcirc_prefix_tl { \tl_use:N \l_@@_spcirc_prefix_tl - } \l_@@_spcirc_intent_tl } \tl_set:Ne \l_@@_spcirc_connector_arg_tl { _ \l_@@_spcirc_connector_tl :prefix( $ pt ) } \bool_if:NTF \l_@@_spcirc_diameter_bool { \tl_set:Nn \l_@@_spcirc_radio_arg_tl { _y-diámetro- } } { \tl_set:Nn \l_@@_spcirc_radio_arg_tl { _y-radio- } } } % \end{macrocode} % With \mykey{concept}|=false| and \mykey{read-arg} empty, the same `|_-|' and % |_($pt)| silencing applies as with \mykey{silent}. With \mykey{read-arg} % given, none of the three is silenced; the connector and the words |y-radio| % and |y-diámetro| are fixed and \mykey{read-arg} does not change them. % \begin{macrocode} { \tl_if_empty:NTF \l_@@_spcirc_read_arg_tl { \tl_set:Nn \l_@@_spcirc_intent_arg_tl { _- } \tl_set:Ne \l_@@_spcirc_connector_arg_tl { _( $ pt ) } \tl_set:Nn \l_@@_spcirc_radio_arg_tl { _- } } { \tl_set:Ne \l_@@_spcirc_intent_arg_tl { _ \l_@@_spcirc_intent_tl } \tl_set:Ne \l_@@_spcirc_connector_arg_tl { _ \l_@@_spcirc_connector_tl :prefix( $ pt ) } \bool_if:NTF \l_@@_spcirc_diameter_bool { \tl_set:Nn \l_@@_spcirc_radio_arg_tl { _y-diámetro- } } { \tl_set:Nn \l_@@_spcirc_radio_arg_tl { _y-radio- } } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcirc_exec:nn} % Receives the |d()| argument in `|#2|', which is an explicit empty group when omitted % because |\spcirc| converts |-NoValue-| to |{ }| before calling % \myhlc|\@@_spcirc_exec:nn|. When the group is empty, the function sets % \myhlc|\l_@@_spcirc_has_arg_bool| to |false| and does not call % \myhlc|\@@_luafun_geo_circ_parse_and_set:n|. Otherwise it calls that % function, reports |spcirc-invalid-arg| on failure and delegates to % \myhlc|\@@_spcirc_mathml:|, which calls % \myhlc|\@@_spcirc_build_concept:|. % % Before processing the \mymarg{argument}, it sets the optional \mymeta{keys} passed in `|#1|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spcirc_exec:nn { \keys_set:nn { spintent / spcirc } {#1} \tl_if_empty:nTF {#2} { \bool_set_false:N \l_@@_spcirc_has_arg_bool } { \bool_set_true:N \l_@@_spcirc_has_arg_bool \@@_luafun_geo_circ_parse_and_set:n { \exp_not:n {#2} } \str_if_eq:VnT \l_@@_geo_luaset_error_str { true } { \msg_error:nne { spintent } { spcirc-invalid-arg } { \exp_not:n {#2} } } } \@@_spcirc_mathml: } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcirc_mathml:} % Calls \myhlc|\@@_spcirc_build_concept:| and wraps the symbol % \myhlc|\l_@@_spcirc_intent_arg_tl| in its own native |\MathMLintent|. This % is the whole result when \myhlc|\l_@@_spcirc_has_arg_bool| is |false|, % that is, for |\spcirc| with no |(...)|. % % Otherwise, the group |(|center|)|, held in % \myhlc|\l_@@_spcirc_connector_arg_tl|, follows as a separate sibling, outside the |\MathMLintent| of the symbol. % When a radius is present, the delimiter |,| after the center carries % \myhlc|\l_@@_spcirc_radio_arg_tl| and \myhlc|\@@_spcirc_radio_print:| % follows as a sibling; with \mykey{silent}|=true|, it is wrapped in a literal % `|_-|' |\MathMLintent| instead. % % The symbol and, when \mykey{sub-index} is given, its subscript number are % silenced separately with \myhlc|\@@_luafun_mrow_intent_arg:nn|, one queue % entry per leaf. The delimiter |(| is silenced in the same way but without % the usual |{...}| around the character, so that \mypkg{luamml} keeps its % math class and produces || instead of ||. % % The final |)|, which follows \myhlc|\@@_spcirc_radio_print:|, is typeset as % |\rparen| inside its own native |\MathMLintent{:silent}|, outside the shared % queue. % % The center itself is read through \myhlc|\@@_mathmlarg_point:nnn|, % referenced as |$pt|. % \begin{macrocode} \cs_new_protected:Nn \@@_spcirc_mathml: { \@@_invisible_sep: % (luamml issues #26) \@@_spcirc_build_concept: \MathMLintent { \l_@@_spcirc_intent_arg_tl } { \@@_inner_sep: \@@_luafun_mrow_intent_arg:nn { } { intent = ":silent" } { \@@_spcirc_sym: } \str_if_empty:NF \l_@@_spcirc_sub_index_str { \c_math_subscript_token { \@@_luafun_mrow_intent_arg:nn { } { intent = ":silent" } { \l_@@_spcirc_sub_index_str } } } } \bool_if:NT \l_@@_spcirc_has_arg_bool { \str_if_empty:NTF \l_@@_geo_luaset_circ_radio_type_str { \MathMLintent { \l_@@_spcirc_connector_arg_tl } { \@@_inner_sep: \@@_luafun_mrow_intent_arg:nn { } { intent = ":silent" } \lparen \@@_mathmlarg_point:nnV { pt } { spcirc } \l_@@_geo_luaset_points_clist \@@_luafun_mrow_intent_arg:nn { } { intent = ":silent" } \rparen } } { \MathMLintent { \l_@@_spcirc_connector_arg_tl } { \@@_inner_sep: \@@_luafun_mrow_intent_arg:nn { } { intent = ":silent" } \lparen \@@_mathmlarg_point:nnV { pt } { spcirc } \l_@@_geo_luaset_points_clist } \MathMLintent { \l_@@_spcirc_radio_arg_tl } { , } \bool_if:NTF \l_@@_spcirc_silent_bool { \MathMLintent { _- } { \@@_inner_sep: \@@_spcirc_radio_print: } } { \@@_spcirc_radio_print: } \MathMLintent { :silent } { \rparen } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spcirc} % The public command \ics*{spcirc} enforces math mode, opens a local group, % and delegates execution to \myhlc|\@@_spcirc_exec:nn|. An omitted radius % forwards an explicit empty group instead of the |-NoValue-| marker. % \begin{macrocode} \NewDocumentCommand \spcirc { O{} d() } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \tl_if_novalue:nTF {#2} { \@@_spcirc_exec:nn {#1} { } } { \@@_spcirc_exec:nn {#1} {#2} } \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spcirc } } } % \end{macrocode} % \end{macro} % % \subsection{The commands \cs{spAcirc} and \cs{spPcirc}}\label{cmd:spAcirc} % % % The user commands |\spAcirc| and |\spPcirc| typeset the area and % the perimeter of a circle or a circumference. The argument follows % |\spcirc|'s own point-and-radius grammar for a real center, with an % optional radius; when no comma is present and no valid center is % found, it falls back to a bare number/letter shortcut, reusing the % same Lua entry point as |\spAfig|/|\spPfig|. A table letter matched % by that fallback is rejected, since it has no meaning for a circle. % % \subsubsection{Definition of variables for \cs[no-index]{spAcirc} and \cs[no-index]{spPcirc}} % % \begin{variable}[int] % { % \l_@@_spAcirc_mode_str, \l_@@_spPcirc_mode_str, % \l_@@_spAcirc_print_sym_str, \l_@@_spPcirc_print_sym_str, % \l_@@_spAcirc_read_sty_str, \l_@@_spPcirc_read_sty_str, % \l_@@_spAcirc_sub_index_str, \l_@@_spPcirc_sub_index_str, % \l_@@_spAcirc_prefix_tl, \l_@@_spPcirc_prefix_tl, % \l_@@_spAcirc_read_arg_tl, \l_@@_spPcirc_read_arg_tl, % } % These variables store the value set by each corresponding key: % \mykey[spAcirc,spPcirc]{read-cmd}, \mykey[spAcirc,spPcirc]{print-sym}, % \mykey[spAcirc,spPcirc]{read-sty}, % \mykey[spAcirc,spPcirc]{sub-index}, \mykey[spAcirc,spPcirc]{prefix} and % \mykey[spAcirc,spPcirc]{read-arg}, respectively. % \begin{macrocode} \clist_map_inline:nn { Acirc , Pcirc } { \str_new:c { l_@@_sp #1 _mode_str } \str_new:c { l_@@_sp #1 _print_sym_str } \str_new:c { l_@@_sp #1 _read_sty_str } \str_new:c { l_@@_sp #1 _sub_index_str } \tl_new:c { l_@@_sp #1 _prefix_tl } \tl_new:c { l_@@_sp #1 _read_arg_tl } } % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spAcirc} and \cs[no-index]{spPcirc}} % % \begin{macro}[int]{spAcirc-invalid-arg, spPcirc-invalid-arg} % The \enquote{error}s |spAcirc-invalid-arg| and |spPcirc-invalid-arg| are % raised when the argument does not match any of the accepted forms: empty, a % center point, optionally followed by a comma and a radius, or a bare % number/letter. % \begin{macrocode} \clist_map_inline:nn { Acirc , Pcirc } { \msg_new:nnnn { spintent } { sp #1 -invalid-arg } { Invalid~argument~'##2'~for~'\tl_trim_spaces:n {##1}'~\msg_line_context:. } { See~the~manual~for~the~accepted~argument~forms. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spAcirc} and \cs[no-index]{spPcirc}} % % \begin{macro}[int] % { % read-cmd, % prefix, % print-sym, % read-sty, % concept, % sub-index, % read-sub, % read-arg, % silent, % unknown % } % We define the shared \mymeta{keys} \mykey[spAcirc,spPcirc]{read-cmd}, % \mykey[spAcirc,spPcirc]{prefix}, \mykey[spAcirc,spPcirc]{print-sym}, % \mykey[spAcirc,spPcirc]{read-sty}, \mykey[spAcirc,spPcirc]{concept}, % \mykey[spAcirc,spPcirc]{sub-index}, \mykey[spAcirc,spPcirc]{read-sub}, % \mykey[spAcirc,spPcirc]{read-arg} and \mykey[spAcirc,spPcirc]{silent}. % \begin{macrocode} \keys_define:nn { spintent / sp #1 } { read-cmd .choices:nn = { school , mathcat } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:cn { l_@@_sp #1 _mode_str } { school } } {2} { \str_set:cn { l_@@_sp #1 _mode_str } { mathcat } } } }, read-cmd .initial:n = school, read-cmd .value_required:n = true, read-cmd / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-cmd } { school,~mathcat } { \exp_not:n {##1} }, prefix .code:n = { \@@_sanitize_affix:cn { l_@@_sp #1 _prefix_tl } {##1} }, prefix .initial:n = , prefix .value_required:n = true, print-sym .choices:nn = { usual , odot } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:cn { l_@@_sp #1 _print_sym_str } { usual } } {2} { \str_set:cn { l_@@_sp #1 _print_sym_str } { odot } } } }, print-sym .initial:n = odot, print-sym .value_required:n = true, print-sym / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { print-sym } { usual,~odot } { \exp_not:n {##1} }, read-sty .choices:nn = { circun , circulo } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:cn { l_@@_sp #1 _read_sty_str } { circun } } {2} { \str_set:cn { l_@@_sp #1 _read_sty_str } { circulo } } } }, read-sty .initial:n = circun, read-sty .value_required:n = true, read-sty / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-sty } { circun,~circulo } { \exp_not:n {##1} }, concept .bool_set:c = { l_@@_sp #1 _concept_bool }, concept .initial:n = true, concept .value_required:n = true, sub-index .code:n = { \str_set:cn { l_@@_sp #1 _sub_index_str } {##1} }, sub-index .initial:n = , sub-index .value_required:n = true, read-sub .bool_set:c = { l_@@_sp #1 _read_sub_bool }, read-sub .initial:n = false, read-sub .value_required:n = true, read-arg .code:n = { \@@_sanitize_affix:cn { l_@@_sp #1 _read_arg_tl } {##1} }, read-arg .initial:n = , read-arg .value_required:n = true, silent .bool_set:c = { l_@@_sp #1 _silent_bool }, silent .initial:n = false, silent .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { sp #1 } \@@_unknown_keys_cmd:n {##1} }, } } % \end{macrocode} % \end{macro} % % \begin{variable}[int] % { % \l_@@_spcircfig_short_bool, % \l_@@_spcircfig_luaset_comma_str, % \l_@@_spcircfig_op_tl, % \l_@@_spcircfig_del_word_tl, % \l_@@_spcircfig_intent_tl, % \l_@@_spcircfig_intent_arg_tl, % } % The variable \myhlc|\l_@@_spcircfig_short_bool| stores whether the argument % resolved as the short number or letter form, with % \myhlc|\@@_luafun_geo_afig_parse_and_set:n| or % \myhlc|\@@_luafun_geo_pfig_parse_and_set:n|, and not as a circle. The string % variable \myhlc|\l_@@_spcircfig_luaset_comma_str|, set by % \myhlc|\@@_luafun_spcircfig_comma:n|, is |true| when the argument has a % comma. The variable \myhlc|\l_@@_spcircfig_op_tl| holds \emph{«área»} or % \emph{«perímetro»}. The variable \myhlc|\l_@@_spcircfig_intent_tl| holds the spoken % phrase: \myhlc|\l_@@_spcircfig_del_word_tl|, set by % \myhlc|\@@_spcircfig_del_word:n|, an optional \mykey{sub-index} suffix and, % when a center is given, |-con-centro-en|. The center is read by reference % with \myhlc|\@@_mathmlarg_point:nnn|. % \begin{macrocode} \bool_new:N \l_@@_spcircfig_short_bool \str_new:N \l_@@_spcircfig_luaset_comma_str \tl_new:N \l_@@_spcircfig_op_tl \tl_new:N \l_@@_spcircfig_del_word_tl \tl_new:N \l_@@_spcircfig_intent_tl \tl_new:N \l_@@_spcircfig_intent_arg_tl % \end{macrocode} % \end{variable} % % \begin{macro}[int]{\@@_spcircfig_sym:n} % Typesets |\bigodot| or the plain letter |C|, according to % \mykey[spAcirc,spPcirc]{print-sym}. Receives the short command % name in `|#1|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spcircfig_sym:n { \str_if_eq:vnTF { l_@@_sp #1 _print_sym_str } { odot } { \bigodot } { C } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcircfig_word_and_article:n} % Sets \myhlc|\l_@@_spcirc_word_tl| to \mykey{read-arg} or % |circunferencia|/|círculo| from \mykey{read-sty}, and % \myhlc|\l_@@_spcirc_articulo_str| to the matching article, which % always follows \mykey{read-sty} even when \mykey{read-arg} % overrides the word. Receives the short command name in `|#1|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spcircfig_word_and_article:n { \str_if_eq:vnTF { l_@@_sp #1 _read_sty_str } { circun } { \str_set:Nn \l_@@_spcirc_articulo_str { la } } { \str_set:Nn \l_@@_spcirc_articulo_str { el } } \tl_if_empty:cTF { l_@@_sp #1 _read_arg_tl } { \str_if_eq:vnTF { l_@@_sp #1 _read_sty_str } { circun } { \tl_set:Nn \l_@@_spcirc_word_tl { circunferencia } } { \tl_set:Nn \l_@@_spcirc_word_tl { círculo } } } { \tl_set:Ne \l_@@_spcirc_word_tl { \tl_use:c { l_@@_sp #1 _read_arg_tl } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcircfig_del_word:n} % Sets \myhlc|\l_@@_spcircfig_del_word_tl| to the word of % \myhlc|\@@_spcircfig_word_and_article:n|, prefixed with |del-| or |de-la-|. % Receives the short command name in `|#1|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spcircfig_del_word:n { \@@_spcircfig_word_and_article:n {#1} \str_if_eq:VnTF \l_@@_spcirc_articulo_str { el } { \tl_set:Ne \l_@@_spcircfig_del_word_tl { del- \l_@@_spcirc_word_tl } } { \tl_set:Ne \l_@@_spcircfig_del_word_tl { de-la- \l_@@_spcirc_word_tl } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcircfig_build_short:n} % Handles the short number or letter case: builds % \myhlc|\l_@@_spcircfig_intent_arg_tl| as % \myhlc|_\l_@@_spcircfig_op_tl|\myhlc|-|\mymeta{number word}, with % \mykey{prefix} placed after the leading |_| when given and without % |:prefix|, or as `|_-|' when \mykey{silent} is |true| or \mykey{concept} is % |false|. Receives the short command name in `|#1|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spcircfig_build_short:n { \bool_if:cTF { l_@@_sp #1 _silent_bool } { \tl_set:Nn \l_@@_spcircfig_intent_arg_tl { _- } } { \bool_if:cTF { l_@@_sp #1 _concept_bool } { \@@_spcircfig_del_word:n {#1} \tl_set:Ne \l_@@_spcircfig_intent_arg_tl { _ \tl_if_empty:cF { l_@@_sp #1 _prefix_tl } { \tl_use:c { l_@@_sp #1 _prefix_tl } - } \l_@@_spcircfig_op_tl - \l_@@_spcircfig_del_word_tl - \bool_if:cT { l_@@_sp #1 _read_sub_bool } { sub- } \l_@@_geo_luaset_sub_spoken_str } } { \tl_set:Nn \l_@@_spcircfig_intent_arg_tl { _- } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcircfig_finish_intent:nn} % Appends an optional \mykey{sub-index} suffix and, when |#2| is non-empty, % |-con-centro-en| to \myhlc|\l_@@_spcircfig_intent_tl|. Receives the short % command name in `|#1|' and the argument in `|#2|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spcircfig_finish_intent:nn { \str_if_empty:cF { l_@@_sp #1 _sub_index_str } { \tl_put_right:Ne \l_@@_spcircfig_intent_tl { - \bool_if:cT { l_@@_sp #1 _read_sub_bool } { sub- } \str_use:c { l_@@_sp #1 _sub_index_str } } } \tl_if_empty:nF {#2} { \tl_put_right:Nn \l_@@_spcircfig_intent_tl { -con-centro-en } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcircfig_build_concept:nn} % Builds \myhlc|\l_@@_spcircfig_intent_arg_tl|. It is `|_-|' when \mykey{silent} % is |true|, whatever |#2| is. With \mykey{concept}|=false| and % \mykey{read-arg} empty, it is `|_-|', or |_($pt)| when |#2| is non-empty. % Otherwise it is \myhlc|_\l_@@_spcircfig_intent_tl|, with |:prefix($pt)| % appended only when |#2| is non-empty and with \mykey{prefix} placed after % the leading |_| when given. With \mykey{concept}|=true|, % \myhlc|\l_@@_spcircfig_intent_tl| is % \myhlc|\l_@@_spcircfig_op_tl|\myhlc|-|\myhlc|\l_@@_spcircfig_del_word_tl|, % set by \myhlc|\@@_spcircfig_del_word:n|; with \mykey{concept}|=false| and % \mykey{read-arg} given, it is \mykey{read-arg} as given, without % \myhlc|\l_@@_spcircfig_op_tl|, and the user writes the article if needed. In % both cases an optional \mykey{sub-index} suffix and, when |#2| is non-empty, % |-con-centro-en| follow. Receives the short command name in `|#1|' and the % argument in `|#2|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spcircfig_build_concept:nn { \bool_if:cTF { l_@@_sp #1 _silent_bool } { \tl_set:Nn \l_@@_spcircfig_intent_arg_tl { _- } } { \bool_if:cTF { l_@@_sp #1 _concept_bool } { \@@_spcircfig_del_word:n {#1} \tl_set:Ne \l_@@_spcircfig_intent_tl { \l_@@_spcircfig_op_tl - \l_@@_spcircfig_del_word_tl } \@@_spcircfig_finish_intent:nn {#1} {#2} \tl_set:Ne \l_@@_spcircfig_intent_arg_tl { _ \tl_if_empty:cF { l_@@_sp #1 _prefix_tl } { \tl_use:c { l_@@_sp #1 _prefix_tl } - } \l_@@_spcircfig_intent_tl \tl_if_empty:nF {#2} { :prefix ( $ pt ) } } } { \tl_if_empty:cTF { l_@@_sp #1 _read_arg_tl } { \tl_if_empty:nTF {#2} { \tl_set:Nn \l_@@_spcircfig_intent_arg_tl { _- } } { \tl_set:Ne \l_@@_spcircfig_intent_arg_tl { _( $ pt ) } } } { \tl_set:Ne \l_@@_spcircfig_intent_tl { \tl_use:c { l_@@_sp #1 _read_arg_tl } } \@@_spcircfig_finish_intent:nn {#1} {#2} \tl_set:Ne \l_@@_spcircfig_intent_arg_tl { _ \l_@@_spcircfig_intent_tl \tl_if_empty:nF {#2} { :prefix ( $ pt ) } } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcircfig_exec:nnn} % Receives the short command name in `|#1|' and the argument in `|#3|'. Sets % \myhlc|\l_@@_spcircfig_op_tl| to \emph{«área»} or \emph{«perímetro»}. With |#3| empty, it % sets \myhlc|\l_@@_spcircfig_short_bool| to |false| and parses nothing else. % With a comma in |#3|, it reports |spAcirc-invalid-arg| or % |spPcirc-invalid-arg|. Without a comma, it calls % \myhlc|\@@_luafun_geo_circ_parse_and_set:n|; on error, it tries % \myhlc|\@@_luafun_geo_afig_parse_and_set:n| or % \myhlc|\@@_luafun_geo_pfig_parse_and_set:n| for the short number or letter % form, and a table-letter match is an error. It reports |spAcirc-invalid-arg| % or |spPcirc-invalid-arg| on failure, and then delegates to % \myhlc|\@@_spcircfig_build_short:n| or % \myhlc|\@@_spcircfig_build_concept:nn| and to % \myhlc|\@@_spcircfig_mathml:nn|. % % \begin{important}* % The commands |\spAcirc| and |\spPcirc| take no radius or diameter: the % argument names the figure, |A_O|, and the values needed to compute it are % written elsewhere in the document. % \end{important} % % Before processing the \mymarg{argument}, it sets the optional \mymeta{keys} passed in `|#2|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spcircfig_exec:nnn { \keys_set:nn { spintent / sp #1 } {#2} \str_if_eq:eeTF {#1} { Acirc } { \tl_set:Nn \l_@@_spcircfig_op_tl { área } } { \tl_set:Nn \l_@@_spcircfig_op_tl { perímetro } } \tl_if_empty:nTF {#3} { \bool_set_false:N \l_@@_spcircfig_short_bool } { \bool_set_false:N \l_@@_spcircfig_short_bool \@@_luafun_spcircfig_comma:n { \exp_not:n {#3} } \str_if_eq:VnTF \l_@@_spcircfig_luaset_comma_str { true } { \msg_error:nnee { spintent } { sp #1 -invalid-arg } { \c_backslash_str sp #1 } { \exp_not:n {#3} } } { \@@_luafun_geo_circ_parse_and_set:n { \exp_not:n {#3} } \str_if_eq:VnT \l_@@_geo_luaset_error_str { true } { \str_if_eq:eeTF {#1} { Acirc } { \@@_luafun_geo_afig_parse_and_set:n { \exp_not:n {#3} } } { \@@_luafun_geo_pfig_parse_and_set:n { \exp_not:n {#3} } } \str_if_eq:VnF \l_@@_geo_luaset_error_str { true } { \str_if_empty:NTF \l_@@_geo_luaset_letra_word_str { \bool_set_true:N \l_@@_spcircfig_short_bool } { \str_set:Nn \l_@@_geo_luaset_error_str { true } } } } \bool_if:NF \l_@@_spcircfig_short_bool { \str_if_eq:VnT \l_@@_geo_luaset_error_str { true } { \msg_error:nnee { spintent } { sp #1 -invalid-arg } { \c_backslash_str sp #1 } { \exp_not:n {#3} } } } } } \bool_if:NTF \l_@@_spcircfig_short_bool { \@@_spcircfig_build_short:n {#1} } { \@@_spcircfig_build_concept:nn {#1} {#3} } \@@_spcircfig_mathml:nn {#1} {#3} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcircfig_mathml:nn} % Renders \myhlc|\l_@@_spcircfig_intent_arg_tl|, already built by % \myhlc|\@@_spcircfig_exec:nnn|. There are three cases: the short case % prints the symbol with the short value as its subscript; an empty |#2| % prints the symbol alone; a center prints the symbol, plus the optional % \mykey{sub-index} subscript, and the center between parentheses. In every % case a single native |\MathMLintent| wraps % \myhlc|\@@_mathcal:n|\mymarg{A \textnormal{\textcolor{lightgray}{\textbar}} P} % and the whole subscript. The center is read with % \myhlc|\@@_mathmlarg_point:nnn|, referenced as |$pt|. Receives the short % command name in `|#1|' and the argument in `|#2|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spcircfig_mathml:nn { \@@_invisible_sep: % (luamml issues #26) \MathMLintent { \l_@@_spcircfig_intent_arg_tl } { \@@_inner_sep: \@@_luafun_mrow_intent_arg:nn { } { intent = ":silent" } { \str_if_eq:eeTF {#1} { Acirc } { \@@_mathcal:n { A } } { \@@_mathcal:n { P } } } \bool_if:NTF \l_@@_spcircfig_short_bool { \c_math_subscript_token { \@@_spcircfig_sym:n {#1} \c_math_subscript_token { \l_@@_geo_luaset_print_tl } } } { \c_math_subscript_token { \MathMLintent { :silent } { \@@_spcircfig_sym:n {#1} } \str_if_empty:cF { l_@@_sp #1 _sub_index_str } { \c_math_subscript_token { \MathMLintent { :silent } { \str_use:c { l_@@_sp #1 _sub_index_str } } } } \tl_if_empty:nF {#2} { \MathMLintent { :silent } { \lparen } \@@_mathmlarg_point:nnV { pt } { sp #1 } \l_@@_geo_luaset_points_clist \MathMLintent { :silent } { \rparen } } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spAcirc, \spPcirc} % Both public commands \ics*{spAcirc} and \ics*{spPcirc} enforce % math mode, open a local group, and delegate execution to % \myhlc|\@@_spcircfig_exec:nnn|. % \begin{macrocode} \NewDocumentCommand \spAcirc { O{} d() } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \tl_if_novalue:nTF {#2} { \@@_spcircfig_exec:nnn { Acirc } {#1} { } } { \@@_spcircfig_exec:nnn { Acirc } {#1} {#2} } \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spAcirc } } } \NewDocumentCommand \spPcirc { O{} d() } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \tl_if_novalue:nTF {#2} { \@@_spcircfig_exec:nnn { Pcirc } {#1} { } } { \@@_spcircfig_exec:nnn { Pcirc } {#1} {#2} } \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spPcirc } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spVol}}\label{cmd:spVol} % % The user-level command |\spVol| provides a semantic interface for % the volume of a solid, always typesetting |\mathcal{V}|. % % \subsubsection{Definition of variables for \cs[no-index]{spVol}} % % \begin{variable}[int] % { % \l_@@_spVol_mode_str, % \l_@@_spVol_sub_index_str, % \l_@@_spVol_read_arg_tl, % \l_@@_spVol_word_tl, % \l_@@_spVol_suffix_tl, % \l_@@_spVol_native_bool, % \l_@@_spVol_read_arg_full_tl, % } % The variable \myhlc|\l_@@_spVol_mode_str| holds the \mykey{read-cmd} % choice, % |school| or |mathcat|; \myhlc|\l_@@_spVol_sub_index_str| the raw % \mykey{sub-index} text; \myhlc|\l_@@_spVol_read_arg_tl| the % sanitized \mykey{read-arg} text. \myhlc|\l_@@_spVol_word_tl| and % \myhlc|\l_@@_spVol_suffix_tl| are scratch variables holding the % word to voice, which is \emph{«volumen»}, and the text to % attach after it; \myhlc|\l_@@_spVol_native_bool| records whether % that text should be combined natively with the subscript, % silenced, or left for \mynvmath{MathCAT} to read on its own. % The variable \myhlc|\l_@@_spVol_read_arg_full_tl| holds the text to voice when % \mykey{read-arg} is given. % \begin{macrocode} \str_new:N \l_@@_spVol_mode_str \str_new:N \l_@@_spVol_sub_index_str \tl_new:N \l_@@_spVol_read_arg_tl \tl_new:N \l_@@_spVol_word_tl \tl_new:N \l_@@_spVol_suffix_tl \bool_new:N \l_@@_spVol_native_bool \tl_new:N \l_@@_spVol_read_arg_full_tl % \end{macrocode} % \end{variable} % % \subsubsection{Definition of keys for \cs[no-index]{spVol}} % % \begin{macro}[int] % { % read-cmd, % sub-index, % read-sub, % read-arg, % concept, % silent, % unknown, % } % We define the \mymeta{keys} \mykey[spVol]{read-cmd}, % \mykey[spVol]{sub-index}, \mykey[spVol]{read-sub}, % \mykey[spVol]{read-arg}, \mykey[spVol]{concept} and % \mykey[spVol]{silent}. % \begin{macrocode} \keys_define:nn { spintent / spVol } { read-cmd .choices:nn = { school , mathcat } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spVol_mode_str { school } } {2} { \str_set:Nn \l_@@_spVol_mode_str { mathcat } } } }, read-cmd .initial:n = school, read-cmd .value_required:n = true, read-cmd / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { read-cmd } { school,~mathcat } { \exp_not:n {#1} }, sub-index .code:n = { \str_set:Nn \l_@@_spVol_sub_index_str {#1} }, sub-index .initial:n = , sub-index .value_required:n = true, read-sub .bool_set:N = \l_@@_spVol_read_sub_bool, read-sub .initial:n = false, read-sub .value_required:n = true, read-arg .code:n = { \@@_sanitize_affix:Nn \l_@@_spVol_read_arg_tl {#1} }, read-arg .initial:n = , read-arg .value_required:n = true, concept .bool_set:N = \l_@@_spVol_concept_bool, concept .initial:n = true, concept .value_required:n = true, silent .bool_set:N = \l_@@_spVol_silent_bool, silent .initial:n = false, silent .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spVol } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spVol}} % % \begin{macro}[int]{\@@_spVol_mathml:} % The function \myhlc|\@@_spVol_mathml:| calls % \myhlc|\@@_spVol_silent:| when \mykey{silent} is |true|, or % \myhlc|\@@_spVol_verbal:| otherwise. % \begin{macrocode} \cs_new_protected:Nn \@@_spVol_mathml: { \@@_invisible_sep: \bool_if:NTF \l_@@_spVol_silent_bool { \@@_spVol_silent: } { \@@_spVol_verbal: } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spVol_silent:} % The function \myhlc|\@@_spVol_silent:| typesets |\mathcal{V}| with the % intent `|_-|': the symbol alone with the shared queue when there is no % \mykey{sub-index}, or the symbol and the subscript together in one native % |\MathMLintent|, with the subscript silenced, when there is. % \begin{macrocode} \cs_new_protected:Nn \@@_spVol_silent: { \str_if_empty:NTF \l_@@_spVol_sub_index_str { \@@_luafun_mrow_intent_arg:nn { } { intent = "_-" } { \mathcal{V} } } { \MathMLintent { _- } { \@@_inner_sep: \mathcal{V} \c_math_subscript_token { \MathMLintent { :silent } { \l_@@_spVol_sub_index_str } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spVol_verbal:} % The function \myhlc|\@@_spVol_verbal:| delegates to % \myhlc|\@@_spVol_read_arg_print:| when \mykey{read-arg} is given and, % otherwise, with \mykey{concept}|=false|, to \myhlc|\@@_spVol_symbol_print:|. % With \mykey{concept}|=true| it sets \myhlc|\l_@@_spVol_word_tl| to \emph{«volumen»} % and \myhlc|\l_@@_spVol_suffix_tl| to |sub-|\mymeta{value} or \mymeta{value}, % according to \mykey{read-sub}; the suffix is combined natively only with % \mykey{read-cmd}|=school|, and with \mykey{read-cmd}|=mathcat| it is left to % \mynvmath{MathCAT}. It delegates the typesetting to % \myhlc|\@@_spVol_attach_subscript:|. % \begin{macrocode} \cs_new_protected:Nn \@@_spVol_verbal: { \tl_if_empty:NTF \l_@@_spVol_read_arg_tl { \bool_if:NTF \l_@@_spVol_concept_bool { \tl_set:Nn \l_@@_spVol_word_tl { volumen } \str_if_eq:VnTF \l_@@_spVol_mode_str { school } { \bool_set_true:N \l_@@_spVol_native_bool } { \bool_set_false:N \l_@@_spVol_native_bool } \tl_set:Ne \l_@@_spVol_suffix_tl { \bool_if:NT \l_@@_spVol_read_sub_bool { sub- } \l_@@_spVol_sub_index_str } \@@_spVol_attach_subscript: } { \@@_spVol_symbol_print: } } { \@@_spVol_read_arg_print: } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spVol_symbol_print:} % With \mykey{concept}|=false|, the symbol itself is read, but only when % \mykey{read-cmd} is |school|: with no \mykey{sub-index}, the shared queue % tags the symbol with the intent |_V-|; with one, the symbol and the % subscript are wrapped together in one native |\MathMLintent|, with the % subscript silenced. With \mykey{read-cmd}|=mathcat|, the intent is left % empty and \mynvmath{MathCAT} applies its own rules to the symbol and its % subscript. % \begin{macrocode} \cs_new_protected:Nn \@@_spVol_symbol_print: { \str_if_empty:NTF \l_@@_spVol_sub_index_str { \str_if_eq:VnTF \l_@@_spVol_mode_str { school } { \@@_luafun_mrow_intent_arg:nn { } { intent = "_V-" } { \mathcal{V} } } { \mathcal{V} } } { \str_if_eq:VnTF \l_@@_spVol_mode_str { school } { \MathMLintent { _V- \bool_if:NT \l_@@_spVol_read_sub_bool { sub- } \l_@@_spVol_sub_index_str } { \@@_inner_sep: \mathcal{V} \c_math_subscript_token { \MathMLintent { :silent } { \l_@@_spVol_sub_index_str } } } } { \mathcal{V} \c_math_subscript_token { \l_@@_spVol_sub_index_str } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spVol_attach_subscript:} % The function \myhlc|\@@_spVol_attach_subscript:| typesets % |\mathcal{V}|. An empty \mykey{sub-index} tags the symbol alone % through the shared queue, and \myhlc|\l_@@_spVol_suffix_tl| is % discarded since there is no subscript to attach it to. A given % one combines symbol, subscript, and % \myhlc|\l_@@_spVol_suffix_tl| in one native |\MathMLintent|, % subscript silenced, when \myhlc|\l_@@_spVol_native_bool| is % |true|, or tags the symbol alone through the shared queue with the % subscript left bare when |false|. % \begin{macrocode} \cs_new_protected:Nn \@@_spVol_attach_subscript: { \str_if_empty:NTF \l_@@_spVol_sub_index_str { \@@_luafun_mrow_intent_arg:nn { } { intent = "_ \l_@@_spVol_word_tl :nofix" } { \mathcal{V} } } { \bool_if:NTF \l_@@_spVol_native_bool { \MathMLintent { _ \l_@@_spVol_word_tl - \l_@@_spVol_suffix_tl :nofix } { \@@_inner_sep: \mathcal{V} \c_math_subscript_token { \MathMLintent { :silent } { \l_@@_spVol_sub_index_str } } } } { \@@_luafun_mrow_intent_arg:nn { } { intent = "_ \l_@@_spVol_word_tl :nofix" } { \mathcal{V} } \c_math_subscript_token { \l_@@_spVol_sub_index_str } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spVol_read_arg_print:} % With \mykey{read-arg} given, \mykey{sub-index}/\mykey{read-sub} % are ignored for the \emph{spoken} content; the subscript, if any, % still displays, silenced. With \mykey{concept}|=true|, % \myhlc|\l_@@_spVol_read_arg_full_tl| becomes \emph{«volumen»} followed by % \mykey{read-arg}'s text; with |false|, \mykey{read-arg}'s text % alone. Combined natively regardless of \mykey{read-cmd}: with no % \mykey{sub-index}, the shared queue tags the symbol alone; with % one, symbol and subscript are wrapped together in one native % |\MathMLintent|. % \begin{macrocode} \cs_new_protected:Nn \@@_spVol_read_arg_print: { \bool_if:NTF \l_@@_spVol_concept_bool { \tl_set:Ne \l_@@_spVol_read_arg_full_tl { volumen- \tl_use:N \l_@@_spVol_read_arg_tl } } { \tl_set_eq:NN \l_@@_spVol_read_arg_full_tl \l_@@_spVol_read_arg_tl } \str_if_empty:NTF \l_@@_spVol_sub_index_str { \@@_luafun_mrow_intent_arg:nn { } { intent = "_ \l_@@_spVol_read_arg_full_tl :nofix" } { \mathcal{V} } } { \MathMLintent { _ \l_@@_spVol_read_arg_full_tl :nofix } { \@@_inner_sep: \mathcal{V} \c_math_subscript_token { \MathMLintent { :silent } { \l_@@_spVol_sub_index_str } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spVol} % The public command \ics*{spVol} enforces \emph{math mode}, opens a % local group, and delegates execution to \myhlc|\@@_spVol_mathml:|. % \begin{macrocode} \NewDocumentCommand \spVol { O{} } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \keys_set:nn { spintent / spVol } {#1} \@@_spVol_mathml: \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spVol } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spProm}}\label{cmd:spProm} % % The user command |\spProm| typesets its \mymarg{argument} with a bar % on top, |\bar|, the usual notation for a mean, and reads it % \emph{«promedio de»} followed by the argument. The argument is not % parsed: it is typeset as given and read by \mynvmath{MathCAT} with its % own rules. % % \subsubsection{Definition of variables for \cs[no-index]{spProm}} % % \begin{variable}[int] % { % \l_@@_spProm_read_arg_tl, % \l_@@_spProm_intent_tl, % } % The variable \myhlc|\l_@@_spProm_read_arg_tl| holds the normalized text % of the \mykey{read-arg} key, and % \myhlc|\l_@@_spProm_intent_tl| holds the name of the intent used by % \myhlc|\@@_spProm_exec:nn|. % \begin{macrocode} \tl_new:N \l_@@_spProm_read_arg_tl \tl_new:N \l_@@_spProm_intent_tl % \end{macrocode} % \end{variable} % % \subsubsection{Definition of keys for \cs[no-index]{spProm}} % % \begin{macro}[int]{silent, read-arg, unknown} % We define the \mymeta{keys} \mykey[spProm]{silent} and % \mykey[spProm]{read-arg}. % \begin{macrocode} \keys_define:nn { spintent / spProm } { silent .bool_set:N = \l_@@_spProm_silent_bool, silent .initial:n = false, silent .value_required:n = true, read-arg .code:n = { \@@_sanitize_affix:Nn \l_@@_spProm_read_arg_tl {#1} }, read-arg .initial:n = , read-arg .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spProm } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spProm}} % % \begin{macro}[int]{\@@_spProm_exec:nn} % The function \myhlc|\@@_spProm_exec:nn| receives the optional % \mymeta{keys} in `|#1|' and the \mymarg{argument} in `|#2|'. It sets the % \mymeta{keys} and calls \myhlc|\@@_spProm_mathml:n|. % \begin{macrocode} \cs_new_protected:Nn \@@_spProm_exec:nn { \keys_set:nn { spintent / spProm } {#1} \@@_spProm_mathml:n {#2} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spProm_mathml:n} % The function \myhlc|\@@_spProm_mathml:n| receives the \mymarg{argument} % in `|#1|'. Under \mykey{silent} the whole expression is silenced as one % unit with the intent `|_-|'. % % Otherwise the \mymarg{argument} is tagged through a native % |\MathMLarg|, |a|, inside |\bar|, and the outer |\MathMLintent| % reads \myhlc|_promedio-de:prefix($a)|. When \mykey{read-arg} is not % empty, its text replaces |promedio-de|. The inner ghost % \myhlc|\@@_inner_sep:| keeps the content of the intent from % collapsing into a single child; a second one inside the % |\MathMLarg| does the same for the argument, whose |arg| attribute % would otherwise be lost when the argument is a single compound % element, such as a letter with a subscript. % \begin{macrocode} \cs_new_protected:Nn \@@_spProm_mathml:n { \@@_invisible_sep: \bool_if:NTF \l_@@_spProm_silent_bool { \MathMLintent { _- } { \@@_inner_sep: \bar {#1} } } { \tl_if_empty:NTF \l_@@_spProm_read_arg_tl { \tl_set:Nn \l_@@_spProm_intent_tl { promedio-de } } { \tl_set_eq:NN \l_@@_spProm_intent_tl \l_@@_spProm_read_arg_tl } \MathMLintent { _ \l_@@_spProm_intent_tl :prefix($a) } { \@@_inner_sep: \bar { \MathMLarg { a } { \@@_inner_sep: #1 } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spProm} % The public command \ics*{spProm} enforces \emph{math mode}, opens a % local group, and delegates execution to \myhlc|\@@_spProm_exec:nn|. % \begin{macrocode} \NewDocumentCommand \spProm { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_spProm_exec:nn {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spProm } } } % \end{macrocode} % \end{macro} % % \subsection{The commands of the \texttt{stat} family}\label{cmd:stat} % % The user commands |\spmedia|, |\spmediana|, |\spmoda|, |\spdesv|, % |\spvarz|, |\sprango|, |\spriq|, |\spmin| and |\spmax| typeset the % usual symbols of descriptive statistics: |MA| for the arithmetic mean, |MD| for the % median, |MO| for the mode, |s| for the standard deviation, |s^{2}| for the variance, % |R| for the range, |RIQ| for the interquartile range, |x_{\min}| for the minimum, and % |x_{\max}| for the maximum. Each one is read with the name of the concept; % |\spdesv| and |\spvarz| also read their letter. % % \subsubsection{Definition of variables for the \texttt{stat} family} % % \begin{variable}[int] % { % \l_@@_spmedia_sub_index_str, % \l_@@_spmedia_read_arg_tl, % \l_@@_spmediana_sub_index_str, % \l_@@_spmediana_read_arg_tl, % \l_@@_spmoda_sub_index_str, % \l_@@_spmoda_read_arg_tl, % \l_@@_spdesv_sub_index_str, % \l_@@_spdesv_read_arg_tl, % \l_@@_spvarz_sub_index_str, % \l_@@_spvarz_read_arg_tl, % \l_@@_sprango_sub_index_str, % \l_@@_sprango_read_arg_tl, % \l_@@_spriq_sub_index_str, % \l_@@_spriq_read_arg_tl, % \l_@@_spmin_sub_index_str, % \l_@@_spmin_read_arg_tl, % \l_@@_spmax_sub_index_str, % \l_@@_spmax_read_arg_tl, % } % Each command of the family has its own copy of the variables % \myhlc|_sub_index_str| and \myhlc|_read_arg_tl|, which hold the values of % the keys \mykey{sub-index} and \mykey{read-arg}, so each command can be % configured separately with |\setspintent|. Since the symbol of |\spmin| % and |\spmax| already carries a subscript, they have no \mykey{sub-index} % nor \mykey{read-sub} keys; their variable \myhlc|_sub_index_str| exists % only because \myhlc|\@@_stat_mathml:nnnnnn| reads it, and always keeps its % initial value. % \begin{macrocode} \clist_map_inline:nn { spmedia , spmediana , spmoda , spdesv , spvarz , sprango , spriq , spmin , spmax } { \str_new:c { l_@@_ #1 _sub_index_str } \tl_new:c { l_@@_ #1 _read_arg_tl } } % \end{macrocode} % \end{variable} % % \subsubsection{Definition of keys for the \texttt{stat} family} % % \begin{macro}[int]{silent, sub-index, read-sub, read-arg, unknown} % We define the \mymeta{keys} % \mykey[spmedia,spmediana,spmoda,spdesv,spvarz,sprango,spriq]{silent}, % \mykey[spmedia,spmediana,spmoda,spdesv,spvarz,sprango,spriq]{sub-index}, % \mykey[spmedia,spmediana,spmoda,spdesv,spvarz,sprango,spriq]{read-sub} % and \mykey[spmedia,spmediana,spmoda,spdesv,spvarz,sprango,spriq]{read-arg} % for the seven commands |\spmedia|, |\spmediana|, |\spmoda|, |\spdesv|, % |\spvarz|, |\sprango| and |\spriq|. % \begin{macrocode} \clist_map_inline:nn { spmedia , spmediana , spmoda , spdesv , spvarz , sprango , spriq } { \keys_define:nn { spintent / #1 } { silent .bool_set:c = { l_@@_ #1 _silent_bool }, silent .initial:n = false, silent .value_required:n = true, sub-index .code:n = { \str_set:cn { l_@@_ #1 _sub_index_str } {##1} }, sub-index .initial:n = , sub-index .value_required:n = true, read-sub .bool_set:c = { l_@@_ #1 _read_sub_bool }, read-sub .initial:n = false, read-sub .value_required:n = true, read-arg .code:n = { \@@_sanitize_affix:cn { l_@@_ #1 _read_arg_tl } {##1} }, read-arg .initial:n = , read-arg .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str {#1} \@@_unknown_keys_cmd:n {##1} }, } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{sigma} % The key \mykey[spdesv,spvarz]{sigma} exists only for |\spdesv| and % |\spvarz|, and it starts in |false|. % \begin{macrocode} \clist_map_inline:nn { spdesv , spvarz } { \keys_define:nn { spintent / #1 } { sigma .bool_set:c = { l_@@_ #1 _sigma_bool }, sigma .initial:n = false, sigma .value_required:n = true, read-sub .initial:n = true, } } % \end{macrocode} % \begin{important}* % For |\spdesv| and |\spvarz|, which read a letter, \mykey{read-sub} % starts in |true|, since |s sub x| sounds more natural than |s de x| and it % avoids hearing the \mymeta{x} as part of the exponent in % |varianza s de x al cuadrado|; for the rest of the commands it % starts in |false|. % \end{important} % \end{macro} % % \begin{macro}[int]{silent, read-arg, unknown} % Since the symbol of |\spmin| and |\spmax| already carries a subscript, % we define for them only the \mymeta{keys} \mykey[spmin,spmax]{silent} % and \mykey[spmin,spmax]{read-arg}. % \begin{macrocode} \clist_map_inline:nn { spmin , spmax } { \keys_define:nn { spintent / #1 } { silent .bool_set:c = { l_@@_ #1 _silent_bool }, silent .initial:n = false, silent .value_required:n = true, read-arg .code:n = { \@@_sanitize_affix:cn { l_@@_ #1 _read_arg_tl } {##1} }, read-arg .initial:n = , read-arg .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str {#1} \@@_unknown_keys_cmd:n {##1} }, } } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for the \texttt{stat} family} % % \begin{macro}[int]{\@@_stat_typeset:nnn} % The function \myhlc|\@@_stat_typeset:nnn| typesets the symbol. It % receives the short name of the command in `|#1|', the symbol in `|#2|' % and the exponent in `|#3|', empty when there is none. The symbol is % followed by the \mykey{sub-index}, if present, with its own % |\MathMLintent| set to |:silent|, and then by the exponent. % \begin{macrocode} \cs_new_protected:Nn \@@_stat_typeset:nnn { #2 \str_if_empty:cF { l_@@_ #1 _sub_index_str } { \c_math_subscript_token { \MathMLintent { :silent } { \str_use:c { l_@@_ #1 _sub_index_str } } } } \tl_if_blank:nF {#3} { \c_math_superscript_token {#3} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_stat_mathml:nnnnnn} % The function \myhlc|\@@_stat_mathml:nnnnnn| receives the short name of the % command in `|#1|', the symbol in `|#2|', the exponent in `|#3|', the % name of the concept in `|#4|', the name of the letter in `|#5|', empty % when the symbol is not a letter, and the spoken form of the exponent in % `|#6|', empty when there is none, the last three already written as % they go in the intent, with hyphens. The symbol, with its subscript and % exponent, is always typeset by \myhlc|\@@_stat_typeset:nnn| inside one % native |\MathMLintent|, preceded by the inner ghost % \myhlc|\@@_inner_sep:|. % % With \mykey{silent}|=true|, the intent is `|_-|'. With \mykey{read-arg} % given, the intent is |_|\mymeta{read-arg}|:nofix| and % \mykey{sub-index} and \mykey{read-sub} are ignored for the spoken % content; the subscript, if any, still displays, silenced. Otherwise % the spoken content is the concept, followed by the letter and, when % there is a \mykey{sub-index}, by |de-|\mymeta{value}, or by |sub-|\mymeta{value} % when \mykey{read-sub} is |true|, which is its default for |\spdesv| and % |\spvarz|, and last by the exponent. With no % \mykey{sub-index} the intent ends in |:nofix|. % \begin{macrocode} \cs_new_protected:Nn \@@_stat_mathml:nnnnnn { \@@_invisible_sep: \bool_if:cTF { l_@@_ #1 _silent_bool } { \MathMLintent { _- } { \@@_inner_sep: \@@_stat_typeset:nnn {#1} {#2} {#3} } } { \tl_if_empty:cTF { l_@@_ #1 _read_arg_tl } { \str_if_empty:cTF { l_@@_ #1 _sub_index_str } { \MathMLintent { _ #4 \tl_if_blank:nF {#5} { - #5 } \tl_if_blank:nF {#6} { - #6 } :nofix } { \@@_inner_sep: \@@_stat_typeset:nnn {#1} {#2} {#3} } } { \MathMLintent { _ #4 \tl_if_blank:nF {#5} { - #5 } - \bool_if:cTF { l_@@_ #1 _read_sub_bool } { sub } { de } - \str_use:c { l_@@_ #1 _sub_index_str } \tl_if_blank:nF {#6} { - #6 } } { \@@_inner_sep: \@@_stat_typeset:nnn {#1} {#2} {#3} } } } { \MathMLintent { _ \tl_use:c { l_@@_ #1 _read_arg_tl } :nofix } { \@@_inner_sep: \@@_stat_typeset:nnn {#1} {#2} {#3} } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_stat_exec:nnn, \@@_stat_simple:nnn} % The function \myhlc|\@@_stat_exec:nnn| holds what all the commands of % the family share. It receives the short name of the command in `|#1|', % the optional \mymeta{keys} in `|#2|' and the code that prints the symbol % in `|#3|'. It enforces \emph{math mode}, opens a local group, sets the % \mymeta{keys} and only then runs `|#3|', so that the code can depend on % the \mymeta{keys} already set, as \mykey{sigma} does. % % The function \myhlc|\@@_stat_simple:nnn| is the short form of % \myhlc|\@@_stat_mathml:nnnnnn| for the symbols with neither a letter nor % an exponent. It receives the short name in `|#1|', the symbol in `|#2|' % and the name of the concept in `|#3|'. % \begin{macrocode} \cs_new_protected:Nn \@@_stat_exec:nnn { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \keys_set:nn { spintent / #1 } {#2} #3 \group_end: } { \msg_error:nne { spintent } { need-math-mode } { \exp_not:c {#1} } } } \cs_new_protected:Nn \@@_stat_simple:nnn { \@@_stat_mathml:nnnnnn {#1} {#2} { } {#3} { } { } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spmedia, \spmediana, \spmoda} % The public commands \ics*{spmedia}, \ics*{spmediana} and \ics*{spmoda} delegate % execution to \myhlc|\@@_stat_exec:nnn|, which prints the symbols |MA|, % |MD| and |MO| through \myhlc|\@@_stat_simple:nnn| with the concepts % |media-aritmética|, |mediana| and |moda|. % \begin{macrocode} \NewDocumentCommand \spmedia { O{} } { \@@_stat_exec:nnn { spmedia } {#1} { \@@_stat_simple:nnn { spmedia } { \mathrm{MA} } { media-aritmética } } } \NewDocumentCommand \spmediana { O{} } { \@@_stat_exec:nnn { spmediana } {#1} { \@@_stat_simple:nnn { spmediana } { \mathrm{MD} } { mediana } } } \NewDocumentCommand \spmoda { O{} } { \@@_stat_exec:nnn { spmoda } {#1} { \@@_stat_simple:nnn { spmoda } { \mathrm{MO} } { moda } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spdesv, \spvarz} % The public commands \ics*{spdesv} and \ics*{spvarz} work the same way, but call % \myhlc|\@@_stat_mathml:nnnnnn| directly. The symbol is |s|, or |\sigma| % when \mykey{sigma} is |true|, and its name is part of the spoken % content; |\spvarz| adds the exponent |2|, spoken |al-cuadrado|. The % concepts are |desviación-estándar| and |varianza|. % \begin{macrocode} \NewDocumentCommand \spdesv { O{} } { \@@_stat_exec:nnn { spdesv } {#1} { \bool_if:NTF \l_@@_spdesv_sigma_bool { \@@_stat_mathml:nnnnnn { spdesv } { \sigma } { } { desviación-estándar } { sigma } { } } { \@@_stat_mathml:nnnnnn { spdesv } { s } { } { desviación-estándar } { s } { } } } } \NewDocumentCommand \spvarz { O{} } { \@@_stat_exec:nnn { spvarz } {#1} { \bool_if:NTF \l_@@_spvarz_sigma_bool { \@@_stat_mathml:nnnnnn { spvarz } { \sigma } { 2 } { varianza } { sigma } { al-cuadrado } } { \@@_stat_mathml:nnnnnn { spvarz } { s } { 2 } { varianza } { s } { al-cuadrado } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\sprango, \spriq, \spmin, \spmax} % The public commands \ics*{sprango}, \ics*{spriq}, \ics*{spmin} and \ics*{spmax} work % like the first three, with the symbols |R|, |RIQ|, |x_{\min}| and % |x_{\max}| and the concepts |rango|, |rango-intercuartil|, |mínimo| and % |máximo|. % \begin{macrocode} \NewDocumentCommand \sprango { O{} } { \@@_stat_exec:nnn { sprango } {#1} { \@@_stat_simple:nnn { sprango } { R } { rango } } } \NewDocumentCommand \spriq { O{} } { \@@_stat_exec:nnn { spriq } {#1} { \@@_stat_simple:nnn { spriq } { \mathrm{RIQ} } { rango-intercuartil } } } \NewDocumentCommand \spmin { O{} } { \@@_stat_exec:nnn { spmin } {#1} { \@@_stat_simple:nnn { spmin } { x \c_math_subscript_token { \min } } { mínimo } } } \NewDocumentCommand \spmax { O{} } { \@@_stat_exec:nnn { spmax } {#1} { \@@_stat_simple:nnn { spmax } { x \c_math_subscript_token { \max } } { máximo } } } % \end{macrocode} % \end{macro} % % \subsection{The commands of the \texttt{stat\_pos} family}\label{cmd:stat-pos} % % The user commands |\spcuartil|, |\spdecil| and |\spPerc| typeset the % measures of position: |Q_{k}| for quartiles, |D_{k}| for deciles, and |P_{k}| % for percentiles. The index |k| is the \mymarg{argument}: an integer from % |1| to |3|, |1| to |9| and |1| to |99| respectively. Each one is read % with the name of the measure and the index, for example, % \emph{«cuartil 1»}. % % \subsubsection{Definition of variables for the \texttt{stat\_pos} family} % % \begin{variable}[int] % { % \l_@@_spcuartil_read_arg_tl, % \l_@@_spdecil_read_arg_tl, % \l_@@_spPerc_read_arg_tl, % \l_@@_stat_pos_error_bool, % \l_@@_stat_pos_value_int, % } % Each command of the family has its own copy of the variable % \myhlc|_read_arg_tl|, so each command can be configured separately with % |\setspintent|. The boolean variable % \myhlc|\l_@@_stat_pos_error_bool| is % set to |true| by \myhlc|\@@_stat_pos_check:nnn| when the index is rejected, % so that nothing is typeset. The integer variable % \myhlc|\l_@@_stat_pos_value_int| holds the accepted index without leading % zeros. % \begin{macrocode} \clist_map_inline:nn { spcuartil , spdecil , spPerc } { \tl_new:c { l_@@_ #1 _read_arg_tl } } \bool_new:N \l_@@_stat_pos_error_bool \int_new:N \l_@@_stat_pos_value_int % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for the \texttt{stat\_pos} family} % % \begin{macro}[int]{stat-pos-invalid-index} % The \enquote{error} |stat-pos-invalid-index| is raised when the index passed % to |\spcuartil|, |\spdecil| or |\spPerc| is not an integer without sign % between |1| and the maximum of the measure. % \begin{macrocode} \msg_new:nnnn { spintent } { stat-pos-invalid-index } { Invalid~index~'#2'~for~'\tl_trim_spaces:n {#1}'~\msg_line_context:. } { Only~an~integer~without~sign~between~'1'~and~'#3'~is~accepted. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for the \texttt{stat\_pos} family} % % \begin{macro}[int]{silent, read-arg, unknown} % We define the \mymeta{keys} \mykey[spcuartil,spdecil,spPerc]{silent} % and \mykey[spcuartil,spdecil,spPerc]{read-arg} for the three commands. % \begin{macrocode} \clist_map_inline:nn { spcuartil , spdecil , spPerc } { \keys_define:nn { spintent / #1 } { silent .bool_set:c = { l_@@_ #1 _silent_bool }, silent .initial:n = false, silent .value_required:n = true, read-arg .code:n = { \@@_sanitize_affix:cn { l_@@_ #1 _read_arg_tl } {##1} }, read-arg .initial:n = , read-arg .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str {#1} \@@_unknown_keys_cmd:n {##1} }, } } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for the \texttt{stat\_pos} family} % % \begin{macro}[int]{\@@_stat_pos_check:nnn} % The function \myhlc|\@@_stat_pos_check:nnn| receives the short name of the % command in `|#1|', the maximum of the index in `|#2|' and the index % in `|#3|'. It calls \myhlc|\@@_luafun_clean_split_and_set:n| on the % index and accepts it only if it is an integer without sign between % |1| and `|#2|'. Otherwise it raises |stat-pos-invalid-index|, whatever the % value of \mykey{silent}, and sets \myhlc|\l_@@_stat_pos_error_bool| to |true|. % \begin{macrocode} \cs_new_protected:Nn \@@_stat_pos_check:nnn { \@@_luafun_clean_split_and_set:n { \exp_not:n {#3} } \bool_set_true:N \l_@@_stat_pos_error_bool \bool_lazy_and:nnT { \str_if_eq_p:Vn \l_@@_luaset_has_integer_str { true } } { \tl_if_empty_p:N \l_@@_luaset_sign_tl } { \bool_lazy_and:nnT { \int_compare_p:nNn { \l_@@_luaset_only_part_int_str } > { 0 } } { \int_compare_p:nNn { \l_@@_luaset_only_part_int_str } < { #2 + 1 } } { \bool_set_false:N \l_@@_stat_pos_error_bool } } \bool_if:NT \l_@@_stat_pos_error_bool { \msg_error:nneee { spintent } { stat-pos-invalid-index } { \token_to_str:c {#1} } { \exp_not:n {#3} } {#2} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_stat_pos_typeset:nn} % The function \myhlc|\@@_stat_pos_typeset:nn| typesets the symbol in `|#1|' % with the index in `|#2|' as a subscript, with its own |\MathMLintent| % set to |:silent|. % \begin{macrocode} \cs_new_protected:Nn \@@_stat_pos_typeset:nn { #1 \c_math_subscript_token { \MathMLintent { :silent } {#2} } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_stat_pos_mathml:nnnn, \@@_stat_pos_mathml:nnnV} % The function \myhlc|\@@_stat_pos_mathml:nnnn| receives the short name of the % command in `|#1|', the symbol in `|#2|', the name of the measure in % `|#3|' and the index in `|#4|'. The symbol with its index is always % typeset by \myhlc|\@@_stat_pos_typeset:nn| inside one native % |\MathMLintent|, preceded by the inner ghost \myhlc|\@@_inner_sep:|. % % With \mykey{silent}|=true|, the intent is `|_-|'. With \mykey{read-arg} % given, the intent is |_|\mymeta{read-arg}|:nofix|. Otherwise the % spoken content is the name of the measure followed by the index, as in % |_cuartil-1:nofix|. % \begin{macrocode} \cs_new_protected:Nn \@@_stat_pos_mathml:nnnn { \@@_invisible_sep: \bool_if:cTF { l_@@_ #1 _silent_bool } { \MathMLintent { _- } { \@@_inner_sep: \@@_stat_pos_typeset:nn {#2} {#4} } } { \tl_if_empty:cTF { l_@@_ #1 _read_arg_tl } { \MathMLintent { _ #3 - #4 :nofix } { \@@_inner_sep: \@@_stat_pos_typeset:nn {#2} {#4} } } { \MathMLintent { _ \tl_use:c { l_@@_ #1 _read_arg_tl } :nofix } { \@@_inner_sep: \@@_stat_pos_typeset:nn {#2} {#4} } } } } \cs_generate_variant:Nn \@@_stat_pos_mathml:nnnn { nnnV } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_stat_pos_exec:nnnnn} % The function \myhlc|\@@_stat_pos_exec:nnnnn| receives the short name of the % command in `|#1|', the symbol in `|#2|', the name of the measure in % `|#3|', the maximum of the index in `|#4|' and the index itself in % `|#5|'. It validates the index with \myhlc|\@@_stat_pos_check:nnn| and, if % it is accepted, stores its value in \myhlc|\l_@@_stat_pos_value_int|, so % |025| is printed and read as |25|, and prints it through % \myhlc|\@@_stat_pos_mathml:nnnn|. % \begin{macrocode} \cs_new_protected:Nn \@@_stat_pos_exec:nnnnn { \@@_stat_pos_check:nnn {#1} {#4} {#5} \bool_if:NF \l_@@_stat_pos_error_bool { \int_set:NV \l_@@_stat_pos_value_int \l_@@_luaset_only_part_int_str \@@_stat_pos_mathml:nnnV {#1} {#2} {#3} \l_@@_stat_pos_value_int } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spcuartil, \spdecil, \spPerc} % The public commands \ics*{spcuartil}, \ics*{spdecil} and \ics*{spPerc} enforce % \emph{math mode}, open a local group, set the \mymeta{keys} and % delegate execution to \myhlc|\@@_stat_pos_exec:nnnnn| with the symbols % |Q|, |D| and |P|, the measures |cuartil|, |decil| and |percentil| and % the maxima |3|, |9| and |99|. % \begin{macrocode} \NewDocumentCommand \spcuartil { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \keys_set:nn { spintent / spcuartil } {#1} \@@_stat_pos_exec:nnnnn { spcuartil } { Q } { cuartil } { 3 } {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spcuartil } } } \NewDocumentCommand \spdecil { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \keys_set:nn { spintent / spdecil } {#1} \@@_stat_pos_exec:nnnnn { spdecil } { D } { decil } { 9 } {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spdecil } } } \NewDocumentCommand \spPerc { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \keys_set:nn { spintent / spPerc } {#1} \@@_stat_pos_exec:nnnnn { spPerc } { P } { percentil } { 99 } {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spPerc } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spFrec}}\label{cmd:spFrec} % % The user command |\spFrec| typesets the usual symbols of the frequencies % of a table of data: |f| for the absolute frequency, |F| for the accumulated absolute, % |f_{r}| for the relative, |F_{r}| for the accumulated relative, \myhlc|f_{r}\%| for the % percentage, and \myhlc|F_{r}\%| for the accumulated percentage. The symbol is % the \mymarg{argument}, written exactly as it is printed, and it is read % with the name of the frequency, for example, \emph{«frecuencia absoluta % acumulada»}. % % \subsubsection{Definition of variables for \cs[no-index]{spFrec}} % % \begin{variable}[int] % { % \l_@@_spFrec_sub_index_str, % \l_@@_spFrec_read_arg_tl, % } % The string variable \myhlc|\l_@@_spFrec_sub_index_str| and the token % list variable \myhlc|\l_@@_spFrec_read_arg_tl| hold the values of the % keys \mykey[spFrec]{sub-index} and \mykey[spFrec]{read-arg}. % \begin{macrocode} \str_new:N \l_@@_spFrec_sub_index_str \tl_new:N \l_@@_spFrec_read_arg_tl % \end{macrocode} % \end{variable} % % \begin{variable}[int]{\l_@@_spFrec_symbol_str, \l_@@_spFrec_concept_tl} % The string variable \myhlc|\l_@@_spFrec_symbol_str| holds the % \mymarg{argument} without spaces, and the token list variable % \myhlc|\l_@@_spFrec_concept_tl| the name of the frequency, empty while % the symbol is not in the table. % \begin{macrocode} \str_new:N \l_@@_spFrec_symbol_str \tl_new:N \l_@@_spFrec_concept_tl % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spFrec}} % % \begin{macro}[int]{spFrec-invalid-symbol, spFrec-invalid-sub-index} % The \enquote{error} |spFrec-invalid-symbol| is raised when the symbol is % not in the table and the key \mykey[spFrec]{read-arg} was not given. % The \enquote{error} |spFrec-invalid-sub-index| is raised when the key % \mykey[spFrec]{sub-index} is given with a symbol other than |f| or |F|. % \begin{macrocode} \msg_new:nnnn { spintent } { spFrec-invalid-symbol } { Invalid~symbol~'#1'~for~'\token_to_str:N \spFrec'~\msg_line_context:. } { Use~'f',~'F',~'f_{r}',~'F_{r}',~'f_{r}\iow_char:N \%'~or~'F_{r}\iow_char:N \%',~or~ give~the~key~'read-arg'. } \msg_new:nnnn { spintent } { spFrec-invalid-sub-index } { Invalid~key~'sub-index'~for~the~symbol~'#1'~\msg_line_context:. } { Only~the~symbols~'f'~and~'F'~accept~the~key~'sub-index'. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spFrec}} % % \begin{macro}[int]{silent, sub-index, read-sub, read-arg, unknown} % We define the \mymeta{keys} \mykey[spFrec]{silent}, % \mykey[spFrec]{sub-index}, \mykey[spFrec]{read-sub} and % \mykey[spFrec]{read-arg}, the third one starting in |true|. % \begin{macrocode} \keys_define:nn { spintent / spFrec } { silent .bool_set:N = \l_@@_spFrec_silent_bool, silent .initial:n = false, silent .value_required:n = true, sub-index .code:n = { \str_set:Nn \l_@@_spFrec_sub_index_str {#1} }, sub-index .initial:n = , sub-index .value_required:n = true, read-sub .bool_set:N = \l_@@_spFrec_read_sub_bool, read-sub .initial:n = true, read-sub .value_required:n = true, read-arg .code:n = { \@@_sanitize_affix:Nn \l_@@_spFrec_read_arg_tl {#1} }, read-arg .initial:n = , read-arg .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spFrec } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spFrec}} % % \begin{macro}[int]{\@@_spFrec_exec:n} % The function \myhlc|\@@_spFrec_exec:n| receives the symbol in `|#1|'. % It removes the spaces and looks the result up in the table, setting % \myhlc|\l_@@_spFrec_concept_tl| to |frecuencia-absoluta|, % |frecuencia-absoluta-acumulada|, |frecuencia-relativa|, % |frecuencia-relativa-acumulada|, |frecuencia-porcentual| or % |frecuencia-porcentual-acumulada|. % % If the symbol is not in the table, it is rejected with % |spFrec-invalid-symbol|, unless the key \mykey[spFrec]{read-arg} was % given: then the symbol is printed as it was written and read as % \mykey{read-arg} says. A \mykey{sub-index} is accepted only with the % symbols |f| and |F|; with any other symbol, even with % \mykey{read-arg}, it is rejected with |spFrec-invalid-sub-index|. The % \mymarg{argument} is rejected whatever the value of the key % \mykey{silent}. When it is accepted, the function delegates the % printing to \myhlc|\@@_stat_mathml:nnnnnn|. % \begin{macrocode} \cs_new_protected:Nn \@@_spFrec_exec:n { \str_set:Ne \l_@@_spFrec_symbol_str { \tl_to_str:n {#1} } \str_remove_all:Nn \l_@@_spFrec_symbol_str { ~ } \tl_clear:N \l_@@_spFrec_concept_tl \str_case:Vn \l_@@_spFrec_symbol_str { { f } { \tl_set:Nn \l_@@_spFrec_concept_tl { frecuencia-absoluta } } { F } { \tl_set:Nn \l_@@_spFrec_concept_tl { frecuencia-absoluta-acumulada } } { f_{r} } { \tl_set:Nn \l_@@_spFrec_concept_tl { frecuencia-relativa } } { F_{r} } { \tl_set:Nn \l_@@_spFrec_concept_tl { frecuencia-relativa-acumulada } } { f_{r}\% } { \tl_set:Nn \l_@@_spFrec_concept_tl { frecuencia-porcentual } } { F_{r}\% } { \tl_set:Nn \l_@@_spFrec_concept_tl { frecuencia-porcentual-acumulada } } } \bool_lazy_and:nnTF { \tl_if_empty_p:N \l_@@_spFrec_concept_tl } { \tl_if_empty_p:N \l_@@_spFrec_read_arg_tl } { \msg_error:nnn { spintent } { spFrec-invalid-symbol } {#1} } { \bool_lazy_and:nnTF { \bool_not_p:n { \str_if_empty_p:N \l_@@_spFrec_sub_index_str } } { \bool_not_p:n { \bool_lazy_or_p:nn { \str_if_eq_p:Vn \l_@@_spFrec_symbol_str { f } } { \str_if_eq_p:Vn \l_@@_spFrec_symbol_str { F } } } } { \msg_error:nnn { spintent } { spFrec-invalid-sub-index } {#1} } { \@@_stat_mathml:nnnnnn { spFrec } {#1} { } { \l_@@_spFrec_concept_tl } { } { } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spFrec} % The public command \ics*{spFrec} enforces \emph{math mode}, opens a local % group, sets the \mymeta{keys} and delegates execution to % \myhlc|\@@_spFrec_exec:n|. % \begin{macrocode} \NewDocumentCommand \spFrec { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \keys_set:nn { spintent / spFrec } {#1} \@@_spFrec_exec:n {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spFrec } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spcase}}\label{cmd:spcase} % % The user command |\spcase| typesets the symbols of the cases of a % probability: |C_{f}| for the favorable cases, |C_{p}| for the possible ones, and |C_{t}| for the total. % The letter is the \mymarg{argument}, and the symbol is read with the name % of the cases, for example, \emph{«casos favorables»}. % % \subsubsection{Definition of variables for \cs[no-index]{spcase}} % % \begin{variable}[int] % { % \l_@@_spcase_sub_index_str, % \l_@@_spcase_read_arg_tl, % } % The token list variable \myhlc|\l_@@_spcase_read_arg_tl| holds the value % of the key \mykey[spcase]{read-arg}. This command has no % \mykey{sub-index} key, so the string variable % \myhlc|\l_@@_spcase_sub_index_str| exists only because % \myhlc|\@@_stat_mathml:nnnnnn| reads it, and always keeps its initial % value. % \begin{macrocode} \str_new:N \l_@@_spcase_sub_index_str \tl_new:N \l_@@_spcase_read_arg_tl % \end{macrocode} % \end{variable} % % \begin{variable}[int]{\l_@@_spcase_letter_str, \l_@@_spcase_concept_tl} % The string variable \myhlc|\l_@@_spcase_letter_str| holds the % \mymarg{argument} without spaces, and the token list variable % \myhlc|\l_@@_spcase_concept_tl| the name of the cases, empty while the % letter is not in the table. % \begin{macrocode} \str_new:N \l_@@_spcase_letter_str \tl_new:N \l_@@_spcase_concept_tl % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spcase}} % % \begin{macro}[int]{spcase-invalid-letter} % The \enquote{error} |spcase-invalid-letter| is raised when the % \mymarg{argument} is not one of the letters |f|, |p| or |t|. % \begin{macrocode} \msg_new:nnnn { spintent } { spcase-invalid-letter } { Invalid~letter~'#1'~for~'\token_to_str:N \spcase'~\msg_line_context:. } { The~letter~must~be~'f',~'p'~or~'t'. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spcase}} % % \begin{macro}[int]{silent, read-arg, unknown} % We define the \mymeta{keys} \mykey[spcase]{silent} and % \mykey[spcase]{read-arg}. % \begin{macrocode} \keys_define:nn { spintent / spcase } { silent .bool_set:N = \l_@@_spcase_silent_bool, silent .initial:n = false, silent .value_required:n = true, read-arg .code:n = { \@@_sanitize_affix:Nn \l_@@_spcase_read_arg_tl {#1} }, read-arg .initial:n = , read-arg .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spcase } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spcase}} % % \begin{macro}[int]{\@@_spcase_exec:n} % The function \myhlc|\@@_spcase_exec:n| receives the letter in `|#1|'. % It removes the spaces and looks the result up in the table, setting % \myhlc|\l_@@_spcase_concept_tl| to |casos-favorables|, |casos-posibles| or % |casos-totales|. If the letter is not in the table, it is rejected with % |spcase-invalid-letter| whatever the value of the keys % \mykey[spcase]{silent} and \mykey[spcase]{read-arg}. Otherwise the % function delegates the printing of the symbol, with the letter as its % subscript, to \myhlc|\@@_stat_mathml:nnnnnn|. % \begin{macrocode} \cs_new_protected:Nn \@@_spcase_exec:n { \str_set:Ne \l_@@_spcase_letter_str { \tl_to_str:n {#1} } \str_remove_all:Nn \l_@@_spcase_letter_str { ~ } \tl_clear:N \l_@@_spcase_concept_tl \str_case:Vn \l_@@_spcase_letter_str { { f } { \tl_set:Nn \l_@@_spcase_concept_tl { casos-favorables } } { p } { \tl_set:Nn \l_@@_spcase_concept_tl { casos-posibles } } { t } { \tl_set:Nn \l_@@_spcase_concept_tl { casos-totales } } } \tl_if_empty:NTF \l_@@_spcase_concept_tl { \msg_error:nnn { spintent } { spcase-invalid-letter } {#1} } { \@@_stat_mathml:nnnnnn { spcase } { C \c_math_subscript_token { \str_use:N \l_@@_spcase_letter_str } } { } { \l_@@_spcase_concept_tl } { } { } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spcase} % The public command \ics*{spcase} enforces \emph{math mode}, opens a local % group, sets the \mymeta{keys} and delegates execution to % \myhlc|\@@_spcase_exec:n|. % \begin{macrocode} \NewDocumentCommand \spcase { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \keys_set:nn { spintent / spcase } {#1} \@@_spcase_exec:n {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spcase } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spOmega}}\label{cmd:spOmega} % % The user command |\spOmega| typesets the symbol |\Omega| of the sample % space, or |\#\Omega|, which is its cardinality, with the key \mykey[spOmega]{card}. % It is read \emph{«espacio muestral»} or \emph{«cardinalidad del espacio % muestral»}. % % \subsubsection{Definition of variables for \cs[no-index]{spOmega}} % % \begin{variable}[int] % { % \l_@@_spOmega_sub_index_str, % \l_@@_spOmega_read_arg_tl, % \l_@@_spOmega_read_sub_bool, % } % The string variable \myhlc|\l_@@_spOmega_sub_index_str| and the token % list variable \myhlc|\l_@@_spOmega_read_arg_tl| hold the values of the % keys \mykey[spOmega]{sub-index} and \mykey[spOmega]{read-arg}. This % command has no \mykey{read-sub} key, so the boolean variable % \myhlc|\l_@@_spOmega_read_sub_bool| starts as |true| and the subscript % is always spoken with \emph{«sub»}. % \begin{macrocode} \str_new:N \l_@@_spOmega_sub_index_str \tl_new:N \l_@@_spOmega_read_arg_tl \bool_new:N \l_@@_spOmega_read_sub_bool \bool_set_true:N \l_@@_spOmega_read_sub_bool % \end{macrocode} % \end{variable} % % \subsubsection{Definition of keys for \cs[no-index]{spOmega}} % % \begin{macro}[int]{silent, sub-index, read-arg, card, unknown} % We define the \mymeta{keys} \mykey[spOmega]{silent}, % \mykey[spOmega]{sub-index}, \mykey[spOmega]{read-arg} and % \mykey[spOmega]{card}, the last one starting in |false|. % \begin{macrocode} \keys_define:nn { spintent / spOmega } { silent .bool_set:N = \l_@@_spOmega_silent_bool, silent .initial:n = false, silent .value_required:n = true, sub-index .code:n = { \str_set:Nn \l_@@_spOmega_sub_index_str {#1} }, sub-index .initial:n = , sub-index .value_required:n = true, read-arg .code:n = { \@@_sanitize_affix:Nn \l_@@_spOmega_read_arg_tl {#1} }, read-arg .initial:n = , read-arg .value_required:n = true, card .bool_set:N = \l_@@_spOmega_card_bool, card .initial:n = false, card .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spOmega } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spOmega}} % % \begin{macro}{\spOmega} % The public command \ics*{spOmega} enforces \emph{math mode}, opens a local % group, sets the \mymeta{keys} and delegates execution to % \myhlc|\@@_stat_mathml:nnnnnn|, with the symbol |\Omega| and the concept % |espacio-muestral|, or the symbol |\#\Omega| and the concept % |cardinalidad-del-espacio-muestral| when \mykey[spOmega]{card} is |true|. % \begin{macrocode} \NewDocumentCommand \spOmega { O{} } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \keys_set:nn { spintent / spOmega } {#1} \bool_if:NTF \l_@@_spOmega_card_bool { \@@_stat_mathml:nnnnnn { spOmega } { \# \Omega } { } { cardinalidad-del-espacio-muestral } { } { } } { \@@_stat_mathml:nnnnnn { spOmega } { \Omega } { } { espacio-muestral } { } { } } \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spOmega } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spPval}}\label{cmd:spPval} % % The user command |\spPval| typesets the value of a probability as a fraction % |a|/|b|, using |\dfrac| or |\frac| according to \mykey[spPval]{use-dfrac}, read % natively as \emph{«a de b»}, without parsing in Lua. % % \subsubsection{Definition of keys for \cs[no-index]{spPval}} % % \begin{macro}[int]{silent, use-dfrac, unknown} % We define the \mymeta{keys} \mykey[spPval]{silent} and % \mykey[spPval]{use-dfrac}. % \begin{macrocode} \keys_define:nn { spintent / spPval } { silent .bool_set:N = \l_@@_spPval_silent_bool, silent .initial:n = false, silent .value_required:n = true, use-dfrac .bool_set:N = \l_@@_spPval_use_dfrac_bool, use-dfrac .initial:n = false, use-dfrac .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spPval } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spPval_exec:nnn} % The function \myhlc|\@@_spPval_exec:nnn| receives the optional % \mymeta{keys} in `|#1|', the numerator in `|#2|' and the denominator in % `|#3|'. It sets the \mymeta{keys} and calls % \myhlc|\@@_spPval_mathml:nn|. % \begin{macrocode} \cs_new_protected:Nn \@@_spPval_exec:nnn { \keys_set:nn { spintent / spPval } {#1} \@@_spPval_mathml:nn {#2} {#3} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spPval_mathml:nn} % The function \myhlc|\@@_spPval_mathml:nn| receives the numerator in % `|#1|' and the denominator in `|#2|'. Under \mykey{silent}, the % fraction, |\frac| or |\dfrac| per \mykey{use-dfrac}, is silenced as one % unit. Otherwise numerator and denominator are each tagged through a % native |\MathMLarg|, as |num| and |denom|, inside the fraction, and the outer % |\MathMLintent| reads \myhlc|_de:infix($num,$denom)|. % \begin{macrocode} \cs_new_protected:Nn \@@_spPval_mathml:nn { \@@_invisible_sep: \bool_if:NTF \l_@@_spPval_silent_bool { \MathMLintent { _- } { \@@_inner_sep: \bool_if:NTF \l_@@_spPval_use_dfrac_bool { \dfrac {#1} {#2} } { \frac {#1} {#2} } } } { \MathMLintent { _de:infix($num,$denom) } { \@@_inner_sep: \bool_if:NTF \l_@@_spPval_use_dfrac_bool { \dfrac { \MathMLarg { num } {#1} } { \MathMLarg { denom } {#2} } } { \frac { \MathMLarg { num } {#1} } { \MathMLarg { denom } {#2} } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spPval} % The public command \ics*{spPval} enforces \emph{math mode}, opens a local % group and delegates execution to \myhlc|\@@_spPval_exec:nnn|. % \begin{macrocode} \NewDocumentCommand \spPval { O{} m m } { \mode_if_math:TF { \group_begin: \@@_spPval_exec:nnn {#1} {#2} {#3} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spPval } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spProb}}\label{cmd:spProb} % % The user command |\spProb| typesets the probability of an event as % |P(|\mymarg{event}|)|. The \mymarg{argument} is written as it is printed: % a single event, or two events joined by exactly one of |\cap|, |\cup|, % |\mid| or the character `\texttt{\textbar}', which are read \emph{«probabilidad de % A»}, \emph{«probabilidad de A y B»}, \emph{«probabilidad de A o B»} and % \emph{«probabilidad de A dado B»}. % % \subsubsection{Definition of variables for \cs[no-index]{spProb}} % % \begin{variable}[int] % { % \l_@@_spProb_luaset_error_str, % \l_@@_spProb_luaset_op_str, % \l_@@_spProb_luaset_a_str, % \l_@@_spProb_luaset_b_str, % } % Variables set by the function \myhlc|\@@_luafun_spProb_parse_and_set:n|. % The string variable \myhlc|\l_@@_spProb_luaset_error_str| is |true| when % the \mymarg{argument} is invalid. The string variable % \myhlc|\l_@@_spProb_luaset_op_str| is |none|, |cap|, |cup| or |mid|, and % \myhlc|\l_@@_spProb_luaset_a_str| and \myhlc|\l_@@_spProb_luaset_b_str| % hold the event before and after the operator; the second one is empty % when there is no operator. % \begin{macrocode} \str_new:N \l_@@_spProb_luaset_error_str \str_new:N \l_@@_spProb_luaset_op_str \str_new:N \l_@@_spProb_luaset_a_str \str_new:N \l_@@_spProb_luaset_b_str % \end{macrocode} % \end{variable} % % \begin{variable}[int]{\l_@@_spProb_read_arg_tl} % The token list variable \myhlc|\l_@@_spProb_read_arg_tl| stores the key % \mykey[spProb]{read-arg}. % \begin{macrocode} \tl_new:N \l_@@_spProb_read_arg_tl % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spProb}} % % \begin{macro}[int]{spProb-invalid-arg} % The \enquote{error} |spProb-invalid-arg| is raised when the % \mymarg{argument} is empty, has unbalanced braces or parentheses, has an % empty event, or has more than one operator. % \begin{macrocode} \msg_new:nnnn { spintent } { spProb-invalid-arg } { Invalid~argument~'#1'~for~'\token_to_str:N \spProb'~\msg_line_context:. } { Use~one~event,~or~two~joined~by~one~of~'\token_to_str:N \cap',~ '\token_to_str:N \cup',~'\token_to_str:N \mid'~or~'|'~(outside~braces~ and~parentheses). } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spProb}} % % \begin{macro}[int]{silent, read-arg, unknown} % We define the \mymeta{keys} \mykey[spProb]{silent} and % \mykey[spProb]{read-arg}. % \begin{macrocode} \keys_define:nn { spintent / spProb } { silent .bool_set:N = \l_@@_spProb_silent_bool, silent .initial:n = false, silent .value_required:n = true, read-arg .code:n = { \@@_sanitize_affix:Nn \l_@@_spProb_read_arg_tl {#1} }, read-arg .initial:n = , read-arg .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spProb } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spProb}} % % \begin{variable}[int]{\l_@@_spProb_nested_bool} % The boolean variable \myhlc|\l_@@_spProb_nested_bool| makes the % redefinition of |\cap|, |\cup| and |\mid| happen only once when the % groups are nested, for example, a |\spcomp| inside |\spProb|, so that the % saved meanings are the original ones. % \begin{macrocode} \bool_new:N \l_@@_spProb_nested_bool % \end{macrocode} % \end{variable} % % \begin{macro}[int]{\@@_spProb_cap:, \@@_spProb_cup:, \@@_spProb_mid:, \@@_spProb_nested_ops:, \@@_spProb_event:N} % The functions \myhlc|\@@_spProb_cap:|, \myhlc|\@@_spProb_cup:| and % \myhlc|\@@_spProb_mid:| wrap the saved meaning of |\cap|, |\cup| and % |\mid|, each in its own |\MathMLintent|, which is |_y-|, |_o| and |_dado| respectively, while keeping % its math class and therefore its spacing. % The function \myhlc|\@@_spProb_nested_ops:| saves the three meanings and % redefines, locally, |\cap|, |\cup| and |\mid| as these functions. The function % \myhlc|\@@_spProb_event:N| typesets the event stored in its argument % inside a group with these definitions, so the operators nested in % braces or parentheses are also read; the operators at the top level never % go through it, they are printed by \myhlc|\@@_spProb_mathml:|. % \begin{macrocode} \cs_new_protected:Nn \@@_spProb_cap: { \mathbin { \MathMLintent { _y- } { \@@_spProb_cap_orig: } } } \cs_new_protected:Nn \@@_spProb_cup: { \mathbin { \MathMLintent { _o } { \@@_spProb_cup_orig: } } } \cs_new_protected:Nn \@@_spProb_mid: { \mathrel { \MathMLintent { _dado } { \@@_spProb_mid_orig: } } } \cs_new_protected:Nn \@@_spProb_nested_ops: { \bool_if:NF \l_@@_spProb_nested_bool { \bool_set_true:N \l_@@_spProb_nested_bool \cs_set_eq:NN \@@_spProb_cap_orig: \cap \cs_set_eq:NN \@@_spProb_cup_orig: \cup \cs_set_eq:NN \@@_spProb_mid_orig: \mid \cs_set_eq:NN \cap \@@_spProb_cap: \cs_set_eq:NN \cup \@@_spProb_cup: \cs_set_eq:NN \mid \@@_spProb_mid: } } \cs_new_protected:Nn \@@_spProb_event:N { \group_begin: \@@_spProb_nested_ops: \str_use:N #1 \group_end: } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spProb_content:} % The function \myhlc|\@@_spProb_content:| typesets the event, or the two % events and the operator, without any annotation. % \begin{macrocode} \cs_new_protected:Nn \@@_spProb_content: { \str_use:N \l_@@_spProb_luaset_a_str \str_case:Vn \l_@@_spProb_luaset_op_str { { cap } { \cap \str_use:N \l_@@_spProb_luaset_b_str } { cup } { \cup \str_use:N \l_@@_spProb_luaset_b_str } { mid } { \mid \str_use:N \l_@@_spProb_luaset_b_str } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spProb_mathml:} % The function \myhlc|\@@_spProb_mathml:| prints |P(|\mymeta{content}|)| % inside one native |\MathMLintent|, preceded by the outer ghost % \myhlc|\@@_invisible_sep:| and by the inner ghost % \myhlc|\@@_inner_sep:|. % % With \mykey{silent}|=true|, the intent is `|_-|'. With \mykey{read-arg} % given, the intent is |_|\mymeta{read-arg}|:nofix|. Otherwise the % intent is |_probabilidad-de:prefix($a)| with the single event tagged as % |a|; with an operator, the intent is |_probabilidad-de:prefix($p)|, where % |p| is the group of the two events, which carries its own intent % |_y-:infix($a,$b)|, |_o:infix($a,$b)| or |_dado:infix($a,$b)| according to % the operator, with the two events tagged as |a| and |b|. % \begin{macrocode} \cs_new_protected:Nn \@@_spProb_mathml: { \@@_invisible_sep: \bool_if:NTF \l_@@_spProb_silent_bool { \MathMLintent { _- } { \@@_inner_sep: P \lparen \@@_spProb_content: \rparen } } { \tl_if_empty:NTF \l_@@_spProb_read_arg_tl { \str_if_eq:VnTF \l_@@_spProb_luaset_op_str { none } { \MathMLintent { _probabilidad-de:prefix($a) } { \@@_inner_sep: P \lparen \MathMLarg { a } { \@@_inner_sep: \@@_spProb_event:N \l_@@_spProb_luaset_a_str } \rparen } } { \MathMLintent { _probabilidad-de:prefix($p) } { \@@_inner_sep: P \lparen \MathMLarg { p } { \MathMLintent { _ \str_case:Vn \l_@@_spProb_luaset_op_str { { cap } { y- } { cup } { o } { mid } { dado } } :infix($a,$b) } { \@@_inner_sep: \MathMLarg { a } { \@@_inner_sep: \@@_spProb_event:N \l_@@_spProb_luaset_a_str } \str_case:Vn \l_@@_spProb_luaset_op_str { { cap } { \cap } { cup } { \cup } { mid } { \mid } } \MathMLarg { b } { \@@_inner_sep: \@@_spProb_event:N \l_@@_spProb_luaset_b_str } } } \rparen } } } { \MathMLintent { _ \tl_use:N \l_@@_spProb_read_arg_tl :nofix } { \@@_inner_sep: P \lparen \@@_spProb_content: \rparen } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spProb_exec:nn} % The function \myhlc|\@@_spProb_exec:nn| sets the \mymeta{keys} in % `|#1|' and calls \myhlc|\@@_luafun_spProb_parse_and_set:n| with the % \mymarg{argument} in `|#2|'. If the Lua module reports an error, it is % rejected with |spProb-invalid-arg| whatever the value of the key % \mykey{silent}, and nothing is printed; otherwise it delegates the % printing to \myhlc|\@@_spProb_mathml:|. % \begin{macrocode} \cs_new_protected:Nn \@@_spProb_exec:nn { \keys_set:nn { spintent / spProb } {#1} \@@_luafun_spProb_parse_and_set:n { \exp_not:n {#2} } \str_if_eq:VnTF \l_@@_spProb_luaset_error_str { true } { \msg_error:nnn { spintent } { spProb-invalid-arg } {#2} } { \@@_spProb_mathml: } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spProb} % The public command \ics*{spProb} enforces \emph{math mode}, opens a local % group and delegates execution to \myhlc|\@@_spProb_exec:nn|. % \begin{macrocode} \NewDocumentCommand \spProb { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_spProb_exec:nn {#1} {#2} \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spProb } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spcomp}}\label{cmd:spcomp} % % The user command |\spcomp| typesets the complement of an event, as % \mymeta{event}|^{c}| or, with the key \mykey[spcomp]{bar}, as % |\overline{|\mymeta{event}|}|, and reads it \emph{«complemento de»} the % event. % % \subsubsection{Definition of variables for \cs[no-index]{spcomp}} % % \begin{variable}[int] % { % \l_@@_spcomp_read_arg_tl, % \l_@@_spcomp_wrap_parens_str, % \l_@@_spcomp_do_paren_bool, % \l_@@_spcomp_compound_bool, % \l_@@_spcomp_luaset_compound_str, % } % The token list variable \myhlc|\l_@@_spcomp_read_arg_tl| stores the key % \mykey[spcomp]{read-arg}, and the string variable % \myhlc|\l_@@_spcomp_wrap_parens_str| the key % \mykey[spcomp]{wrap-parens}. The boolean variable % \myhlc|\l_@@_spcomp_do_paren_bool| tells whether the event is printed % in parentheses once the key and the event have been examined. The % boolean variable \myhlc|\l_@@_spcomp_compound_bool| is true when the % event contains |\cap|, |\cup|, |\setminus| or |\mid|, whatever the % keys are; Lua decides it with the function % \myhlc|\@@_luafun_spcomp_compound:n| and leaves the answer in the % string variable \myhlc|\l_@@_spcomp_luaset_compound_str|. % \begin{macrocode} \tl_new:N \l_@@_spcomp_read_arg_tl \str_new:N \l_@@_spcomp_wrap_parens_str \bool_new:N \l_@@_spcomp_do_paren_bool \bool_new:N \l_@@_spcomp_compound_bool \str_new:N \l_@@_spcomp_luaset_compound_str % \end{macrocode} % \end{variable} % % \subsubsection{Definition of keys for \cs[no-index]{spcomp}} % % \begin{macro}[int]{silent, bar, wrap-parens, read-arg, unknown} % We define the \mymeta{keys} \mykey[spcomp]{silent}, \mykey[spcomp]{bar}, % \mykey[spcomp]{wrap-parens} and \mykey[spcomp]{read-arg}. % \begin{macrocode} \keys_define:nn { spintent / spcomp } { silent .bool_set:N = \l_@@_spcomp_silent_bool, silent .initial:n = false, silent .value_required:n = true, bar .bool_set:N = \l_@@_spcomp_bar_bool, bar .initial:n = false, bar .value_required:n = true, wrap-parens .choices:nn = { auto , true , false } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spcomp_wrap_parens_str { auto } } {2} { \str_set:Nn \l_@@_spcomp_wrap_parens_str { true } } {3} { \str_set:Nn \l_@@_spcomp_wrap_parens_str { false } } } }, wrap-parens .initial:n = auto, wrap-parens .value_required:n = true, wrap-parens / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { wrap-parens } { auto,~true,~false } { \exp_not:n {#1} }, read-arg .code:n = { \@@_sanitize_affix:Nn \l_@@_spcomp_read_arg_tl {#1} }, read-arg .initial:n = , read-arg .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spcomp } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spcomp}} % % \begin{macro}[int]{\@@_spcomp_set_paren:n, \@@_spcomp_event:n} % The function \myhlc|\@@_spcomp_set_paren:n| sets % \myhlc|\l_@@_spcomp_compound_bool| and % \myhlc|\l_@@_spcomp_do_paren_bool|. The first one is true when Lua % reports that the event contains |\cap|, |\cup|, |\setminus| or |\mid|. The second one % follows the key \mykey[spcomp]{wrap-parens}: with |auto| it is true when the % event is compound, and it is always false when the key % \mykey[spcomp]{bar} is set, since the overline already covers the event. The % function % \myhlc|\@@_spcomp_event:n| typesets the event in a group where the % operators nested in it are read as in \myhlc|\@@_spProb_event:N|, and % in parentheses when \myhlc|\l_@@_spcomp_do_paren_bool| is true. The % parentheses belong to the event, so that they are inside the % |\MathMLarg| and are read. % \begin{macrocode} \cs_new_protected:Nn \@@_spcomp_set_paren:n { \bool_set_false:N \l_@@_spcomp_do_paren_bool \bool_set_false:N \l_@@_spcomp_compound_bool \@@_luafun_spcomp_compound:n { \exp_not:n {#1} } \str_if_eq:VnT \l_@@_spcomp_luaset_compound_str { true } { \bool_set_true:N \l_@@_spcomp_compound_bool } \bool_if:NF \l_@@_spcomp_bar_bool { \str_case:Vn \l_@@_spcomp_wrap_parens_str { { true } { \bool_set_true:N \l_@@_spcomp_do_paren_bool } { auto } { \bool_if:NT \l_@@_spcomp_compound_bool { \bool_set_true:N \l_@@_spcomp_do_paren_bool } } } } } \cs_new_protected:Nn \@@_spcomp_event:n { \group_begin: \@@_spProb_nested_ops: \bool_if:NTF \l_@@_spcomp_do_paren_bool { \lparen #1 \rparen } {#1} \group_end: } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcomp_typeset:n} % The function \myhlc|\@@_spcomp_typeset:n| typesets its argument as the % complement of the event, with the exponent |c| or with an overline % according to the key \mykey[spcomp]{bar}. % \begin{macrocode} \cs_new_protected:Nn \@@_spcomp_typeset:n { \bool_if:NTF \l_@@_spcomp_bar_bool { \overline {#1} } { {#1} ^ { c } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcomp_exec:nn} % The function \myhlc|\@@_spcomp_exec:nn| sets the \mymeta{keys} in % `|#1|', calls \myhlc|\@@_spcomp_set_paren:n| with the event in `|#2|' % and then \myhlc|\@@_spcomp_mathml:n|. % \begin{macrocode} \cs_new_protected:Nn \@@_spcomp_exec:nn { \keys_set:nn { spintent / spcomp } {#1} \@@_spcomp_set_paren:n {#2} \@@_spcomp_mathml:n {#2} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcomp_mathml:n} % The function \myhlc|\@@_spcomp_mathml:n| prints the complement of the % event `|#1|' inside one native |\MathMLintent|, preceded by the outer % ghost \myhlc|\@@_invisible_sep:| and by the inner ghost % \myhlc|\@@_inner_sep:|. With \mykey{silent}% % |=true| the intent is `|_-|'; with \mykey{read-arg} given it is % |_|\mymeta{read-arg}|:nofix|; otherwise it is % |_complemento-de:prefix($a)| with the event tagged as |a|; when the % key \mykey[spcomp]{bar} is set and the event is compound, the intent is % |_complemento-de-la-expresión:prefix($a)|, because the overline is not % read and the reading would not tell what the complement covers. % \begin{macrocode} \cs_new_protected:Nn \@@_spcomp_mathml:n { \@@_invisible_sep: \bool_if:NTF \l_@@_spcomp_silent_bool { \MathMLintent { _- } { \@@_inner_sep: \@@_spcomp_typeset:n { \@@_spcomp_event:n {#1} } } } { \tl_if_empty:NTF \l_@@_spcomp_read_arg_tl { \MathMLintent { \bool_lazy_and:nnTF { \bool_if_p:N \l_@@_spcomp_bar_bool } { \bool_if_p:N \l_@@_spcomp_compound_bool } { _complemento-de-la-expresión:prefix($a) } { _complemento-de:prefix($a) } } { \@@_inner_sep: \@@_spcomp_typeset:n { \MathMLarg { a } { \@@_inner_sep: \@@_spcomp_event:n {#1} } } } } { \MathMLintent { _ \tl_use:N \l_@@_spcomp_read_arg_tl :nofix } { \@@_inner_sep: \@@_spcomp_typeset:n { \@@_spcomp_event:n {#1} } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{spcomp-empty-arg} % The \enquote{error} |spcomp-empty-arg| is raised when the argument of % |\spcomp| is missing or \emph{blank}. % \begin{macrocode} \msg_new:nnnn { spintent } { spcomp-empty-arg } { Empty~argument~for~'\token_to_str:N \spcomp'~\msg_line_context:. } { The~event~is~required. } % \end{macrocode} % \end{macro} % % \begin{macro}{\spcomp} % The public command \ics*{spcomp} enforces \emph{math mode}, opens a local % group and delegates execution to \myhlc|\@@_spcomp_exec:nn|. % \begin{macrocode} \NewDocumentCommand \spcomp { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \tl_if_blank:nTF {#2} { \msg_error:nn { spintent } { spcomp-empty-arg } } { \@@_spcomp_exec:nn {#1} {#2} } \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spcomp } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spcomb}}\label{cmd:spcomb} % % The user command |\spcomb| typesets the counting symbols of % combinatorics: combinations, variations, arrangements and permutations, % with or without repetition, and circular permutations. The keys % \mykey[spcomb]{kind}, \mykey[spcomb]{repeat} and \mykey[spcomb]{circular} choose % the symbol and the reading; the key \mykey[spcomb]{print-sty} chooses between % the binomial coefficient and the letter form. % % \subsubsection{Definition of variables for \cs[no-index]{spcomb}} % % \begin{variable}[int] % { % \l_@@_spcomb_luaset_single_str, % \l_@@_spcomb_luaset_raw_n_str, % \l_@@_spcomb_luaset_raw_k_str, % \l_@@_spcomb_luaset_error_str, % \l_@@_spcomb_luaset_n_str, % \l_@@_spcomb_luaset_k_str, % \l_@@_spcomb_luaset_list_tl, % } % The function \myhlc|\@@_luafun_spcomb_split:n| splits the argument of the % command at its first comma of top level, ignoring the commas inside braces, % parentheses or brackets, and sets the string variables % \myhlc|\l_@@_spcomb_luaset_raw_n_str|, what precedes the comma, and % \myhlc|\l_@@_spcomb_luaset_raw_k_str|, what follows it; the string % variable \myhlc|\l_@@_spcomb_luaset_single_str| is |true| when the % argument has no such comma. The other variables are set by the function % \myhlc|\@@_luafun_spcomb_prepare:nnnn|. % The string variable \myhlc|\l_@@_spcomb_luaset_error_str| is |true| when % the arguments are invalid. The string variables % \myhlc|\l_@@_spcomb_luaset_n_str| and \myhlc|\l_@@_spcomb_luaset_k_str| % hold the arguments without the surrounding spaces. In the |list| mode, % \myhlc|\l_@@_spcomb_luaset_list_tl| holds the multiplicities, each one % in its own braces. % \begin{macrocode} \str_new:N \l_@@_spcomb_luaset_single_str \str_new:N \l_@@_spcomb_luaset_raw_n_str \str_new:N \l_@@_spcomb_luaset_raw_k_str \str_new:N \l_@@_spcomb_luaset_error_str \str_new:N \l_@@_spcomb_luaset_n_str \str_new:N \l_@@_spcomb_luaset_k_str \tl_new:N \l_@@_spcomb_luaset_list_tl % \end{macrocode} % \end{variable} % % \begin{variable}[int] % { % \l_@@_spcomb_read_arg_tl, % \l_@@_spcomb_kind_str, % \l_@@_spcomb_print_sty_str, % } % The token list variable \myhlc|\l_@@_spcomb_read_arg_tl| stores the key % \mykey[spcomb]{read-arg}, and the string variables store the keys % \mykey[spcomb]{kind} and \mykey[spcomb]{print-sty}. % \begin{macrocode} \tl_new:N \l_@@_spcomb_read_arg_tl \str_new:N \l_@@_spcomb_kind_str \str_new:N \l_@@_spcomb_print_sty_str % \end{macrocode} % \end{variable} % % \begin{variable}[int] % { % \l_@@_spcomb_letter_tl, % \l_@@_spcomb_concept_tl, % \l_@@_spcomb_mode_str, % \l_@@_spcomb_problem_str, % \l_@@_spcomb_use_binom_bool, % \l_@@_spcomb_list_seq, % \l_@@_spcomb_last_tl, % } % Variables computed from the keys. The token list variable % \myhlc|\l_@@_spcomb_letter_tl| holds the letters of the symbol: |C|, % |V|, |A| or |P|, followed by |R| and/or |C|; and % \myhlc|\l_@@_spcomb_concept_tl| the name of the concept that is read, % before the final |-de|. The string variable % \myhlc|\l_@@_spcomb_mode_str| is |one|, |pair| or |list| according to % the arguments the symbol takes, and \myhlc|\l_@@_spcomb_problem_str| % describes an incompatible combination of keys, it is empty when there is % none. The boolean variable \myhlc|\l_@@_spcomb_use_binom_bool| is true % when the symbol is printed with |\binom|. The sequence % \myhlc|\l_@@_spcomb_list_seq| holds the multiplicities to print, and the % token list \myhlc|\l_@@_spcomb_last_tl| the last one. % \begin{macrocode} \tl_new:N \l_@@_spcomb_letter_tl \tl_new:N \l_@@_spcomb_concept_tl \str_new:N \l_@@_spcomb_mode_str \str_new:N \l_@@_spcomb_problem_str \bool_new:N \l_@@_spcomb_use_binom_bool \seq_new:N \l_@@_spcomb_list_seq \tl_new:N \l_@@_spcomb_last_tl % \end{macrocode} % \end{variable} % % \subsubsection{Definition of error messages for \cs[no-index]{spcomb}} % % \begin{macro}[int] % { % spcomb-invalid-keys, % spcomb-missing-arg, % spcomb-extra-arg, % spcomb-invalid-arg, % } % The \enquote{error} |spcomb-invalid-keys| is raised for a combination of % keys that does not exist: \mykey{circular} without % \mykey{kind}|=permutation|, \mykey{circular} with \mykey{repeat}, or \mykey{print-sty}|=binom| % for anything but combinations without repetition. The \enquote{error} % |spcomb-missing-arg| is raised when |k| is missing and the symbol needs it. % The \enquote{error} |spcomb-extra-arg| is raised when |k| is given and the % symbol does not take it. The \enquote{error} |spcomb-invalid-arg| is raised % when the arguments are empty, when the list of multiplicities has fewer than % two elements or an empty one, or when integers are not possible, because |k| is greater % than |n| or the multiplicities add more than |n|. % \begin{macrocode} \msg_new:nnnn { spintent } { spcomb-invalid-keys } { Incompatible~keys~for~'\token_to_str:N \spcomb':~'#1'~\msg_line_context:. } { 'circular'~needs~'kind=permutation'~and~'repeat=false';~'print-sty=binom'~needs~ 'kind=combination'~and~'repeat=false'. } \msg_new:nnnn { spintent } { spcomb-missing-arg } { Missing~'k'~(after~the~comma)~for~'\token_to_str:N \spcomb'~\msg_line_context:. } { This~kind~of~symbol~needs~'k'~or~the~list~of~repetitions~after~the~ comma:~'\token_to_str:N \spcomb {n,k}'. } \msg_new:nnnn { spintent } { spcomb-extra-arg } { Unexpected~comma~and~'k'~for~'\token_to_str:N \spcomb'~\msg_line_context:. } { Circular~permutations~take~only~'n':~'\token_to_str:N \spcomb {n}'. } \msg_new:nnnn { spintent } { spcomb-invalid-arg } { Invalid~arguments~'#1'~and~'#2'~for~'\token_to_str:N \spcomb'~\msg_line_context:. } { Use~'n',~'n,k'~or~'n,a,b,...'~(at~least~two~repetitions,~none~empty);~if~ integers,~'k~<=~n'~and~'a+b+...~<=~n'. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spcomb}} % % \begin{macro}[int]{silent, kind, repeat, circular, print-sty, read-arg, unknown} % We define the \mymeta{keys} \mykey[spcomb]{silent}, % \mykey[spcomb]{kind}, \mykey[spcomb]{repeat}, \mykey[spcomb]{circular}, % \mykey[spcomb]{print-sty} and \mykey[spcomb]{read-arg}. % \begin{macrocode} \keys_define:nn { spintent / spcomb } { silent .bool_set:N = \l_@@_spcomb_silent_bool, silent .initial:n = false, silent .value_required:n = true, repeat .bool_set:N = \l_@@_spcomb_repeat_bool, repeat .initial:n = false, repeat .value_required:n = true, circular .bool_set:N = \l_@@_spcomb_circular_bool, circular .initial:n = false, circular .value_required:n = true, kind .choices:nn = { combination , variation , arrangement , permutation } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spcomb_kind_str { combination } } {2} { \str_set:Nn \l_@@_spcomb_kind_str { variation } } {3} { \str_set:Nn \l_@@_spcomb_kind_str { arrangement } } {4} { \str_set:Nn \l_@@_spcomb_kind_str { permutation } } } }, kind .initial:n = combination, kind .value_required:n = true, kind / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { kind } { combination,~variation,~arrangement,~permutation } { \exp_not:n {#1} }, print-sty .choices:nn = { auto , binom , letter } { \int_case:nn { \l_keys_choice_int } { {1} { \str_set:Nn \l_@@_spcomb_print_sty_str { auto } } {2} { \str_set:Nn \l_@@_spcomb_print_sty_str { binom } } {3} { \str_set:Nn \l_@@_spcomb_print_sty_str { letter } } } }, print-sty .initial:n = auto, print-sty .value_required:n = true, print-sty / unknown .code:n = \msg_error:nneee { spintent } { unknown-choice } { print-sty } { auto,~binom,~letter } { \exp_not:n {#1} }, read-arg .code:n = { \@@_sanitize_affix:Nn \l_@@_spcomb_read_arg_tl {#1} }, read-arg .initial:n = , read-arg .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spcomb } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spcomb}} % % \begin{macro}[int]{\@@_spcomb_setup:} % The function \myhlc|\@@_spcomb_setup:| computes the letters and the % concept from the keys, decides whether |\binom| is used, and leaves in % \myhlc|\l_@@_spcomb_problem_str| the description of an incompatible % combination of keys, if any. % \begin{macrocode} \cs_new_protected:Nn \@@_spcomb_setup: { \str_clear:N \l_@@_spcomb_problem_str \str_case:Vn \l_@@_spcomb_kind_str { { combination } { \tl_set:Nn \l_@@_spcomb_letter_tl { C } \tl_set:Nn \l_@@_spcomb_concept_tl { combinación } } { variation } { \tl_set:Nn \l_@@_spcomb_letter_tl { V } \tl_set:Nn \l_@@_spcomb_concept_tl { variación } } { arrangement } { \tl_set:Nn \l_@@_spcomb_letter_tl { A } \tl_set:Nn \l_@@_spcomb_concept_tl { arreglo } } { permutation } { \tl_set:Nn \l_@@_spcomb_letter_tl { P } \tl_set:Nn \l_@@_spcomb_concept_tl { permutación } } } \bool_if:NT \l_@@_spcomb_circular_bool { \bool_lazy_and:nnTF { \str_if_eq_p:Vn \l_@@_spcomb_kind_str { permutation } } { ! \bool_if_p:N \l_@@_spcomb_repeat_bool } { \tl_put_right:Nn \l_@@_spcomb_letter_tl { C } \tl_put_right:Nn \l_@@_spcomb_concept_tl { -circular } } { \str_set:Nn \l_@@_spcomb_problem_str { 'circular'~requires~'kind=permutation'~and~'repeat=false' } } } \bool_if:NT \l_@@_spcomb_repeat_bool { \tl_put_right:Nn \l_@@_spcomb_letter_tl { R } \tl_put_right:Nn \l_@@_spcomb_concept_tl { -con-repetición } } \bool_set_false:N \l_@@_spcomb_use_binom_bool \bool_lazy_and:nnTF { \str_if_eq_p:Vn \l_@@_spcomb_kind_str { combination } } { ! \bool_if_p:N \l_@@_spcomb_repeat_bool } { \str_if_eq:VnF \l_@@_spcomb_print_sty_str { letter } { \bool_set_true:N \l_@@_spcomb_use_binom_bool } } { \str_if_eq:VnT \l_@@_spcomb_print_sty_str { binom } { \str_if_empty:NT \l_@@_spcomb_problem_str { \str_set:Nn \l_@@_spcomb_problem_str { 'print-sty=binom'~requires~'kind=combination'~and~'repeat=false' } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int] % { % \@@_spcomb_arg:nn, % \@@_spcomb_arg_ann:nn, % \@@_spcomb_arg_plain:nn, % \@@_spcomb_sep:, % \@@_spcomb_sep_ann:, % \@@_spcomb_sep_plain:, % \@@_spcomb_last:n, % \@@_spcomb_last_ann:n, % \@@_spcomb_last_plain:n, % } % The functions \myhlc|\@@_spcomb_arg:nn|, \myhlc|\@@_spcomb_sep:| and % \myhlc|\@@_spcomb_last:n| are the places where the symbol is annotated. % They are set locally to the annotated versions, which use an |\MathMLarg| with the % inner ghost, a silent comma, and a last multiplicity that carries the % intent |_y-:prefix($l)| so that it reads \emph{\enquote{y 4}}, or to the plain % ones, the content alone and a plain comma, so that the same function % \myhlc|\@@_spcomb_symbol:| prints the symbol in both cases. % \begin{macrocode} \cs_new_protected:Nn \@@_spcomb_arg_ann:nn { \MathMLarg {#1} { \@@_inner_sep: #2 } } \cs_new_protected:Nn \@@_spcomb_arg_plain:nn { #2 } \cs_new_eq:NN \@@_spcomb_arg:nn \@@_spcomb_arg_plain:nn \cs_new_protected:Nn \@@_spcomb_sep_ann: { \MathMLintent { :silent } { , } } \cs_new_protected:Nn \@@_spcomb_sep_plain: { , } \cs_new_eq:NN \@@_spcomb_sep: \@@_spcomb_sep_plain: \cs_new_protected:Nn \@@_spcomb_last_ann:n { \MathMLintent { _y-:prefix($l) } { \@@_inner_sep: \MathMLarg { l } { \@@_inner_sep: #1 } } } \cs_new_protected:Nn \@@_spcomb_last_plain:n { #1 } \cs_new_eq:NN \@@_spcomb_last:n \@@_spcomb_last_plain:n % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcomb_list:} % The function \myhlc|\@@_spcomb_list:| typesets the multiplicities % separated by commas; the last one reads with a \emph{\enquote{y}} before it, % \emph{\enquote{3 3 y 4}}, through the intent |_y-:prefix($l)|. % \begin{macrocode} \cs_new_protected:Nn \@@_spcomb_list: { \seq_clear:N \l_@@_spcomb_list_seq \tl_map_inline:Nn \l_@@_spcomb_luaset_list_tl { \seq_put_right:Nn \l_@@_spcomb_list_seq {##1} } \seq_pop_right:NN \l_@@_spcomb_list_seq \l_@@_spcomb_last_tl \seq_use:Nn \l_@@_spcomb_list_seq { \@@_spcomb_sep: } \@@_spcomb_sep: \exp_args:NV \@@_spcomb_last:n \l_@@_spcomb_last_tl } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcomb_symbol:} % The function \myhlc|\@@_spcomb_symbol:| typesets the symbol: |\binom| % with |n| and |k|, or the letters with |k| as the subscript and |n| as the % superscript. With one argument |n| is the subscript, and with a list of % multiplicities they form the superscript. The letters are grouped so the % scripts belong to all of them. % \begin{macrocode} \cs_new_protected:Nn \@@_spcomb_symbol: { \bool_if:NTF \l_@@_spcomb_use_binom_bool { \int_compare:nNnTF { \mathstyle } < { 2 } { \Bigl } { \bigl } \lparen \genfrac { } { } { 0pt } { } { \@@_spcomb_arg:nn { n } { \str_use:N \l_@@_spcomb_luaset_n_str } } { \@@_spcomb_arg:nn { k } { \str_use:N \l_@@_spcomb_luaset_k_str } } \int_compare:nNnTF { \mathstyle } < { 2 } { \Bigr } { \bigr } \rparen } { { \tl_use:N \l_@@_spcomb_letter_tl } \str_case:Vn \l_@@_spcomb_mode_str { { pair } { \c_math_subscript_token { \@@_spcomb_arg:nn { k } { \str_use:N \l_@@_spcomb_luaset_k_str } } ^ { \@@_spcomb_arg:nn { n } { \str_use:N \l_@@_spcomb_luaset_n_str } } } { one } { \c_math_subscript_token { \@@_spcomb_arg:nn { n } { \str_use:N \l_@@_spcomb_luaset_n_str } } } { list } { \c_math_subscript_token { \@@_spcomb_arg:nn { n } { \str_use:N \l_@@_spcomb_luaset_n_str } } ^ { \@@_spcomb_arg:nn { m } { \@@_spcomb_list: } } } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcomb_mathml:} % The function \myhlc|\@@_spcomb_mathml:| prints the symbol inside one native % |\MathMLintent|, preceded by the outer ghost \myhlc|\@@_invisible_sep:| % and by the inner ghost \myhlc|\@@_inner_sep:|. % % With \mykey{silent}|=true| the intent is `|_-|'. With \mykey{read-arg} % given it is |_|\mymeta{read-arg}|:nofix|. Otherwise the intent of the % whole symbol is the concept followed by |-de|: with |one| argument it is % |_|\mymeta{concept}|-de:prefix($c)|, where |c| wraps the symbol with the % intent |_elementos:postfix($n)|; with |pair| it is % |_|\mymeta{concept}|-de:prefix($c)|, where |c| is the symbol, which % carries its own intent |_elementos-tomados-de-a:infix($n,$k)|, except for the binomial % coefficient, whose single intent is |_sobre:infix($n,$k)|; with |list| it is % |_|\mymeta{concept}|-de:prefix($c)|, where |c| is the symbol, which % carries the intent |_elementos-con-repeticiones:infix($n,$m)|, and |m| is the % group of the multiplicities, typeset by \myhlc|\@@_spcomb_list:|. % \begin{macrocode} \cs_new_protected:Nn \@@_spcomb_mathml: { \@@_invisible_sep: \bool_if:NTF \l_@@_spcomb_silent_bool { \cs_set_eq:NN \@@_spcomb_arg:nn \@@_spcomb_arg_plain:nn \cs_set_eq:NN \@@_spcomb_sep: \@@_spcomb_sep_plain: \cs_set_eq:NN \@@_spcomb_last:n \@@_spcomb_last_plain:n \MathMLintent { _- } { \@@_inner_sep: \@@_spcomb_symbol: } } { \tl_if_empty:NTF \l_@@_spcomb_read_arg_tl { \cs_set_eq:NN \@@_spcomb_arg:nn \@@_spcomb_arg_ann:nn \cs_set_eq:NN \@@_spcomb_sep: \@@_spcomb_sep_ann: \cs_set_eq:NN \@@_spcomb_last:n \@@_spcomb_last_ann:n \str_case:Vn \l_@@_spcomb_mode_str { { one } { \MathMLintent { _ \tl_use:N \l_@@_spcomb_concept_tl -de:prefix($c) } { \@@_inner_sep: \MathMLarg { c } { \MathMLintent { _elementos:postfix($n) } { \@@_inner_sep: \@@_spcomb_symbol: } } } } { pair } { \bool_if:NTF \l_@@_spcomb_use_binom_bool { \MathMLintent { _sobre:infix($n,$k) } { \@@_inner_sep: \@@_spcomb_symbol: } } { \MathMLintent { _ \tl_use:N \l_@@_spcomb_concept_tl -de:prefix($c) } { \@@_inner_sep: \MathMLarg { c } { \MathMLintent { _elementos-tomados-de-a:infix($n,$k) } { \@@_inner_sep: \@@_spcomb_symbol: } } } } } { list } { \MathMLintent { _ \tl_use:N \l_@@_spcomb_concept_tl -de:prefix($c) } { \@@_inner_sep: \MathMLarg { c } { \MathMLintent { _elementos-con-repeticiones:infix($n,$m) } { \@@_inner_sep: \@@_spcomb_symbol: } } } } } } { \cs_set_eq:NN \@@_spcomb_arg:nn \@@_spcomb_arg_plain:nn \cs_set_eq:NN \@@_spcomb_sep: \@@_spcomb_sep_plain: \cs_set_eq:NN \@@_spcomb_last:n \@@_spcomb_last_plain:n \MathMLintent { _ \tl_use:N \l_@@_spcomb_read_arg_tl :nofix } { \@@_inner_sep: \@@_spcomb_symbol: } } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcomb_exec:nn} % The function \myhlc|\@@_spcomb_exec:nn| sets the \mymeta{keys} in `|#1|', % rejects an incompatible combination of keys, asks Lua to split the % argument `|#2|' at its first comma, decides the mode from the keys and % from the variable \myhlc|\l_@@_spcomb_luaset_single_str|, and calls % \myhlc|\@@_luafun_spcomb_prepare:nnnn| with the pieces. Whatever the % value of \mykey{silent}, an invalid case is rejected with its error % message and nothing is printed; otherwise the printing is delegated to % \myhlc|\@@_spcomb_mathml:|. % \begin{macrocode} \cs_new_protected:Nn \@@_spcomb_exec:nn { \keys_set:nn { spintent / spcomb } {#1} \@@_spcomb_setup: \str_if_empty:NTF \l_@@_spcomb_problem_str { \@@_luafun_spcomb_split:n { \exp_not:n {#2} } \bool_if:NTF \l_@@_spcomb_circular_bool { \str_if_eq:VnTF \l_@@_spcomb_luaset_single_str { true } { \str_set:Nn \l_@@_spcomb_mode_str { one } } { \str_set:Nn \l_@@_spcomb_mode_str { extra } } } { \bool_lazy_and:nnTF { \str_if_eq_p:Vn \l_@@_spcomb_kind_str { permutation } } { \bool_if_p:N \l_@@_spcomb_repeat_bool } { \str_if_eq:VnTF \l_@@_spcomb_luaset_single_str { true } { \str_set:Nn \l_@@_spcomb_mode_str { missing } } { \str_set:Nn \l_@@_spcomb_mode_str { list } } } { \str_if_eq:VnTF \l_@@_spcomb_luaset_single_str { true } { \str_if_eq:VnTF \l_@@_spcomb_kind_str { permutation } { \str_set:Nn \l_@@_spcomb_mode_str { one } } { \str_set:Nn \l_@@_spcomb_mode_str { missing } } } { \str_set:Nn \l_@@_spcomb_mode_str { pair } } } } \str_case:VnF \l_@@_spcomb_mode_str { { extra } { \msg_error:nn { spintent } { spcomb-extra-arg } } { missing } { \msg_error:nn { spintent } { spcomb-missing-arg } } } { \@@_luafun_spcomb_prepare:nnnn { \l_@@_spcomb_mode_str } { \l_@@_spcomb_luaset_raw_n_str } { \l_@@_spcomb_luaset_raw_k_str } { \bool_if:NTF \l_@@_spcomb_repeat_bool { false } { true } } \str_if_eq:VnTF \l_@@_spcomb_luaset_error_str { true } { \msg_error:nnee { spintent } { spcomb-invalid-arg } { \l_@@_spcomb_luaset_raw_n_str } { \l_@@_spcomb_luaset_raw_k_str } } { \@@_spcomb_mathml: } } } { \msg_error:nne { spintent } { spcomb-invalid-keys } { \l_@@_spcomb_problem_str } } } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spcomb_begin:nn} % The function \myhlc|\@@_spcomb_begin:nn| opens a local group, activates % the scope attribute and delegates execution to % \myhlc|\@@_spcomb_exec:nn|. % \begin{macrocode} \cs_new_protected:Nn \@@_spcomb_begin:nn { \group_begin: \attribute \l_@@_scope_attr_int = 1 \@@_spcomb_exec:nn {#1} {#2} \group_end: } % \end{macrocode} % \end{macro} % % \begin{macro}{\spcomb} % The public command \ics*{spcomb} enforces \emph{math mode} and delegates % execution to \myhlc|\@@_spcomb_begin:nn|, which opens a local group. The % mandatory argument `|#2|' is |n| or |n,k|, and Lua splits it. % \begin{macrocode} \NewDocumentCommand \spcomb { O{} m } { \mode_if_math:TF { \@@_spcomb_begin:nn {#1} {#2} } { \msg_error:nnn { spintent } { need-math-mode } { \spcomb } } } % \end{macrocode} % \end{macro} % % \subsection{The command \cs{spfact}}\label{cmd:spfact} % % The user command |\spfact| typesets the factorial |n!| and reads it % \emph{«n factorial»}. It has the keys \mykey[spfact]{silent} and % \mykey[spfact]{read-arg}. % % \subsubsection{Definition of variables for \cs[no-index]{spfact}} % % \begin{macro}[int]{\l_@@_spfact_read_arg_tl} % The token list variable \myhlc|\l_@@_spfact_read_arg_tl| stores the key % \mykey[spfact]{read-arg}. % \begin{macrocode} \tl_new:N \l_@@_spfact_read_arg_tl % \end{macrocode} % \end{macro} % % \subsubsection{Definition of error messages for \cs[no-index]{spfact}} % % \begin{macro}[int]{spfact-empty-arg} % The \enquote{error} |spfact-empty-arg| is raised when the argument of % |\spfact| is missing or \emph{blank}. % \begin{macrocode} \msg_new:nnnn { spintent } { spfact-empty-arg } { Empty~argument~for~'\token_to_str:N \spfact'~\msg_line_context:. } { The~number~n~is~required. } % \end{macrocode} % \end{macro} % % \subsubsection{Definition of keys for \cs[no-index]{spfact}} % % \begin{macro}[int]{silent, read-arg, unknown} % We define the \mymeta{keys} \mykey[spfact]{silent} and \mykey[spfact]{read-arg}. % \begin{macrocode} \keys_define:nn { spintent / spfact } { silent .bool_set:N = \l_@@_spfact_silent_bool, silent .initial:n = false, silent .value_required:n = true, read-arg .code:n = { \@@_sanitize_affix:Nn \l_@@_spfact_read_arg_tl {#1} }, read-arg .initial:n = , read-arg .value_required:n = true, unknown .code:n = { \str_set:Nn \l_@@_command_name_str { spfact } \@@_unknown_keys_cmd:n {#1} }, } % \end{macrocode} % \end{macro} % % \subsubsection{Internal functions for \cs[no-index]{spfact}} % % \begin{macro}[int]{\@@_spfact_exec:nn} % The function \myhlc|\@@_spfact_exec:nn| sets the \mymeta{keys} in `|#1|' % and calls \myhlc|\@@_spfact_mathml:n| with the argument in `|#2|'. % \begin{macrocode} \cs_new_protected:Nn \@@_spfact_exec:nn { \keys_set:nn { spintent / spfact } {#1} \@@_spfact_mathml:n {#2} } % \end{macrocode} % \end{macro} % % \begin{macro}[int]{\@@_spfact_mathml:n} % The function \myhlc|\@@_spfact_mathml:n| prints the factorial of `|#1|' % inside one native |\MathMLintent|, preceded by the outer ghost % \myhlc|\@@_invisible_sep:| and followed by the inner ghost % \myhlc|\@@_inner_sep:|. With \mykey{silent}|=true| the intent is `|_-|'; % with \mykey{read-arg} given it is |_|\mymeta{read-arg}|:nofix|; % otherwise it is |_factorial:postfix($n)|. % \begin{macrocode} \cs_new_protected:Nn \@@_spfact_mathml:n { \@@_invisible_sep: \bool_if:NTF \l_@@_spfact_silent_bool { \MathMLintent { _- } { \@@_inner_sep: #1 ! } } { \tl_if_empty:NTF \l_@@_spfact_read_arg_tl { \MathMLintent { _factorial:postfix($n) } { \@@_inner_sep: \MathMLarg { n } { \@@_inner_sep: #1 } ! } } { \MathMLintent { _ \tl_use:N \l_@@_spfact_read_arg_tl :nofix } { \@@_inner_sep: #1 ! } } } } % \end{macrocode} % \end{macro} % % \begin{macro}{\spfact} % The public command \ics*{spfact} enforces \emph{math mode}, opens a local % group and delegates execution to \myhlc|\@@_spfact_exec:nn|. % \begin{macrocode} \NewDocumentCommand \spfact { O{} m } { \mode_if_math:TF { \group_begin: \attribute \l_@@_scope_attr_int = 1 \tl_if_blank:nTF {#2} { \msg_error:nn { spintent } { spfact-empty-arg } } { \@@_spfact_exec:nn {#1} {#2} } \group_end: } { \msg_error:nnn { spintent } { need-math-mode } { \spfact } } } % \end{macrocode} % \end{macro} % % \subsection{Finish package} % % Finish package implementation. % % \begin{macrocode} \file_input_stop: % % \end{macrocode} % \Finale % \endinput % \iffalse %<*spintentlua> % \fi --[[ Lua module spintent.lua for spintent package - v1.0 [2026-10-20] --]] -- CACHÉ, LPEG Y HERRAMIENTAS GLOBALES local tonumber = tonumber local tostring = tostring local math_floor = math.floor local math_sqrt = math.sqrt local math_min = math.min local t_sort = table.sort local s_format = string.format local t_concat = table.concat local t_unpack = table.unpack or unpack local s_match = string.match local s_gmatch = string.gmatch local s_sub = string.sub local s_gsub = string.gsub local s_lower = string.lower local s_upper = string.upper local s_byte = string.byte local pairs = pairs local d_todirect = node.direct.todirect local d_tonode = node.direct.tonode local d_traverse = node.direct.traverse_glyph local d_getfield = node.direct.getfield local d_new = node.direct.new local d_setfield = node.direct.setfield local token_set_macro = token.set_macro local document_cctab = luatexbase.registernumber("c_document_cctab") local lpeg_base = lpeg or require("lpeg") local P, R, S, Cs, Ct, Cg, C, Cc = lpeg_base.P, lpeg_base.R, lpeg_base.S, lpeg_base.Cs, lpeg_base.Ct, lpeg_base.Cg, lpeg_base.C, lpeg_base.Cc local spintent_spshort_V = lpeg_base.V local spintent_digit = R"09" local spintent_math_space = P"\\," + P"\\-" + P"\\;" + P"\\:" + P"\\!" + P"\\>" + P"\\quad" + P"\\qquad" + P"~" + P"\\ " + P"\\thinspace" + P"\\nobreakspace" local spintent_discard_space = (S" \t\r\n" + spintent_math_space) / "" local spintent_opt_spaces = spintent_discard_space^0 -- Trim de espacios centralizado en LPeg (sin backtracking) local spintent_ws = S" \t\r\n" local spintent_trim_pattern = spintent_ws^0 * C((P(1) - spintent_ws^0 * P(-1))^0) * spintent_ws^0 local function spintent_trim(str) return spintent_trim_pattern:match(str) end -- Combina trim + minúsculas para los casos que no necesitan conservar -- el case original después. local function spintent_normalize_key(str) return str and s_lower(spintent_trim(str)) or "" end -- Eliminación de espacios matemáticos para cálculos local spintent_num_chunk = Cs( spintent_digit * (spintent_opt_spaces * spintent_digit)^0 ) local spintent_num_chunk_keep = Cs( spintent_digit * ( ((S" \t\r\n" + spintent_math_space)^0 * #spintent_digit) * spintent_digit )^0 ) -- luaotfload workaround (https://github.com/latex3/luaotfload/pull/342) do local original = luatexbase.remove_from_callback( "luaotfload.patch_font", "luaotfload.rewrite_fontname" ) if original then luatexbase.add_to_callback( "luaotfload.patch_font", function(tfmdata, specification) if type(specification) == "string" then return original(tfmdata, specification) else tfmdata.name = specification.name end end, "luaotfload.rewrite_fontname" ) end end -- Registro de las funciones para expl3 local function register_tex_cmd(name, func, args) name = "__spintent_" .. name .. ":" .. string.rep("n", #args) local scanners = {} for i = 1, #args do local scan_type = (args[i] == "string" and "scan_argument") or "scan_" .. args[i] scanners[i] = token[scan_type] end local scanning_func if #scanners == 0 then scanning_func = func elseif #scanners == 1 then local s1 = scanners[1] scanning_func = function() func(s1()) end elseif #scanners == 2 then local s1, s2 = scanners[1], scanners[2] scanning_func = function() func(s1(), s2()) end else scanning_func = function() local values = {} for i = 1, #scanners do values[i] = scanners[i]() end func(t_unpack(values)) end end local index = luatexbase.new_luafunction(name) lua.get_functions_table()[index] = scanning_func token.set_lua(name, index, "global", "protected") end -- spintent: anotación de MathML con 'intent'/'arg' sin token -- fantasma. Aplica cuando el contenido es un noad simple o -- compuesto (mi, msub, msup, radical, fraction) cuyo 'nucleus' -- es alcanzable directamente en la lista de nivel superior, o -- anidado dentro de un accent/contenedor con múltiples items -- (el callback recursa para encontrarlo). local spintent_sub_mlist_t = node.id("sub_mlist") local spintent_pending_mrow_intent_queue = {} -- El segundo argumento es una lista estilo Lua (key = val), evaluada -- con load(); mismo mecanismo que \luamml_annotate:en usa para su propio -- core=/mathml=. register_tex_cmd("luafun_mrow_intent_arg", function(concept_intent, kv_string) local opts = load('return {' .. kv_string .. '}')() spintent_pending_mrow_intent_queue[#spintent_pending_mrow_intent_queue + 1] = { concept_intent = (concept_intent ~= "") and concept_intent or nil, arg_name = opts.arg, child_intent = opts.intent, } end, { "string", "string" }) local spintent_sub_mlist_t = node.id("sub_mlist") -- Numero de atributo asignado por el propio kernel de LuaTeX (sin -- riesgo de colision con otro paquete). local spintent_scope_attr = luatexbase.new_attribute("spintent_scope") token_set_macro("l__spintent_luaset_scope_attr_str", tostring(spintent_scope_attr)) -- Reclama 'target' con la siguiente entrada de la cola. Solo si -- tiene spintent_scope_attr=1 (tipografiado dentro de un comando -- de spintent) y todavia no tiene mathml_filter propio. local function spintent_claim_target(target, properties) if node.has_attribute(target, spintent_scope_attr) ~= 1 then return end local existing = properties[target] if existing and existing.mathml_filter then return end local entry = table.remove(spintent_pending_mrow_intent_queue, 1) local concept_intent, arg_name, child_intent if type(entry) == "table" then concept_intent = entry.concept_intent arg_name = entry.arg_name child_intent = entry.child_intent else concept_intent = entry end local p = properties[target] or {} p.mathml_filter = function(result, core) if arg_name then result.arg = arg_name end if child_intent then result.intent = child_intent end if result[0] == "mrow" then if concept_intent then result.intent = concept_intent end return result, core end if concept_intent then return { [0] = "mrow", intent = concept_intent, result }, nil end return result, core end properties[target] = p end -- Recursa dentro de cualquier sub_mlist encontrado en .nucleus O EN -- .sub (subíndice) -- \c_math_subscript_token pone el contenido ahí, -- no en .nucleus, así que sin esto la cola nunca alcanza nada -- dentro de un subíndice. local function spintent_try_annotate(n, properties) if #spintent_pending_mrow_intent_queue == 0 then return end if n.nucleus then local nucleus = n.nucleus if nucleus.id == spintent_sub_mlist_t and nucleus.head then for inner in node.traverse(nucleus.head) do spintent_try_annotate(inner, properties) if #spintent_pending_mrow_intent_queue == 0 then return end end else spintent_claim_target(nucleus, properties) end end if #spintent_pending_mrow_intent_queue == 0 then return end if n.sub then local sub = n.sub if sub.id == spintent_sub_mlist_t and sub.head then for inner in node.traverse(sub.head) do spintent_try_annotate(inner, properties) if #spintent_pending_mrow_intent_queue == 0 then return end end else spintent_claim_target(sub, properties) end end end luatexbase.add_to_callback("pre_mlist_to_hlist_filter", function(mlist, style) if #spintent_pending_mrow_intent_queue == 0 then return true end local properties = node.get_properties_table() for n in node.traverse(mlist) do if #spintent_pending_mrow_intent_queue == 0 then break end spintent_try_annotate(n, properties) end -- Cualquier entrada sobrante (un comando pidio mas anotaciones de -- las que encontro hojas libres) se descarta aca -- si no, se -- queda esperando y puede terminar aplicandose, sin aviso, a una -- formula completamente distinta mas adelante en el documento. if #spintent_pending_mrow_intent_queue > 0 then spintent_pending_mrow_intent_queue = {} end return true end, "spintent.wrap_mrow_intent") luatexbase.declare_callback_rule("pre_mlist_to_hlist_filter", "spintent.wrap_mrow_intent", "before", "luamml.to_mathml") -- Recibe "true"/"false" desde expl3, una sola vez, justo despues de -- cargar el modulo -- activa/desactiva la marca sptmp="inner"/"outer" -- en las dos funciones fantasma. local spintent_debug_ghost = false register_tex_cmd("luafun_set_debug_ghost", function(state) spintent_debug_ghost = (state == "true") end, { "string" }) -- Reemplaza \c__spintent_invisible_sep_int cuando se usa junto a -- \MathMLintent nativo (necesita un segundo hermano para no -- colapsar). Se auto-anota con su propio mathml_filter al -- crearse -- por eso la recursión de wrap_mrow_intent_arg lo -- reconoce como "ya reclamado" y lo salta, en vez de confundirlo -- con un objetivo libre de la cola. A diferencia del fantasma -- exterior, este NO se deduplica: puede hacer falta más de una -- vez por fórmula (una por cada \MathMLintent anidado). register_tex_cmd("luafun_inner_sep", function() local kernel = node.new("math_char") kernel.char = 0x2063 kernel.fam = 0 local noad = node.new("noad") noad.subtype = 0 noad.nucleus = kernel local properties = node.get_properties_table() local p = properties[kernel] or {} p.mathml_filter = function(result, core) result.intent = ":silent" if spintent_debug_ghost then result.sptmp = "inner" end return result, core end properties[kernel] = p node.write(noad) end, {}) -- \__spintent_invisible_sep: (fantasma exterior, ). -- Necesario cuando el resultado anotado es el único elemento de -- nivel superior de la fórmula: sin un segundo hermano, to_math -- renombra el resultante directamente a -- en vez de dejarlo como hijo (luamml issue #26). local spintent_ghost_injected = false register_tex_cmd("luafun_invisible_sep", function() if spintent_ghost_injected then return end spintent_ghost_injected = true local kernel = node.new("math_char") kernel.char = 0x2063 kernel.fam = 0 local noad = node.new("noad") noad.subtype = 0 -- noad_ord. Forzar op (1) da correcto -- pero agrega espaciado real de operador al -- PDF (rspace visible). Se acepta sin -- efecto lateral -- intent=':silent' hace -- que la etiqueta (mi/mo) sea irrelevante -- para cualquier lector. noad.nucleus = kernel local properties = node.get_properties_table() local p = properties[kernel] or {} p.mathml_filter = function(result, core) result.intent = ":silent" if spintent_debug_ghost then result.sptmp = "outer" end return result, core end properties[kernel] = p node.write(noad) end, {}) -- Marca el fantasma exterior como ya colocado, sin escribirlo: lo usa -- \spmQ para que \spnZ no lo escriba dentro de la fraccion y los -- comandos siguientes de la formula se comporten como antes. register_tex_cmd("luafun_invisible_sep_mark", function() spintent_ghost_injected = true end, {}) luatexbase.add_to_callback("pre_mlist_to_hlist_filter", function(mlist, style) spintent_ghost_injected = false return true end, "spintent.reset_ghost") -- Implementación interna para \mathcal clásico y \right_angle_sqr local function spintent_find_free_family() for i = 15, 1, -1 do local ok, id = pcall(tex.getfontoffamily, i) if ok and (not id or id == 0) then return i end end return nil end local function spintent_load_scaled(filename, size, features) return luaotfload.define_font { lookup = "file", name = filename, size = size, features = { normal = features }, } end local function spintent_load_family(fontfile, features, fam) features = table.merged(features, { mode = "base", script = "math", language = "dflt", }) local main_id = tex.getfontoffamily(0) local main_fd = main_id and font.getfont(main_id) if not main_fd then luatexbase.module_error("spintent", "familia matematica 0 sin fuente \z -- llama a \\setmathfont antes de \\begin{document}") return false end local cur_size = main_fd.size local text_id = spintent_load_scaled(fontfile, cur_size, features) if not text_id then return false end local mc = font.getfont(text_id).MathConstants local script_pct = (mc and mc.ScriptPercentScaleDown or 70) / 100 local scriptscr_pct = (mc and mc.ScriptScriptPercentScaleDown or 50) / 100 local script_id = spintent_load_scaled(fontfile, math.floor(cur_size * script_pct), features) local scriptscript_id = spintent_load_scaled(fontfile, math.floor(cur_size * scriptscr_pct), features) if not script_id or not scriptscript_id then return false end tex.definefont(true, "MathFBtmpText", text_id) tex.definefont(true, "MathFBtmpScript", script_id) tex.definefont(true, "MathFBtmpScriptScr", scriptscript_id) tex.runtoks(function() tex.sprint(document_cctab, string.format( "\\global\\textfont%d=\\MathFBtmpText\z \\global\\scriptfont%d=\\MathFBtmpScript\z \\global\\scriptscriptfont%d=\\MathFBtmpScriptScr", fam, fam, fam)) end) return true end local spintent_cal_slots = { A=0x1D49C, B=0x212C, C=0x1D49E, D=0x1D49F, E=0x2130, F=0x2131, G=0x1D4A2, H=0x210B, I=0x2110, J=0x1D4A5, K=0x1D4A6, L=0x2112, M=0x2133, N=0x1D4A9, O=0x1D4AA, P=0x1D4AB, Q=0x1D4AC, R=0x211B, S=0x1D4AE, T=0x1D4AF, U=0x1D4B0, V=0x1D4B1, W=0x1D4B2, X=0x1D4B3, Y=0x1D4B4, Z=0x1D4B5, } -- spintent_init_math_fallbacks, diferida a \AtBeginDocument. local spintent_cal_fam, spintent_cal_fam_ok local spintent_sym_fam, spintent_sym_fam_ok local function spintent_classic_mathcal(letters) if not spintent_cal_fam_ok then tex.sprint(document_cctab, letters); return end local parts = {} for i = 1, #letters do local ch = letters:sub(i,i) local cp = spintent_cal_slots[ch] parts[#parts+1] = cp and string.format([[\Umathchar 0 %d "%X ]], spintent_cal_fam, cp) or ch end tex.sprint(document_cctab, parts) end register_tex_cmd("luafun_classic_mathcal", spintent_classic_mathcal, {"string"}) local function spintent_right_angle_sqr() if not spintent_sym_fam_ok then return end tex.sprint(document_cctab, string.format([[\Umathchar 0 %d "299C ]], spintent_sym_fam)) end register_tex_cmd("luafun_right_angle_sqr", spintent_right_angle_sqr, {}) -- Se llama desde \AtBeginDocument en spintent.sty, no al cargar el modulo -- Si no hay fuente matematica cargada, degrada con un aviso.. local function spintent_init_math_fallbacks() spintent_cal_fam = spintent_find_free_family() spintent_cal_fam_ok = spintent_cal_fam and spintent_load_family("NewCMMath-Regular.otf", {}, spintent_cal_fam) token_set_macro("l__spintent_luaset_cal_fam_ok_str", spintent_cal_fam_ok and "true" or "false") spintent_sym_fam = spintent_find_free_family() spintent_sym_fam_ok = spintent_sym_fam and spintent_load_family("NewCMMath-Regular.otf", {}, spintent_sym_fam) token_set_macro("l__spintent_luaset_sym_fam_ok_str", spintent_sym_fam_ok and "true" or "false") end register_tex_cmd("luafun_init_math_fallbacks", spintent_init_math_fallbacks, {}) -- MOTOR CENTRAL Y NÚMEROS (\spnum y \spunit) local spintent_num_sign = S "+-" local spintent_num_decimal = S ".," + P "{.}" + P "{,}" local spintent_num_semi = P ";" local spintent_forbidden_in_extra = spintent_num_decimal + spintent_num_semi local spintent_custom_spunit_aliases = {} local spintent_extra_char = P(1) - spintent_forbidden_in_extra local spintent_number_pattern = Ct( Cg(spintent_num_sign^-1, "sign") * spintent_opt_spaces * Cg(spintent_num_chunk_keep^-1, "integer") * ( spintent_opt_spaces * Cg(C(spintent_num_decimal), "decimal") * spintent_opt_spaces * Cg(spintent_num_chunk_keep, "fraction") )^-1 * ( spintent_opt_spaces * spintent_num_semi * spintent_opt_spaces * Cg(spintent_num_chunk_keep, "period") )^-1 * ( spintent_opt_spaces * S"eE" * spintent_opt_spaces * Cg(Cs(spintent_num_sign^-1 * spintent_num_chunk), "exponent") )^-1 * spintent_opt_spaces * Cg(Cs(spintent_extra_char^0), "extra") * P(-1) ) local function spintent_rae_format_digits(str_num, reverse) if not str_num or str_num == "" then return "" end str_num = tostring(str_num) local len = #str_num -- Los números en Lua son 1 byte por carácter (ASCII) if len <= 4 then return str_num end local chunks = {} if reverse then for i = 1, len, 3 do chunks[#chunks + 1] = s_sub(str_num, i, i + 2) end else local first = len % 3 if first == 0 then first = 3 end chunks[#chunks + 1] = s_sub(str_num, 1, first) for i = first + 1, len, 3 do chunks[#chunks + 1] = s_sub(str_num, i, i + 2) end end return t_concat(chunks, "\\,") end local spintent_strip_math_spaces = Cs((spintent_discard_space + P(1)) ^ 0) register_tex_cmd("luafun_clean_split_and_set", function(raw_string) raw_string = spintent_trim(raw_string) local result = spintent_number_pattern:match(raw_string) -- Prevención de creación de tabla si falla el match (nil or {}) local r_sign = (result and result.sign) or "" local r_int = (result and result.integer) or "" local r_frac = (result and result.fraction) or "" local r_int_clean = spintent_strip_math_spaces:match(r_int) local r_frac_clean = spintent_strip_math_spaces:match(r_frac) local r_dec = (result and result.decimal) or "" local r_period = (result and result.period) or "" local r_extra = (result and result.extra) local r_exp = (result and result.exponent) or "" local has_exponent = (r_exp ~= "") and "true" or "false" local above = "" local below = "" local unit_status = "valid" local has_units = "false" local denom_has_numeric = "false" if r_extra then local extra = spintent_trim(r_extra) if spintent_custom_spunit_aliases[extra] then extra = spintent_custom_spunit_aliases[extra] end if extra ~= "" then has_units = "true" local parts = {} for part in s_gmatch(extra, "[^/]+") do parts[#parts + 1] = part end if #parts > 2 or s_match(extra, "/%s*/") then unit_status = "multislash" else above = parts[1] or "" below = parts[2] or "" if below ~= "" and s_match(below, "^%s*%d") then denom_has_numeric = "true" end end end end local is_million_clean = "false" if r_int_clean ~= "" then local int_value = tonumber(r_int_clean) if int_value and int_value > 999999 and (int_value % 1000000 == 0) then is_million_clean = "true" end end local has_dec = r_dec ~= "" local has_per = r_period ~= "" local dec_and_per = "false" local not_dec_and_per = "false" local not_dec_not_per = "false" if has_dec and has_per then dec_and_per = "true" elseif not has_dec and has_per then not_dec_and_per = "true" elseif not has_dec and not has_per then not_dec_not_per = "true" end local has_integer = "false" if r_int_clean ~= "" and r_dec == "" and r_frac_clean == "" and r_period == "" and has_exponent == "false" and has_units == "false" then has_integer = "true" end token_set_macro("l__spintent_luaset_part_int_tl", r_int) token_set_macro("l__spintent_luaset_part_dec_tl", r_frac) token_set_macro("l__spintent_luaset_sign_tl", r_sign) token_set_macro("l__spintent_luaset_dec_sep_tl", r_dec) token_set_macro("l__spintent_luaset_part_period_tl", r_period) token_set_macro("l__spintent_luaset_only_part_int_str", r_int_clean) token_set_macro("l__spintent_luaset_only_part_dec_str", r_frac_clean) token_set_macro("l__spintent_luaset_only_part_period_str", (result and result.period) or "") token_set_macro("l__spintent_luaset_format_part_int_str", spintent_rae_format_digits(r_int_clean, false)) token_set_macro("l__spintent_luaset_format_part_dec_str", spintent_rae_format_digits(r_frac_clean, true)) token_set_macro("l__spintent_luaset_millons_str", is_million_clean) token_set_macro("l__spintent_luaset_decimal_period_str", dec_and_per) token_set_macro("l__spintent_luaset_not_decimal_period_str", not_dec_and_per) token_set_macro("l__spintent_luaset_not_decimal_not_period_str", not_dec_not_per) token_set_macro("l__spintent_luaset_has_exponent_str", has_exponent) token_set_macro("l__spintent_luaset_exponent_tl", r_exp) token_set_macro("l__spintent_luaset_has_units_str", has_units) token_set_macro("l__spintent_luaset_status_str", unit_status) token_set_macro("l__spintent_luaset_arg_above_tl", above) token_set_macro("l__spintent_luaset_arg_below_tl", below) token_set_macro("l__spintent_luaset_denom_has_number_str", denom_has_numeric) token_set_macro("l__spintent_luaset_has_integer_str", has_integer) if r_frac_clean ~= "" and s_match(r_frac_clean, "^0+$") then token_set_macro("l__spintent_luaset_dec_is_all_zeros_str", "true") else token_set_macro("l__spintent_luaset_dec_is_all_zeros_str", "false") end end, { "string" }) -- UNIDADES FÍSICAS Y CIENTÍFICAS (\spunit) local spintent_units = { ["°K"] = "°K", ["A"] = "A", ["B"] = "B", ["Bq"] = "Bq", ["C"] = "C", ["Da"] = "Da", ["F"] = "F", ["Gy"] = "Gy", ["H"] = "H", ["Hz"] = "Hz", ["J"] = "J", ["K"] = "K", ["l"] = "l", ["L"] = "L", ["ℓ"] = "ℓ", ["N"] = "N", ["Np"] = "Np", ["Pa"] = "Pa", ["S"] = "S", ["Sv"] = "Sv", ["T"] = "T", ["V"] = "V", ["W"] = "W", ["Wb"] = "Wb", ["a"] = "a", ["atm"] = "atm", ["au"] = "au", ["b"] = "b", ["bar"] = "bar", ["cal"] = "cal", ["cd"] = "cd", ["d"] = "d", ["dB"] = "dB", ["dyn"] = "dyn", ["eV"] = "eV", ["erg"] = "erg", ["g"] = "g", ["h"] = "h", ["ha"] = "ha", ["kat"] = "kat", ["kg"] = "kg", ["km"] = "km", ["lm"] = "lm", ["lx"] = "lx", ["m"] = "m", ["cm"] = "cm", ["min"] = "min", ["mm"] = "mm", ["mol"] = "mol", ["pc"] = "pc", ["rad"] = "rad", ["rpm"] = "rpm", ["s"] = "s", ["sr"] = "sr", ["t"] = "t", ["u"] = "u", ["w"] = "w", ["y"] = "y", ["°"] = "°", ["°C"] = "°C", ["°F"] = "°F", ["Ω"] = "Ω", ["℧"] = "℧", ["′"] = "′", ["″"] = "″", ["Å"] = "Å", ["µ"] = "µ", ["in"] = "in", ["pulgada"] = "in", ["pulgadas"] = "in", ["ft"] = "ft", ["pie"] = "ft", ["pies"] = "ft", ["yd"] = "yd", ["yarda"] = "yd", ["yardas"] = "yd", ["mi"] = "mi", ["milla"] = "mi", ["millas"] = "mi", ["lb"] = "lb", ["libra"] = "lb", ["libras"] = "lb", ["oz"] = "oz", ["onza"] = "oz", ["onzas"] = "oz", ["gal"] = "gal", ["galon"] = "gal", ["galones"] = "gal", ["hora"] = "h", ["horas"] = "h", ["dia"] = "d", ["dias"] = "d", ["caloria"] = "cal", ["calorias"] = "cal", ["faradio"] = "F", ["culombio"] = "C", ["bit"] = "b", ["byte"] = "B", ["bytes"] = "B", ["fahrenheit"] = "°F", ["kelvin"] = "K", ["Kelvin"] = "K", ["ohmio"] = "Ω", ["ohm"] = "Ω", ["Omega"] = "Ω", ["amperio"] = "A", ["ampere"] = "A", ["voltio"] = "V", ["volt"] = "V", ["vatio"] = "W", ["watt"] = "W", ["julio"] = "J", ["joule"] = "J", ["newton"] = "N", ["pascal"] = "Pa", ["hercio"] = "Hz", ["hertz"] = "Hz", ["metro"] = "m", ["metros"] = "m", ["segundo"] = "s", ["segundos"] = "s", ["litro"] = "l", ["l-litro"] = "l", ["gramo"] = "g", ["gramos"] = "g", ["angstrom"] = "Å", ["celsius"] = "°C", ["degC"] = "°C", ["℃"] = "°C", ["degF"] = "°F", ["℉"] = "°F", ["micro"] = "µ", ["minmin"] = "′", ["segseg"] = "″", ["u-masa"] = "u", ["mho"] = "℧", ["㎡"] = "m^2", ["㎢"] = "km^2", ["㎠"] = "cm^2", ["㎜²"] = "mm^2", ["㎥"] = "m^3", ["㎦"] = "km^3", ["㎤"] = "cm^3", ["㎜³"] = "mm^3", ["㎐"] = "Hz", ["㎑"] = "kHz", ["㎒"] = "MHz", ["㎓"] = "GHz", ["㎔"] = "THz", ["㎽"] = "mW", ["㎾"] = "kW", ["㎿"] = "MW", ["㎴"] = "V", ["㎵"] = "kV", ["㎶"] = "µV", ["㎷"] = "mV", ["㎸"] = "kV", ["㎀"] = "pA", ["㎁"] = "nA", ["㎂"] = "µA", ["㎃"] = "mA", ["㎄"] = "kA", ["㎫"] = "MPa", ["㎬"] = "GPa", ["㎺"] = "pW", ["㎻"] = "nW", ["㎼"] = "µW", ["㎍"] = "µg", ["㎎"] = "mg", ["㎏"] = "kg", ["㎜"] = "mm", ["㎝"] = "cm", ["㎞"] = "km", ["㎛"] = "µm", ["㎕"] = "µL", ["㎖"] = "mL", ["㎗"] = "dL", ["㎘"] = "kL", ["㎧"] = "m/s", ["㎨"] = "m/s^2", ["㎭"] = "rad", ["㎮"] = "rad/s", ["㎯"] = "rad/s^2" } local spintent_unit_compact_spoken_names = { ["㎡"] = "metro-cuadrado", ["㎥"] = "metro-cúbico", ["㎢"] = "kilómetro-cuadrado", ["㎟"] = "milímetro-cuadrado", ["㎠"] = "centímetro-cuadrado", ["㎣"] = "milímetro-cúbico", ["㎤"] = "centímetro-cúbico", ["㎦"] = "kilómetro-cúbico", ["㎧"] = "metros-por-segundo", ["㎨"] = "metros-por-segundo-al-cuadrado", ["㎐"] = "hercio", ["℃"] = "grados-celsius", ["℉"] = "grados-fahrenheit", ["'"] = "minutos", ["''"] = "segundos", ['"'] = "segundos", ["minmin"] = "minutos", ["segseg"] = "segundos", ["′"] = "minutos", ["″"] = "segundos", } local spintent_custom_spunit_spoken_names = {} local spintent_normalizations = { ["K"] = "K", ["Ω"] = "Ω", ["ℓ"] = "\\ell", ["μ"] = "µ", ["°K"] = "°K", ["'"] = "′", ["''"] = "″", ['"'] = "″", ["minmin"] = "′", ["segseg"] = "″" } local spintent_tex_accents = { ["\\'a"] = "á", ["\\'e"] = "é", ["\\'i"] = "í", ["\\'o"] = "ó", ["\\'u"] = "ú", ["\\'A"] = "Á", ["\\'E"] = "É", ["\\'I"] = "Í", ["\\'O"] = "Ó", ["\\'U"] = "Ú", ["\\\"u"] = "ü", ["\\\"U"] = "Ü", ["\\~n"] = "ñ", ["\\~N"] = "Ñ" } local function spintent_sanitize_for_screen_reader(raw_string) if not raw_string then return "" end local clean = s_gsub(raw_string, "[{}]", "") clean = s_gsub(clean, "\\[\'~\x22].", spintent_tex_accents) clean = s_gsub(clean, "%s+", "-") return clean end register_tex_cmd("luafun_define_custom_unit", function(unit_symbol, spoken_name) if spintent_units[unit_symbol] or spintent_custom_spunit_spoken_names[unit_symbol] or spintent_custom_spunit_aliases[unit_symbol] then token_set_macro("l__spintent_spunit_luaset_status_str", "duplicate") return end -- Sanitizamos y aglutinamos para MathCAT local clean_spoken_name = spintent_sanitize_for_screen_reader(spoken_name) -- El prefijo _ marca un intent propio, no reconocido por MathCAT spintent_custom_spunit_spoken_names[unit_symbol] = "_" .. clean_spoken_name spintent_units[unit_symbol] = unit_symbol token_set_macro("l__spintent_spunit_luaset_status_str", "success") end, { "string", "string" }) -- °, ′, ″ son los tres símbolos sexagesimales reconocidos -- chequeo -- compartido por las dos ramas de spintent_spunit_lookup. local function spintent_is_sexagesimal_symbol(s) return s == "°" or s == "′" or s == "″" end -- Busca una unidad con exponente opcional ("m", "m^2", "㎡"). Devuelve -- nil si no existe; si existe, { canonical, read, exp, sexagesimal } con -- 'exp' vacío cuando la unidad no lleva exponente. local function spintent_spunit_lookup(raw_unit_name) raw_unit_name = s_gsub(raw_unit_name, "%s+", "") local clean_exp_format = s_gsub(raw_unit_name, "[{}]", "") local search_name = spintent_normalizations[clean_exp_format] or spintent_normalizations[raw_unit_name] or raw_unit_name local canonical = spintent_units[search_name] or spintent_units[clean_exp_format] if canonical then local base_clean = s_gsub(canonical, "%^%-?%d+", "") local spoken = spintent_custom_spunit_spoken_names[base_clean] or spintent_unit_compact_spoken_names[raw_unit_name] or spintent_unit_compact_spoken_names[clean_exp_format] or spintent_unit_compact_spoken_names[search_name] or ":unit" local base, exp = s_match(canonical, "([^%^]+)%^(%-?%d+)") if not (base and exp) then base, exp = canonical, "" end return { canonical = base, read = spoken, exp = exp, sexagesimal = spintent_is_sexagesimal_symbol(canonical), } end local literal_base, literal_exp = s_match(clean_exp_format, "([^%^]+)%^(%-?%d+)") if literal_base and literal_exp then local canonical_base = spintent_units[spintent_normalizations[literal_base] or literal_base] if canonical_base then return { canonical = canonical_base, read = spintent_custom_spunit_spoken_names[canonical_base] or ":unit", exp = literal_exp, sexagesimal = spintent_is_sexagesimal_symbol(canonical_base), } end end return nil end -- \spunit: parte un producto de unidades ("u1*u2^n*...") en sus términos -- y busca cada uno. Para el término deja en -- l__spintent_spunit_luaset___{canonical,read}_str y _exp_tl -- sus datos, y en l__spintent_spunit_luaset__{n,found,sexa}_str el -- número de términos, si todos existen y si es una única unidad -- sexagesimal. 'which' es "above" o "below". register_tex_cmd("luafun_spunit_parse_product", function(which, product) local prefix = "l__spintent_spunit_luaset_" .. which .. "_" local n, all_found, sexagesimal = 0, true, false for term in s_gmatch(product .. "*", "([^*]*)%*") do n = n + 1 local info = spintent_spunit_lookup(term) if info then token_set_macro(prefix .. n .. "_canonical_str", info.canonical) token_set_macro(prefix .. n .. "_read_str", info.read) token_set_macro(prefix .. n .. "_exp_tl", info.exp) sexagesimal = info.sexagesimal else all_found = false end end token_set_macro(prefix .. "n_str", tostring(n)) token_set_macro(prefix .. "found_str", all_found and "true" or "false") token_set_macro(prefix .. "sexa_str", (all_found and n == 1 and sexagesimal) and "true" or "false") end, { "string", "string" }) register_tex_cmd("luafun_define_spunit_alias", function(alias_name, unit_expression) alias_name = s_gsub(alias_name, "%s+", "") unit_expression = spintent_trim(unit_expression) if spintent_units[alias_name] or spintent_normalizations[alias_name] or spintent_custom_spunit_aliases[alias_name] then token_set_macro("l__spintent_spunit_luaset_status_str", "duplicate") return end local is_valid = true local _, slash_count = s_gsub(unit_expression, "/", "") if slash_count > 1 then is_valid = false end if s_match(unit_expression, "[^%a°ΩµÅℓ′″℧%*%/%^%-%d%s%'%\x22{}]") then is_valid = false end if is_valid then for unit_base in s_gmatch(unit_expression, "[%a°ΩµÅℓ′″℧%'%\x22]+") do if not (spintent_units[unit_base] or spintent_normalizations[unit_base]) then is_valid = false; break end end end if not is_valid then token_set_macro("l__spintent_spunit_luaset_status_str", "invalid-expr") else spintent_custom_spunit_aliases[alias_name] = unit_expression token_set_macro("l__spintent_spunit_luaset_status_str", "success") end end, { "string", "string" }) -- DIVISAS Y MONEDAS (\spmoney) local spintent_internal_currencies = { ["$"] = "$", ["€"] = "€", ["£"] = "£", ["¥"] = "¥", ["¢"] = "¢", ["₩"] = "₩", ["₪"] = "₪", ["₹"] = "₹", ["₽"] = "₽", ["₺"] = "₺", ["₴"] = "₴", ["dolar"] = "$", ["dolares"] = "$", ["usd"] = "$", ["peso"] = "$", ["pesos"] = "$", ["clp"] = "$", ["mxn"] = "$", ["euro"] = "€", ["euros"] = "€", ["eur"] = "€", ["libra"] = "£", ["libras"] = "£", ["gbp"] = "£", ["yen"] = "¥", ["yenes"] = "¥", ["yuan"] = "¥", ["yuanes"] = "¥", ["jpy"] = "¥", ["cny"] = "¥", ["centavo"] = "¢", ["centavos"] = "¢", ["won"] = "₩", ["wons"] = "₩", ["krw"] = "₩", ["shekel"] = "₪", ["shekels"] = "₪", ["sequel"] = "₪", ["sequeles"] = "₪", ["ils"] = "₪", ["rupia"] = "₹", ["rupias"] = "₹", ["inr"] = "₹", ["rublo"] = "₽", ["rublos"] = "₽", ["rub"] = "₽", ["lira"] = "₺", ["liras"] = "₺", ["try"] = "₺", ["grivna"] = "₴", ["grivnas"] = "₴", ["uah"] = "₴", } local spintent_currency_grammatical_dict = { ["$"] = { sing = "peso", plur = "pesos", conde = "de-pesos" }, ["peso"] = { sing = "peso", plur = "pesos", conde = "de-pesos" }, ["pesos"] = { sing = "peso", plur = "pesos", conde = "de-pesos" }, ["clp"] = { sing = "peso-chileno", plur = "pesos-chilenos", conde = "de-pesos-chilenos" }, ["mxn"] = { sing = "peso-mexicano", plur = "pesos-mexicanos", conde = "de-pesos-mexicanos" }, ["dolar"] = { sing = "dólar", plur = "dólares", conde = "de-dólares" }, ["dolares"] = { sing = "dólar", plur = "dólares", conde = "de-dólares" }, ["usd"] = { sing = "dólar-estadounidense", plur = "dólares-estadounidenses", conde = "de-dólares-estadounidenses" }, ["euro"] = { sing = "euro", plur = "euros", conde = "de-euros" }, ["euros"] = { sing = "euro", plur = "euros", conde = "de-euros" }, ["eur"] = { sing = "euro", plur = "euros", conde = "de-euros" }, ["libra"] = { sing = "libra", plur = "libras", conde = "de-libras" }, ["libras"] = { sing = "libra", plur = "libras", conde = "de-libras" }, ["gbp"] = { sing = "libra", plur = "libras", conde = "de-libras" }, ["yen"] = { sing = "yen", plur = "yenes", conde = "de-yenes" }, ["yenes"] = { sing = "yen", plur = "yenes", conde = "de-yenes" }, ["jpy"] = { sing = "yen", plur = "yenes", conde = "de-yenes" }, ["yuan"] = { sing = "yuan", plur = "yuanes", conde = "de-yuanes" }, ["yuanes"] = { sing = "yuan", plur = "yuanes", conde = "de-yuanes" }, ["cny"] = { sing = "yuan", plur = "yuanes", conde = "de-yuanes" }, ["won"] = { sing = "won", plur = "wons", conde = "de-wons" }, ["krw"] = { sing = "won", plur = "wons", conde = "de-wons" }, ["shekel"] = { sing = "séquel", plur = "séqueles", conde = "de-séqueles" }, ["ils"] = { sing = "séquel", plur = "séqueles", conde = "de-séqueles" }, ["rupia"] = { sing = "rupia", plur = "rupias", conde = "de-rupias" }, ["inr"] = { sing = "rupia", plur = "rupias", conde = "de-rupias" }, ["rublo"] = { sing = "rublo", plur = "rublos", conde = "de-rublos" }, ["rub"] = { sing = "rublo", plur = "rublos", conde = "de-rublos" }, ["lira"] = { sing = "lira", plur = "liras", conde = "de-liras" }, ["try"] = { sing = "lira", plur = "liras", conde = "de-liras" }, ["grivna"] = { sing = "grivna", plur = "grivnas", conde = "de-grivnas" }, ["uah"] = { sing = "grivna", plur = "grivnas", conde = "de-grivnas" }, ["€"] = { sing = "euro", plur = "euros", conde = "de-euros" }, ["£"] = { sing = "libra", plur = "libras", conde = "de-libras" }, ["¥"] = { sing = "yen", plur = "yenes", conde = "de-yenes" }, ["₩"] = { sing = "won", plur = "wons", conde = "de-wons" }, ["₪"] = { sing = "séquel", plur = "séqueles", conde = "de-séqueles" }, ["₹"] = { sing = "rupia", plur = "rupias", conde = "de-rupias" }, ["₽"] = { sing = "rublo", plur = "rublos", conde = "de-rublos" }, ["₺"] = { sing = "lira", plur = "liras", conde = "de-liras" }, ["₴"] = { sing = "grivna", plur = "grivnas", conde = "de-grivnas" }, ["¢"] = { sing = "centavo", plur = "centavos", conde = "de-centavos" }, ["centavo"] = { sing = "centavo", plur = "centavos", conde = "de-centavos" }, ["centavos"] = { sing = "centavo", plur = "centavos", conde = "de-centavos" }, } local spintent_currency_subunits_matrix = { ["pesos"] = { sing = "centavo", plur = "centavos" }, ["pesos-chilenos"] = nil, ["pesos-colombianos"] = nil, ["pesos-mexicanos"] = { sing = "centavo", plur = "centavos" }, ["dólares"] = { sing = "centavo", plur = "centavos" }, ["dólares-estadounidenses"] = { sing = "centavo", plur = "centavos" }, ["euros"] = { sing = "céntimo", plur = "céntimos" }, ["libras"] = { sing = "penique", plur = "peniques" }, ["yenes"] = nil, ["yuanes"] = { sing = "fen", plur = "fen" }, ["wons"] = nil, ["séqueles"] = { sing = "ágora", plur = "agorot" }, ["rupias"] = { sing = "paisa", plur = "paisas" }, ["rublos"] = { sing = "kopek", plur = "kopeks" }, ["liras"] = { sing = "kuruş", plur = "kuruş" }, ["grivnas"] = { sing = "kopek", plur = "kopeks" } } local spintent_currency_spoken_names = { ["$"] = "pesos", ["peso"] = "pesos", ["pesos"] = "pesos", ["clp"] = "pesos-chilenos", ["mxn"] = "pesos-mexicanos", ["dolar"] = "dólares-estadounidenses", ["dolares"] = "dólares-estadounidenses", ["usd"] = "dólares-estadounidenses", ["euro"] = "euros", ["euros"] = "euros", ["eur"] = "euros", ["libra"] = "libras", ["libras"] = "libras", ["gbp"] = "libras", ["yen"] = "yenes", ["yenes"] = "yenes", ["jpy"] = "yenes", ["yuan"] = "yuanes", ["yuanes"] = "yuanes", ["cny"] = "yuanes", ["centavo"] = "centavos", ["centavos"]= "centavos", ["won"] = "wons", ["krw"] = "wons", ["shekel"] = "séqueles", ["ils"] = "séqueles", ["rupia"] = "rupias", ["inr"] = "rupias", ["rublo"] = "rublos", ["rub"] = "rublos", ["lira"] = "liras", ["try"] = "liras", ["grivna"] = "grivnas", ["uah"] = "grivnas", ["€"] = "euros", ["£"] = "libras", ["¥"] = "yenes", ["¢"] = "centavos", ["₩"] = "wons", ["₪"] = "séqueles", ["₹"] = "rupias", ["₽"] = "rublos", ["₺"] = "liras", ["₴"] = "grivnas", } -- Clave de búsqueda de una moneda: sin espacios en los extremos, \$ igual -- que $, sin acentos y en minúsculas local spintent_money_accents = { ["á"] = "a", ["é"] = "e", ["í"] = "i", ["ó"] = "o", ["ú"] = "u", ["ü"] = "u", ["Á"] = "a", ["É"] = "e", ["Í"] = "i", ["Ó"] = "o", ["Ú"] = "u", ["Ü"] = "u", } local function spintent_money_key(str) local key = spintent_trim(str) key = s_gsub(key, "^\\%$", "$") key = s_gsub(key, "[\xC3][\x80-\xBF]", spintent_money_accents) return s_lower(key) end -- Códigos ISO 4217 que se imprimen como código cuando se escriben en mayúsculas local spintent_money_iso_codes = { clp = true, mxn = true, usd = true, eur = true, gbp = true, jpy = true, cny = true, krw = true, ils = true, inr = true, rub = true, ["try"] = true, uah = true, } -- Escritura válida de una moneda: sin mayúsculas (símbolos y alias) o un -- código ISO escrito todo en mayúsculas; cualquier otra mezcla no es válida local function spintent_money_valid_case(raw, clean) local trimmed = spintent_trim(raw) if not (s_match(trimmed, "%u") or s_match(trimmed, "\xC3[\x80-\x9E]")) then return true end return spintent_money_iso_codes[clean] and trimmed == s_upper(trimmed) end register_tex_cmd("luafun_spmoney_lookup_data", function(currency_name) local trimmed = spintent_trim(currency_name) local clean = spintent_money_key(currency_name) local resolved_symbol = spintent_internal_currencies[clean] or "$" local gram_entry = spintent_currency_grammatical_dict[clean] or spintent_currency_grammatical_dict[resolved_symbol] or { sing = "peso", plur = "pesos", conde = "de-pesos" } local position = "pre" if resolved_symbol == "€" or resolved_symbol == "¢" then position = "post" end if spintent_money_iso_codes[clean] and trimmed == s_upper(trimmed) then token_set_macro("l__spintent_spmoney_luaset_print_iso_str", "true") else token_set_macro("l__spintent_spmoney_luaset_print_iso_str", "false") end local sub_db = spintent_currency_subunits_matrix[gram_entry.plur] token_set_macro("l__spintent_spmoney_luaset_symbol_str", resolved_symbol) token_set_macro("l__spintent_spmoney_luaset_position_str", position) token_set_macro("l__spintent_spmoney_luaset_sing_str", gram_entry.sing) token_set_macro("l__spintent_spmoney_luaset_plur_str", gram_entry.plur) token_set_macro("l__spintent_spmoney_luaset_conde_str", gram_entry.conde) if sub_db then token_set_macro("l__spintent_spmoney_luaset_subsing_str", sub_db.sing) token_set_macro("l__spintent_spmoney_luaset_subplur_str", sub_db.plur) else token_set_macro("l__spintent_spmoney_luaset_subsing_str", "") token_set_macro("l__spintent_spmoney_luaset_subplur_str", "") end end, { "string" }) register_tex_cmd("luafun_spmoney_normalize_key", function(raw_input) local clean = spintent_money_key(raw_input) local resolved_symbol = spintent_internal_currencies[clean] local resolved_spoken = nil if resolved_symbol then resolved_spoken = spintent_currency_spoken_names[clean] or spintent_currency_spoken_names[resolved_symbol] else resolved_spoken = spintent_currency_spoken_names[clean] end if resolved_spoken and spintent_money_valid_case(raw_input, clean) then token_set_macro("l__spintent_spmoney_luaset_currency_arg_str", resolved_spoken) token_set_macro("l__spintent_spmoney_luaset_status_str", "found") else token_set_macro("l__spintent_spmoney_luaset_currency_arg_str", raw_input) token_set_macro("l__spintent_spmoney_luaset_status_str", "notfound") end end, { "string" }) register_tex_cmd("luafun_spmoney_prepare_input", function(raw_input) -- Dígitos, signos, espacios, separadores numéricos y espacios matemáticos local num_item = spintent_math_space + S"0123456789 \t\r\n+-.,{}:;" -- Extrae solo la moneda (borra la parte numérica) local curr_part = Cs(((num_item / "") + P(1))^0):match(raw_input) -- Extrae solo el número matemático puro (conserva los espacios matemáticos) local num_part = Cs((num_item + (P(1) / ""))^0):match(raw_input) token_set_macro("l__spintent_spmoney_extracted_curr_str", curr_part) token_set_macro("l__spintent_spmoney_extracted_num_tl", num_part) end, { "string" }) -- ABREVIATURAS Y ORDINALES (\spshort) local spintent_spshort_dict = { ["dr.a"] = { expand = "doctora", layout_type = "superscript", base = "Dr.", suffix = "a" }, ["dr.as"] = { expand = "doctoras", layout_type = "superscript", base = "Dr.", suffix = "as" }, ["prof.a"] = { expand = "profesora", layout_type = "superscript", base = "Prof.", suffix = "a" }, ["d.a"] = { expand = "doña", layout_type = "superscript", base = "D.", suffix = "a" }, ["m.a"] = { expand = "maría", layout_type = "superscript", base = "M.", suffix = "a" }, ["n.o"] = { expand = "número", layout_type = "superscript", base = "N.", suffix = "o" }, ["n.os"] = { expand = "números", layout_type = "superscript", base = "N.", suffix = "os" }, ["c.ia"] = { expand = "compañía", layout_type = "superscript", base = "C.", suffix = "ia" }, ["dr.ª"] = { expand = "doctora", layout_type = "superscript", base = "Dr.", suffix = "a" }, ["d.ª"] = { expand = "doña", layout_type = "superscript", base = "D.", suffix = "a" }, ["n.º"] = { expand = "número", layout_type = "superscript", base = "N.", suffix = "o" }, ["pág."] = { expand = "página", layout_type = "linear_regular", output = "pág." }, ["pag."] = { expand = "página", layout_type = "linear_regular", output = "pág." }, ["vol."] = { expand = "volumen", layout_type = "linear_regular", output = "vol." }, ["etc."] = { expand = "etcétera", layout_type = "linear_regular", output = "etc." }, ["et al."] = { expand = "y otros", layout_type = "linear_regular", output = "et\u{00A0}al." }, ["et. al."] = { expand = "y otros", layout_type = "linear_regular", output = "et\u{00A0}al." }, ["ibíd."] = { expand = "ibídem", layout_type = "linear_regular", output = "ibíd." }, ["ibid."] = { expand = "ibídem", layout_type = "linear_regular", output = "ibíd." }, ["op. cit."] = { expand = "obra citada", layout_type = "linear_regular", output = "op.\u{00A0}cit." }, ["op.cit."] = { expand = "obra citada", layout_type = "linear_regular", output = "op.\u{00A0}cit." }, ["loc. cit."] = { expand = "lugar citado", layout_type = "linear_regular", output = "loc.\u{00A0}cit." }, ["loc.cit."] = { expand = "lugar citado", layout_type = "linear_regular", output = "loc.\u{00A0}cit." }, ["v. gr."] = { expand = "verbigracia", layout_type = "linear_regular", output = "v.\u{00A0}gr." }, ["v.gr."] = { expand = "verbigracia", layout_type = "linear_regular", output = "v.\u{00A0}gr." }, ["i. e."] = { expand = "esto es", layout_type = "linear_regular", output = "i.\u{00A0}e." }, ["i.e."] = { expand = "esto es", layout_type = "linear_regular", output = "i.\u{00A0}e." }, ["e. g."] = { expand = "por ejemplo", layout_type = "linear_regular", output = "e.\u{00A0}g." }, ["e.g."] = { expand = "por ejemplo", layout_type = "linear_regular", output = "e.\u{00A0}g." }, ["p. ej."] = { expand = "por ejemplo", layout_type = "linear_regular", output = "p.\u{00A0}ej." }, ["p.ej."] = { expand = "por ejemplo", layout_type = "linear_regular", output = "p.\u{00A0}ej." }, ["sr."] = { expand = "señor", layout_type = "linear_regular", output = "Sr." }, ["sra."] = { expand = "señora", layout_type = "linear_regular", output = "Sra." }, ["srta."] = { expand = "señorita", layout_type = "linear_regular", output = "Srta." }, ["dr."] = { expand = "doctor", layout_type = "linear_regular", output = "Dr." }, ["dra."] = { expand = "doctora", layout_type = "linear_regular", output = "Dra." }, ["dres."] = { expand = "doctores", layout_type = "linear_regular", output = "Dres." }, ["dras."] = { expand = "doctoras", layout_type = "linear_regular", output = "Dras." }, ["prof."] = { expand = "profesor", layout_type = "linear_regular", output = "Prof." }, ["profa."] = { expand = "profesora", layout_type = "linear_regular", output = "Profa." }, ["profs."] = { expand = "profesores", layout_type = "linear_regular", output = "Profs." }, ["ing."] = { expand = "ingeniero", layout_type = "linear_regular", output = "Ing." }, ["ings."] = { expand = "ingenieros", layout_type = "linear_regular", output = "Ings." }, ["lic."] = { expand = "licenciado", layout_type = "linear_regular", output = "Lic." }, ["v. b."] = { expand = "visto bueno", layout_type = "linear_regular", output = "V.\u{00A0}B." }, ["v.b."] = { expand = "visto bueno", layout_type = "linear_regular", output = "V.\u{00A0}B." }, ["s. a."] = { expand = "sociedad anónima", layout_type = "linear_caps", output = "S.\u{00A0}A." }, ["s.a."] = { expand = "sociedad anónima", layout_type = "linear_caps", output = "S.\u{00A0}A." }, ["ee. uu."] = { expand = "estados unidos", layout_type = "linear_caps", output = "EE.\u{00A0}UU." }, ["ee.uu."] = { expand = "estados unidos", layout_type = "linear_caps", output = "EE.\u{00A0}UU." }, ["dd. hh."] = { expand = "derechos humanos", layout_type = "linear_caps", output = "DD.\u{00A0}HH." }, ["dd.hh."] = { expand = "derechos humanos", layout_type = "linear_caps", output = "DD.\u{00A0}HH." }, ["jj. oo."] = { expand = "juegos olímpicos", layout_type = "linear_caps", output = "JJ.\u{00A0}OO." }, ["jj.oo."] = { expand = "juegos olímpicos", layout_type = "linear_caps", output = "JJ.\u{00A0}OO." }, ["ff. aa."] = { expand = "fuerzas armadas", layout_type = "linear_caps", output = "FF.\u{00A0}AA." }, ["ff.aa."] = { expand = "fuerzas armadas", layout_type = "linear_caps", output = "FF.\u{00A0}AA." }, ["rr. ee."] = { expand = "relaciones exteriores", layout_type = "linear_caps", output = "RR.\u{00A0}EE." }, ["rr.ee."] = { expand = "relaciones exteriores", layout_type = "linear_caps", output = "RR.\u{00A0}EE." }, ["cc. aa."] = { expand = "comunidades autónomas", layout_type = "linear_caps", output = "CC.\u{00A0}AA." }, ["cc.aa."] = { expand = "comunidades autónomas", layout_type = "linear_caps", output = "CC.\u{00A0}AA." }, ["p. d."] = { expand = "posdata", layout_type = "linear_caps", output = "P.\u{00A0}D." }, ["p.d."] = { expand = "posdata", layout_type = "linear_caps", output = "P.\u{00A0}D." }, -- Solo por compatibilidad (no deben llevar punto acorde a la RAE) ["d. n. i."] = { expand = "documento nacional de identidad", layout_type = "linear_caps", output = "D.\u{00A0}N.\u{00A0}I." }, ["d.n.i."] = { expand = "documento nacional de identidad", layout_type = "linear_caps", output = "D.\u{00A0}N.\u{00A0}I." }, ["r. u. t."] = { expand = "rol único tributario", layout_type = "linear_caps", output = "R.\u{00A0}U.\u{00A0}T." }, ["r.u.t."] = { expand = "rol único tributario", layout_type = "linear_caps", output = "R.\u{00A0}U.\u{00A0}T." }, ["r. u. n."] = { expand = "rol único nacional", layout_type = "linear_caps", output = "R.\u{00A0}U.\u{00A0}N." }, ["r.u.n."] = { expand = "rol único nacional", layout_type = "linear_caps", output = "R.\u{00A0}U.\u{00A0}N." }, ["a. c."] = { expand = "antes de Cristo", layout_type = "linear_mixed", output = "a.\u{00A0}C." }, ["a.c."] = { expand = "antes de Cristo", layout_type = "linear_mixed", output = "a.\u{00A0}C." }, ["d. c."] = { expand = "después de Cristo", layout_type = "linear_mixed", output = "d.\u{00A0}C." }, ["d.c."] = { expand = "después de Cristo", layout_type = "linear_mixed", output = "d.\u{00A0}C." }, ["dni"] = { expand = "documento nacional de identidad", layout_type = "small_caps_pure", output = "DNI" }, ["run"] = { expand = "rol único nacional", layout_type = "small_caps_pure", output = "RUN" }, ["rut"] = { expand = "rol único tributario", layout_type = "small_caps_pure", output = "RUT" }, ["onu"] = { expand = "organización de las naciones unidas", layout_type = "small_caps_pure", output = "ONU" }, ["rae"] = { expand = "real academia española", layout_type = "small_caps_pure", output = "RAE" }, ["ong"] = { expand = "organización no gubernamental", layout_type = "small_caps_pure", output = "ONG" }, ["ongs"] = { expand = "organizaciones no gubernamentales", layout_type = "small_caps_pure", output = "ONGs" }, ["urss"] = { expand = "unión de repúblicas socialistas soviéticas", layout_type = "small_caps_pure", output = "URSS" }, ["oea"] = { expand = "organización de los estados americanos", layout_type = "small_caps_pure", output = "OEA" }, ["oms"] = { expand = "organización mundial de la salud", layout_type = "small_caps_pure", output = "OMS" }, ["fmi"] = { expand = "fondo monetario internacional", layout_type = "small_caps_pure", output = "FMI" }, ["bid"] = { expand = "banco interamericano de desarrollo", layout_type = "small_caps_pure", output = "BID" }, ["otan"] = { expand = "organización del tratado del atlántico norte", layout_type = "small_caps_pure", output = "OTAN" }, ["unam"] = { expand = "universidad nacional autónoma de méxico", layout_type = "small_caps_pure", output = "UNAM" }, ["pib"] = { expand = "producto interno bruto", layout_type = "small_caps_pure", output = "PIB" }, ["ue"] = { expand = "unión europea", layout_type = "small_caps_pure", output = "UE" }, ["eau"] = { expand = "emiratos árabes unidos", layout_type = "small_caps_pure", output = "EAU" }, ["ru"] = { expand = "reino unido", layout_type = "small_caps_pure", output = "RU" }, ["rca"] = { expand = "república centroafricana", layout_type = "small_caps_pure", output = "RCA" }, ["rdc"] = { expand = "república democrática del congo", layout_type = "small_caps_pure", output = "RDC" }, ["rfa"] = { expand = "república federal alemana", layout_type = "small_caps_pure", output = "RFA" }, ["rda"] = { expand = "república democrática alemana", layout_type = "small_caps_pure", output = "RDA" }, ["unicef"] = { expand = "fondo de las naciones unidas para la infancia", layout_type = "acronym_long", output = "Unicef" }, ["unesco"] = { expand = "organización de las naciones unidas para la educación, la ciencia y la cultura", layout_type = "acronym_long", output = "Unesco" }, ["mercosur"] = { expand = "mercado común del sur", layout_type = "acronym_long", output = "Mercosur" }, ["cepal"] = { expand = "comisión económica para américa latina", layout_type = "acronym_long", output = "Cepal" }, ["celac"] = { expand = "comunidad de estados latinoamericanos", layout_type = "acronym_long", output = "Celac" }, ["unasur"] = { expand = "unión de naciones suramericanas", layout_type = "acronym_long", output = "Unasur" }, ["acnur"] = { expand = "alto comisionado de las naciones unidas", layout_type = "acronym_long", output = "Acnur" }, ["mineduc"] = { expand = "ministerio de educación", layout_type = "acronym_long", output = "Mineduc" }, ["senadis"] = { expand = "servicio nacional de la discapacidad", layout_type = "acronym_long", output = "Senadis" }, ["conicet"] = { expand = "consejo nacional de investigaciones", layout_type = "acronym_long", output = "Conicet" } } local spintent_spshort_ord_suffixes = { ["a"] = "ª", ["o"] = "º", ["er"] = "er", ["os"] = "os", ["as"] = "as" } local spintent_ord_units = { [1] = "primer", [2] = "segund", [3] = "tercer", [4] = "cuart", [5] = "quint", [6] = "sext", [7] = "séptim", [8] = "octav", [9] = "noven" } local spintent_ord_tens = { [1] = "décim", [2] = "vigésim", [3] = "trigésim", [4] = "cuadragésim", [5] = "quincuagésim",[6] = "sexagésim", [7] = "septuagésim", [8] = "octogésim", [9] = "nonagésim" } local function spintent_get_semantic_ordinal(val, suffix) if val < 1 or val > 99 then return val .. (spintent_spshort_ord_suffixes[suffix] or suffix) end local end_char = (suffix == "a" or suffix == "as") and "a" or "o" local plural = (suffix == "as" or suffix == "os") and "s" or "" if suffix == "er" then end_char = "o" plural = "" end local ten_val = math_floor(val / 10) local unit_val = val % 10 if val == 11 and suffix ~= "er" then return "undécim" .. end_char .. plural end if val == 12 then return "duodécim" .. end_char .. plural end local result = "" if ten_val > 0 then result = spintent_ord_tens[ten_val] .. end_char .. plural end if unit_val > 0 then local unit_str = spintent_ord_units[unit_val] if unit_val == 1 and suffix ~= "er" then unit_str = "primer" end if unit_val == 3 and suffix ~= "er" then unit_str = "tercer" end local unit_end = (suffix == "er") and "" or (end_char .. plural) local full_unit = unit_str .. unit_end if ten_val > 0 then result = result .. " " .. full_unit else result = full_unit end end return result end local spintent_spshort_digits = spintent_digit^1 local spintent_spshort_dot = P(".") local spintent_spshort_raw_suffix = C(P("er") + P("os") + P("as") + P("a") + P("o")) + (P("ª") / "a") + (P("º") / "o") local spintent_spshort_illegal_suff = C((R("az") + R("AZ"))^1) local spintent_spshort_ord_grammar = P({ "Entry", Entry = #spintent_digit * spintent_spshort_V("Main"), Main = C(spintent_spshort_digits) * spintent_spshort_dot * (spintent_spshort_raw_suffix + spintent_spshort_illegal_suff * Cg(Cc(true))) }) local function spintent_spshort_execute_analysis(raw_input) local clean_input = spintent_normalize_key(raw_input) local dict_match = spintent_spshort_dict[clean_input] if not dict_match then if s_sub(clean_input, -1) == "." then dict_match = spintent_spshort_dict[s_sub(clean_input, 1, -2)] else dict_match = spintent_spshort_dict[clean_input .. "."] end end if dict_match then token_set_macro("l__spintent_spshort_luaset_status_str", "success") token_set_macro("l__spintent_spshort_luaset_layout_str", dict_match.layout_type) token_set_macro("l__spintent_spshort_luaset_expand_str", dict_match.expand) if dict_match.layout_type == "superscript" then token_set_macro("l__spintent_spshort_luaset_base_str", dict_match.base) token_set_macro("l__spintent_spshort_luaset_suffix_str", dict_match.suffix) else token_set_macro("l__spintent_spshort_luaset_output_str", dict_match.output) end return end local ordinal_input = clean_input if s_sub(ordinal_input, -1) == "." and not s_match(ordinal_input, "%d+%.%a+$") and not s_match(ordinal_input, "^%d+%.") then ordinal_input = s_sub(ordinal_input, 1, -2) end local num_part, suffix_part, is_illegal = spintent_spshort_ord_grammar:match(ordinal_input) if num_part and not is_illegal and spintent_spshort_ord_suffixes[suffix_part] then if suffix_part == "er" then local val = tonumber(num_part) local last_digit = val % 10 if last_digit ~= 1 and last_digit ~= 3 then token_set_macro("l__spintent_spshort_luaset_status_str", "error") return end end local semantic_read = spintent_get_semantic_ordinal(tonumber(num_part), suffix_part) token_set_macro("l__spintent_spshort_luaset_status_str", "success") token_set_macro("l__spintent_spshort_luaset_layout_str", "superscript") token_set_macro("l__spintent_spshort_luaset_expand_str", semantic_read) token_set_macro("l__spintent_spshort_luaset_base_str", num_part .. ".") token_set_macro("l__spintent_spshort_luaset_suffix_str", spintent_spshort_ord_suffixes[suffix_part]) return end if s_match(raw_input, "^%a+$") then token_set_macro("l__spintent_spshort_luaset_status_str", "fallback") token_set_macro("l__spintent_spshort_luaset_layout_str", "none") token_set_macro("l__spintent_spshort_luaset_expand_str", raw_input) token_set_macro("l__spintent_spshort_luaset_output_str", raw_input) else token_set_macro("l__spintent_spshort_luaset_status_str", "error") end end register_tex_cmd("luafun_spshort_process", function(raw_input) spintent_spshort_execute_analysis(raw_input) end, { "string" }) -- SIGLOS Y NÚMEROS ROMANOS (\spsiglo) local spintent_arab_to_roman_map = { {1000, "m"}, {900, "cm"}, {500, "d"}, {400, "cd"}, {100, "c"}, {90, "xc"}, {50, "l"}, {40, "xl"}, {10, "x"}, {9, "ix"}, {5, "v"}, {4, "iv"}, {1, "i"} } local spintent_roman_to_arab_byte_map = { [105] = 1, -- 'i' [118] = 5, -- 'v' [120] = 10, -- 'x' [108] = 50, -- 'l' [99] = 100, -- 'c' [100] = 500, -- 'd' [109] = 1000 -- 'm' } local function spintent_arabic_to_roman(num) local result = {} for i = 1, #spintent_arab_to_roman_map do local pair = spintent_arab_to_roman_map[i] while num >= pair[1] do result[#result + 1] = pair[2] num = num - pair[1] end end return t_concat(result) end local function spintent_roman_to_arabic(str_roman) local total = 0 local i = 1 local len = #str_roman while i <= len do local v1 = spintent_roman_to_arab_byte_map[s_byte(str_roman, i)] or 0 if i + 1 <= len then local v2 = spintent_roman_to_arab_byte_map[s_byte(str_roman, i + 1)] or 0 if v1 < v2 then total = total + (v2 - v1) i = i + 2 else total = total + v1 i = i + 1 end else total = total + v1 i = i + 1 end end return total end register_tex_cmd("luafun_spsiglo_parse", function(raw_siglo_input) local clean = s_lower(s_gsub(raw_siglo_input, "%s+", "")) local arabic_val = nil local roman_val = nil local is_error = false if s_match(clean, "^%d+$") then arabic_val = tonumber(clean) if arabic_val > 0 and arabic_val < 4000 then roman_val = spintent_arabic_to_roman(arabic_val) else is_error = true end elseif s_match(clean, "^[ivxlcdm]+$") and clean ~= "" then roman_val = clean arabic_val = spintent_roman_to_arabic(clean) if arabic_val >= 4000 or spintent_arabic_to_roman(arabic_val) ~= clean then is_error = true end else is_error = true end if is_error then token_set_macro("l__spintent_spsiglo_luaset_error_str", "true") token_set_macro("l__spintent_spsiglo_luaset_arabic_str", "") token_set_macro("l__spintent_spsiglo_luaset_roman_min_str", "") else token_set_macro("l__spintent_spsiglo_luaset_error_str", "false") token_set_macro("l__spintent_spsiglo_luaset_arabic_str", tostring(arabic_val)) token_set_macro("l__spintent_spsiglo_luaset_roman_min_str", roman_val) end end, { "string" }) -- FECHA Y HORA (\spdate, \sptime) local spintent_date_sep = C(S"/-") -- Patrón neutro: 3 partes numéricas divididas por 2 separadores local spintent_date_pattern = Ct( Cg(spintent_num_chunk, "p1") * Cg(spintent_date_sep, "sep1") * Cg(spintent_num_chunk, "p2") * Cg(spintent_date_sep, "sep2") * Cg(spintent_num_chunk, "p3") * P(-1) ) -- Función auxiliar: Validar que el día y mes existan (¡incluyendo bisiestos!) local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} local function spintent_is_valid_date(d, m, y) if m < 1 or m > 12 then return false end if d < 1 then return false end -- Ajuste para febrero en años bisiestos if m == 2 then local is_leap = (y % 4 == 0 and y % 100 ~= 0) or (y % 400 == 0) if is_leap then return d <= 29 end end return d <= days_in_month[m] end register_tex_cmd("luafun_spdate_parse", function(raw_date_input) if not raw_date_input then return end local clean_str = s_gsub(raw_date_input, "%s+", "") local result = spintent_date_pattern:match(clean_str) -- Si no encaja el patrón, o si el usuario mezcla separadores (ej. 2024-01/02) -> error if not result or result.sep1 ~= result.sep2 then token_set_macro("l__spintent_spdate_luaset_error_str", "true") return end local p1, p2, p3 = result.p1, result.p2, result.p3 local sep = result.sep1 local year, month, day -- Como spintent_num_chunk elimina espacios, las longitudes son exactas local p1_len = #p1 local p3_len = #p3 -- Detección del Año if p1_len == 4 and p3_len <= 2 then year, month, day = p1, p2, p3 elseif p3_len == 4 and p1_len <= 2 then day, month, year = p1, p2, p3 else -- Si ninguno tiene 4 dígitos (ej. 12-10-81) o ambos lo tienen, hay ambigüedad token_set_macro("l__spintent_spdate_luaset_error_str", "true") return end local num_y, num_m, num_d = tonumber(year), tonumber(month), tonumber(day) -- Validación de calendario real if not (num_y and num_m and num_d) or not spintent_is_valid_date(num_d, num_m, num_y) then token_set_macro("l__spintent_spdate_luaset_error_str", "true") return end -- Reglas de renderizado visual local output_str if p1_len == 4 and sep == "/" then -- Único caso a corregir: YYYY/MM/DD se invierte a DD/MM/YYYY output_str = day .. "/" .. month .. "/" .. year else -- Para el resto, se devuelve EXACTAMENTE el mismo formato que ingresó output_str = p1 .. sep .. p2 .. sep .. p3 end -- Si sobrevivió a todo, pasamos las variables limpias a TeX token_set_macro("l__spintent_spdate_luaset_error_str", "false") token_set_macro("l__spintent_spdate_luaset_day_str", day) token_set_macro("l__spintent_spdate_luaset_month_str", month) token_set_macro("l__spintent_spdate_luaset_year_str", year) token_set_macro("l__spintent_spdate_luaset_output_str", output_str) end, { "string" }) local spintent_time_pattern = Ct( Cg(spintent_num_chunk, "h") * P":" * Cg(spintent_num_chunk, "m") * (P":" * Cg(spintent_num_chunk, "s"))^-1 * P(-1) ) register_tex_cmd("luafun_sptime_parse", function(raw_time_input) if not raw_time_input then return end local clean_str = s_gsub(raw_time_input, "%s+", "") local result = spintent_time_pattern:match(clean_str) if not result then token_set_macro("l__spintent_sptime_luaset_error_str", "true") return end local h = tonumber(result.h) local m = tonumber(result.m) local s = result.s and tonumber(result.s) or nil if not h or not m or h >= 24 or m >= 60 or (s and s >= 60) then token_set_macro("l__spintent_sptime_luaset_error_str", "true") return end token_set_macro("l__spintent_sptime_luaset_error_str", "false") token_set_macro("l__spintent_sptime_luaset_base_str", result.h .. ":" .. result.m) -- Solo es "fracción especial" si la hora es < 13 y los minutos son 15 o 30 if h < 13 and (m == 15 or m == 30) then token_set_macro("l__spintent_sptime_luaset_is_fraction_str", "true") else token_set_macro("l__spintent_sptime_luaset_is_fraction_str", "false") end if result.s then token_set_macro("l__spintent_sptime_luaset_has_seconds_str", "true") token_set_macro("l__spintent_sptime_luaset_seconds_str", result.s) else token_set_macro("l__spintent_sptime_luaset_has_seconds_str", "false") token_set_macro("l__spintent_sptime_luaset_seconds_str", "") end end, { "string" }) -- MATEMÁTICAS: MCM Y MCD (\spmcm, \spmcd) local function spintent_gcd_algorithm(val_a, val_b) while val_b ~= 0 do val_a, val_b = val_b, val_a % val_b end return val_a end local function spintent_lcm_algorithm(val_a, val_b) if val_a == 0 or val_b == 0 then return 0 end return math_floor((val_a * val_b) / spintent_gcd_algorithm(val_a, val_b)) end local function spintent_execute_mcm_mcd_result(raw_csv_list, tl_out, operation_fn) local numbers = {} for item in s_gmatch(raw_csv_list, "([^,]+)") do local clean_item = spintent_trim(item) local result = spintent_number_pattern:match(clean_item) or {} local r_int = result.integer local r_sign = result.sign local r_dec = result.decimal local r_per = result.period local r_ext = result.extra local es_natural = r_int and (not r_sign or r_sign == "") and (not r_dec or r_dec == "") and (not r_per or r_per == "") and (not r_ext or s_gsub(r_ext, "%s+", "") == "") if es_natural then numbers[#numbers + 1] = tonumber(r_int) else return end end if #numbers == 0 then return end local final_result = numbers[1] for i = 2, #numbers do final_result = operation_fn(final_result, numbers[i]) end token_set_macro(tl_out, s_format("%d", final_result)) end register_tex_cmd("luafun_calculate_mcd", function(raw_csv_list) spintent_execute_mcm_mcd_result(raw_csv_list, "l__spintent_spmcd_luaset_mcd_value_tl", spintent_gcd_algorithm) end, { "string" }) register_tex_cmd("luafun_calculate_mcm", function(raw_csv_list) spintent_execute_mcm_mcd_result(raw_csv_list, "l__spintent_spmcm_luaset_mcm_value_tl", spintent_lcm_algorithm) end, { "string" }) -- Clasifica CADA argumento de la lista (sin abortar en el primero no -- numerico, a diferencia de spintent_execute_mcm_mcd_result) register_tex_cmd("luafun_mc_classify_args", function(raw_csv_list) local types = {} local all_numeric = true for item in s_gmatch(raw_csv_list, "([^,]+)") do local clean_item = spintent_trim(item) local result = spintent_number_pattern:match(clean_item) or {} local es_natural = result.integer and (not result.sign or result.sign == "") and (not result.decimal or result.decimal == "") and (not result.period or result.period == "") and (not result.extra or s_gsub(result.extra, "%s+", "") == "") if es_natural then types[#types + 1] = "number" else types[#types + 1] = "other" all_numeric = false end end token_set_macro("l__spintent_mc_luaset_types_clist", table.concat(types, ",")) token_set_macro("l__spintent_mc_luaset_all_numeric_str", all_numeric and "true" or "false") end, { "string" }) -- Clasifica un solo argumento (mismo chequeo que -- spintent_execute_mcm_mcd_result) -- para \spnD/\spnM. Un cero se -- marca como error: el argumento debe ser un entero positivo. register_tex_cmd("luafun_spnmd_classify", function(raw_arg) local clean_item = spintent_trim(raw_arg) local result = spintent_number_pattern:match(clean_item) or {} local es_natural = result.integer and (not result.sign or result.sign == "") and (not result.decimal or result.decimal == "") and (not result.period or result.period == "") and (not result.extra or s_gsub(result.extra, "%s+", "") == "") local es_cero = es_natural and s_match(result.integer, "^0+$") ~= nil token_set_macro("l__spintent_spnmd_luaset_is_number_str", (es_natural and not es_cero) and "true" or "false") token_set_macro("l__spintent_spnM_spnD_luaset_error_str", es_cero and "true" or "false") end, { "string" }) -- SISTEMA SEXAGESIMAL (\spang) -- Los minutos y los segundos son opcionales: "45", "45:30" y "45:30:15" -- son válidos; una entrada vacía no. local spintent_angle_sexag_pattern = Ct( -P(-1) * Cg(spintent_num_chunk^-1, "a") * (P ":" * Cg(spintent_num_chunk^-1, "b") * (P ":" * Cg(spintent_num_chunk^-1, "c"))^-1)^-1 * P(-1) ) register_tex_cmd("luafun_spang_parse", function(raw_sexag_str) raw_sexag_str = s_gsub(raw_sexag_str, "%s+", "") local result = spintent_angle_sexag_pattern:match(raw_sexag_str) if not result then token_set_macro("l__spintent_spang_luaset_error_str", "true") return end token_set_macro("l__spintent_spang_luaset_error_str", "false") token_set_macro("l__spintent_spang_luaset_grado_str", result.a or "") token_set_macro("l__spintent_spang_luaset_minuto_str", result.b or "") token_set_macro("l__spintent_spang_luaset_segundo_str", result.c or "") end, { "string" }) -- MATEMÁTICAS: MÚLTIPLOS Y DIVISORES (\spnM, \spnD) local function spintent_calculate_multiples(n_val, count) local results = {} for i = 1, count do results[#results + 1] = tostring(n_val * i) end return t_concat(results, ", ") end local function spintent_calculate_divisors(n_val, limit) local divisors = {} -- Algoritmo optimizado de raíz cuadrada para divisores rápidos for i = 1, math_floor(math_sqrt(n_val)) do if n_val % i == 0 then divisors[#divisors + 1] = i -- Si el cociente es diferente, agregarlo también local quotient = n_val // i if i ~= quotient then divisors[#divisors + 1] = quotient end end end -- Ordenar los divisores de menor a mayor t_sort(divisors) local results = {} local actual_count = #divisors local count_to_show = (limit > 0) and math_min(limit, actual_count) or actual_count for i = 1, count_to_show do results[#results + 1] = tostring(divisors[i]) end local is_truncated = (count_to_show < actual_count) and "true" or "false" return t_concat(results, ", "), is_truncated end local function spintent_execute_spnM_spnD_result(raw_n, raw_limit, is_multiple) token_set_macro("l__spintent_spnM_spnD_luaset_error_str", "false") local num_n = tonumber(raw_n) if not num_n or num_n < 1 then token_set_macro("l__spintent_spnM_spnD_luaset_error_str", "true") return end local clean_limit = spintent_normalize_key(raw_limit) local num_limit = tonumber(clean_limit) or 0 if clean_limit == "all" or clean_limit == "" then num_limit = 0 end if is_multiple then if num_limit <= 0 then num_limit = 5 end -- 5 múltiplos por defecto local result_str = spintent_calculate_multiples(num_n, num_limit) token_set_macro("l__spintent_spnM_luaset_result_tl", result_str) token_set_macro("l__spintent_spnM_luaset_is_truncated_str", "true") -- Siempre infinitos else local result_str, is_truncated = spintent_calculate_divisors(num_n, num_limit) token_set_macro("l__spintent_spnD_luaset_result_tl", result_str) token_set_macro("l__spintent_spnD_luaset_is_truncated_str", is_truncated) end end register_tex_cmd("luafun_calcular_nM", function(raw_n, raw_limit) spintent_execute_spnM_spnD_result(raw_n, raw_limit, true) end, { "string", "string" }) register_tex_cmd("luafun_calcular_nD", function(raw_n, raw_limit) spintent_execute_spnM_spnD_result(raw_n, raw_limit, false) end, { "string", "string" }) -- Diccionario simbolo/comando -> lectura para \spnewread/\spread local spintent_spread_dict = {} register_tex_cmd("luafun_spread_new", function(sym, reading) if spintent_spread_dict[sym] then token_set_macro("l__spintent_spread_luaset_status_str", "duplicate") else spintent_spread_dict[sym] = reading token_set_macro("l__spintent_spread_luaset_status_str", "ok") end end, { "string", "string" }) register_tex_cmd("luafun_spread_lookup", function(sym) local reading = spintent_spread_dict[sym] if reading then token_set_macro("l__spintent_spread_luaset_status_str", "found") token_set_macro("l__spintent_spread_luaset_reading_str", reading) else token_set_macro("l__spintent_spread_luaset_status_str", "notfound") end end, { "string" }) -- NÚMEROS MIXTOS (\spmQ); ver https://tex.stackexchange.com/q/764828 local spintent_font_cache = {} local spintent_measuring = false -- Tabla constante: nunca cambia entre llamadas, se construye una sola vez -- en vez de en cada invocación de \spmQ. local spintent_mathstyle_names = { [0] = "display", [1] = "display", [2] = "text", [3] = "text", [4] = "script", [5] = "script", [6] = "scriptscript", [7] = "scriptscript", } local function spintent_get_scaled_font(id, factor) local key = tostring(id) .. ":" .. tostring(factor) if spintent_font_cache[key] then return spintent_font_cache[key] end local old_font = font.getfont(id) if not old_font or not old_font.specification then return id end local specification = old_font.specification specification.size = math_floor(old_font.size * factor) local new_id = luaotfload.define_font(specification) spintent_font_cache[key] = new_id return new_id end local function spintent_build_char_noads(str) local head, tail for i = 1, #str do local kernel = node.new("math_char") kernel.char = str:byte(i) kernel.fam = 0 local n = node.new("noad") n.subtype = 0 n.nucleus = kernel if not head then head = n tail = n else tail.next = n tail = n end end return head end local function spintent_build_submlist_noad(str) local mlist = node.new("sub_mlist") mlist.head = spintent_build_char_noads(str) local noad = node.new("noad") noad.subtype = 0 noad.nucleus = mlist return noad end local function spintent_medir_frac(dt, cmd) spintent_measuring = true tex.runtoks(function() tex.sprint(document_cctab, string.format([[\setbox0\hbox{$\%sstyle\%s{0}{0}$}]], dt, cmd) ) end) spintent_measuring = false local h = tex.box[0].height local dp = tex.box[0].depth tex.box[0] = nil return h, dp end register_tex_cmd("luafun_spmQ_scale_int", function(entero, frac_cmd) local display_type = (frac_cmd == "dfrac") and "display" or (spintent_mathstyle_names[tonumber(tex.mathstyle)] or "text") -- 1. Medir fracción local frac_height, frac_depth = spintent_medir_frac(display_type, frac_cmd) local ht_frac_real = frac_height + frac_depth -- 2. Construir y medir la hlist del entero UNA sola vez, antes de -- escalarla. local h_esc = node.mlist_to_hlist(spintent_build_submlist_noad(entero), display_type, false) local ht_entero = h_esc.height + h_esc.depth -- 3. Factor local factor = (ht_entero == 0) and 1.0 or (ht_frac_real / ht_entero) -- 4. Escalado + ancho + glyphs físicos en una sola pasada node.direct local ancho = 0 local glyph_nodes = {} for n in d_traverse(d_todirect(h_esc.list)) do local ch = d_getfield(n, "char") if ch >= 48 and ch <= 57 then local new_id = spintent_get_scaled_font(d_getfield(n, "font"), factor) d_setfield(n, "font", new_id) local f = font.getfont(new_id) if f and f.characters then local c = f.characters[ch] if c then d_setfield(n, "width", c.width) d_setfield(n, "height", c.height) d_setfield(n, "depth", c.depth) end end ancho = ancho + d_getfield(n, "width") glyph_nodes[#glyph_nodes + 1] = d_tonode(n) else ancho = ancho + d_getfield(n, "width") end end h_esc.width = ancho h_esc.height = frac_height h_esc.depth = frac_depth h_esc.shift = math_floor(frac_depth + 0.5) -- 5. sub_box con mathml_filter usando node.direct local sub = d_new("sub_box") d_setfield(sub, "head", d_todirect(h_esc)) local noad = d_new("noad") d_setfield(noad, "subtype", 0) d_setfield(noad, "nucleus", sub) -- 6. Devolvemos a userdata para interactuar con las properties de luamml y node.write local sub_user = d_tonode(sub) local noad_user = d_tonode(noad) local mml_mn = { [0] = 'mn', entero, [':nodes'] = glyph_nodes, [':actual'] = entero, } local properties = node.get_properties_table() local p = properties[sub_user] or {} p.mathml_filter = function(_mml, _core) return mml_mn, mml_mn end properties[sub_user] = p -- 7. Inyectar el noad con luamml deshabilitado temporalmente. -- node.write aquí solo deposita el noad en la lista matemática -- actual; el callback de luamml se dispara al CERRAR el grupo -- matemático (al final de $ o \]). Por tanto deshabilitar el -- flag aquí NO suprime el procesamiento del noad — luamml lo -- verá igual cuando procese la lista completa. -- Lo que sí suprime es cualquier callback intermedio espurio -- que pudiera dispararse durante el node.write mismo. node.write(noad_user) end, {"string", "string"}) -- GEOMETRÍA PLANA local spintent_geo_cont_sub = C((1 - P"}")^1) local spintent_geo_primas = C(P"'"^1) local spintent_geo_punto = Ct( C(R"AZ") * ( P"_{" * spintent_geo_cont_sub * P"}" * Cc"sub" + spintent_geo_primas * Cc"prima" + Cc"" * Cc"" ) ) -- No usa spintent_opt_spaces: su "/ ''" (captura de sustitución), -- usada dentro de un Ct externo, no se descarta — se inserta como -- elemento string "" suelto en la tabla, duplicando el conteo real -- de puntos. local spintent_geo_ws = S" \t\r\n" local spintent_geo_ws_opt = spintent_geo_ws^0 local spintent_geo_sep = spintent_geo_ws_opt * P"," * spintent_geo_ws_opt local spintent_geo_grammar = Ct( spintent_geo_punto * (spintent_geo_sep * spintent_geo_punto)^0 * P(-1) ) -- Sin anclaje final: se re-chequea la cardinalidad exacta después -- (\spPoly, \spAfig, \spPfig). local spintent_geo_poly_grammar = Ct( spintent_geo_punto * (spintent_geo_sep * spintent_geo_punto)^0 ) -- Puntos concatenados sin separador (p.ej. "AB", "ABC", "A_{1}B_{2}"). -- Misma forma de tabla de salida que spintent_geo_grammar/poly_grammar, -- así que spintent_geo_build_intent/build_visual la consumen sin -- cambios. Usada como alternativa a la lista separada por comas en -- \sprecta, \sprayo, \spside, \spmside, \sparc, \spmarc, \spPoly, -- \spAfig, \spPfig. local spintent_geo_puntos_concat = Ct( spintent_geo_punto^1 ) * P(-1) -- Gramática para nombre propio de recta (mayúsculas Y minúsculas). -- IMPORTANTE: se prueba SIEMPRE antes que spintent_geo_puntos_concat -- en luafun_geo_parse_and_set, para que una sola letra sin coma -- ("A") siga interpretándose como nombre de recta en \sprecta (no -- como lista de 1 punto). local spintent_geo_nombre_recta = Ct( C(R"AZ" + R"az") * ( P"_{" * spintent_geo_cont_sub * P"}" * Cc"sub" + spintent_geo_primas * Cc"prima" + Cc"" * Cc"" ) ) * P(-1) local spintent_geo_num_a_texto = { ["0"] = "cero", ["1"] = "uno", ["2"] = "dos", ["3"] = "tres", ["4"] = "cuatro",["5"] = "cinco", ["6"] = "seis", ["7"] = "siete", ["8"] = "ocho", ["9"] = "nueve", } local spintent_geo_prima_prefijo = { "prima", "doble-prima", "triple-prima", "cuádruple-prima" } -- Normaliza Unicode (′″‴) y comandos (^{\prime}, ^{\dprime}, ...) a -- comillas rectas, único formato que spintent_geo_primas reconoce. local function spintent_geo_normalize_primes(str) str = s_gsub(str, "‴", "'''") str = s_gsub(str, "″", "''") str = s_gsub(str, "′", "'") -- Los espacios que TeX deja tras un comando (\prime }) no cuentan. str = s_gsub(str, "%^%{([^%}]*)%}", function(inner) local compact = (s_gsub(inner, "%s+", "")) if compact == "\\prime" then return "'" end if compact == "\\prime\\prime" then return "''" end if compact == "\\prime\\prime\\prime" then return "'''" end if compact == "\\dprime" then return "''" end if compact == "\\trprime" then return "'''" end return "^{" .. inner .. "}" end) return str end -- cmd == "" (silent en modo school) envuelve el resultado en -- guiones bajos ("_...-") para que MathCAT lo trate como literal por -- default-text y no aplique sus reglas de mayúsculas a un suelto. local function spintent_geo_build_intent(cmd, puntos) local partes = {} if cmd ~= "" then partes[#partes + 1] = cmd end for _, pt in ipairs(puntos) do local letra, mod, tipo = pt[1], pt[2], pt[3] partes[#partes + 1] = letra if tipo == "sub" then partes[#partes + 1] = "sub" partes[#partes + 1] = spintent_geo_num_a_texto[mod] or mod elseif tipo == "prima" then local n = #mod partes[#partes + 1] = spintent_geo_prima_prefijo[n] or (n .. "-prima") end end local resultado = t_concat(partes, "-") if cmd == "" then resultado = "_" .. resultado .. "-" end return resultado end -- Constructores "puros": solo el cuerpo (puntos convertidos a texto), -- sin concepto ni envoltorio "_...-" — usados por las funciones que ya -- no reciben "cmd"/"read_arg" desde expl3 (se antepone/envuelve del -- lado expl3, después de la llamada). local function spintent_geo_build_body(puntos) return spintent_geo_build_intent("", puntos):match("^_(.-)-$") end local function spintent_geo_build_body_nombre(nombre) return spintent_geo_build_body({ nombre }) end -- Un solo punto {letra, contenido, tipo}: usada tanto para el texto -- visual (TeX) como para reconstruir el crudo sin combinar de -- "points_clist" -- el mismo formato sirve para las dos cosas, ya que -- para "prima" el contenido ya trae las comillas ("'", "''", ...) -- listas para pegar, sin necesidad de reconstruirlas caracter por -- caracter. local function spintent_geo_build_visual_nombre(nombre) local letra, mod, tipo = nombre[1], nombre[2], nombre[3] if tipo == "sub" then return letra .. "_{" .. mod .. "}" elseif tipo == "prima" then return letra .. mod else return letra end end local function spintent_geo_build_visual(puntos) local partes = {} for _, pt in ipairs(puntos) do partes[#partes + 1] = spintent_geo_build_visual_nombre(pt) end return t_concat(partes) end -- Arma "l__spintent_geo_luaset_points_clist" (la lista de puntos SIN -- combinar, separada por comas) a partir de una tabla 'puntos', para -- que expl3 pueda referenciar cada uno por separado (__spintent_mathmlarg_point:nnn). local function spintent_geo_set_points_clist(puntos) local raw_parts = {} for i, punto in ipairs(puntos) do raw_parts[i] = spintent_geo_build_visual_nombre(punto) end token_set_macro("l__spintent_geo_luaset_points_clist", t_concat(raw_parts, ",")) end -- Punto: \spPoint local spintent_geo_single_point_grammar = spintent_geo_punto * P(-1) register_tex_cmd("luafun_geo_point_parse_and_set", function(csv) csv = spintent_trim(csv) csv = spintent_geo_normalize_primes(csv) local punto = spintent_geo_single_point_grammar:match(csv) if not punto then token_set_macro("l__spintent_geo_luaset_error_str", "true") token_set_macro("l__spintent_geo_luaset_intent_str", "") token_set_macro("l__spintent_geo_luaset_print_tl", "") token_set_macro("l__spintent_geo_luaset_letra_str", "") token_set_macro("l__spintent_geo_luaset_sub_str", "") token_set_macro("l__spintent_geo_luaset_tipo_str", "") return end token_set_macro("l__spintent_geo_luaset_error_str", "false") token_set_macro("l__spintent_geo_luaset_intent_str", spintent_geo_build_body_nombre(punto)) token_set_macro("l__spintent_geo_luaset_print_tl", spintent_geo_build_visual_nombre(punto)) -- letra/sub/tipo por separado: así \__spintent_mathmlarg_point:nnn puede -- silenciar el subíndice VISUAL aparte de la base; pegado en una sola -- cadena quedaría sin proteger y la cola compartida podría -- asignárselo a otro comando (\spPoint{A_{1}}\spPoint{B}). token_set_macro("l__spintent_geo_luaset_letra_str", punto[1]) token_set_macro("l__spintent_geo_luaset_sub_str", punto[2] or "") token_set_macro("l__spintent_geo_luaset_tipo_str", punto[3] or "") end, { "string" }) -- Recta, rayo, segmento y arco -- Orden de intentos en la rama sin coma: nombre de recta primero -- (preserva la semántica de \sprecta con una sola letra), luego -- puntos concatenados sin separador, luego error. local function spintent_geo_set_error_recta() token_set_macro("l__spintent_geo_luaset_error_str", "true") token_set_macro("l__spintent_geo_luaset_intent_str", "") token_set_macro("l__spintent_geo_luaset_print_tl", "") token_set_macro("l__spintent_geo_luaset_is_name_str", "false") token_set_macro("l__spintent_geo_luaset_points_clist", "") token_set_macro("l__spintent_geo_luaset_letra_str", "") token_set_macro("l__spintent_geo_luaset_sub_str", "") token_set_macro("l__spintent_geo_luaset_tipo_str", "") end register_tex_cmd("luafun_geo_parse_and_set", function(csv) csv = spintent_trim(csv) csv = spintent_geo_normalize_primes(csv) local tiene_coma = s_match(csv, ",") if tiene_coma then local puntos = spintent_geo_grammar:match(csv) if not puntos or #puntos == 0 then spintent_geo_set_error_recta() return end token_set_macro("l__spintent_geo_luaset_error_str", "false") token_set_macro("l__spintent_geo_luaset_is_name_str", "false") token_set_macro("l__spintent_geo_luaset_intent_str", spintent_geo_build_body(puntos)) token_set_macro("l__spintent_geo_luaset_print_tl", spintent_geo_build_visual(puntos)) spintent_geo_set_points_clist(puntos) return end local nombre = spintent_geo_nombre_recta:match(csv) if nombre then token_set_macro("l__spintent_geo_luaset_error_str", "false") token_set_macro("l__spintent_geo_luaset_is_name_str", "true") token_set_macro("l__spintent_geo_luaset_intent_str", spintent_geo_build_body_nombre(nombre)) token_set_macro("l__spintent_geo_luaset_print_tl", spintent_geo_build_visual_nombre(nombre)) token_set_macro("l__spintent_geo_luaset_points_clist", "") -- nombre = 1 solo punto, no aplica -- letra/sub/tipo por separado -- mismo motivo que en -- luafun_geo_point_parse_and_set: permite silenciar el -- subíndice VISUAL aparte de la base. token_set_macro("l__spintent_geo_luaset_letra_str", nombre[1]) token_set_macro("l__spintent_geo_luaset_sub_str", nombre[2] or "") token_set_macro("l__spintent_geo_luaset_tipo_str", nombre[3] or "") return end local puntos = spintent_geo_puntos_concat:match(csv) if puntos and #puntos > 0 then token_set_macro("l__spintent_geo_luaset_error_str", "false") token_set_macro("l__spintent_geo_luaset_is_name_str", "false") token_set_macro("l__spintent_geo_luaset_intent_str", spintent_geo_build_body(puntos)) token_set_macro("l__spintent_geo_luaset_print_tl", spintent_geo_build_visual(puntos)) spintent_geo_set_points_clist(puntos) return end spintent_geo_set_error_recta() end, { "string" }) -- Ángulo: \spangle, \spmangle local spintent_geo_angulo_un_punto = spintent_geo_punto * P(-1) local function spintent_geo_build_body_angulo(csv) local tiene_coma = s_match(csv, ",") if tiene_coma then local puntos = spintent_geo_grammar:match(csv) if not puntos or #puntos == 0 then return nil, nil, nil end local cuerpo = spintent_geo_build_body(puntos) local visual = spintent_geo_build_visual(puntos) spintent_geo_set_points_clist(puntos) return cuerpo, visual, "tres-puntos" end local un_punto = spintent_geo_angulo_un_punto:match(csv) if un_punto then local base = spintent_geo_build_body_nombre(un_punto) local cuerpo = "en-" .. base local visual = spintent_geo_build_visual_nombre(un_punto) spintent_geo_set_points_clist({ un_punto }) return cuerpo, visual, "un-punto" end -- Concatenado sin coma ("AOB"): reutiliza la misma -- gramática ya compartida con el resto de la familia geo. local puntos_concat = spintent_geo_puntos_concat:match(csv) if puntos_concat and #puntos_concat >= 2 then local cuerpo = spintent_geo_build_body(puntos_concat) local visual = spintent_geo_build_visual(puntos_concat) spintent_geo_set_points_clist(puntos_concat) return cuerpo, visual, "tres-puntos" end -- Si llegó hasta aquí con un "_" en el texto, es un intento -- fallido de notación de punto (p.ej. "A_1" sin llaves) — Lua no -- sabe leer nada con "_" salvo el patrón letra+"_{...}" ya -- descartado arriba. Se rechaza en vez de aceptarlo como nombre. if s_match(csv, "_") then return nil, nil, nil end local nombre = spintent_trim(csv) if nombre == "" then return nil, nil, nil end local es_numero = s_match(nombre, "^%d+$") ~= nil local cuerpo = es_numero and nombre or spintent_sanitize_for_screen_reader(nombre) token_set_macro("l__spintent_geo_luaset_points_clist", "") token_set_macro("l__spintent_geo_luaset_nombre_es_num_str", es_numero and "true" or "false") return cuerpo, nombre, "nombre" end register_tex_cmd("luafun_geo_angulo_parse_and_set", function(csv) csv = spintent_trim(csv) csv = spintent_geo_normalize_primes(csv) local cuerpo, visual, caso = spintent_geo_build_body_angulo(csv) if not visual then token_set_macro("l__spintent_geo_luaset_error_str", "true") token_set_macro("l__spintent_geo_luaset_intent_str", "") token_set_macro("l__spintent_geo_luaset_print_tl", "") token_set_macro("l__spintent_geo_luaset_angulo_case_str", "") token_set_macro("l__spintent_geo_luaset_points_clist", "") return end token_set_macro("l__spintent_geo_luaset_error_str", "false") token_set_macro("l__spintent_geo_luaset_angulo_case_str", caso) token_set_macro("l__spintent_geo_luaset_intent_str", cuerpo) token_set_macro("l__spintent_geo_luaset_print_tl", visual) end, { "string" }) -- Polígono: \spPoly, \spAfig, \spPfig local spintent_geo_poly_npts = { [3] = { cmd = "triángulo", sym = "triangle" }, [4] = { cmd = "cuadrado", sym = "square" }, [5] = { cmd = "pentágono", sym = "" }, [6] = { cmd = "hexágono", sym = "" }, [7] = { cmd = "heptágono", sym = "" }, [8] = { cmd = "octágono", sym = "" }, } -- Parsea el argumento de \spPoly: lista de vértices separados por -- coma, o concatenados sin separador (mínimo 3). Establece: -- l__spintent_geo_luaset_error_str -- "true"/"false" -- l__spintent_geo_luaset_poly_sym_str -- "triangle"/"square"/"" (según cantidad de vértices) -- l__spintent_geo_luaset_concept_str -- nombre del polígono ("triángulo", "cuadrado", ...) -- l__spintent_geo_luaset_print_tl -- vértices concatenados, visual TeX ("ABC") -- l__spintent_geo_luaset_points_clist -- vértices sin combinar, separados por coma ("A,B,C") register_tex_cmd("luafun_geo_poly_parse_and_set", function(csv) csv = spintent_trim(csv) csv = spintent_geo_normalize_primes(csv) local tiene_coma = s_match(csv, ",") local puntos if tiene_coma then puntos = spintent_geo_poly_grammar:match(csv) else puntos = spintent_geo_puntos_concat:match(csv) end if not puntos or #puntos < 3 then token_set_macro("l__spintent_geo_luaset_error_str", "true") token_set_macro("l__spintent_geo_luaset_print_tl", "") token_set_macro("l__spintent_geo_luaset_poly_sym_str", "") token_set_macro("l__spintent_geo_luaset_points_clist","") return end local info = spintent_geo_poly_npts[#puntos] local cmd_base = info and info.cmd or "polígono" local sym = info and info.sym or "" token_set_macro("l__spintent_geo_luaset_error_str", "false") token_set_macro("l__spintent_geo_luaset_poly_sym_str", sym) token_set_macro("l__spintent_geo_luaset_concept_str", cmd_base) token_set_macro("l__spintent_geo_luaset_print_tl", spintent_geo_build_visual(puntos)) spintent_geo_set_points_clist(puntos) end, { "string" }) -- Tabla de letras válidas para el argumento único de \spAfig/\spPfig -- (p.ej. "F_{1}", "P", "B_{2}"). Cada letra lleva su palabra y el -- conector gramatical correcto: sustantivos usan "de-la"/"del" según -- género; adjetivos usan conector vacío (van pegados directamente -- tras "área"/"perímetro"). local spintent_geo_fig_letras = { F = { palabra = "figura", conector = "de-la" }, P = { palabra = "polígono", conector = "del" }, B = { palabra = "basal", conector = "" }, L = { palabra = "lateral", conector = "" }, T = { palabra = "total", conector = "" }, } -- Letra de la tabla, con subíndice opcional (dígitos u otra letra). local spintent_geo_fig_letra_sola = Ct( C(S"FPBLT") * ( P"_{" * spintent_geo_cont_sub * P"}" * Cc"sub" + Cc"" * Cc"" ) ) * P(-1) -- Dígito(s) sueltos, o una sola letra suelta (sin prefijo de tabla) — -- se convierten directamente en el subíndice de \mathcal{A} (o del -- símbolo, si print-sym!=none). local spintent_geo_fig_sub_simple = (C(R"09"^1) + C(R"az")) * P(-1) -- Devuelve: ok, visual, palabra, conector, sub_spoken. local function spintent_geo_fig_parse_arg(csv) local con_letra = spintent_geo_fig_letra_sola:match(csv) if con_letra then local letra, sub, tipo = con_letra[1], con_letra[2], con_letra[3] local info = spintent_geo_fig_letras[letra] if tipo == "sub" then local visual = letra .. "_{" .. sub .. "}" local sub_spoken = spintent_geo_num_a_texto[sub] or sub return true, visual, info.palabra, info.conector, sub_spoken else return true, letra, info.palabra, info.conector, "" end end local solo = spintent_geo_fig_sub_simple:match(csv) if solo then local sub_spoken = spintent_geo_num_a_texto[solo] or solo return true, solo, "", "", sub_spoken end return false end -- Arma y establece las variables de salida para el caso de vértices -- reales (lista de al menos 3 puntos), sin importar si vinieron -- separados por comas o concatenados sin separador. Siempre usa el -- nombre inferido por cardinalidad; expl3 arma el intent completo por -- su cuenta (via __spintent_mathmlarg_point:nnn, punto por punto). local function spintent_geo_fig_set_vertices(op, puntos) local info = spintent_geo_poly_npts[#puntos] local cmd_fig = info and info.cmd or "polígono" local sym = (info and info.sym) or "" local concepto = op .. "-del-" .. cmd_fig token_set_macro("l__spintent_geo_luaset_error_str", "false") token_set_macro("l__spintent_geo_luaset_poly_sym_str", sym) token_set_macro("l__spintent_geo_luaset_concept_str", concepto) token_set_macro("l__spintent_geo_luaset_print_tl", spintent_geo_build_visual(puntos)) token_set_macro("l__spintent_geo_luaset_letra_word_str", "") token_set_macro("l__spintent_geo_luaset_letra_conector_str","") token_set_macro("l__spintent_geo_luaset_sub_spoken_str", "") spintent_geo_set_points_clist(puntos) end -- Reset compartido por los tres casos que no traen vértices reales -- (argumento vacío, error, y letra sola de la tabla F/P/B/L/T): ningún -- campo de vértices aplica, solo cambian error/concept/print/letra. local function spintent_geo_fig_reset(error_val, concept_val, print_val, palabra, conector, sub_spoken) token_set_macro("l__spintent_geo_luaset_error_str", error_val) token_set_macro("l__spintent_geo_luaset_poly_sym_str", "") token_set_macro("l__spintent_geo_luaset_concept_str", concept_val) token_set_macro("l__spintent_geo_luaset_print_tl", print_val) token_set_macro("l__spintent_geo_luaset_letra_word_str", palabra) token_set_macro("l__spintent_geo_luaset_letra_conector_str", conector) token_set_macro("l__spintent_geo_luaset_sub_spoken_str", sub_spoken) token_set_macro("l__spintent_geo_luaset_points_clist", "") end -- l__spintent_geo_luaset_concept_str lleva solo el nombre del -- concepto ("área-del-triángulo", sin vértices) — a diferencia del -- resto de la familia, aquí solo se conoce tras el parseo (por la -- cardinalidad), no antes. -- -- Orden de intentos sin coma: gramática de argumento único (número/ -- letra/tabla) primero; si falla, puntos concatenados como lista de -- vértices (mínimo 3); si también falla, error. local function spintent_geo_fig_parse_and_set(op, csv) csv = spintent_trim(csv) csv = spintent_geo_normalize_primes(csv) if csv == "" then spintent_geo_fig_reset("false", op, "", "", "", "") return end local tiene_coma = s_match(csv, ",") if tiene_coma then local puntos = spintent_geo_poly_grammar:match(csv) if not puntos or #puntos < 3 then spintent_geo_fig_reset("true", "", "", "", "", "") return end spintent_geo_fig_set_vertices(op, puntos) return end local ok, visual, palabra, conector, sub_spoken = spintent_geo_fig_parse_arg(csv) if ok then spintent_geo_fig_reset("false", op, visual, palabra, conector, sub_spoken) return end local puntos = spintent_geo_puntos_concat:match(csv) if puntos and #puntos >= 3 then spintent_geo_fig_set_vertices(op, puntos) return end spintent_geo_fig_reset("true", "", "", "", "", "") end register_tex_cmd("luafun_geo_afig_parse_and_set", function(csv) spintent_geo_fig_parse_and_set("área", csv) end, { "string" }) register_tex_cmd("luafun_geo_pfig_parse_and_set", function(csv) spintent_geo_fig_parse_and_set("perímetro", csv) end, { "string" }) -- Círculo y circunferencia: \spcirc -- -- El radio se clasifica por forma (nunca por llave): número (entero o -- decimal, con coma o punto), medida (número+unidad, delegada a -- spnum/spunit desde expl3), letra suelta, o segmento de dos letras -- (side/mside según radio-sty, decidido en expl3). Lua solo clasifica -- y extrae; no arma intent de medida (eso es trabajo ya existente de -- \spnum/\spunit en expl3). local spintent_geo_single_point = spintent_geo_punto * P(-1) -- "number": entero o decimal. "2.5 cm" no coincide (tiene unidad) y -- sigue siendo "measure". local spintent_geo_radio_number = C(R"09"^1 * (S".," * R"09"^1)^-1) * P(-1) local spintent_geo_radio_label = C(R"az" + R"AZ") * P(-1) local spintent_geo_radio_segment = Ct( spintent_geo_punto * spintent_geo_punto ) * P(-1) -- "measure": empieza con dígitos y tiene algo más después (unidad), -- ya sea con espacio o pegado. No se valida la unidad aquí — eso -- queda para spunit en expl3. local spintent_geo_radio_measure = C(R"09"^1) * S" "^0 * (1 - P(-1))^1 * P(-1) register_tex_cmd("luafun_geo_circ_parse_and_set", function(csv) csv = spintent_trim(csv) csv = spintent_geo_normalize_primes(csv) local coma_pos = s_match(csv, "()%,") local centro_str = coma_pos and csv:sub(1, coma_pos - 1) or csv local radio_str = coma_pos and spintent_trim(csv:sub(coma_pos + 1)) or "" local centro = spintent_geo_single_point:match(spintent_trim(centro_str)) if not centro then token_set_macro("l__spintent_geo_luaset_error_str", "true") token_set_macro("l__spintent_geo_luaset_print_tl", "") token_set_macro("l__spintent_geo_luaset_circ_radio_type_str", "") token_set_macro("l__spintent_geo_luaset_circ_radio_raw_str", "") token_set_macro("l__spintent_geo_luaset_points_clist", "") return end token_set_macro("l__spintent_geo_luaset_error_str", "false") token_set_macro("l__spintent_geo_luaset_print_tl", spintent_geo_build_visual_nombre(centro)) spintent_geo_set_points_clist({ centro }) if radio_str == "" then token_set_macro("l__spintent_geo_luaset_circ_radio_type_str", "") token_set_macro("l__spintent_geo_luaset_circ_radio_raw_str", "") return end local seg_puntos = spintent_geo_radio_segment:match(radio_str) if seg_puntos then token_set_macro("l__spintent_geo_luaset_circ_radio_type_str", "segment") token_set_macro("l__spintent_geo_luaset_circ_radio_raw_str", spintent_geo_build_visual(seg_puntos)) return end local numero = spintent_geo_radio_number:match(radio_str) if numero then token_set_macro("l__spintent_geo_luaset_circ_radio_type_str", "number") token_set_macro("l__spintent_geo_luaset_circ_radio_raw_str", numero) return end local etiqueta = spintent_geo_radio_label:match(radio_str) if etiqueta then token_set_macro("l__spintent_geo_luaset_circ_radio_type_str", "label") token_set_macro("l__spintent_geo_luaset_circ_radio_raw_str", etiqueta) return end if spintent_geo_radio_measure:match(radio_str) then token_set_macro("l__spintent_geo_luaset_circ_radio_type_str", "measure") token_set_macro("l__spintent_geo_luaset_circ_radio_raw_str", radio_str) return end token_set_macro("l__spintent_geo_luaset_error_str", "true") token_set_macro("l__spintent_geo_luaset_circ_radio_type_str", "") token_set_macro("l__spintent_geo_luaset_circ_radio_raw_str", "") end, { "string" }) -- Coordenadas -- \spcoord (modo school): separa "#1,#2" o "#1;#2", clasifica cada -- mitad (numero/crudo), y distingue frac/dfrac/ninguno para el -- tamano del parentesis -- \mathstyle se lee directo en expl3, -- no hace falta reportarlo desde aca. -- -- Reutiliza spintent_number_pattern (la gramatica LPeg de -- luafun_clean_split_and_set) solo como clasificador -- sin llamar a -- esa funcion completa ni disparar sus propios token_set_macro de -- unidades/millones/exponente, que no aplican aca. -- Altura "alta" de un valor: busca \dfrac, \frac o \spmQ en CUALQUIER -- posicion (como \spinterval), pero ignora lo que quede dentro de un -- subindice o superindice (x_{\frac{1}{2}} no agranda el parentesis). -- Devuelve "dfrac", "frac" o "none". local function spintent_spcoord_tall(value) local v = value v = v:gsub("[_^]%s*%b{}", "") v = v:gsub("[_^]%s*\\%a+", "") v = v:gsub("[_^]%s*[^%s{\\]", "") if s_match(v, "\\dfrac%f[%A]") then return "dfrac" end if s_match(v, "\\spmQ%s*%[[^%]]*use%-dfrac%s*=%s*true") then return "dfrac" end if s_match(v, "\\frac%f[%A]") or s_match(v, "\\spmQ%f[%A]") then return "frac" end return "none" end local function spintent_spcoord_classify_base(part) local sign, rest = "", part local first = s_sub(part, 1, 1) if first == "+" or first == "-" then sign = first rest = spintent_trim(s_sub(part, 2)) end local tall = spintent_spcoord_tall(rest) if tall ~= "none" then return sign, "raw", rest, tall end local num_result = spintent_number_pattern:match(rest) if num_result and (num_result.extra or "") == "" and ((num_result.integer or "") ~= "" or (num_result.fraction or "") ~= "") then return sign, "number", rest, "none" end return sign, "raw", rest, "none" end -- Un valor "raw" que es un solo nodo con subíndice, potencia o raíz -- (x_{1}, a^{2}, \sqrt{2}) pierde el arg de \MathMLarg en luamml: se -- avisa con una quinta salida para que TeX le agregue un segundo hijo. local function spintent_spcoord_classify(part) local sign, type_, value, has_frac = spintent_spcoord_classify_base(part) local comp = "false" if type_ == "raw" and not s_match(value, "^\\d?frac%f[%A]") and (s_match(value, "[_^]") or s_match(value, "\\sqrt")) then comp = "true" end return sign, type_, value, has_frac, comp end -- profundidad final de llaves y posiciones de ";" y "," a profundidad 0 local function spintent_interval_scan(inner) local depth, semis, commas = 0, {}, {} local i, n = 1, #inner while i <= n do local c = s_sub(inner, i, i) if c == "\\" then i = i + 2 else if c == "{" then depth = depth + 1 elseif c == "}" then depth = depth - 1 elseif depth == 0 then if c == ";" then semis[#semis + 1] = i elseif c == "," then commas[#commas + 1] = i end end i = i + 1 end end return depth, semis, commas end local function spintent_spcoord_set_error() token_set_macro("l__spintent_spcoord_luaset_error_str", "true") token_set_macro("l__spintent_spcoord_luaset_sep_str", "") token_set_macro("l__spintent_spcoord_luaset_x_sign_str", "") token_set_macro("l__spintent_spcoord_luaset_x_type_str", "") token_set_macro("l__spintent_spcoord_luaset_x_value_str", "") token_set_macro("l__spintent_spcoord_luaset_x_has_frac_str", "") token_set_macro("l__spintent_spcoord_luaset_x_composite_str", "") token_set_macro("l__spintent_spcoord_luaset_y_sign_str", "") token_set_macro("l__spintent_spcoord_luaset_y_type_str", "") token_set_macro("l__spintent_spcoord_luaset_y_value_str", "") token_set_macro("l__spintent_spcoord_luaset_y_has_frac_str", "") token_set_macro("l__spintent_spcoord_luaset_y_composite_str", "") end register_tex_cmd("luafun_spcoord_parse_and_set", function(raw_content) raw_content = spintent_trim(raw_content) -- el separador es un ";" o, si no hay, una unica "," a profundidad -- de llaves 0 (a_{1,2}, b es valido); con ";" las comas son decimales local depth, semis, commas = spintent_interval_scan(raw_content) local pos, sep_char if depth ~= 0 then spintent_spcoord_set_error() return elseif #semis > 0 then if #semis > 1 then spintent_spcoord_set_error() return end pos, sep_char = semis[1], ";" elseif #commas == 1 then pos, sep_char = commas[1], "," else spintent_spcoord_set_error() return end local x_raw = spintent_trim(s_sub(raw_content, 1, pos - 1)) local y_raw = spintent_trim(s_sub(raw_content, pos + 1)) if x_raw == "" or y_raw == "" then spintent_spcoord_set_error() return end local x_sign, x_type, x_value, x_has_frac, x_comp = spintent_spcoord_classify(x_raw) local y_sign, y_type, y_value, y_has_frac, y_comp = spintent_spcoord_classify(y_raw) token_set_macro("l__spintent_spcoord_luaset_error_str", "false") token_set_macro("l__spintent_spcoord_luaset_sep_str", sep_char) token_set_macro("l__spintent_spcoord_luaset_x_sign_str", x_sign) token_set_macro("l__spintent_spcoord_luaset_x_type_str", x_type) token_set_macro("l__spintent_spcoord_luaset_x_value_str", x_value) token_set_macro("l__spintent_spcoord_luaset_x_has_frac_str", x_has_frac) token_set_macro("l__spintent_spcoord_luaset_x_composite_str", x_comp) token_set_macro("l__spintent_spcoord_luaset_y_sign_str", y_sign) token_set_macro("l__spintent_spcoord_luaset_y_type_str", y_type) token_set_macro("l__spintent_spcoord_luaset_y_value_str", y_value) token_set_macro("l__spintent_spcoord_luaset_y_has_frac_str", y_has_frac) token_set_macro("l__spintent_spcoord_luaset_y_composite_str", y_comp) end, { "string" }) -- Intervalos -- \spinterval: delimitador ( [ ] a la izquierda y ) ] [ a la derecha; el -- separador es un ";" o, si no hay, una única "," a profundidad de llaves 0. local spintent_interval_left_open = { ["("] = true, ["["] = false, ["]"] = true } local spintent_interval_right_open = { [")"] = true, ["]"] = false, ["["] = true } local spintent_interval_keys_end = { "sign", "type", "value", "inf", "tall", "composite", "integer", "short", "braced" } local spintent_interval_keys_all = { "left_sym", "right_sym", "type", "sep", "tall" } local function spintent_interval_set_error(code) token_set_macro("l__spintent_spinterval_luaset_error_str", "true") token_set_macro("l__spintent_spinterval_luaset_error_code_str", code) for _, k in ipairs(spintent_interval_keys_all) do token_set_macro("l__spintent_spinterval_luaset_" .. k .. "_str", "") end for _, p in ipairs({ "a", "b" }) do for _, k in ipairs(spintent_interval_keys_end) do token_set_macro("l__spintent_spinterval_luaset_" .. p .. "_" .. k .. "_str", "") end end end local function spintent_interval_infinity(part) local compact = string.gsub(part, "%s+", "") if compact == "\\infty" or compact == "+\\infty" then return "pos" elseif compact == "-\\infty" then return "neg" end return "none" end local function spintent_interval_tall(value) if s_match(value, "\\dfrac") then return "dfrac" elseif s_match(value, "\\frac") then return "frac" end return "none" end register_tex_cmd("luafun_spinterval_parse_and_set", function(raw_content) raw_content = spintent_trim(raw_content) local left = s_sub(raw_content, 1, 1) local right = s_sub(raw_content, -1) local left_open, right_open = spintent_interval_left_open[left], spintent_interval_right_open[right] if #raw_content < 2 or left_open == nil or right_open == nil then spintent_interval_set_error("delim") return end local inner = spintent_trim(s_sub(raw_content, 2, -2)) if inner == "" then spintent_interval_set_error("vacio") return end local depth, semis, commas = spintent_interval_scan(inner) if depth ~= 0 then spintent_interval_set_error("llaves") return end local pos, sep_char if #semis > 0 then if #semis > 1 then spintent_interval_set_error("ambiguo") return end pos, sep_char = semis[1], ";" elseif #commas == 1 then pos, sep_char = commas[1], "," elseif #commas == 0 then spintent_interval_set_error("sep") return else spintent_interval_set_error("ambiguo") return end local a_raw = spintent_trim(s_sub(inner, 1, pos - 1)) local b_raw = spintent_trim(s_sub(inner, pos + 1)) if a_raw == "" or b_raw == "" then spintent_interval_set_error("vacio") return end local a_inf = spintent_interval_infinity(a_raw) local b_inf = spintent_interval_infinity(b_raw) if a_inf == "pos" or b_inf == "neg" then spintent_interval_set_error("inf-orden") return end if (a_inf == "neg" and not left_open) or (b_inf == "pos" and not right_open) then spintent_interval_set_error("inf-cerrado") return end local a_sign, a_type, a_value, _, a_comp = spintent_spcoord_classify(a_raw) local b_sign, b_type, b_value, _, b_comp = spintent_spcoord_classify(b_raw) if a_inf ~= "none" then a_type = "infinity" end if b_inf ~= "none" then b_type = "infinity" end -- is_integer: solo dígitos; is_short: además a lo más tres dígitos local function is_integer(v) return string.find(v, "^%d+$") and "true" or "false" end local function is_short(v) return string.find(v, "^%d%d?%d?$") and "true" or "false" end local a_tall = spintent_interval_tall(a_value) local b_tall = spintent_interval_tall(b_value) local tall = "none" if a_tall == "dfrac" or b_tall == "dfrac" then tall = "dfrac" elseif a_tall == "frac" or b_tall == "frac" then tall = "frac" end local kind if left_open and right_open then kind = "open" elseif (not left_open) and (not right_open) then kind = "closed" elseif left_open then kind = "open-closed" else kind = "closed-open" end token_set_macro("l__spintent_spinterval_luaset_error_str", "false") token_set_macro("l__spintent_spinterval_luaset_error_code_str", "") token_set_macro("l__spintent_spinterval_luaset_left_sym_str", left) token_set_macro("l__spintent_spinterval_luaset_right_sym_str", right) token_set_macro("l__spintent_spinterval_luaset_type_str", kind) token_set_macro("l__spintent_spinterval_luaset_sep_str", sep_char) token_set_macro("l__spintent_spinterval_luaset_tall_str", tall) token_set_macro("l__spintent_spinterval_luaset_a_sign_str", a_sign) token_set_macro("l__spintent_spinterval_luaset_a_type_str", a_type) token_set_macro("l__spintent_spinterval_luaset_a_value_str", a_value) token_set_macro("l__spintent_spinterval_luaset_a_inf_str", a_inf) token_set_macro("l__spintent_spinterval_luaset_a_tall_str", a_tall) token_set_macro("l__spintent_spinterval_luaset_a_composite_str", a_comp) token_set_macro("l__spintent_spinterval_luaset_a_integer_str", is_integer(a_value)) token_set_macro("l__spintent_spinterval_luaset_a_short_str", is_short(a_value)) token_set_macro("l__spintent_spinterval_luaset_a_braced_str", (s_gsub(a_value, ",", "{,}"))) token_set_macro("l__spintent_spinterval_luaset_b_sign_str", b_sign) token_set_macro("l__spintent_spinterval_luaset_b_type_str", b_type) token_set_macro("l__spintent_spinterval_luaset_b_value_str", b_value) token_set_macro("l__spintent_spinterval_luaset_b_inf_str", b_inf) token_set_macro("l__spintent_spinterval_luaset_b_tall_str", b_tall) token_set_macro("l__spintent_spinterval_luaset_b_composite_str", b_comp) token_set_macro("l__spintent_spinterval_luaset_b_integer_str", is_integer(b_value)) token_set_macro("l__spintent_spinterval_luaset_b_short_str", is_short(b_value)) token_set_macro("l__spintent_spinterval_luaset_b_braced_str", (s_gsub(b_value, ",", "{,}"))) end, { "string" }) -- \spProb: separa A \cap B, A \cup B o A \mid B (también A | B). -- Solo cuenta el nivel superior: lo que está entre llaves, paréntesis -- o corchetes (también \left...\right) no se examina. local spintent_prob_words = { cap = "cap", cup = "cup", mid = "mid", vert = "mid" } local function spintent_prob_set_error() token_set_macro("l__spintent_spProb_luaset_error_str", "true") token_set_macro("l__spintent_spProb_luaset_op_str", "") token_set_macro("l__spintent_spProb_luaset_a_str", "") token_set_macro("l__spintent_spProb_luaset_b_str", "") end register_tex_cmd("luafun_spProb_parse_and_set", function(raw_content) raw_content = spintent_trim(raw_content) local depth, ops = 0, {} local i, n = 1, #raw_content while i <= n do local c = s_sub(raw_content, i, i) if c == "\\" then local word = s_match(raw_content, "^\\(%a+)", i) if word == "left" or word == "right" then local j = i + 1 + #word while s_sub(raw_content, j, j) == " " do j = j + 1 end local d = s_sub(raw_content, j, j) if word == "left" and (d == "(" or d == "[") then depth = depth + 1 elseif word == "right" and (d == ")" or d == "]") then depth = depth - 1 end i = j + 1 elseif word and depth == 0 and spintent_prob_words[word] then ops[#ops + 1] = { i, 1 + #word, spintent_prob_words[word] } i = i + 1 + #word elseif word then i = i + 1 + #word else local d = s_sub(raw_content, i + 1, i + 1) if d == "{" then depth = depth + 1 elseif d == "}" then depth = depth - 1 end i = i + 2 end else if c == "{" or c == "(" or c == "[" then depth = depth + 1 elseif c == "}" or c == ")" or c == "]" then depth = depth - 1 elseif c == "|" and depth == 0 then ops[#ops + 1] = { i, 1, "mid" } end if depth < 0 then spintent_prob_set_error() return end i = i + 1 end end if depth ~= 0 or raw_content == "" or #ops > 1 then spintent_prob_set_error() return end local a_raw, b_raw, op = raw_content, "", "none" if #ops == 1 then op = ops[1][3] a_raw = spintent_trim(s_sub(raw_content, 1, ops[1][1] - 1)) b_raw = spintent_trim(s_sub(raw_content, ops[1][1] + ops[1][2])) if a_raw == "" or b_raw == "" then spintent_prob_set_error() return end end token_set_macro("l__spintent_spProb_luaset_error_str", "false") token_set_macro("l__spintent_spProb_luaset_op_str", op) token_set_macro("l__spintent_spProb_luaset_a_str", a_raw) token_set_macro("l__spintent_spProb_luaset_b_str", b_raw) end, { "string" }) -- \spAcirc, \spPcirc: una coma en el argumento nunca es válida. register_tex_cmd("luafun_spcircfig_comma", function(raw) token_set_macro("l__spintent_spcircfig_luaset_comma_str", string.find(raw, ",", 1, true) and "true" or "false") end, { "string" }) -- \spcomp: un evento es compuesto si contiene \cap, \cup, \setminus -- o \mid (a cualquier profundidad de llaves). local spintent_compound_ops = { cap = true, cup = true, setminus = true, mid = true } register_tex_cmd("luafun_spcomp_compound", function(raw) local compound, i, n = "false", 1, #raw while i <= n do if s_sub(raw, i, i) == "\\" then local word = s_match(raw, "^%a+", i + 1) if word then if spintent_compound_ops[word] then compound = "true"; break end i = i + 1 + #word else i = i + 2 end else i = i + 1 end end token_set_macro("l__spintent_spcomp_luaset_compound_str", compound) end, { "string" }) -- \spabs: decide si se lee el cierre del valor absoluto. Cuenta los -- tokens del argumento sin espacios y sin un signo - o + inicial -- (primero -, luego +); una secuencia de control, un grupo entre -- llaves y cualquier otro carácter son un token cada uno. Hay cierre -- si hay más de un token. register_tex_cmd("luafun_spabs_close", function(raw) local i, n, count = 1, #raw, 0 local function skip_spaces() while s_sub(raw, i, i) == " " do i = i + 1 end end skip_spaces() for _, sign in ipairs({ "-", "+" }) do if s_sub(raw, i, i) == sign then i = i + 1 skip_spaces() end end while i <= n do local c = s_sub(raw, i, i) if c == " " then i = i + 1 else count = count + 1 if c == "\\" then local word = s_match(raw, "^%a+", i + 1) i = word and (i + 1 + #word) or (i + 2) elseif c == "{" then local depth = 0 repeat local d = s_sub(raw, i, i) if d == "\\" then i = i + 1 elseif d == "{" then depth = depth + 1 elseif d == "}" then depth = depth - 1 end i = i + 1 until depth == 0 or i > n else i = i + 1 end end end token_set_macro("l__spintent_spabs_luaset_close_str", count > 1 and "true" or "false") end, { "string" }) -- \spcomb: combinatoria. El argumento único es n o n,k. La función -- luafun_spcomb_split lo parte en la primera coma de nivel superior -- y luafun_spcomb_prepare valida y deja listos n y k según el modo -- que decide expl3: -- pair n y k (k <= n si ambos son enteros y check_le es "true") -- list n y una lista de multiplicidades separadas por comas -- (permutaciones con repetición); queda como {a}{b}{c} -- one solo n -- Las comas que quedan dentro de llaves, paréntesis o corchetes -- (también \left...\right) no separan elementos. Si todo es entero, -- la suma de las multiplicidades no puede pasar de n. local function spintent_comb_set_error() token_set_macro("l__spintent_spcomb_luaset_error_str", "true") token_set_macro("l__spintent_spcomb_luaset_n_str", "") token_set_macro("l__spintent_spcomb_luaset_k_str", "") token_set_macro("l__spintent_spcomb_luaset_list_tl", "") end local function spintent_comb_split(raw) local items, depth, start = {}, 0, 1 local i, n = 1, #raw while i <= n do local c = s_sub(raw, i, i) if c == "\\" then local word = s_match(raw, "^\\(%a+)", i) if word == "left" or word == "right" then local j = i + 1 + #word while s_sub(raw, j, j) == " " do j = j + 1 end local d = s_sub(raw, j, j) if word == "left" and (d == "(" or d == "[") then depth = depth + 1 elseif word == "right" and (d == ")" or d == "]") then depth = depth - 1 end i = j + 1 elseif word then i = i + 1 + #word else local d = s_sub(raw, i + 1, i + 1) if d == "{" then depth = depth + 1 elseif d == "}" then depth = depth - 1 end i = i + 2 end else if c == "{" or c == "(" or c == "[" then depth = depth + 1 elseif c == "}" or c == ")" or c == "]" then depth = depth - 1 elseif c == "," and depth == 0 then items[#items + 1] = s_sub(raw, start, i - 1) start = i + 1 end if depth < 0 then return nil end i = i + 1 end end if depth ~= 0 then return nil end items[#items + 1] = s_sub(raw, start) return items end register_tex_cmd("luafun_spcomb_split", function(raw) local items = spintent_comb_split(raw) local raw_n, raw_k, single = raw, "", "true" if items and #items >= 2 then raw_n, raw_k, single = items[1], t_concat(items, ",", 2), "false" end token_set_macro("l__spintent_spcomb_luaset_raw_n_str", raw_n) token_set_macro("l__spintent_spcomb_luaset_raw_k_str", raw_k) token_set_macro("l__spintent_spcomb_luaset_single_str", single) end, { "string" }) register_tex_cmd("luafun_spcomb_prepare", function(mode, raw_n, raw_k, check_le) local n_s = spintent_trim(raw_n) local k_s = spintent_trim(raw_k) if n_s == "" then return spintent_comb_set_error() end local list = "" if mode == "pair" then if k_s == "" then return spintent_comb_set_error() end if check_le == "true" and s_match(n_s, "^%d+$") and s_match(k_s, "^%d+$") and tonumber(k_s) > tonumber(n_s) then return spintent_comb_set_error() end elseif mode == "list" then local items = spintent_comb_split(k_s) if not items or #items < 2 then return spintent_comb_set_error() end local numeric = s_match(n_s, "^%d+$") ~= nil local sum, parts = 0, {} for _, item in ipairs(items) do item = spintent_trim(item) if item == "" then return spintent_comb_set_error() end if numeric and s_match(item, "^%d+$") then sum = sum + tonumber(item) else numeric = false end parts[#parts + 1] = "{" .. item .. "}" end if numeric and sum > tonumber(n_s) then return spintent_comb_set_error() end list = t_concat(parts) end token_set_macro("l__spintent_spcomb_luaset_error_str", "false") token_set_macro("l__spintent_spcomb_luaset_n_str", n_s) token_set_macro("l__spintent_spcomb_luaset_k_str", k_s) token_set_macro("l__spintent_spcomb_luaset_list_tl", list) end, { "string", "string", "string", "string" }) -- FUNCIONES: \spfun, \spdom, \sprec, \spcod -- -- \spfun reconoce nombre(arg, arg, ...) donde nombre es una letra -- (mayúscula o minúscula) con subíndice entre llaves (letras o -- dígitos) o con primas, igual que un punto de la geometría, y con -- el exponente ^{-1} opcional de la función inversa (que no se -- combina con primas). Solo la inversa puede ir sin paréntesis. Los -- argumentos se separan por las comas de nivel superior: las que -- quedan dentro de llaves, paréntesis o corchetes no cuentan. -- Deja el nombre en las mismas variables que usa la familia geo -- (letra/sub/tipo/intent/print) y cada argumento en -- l__spintent_spfun_luaset_arg__tl. Un argumento que es a su vez -- una llamada (f(g(x)), f(g^{-1}(y))) se analiza de forma recursiva y -- queda marcado "call"; expl3 vuelve a llamar a esta función con él. -- -- \spdom, \sprec y \spcod reciben solo el nombre (con la misma -- gramática) y lo dejan en las mismas variables. local spintent_fun_alnum = R"09" + R"az" + R"AZ" local spintent_fun_head = Ct( C(R"az" + R"AZ") * ( P"_{" * C(spintent_fun_alnum^1) * P"}" * Cc"sub" + spintent_geo_primas * Cc"prima" + Cc"" * Cc"" ) ) * lpeg_base.Cp() -- Nombre + exponente ^{-1} opcional. Devuelve nombre, inv (booleano) y -- el resto del texto; nil si no empieza con un nombre válido o si la -- inversa va junto a primas. Los espacios dentro del exponente y antes -- de él no cuentan. local function spintent_fun_parse_name(raw) local nombre, pos = spintent_fun_head:match(raw) if not nombre then return nil end local rest = s_sub(raw, pos) local inv = false local after = s_match(rest, "^%s*%^%s*%{%s*%-%s*1%s*%}(.*)$") if after then if nombre[3] == "prima" then return nil end inv, rest = true, after end return nombre, inv, rest end -- Separa por las comas de nivel superior. Devuelve nil si los -- delimitadores no quedan balanceados. local function spintent_fun_split_args(inner) local args, depth, start = {}, 0, 1 local i, n = 1, #inner while i <= n do local c = s_sub(inner, i, i) if c == "\\" then i = i + 2 else if c == "{" or c == "(" or c == "[" then depth = depth + 1 elseif c == "}" or c == ")" or c == "]" then depth = depth - 1 if depth < 0 then return nil end elseif c == "," and depth == 0 then args[#args + 1] = s_sub(inner, start, i - 1) start = i + 1 end i = i + 1 end end if depth ~= 0 then return nil end args[#args + 1] = s_sub(inner, start) return args end -- Un argumento "simple" es un único nodo (letra, número o comando -- como \alpha): lleva su arg directo. Cualquier otro (2x, x+1, -- x_{1}, \frac{1}{2}, \sqrt{2}) necesita el separador interno para -- que luamml le conserve el arg. local function spintent_fun_is_simple(arg) return s_match(arg, "^%w$") ~= nil or s_match(arg, "^%d+$") ~= nil or s_match(arg, "^\\%a+$") ~= nil end -- ¿El argumento es, entero, una llamada nombre(...) o nombre^{-1}? -- Se comprueba solo la forma; su validez se analiza aparte. local function spintent_fun_is_call(arg) local nombre, inv, rest = spintent_fun_parse_name(arg) if not nombre then return false end local inner = s_match(rest, "^%s*%((.*)%)%s*$") if inner then return spintent_fun_split_args(inner) ~= nil end return inv and s_match(rest, "^%s*$") ~= nil end local spintent_fun_analyze -- Clasifica una lista de argumentos en crudo: devuelve { texto, tipo } -- por argumento ("true" un solo nodo, "false" expresión, "call" otra -- llamada ya validada de forma recursiva) o nil si alguno es inválido. local function spintent_fun_classify_args(args) for i = 1, #args do local a = spintent_trim(args[i]) if a == "" then return nil end local kind if spintent_fun_is_simple(a) then kind = "true" elseif spintent_fun_is_call(a) then if not spintent_fun_analyze(a) then return nil end kind = "call" else kind = "false" end args[i] = { a, kind } end return args end -- Analiza una llamada completa. Devuelve nombre, inv y la lista de -- argumentos { texto, tipo }; nil si algo no es válido en cualquier -- nivel. spintent_fun_analyze = function(raw) local nombre, inv, rest = spintent_fun_parse_name(raw) if not nombre then return nil end local args local inner = s_match(rest, "^%s*%((.*)%)%s*$") if inner then args = spintent_fun_split_args(inner) elseif inv and s_match(rest, "^%s*$") then args = {} end if not args then return nil end args = spintent_fun_classify_args(args) if not args then return nil end return nombre, inv, args end local function spintent_fun_set_error() token_set_macro("l__spintent_spfun_luaset_error_str", "true") token_set_macro("l__spintent_spfun_luaset_nargs_str", "0") token_set_macro("l__spintent_spfun_luaset_inv_str", "false") token_set_macro("l__spintent_geo_luaset_intent_str", "") token_set_macro("l__spintent_geo_luaset_print_tl", "") token_set_macro("l__spintent_geo_luaset_letra_str", "") token_set_macro("l__spintent_geo_luaset_sub_str", "") token_set_macro("l__spintent_geo_luaset_tipo_str", "") end -- Deja el nombre en las variables compartidas. Con la inversa, el -- intent literal termina en "-inversa" (f-inversa, f-sub-uno-inversa). local function spintent_fun_set_name(nombre, inv) local intent = spintent_geo_build_body_nombre(nombre) local visual = spintent_geo_build_visual_nombre(nombre) if inv then intent = intent .. "-inversa" visual = visual .. "^{-1}" end token_set_macro("l__spintent_geo_luaset_intent_str", intent) token_set_macro("l__spintent_geo_luaset_print_tl", visual) token_set_macro("l__spintent_geo_luaset_letra_str", nombre[1]) token_set_macro("l__spintent_geo_luaset_sub_str", nombre[2] or "") token_set_macro("l__spintent_geo_luaset_tipo_str", nombre[3] or "") token_set_macro("l__spintent_spfun_luaset_inv_str", inv and "true" or "false") end register_tex_cmd("luafun_spfun_parse_and_set", function(raw) raw = spintent_trim(raw) raw = spintent_geo_normalize_primes(raw) local nombre, inv, args = spintent_fun_analyze(raw) if not nombre then spintent_fun_set_error() return end token_set_macro("l__spintent_spfun_luaset_error_str", "false") token_set_macro("l__spintent_spfun_luaset_nargs_str", tostring(#args)) for i = 1, #args do token_set_macro("l__spintent_spfun_luaset_arg_" .. i .. "_tl", args[i][1]) token_set_macro("l__spintent_spfun_luaset_simple_" .. i .. "_str", args[i][2]) end spintent_fun_set_name(nombre, inv) end, { "string" }) register_tex_cmd("luafun_spdom_parse_and_set", function(raw) raw = spintent_trim(raw) raw = spintent_geo_normalize_primes(raw) local nombre, inv, rest = spintent_fun_parse_name(raw) if not nombre or not s_match(rest, "^%s*$") then spintent_fun_set_error() token_set_macro("l__spintent_spdom_luaset_error_str", "true") return end token_set_macro("l__spintent_spdom_luaset_error_str", "false") spintent_fun_set_name(nombre, inv) end, { "string" }) -- \spfuncomp: composición lineal f\circ g, (f\circ g)(x) -- -- Formas: f\circ g\circ ... | (f\circ g) | (f\circ g)(args) -- Cada función es un nombre como en \spfun (letra con subíndice, -- primas o ^{-1}); hay al menos dos. Los argumentos se analizan -- igual que en \spfun (simple / expresión / llamada). -- Índice del paréntesis que cierra al de la posición i, o nil. local function spintent_fun_match_paren(str, i) local depth = 0 local n = #str while i <= n do local c = s_sub(str, i, i) if c == "\\" then i = i + 2 else if c == "{" or c == "(" or c == "[" then depth = depth + 1 elseif c == "}" or c == ")" or c == "]" then depth = depth - 1 if depth == 0 then return i end if depth < 0 then return nil end end i = i + 1 end end return nil end -- Parte por los \circ (o ∘) de nivel superior. local function spintent_fun_split_circ(str) local parts, depth, start = {}, 0, 1 local i, n = 1, #str while i <= n do local c = s_sub(str, i, i) if c == "\\" then if depth == 0 and s_sub(str, i, i + 4) == "\\circ" and not s_match(s_sub(str, i + 5, i + 5), "%a") then parts[#parts + 1] = s_sub(str, start, i - 1) i = i + 5 start = i else i = i + 2 end elseif depth == 0 and s_sub(str, i, i + 2) == "∘" then parts[#parts + 1] = s_sub(str, start, i - 1) i = i + 3 start = i else if c == "{" or c == "(" or c == "[" then depth = depth + 1 elseif c == "}" or c == ")" or c == "]" then depth = depth - 1 if depth < 0 then return nil end end i = i + 1 end end if depth ~= 0 then return nil end parts[#parts + 1] = s_sub(str, start) return parts end -- Lista de funciones { intent, visual } separadas por \circ; al menos -- dos y cada una un nombre completo. nil si algo no es válido. local function spintent_fun_comp_funs(str) local parts = spintent_fun_split_circ(str) if not parts or #parts < 2 then return nil end local funs = {} for i = 1, #parts do local nombre, inv, rest = spintent_fun_parse_name(spintent_trim(parts[i])) if not nombre or not s_match(rest, "^%s*$") then return nil end local intent = spintent_geo_build_body_nombre(nombre) local visual = spintent_geo_build_visual_nombre(nombre) if inv then intent = intent .. "-inversa" visual = visual .. "^{-1}" end funs[i] = { intent, visual, nombre[1], nombre[2] or "", nombre[3] or "", inv } end return funs end local function spintent_fun_comp_error() token_set_macro("l__spintent_spfuncomp_luaset_error_str", "true") end register_tex_cmd("luafun_spfuncomp_parse_and_set", function(raw) raw = spintent_trim(raw) raw = spintent_geo_normalize_primes(raw) local funs, args, paren if s_sub(raw, 1, 1) == "(" then local close = spintent_fun_match_paren(raw, 1) if not close then spintent_fun_comp_error() return end paren = true funs = spintent_fun_comp_funs(s_sub(raw, 2, close - 1)) local rest = spintent_trim(s_sub(raw, close + 1)) if rest ~= "" then if s_sub(rest, 1, 1) ~= "(" then spintent_fun_comp_error() return end local c2 = spintent_fun_match_paren(rest, 1) if c2 ~= #rest then spintent_fun_comp_error() return end args = spintent_fun_split_args(s_sub(rest, 2, c2 - 1)) if not args then spintent_fun_comp_error() return end args = spintent_fun_classify_args(args) if not args then spintent_fun_comp_error() return end end else paren = false funs = spintent_fun_comp_funs(raw) end if not funs then spintent_fun_comp_error() return end token_set_macro("l__spintent_spfuncomp_luaset_error_str", "false") token_set_macro("l__spintent_spfuncomp_luaset_n_str", tostring(#funs)) token_set_macro("l__spintent_spfuncomp_luaset_paren_str", paren and "true" or "false") token_set_macro("l__spintent_spfuncomp_luaset_apply_str", args and "true" or "false") for i = 1, #funs do token_set_macro("l__spintent_spfuncomp_luaset_intent_" .. i .. "_str", funs[i][1]) token_set_macro("l__spintent_spfuncomp_luaset_print_" .. i .. "_tl", funs[i][2]) token_set_macro("l__spintent_spfuncomp_luaset_letra_" .. i .. "_str", funs[i][3]) token_set_macro("l__spintent_spfuncomp_luaset_sub_" .. i .. "_str", funs[i][4]) token_set_macro("l__spintent_spfuncomp_luaset_tipo_" .. i .. "_str", funs[i][5]) token_set_macro("l__spintent_spfuncomp_luaset_inv_" .. i .. "_str", funs[i][6] and "true" or "false") end if args then token_set_macro("l__spintent_spfun_luaset_nargs_str", tostring(#args)) for i = 1, #args do token_set_macro("l__spintent_spfun_luaset_arg_" .. i .. "_tl", args[i][1]) token_set_macro("l__spintent_spfun_luaset_simple_" .. i .. "_str", args[i][2]) end end end, { "string" }) % \iffalse % % \fi % % \iffalse %<@@=> %<*spitool> % \fi #!/usr/bin/env texlua -- Must be the first line of any texlua script: LuaTeX doesn't add the -- kpse-aware require() searcher until this has run, and other things -- are half-broken without it too. kpse.set_program_name("luatex") local SPITOOL_VERSION = "1.0" local lfs = require("lfs") -- OS detection and Quoting helper (POSIX vs Windows) local is_windows = package.config:sub(1, 1) == '\\' local function quote_arg(v) if is_windows then if v:find("%%") then io.stderr:write("[WARNING] argument contains '%': " .. v .. " -- cmd.exe may expand it as an environment variable.\n") end return '"' .. v:gsub('"', '\\"') .. '"' else return "'" .. v:gsub("'", "'\\''") .. "'" end end -- Helper: Check executable presence in system PATH local function has_cmd(cmd) local null_dev = is_windows and "NUL" or "/dev/null" local check = is_windows and ("where " .. cmd .. " > " .. null_dev .. " 2>&1") or ("command -v " .. cmd .. " > " .. null_dev .. " 2>&1") local res = os.execute(check) return (res == true or res == 0) end -- Input filename safety validation (prevents command & TeX injection) local function is_safe_filename(filename) if filename:match("[%s\"'%%&|<>^`$%{%}%[%]!#~]") then return false end return true end -- File name without its directory part (TeX's \jobname never has one) local function file_name_only(path) return path:match("([^/\\]+)$") or path end -- Config & CLI Parser State local config = { path = nil, strict = false, overwrite = false, check_only = false, engine = nil, help = false, version = false } local input_files = {} -- Set when any validation or compilation step fails (drives the exit code) local had_failure = false -- True only for regular files (directories are rejected) local function is_regular_file(path) local attr = lfs.attributes(path) return attr ~= nil and attr.mode == "file" end -- Smart file path resolution (allows omitting extensions) local function resolve_input_file(input) if is_regular_file(input) then return input end local candidates if config.check_only then candidates = { input .. ".pdf" } else candidates = { input .. ".tex", input .. ".ltx", input .. ".pdf", input .. "-luamml-mathml.html", input .. ".html" } end for _, candidate in ipairs(candidates) do if is_regular_file(candidate) then return candidate end end return nil end -- Check if output file exists and enforce --overwrite protection local function check_output_permission(output_path, overwrite_flag) local check = io.open(output_path, "r") if check then check:close() if not overwrite_flag then io.stderr:write("Error: output file '" .. output_path .. "' already exists. Use -o or --overwrite to replace it.\n") return false end end return true end -- Docopt-style CLI parser local i = 1 local parse_flags = true while i <= #arg do local current = arg[i] if parse_flags and current == "--" then parse_flags = false elseif parse_flags and (current == "--help" or current == "-h") then config.help = true elseif parse_flags and (current == "--version" or current == "-v") then config.version = true elseif parse_flags and (current == "--strict" or current == "-s") then config.strict = true elseif parse_flags and (current == "--overwrite" or current == "-o") then config.overwrite = true elseif parse_flags and (current == "--check-only" or current == "-c") then config.check_only = true elseif parse_flags and (current == "--path" or current == "-p") then if not arg[i+1] then io.stderr:write("Error: option '" .. current .. "' requires a value\n") os.exit(1) end i = i + 1 config.path = arg[i] elseif parse_flags and (current:match("^%-%-path=(.*)") or current:match("^%-p=(.*)")) then config.path = current:match("^%-%-path=(.*)") or current:match("^%-p=(.*)") elseif parse_flags and (current == "--engine" or current == "-e") then if not arg[i+1] then io.stderr:write("Error: option '" .. current .. "' requires a value\n") os.exit(1) end i = i + 1 config.engine = arg[i] elseif parse_flags and (current:match("^%-%-engine=(.*)") or current:match("^%-e=(.*)")) then config.engine = current:match("^%-%-engine=(.*)") or current:match("^%-e=(.*)") else if parse_flags and current:sub(1,1) == "-" then io.stderr:write("Error: unrecognized option '" .. current .. "'\n") os.exit(1) else table.insert(input_files, current) end end i = i + 1 end -- Zero arguments check: show help if running from interactive terminal (TTY) if #arg == 0 then local ffi = require("ffi") ffi.cdef[[ int isatty(int fd); int _isatty(int fd); ]] local is_tty if ffi.os == "Windows" then is_tty = ffi.C._isatty(0) ~= 0 else is_tty = ffi.C.isatty(0) ~= 0 end if is_tty then config.help = true end end -- Docopt Help banner if config.help then print("spitool v" .. SPITOOL_VERSION .. " - Tool for cleaning luamml ghosts and validating LaTeX tagged PDFs") print([[ Usage: spitool [options] Description: spitool prepares the MathML files generated by luamml for mathml-AF: it removes the invisible ghost characters (U+2063 with sptmp attributes) and writes the result to -mathml.html. Depending on the input file: .pdf validates the tag structure (rnv-wrapp) and the PDF/UA-2 conformance (veraPDF), if the tools exist. .html cleans an existing -luamml-mathml.html. .tex runs the TeX engine once in a temporary directory with AFghost=true to obtain the luamml HTML, then cleans it. The temporary PDF is discarded and not validated. Options: -c, --check-only Only validate; the input must be a .pdf file. -o, --overwrite Force replacing existing output files (-mathml.html). -p, --path= Target directory where output files will be created (created if it does not exist). -e, --engine= TeX engine used to compile (default: lualatex-dev if available, otherwise lualatex). -s, --strict Stop at the first failure (compilation or validation). Without it, all files are processed; in both cases the exit code is non-zero if anything failed. -h, --help Show this usage summary and exit. -v, --version Show spitool version number and exit. Examples: $ spitool -c sample.pdf $ spitool -c -s sample $ spitool sample-luamml-mathml.html $ spitool -o -p build/ sample.tex Issues and reports: Repository : https://github.com/pablgonz/spanish-mathml-intent Bug tracker: https://github.com/pablgonz/spanish-mathml-intent/issues Copyright(C) 2026 by Pablo González L educarchile.cl> ]]) os.exit(0) end -- Version banner if config.version then print("spitool v" .. SPITOOL_VERSION) os.exit(0) end if config.engine and not config.engine:match("^[%w%._%-]+$") then io.stderr:write("Error: illegal characters in engine name: " .. config.engine .. "\n") os.exit(1) end if config.check_only and (config.path or config.overwrite) then io.stderr:write("[WARNING] -p/--path and -o/--overwrite have no effect with -c/--check-only.\n") end if #input_files == 0 then io.stderr:write("Error: no input file specified. Use --help for usage information.\n") os.exit(1) end -- Recursive directory removal helper local function remove_dir_recursive(path) if not path or path == "" then return end local attr = lfs.attributes(path) if not attr or attr.mode ~= "directory" then return end for entry in lfs.dir(path) do if entry ~= "." and entry ~= ".." then local full_path = path .. "/" .. entry -- symlinkattributes: never follow links out of the temp dir local entry_attr = lfs.symlinkattributes(full_path) if entry_attr and entry_attr.mode == "directory" then remove_dir_recursive(full_path) else os.remove(full_path) end end end lfs.rmdir(path) end -- Secure temp directory helpers local function system_temp_dir() if is_windows then return os.getenv("TEMP") or os.getenv("TMP") or "C:\\Windows\\Temp" else return os.getenv("TMPDIR") or "/tmp" end end local function secure_temp_subdir() -- POSIX: mktemp -d creates the directory atomically with mode 700 if not is_windows and has_cmd("mktemp") then local template = system_temp_dir():gsub("/$", "") .. "/spitool-XXXXXX" local p = io.popen("mktemp -d " .. quote_arg(template) .. " 2>/dev/null") if p then local dir = p:read("*l") p:close() if dir and dir ~= "" and lfs.attributes(dir, "mode") == "directory" then return dir end end end -- Fallback (Windows, or no mktemp) local base = system_temp_dir() local sep = package.config:sub(1, 1) math.randomseed(os.time() + math.floor(os.clock() * 100000)) local unique = string.format("%d_%d", os.time(), math.random(100000, 999999)) local tmpdir = base .. sep .. "spitool-" .. unique local ok, err = lfs.mkdir(tmpdir) if not ok then io.stderr:write("Error: failed to create secure temp directory: " .. tostring(err) .. "\n") os.exit(1) end if not is_windows then os.execute("chmod 700 " .. quote_arg(tmpdir)) end return tmpdir end -- Create the output directory (and parents) if needed local function ensure_dir(path) if lfs.attributes(path, "mode") == "directory" then return true end local current = path:sub(1, 1) == "/" and "/" or "" for part in path:gmatch("[^/\\]+") do current = (current == "" or current:sub(-1) == "/") and (current .. part) or (current .. "/" .. part) if lfs.attributes(current, "mode") ~= "directory" then local ok, err = lfs.mkdir(current) if not ok then return nil, err end end end return true end -- Shared cleanup: Strips ghost tags/characters from dirty HTML -- -- Each ghost is a U+2063 element. It is -- removed with a pattern over the whole file content (not line by line), so -- it works both for indented HTML (one element per line) and for flat HTML -- (a whole formula on a single line). Elements with an unknown sptmp kind -- are left untouched. local function strip_ghosts(input_path, output_path) local html_file = io.open(input_path, "rb") if not html_file then return nil, "failed to open " .. input_path end local content = html_file:read("*a") html_file:close() local count_outer, count_inner = 0, 0 -- U+2063 may appear as the literal character or as a character reference local ghost_forms = { "\u{2063}", "⁣", "⁣", "⁣" } local clean = content for _, ghost in ipairs(ghost_forms) do -- optional leading newline + indentation is removed together with the tag local pattern = "\n?[ \t]*]-)sptmp=\"(%a+)\"([^>]-)>" .. ghost:gsub("%p", "%%%0") .. "" clean = clean:gsub(pattern, function(_, kind) if kind == "outer" then count_outer = count_outer + 1 elseif kind == "inner" then count_inner = count_inner + 1 else return nil -- unknown kind: keep the match as is end return "" end) end local out = io.open(output_path, "wb") if not out then return nil, "failed to create " .. output_path end out:write(clean) out:close() local function count_lines(s) local _, n = s:gsub("\n", "\n") return n + ((s ~= "" and s:sub(-1) ~= "\n") and 1 or 0) end return { total_lines = count_lines(content), count_outer = count_outer, count_inner = count_inner, kept_lines = count_lines(clean), } end local function report_stats(stats, output_name, empty_hint) print(string.format(" -> Lines read: %d", stats.total_lines)) print(string.format(" -> 'outer' ghosts removed: %d", stats.count_outer)) print(string.format(" -> 'inner' ghosts removed: %d", stats.count_inner)) print(string.format(" -> Generated: %s (%d lines)", output_name, stats.kept_lines)) if stats.count_outer == 0 and stats.count_inner == 0 then print("[WARNING] No ghost characters found.") print(" " .. empty_hint) return false end return true end -- Mode 1: PDF Validation Only (No TeX compilation) local function run_pdf_mode(pdf_filename) local run_rnv = has_cmd("rnv-wrapp") and has_cmd("show-pdf-tags") local run_verapdf = has_cmd("verapdf") if not run_rnv and not run_verapdf then io.stderr:write("Error: no PDF validators (show-pdf-tags/rnv-wrapp or veraPDF) detected in PATH.\n") os.exit(1) end print("==================================================") print(" Testing PDF compliance: " .. pdf_filename) print("==================================================") if run_rnv and run_verapdf then print("Validators detected: show-pdf-tags, rnv-wrapp & veraPDF") elseif run_rnv then print("Validators detected: show-pdf-tags & rnv-wrapp") elseif run_verapdf then print("Validator detected: veraPDF") end local null_dev = is_windows and "NUL" or "/dev/null" local validation_failed = false -- 1. RNC tag structure check if run_rnv then local cmd_rnv = "show-pdf-tags --xml " .. quote_arg(pdf_filename) .. " | rnv-wrapp" local res_rnv = os.execute(cmd_rnv .. " > " .. null_dev .. " 2>&1") if res_rnv ~= true and res_rnv ~= 0 then print("\n[RNC Validation Error Output]:") os.execute(cmd_rnv) io.stderr:write("Error: tag structure validation (rnv-wrapp) failed for " .. pdf_filename .. "\n") validation_failed = true end end -- 2. PDF/UA-2 compliance check if run_verapdf then local cmd_vera = "verapdf --flavour ua2 --format text " .. quote_arg(pdf_filename) local res_vera = os.execute(cmd_vera .. " > " .. null_dev .. " 2>&1") if res_vera ~= true and res_vera ~= 0 then print("\n[PDF/UA-2 Validation Error Output]:") os.execute(cmd_vera) io.stderr:write("Error: veraPDF (PDF/UA-2) validation failed for " .. pdf_filename .. "\n") validation_failed = true end end if not validation_failed then print("PASS") else print("FAIL") had_failure = true if config.strict then os.exit(1) end end end -- Mode 2: Clean pre-existing *-luamml-mathml.html local function run_clean_mode(html_filename) local basename = html_filename:match("^(.*)%-luamml%-mathml%.html$") if not basename then io.stderr:write("Error: expected filename ending in -luamml-mathml.html: " .. html_filename .. "\n") os.exit(1) end -- With -p/--path the output goes to that directory, named after the -- file only (the input directory is not repeated inside it) local output_name if config.path then output_name = config.path:gsub("[/\\]$", "") .. "/" .. file_name_only(basename) .. "-mathml.html" else output_name = basename .. "-mathml.html" end if not check_output_permission(output_name, config.overwrite) then os.exit(1) end if config.path then local ok, derr = ensure_dir(config.path) if not ok then io.stderr:write("Error: could not create directory '" .. config.path .. "': " .. tostring(derr) .. "\n") os.exit(1) end end print("==================================================") print(" Processing: " .. html_filename) print("==================================================") local stats, err = strip_ghosts(html_filename, output_name) if not stats then io.stderr:write("Error: " .. err .. "\n") os.exit(1) end local found_something = report_stats(stats, output_name, "If this .html was not generated via spitool from a .tex source, " .. "it might not contain ghosts -- try passing the .tex/.ltx file directly.") if not found_something then os.remove(output_name) return end end -- Mode 3: Compile .tex/.ltx and clean the generated HTML local function run_generate_mode(tex_filename) local basename, ext = tex_filename:match("^(.*)%.(%a+)$") if not basename or (ext ~= "tex" and ext ~= "ltx") then io.stderr:write("Error: expected a .tex or .ltx file: " .. tostring(tex_filename) .. "\n") os.exit(1) end -- TeX names its output files after the jobname, which has no directory local jobname = file_name_only(basename) local output_name if config.path then output_name = config.path:gsub("[/\\]$", "") .. "/" .. jobname .. "-mathml.html" else output_name = basename .. "-mathml.html" end if not check_output_permission(output_name, config.overwrite) then os.exit(1) end if config.path then local ok, derr = ensure_dir(config.path) if not ok then io.stderr:write("Error: could not create directory '" .. config.path .. "': " .. tostring(derr) .. "\n") os.exit(1) end end local source = io.open(tex_filename, "r") if not source then io.stderr:write("Error: could not open " .. tex_filename .. "\n") os.exit(1) end source:close() local tmpdir = secure_temp_subdir() -- The temp directory is kept when compilation fails, so the .log -- mentioned in the error message is still there to be read local keep_tmp = false -- Guaranteed cleanup closure local function cleanup_and_exit(code) if not keep_tmp then remove_dir_recursive(tmpdir) end os.exit(code or 0) end local engine = config.engine if not engine then engine = has_cmd("lualatex-dev") and "lualatex-dev" or "lualatex" end if not has_cmd(engine) then io.stderr:write("Error: TeX engine '" .. engine .. "' not found in PATH.\n") cleanup_and_exit(1) end local latex_cmd = string.format( '%s --interaction=nonstopmode --output-directory=%s %s', engine, quote_arg(tmpdir), quote_arg("\\PassOptionsToPackage{AFghost=true}{spanish-mathml-intent}\\input{" .. tex_filename .. "}") ) print("==================================================") print(" Compiling with AFghost=true for " .. tex_filename) print("==================================================") local null_dev = is_windows and "NUL" or "/dev/null" local res = os.execute(latex_cmd .. " > " .. null_dev .. " 2>&1") if res ~= true and res ~= 0 then keep_tmp = true had_failure = true io.stderr:write("Error: compilation failed. Log file available in: " .. tmpdir .. "\n") if config.strict then cleanup_and_exit(1) end end local generated_html = tmpdir .. "/" .. jobname .. "-luamml-mathml.html" local check = io.open(generated_html, "r") if not check then keep_tmp = true io.stderr:write("Error: " .. generated_html .. " was not generated. Verify spanish-mathml-intent package loading in " .. tex_filename .. "\n") had_failure = true cleanup_and_exit(1) end check:close() print("\n==================================================") print(" Cleaning ghost characters") print("==================================================") local stats, err = strip_ghosts(generated_html, output_name) if not stats then io.stderr:write("Error: " .. err .. "\n") cleanup_and_exit(1) end local found_something = report_stats(stats, output_name, "Forced AFghost=true during compilation -- if no ghosts were found, " .. "verify that spanish-mathml-intent is loaded in " .. tex_filename .. ".") if not found_something then os.remove(output_name) end -- Guaranteed cleanup of temporary directory upon completion -- (no os.exit here: other input files may still be pending) if not keep_tmp then remove_dir_recursive(tmpdir) end end -- Main Execution Loop for _, arg_input in ipairs(input_files) do local resolved_file = resolve_input_file(arg_input) if not resolved_file then io.stderr:write("Error: could not find file '" .. arg_input .. "'\n") os.exit(1) end if not is_safe_filename(resolved_file) then io.stderr:write("Error: input filename contains unsafe or illegal characters: " .. resolved_file .. "\n") os.exit(1) end if config.check_only and not resolved_file:match("%.pdf$") then io.stderr:write("Error: -c/--check-only expects a .pdf file: " .. resolved_file .. "\n") os.exit(1) end if resolved_file:match("%.pdf$") then run_pdf_mode(resolved_file) elseif resolved_file:match("%.html$") then run_clean_mode(resolved_file) elseif resolved_file:match("%.tex$") or resolved_file:match("%.ltx$") then run_generate_mode(resolved_file) else io.stderr:write("Error: unrecognized file extension for '" .. resolved_file .. "'\n") os.exit(1) end end if had_failure then os.exit(1) end % \iffalse % % \fi