6

Please consider the following code:

\documentclass{article}

\usepackage{ruby}

\newcommand{\strsymbol}{\large\bfseries∗}
\newcommand{\unssymbol}{\large·}
\newcommand{\str}[1]{\textbf{\ruby{#1}{\strsymbol}}{}}
\newcommand{\uns}[1]{\ruby{#1}{\unssymbol}{}}

\begin{document}

\uns{A} \uns{po}\uns{ly}\str{syl}\uns{la}\uns{ble} \uns{should} \uns{be} \str{fun} \uns{to} \uns{de}\str{fine}.

\end{document}

It generates output like this:

enter image description here (Well I use nicer fonts and symbols, but you get the idea).

As you can see, that code is quite unwieldly. Ideally I'd like some function to accept an argument like this:

\stress{A po-ly*syl*la-ble should be *fun* to de*fine*.}

In fact, the working example up there wasn't written by hand; it's the output of some ugly Emacs Lisp code I wrote to convert the nicer argument format. It just loops over the characters handling the several different cases (*, -, Unicode alphanumerics, and space/punctuation), keeping track of state to start and end the Latex commands accordingly.

I'm totally new to Tex programming, and was wondering whether a Latex solution is possible. I have to use Xetex because of reasons. My attempts didn't get very far; I can't loop over strings with \@tfor because it eats inner whitespace, and even if I could, I don't know how to write a command piecemeal spanning many loop interactions (like \ruby{}). I saw the answer about Expandable 'character scanning' command that preserves spaces , but I couldn't even being to figure out how one should modify the sample code to implement something like this.

What's the best approach to write a Latex macro accepting an argument like this?

1 Answer 1

4

With l3regex:

\documentclass{article}

\usepackage{ruby}
\usepackage{textcomp}
\usepackage{xparse,l3regex}

\newcommand{\strsymbol}{\large\bfseries\textasteriskcentered}
\newcommand{\unssymbol}{\large\textperiodcentered}
\newcommand{\str}[1]{\textbf{\ruby{#1}{\strsymbol}}{}}
\newcommand{\uns}[1]{\ruby{#1}{\unssymbol}{}}

\ExplSyntaxOn
\NewDocumentCommand{\stress}{m}
 {
  \leoboiko_stress:n { #1 }
 }

\tl_new:N \l_leoboiko_stress_sentence_tl

\cs_new_protected:Nn \leoboiko_stress:n
 {
  \tl_set:Nn \l_leoboiko_stress_sentence_tl { #1 }
  % change *<syllable>* into -*<syllable>*-
  \regex_replace_all:nnN { \*(.*?)\* } { -*\1*- } \l_leoboiko_stress_sentence_tl
  % change <space> into -<space>
  \regex_replace_all:nnN { \s } { -\cS\  } \l_leoboiko_stress_sentence_tl
  % change <space> into <space>-
  \regex_replace_all:nnN { \s } { \cS\ - } \l_leoboiko_stress_sentence_tl
  % add - at either end
  \regex_replace_once:nnN { \A (.*) \Z } { - \1 - } \l_leoboiko_stress_sentence_tl
  % normalize -- to -
  \regex_replace_all:nnN { \-\- } { \cO\- } \l_leoboiko_stress_sentence_tl
  % change -<letters>- into \uns{letters}-
  \regex_replace_all:nnN { \-([[:alpha:]]*?)\- } { \c{uns}\cB\{\1\cE\}- } \l_leoboiko_stress_sentence_tl
  % do it again
  \regex_replace_all:nnN { \-([[:alpha:]]*?)\- } { \c{uns}\cB\{\1\cE\}- } \l_leoboiko_stress_sentence_tl
  % remove the surplus -
  \regex_replace_all:nnN { \- } { } \l_leoboiko_stress_sentence_tl
  % change *<syllable>* into \str{<syllable>}
  \regex_replace_all:nnN { \*(.*?)\* } { \c{str}\cB\{\1\cE\} } \l_leoboiko_stress_sentence_tl
  % print
  \tl_use:N \l_leoboiko_stress_sentence_tl
 }

\ExplSyntaxOff

\begin{document}

\uns{A} \uns{po}\uns{ly}\str{syl}\uns{la}\uns{ble} \uns{should} \uns{be} \str{fun} \uns{to} \uns{de}\str{fine}

\stress{A po-ly*syl*la-ble should be *fun* to de*fine*}

\end{document}

enter image description here

1
  • there's a regex replace⁇ that's cheating! kidding, this is wonderful. thank you! Commented May 16, 2017 at 8:15

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .