Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

Having to treat a considerable amount of page ranges, I'd like to have a robust and simple command that would parse and print them correctly.

  1. The most simple case is e.g.

    264,15-26

    which means:

    page 264, lines 15 to 26

  2. Then we have things like

    264,15-266,26

    which translates to

    page 264 line 15 to page 266 line 26

  3. More tricky, we'd have something like

    264,15-266,26+277,13-14+312,14-316,23

    which means we actually have three sequences here:

    from page 264 line 15 to page 266 line 26 and page 277 lines 13-14 and from page 312 line 14 to page 316 line 23

    That's to say, the command should work whatever the amount of "type 1" or "type 2" sequences is provided to it.

I'd like to put whatever the page range is (type 1 or 2 or 3) in a single command and have every one of them printed in the readable and accurate way... Pretty tricky for poor old humanities student like me!

share|improve this question

2 Answers 2

Here is a LuaLaTeX solution:

Minimal Working Example

\documentclass{article}
\directlua{dofile("parse.lua")}
\def\parserange#1{\directlua{tex.sprint(parsePageRanges("#1"))}}
\begin{document}

\begin{itemize}
\item \parserange{264,15-26}
\item \parserange{264,15-266,26}
\item \parserange{264,15-266,26+277,13-14+312,14-316,23}
\end{itemize}
\end{document}

Result

Result

Lua code

To be saved in the file parse.lua

function split(text, sep)
    sep = sep or "\n"
    local lines = {}
    local pos = 1
    while true do
        local b,e = text:find(sep, pos)
        if not b then table.insert(lines, text:sub(pos)) break end
        table.insert(lines, text:sub(pos, b-1))
        pos = e + 1
    end
    return lines
end

function parsePageRanges(s)
    ranges=split(s, "+")
    parsed_ranges = {}
    for i,range in ipairs(ranges) do
         ends = split(range, "-")
         if #ends ~=2 then
             return("Error in page range ``"..range.."''")
         end
         r={}
         r[1] = split(ends[1], ",")
         r[2] = split(ends[2], ",")
         if #r[2]==1 then
             p1=r[1][1]; l1=r[1][2]; l2=r[2][1]
             table.insert(parsed_ranges, "page "..p1..", lines "..l1.." to "..l2)
         else
             p1=r[1][1]; l1=r[1][2]; p2=r[2][1]; l2=r[2][2]
             table.insert(parsed_ranges, "from page "..p1.." line "..l1.." to page "..p2.." line "..l2)
         end
    end
    return table.concat(parsed_ranges, " and ")
end
share|improve this answer

Here's a LaTeX3 implementation.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn

\NewDocumentCommand{\parsepages}{m}
 {
  \pierre_parse_pages:n { #1 }
 }

\seq_new:N \l_pierre_blocks_seq
\seq_new:N \l_pierre_output_seq
\seq_new:N \l_pierre_temp_seq

\cs_new_protected:Npn \pierre_parse_pages:n #1
 {
  % decompose the argument into blocks delimited by +
  \seq_set_split:Nnn \l_pierre_blocks_seq { + } { #1 }
  \seq_clear:N \l_pierre_output_seq
  % process each block
  \seq_map_inline:Nn \l_pierre_blocks_seq
   {
    \pierre_parse_block:n { ##1 }
   }
  % print the result
  \seq_use:Nnnn \l_pierre_output_seq {~and~} {~and~} {~and~}
 }

\cs_new_protected:Npn \pierre_parse_block:n #1
 {
  \tl_if_in:nnTF { #1 } { - }
   {% we have a "complex block"; either a,b-c,d or a,b-c
    \pierre_parse_complex_block:n { #1 }
   }
   {% a "simple block" a,b
    \pierre_parse_simple_block:w #1 \q_stop
   }
 }

\cs_new_protected:Npn \pierre_parse_simple_block:w #1 , #2 \q_stop
 {
  \seq_put_right:Nn \l_pierre_output_seq
   {
    page~#1~line~#2
   }
 }

\cs_new_protected:Npn \pierre_parse_complex_block:n #1
 {
  \seq_set_split:Nnn \l_pierre_temp_seq { , } { #1 }
  \int_compare:nTF { \seq_count:N \l_pierre_temp_seq = 2 }
   {% only one comma: a,b-c
    \pierre_single_page:w #1 \q_stop
   }
   {% two commas: a,b-c,d
    \pierre_multi_page:w #1 \q_stop
   }
 }

\cs_new_protected:Npn \pierre_single_page:w #1 , #2 - #3 \q_stop
 {
  \seq_put_right:Nn \l_pierre_output_seq
   {
    page~#1~lines~#2~to~#3
   }
 }

\cs_new_protected:Npn \pierre_multi_page:w #1 , #2 - #3 , #4 \q_stop
 {
  \seq_put_right:Nn \l_pierre_output_seq
   {
    from~page~#1~line~#2~to~page~#3~line~#4
   }
 }
\ExplSyntaxOff

\begin{document}

% just for formatting the example
\setlength{\parindent}{0pt}
\setlength{\parskip}{1ex}

\parsepages{264,15}

\parsepages{264,15-26}

\parsepages{264,15-266,26}

\parsepages{264,15-266,26+277,13-14+312,14-316,23}

\end{document}

enter image description here

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.