Add a TeX driver
[pspp] / src / output / tex-parsing.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2020 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "gl/xalloc.h"
20
21 #include "tex-parsing.h"
22 #include "libpspp/ll.h"
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 enum state
29   {
30    STATE_INITIAL,
31    STATE_CS,
32    STATE_COMMENT
33   };
34
35
36 /* Return the category of C.
37    These are the default TeX categories as defined in Chapter 7 of
38    The TeXbook  */
39 static enum tex_cat
40 category (const char c)
41 {
42   if (c >= 'A' && c <= 'Z')
43     return CAT_LETTER;
44
45   if (c >= 'a' && c <= 'z')
46     return CAT_LETTER;
47
48   switch (c)
49     {
50     case '\\':
51       return CAT_ESCAPE;
52     case '{':
53       return CAT_BEGIN_GROUP;
54     case '}':
55       return CAT_END_GROUP;
56     case '$':
57       return CAT_MATH_MODE;
58     case '&':
59       return CAT_ALIGNMENT;
60     case '#':
61       return CAT_PARAMETER;
62     case '^':
63       return CAT_SUPERSCRIPT;
64     case '_':
65       return CAT_SUBSCRIPT;
66     case '~':
67       return CAT_ACTIVE;
68     case ' ':
69     case '\t':
70       return CAT_SPACE;
71     case '\n':
72     case '\r':
73       return CAT_EOL;
74     case '%':
75       return CAT_COMMENT;
76     case 127:
77       return CAT_INVALID;
78     case 0:
79       return CAT_IGNORED;
80     }
81
82   return CAT_OTHER;
83 }
84
85
86 /* Parse the TeX fragment STR into TeX tokens and push them
87    on to LIST. */
88 void
89 tex_parse (const char *str, struct ll_list *list)
90 {
91   enum state state = STATE_INITIAL;
92   struct tex_token *token = NULL;
93   int c;
94   while ((c = *str++) != '\0')
95     {
96       enum tex_cat cat = category (c);
97
98       if (state == STATE_COMMENT)
99         {
100           ds_put_byte (&token->str, c);
101           if (cat == CAT_EOL)
102             {
103               token->cat = CAT_COMMENT;
104               ll_push_tail (list, &token->ll);
105               state = STATE_INITIAL;
106             }
107         }
108       else if (state == STATE_INITIAL)
109         {
110           token = XZALLOC (struct tex_token);
111           ds_init_empty (&token->str);
112           if (cat == CAT_COMMENT)
113             {
114               ds_put_byte (&token->str, c);
115               state = STATE_COMMENT;
116             }
117           else if (cat == CAT_ESCAPE)
118             {
119               ds_put_byte (&token->str, c);
120               state = STATE_CS;
121             }
122           else
123             {
124               ds_put_byte (&token->str, c);
125               token->cat = category (c);
126               ll_push_tail (list, &token->ll);
127             }
128         }
129       else if (state == STATE_CS)
130         {
131           ds_put_byte (&token->str, c);
132           if (cat != CAT_LETTER)
133             {
134               if (ds_length (&token->str) > 2)
135                 {
136                   ds_truncate (&token->str, ds_length (&token->str) - 1);
137                   str--;
138                 }
139               token->cat = CAT_CONTROL_SEQ;
140               ll_push_tail (list, &token->ll);
141               state = STATE_INITIAL;
142             }
143         }
144     }
145   if (state == STATE_CS)
146     {
147       /* The end of the string was encountered whilst processing
148          a control sequence.  */
149
150       /* A \ at the end of the string must be erroneous.  */
151       assert (ds_length (&token->str) > 1);
152       token->cat = CAT_CONTROL_SEQ;
153       ll_push_tail (list, &token->ll);
154       state = STATE_INITIAL;
155     }
156
157   assert (state == STATE_INITIAL);
158 }