1

I want to write my own document class as a literate program with as minimal of a dependency tree as possible as an exercise to test my ability.

I've implemented a stable foreach loop by shredding an argument, but similarly attempting to parse a calendar failed.

\def\@pointer{\@pointer}
\long\def\@iterate#1#2#3,{
    \def\cell{#3}
    \ifx\cell\@pointer
        \let\next=\relax
    \else
        \def#1{#3}
        #2
        \def\next{\@iterate{#1}{#2}}
    \fi
    \next
}
\long\gdef\foreach#1in#2#3%
{\@iterate{#1}{#3}#2,\@pointer,}

\def\@testOccidental#1 #2, #3{
    \def\subject{#1}
    \foreach \month in {January,February,March,April,May,June,July,August,October,September,November,December}{
        \ifx\subject\month
            #1 #2, #3 (Occidental Solar)
        \fi
    }
}
\def\checkCalendar#1{
    \def\subject{#1}
    \@testOccidental\subject
}

\checkCalendar{\today}
  • 2
    \@testOccidental is defined with three arguments, you only pass one. – egreg Jun 07 '19 at 20:58
  • 3
    what do you mean by "in what ways can you write a parser"? xmltex for example implements an xml parser using no packages (or even latex format) commands it just uses tex primitives so it can be loaded in initex with no preloaded format. – David Carlisle Jun 07 '19 at 21:17
  • 1
    you are missing lots of % your iterate loop will insert several spaces at each iteration. – David Carlisle Jun 07 '19 at 21:18
  • 1
    See listofitems.tex as a plain-TeX package input, for nested parsing tools. https://ctan.org/pkg/listofitems – Steven B. Segletes Jun 07 '19 at 22:08
  • See also https://tex.stackexchange.com/questions/233085/basics-of-parsing?r=SearchResults&s=1|37.1904 – John Kormylo Jun 08 '19 at 01:41

1 Answers1

5

The question title asks an unanswerable question about how (in general) to write a parser, but restricting to the code posted

\catcode`\@=11

\def\@pointer{\@pointer}
\long\def\@iterate#1#2#3,{
    \def\cell{#3}
    \ifx\cell\@pointer
        \let\next=\relax
    \else
        \def#1{#3}
        #2
        \def\next{\@iterate{#1}{#2}}
    \fi
    \next
}



\long\gdef\foreach#1in#2#3%
{\@iterate{#1}{#3}#2,\@pointer,}

X\foreach \z in {a,b,c,d}{[\z]}X

\bye

The loop works but adds a lot of space to the output

enter image description here

Removing the space tokens from the definition prodces

enter image description here

\catcode`\@=11

\def\@pointer{\@pointer}
\long\def\@iterate#1#2#3,{%
    \def\cell{#3}%
    \ifx\cell\@pointer
        \let\next=\relax
    \else
        \def#1{#3}%
        #2%
        \def\next{\@iterate{#1}{#2}}%
    \fi
    \next
}



\long\gdef\foreach#1in#2#3%
{\@iterate{#1}{#3}#2,\@pointer,}

X\foreach \z in {a,b,c,d}{[\z]}X

\bye

It is harder to guess the intent of the calendar example.

\def\@testOccidental#1 #2, #3{

defines a command that takes three arguments, the first delimited by a space, the second by a comma and space and the third being an un-delimited {..} argument.

You only use this in one place, as \@testOccidental\subject where there is just a single token and no space or comma delimiters. \subject is defined by \def\subject{\today} so \ifx\subject\month is always going to be false, (and \month is a bad name for the loop variable as it is a TeX primitive for the current month number)

Trying to guess the intention of the loop over the months, perhaps something like:

enter image description here

\catcode`\@=11

\def\@pointer{\@pointer}
\long\def\@iterate#1#2#3,{%
    \def\cell{#3}%
    \ifx\cell\@pointer
        \let\next=\relax
    \else
        \def#1{#3}%
        #2%
        \def\next{\@iterate{#1}{#2}}%
    \fi
    \next
}



\long\gdef\foreach#1in#2#3%
{\@iterate{#1}{#3}#2,\@pointer,}

X\foreach \z in {a,b,c,d}{[\z]}X






\def\@testOccidental#1 #2, #3\relax{%
    \def\subject{#1}%
    \foreach \xmonth in {January,February,March,April,May,June,July,August,October,September,November,December}{%
        \ifx\subject\xmonth
            #1 #2, #3 (Occidental Solar)\par
        \else
             not \xmonth\par % just for debugging
        \fi
    }%
}
\def\checkCalendar#1{%
    \@testOccidental#1\relax
}

\checkCalendar{June 7, 2019}

\bye
David Carlisle
  • 757,742
  • Not my intention to limit the answer, but I want to share my progress with one way. So other than a few whitespace errors, the code only failed due to \month turning out a TeX primitive.

    Of course, I'd appreciate knowing about more ways to write a parser in TeX.

    – Anonymouse Jun 13 '19 at 16:22
  • What purpose might \relax serve in the code? – Anonymouse Jun 13 '19 at 16:24
  • @Anonymouse \relax is a non expandable tex primitive that does nothing, I'm using it mostly just to delimit arguments here – David Carlisle Jun 13 '19 at 16:48