10b5176b54f57082e9b61f30eab970aba2e70fc6
[platform/upstream/groff.git] / src / preproc / eqn / delim.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1989-2014  Free Software Foundation, Inc.
3      Written by James Clark (jjc@jclark.com)
4
5 This file is part of groff.
6
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19
20 #include "eqn.h"
21 #include "pbox.h"
22
23 enum left_or_right_t { LEFT_DELIM = 01, RIGHT_DELIM = 02 };
24
25 // Small must be none-zero and must exist in each device.
26 // Small will be put in the roman font, others are assumed to be
27 // on the special font (so no font change will be necessary.)
28
29 struct delimiter {
30   const char *name;
31   int flags;
32   const char *small;
33   const char *chain_format;
34   const char *ext;
35   const char *top;
36   const char *mid;
37   const char *bot;
38 } delim_table[] = {
39   {
40     "(", LEFT_DELIM|RIGHT_DELIM, "(", "\\[parenleft%s]",
41     "\\[parenleftex]",
42     "\\[parenlefttp]",
43     0,
44     "\\[parenleftbt]",
45   },
46   {
47     ")", LEFT_DELIM|RIGHT_DELIM, ")", "\\[parenright%s]",
48     "\\[parenrightex]",
49     "\\[parenrighttp]",
50     0,
51     "\\[parenrightbt]",
52   },
53   {
54     "[", LEFT_DELIM|RIGHT_DELIM, "[", "\\[bracketleft%s]",
55     "\\[bracketleftex]",
56     "\\[bracketlefttp]",
57     0,
58     "\\[bracketleftbt]",
59   },
60   {
61     "]", LEFT_DELIM|RIGHT_DELIM, "]", "\\[bracketright%s]",
62     "\\[bracketrightex]",
63     "\\[bracketrighttp]",
64     0,
65     "\\[bracketrightbt]",
66   },
67   {
68     "{", LEFT_DELIM|RIGHT_DELIM, "{", "\\[braceleft%s]",
69     "\\[braceleftex]",
70     "\\[bracelefttp]",
71     "\\[braceleftmid]",
72     "\\[braceleftbt]",
73   },
74   {
75     "}", LEFT_DELIM|RIGHT_DELIM, "}", "\\[braceright%s]",
76     "\\[bracerightex]",
77     "\\[bracerighttp]",
78     "\\[bracerightmid]",
79     "\\[bracerightbt]",
80   },
81   {
82     "|", LEFT_DELIM|RIGHT_DELIM, "|", "\\[bar%s]",
83     "\\[barex]",
84     0,
85     0,
86     0,
87   },
88   {
89     "floor", LEFT_DELIM, "\\(lf", "\\[floorleft%s]",
90     "\\[bracketleftex]",
91     0,
92     0,
93     "\\[bracketleftbt]",
94   },
95   {
96     "floor", RIGHT_DELIM, "\\(rf", "\\[floorright%s]",
97     "\\[bracketrightex]",
98     0,
99     0,
100     "\\[bracketrightbt]",
101   },
102   {
103     "ceiling", LEFT_DELIM, "\\(lc", "\\[ceilingleft%s]",
104     "\\[bracketleftex]",
105     "\\[bracketlefttp]",
106     0,
107     0,
108   },
109   {
110     "ceiling", RIGHT_DELIM, "\\(rc", "\\[ceilingright%s]",
111     "\\[bracketrightex]",
112     "\\[bracketrighttp]",
113     0,
114     0,
115   },
116   {
117     "||", LEFT_DELIM|RIGHT_DELIM, "|", "\\[bar%s]",
118     "\\[bardblex]",
119     0,
120     0,
121     0,
122   },
123   {
124     "<", LEFT_DELIM|RIGHT_DELIM, "\\(la", "\\[angleleft%s]",
125     0,
126     0,
127     0,
128     0,
129   },
130   {
131     ">", LEFT_DELIM|RIGHT_DELIM, "\\(ra", "\\[angleright%s]",
132     0,
133     0,
134     0,
135     0,
136   },
137   {
138     "uparrow", LEFT_DELIM|RIGHT_DELIM, "\\(ua", "\\[arrowup%s]",
139     "\\[arrowvertex]",
140     "\\[arrowverttp]",
141     0,
142     0,
143   },
144   {
145     "downarrow", LEFT_DELIM|RIGHT_DELIM, "\\(da", "\\[arrowdown%s]",
146     "\\[arrowvertex]",
147     0,
148     0,
149     "\\[arrowvertbt]",
150   },
151   {
152     "updownarrow", LEFT_DELIM|RIGHT_DELIM, "\\(va", "\\[arrowupdown%s]",
153     "\\[arrowvertex]",
154     "\\[arrowverttp]",
155     0,
156     "\\[arrowvertbt]",
157   },
158 };
159
160 const int DELIM_TABLE_SIZE = int(sizeof(delim_table)/sizeof(delim_table[0]));
161
162 class delim_box : public box {
163 private:
164   char *left;
165   char *right;
166   box *p;
167 public:
168   delim_box(char *, box *, char *);
169   ~delim_box();
170   int compute_metrics(int);
171   void output();
172   void check_tabs(int);
173   void debug_print();
174 };
175
176 box *make_delim_box(char *l, box *pp, char *r)
177 {
178   if (l != 0 && *l == '\0') {
179     a_delete l;
180     l = 0;
181   }
182   if (r != 0 && *r == '\0') {
183     a_delete r;
184     r = 0;
185   }
186   return new delim_box(l, pp, r);
187 }
188
189 delim_box::delim_box(char *l, box *pp, char *r)
190 : left(l), right(r), p(pp)
191 {
192 }
193
194 delim_box::~delim_box()
195 {
196   a_delete left;
197   a_delete right;
198   delete p;
199 }
200
201 static void build_extensible(const char *ext, const char *top, const char *mid,
202                              const char *bot)
203 {
204   assert(ext != 0);
205   printf(".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
206          ext);
207   printf(".nr " EXT_HEIGHT_REG " 0\\n[rst]\n");
208   printf(".nr " EXT_DEPTH_REG " 0-\\n[rsb]\n");
209   if (top) {
210     printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]"
211            ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
212            top);
213     printf(".nr " TOP_HEIGHT_REG " 0\\n[rst]\n");
214     printf(".nr " TOP_DEPTH_REG " 0-\\n[rsb]\n");
215   }
216   if (mid) {
217     printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]"
218            ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
219            mid);
220     printf(".nr " MID_HEIGHT_REG " 0\\n[rst]\n");
221     printf(".nr " MID_DEPTH_REG " 0-\\n[rsb]\n");
222   }
223   if (bot) {
224     printf(".nr " DELIM_WIDTH_REG " 0\\n[" DELIM_WIDTH_REG "]"
225            ">?\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n",
226            bot);
227     printf(".nr " BOT_HEIGHT_REG " 0\\n[rst]\n");
228     printf(".nr " BOT_DEPTH_REG " 0-\\n[rsb]\n");
229   }
230   printf(".nr " TOTAL_HEIGHT_REG " 0");
231   if (top)
232     printf("+\\n[" TOP_HEIGHT_REG "]+\\n[" TOP_DEPTH_REG "]");
233   if (bot)
234     printf("+\\n[" BOT_HEIGHT_REG "]+\\n[" BOT_DEPTH_REG "]");
235   if (mid)
236     printf("+\\n[" MID_HEIGHT_REG "]+\\n[" MID_DEPTH_REG "]");
237   printf("\n");
238   // determine how many extensible characters we need
239   printf(".nr " TEMP_REG " \\n[" DELTA_REG "]-\\n[" TOTAL_HEIGHT_REG "]");
240   if (mid)
241     printf("/2");
242   printf(">?0+\\n[" EXT_HEIGHT_REG "]+\\n[" EXT_DEPTH_REG "]-1/(\\n["
243          EXT_HEIGHT_REG "]+\\n[" EXT_DEPTH_REG "])\n");
244   
245   printf(".nr " TOTAL_HEIGHT_REG " +(\\n[" EXT_HEIGHT_REG "]+\\n["
246          EXT_DEPTH_REG "]*\\n[" TEMP_REG "]");
247   if (mid)
248     printf("*2");
249   printf(")\n");
250   printf(".ds " DELIM_STRING " \\Z" DELIMITER_CHAR
251          "\\v'-%dM-(\\n[" TOTAL_HEIGHT_REG "]u/2u)'\n",
252          axis_height);
253   if (top)
254     printf(".as " DELIM_STRING " \\v'\\n[" TOP_HEIGHT_REG "]u'"
255            "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
256            "\\v'\\n[" TOP_DEPTH_REG "]u'\n",
257            top);
258
259   // this macro appends $2 copies of $3 to string $1
260   printf(".de " REPEAT_APPEND_STRING_MACRO "\n"
261          ".if \\\\$2 \\{.as \\\\$1 \"\\\\$3\n"
262          "." REPEAT_APPEND_STRING_MACRO " \\\\$1 \\\\$2-1 \"\\\\$3\"\n"
263          ".\\}\n"
264          "..\n");
265
266   printf("." REPEAT_APPEND_STRING_MACRO " " DELIM_STRING " \\n[" TEMP_REG "] "
267          "\\v'\\n[" EXT_HEIGHT_REG "]u'"
268          "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR 
269          "\\v'\\n[" EXT_DEPTH_REG "]u'\n",
270          ext);
271
272   if (mid) {
273     printf(".as " DELIM_STRING " \\v'\\n[" MID_HEIGHT_REG "]u'"
274            "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
275            "\\v'\\n[" MID_DEPTH_REG "]u'\n",
276            mid);
277     printf("." REPEAT_APPEND_STRING_MACRO " " DELIM_STRING 
278            " \\n[" TEMP_REG "] "
279            "\\v'\\n[" EXT_HEIGHT_REG "]u'"
280            "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
281            "\\v'\\n[" EXT_DEPTH_REG "]u'\n",
282            ext);
283   }
284   if (bot)
285     printf(".as " DELIM_STRING " \\v'\\n[" BOT_HEIGHT_REG "]u'"
286            "\\Z" DELIMITER_CHAR "%s" DELIMITER_CHAR
287            "\\v'\\n[" BOT_DEPTH_REG "]u'\n",
288            bot);
289   printf(".as " DELIM_STRING " " DELIMITER_CHAR "\n");
290 }
291
292 static void define_extensible_string(char *delim, int uid,
293                                      left_or_right_t left_or_right)
294 {
295   printf(".ds " DELIM_STRING "\n");
296   delimiter *d = delim_table;
297   int delim_len = strlen(delim);
298   int i;
299   for (i = 0; i < DELIM_TABLE_SIZE; i++, d++)
300     if (strncmp(delim, d->name, delim_len) == 0 
301         && (left_or_right & d->flags) != 0)
302       break;
303   if (i >= DELIM_TABLE_SIZE) {
304     error("there is no `%1' delimiter", delim);
305     printf(".nr " DELIM_WIDTH_REG " 0\n");
306     return;
307   }
308
309   printf(".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "\\f[%s]%s\\fP" DELIMITER_CHAR "\n"
310          ".ds " DELIM_STRING " \\Z" DELIMITER_CHAR
311            "\\v'\\n[rsb]u+\\n[rst]u/2u-%dM'\\f[%s]%s\\fP" DELIMITER_CHAR "\n"
312          ".nr " TOTAL_HEIGHT_REG " \\n[rst]-\\n[rsb]\n"
313          ".if \\n[" TOTAL_HEIGHT_REG "]<\\n[" DELTA_REG "] "
314          "\\{",
315          current_roman_font, d->small, axis_height,
316          current_roman_font, d->small);
317          
318   char buf[256];
319   sprintf(buf, d->chain_format, "\\\\n[" INDEX_REG "]");
320   printf(".nr " INDEX_REG " 0\n"
321          ".de " TEMP_MACRO "\n"
322          ".ie c%s \\{\\\n"
323          ".nr " DELIM_WIDTH_REG " 0\\w" DELIMITER_CHAR "%s" DELIMITER_CHAR "\n"
324          ".ds " DELIM_STRING " \\Z" DELIMITER_CHAR
325            "\\v'\\\\n[rsb]u+\\\\n[rst]u/2u-%dM'%s" DELIMITER_CHAR "\n"
326          ".nr " TOTAL_HEIGHT_REG " \\\\n[rst]-\\\\n[rsb]\n"
327          ".if \\\\n[" TOTAL_HEIGHT_REG "]<\\n[" DELTA_REG "] "
328          "\\{.nr " INDEX_REG " +1\n"
329          "." TEMP_MACRO "\n"
330          ".\\}\\}\n"
331          ".el .nr " INDEX_REG " 0-1\n"
332          "..\n"
333          "." TEMP_MACRO "\n",
334          buf, buf, axis_height, buf);
335   if (d->ext) {
336     printf(".if \\n[" INDEX_REG "]<0 \\{.if c%s \\{\\\n", d->ext);
337     build_extensible(d->ext, d->top, d->mid, d->bot);
338     printf(".\\}\\}\n");
339   }
340   printf(".\\}\n");
341   printf(".as " DELIM_STRING " \\h'\\n[" DELIM_WIDTH_REG "]u'\n");
342   printf(".nr " WIDTH_FORMAT " +\\n[" DELIM_WIDTH_REG "]\n", uid);
343   printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]"
344          ">?(\\n[" TOTAL_HEIGHT_REG "]/2+%dM)\n",
345          uid, uid, axis_height);
346   printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]"
347          ">?(\\n[" TOTAL_HEIGHT_REG "]/2-%dM)\n",
348          uid, uid, axis_height);
349 }
350
351 int delim_box::compute_metrics(int style)
352 {
353   int r = p->compute_metrics(style);
354   printf(".nr " WIDTH_FORMAT " 0\\n[" WIDTH_FORMAT "]\n", uid, p->uid);
355   printf(".nr " HEIGHT_FORMAT " \\n[" HEIGHT_FORMAT "]\n", uid, p->uid);
356   printf(".nr " DEPTH_FORMAT " \\n[" DEPTH_FORMAT "]\n", uid, p->uid);
357   printf(".nr " DELTA_REG " \\n[" HEIGHT_FORMAT "]-%dM"
358          ">?(\\n[" DEPTH_FORMAT "]+%dM)\n",
359          p->uid, axis_height, p->uid, axis_height);
360   printf(".nr " DELTA_REG " 0\\n[" DELTA_REG "]*%d/500"
361          ">?(\\n[" DELTA_REG "]*2-%dM)\n",
362          delimiter_factor, delimiter_shortfall);
363   if (left) {
364     define_extensible_string(left, uid, LEFT_DELIM);
365     printf(".rn " DELIM_STRING " " LEFT_DELIM_STRING_FORMAT "\n",
366            uid);
367     if (r)
368       printf(".nr " MARK_REG " +\\n[" DELIM_WIDTH_REG "]\n");
369   }
370   if (right) {
371     define_extensible_string(right, uid, RIGHT_DELIM);
372     printf(".rn " DELIM_STRING " " RIGHT_DELIM_STRING_FORMAT "\n",
373            uid);
374   }
375   return r;
376 }
377
378 void delim_box::output()
379 {
380   if (output_format == troff) {
381     if (left)
382       printf("\\*[" LEFT_DELIM_STRING_FORMAT "]", uid);
383     p->output();
384     if (right)
385       printf("\\*[" RIGHT_DELIM_STRING_FORMAT "]", uid);
386   }
387   else if (output_format == mathml) {
388     printf("<mrow><mo>%s</mo>", left);
389     p->output();
390     printf("<mo>%s</mo></mrow>", right);
391   }
392 }
393
394 void delim_box::check_tabs(int level)
395 {
396   p->check_tabs(level);
397 }
398
399 void delim_box::debug_print()
400 {
401   fprintf(stderr, "left \"%s\" { ", left ? left : "");
402   p->debug_print();
403   fprintf(stderr, " }");
404   if (right)
405     fprintf(stderr, " right \"%s\"", right);
406 }
407