6

I am rewriting a package I wrote for XeLaTeX to handle unicode blocks. This means dealing with setting up character classes for all characters from block "BlockName" starting at codepoint 1234 and ending at codepoint 5678.

Right now I have a macro that takes three inputs, and transforms them as:

\newcounter{glyphcounter}
\newcommand{\@defineUnicodeClass}[3]{
 \newXeTeXintercharclass#1
 \forloop{glyphcounter}{#2}{\value{glyphcounter}<#3}\XeTeXcharclass\value{glyphcounter}=#1}
 \XeTeXcharclass#3=#1}

This then gets called many, many times:

\@defineUnicodeClass{\AegeanNumbersClass}{65792}{65855}
\@defineUnicodeClass{\AlphabeticPresentationFormsClass}{64256}{64335}
\@defineUnicodeClass{\AncientGreekMusicalNotationClass}{119296}{119375}
...

I'd like to compact this in such a way that I can simply define the list of blocks, a la {{AegeanNumbers,65792,65855},{AlphabeticPresentationForms,64256,64335},{AncientGreekMusicalNotation,119296,119375},...} and then run through this list to generate everything for each block.

I was thinking about writing the data definition as {AegeanNumbers,65792,65855,AlphabeticPresentationForms,64256,64335,AncientGreekMusicalNotation,119296,119375,...}, so just a concatenated string in which every first%3 term is a new block definition, then having a macro that checks whether the input is empty, and if not takes the first three terms in that list, deals with them, then recurses with the list "tail", but I'm having a hard time implementing this in TeX.

If this is the best way to go about it, how do I do so? If not, what's a better way to run through this kind of data?

I had a look at http://maraist.org/comma-separated-lists-in-latex_08-2009 to figure out how to get the head/tail splitting to happen, but I am not having much luck getting this to work in XeLaTeX. I tried

\def\myTestCommand#1,#2{#1}

just to see whether it would do something, but calling it with "\myTestCommand{a,b,c,d}" just leads to a "runaway argument?" error.

I also had a look at the \forcsvlist command from the etoolbox package, but from what I can tell this only understands a comma separated list in which every element is the same kind of thing, and has no concept of tuples. I could write a switching macro with two state variable macros to effect an "if states empty, fill first var, if one filled, fill second var, if both filled, call the following macro and empty both vars" but if someone has a cleaner approach, that would be much preferred.

4
  • I'm sure \foreach from pgffor would be able to manage this. Look at the package documentation...
    – Werner
    Commented Oct 6, 2011 at 21:28
  • that suffers from the same issue as \forcsvlist; it doesn't understand compound lists, so processing triplets requires the same bit of hackery. Commented Oct 6, 2011 at 21:54
  • I clearly missed the part where a tuple delimiter can be given in the foreach command. Nice recommendation - can't mark that as "the answer" while it's just a comment though, so if you care to post it as answer, I shall gladly mark that as the correct answer. Commented Oct 6, 2011 at 22:04
  • I am not very familiar with the many possibilities offered by pgf (including pgffor), so someone else might be able to help you set something up.
    – Werner
    Commented Oct 7, 2011 at 7:04

3 Answers 3

3

Here are two ways to handle the definition of the commands as well as the iteration. Both require the definition of a macro that can define the commands:

\def\CommandFactory#1#2#3{%
      \expandafter\gdef\csname#1\endcsname{#1 class}
      \expandafter\gdef\csname#1start\endcsname{#2}
      \expandafter\gdef\csname#1end\endcsname{#3}
 }

This can be viewed as a utility to define objects or struct. In the above three commands are defined for example aegean, aegeanstart and aegeanend. If you use a dot or an @ the structure is more obvious, aegean@start, aegean@end.

Now back to iteration, the first method uses \@elt, which is a short for element and a Lisp relic found in LaTeX.

\def\@elt#1,#2,#3,{\CommandFactory{#1}{#2}{#3}} 
\def\unicoderange{\@elt test,1,2,\@elt testi,2,3,\@elt testii,4,5,}

this is Knuth's way of handling lists (he uses \\ rather than \@elt).

The second way to handle iteration is to use LaTeX's @for with an example shown in the minimal below. I have used \i to make it more intuitive so be aware to run the code through a group or save and restore its definition otherwise the dotless i will disappear!

\documentclass{article}
\begin{document}

\def\CommandFactory#1#2#3{%
  \expandafter\gdef\csname#1\endcsname{#1 class}
  \expandafter\gdef\csname#1start\endcsname{#2}
  \expandafter\gdef\csname#1end\endcsname{#3}
}

\def\unicodeClasses{aegean,musical,alphabetic}

\CommandFactory{aegean}{65792}{65855}
\CommandFactory{musical}{119296}{119375}
\CommandFactory{alphabetic}{64256}{64335}

\makeatletter
\@for \i:=\unicodeClasses \do{
   \csname\i\endcsname, 
   \csname\i start\endcsname-% 
   \csname\i end\endcsname, 
}


%% another way to iterate using @elt
\def\@elt#1,#2,#3,{\CommandFactory{#1}{#2}{#3}} 
\def\unicoderange{\@elt test,1,2,\@elt testi,2,3,\@elt testii,4,5,}

\unicoderange

\testi
\makeatother

\end{document}
1

I don't know the looping macros for LaTeX, but this is how you can process a nested comma list in ConTeXt

\def\definecharacterlist
    {\dosingleargument\dodefinecharacterlist}

\def\dodefinecharacterlist[#1]%
    {\processcommalist[#1]\defineUnicodeCharacter}

\def\defineUnicodeCharacter#1%
    {\dostartdefineUnicodeCharacter#1\dostopdefineUnicodeCharacter}

\def\dostartdefineUnicodeCharacter#1,#2,#3\dostopdefineUnicodeCharacter
    {\writestatus{DEBUG}{1: #1; 2: #2; 3: #3}}

\definecharacterlist[{AegeanNumbers,65792,65855},{AlphabeticPresentationForms,64256,64335},{AncientGreekMusicalNotation,119296,119375}]

The \dosingleargument is just ConTeXt's way of doing optional arguments. You can replace \processcommalist by the equivalent LaTeX looping macro, and replace \writestatus by any macro that you want.

1

Would a syntax such as

\@defineUnicodeclasses{AegeanNumbers/65792/65855,
  AlphabeticPresentationForms/64256/64335,
  AncientGreekMusicalNotation/119296/119375}

be good? I think so. Then

\def\@defineUnicodeclasses#1{%
  \def\@Unicodeclasseslist{#1}%
  \@for\next:=\@Unicodeclasseslist\do
    {\expandafter\do@Unicodeclasslist\next\@nil}}
\def\do@Unicodeclasslist#1/#2/#3\@nil{%
  \count@=\numexpr#2-1\relax
  \expandafter\newXeTeXintercharclass\csname#1\endcsname
  \loop\ifnum\count@<#3\relax
    \advance\count@\@ne
    \XeTeXcharclass\count@=\csname#1\endcsname
  \repeat}

should be what you need. But I don't see much advantage over multiple \@defineUnicodeclass declarations.

3
  • That code generates a "Runaway Argument?" error for me when I run it through xelatex. Commented Oct 6, 2011 at 23:34
  • @Mike'Pomax'Kamermans It's exactly the same code, which doesn't produce errors to me.
    – egreg
    Commented Oct 7, 2011 at 7:46
  • @egreg: no need for the \expandafter in the second-to-last line: the \csname construction is expanded when TeX looks for a number. Commented Oct 22, 2011 at 23:10

You must log in to answer this question.

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