1 % $Id: mp.web,v 1.8 2005/08/24 10:54:02 taco Exp $
2 % MetaPost, by John Hobby. Public domain.
4 % Much of this program was copied with permission from MF.web Version 1.9
5 % It interprets a language very similar to D.E. Knuth's METAFONT, but with
6 % changes designed to make it more suitable for PostScript output.
8 % TeX is a trademark of the American Mathematical Society.
9 % METAFONT is a trademark of Addison-Wesley Publishing Company.
10 % PostScript is a trademark of Adobe Systems Incorporated.
12 % Here is TeX material that gets inserted after \input webmac
13 \def\hang{\hangindent 3em\noindent\ignorespaces}
14 \def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
17 \def\ph{\hbox{Pascal-H}}
18 \def\psqrt#1{\sqrt{\mathstrut#1}}
20 \def\pct!{{\char`\%}} % percent sign in ordinary text
21 \font\tenlogo=logo10 % font used for the METAFONT logo
23 \def\MF{{\tenlogo META}\-{\tenlogo FONT}}
24 \def\MP{{\tenlogo META}\-{\tenlogo POST}}
25 \def\[#1]{#1.} % from pascal web
26 \def\<#1>{$\langle#1\rangle$}
27 \def\section{\mathhexbox278}
28 \let\swap=\leftrightarrow
29 \def\round{\mathop{\rm round}\nolimits}
30 \mathchardef\vb="026A % synonym for `\|'
32 \def\(#1){} % this is used to make section names sort themselves better
33 \def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
35 \def\glob{15} % this should be the section number of "<Global...>"
36 \def\gglob{23, 28} % this should be the next two sections of "<Global...>"
41 This is \MP, a graphics-language processor based on D. E. Knuth's \MF.
43 The main purpose of the following program is to explain the algorithms of \MP\
44 as clearly as possible. As a result, the program will not necessarily be very
45 efficient when a particular \PASCAL\ compiler has translated it into a
46 particular machine language. However, the program has been written so that it
47 can be tuned to run efficiently in a wide variety of operating environments
48 by making comparatively few changes. Such flexibility is possible because
49 the documentation that follows is written in the \.{WEB} language, which is
50 at a higher level than \PASCAL; the preprocessing step that converts \.{WEB}
51 to \PASCAL\ is able to introduce most of the necessary refinements.
52 Semi-automatic translation to other languages is also feasible, because the
53 program below does not make extensive use of features that are peculiar to
56 A large piece of software like \MP\ has inherent complexity that cannot
57 be reduced below a certain level of difficulty, although each individual
58 part is fairly simple by itself. The \.{WEB} language is intended to make
59 the algorithms as readable as possible, by reflecting the way the
60 individual program pieces fit together and by providing the
61 cross-references that connect different parts. Detailed comments about
62 what is going on, and about why things were done in certain ways, have
63 been liberally sprinkled throughout the program. These comments explain
64 features of the implementation, but they rarely attempt to explain the
65 \MP\ language itself, since the reader is supposed to be familiar with
66 {\sl The {\logos METAFONT\/}book} as well as the manual
68 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
69 {\sl A User's Manual for MetaPost}, Computing Science Technical Report 162,
70 AT\AM T Bell Laboratories.
72 @ The present implementation is a preliminary version, but the possibilities
73 for new features are limited by the desire to remain as nearly compatible
74 with \MF\ as possible.
76 On the other hand, the \.{WEB} description can be extended without changing
77 the core of the program, and it has been designed so that such
78 extensions are not extremely difficult to make.
79 The |banner| string defined here should be changed whenever \MP\
80 undergoes any modifications, so that it will be clear which version of
81 \MP\ might be the guilty party when a problem arises.
83 @^system dependencies@>
85 @d banner "This is MetaPost, Version 1.002" /* printed when \MP\ starts */
86 @d metapost_version "1.002"
87 @d mplib_version "0.10"
88 @d version_string " (Cweb version 0.10)"
90 @ Different \PASCAL s have slightly different conventions, and the present
92 program is expressed in a version of \PASCAL\ that D. E. Knuth used for \MF.
93 Constructions that apply to
94 this particular compiler, which we shall call \ph, should help the
95 reader see how to make an appropriate interface for other systems
96 if necessary. (\ph\ is Charles Hedrick's modification of a compiler
97 @^Hedrick, Charles Locke@>
98 for the DECsystem-10 that was originally developed at the University of
99 Hamburg; cf.\ {\sl SOFTWARE---Practice \AM\ Experience \bf6} (1976),
100 29--42. The \MP\ program below is intended to be adaptable, without
101 extensive changes, to most other versions of \PASCAL\ and commonly used
102 \PASCAL-to-C translators, so it does not fully
104 use the admirable features of \ph. Indeed, a conscious effort has been
105 made here to avoid using several idiosyncratic features of standard
106 \PASCAL\ itself, so that most of the code can be translated mechanically
107 into other high-level languages. For example, the `\&{with}' and `\\{new}'
108 features are not used, nor are pointer types, set types, or enumerated
109 scalar types; there are no `\&{var}' parameters, except in the case of files;
110 there are no tag fields on variant records; there are no |real| variables;
111 no procedures are declared local to other procedures.)
113 The portions of this program that involve system-dependent code, where
114 changes might be necessary because of differences between \PASCAL\ compilers
115 and/or differences between
116 operating systems, can be identified by looking at the sections whose
117 numbers are listed under `system dependencies' in the index. Furthermore,
118 the index entries for `dirty \PASCAL' list all places where the restrictions
119 of \PASCAL\ have not been followed perfectly, for one reason or another.
120 @^system dependencies@>
123 @ The program begins with a normal \PASCAL\ program heading, whose
124 components will be filled in later, using the conventions of \.{WEB}.
126 For example, the portion of the program called `\X\glob:Global
127 variables\X' below will be replaced by a sequence of variable declarations
128 that starts in $\section\glob$ of this documentation. In this way, we are able
129 to define each individual global variable when we are prepared to
130 understand what it means; we do not have to define all of the globals at
131 once. Cross references in $\section\glob$, where it says ``See also
132 sections \gglob, \dots,'' also make it possible to look at the set of
133 all global variables, if desired. Similar remarks apply to the other
134 portions of the program heading.
136 Actually the heading shown here is not quite normal: The |program| line
137 does not mention any |output| file, because \ph\ would ask the \MP\ user
138 to specify a file name if |output| were specified here.
139 @^system dependencies@>
145 # ifndef LIBAVL_ALLOCATOR
146 # define LIBAVL_ALLOCATOR
147 struct libavl_allocator {
148 void *(*libavl_malloc) (struct libavl_allocator *, size_t libavl_size);
149 void (*libavl_free) (struct libavl_allocator *, void *libavl_block);
152 typedef struct psout_data_struct * psout_data;
153 typedef struct MP_instance * MP;
155 typedef signed int integer;
156 @<Types in the outer block@>
157 typedef struct MP_options {
161 @<Exported function headers@>
164 @<Constants in the outer block@>
165 typedef struct MP_instance {
175 #include <unistd.h> /* for access() */
176 #include <time.h> /* for struct tm \& co */
178 #include "mpmp.h" /* internal header */
179 #include "mppsout.h" /* internal header */
182 @<Basic printing procedures@>
183 @<Error handling procedures@>
185 @ Here are the functions that set up the \MP\ instance.
188 @<Declare |mp_reallocate| functions@>;
189 struct MP_options *mp_options (void) {
190 struct MP_options *opt;
191 opt = xmalloc(1,sizeof(MP_options));
192 memset (opt,0,sizeof(MP_options));
195 MP mp_new (struct MP_options *opt) {
197 mp = xmalloc(1,sizeof(MP_instance));
198 @<Set |ini_version|@>;
199 @<Allocate or initialize variables@>
200 if (opt->main_memory>mp->mem_max)
201 mp_reallocate_memory(mp,opt->main_memory);
202 mp_reallocate_paths(mp,1000);
203 mp_reallocate_fonts(mp,8);
205 mp->term_out = stdout;
208 void mp_free (MP mp) {
209 int k; /* loop variable */
210 @<Dealloc variables@>
215 boolean mp_initialize (MP mp) { /* this procedure gets things started properly */
216 @<Local variables for initialization@>
217 mp->history=fatal_error_stop; /* in case we quit during initialization */
218 t_open_out; /* open the terminal for output */
219 @<Check the ``constant'' values...@>;
221 fprintf(stdout,"Ouch---my internal constants have been clobbered!\n"
222 "---case %i",(int)mp->bad);
226 @<Set initial values of key variables@>
227 if (mp->ini_version) {
228 @<Run inimpost commands@>;
230 @<Initialize the output routines@>;
231 @<Get the first line of input and prepare to start@>;
232 mp_set_job_id(mp,mp->internal[year],mp->internal[month],
233 mp->internal[day],mp->internal[mp_time]);
234 mp_init_map_file(mp, mp->troff_mode);
235 mp->history=spotless; /* ready to go! */
236 if (mp->troff_mode) {
237 mp->internal[gtroffmode]=unity;
238 mp->internal[prologues]=unity;
240 if ( mp->start_sym>0 ) { /* insert the `\&{everyjob}' symbol */
241 mp->cur_sym=mp->start_sym; mp_back_input(mp);
247 @<Exported function headers@>=
248 extern struct MP_options *mp_options (void);
249 extern MP mp_new (struct MP_options *opt) ;
250 extern void mp_free (MP mp);
251 extern boolean mp_initialize (MP mp);
254 @ The overall \MP\ program begins with the heading just shown, after which
255 comes a bunch of procedure declarations and function declarations.
256 Finally we will get to the main program, which begins with the
257 comment `|start_here|'. If you want to skip down to the
258 main program now, you can look up `|start_here|' in the index.
259 But the author suggests that the best way to understand this program
260 is to follow pretty much the order of \MP's components as they appear in the
261 \.{WEB} description you are now reading, since the present ordering is
262 intended to combine the advantages of the ``bottom up'' and ``top down''
263 approaches to the problem of understanding a somewhat complicated system.
265 @ Some of the code below is intended to be used only when diagnosing the
266 strange behavior that sometimes occurs when \MP\ is being installed or
267 when system wizards are fooling around with \MP\ without quite knowing
268 what they are doing. Such code will not normally be compiled; it is
269 delimited by the preprocessor test `|#ifdef DEBUG .. #endif|'.
271 @ This program has two important variations: (1) There is a long and slow
272 version called \.{INIMP}, which does the extra calculations needed to
274 initialize \MP's internal tables; and (2)~there is a shorter and faster
275 production version, which cuts the initialization to a bare minimum.
277 Which is which is decided at runtime.
279 @ The following parameters can be changed at compile time to extend or
280 reduce \MP's capacity. They may have different values in \.{INIMP} and
281 in production versions of \MP.
283 @^system dependencies@>
286 #define file_name_size 255 /* file names shouldn't be longer than this */
287 #define bistack_size 1500 /* size of stack for bisection algorithms;
288 should probably be left at this value */
290 @ Like the preceding parameters, the following quantities can be changed
291 at compile time to extend or reduce \MP's capacity. But if they are changed,
292 it is necessary to rerun the initialization program \.{INIMP}
294 to generate new tables for the production \MP\ program.
295 One can't simply make helter-skelter changes to the following constants,
296 since certain rather complex initialization
297 numbers are computed from them.
300 int max_strings; /* maximum number of strings; must not exceed |max_halfword| */
301 int pool_size; /* maximum number of characters in strings, including all
302 error messages and help texts, and the names of all identifiers */
303 int error_line; /* width of context lines on terminal error messages */
304 int half_error_line; /* width of first lines of contexts in terminal
305 error messages; should be between 30 and |error_line-15| */
306 int max_print_line; /* width of longest text lines output; should be at least 60 */
307 int mem_max; /* greatest index in \MP's internal |mem| array;
308 must be strictly less than |max_halfword|;
309 must be equal to |mem_top| in \.{INIMP}, otherwise |>=mem_top| */
310 int mem_top; /* largest index in the |mem| array dumped by \.{INIMP};
311 must not be greater than |mem_max| */
312 int hash_size; /* maximum number of symbolic tokens,
313 must be less than |max_halfword-3*param_size| */
314 int hash_prime; /* a prime number equal to about 85\pct! of |hash_size| */
315 int param_size; /* maximum number of simultaneous macro parameters */
316 int max_in_open; /* maximum number of input files and error insertions that
317 can be going on simultaneously */
319 @ @<Option variables@>=
330 @d set_value(a,b,c) do { a=c; if (b>c) a=b; } while (0)
335 set_value(mp->error_line,opt->error_line,79);
336 set_value(mp->half_error_line,opt->half_error_line,50);
337 set_value(mp->max_print_line,opt->max_print_line,79);
340 set_value(mp->hash_size,opt->hash_size,9500);
341 set_value(mp->hash_prime,opt->hash_prime,7919);
342 set_value(mp->param_size,opt->param_size,150);
343 set_value(mp->max_in_open,opt->max_in_open,10);
346 @ In case somebody has inadvertently made bad settings of the ``constants,''
347 \MP\ checks them using a global variable called |bad|.
349 This is the first of many sections of \MP\ where global variables are
353 integer bad; /* is some ``constant'' wrong? */
355 @ Later on we will say `\ignorespaces|if (mem_max>=max_halfword) bad=10;|',
356 or something similar. (We can't do that until |max_halfword| has been defined.)
358 @<Check the ``constant'' values for consistency@>=
360 if ( (mp->half_error_line<30)||(mp->half_error_line>mp->error_line-15) ) mp->bad=1;
361 if ( mp->max_print_line<60 ) mp->bad=2;
362 if ( mp->mem_top<=1100 ) mp->bad=4;
363 if (mp->hash_prime>mp->hash_size ) mp->bad=5;
365 @ Labels are given symbolic names by the following definitions, so that
366 occasional |goto| statements will be meaningful. We insert the label
367 `|exit|:' just before the `\ignorespaces|end|\unskip' of a procedure in
368 which we have used the `|return|' statement defined below; the label
369 `|restart|' is occasionally used at the very beginning of a procedure; and
370 the label `|reswitch|' is occasionally used just prior to a |case|
371 statement in which some cases change the conditions and we wish to branch
372 to the newly applicable case. Loops that are set up with the |loop|
373 construction defined below are commonly exited by going to `|done|' or to
374 `|found|' or to `|not_found|', and they are sometimes repeated by going to
375 `|continue|'. If two or more parts of a subroutine start differently but
376 end up the same, the shared code may be gathered together at
379 Incidentally, this program never declares a label that isn't actually used,
380 because some fussy \PASCAL\ compilers will complain about redundant labels.
382 @d label_exit 10 /* go here to leave a procedure */
383 @d restart 20 /* go here to start a procedure again */
384 @d reswitch 21 /* go here to start a case statement again */
385 @d continue 22 /* go here to resume a loop */
386 @d done 30 /* go here to exit a loop */
387 @d done1 31 /* like |done|, when there is more than one loop */
388 @d done2 32 /* for exiting the second loop in a long block */
389 @d done3 33 /* for exiting the third loop in a very long block */
390 @d done4 34 /* for exiting the fourth loop in an extremely long block */
391 @d done5 35 /* for exiting the fifth loop in an immense block */
392 @d done6 36 /* for exiting the sixth loop in a block */
393 @d found 40 /* go here when you've found it */
394 @d found1 41 /* like |found|, when there's more than one per routine */
395 @d found2 42 /* like |found|, when there's more than two per routine */
396 @d found3 43 /* like |found|, when there's more than three per routine */
397 @d not_found 45 /* go here when you've found nothing */
398 @d common_ending 50 /* go here when you want to merge with another branch */
400 @ Here are some macros for common programming idioms.
402 @d incr(A) (A)=(A)+1 /* increase a variable by unity */
403 @d decr(A) (A)=(A)-1 /* decrease a variable by unity */
404 @d negate(A) (A)=-(A) /* change the sign of a variable */
407 @d do_nothing /* empty statement */
408 @d Return goto exit /* terminate a procedure call */
409 @f return nil /* \.{WEB} will henceforth say |return| instead of \\{return} */
411 @* \[2] The character set.
412 In order to make \MP\ readily portable to a wide variety of
413 computers, all of its input text is converted to an internal eight-bit
414 code that includes standard ASCII, the ``American Standard Code for
415 Information Interchange.'' This conversion is done immediately when each
416 character is read in. Conversely, characters are converted from ASCII to
417 the user's external representation just before they are output to a
421 Such an internal code is relevant to users of \MP\ only with respect to
422 the \&{char} and \&{ASCII} operations, and the comparison of strings.
424 @ Characters of text that have been converted to \MP's internal form
425 are said to be of type |ASCII_code|, which is a subrange of the integers.
428 typedef unsigned char ASCII_code; /* eight-bit numbers */
430 @ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
431 character sets were common, so it did not make provision for lowercase
432 letters. Nowadays, of course, we need to deal with both capital and small
433 letters in a convenient way, especially in a program for font design;
434 so the present specification of \MP\ has been written under the assumption
435 that the \PASCAL\ compiler and run-time system permit the use of text files
436 with more than 64 distinguishable characters. More precisely, we assume that
437 the character set contains at least the letters and symbols associated
438 with ASCII codes 040 through 0176; all of these characters are now
439 available on most computer terminals.
441 Since we are dealing with more characters than were present in the first
442 \PASCAL\ compilers, we have to decide what to call the associated data
443 type. Some \PASCAL s use the original name |char| for the
444 characters in text files, even though there now are more than 64 such
445 characters, while other \PASCAL s consider |char| to be a 64-element
446 subrange of a larger data type that has some other name.
448 In order to accommodate this difference, we shall use the name |text_char|
449 to stand for the data type of the characters that are converted to and
450 from |ASCII_code| when they are input and output. We shall also assume
451 that |text_char| consists of the elements |chr(first_text_char)| through
452 |chr(last_text_char)|, inclusive. The following definitions should be
453 adjusted if necessary.
454 @^system dependencies@>
456 @d first_text_char 0 /* ordinal number of the smallest element of |text_char| */
457 @d last_text_char 255 /* ordinal number of the largest element of |text_char| */
460 typedef unsigned char text_char; /* the data type of characters in text files */
462 @ @<Local variables for init...@>=
465 @ The \MP\ processor converts between ASCII code and
466 the user's external character set by means of arrays |xord| and |xchr|
467 that are analogous to \PASCAL's |ord| and |chr| functions.
469 @d xchr(A) mp->xchr[(A)]
470 @d xord(A) mp->xord[(A)]
473 ASCII_code xord[256]; /* specifies conversion of input characters */
474 text_char xchr[256]; /* specifies conversion of output characters */
476 @ The core system assumes all 8-bit is acceptable. If it is not,
477 a change file has to alter the below section.
478 @^system dependencies@>
480 Additionally, people with extended character sets can
481 assign codes arbitrarily, giving an |xchr| equivalent to whatever
482 characters the users of \MP\ are allowed to have in their input files.
483 Appropriate changes to \MP's |char_class| table should then be made.
484 (Unlike \TeX, each installation of \MP\ has a fixed assignment of category
485 codes, called the |char_class|.) Such changes make portability of programs
486 more difficult, so they should be introduced cautiously if at all.
487 @^character set dependencies@>
488 @^system dependencies@>
491 for (i=0;i<=0377;i++) { xchr(i)=i; }
493 @ The following system-independent code makes the |xord| array contain a
494 suitable inverse to the information in |xchr|. Note that if |xchr[i]=xchr[j]|
495 where |i<j<0177|, the value of |xord[xchr[i]]| will turn out to be
496 |j| or more; hence, standard ASCII code numbers will be used instead of
497 codes below 040 in case there is a coincidence.
500 for (i=first_text_char;i<=last_text_char;i++) {
503 for (i=0200;i<=0377;i++) { xord(xchr(i))=i;}
504 for (i=0;i<=0176;i++) { xord(xchr(i))=i;}
506 @* \[3] Input and output.
507 The bane of portability is the fact that different operating systems treat
508 input and output quite differently, perhaps because computer scientists
509 have not given sufficient attention to this problem. People have felt somehow
510 that input and output are not part of ``real'' programming. Well, it is true
511 that some kinds of programming are more fun than others. With existing
512 input/output conventions being so diverse and so messy, the only sources of
513 joy in such parts of the code are the rare occasions when one can find a
514 way to make the program a little less bad than it might have been. We have
515 two choices, either to attack I/O now and get it over with, or to postpone
516 I/O until near the end. Neither prospect is very attractive, so let's
519 The basic operations we need to do are (1)~inputting and outputting of
520 text, to or from a file or the user's terminal; (2)~inputting and
521 outputting of eight-bit bytes, to or from a file; (3)~instructing the
522 operating system to initiate (``open'') or to terminate (``close'') input or
523 output from a specified file; (4)~testing whether the end of an input
524 file has been reached; (5)~display of bits on the user's screen.
525 The bit-display operation will be discussed in a later section; we shall
526 deal here only with more traditional kinds of I/O.
528 @ Finding files happens in a slightly roundabout fashion: the \MP\
529 instance object contains a field that holds a function pointer that finds a
530 file, and returns its name, or NULL. For this, it receives three
531 parameters: the non-qualified name |fname|, the intended |fopen|
532 operation type |fmode|, and the type of the file |ftype|.
534 The file types that are passed on in |ftype| can be used to
535 differentiate file searches if a library like kpathsea is used,
536 the fopen mode is passed along for the same reason.
539 typedef unsigned char eight_bits ; /* unsigned one-byte quantity */
541 mp_filetype_program = 1, /* \MP\ language input */
542 mp_filetype_log, /* the log file */
543 mp_filetype_postscript, /* the postscript output */
544 mp_filetype_text, /* text files for readfrom and writeto primitives */
545 mp_filetype_memfile, /* memory dumps */
546 mp_filetype_metrics, /* TeX font metric files */
547 mp_filetype_fontmap, /* PostScript font mapping files */
548 mp_filetype_font, /* PostScript type1 font programs */
549 mp_filetype_encoding, /* PostScript font encoding files */
551 typedef char *(*file_finder)(char *, char *, int);
554 file_finder find_file;
556 @ @<Option variables@>=
557 file_finder find_file;
559 @ The default function for finding files is |mp_find_file|. It is
560 pretty stupid: it will only find files in the current directory.
563 char *mp_find_file (char *fname, char *fmode, int ftype) {
564 if (fmode[0] != 'r' || access (fname,R_OK) || ftype)
565 return xstrdup(fname);
569 @ This has to be done very early on, so it is best to put it in with
570 the |mp_new| allocations
572 @d set_callback_option(A) do { mp->A = mp_##A;
573 if (opt->A!=NULL) mp->A = opt->A;
576 @<Allocate or initialize ...@>=
577 set_callback_option(find_file);
579 @ Because |mp_find_file| is used so early, it has to be in the helpers
583 char *mp_find_file (char *fname, char *fmode, int ftype) ;
585 @ The function to open files can now be very short.
588 FILE *mp_open_file(MP mp, char *fname, char *fmode, int ftype) {
589 char *s = (mp->find_file)(fname,fmode,ftype);
591 FILE *f = fopen(s, fmode);
598 @ This is a legacy interface: (almost) all file names pass through |name_of_file|.
601 char name_of_file[file_name_size+1]; /* the name of a system file */
602 int name_length;/* this many characters are actually
603 relevant in |name_of_file| (the rest are blank) */
604 boolean print_found_names; /* configuration parameter */
606 @ @<Option variables@>=
607 boolean print_found_names; /* configuration parameter */
609 @ If this parameter is true, the terminal and log will report the found
610 file names for input files instead of the requested ones.
611 It is off by default because it creates an extra filename lookup.
613 @<Allocate or initialize ...@>=
614 mp->print_found_names = (opt->print_found_names>0 ? true : false);
616 @ \MP's file-opening procedures return |false| if no file identified by
617 |name_of_file| could be opened.
619 The |OPEN_FILE| macro takes care of the |print_found_names| parameter.
620 It is not used for opening a mem file for read, because that file name
624 if (mp->print_found_names) {
625 char *s = (mp->find_file)(mp->name_of_file,A,ftype);
627 *f = mp_open_file(mp,mp->name_of_file,A, ftype);
628 strncpy(mp->name_of_file,s,file_name_size);
634 *f = mp_open_file(mp,mp->name_of_file,A, ftype);
637 return (*f ? true : false)
640 boolean mp_a_open_in (MP mp, FILE **f, int ftype) {
641 /* open a text file for input */
645 boolean mp_w_open_in (MP mp, FILE **f) {
646 /* open a word file for input */
647 *f = mp_open_file(mp,mp->name_of_file,"rb",mp_filetype_memfile);
648 return (*f ? true : false);
651 boolean mp_a_open_out (MP mp, FILE **f, int ftype) {
652 /* open a text file for output */
656 boolean mp_b_open_out (MP mp, FILE **f, int ftype) {
657 /* open a binary file for output */
661 boolean mp_w_open_out (MP mp, FILE**f) {
662 /* open a word file for output */
663 int ftype = mp_filetype_memfile;
668 FILE *mp_open_file(MP mp, char *fname, char *fmode, int ftype);
670 @ Binary input and output are done with \PASCAL's ordinary |get| and |put|
671 procedures, so we don't have to make any other special arrangements for
672 binary~I/O. Text output is also easy to do with standard \PASCAL\ routines.
673 The treatment of text input is more difficult, however, because
674 of the necessary translation to |ASCII_code| values.
675 \MP's conventions should be efficient, and they should
676 blend nicely with the user's operating environment.
678 @ Input from text files is read one line at a time, using a routine called
679 |input_ln|. This function is defined in terms of global variables called
680 |buffer|, |first|, and |last| that will be described in detail later; for
681 now, it suffices for us to know that |buffer| is an array of |ASCII_code|
682 values, and that |first| and |last| are indices into this array
683 representing the beginning and ending of a line of text.
686 size_t buf_size; /* maximum number of characters simultaneously present in
687 current lines of open files */
688 ASCII_code *buffer; /* lines of characters being read */
689 size_t first; /* the first unused position in |buffer| */
690 size_t last; /* end of the line just input to |buffer| */
691 size_t max_buf_stack; /* largest index used in |buffer| */
693 @ @<Allocate or initialize ...@>=
695 mp->buffer = xmalloc((mp->buf_size+1),sizeof(ASCII_code));
697 @ @<Dealloc variables@>=
701 void mp_reallocate_buffer(MP mp, size_t l) {
703 if (l>max_halfword) {
704 mp_confusion(mp,"buffer size"); /* can't happen (I hope) */
706 buffer = xmalloc((l+1),sizeof(ASCII_code));
707 memcpy(buffer,mp->buffer,(mp->buf_size+1));
709 mp->buffer = buffer ;
713 @ The |input_ln| function brings the next line of input from the specified
714 field into available positions of the buffer array and returns the value
715 |true|, unless the file has already been entirely read, in which case it
716 returns |false| and sets |last:=first|. In general, the |ASCII_code|
717 numbers that represent the next line of the file are input into
718 |buffer[first]|, |buffer[first+1]|, \dots, |buffer[last-1]|; and the
719 global variable |last| is set equal to |first| plus the length of the
720 line. Trailing blanks are removed from the line; thus, either |last=first|
721 (in which case the line was entirely blank) or |buffer[last-1]<>" "|.
724 An overflow error is given, however, if the normal actions of |input_ln|
725 would make |last>=buf_size|; this is done so that other parts of \MP\
726 can safely look at the contents of |buffer[last+1]| without overstepping
727 the bounds of the |buffer| array. Upon entry to |input_ln|, the condition
728 |first<buf_size| will always hold, so that there is always room for an
731 The variable |max_buf_stack|, which is used to keep track of how large
732 the |buf_size| parameter must be to accommodate the present job, is
733 also kept up to date by |input_ln|.
735 If the |bypass_eoln| parameter is |true|, |input_ln| will do a |get|
736 before looking at the first character of the line; this skips over
737 an |eoln| that was in |f^|. The procedure does not do a |get| when it
738 reaches the end of the line; therefore it can be used to acquire input
739 from the user's terminal as well as from ordinary text files.
741 Standard \PASCAL\ says that a file should have |eoln| immediately
742 before |eof|, but \MP\ needs only a weaker restriction: If |eof|
743 occurs in the middle of a line, the system function |eoln| should return
744 a |true| result (even though |f^| will be undefined).
747 boolean mp_input_ln (MP mp,FILE * f, boolean bypass_eoln) {
748 /* inputs the next line or returns |false| */
749 int last_nonblank; /* |last| with trailing blanks removed */
755 if (c!='\n' && c!='\r') {
759 /* input the first character of the line into |f^| */
760 mp->last=mp->first; /* cf.\ Matthew 19\thinspace:\thinspace30 */
764 last_nonblank=mp->first;
765 while (c!=EOF && c!='\n' && c!='\r') {
766 if ( mp->last>=mp->max_buf_stack ) {
767 mp->max_buf_stack=mp->last+1;
768 if ( mp->max_buf_stack==mp->buf_size ) {
769 mp_reallocate_buffer(mp,(mp->buf_size+(mp->buf_size>>2)));
772 mp->buffer[mp->last]=xord(c);
774 if ( mp->buffer[mp->last-1]!=' ' )
775 last_nonblank=mp->last;
781 mp->last=last_nonblank;
785 @ The user's terminal acts essentially like other files of text, except
786 that it is used both for input and for output. When the terminal is
787 considered an input file, the file variable is called |term_in|, and when it
788 is considered an output file the file variable is |term_out|.
789 @^system dependencies@>
792 FILE * term_in; /* the terminal as an input file */
793 FILE * term_out; /* the terminal as an output file */
795 @ Here is how to open the terminal files. In the default configuration,
796 nothing happens except that the command line (if there is one) is copied
797 to the input buffer. The variable |command_line| will be filled by the
798 |main| procedure. The copying can not be done earlier in the program
799 logic because in the |INI| version, the |buffer| is also used for primitive
802 @^system dependencies@>
804 @d t_open_out /* open the terminal for text output */
805 @d t_open_in do { /* open the terminal for text input */
806 if (mp->command_line!=NULL) {
807 mp->last = strlen(mp->command_line);
808 strncpy((char *)mp->buffer,mp->command_line,mp->last);
809 xfree(mp->command_line);
816 @ @<Option variables@>=
819 @ @<Allocate or initialize ...@>=
820 mp->command_line = opt->command_line;
822 @ Sometimes it is necessary to synchronize the input/output mixture that
823 happens on the user's terminal, and three system-dependent
824 procedures are used for this
825 purpose. The first of these, |update_terminal|, is called when we want
826 to make sure that everything we have output to the terminal so far has
827 actually left the computer's internal buffers and been sent.
828 The second, |clear_terminal|, is called when we wish to cancel any
829 input that the user may have typed ahead (since we are about to
830 issue an unexpected error message). The third, |wake_up_terminal|,
831 is supposed to revive the terminal if the user has disabled it by
832 some instruction to the operating system. The following macros show how
833 these operations can be specified in \ph:
834 @^system dependencies@>
836 @d update_terminal fflush(mp->term_out) /* empty the terminal output buffer */
837 @d clear_terminal do_nothing /* clear the terminal input buffer */
838 @d wake_up_terminal fflush(mp->term_out) /* cancel the user's cancellation of output */
840 @ We need a special routine to read the first line of \MP\ input from
841 the user's terminal. This line is different because it is read before we
842 have opened the transcript file; there is sort of a ``chicken and
843 egg'' problem here. If the user types `\.{input cmr10}' on the first
844 line, or if some macro invoked by that line does such an \.{input},
845 the transcript file will be named `\.{cmr10.log}'; but if no \.{input}
846 commands are performed during the first line of terminal input, the transcript
847 file will acquire its default name `\.{mpout.log}'. (The transcript file
848 will not contain error messages generated by the first line before the
849 first \.{input} command.)
851 The first line is even more special if we are lucky enough to have an operating
852 system that treats \MP\ differently from a run-of-the-mill \PASCAL\ object
853 program. It's nice to let the user start running a \MP\ job by typing
854 a command line like `\.{MP cmr10}'; in such a case, \MP\ will operate
855 as if the first line of input were `\.{cmr10}', i.e., the first line will
856 consist of the remainder of the command line, after the part that invoked \MP.
858 @ Different systems have different ways to get started. But regardless of
859 what conventions are adopted, the routine that initializes the terminal
860 should satisfy the following specifications:
862 \yskip\textindent{1)}It should open file |term_in| for input from the
863 terminal. (The file |term_out| will already be open for output to the
866 \textindent{2)}If the user has given a command line, this line should be
867 considered the first line of terminal input. Otherwise the
868 user should be prompted with `\.{**}', and the first line of input
869 should be whatever is typed in response.
871 \textindent{3)}The first line of input, which might or might not be a
872 command line, should appear in locations |first| to |last-1| of the
875 \textindent{4)}The global variable |loc| should be set so that the
876 character to be read next by \MP\ is in |buffer[loc]|. This
877 character should not be blank, and we should have |loc<last|.
879 \yskip\noindent(It may be necessary to prompt the user several times
880 before a non-blank line comes in. The prompt is `\.{**}' instead of the
881 later `\.*' because the meaning is slightly different: `\.{input}' need
882 not be typed immediately after~`\.{**}'.)
884 @d loc mp->cur_input.loc_field /* location of first unread character in |buffer| */
886 @ The following program does the required initialization
887 without retrieving a possible command line.
888 It should be clear how to modify this routine to deal with command lines,
889 if the system permits them.
890 @^system dependencies@>
893 boolean mp_init_terminal (MP mp) { /* gets the terminal input started */
900 wake_up_terminal; fprintf(mp->term_out,"**"); update_terminal;
902 if ( ! mp_input_ln(mp, mp->term_in,true) ) { /* this shouldn't happen */
903 fprintf(mp->term_out,"\n! End of file on the terminal... why?");
904 @.End of file on the terminal@>
908 while ( (loc<(int)mp->last)&&(mp->buffer[loc]==' ') )
910 if ( loc<(int)mp->last ) {
911 return true; /* return unless the line was all blank */
913 fprintf(mp->term_out,"Please type the name of your input file.\n");
918 boolean mp_init_terminal (MP mp) ;
921 @* \[4] String handling.
922 Symbolic token names and diagnostic messages are variable-length strings
923 of eight-bit characters. Since \PASCAL\ does not have a well-developed string
924 mechanism, \MP\ does all of its string processing by homegrown methods.
926 \MP\ uses strings more extensively than \MF\ does, but the necessary
927 operations can still be handled with a fairly simple data structure.
928 The array |str_pool| contains all of the (eight-bit) ASCII codes in all
929 of the strings, and the array |str_start| contains indices of the starting
930 points of each string. Strings are referred to by integer numbers, so that
931 string number |s| comprises the characters |str_pool[j]| for
932 |str_start[s]<=j<str_start[ss]| where |ss=next_str[s]|. The string pool
933 is allocated sequentially and |str_pool[pool_ptr]| is the next unused
934 location. The first string number not currently in use is |str_ptr|
935 and |next_str[str_ptr]| begins a list of free string numbers. String
936 pool entries |str_start[str_ptr]| up to |pool_ptr| are reserved for a
937 string currently being constructed.
939 String numbers 0 to 255 are reserved for strings that correspond to single
940 ASCII characters. This is in accordance with the conventions of \.{WEB},
942 which converts single-character strings into the ASCII code number of the
943 single character involved, while it converts other strings into integers
944 and builds a string pool file. Thus, when the string constant \.{"."} appears
945 in the program below, \.{WEB} converts it into the integer 46, which is the
946 ASCII code for a period, while \.{WEB} will convert a string like \.{"hello"}
947 into some integer greater than~255. String number 46 will presumably be the
948 single character `\..'\thinspace; but some ASCII codes have no standard visible
949 representation, and \MP\ may need to be able to print an arbitrary
950 ASCII character, so the first 256 strings are used to specify exactly what
951 should be printed for each of the 256 possibilities.
954 typedef int pool_pointer; /* for variables that point into |str_pool| */
955 typedef int str_number; /* for variables that point into |str_start| */
958 ASCII_code *str_pool; /* the characters */
959 pool_pointer *str_start; /* the starting pointers */
960 str_number *next_str; /* for linking strings in order */
961 pool_pointer pool_ptr; /* first unused position in |str_pool| */
962 str_number str_ptr; /* number of the current string being created */
963 pool_pointer init_pool_ptr; /* the starting value of |pool_ptr| */
964 str_number init_str_use; /* the initial number of strings in use */
965 pool_pointer max_pool_ptr; /* the maximum so far of |pool_ptr| */
966 str_number max_str_ptr; /* the maximum so far of |str_ptr| */
968 @ @<Allocate or initialize ...@>=
969 mp->str_pool = xmalloc ((mp->pool_size +1),sizeof(ASCII_code));
970 mp->str_start = xmalloc ((mp->max_strings+1),sizeof(pool_pointer));
971 mp->next_str = xmalloc ((mp->max_strings+1),sizeof(str_number));
973 @ @<Dealloc variables@>=
975 xfree(mp->str_start);
978 @ Most printing is done from |char *|s, but sometimes not. Here are
979 functions that convert an internal string into a |char *| for use
980 by the printing routines, and vice versa.
982 @d str(A) mp_str(mp,A)
983 @d rts(A) mp_rts(mp,A)
985 @<Exported function headers@>=
986 int mp_xstrcmp (const char *a, const char *b);
987 char * mp_str (MP mp, str_number s);
990 str_number mp_rts (MP mp, char *s);
991 str_number mp_make_string (MP mp);
993 @ The attempt to catch interrupted strings that is in |mp_rts|, is not
994 very good: it does not handle nesting over more than one level.
997 int mp_xstrcmp (const char *a, const char *b) {
998 if (a==NULL && b==NULL)
1008 char * mp_str (MP mp, str_number ss) {
1011 if (ss==mp->str_ptr) {
1015 s = xmalloc(len+1,sizeof(char));
1016 strncpy(s,(char *)(mp->str_pool+(mp->str_start[ss])),len);
1021 str_number mp_rts (MP mp, char *s) {
1022 int r; /* the new string */
1023 int old; /* a possible string in progress */
1027 } else if (strlen(s)==1) {
1031 str_room((integer)strlen(s));
1032 if (mp->str_start[mp->str_ptr]<mp->pool_ptr)
1033 old = mp_make_string(mp);
1038 r = mp_make_string(mp);
1040 str_room(length(old));
1041 while (i<length(old)) {
1042 append_char((mp->str_start[old]+i));
1044 mp_flush_string(mp,old);
1050 @ Except for |strs_used_up|, the following string statistics are only
1051 maintained when code between |stat| $\ldots$ |tats| delimiters is not
1055 integer strs_used_up; /* strings in use or unused but not reclaimed */
1056 integer pool_in_use; /* total number of cells of |str_pool| actually in use */
1057 integer strs_in_use; /* total number of strings actually in use */
1058 integer max_pl_used; /* maximum |pool_in_use| so far */
1059 integer max_strs_used; /* maximum |strs_in_use| so far */
1061 @ Several of the elementary string operations are performed using \.{WEB}
1062 macros instead of \PASCAL\ procedures, because many of the
1063 operations are done quite frequently and we want to avoid the
1064 overhead of procedure calls. For example, here is
1065 a simple macro that computes the length of a string.
1068 @d str_stop(A) mp->str_start[mp->next_str[(A)]] /* one cell past the end of string
1070 @d length(A) (str_stop((A))-mp->str_start[(A)]) /* the number of characters in string \# */
1072 @ The length of the current string is called |cur_length|. If we decide that
1073 the current string is not needed, |flush_cur_string| resets |pool_ptr| so that
1074 |cur_length| becomes zero.
1076 @d cur_length (mp->pool_ptr - mp->str_start[mp->str_ptr])
1077 @d flush_cur_string mp->pool_ptr=mp->str_start[mp->str_ptr]
1079 @ Strings are created by appending character codes to |str_pool|.
1080 The |append_char| macro, defined here, does not check to see if the
1081 value of |pool_ptr| has gotten too high; this test is supposed to be
1082 made before |append_char| is used.
1084 To test if there is room to append |l| more characters to |str_pool|,
1085 we shall write |str_room(l)|, which tries to make sure there is enough room
1086 by compacting the string pool if necessary. If this does not work,
1087 |do_compaction| aborts \MP\ and gives an apologetic error message.
1089 @d append_char(A) /* put |ASCII_code| \# at the end of |str_pool| */
1090 { mp->str_pool[mp->pool_ptr]=(A); incr(mp->pool_ptr);
1092 @d str_room(A) /* make sure that the pool hasn't overflowed */
1093 { if ( mp->pool_ptr+(A) > mp->max_pool_ptr ) {
1094 if ( mp->pool_ptr+(A) > mp->pool_size ) mp_do_compaction(mp, (A));
1095 else mp->max_pool_ptr=mp->pool_ptr+(A); }
1098 @ The following routine is similar to |str_room(1)| but it uses the
1099 argument |mp->pool_size| to prevent |do_compaction| from aborting when
1100 string space is exhausted.
1102 @<Declare the procedure called |unit_str_room|@>=
1103 void mp_unit_str_room (MP mp);
1106 void mp_unit_str_room (MP mp) {
1107 if ( mp->pool_ptr>=mp->pool_size ) mp_do_compaction(mp, mp->pool_size);
1108 if ( mp->pool_ptr>=mp->max_pool_ptr ) mp->max_pool_ptr=mp->pool_ptr+1;
1111 @ \MP's string expressions are implemented in a brute-force way: Every
1112 new string or substring that is needed is simply copied into the string pool.
1113 Space is eventually reclaimed by a procedure called |do_compaction| with
1114 the aid of a simple system system of reference counts.
1115 @^reference counts@>
1117 The number of references to string number |s| will be |str_ref[s]|. The
1118 special value |str_ref[s]=max_str_ref=127| is used to denote an unknown
1119 positive number of references; such strings will never be recycled. If
1120 a string is ever referred to more than 126 times, simultaneously, we
1121 put it in this category. Hence a single byte suffices to store each |str_ref|.
1123 @d max_str_ref 127 /* ``infinite'' number of references */
1124 @d add_str_ref(A) { if ( mp->str_ref[(A)]<max_str_ref ) incr(mp->str_ref[(A)]);
1130 @ @<Allocate or initialize ...@>=
1131 mp->str_ref = xmalloc ((mp->max_strings+1),sizeof(int));
1133 @ @<Dealloc variables@>=
1136 @ Here's what we do when a string reference disappears:
1138 @d delete_str_ref(A) {
1139 if ( mp->str_ref[(A)]<max_str_ref ) {
1140 if ( mp->str_ref[(A)]>1 ) decr(mp->str_ref[(A)]);
1141 else mp_flush_string(mp, (A));
1145 @<Declare the procedure called |flush_string|@>=
1146 void mp_flush_string (MP mp,str_number s) ;
1149 @ We can't flush the first set of static strings at all, so there
1150 is no point in trying
1153 void mp_flush_string (MP mp,str_number s) {
1155 mp->pool_in_use=mp->pool_in_use-length(s);
1156 decr(mp->strs_in_use);
1157 if ( mp->next_str[s]!=mp->str_ptr ) {
1161 decr(mp->strs_used_up);
1163 mp->pool_ptr=mp->str_start[mp->str_ptr];
1167 @ C literals cannot be simply added, they need to be set so they can't
1170 @d intern(A) mp_intern(mp,(A))
1173 str_number mp_intern (MP mp, char *s) {
1176 mp->str_ref[r] = max_str_ref;
1181 str_number mp_intern (MP mp, char *s);
1184 @ Once a sequence of characters has been appended to |str_pool|, it
1185 officially becomes a string when the function |make_string| is called.
1186 This function returns the identification number of the new string as its
1189 When getting the next unused string number from the linked list, we pretend
1191 $$ \hbox{|max_str_ptr+1|, |max_str_ptr+2|, $\ldots$, |mp->max_strings|} $$
1192 are linked sequentially even though the |next_str| entries have not been
1193 initialized yet. We never allow |str_ptr| to reach |mp->max_strings|;
1194 |do_compaction| is responsible for making sure of this.
1197 @<Declare the procedure called |do_compaction|@>;
1198 @<Declare the procedure called |unit_str_room|@>;
1199 str_number mp_make_string (MP mp);
1202 str_number mp_make_string (MP mp) { /* current string enters the pool */
1203 str_number s; /* the new string */
1206 mp->str_ptr=mp->next_str[s];
1207 if ( mp->str_ptr>mp->max_str_ptr ) {
1208 if ( mp->str_ptr==mp->max_strings ) {
1210 mp_do_compaction(mp, 0);
1214 if ( mp->strs_used_up!=mp->max_str_ptr ) mp_confusion(mp, "s");
1215 @:this can't happen s}{\quad \.s@>
1217 mp->max_str_ptr=mp->str_ptr;
1218 mp->next_str[mp->str_ptr]=mp->max_str_ptr+1;
1222 mp->str_start[mp->str_ptr]=mp->pool_ptr;
1223 incr(mp->strs_used_up);
1224 incr(mp->strs_in_use);
1225 mp->pool_in_use=mp->pool_in_use+length(s);
1226 if ( mp->pool_in_use>mp->max_pl_used )
1227 mp->max_pl_used=mp->pool_in_use;
1228 if ( mp->strs_in_use>mp->max_strs_used )
1229 mp->max_strs_used=mp->strs_in_use;
1233 @ The most interesting string operation is string pool compaction. The idea
1234 is to recover unused space in the |str_pool| array by recopying the strings
1235 to close the gaps created when some strings become unused. All string
1236 numbers~$k$ where |str_ref[k]=0| are to be linked into the list of free string
1237 numbers after |str_ptr|. If this fails to free enough pool space we issue an
1238 |overflow| error unless |needed=mp->pool_size|. Calling |do_compaction|
1239 with |needed=mp->pool_size| supresses all overflow tests.
1241 The compaction process starts with |last_fixed_str| because all lower numbered
1242 strings are permanently allocated with |max_str_ref| in their |str_ref| entries.
1245 str_number last_fixed_str; /* last permanently allocated string */
1246 str_number fixed_str_use; /* number of permanently allocated strings */
1248 @ @<Declare the procedure called |do_compaction|@>=
1249 void mp_do_compaction (MP mp, pool_pointer needed) ;
1252 void mp_do_compaction (MP mp, pool_pointer needed) {
1253 str_number str_use; /* a count of strings in use */
1254 str_number r,s,t; /* strings being manipulated */
1255 pool_pointer p,q; /* destination and source for copying string characters */
1256 @<Advance |last_fixed_str| as far as possible and set |str_use|@>;
1257 r=mp->last_fixed_str;
1260 while ( s!=mp->str_ptr ) {
1261 while ( mp->str_ref[s]==0 ) {
1262 @<Advance |s| and add the old |s| to the list of free string numbers;
1263 then |break| if |s=str_ptr|@>;
1265 r=s; s=mp->next_str[s];
1267 @<Move string |r| back so that |str_start[r]=p|; make |p| the location
1268 after the end of the string@>;
1270 @<Move the current string back so that it starts at |p|@>;
1271 if ( needed<mp->pool_size ) {
1272 @<Make sure that there is room for another string with |needed| characters@>;
1274 @<Account for the compaction and make sure the statistics agree with the
1276 mp->strs_used_up=str_use;
1279 @ @<Advance |last_fixed_str| as far as possible and set |str_use|@>=
1280 t=mp->next_str[mp->last_fixed_str];
1281 while (t!=mp->str_ptr && mp->str_ref[t]==max_str_ref) {
1282 incr(mp->fixed_str_use);
1283 mp->last_fixed_str=t;
1286 str_use=mp->fixed_str_use
1288 @ Because of the way |flush_string| has been written, it should never be
1289 necessary to |break| here. The extra line of code seems worthwhile to
1290 preserve the generality of |do_compaction|.
1292 @<Advance |s| and add the old |s| to the list of free string numbers;...@>=
1297 mp->next_str[t]=mp->next_str[mp->str_ptr];
1298 mp->next_str[mp->str_ptr]=t;
1299 if ( s==mp->str_ptr ) break;
1302 @ The string currently starts at |str_start[r]| and ends just before
1303 |str_start[s]|. We don't change |str_start[s]| because it might be needed
1304 to locate the next string.
1306 @<Move string |r| back so that |str_start[r]=p|; make |p| the location...@>=
1309 while ( q<mp->str_start[s] ) {
1310 mp->str_pool[p]=mp->str_pool[q];
1314 @ Pointers |str_start[str_ptr]| and |pool_ptr| have not been updated. When
1315 we do this, anything between them should be moved.
1317 @ @<Move the current string back so that it starts at |p|@>=
1318 q=mp->str_start[mp->str_ptr];
1319 mp->str_start[mp->str_ptr]=p;
1320 while ( q<mp->pool_ptr ) {
1321 mp->str_pool[p]=mp->str_pool[q];
1326 @ We must remember that |str_ptr| is not allowed to reach |mp->max_strings|.
1328 @<Make sure that there is room for another string with |needed| char...@>=
1329 if ( str_use>=mp->max_strings-1 )
1330 mp_reallocate_strings (mp,str_use);
1331 if ( mp->pool_ptr+needed>mp->max_pool_ptr ) {
1332 mp_reallocate_pool(mp, mp->pool_ptr+needed);
1333 mp->max_pool_ptr=mp->pool_ptr+needed;
1337 void mp_reallocate_strings (MP mp, str_number str_use) ;
1338 void mp_reallocate_pool(MP mp, pool_pointer needed) ;
1341 void mp_reallocate_strings (MP mp, str_number str_use) {
1342 while ( str_use>=mp->max_strings-1 ) {
1343 int l = mp->max_strings + (mp->max_strings>>2);
1344 XREALLOC (mp->str_ref, l, int);
1345 XREALLOC (mp->str_start, l, pool_pointer);
1346 XREALLOC (mp->next_str, l, str_number);
1347 mp->max_strings = l;
1350 void mp_reallocate_pool(MP mp, pool_pointer needed) {
1351 while ( needed>mp->pool_size ) {
1352 int l = mp->pool_size + (mp->pool_size>>2);
1353 XREALLOC (mp->str_pool, l, ASCII_code);
1358 @ @<Account for the compaction and make sure the statistics agree with...@>=
1359 if ( (mp->str_start[mp->str_ptr]!=mp->pool_in_use)||(str_use!=mp->strs_in_use) )
1360 mp_confusion(mp, "string");
1361 @:this can't happen string}{\quad string@>
1362 incr(mp->pact_count);
1363 mp->pact_chars=mp->pact_chars+mp->pool_ptr-str_stop(mp->last_fixed_str);
1364 mp->pact_strs=mp->pact_strs+str_use-mp->fixed_str_use;
1366 s=mp->str_ptr; t=str_use;
1367 while ( s<=mp->max_str_ptr ){
1368 if ( t>mp->max_str_ptr ) mp_confusion(mp, "\"");
1369 incr(t); s=mp->next_str[s];
1371 if ( t<=mp->max_str_ptr ) mp_confusion(mp, "\"");
1374 @ A few more global variables are needed to keep track of statistics when
1375 |stat| $\ldots$ |tats| blocks are not commented out.
1378 integer pact_count; /* number of string pool compactions so far */
1379 integer pact_chars; /* total number of characters moved during compactions */
1380 integer pact_strs; /* total number of strings moved during compactions */
1382 @ @<Initialize compaction statistics@>=
1387 @ The following subroutine compares string |s| with another string of the
1388 same length that appears in |buffer| starting at position |k|;
1389 the result is |true| if and only if the strings are equal.
1392 boolean mp_str_eq_buf (MP mp,str_number s, integer k) {
1393 /* test equality of strings */
1394 pool_pointer j; /* running index */
1396 while ( j<str_stop(s) ) {
1397 if ( mp->str_pool[j++]!=mp->buffer[k++] )
1403 @ Here is a similar routine, but it compares two strings in the string pool,
1404 and it does not assume that they have the same length. If the first string
1405 is lexicographically greater than, less than, or equal to the second,
1406 the result is respectively positive, negative, or zero.
1409 integer mp_str_vs_str (MP mp, str_number s, str_number t) {
1410 /* test equality of strings */
1411 pool_pointer j,k; /* running indices */
1412 integer ls,lt; /* lengths */
1413 integer l; /* length remaining to test */
1414 ls=length(s); lt=length(t);
1415 if ( ls<=lt ) l=ls; else l=lt;
1416 j=mp->str_start[s]; k=mp->str_start[t];
1418 if ( mp->str_pool[j]!=mp->str_pool[k] ) {
1419 return (mp->str_pool[j]-mp->str_pool[k]);
1426 @ The initial values of |str_pool|, |str_start|, |pool_ptr|,
1427 and |str_ptr| are computed by the \.{INIMP} program, based in part
1428 on the information that \.{WEB} has output while processing \MP.
1433 void mp_get_strings_started (MP mp) {
1434 /* initializes the string pool,
1435 but returns |false| if something goes wrong */
1436 int k; /* small indices or counters */
1437 str_number g; /* a new string */
1438 mp->pool_ptr=0; mp->str_ptr=0; mp->max_pool_ptr=0; mp->max_str_ptr=0;
1441 mp->pool_in_use=0; mp->strs_in_use=0;
1442 mp->max_pl_used=0; mp->max_strs_used=0;
1443 @<Initialize compaction statistics@>;
1445 @<Make the first 256 strings@>;
1446 g=mp_make_string(mp); /* string 256 == "" */
1447 mp->str_ref[g]=max_str_ref;
1448 mp->last_fixed_str=mp->str_ptr-1;
1449 mp->fixed_str_use=mp->str_ptr;
1454 void mp_get_strings_started (MP mp);
1456 @ The first 256 strings will consist of a single character only.
1458 @<Make the first 256...@>=
1459 for (k=0;k<=255;k++) {
1461 g=mp_make_string(mp);
1462 mp->str_ref[g]=max_str_ref;
1465 @ The first 128 strings will contain 95 standard ASCII characters, and the
1466 other 33 characters will be printed in three-symbol form like `\.{\^\^A}'
1467 unless a system-dependent change is made here. Installations that have
1468 an extended character set, where for example |xchr[032]=@t\.{'^^Z'}@>|,
1469 would like string 032 to be printed as the single character 032 instead
1470 of the three characters 0136, 0136, 0132 (\.{\^\^Z}). On the other hand,
1471 even people with an extended character set will want to represent string
1472 015 by \.{\^\^M}, since 015 is ASCII's ``carriage return'' code; the idea is
1473 to produce visible strings instead of tabs or line-feeds or carriage-returns
1474 or bell-rings or characters that are treated anomalously in text files.
1476 Unprintable characters of codes 128--255 are, similarly, rendered
1477 \.{\^\^80}--\.{\^\^ff}.
1479 The boolean expression defined here should be |true| unless \MP\ internal
1480 code number~|k| corresponds to a non-troublesome visible symbol in the
1481 local character set.
1482 If character |k| cannot be printed, and |k<0200|, then character |k+0100| or
1483 |k-0100| must be printable; moreover, ASCII codes |[060..071, 0141..0146]|
1485 @^character set dependencies@>
1486 @^system dependencies@>
1488 @<Character |k| cannot be printed@>=
1491 @* \[5] On-line and off-line printing.
1492 Messages that are sent to a user's terminal and to the transcript-log file
1493 are produced by several `|print|' procedures. These procedures will
1494 direct their output to a variety of places, based on the setting of
1495 the global variable |selector|, which has the following possible
1499 \hang |term_and_log|, the normal setting, prints on the terminal and on the
1502 \hang |log_only|, prints only on the transcript file.
1504 \hang |term_only|, prints only on the terminal.
1506 \hang |no_print|, doesn't print at all. This is used only in rare cases
1507 before the transcript file is open.
1509 \hang |ps_file_only| prints only on the \ps\ output file.
1511 \hang |pseudo|, puts output into a cyclic buffer that is used
1512 by the |show_context| routine; when we get to that routine we shall discuss
1513 the reasoning behind this curious mode.
1515 \hang |new_string|, appends the output to the current string in the
1518 \hang |>=write_file| prints on one of the files used for the \&{write}
1519 @:write_}{\&{write} primitive@>
1523 \noindent The symbolic names `|term_and_log|', etc., have been assigned
1524 numeric codes that satisfy the convenient relations |no_print+1=term_only|,
1525 |no_print+2=log_only|, |term_only+2=log_only+1=term_and_log|. These
1526 relations are not used when |selector| could be |pseudo|, |new_string|,
1527 or |ps_file_only|. We need not check for unprintable characters when
1530 Four additional global variables, |tally|, |term_offset|, |file_offset|,
1531 and |ps_offset| record the number of characters that have been printed
1532 since they were most recently cleared to zero. We use |tally| to record
1533 the length of (possibly very long) stretches of printing; |term_offset|,
1534 |file_offset|, and |ps_offset|, on the other hand, keep track of how many
1535 characters have appeared so far on the current line that has been output
1536 to the terminal, the transcript file, or the \ps\ output file, respectively.
1538 @d new_string 0 /* printing is deflected to the string pool */
1539 @d ps_file_only 1 /* printing goes to the \ps\ output file */
1540 @d pseudo 2 /* special |selector| setting for |show_context| */
1541 @d no_print 3 /* |selector| setting that makes data disappear */
1542 @d term_only 4 /* printing is destined for the terminal only */
1543 @d log_only 5 /* printing is destined for the transcript file only */
1544 @d term_and_log 6 /* normal |selector| setting */
1545 @d write_file 7 /* first write file selector */
1548 FILE * log_file; /* transcript of \MP\ session */
1549 FILE * ps_file; /* the generic font output goes here */
1550 unsigned int selector; /* where to print a message */
1551 unsigned char dig[23]; /* digits in a number being output */
1552 integer tally; /* the number of characters recently printed */
1553 unsigned int term_offset;
1554 /* the number of characters on the current terminal line */
1555 unsigned int file_offset;
1556 /* the number of characters on the current file line */
1558 /* the number of characters on the current \ps\ file line */
1559 ASCII_code *trick_buf; /* circular buffer for pseudoprinting */
1560 integer trick_count; /* threshold for pseudoprinting, explained later */
1561 integer first_count; /* another variable for pseudoprinting */
1563 @ @<Allocate or initialize ...@>=
1564 memset(mp->dig,0,23);
1565 mp->trick_buf = xmalloc((mp->error_line+1),sizeof(ASCII_code));
1567 @ @<Dealloc variables@>=
1568 xfree(mp->trick_buf);
1570 @ @<Initialize the output routines@>=
1571 mp->selector=term_only; mp->tally=0; mp->term_offset=0; mp->file_offset=0; mp->ps_offset=0;
1573 @ Macro abbreviations for output to the terminal and to the log file are
1574 defined here for convenience. Some systems need special conventions
1575 for terminal output, and it is possible to adhere to those conventions
1576 by changing |wterm|, |wterm_ln|, and |wterm_cr| here.
1577 @^system dependencies@>
1579 @d wterm(A) fprintf(mp->term_out,"%s",(A))
1580 @d wterm_chr(A)fprintf(mp->term_out,"%c",(A))
1581 @d wterm_ln(A) fprintf(mp->term_out,"\n%s",(A))
1582 @d wterm_cr fprintf(mp->term_out,"\n")
1583 @d wlog(A) fprintf(mp->log_file,"%s",(A))
1584 @d wlog_chr(A) fprintf(mp->log_file,"%c",(A))
1585 @d wlog_ln(A) fprintf(mp->log_file,"\n%s",(A))
1586 @d wlog_cr fprintf(mp->log_file, "\n")
1587 @d wps(A) fprintf(mp->ps_file,"%s",(A))
1588 @d wps_chr(A) fprintf(mp->ps_file,"%c",(A))
1589 @d wps_ln(A) fprintf(mp->ps_file,,"\n%s",(A))
1590 @d wps_cr fprintf(mp->ps_file,"\n")
1592 @ To end a line of text output, we call |print_ln|. Cases |0..max_write_files|
1593 use an array |wr_file| that will be declared later.
1595 @d mp_print_text(A) mp_print_str(mp,text((A)))
1598 void mp_print_ln (MP mp);
1599 void mp_print_visible_char (MP mp, ASCII_code s);
1600 void mp_print_char (MP mp, ASCII_code k);
1601 void mp_print (MP mp, char *s);
1602 void mp_print_str (MP mp, str_number s);
1603 void mp_print_nl (MP mp, char *s);
1604 void mp_print_two (MP mp,scaled x, scaled y) ;
1605 void mp_print_scaled (MP mp,scaled s);
1607 @ @<Basic print...@>=
1608 void mp_print_ln (MP mp) { /* prints an end-of-line */
1609 switch (mp->selector) {
1612 mp->term_offset=0; mp->file_offset=0;
1615 wlog_cr; mp->file_offset=0;
1618 wterm_cr; mp->term_offset=0;
1621 wps_cr; mp->ps_offset=0;
1628 fprintf(mp->wr_file[(mp->selector-write_file)],"\n");
1630 } /* note that |tally| is not affected */
1632 @ The |print_visible_char| procedure sends one character to the desired
1633 destination, using the |xchr| array to map it into an external character
1634 compatible with |input_ln|. (It assumes that it is always called with
1635 a visible ASCII character.) All printing comes through |print_ln| or
1636 |print_char|, which ultimately calls |print_visible_char|, hence these
1637 routines are the ones that limit lines to at most |max_print_line| characters.
1638 But we must make an exception for the \ps\ output file since it is not safe
1639 to cut up lines arbitrarily in \ps.
1641 Procedure |unit_str_room| needs to be declared |forward| here because it calls
1642 |do_compaction| and |do_compaction| can call the error routines. Actually,
1643 |unit_str_room| avoids |overflow| errors but it can call |confusion|.
1645 @<Basic printing...@>=
1646 void mp_print_visible_char (MP mp, ASCII_code s) { /* prints a single character */
1647 switch (mp->selector) {
1649 wterm_chr(xchr(s)); wlog_chr(xchr(s));
1650 incr(mp->term_offset); incr(mp->file_offset);
1651 if ( mp->term_offset==(unsigned)mp->max_print_line ) {
1652 wterm_cr; mp->term_offset=0;
1654 if ( mp->file_offset==(unsigned)mp->max_print_line ) {
1655 wlog_cr; mp->file_offset=0;
1659 wlog_chr(xchr(s)); incr(mp->file_offset);
1660 if ( mp->file_offset==(unsigned)mp->max_print_line ) mp_print_ln(mp);
1663 wterm_chr(xchr(s)); incr(mp->term_offset);
1664 if ( mp->term_offset==(unsigned)mp->max_print_line ) mp_print_ln(mp);
1668 wps_cr; mp->ps_offset=0;
1670 wps_chr(xchr(s)); incr(mp->ps_offset);
1676 if ( mp->tally<mp->trick_count )
1677 mp->trick_buf[mp->tally % mp->error_line]=s;
1680 if ( mp->pool_ptr>=mp->max_pool_ptr ) {
1681 mp_unit_str_room(mp);
1682 if ( mp->pool_ptr>=mp->pool_size )
1683 goto DONE; /* drop characters if string space is full */
1688 fprintf(mp->wr_file[(mp->selector-write_file)],"%c",xchr(s));
1694 @ The |print_char| procedure sends one character to the desired destination.
1695 File names and string expressions might contain |ASCII_code| values that
1696 can't be printed using |print_visible_char|. These characters will be
1697 printed in three- or four-symbol form like `\.{\^\^A}' or `\.{\^\^e4}'.
1698 (This procedure assumes that it is safe to bypass all checks for unprintable
1699 characters when |selector| is in the range |0..max_write_files-1| or when
1700 |selector=ps_file_only|. In the former case the user might want to write
1701 unprintable characters, and in the latter case the \ps\ printing routines
1702 check their arguments themselves before calling |print_char| or |print|.)
1704 @d print_lc_hex(A) do { l=(A);
1705 mp_print_visible_char(mp, (l<10 ? l+'0' : l-10+'a'));
1708 @<Basic printing...@>=
1709 void mp_print_char (MP mp, ASCII_code k) { /* prints a single character */
1710 int l; /* small index or counter */
1711 if ( mp->selector<pseudo || mp->selector>=write_file) {
1712 mp_print_visible_char(mp, k);
1713 } else if ( @<Character |k| cannot be printed@> ) {
1716 mp_print_visible_char(mp, k+0100);
1717 } else if ( k<0200 ) {
1718 mp_print_visible_char(mp, k-0100);
1720 print_lc_hex(k / 16);
1721 print_lc_hex(k % 16);
1724 mp_print_visible_char(mp, k);
1728 @ An entire string is output by calling |print|. Note that if we are outputting
1729 the single standard ASCII character \.c, we could call |print("c")|, since
1730 |"c"=99| is the number of a single-character string, as explained above. But
1731 |print_char("c")| is quicker, so \MP\ goes directly to the |print_char|
1732 routine when it knows that this is safe. (The present implementation
1733 assumes that it is always safe to print a visible ASCII character.)
1734 @^system dependencies@>
1737 void mp_do_print (MP mp, char *ss, unsigned int len) { /* prints string |s| */
1740 mp_print_char(mp, ss[j]); incr(j);
1746 void mp_print (MP mp, char *ss) {
1747 mp_do_print(mp, ss, strlen(ss));
1749 void mp_print_str (MP mp, str_number s) {
1750 pool_pointer j; /* current character code position */
1751 if ( (s<0)||(s>mp->max_str_ptr) ) {
1752 mp_do_print(mp,"???",3); /* this can't happen */
1756 mp_do_print(mp, (char *)(mp->str_pool+j), (str_stop(s)-j));
1760 @ Here is the very first thing that \MP\ prints: a headline that identifies
1761 the version number and base name. The |term_offset| variable is temporarily
1762 incorrect, but the discrepancy is not serious since we assume that the banner
1763 and mem identifier together will occupy at most |max_print_line|
1764 character positions.
1766 @<Initialize the output...@>=
1768 wterm (version_string);
1769 if (mp->mem_ident!=NULL)
1770 mp_print(mp,mp->mem_ident);
1774 @ The procedure |print_nl| is like |print|, but it makes sure that the
1775 string appears at the beginning of a new line.
1778 void mp_print_nl (MP mp, char *s) { /* prints string |s| at beginning of line */
1779 switch(mp->selector) {
1781 if ( (mp->term_offset>0)||(mp->file_offset>0) ) mp_print_ln(mp);
1784 if ( mp->file_offset>0 ) mp_print_ln(mp);
1787 if ( mp->term_offset>0 ) mp_print_ln(mp);
1790 if ( mp->ps_offset>0 ) mp_print_ln(mp);
1796 } /* there are no other cases */
1800 @ An array of digits in the range |0..9| is printed by |print_the_digs|.
1803 void mp_print_the_digs (MP mp, eight_bits k) {
1804 /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */
1806 decr(k); mp_print_char(mp, '0'+mp->dig[k]);
1810 @ The following procedure, which prints out the decimal representation of a
1811 given integer |n|, has been written carefully so that it works properly
1812 if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div|
1813 to negative arguments, since such operations are not implemented consistently
1814 by all \PASCAL\ compilers.
1817 void mp_print_int (MP mp,integer n) { /* prints an integer in decimal form */
1818 integer m; /* used to negate |n| in possibly dangerous cases */
1819 int k = 0; /* index to current digit; we assume that $|n|<10^{23}$ */
1821 mp_print_char(mp, '-');
1822 if ( n>-100000000 ) {
1825 m=-1-n; n=m / 10; m=(m % 10)+1; k=1;
1829 mp->dig[0]=0; incr(n);
1834 mp->dig[k]=n % 10; n=n / 10; incr(k);
1836 mp_print_the_digs(mp, k);
1840 void mp_print_int (MP mp,integer n);
1842 @ \MP\ also makes use of a trivial procedure to print two digits. The
1843 following subroutine is usually called with a parameter in the range |0<=n<=99|.
1846 void mp_print_dd (MP mp,integer n) { /* prints two least significant digits */
1848 mp_print_char(mp, '0'+(n / 10));
1849 mp_print_char(mp, '0'+(n % 10));
1852 @ Here is a procedure that asks the user to type a line of input,
1853 assuming that the |selector| setting is either |term_only| or |term_and_log|.
1854 The input is placed into locations |first| through |last-1| of the
1855 |buffer| array, and echoed on the transcript file if appropriate.
1857 This procedure is never called when |interaction<mp_scroll_mode|.
1859 @d prompt_input(A) do {
1860 wake_up_terminal; mp_print(mp, (A)); mp_term_input(mp);
1861 } while (0) /* prints a string and gets a line of input */
1864 void mp_term_input (MP mp) { /* gets a line from the terminal */
1865 size_t k; /* index into |buffer| */
1866 update_terminal; /* Now the user sees the prompt for sure */
1867 if (!mp_input_ln(mp, mp->term_in,true))
1868 mp_fatal_error(mp, "End of file on the terminal!");
1869 @.End of file on the terminal@>
1870 mp->term_offset=0; /* the user's line ended with \<\rm return> */
1871 decr(mp->selector); /* prepare to echo the input */
1872 if ( mp->last!=mp->first ) {
1873 for (k=mp->first;k<=mp->last-1;k++) {
1874 mp_print_char(mp, mp->buffer[k]);
1878 mp->buffer[mp->last]='%';
1879 incr(mp->selector); /* restore previous status */
1882 @* \[6] Reporting errors.
1883 When something anomalous is detected, \MP\ typically does something like this:
1884 $$\vbox{\halign{#\hfil\cr
1885 |print_err("Something anomalous has been detected");|\cr
1886 |help3("This is the first line of my offer to help.")|\cr
1887 |("This is the second line. I'm trying to")|\cr
1888 |("explain the best way for you to proceed.");|\cr
1890 A two-line help message would be given using |help2|, etc.; these informal
1891 helps should use simple vocabulary that complements the words used in the
1892 official error message that was printed. (Outside the U.S.A., the help
1893 messages should preferably be translated into the local vernacular. Each
1894 line of help is at most 60 characters long, in the present implementation,
1895 so that |max_print_line| will not be exceeded.)
1897 The |print_err| procedure supplies a `\.!' before the official message,
1898 and makes sure that the terminal is awake if a stop is going to occur.
1899 The |error| procedure supplies a `\..' after the official message, then it
1900 shows the location of the error; and if |interaction=error_stop_mode|,
1901 it also enters into a dialog with the user, during which time the help
1902 message may be printed.
1903 @^system dependencies@>
1905 @ The global variable |interaction| has four settings, representing increasing
1906 amounts of user interaction:
1910 mp_unspecified_mode=0, /* extra value for command-line switch */
1911 mp_batch_mode, /* omits all stops and omits terminal output */
1912 mp_nonstop_mode, /* omits all stops */
1913 mp_scroll_mode, /* omits error stops */
1914 mp_error_stop_mode, /* stops at every opportunity to interact */
1918 int interaction; /* current level of interaction */
1920 @ @<Option variables@>=
1921 int interaction; /* current level of interaction */
1923 @ Set it here so it can be overwritten by the commandline
1925 @<Allocate or initialize ...@>=
1926 mp->interaction=opt->interaction;
1927 if (mp->interaction==mp_unspecified_mode || mp->interaction>mp_error_stop_mode)
1928 mp->interaction=mp_error_stop_mode;
1929 if (mp->interaction<mp_unspecified_mode)
1930 mp->interaction=mp_batch_mode;
1934 @d print_err(A) mp_print_err(mp,(A))
1937 void mp_print_err(MP mp, char * A);
1940 void mp_print_err(MP mp, char * A) {
1941 if ( mp->interaction==mp_error_stop_mode )
1943 mp_print_nl(mp, "! ");
1949 @ \MP\ is careful not to call |error| when the print |selector| setting
1950 might be unusual. The only possible values of |selector| at the time of
1953 \yskip\hang|no_print| (when |interaction=mp_batch_mode|
1954 and |log_file| not yet open);
1956 \hang|term_only| (when |interaction>mp_batch_mode| and |log_file| not yet open);
1958 \hang|log_only| (when |interaction=mp_batch_mode| and |log_file| is open);
1960 \hang|term_and_log| (when |interaction>mp_batch_mode| and |log_file| is open).
1962 @<Initialize the print |selector| based on |interaction|@>=
1963 if ( mp->interaction==mp_batch_mode ) mp->selector=no_print; else mp->selector=term_only
1965 @ A global variable |deletions_allowed| is set |false| if the |get_next|
1966 routine is active when |error| is called; this ensures that |get_next|
1967 will never be called recursively.
1970 The global variable |history| records the worst level of error that
1971 has been detected. It has four possible values: |spotless|, |warning_issued|,
1972 |error_message_issued|, and |fatal_error_stop|.
1974 Another global variable, |error_count|, is increased by one when an
1975 |error| occurs without an interactive dialog, and it is reset to zero at
1976 the end of every statement. If |error_count| reaches 100, \MP\ decides
1977 that there is no point in continuing further.
1979 @d spotless 0 /* |history| value when nothing has been amiss yet */
1980 @d warning_issued 1 /* |history| value when |begin_diagnostic| has been called */
1981 @d error_message_issued 2 /* |history| value when |error| has been called */
1982 @d fatal_error_stop 3 /* |history| value when termination was premature */
1985 boolean deletions_allowed; /* is it safe for |error| to call |get_next|? */
1986 int history; /* has the source input been clean so far? */
1987 int error_count; /* the number of scrolled errors since the last statement ended */
1989 @ The value of |history| is initially |fatal_error_stop|, but it will
1990 be changed to |spotless| if \MP\ survives the initialization process.
1992 @<Allocate or ...@>=
1993 mp->deletions_allowed=true; mp->error_count=0; /* |history| is initialized elsewhere */
1995 @ Since errors can be detected almost anywhere in \MP, we want to declare the
1996 error procedures near the beginning of the program. But the error procedures
1997 in turn use some other procedures, which need to be declared |forward|
1998 before we get to |error| itself.
2000 It is possible for |error| to be called recursively if some error arises
2001 when |get_next| is being used to delete a token, and/or if some fatal error
2002 occurs while \MP\ is trying to fix a non-fatal one. But such recursion
2004 is never more than two levels deep.
2007 void mp_get_next (MP mp);
2008 void mp_term_input (MP mp);
2009 void mp_show_context (MP mp);
2010 void mp_begin_file_reading (MP mp);
2011 void mp_open_log_file (MP mp);
2012 void mp_clear_for_error_prompt (MP mp);
2013 void mp_debug_help (MP mp);
2014 @<Declare the procedure called |flush_string|@>
2017 void mp_normalize_selector (MP mp);
2019 @ Individual lines of help are recorded in the array |help_line|, which
2020 contains entries in positions |0..(help_ptr-1)|. They should be printed
2021 in reverse order, i.e., with |help_line[0]| appearing last.
2023 @d hlp1(A) mp->help_line[0]=(A); }
2024 @d hlp2(A) mp->help_line[1]=(A); hlp1
2025 @d hlp3(A) mp->help_line[2]=(A); hlp2
2026 @d hlp4(A) mp->help_line[3]=(A); hlp3
2027 @d hlp5(A) mp->help_line[4]=(A); hlp4
2028 @d hlp6(A) mp->help_line[5]=(A); hlp5
2029 @d help0 mp->help_ptr=0 /* sometimes there might be no help */
2030 @d help1 { mp->help_ptr=1; hlp1 /* use this with one help line */
2031 @d help2 { mp->help_ptr=2; hlp2 /* use this with two help lines */
2032 @d help3 { mp->help_ptr=3; hlp3 /* use this with three help lines */
2033 @d help4 { mp->help_ptr=4; hlp4 /* use this with four help lines */
2034 @d help5 { mp->help_ptr=5; hlp5 /* use this with five help lines */
2035 @d help6 { mp->help_ptr=6; hlp6 /* use this with six help lines */
2038 char * help_line[6]; /* helps for the next |error| */
2039 unsigned int help_ptr; /* the number of help lines present */
2040 boolean use_err_help; /* should the |err_help| string be shown? */
2041 str_number err_help; /* a string set up by \&{errhelp} */
2042 str_number filename_template; /* a string set up by \&{filenametemplate} */
2044 @ @<Allocate or ...@>=
2045 mp->help_ptr=0; mp->use_err_help=false; mp->err_help=0; mp->filename_template=0;
2047 @ The |jump_out| procedure just cuts across all active procedure levels and
2048 goes to |end_of_MP|. This is the only nonlocal |goto| statement in the
2049 whole program. It is used when there is no recovery from a particular error.
2051 Some \PASCAL\ compilers do not implement non-local |goto| statements.
2052 @^system dependencies@>
2053 In such cases the body of |jump_out| should simply be
2054 `|close_files_and_terminate|;\thinspace' followed by a call on some system
2055 procedure that quietly terminates the program.
2058 void mp_jump_out (MP mp) {
2062 @ Here now is the general |error| routine.
2065 void mp_error (MP mp) { /* completes the job of error reporting */
2066 ASCII_code c; /* what the user types */
2067 integer s1,s2,s3; /* used to save global variables when deleting tokens */
2068 pool_pointer j; /* character position being printed */
2069 if ( mp->history<error_message_issued ) mp->history=error_message_issued;
2070 mp_print_char(mp, '.'); mp_show_context(mp);
2071 if ( mp->interaction==mp_error_stop_mode ) {
2072 @<Get user's advice and |return|@>;
2074 incr(mp->error_count);
2075 if ( mp->error_count==100 ) {
2076 mp_print_nl(mp,"(That makes 100 errors; please try again.)");
2077 @.That makes 100 errors...@>
2078 mp->history=fatal_error_stop; mp_jump_out(mp);
2080 @<Put help message on the transcript file@>;
2082 void mp_warn (MP mp, char *msg) {
2083 int saved_selector = mp->selector;
2084 mp_normalize_selector(mp);
2085 mp_print_nl(mp,"Warning: ");
2087 mp->selector = saved_selector;
2091 void mp_error (MP mp);
2092 void mp_warn (MP mp, char *msg);
2095 @ @<Get user's advice...@>=
2098 mp_clear_for_error_prompt(mp); prompt_input("? ");
2100 if ( mp->last==mp->first ) return;
2101 c=mp->buffer[mp->first];
2102 if ( c>='a' ) c=c+'A'-'a'; /* convert to uppercase */
2103 @<Interpret code |c| and |return| if done@>;
2106 @ It is desirable to provide an `\.E' option here that gives the user
2107 an easy way to return from \MP\ to the system editor, with the offending
2108 line ready to be edited. But such an extension requires some system
2109 wizardry, so the present implementation simply types out the name of the
2111 edited and the relevant line number.
2112 @^system dependencies@>
2115 typedef void (*run_editor_command)(MP, char *, int);
2118 run_editor_command run_editor;
2120 @ @<Option variables@>=
2121 run_editor_command run_editor;
2123 @ @<Allocate or initialize ...@>=
2124 set_callback_option(run_editor);
2126 @ @<Exported function headers@>=
2127 void mp_run_editor (MP mp, char *fname, int fline);
2129 @ @c void mp_run_editor (MP mp, char *fname, int fline) {
2130 mp_print_nl(mp, "You want to edit file ");
2131 @.You want to edit file x@>
2132 mp_print(mp, fname);
2133 mp_print(mp, " at line ");
2134 mp_print_int(mp, fline);
2135 mp->interaction=mp_scroll_mode;
2140 There is a secret `\.D' option available when the debugging routines haven't
2144 @<Interpret code |c| and |return| if done@>=
2146 case '0': case '1': case '2': case '3': case '4':
2147 case '5': case '6': case '7': case '8': case '9':
2148 if ( mp->deletions_allowed ) {
2149 @<Delete |c-"0"| tokens and |continue|@>;
2154 mp_debug_help(mp); continue;
2158 if ( mp->file_ptr>0 ){
2159 (mp->run_editor)(mp,
2160 str(mp->input_stack[mp->file_ptr].name_field),
2165 @<Print the help information and |continue|@>;
2168 @<Introduce new material from the terminal and |return|@>;
2170 case 'Q': case 'R': case 'S':
2171 @<Change the interaction level and |return|@>;
2174 mp->interaction=mp_scroll_mode; mp_jump_out(mp);
2179 @<Print the menu of available options@>
2181 @ @<Print the menu...@>=
2183 mp_print(mp, "Type <return> to proceed, S to scroll future error messages,");
2184 @.Type <return> to proceed...@>
2185 mp_print_nl(mp, "R to run without stopping, Q to run quietly,");
2186 mp_print_nl(mp, "I to insert something, ");
2187 if ( mp->file_ptr>0 )
2188 mp_print(mp, "E to edit your file,");
2189 if ( mp->deletions_allowed )
2190 mp_print_nl(mp, "1 or ... or 9 to ignore the next 1 to 9 tokens of input,");
2191 mp_print_nl(mp, "H for help, X to quit.");
2194 @ Here the author of \MP\ apologizes for making use of the numerical
2195 relation between |"Q"|, |"R"|, |"S"|, and the desired interaction settings
2196 |mp_batch_mode|, |mp_nonstop_mode|, |mp_scroll_mode|.
2197 @^Knuth, Donald Ervin@>
2199 @<Change the interaction...@>=
2201 mp->error_count=0; mp->interaction=mp_batch_mode+c-'Q';
2202 mp_print(mp, "OK, entering ");
2204 case 'Q': mp_print(mp, "batchmode"); decr(mp->selector); break;
2205 case 'R': mp_print(mp, "nonstopmode"); break;
2206 case 'S': mp_print(mp, "scrollmode"); break;
2207 } /* there are no other cases */
2208 mp_print(mp, "..."); mp_print_ln(mp); update_terminal; return;
2211 @ When the following code is executed, |buffer[(first+1)..(last-1)]| may
2212 contain the material inserted by the user; otherwise another prompt will
2213 be given. In order to understand this part of the program fully, you need
2214 to be familiar with \MP's input stacks.
2216 @<Introduce new material...@>=
2218 mp_begin_file_reading(mp); /* enter a new syntactic level for terminal input */
2219 if ( mp->last>mp->first+1 ) {
2220 loc=mp->first+1; mp->buffer[mp->first]=' ';
2222 prompt_input("insert>"); loc=mp->first;
2225 mp->first=mp->last+1; mp->cur_input.limit_field=mp->last; return;
2228 @ We allow deletion of up to 99 tokens at a time.
2230 @<Delete |c-"0"| tokens...@>=
2232 s1=mp->cur_cmd; s2=mp->cur_mod; s3=mp->cur_sym; mp->OK_to_interrupt=false;
2233 if ( (mp->last>mp->first+1) && (mp->buffer[mp->first+1]>='0')&&(mp->buffer[mp->first+1]<='9') )
2234 c=c*10+mp->buffer[mp->first+1]-'0'*11;
2238 mp_get_next(mp); /* one-level recursive call of |error| is possible */
2239 @<Decrease the string reference count, if the current token is a string@>;
2242 mp->cur_cmd=s1; mp->cur_mod=s2; mp->cur_sym=s3; mp->OK_to_interrupt=true;
2243 help2("I have just deleted some text, as you asked.")
2244 ("You can now delete more, or insert, or whatever.");
2245 mp_show_context(mp);
2249 @ @<Print the help info...@>=
2251 if ( mp->use_err_help ) {
2252 @<Print the string |err_help|, possibly on several lines@>;
2253 mp->use_err_help=false;
2255 if ( mp->help_ptr==0 ) {
2256 help2("Sorry, I don't know how to help in this situation.")
2257 ("Maybe you should try asking a human?");
2260 decr(mp->help_ptr); mp_print(mp, mp->help_line[mp->help_ptr]); mp_print_ln(mp);
2261 } while (mp->help_ptr!=0);
2263 help4("Sorry, I already gave what help I could...")
2264 ("Maybe you should try asking a human?")
2265 ("An error might have occurred before I noticed any problems.")
2266 ("``If all else fails, read the instructions.''");
2270 @ @<Print the string |err_help|, possibly on several lines@>=
2271 j=mp->str_start[mp->err_help];
2272 while ( j<str_stop(mp->err_help) ) {
2273 if ( mp->str_pool[j]!='%' ) mp_print_str(mp, mp->str_pool[j]);
2274 else if ( j+1==str_stop(mp->err_help) ) mp_print_ln(mp);
2275 else if ( mp->str_pool[j+1]!='%' ) mp_print_ln(mp);
2276 else { incr(j); mp_print_char(mp, '%'); };
2280 @ @<Put help message on the transcript file@>=
2281 if ( mp->interaction>mp_batch_mode ) decr(mp->selector); /* avoid terminal output */
2282 if ( mp->use_err_help ) {
2283 mp_print_nl(mp, "");
2284 @<Print the string |err_help|, possibly on several lines@>;
2286 while ( mp->help_ptr>0 ){
2287 decr(mp->help_ptr); mp_print_nl(mp, mp->help_line[mp->help_ptr]);
2291 if ( mp->interaction>mp_batch_mode ) incr(mp->selector); /* re-enable terminal output */
2294 @ In anomalous cases, the print selector might be in an unknown state;
2295 the following subroutine is called to fix things just enough to keep
2296 running a bit longer.
2299 void mp_normalize_selector (MP mp) {
2300 if ( mp->log_opened ) mp->selector=term_and_log;
2301 else mp->selector=term_only;
2302 if ( mp->job_name==NULL ) mp_open_log_file(mp);
2303 if ( mp->interaction==mp_batch_mode ) decr(mp->selector);
2306 @ The following procedure prints \MP's last words before dying.
2308 @d succumb { if ( mp->interaction==mp_error_stop_mode )
2309 mp->interaction=mp_scroll_mode; /* no more interaction */
2310 if ( mp->log_opened ) mp_error(mp);
2311 /* if ( mp->interaction>mp_batch_mode ) mp_debug_help(mp); */
2312 mp->history=fatal_error_stop; mp_jump_out(mp); /* irrecoverable error */
2316 void mp_fatal_error (MP mp, char *s) { /* prints |s|, and that's it */
2317 mp_normalize_selector(mp);
2318 print_err("Emergency stop"); help1(s); succumb;
2323 void mp_fatal_error (MP mp, char *s);
2326 @ Here is the most dreaded error message.
2329 void mp_overflow (MP mp, char *s, integer n) { /* stop due to finiteness */
2330 mp_normalize_selector(mp);
2331 print_err("MetaPost capacity exceeded, sorry [");
2332 @.MetaPost capacity exceeded ...@>
2333 mp_print(mp, s); mp_print_char(mp, '='); mp_print_int(mp, n); mp_print_char(mp, ']');
2334 help2("If you really absolutely need more capacity,")
2335 ("you can ask a wizard to enlarge me.");
2340 void mp_overflow (MP mp, char *s, integer n);
2342 @ The program might sometime run completely amok, at which point there is
2343 no choice but to stop. If no previous error has been detected, that's bad
2344 news; a message is printed that is really intended for the \MP\
2345 maintenance person instead of the user (unless the user has been
2346 particularly diabolical). The index entries for `this can't happen' may
2347 help to pinpoint the problem.
2351 void mp_confusion (MP mp,char *s);
2353 @ @<Error hand...@>=
2354 void mp_confusion (MP mp,char *s) {
2355 /* consistency check violated; |s| tells where */
2356 mp_normalize_selector(mp);
2357 if ( mp->history<error_message_issued ) {
2358 print_err("This can't happen ("); mp_print(mp, s); mp_print_char(mp, ')');
2359 @.This can't happen@>
2360 help1("I'm broken. Please show this to someone who can fix can fix");
2362 print_err("I can\'t go on meeting you like this");
2363 @.I can't go on...@>
2364 help2("One of your faux pas seems to have wounded me deeply...")
2365 ("in fact, I'm barely conscious. Please fix it and try again.");
2370 @ Users occasionally want to interrupt \MP\ while it's running.
2371 If the \PASCAL\ runtime system allows this, one can implement
2372 a routine that sets the global variable |interrupt| to some nonzero value
2373 when such an interrupt is signaled. Otherwise there is probably at least
2374 a way to make |interrupt| nonzero using the \PASCAL\ debugger.
2375 @^system dependencies@>
2378 @d check_interrupt { if ( mp->interrupt!=0 )
2379 mp_pause_for_instructions(mp); }
2382 integer interrupt; /* should \MP\ pause for instructions? */
2383 boolean OK_to_interrupt; /* should interrupts be observed? */
2385 @ @<Allocate or ...@>=
2386 mp->interrupt=0; mp->OK_to_interrupt=true;
2388 @ When an interrupt has been detected, the program goes into its
2389 highest interaction level and lets the user have the full flexibility of
2390 the |error| routine. \MP\ checks for interrupts only at times when it is
2394 void mp_pause_for_instructions (MP mp) {
2395 if ( mp->OK_to_interrupt ) {
2396 mp->interaction=mp_error_stop_mode;
2397 if ( (mp->selector==log_only)||(mp->selector==no_print) )
2399 print_err("Interruption");
2402 ("Try to insert some instructions for me (e.g.,`I show x'),")
2403 ("unless you just want to quit by typing `X'.");
2404 mp->deletions_allowed=false; mp_error(mp); mp->deletions_allowed=true;
2409 @ Many of \MP's error messages state that a missing token has been
2410 inserted behind the scenes. We can save string space and program space
2411 by putting this common code into a subroutine.
2414 void mp_missing_err (MP mp, char *s) {
2415 print_err("Missing `"); mp_print(mp, s); mp_print(mp, "' has been inserted");
2416 @.Missing...inserted@>
2419 @* \[7] Arithmetic with scaled numbers.
2420 The principal computations performed by \MP\ are done entirely in terms of
2421 integers less than $2^{31}$ in magnitude; thus, the arithmetic specified in this
2422 program can be carried out in exactly the same way on a wide variety of
2423 computers, including some small ones.
2426 But \PASCAL\ does not define the |div|
2427 operation in the case of negative dividends; for example, the result of
2428 |(-2*n-1) div 2| is |-(n+1)| on some computers and |-n| on others.
2429 There are two principal types of arithmetic: ``translation-preserving,''
2430 in which the identity |(a+q*b)div b=(a div b)+q| is valid; and
2431 ``negation-preserving,'' in which |(-a)div b=-(a div b)|. This leads to
2432 two \MP s, which can produce different results, although the differences
2433 should be negligible when the language is being used properly.
2434 The \TeX\ processor has been defined carefully so that both varieties
2435 of arithmetic will produce identical output, but it would be too
2436 inefficient to constrain \MP\ in a similar way.
2438 @d el_gordo 017777777777 /* $2^{31}-1$, the largest value that \MP\ likes */
2440 @ One of \MP's most common operations is the calculation of
2441 $\lfloor{a+b\over2}\rfloor$,
2442 the midpoint of two given integers |a| and~|b|. The only decent way to do
2443 this in \PASCAL\ is to write `|(a+b) div 2|'; but on most machines it is
2444 far more efficient to calculate `|(a+b)| right shifted one bit'.
2446 Therefore the midpoint operation will always be denoted by `|half(a+b)|'
2447 in this program. If \MP\ is being implemented with languages that permit
2448 binary shifting, the |half| macro should be changed to make this operation
2449 as efficient as possible. Since some languages have shift operators that can
2450 only be trusted to work on positive numbers, there is also a macro |halfp|
2451 that is used only when the quantity being halved is known to be positive
2454 @d half(A) ((A)) / 2
2455 @d halfp(A) ((A)) / 2
2457 @ A single computation might use several subroutine calls, and it is
2458 desirable to avoid producing multiple error messages in case of arithmetic
2459 overflow. So the routines below set the global variable |arith_error| to |true|
2460 instead of reporting errors directly to the user.
2463 boolean arith_error; /* has arithmetic overflow occurred recently? */
2465 @ @<Allocate or ...@>=
2466 mp->arith_error=false;
2468 @ At crucial points the program will say |check_arith|, to test if
2469 an arithmetic error has been detected.
2471 @d check_arith { if ( mp->arith_error ) mp_clear_arith(mp); }
2474 void mp_clear_arith (MP mp) {
2475 print_err("Arithmetic overflow");
2476 @.Arithmetic overflow@>
2477 help4("Uh, oh. A little while ago one of the quantities that I was")
2478 ("computing got too large, so I'm afraid your answers will be")
2479 ("somewhat askew. You'll probably have to adopt different")
2480 ("tactics next time. But I shall try to carry on anyway.");
2482 mp->arith_error=false;
2485 @ Addition is not always checked to make sure that it doesn't overflow,
2486 but in places where overflow isn't too unlikely the |slow_add| routine
2489 @c integer mp_slow_add (MP mp,integer x, integer y) {
2491 if ( y<=el_gordo-x ) {
2494 mp->arith_error=true;
2497 } else if ( -y<=el_gordo+x ) {
2500 mp->arith_error=true;
2505 @ Fixed-point arithmetic is done on {\sl scaled integers\/} that are multiples
2506 of $2^{-16}$. In other words, a binary point is assumed to be sixteen bit
2507 positions from the right end of a binary computer word.
2509 @d quarter_unit 040000 /* $2^{14}$, represents 0.250000 */
2510 @d half_unit 0100000 /* $2^{15}$, represents 0.50000 */
2511 @d three_quarter_unit 0140000 /* $3\cdot2^{14}$, represents 0.75000 */
2512 @d unity 0200000 /* $2^{16}$, represents 1.00000 */
2513 @d two 0400000 /* $2^{17}$, represents 2.00000 */
2514 @d three 0600000 /* $2^{17}+2^{16}$, represents 3.00000 */
2517 typedef integer scaled; /* this type is used for scaled integers */
2518 typedef unsigned char small_number; /* this type is self-explanatory */
2520 @ The following function is used to create a scaled integer from a given decimal
2521 fraction $(.d_0d_1\ldots d_{k-1})$, where |0<=k<=17|. The digit $d_i$ is
2522 given in |dig[i]|, and the calculation produces a correctly rounded result.
2525 scaled mp_round_decimals (MP mp,small_number k) {
2526 /* converts a decimal fraction */
2527 integer a = 0; /* the accumulator */
2529 a=(a+mp->dig[k]*two) / 10;
2534 @ Conversely, here is a procedure analogous to |print_int|. If the output
2535 of this procedure is subsequently read by \MP\ and converted by the
2536 |round_decimals| routine above, it turns out that the original value will
2537 be reproduced exactly. A decimal point is printed only if the value is
2538 not an integer. If there is more than one way to print the result with
2539 the optimum number of digits following the decimal point, the closest
2540 possible value is given.
2542 The invariant relation in the \&{repeat} loop is that a sequence of
2543 decimal digits yet to be printed will yield the original number if and only if
2544 they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
2545 We can stop if and only if $f=0$ satisfies this condition; the loop will
2546 terminate before $s$ can possibly become zero.
2548 @<Basic printing...@>=
2549 void mp_print_scaled (MP mp,scaled s) { /* prints scaled real, rounded to five digits */
2550 scaled delta; /* amount of allowable inaccuracy */
2552 mp_print_char(mp, '-');
2553 negate(s); /* print the sign, if negative */
2555 mp_print_int(mp, s / unity); /* print the integer part */
2559 mp_print_char(mp, '.');
2562 s=s+0100000-(delta / 2); /* round the final digit */
2563 mp_print_char(mp, '0'+(s / unity));
2570 @ We often want to print two scaled quantities in parentheses,
2571 separated by a comma.
2573 @<Basic printing...@>=
2574 void mp_print_two (MP mp,scaled x, scaled y) { /* prints `|(x,y)|' */
2575 mp_print_char(mp, '(');
2576 mp_print_scaled(mp, x);
2577 mp_print_char(mp, ',');
2578 mp_print_scaled(mp, y);
2579 mp_print_char(mp, ')');
2582 @ The |scaled| quantities in \MP\ programs are generally supposed to be
2583 less than $2^{12}$ in absolute value, so \MP\ does much of its internal
2584 arithmetic with 28~significant bits of precision. A |fraction| denotes
2585 a scaled integer whose binary point is assumed to be 28 bit positions
2588 @d fraction_half 01000000000 /* $2^{27}$, represents 0.50000000 */
2589 @d fraction_one 02000000000 /* $2^{28}$, represents 1.00000000 */
2590 @d fraction_two 04000000000 /* $2^{29}$, represents 2.00000000 */
2591 @d fraction_three 06000000000 /* $3\cdot2^{28}$, represents 3.00000000 */
2592 @d fraction_four 010000000000 /* $2^{30}$, represents 4.00000000 */
2595 typedef integer fraction; /* this type is used for scaled fractions */
2597 @ In fact, the two sorts of scaling discussed above aren't quite
2598 sufficient; \MP\ has yet another, used internally to keep track of angles
2599 in units of $2^{-20}$ degrees.
2601 @d forty_five_deg 0264000000 /* $45\cdot2^{20}$, represents $45^\circ$ */
2602 @d ninety_deg 0550000000 /* $90\cdot2^{20}$, represents $90^\circ$ */
2603 @d one_eighty_deg 01320000000 /* $180\cdot2^{20}$, represents $180^\circ$ */
2604 @d three_sixty_deg 02640000000 /* $360\cdot2^{20}$, represents $360^\circ$ */
2607 typedef integer angle; /* this type is used for scaled angles */
2609 @ The |make_fraction| routine produces the |fraction| equivalent of
2610 |p/q|, given integers |p| and~|q|; it computes the integer
2611 $f=\lfloor2^{28}p/q+{1\over2}\rfloor$, when $p$ and $q$ are
2612 positive. If |p| and |q| are both of the same scaled type |t|,
2613 the ``type relation'' |make_fraction(t,t)=fraction| is valid;
2614 and it's also possible to use the subroutine ``backwards,'' using
2615 the relation |make_fraction(t,fraction)=t| between scaled types.
2617 If the result would have magnitude $2^{31}$ or more, |make_fraction|
2618 sets |arith_error:=true|. Most of \MP's internal computations have
2619 been designed to avoid this sort of error.
2621 If this subroutine were programmed in assembly language on a typical
2622 machine, we could simply compute |(@t$2^{28}$@>*p)div q|, since a
2623 double-precision product can often be input to a fixed-point division
2624 instruction. But when we are restricted to \PASCAL\ arithmetic it
2625 is necessary either to resort to multiple-precision maneuvering
2626 or to use a simple but slow iteration. The multiple-precision technique
2627 would be about three times faster than the code adopted here, but it
2628 would be comparatively long and tricky, involving about sixteen
2629 additional multiplications and divisions.
2631 This operation is part of \MP's ``inner loop''; indeed, it will
2632 consume nearly 10\pct! of the running time (exclusive of input and output)
2633 if the code below is left unchanged. A machine-dependent recoding
2634 will therefore make \MP\ run faster. The present implementation
2635 is highly portable, but slow; it avoids multiplication and division
2636 except in the initial stage. System wizards should be careful to
2637 replace it with a routine that is guaranteed to produce identical
2638 results in all cases.
2639 @^system dependencies@>
2641 As noted below, a few more routines should also be replaced by machine-dependent
2642 code, for efficiency. But when a procedure is not part of the ``inner loop,''
2643 such changes aren't advisable; simplicity and robustness are
2644 preferable to trickery, unless the cost is too high.
2648 fraction mp_make_fraction (MP mp,integer p, integer q);
2649 integer mp_take_scaled (MP mp,integer q, scaled f) ;
2651 @ If FIXPT is not defined, we need these preprocessor values
2653 @d ELGORDO 0x7fffffff
2654 @d TWEXP31 2147483648.0
2655 @d TWEXP28 268435456.0
2657 @d TWEXP_16 (1.0/65536.0)
2658 @d TWEXP_28 (1.0/268435456.0)
2662 fraction mp_make_fraction (MP mp,integer p, integer q) {
2664 integer f; /* the fraction bits, with a leading 1 bit */
2665 integer n; /* the integer part of $\vert p/q\vert$ */
2666 integer be_careful; /* disables certain compiler optimizations */
2667 boolean negative = false; /* should the result be negated? */
2669 negate(p); negative=true;
2673 if ( q==0 ) mp_confusion(mp, '/');
2675 @:this can't happen /}{\quad \./@>
2676 negate(q); negative = ! negative;
2680 mp->arith_error=true;
2681 return ( negative ? -el_gordo : el_gordo);
2683 n=(n-1)*fraction_one;
2684 @<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>;
2685 return (negative ? (-(f+n)) : (f+n));
2691 if (q==0) mp_confusion(mp,'/');
2693 d = TWEXP28 * (double)p /(double)q;
2696 if (d>=TWEXP31) {mp->arith_error=true; return ELGORDO;}
2698 if (d==i && ( ((q>0 ? -q : q)&077777)
2699 * (((i&037777)<<1)-1) & 04000)!=0) --i;
2702 if (d<= -TWEXP31) {mp->arith_error=true; return -ELGORDO;}
2704 if (d==i && ( ((q>0 ? q : -q)&077777)
2705 * (((i&037777)<<1)+1) & 04000)!=0) ++i;
2711 @ The |repeat| loop here preserves the following invariant relations
2712 between |f|, |p|, and~|q|:
2713 (i)~|0<=p<q|; (ii)~$fq+p=2^k(q+p_0)$, where $k$ is an integer and
2714 $p_0$ is the original value of~$p$.
2716 Notice that the computation specifies
2717 |(p-q)+p| instead of |(p+p)-q|, because the latter could overflow.
2718 Let us hope that optimizing compilers do not miss this point; a
2719 special variable |be_careful| is used to emphasize the necessary
2720 order of computation. Optimizing compilers should keep |be_careful|
2721 in a register, not store it in memory.
2724 @<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>=
2728 be_careful=p-q; p=be_careful+p;
2734 } while (f<fraction_one);
2736 if ( be_careful+p>=0 ) incr(f);
2739 @ The dual of |make_fraction| is |take_fraction|, which multiplies a
2740 given integer~|q| by a fraction~|f|. When the operands are positive, it
2741 computes $p=\lfloor qf/2^{28}+{1\over2}\rfloor$, a symmetric function
2744 This routine is even more ``inner loopy'' than |make_fraction|;
2745 the present implementation consumes almost 20\pct! of \MP's computation
2746 time during typical jobs, so a machine-language substitute is advisable.
2747 @^inner loop@> @^system dependencies@>
2750 integer mp_take_fraction (MP mp,integer q, fraction f) ;
2754 integer mp_take_fraction (MP mp,integer q, fraction f) {
2755 integer p; /* the fraction so far */
2756 boolean negative; /* should the result be negated? */
2757 integer n; /* additional multiple of $q$ */
2758 integer be_careful; /* disables certain compiler optimizations */
2759 @<Reduce to the case that |f>=0| and |q>0|@>;
2760 if ( f<fraction_one ) {
2763 n=f / fraction_one; f=f % fraction_one;
2764 if ( q<=el_gordo / n ) {
2767 mp->arith_error=true; n=el_gordo;
2771 @<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>;
2772 be_careful=n-el_gordo;
2773 if ( be_careful+p>0 ){
2774 mp->arith_error=true; n=el_gordo-p;
2781 integer mp_take_fraction (MP mp,integer p, fraction q) {
2784 d = (double)p * (double)q * TWEXP_28;
2788 if (d!=TWEXP31 || (((p&077777)*(q&077777))&040000)==0)
2789 mp->arith_error = true;
2793 if (d==i && (((p&077777)*(q&077777))&040000)!=0) --i;
2797 if (d!= -TWEXP31 || ((-(p&077777)*(q&077777))&040000)==0)
2798 mp->arith_error = true;
2802 if (d==i && ((-(p&077777)*(q&077777))&040000)!=0) ++i;
2808 @ @<Reduce to the case that |f>=0| and |q>0|@>=
2812 negate( f); negative=true;
2815 negate(q); negative=! negative;
2818 @ The invariant relations in this case are (i)~$\lfloor(qf+p)/2^k\rfloor
2819 =\lfloor qf_0/2^{28}+{1\over2}\rfloor$, where $k$ is an integer and
2820 $f_0$ is the original value of~$f$; (ii)~$2^k\L f<2^{k+1}$.
2823 @<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>=
2824 p=fraction_half; /* that's $2^{27}$; the invariants hold now with $k=28$ */
2825 if ( q<fraction_four ) {
2827 if ( odd(f) ) p=halfp(p+q); else p=halfp(p);
2832 if ( odd(f) ) p=p+halfp(q-p); else p=halfp(p);
2838 @ When we want to multiply something by a |scaled| quantity, we use a scheme
2839 analogous to |take_fraction| but with a different scaling.
2840 Given positive operands, |take_scaled|
2841 computes the quantity $p=\lfloor qf/2^{16}+{1\over2}\rfloor$.
2843 Once again it is a good idea to use a machine-language replacement if
2844 possible; otherwise |take_scaled| will use more than 2\pct! of the running time
2845 when the Computer Modern fonts are being generated.
2850 integer mp_take_scaled (MP mp,integer q, scaled f) {
2851 integer p; /* the fraction so far */
2852 boolean negative; /* should the result be negated? */
2853 integer n; /* additional multiple of $q$ */
2854 integer be_careful; /* disables certain compiler optimizations */
2855 @<Reduce to the case that |f>=0| and |q>0|@>;
2859 n=f / unity; f=f % unity;
2860 if ( q<=el_gordo / n ) {
2863 mp->arith_error=true; n=el_gordo;
2867 @<Compute $p=\lfloor qf/2^{16}+{1\over2}\rfloor-q$@>;
2868 be_careful=n-el_gordo;
2869 if ( be_careful+p>0 ) {
2870 mp->arith_error=true; n=el_gordo-p;
2872 return ( negative ?(-(n+p)) :(n+p));
2874 integer mp_take_scaled (MP mp,integer p, scaled q) {
2877 d = (double)p * (double)q * TWEXP_16;
2881 if (d!=TWEXP31 || (((p&077777)*(q&077777))&040000)==0)
2882 mp->arith_error = true;
2886 if (d==i && (((p&077777)*(q&077777))&040000)!=0) --i;
2890 if (d!= -TWEXP31 || ((-(p&077777)*(q&077777))&040000)==0)
2891 mp->arith_error = true;
2895 if (d==i && ((-(p&077777)*(q&077777))&040000)!=0) ++i;
2901 @ @<Compute $p=\lfloor qf/2^{16}+{1\over2}\rfloor-q$@>=
2902 p=half_unit; /* that's $2^{15}$; the invariants hold now with $k=16$ */
2904 if ( q<fraction_four ) {
2906 p = (odd(f) ? halfp(p+q) : halfp(p));
2911 p = (odd(f) ? p+halfp(q-p) : halfp(p));
2916 @ For completeness, there's also |make_scaled|, which computes a
2917 quotient as a |scaled| number instead of as a |fraction|.
2918 In other words, the result is $\lfloor2^{16}p/q+{1\over2}\rfloor$, if the
2919 operands are positive. \ (This procedure is not used especially often,
2920 so it is not part of \MP's inner loop.)
2923 scaled mp_make_scaled (MP mp,integer p, integer q) {
2925 integer f; /* the fraction bits, with a leading 1 bit */
2926 integer n; /* the integer part of $\vert p/q\vert$ */
2927 boolean negative; /* should the result be negated? */
2928 integer be_careful; /* disables certain compiler optimizations */
2929 if ( p>=0 ) negative=false;
2930 else { negate(p); negative=true; };
2933 if ( q==0 ) mp_confusion(mp, "/");
2934 @:this can't happen /}{\quad \./@>
2936 negate(q); negative=! negative;
2940 mp->arith_error=true;
2941 return (negative ? (-el_gordo) : el_gordo);
2944 @<Compute $f=\lfloor 2^{16}(1+p/q)+{1\over2}\rfloor$@>;
2945 return ( negative ? (-(f+n)) :(f+n));
2951 if (q==0) mp_confusion(mp,"/");
2953 d = TWEXP16 * (double)p /(double)q;
2956 if (d>=TWEXP31) {mp->arith_error=true; return ELGORDO;}
2958 if (d==i && ( ((q>0 ? -q : q)&077777)
2959 * (((i&037777)<<1)-1) & 04000)!=0) --i;
2962 if (d<= -TWEXP31) {mp->arith_error=true; return -ELGORDO;}
2964 if (d==i && ( ((q>0 ? q : -q)&077777)
2965 * (((i&037777)<<1)+1) & 04000)!=0) ++i;
2971 @ @<Compute $f=\lfloor 2^{16}(1+p/q)+{1\over2}\rfloor$@>=
2974 be_careful=p-q; p=be_careful+p;
2975 if ( p>=0 ) f=f+f+1;
2976 else { f+=f; p=p+q; };
2979 if ( be_careful+p>=0 ) incr(f)
2981 @ Here is a typical example of how the routines above can be used.
2982 It computes the function
2983 $${1\over3\tau}f(\theta,\phi)=
2984 {\tau^{-1}\bigl(2+\sqrt2\,(\sin\theta-{1\over16}\sin\phi)
2985 (\sin\phi-{1\over16}\sin\theta)(\cos\theta-\cos\phi)\bigr)\over
2986 3\,\bigl(1+{1\over2}(\sqrt5-1)\cos\theta+{1\over2}(3-\sqrt5\,)\cos\phi\bigr)},$$
2987 where $\tau$ is a |scaled| ``tension'' parameter. This is \MP's magic
2988 fudge factor for placing the first control point of a curve that starts
2989 at an angle $\theta$ and ends at an angle $\phi$ from the straight path.
2990 (Actually, if the stated quantity exceeds 4, \MP\ reduces it to~4.)
2992 The trigonometric quantity to be multiplied by $\sqrt2$ is less than $\sqrt2$.
2993 (It's a sum of eight terms whose absolute values can be bounded using
2994 relations such as $\sin\theta\cos\theta\L{1\over2}$.) Thus the numerator
2995 is positive; and since the tension $\tau$ is constrained to be at least
2996 $3\over4$, the numerator is less than $16\over3$. The denominator is
2997 nonnegative and at most~6. Hence the fixed-point calculations below
2998 are guaranteed to stay within the bounds of a 32-bit computer word.
3000 The angles $\theta$ and $\phi$ are given implicitly in terms of |fraction|
3001 arguments |st|, |ct|, |sf|, and |cf|, representing $\sin\theta$, $\cos\theta$,
3002 $\sin\phi$, and $\cos\phi$, respectively.
3005 fraction mp_velocity (MP mp,fraction st, fraction ct, fraction sf,
3006 fraction cf, scaled t) {
3007 integer acc,num,denom; /* registers for intermediate calculations */
3008 acc=mp_take_fraction(mp, st-(sf / 16), sf-(st / 16));
3009 acc=mp_take_fraction(mp, acc,ct-cf);
3010 num=fraction_two+mp_take_fraction(mp, acc,379625062);
3011 /* $2^{28}\sqrt2\approx379625062.497$ */
3012 denom=fraction_three+mp_take_fraction(mp, ct,497706707)+mp_take_fraction(mp, cf,307599661);
3013 /* $3\cdot2^{27}\cdot(\sqrt5-1)\approx497706706.78$ and
3014 $3\cdot2^{27}\cdot(3-\sqrt5\,)\approx307599661.22$ */
3015 if ( t!=unity ) num=mp_make_scaled(mp, num,t);
3016 /* |make_scaled(fraction,scaled)=fraction| */
3017 if ( num / 4>=denom )
3018 return fraction_four;
3020 return mp_make_fraction(mp, num, denom);
3023 @ The following somewhat different subroutine tests rigorously if $ab$ is
3024 greater than, equal to, or less than~$cd$,
3025 given integers $(a,b,c,d)$. In most cases a quick decision is reached.
3026 The result is $+1$, 0, or~$-1$ in the three respective cases.
3028 @d mp_ab_vs_cd(M,A,B,C,D) mp_do_ab_vs_cd(A,B,C,D)
3031 integer mp_do_ab_vs_cd (integer a,integer b, integer c, integer d) {
3032 integer q,r; /* temporary registers */
3033 @<Reduce to the case that |a,c>=0|, |b,d>0|@>;
3035 q = a / d; r = c / b;
3037 return ( q>r ? 1 : -1);
3038 q = a % d; r = c % b;
3041 if ( q==0 ) return -1;
3043 } /* now |a>d>0| and |c>b>0| */
3046 @ @<Reduce to the case that |a...@>=
3047 if ( a<0 ) { negate(a); negate(b); };
3048 if ( c<0 ) { negate(c); negate(d); };
3051 if ( (a==0||b==0)&&(c==0||d==0) ) return 0;
3055 return ( a==0 ? 0 : -1);
3056 q=a; a=c; c=q; q=-b; b=-d; d=q;
3057 } else if ( b<=0 ) {
3058 if ( b<0 ) if ( a>0 ) return -1;
3059 return (c==0 ? 0 : -1);
3062 @ We conclude this set of elementary routines with some simple rounding
3063 and truncation operations that are coded in a machine-independent fashion.
3064 The routines are slightly complicated because we want them to work
3065 without overflow whenever $-2^{31}\L x<2^{31}$.
3068 #define mp_floor_scaled(M,i) ((i)&(-65536))
3069 #define mp_round_unscaled(M,i) (((i>>15)+1)>>1)
3070 #define mp_round_fraction(M,i) (((i>>11)+1)>>1)
3073 @* \[8] Algebraic and transcendental functions.
3074 \MP\ computes all of the necessary special functions from scratch, without
3075 relying on |real| arithmetic or system subroutines for sines, cosines, etc.
3077 @ To get the square root of a |scaled| number |x|, we want to calculate
3078 $s=\lfloor 2^8\!\sqrt x +{1\over2}\rfloor$. If $x>0$, this is the unique
3079 integer such that $2^{16}x-s\L s^2<2^{16}x+s$. The following subroutine
3080 determines $s$ by an iterative method that maintains the invariant
3081 relations $x=2^{46-2k}x_0\bmod 2^{30}$, $0<y=\lfloor 2^{16-2k}x_0\rfloor
3082 -s^2+s\L q=2s$, where $x_0$ is the initial value of $x$. The value of~$y$
3083 might, however, be zero at the start of the first iteration.
3086 scaled mp_square_rt (MP mp,scaled x) ;
3089 scaled mp_square_rt (MP mp,scaled x) {
3090 small_number k; /* iteration control counter */
3091 integer y,q; /* registers for intermediate calculations */
3093 @<Handle square root of zero or negative argument@>;
3096 while ( x<fraction_two ) { /* i.e., |while x<@t$2^{29}$@>|\unskip */
3099 if ( x<fraction_four ) y=0;
3100 else { x=x-fraction_four; y=1; };
3102 @<Decrease |k| by 1, maintaining the invariant
3103 relations between |x|, |y|, and~|q|@>;
3109 @ @<Handle square root of zero...@>=
3112 print_err("Square root of ");
3113 @.Square root...replaced by 0@>
3114 mp_print_scaled(mp, x); mp_print(mp, " has been replaced by 0");
3115 help2("Since I don't take square roots of negative numbers,")
3116 ("I'm zeroing this one. Proceed, with fingers crossed.");
3122 @ @<Decrease |k| by 1, maintaining...@>=
3124 if ( x>=fraction_four ) { /* note that |fraction_four=@t$2^{30}$@>| */
3125 x=x-fraction_four; incr(y);
3127 x+=x; y=y+y-q; q+=q;
3128 if ( x>=fraction_four ) { x=x-fraction_four; incr(y); };
3129 if ( y>q ){ y=y-q; q=q+2; }
3130 else if ( y<=0 ) { q=q-2; y=y+q; };
3133 @ Pythagorean addition $\psqrt{a^2+b^2}$ is implemented by an elegant
3134 iterative scheme due to Cleve Moler and Donald Morrison [{\sl IBM Journal
3135 @^Moler, Cleve Barry@>
3136 @^Morrison, Donald Ross@>
3137 of Research and Development\/ \bf27} (1983), 577--581]. It modifies |a| and~|b|
3138 in such a way that their Pythagorean sum remains invariant, while the
3139 smaller argument decreases.
3142 integer mp_pyth_add (MP mp,integer a, integer b) {
3143 fraction r; /* register used to transform |a| and |b| */
3144 boolean big; /* is the result dangerously near $2^{31}$? */
3146 if ( a<b ) { r=b; b=a; a=r; }; /* now |0<=b<=a| */
3148 if ( a<fraction_two ) {
3151 a=a / 4; b=b / 4; big=true;
3152 }; /* we reduced the precision to avoid arithmetic overflow */
3153 @<Replace |a| by an approximation to $\psqrt{a^2+b^2}$@>;
3155 if ( a<fraction_two ) {
3158 mp->arith_error=true; a=el_gordo;
3165 @ The key idea here is to reflect the vector $(a,b)$ about the
3166 line through $(a,b/2)$.
3168 @<Replace |a| by an approximation to $\psqrt{a^2+b^2}$@>=
3170 r=mp_make_fraction(mp, b,a);
3171 r=mp_take_fraction(mp, r,r); /* now $r\approx b^2/a^2$ */
3173 r=mp_make_fraction(mp, r,fraction_four+r);
3174 a=a+mp_take_fraction(mp, a+a,r); b=mp_take_fraction(mp, b,r);
3178 @ Here is a similar algorithm for $\psqrt{a^2-b^2}$.
3179 It converges slowly when $b$ is near $a$, but otherwise it works fine.
3182 integer mp_pyth_sub (MP mp,integer a, integer b) {
3183 fraction r; /* register used to transform |a| and |b| */
3184 boolean big; /* is the input dangerously near $2^{31}$? */
3187 @<Handle erroneous |pyth_sub| and set |a:=0|@>;
3189 if ( a<fraction_four ) {
3192 a=halfp(a); b=halfp(b); big=true;
3194 @<Replace |a| by an approximation to $\psqrt{a^2-b^2}$@>;
3200 @ @<Replace |a| by an approximation to $\psqrt{a^2-b^2}$@>=
3202 r=mp_make_fraction(mp, b,a);
3203 r=mp_take_fraction(mp, r,r); /* now $r\approx b^2/a^2$ */
3205 r=mp_make_fraction(mp, r,fraction_four-r);
3206 a=a-mp_take_fraction(mp, a+a,r); b=mp_take_fraction(mp, b,r);
3209 @ @<Handle erroneous |pyth_sub| and set |a:=0|@>=
3212 print_err("Pythagorean subtraction "); mp_print_scaled(mp, a);
3213 mp_print(mp, "+-+"); mp_print_scaled(mp, b);
3214 mp_print(mp, " has been replaced by 0");
3216 help2("Since I don't take square roots of negative numbers,")
3217 ("I'm zeroing this one. Proceed, with fingers crossed.");
3223 @ The subroutines for logarithm and exponential involve two tables.
3224 The first is simple: |two_to_the[k]| equals $2^k$. The second involves
3225 a bit more calculation, which the author claims to have done correctly:
3226 |spec_log[k]| is $2^{27}$ times $\ln\bigl(1/(1-2^{-k})\bigr)=
3227 2^{-k}+{1\over2}2^{-2k}+{1\over3}2^{-3k}+\cdots\,$, rounded to the
3230 @d two_to_the(A) (1<<(A))
3233 static const integer spec_log[29] = { 0, /* special logarithms */
3234 93032640, 38612034, 17922280, 8662214, 4261238, 2113709,
3235 1052693, 525315, 262400, 131136, 65552, 32772, 16385,
3236 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 1 };
3238 @ @<Local variables for initialization@>=
3239 integer k; /* all-purpose loop index */
3242 @ Here is the routine that calculates $2^8$ times the natural logarithm
3243 of a |scaled| quantity; it is an integer approximation to $2^{24}\ln(x/2^{16})$,
3244 when |x| is a given positive integer.
3246 The method is based on exercise 1.2.2--25 in {\sl The Art of Computer
3247 Programming\/}: During the main iteration we have $1\L 2^{-30}x<1/(1-2^{1-k})$,
3248 and the logarithm of $2^{30}x$ remains to be added to an accumulator
3249 register called~$y$. Three auxiliary bits of accuracy are retained in~$y$
3250 during the calculation, and sixteen auxiliary bits to extend |y| are
3251 kept in~|z| during the initial argument reduction. (We add
3252 $100\cdot2^{16}=6553600$ to~|z| and subtract 100 from~|y| so that |z| will
3253 not become negative; also, the actual amount subtracted from~|y| is~96,
3254 not~100, because we want to add~4 for rounding before the final division by~8.)
3257 scaled mp_m_log (MP mp,scaled x) {
3258 integer y,z; /* auxiliary registers */
3259 integer k; /* iteration counter */
3261 @<Handle non-positive logarithm@>;
3263 y=1302456956+4-100; /* $14\times2^{27}\ln2\approx1302456956.421063$ */
3264 z=27595+6553600; /* and $2^{16}\times .421063\approx 27595$ */
3265 while ( x<fraction_four ) {
3266 x+=x; y=y-93032639; z=z-48782;
3267 } /* $2^{27}\ln2\approx 93032639.74436163$ and $2^{16}\times.74436163\approx 48782$ */
3268 y=y+(z / unity); k=2;
3269 while ( x>fraction_four+4 ) {
3270 @<Increase |k| until |x| can be multiplied by a
3271 factor of $2^{-k}$, and adjust $y$ accordingly@>;
3277 @ @<Increase |k| until |x| can...@>=
3279 z=((x-1) / two_to_the(k))+1; /* $z=\lceil x/2^k\rceil$ */
3280 while ( x<fraction_four+z ) { z=halfp(z+1); k=k+1; };
3281 y=y+spec_log[k]; x=x-z;
3284 @ @<Handle non-positive logarithm@>=
3286 print_err("Logarithm of ");
3287 @.Logarithm...replaced by 0@>
3288 mp_print_scaled(mp, x); mp_print(mp, " has been replaced by 0");
3289 help2("Since I don't take logs of non-positive numbers,")
3290 ("I'm zeroing this one. Proceed, with fingers crossed.");
3295 @ Conversely, the exponential routine calculates $\exp(x/2^8)$,
3296 when |x| is |scaled|. The result is an integer approximation to
3297 $2^{16}\exp(x/2^{24})$, when |x| is regarded as an integer.
3300 scaled mp_m_exp (MP mp,scaled x) {
3301 small_number k; /* loop control index */
3302 integer y,z; /* auxiliary registers */
3303 if ( x>174436200 ) {
3304 /* $2^{24}\ln((2^{31}-1)/2^{16})\approx 174436199.51$ */
3305 mp->arith_error=true;
3307 } else if ( x<-197694359 ) {
3308 /* $2^{24}\ln(2^{-1}/2^{16})\approx-197694359.45$ */
3312 z=-8*x; y=04000000; /* $y=2^{20}$ */
3314 if ( x<=127919879 ) {
3316 /* $2^{27}\ln((2^{31}-1)/2^{20})\approx 1023359037.125$ */
3318 z=8*(174436200-x); /* |z| is always nonnegative */
3322 @<Multiply |y| by $\exp(-z/2^{27})$@>;
3324 return ((y+8) / 16);
3330 @ The idea here is that subtracting |spec_log[k]| from |z| corresponds
3331 to multiplying |y| by $1-2^{-k}$.
3333 A subtle point (which had to be checked) was that if $x=127919879$, the
3334 value of~|y| will decrease so that |y+8| doesn't overflow. In fact,
3335 $z$ will be 5 in this case, and |y| will decrease by~64 when |k=25|
3336 and by~16 when |k=27|.
3338 @<Multiply |y| by...@>=
3341 while ( z>=spec_log[k] ) {
3343 y=y-1-((y-two_to_the(k-1)) / two_to_the(k));
3348 @ The trigonometric subroutines use an auxiliary table such that
3349 |spec_atan[k]| contains an approximation to the |angle| whose tangent
3350 is~$1/2^k$. $\arctan2^{-k}$ times $2^{20}\cdot180/\pi$
3353 static const angle spec_atan[27] = { 0, 27855475, 14718068, 7471121, 3750058,
3354 1876857, 938658, 469357, 234682, 117342, 58671, 29335, 14668, 7334, 3667,
3355 1833, 917, 458, 229, 115, 57, 29, 14, 7, 4, 2, 1 };
3357 @ Given integers |x| and |y|, not both zero, the |n_arg| function
3358 returns the |angle| whose tangent points in the direction $(x,y)$.
3359 This subroutine first determines the correct octant, then solves the
3360 problem for |0<=y<=x|, then converts the result appropriately to
3361 return an answer in the range |-one_eighty_deg<=@t$\theta$@><=one_eighty_deg|.
3362 (The answer is |+one_eighty_deg| if |y=0| and |x<0|, but an answer of
3363 |-one_eighty_deg| is possible if, for example, |y=-1| and $x=-2^{30}$.)
3365 The octants are represented in a ``Gray code,'' since that turns out
3366 to be computationally simplest.
3372 @d second_octant (first_octant+switch_x_and_y)
3373 @d third_octant (first_octant+switch_x_and_y+negate_x)
3374 @d fourth_octant (first_octant+negate_x)
3375 @d fifth_octant (first_octant+negate_x+negate_y)
3376 @d sixth_octant (first_octant+switch_x_and_y+negate_x+negate_y)
3377 @d seventh_octant (first_octant+switch_x_and_y+negate_y)
3378 @d eighth_octant (first_octant+negate_y)
3381 angle mp_n_arg (MP mp,integer x, integer y) {
3382 angle z; /* auxiliary register */
3383 integer t; /* temporary storage */
3384 small_number k; /* loop counter */
3385 int octant; /* octant code */
3387 octant=first_octant;
3389 negate(x); octant=first_octant+negate_x;
3392 negate(y); octant=octant+negate_y;
3395 t=y; y=x; x=t; octant=octant+switch_x_and_y;
3398 @<Handle undefined arg@>;
3400 @<Set variable |z| to the arg of $(x,y)$@>;
3401 @<Return an appropriate answer based on |z| and |octant|@>;
3405 @ @<Handle undefined arg@>=
3407 print_err("angle(0,0) is taken as zero");
3408 @.angle(0,0)...zero@>
3409 help2("The `angle' between two identical points is undefined.")
3410 ("I'm zeroing this one. Proceed, with fingers crossed.");
3415 @ @<Return an appropriate answer...@>=
3417 case first_octant: return z;
3418 case second_octant: return (ninety_deg-z);
3419 case third_octant: return (ninety_deg+z);
3420 case fourth_octant: return (one_eighty_deg-z);
3421 case fifth_octant: return (z-one_eighty_deg);
3422 case sixth_octant: return (-z-ninety_deg);
3423 case seventh_octant: return (z-ninety_deg);
3424 case eighth_octant: return (-z);
3425 }; /* there are no other cases */
3428 @ At this point we have |x>=y>=0|, and |x>0|. The numbers are scaled up
3429 or down until $2^{28}\L x<2^{29}$, so that accurate fixed-point calculations
3432 @<Set variable |z| to the arg...@>=
3433 while ( x>=fraction_two ) {
3434 x=halfp(x); y=halfp(y);
3438 while ( x<fraction_one ) {
3441 @<Increase |z| to the arg of $(x,y)$@>;
3444 @ During the calculations of this section, variables |x| and~|y|
3445 represent actual coordinates $(x,2^{-k}y)$. We will maintain the
3446 condition |x>=y|, so that the tangent will be at most $2^{-k}$.
3447 If $x<2y$, the tangent is greater than $2^{-k-1}$. The transformation
3448 $(a,b)\mapsto(a+b\tan\phi,b-a\tan\phi)$ replaces $(a,b)$ by
3449 coordinates whose angle has decreased by~$\phi$; in the special case
3450 $a=x$, $b=2^{-k}y$, and $\tan\phi=2^{-k-1}$, this operation reduces
3451 to the particularly simple iteration shown here. [Cf.~John E. Meggitt,
3452 @^Meggitt, John E.@>
3453 {\sl IBM Journal of Research and Development\/ \bf6} (1962), 210--226.]
3455 The initial value of |x| will be multiplied by at most
3456 $(1+{1\over2})(1+{1\over8})(1+{1\over32})\cdots\approx 1.7584$; hence
3457 there is no chance of integer overflow.
3459 @<Increase |z|...@>=
3464 z=z+spec_atan[k]; t=x; x=x+(y / two_to_the(k+k)); y=y-t;
3469 if ( y>x ) { z=z+spec_atan[k]; y=y-x; };
3472 @ Conversely, the |n_sin_cos| routine takes an |angle| and produces the sine
3473 and cosine of that angle. The results of this routine are
3474 stored in global integer variables |n_sin| and |n_cos|.
3477 fraction n_sin;fraction n_cos; /* results computed by |n_sin_cos| */
3479 @ Given an integer |z| that is $2^{20}$ times an angle $\theta$ in degrees,
3480 the purpose of |n_sin_cos(z)| is to set
3481 |x=@t$r\cos\theta$@>| and |y=@t$r\sin\theta$@>| (approximately),
3482 for some rather large number~|r|. The maximum of |x| and |y|
3483 will be between $2^{28}$ and $2^{30}$, so that there will be hardly
3484 any loss of accuracy. Then |x| and~|y| are divided by~|r|.
3487 void mp_n_sin_cos (MP mp,angle z) { /* computes a multiple of the sine
3489 small_number k; /* loop control variable */
3490 int q; /* specifies the quadrant */
3491 fraction r; /* magnitude of |(x,y)| */
3492 integer x,y,t; /* temporary registers */
3493 while ( z<0 ) z=z+three_sixty_deg;
3494 z=z % three_sixty_deg; /* now |0<=z<three_sixty_deg| */
3495 q=z / forty_five_deg; z=z % forty_five_deg;
3496 x=fraction_one; y=x;
3497 if ( ! odd(q) ) z=forty_five_deg-z;
3498 @<Subtract angle |z| from |(x,y)|@>;
3499 @<Convert |(x,y)| to the octant determined by~|q|@>;
3500 r=mp_pyth_add(mp, x,y);
3501 mp->n_cos=mp_make_fraction(mp, x,r);
3502 mp->n_sin=mp_make_fraction(mp, y,r);
3505 @ In this case the octants are numbered sequentially.
3507 @<Convert |(x,...@>=
3510 case 1: t=x; x=y; y=t; break;
3511 case 2: t=x; x=-y; y=t; break;
3512 case 3: negate(x); break;
3513 case 4: negate(x); negate(y); break;
3514 case 5: t=x; x=-y; y=-t; break;
3515 case 6: t=x; x=y; y=-t; break;
3516 case 7: negate(y); break;
3517 } /* there are no other cases */
3519 @ The main iteration of |n_sin_cos| is similar to that of |n_arg| but
3520 applied in reverse. The values of |spec_atan[k]| decrease slowly enough
3521 that this loop is guaranteed to terminate before the (nonexistent) value
3522 |spec_atan[27]| would be required.
3524 @<Subtract angle |z|...@>=
3527 if ( z>=spec_atan[k] ) {
3528 z=z-spec_atan[k]; t=x;
3529 x=t+y / two_to_the(k);
3530 y=y-t / two_to_the(k);
3534 if ( y<0 ) y=0 /* this precaution may never be needed */
3536 @ And now let's complete our collection of numeric utility routines
3537 by considering random number generation.
3538 \MP\ generates pseudo-random numbers with the additive scheme recommended
3539 in Section 3.6 of {\sl The Art of Computer Programming}; however, the
3540 results are random fractions between 0 and |fraction_one-1|, inclusive.
3542 There's an auxiliary array |randoms| that contains 55 pseudo-random
3543 fractions. Using the recurrence $x_n=(x_{n-55}-x_{n-31})\bmod 2^{28}$,
3544 we generate batches of 55 new $x_n$'s at a time by calling |new_randoms|.
3545 The global variable |j_random| tells which element has most recently
3547 The global variable |sys_random_seed| was introduced in version 0.9,
3548 for the sole reason of stressing the fact that the initial value of the
3549 random seed is system-dependant. The pascal code below will initialize
3550 this variable to |(internal[time] div unity)+internal[day]|, but this is
3551 not good enough on modern fast machines that are capable of running
3552 multiple MetaPost processes within the same second.
3553 @^system dependencies@>
3556 fraction randoms[55]; /* the last 55 random values generated */
3557 int j_random; /* the number of unused |randoms| */
3558 scaled sys_random_seed; /* the default random seed */
3561 typedef scaled (*get_random_seed_command)(MP mp);
3564 get_random_seed_command get_random_seed;
3566 @ @<Option variables@>=
3567 get_random_seed_command get_random_seed;
3569 @ @<Allocate or initialize ...@>=
3570 set_callback_option(get_random_seed);
3572 @ @<Exported function headers@>=
3573 scaled mp_get_random_seed (MP mp);
3576 scaled mp_get_random_seed (MP mp) {
3577 return (mp->internal[mp_time] / unity)+mp->internal[day];
3580 @ To consume a random fraction, the program below will say `|next_random|'
3581 and then it will fetch |randoms[j_random]|.
3583 @d next_random { if ( mp->j_random==0 ) mp_new_randoms(mp);
3584 else decr(mp->j_random); }
3587 void mp_new_randoms (MP mp) {
3588 int k; /* index into |randoms| */
3589 fraction x; /* accumulator */
3590 for (k=0;k<=23;k++) {
3591 x=mp->randoms[k]-mp->randoms[k+31];
3592 if ( x<0 ) x=x+fraction_one;
3595 for (k=24;k<= 54;k++){
3596 x=mp->randoms[k]-mp->randoms[k-24];
3597 if ( x<0 ) x=x+fraction_one;
3604 void mp_init_randoms (MP mp,scaled seed);
3606 @ To initialize the |randoms| table, we call the following routine.
3609 void mp_init_randoms (MP mp,scaled seed) {
3610 fraction j,jj,k; /* more or less random integers */
3611 int i; /* index into |randoms| */
3613 while ( j>=fraction_one ) j=halfp(j);
3615 for (i=0;i<=54;i++ ){
3617 if ( k<0 ) k=k+fraction_one;
3618 mp->randoms[(i*21)% 55]=j;
3622 mp_new_randoms(mp); /* ``warm up'' the array */
3625 @ To produce a uniform random number in the range |0<=u<x| or |0>=u>x|
3626 or |0=u=x|, given a |scaled| value~|x|, we proceed as shown here.
3628 Note that the call of |take_fraction| will produce the values 0 and~|x|
3629 with about half the probability that it will produce any other particular
3630 values between 0 and~|x|, because it rounds its answers.
3633 scaled mp_unif_rand (MP mp,scaled x) {
3634 scaled y; /* trial value */
3635 next_random; y=mp_take_fraction(mp, abs(x),mp->randoms[mp->j_random]);
3636 if ( y==abs(x) ) return 0;
3637 else if ( x>0 ) return y;
3641 @ Finally, a normal deviate with mean zero and unit standard deviation
3642 can readily be obtained with the ratio method (Algorithm 3.4.1R in
3643 {\sl The Art of Computer Programming\/}).
3646 scaled mp_norm_rand (MP mp) {
3647 integer x,u,l; /* what the book would call $2^{16}X$, $2^{28}U$, and $-2^{24}\ln U$ */
3651 x=mp_take_fraction(mp, 112429,mp->randoms[mp->j_random]-fraction_half);
3652 /* $2^{16}\sqrt{8/e}\approx 112428.82793$ */
3653 next_random; u=mp->randoms[mp->j_random];
3654 } while (abs(x)>=u);
3655 x=mp_make_fraction(mp, x,u);
3656 l=139548960-mp_m_log(mp, u); /* $2^{24}\cdot12\ln2\approx139548959.6165$ */
3657 } while (mp_ab_vs_cd(mp, 1024,l,x,x)<0);
3661 @* \[9] Packed data.
3662 In order to make efficient use of storage space, \MP\ bases its major data
3663 structures on a |memory_word|, which contains either a (signed) integer,
3664 possibly scaled, or a small number of fields that are one half or one
3665 quarter of the size used for storing integers.
3667 If |x| is a variable of type |memory_word|, it contains up to four
3668 fields that can be referred to as follows:
3669 $$\vbox{\halign{\hfil#&#\hfil&#\hfil\cr
3670 |x|&.|int|&(an |integer|)\cr
3671 |x|&.|sc|\qquad&(a |scaled| integer)\cr
3672 |x.hh.lh|, |x.hh|&.|rh|&(two halfword fields)\cr
3673 |x.hh.b0|, |x.hh.b1|, |x.hh|&.|rh|&(two quarterword fields, one halfword
3675 |x.qqqq.b0|, |x.qqqq.b1|, |x.qqqq|&.|b2|, |x.qqqq.b3|\hskip-100pt
3676 &\qquad\qquad\qquad(four quarterword fields)\cr}}$$
3677 This is somewhat cumbersome to write, and not very readable either, but
3678 macros will be used to make the notation shorter and more transparent.
3679 The code below gives a formal definition of |memory_word| and
3680 its subsidiary types, using packed variant records. \MP\ makes no
3681 assumptions about the relative positions of the fields within a word.
3683 @d max_quarterword 0x3FFF /* largest allowable value in a |quarterword| */
3684 @d max_halfword 0xFFFFFFF /* largest allowable value in a |halfword| */
3686 @ Here are the inequalities that the quarterword and halfword values
3687 must satisfy (or rather, the inequalities that they mustn't satisfy):
3689 @<Check the ``constant''...@>=
3690 if (mp->ini_version) {
3691 if ( mp->mem_max!=mp->mem_top ) mp->bad=8;
3693 if ( mp->mem_max<mp->mem_top ) mp->bad=8;
3695 if ( max_quarterword<255 ) mp->bad=9;
3696 if ( max_halfword<65535 ) mp->bad=10;
3697 if ( max_quarterword>max_halfword ) mp->bad=11;
3698 if ( mp->mem_max>=max_halfword ) mp->bad=12;
3699 if ( mp->max_strings>max_halfword ) mp->bad=13;
3701 @ The macros |qi| and |qo| are used for input to and output
3702 from quarterwords. These are legacy macros.
3703 @^system dependencies@>
3705 @d qo(A) (A) /* to read eight bits from a quarterword */
3706 @d qi(A) (A) /* to store eight bits in a quarterword */
3708 @ The reader should study the following definitions closely:
3709 @^system dependencies@>
3711 @d sc cint /* |scaled| data is equivalent to |integer| */
3714 typedef short quarterword; /* 1/4 of a word */
3715 typedef int halfword; /* 1/2 of a word */
3720 struct { /* Make B0,B1 overlap the most significant bytes of LH. */
3727 quarterword B2, B3, B0, B1;
3742 @ When debugging, we may want to print a |memory_word| without knowing
3743 what type it is; so we print it in all modes.
3744 @^dirty \PASCAL@>@^debugging@>
3747 void mp_print_word (MP mp,memory_word w) {
3748 /* prints |w| in all ways */
3749 mp_print_int(mp, w.cint); mp_print_char(mp, ' ');
3750 mp_print_scaled(mp, w.sc); mp_print_char(mp, ' ');
3751 mp_print_scaled(mp, w.sc / 010000); mp_print_ln(mp);
3752 mp_print_int(mp, w.hh.lh); mp_print_char(mp, '=');
3753 mp_print_int(mp, w.hh.b0); mp_print_char(mp, ':');
3754 mp_print_int(mp, w.hh.b1); mp_print_char(mp, ';');
3755 mp_print_int(mp, w.hh.rh); mp_print_char(mp, ' ');
3756 mp_print_int(mp, w.qqqq.b0); mp_print_char(mp, ':');
3757 mp_print_int(mp, w.qqqq.b1); mp_print_char(mp, ':');
3758 mp_print_int(mp, w.qqqq.b2); mp_print_char(mp, ':');
3759 mp_print_int(mp, w.qqqq.b3);
3763 @* \[10] Dynamic memory allocation.
3765 The \MP\ system does nearly all of its own memory allocation, so that it
3766 can readily be transported into environments that do not have automatic
3767 facilities for strings, garbage collection, etc., and so that it can be in
3768 control of what error messages the user receives. The dynamic storage
3769 requirements of \MP\ are handled by providing a large array |mem| in
3770 which consecutive blocks of words are used as nodes by the \MP\ routines.
3772 Pointer variables are indices into this array, or into another array
3773 called |eqtb| that will be explained later. A pointer variable might
3774 also be a special flag that lies outside the bounds of |mem|, so we
3775 allow pointers to assume any |halfword| value. The minimum memory
3776 index represents a null pointer.
3778 @d null 0 /* the null pointer */
3781 typedef halfword pointer; /* a flag or a location in |mem| or |eqtb| */
3783 @ The |mem| array is divided into two regions that are allocated separately,
3784 but the dividing line between these two regions is not fixed; they grow
3785 together until finding their ``natural'' size in a particular job.
3786 Locations less than or equal to |lo_mem_max| are used for storing
3787 variable-length records consisting of two or more words each. This region
3788 is maintained using an algorithm similar to the one described in exercise
3789 2.5--19 of {\sl The Art of Computer Programming}. However, no size field
3790 appears in the allocated nodes; the program is responsible for knowing the
3791 relevant size when a node is freed. Locations greater than or equal to
3792 |hi_mem_min| are used for storing one-word records; a conventional
3793 \.{AVAIL} stack is used for allocation in this region.
3795 Locations of |mem| between |0| and |mem_top| may be dumped as part
3796 of preloaded format files, by the \.{INIMP} preprocessor.
3798 Production versions of \MP\ may extend the memory at the top end in order to
3799 provide more space; these locations, between |mem_top| and |mem_max|,
3800 are always used for single-word nodes.
3802 The key pointers that govern |mem| allocation have a prescribed order:
3803 $$\hbox{|null=0<lo_mem_max<hi_mem_min<mem_top<=mem_end<=mem_max|.}$$
3806 memory_word *mem; /* the big dynamic storage area */
3807 pointer lo_mem_max; /* the largest location of variable-size memory in use */
3808 pointer hi_mem_min; /* the smallest location of one-word memory in use */
3812 @d xfree(A) do { mp_xfree(A); A=NULL; } while (0)
3813 @d xrealloc mp_xrealloc
3814 @d xmalloc mp_xmalloc
3815 @d xstrdup mp_xstrdup
3816 @d XREALLOC(a,b,c) a = xrealloc(a,(b+1),sizeof(c));
3818 @<Declare helpers@>=
3819 void mp_xfree (void *x);
3820 void *mp_xrealloc (void *p, size_t nmem, size_t size) ;
3821 void *mp_xmalloc (size_t nmem, size_t size) ;
3822 char *mp_xstrdup(const char *s);
3824 @ The |max_size_test| guards against overflow, on the assumption that
3825 |size_t| is at least 31bits wide.
3827 @d max_size_test 0x7FFFFFFF
3830 void mp_xfree (void *x) {
3831 if (x!=NULL) free(x);
3833 void *mp_xrealloc (void *p, size_t nmem, size_t size) {
3835 if ((max_size_test/size)<nmem) {
3836 fprintf(stderr,"Memory size overflow!\n");
3839 w = realloc (p,(nmem*size));
3841 fprintf(stderr,"Out of memory!\n");
3846 void *mp_xmalloc (size_t nmem, size_t size) {
3848 if ((max_size_test/size)<nmem) {
3849 fprintf(stderr,"Memory size overflow!\n");
3852 w = malloc (nmem*size);
3854 fprintf(stderr,"Out of memory!\n");
3859 char *mp_xstrdup(const char *s) {
3865 fprintf(stderr,"Out of memory!\n");
3873 @<Allocate or initialize ...@>=
3874 mp->mem = xmalloc ((mp->mem_max+1),sizeof (memory_word));
3875 memset(mp->mem,0,(mp->mem_max+1)*sizeof (memory_word));
3877 @ @<Dealloc variables@>=
3880 @ Users who wish to study the memory requirements of particular applications can
3881 can use optional special features that keep track of current and
3882 maximum memory usage. When code between the delimiters |stat| $\ldots$
3883 |tats| is not ``commented out,'' \MP\ will run a bit slower but it will
3884 report these statistics when |tracing_stats| is positive.
3887 integer var_used; integer dyn_used; /* how much memory is in use */
3889 @ Let's consider the one-word memory region first, since it's the
3890 simplest. The pointer variable |mem_end| holds the highest-numbered location
3891 of |mem| that has ever been used. The free locations of |mem| that
3892 occur between |hi_mem_min| and |mem_end|, inclusive, are of type
3893 |two_halves|, and we write |info(p)| and |link(p)| for the |lh|
3894 and |rh| fields of |mem[p]| when it is of this type. The single-word
3895 free locations form a linked list
3896 $$|avail|,\;\hbox{|link(avail)|},\;\hbox{|link(link(avail))|},\;\ldots$$
3897 terminated by |null|.
3899 @d link(A) mp->mem[(A)].hh.rh /* the |link| field of a memory word */
3900 @d info(A) mp->mem[(A)].hh.lh /* the |info| field of a memory word */
3903 pointer avail; /* head of the list of available one-word nodes */
3904 pointer mem_end; /* the last one-word node used in |mem| */
3906 @ If one-word memory is exhausted, it might mean that the user has forgotten
3907 a token like `\&{enddef}' or `\&{endfor}'. We will define some procedures
3908 later that try to help pinpoint the trouble.
3911 @<Declare the procedure called |show_token_list|@>;
3912 @<Declare the procedure called |runaway|@>
3914 @ The function |get_avail| returns a pointer to a new one-word node whose
3915 |link| field is null. However, \MP\ will halt if there is no more room left.
3919 pointer mp_get_avail (MP mp) { /* single-word node allocation */
3920 pointer p; /* the new node being got */
3921 p=mp->avail; /* get top location in the |avail| stack */
3923 mp->avail=link(mp->avail); /* and pop it off */
3924 } else if ( mp->mem_end<mp->mem_max ) { /* or go into virgin territory */
3925 incr(mp->mem_end); p=mp->mem_end;
3927 decr(mp->hi_mem_min); p=mp->hi_mem_min;
3928 if ( mp->hi_mem_min<=mp->lo_mem_max ) {
3929 mp_runaway(mp); /* if memory is exhausted, display possible runaway text */
3930 mp_overflow(mp, "main memory size",mp->mem_max);
3931 /* quit; all one-word nodes are busy */
3932 @:MetaPost capacity exceeded main memory size}{\quad main memory size@>
3935 link(p)=null; /* provide an oft-desired initialization of the new node */
3936 incr(mp->dyn_used);/* maintain statistics */
3940 @ Conversely, a one-word node is recycled by calling |free_avail|.
3942 @d free_avail(A) /* single-word node liberation */
3943 { link((A))=mp->avail; mp->avail=(A); decr(mp->dyn_used); }
3945 @ There's also a |fast_get_avail| routine, which saves the procedure-call
3946 overhead at the expense of extra programming. This macro is used in
3947 the places that would otherwise account for the most calls of |get_avail|.
3950 @d fast_get_avail(A) {
3951 (A)=mp->avail; /* avoid |get_avail| if possible, to save time */
3952 if ( (A)==null ) { (A)=mp_get_avail(mp); }
3953 else { mp->avail=link((A)); link((A))=null; incr(mp->dyn_used); }
3956 @ The available-space list that keeps track of the variable-size portion
3957 of |mem| is a nonempty, doubly-linked circular list of empty nodes,
3958 pointed to by the roving pointer |rover|.
3960 Each empty node has size 2 or more; the first word contains the special
3961 value |max_halfword| in its |link| field and the size in its |info| field;
3962 the second word contains the two pointers for double linking.
3964 Each nonempty node also has size 2 or more. Its first word is of type
3965 |two_halves|\kern-1pt, and its |link| field is never equal to |max_halfword|.
3966 Otherwise there is complete flexibility with respect to the contents
3967 of its other fields and its other words.
3969 (We require |mem_max<max_halfword| because terrible things can happen
3970 when |max_halfword| appears in the |link| field of a nonempty node.)
3972 @d empty_flag max_halfword /* the |link| of an empty variable-size node */
3973 @d is_empty(A) (link((A))==empty_flag) /* tests for empty node */
3974 @d node_size info /* the size field in empty variable-size nodes */
3975 @d llink(A) info((A)+1) /* left link in doubly-linked list of empty nodes */
3976 @d rlink(A) link((A)+1) /* right link in doubly-linked list of empty nodes */
3979 pointer rover; /* points to some node in the list of empties */
3981 @ A call to |get_node| with argument |s| returns a pointer to a new node
3982 of size~|s|, which must be 2~or more. The |link| field of the first word
3983 of this new node is set to null. An overflow stop occurs if no suitable
3986 If |get_node| is called with $s=2^{30}$, it simply merges adjacent free
3987 areas and returns the value |max_halfword|.
3990 pointer mp_get_node (MP mp,integer s) ;
3993 pointer mp_get_node (MP mp,integer s) { /* variable-size node allocation */
3994 pointer p; /* the node currently under inspection */
3995 pointer q; /* the node physically after node |p| */
3996 integer r; /* the newly allocated node, or a candidate for this honor */
3997 integer t,tt; /* temporary registers */
4000 p=mp->rover; /* start at some free node in the ring */
4002 @<Try to allocate within node |p| and its physical successors,
4003 and |goto found| if allocation was possible@>;
4004 p=rlink(p); /* move to the next node in the ring */
4005 } while (p!=mp->rover); /* repeat until the whole list has been traversed */
4006 if ( s==010000000000 ) {
4007 return max_halfword;
4009 if ( mp->lo_mem_max+2<mp->hi_mem_min ) {
4010 if ( mp->lo_mem_max+2<=max_halfword ) {
4011 @<Grow more variable-size memory and |goto restart|@>;
4014 mp_overflow(mp, "main memory size",mp->mem_max);
4015 /* sorry, nothing satisfactory is left */
4016 @:MetaPost capacity exceeded main memory size}{\quad main memory size@>
4018 link(r)=null; /* this node is now nonempty */
4019 mp->var_used=mp->var_used+s; /* maintain usage statistics */
4023 @ The lower part of |mem| grows by 1000 words at a time, unless
4024 we are very close to going under. When it grows, we simply link
4025 a new node into the available-space list. This method of controlled
4026 growth helps to keep the |mem| usage consecutive when \MP\ is
4027 implemented on ``virtual memory'' systems.
4030 @<Grow more variable-size memory and |goto restart|@>=
4032 if ( mp->hi_mem_min-mp->lo_mem_max>=1998 ) {
4033 t=mp->lo_mem_max+1000;
4035 t=mp->lo_mem_max+1+(mp->hi_mem_min-mp->lo_mem_max) / 2;
4036 /* |lo_mem_max+2<=t<hi_mem_min| */
4038 if ( t>max_halfword ) t=max_halfword;
4039 p=llink(mp->rover); q=mp->lo_mem_max; rlink(p)=q; llink(mp->rover)=q;
4040 rlink(q)=mp->rover; llink(q)=p; link(q)=empty_flag; node_size(q)=t-mp->lo_mem_max;
4041 mp->lo_mem_max=t; link(mp->lo_mem_max)=null; info(mp->lo_mem_max)=null;
4046 @ @<Try to allocate...@>=
4047 q=p+node_size(p); /* find the physical successor */
4048 while ( is_empty(q) ) { /* merge node |p| with node |q| */
4049 t=rlink(q); tt=llink(q);
4051 if ( q==mp->rover ) mp->rover=t;
4052 llink(t)=tt; rlink(tt)=t;
4057 @<Allocate from the top of node |p| and |goto found|@>;
4060 if ( rlink(p)!=p ) {
4061 @<Allocate entire node |p| and |goto found|@>;
4064 node_size(p)=q-p /* reset the size in case it grew */
4066 @ @<Allocate from the top...@>=
4068 node_size(p)=r-p; /* store the remaining size */
4069 mp->rover=p; /* start searching here next time */
4073 @ Here we delete node |p| from the ring, and let |rover| rove around.
4075 @<Allocate entire...@>=
4077 mp->rover=rlink(p); t=llink(p);
4078 llink(mp->rover)=t; rlink(t)=mp->rover;
4082 @ Conversely, when some variable-size node |p| of size |s| is no longer needed,
4083 the operation |free_node(p,s)| will make its words available, by inserting
4084 |p| as a new empty node just before where |rover| now points.
4087 void mp_free_node (MP mp, pointer p, halfword s) ;
4090 void mp_free_node (MP mp, pointer p, halfword s) { /* variable-size node
4092 pointer q; /* |llink(rover)| */
4093 node_size(p)=s; link(p)=empty_flag;
4095 q=llink(mp->rover); llink(p)=q; rlink(p)=mp->rover; /* set both links */
4096 llink(mp->rover)=p; rlink(q)=p; /* insert |p| into the ring */
4097 mp->var_used=mp->var_used-s; /* maintain statistics */
4100 @ Just before \.{INIMP} writes out the memory, it sorts the doubly linked
4101 available space list. The list is probably very short at such times, so a
4102 simple insertion sort is used. The smallest available location will be
4103 pointed to by |rover|, the next-smallest by |rlink(rover)|, etc.
4106 void mp_sort_avail (MP mp) { /* sorts the available variable-size nodes
4108 pointer p,q,r; /* indices into |mem| */
4109 pointer old_rover; /* initial |rover| setting */
4110 p=mp_get_node(mp, 010000000000); /* merge adjacent free areas */
4111 p=rlink(mp->rover); rlink(mp->rover)=max_halfword; old_rover=mp->rover;
4112 while ( p!=old_rover ) {
4113 @<Sort |p| into the list starting at |rover|
4114 and advance |p| to |rlink(p)|@>;
4117 while ( rlink(p)!=max_halfword ) {
4118 llink(rlink(p))=p; p=rlink(p);
4120 rlink(p)=mp->rover; llink(mp->rover)=p;
4123 @ The following |while| loop is guaranteed to
4124 terminate, since the list that starts at
4125 |rover| ends with |max_halfword| during the sorting procedure.
4128 if ( p<mp->rover ) {
4129 q=p; p=rlink(q); rlink(q)=mp->rover; mp->rover=q;
4132 while ( rlink(q)<p ) q=rlink(q);
4133 r=rlink(p); rlink(p)=rlink(q); rlink(q)=p; p=r;
4136 @* \[11] Memory layout.
4137 Some areas of |mem| are dedicated to fixed usage, since static allocation is
4138 more efficient than dynamic allocation when we can get away with it. For
4139 example, locations |0| to |1| are always used to store a
4140 two-word dummy token whose second word is zero.
4141 The following macro definitions accomplish the static allocation by giving
4142 symbolic names to the fixed positions. Static variable-size nodes appear
4143 in locations |0| through |lo_mem_stat_max|, and static single-word nodes
4144 appear in locations |hi_mem_stat_min| through |mem_top|, inclusive.
4146 @d null_dash (2) /* the first two words are reserved for a null value */
4147 @d dep_head (null_dash+3) /* we will define |dash_node_size=3| */
4148 @d zero_val (dep_head+2) /* two words for a permanently zero value */
4149 @d temp_val (zero_val+2) /* two words for a temporary value node */
4150 @d end_attr temp_val /* we use |end_attr+2| only */
4151 @d inf_val (end_attr+2) /* and |inf_val+1| only */
4152 @d test_pen (inf_val+2)
4153 /* nine words for a pen used when testing the turning number */
4154 @d bad_vardef (test_pen+9) /* two words for \&{vardef} error recovery */
4155 @d lo_mem_stat_max (bad_vardef+1) /* largest statically
4156 allocated word in the variable-size |mem| */
4158 @d sentinel mp->mem_top /* end of sorted lists */
4159 @d temp_head (mp->mem_top-1) /* head of a temporary list of some kind */
4160 @d hold_head (mp->mem_top-2) /* head of a temporary list of another kind */
4161 @d spec_head (mp->mem_top-3) /* head of a list of unprocessed \&{special} items */
4162 @d hi_mem_stat_min (mp->mem_top-3) /* smallest statically allocated word in
4163 the one-word |mem| */
4165 @ The following code gets the dynamic part of |mem| off to a good start,
4166 when \MP\ is initializing itself the slow way.
4168 @<Initialize table entries (done by \.{INIMP} only)@>=
4169 @^data structure assumptions@>
4170 mp->rover=lo_mem_stat_max+1; /* initialize the dynamic memory */
4171 link(mp->rover)=empty_flag;
4172 node_size(mp->rover)=1000; /* which is a 1000-word available node */
4173 llink(mp->rover)=mp->rover; rlink(mp->rover)=mp->rover;
4174 mp->lo_mem_max=mp->rover+1000; link(mp->lo_mem_max)=null; info(mp->lo_mem_max)=null;
4175 for (k=hi_mem_stat_min;k<=(int)mp->mem_top;k++) {
4176 mp->mem[k]=mp->mem[mp->lo_mem_max]; /* clear list heads */
4178 mp->avail=null; mp->mem_end=mp->mem_top;
4179 mp->hi_mem_min=hi_mem_stat_min; /* initialize the one-word memory */
4180 mp->var_used=lo_mem_stat_max+1;
4181 mp->dyn_used=mp->mem_top+1-(hi_mem_stat_min); /* initialize statistics */
4182 @<Initialize a pen at |test_pen| so that it fits in nine words@>;
4184 @ The procedure |flush_list(p)| frees an entire linked list of one-word
4185 nodes that starts at a given position, until coming to |sentinel| or a
4186 pointer that is not in the one-word region. Another procedure,
4187 |flush_node_list|, frees an entire linked list of one-word and two-word
4188 nodes, until coming to a |null| pointer.
4192 void mp_flush_list (MP mp,pointer p) { /* makes list of single-word nodes available */
4193 pointer q,r; /* list traversers */
4194 if ( p>=mp->hi_mem_min ) if ( p!=sentinel ) {
4199 if ( r<mp->hi_mem_min ) break;
4200 } while (r!=sentinel);
4201 /* now |q| is the last node on the list */
4202 link(q)=mp->avail; mp->avail=p;
4206 void mp_flush_node_list (MP mp,pointer p) {
4207 pointer q; /* the node being recycled */
4210 if ( q<mp->hi_mem_min )
4211 mp_free_node(mp, q,2);
4217 @ If \MP\ is extended improperly, the |mem| array might get screwed up.
4218 For example, some pointers might be wrong, or some ``dead'' nodes might not
4219 have been freed when the last reference to them disappeared. Procedures
4220 |check_mem| and |search_mem| are available to help diagnose such
4221 problems. These procedures make use of two arrays called |free| and
4222 |was_free| that are present only if \MP's debugging routines have
4223 been included. (You may want to decrease the size of |mem| while you
4227 Because |boolean|s are typedef-d as ints, it is better to use
4228 unsigned chars here.
4231 unsigned char *free; /* free cells */
4232 unsigned char *was_free; /* previously free cells */
4233 pointer was_mem_end; pointer was_lo_max; pointer was_hi_min;
4234 /* previous |mem_end|, |lo_mem_max|,and |hi_mem_min| */
4235 boolean panicking; /* do we want to check memory constantly? */
4237 @ @<Allocate or initialize ...@>=
4238 mp->free = xmalloc ((mp->mem_max+1),sizeof (unsigned char));
4239 mp->was_free = xmalloc ((mp->mem_max+1), sizeof (unsigned char));
4241 @ @<Dealloc variables@>=
4243 xfree(mp->was_free);
4245 @ @<Allocate or ...@>=
4246 mp->was_mem_end=0; /* indicate that everything was previously free */
4247 mp->was_lo_max=0; mp->was_hi_min=mp->mem_max;
4248 mp->panicking=false;
4250 @ @<Declare |mp_reallocate| functions@>=
4251 void mp_reallocate_memory(MP mp, int l) ;
4254 void mp_reallocate_memory(MP mp, int l) {
4255 XREALLOC(mp->free, l, unsigned char);
4256 XREALLOC(mp->was_free, l, unsigned char);
4258 int newarea = l-mp->mem_max;
4259 XREALLOC(mp->mem, l, memory_word);
4260 memset (mp->mem+(mp->mem_max+1),0,sizeof(memory_word)*(newarea));
4262 XREALLOC(mp->mem, l, memory_word);
4263 memset(mp->mem,0,sizeof(memory_word)*(l+1));
4266 if (mp->ini_version)
4272 @ Procedure |check_mem| makes sure that the available space lists of
4273 |mem| are well formed, and it optionally prints out all locations
4274 that are reserved now but were free the last time this procedure was called.
4277 void mp_check_mem (MP mp,boolean print_locs ) {
4278 pointer p,q,r; /* current locations of interest in |mem| */
4279 boolean clobbered; /* is something amiss? */
4280 for (p=0;p<=mp->lo_mem_max;p++) {
4281 mp->free[p]=false; /* you can probably do this faster */
4283 for (p=mp->hi_mem_min;p<= mp->mem_end;p++) {
4284 mp->free[p]=false; /* ditto */
4286 @<Check single-word |avail| list@>;
4287 @<Check variable-size |avail| list@>;
4288 @<Check flags of unavailable nodes@>;
4289 @<Check the list of linear dependencies@>;
4291 @<Print newly busy locations@>;
4293 memcpy(mp->was_free,mp->free, sizeof(char)*(mp->mem_end+1));
4294 mp->was_mem_end=mp->mem_end;
4295 mp->was_lo_max=mp->lo_mem_max;
4296 mp->was_hi_min=mp->hi_mem_min;
4299 @ @<Check single-word...@>=
4300 p=mp->avail; q=null; clobbered=false;
4302 if ( (p>mp->mem_end)||(p<mp->hi_mem_min) ) clobbered=true;
4303 else if ( mp->free[p] ) clobbered=true;
4305 mp_print_nl(mp, "AVAIL list clobbered at ");
4306 @.AVAIL list clobbered...@>
4307 mp_print_int(mp, q); break;
4309 mp->free[p]=true; q=p; p=link(q);
4312 @ @<Check variable-size...@>=
4313 p=mp->rover; q=null; clobbered=false;
4315 if ( (p>=mp->lo_mem_max)||(p<0) ) clobbered=true;
4316 else if ( (rlink(p)>=mp->lo_mem_max)||(rlink(p)<0) ) clobbered=true;
4317 else if ( !(is_empty(p))||(node_size(p)<2)||
4318 (p+node_size(p)>mp->lo_mem_max)|| (llink(rlink(p))!=p) ) clobbered=true;
4320 mp_print_nl(mp, "Double-AVAIL list clobbered at ");
4321 @.Double-AVAIL list clobbered...@>
4322 mp_print_int(mp, q); break;
4324 for (q=p;q<=p+node_size(p)-1;q++) { /* mark all locations free */
4325 if ( mp->free[q] ) {
4326 mp_print_nl(mp, "Doubly free location at ");
4327 @.Doubly free location...@>
4328 mp_print_int(mp, q); break;
4333 } while (p!=mp->rover)
4336 @ @<Check flags...@>=
4338 while ( p<=mp->lo_mem_max ) { /* node |p| should not be empty */
4339 if ( is_empty(p) ) {
4340 mp_print_nl(mp, "Bad flag at "); mp_print_int(mp, p);
4343 while ( (p<=mp->lo_mem_max) && ! mp->free[p] ) incr(p);
4344 while ( (p<=mp->lo_mem_max) && mp->free[p] ) incr(p);
4347 @ @<Print newly busy...@>=
4349 @<Do intialization required before printing new busy locations@>;
4350 mp_print_nl(mp, "New busy locs:");
4352 for (p=0;p<= mp->lo_mem_max;p++ ) {
4353 if ( ! mp->free[p] && ((p>mp->was_lo_max) || mp->was_free[p]) ) {
4354 @<Indicate that |p| is a new busy location@>;
4357 for (p=mp->hi_mem_min;p<=mp->mem_end;p++ ) {
4358 if ( ! mp->free[p] &&
4359 ((p<mp->was_hi_min) || (p>mp->was_mem_end) || mp->was_free[p]) ) {
4360 @<Indicate that |p| is a new busy location@>;
4363 @<Finish printing new busy locations@>;
4366 @ There might be many new busy locations so we are careful to print contiguous
4367 blocks compactly. During this operation |q| is the last new busy location and
4368 |r| is the start of the block containing |q|.
4370 @<Indicate that |p| is a new busy location@>=
4374 mp_print(mp, ".."); mp_print_int(mp, q);
4376 mp_print_char(mp, ' '); mp_print_int(mp, p);
4382 @ @<Do intialization required before printing new busy locations@>=
4383 q=mp->mem_max; r=mp->mem_max
4385 @ @<Finish printing new busy locations@>=
4387 mp_print(mp, ".."); mp_print_int(mp, q);
4390 @ The |search_mem| procedure attempts to answer the question ``Who points
4391 to node~|p|?'' In doing so, it fetches |link| and |info| fields of |mem|
4392 that might not be of type |two_halves|. Strictly speaking, this is
4394 undefined in \PASCAL, and it can lead to ``false drops'' (words that seem to
4395 point to |p| purely by coincidence). But for debugging purposes, we want
4396 to rule out the places that do {\sl not\/} point to |p|, so a few false
4397 drops are tolerable.
4400 void mp_search_mem (MP mp, pointer p) { /* look for pointers to |p| */
4401 integer q; /* current position being searched */
4402 for (q=0;q<=mp->lo_mem_max;q++) {
4404 mp_print_nl(mp, "LINK("); mp_print_int(mp, q); mp_print_char(mp, ')');
4407 mp_print_nl(mp, "INFO("); mp_print_int(mp, q); mp_print_char(mp, ')');
4410 for (q=mp->hi_mem_min;q<=mp->mem_end;q++) {
4412 mp_print_nl(mp, "LINK("); mp_print_int(mp, q); mp_print_char(mp, ')');
4415 mp_print_nl(mp, "INFO("); mp_print_int(mp, q); mp_print_char(mp, ')');
4418 @<Search |eqtb| for equivalents equal to |p|@>;
4421 @* \[12] The command codes.
4422 Before we can go much further, we need to define symbolic names for the internal
4423 code numbers that represent the various commands obeyed by \MP. These codes
4424 are somewhat arbitrary, but not completely so. For example,
4425 some codes have been made adjacent so that |case| statements in the
4426 program need not consider cases that are widely spaced, or so that |case|
4427 statements can be replaced by |if| statements. A command can begin an
4428 expression if and only if its code lies between |min_primary_command| and
4429 |max_primary_command|, inclusive. The first token of a statement that doesn't
4430 begin with an expression has a command code between |min_command| and
4431 |max_statement_command|, inclusive. Anything less than |min_command| is
4432 eliminated during macro expansions, and anything no more than |max_pre_command|
4433 is eliminated when expanding \TeX\ material. Ranges such as
4434 |min_secondary_command..max_secondary_command| are used when parsing
4435 expressions, but the relative ordering within such a range is generally not
4438 The ordering of the highest-numbered commands
4439 (|comma<semicolon<end_group<stop|) is crucial for the parsing and
4440 error-recovery methods of this program as is the ordering |if_test<fi_or_else|
4441 for the smallest two commands. The ordering is also important in the ranges
4442 |numeric_token..plus_or_minus| and |left_brace..ampersand|.
4444 At any rate, here is the list, for future reference.
4446 @d start_tex 1 /* begin \TeX\ material (\&{btex}, \&{verbatimtex}) */
4447 @d etex_marker 2 /* end \TeX\ material (\&{etex}) */
4448 @d mpx_break 3 /* stop reading an \.{MPX} file (\&{mpxbreak}) */
4449 @d max_pre_command mpx_break
4450 @d if_test 4 /* conditional text (\&{if}) */
4451 @d fi_or_else 5 /* delimiters for conditionals (\&{elseif}, \&{else}, \&{fi} */
4452 @d input 6 /* input a source file (\&{input}, \&{endinput}) */
4453 @d iteration 7 /* iterate (\&{for}, \&{forsuffixes}, \&{forever}, \&{endfor}) */
4454 @d repeat_loop 8 /* special command substituted for \&{endfor} */
4455 @d exit_test 9 /* premature exit from a loop (\&{exitif}) */
4456 @d relax 10 /* do nothing (\.{\char`\\}) */
4457 @d scan_tokens 11 /* put a string into the input buffer */
4458 @d expand_after 12 /* look ahead one token */
4459 @d defined_macro 13 /* a macro defined by the user */
4460 @d min_command (defined_macro+1)
4461 @d save_command 14 /* save a list of tokens (\&{save}) */
4462 @d interim_command 15 /* save an internal quantity (\&{interim}) */
4463 @d let_command 16 /* redefine a symbolic token (\&{let}) */
4464 @d new_internal 17 /* define a new internal quantity (\&{newinternal}) */
4465 @d macro_def 18 /* define a macro (\&{def}, \&{vardef}, etc.) */
4466 @d ship_out_command 19 /* output a character (\&{shipout}) */
4467 @d add_to_command 20 /* add to edges (\&{addto}) */
4468 @d bounds_command 21 /* add bounding path to edges (\&{setbounds}, \&{clip}) */
4469 @d tfm_command 22 /* command for font metric info (\&{ligtable}, etc.) */
4470 @d protection_command 23 /* set protection flag (\&{outer}, \&{inner}) */
4471 @d show_command 24 /* diagnostic output (\&{show}, \&{showvariable}, etc.) */
4472 @d mode_command 25 /* set interaction level (\&{batchmode}, etc.) */
4473 @d random_seed 26 /* initialize random number generator (\&{randomseed}) */
4474 @d message_command 27 /* communicate to user (\&{message}, \&{errmessage}) */
4475 @d every_job_command 28 /* designate a starting token (\&{everyjob}) */
4476 @d delimiters 29 /* define a pair of delimiters (\&{delimiters}) */
4477 @d special_command 30 /* output special info (\&{special})
4478 or font map info (\&{fontmapfile}, \&{fontmapline}) */
4479 @d write_command 31 /* write text to a file (\&{write}) */
4480 @d type_name 32 /* declare a type (\&{numeric}, \&{pair}, etc. */
4481 @d max_statement_command type_name
4482 @d min_primary_command type_name
4483 @d left_delimiter 33 /* the left delimiter of a matching pair */
4484 @d begin_group 34 /* beginning of a group (\&{begingroup}) */
4485 @d nullary 35 /* an operator without arguments (e.g., \&{normaldeviate}) */
4486 @d unary 36 /* an operator with one argument (e.g., \&{sqrt}) */
4487 @d str_op 37 /* convert a suffix to a string (\&{str}) */
4488 @d cycle 38 /* close a cyclic path (\&{cycle}) */
4489 @d primary_binary 39 /* binary operation taking `\&{of}' (e.g., \&{point}) */
4490 @d capsule_token 40 /* a value that has been put into a token list */
4491 @d string_token 41 /* a string constant (e.g., |"hello"|) */
4492 @d internal_quantity 42 /* internal numeric parameter (e.g., \&{pausing}) */
4493 @d min_suffix_token internal_quantity
4494 @d tag_token 43 /* a symbolic token without a primitive meaning */
4495 @d numeric_token 44 /* a numeric constant (e.g., \.{3.14159}) */
4496 @d max_suffix_token numeric_token
4497 @d plus_or_minus 45 /* either `\.+' or `\.-' */
4498 @d max_primary_command plus_or_minus /* should also be |numeric_token+1| */
4499 @d min_tertiary_command plus_or_minus
4500 @d tertiary_secondary_macro 46 /* a macro defined by \&{secondarydef} */
4501 @d tertiary_binary 47 /* an operator at the tertiary level (e.g., `\.{++}') */
4502 @d max_tertiary_command tertiary_binary
4503 @d left_brace 48 /* the operator `\.{\char`\{}' */
4504 @d min_expression_command left_brace
4505 @d path_join 49 /* the operator `\.{..}' */
4506 @d ampersand 50 /* the operator `\.\&' */
4507 @d expression_tertiary_macro 51 /* a macro defined by \&{tertiarydef} */
4508 @d expression_binary 52 /* an operator at the expression level (e.g., `\.<') */
4509 @d equals 53 /* the operator `\.=' */
4510 @d max_expression_command equals
4511 @d and_command 54 /* the operator `\&{and}' */
4512 @d min_secondary_command and_command
4513 @d secondary_primary_macro 55 /* a macro defined by \&{primarydef} */
4514 @d slash 56 /* the operator `\./' */
4515 @d secondary_binary 57 /* an operator at the binary level (e.g., \&{shifted}) */
4516 @d max_secondary_command secondary_binary
4517 @d param_type 58 /* type of parameter (\&{primary}, \&{expr}, \&{suffix}, etc.) */
4518 @d controls 59 /* specify control points explicitly (\&{controls}) */
4519 @d tension 60 /* specify tension between knots (\&{tension}) */
4520 @d at_least 61 /* bounded tension value (\&{atleast}) */
4521 @d curl_command 62 /* specify curl at an end knot (\&{curl}) */
4522 @d macro_special 63 /* special macro operators (\&{quote}, \.{\#\AT!}, etc.) */
4523 @d right_delimiter 64 /* the right delimiter of a matching pair */
4524 @d left_bracket 65 /* the operator `\.[' */
4525 @d right_bracket 66 /* the operator `\.]' */
4526 @d right_brace 67 /* the operator `\.{\char`\}}' */
4527 @d with_option 68 /* option for filling (\&{withpen}, \&{withweight}, etc.) */
4529 /* variant of \&{addto} (\&{contour}, \&{doublepath}, \&{also}) */
4530 @d of_token 70 /* the operator `\&{of}' */
4531 @d to_token 71 /* the operator `\&{to}' */
4532 @d step_token 72 /* the operator `\&{step}' */
4533 @d until_token 73 /* the operator `\&{until}' */
4534 @d within_token 74 /* the operator `\&{within}' */
4535 @d lig_kern_token 75
4536 /* the operators `\&{kern}' and `\.{=:}' and `\.{=:\char'174}, etc. */
4537 @d assignment 76 /* the operator `\.{:=}' */
4538 @d skip_to 77 /* the operation `\&{skipto}' */
4539 @d bchar_label 78 /* the operator `\.{\char'174\char'174:}' */
4540 @d double_colon 79 /* the operator `\.{::}' */
4541 @d colon 80 /* the operator `\.:' */
4543 @d comma 81 /* the operator `\.,', must be |colon+1| */
4544 @d end_of_statement (mp->cur_cmd>comma)
4545 @d semicolon 82 /* the operator `\.;', must be |comma+1| */
4546 @d end_group 83 /* end a group (\&{endgroup}), must be |semicolon+1| */
4547 @d stop 84 /* end a job (\&{end}, \&{dump}), must be |end_group+1| */
4548 @d max_command_code stop
4549 @d outer_tag (max_command_code+1) /* protection code added to command code */
4552 typedef int command_code;
4554 @ Variables and capsules in \MP\ have a variety of ``types,''
4555 distinguished by the code numbers defined here. These numbers are also
4556 not completely arbitrary. Things that get expanded must have types
4557 |>mp_independent|; a type remaining after expansion is numeric if and only if
4558 its code number is at least |numeric_type|; objects containing numeric
4559 parts must have types between |transform_type| and |pair_type|;
4560 all other types must be smaller than |transform_type|; and among the types
4561 that are not unknown or vacuous, the smallest two must be |boolean_type|
4562 and |string_type| in that order.
4564 @d undefined 0 /* no type has been declared */
4565 @d unknown_tag 1 /* this constant is added to certain type codes below */
4566 @d unknown_types mp_unknown_boolean: case mp_unknown_string:
4567 case mp_unknown_pen: case mp_unknown_picture: case mp_unknown_path
4571 mp_vacuous=1, /* no expression was present */
4572 mp_boolean_type, /* \&{boolean} with a known value */
4574 mp_string_type, /* \&{string} with a known value */
4576 mp_pen_type, /* \&{pen} with a known value */
4578 mp_path_type, /* \&{path} with a known value */
4580 mp_picture_type, /* \&{picture} with a known value */
4582 mp_transform_type, /* \&{transform} variable or capsule */
4583 mp_color_type, /* \&{color} variable or capsule */
4584 mp_cmykcolor_type, /* \&{cmykcolor} variable or capsule */
4585 mp_pair_type, /* \&{pair} variable or capsule */
4586 mp_numeric_type, /* variable that has been declared \&{numeric} but not used */
4587 mp_known, /* \&{numeric} with a known value */
4588 mp_dependent, /* a linear combination with |fraction| coefficients */
4589 mp_proto_dependent, /* a linear combination with |scaled| coefficients */
4590 mp_independent, /* \&{numeric} with unknown value */
4591 mp_token_list, /* variable name or suffix argument or text argument */
4592 mp_structured, /* variable with subscripts and attributes */
4593 mp_unsuffixed_macro, /* variable defined with \&{vardef} but no \.{\AT!\#} */
4594 mp_suffixed_macro /* variable defined with \&{vardef} and \.{\AT!\#} */
4598 void mp_print_type (MP mp,small_number t) ;
4600 @ @<Basic printing procedures@>=
4601 void mp_print_type (MP mp,small_number t) {
4603 case mp_vacuous:mp_print(mp, "mp_vacuous"); break;
4604 case mp_boolean_type:mp_print(mp, "boolean"); break;
4605 case mp_unknown_boolean:mp_print(mp, "unknown boolean"); break;
4606 case mp_string_type:mp_print(mp, "string"); break;
4607 case mp_unknown_string:mp_print(mp, "unknown string"); break;
4608 case mp_pen_type:mp_print(mp, "pen"); break;
4609 case mp_unknown_pen:mp_print(mp, "unknown pen"); break;
4610 case mp_path_type:mp_print(mp, "path"); break;
4611 case mp_unknown_path:mp_print(mp, "unknown path"); break;
4612 case mp_picture_type:mp_print(mp, "picture"); break;
4613 case mp_unknown_picture:mp_print(mp, "unknown picture"); break;
4614 case mp_transform_type:mp_print(mp, "transform"); break;
4615 case mp_color_type:mp_print(mp, "color"); break;
4616 case mp_cmykcolor_type:mp_print(mp, "cmykcolor"); break;
4617 case mp_pair_type:mp_print(mp, "pair"); break;
4618 case mp_known:mp_print(mp, "known numeric"); break;
4619 case mp_dependent:mp_print(mp, "dependent"); break;
4620 case mp_proto_dependent:mp_print(mp, "proto-dependent"); break;
4621 case mp_numeric_type:mp_print(mp, "numeric"); break;
4622 case mp_independent:mp_print(mp, "independent"); break;
4623 case mp_token_list:mp_print(mp, "token list"); break;
4624 case mp_structured:mp_print(mp, "mp_structured"); break;
4625 case mp_unsuffixed_macro:mp_print(mp, "unsuffixed macro"); break;
4626 case mp_suffixed_macro:mp_print(mp, "suffixed macro"); break;
4627 default: mp_print(mp, "undefined"); break;
4631 @ Values inside \MP\ are stored in two-word nodes that have a |name_type|
4632 as well as a |type|. The possibilities for |name_type| are defined
4633 here; they will be explained in more detail later.
4637 mp_root=0, /* |name_type| at the top level of a variable */
4638 mp_saved_root, /* same, when the variable has been saved */
4639 mp_structured_root, /* |name_type| where a |mp_structured| branch occurs */
4640 mp_subscr, /* |name_type| in a subscript node */
4641 mp_attr, /* |name_type| in an attribute node */
4642 mp_x_part_sector, /* |name_type| in the \&{xpart} of a node */
4643 mp_y_part_sector, /* |name_type| in the \&{ypart} of a node */
4644 mp_xx_part_sector, /* |name_type| in the \&{xxpart} of a node */
4645 mp_xy_part_sector, /* |name_type| in the \&{xypart} of a node */
4646 mp_yx_part_sector, /* |name_type| in the \&{yxpart} of a node */
4647 mp_yy_part_sector, /* |name_type| in the \&{yypart} of a node */
4648 mp_red_part_sector, /* |name_type| in the \&{redpart} of a node */
4649 mp_green_part_sector, /* |name_type| in the \&{greenpart} of a node */
4650 mp_blue_part_sector, /* |name_type| in the \&{bluepart} of a node */
4651 mp_cyan_part_sector, /* |name_type| in the \&{redpart} of a node */
4652 mp_magenta_part_sector, /* |name_type| in the \&{greenpart} of a node */
4653 mp_yellow_part_sector, /* |name_type| in the \&{bluepart} of a node */
4654 mp_black_part_sector, /* |name_type| in the \&{greenpart} of a node */
4655 mp_grey_part_sector, /* |name_type| in the \&{bluepart} of a node */
4656 mp_capsule, /* |name_type| in stashed-away subexpressions */
4657 mp_token /* |name_type| in a numeric token or string token */
4660 @ Primitive operations that produce values have a secondary identification
4661 code in addition to their command code; it's something like genera and species.
4662 For example, `\.*' has the command code |primary_binary|, and its
4663 secondary identification is |times|. The secondary codes start at 30 so that
4664 they don't overlap with the type codes; some type codes (e.g., |mp_string_type|)
4665 are used as operators as well as type identifications. The relative values
4666 are not critical, except for |true_code..false_code|, |or_op..and_op|,
4667 and |filled_op..bounded_op|. The restrictions are that
4668 |and_op-false_code=or_op-true_code|, that the ordering of
4669 |x_part...blue_part| must match that of |x_part_sector..mp_blue_part_sector|,
4670 and the ordering of |filled_op..bounded_op| must match that of the code
4671 values they test for.
4673 @d true_code 30 /* operation code for \.{true} */
4674 @d false_code 31 /* operation code for \.{false} */
4675 @d null_picture_code 32 /* operation code for \.{nullpicture} */
4676 @d null_pen_code 33 /* operation code for \.{nullpen} */
4677 @d job_name_op 34 /* operation code for \.{jobname} */
4678 @d read_string_op 35 /* operation code for \.{readstring} */
4679 @d pen_circle 36 /* operation code for \.{pencircle} */
4680 @d normal_deviate 37 /* operation code for \.{normaldeviate} */
4681 @d read_from_op 38 /* operation code for \.{readfrom} */
4682 @d close_from_op 39 /* operation code for \.{closefrom} */
4683 @d odd_op 40 /* operation code for \.{odd} */
4684 @d known_op 41 /* operation code for \.{known} */
4685 @d unknown_op 42 /* operation code for \.{unknown} */
4686 @d not_op 43 /* operation code for \.{not} */
4687 @d decimal 44 /* operation code for \.{decimal} */
4688 @d reverse 45 /* operation code for \.{reverse} */
4689 @d make_path_op 46 /* operation code for \.{makepath} */
4690 @d make_pen_op 47 /* operation code for \.{makepen} */
4691 @d oct_op 48 /* operation code for \.{oct} */
4692 @d hex_op 49 /* operation code for \.{hex} */
4693 @d ASCII_op 50 /* operation code for \.{ASCII} */
4694 @d char_op 51 /* operation code for \.{char} */
4695 @d length_op 52 /* operation code for \.{length} */
4696 @d turning_op 53 /* operation code for \.{turningnumber} */
4697 @d color_model_part 54 /* operation code for \.{colormodel} */
4698 @d x_part 55 /* operation code for \.{xpart} */
4699 @d y_part 56 /* operation code for \.{ypart} */
4700 @d xx_part 57 /* operation code for \.{xxpart} */
4701 @d xy_part 58 /* operation code for \.{xypart} */
4702 @d yx_part 59 /* operation code for \.{yxpart} */
4703 @d yy_part 60 /* operation code for \.{yypart} */
4704 @d red_part 61 /* operation code for \.{redpart} */
4705 @d green_part 62 /* operation code for \.{greenpart} */
4706 @d blue_part 63 /* operation code for \.{bluepart} */
4707 @d cyan_part 64 /* operation code for \.{cyanpart} */
4708 @d magenta_part 65 /* operation code for \.{magentapart} */
4709 @d yellow_part 66 /* operation code for \.{yellowpart} */
4710 @d black_part 67 /* operation code for \.{blackpart} */
4711 @d grey_part 68 /* operation code for \.{greypart} */
4712 @d font_part 69 /* operation code for \.{fontpart} */
4713 @d text_part 70 /* operation code for \.{textpart} */
4714 @d path_part 71 /* operation code for \.{pathpart} */
4715 @d pen_part 72 /* operation code for \.{penpart} */
4716 @d dash_part 73 /* operation code for \.{dashpart} */
4717 @d sqrt_op 74 /* operation code for \.{sqrt} */
4718 @d m_exp_op 75 /* operation code for \.{mexp} */
4719 @d m_log_op 76 /* operation code for \.{mlog} */
4720 @d sin_d_op 77 /* operation code for \.{sind} */
4721 @d cos_d_op 78 /* operation code for \.{cosd} */
4722 @d floor_op 79 /* operation code for \.{floor} */
4723 @d uniform_deviate 80 /* operation code for \.{uniformdeviate} */
4724 @d char_exists_op 81 /* operation code for \.{charexists} */
4725 @d font_size 82 /* operation code for \.{fontsize} */
4726 @d ll_corner_op 83 /* operation code for \.{llcorner} */
4727 @d lr_corner_op 84 /* operation code for \.{lrcorner} */
4728 @d ul_corner_op 85 /* operation code for \.{ulcorner} */
4729 @d ur_corner_op 86 /* operation code for \.{urcorner} */
4730 @d arc_length 87 /* operation code for \.{arclength} */
4731 @d angle_op 88 /* operation code for \.{angle} */
4732 @d cycle_op 89 /* operation code for \.{cycle} */
4733 @d filled_op 90 /* operation code for \.{filled} */
4734 @d stroked_op 91 /* operation code for \.{stroked} */
4735 @d textual_op 92 /* operation code for \.{textual} */
4736 @d clipped_op 93 /* operation code for \.{clipped} */
4737 @d bounded_op 94 /* operation code for \.{bounded} */
4738 @d plus 95 /* operation code for \.+ */
4739 @d minus 96 /* operation code for \.- */
4740 @d times 97 /* operation code for \.* */
4741 @d over 98 /* operation code for \./ */
4742 @d pythag_add 99 /* operation code for \.{++} */
4743 @d pythag_sub 100 /* operation code for \.{+-+} */
4744 @d or_op 101 /* operation code for \.{or} */
4745 @d and_op 102 /* operation code for \.{and} */
4746 @d less_than 103 /* operation code for \.< */
4747 @d less_or_equal 104 /* operation code for \.{<=} */
4748 @d greater_than 105 /* operation code for \.> */
4749 @d greater_or_equal 106 /* operation code for \.{>=} */
4750 @d equal_to 107 /* operation code for \.= */
4751 @d unequal_to 108 /* operation code for \.{<>} */
4752 @d concatenate 109 /* operation code for \.\& */
4753 @d rotated_by 110 /* operation code for \.{rotated} */
4754 @d slanted_by 111 /* operation code for \.{slanted} */
4755 @d scaled_by 112 /* operation code for \.{scaled} */
4756 @d shifted_by 113 /* operation code for \.{shifted} */
4757 @d transformed_by 114 /* operation code for \.{transformed} */
4758 @d x_scaled 115 /* operation code for \.{xscaled} */
4759 @d y_scaled 116 /* operation code for \.{yscaled} */
4760 @d z_scaled 117 /* operation code for \.{zscaled} */
4761 @d in_font 118 /* operation code for \.{infont} */
4762 @d intersect 119 /* operation code for \.{intersectiontimes} */
4763 @d double_dot 120 /* operation code for improper \.{..} */
4764 @d substring_of 121 /* operation code for \.{substring} */
4765 @d min_of substring_of
4766 @d subpath_of 122 /* operation code for \.{subpath} */
4767 @d direction_time_of 123 /* operation code for \.{directiontime} */
4768 @d point_of 124 /* operation code for \.{point} */
4769 @d precontrol_of 125 /* operation code for \.{precontrol} */
4770 @d postcontrol_of 126 /* operation code for \.{postcontrol} */
4771 @d pen_offset_of 127 /* operation code for \.{penoffset} */
4772 @d arc_time_of 128 /* operation code for \.{arctime} */
4773 @d mp_version 129 /* operation code for \.{mpversion} */
4775 @c void mp_print_op (MP mp,quarterword c) {
4776 if (c<=mp_numeric_type ) {
4777 mp_print_type(mp, c);
4780 case true_code:mp_print(mp, "true"); break;
4781 case false_code:mp_print(mp, "false"); break;
4782 case null_picture_code:mp_print(mp, "nullpicture"); break;
4783 case null_pen_code:mp_print(mp, "nullpen"); break;
4784 case job_name_op:mp_print(mp, "jobname"); break;
4785 case read_string_op:mp_print(mp, "readstring"); break;
4786 case pen_circle:mp_print(mp, "pencircle"); break;
4787 case normal_deviate:mp_print(mp, "normaldeviate"); break;
4788 case read_from_op:mp_print(mp, "readfrom"); break;
4789 case close_from_op:mp_print(mp, "closefrom"); break;
4790 case odd_op:mp_print(mp, "odd"); break;
4791 case known_op:mp_print(mp, "known"); break;
4792 case unknown_op:mp_print(mp, "unknown"); break;
4793 case not_op:mp_print(mp, "not"); break;
4794 case decimal:mp_print(mp, "decimal"); break;
4795 case reverse:mp_print(mp, "reverse"); break;
4796 case make_path_op:mp_print(mp, "makepath"); break;
4797 case make_pen_op:mp_print(mp, "makepen"); break;
4798 case oct_op:mp_print(mp, "oct"); break;
4799 case hex_op:mp_print(mp, "hex"); break;
4800 case ASCII_op:mp_print(mp, "ASCII"); break;
4801 case char_op:mp_print(mp, "char"); break;
4802 case length_op:mp_print(mp, "length"); break;
4803 case turning_op:mp_print(mp, "turningnumber"); break;
4804 case x_part:mp_print(mp, "xpart"); break;
4805 case y_part:mp_print(mp, "ypart"); break;
4806 case xx_part:mp_print(mp, "xxpart"); break;
4807 case xy_part:mp_print(mp, "xypart"); break;
4808 case yx_part:mp_print(mp, "yxpart"); break;
4809 case yy_part:mp_print(mp, "yypart"); break;
4810 case red_part:mp_print(mp, "redpart"); break;
4811 case green_part:mp_print(mp, "greenpart"); break;
4812 case blue_part:mp_print(mp, "bluepart"); break;
4813 case cyan_part:mp_print(mp, "cyanpart"); break;
4814 case magenta_part:mp_print(mp, "magentapart"); break;
4815 case yellow_part:mp_print(mp, "yellowpart"); break;
4816 case black_part:mp_print(mp, "blackpart"); break;
4817 case grey_part:mp_print(mp, "greypart"); break;
4818 case color_model_part:mp_print(mp, "colormodel"); break;
4819 case font_part:mp_print(mp, "fontpart"); break;
4820 case text_part:mp_print(mp, "textpart"); break;
4821 case path_part:mp_print(mp, "pathpart"); break;
4822 case pen_part:mp_print(mp, "penpart"); break;
4823 case dash_part:mp_print(mp, "dashpart"); break;
4824 case sqrt_op:mp_print(mp, "sqrt"); break;
4825 case m_exp_op:mp_print(mp, "mexp"); break;
4826 case m_log_op:mp_print(mp, "mlog"); break;
4827 case sin_d_op:mp_print(mp, "sind"); break;
4828 case cos_d_op:mp_print(mp, "cosd"); break;
4829 case floor_op:mp_print(mp, "floor"); break;
4830 case uniform_deviate:mp_print(mp, "uniformdeviate"); break;
4831 case char_exists_op:mp_print(mp, "charexists"); break;
4832 case font_size:mp_print(mp, "fontsize"); break;
4833 case ll_corner_op:mp_print(mp, "llcorner"); break;
4834 case lr_corner_op:mp_print(mp, "lrcorner"); break;
4835 case ul_corner_op:mp_print(mp, "ulcorner"); break;
4836 case ur_corner_op:mp_print(mp, "urcorner"); break;
4837 case arc_length:mp_print(mp, "arclength"); break;
4838 case angle_op:mp_print(mp, "angle"); break;
4839 case cycle_op:mp_print(mp, "cycle"); break;
4840 case filled_op:mp_print(mp, "filled"); break;
4841 case stroked_op:mp_print(mp, "stroked"); break;
4842 case textual_op:mp_print(mp, "textual"); break;
4843 case clipped_op:mp_print(mp, "clipped"); break;
4844 case bounded_op:mp_print(mp, "bounded"); break;
4845 case plus:mp_print_char(mp, '+'); break;
4846 case minus:mp_print_char(mp, '-'); break;
4847 case times:mp_print_char(mp, '*'); break;
4848 case over:mp_print_char(mp, '/'); break;
4849 case pythag_add:mp_print(mp, "++"); break;
4850 case pythag_sub:mp_print(mp, "+-+"); break;
4851 case or_op:mp_print(mp, "or"); break;
4852 case and_op:mp_print(mp, "and"); break;
4853 case less_than:mp_print_char(mp, '<'); break;
4854 case less_or_equal:mp_print(mp, "<="); break;
4855 case greater_than:mp_print_char(mp, '>'); break;
4856 case greater_or_equal:mp_print(mp, ">="); break;
4857 case equal_to:mp_print_char(mp, '='); break;
4858 case unequal_to:mp_print(mp, "<>"); break;
4859 case concatenate:mp_print(mp, "&"); break;
4860 case rotated_by:mp_print(mp, "rotated"); break;
4861 case slanted_by:mp_print(mp, "slanted"); break;
4862 case scaled_by:mp_print(mp, "scaled"); break;
4863 case shifted_by:mp_print(mp, "shifted"); break;
4864 case transformed_by:mp_print(mp, "transformed"); break;
4865 case x_scaled:mp_print(mp, "xscaled"); break;
4866 case y_scaled:mp_print(mp, "yscaled"); break;
4867 case z_scaled:mp_print(mp, "zscaled"); break;
4868 case in_font:mp_print(mp, "infont"); break;
4869 case intersect:mp_print(mp, "intersectiontimes"); break;
4870 case substring_of:mp_print(mp, "substring"); break;
4871 case subpath_of:mp_print(mp, "subpath"); break;
4872 case direction_time_of:mp_print(mp, "directiontime"); break;
4873 case point_of:mp_print(mp, "point"); break;
4874 case precontrol_of:mp_print(mp, "precontrol"); break;
4875 case postcontrol_of:mp_print(mp, "postcontrol"); break;
4876 case pen_offset_of:mp_print(mp, "penoffset"); break;
4877 case arc_time_of:mp_print(mp, "arctime"); break;
4878 case mp_version:mp_print(mp, "mpversion"); break;
4879 default: mp_print(mp, ".."); break;
4884 @ \MP\ also has a bunch of internal parameters that a user might want to
4885 fuss with. Every such parameter has an identifying code number, defined here.
4887 @d tracing_titles 1 /* show titles online when they appear */
4888 @d tracing_equations 2 /* show each variable when it becomes known */
4889 @d tracing_capsules 3 /* show capsules too */
4890 @d tracing_choices 4 /* show the control points chosen for paths */
4891 @d tracing_specs 5 /* show path subdivision prior to filling with polygonal a pen */
4892 @d tracing_commands 6 /* show commands and operations before they are performed */
4893 @d tracing_restores 7 /* show when a variable or internal is restored */
4894 @d tracing_macros 8 /* show macros before they are expanded */
4895 @d tracing_output 9 /* show digitized edges as they are output */
4896 @d tracing_stats 10 /* show memory usage at end of job */
4897 @d tracing_lost_chars 11 /* show characters that aren't \&{infont} */
4898 @d tracing_online 12 /* show long diagnostics on terminal and in the log file */
4899 @d year 13 /* the current year (e.g., 1984) */
4900 @d month 14 /* the current month (e.g, 3 $\equiv$ March) */
4901 @d day 15 /* the current day of the month */
4902 @d mp_time 16 /* the number of minutes past midnight when this job started */
4903 @d char_code 17 /* the number of the next character to be output */
4904 @d char_ext 18 /* the extension code of the next character to be output */
4905 @d char_wd 19 /* the width of the next character to be output */
4906 @d char_ht 20 /* the height of the next character to be output */
4907 @d char_dp 21 /* the depth of the next character to be output */
4908 @d char_ic 22 /* the italic correction of the next character to be output */
4909 @d design_size 23 /* the unit of measure used for |char_wd..char_ic|, in points */
4910 @d pausing 24 /* positive to display lines on the terminal before they are read */
4911 @d showstopping 25 /* positive to stop after each \&{show} command */
4912 @d fontmaking 26 /* positive if font metric output is to be produced */
4913 @d linejoin 27 /* as in \ps: 0 for mitered, 1 for round, 2 for beveled */
4914 @d linecap 28 /* as in \ps: 0 for butt, 1 for round, 2 for square */
4915 @d miterlimit 29 /* controls miter length as in \ps */
4916 @d warning_check 30 /* controls error message when variable value is large */
4917 @d boundary_char 31 /* the right boundary character for ligatures */
4918 @d prologues 32 /* positive to output conforming PostScript using built-in fonts */
4919 @d true_corners 33 /* positive to make \&{llcorner} etc. ignore \&{setbounds} */
4920 @d default_color_model 34 /* the default color model for unspecified items */
4921 @d restore_clip_color 35
4922 @d mpprocset 36 /* wether or not create PostScript command shortcuts */
4923 @d gtroffmode 37 /* whether the user specified |-troff| on the command line */
4924 @d max_given_internal 37
4927 scaled *internal; /* the values of internal quantities */
4928 char **int_name; /* their names */
4929 int int_ptr; /* the maximum internal quantity defined so far */
4930 int max_internal; /* current maximum number of internal quantities */
4933 @ @<Option variables@>=
4936 @ @<Allocate or initialize ...@>=
4937 mp->max_internal=2*max_given_internal;
4938 mp->internal = xmalloc ((mp->max_internal+1), sizeof(scaled));
4939 mp->int_name = xmalloc ((mp->max_internal+1), sizeof(char *));
4940 mp->troff_mode=(opt->troff_mode>0 ? true : false);
4943 int mp_troff_mode(MP mp);
4946 int mp_troff_mode(MP mp) { return mp->troff_mode; }
4948 @ @<Set initial ...@>=
4949 for (k=0;k<= mp->max_internal; k++ ) {
4951 mp->int_name[k]=NULL;
4953 mp->int_ptr=max_given_internal;
4955 @ The symbolic names for internal quantities are put into \MP's hash table
4956 by using a routine called |primitive|, which will be defined later. Let us
4957 enter them now, so that we don't have to list all those names again
4960 @<Put each of \MP's primitives into the hash table@>=
4961 mp_primitive(mp, "tracingtitles",internal_quantity,tracing_titles);
4962 @:tracingtitles_}{\&{tracingtitles} primitive@>
4963 mp_primitive(mp, "tracingequations",internal_quantity,tracing_equations);
4964 @:tracing_equations_}{\&{tracingequations} primitive@>
4965 mp_primitive(mp, "tracingcapsules",internal_quantity,tracing_capsules);
4966 @:tracing_capsules_}{\&{tracingcapsules} primitive@>
4967 mp_primitive(mp, "tracingchoices",internal_quantity,tracing_choices);
4968 @:tracing_choices_}{\&{tracingchoices} primitive@>
4969 mp_primitive(mp, "tracingspecs",internal_quantity,tracing_specs);
4970 @:tracing_specs_}{\&{tracingspecs} primitive@>
4971 mp_primitive(mp, "tracingcommands",internal_quantity,tracing_commands);
4972 @:tracing_commands_}{\&{tracingcommands} primitive@>
4973 mp_primitive(mp, "tracingrestores",internal_quantity,tracing_restores);
4974 @:tracing_restores_}{\&{tracingrestores} primitive@>
4975 mp_primitive(mp, "tracingmacros",internal_quantity,tracing_macros);
4976 @:tracing_macros_}{\&{tracingmacros} primitive@>
4977 mp_primitive(mp, "tracingoutput",internal_quantity,tracing_output);
4978 @:tracing_output_}{\&{tracingoutput} primitive@>
4979 mp_primitive(mp, "tracingstats",internal_quantity,tracing_stats);
4980 @:tracing_stats_}{\&{tracingstats} primitive@>
4981 mp_primitive(mp, "tracinglostchars",internal_quantity,tracing_lost_chars);
4982 @:tracing_lost_chars_}{\&{tracinglostchars} primitive@>
4983 mp_primitive(mp, "tracingonline",internal_quantity,tracing_online);
4984 @:tracing_online_}{\&{tracingonline} primitive@>
4985 mp_primitive(mp, "year",internal_quantity,year);
4986 @:year_}{\&{year} primitive@>
4987 mp_primitive(mp, "month",internal_quantity,month);
4988 @:month_}{\&{month} primitive@>
4989 mp_primitive(mp, "day",internal_quantity,day);
4990 @:day_}{\&{day} primitive@>
4991 mp_primitive(mp, "time",internal_quantity,mp_time);
4992 @:time_}{\&{time} primitive@>
4993 mp_primitive(mp, "charcode",internal_quantity,char_code);
4994 @:char_code_}{\&{charcode} primitive@>
4995 mp_primitive(mp, "charext",internal_quantity,char_ext);
4996 @:char_ext_}{\&{charext} primitive@>
4997 mp_primitive(mp, "charwd",internal_quantity,char_wd);
4998 @:char_wd_}{\&{charwd} primitive@>
4999 mp_primitive(mp, "charht",internal_quantity,char_ht);
5000 @:char_ht_}{\&{charht} primitive@>
5001 mp_primitive(mp, "chardp",internal_quantity,char_dp);
5002 @:char_dp_}{\&{chardp} primitive@>
5003 mp_primitive(mp, "charic",internal_quantity,char_ic);
5004 @:char_ic_}{\&{charic} primitive@>
5005 mp_primitive(mp, "designsize",internal_quantity,design_size);
5006 @:design_size_}{\&{designsize} primitive@>
5007 mp_primitive(mp, "pausing",internal_quantity,pausing);
5008 @:pausing_}{\&{pausing} primitive@>
5009 mp_primitive(mp, "showstopping",internal_quantity,showstopping);
5010 @:showstopping_}{\&{showstopping} primitive@>
5011 mp_primitive(mp, "fontmaking",internal_quantity,fontmaking);
5012 @:fontmaking_}{\&{fontmaking} primitive@>
5013 mp_primitive(mp, "linejoin",internal_quantity,linejoin);
5014 @:linejoin_}{\&{linejoin} primitive@>
5015 mp_primitive(mp, "linecap",internal_quantity,linecap);
5016 @:linecap_}{\&{linecap} primitive@>
5017 mp_primitive(mp, "miterlimit",internal_quantity,miterlimit);
5018 @:miterlimit_}{\&{miterlimit} primitive@>
5019 mp_primitive(mp, "warningcheck",internal_quantity,warning_check);
5020 @:warning_check_}{\&{warningcheck} primitive@>
5021 mp_primitive(mp, "boundarychar",internal_quantity,boundary_char);
5022 @:boundary_char_}{\&{boundarychar} primitive@>
5023 mp_primitive(mp, "prologues",internal_quantity,prologues);
5024 @:prologues_}{\&{prologues} primitive@>
5025 mp_primitive(mp, "truecorners",internal_quantity,true_corners);
5026 @:true_corners_}{\&{truecorners} primitive@>
5027 mp_primitive(mp, "mpprocset",internal_quantity,mpprocset);
5028 @:mpprocset_}{\&{mpprocset} primitive@>
5029 mp_primitive(mp, "troffmode",internal_quantity,gtroffmode);
5030 @:troffmode_}{\&{troffmode} primitive@>
5031 mp_primitive(mp, "defaultcolormodel",internal_quantity,default_color_model);
5032 @:default_color_model_}{\&{defaultcolormodel} primitive@>
5033 mp_primitive(mp, "restoreclipcolor",internal_quantity,restore_clip_color);
5034 @:restore_clip_color_}{\&{restoreclipcolor} primitive@>
5036 @ Colors can be specified in four color models. In the special
5037 case of |no_model|, MetaPost does not output any color operator to
5038 the postscript output.
5040 Note: these values are passed directly on to |with_option|. This only
5041 works because the other possible values passed to |with_option| are
5042 8 and 10 respectively (from |with_pen| and |with_picture|).
5044 There is a first state, that is only used for |gs_colormodel|. It flags
5045 the fact that there has not been any kind of color specification by
5046 the user so far in the game.
5052 @d uninitialized_model 9
5054 @<Initialize table entries (done by \.{INIMP} only)@>=
5055 mp->internal[default_color_model]=(rgb_model*unity);
5056 mp->internal[restore_clip_color]=unity;
5058 @ Well, we do have to list the names one more time, for use in symbolic
5061 @<Initialize table...@>=
5062 mp->int_name[tracing_titles]=xstrdup("tracingtitles");
5063 mp->int_name[tracing_equations]=xstrdup("tracingequations");
5064 mp->int_name[tracing_capsules]=xstrdup("tracingcapsules");
5065 mp->int_name[tracing_choices]=xstrdup("tracingchoices");
5066 mp->int_name[tracing_specs]=xstrdup("tracingspecs");
5067 mp->int_name[tracing_commands]=xstrdup("tracingcommands");
5068 mp->int_name[tracing_restores]=xstrdup("tracingrestores");
5069 mp->int_name[tracing_macros]=xstrdup("tracingmacros");
5070 mp->int_name[tracing_output]=xstrdup("tracingoutput");
5071 mp->int_name[tracing_stats]=xstrdup("tracingstats");
5072 mp->int_name[tracing_lost_chars]=xstrdup("tracinglostchars");
5073 mp->int_name[tracing_online]=xstrdup("tracingonline");
5074 mp->int_name[year]=xstrdup("year");
5075 mp->int_name[month]=xstrdup("month");
5076 mp->int_name[day]=xstrdup("day");
5077 mp->int_name[mp_time]=xstrdup("time");
5078 mp->int_name[char_code]=xstrdup("charcode");
5079 mp->int_name[char_ext]=xstrdup("charext");
5080 mp->int_name[char_wd]=xstrdup("charwd");
5081 mp->int_name[char_ht]=xstrdup("charht");
5082 mp->int_name[char_dp]=xstrdup("chardp");
5083 mp->int_name[char_ic]=xstrdup("charic");
5084 mp->int_name[design_size]=xstrdup("designsize");
5085 mp->int_name[pausing]=xstrdup("pausing");
5086 mp->int_name[showstopping]=xstrdup("showstopping");
5087 mp->int_name[fontmaking]=xstrdup("fontmaking");
5088 mp->int_name[linejoin]=xstrdup("linejoin");
5089 mp->int_name[linecap]=xstrdup("linecap");
5090 mp->int_name[miterlimit]=xstrdup("miterlimit");
5091 mp->int_name[warning_check]=xstrdup("warningcheck");
5092 mp->int_name[boundary_char]=xstrdup("boundarychar");
5093 mp->int_name[prologues]=xstrdup("prologues");
5094 mp->int_name[true_corners]=xstrdup("truecorners");
5095 mp->int_name[default_color_model]=xstrdup("defaultcolormodel");
5096 mp->int_name[mpprocset]=xstrdup("mpprocset");
5097 mp->int_name[gtroffmode]=xstrdup("troffmode");
5098 mp->int_name[restore_clip_color]=xstrdup("restoreclipcolor");
5100 @ The following procedure, which is called just before \MP\ initializes its
5101 input and output, establishes the initial values of the date and time.
5102 @^system dependencies@>
5104 Note that the values are |scaled| integers. Hence \MP\ can no longer
5105 be used after the year 32767.
5108 void mp_fix_date_and_time (MP mp) {
5109 time_t clock = time ((time_t *) 0);
5110 struct tm *tmptr = localtime (&clock);
5111 mp->internal[mp_time]=
5112 (tmptr->tm_hour*60+tmptr->tm_min)*unity; /* minutes since midnight */
5113 mp->internal[day]=(tmptr->tm_mday)*unity; /* fourth day of the month */
5114 mp->internal[month]=(tmptr->tm_mon+1)*unity; /* seventh month of the year */
5115 mp->internal[year]=(tmptr->tm_year+1900)*unity; /* Anno Domini */
5119 void mp_fix_date_and_time (MP mp) ;
5121 @ \MP\ is occasionally supposed to print diagnostic information that
5122 goes only into the transcript file, unless |tracing_online| is positive.
5123 Now that we have defined |tracing_online| we can define
5124 two routines that adjust the destination of print commands:
5127 void mp_begin_diagnostic (MP mp) ;
5128 void mp_end_diagnostic (MP mp,boolean blank_line);
5129 void mp_print_diagnostic (MP mp, char *s, char *t, boolean nuline) ;
5131 @ @<Basic printing...@>=
5132 @<Declare a function called |true_line|@>;
5133 void mp_begin_diagnostic (MP mp) { /* prepare to do some tracing */
5134 mp->old_setting=mp->selector;
5135 if ( mp->selector==ps_file_only ) mp->selector=mp->non_ps_setting;
5136 if ((mp->internal[tracing_online]<=0)&&(mp->selector==term_and_log)){
5138 if ( mp->history==spotless ) mp->history=warning_issued;
5142 void mp_end_diagnostic (MP mp,boolean blank_line) {
5143 /* restore proper conditions after tracing */
5144 mp_print_nl(mp, "");
5145 if ( blank_line ) mp_print_ln(mp);
5146 mp->selector=mp->old_setting;
5149 @ The global variable |non_ps_setting| is initialized when it is time to print
5153 unsigned int old_setting;
5154 unsigned int non_ps_setting;
5156 @ We will occasionally use |begin_diagnostic| in connection with line-number
5157 printing, as follows. (The parameter |s| is typically |"Path"| or
5158 |"Cycle spec"|, etc.)
5160 @<Basic printing...@>=
5161 void mp_print_diagnostic (MP mp, char *s, char *t, boolean nuline) {
5162 mp_begin_diagnostic(mp);
5163 if ( nuline ) mp_print_nl(mp, s); else mp_print(mp, s);
5164 mp_print(mp, " at line ");
5165 mp_print_int(mp, mp_true_line(mp));
5166 mp_print(mp, t); mp_print_char(mp, ':');
5169 @ The 256 |ASCII_code| characters are grouped into classes by means of
5170 the |char_class| table. Individual class numbers have no semantic
5171 or syntactic significance, except in a few instances defined here.
5172 There's also |max_class|, which can be used as a basis for additional
5173 class numbers in nonstandard extensions of \MP.
5175 @d digit_class 0 /* the class number of \.{0123456789} */
5176 @d period_class 1 /* the class number of `\..' */
5177 @d space_class 2 /* the class number of spaces and nonstandard characters */
5178 @d percent_class 3 /* the class number of `\.\%' */
5179 @d string_class 4 /* the class number of `\."' */
5180 @d right_paren_class 8 /* the class number of `\.)' */
5181 @d isolated_classes 5: case 6: case 7: case 8 /* characters that make length-one tokens only */
5182 @d letter_class 9 /* letters and the underline character */
5183 @d left_bracket_class 17 /* `\.[' */
5184 @d right_bracket_class 18 /* `\.]' */
5185 @d invalid_class 20 /* bad character in the input */
5186 @d max_class 20 /* the largest class number */
5189 int char_class[256]; /* the class numbers */
5191 @ If changes are made to accommodate non-ASCII character sets, they should
5192 follow the guidelines in Appendix~C of {\sl The {\logos METAFONT\/}book}.
5193 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
5194 @^system dependencies@>
5196 @<Set initial ...@>=
5197 for (k='0';k<='9';k++)
5198 mp->char_class[k]=digit_class;
5199 mp->char_class['.']=period_class;
5200 mp->char_class[' ']=space_class;
5201 mp->char_class['%']=percent_class;
5202 mp->char_class['"']=string_class;
5203 mp->char_class[',']=5;
5204 mp->char_class[';']=6;
5205 mp->char_class['(']=7;
5206 mp->char_class[')']=right_paren_class;
5207 for (k='A';k<= 'Z';k++ )
5208 mp->char_class[k]=letter_class;
5209 for (k='a';k<='z';k++)
5210 mp->char_class[k]=letter_class;
5211 mp->char_class['_']=letter_class;
5212 mp->char_class['<']=10;
5213 mp->char_class['=']=10;
5214 mp->char_class['>']=10;
5215 mp->char_class[':']=10;
5216 mp->char_class['|']=10;
5217 mp->char_class['`']=11;
5218 mp->char_class['\'']=11;
5219 mp->char_class['+']=12;
5220 mp->char_class['-']=12;
5221 mp->char_class['/']=13;
5222 mp->char_class['*']=13;
5223 mp->char_class['\\']=13;
5224 mp->char_class['!']=14;
5225 mp->char_class['?']=14;
5226 mp->char_class['#']=15;
5227 mp->char_class['&']=15;
5228 mp->char_class['@@']=15;
5229 mp->char_class['$']=15;
5230 mp->char_class['^']=16;
5231 mp->char_class['~']=16;
5232 mp->char_class['[']=left_bracket_class;
5233 mp->char_class[']']=right_bracket_class;
5234 mp->char_class['{']=19;
5235 mp->char_class['}']=19;
5237 mp->char_class[k]=invalid_class;
5238 mp->char_class['\t']=space_class;
5239 mp->char_class['\f']=space_class;
5240 for (k=127;k<=255;k++)
5241 mp->char_class[k]=invalid_class;
5243 @* \[13] The hash table.
5244 Symbolic tokens are stored and retrieved by means of a fairly standard hash
5245 table algorithm called the method of ``coalescing lists'' (cf.\ Algorithm 6.4C
5246 in {\sl The Art of Computer Programming\/}). Once a symbolic token enters the
5247 table, it is never removed.
5249 The actual sequence of characters forming a symbolic token is
5250 stored in the |str_pool| array together with all the other strings. An
5251 auxiliary array |hash| consists of items with two halfword fields per
5252 word. The first of these, called |next(p)|, points to the next identifier
5253 belonging to the same coalesced list as the identifier corresponding to~|p|;
5254 and the other, called |text(p)|, points to the |str_start| entry for
5255 |p|'s identifier. If position~|p| of the hash table is empty, we have
5256 |text(p)=0|; if position |p| is either empty or the end of a coalesced
5257 hash list, we have |next(p)=0|.
5259 An auxiliary pointer variable called |hash_used| is maintained in such a
5260 way that all locations |p>=hash_used| are nonempty. The global variable
5261 |st_count| tells how many symbolic tokens have been defined, if statistics
5264 The first 256 locations of |hash| are reserved for symbols of length one.
5266 There's a parallel array called |eqtb| that contains the current equivalent
5267 values of each symbolic token. The entries of this array consist of
5268 two halfwords called |eq_type| (a command code) and |equiv| (a secondary
5269 piece of information that qualifies the |eq_type|).
5271 @d next(A) mp->hash[(A)].lh /* link for coalesced lists */
5272 @d text(A) mp->hash[(A)].rh /* string number for symbolic token name */
5273 @d eq_type(A) mp->eqtb[(A)].lh /* the current ``meaning'' of a symbolic token */
5274 @d equiv(A) mp->eqtb[(A)].rh /* parametric part of a token's meaning */
5275 @d hash_base 257 /* hashing actually starts here */
5276 @d hash_is_full (mp->hash_used==hash_base) /* are all positions occupied? */
5279 pointer hash_used; /* allocation pointer for |hash| */
5280 integer st_count; /* total number of known identifiers */
5282 @ Certain entries in the hash table are ``frozen'' and not redefinable,
5283 since they are used in error recovery.
5285 @d hash_top (hash_base+mp->hash_size) /* the first location of the frozen area */
5286 @d frozen_inaccessible hash_top /* |hash| location to protect the frozen area */
5287 @d frozen_repeat_loop (hash_top+1) /* |hash| location of a loop-repeat token */
5288 @d frozen_right_delimiter (hash_top+2) /* |hash| location of a permanent `\.)' */
5289 @d frozen_left_bracket (hash_top+3) /* |hash| location of a permanent `\.[' */
5290 @d frozen_slash (hash_top+4) /* |hash| location of a permanent `\./' */
5291 @d frozen_colon (hash_top+5) /* |hash| location of a permanent `\.:' */
5292 @d frozen_semicolon (hash_top+6) /* |hash| location of a permanent `\.;' */
5293 @d frozen_end_for (hash_top+7) /* |hash| location of a permanent \&{endfor} */
5294 @d frozen_end_def (hash_top+8) /* |hash| location of a permanent \&{enddef} */
5295 @d frozen_fi (hash_top+9) /* |hash| location of a permanent \&{fi} */
5296 @d frozen_end_group (hash_top+10) /* |hash| location of a permanent `\.{endgroup}' */
5297 @d frozen_etex (hash_top+11) /* |hash| location of a permanent \&{etex} */
5298 @d frozen_mpx_break (hash_top+12) /* |hash| location of a permanent \&{mpxbreak} */
5299 @d frozen_bad_vardef (hash_top+13) /* |hash| location of `\.{a bad variable}' */
5300 @d frozen_undefined (hash_top+14) /* |hash| location that never gets defined */
5301 @d hash_end (hash_top+14) /* the actual size of the |hash| and |eqtb| arrays */
5304 two_halves *hash; /* the hash table */
5305 two_halves *eqtb; /* the equivalents */
5307 @ @<Allocate or initialize ...@>=
5308 mp->hash = xmalloc((hash_end+1),sizeof(two_halves));
5309 mp->eqtb = xmalloc((hash_end+1),sizeof(two_halves));
5311 @ @<Dealloc variables@>=
5316 next(1)=0; text(1)=0; eq_type(1)=tag_token; equiv(1)=null;
5317 for (k=2;k<=hash_end;k++) {
5318 mp->hash[k]=mp->hash[1]; mp->eqtb[k]=mp->eqtb[1];
5321 @ @<Initialize table entries...@>=
5322 mp->hash_used=frozen_inaccessible; /* nothing is used */
5324 text(frozen_bad_vardef)=intern("a bad variable");
5325 text(frozen_etex)=intern("etex");
5326 text(frozen_mpx_break)=intern("mpxbreak");
5327 text(frozen_fi)=intern("fi");
5328 text(frozen_end_group)=intern("endgroup");
5329 text(frozen_end_def)=intern("enddef");
5330 text(frozen_end_for)=intern("endfor");
5331 text(frozen_semicolon)=intern(";");
5332 text(frozen_colon)=intern(":");
5333 text(frozen_slash)=intern("/");
5334 text(frozen_left_bracket)=intern("[");
5335 text(frozen_right_delimiter)=intern(")");
5336 text(frozen_inaccessible)=intern(" INACCESSIBLE");
5337 eq_type(frozen_right_delimiter)=right_delimiter;
5339 @ @<Check the ``constant'' values...@>=
5340 if ( hash_end+mp->max_internal>max_halfword ) mp->bad=17;
5342 @ Here is the subroutine that searches the hash table for an identifier
5343 that matches a given string of length~|l| appearing in |buffer[j..
5344 (j+l-1)]|. If the identifier is not found, it is inserted; hence it
5345 will always be found, and the corresponding hash table address
5349 pointer mp_id_lookup (MP mp,integer j, integer l) { /* search the hash table */
5350 integer h; /* hash code */
5351 pointer p; /* index in |hash| array */
5352 pointer k; /* index in |buffer| array */
5354 @<Treat special case of length 1 and |break|@>;
5356 @<Compute the hash code |h|@>;
5357 p=h+hash_base; /* we start searching here; note that |0<=h<hash_prime| */
5359 if (text(p)>0 && length(text(p))==l && mp_str_eq_buf(mp, text(p),j))
5362 @<Insert a new symbolic token after |p|, then
5363 make |p| point to it and |break|@>;
5370 @ @<Treat special case of length 1...@>=
5371 p=mp->buffer[j]+1; text(p)=p-1; return p;
5374 @ @<Insert a new symbolic...@>=
5379 mp_overflow(mp, "hash size",mp->hash_size);
5380 @:MetaPost capacity exceeded hash size}{\quad hash size@>
5381 decr(mp->hash_used);
5382 } while (text(mp->hash_used)!=0); /* search for an empty location in |hash| */
5383 next(p)=mp->hash_used;
5387 for (k=j;k<=j+l-1;k++) {
5388 append_char(mp->buffer[k]);
5390 text(p)=mp_make_string(mp);
5391 mp->str_ref[text(p)]=max_str_ref;
5397 @ The value of |hash_prime| should be roughly 85\pct! of |hash_size|, and it
5398 should be a prime number. The theory of hashing tells us to expect fewer
5399 than two table probes, on the average, when the search is successful.
5400 [See J.~S. Vitter, {\sl Journal of the ACM\/ \bf30} (1983), 231--258.]
5401 @^Vitter, Jeffrey Scott@>
5403 @<Compute the hash code |h|@>=
5405 for (k=j+1;k<=j+l-1;k++){
5406 h=h+h+mp->buffer[k];
5407 while ( h>=mp->hash_prime ) h=h-mp->hash_prime;
5410 @ @<Search |eqtb| for equivalents equal to |p|@>=
5411 for (q=1;q<=hash_end;q++) {
5412 if ( equiv(q)==p ) {
5413 mp_print_nl(mp, "EQUIV(");
5414 mp_print_int(mp, q);
5415 mp_print_char(mp, ')');
5419 @ We need to put \MP's ``primitive'' symbolic tokens into the hash
5420 table, together with their command code (which will be the |eq_type|)
5421 and an operand (which will be the |equiv|). The |primitive| procedure
5422 does this, in a way that no \MP\ user can. The global value |cur_sym|
5423 contains the new |eqtb| pointer after |primitive| has acted.
5426 void mp_primitive (MP mp, char *ss, halfword c, halfword o) {
5427 pool_pointer k; /* index into |str_pool| */
5428 small_number j; /* index into |buffer| */
5429 small_number l; /* length of the string */
5432 k=mp->str_start[s]; l=str_stop(s)-k;
5433 /* we will move |s| into the (empty) |buffer| */
5434 for (j=0;j<=l-1;j++) {
5435 mp->buffer[j]=mp->str_pool[k+j];
5437 mp->cur_sym=mp_id_lookup(mp, 0,l);
5438 if ( s>=256 ) { /* we don't want to have the string twice */
5439 mp_flush_string(mp, text(mp->cur_sym)); text(mp->cur_sym)=s;
5441 eq_type(mp->cur_sym)=c;
5442 equiv(mp->cur_sym)=o;
5446 @ Many of \MP's primitives need no |equiv|, since they are identifiable
5447 by their |eq_type| alone. These primitives are loaded into the hash table
5450 @<Put each of \MP's primitives into the hash table@>=
5451 mp_primitive(mp, "..",path_join,0);
5452 @:.._}{\.{..} primitive@>
5453 mp_primitive(mp, "[",left_bracket,0); mp->eqtb[frozen_left_bracket]=mp->eqtb[mp->cur_sym];
5454 @:[ }{\.{[} primitive@>
5455 mp_primitive(mp, "]",right_bracket,0);
5456 @:] }{\.{]} primitive@>
5457 mp_primitive(mp, "}",right_brace,0);
5458 @:]]}{\.{\char`\}} primitive@>
5459 mp_primitive(mp, "{",left_brace,0);
5460 @:][}{\.{\char`\{} primitive@>
5461 mp_primitive(mp, ":",colon,0); mp->eqtb[frozen_colon]=mp->eqtb[mp->cur_sym];
5462 @:: }{\.{:} primitive@>
5463 mp_primitive(mp, "::",double_colon,0);
5464 @::: }{\.{::} primitive@>
5465 mp_primitive(mp, "||:",bchar_label,0);
5466 @:::: }{\.{\char'174\char'174:} primitive@>
5467 mp_primitive(mp, ":=",assignment,0);
5468 @::=_}{\.{:=} primitive@>
5469 mp_primitive(mp, ",",comma,0);
5470 @:, }{\., primitive@>
5471 mp_primitive(mp, ";",semicolon,0); mp->eqtb[frozen_semicolon]=mp->eqtb[mp->cur_sym];
5472 @:; }{\.; primitive@>
5473 mp_primitive(mp, "\\",relax,0);
5474 @:]]\\}{\.{\char`\\} primitive@>
5476 mp_primitive(mp, "addto",add_to_command,0);
5477 @:add_to_}{\&{addto} primitive@>
5478 mp_primitive(mp, "atleast",at_least,0);
5479 @:at_least_}{\&{atleast} primitive@>
5480 mp_primitive(mp, "begingroup",begin_group,0); mp->bg_loc=mp->cur_sym;
5481 @:begin_group_}{\&{begingroup} primitive@>
5482 mp_primitive(mp, "controls",controls,0);
5483 @:controls_}{\&{controls} primitive@>
5484 mp_primitive(mp, "curl",curl_command,0);
5485 @:curl_}{\&{curl} primitive@>
5486 mp_primitive(mp, "delimiters",delimiters,0);
5487 @:delimiters_}{\&{delimiters} primitive@>
5488 mp_primitive(mp, "endgroup",end_group,0);
5489 mp->eqtb[frozen_end_group]=mp->eqtb[mp->cur_sym]; mp->eg_loc=mp->cur_sym;
5490 @:endgroup_}{\&{endgroup} primitive@>
5491 mp_primitive(mp, "everyjob",every_job_command,0);
5492 @:every_job_}{\&{everyjob} primitive@>
5493 mp_primitive(mp, "exitif",exit_test,0);
5494 @:exit_if_}{\&{exitif} primitive@>
5495 mp_primitive(mp, "expandafter",expand_after,0);
5496 @:expand_after_}{\&{expandafter} primitive@>
5497 mp_primitive(mp, "interim",interim_command,0);
5498 @:interim_}{\&{interim} primitive@>
5499 mp_primitive(mp, "let",let_command,0);
5500 @:let_}{\&{let} primitive@>
5501 mp_primitive(mp, "newinternal",new_internal,0);
5502 @:new_internal_}{\&{newinternal} primitive@>
5503 mp_primitive(mp, "of",of_token,0);
5504 @:of_}{\&{of} primitive@>
5505 mp_primitive(mp, "randomseed",random_seed,0);
5506 @:random_seed_}{\&{randomseed} primitive@>
5507 mp_primitive(mp, "save",save_command,0);
5508 @:save_}{\&{save} primitive@>
5509 mp_primitive(mp, "scantokens",scan_tokens,0);
5510 @:scan_tokens_}{\&{scantokens} primitive@>
5511 mp_primitive(mp, "shipout",ship_out_command,0);
5512 @:ship_out_}{\&{shipout} primitive@>
5513 mp_primitive(mp, "skipto",skip_to,0);
5514 @:skip_to_}{\&{skipto} primitive@>
5515 mp_primitive(mp, "special",special_command,0);
5516 @:special}{\&{special} primitive@>
5517 mp_primitive(mp, "fontmapfile",special_command,1);
5518 @:fontmapfile}{\&{fontmapfile} primitive@>
5519 mp_primitive(mp, "fontmapline",special_command,2);
5520 @:fontmapline}{\&{fontmapline} primitive@>
5521 mp_primitive(mp, "step",step_token,0);
5522 @:step_}{\&{step} primitive@>
5523 mp_primitive(mp, "str",str_op,0);
5524 @:str_}{\&{str} primitive@>
5525 mp_primitive(mp, "tension",tension,0);
5526 @:tension_}{\&{tension} primitive@>
5527 mp_primitive(mp, "to",to_token,0);
5528 @:to_}{\&{to} primitive@>
5529 mp_primitive(mp, "until",until_token,0);
5530 @:until_}{\&{until} primitive@>
5531 mp_primitive(mp, "within",within_token,0);
5532 @:within_}{\&{within} primitive@>
5533 mp_primitive(mp, "write",write_command,0);
5534 @:write_}{\&{write} primitive@>
5536 @ Each primitive has a corresponding inverse, so that it is possible to
5537 display the cryptic numeric contents of |eqtb| in symbolic form.
5538 Every call of |primitive| in this program is therefore accompanied by some
5539 straightforward code that forms part of the |print_cmd_mod| routine
5542 @<Cases of |print_cmd_mod| for symbolic printing of primitives@>=
5543 case add_to_command:mp_print(mp, "addto"); break;
5544 case assignment:mp_print(mp, ":="); break;
5545 case at_least:mp_print(mp, "atleast"); break;
5546 case bchar_label:mp_print(mp, "||:"); break;
5547 case begin_group:mp_print(mp, "begingroup"); break;
5548 case colon:mp_print(mp, ":"); break;
5549 case comma:mp_print(mp, ","); break;
5550 case controls:mp_print(mp, "controls"); break;
5551 case curl_command:mp_print(mp, "curl"); break;
5552 case delimiters:mp_print(mp, "delimiters"); break;
5553 case double_colon:mp_print(mp, "::"); break;
5554 case end_group:mp_print(mp, "endgroup"); break;
5555 case every_job_command:mp_print(mp, "everyjob"); break;
5556 case exit_test:mp_print(mp, "exitif"); break;
5557 case expand_after:mp_print(mp, "expandafter"); break;
5558 case interim_command:mp_print(mp, "interim"); break;
5559 case left_brace:mp_print(mp, "{"); break;
5560 case left_bracket:mp_print(mp, "["); break;
5561 case let_command:mp_print(mp, "let"); break;
5562 case new_internal:mp_print(mp, "newinternal"); break;
5563 case of_token:mp_print(mp, "of"); break;
5564 case path_join:mp_print(mp, ".."); break;
5565 case random_seed:mp_print(mp, "randomseed"); break;
5566 case relax:mp_print_char(mp, '\\'); break;
5567 case right_brace:mp_print(mp, "}"); break;
5568 case right_bracket:mp_print(mp, "]"); break;
5569 case save_command:mp_print(mp, "save"); break;
5570 case scan_tokens:mp_print(mp, "scantokens"); break;
5571 case semicolon:mp_print(mp, ";"); break;
5572 case ship_out_command:mp_print(mp, "shipout"); break;
5573 case skip_to:mp_print(mp, "skipto"); break;
5574 case special_command: if ( m==2 ) mp_print(mp, "fontmapline"); else
5575 if ( m==1 ) mp_print(mp, "fontmapfile"); else
5576 mp_print(mp, "special"); break;
5577 case step_token:mp_print(mp, "step"); break;
5578 case str_op:mp_print(mp, "str"); break;
5579 case tension:mp_print(mp, "tension"); break;
5580 case to_token:mp_print(mp, "to"); break;
5581 case until_token:mp_print(mp, "until"); break;
5582 case within_token:mp_print(mp, "within"); break;
5583 case write_command:mp_print(mp, "write"); break;
5585 @ We will deal with the other primitives later, at some point in the program
5586 where their |eq_type| and |equiv| values are more meaningful. For example,
5587 the primitives for macro definitions will be loaded when we consider the
5588 routines that define macros.
5589 It is easy to find where each particular
5590 primitive was treated by looking in the index at the end; for example, the
5591 section where |"def"| entered |eqtb| is listed under `\&{def} primitive'.
5593 @* \[14] Token lists.
5594 A \MP\ token is either symbolic or numeric or a string, or it denotes
5595 a macro parameter or capsule; so there are five corresponding ways to encode it
5597 internally: (1)~A symbolic token whose hash code is~|p|
5598 is represented by the number |p|, in the |info| field of a single-word
5599 node in~|mem|. (2)~A numeric token whose |scaled| value is~|v| is
5600 represented in a two-word node of~|mem|; the |type| field is |known|,
5601 the |name_type| field is |token|, and the |value| field holds~|v|.
5602 The fact that this token appears in a two-word node rather than a
5603 one-word node is, of course, clear from the node address.
5604 (3)~A string token is also represented in a two-word node; the |type|
5605 field is |mp_string_type|, the |name_type| field is |token|, and the
5606 |value| field holds the corresponding |str_number|. (4)~Capsules have
5607 |name_type=capsule|, and their |type| and |value| fields represent
5608 arbitrary values (in ways to be explained later). (5)~Macro parameters
5609 are like symbolic tokens in that they appear in |info| fields of
5610 one-word nodes. The $k$th parameter is represented by |expr_base+k| if it
5611 is of type \&{expr}, or by |suffix_base+k| if it is of type \&{suffix}, or
5612 by |text_base+k| if it is of type \&{text}. (Here |0<=k<param_size|.)
5613 Actual values of these parameters are kept in a separate stack, as we will
5614 see later. The constants |expr_base|, |suffix_base|, and |text_base| are,
5615 of course, chosen so that there will be no confusion between symbolic
5616 tokens and parameters of various types.
5619 the `\\{type}' field of a node has nothing to do with ``type'' in a
5620 printer's sense. It's curious that the same word is used in such different ways.
5622 @d type(A) mp->mem[(A)].hh.b0 /* identifies what kind of value this is */
5623 @d name_type(A) mp->mem[(A)].hh.b1 /* a clue to the name of this value */
5624 @d token_node_size 2 /* the number of words in a large token node */
5625 @d value_loc(A) ((A)+1) /* the word that contains the |value| field */
5626 @d value(A) mp->mem[value_loc((A))].cint /* the value stored in a large token node */
5627 @d expr_base (hash_end+1) /* code for the zeroth \&{expr} parameter */
5628 @d suffix_base (expr_base+mp->param_size) /* code for the zeroth \&{suffix} parameter */
5629 @d text_base (suffix_base+mp->param_size) /* code for the zeroth \&{text} parameter */
5631 @<Check the ``constant''...@>=
5632 if ( text_base+mp->param_size>max_halfword ) mp->bad=18;
5634 @ We have set aside a two word node beginning at |null| so that we can have
5635 |value(null)=0|. We will make use of this coincidence later.
5637 @<Initialize table entries...@>=
5638 link(null)=null; value(null)=0;
5640 @ A numeric token is created by the following trivial routine.
5643 pointer mp_new_num_tok (MP mp,scaled v) {
5644 pointer p; /* the new node */
5645 p=mp_get_node(mp, token_node_size); value(p)=v;
5646 type(p)=mp_known; name_type(p)=mp_token;
5650 @ A token list is a singly linked list of nodes in |mem|, where
5651 each node contains a token and a link. Here's a subroutine that gets rid
5652 of a token list when it is no longer needed.
5655 void mp_token_recycle (MP mp);
5658 @c void mp_flush_token_list (MP mp,pointer p) {
5659 pointer q; /* the node being recycled */
5662 if ( q>=mp->hi_mem_min ) {
5666 case mp_vacuous: case mp_boolean_type: case mp_known:
5668 case mp_string_type:
5669 delete_str_ref(value(q));
5671 case unknown_types: case mp_pen_type: case mp_path_type:
5672 case mp_picture_type: case mp_pair_type: case mp_color_type:
5673 case mp_cmykcolor_type: case mp_transform_type: case mp_dependent:
5674 case mp_proto_dependent: case mp_independent:
5675 mp->g_pointer=q; mp_token_recycle(mp);
5677 default: mp_confusion(mp, "token");
5678 @:this can't happen token}{\quad token@>
5680 mp_free_node(mp, q,token_node_size);
5685 @ The procedure |show_token_list|, which prints a symbolic form of
5686 the token list that starts at a given node |p|, illustrates these
5687 conventions. The token list being displayed should not begin with a reference
5688 count. However, the procedure is intended to be fairly robust, so that if the
5689 memory links are awry or if |p| is not really a pointer to a token list,
5690 almost nothing catastrophic can happen.
5692 An additional parameter |q| is also given; this parameter is either null
5693 or it points to a node in the token list where a certain magic computation
5694 takes place that will be explained later. (Basically, |q| is non-null when
5695 we are printing the two-line context information at the time of an error
5696 message; |q| marks the place corresponding to where the second line
5699 The generation will stop, and `\.{\char`\ ETC.}' will be printed, if the length
5700 of printing exceeds a given limit~|l|; the length of printing upon entry is
5701 assumed to be a given amount called |null_tally|. (Note that
5702 |show_token_list| sometimes uses itself recursively to print
5703 variable names within a capsule.)
5706 Unusual entries are printed in the form of all-caps tokens
5707 preceded by a space, e.g., `\.{\char`\ BAD}'.
5710 void mp_print_capsule (MP mp);
5712 @ @<Declare the procedure called |show_token_list|@>=
5713 void mp_show_token_list (MP mp, integer p, integer q, integer l,
5714 integer null_tally) ;
5717 void mp_show_token_list (MP mp, integer p, integer q, integer l,
5718 integer null_tally) {
5719 small_number class,c; /* the |char_class| of previous and new tokens */
5720 integer r,v; /* temporary registers */
5721 class=percent_class;
5722 mp->tally=null_tally;
5723 while ( (p!=null) && (mp->tally<l) ) {
5725 @<Do magic computation@>;
5726 @<Display token |p| and set |c| to its class;
5727 but |return| if there are problems@>;
5731 mp_print(mp, " ETC.");
5736 @ @<Display token |p| and set |c| to its class...@>=
5737 c=letter_class; /* the default */
5738 if ( (p<0)||(p>mp->mem_end) ) {
5739 mp_print(mp, " CLOBBERED"); return;
5742 if ( p<mp->hi_mem_min ) {
5743 @<Display two-word token@>;
5746 if ( r>=expr_base ) {
5747 @<Display a parameter token@>;
5751 @<Display a collective subscript@>
5753 mp_print(mp, " IMPOSSIBLE");
5758 if ( (r<0)||(r>mp->max_str_ptr) ) {
5759 mp_print(mp, " NONEXISTENT");
5762 @<Print string |r| as a symbolic token
5763 and set |c| to its class@>;
5769 @ @<Display two-word token@>=
5770 if ( name_type(p)==mp_token ) {
5771 if ( type(p)==mp_known ) {
5772 @<Display a numeric token@>;
5773 } else if ( type(p)!=mp_string_type ) {
5774 mp_print(mp, " BAD");
5777 mp_print_char(mp, '"'); mp_print_str(mp, value(p)); mp_print_char(mp, '"');
5780 } else if ((name_type(p)!=mp_capsule)||(type(p)<mp_vacuous)||(type(p)>mp_independent) ) {
5781 mp_print(mp, " BAD");
5783 mp->g_pointer=p; mp_print_capsule(mp); c=right_paren_class;
5786 @ @<Display a numeric token@>=
5787 if ( class==digit_class )
5788 mp_print_char(mp, ' ');
5791 if ( class==left_bracket_class )
5792 mp_print_char(mp, ' ');
5793 mp_print_char(mp, '['); mp_print_scaled(mp, v); mp_print_char(mp, ']');
5794 c=right_bracket_class;
5796 mp_print_scaled(mp, v); c=digit_class;
5800 @ Strictly speaking, a genuine token will never have |info(p)=0|.
5801 But we will see later (in the |print_variable_name| routine) that
5802 it is convenient to let |info(p)=0| stand for `\.{[]}'.
5804 @<Display a collective subscript@>=
5806 if ( class==left_bracket_class )
5807 mp_print_char(mp, ' ');
5808 mp_print(mp, "[]"); c=right_bracket_class;
5811 @ @<Display a parameter token@>=
5813 if ( r<suffix_base ) {
5814 mp_print(mp, "(EXPR"); r=r-(expr_base);
5816 } else if ( r<text_base ) {
5817 mp_print(mp, "(SUFFIX"); r=r-(suffix_base);
5820 mp_print(mp, "(TEXT"); r=r-(text_base);
5823 mp_print_int(mp, r); mp_print_char(mp, ')'); c=right_paren_class;
5827 @ @<Print string |r| as a symbolic token...@>=
5829 c=mp->char_class[mp->str_pool[mp->str_start[r]]];
5832 case letter_class:mp_print_char(mp, '.'); break;
5833 case isolated_classes: break;
5834 default: mp_print_char(mp, ' '); break;
5837 mp_print_str(mp, r);
5840 @ The following procedures have been declared |forward| with no parameters,
5841 because the author dislikes \PASCAL's convention about |forward| procedures
5842 with parameters. It was necessary to do something, because |show_token_list|
5843 is recursive (although the recursion is limited to one level), and because
5844 |flush_token_list| is syntactically (but not semantically) recursive.
5847 @<Declare miscellaneous procedures that were declared |forward|@>=
5848 void mp_print_capsule (MP mp) {
5849 mp_print_char(mp, '('); mp_print_exp(mp, mp->g_pointer,0); mp_print_char(mp, ')');
5852 void mp_token_recycle (MP mp) {
5853 mp_recycle_value(mp, mp->g_pointer);
5857 pointer g_pointer; /* (global) parameter to the |forward| procedures */
5859 @ Macro definitions are kept in \MP's memory in the form of token lists
5860 that have a few extra one-word nodes at the beginning.
5862 The first node contains a reference count that is used to tell when the
5863 list is no longer needed. To emphasize the fact that a reference count is
5864 present, we shall refer to the |info| field of this special node as the
5866 @^reference counts@>
5868 The next node or nodes after the reference count serve to describe the
5869 formal parameters. They either contain a code word that specifies all
5870 of the parameters, or they contain zero or more parameter tokens followed
5871 by the code `|general_macro|'.
5874 /* reference count preceding a macro definition or picture header */
5875 @d add_mac_ref(A) incr(ref_count((A))) /* make a new reference to a macro list */
5876 @d general_macro 0 /* preface to a macro defined with a parameter list */
5877 @d primary_macro 1 /* preface to a macro with a \&{primary} parameter */
5878 @d secondary_macro 2 /* preface to a macro with a \&{secondary} parameter */
5879 @d tertiary_macro 3 /* preface to a macro with a \&{tertiary} parameter */
5880 @d expr_macro 4 /* preface to a macro with an undelimited \&{expr} parameter */
5881 @d of_macro 5 /* preface to a macro with
5882 undelimited `\&{expr} |x| \&{of}~|y|' parameters */
5883 @d suffix_macro 6 /* preface to a macro with an undelimited \&{suffix} parameter */
5884 @d text_macro 7 /* preface to a macro with an undelimited \&{text} parameter */
5887 void mp_delete_mac_ref (MP mp,pointer p) {
5888 /* |p| points to the reference count of a macro list that is
5889 losing one reference */
5890 if ( ref_count(p)==null ) mp_flush_token_list(mp, p);
5891 else decr(ref_count(p));
5894 @ The following subroutine displays a macro, given a pointer to its
5898 @<Declare the procedure called |print_cmd_mod|@>;
5899 void mp_show_macro (MP mp, pointer p, integer q, integer l) {
5900 pointer r; /* temporary storage */
5901 p=link(p); /* bypass the reference count */
5902 while ( info(p)>text_macro ){
5903 r=link(p); link(p)=null;
5904 mp_show_token_list(mp, p,null,l,0); link(p)=r; p=r;
5905 if ( l>0 ) l=l-mp->tally; else return;
5906 } /* control printing of `\.{ETC.}' */
5910 case general_macro:mp_print(mp, "->"); break;
5912 case primary_macro: case secondary_macro: case tertiary_macro:
5913 mp_print_char(mp, '<');
5914 mp_print_cmd_mod(mp, param_type,info(p));
5915 mp_print(mp, ">->");
5917 case expr_macro:mp_print(mp, "<expr>->"); break;
5918 case of_macro:mp_print(mp, "<expr>of<primary>->"); break;
5919 case suffix_macro:mp_print(mp, "<suffix>->"); break;
5920 case text_macro:mp_print(mp, "<text>->"); break;
5921 } /* there are no other cases */
5922 mp_show_token_list(mp, link(p),q,l-mp->tally,0);
5925 @* \[15] Data structures for variables.
5926 The variables of \MP\ programs can be simple, like `\.x', or they can
5927 combine the structural properties of arrays and records, like `\.{x20a.b}'.
5928 A \MP\ user assigns a type to a variable like \.{x20a.b} by saying, for
5929 example, `\.{boolean} \.{x20a.b}'. It's time for us to study how such
5930 things are represented inside of the computer.
5932 Each variable value occupies two consecutive words, either in a two-word
5933 node called a value node, or as a two-word subfield of a larger node. One
5934 of those two words is called the |value| field; it is an integer,
5935 containing either a |scaled| numeric value or the representation of some
5936 other type of quantity. (It might also be subdivided into halfwords, in
5937 which case it is referred to by other names instead of |value|.) The other
5938 word is broken into subfields called |type|, |name_type|, and |link|. The
5939 |type| field is a quarterword that specifies the variable's type, and
5940 |name_type| is a quarterword from which \MP\ can reconstruct the
5941 variable's name (sometimes by using the |link| field as well). Thus, only
5942 1.25 words are actually devoted to the value itself; the other
5943 three-quarters of a word are overhead, but they aren't wasted because they
5944 allow \MP\ to deal with sparse arrays and to provide meaningful diagnostics.
5946 In this section we shall be concerned only with the structural aspects of
5947 variables, not their values. Later parts of the program will change the
5948 |type| and |value| fields, but we shall treat those fields as black boxes
5949 whose contents should not be touched.
5951 However, if the |type| field is |mp_structured|, there is no |value| field,
5952 and the second word is broken into two pointer fields called |attr_head|
5953 and |subscr_head|. Those fields point to additional nodes that
5954 contain structural information, as we shall see.
5956 @d subscr_head_loc(A) (A)+1 /* where |value|, |subscr_head| and |attr_head| are */
5957 @d attr_head(A) info(subscr_head_loc((A))) /* pointer to attribute info */
5958 @d subscr_head(A) link(subscr_head_loc((A))) /* pointer to subscript info */
5959 @d value_node_size 2 /* the number of words in a value node */
5961 @ An attribute node is three words long. Two of these words contain |type|
5962 and |value| fields as described above, and the third word contains
5963 additional information: There is an |attr_loc| field, which contains the
5964 hash address of the token that names this attribute; and there's also a
5965 |parent| field, which points to the value node of |mp_structured| type at the
5966 next higher level (i.e., at the level to which this attribute is
5967 subsidiary). The |name_type| in an attribute node is `|attr|'. The
5968 |link| field points to the next attribute with the same parent; these are
5969 arranged in increasing order, so that |attr_loc(link(p))>attr_loc(p)|. The
5970 final attribute node links to the constant |end_attr|, whose |attr_loc|
5971 field is greater than any legal hash address. The |attr_head| in the
5972 parent points to a node whose |name_type| is |mp_structured_root|; this
5973 node represents the null attribute, i.e., the variable that is relevant
5974 when no attributes are attached to the parent. The |attr_head| node is either
5975 a value node, a subscript node, or an attribute node, depending on what
5976 the parent would be if it were not structured; but the subscript and
5977 attribute fields are ignored, so it effectively contains only the data of
5978 a value node. The |link| field in this special node points to an attribute
5979 node whose |attr_loc| field is zero; the latter node represents a collective
5980 subscript `\.{[]}' attached to the parent, and its |link| field points to
5981 the first non-special attribute node (or to |end_attr| if there are none).
5983 A subscript node likewise occupies three words, with |type| and |value| fields
5984 plus extra information; its |name_type| is |subscr|. In this case the
5985 third word is called the |subscript| field, which is a |scaled| integer.
5986 The |link| field points to the subscript node with the next larger
5987 subscript, if any; otherwise the |link| points to the attribute node
5988 for collective subscripts at this level. We have seen that the latter node
5989 contains an upward pointer, so that the parent can be deduced.
5991 The |name_type| in a parent-less value node is |root|, and the |link|
5992 is the hash address of the token that names this value.
5994 In other words, variables have a hierarchical structure that includes
5995 enough threads running around so that the program is able to move easily
5996 between siblings, parents, and children. An example should be helpful:
5997 (The reader is advised to draw a picture while reading the following
5998 description, since that will help to firm up the ideas.)
5999 Suppose that `\.x' and `\.{x.a}' and `\.{x[]b}' and `\.{x5}'
6000 and `\.{x20b}' have been mentioned in a user's program, where
6001 \.{x[]b} has been declared to be of \&{boolean} type. Let |h(x)|, |h(a)|,
6002 and |h(b)| be the hash addresses of \.x, \.a, and~\.b. Then
6003 |eq_type(h(x))=name| and |equiv(h(x))=p|, where |p|~is a two-word value
6004 node with |name_type(p)=root| and |link(p)=h(x)|. We have |type(p)=mp_structured|,
6005 |attr_head(p)=q|, and |subscr_head(p)=r|, where |q| points to a value
6006 node and |r| to a subscript node. (Are you still following this? Use
6007 a pencil to draw a diagram.) The lone variable `\.x' is represented by
6008 |type(q)| and |value(q)|; furthermore
6009 |name_type(q)=mp_structured_root| and |link(q)=q1|, where |q1| points
6010 to an attribute node representing `\.{x[]}'. Thus |name_type(q1)=attr|,
6011 |attr_loc(q1)=collective_subscript=0|, |parent(q1)=p|,
6012 |type(q1)=mp_structured|, |attr_head(q1)=qq|, and |subscr_head(q1)=qq1|;
6013 |qq| is a value node with |type(qq)=mp_numeric_type| (assuming that \.{x5} is
6014 numeric, because |qq| represents `\.{x[]}' with no further attributes),
6015 |name_type(qq)=mp_structured_root|, and
6016 |link(qq)=qq1|. (Now pay attention to the next part.) Node |qq1| is
6017 an attribute node representing `\.{x[][]}', which has never yet
6018 occurred; its |type| field is |undefined|, and its |value| field is
6019 undefined. We have |name_type(qq1)=attr|, |attr_loc(qq1)=collective_subscript|,
6020 |parent(qq1)=q1|, and |link(qq1)=qq2|. Since |qq2| represents
6021 `\.{x[]b}', |type(qq2)=mp_unknown_boolean|; also |attr_loc(qq2)=h(b)|,
6022 |parent(qq2)=q1|, |name_type(qq2)=attr|, |link(qq2)=end_attr|.
6023 (Maybe colored lines will help untangle your picture.)
6024 Node |r| is a subscript node with |type| and |value|
6025 representing `\.{x5}'; |name_type(r)=subscr|, |subscript(r)=5.0|,
6026 and |link(r)=r1| is another subscript node. To complete the picture,
6027 see if you can guess what |link(r1)| is; give up? It's~|q1|.
6028 Furthermore |subscript(r1)=20.0|, |name_type(r1)=subscr|,
6029 |type(r1)=mp_structured|, |attr_head(r1)=qqq|, |subscr_head(r1)=qqq1|,
6030 and we finish things off with three more nodes
6031 |qqq|, |qqq1|, and |qqq2| hung onto~|r1|. (Perhaps you should start again
6032 with a larger sheet of paper.) The value of variable \.{x20b}
6033 appears in node~|qqq2|, as you can well imagine.
6035 If the example in the previous paragraph doesn't make things crystal
6036 clear, a glance at some of the simpler subroutines below will reveal how
6037 things work out in practice.
6039 The only really unusual thing about these conventions is the use of
6040 collective subscript attributes. The idea is to avoid repeating a lot of
6041 type information when many elements of an array are identical macros
6042 (for which distinct values need not be stored) or when they don't have
6043 all of the possible attributes. Branches of the structure below collective
6044 subscript attributes do not carry actual values except for macro identifiers;
6045 branches of the structure below subscript nodes do not carry significant
6046 information in their collective subscript attributes.
6048 @d attr_loc_loc(A) ((A)+2) /* where the |attr_loc| and |parent| fields are */
6049 @d attr_loc(A) info(attr_loc_loc((A))) /* hash address of this attribute */
6050 @d parent(A) link(attr_loc_loc((A))) /* pointer to |mp_structured| variable */
6051 @d subscript_loc(A) ((A)+2) /* where the |subscript| field lives */
6052 @d subscript(A) mp->mem[subscript_loc((A))].sc /* subscript of this variable */
6053 @d attr_node_size 3 /* the number of words in an attribute node */
6054 @d subscr_node_size 3 /* the number of words in a subscript node */
6055 @d collective_subscript 0 /* code for the attribute `\.{[]}' */
6057 @<Initialize table...@>=
6058 attr_loc(end_attr)=hash_end+1; parent(end_attr)=null;
6060 @ Variables of type \&{pair} will have values that point to four-word
6061 nodes containing two numeric values. The first of these values has
6062 |name_type=mp_x_part_sector| and the second has |name_type=mp_y_part_sector|;
6063 the |link| in the first points back to the node whose |value| points
6064 to this four-word node.
6066 Variables of type \&{transform} are similar, but in this case their
6067 |value| points to a 12-word node containing six values, identified by
6068 |x_part_sector|, |y_part_sector|, |mp_xx_part_sector|, |mp_xy_part_sector|,
6069 |mp_yx_part_sector|, and |mp_yy_part_sector|.
6070 Finally, variables of type \&{color} have three values in six words
6071 identified by |mp_red_part_sector|, |mp_green_part_sector|, and |mp_blue_part_sector|.
6073 When an entire structured variable is saved, the |root| indication
6074 is temporarily replaced by |saved_root|.
6076 Some variables have no name; they just are used for temporary storage
6077 while expressions are being evaluated. We call them {\sl capsules}.
6079 @d x_part_loc(A) (A) /* where the \&{xpart} is found in a pair or transform node */
6080 @d y_part_loc(A) ((A)+2) /* where the \&{ypart} is found in a pair or transform node */
6081 @d xx_part_loc(A) ((A)+4) /* where the \&{xxpart} is found in a transform node */
6082 @d xy_part_loc(A) ((A)+6) /* where the \&{xypart} is found in a transform node */
6083 @d yx_part_loc(A) ((A)+8) /* where the \&{yxpart} is found in a transform node */
6084 @d yy_part_loc(A) ((A)+10) /* where the \&{yypart} is found in a transform node */
6085 @d red_part_loc(A) (A) /* where the \&{redpart} is found in a color node */
6086 @d green_part_loc(A) ((A)+2) /* where the \&{greenpart} is found in a color node */
6087 @d blue_part_loc(A) ((A)+4) /* where the \&{bluepart} is found in a color node */
6088 @d cyan_part_loc(A) (A) /* where the \&{cyanpart} is found in a color node */
6089 @d magenta_part_loc(A) ((A)+2) /* where the \&{magentapart} is found in a color node */
6090 @d yellow_part_loc(A) ((A)+4) /* where the \&{yellowpart} is found in a color node */
6091 @d black_part_loc(A) ((A)+6) /* where the \&{blackpart} is found in a color node */
6092 @d grey_part_loc(A) (A) /* where the \&{greypart} is found in a color node */
6094 @d pair_node_size 4 /* the number of words in a pair node */
6095 @d transform_node_size 12 /* the number of words in a transform node */
6096 @d color_node_size 6 /* the number of words in a color node */
6097 @d cmykcolor_node_size 8 /* the number of words in a color node */
6100 small_number big_node_size[mp_pair_type+1];
6101 small_number sector0[mp_pair_type+1];
6102 small_number sector_offset[mp_black_part_sector+1];
6104 @ The |sector0| array gives for each big node type, |name_type| values
6105 for its first subfield; the |sector_offset| array gives for each
6106 |name_type| value, the offset from the first subfield in words;
6107 and the |big_node_size| array gives the size in words for each type of
6111 mp->big_node_size[mp_transform_type]=transform_node_size;
6112 mp->big_node_size[mp_pair_type]=pair_node_size;
6113 mp->big_node_size[mp_color_type]=color_node_size;
6114 mp->big_node_size[mp_cmykcolor_type]=cmykcolor_node_size;
6115 mp->sector0[mp_transform_type]=mp_x_part_sector;
6116 mp->sector0[mp_pair_type]=mp_x_part_sector;
6117 mp->sector0[mp_color_type]=mp_red_part_sector;
6118 mp->sector0[mp_cmykcolor_type]=mp_cyan_part_sector;
6119 for (k=mp_x_part_sector;k<= mp_yy_part_sector;k++ ) {
6120 mp->sector_offset[k]=2*(k-mp_x_part_sector);
6122 for (k=mp_red_part_sector;k<= mp_blue_part_sector ; k++) {
6123 mp->sector_offset[k]=2*(k-mp_red_part_sector);
6125 for (k=mp_cyan_part_sector;k<= mp_black_part_sector;k++ ) {
6126 mp->sector_offset[k]=2*(k-mp_cyan_part_sector);
6129 @ If |type(p)=mp_pair_type| or |mp_transform_type| and if |value(p)=null|, the
6130 procedure call |init_big_node(p)| will allocate a pair or transform node
6131 for~|p|. The individual parts of such nodes are initially of type
6135 void mp_init_big_node (MP mp,pointer p) {
6136 pointer q; /* the new node */
6137 small_number s; /* its size */
6138 s=mp->big_node_size[type(p)]; q=mp_get_node(mp, s);
6141 @<Make variable |q+s| newly independent@>;
6142 name_type(q+s)=halfp(s)+mp->sector0[type(p)];
6145 link(q)=p; value(p)=q;
6148 @ The |id_transform| function creates a capsule for the
6149 identity transformation.
6152 pointer mp_id_transform (MP mp) {
6153 pointer p,q,r; /* list manipulation registers */
6154 p=mp_get_node(mp, value_node_size); type(p)=mp_transform_type;
6155 name_type(p)=mp_capsule; value(p)=null; mp_init_big_node(mp, p); q=value(p);
6156 r=q+transform_node_size;
6159 type(r)=mp_known; value(r)=0;
6161 value(xx_part_loc(q))=unity;
6162 value(yy_part_loc(q))=unity;
6166 @ Tokens are of type |tag_token| when they first appear, but they point
6167 to |null| until they are first used as the root of a variable.
6168 The following subroutine establishes the root node on such grand occasions.
6171 void mp_new_root (MP mp,pointer x) {
6172 pointer p; /* the new node */
6173 p=mp_get_node(mp, value_node_size); type(p)=undefined; name_type(p)=mp_root;
6174 link(p)=x; equiv(x)=p;
6177 @ These conventions for variable representation are illustrated by the
6178 |print_variable_name| routine, which displays the full name of a
6179 variable given only a pointer to its two-word value packet.
6182 void mp_print_variable_name (MP mp, pointer p);
6185 void mp_print_variable_name (MP mp, pointer p) {
6186 pointer q; /* a token list that will name the variable's suffix */
6187 pointer r; /* temporary for token list creation */
6188 while ( name_type(p)>=mp_x_part_sector ) {
6189 @<Preface the output with a part specifier; |return| in the
6190 case of a capsule@>;
6193 while ( name_type(p)>mp_saved_root ) {
6194 @<Ascend one level, pushing a token onto list |q|
6195 and replacing |p| by its parent@>;
6197 r=mp_get_avail(mp); info(r)=link(p); link(r)=q;
6198 if ( name_type(p)==mp_saved_root ) mp_print(mp, "(SAVED)");
6200 mp_show_token_list(mp, r,null,el_gordo,mp->tally);
6201 mp_flush_token_list(mp, r);
6204 @ @<Ascend one level, pushing a token onto list |q|...@>=
6206 if ( name_type(p)==mp_subscr ) {
6207 r=mp_new_num_tok(mp, subscript(p));
6210 } while (name_type(p)!=mp_attr);
6211 } else if ( name_type(p)==mp_structured_root ) {
6212 p=link(p); goto FOUND;
6214 if ( name_type(p)!=mp_attr ) mp_confusion(mp, "var");
6215 @:this can't happen var}{\quad var@>
6216 r=mp_get_avail(mp); info(r)=attr_loc(p);
6223 @ @<Preface the output with a part specifier...@>=
6224 { switch (name_type(p)) {
6225 case mp_x_part_sector: mp_print_char(mp, 'x'); break;
6226 case mp_y_part_sector: mp_print_char(mp, 'y'); break;
6227 case mp_xx_part_sector: mp_print(mp, "xx"); break;
6228 case mp_xy_part_sector: mp_print(mp, "xy"); break;
6229 case mp_yx_part_sector: mp_print(mp, "yx"); break;
6230 case mp_yy_part_sector: mp_print(mp, "yy"); break;
6231 case mp_red_part_sector: mp_print(mp, "red"); break;
6232 case mp_green_part_sector: mp_print(mp, "green"); break;
6233 case mp_blue_part_sector: mp_print(mp, "blue"); break;
6234 case mp_cyan_part_sector: mp_print(mp, "cyan"); break;
6235 case mp_magenta_part_sector: mp_print(mp, "magenta"); break;
6236 case mp_yellow_part_sector: mp_print(mp, "yellow"); break;
6237 case mp_black_part_sector: mp_print(mp, "black"); break;
6238 case mp_grey_part_sector: mp_print(mp, "grey"); break;
6240 mp_print(mp, "%CAPSULE"); mp_print_int(mp, p-null); return;
6243 } /* there are no other cases */
6244 mp_print(mp, "part ");
6245 p=link(p-mp->sector_offset[name_type(p)]);
6248 @ The |interesting| function returns |true| if a given variable is not
6249 in a capsule, or if the user wants to trace capsules.
6252 boolean mp_interesting (MP mp,pointer p) {
6253 small_number t; /* a |name_type| */
6254 if ( mp->internal[tracing_capsules]>0 ) {
6258 if ( t>=mp_x_part_sector ) if ( t!=mp_capsule )
6259 t=name_type(link(p-mp->sector_offset[t]));
6260 return (t!=mp_capsule);
6264 @ Now here is a subroutine that converts an unstructured type into an
6265 equivalent structured type, by inserting a |mp_structured| node that is
6266 capable of growing. This operation is done only when |name_type(p)=root|,
6267 |subscr|, or |attr|.
6269 The procedure returns a pointer to the new node that has taken node~|p|'s
6270 place in the structure. Node~|p| itself does not move, nor are its
6271 |value| or |type| fields changed in any way.
6274 pointer mp_new_structure (MP mp,pointer p) {
6275 pointer q,r=0; /* list manipulation registers */
6276 switch (name_type(p)) {
6278 q=link(p); r=mp_get_node(mp, value_node_size); equiv(q)=r;
6281 @<Link a new subscript node |r| in place of node |p|@>;
6284 @<Link a new attribute node |r| in place of node |p|@>;
6287 mp_confusion(mp, "struct");
6288 @:this can't happen struct}{\quad struct@>
6291 link(r)=link(p); type(r)=mp_structured; name_type(r)=name_type(p);
6292 attr_head(r)=p; name_type(p)=mp_structured_root;
6293 q=mp_get_node(mp, attr_node_size); link(p)=q; subscr_head(r)=q;
6294 parent(q)=r; type(q)=undefined; name_type(q)=mp_attr; link(q)=end_attr;
6295 attr_loc(q)=collective_subscript;
6299 @ @<Link a new subscript node |r| in place of node |p|@>=
6304 } while (name_type(q)!=mp_attr);
6305 q=parent(q); r=subscr_head_loc(q); /* |link(r)=subscr_head(q)| */
6309 r=mp_get_node(mp, subscr_node_size);
6310 link(q)=r; subscript(r)=subscript(p);
6313 @ If the attribute is |collective_subscript|, there are two pointers to
6314 node~|p|, so we must change both of them.
6316 @<Link a new attribute node |r| in place of node |p|@>=
6318 q=parent(p); r=attr_head(q);
6322 r=mp_get_node(mp, attr_node_size); link(q)=r;
6323 mp->mem[attr_loc_loc(r)]=mp->mem[attr_loc_loc(p)]; /* copy |attr_loc| and |parent| */
6324 if ( attr_loc(p)==collective_subscript ) {
6325 q=subscr_head_loc(parent(p));
6326 while ( link(q)!=p ) q=link(q);
6331 @ The |find_variable| routine is given a pointer~|t| to a nonempty token
6332 list of suffixes; it returns a pointer to the corresponding two-word
6333 value. For example, if |t| points to token \.x followed by a numeric
6334 token containing the value~7, |find_variable| finds where the value of
6335 \.{x7} is stored in memory. This may seem a simple task, and it
6336 usually is, except when \.{x7} has never been referenced before.
6337 Indeed, \.x may never have even been subscripted before; complexities
6338 arise with respect to updating the collective subscript information.
6340 If a macro type is detected anywhere along path~|t|, or if the first
6341 item on |t| isn't a |tag_token|, the value |null| is returned.
6342 Otherwise |p| will be a non-null pointer to a node such that
6343 |undefined<type(p)<mp_structured|.
6345 @d abort_find { return null; }
6348 pointer mp_find_variable (MP mp,pointer t) {
6349 pointer p,q,r,s; /* nodes in the ``value'' line */
6350 pointer pp,qq,rr,ss; /* nodes in the ``collective'' line */
6351 integer n; /* subscript or attribute */
6352 memory_word save_word; /* temporary storage for a word of |mem| */
6354 p=info(t); t=link(t);
6355 if ( (eq_type(p) % outer_tag) != tag_token ) abort_find;
6356 if ( equiv(p)==null ) mp_new_root(mp, p);
6359 @<Make sure that both nodes |p| and |pp| are of |mp_structured| type@>;
6360 if ( t<mp->hi_mem_min ) {
6361 @<Descend one level for the subscript |value(t)|@>
6363 @<Descend one level for the attribute |info(t)|@>;
6367 if ( type(pp)>=mp_structured ) {
6368 if ( type(pp)==mp_structured ) pp=attr_head(pp); else abort_find;
6370 if ( type(p)==mp_structured ) p=attr_head(p);
6371 if ( type(p)==undefined ) {
6372 if ( type(pp)==undefined ) { type(pp)=mp_numeric_type; value(pp)=null; };
6373 type(p)=type(pp); value(p)=null;
6378 @ Although |pp| and |p| begin together, they diverge when a subscript occurs;
6379 |pp|~stays in the collective line while |p|~goes through actual subscript
6382 @<Make sure that both nodes |p| and |pp|...@>=
6383 if ( type(pp)!=mp_structured ) {
6384 if ( type(pp)>mp_structured ) abort_find;
6385 ss=mp_new_structure(mp, pp);
6388 }; /* now |type(pp)=mp_structured| */
6389 if ( type(p)!=mp_structured ) /* it cannot be |>mp_structured| */
6390 p=mp_new_structure(mp, p) /* now |type(p)=mp_structured| */
6392 @ We want this part of the program to be reasonably fast, in case there are
6394 lots of subscripts at the same level of the data structure. Therefore
6395 we store an ``infinite'' value in the word that appears at the end of the
6396 subscript list, even though that word isn't part of a subscript node.
6398 @<Descend one level for the subscript |value(t)|@>=
6401 pp=link(attr_head(pp)); /* now |attr_loc(pp)=collective_subscript| */
6402 q=link(attr_head(p)); save_word=mp->mem[subscript_loc(q)];
6403 subscript(q)=el_gordo; s=subscr_head_loc(p); /* |link(s)=subscr_head(p)| */
6406 } while (n>subscript(s));
6407 if ( n==subscript(s) ) {
6410 p=mp_get_node(mp, subscr_node_size); link(r)=p; link(p)=s;
6411 subscript(p)=n; name_type(p)=mp_subscr; type(p)=undefined;
6413 mp->mem[subscript_loc(q)]=save_word;
6416 @ @<Descend one level for the attribute |info(t)|@>=
6422 } while (n>attr_loc(ss));
6423 if ( n<attr_loc(ss) ) {
6424 qq=mp_get_node(mp, attr_node_size); link(rr)=qq; link(qq)=ss;
6425 attr_loc(qq)=n; name_type(qq)=mp_attr; type(qq)=undefined;
6426 parent(qq)=pp; ss=qq;
6431 pp=ss; s=attr_head(p);
6434 } while (n>attr_loc(s));
6435 if ( n==attr_loc(s) ) {
6438 q=mp_get_node(mp, attr_node_size); link(r)=q; link(q)=s;
6439 attr_loc(q)=n; name_type(q)=mp_attr; type(q)=undefined;
6445 @ Variables lose their former values when they appear in a type declaration,
6446 or when they are defined to be macros or \&{let} equal to something else.
6447 A subroutine will be defined later that recycles the storage associated
6448 with any particular |type| or |value|; our goal now is to study a higher
6449 level process called |flush_variable|, which selectively frees parts of a
6452 This routine has some complexity because of examples such as
6453 `\hbox{\tt numeric x[]a[]b}'
6454 which recycles all variables of the form \.{x[i]a[j]b} (and no others), while
6455 `\hbox{\tt vardef x[]a[]=...}'
6456 discards all variables of the form \.{x[i]a[j]} followed by an arbitrary
6457 suffix, except for the collective node \.{x[]a[]} itself. The obvious way
6458 to handle such examples is to use recursion; so that's what we~do.
6461 Parameter |p| points to the root information of the variable;
6462 parameter |t| points to a list of one-word nodes that represent
6463 suffixes, with |info=collective_subscript| for subscripts.
6466 @<Declare subroutines for printing expressions@>
6467 @<Declare basic dependency-list subroutines@>
6468 @<Declare the recycling subroutines@>
6469 void mp_flush_cur_exp (MP mp,scaled v) ;
6470 @<Declare the procedure called |flush_below_variable|@>
6473 void mp_flush_variable (MP mp,pointer p, pointer t, boolean discard_suffixes) {
6474 pointer q,r; /* list manipulation */
6475 halfword n; /* attribute to match */
6477 if ( type(p)!=mp_structured ) return;
6478 n=info(t); t=link(t);
6479 if ( n==collective_subscript ) {
6480 r=subscr_head_loc(p); q=link(r); /* |q=subscr_head(p)| */
6481 while ( name_type(q)==mp_subscr ){
6482 mp_flush_variable(mp, q,t,discard_suffixes);
6484 if ( type(q)==mp_structured ) r=q;
6485 else { link(r)=link(q); mp_free_node(mp, q,subscr_node_size); }
6495 } while (attr_loc(p)<n);
6496 if ( attr_loc(p)!=n ) return;
6498 if ( discard_suffixes ) {
6499 mp_flush_below_variable(mp, p);
6501 if ( type(p)==mp_structured ) p=attr_head(p);
6502 mp_recycle_value(mp, p);
6506 @ The next procedure is simpler; it wipes out everything but |p| itself,
6507 which becomes undefined.
6509 @<Declare the procedure called |flush_below_variable|@>=
6510 void mp_flush_below_variable (MP mp, pointer p);
6513 void mp_flush_below_variable (MP mp,pointer p) {
6514 pointer q,r; /* list manipulation registers */
6515 if ( type(p)!=mp_structured ) {
6516 mp_recycle_value(mp, p); /* this sets |type(p)=undefined| */
6519 while ( name_type(q)==mp_subscr ) {
6520 mp_flush_below_variable(mp, q); r=q; q=link(q);
6521 mp_free_node(mp, r,subscr_node_size);
6523 r=attr_head(p); q=link(r); mp_recycle_value(mp, r);
6524 if ( name_type(p)<=mp_saved_root ) mp_free_node(mp, r,value_node_size);
6525 else mp_free_node(mp, r,subscr_node_size);
6526 /* we assume that |subscr_node_size=attr_node_size| */
6528 mp_flush_below_variable(mp, q); r=q; q=link(q); mp_free_node(mp, r,attr_node_size);
6529 } while (q!=end_attr);
6534 @ Just before assigning a new value to a variable, we will recycle the
6535 old value and make the old value undefined. The |und_type| routine
6536 determines what type of undefined value should be given, based on
6537 the current type before recycling.
6540 small_number mp_und_type (MP mp,pointer p) {
6542 case undefined: case mp_vacuous:
6544 case mp_boolean_type: case mp_unknown_boolean:
6545 return mp_unknown_boolean;
6546 case mp_string_type: case mp_unknown_string:
6547 return mp_unknown_string;
6548 case mp_pen_type: case mp_unknown_pen:
6549 return mp_unknown_pen;
6550 case mp_path_type: case mp_unknown_path:
6551 return mp_unknown_path;
6552 case mp_picture_type: case mp_unknown_picture:
6553 return mp_unknown_picture;
6554 case mp_transform_type: case mp_color_type: case mp_cmykcolor_type:
6555 case mp_pair_type: case mp_numeric_type:
6557 case mp_known: case mp_dependent: case mp_proto_dependent: case mp_independent:
6558 return mp_numeric_type;
6559 } /* there are no other cases */
6563 @ The |clear_symbol| routine is used when we want to redefine the equivalent
6564 of a symbolic token. It must remove any variable structure or macro
6565 definition that is currently attached to that symbol. If the |saving|
6566 parameter is true, a subsidiary structure is saved instead of destroyed.
6569 void mp_clear_symbol (MP mp,pointer p, boolean saving) {
6570 pointer q; /* |equiv(p)| */
6572 switch (eq_type(p) % outer_tag) {
6574 case secondary_primary_macro:
6575 case tertiary_secondary_macro:
6576 case expression_tertiary_macro:
6577 if ( ! saving ) mp_delete_mac_ref(mp, q);
6582 name_type(q)=mp_saved_root;
6584 mp_flush_below_variable(mp, q); mp_free_node(mp,q,value_node_size);
6591 mp->eqtb[p]=mp->eqtb[frozen_undefined];
6594 @* \[16] Saving and restoring equivalents.
6595 The nested structure given by \&{begingroup} and \&{endgroup}
6596 allows |eqtb| entries to be saved and restored, so that temporary changes
6597 can be made without difficulty. When the user requests a current value to
6598 be saved, \MP\ puts that value into its ``save stack.'' An appearance of
6599 \&{endgroup} ultimately causes the old values to be removed from the save
6600 stack and put back in their former places.
6602 The save stack is a linked list containing three kinds of entries,
6603 distinguished by their |info| fields. If |p| points to a saved item,
6607 |info(p)=0| stands for a group boundary; each \&{begingroup} contributes
6608 such an item to the save stack and each \&{endgroup} cuts back the stack
6609 until the most recent such entry has been removed.
6612 |info(p)=q|, where |1<=q<=hash_end|, means that |mem[p+1]| holds the former
6613 contents of |eqtb[q]|. Such save stack entries are generated by \&{save}
6614 commands or suitable \&{interim} commands.
6617 |info(p)=hash_end+q|, where |q>0|, means that |value(p)| is a |scaled|
6618 integer to be restored to internal parameter number~|q|. Such entries
6619 are generated by \&{interim} commands.
6622 The global variable |save_ptr| points to the top item on the save stack.
6624 @d save_node_size 2 /* number of words per non-boundary save-stack node */
6625 @d saved_equiv(A) mp->mem[(A)+1].hh /* where an |eqtb| entry gets saved */
6626 @d save_boundary_item(A) { (A)=mp_get_avail(mp); info((A))=0;
6627 link((A))=mp->save_ptr; mp->save_ptr=(A);
6631 pointer save_ptr; /* the most recently saved item */
6633 @ @<Set init...@>=mp->save_ptr=null;
6635 @ The |save_variable| routine is given a hash address |q|; it salts this
6636 address in the save stack, together with its current equivalent,
6637 then makes token~|q| behave as though it were brand new.
6639 Nothing is stacked when |save_ptr=null|, however; there's no way to remove
6640 things from the stack when the program is not inside a group, so there's
6641 no point in wasting the space.
6643 @c void mp_save_variable (MP mp,pointer q) {
6644 pointer p; /* temporary register */
6645 if ( mp->save_ptr!=null ){
6646 p=mp_get_node(mp, save_node_size); info(p)=q; link(p)=mp->save_ptr;
6647 saved_equiv(p)=mp->eqtb[q]; mp->save_ptr=p;
6649 mp_clear_symbol(mp, q,(mp->save_ptr!=null));
6652 @ Similarly, |save_internal| is given the location |q| of an internal
6653 quantity like |tracing_pens|. It creates a save stack entry of the
6656 @c void mp_save_internal (MP mp,halfword q) {
6657 pointer p; /* new item for the save stack */
6658 if ( mp->save_ptr!=null ){
6659 p=mp_get_node(mp, save_node_size); info(p)=hash_end+q;
6660 link(p)=mp->save_ptr; value(p)=mp->internal[q]; mp->save_ptr=p;
6664 @ At the end of a group, the |unsave| routine restores all of the saved
6665 equivalents in reverse order. This routine will be called only when there
6666 is at least one boundary item on the save stack.
6669 void mp_unsave (MP mp) {
6670 pointer q; /* index to saved item */
6671 pointer p; /* temporary register */
6672 while ( info(mp->save_ptr)!=0 ) {
6673 q=info(mp->save_ptr);
6675 if ( mp->internal[tracing_restores]>0 ) {
6676 mp_begin_diagnostic(mp); mp_print_nl(mp, "{restoring ");
6677 mp_print(mp, mp->int_name[q-(hash_end)]); mp_print_char(mp, '=');
6678 mp_print_scaled(mp, value(mp->save_ptr)); mp_print_char(mp, '}');
6679 mp_end_diagnostic(mp, false);
6681 mp->internal[q-(hash_end)]=value(mp->save_ptr);
6683 if ( mp->internal[tracing_restores]>0 ) {
6684 mp_begin_diagnostic(mp); mp_print_nl(mp, "{restoring ");
6685 mp_print_text(q); mp_print_char(mp, '}');
6686 mp_end_diagnostic(mp, false);
6688 mp_clear_symbol(mp, q,false);
6689 mp->eqtb[q]=saved_equiv(mp->save_ptr);
6690 if ( eq_type(q) % outer_tag==tag_token ) {
6692 if ( p!=null ) name_type(p)=mp_root;
6695 p=link(mp->save_ptr);
6696 mp_free_node(mp, mp->save_ptr,save_node_size); mp->save_ptr=p;
6698 p=link(mp->save_ptr); free_avail(mp->save_ptr); mp->save_ptr=p;
6701 @* \[17] Data structures for paths.
6702 When a \MP\ user specifies a path, \MP\ will create a list of knots
6703 and control points for the associated cubic spline curves. If the
6704 knots are $z_0$, $z_1$, \dots, $z_n$, there are control points
6705 $z_k^+$ and $z_{k+1}^-$ such that the cubic splines between knots
6706 $z_k$ and $z_{k+1}$ are defined by B\'ezier's formula
6707 @:Bezier}{B\'ezier, Pierre Etienne@>
6708 $$\eqalign{z(t)&=B(z_k,z_k^+,z_{k+1}^-,z_{k+1};t)\cr
6709 &=(1-t)^3z_k+3(1-t)^2tz_k^++3(1-t)t^2z_{k+1}^-+t^3z_{k+1}\cr}$$
6712 There is a 8-word node for each knot $z_k$, containing one word of
6713 control information and six words for the |x| and |y| coordinates of
6714 $z_k^-$ and $z_k$ and~$z_k^+$. The control information appears in the
6715 |left_type| and |right_type| fields, which each occupy a quarter of
6716 the first word in the node; they specify properties of the curve as it
6717 enters and leaves the knot. There's also a halfword |link| field,
6718 which points to the following knot, and a final supplementary word (of
6719 which only a quarter is used).
6721 If the path is a closed contour, knots 0 and |n| are identical;
6722 i.e., the |link| in knot |n-1| points to knot~0. But if the path
6723 is not closed, the |left_type| of knot~0 and the |right_type| of knot~|n|
6724 are equal to |endpoint|. In the latter case the |link| in knot~|n| points
6725 to knot~0, and the control points $z_0^-$ and $z_n^+$ are not used.
6727 @d left_type(A) mp->mem[(A)].hh.b0 /* characterizes the path entering this knot */
6728 @d right_type(A) mp->mem[(A)].hh.b1 /* characterizes the path leaving this knot */
6729 @d endpoint 0 /* |left_type| at path beginning and |right_type| at path end */
6730 @d x_coord(A) mp->mem[(A)+1].sc /* the |x| coordinate of this knot */
6731 @d y_coord(A) mp->mem[(A)+2].sc /* the |y| coordinate of this knot */
6732 @d left_x(A) mp->mem[(A)+3].sc /* the |x| coordinate of previous control point */
6733 @d left_y(A) mp->mem[(A)+4].sc /* the |y| coordinate of previous control point */
6734 @d right_x(A) mp->mem[(A)+5].sc /* the |x| coordinate of next control point */
6735 @d right_y(A) mp->mem[(A)+6].sc /* the |y| coordinate of next control point */
6736 @d x_loc(A) ((A)+1) /* where the |x| coordinate is stored in a knot */
6737 @d y_loc(A) ((A)+2) /* where the |y| coordinate is stored in a knot */
6738 @d knot_coord(A) mp->mem[(A)].sc /* |x| or |y| coordinate given |x_loc| or |y_loc| */
6739 @d left_coord(A) mp->mem[(A)+2].sc
6740 /* coordinate of previous control point given |x_loc| or |y_loc| */
6741 @d right_coord(A) mp->mem[(A)+4].sc
6742 /* coordinate of next control point given |x_loc| or |y_loc| */
6743 @d knot_node_size 8 /* number of words in a knot node */
6745 @ Before the B\'ezier control points have been calculated, the memory
6746 space they will ultimately occupy is taken up by information that can be
6747 used to compute them. There are four cases:
6750 \textindent{$\bullet$} If |right_type=open|, the curve should leave
6751 the knot in the same direction it entered; \MP\ will figure out a
6755 \textindent{$\bullet$} If |right_type=curl|, the curve should leave the
6756 knot in a direction depending on the angle at which it enters the next
6757 knot and on the curl parameter stored in |right_curl|.
6760 \textindent{$\bullet$} If |right_type=given|, the curve should leave the
6761 knot in a nonzero direction stored as an |angle| in |right_given|.
6764 \textindent{$\bullet$} If |right_type=explicit|, the B\'ezier control
6765 point for leaving this knot has already been computed; it is in the
6766 |right_x| and |right_y| fields.
6769 The rules for |left_type| are similar, but they refer to the curve entering
6770 the knot, and to \\{left} fields instead of \\{right} fields.
6772 Non-|explicit| control points will be chosen based on ``tension'' parameters
6773 in the |left_tension| and |right_tension| fields. The
6774 `\&{atleast}' option is represented by negative tension values.
6775 @:at_least_}{\&{atleast} primitive@>
6777 For example, the \MP\ path specification
6778 $$\.{z0..z1..tension atleast 1..\{curl 2\}z2..z3\{-1,-2\}..tension
6780 where \.p is the path `\.{z4..controls z45 and z54..z5}', will be represented
6782 \def\lodash{\hbox to 1.1em{\thinspace\hrulefill\thinspace}}
6783 $$\vbox{\halign{#\hfil&&\qquad#\hfil\cr
6784 |left_type|&\\{left} info&|x_coord,y_coord|&|right_type|&\\{right} info\cr
6786 |endpoint|&\lodash$,\,$\lodash&$x_0,y_0$&|curl|&$1.0,1.0$\cr
6787 |open|&\lodash$,1.0$&$x_1,y_1$&|open|&\lodash$,-1.0$\cr
6788 |curl|&$2.0,-1.0$&$x_2,y_2$&|curl|&$2.0,1.0$\cr
6789 |given|&$d,1.0$&$x_3,y_3$&|given|&$d,3.0$\cr
6790 |open|&\lodash$,4.0$&$x_4,y_4$&|explicit|&$x_{45},y_{45}$\cr
6791 |explicit|&$x_{54},y_{54}$&$x_5,y_5$&|endpoint|&\lodash$,\,$\lodash\cr}}$$
6792 Here |d| is the |angle| obtained by calling |n_arg(-unity,-two)|.
6793 Of course, this example is more complicated than anything a normal user
6796 These types must satisfy certain restrictions because of the form of \MP's
6798 (i)~|open| type never appears in the same node together with |endpoint|,
6800 (ii)~The |right_type| of a node is |explicit| if and only if the
6801 |left_type| of the following node is |explicit|.
6802 (iii)~|endpoint| types occur only at the ends, as mentioned above.
6804 @d left_curl left_x /* curl information when entering this knot */
6805 @d left_given left_x /* given direction when entering this knot */
6806 @d left_tension left_y /* tension information when entering this knot */
6807 @d right_curl right_x /* curl information when leaving this knot */
6808 @d right_given right_x /* given direction when leaving this knot */
6809 @d right_tension right_y /* tension information when leaving this knot */
6810 @d explicit 1 /* |left_type| or |right_type| when control points are known */
6811 @d given 2 /* |left_type| or |right_type| when a direction is given */
6812 @d curl 3 /* |left_type| or |right_type| when a curl is desired */
6813 @d open 4 /* |left_type| or |right_type| when \MP\ should choose the direction */
6815 @ Knots can be user-supplied, or they can be created by program code,
6816 like the |split_cubic| function, or |copy_path|. The distinction is
6817 needed for the cleanup routine that runs after |split_cubic|, because
6818 it should only delete knots it has previously inserted, and never
6819 anything that was user-supplied. In order to be able to differentiate
6820 one knot from another, we will set |originator(p):=metapost_user| when
6821 it appeared in the actual metapost program, and
6822 |originator(p):=program_code| in all other cases.
6824 @d originator(A) mp->mem[(A)+7].hh.b0 /* the creator of this knot */
6825 @d program_code 0 /* not created by a user */
6826 @d metapost_user 1 /* created by a user */
6828 @ Here is a routine that prints a given knot list
6829 in symbolic form. It illustrates the conventions discussed above,
6830 and checks for anomalies that might arise while \MP\ is being debugged.
6832 @<Declare subroutines for printing expressions@>=
6833 void mp_pr_path (MP mp,pointer h);
6836 void mp_pr_path (MP mp,pointer h) {
6837 pointer p,q; /* for list traversal */
6841 if ( (p==null)||(q==null) ) {
6842 mp_print_nl(mp, "???"); return; /* this won't happen */
6845 @<Print information for adjacent knots |p| and |q|@>;
6848 if ( (p!=h)||(left_type(h)!=endpoint) ) {
6849 @<Print two dots, followed by |given| or |curl| if present@>;
6852 if ( left_type(h)!=endpoint )
6853 mp_print(mp, "cycle");
6856 @ @<Print information for adjacent knots...@>=
6857 mp_print_two(mp, x_coord(p),y_coord(p));
6858 switch (right_type(p)) {
6860 if ( left_type(p)==open ) mp_print(mp, "{open?}"); /* can't happen */
6862 if ( (left_type(q)!=endpoint)||(q!=h) ) q=null; /* force an error */
6866 @<Print control points between |p| and |q|, then |goto done1|@>;
6869 @<Print information for a curve that begins |open|@>;
6873 @<Print information for a curve that begins |curl| or |given|@>;
6876 mp_print(mp, "???"); /* can't happen */
6880 if ( left_type(q)<=explicit ) {
6881 mp_print(mp, "..control?"); /* can't happen */
6883 } else if ( (right_tension(p)!=unity)||(left_tension(q)!=unity) ) {
6884 @<Print tension between |p| and |q|@>;
6887 @ Since |n_sin_cos| produces |fraction| results, which we will print as if they
6888 were |scaled|, the magnitude of a |given| direction vector will be~4096.
6890 @<Print two dots...@>=
6892 mp_print_nl(mp, " ..");
6893 if ( left_type(p)==given ) {
6894 mp_n_sin_cos(mp, left_given(p)); mp_print_char(mp, '{');
6895 mp_print_scaled(mp, mp->n_cos); mp_print_char(mp, ',');
6896 mp_print_scaled(mp, mp->n_sin); mp_print_char(mp, '}');
6897 } else if ( left_type(p)==curl ){
6898 mp_print(mp, "{curl ");
6899 mp_print_scaled(mp, left_curl(p)); mp_print_char(mp, '}');
6903 @ @<Print tension between |p| and |q|@>=
6905 mp_print(mp, "..tension ");
6906 if ( right_tension(p)<0 ) mp_print(mp, "atleast");
6907 mp_print_scaled(mp, abs(right_tension(p)));
6908 if ( right_tension(p)!=left_tension(q) ){
6909 mp_print(mp, " and ");
6910 if ( left_tension(q)<0 ) mp_print(mp, "atleast");
6911 mp_print_scaled(mp, abs(left_tension(q)));
6915 @ @<Print control points between |p| and |q|, then |goto done1|@>=
6917 mp_print(mp, "..controls ");
6918 mp_print_two(mp, right_x(p),right_y(p));
6919 mp_print(mp, " and ");
6920 if ( left_type(q)!=explicit ) {
6921 mp_print(mp, "??"); /* can't happen */
6924 mp_print_two(mp, left_x(q),left_y(q));
6929 @ @<Print information for a curve that begins |open|@>=
6930 if ( (left_type(p)!=explicit)&&(left_type(p)!=open) ) {
6931 mp_print(mp, "{open?}"); /* can't happen */
6935 @ A curl of 1 is shown explicitly, so that the user sees clearly that
6936 \MP's default curl is present.
6938 The code here uses the fact that |left_curl==left_given| and
6939 |right_curl==right_given|.
6941 @<Print information for a curve that begins |curl|...@>=
6943 if ( left_type(p)==open )
6944 mp_print(mp, "??"); /* can't happen */
6946 if ( right_type(p)==curl ) {
6947 mp_print(mp, "{curl "); mp_print_scaled(mp, right_curl(p));
6949 mp_n_sin_cos(mp, right_given(p)); mp_print_char(mp, '{');
6950 mp_print_scaled(mp, mp->n_cos); mp_print_char(mp, ',');
6951 mp_print_scaled(mp, mp->n_sin);
6953 mp_print_char(mp, '}');
6956 @ It is convenient to have another version of |pr_path| that prints the path
6957 as a diagnostic message.
6959 @<Declare subroutines for printing expressions@>=
6960 void mp_print_path (MP mp,pointer h, char *s, boolean nuline) {
6961 mp_print_diagnostic(mp, "Path", s, nuline); mp_print_ln(mp);
6964 mp_end_diagnostic(mp, true);
6967 @ If we want to duplicate a knot node, we can say |copy_knot|:
6970 pointer mp_copy_knot (MP mp,pointer p) {
6971 pointer q; /* the copy */
6972 int k; /* runs through the words of a knot node */
6973 q=mp_get_node(mp, knot_node_size);
6974 for (k=0;k<=knot_node_size-1;k++) {
6975 mp->mem[q+k]=mp->mem[p+k];
6977 originator(q)=originator(p);
6981 @ The |copy_path| routine makes a clone of a given path.
6984 pointer mp_copy_path (MP mp, pointer p) {
6985 pointer q,pp,qq; /* for list manipulation */
6986 q=mp_copy_knot(mp, p);
6989 link(qq)=mp_copy_knot(mp, pp);
6997 @ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure
6998 returns a pointer to the first node of the copy, if the path is a cycle,
6999 but to the final node of a non-cyclic copy. The global
7000 variable |path_tail| will point to the final node of the original path;
7001 this trick makes it easier to implement `\&{doublepath}'.
7003 All node types are assumed to be |endpoint| or |explicit| only.
7006 pointer mp_htap_ypoc (MP mp,pointer p) {
7007 pointer q,pp,qq,rr; /* for list manipulation */
7008 q=mp_get_node(mp, knot_node_size); /* this will correspond to |p| */
7011 right_type(qq)=left_type(pp); left_type(qq)=right_type(pp);
7012 x_coord(qq)=x_coord(pp); y_coord(qq)=y_coord(pp);
7013 right_x(qq)=left_x(pp); right_y(qq)=left_y(pp);
7014 left_x(qq)=right_x(pp); left_y(qq)=right_y(pp);
7015 originator(qq)=originator(pp);
7016 if ( link(pp)==p ) {
7017 link(q)=qq; mp->path_tail=pp; return q;
7019 rr=mp_get_node(mp, knot_node_size); link(rr)=qq; qq=rr; pp=link(pp);
7024 pointer path_tail; /* the node that links to the beginning of a path */
7026 @ When a cyclic list of knot nodes is no longer needed, it can be recycled by
7027 calling the following subroutine.
7029 @<Declare the recycling subroutines@>=
7030 void mp_toss_knot_list (MP mp,pointer p) ;
7033 void mp_toss_knot_list (MP mp,pointer p) {
7034 pointer q; /* the node being freed */
7035 pointer r; /* the next node */
7039 mp_free_node(mp, q,knot_node_size); q=r;
7043 @* \[18] Choosing control points.
7044 Now we must actually delve into one of \MP's more difficult routines,
7045 the |make_choices| procedure that chooses angles and control points for
7046 the splines of a curve when the user has not specified them explicitly.
7047 The parameter to |make_choices| points to a list of knots and
7048 path information, as described above.
7050 A path decomposes into independent segments at ``breakpoint'' knots,
7051 which are knots whose left and right angles are both prespecified in
7052 some way (i.e., their |left_type| and |right_type| aren't both open).
7055 @<Declare the procedure called |solve_choices|@>;
7056 void mp_make_choices (MP mp,pointer knots) {
7057 pointer h; /* the first breakpoint */
7058 pointer p,q; /* consecutive breakpoints being processed */
7059 @<Other local variables for |make_choices|@>;
7060 check_arith; /* make sure that |arith_error=false| */
7061 if ( mp->internal[tracing_choices]>0 )
7062 mp_print_path(mp, knots,", before choices",true);
7063 @<If consecutive knots are equal, join them explicitly@>;
7064 @<Find the first breakpoint, |h|, on the path;
7065 insert an artificial breakpoint if the path is an unbroken cycle@>;
7068 @<Fill in the control points between |p| and the next breakpoint,
7069 then advance |p| to that breakpoint@>;
7071 if ( mp->internal[tracing_choices]>0 )
7072 mp_print_path(mp, knots,", after choices",true);
7073 if ( mp->arith_error ) {
7074 @<Report an unexpected problem during the choice-making@>;
7078 @ @<Report an unexpected problem during the choice...@>=
7080 print_err("Some number got too big");
7081 @.Some number got too big@>
7082 help2("The path that I just computed is out of range.")
7083 ("So it will probably look funny. Proceed, for a laugh.");
7084 mp_put_get_error(mp); mp->arith_error=false;
7087 @ Two knots in a row with the same coordinates will always be joined
7088 by an explicit ``curve'' whose control points are identical with the
7091 @<If consecutive knots are equal, join them explicitly@>=
7095 if ( x_coord(p)==x_coord(q) && y_coord(p)==y_coord(q) && right_type(p)>explicit ) {
7096 right_type(p)=explicit;
7097 if ( left_type(p)==open ) {
7098 left_type(p)=curl; left_curl(p)=unity;
7100 left_type(q)=explicit;
7101 if ( right_type(q)==open ) {
7102 right_type(q)=curl; right_curl(q)=unity;
7104 right_x(p)=x_coord(p); left_x(q)=x_coord(p);
7105 right_y(p)=y_coord(p); left_y(q)=y_coord(p);
7110 @ If there are no breakpoints, it is necessary to compute the direction
7111 angles around an entire cycle. In this case the |left_type| of the first
7112 node is temporarily changed to |end_cycle|.
7114 @d end_cycle (open+1)
7116 @<Find the first breakpoint, |h|, on the path...@>=
7119 if ( left_type(h)!=open ) break;
7120 if ( right_type(h)!=open ) break;
7123 left_type(h)=end_cycle; break;
7127 @ If |right_type(p)<given| and |q=link(p)|, we must have
7128 |right_type(p)=left_type(q)=explicit| or |endpoint|.
7130 @<Fill in the control points between |p| and the next breakpoint...@>=
7132 if ( right_type(p)>=given ) {
7133 while ( (left_type(q)==open)&&(right_type(q)==open) ) q=link(q);
7134 @<Fill in the control information between
7135 consecutive breakpoints |p| and |q|@>;
7136 } else if ( right_type(p)==endpoint ) {
7137 @<Give reasonable values for the unused control points between |p| and~|q|@>;
7141 @ This step makes it possible to transform an explicitly computed path without
7142 checking the |left_type| and |right_type| fields.
7144 @<Give reasonable values for the unused control points between |p| and~|q|@>=
7146 right_x(p)=x_coord(p); right_y(p)=y_coord(p);
7147 left_x(q)=x_coord(q); left_y(q)=y_coord(q);
7150 @ Before we can go further into the way choices are made, we need to
7151 consider the underlying theory. The basic ideas implemented in |make_choices|
7152 are due to John Hobby, who introduced the notion of ``mock curvature''
7153 @^Hobby, John Douglas@>
7154 at a knot. Angles are chosen so that they preserve mock curvature when
7155 a knot is passed, and this has been found to produce excellent results.
7157 It is convenient to introduce some notations that simplify the necessary
7158 formulas. Let $d_{k,k+1}=\vert z\k-z_k\vert$ be the (nonzero) distance
7159 between knots |k| and |k+1|; and let
7160 $${z\k-z_k\over z_k-z_{k-1}}={d_{k,k+1}\over d_{k-1,k}}e^{i\psi_k}$$
7161 so that a polygonal line from $z_{k-1}$ to $z_k$ to $z\k$ turns left
7162 through an angle of~$\psi_k$. We assume that $\vert\psi_k\vert\L180^\circ$.
7163 The control points for the spline from $z_k$ to $z\k$ will be denoted by
7164 $$\eqalign{z_k^+&=z_k+
7165 \textstyle{1\over3}\rho_k e^{i\theta_k}(z\k-z_k),\cr
7167 \textstyle{1\over3}\sigma\k e^{-i\phi\k}(z\k-z_k),\cr}$$
7168 where $\rho_k$ and $\sigma\k$ are nonnegative ``velocity ratios'' at the
7169 beginning and end of the curve, while $\theta_k$ and $\phi\k$ are the
7170 corresponding ``offset angles.'' These angles satisfy the condition
7171 $$\theta_k+\phi_k+\psi_k=0,\eqno(*)$$
7172 whenever the curve leaves an intermediate knot~|k| in the direction that
7175 @ Let $\alpha_k$ and $\beta\k$ be the reciprocals of the ``tension'' of
7176 the curve at its beginning and ending points. This means that
7177 $\rho_k=\alpha_k f(\theta_k,\phi\k)$ and $\sigma\k=\beta\k f(\phi\k,\theta_k)$,
7178 where $f(\theta,\phi)$ is \MP's standard velocity function defined in
7179 the |velocity| subroutine. The cubic spline $B(z_k^{\phantom+},z_k^+,
7180 z\k^-,z\k^{\phantom+};t)$
7183 $${2\sigma\k\sin(\theta_k+\phi\k)-6\sin\theta_k\over\rho_k^2d_{k,k+1}}
7184 \qquad{\rm and}\qquad
7185 {2\rho_k\sin(\theta_k+\phi\k)-6\sin\phi\k\over\sigma\k^2d_{k,k+1}}$$
7186 at |t=0| and |t=1|, respectively. The mock curvature is the linear
7188 approximation to this true curvature that arises in the limit for
7189 small $\theta_k$ and~$\phi\k$, if second-order terms are discarded.
7190 The standard velocity function satisfies
7191 $$f(\theta,\phi)=1+O(\theta^2+\theta\phi+\phi^2);$$
7192 hence the mock curvatures are respectively
7193 $${2\beta\k(\theta_k+\phi\k)-6\theta_k\over\alpha_k^2d_{k,k+1}}
7194 \qquad{\rm and}\qquad
7195 {2\alpha_k(\theta_k+\phi\k)-6\phi\k\over\beta\k^2d_{k,k+1}}.\eqno(**)$$
7197 @ The turning angles $\psi_k$ are given, and equation $(*)$ above
7198 determines $\phi_k$ when $\theta_k$ is known, so the task of
7199 angle selection is essentially to choose appropriate values for each
7200 $\theta_k$. When equation~$(*)$ is used to eliminate $\phi$~variables
7201 from $(**)$, we obtain a system of linear equations of the form
7202 $$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k,$$
7204 $$A_k={\alpha_{k-1}\over\beta_k^2d_{k-1,k}},
7205 \qquad B_k={3-\alpha_{k-1}\over\beta_k^2d_{k-1,k}},
7206 \qquad C_k={3-\beta\k\over\alpha_k^2d_{k,k+1}},
7207 \qquad D_k={\beta\k\over\alpha_k^2d_{k,k+1}}.$$
7208 The tensions are always $3\over4$ or more, hence each $\alpha$ and~$\beta$
7209 will be at most $4\over3$. It follows that $B_k\G{5\over4}A_k$ and
7210 $C_k\G{5\over4}D_k$; hence the equations are diagonally dominant;
7211 hence they have a unique solution. Moreover, in most cases the tensions
7212 are equal to~1, so that $B_k=2A_k$ and $C_k=2D_k$. This makes the
7213 solution numerically stable, and there is an exponential damping
7214 effect: The data at knot $k\pm j$ affects the angle at knot~$k$ by
7215 a factor of~$O(2^{-j})$.
7217 @ However, we still must consider the angles at the starting and ending
7218 knots of a non-cyclic path. These angles might be given explicitly, or
7219 they might be specified implicitly in terms of an amount of ``curl.''
7221 Let's assume that angles need to be determined for a non-cyclic path
7222 starting at $z_0$ and ending at~$z_n$. Then equations of the form
7223 $$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta_{k+1}=R_k$$
7224 have been given for $0<k<n$, and it will be convenient to introduce
7225 equations of the same form for $k=0$ and $k=n$, where
7226 $$A_0=B_0=C_n=D_n=0.$$
7227 If $\theta_0$ is supposed to have a given value $E_0$, we simply
7228 define $C_0=0$, $D_0=0$, and $R_0=E_0$. Otherwise a curl
7229 parameter, $\gamma_0$, has been specified at~$z_0$; this means
7230 that the mock curvature at $z_0$ should be $\gamma_0$ times the
7231 mock curvature at $z_1$; i.e.,
7232 $${2\beta_1(\theta_0+\phi_1)-6\theta_0\over\alpha_0^2d_{01}}
7233 =\gamma_0{2\alpha_0(\theta_0+\phi_1)-6\phi_1\over\beta_1^2d_{01}}.$$
7234 This equation simplifies to
7235 $$(\alpha_0\chi_0+3-\beta_1)\theta_0+
7236 \bigl((3-\alpha_0)\chi_0+\beta_1\bigr)\theta_1=
7237 -\bigl((3-\alpha_0)\chi_0+\beta_1\bigr)\psi_1,$$
7238 where $\chi_0=\alpha_0^2\gamma_0/\beta_1^2$; so we can set $C_0=
7239 \chi_0\alpha_0+3-\beta_1$, $D_0=(3-\alpha_0)\chi_0+\beta_1$, $R_0=-D_0\psi_1$.
7240 It can be shown that $C_0>0$ and $C_0B_1-A_1D_0>0$ when $\gamma_0\G0$,
7241 hence the linear equations remain nonsingular.
7243 Similar considerations apply at the right end, when the final angle $\phi_n$
7244 may or may not need to be determined. It is convenient to let $\psi_n=0$,
7245 hence $\theta_n=-\phi_n$. We either have an explicit equation $\theta_n=E_n$,
7247 $$\bigl((3-\beta_n)\chi_n+\alpha_{n-1}\bigr)\theta_{n-1}+
7248 (\beta_n\chi_n+3-\alpha_{n-1})\theta_n=0,\qquad
7249 \chi_n={\beta_n^2\gamma_n\over\alpha_{n-1}^2}.$$
7251 When |make_choices| chooses angles, it must compute the coefficients of
7252 these linear equations, then solve the equations. To compute the coefficients,
7253 it is necessary to compute arctangents of the given turning angles~$\psi_k$.
7254 When the equations are solved, the chosen directions $\theta_k$ are put
7255 back into the form of control points by essentially computing sines and
7258 @ OK, we are ready to make the hard choices of |make_choices|.
7259 Most of the work is relegated to an auxiliary procedure
7260 called |solve_choices|, which has been introduced to keep
7261 |make_choices| from being extremely long.
7263 @<Fill in the control information between...@>=
7264 @<Calculate the turning angles $\psi_k$ and the distances $d_{k,k+1}$;
7265 set $n$ to the length of the path@>;
7266 @<Remove |open| types at the breakpoints@>;
7267 mp_solve_choices(mp, p,q,n)
7269 @ It's convenient to precompute quantities that will be needed several
7270 times later. The values of |delta_x[k]| and |delta_y[k]| will be the
7271 coordinates of $z\k-z_k$, and the magnitude of this vector will be
7272 |delta[k]=@t$d_{k,k+1}$@>|. The path angle $\psi_k$ between $z_k-z_{k-1}$
7273 and $z\k-z_k$ will be stored in |psi[k]|.
7276 int path_size; /* maximum number of knots between breakpoints of a path */
7279 scaled *delta; /* knot differences */
7280 angle *psi; /* turning angles */
7282 @ @<Allocate or initialize ...@>=
7288 @ @<Dealloc variables@>=
7294 @ @<Other local variables for |make_choices|@>=
7295 int k,n; /* current and final knot numbers */
7296 pointer s,t; /* registers for list traversal */
7297 scaled delx,dely; /* directions where |open| meets |explicit| */
7298 fraction sine,cosine; /* trig functions of various angles */
7300 @ @<Calculate the turning angles...@>=
7303 k=0; s=p; n=mp->path_size;
7306 mp->delta_x[k]=x_coord(t)-x_coord(s);
7307 mp->delta_y[k]=y_coord(t)-y_coord(s);
7308 mp->delta[k]=mp_pyth_add(mp, mp->delta_x[k],mp->delta_y[k]);
7310 sine=mp_make_fraction(mp, mp->delta_y[k-1],mp->delta[k-1]);
7311 cosine=mp_make_fraction(mp, mp->delta_x[k-1],mp->delta[k-1]);
7312 mp->psi[k]=mp_n_arg(mp, mp_take_fraction(mp, mp->delta_x[k],cosine)+
7313 mp_take_fraction(mp, mp->delta_y[k],sine),
7314 mp_take_fraction(mp, mp->delta_y[k],cosine)-
7315 mp_take_fraction(mp, mp->delta_x[k],sine));
7318 if ( k==mp->path_size ) {
7319 mp_reallocate_paths(mp, mp->path_size+(mp->path_size>>2));
7320 goto RESTART; /* retry, loop size has changed */
7323 } while (! (k>=n)&&(left_type(s)!=end_cycle));
7324 if ( k==n ) mp->psi[n]=0; else mp->psi[k]=mp->psi[1];
7327 @ When we get to this point of the code, |right_type(p)| is either
7328 |given| or |curl| or |open|. If it is |open|, we must have
7329 |left_type(p)=end_cycle| or |left_type(p)=explicit|. In the latter
7330 case, the |open| type is converted to |given|; however, if the
7331 velocity coming into this knot is zero, the |open| type is
7332 converted to a |curl|, since we don't know the incoming direction.
7334 Similarly, |left_type(q)| is either |given| or |curl| or |open| or
7335 |end_cycle|. The |open| possibility is reduced either to |given| or to |curl|.
7337 @<Remove |open| types at the breakpoints@>=
7338 if ( left_type(q)==open ) {
7339 delx=right_x(q)-x_coord(q); dely=right_y(q)-y_coord(q);
7340 if ( (delx==0)&&(dely==0) ) {
7341 left_type(q)=curl; left_curl(q)=unity;
7343 left_type(q)=given; left_given(q)=mp_n_arg(mp, delx,dely);
7346 if ( (right_type(p)==open)&&(left_type(p)==explicit) ) {
7347 delx=x_coord(p)-left_x(p); dely=y_coord(p)-left_y(p);
7348 if ( (delx==0)&&(dely==0) ) {
7349 right_type(p)=curl; right_curl(p)=unity;
7351 right_type(p)=given; right_given(p)=mp_n_arg(mp, delx,dely);
7355 @ Linear equations need to be solved whenever |n>1|; and also when |n=1|
7356 and exactly one of the breakpoints involves a curl. The simplest case occurs
7357 when |n=1| and there is a curl at both breakpoints; then we simply draw
7360 But before coding up the simple cases, we might as well face the general case,
7361 since we must deal with it sooner or later, and since the general case
7362 is likely to give some insight into the way simple cases can be handled best.
7364 When there is no cycle, the linear equations to be solved form a tridiagonal
7365 system, and we can apply the standard technique of Gaussian elimination
7366 to convert that system to a sequence of equations of the form
7367 $$\theta_0+u_0\theta_1=v_0,\quad
7368 \theta_1+u_1\theta_2=v_1,\quad\ldots,\quad
7369 \theta_{n-1}+u_{n-1}\theta_n=v_{n-1},\quad
7371 It is possible to do this diagonalization while generating the equations.
7372 Once $\theta_n$ is known, it is easy to determine $\theta_{n-1}$, \dots,
7373 $\theta_1$, $\theta_0$; thus, the equations will be solved.
7375 The procedure is slightly more complex when there is a cycle, but the
7376 basic idea will be nearly the same. In the cyclic case the right-hand
7377 sides will be $v_k+w_k\theta_0$ instead of simply $v_k$, and we will start
7378 the process off with $u_0=v_0=0$, $w_0=1$. The final equation will be not
7379 $\theta_n=v_n$ but $\theta_n+u_n\theta_1=v_n+w_n\theta_0$; an appropriate
7380 ending routine will take account of the fact that $\theta_n=\theta_0$ and
7381 eliminate the $w$'s from the system, after which the solution can be
7384 When $u_k$, $v_k$, and $w_k$ are being computed, the three pointer
7385 variables |r|, |s|,~|t| will point respectively to knots |k-1|, |k|,
7386 and~|k+1|. The $u$'s and $w$'s are scaled by $2^{28}$, i.e., they are
7387 of type |fraction|; the $\theta$'s and $v$'s are of type |angle|.
7390 angle *theta; /* values of $\theta_k$ */
7391 fraction *uu; /* values of $u_k$ */
7392 angle *vv; /* values of $v_k$ */
7393 fraction *ww; /* values of $w_k$ */
7395 @ @<Allocate or initialize ...@>=
7401 @ @<Dealloc variables@>=
7407 @ @<Declare |mp_reallocate| functions@>=
7408 void mp_reallocate_paths (MP mp, int l);
7411 void mp_reallocate_paths (MP mp, int l) {
7412 XREALLOC (mp->delta_x, l, scaled);
7413 XREALLOC (mp->delta_y, l, scaled);
7414 XREALLOC (mp->delta, l, scaled);
7415 XREALLOC (mp->psi, l, angle);
7416 XREALLOC (mp->theta, l, angle);
7417 XREALLOC (mp->uu, l, fraction);
7418 XREALLOC (mp->vv, l, angle);
7419 XREALLOC (mp->ww, l, fraction);
7423 @ Our immediate problem is to get the ball rolling by setting up the
7424 first equation or by realizing that no equations are needed, and to fit
7425 this initialization into a framework suitable for the overall computation.
7427 @<Declare the procedure called |solve_choices|@>=
7428 @<Declare subroutines needed by |solve_choices|@>;
7429 void mp_solve_choices (MP mp,pointer p, pointer q, halfword n) {
7430 int k; /* current knot number */
7431 pointer r,s,t; /* registers for list traversal */
7432 @<Other local variables for |solve_choices|@>;
7437 @<Get the linear equations started; or |return|
7438 with the control points in place, if linear equations
7441 switch (left_type(s)) {
7442 case end_cycle: case open:
7443 @<Set up equation to match mock curvatures
7444 at $z_k$; then |goto found| with $\theta_n$
7445 adjusted to equal $\theta_0$, if a cycle has ended@>;
7448 @<Set up equation for a curl at $\theta_n$
7452 @<Calculate the given value of $\theta_n$
7455 } /* there are no other cases */
7460 @<Finish choosing angles and assigning control points@>;
7463 @ On the first time through the loop, we have |k=0| and |r| is not yet
7464 defined. The first linear equation, if any, will have $A_0=B_0=0$.
7466 @<Get the linear equations started...@>=
7467 switch (right_type(s)) {
7469 if ( left_type(t)==given ) {
7470 @<Reduce to simple case of two givens and |return|@>
7472 @<Set up the equation for a given value of $\theta_0$@>;
7476 if ( left_type(t)==curl ) {
7477 @<Reduce to simple case of straight line and |return|@>
7479 @<Set up the equation for a curl at $\theta_0$@>;
7483 mp->uu[0]=0; mp->vv[0]=0; mp->ww[0]=fraction_one;
7484 /* this begins a cycle */
7486 } /* there are no other cases */
7488 @ The general equation that specifies equality of mock curvature at $z_k$ is
7489 $$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k,$$
7490 as derived above. We want to combine this with the already-derived equation
7491 $\theta_{k-1}+u_{k-1}\theta_k=v_{k-1}+w_{k-1}\theta_0$ in order to obtain
7493 $\theta_k+u_k\theta\k=v_k+w_k\theta_0$. This can be done by dividing the
7495 $$(B_k-u_{k-1}A_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k-A_kv_{k-1}
7496 -A_kw_{k-1}\theta_0$$
7497 by $B_k-u_{k-1}A_k+C_k$. The trick is to do this carefully with
7498 fixed-point arithmetic, avoiding the chance of overflow while retaining
7501 The calculations will be performed in several registers that
7502 provide temporary storage for intermediate quantities.
7504 @<Other local variables for |solve_choices|@>=
7505 fraction aa,bb,cc,ff,acc; /* temporary registers */
7506 scaled dd,ee; /* likewise, but |scaled| */
7507 scaled lt,rt; /* tension values */
7509 @ @<Set up equation to match mock curvatures...@>=
7510 { @<Calculate the values $\\{aa}=A_k/B_k$, $\\{bb}=D_k/C_k$,
7511 $\\{dd}=(3-\alpha_{k-1})d_{k,k+1}$, $\\{ee}=(3-\beta\k)d_{k-1,k}$,
7512 and $\\{cc}=(B_k-u_{k-1}A_k)/B_k$@>;
7513 @<Calculate the ratio $\\{ff}=C_k/(C_k+B_k-u_{k-1}A_k)$@>;
7514 mp->uu[k]=mp_take_fraction(mp, ff,bb);
7515 @<Calculate the values of $v_k$ and $w_k$@>;
7516 if ( left_type(s)==end_cycle ) {
7517 @<Adjust $\theta_n$ to equal $\theta_0$ and |goto found|@>;
7521 @ Since tension values are never less than 3/4, the values |aa| and
7522 |bb| computed here are never more than 4/5.
7524 @<Calculate the values $\\{aa}=...@>=
7525 if ( abs(right_tension(r))==unity) {
7526 aa=fraction_half; dd=2*mp->delta[k];
7528 aa=mp_make_fraction(mp, unity,3*abs(right_tension(r))-unity);
7529 dd=mp_take_fraction(mp, mp->delta[k],
7530 fraction_three-mp_make_fraction(mp, unity,abs(right_tension(r))));
7532 if ( abs(left_tension(t))==unity ){
7533 bb=fraction_half; ee=2*mp->delta[k-1];
7535 bb=mp_make_fraction(mp, unity,3*abs(left_tension(t))-unity);
7536 ee=mp_take_fraction(mp, mp->delta[k-1],
7537 fraction_three-mp_make_fraction(mp, unity,abs(left_tension(t))));
7539 cc=fraction_one-mp_take_fraction(mp, mp->uu[k-1],aa)
7541 @ The ratio to be calculated in this step can be written in the form
7542 $$\beta_k^2\cdot\\{ee}\over\beta_k^2\cdot\\{ee}+\alpha_k^2\cdot
7543 \\{cc}\cdot\\{dd},$$
7544 because of the quantities just calculated. The values of |dd| and |ee|
7545 will not be needed after this step has been performed.
7547 @<Calculate the ratio $\\{ff}=C_k/(C_k+B_k-u_{k-1}A_k)$@>=
7548 dd=mp_take_fraction(mp, dd,cc); lt=abs(left_tension(s)); rt=abs(right_tension(s));
7549 if ( lt!=rt ) { /* $\beta_k^{-1}\ne\alpha_k^{-1}$ */
7551 ff=mp_make_fraction(mp, lt,rt);
7552 ff=mp_take_fraction(mp, ff,ff); /* $\alpha_k^2/\beta_k^2$ */
7553 dd=mp_take_fraction(mp, dd,ff);
7555 ff=mp_make_fraction(mp, rt,lt);
7556 ff=mp_take_fraction(mp, ff,ff); /* $\beta_k^2/\alpha_k^2$ */
7557 ee=mp_take_fraction(mp, ee,ff);
7560 ff=mp_make_fraction(mp, ee,ee+dd)
7562 @ The value of $u_{k-1}$ will be |<=1| except when $k=1$ and the previous
7563 equation was specified by a curl. In that case we must use a special
7564 method of computation to prevent overflow.
7566 Fortunately, the calculations turn out to be even simpler in this ``hard''
7567 case. The curl equation makes $w_0=0$ and $v_0=-u_0\psi_1$, hence
7568 $-B_1\psi_1-A_1v_0=-(B_1-u_0A_1)\psi_1=-\\{cc}\cdot B_1\psi_1$.
7570 @<Calculate the values of $v_k$ and $w_k$@>=
7571 acc=-mp_take_fraction(mp, mp->psi[k+1],mp->uu[k]);
7572 if ( right_type(r)==curl ) {
7574 mp->vv[k]=acc-mp_take_fraction(mp, mp->psi[1],fraction_one-ff);
7576 ff=mp_make_fraction(mp, fraction_one-ff,cc); /* this is
7577 $B_k/(C_k+B_k-u_{k-1}A_k)<5$ */
7578 acc=acc-mp_take_fraction(mp, mp->psi[k],ff);
7579 ff=mp_take_fraction(mp, ff,aa); /* this is $A_k/(C_k+B_k-u_{k-1}A_k)$ */
7580 mp->vv[k]=acc-mp_take_fraction(mp, mp->vv[k-1],ff);
7581 if ( mp->ww[k-1]==0 ) mp->ww[k]=0;
7582 else mp->ww[k]=-mp_take_fraction(mp, mp->ww[k-1],ff);
7585 @ When a complete cycle has been traversed, we have $\theta_k+u_k\theta\k=
7586 v_k+w_k\theta_0$, for |1<=k<=n|. We would like to determine the value of
7587 $\theta_n$ and reduce the system to the form $\theta_k+u_k\theta\k=v_k$
7588 for |0<=k<n|, so that the cyclic case can be finished up just as if there
7591 The idea in the following code is to observe that
7592 $$\eqalign{\theta_n&=v_n+w_n\theta_0-u_n\theta_1=\cdots\cr
7593 &=v_n+w_n\theta_0-u_n\bigl(v_1+w_1\theta_0-u_1(v_2+\cdots
7594 -u_{n-2}(v_{n-1}+w_{n-1}\theta_0-u_{n-1}\theta_0))\bigr),\cr}$$
7595 so we can solve for $\theta_n=\theta_0$.
7597 @<Adjust $\theta_n$ to equal $\theta_0$ and |goto found|@>=
7599 aa=0; bb=fraction_one; /* we have |k=n| */
7602 aa=mp->vv[k]-mp_take_fraction(mp, aa,mp->uu[k]);
7603 bb=mp->ww[k]-mp_take_fraction(mp, bb,mp->uu[k]);
7604 } while (k!=n); /* now $\theta_n=\\{aa}+\\{bb}\cdot\theta_n$ */
7605 aa=mp_make_fraction(mp, aa,fraction_one-bb);
7606 mp->theta[n]=aa; mp->vv[0]=aa;
7607 for (k=1;k<=n-1;k++) {
7608 mp->vv[k]=mp->vv[k]+mp_take_fraction(mp, aa,mp->ww[k]);
7613 @ @d reduce_angle(A) if ( abs((A))>one_eighty_deg ) {
7614 if ( (A)>0 ) (A)=(A)-three_sixty_deg; else (A)=(A)+three_sixty_deg; }
7616 @<Calculate the given value of $\theta_n$...@>=
7618 mp->theta[n]=left_given(s)-mp_n_arg(mp, mp->delta_x[n-1],mp->delta_y[n-1]);
7619 reduce_angle(mp->theta[n]);
7623 @ @<Set up the equation for a given value of $\theta_0$@>=
7625 mp->vv[0]=right_given(s)-mp_n_arg(mp, mp->delta_x[0],mp->delta_y[0]);
7626 reduce_angle(mp->vv[0]);
7627 mp->uu[0]=0; mp->ww[0]=0;
7630 @ @<Set up the equation for a curl at $\theta_0$@>=
7631 { cc=right_curl(s); lt=abs(left_tension(t)); rt=abs(right_tension(s));
7632 if ( (rt==unity)&&(lt==unity) )
7633 mp->uu[0]=mp_make_fraction(mp, cc+cc+unity,cc+two);
7635 mp->uu[0]=mp_curl_ratio(mp, cc,rt,lt);
7636 mp->vv[0]=-mp_take_fraction(mp, mp->psi[1],mp->uu[0]); mp->ww[0]=0;
7639 @ @<Set up equation for a curl at $\theta_n$...@>=
7640 { cc=left_curl(s); lt=abs(left_tension(s)); rt=abs(right_tension(r));
7641 if ( (rt==unity)&&(lt==unity) )
7642 ff=mp_make_fraction(mp, cc+cc+unity,cc+two);
7644 ff=mp_curl_ratio(mp, cc,lt,rt);
7645 mp->theta[n]=-mp_make_fraction(mp, mp_take_fraction(mp, mp->vv[n-1],ff),
7646 fraction_one-mp_take_fraction(mp, ff,mp->uu[n-1]));
7650 @ The |curl_ratio| subroutine has three arguments, which our previous notation
7651 encourages us to call $\gamma$, $\alpha^{-1}$, and $\beta^{-1}$. It is
7652 a somewhat tedious program to calculate
7653 $${(3-\alpha)\alpha^2\gamma+\beta^3\over
7654 \alpha^3\gamma+(3-\beta)\beta^2},$$
7655 with the result reduced to 4 if it exceeds 4. (This reduction of curl
7656 is necessary only if the curl and tension are both large.)
7657 The values of $\alpha$ and $\beta$ will be at most~4/3.
7659 @<Declare subroutines needed by |solve_choices|@>=
7660 fraction mp_curl_ratio (MP mp,scaled gamma, scaled a_tension,
7662 fraction alpha,beta,num,denom,ff; /* registers */
7663 alpha=mp_make_fraction(mp, unity,a_tension);
7664 beta=mp_make_fraction(mp, unity,b_tension);
7665 if ( alpha<=beta ) {
7666 ff=mp_make_fraction(mp, alpha,beta); ff=mp_take_fraction(mp, ff,ff);
7667 gamma=mp_take_fraction(mp, gamma,ff);
7668 beta=beta / 010000; /* convert |fraction| to |scaled| */
7669 denom=mp_take_fraction(mp, gamma,alpha)+three-beta;
7670 num=mp_take_fraction(mp, gamma,fraction_three-alpha)+beta;
7672 ff=mp_make_fraction(mp, beta,alpha); ff=mp_take_fraction(mp, ff,ff);
7673 beta=mp_take_fraction(mp, beta,ff) / 010000; /* convert |fraction| to |scaled| */
7674 denom=mp_take_fraction(mp, gamma,alpha)+(ff / 1365)-beta;
7675 /* $1365\approx 2^{12}/3$ */
7676 num=mp_take_fraction(mp, gamma,fraction_three-alpha)+beta;
7678 if ( num>=denom+denom+denom+denom ) return fraction_four;
7679 else return mp_make_fraction(mp, num,denom);
7682 @ We're in the home stretch now.
7684 @<Finish choosing angles and assigning control points@>=
7685 for (k=n-1;k>=0;k--) {
7686 mp->theta[k]=mp->vv[k]-mp_take_fraction(mp,mp->theta[k+1],mp->uu[k]);
7691 mp_n_sin_cos(mp, mp->theta[k]); mp->st=mp->n_sin; mp->ct=mp->n_cos;
7692 mp_n_sin_cos(mp, -mp->psi[k+1]-mp->theta[k+1]); mp->sf=mp->n_sin; mp->cf=mp->n_cos;
7693 mp_set_controls(mp, s,t,k);
7697 @ The |set_controls| routine actually puts the control points into
7698 a pair of consecutive nodes |p| and~|q|. Global variables are used to
7699 record the values of $\sin\theta$, $\cos\theta$, $\sin\phi$, and
7700 $\cos\phi$ needed in this calculation.
7706 fraction cf; /* sines and cosines */
7708 @ @<Declare subroutines needed by |solve_choices|@>=
7709 void mp_set_controls (MP mp,pointer p, pointer q, integer k) {
7710 fraction rr,ss; /* velocities, divided by thrice the tension */
7711 scaled lt,rt; /* tensions */
7712 fraction sine; /* $\sin(\theta+\phi)$ */
7713 lt=abs(left_tension(q)); rt=abs(right_tension(p));
7714 rr=mp_velocity(mp, mp->st,mp->ct,mp->sf,mp->cf,rt);
7715 ss=mp_velocity(mp, mp->sf,mp->cf,mp->st,mp->ct,lt);
7716 if ( (right_tension(p)<0)||(left_tension(q)<0) ) {
7717 @<Decrease the velocities,
7718 if necessary, to stay inside the bounding triangle@>;
7720 right_x(p)=x_coord(p)+mp_take_fraction(mp,
7721 mp_take_fraction(mp, mp->delta_x[k],mp->ct)-
7722 mp_take_fraction(mp, mp->delta_y[k],mp->st),rr);
7723 right_y(p)=y_coord(p)+mp_take_fraction(mp,
7724 mp_take_fraction(mp, mp->delta_y[k],mp->ct)+
7725 mp_take_fraction(mp, mp->delta_x[k],mp->st),rr);
7726 left_x(q)=x_coord(q)-mp_take_fraction(mp,
7727 mp_take_fraction(mp, mp->delta_x[k],mp->cf)+
7728 mp_take_fraction(mp, mp->delta_y[k],mp->sf),ss);
7729 left_y(q)=y_coord(q)-mp_take_fraction(mp,
7730 mp_take_fraction(mp, mp->delta_y[k],mp->cf)-
7731 mp_take_fraction(mp, mp->delta_x[k],mp->sf),ss);
7732 right_type(p)=explicit; left_type(q)=explicit;
7735 @ The boundedness conditions $\\{rr}\L\sin\phi\,/\sin(\theta+\phi)$ and
7736 $\\{ss}\L\sin\theta\,/\sin(\theta+\phi)$ are to be enforced if $\sin\theta$,
7737 $\sin\phi$, and $\sin(\theta+\phi)$ all have the same sign. Otherwise
7738 there is no ``bounding triangle.''
7739 @:at_least_}{\&{atleast} primitive@>
7741 @<Decrease the velocities, if necessary...@>=
7742 if (((mp->st>=0)&&(mp->sf>=0))||((mp->st<=0)&&(mp->sf<=0)) ) {
7743 sine=mp_take_fraction(mp, abs(mp->st),mp->cf)+
7744 mp_take_fraction(mp, abs(mp->sf),mp->ct);
7746 sine=mp_take_fraction(mp, sine,fraction_one+unity); /* safety factor */
7747 if ( right_tension(p)<0 )
7748 if ( mp_ab_vs_cd(mp, abs(mp->sf),fraction_one,rr,sine)<0 )
7749 rr=mp_make_fraction(mp, abs(mp->sf),sine);
7750 if ( left_tension(q)<0 )
7751 if ( mp_ab_vs_cd(mp, abs(mp->st),fraction_one,ss,sine)<0 )
7752 ss=mp_make_fraction(mp, abs(mp->st),sine);
7756 @ Only the simple cases remain to be handled.
7758 @<Reduce to simple case of two givens and |return|@>=
7760 aa=mp_n_arg(mp, mp->delta_x[0],mp->delta_y[0]);
7761 mp_n_sin_cos(mp, right_given(p)-aa); mp->ct=mp->n_cos; mp->st=mp->n_sin;
7762 mp_n_sin_cos(mp, left_given(q)-aa); mp->cf=mp->n_cos; mp->sf=-mp->n_sin;
7763 mp_set_controls(mp, p,q,0); return;
7766 @ @<Reduce to simple case of straight line and |return|@>=
7768 right_type(p)=explicit; left_type(q)=explicit;
7769 lt=abs(left_tension(q)); rt=abs(right_tension(p));
7771 if ( mp->delta_x[0]>=0 ) right_x(p)=x_coord(p)+((mp->delta_x[0]+1) / 3);
7772 else right_x(p)=x_coord(p)+((mp->delta_x[0]-1) / 3);
7773 if ( mp->delta_y[0]>=0 ) right_y(p)=y_coord(p)+((mp->delta_y[0]+1) / 3);
7774 else right_y(p)=y_coord(p)+((mp->delta_y[0]-1) / 3);
7776 ff=mp_make_fraction(mp, unity,3*rt); /* $\alpha/3$ */
7777 right_x(p)=x_coord(p)+mp_take_fraction(mp, mp->delta_x[0],ff);
7778 right_y(p)=y_coord(p)+mp_take_fraction(mp, mp->delta_y[0],ff);
7781 if ( mp->delta_x[0]>=0 ) left_x(q)=x_coord(q)-((mp->delta_x[0]+1) / 3);
7782 else left_x(q)=x_coord(q)-((mp->delta_x[0]-1) / 3);
7783 if ( mp->delta_y[0]>=0 ) left_y(q)=y_coord(q)-((mp->delta_y[0]+1) / 3);
7784 else left_y(q)=y_coord(q)-((mp->delta_y[0]-1) / 3);
7786 ff=mp_make_fraction(mp, unity,3*lt); /* $\beta/3$ */
7787 left_x(q)=x_coord(q)-mp_take_fraction(mp, mp->delta_x[0],ff);
7788 left_y(q)=y_coord(q)-mp_take_fraction(mp, mp->delta_y[0],ff);
7793 @* \[19] Measuring paths.
7794 \MP's \&{llcorner}, \&{lrcorner}, \&{ulcorner}, and \&{urcorner} operators
7795 allow the user to measure the bounding box of anything that can go into a
7796 picture. It's easy to get rough bounds on the $x$ and $y$ extent of a path
7797 by just finding the bounding box of the knots and the control points. We
7798 need a more accurate version of the bounding box, but we can still use the
7799 easy estimate to save time by focusing on the interesting parts of the path.
7801 @ Computing an accurate bounding box involves a theme that will come up again
7802 and again. Given a Bernshte{\u\i}n polynomial
7803 @^Bernshte{\u\i}n, Serge{\u\i} Natanovich@>
7804 $$B(z_0,z_1,\ldots,z_n;t)=\sum_k{n\choose k}t^k(1-t)^{n-k}z_k,$$
7805 we can conveniently bisect its range as follows:
7808 \textindent{1)} Let $z_k^{(0)}=z_k$, for |0<=k<=n|.
7811 \textindent{2)} Let $z_k^{(j+1)}={1\over2}(z_k^{(j)}+z\k^{(j)})$, for
7812 |0<=k<n-j|, for |0<=j<n|.
7816 $$B(z_0,z_1,\ldots,z_n;t)=B(z_0^{(0)},z_0^{(1)},\ldots,z_0^{(n)};2t)
7817 =B(z_0^{(n)},z_1^{(n-1)},\ldots,z_n^{(0)};2t-1).$$
7818 This formula gives us the coefficients of polynomials to use over the ranges
7819 $0\L t\L{1\over2}$ and ${1\over2}\L t\L1$.
7821 @ Now here's a subroutine that's handy for all sorts of path computations:
7822 Given a quadratic polynomial $B(a,b,c;t)$, the |crossing_point| function
7823 returns the unique |fraction| value |t| between 0 and~1 at which
7824 $B(a,b,c;t)$ changes from positive to negative, or returns
7825 |t=fraction_one+1| if no such value exists. If |a<0| (so that $B(a,b,c;t)$
7826 is already negative at |t=0|), |crossing_point| returns the value zero.
7828 @d no_crossing { return (fraction_one+1); }
7829 @d one_crossing { return fraction_one; }
7830 @d zero_crossing { return 0; }
7831 @d mp_crossing_point(M,A,B,C) mp_do_crossing_point(A,B,C)
7833 @c fraction mp_do_crossing_point (integer a, integer b, integer c) {
7834 integer d; /* recursive counter */
7835 integer x,xx,x0,x1,x2; /* temporary registers for bisection */
7836 if ( a<0 ) zero_crossing;
7839 if ( c>0 ) { no_crossing; }
7840 else if ( (a==0)&&(b==0) ) { no_crossing;}
7841 else { one_crossing; }
7843 if ( a==0 ) zero_crossing;
7844 } else if ( a==0 ) {
7845 if ( b<=0 ) zero_crossing;
7847 @<Use bisection to find the crossing point, if one exists@>;
7850 @ The general bisection method is quite simple when $n=2$, hence
7851 |crossing_point| does not take much time. At each stage in the
7852 recursion we have a subinterval defined by |l| and~|j| such that
7853 $B(a,b,c;2^{-l}(j+t))=B(x_0,x_1,x_2;t)$, and we want to ``zero in'' on
7854 the subinterval where $x_0\G0$ and $\min(x_1,x_2)<0$.
7856 It is convenient for purposes of calculation to combine the values
7857 of |l| and~|j| in a single variable $d=2^l+j$, because the operation
7858 of bisection then corresponds simply to doubling $d$ and possibly
7859 adding~1. Furthermore it proves to be convenient to modify
7860 our previous conventions for bisection slightly, maintaining the
7861 variables $X_0=2^lx_0$, $X_1=2^l(x_0-x_1)$, and $X_2=2^l(x_1-x_2)$.
7862 With these variables the conditions $x_0\ge0$ and $\min(x_1,x_2)<0$ are
7863 equivalent to $\max(X_1,X_1+X_2)>X_0\ge0$.
7865 The following code maintains the invariant relations
7866 $0\L|x0|<\max(|x1|,|x1|+|x2|)$,
7867 $\vert|x1|\vert<2^{30}$, $\vert|x2|\vert<2^{30}$;
7868 it has been constructed in such a way that no arithmetic overflow
7869 will occur if the inputs satisfy
7870 $a<2^{30}$, $\vert a-b\vert<2^{30}$, and $\vert b-c\vert<2^{30}$.
7872 @<Use bisection to find the crossing point...@>=
7873 d=1; x0=a; x1=a-b; x2=b-c;
7884 if ( x<=x0 ) { if ( x+x2<=x0 ) no_crossing; }
7888 } while (d<fraction_one);
7889 return (d-fraction_one)
7891 @ Here is a routine that computes the $x$ or $y$ coordinate of the point on
7892 a cubic corresponding to the |fraction| value~|t|.
7894 It is convenient to define a \.{WEB} macro |t_of_the_way| such that
7895 |t_of_the_way(a,b)| expands to |a-(a-b)*t|, i.e., to |t[a,b]|.
7897 @d t_of_the_way(A,B) ((A)-mp_take_fraction(mp,(A)-(B),t))
7899 @c scaled mp_eval_cubic (MP mp,pointer p, pointer q, fraction t) {
7900 scaled x1,x2,x3; /* intermediate values */
7901 x1=t_of_the_way(knot_coord(p),right_coord(p));
7902 x2=t_of_the_way(right_coord(p),left_coord(q));
7903 x3=t_of_the_way(left_coord(q),knot_coord(q));
7904 x1=t_of_the_way(x1,x2);
7905 x2=t_of_the_way(x2,x3);
7906 return t_of_the_way(x1,x2);
7909 @ The actual bounding box information is stored in global variables.
7910 Since it is convenient to address the $x$ and $y$ information
7911 separately, we define arrays indexed by |x_code..y_code| and use
7912 macros to give them more convenient names.
7916 mp_x_code=0, /* index for |minx| and |maxx| */
7917 mp_y_code /* index for |miny| and |maxy| */
7921 @d minx mp->bbmin[mp_x_code]
7922 @d maxx mp->bbmax[mp_x_code]
7923 @d miny mp->bbmin[mp_y_code]
7924 @d maxy mp->bbmax[mp_y_code]
7927 scaled bbmin[mp_y_code+1];
7928 scaled bbmax[mp_y_code+1];
7929 /* the result of procedures that compute bounding box information */
7931 @ Now we're ready for the key part of the bounding box computation.
7932 The |bound_cubic| procedure updates |bbmin[c]| and |bbmax[c]| based on
7933 $$B(\hbox{|knot_coord(p)|}, \hbox{|right_coord(p)|},
7934 \hbox{|left_coord(q)|}, \hbox{|knot_coord(q)|};t)
7936 for $0<t\le1$. In other words, the procedure adjusts the bounds to
7937 accommodate |knot_coord(q)| and any extremes over the range $0<t<1$.
7938 The |c| parameter is |x_code| or |y_code|.
7940 @c void mp_bound_cubic (MP mp,pointer p, pointer q, small_number c) {
7941 boolean wavy; /* whether we need to look for extremes */
7942 scaled del1,del2,del3,del,dmax; /* proportional to the control
7943 points of a quadratic derived from a cubic */
7944 fraction t,tt; /* where a quadratic crosses zero */
7945 scaled x; /* a value that |bbmin[c]| and |bbmax[c]| must accommodate */
7947 @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>;
7948 @<Check the control points against the bounding box and set |wavy:=true|
7949 if any of them lie outside@>;
7951 del1=right_coord(p)-knot_coord(p);
7952 del2=left_coord(q)-right_coord(p);
7953 del3=knot_coord(q)-left_coord(q);
7954 @<Scale up |del1|, |del2|, and |del3| for greater accuracy;
7955 also set |del| to the first nonzero element of |(del1,del2,del3)|@>;
7957 negate(del1); negate(del2); negate(del3);
7959 t=mp_crossing_point(mp, del1,del2,del3);
7960 if ( t<fraction_one ) {
7961 @<Test the extremes of the cubic against the bounding box@>;
7966 @ @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>=
7967 if ( x<mp->bbmin[c] ) mp->bbmin[c]=x;
7968 if ( x>mp->bbmax[c] ) mp->bbmax[c]=x
7970 @ @<Check the control points against the bounding box and set...@>=
7972 if ( mp->bbmin[c]<=right_coord(p) )
7973 if ( right_coord(p)<=mp->bbmax[c] )
7974 if ( mp->bbmin[c]<=left_coord(q) )
7975 if ( left_coord(q)<=mp->bbmax[c] )
7978 @ If |del1=del2=del3=0|, it's impossible to obey the title of this
7979 section. We just set |del=0| in that case.
7981 @<Scale up |del1|, |del2|, and |del3| for greater accuracy...@>=
7982 if ( del1!=0 ) del=del1;
7983 else if ( del2!=0 ) del=del2;
7987 if ( abs(del2)>dmax ) dmax=abs(del2);
7988 if ( abs(del3)>dmax ) dmax=abs(del3);
7989 while ( dmax<fraction_half ) {
7990 dmax+=dmax; del1+=del1; del2+=del2; del3+=del3;
7994 @ Since |crossing_point| has tried to choose |t| so that
7995 $B(|del1|,|del2|,|del3|;\tau)$ crosses zero at $\tau=|t|$ with negative
7996 slope, the value of |del2| computed below should not be positive.
7997 But rounding error could make it slightly positive in which case we
7998 must cut it to zero to avoid confusion.
8000 @<Test the extremes of the cubic against the bounding box@>=
8002 x=mp_eval_cubic(mp, p,q,t);
8003 @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>;
8004 del2=t_of_the_way(del2,del3);
8005 /* now |0,del2,del3| represent the derivative on the remaining interval */
8006 if ( del2>0 ) del2=0;
8007 tt=mp_crossing_point(mp, 0,-del2,-del3);
8008 if ( tt<fraction_one ) {
8009 @<Test the second extreme against the bounding box@>;
8013 @ @<Test the second extreme against the bounding box@>=
8015 x=mp_eval_cubic(mp, p,q,t_of_the_way(tt,fraction_one));
8016 @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>;
8019 @ Finding the bounding box of a path is basically a matter of applying
8020 |bound_cubic| twice for each pair of adjacent knots.
8022 @c void mp_path_bbox (MP mp,pointer h) {
8023 pointer p,q; /* a pair of adjacent knots */
8024 minx=x_coord(h); miny=y_coord(h);
8025 maxx=minx; maxy=miny;
8028 if ( right_type(p)==endpoint ) return;
8030 mp_bound_cubic(mp, x_loc(p),x_loc(q),mp_x_code);
8031 mp_bound_cubic(mp, y_loc(p),y_loc(q),mp_y_code);
8036 @ Another important way to measure a path is to find its arc length. This
8037 is best done by using the general bisection algorithm to subdivide the path
8038 until obtaining ``well behaved'' subpaths whose arc lengths can be approximated
8041 Since the arc length is the integral with respect to time of the magnitude of
8042 the velocity, it is natural to use Simpson's rule for the approximation.
8044 If $\dot B(t)$ is the spline velocity, Simpson's rule gives
8045 $$ \vb\dot B(0)\vb + 4\vb\dot B({1\over2})\vb + \vb\dot B(1)\vb \over 6 $$
8046 for the arc length of a path of length~1. For a cubic spline
8047 $B(z_0,z_1,z_2,z_3;t)$, the time derivative $\dot B(t)$ is
8048 $3B(dz_0,dz_1,dz_2;t)$, where $dz_i=z_{i+1}-z_i$. Hence the arc length
8050 $$ {\vb dz_0\vb \over 2} + 2\vb dz_{02}\vb + {\vb dz_2\vb \over 2}, $$
8052 $$ dz_{02}={1\over2}\left({dz_0+dz_1\over 2}+{dz_1+dz_2\over 2}\right)$$
8053 is the result of the bisection algorithm.
8055 @ The remaining problem is how to decide when a subpath is ``well behaved.''
8056 This could be done via the theoretical error bound for Simpson's rule,
8058 but this is impractical because it requires an estimate of the fourth
8059 derivative of the quantity being integrated. It is much easier to just perform
8060 a bisection step and see how much the arc length estimate changes. Since the
8061 error for Simpson's rule is proportional to the fourth power of the sample
8062 spacing, the remaining error is typically about $1\over16$ of the amount of
8063 the change. We say ``typically'' because the error has a pseudo-random behavior
8064 that could cause the two estimates to agree when each contain large errors.
8066 To protect against disasters such as undetected cusps, the bisection process
8067 should always continue until all the $dz_i$ vectors belong to a single
8068 $90^\circ$ sector. This ensures that no point on the spline can have velocity
8069 less than 70\% of the minimum of $\vb dz_0\vb$, $\vb dz_1\vb$ and $\vb dz_2\vb$.
8070 If such a spline happens to produce an erroneous arc length estimate that
8071 is little changed by bisection, the amount of the error is likely to be fairly
8072 small. We will try to arrange things so that freak accidents of this type do
8073 not destroy the inverse relationship between the \&{arclength} and
8074 \&{arctime} operations.
8075 @:arclength_}{\&{arclength} primitive@>
8076 @:arctime_}{\&{arctime} primitive@>
8078 @ The \&{arclength} and \&{arctime} operations are both based on a recursive
8080 function that finds the arc length of a cubic spline given $dz_0$, $dz_1$,
8081 $dz_2$. This |arc_test| routine also takes an arc length goal |a_goal| and
8082 returns the time when the arc length reaches |a_goal| if there is such a time.
8083 Thus the return value is either an arc length less than |a_goal| or, if the
8084 arc length would be at least |a_goal|, it returns a time value decreased by
8085 |two|. This allows the caller to use the sign of the result to distinguish
8086 between arc lengths and time values. On certain types of overflow, it is
8087 possible for |a_goal| and the result of |arc_test| both to be |el_gordo|.
8088 Otherwise, the result is always less than |a_goal|.
8090 Rather than halving the control point coordinates on each recursive call to
8091 |arc_test|, it is better to keep them proportional to velocity on the original
8092 curve and halve the results instead. This means that recursive calls can
8093 potentially use larger error tolerances in their arc length estimates. How
8094 much larger depends on to what extent the errors behave as though they are
8095 independent of each other. To save computing time, we use optimistic assumptions
8096 and increase the tolerance by a factor of about $\sqrt2$ for each recursive
8099 In addition to the tolerance parameter, |arc_test| should also have parameters
8100 for ${1\over3}\vb\dot B(0)\vb$, ${2\over3}\vb\dot B({1\over2})\vb$, and
8101 ${1\over3}\vb\dot B(1)\vb$. These quantities are relatively expensive to compute
8102 and they are needed in different instances of |arc_test|.
8104 @c @t\4@>@<Declare subroutines needed by |arc_test|@>;
8105 scaled mp_arc_test (MP mp, scaled dx0, scaled dy0, scaled dx1, scaled dy1,
8106 scaled dx2, scaled dy2, scaled v0, scaled v02,
8107 scaled v2, scaled a_goal, scaled tol) {
8108 boolean simple; /* are the control points confined to a $90^\circ$ sector? */
8109 scaled dx01, dy01, dx12, dy12, dx02, dy02; /* bisection results */
8111 /* twice the velocity magnitudes at $t={1\over4}$ and $t={3\over4}$ */
8112 scaled arc; /* best arc length estimate before recursion */
8113 @<Other local variables in |arc_test|@>;
8114 @<Bisect the B\'ezier quadratic given by |dx0|, |dy0|, |dx1|, |dy1|,
8116 @<Initialize |v002|, |v022|, and the arc length estimate |arc|; if it overflows
8117 set |arc_test| and |return|@>;
8118 @<Test if the control points are confined to one quadrant or rotating them
8119 $45^\circ$ would put them in one quadrant. Then set |simple| appropriately@>;
8120 if ( simple && (abs(arc-v02-halfp(v0+v2)) <= tol) ) {
8121 if ( arc < a_goal ) {
8124 @<Estimate when the arc length reaches |a_goal| and set |arc_test| to
8125 that time minus |two|@>;
8128 @<Use one or two recursive calls to compute the |arc_test| function@>;
8132 @ The |tol| value should by multiplied by $\sqrt 2$ before making recursive
8133 calls, but $1.5$ is an adequate approximation. It is best to avoid using
8134 |make_fraction| in this inner loop.
8137 @<Use one or two recursive calls to compute the |arc_test| function@>=
8139 @<Set |a_new| and |a_aux| so their sum is |2*a_goal| and |a_new| is as
8140 large as possible@>;
8141 tol = tol + halfp(tol);
8142 a = mp_arc_test(mp, dx0,dy0, dx01,dy01, dx02,dy02, v0, v002,
8143 halfp(v02), a_new, tol);
8145 return (-halfp(two-a));
8147 @<Update |a_new| to reduce |a_new+a_aux| by |a|@>;
8148 b = mp_arc_test(mp, dx02,dy02, dx12,dy12, dx2,dy2,
8149 halfp(v02), v022, v2, a_new, tol);
8151 return (-halfp(-b) - half_unit);
8153 return (a + half(b-a));
8157 @ @<Other local variables in |arc_test|@>=
8158 scaled a,b; /* results of recursive calls */
8159 scaled a_new,a_aux; /* the sum of these gives the |a_goal| */
8161 @ @<Set |a_new| and |a_aux| so their sum is |2*a_goal| and |a_new| is...@>=
8162 a_aux = el_gordo - a_goal;
8163 if ( a_goal > a_aux ) {
8164 a_aux = a_goal - a_aux;
8167 a_new = a_goal + a_goal;
8171 @ There is no need to maintain |a_aux| at this point so we use it as a temporary
8172 to force the additions and subtractions to be done in an order that avoids
8175 @<Update |a_new| to reduce |a_new+a_aux| by |a|@>=
8178 a_new = a_new + a_aux;
8181 @ This code assumes all {\it dx} and {\it dy} variables have magnitude less than
8182 |fraction_four|. To simplify the rest of the |arc_test| routine, we strengthen
8183 this assumption by requiring the norm of each $({\it dx},{\it dy})$ pair to obey
8184 this bound. Note that recursive calls will maintain this invariant.
8186 @<Bisect the B\'ezier quadratic given by |dx0|, |dy0|, |dx1|, |dy1|,...@>=
8187 dx01 = half(dx0 + dx1);
8188 dx12 = half(dx1 + dx2);
8189 dx02 = half(dx01 + dx12);
8190 dy01 = half(dy0 + dy1);
8191 dy12 = half(dy1 + dy2);
8192 dy02 = half(dy01 + dy12)
8194 @ We should be careful to keep |arc<el_gordo| so that calling |arc_test| with
8195 |a_goal=el_gordo| is guaranteed to yield the arc length.
8197 @<Initialize |v002|, |v022|, and the arc length estimate |arc|;...@>=
8198 v002 = mp_pyth_add(mp, dx01+half(dx0+dx02), dy01+half(dy0+dy02));
8199 v022 = mp_pyth_add(mp, dx12+half(dx02+dx2), dy12+half(dy02+dy2));
8201 arc1 = v002 + half(halfp(v0+tmp) - v002);
8202 arc = v022 + half(halfp(v2+tmp) - v022);
8203 if ( (arc < el_gordo-arc1) ) {
8206 mp->arith_error = true;
8207 if ( a_goal==el_gordo ) return (el_gordo);
8211 @ @<Other local variables in |arc_test|@>=
8212 scaled tmp, tmp2; /* all purpose temporary registers */
8213 scaled arc1; /* arc length estimate for the first half */
8215 @ @<Test if the control points are confined to one quadrant or rotating...@>=
8216 simple = ((dx0>=0) && (dx1>=0) && (dx2>=0)) ||
8217 ((dx0<=0) && (dx1<=0) && (dx2<=0));
8219 simple = ((dy0>=0) && (dy1>=0) && (dy2>=0)) ||
8220 ((dy0<=0) && (dy1<=0) && (dy2<=0));
8222 simple = ((dx0>=dy0) && (dx1>=dy1) && (dx2>=dy2)) ||
8223 ((dx0<=dy0) && (dx1<=dy1) && (dx2<=dy2));
8225 simple = ((-dx0>=dy0) && (-dx1>=dy1) && (-dx2>=dy2)) ||
8226 ((-dx0<=dy0) && (-dx1<=dy1) && (-dx2<=dy2));
8229 @ Since Simpson's rule is based on approximating the integrand by a parabola,
8231 it is appropriate to use the same approximation to decide when the integral
8232 reaches the intermediate value |a_goal|. At this point
8234 {\vb\dot B(0)\vb\over 3} &= \hbox{|v0|}, \qquad
8235 {\vb\dot B({1\over4})\vb\over 3} = {\hbox{|v002|}\over 2}, \qquad
8236 {\vb\dot B({1\over2})\vb\over 3} = {\hbox{|v02|}\over 2}, \cr
8237 {\vb\dot B({3\over4})\vb\over 3} &= {\hbox{|v022|}\over 2}, \qquad
8238 {\vb\dot B(1)\vb\over 3} = \hbox{|v2|} \cr
8242 $$ {\vb\dot B(t)\vb\over 3} \approx
8243 \cases{B\left(\hbox{|v0|},
8244 \hbox{|v002|}-{1\over 2}\hbox{|v0|}-{1\over 4}\hbox{|v02|},
8245 {1\over 2}\hbox{|v02|}; 2t \right)&
8246 if $t\le{1\over 2}$\cr
8247 B\left({1\over 2}\hbox{|v02|},
8248 \hbox{|v022|}-{1\over 4}\hbox{|v02|}-{1\over 2}\hbox{|v2|},
8249 \hbox{|v2|}; 2t-1 \right)&
8250 if $t\ge{1\over 2}$.\cr}
8253 We can integrate $\vb\dot B(t)\vb$ by using
8254 $$\int 3B(a,b,c;\tau)\,dt =
8255 {B(0,a,a+b,a+b+c;\tau) + {\rm constant} \over {d\tau\over dt}}.
8258 This construction allows us to find the time when the arc length reaches
8259 |a_goal| by solving a cubic equation of the form
8260 $$ B(0,a,a+b,a+b+c;\tau) = x, $$
8261 where $\tau$ is $2t$ or $2t+1$, $x$ is |a_goal| or |a_goal-arc1|, and $a$, $b$,
8262 and $c$ are the Bernshte{\u\i}n coefficients from $(*)$ divided by
8263 @^Bernshte{\u\i}n, Serge{\u\i} Natanovich@>
8264 $d\tau\over dt$. We shall define a function |solve_rising_cubic| that finds
8265 $\tau$ given $a$, $b$, $c$, and $x$.
8267 @<Estimate when the arc length reaches |a_goal| and set |arc_test| to...@>=
8269 tmp = (v02 + 2) / 4;
8270 if ( a_goal<=arc1 ) {
8273 (halfp(mp_solve_rising_cubic(mp, tmp2, arc1-tmp2-tmp, tmp, a_goal))- two);
8276 return ((half_unit - two) +
8277 halfp(mp_solve_rising_cubic(mp, tmp, arc-arc1-tmp-tmp2, tmp2, a_goal-arc1)));
8281 @ Here is the |solve_rising_cubic| routine that finds the time~$t$ when
8282 $$ B(0, a, a+b, a+b+c; t) = x. $$
8283 This routine is based on |crossing_point| but is simplified by the
8284 assumptions that $B(a,b,c;t)\ge0$ for $0\le t\le1$ and that |0<=x<=a+b+c|.
8285 If rounding error causes this condition to be violated slightly, we just ignore
8286 it and proceed with binary search. This finds a time when the function value
8287 reaches |x| and the slope is positive.
8289 @<Declare subroutines needed by |arc_test|@>=
8290 scaled mp_solve_rising_cubic (MP mp,scaled a, scaled b, scaled c, scaled x) {
8291 scaled ab, bc, ac; /* bisection results */
8292 integer t; /* $2^k+q$ where unscaled answer is in $[q2^{-k},(q+1)2^{-k})$ */
8293 integer xx; /* temporary for updating |x| */
8294 if ( (a<0) || (c<0) ) mp_confusion(mp, "rising?");
8295 @:this can't happen rising?}{\quad rising?@>
8298 } else if ( x >= a+b+c ) {
8302 @<Rescale if necessary to make sure |a|, |b|, and |c| are all less than
8306 @<Subdivide the B\'ezier quadratic defined by |a|, |b|, |c|@>;
8307 xx = x - a - ab - ac;
8308 if ( xx < -x ) { x+=x; b=ab; c=ac; }
8309 else { x = x + xx; a=ac; b=mp->bc; t = t+1; };
8310 } while (t < unity);
8315 @ @<Subdivide the B\'ezier quadratic defined by |a|, |b|, |c|@>=
8320 @ @d one_third_el_gordo 05252525252 /* upper bound on |a|, |b|, and |c| */
8322 @<Rescale if necessary to make sure |a|, |b|, and |c| are all less than...@>=
8323 while ((a>one_third_el_gordo)||(b>one_third_el_gordo)||(c>one_third_el_gordo)) {
8330 @ It is convenient to have a simpler interface to |arc_test| that requires no
8331 unnecessary arguments and ensures that each $({\it dx},{\it dy})$ pair has
8332 length less than |fraction_four|.
8334 @d arc_tol 16 /* quit when change in arc length estimate reaches this */
8336 @c scaled mp_do_arc_test (MP mp,scaled dx0, scaled dy0, scaled dx1,
8337 scaled dy1, scaled dx2, scaled dy2, scaled a_goal) {
8338 scaled v0,v1,v2; /* length of each $({\it dx},{\it dy})$ pair */
8339 scaled v02; /* twice the norm of the quadratic at $t={1\over2}$ */
8340 v0 = mp_pyth_add(mp, dx0,dy0);
8341 v1 = mp_pyth_add(mp, dx1,dy1);
8342 v2 = mp_pyth_add(mp, dx2,dy2);
8343 if ( (v0>=fraction_four) || (v1>=fraction_four) || (v2>=fraction_four) ) {
8344 mp->arith_error = true;
8345 if ( a_goal==el_gordo ) return el_gordo;
8348 v02 = mp_pyth_add(mp, dx1+half(dx0+dx2), dy1+half(dy0+dy2));
8349 return (mp_arc_test(mp, dx0,dy0, dx1,dy1, dx2,dy2,
8350 v0, v02, v2, a_goal, arc_tol));
8354 @ Now it is easy to find the arc length of an entire path.
8356 @c scaled mp_get_arc_length (MP mp,pointer h) {
8357 pointer p,q; /* for traversing the path */
8358 scaled a,a_tot; /* current and total arc lengths */
8361 while ( right_type(p)!=endpoint ){
8363 a = mp_do_arc_test(mp, right_x(p)-x_coord(p), right_y(p)-y_coord(p),
8364 left_x(q)-right_x(p), left_y(q)-right_y(p),
8365 x_coord(q)-left_x(q), y_coord(q)-left_y(q), el_gordo);
8366 a_tot = mp_slow_add(mp, a, a_tot);
8367 if ( q==h ) break; else p=q;
8373 @ The inverse operation of finding the time on a path~|h| when the arc length
8374 reaches some value |arc0| can also be accomplished via |do_arc_test|. Some care
8375 is required to handle very large times or negative times on cyclic paths. For
8376 non-cyclic paths, |arc0| values that are negative or too large cause
8377 |get_arc_time| to return 0 or the length of path~|h|.
8379 If |arc0| is greater than the arc length of a cyclic path~|h|, the result is a
8380 time value greater than the length of the path. Since it could be much greater,
8381 we must be prepared to compute the arc length of path~|h| and divide this into
8382 |arc0| to find how many multiples of the length of path~|h| to add.
8384 @c scaled mp_get_arc_time (MP mp,pointer h, scaled arc0) {
8385 pointer p,q; /* for traversing the path */
8386 scaled t_tot; /* accumulator for the result */
8387 scaled t; /* the result of |do_arc_test| */
8388 scaled arc; /* portion of |arc0| not used up so far */
8389 integer n; /* number of extra times to go around the cycle */
8391 @<Deal with a negative |arc0| value and |return|@>;
8393 if ( arc0==el_gordo ) decr(arc0);
8397 while ( (right_type(p)!=endpoint) && (arc>0) ) {
8399 t = mp_do_arc_test(mp, right_x(p)-x_coord(p), right_y(p)-y_coord(p),
8400 left_x(q)-right_x(p), left_y(q)-right_y(p),
8401 x_coord(q)-left_x(q), y_coord(q)-left_y(q), arc);
8402 @<Update |arc| and |t_tot| after |do_arc_test| has just returned |t|@>;
8404 @<Update |t_tot| and |arc| to avoid going around the cyclic
8405 path too many times but set |arith_error:=true| and |goto done| on
8414 @ @<Update |arc| and |t_tot| after |do_arc_test| has just returned |t|@>=
8415 if ( t<0 ) { t_tot = t_tot + t + two; arc = 0; }
8416 else { t_tot = t_tot + unity; arc = arc - t; }
8418 @ @<Deal with a negative |arc0| value and |return|@>=
8420 if ( left_type(h)==endpoint ) {
8423 p = mp_htap_ypoc(mp, h);
8424 t_tot = -mp_get_arc_time(mp, p, -arc0);
8425 mp_toss_knot_list(mp, p);
8431 @ @<Update |t_tot| and |arc| to avoid going around the cyclic...@>=
8433 n = arc / (arc0 - arc);
8434 arc = arc - n*(arc0 - arc);
8435 if ( t_tot > el_gordo / (n+1) ) {
8436 mp->arith_error = true;
8440 t_tot = (n + 1)*t_tot;
8443 @* \[20] Data structures for pens.
8444 A Pen in \MP\ can be either elliptical or polygonal. Elliptical pens result
8445 in \ps\ \&{stroke} commands, while anything drawn with a polygonal pen is
8446 @:stroke}{\&{stroke} command@>
8447 converted into an area fill as described in the next part of this program.
8448 The mathematics behind this process is based on simple aspects of the theory
8449 of tracings developed by Leo Guibas, Lyle Ramshaw, and Jorge Stolfi
8450 [``A kinematic framework for computational geometry,'' Proc.\ IEEE Symp.\
8451 Foundations of Computer Science {\bf 24} (1983), 100--111].
8453 Polygonal pens are created from paths via \MP's \&{makepen} primitive.
8454 @:makepen_}{\&{makepen} primitive@>
8455 This path representation is almost sufficient for our purposes except that
8456 a pen path should always be a convex polygon with the vertices in
8457 counter-clockwise order.
8458 Since we will need to scan pen polygons both forward and backward, a pen
8459 should be represented as a doubly linked ring of knot nodes. There is
8460 room for the extra back pointer because we do not need the
8461 |left_type| or |right_type| fields. In fact, we don't need the |left_x|,
8462 |left_y|, |right_x|, or |right_y| fields either but we leave these alone
8463 so that certain procedures can operate on both pens and paths. In particular,
8464 pens can be copied using |copy_path| and recycled using |toss_knot_list|.
8467 /* this replaces the |left_type| and |right_type| fields in a pen knot */
8469 @ The |make_pen| procedure turns a path into a pen by initializing
8470 the |knil| pointers and making sure the knots form a convex polygon.
8471 Thus each cubic in the given path becomes a straight line and the control
8472 points are ignored. If the path is not cyclic, the ends are connected by a
8475 @d copy_pen(A) mp_make_pen(mp, mp_copy_path(mp, (A)),false)
8477 @c @<Declare a function called |convex_hull|@>;
8478 pointer mp_make_pen (MP mp,pointer h, boolean need_hull) {
8479 pointer p,q; /* two consecutive knots */
8486 h=mp_convex_hull(mp, h);
8487 @<Make sure |h| isn't confused with an elliptical pen@>;
8492 @ The only information required about an elliptical pen is the overall
8493 transformation that has been applied to the original \&{pencircle}.
8494 @:pencircle_}{\&{pencircle} primitive@>
8495 Since it suffices to keep track of how the three points $(0,0)$, $(1,0)$,
8496 and $(0,1)$ are transformed, an elliptical pen can be stored in a single
8497 knot node and transformed as if it were a path.
8499 @d pen_is_elliptical(A) ((A)==link((A)))
8501 @c pointer mp_get_pen_circle (MP mp,scaled diam) {
8502 pointer h; /* the knot node to return */
8503 h=mp_get_node(mp, knot_node_size);
8504 link(h)=h; knil(h)=h;
8505 originator(h)=program_code;
8506 x_coord(h)=0; y_coord(h)=0;
8507 left_x(h)=diam; left_y(h)=0;
8508 right_x(h)=0; right_y(h)=diam;
8512 @ If the polygon being returned by |make_pen| has only one vertex, it will
8513 be interpreted as an elliptical pen. This is no problem since a degenerate
8514 polygon can equally well be thought of as a degenerate ellipse. We need only
8515 initialize the |left_x|, |left_y|, |right_x|, and |right_y| fields.
8517 @<Make sure |h| isn't confused with an elliptical pen@>=
8518 if ( pen_is_elliptical( h) ){
8519 left_x(h)=x_coord(h); left_y(h)=y_coord(h);
8520 right_x(h)=x_coord(h); right_y(h)=y_coord(h);
8523 @ We have to cheat a little here but most operations on pens only use
8524 the first three words in each knot node.
8525 @^data structure assumptions@>
8527 @<Initialize a pen at |test_pen| so that it fits in nine words@>=
8528 x_coord(test_pen)=-half_unit;
8529 y_coord(test_pen)=0;
8530 x_coord(test_pen+3)=half_unit;
8531 y_coord(test_pen+3)=0;
8532 x_coord(test_pen+6)=0;
8533 y_coord(test_pen+6)=unity;
8534 link(test_pen)=test_pen+3;
8535 link(test_pen+3)=test_pen+6;
8536 link(test_pen+6)=test_pen;
8537 knil(test_pen)=test_pen+6;
8538 knil(test_pen+3)=test_pen;
8539 knil(test_pen+6)=test_pen+3
8541 @ Printing a polygonal pen is very much like printing a path
8543 @<Declare subroutines for printing expressions@>=
8544 void mp_pr_pen (MP mp,pointer h) {
8545 pointer p,q; /* for list traversal */
8546 if ( pen_is_elliptical(h) ) {
8547 @<Print the elliptical pen |h|@>;
8551 mp_print_two(mp, x_coord(p),y_coord(p));
8552 mp_print_nl(mp, " .. ");
8553 @<Advance |p| making sure the links are OK and |return| if there is
8556 mp_print(mp, "cycle");
8560 @ @<Advance |p| making sure the links are OK and |return| if there is...@>=
8562 if ( (q==null) || (knil(q)!=p) ) {
8563 mp_print_nl(mp, "???"); return; /* this won't happen */
8568 @ @<Print the elliptical pen |h|@>=
8570 mp_print(mp, "pencircle transformed (");
8571 mp_print_scaled(mp, x_coord(h));
8572 mp_print_char(mp, ',');
8573 mp_print_scaled(mp, y_coord(h));
8574 mp_print_char(mp, ',');
8575 mp_print_scaled(mp, left_x(h)-x_coord(h));
8576 mp_print_char(mp, ',');
8577 mp_print_scaled(mp, right_x(h)-x_coord(h));
8578 mp_print_char(mp, ',');
8579 mp_print_scaled(mp, left_y(h)-y_coord(h));
8580 mp_print_char(mp, ',');
8581 mp_print_scaled(mp, right_y(h)-y_coord(h));
8582 mp_print_char(mp, ')');
8585 @ Here us another version of |pr_pen| that prints the pen as a diagnostic
8588 @<Declare subroutines for printing expressions@>=
8589 void mp_print_pen (MP mp,pointer h, char *s, boolean nuline) {
8590 mp_print_diagnostic(mp, "Pen",s,nuline); mp_print_ln(mp);
8593 mp_end_diagnostic(mp, true);
8596 @ Making a polygonal pen into a path involves restoring the |left_type| and
8597 |right_type| fields and setting the control points so as to make a polygonal
8601 void mp_make_path (MP mp,pointer h) {
8602 pointer p; /* for traversing the knot list */
8603 small_number k; /* a loop counter */
8604 @<Other local variables in |make_path|@>;
8605 if ( pen_is_elliptical(h) ) {
8606 @<Make the elliptical pen |h| into a path@>;
8610 left_type(p)=explicit;
8611 right_type(p)=explicit;
8612 @<copy the coordinates of knot |p| into its control points@>;
8618 @ @<copy the coordinates of knot |p| into its control points@>=
8619 left_x(p)=x_coord(p);
8620 left_y(p)=y_coord(p);
8621 right_x(p)=x_coord(p);
8622 right_y(p)=y_coord(p)
8624 @ We need an eight knot path to get a good approximation to an ellipse.
8626 @<Make the elliptical pen |h| into a path@>=
8628 @<Extract the transformation parameters from the elliptical pen~|h|@>;
8630 for (k=0;k<=7;k++ ) {
8631 @<Initialize |p| as the |k|th knot of a circle of unit diameter,
8632 transforming it appropriately@>;
8633 if ( k==7 ) link(p)=h; else link(p)=mp_get_node(mp, knot_node_size);
8638 @ @<Extract the transformation parameters from the elliptical pen~|h|@>=
8639 center_x=x_coord(h);
8640 center_y=y_coord(h);
8641 width_x=left_x(h)-center_x;
8642 width_y=left_y(h)-center_y;
8643 height_x=right_x(h)-center_x;
8644 height_y=right_y(h)-center_y
8646 @ @<Other local variables in |make_path|@>=
8647 scaled center_x,center_y; /* translation parameters for an elliptical pen */
8648 scaled width_x,width_y; /* the effect of a unit change in $x$ */
8649 scaled height_x,height_y; /* the effect of a unit change in $y$ */
8650 scaled dx,dy; /* the vector from knot |p| to its right control point */
8652 /* |k| advanced $270^\circ$ around the ring (cf. $\sin\theta=\cos(\theta+270)$) */
8654 @ The only tricky thing here are the tables |half_cos| and |d_cos| used to
8655 find the point $k/8$ of the way around the circle and the direction vector
8658 @<Initialize |p| as the |k|th knot of a circle of unit diameter,...@>=
8660 x_coord(p)=center_x+mp_take_fraction(mp, mp->half_cos[k],width_x)
8661 +mp_take_fraction(mp, mp->half_cos[kk],height_x);
8662 y_coord(p)=center_y+mp_take_fraction(mp, mp->half_cos[k],width_y)
8663 +mp_take_fraction(mp, mp->half_cos[kk],height_y);
8664 dx=-mp_take_fraction(mp, mp->d_cos[kk],width_x)
8665 +mp_take_fraction(mp, mp->d_cos[k],height_x);
8666 dy=-mp_take_fraction(mp, mp->d_cos[kk],width_y)
8667 +mp_take_fraction(mp, mp->d_cos[k],height_y);
8668 right_x(p)=x_coord(p)+dx;
8669 right_y(p)=y_coord(p)+dy;
8670 left_x(p)=x_coord(p)-dx;
8671 left_y(p)=y_coord(p)-dy;
8672 left_type(p)=explicit;
8673 right_type(p)=explicit;
8674 originator(p)=program_code
8677 fraction half_cos[8]; /* ${1\over2}\cos(45k)$ */
8678 fraction d_cos[8]; /* a magic constant times $\cos(45k)$ */
8680 @ The magic constant for |d_cos| is the distance between $({1\over2},0)$ and
8681 $({1\over4}\sqrt2,{1\over4}\sqrt2)$ times the result of the |velocity|
8682 function for $\theta=\phi=22.5^\circ$. This comes out to be
8683 $$ d = {\sqrt{2-\sqrt2}\over 3+3\cos22.5^\circ}
8684 \approx 0.132608244919772.
8688 mp->half_cos[0]=fraction_half;
8689 mp->half_cos[1]=94906266; /* $2^{26}\sqrt2\approx94906265.62$ */
8691 mp->d_cos[0]=35596755; /* $2^{28}d\approx35596754.69$ */
8692 mp->d_cos[1]=25170707; /* $2^{27}\sqrt2\,d\approx25170706.63$ */
8694 for (k=3;k<= 4;k++ ) {
8695 mp->half_cos[k]=-mp->half_cos[4-k];
8696 mp->d_cos[k]=-mp->d_cos[4-k];
8698 for (k=5;k<= 7;k++ ) {
8699 mp->half_cos[k]=mp->half_cos[8-k];
8700 mp->d_cos[k]=mp->d_cos[8-k];
8703 @ The |convex_hull| function forces a pen polygon to be convex when it is
8704 returned by |make_pen| and after any subsequent transformation where rounding
8705 error might allow the convexity to be lost.
8706 The convex hull algorithm used here is described by F.~P. Preparata and
8707 M.~I. Shamos [{\sl Computational Geometry}, Springer-Verlag, 1985].
8709 @<Declare a function called |convex_hull|@>=
8710 @<Declare a procedure called |move_knot|@>;
8711 pointer mp_convex_hull (MP mp,pointer h) { /* Make a polygonal pen convex */
8712 pointer l,r; /* the leftmost and rightmost knots */
8713 pointer p,q; /* knots being scanned */
8714 pointer s; /* the starting point for an upcoming scan */
8715 scaled dx,dy; /* a temporary pointer */
8716 if ( pen_is_elliptical(h) ) {
8719 @<Set |l| to the leftmost knot in polygon~|h|@>;
8720 @<Set |r| to the rightmost knot in polygon~|h|@>;
8723 @<Find any knots on the path from |l| to |r| above the |l|-|r| line and
8724 move them past~|r|@>;
8725 @<Find any knots on the path from |s| to |l| below the |l|-|r| line and
8726 move them past~|l|@>;
8727 @<Sort the path from |l| to |r| by increasing $x$@>;
8728 @<Sort the path from |r| to |l| by decreasing $x$@>;
8731 @<Do a Gramm scan and remove vertices where there is no left turn@>;
8737 @ All comparisons are done primarily on $x$ and secondarily on $y$.
8739 @<Set |l| to the leftmost knot in polygon~|h|@>=
8743 if ( x_coord(p)<=x_coord(l) )
8744 if ( (x_coord(p)<x_coord(l)) || (y_coord(p)<y_coord(l)) )
8749 @ @<Set |r| to the rightmost knot in polygon~|h|@>=
8753 if ( x_coord(p)>=x_coord(r) )
8754 if ( (x_coord(p)>x_coord(r)) || (y_coord(p)>y_coord(r)) )
8759 @ @<Find any knots on the path from |l| to |r| above the |l|-|r| line...@>=
8760 dx=x_coord(r)-x_coord(l);
8761 dy=y_coord(r)-y_coord(l);
8765 if ( mp_ab_vs_cd(mp, dx,y_coord(p)-y_coord(l),dy,x_coord(p)-x_coord(l))>0 )
8766 mp_move_knot(mp, p, r);
8770 @ The |move_knot| procedure removes |p| from a doubly linked list and inserts
8773 @ @<Declare a procedure called |move_knot|@>=
8774 void mp_move_knot (MP mp,pointer p, pointer q) {
8775 link(knil(p))=link(p);
8776 knil(link(p))=knil(p);
8783 @ @<Find any knots on the path from |s| to |l| below the |l|-|r| line...@>=
8787 if ( mp_ab_vs_cd(mp, dx,y_coord(p)-y_coord(l),dy,x_coord(p)-x_coord(l))<0 )
8788 mp_move_knot(mp, p,l);
8792 @ The list is likely to be in order already so we just do linear insertions.
8793 Secondary comparisons on $y$ ensure that the sort is consistent with the
8794 choice of |l| and |r|.
8796 @<Sort the path from |l| to |r| by increasing $x$@>=
8800 while ( x_coord(q)>x_coord(p) ) q=knil(q);
8801 while ( x_coord(q)==x_coord(p) ) {
8802 if ( y_coord(q)>y_coord(p) ) q=knil(q); else break;
8804 if ( q==knil(p) ) p=link(p);
8805 else { p=link(p); mp_move_knot(mp, knil(p),q); };
8808 @ @<Sort the path from |r| to |l| by decreasing $x$@>=
8812 while ( x_coord(q)<x_coord(p) ) q=knil(q);
8813 while ( x_coord(q)==x_coord(p) ) {
8814 if ( y_coord(q)<y_coord(p) ) q=knil(q); else break;
8816 if ( q==knil(p) ) p=link(p);
8817 else { p=link(p); mp_move_knot(mp, knil(p),q); };
8820 @ The condition involving |ab_vs_cd| tests if there is not a left turn
8821 at knot |q|. There usually will be a left turn so we streamline the case
8822 where the |then| clause is not executed.
8824 @<Do a Gramm scan and remove vertices where there...@>=
8828 dx=x_coord(q)-x_coord(p);
8829 dy=y_coord(q)-y_coord(p);
8833 if ( mp_ab_vs_cd(mp, dx,y_coord(q)-y_coord(p),dy,x_coord(q)-x_coord(p))<=0 ) {
8834 @<Remove knot |p| and back up |p| and |q| but don't go past |l|@>;
8839 @ @<Remove knot |p| and back up |p| and |q| but don't go past |l|@>=
8842 mp_free_node(mp, p,knot_node_size);
8843 link(s)=q; knil(q)=s;
8845 else { p=knil(s); q=s; };
8848 @ The |find_offset| procedure sets global variables |(cur_x,cur_y)| to the
8849 offset associated with the given direction |(x,y)|. If two different offsets
8850 apply, it chooses one of them.
8853 void mp_find_offset (MP mp,scaled x, scaled y, pointer h) {
8854 pointer p,q; /* consecutive knots */
8856 /* the transformation matrix for an elliptical pen */
8857 fraction xx,yy; /* untransformed offset for an elliptical pen */
8858 fraction d; /* a temporary register */
8859 if ( pen_is_elliptical(h) ) {
8860 @<Find the offset for |(x,y)| on the elliptical pen~|h|@>
8865 } while (! mp_ab_vs_cd(mp, x_coord(q)-x_coord(p),y, y_coord(q)-y_coord(p),x)>=0);
8868 } while (! mp_ab_vs_cd(mp, x_coord(q)-x_coord(p),y, y_coord(q)-y_coord(p),x)<=0);
8869 mp->cur_x=x_coord(p);
8870 mp->cur_y=y_coord(p);
8876 scaled cur_y; /* all-purpose return value registers */
8878 @ @<Find the offset for |(x,y)| on the elliptical pen~|h|@>=
8879 if ( (x==0) && (y==0) ) {
8880 mp->cur_x=x_coord(h); mp->cur_y=y_coord(h);
8882 @<Find the non-constant part of the transformation for |h|@>;
8883 while ( (abs(x)<fraction_half) && (abs(y)<fraction_half) ){
8886 @<Make |(xx,yy)| the offset on the untransformed \&{pencircle} for the
8887 untransformed version of |(x,y)|@>;
8888 mp->cur_x=x_coord(h)+mp_take_fraction(mp, xx,wx)+mp_take_fraction(mp, yy,hx);
8889 mp->cur_y=y_coord(h)+mp_take_fraction(mp, xx,wy)+mp_take_fraction(mp, yy,hy);
8892 @ @<Find the non-constant part of the transformation for |h|@>=
8893 wx=left_x(h)-x_coord(h);
8894 wy=left_y(h)-y_coord(h);
8895 hx=right_x(h)-x_coord(h);
8896 hy=right_y(h)-y_coord(h)
8898 @ @<Make |(xx,yy)| the offset on the untransformed \&{pencircle} for the...@>=
8899 yy=-(mp_take_fraction(mp, x,hy)+mp_take_fraction(mp, y,-hx));
8900 xx=mp_take_fraction(mp, x,-wy)+mp_take_fraction(mp, y,wx);
8901 d=mp_pyth_add(mp, xx,yy);
8903 xx=half(mp_make_fraction(mp, xx,d));
8904 yy=half(mp_make_fraction(mp, yy,d));
8907 @ Finding the bounding box of a pen is easy except if the pen is elliptical.
8908 But we can handle that case by just calling |find_offset| twice. The answer
8909 is stored in the global variables |minx|, |maxx|, |miny|, and |maxy|.
8912 void mp_pen_bbox (MP mp,pointer h) {
8913 pointer p; /* for scanning the knot list */
8914 if ( pen_is_elliptical(h) ) {
8915 @<Find the bounding box of an elliptical pen@>;
8917 minx=x_coord(h); maxx=minx;
8918 miny=y_coord(h); maxy=miny;
8921 if ( x_coord(p)<minx ) minx=x_coord(p);
8922 if ( y_coord(p)<miny ) miny=y_coord(p);
8923 if ( x_coord(p)>maxx ) maxx=x_coord(p);
8924 if ( y_coord(p)>maxy ) maxy=y_coord(p);
8930 @ @<Find the bounding box of an elliptical pen@>=
8932 mp_find_offset(mp, 0,fraction_one,h);
8934 minx=2*x_coord(h)-mp->cur_x;
8935 mp_find_offset(mp, -fraction_one,0,h);
8937 miny=2*y_coord(h)-mp->cur_y;
8940 @* \[21] Edge structures.
8941 Now we come to \MP's internal scheme for representing pictures.
8942 The representation is very different from \MF's edge structures
8943 because \MP\ pictures contain \ps\ graphics objects instead of pixel
8944 images. However, the basic idea is somewhat similar in that shapes
8945 are represented via their boundaries.
8947 The main purpose of edge structures is to keep track of graphical objects
8948 until it is time to translate them into \ps. Since \MP\ does not need to
8949 know anything about an edge structure other than how to translate it into
8950 \ps\ and how to find its bounding box, edge structures can be just linked
8951 lists of graphical objects. \MP\ has no easy way to determine whether
8952 two such objects overlap, but it suffices to draw the first one first and
8953 let the second one overwrite it if necessary.
8955 @ Let's consider the types of graphical objects one at a time.
8956 First of all, a filled contour is represented by a eight-word node. The first
8957 word contains |type| and |link| fields, and the next six words contain a
8958 pointer to a cyclic path and the value to use for \ps' \&{currentrgbcolor}
8959 parameter. If a pen is used for filling |pen_p|, |ljoin_val| and |miterlim_val|
8960 give the relevant information.
8962 @d path_p(A) link((A)+1)
8963 /* a pointer to the path that needs filling */
8964 @d pen_p(A) info((A)+1)
8965 /* a pointer to the pen to fill or stroke with */
8966 @d color_model(A) type((A)+2) /* the color model */
8967 @d obj_red_loc(A) ((A)+3) /* the first of three locations for the color */
8968 @d obj_cyan_loc obj_red_loc /* the first of four locations for the color */
8969 @d obj_grey_loc obj_red_loc /* the location for the color */
8970 @d red_val(A) mp->mem[(A)+3].sc
8971 /* the red component of the color in the range $0\ldots1$ */
8974 @d green_val(A) mp->mem[(A)+4].sc
8975 /* the green component of the color in the range $0\ldots1$ */
8976 @d magenta_val green_val
8977 @d blue_val(A) mp->mem[(A)+5].sc
8978 /* the blue component of the color in the range $0\ldots1$ */
8979 @d yellow_val blue_val
8980 @d black_val(A) mp->mem[(A)+6].sc
8981 /* the blue component of the color in the range $0\ldots1$ */
8982 @d ljoin_val(A) name_type((A)) /* the value of \&{linejoin} */
8983 @:linejoin_}{\&{linejoin} primitive@>
8984 @d miterlim_val(A) mp->mem[(A)+7].sc /* the value of \&{miterlimit} */
8985 @:miterlimit_}{\&{miterlimit} primitive@>
8986 @d obj_color_part(A) mp->mem[(A)+3-red_part].sc
8987 /* interpret an object pointer that has been offset by |red_part..blue_part| */
8988 @d pre_script(A) mp->mem[(A)+8].hh.lh
8989 @d post_script(A) mp->mem[(A)+8].hh.rh
8994 pointer mp_new_fill_node (MP mp,pointer p) {
8995 /* make a fill node for cyclic path |p| and color black */
8996 pointer t; /* the new node */
8997 t=mp_get_node(mp, fill_node_size);
9000 pen_p(t)=null; /* |null| means don't use a pen */
9005 color_model(t)=uninitialized_model;
9007 post_script(t)=null;
9008 @<Set the |ljoin_val| and |miterlim_val| fields in object |t|@>;
9012 @ @<Set the |ljoin_val| and |miterlim_val| fields in object |t|@>=
9013 if ( mp->internal[linejoin]>unity ) ljoin_val(t)=2;
9014 else if ( mp->internal[linejoin]>0 ) ljoin_val(t)=1;
9015 else ljoin_val(t)=0;
9016 if ( mp->internal[miterlimit]<unity )
9017 miterlim_val(t)=unity;
9019 miterlim_val(t)=mp->internal[miterlimit]
9021 @ A stroked path is represented by an eight-word node that is like a filled
9022 contour node except that it contains the current \&{linecap} value, a scale
9023 factor for the dash pattern, and a pointer that is non-null if the stroke
9024 is to be dashed. The purpose of the scale factor is to allow a picture to
9025 be transformed without touching the picture that |dash_p| points to.
9027 @d dash_p(A) link((A)+9)
9028 /* a pointer to the edge structure that gives the dash pattern */
9029 @d lcap_val(A) type((A)+9)
9030 /* the value of \&{linecap} */
9031 @:linecap_}{\&{linecap} primitive@>
9032 @d dash_scale(A) mp->mem[(A)+10].sc /* dash lengths are scaled by this factor */
9033 @d stroked_node_size 11
9037 pointer mp_new_stroked_node (MP mp,pointer p) {
9038 /* make a stroked node for path |p| with |pen_p(p)| temporarily |null| */
9039 pointer t; /* the new node */
9040 t=mp_get_node(mp, stroked_node_size);
9041 type(t)=stroked_code;
9042 path_p(t)=p; pen_p(t)=null;
9044 dash_scale(t)=unity;
9049 color_model(t)=uninitialized_model;
9051 post_script(t)=null;
9052 @<Set the |ljoin_val| and |miterlim_val| fields in object |t|@>;
9053 if ( mp->internal[linecap]>unity ) lcap_val(t)=2;
9054 else if ( mp->internal[linecap]>0 ) lcap_val(t)=1;
9059 @ When a dashed line is computed in a transformed coordinate system, the dash
9060 lengths get scaled like the pen shape and we need to compensate for this. Since
9061 there is no unique scale factor for an arbitrary transformation, we use the
9062 the square root of the determinant. The properties of the determinant make it
9063 easier to maintain the |dash_scale|. The computation is fairly straight-forward
9064 except for the initialization of the scale factor |s|. The factor of 64 is
9065 needed because |square_rt| scales its result by $2^8$ while we need $2^{14}$
9066 to counteract the effect of |take_fraction|.
9068 @<Declare subroutines needed by |print_edges|@>=
9069 scaled mp_sqrt_det (MP mp,scaled a, scaled b, scaled c, scaled d) {
9070 scaled maxabs; /* $max(|a|,|b|,|c|,|d|)$ */
9071 integer s; /* amount by which the result of |square_rt| needs to be scaled */
9072 @<Initialize |maxabs|@>;
9074 while ( (maxabs<fraction_one) && (s>1) ){
9075 a+=a; b+=b; c+=c; d+=d;
9076 maxabs+=maxabs; s=halfp(s);
9078 return s*mp_square_rt(mp, abs(mp_take_fraction(mp, a,d)-mp_take_fraction(mp, b,c)));
9081 scaled mp_get_pen_scale (MP mp,pointer p) {
9082 return mp_sqrt_det(mp,
9083 left_x(p)-x_coord(p), right_x(p)-x_coord(p),
9084 left_y(p)-y_coord(p), right_y(p)-y_coord(p));
9087 @ @<Initialize |maxabs|@>=
9089 if ( abs(b)>maxabs ) maxabs=abs(b);
9090 if ( abs(c)>maxabs ) maxabs=abs(c);
9091 if ( abs(d)>maxabs ) maxabs=abs(d)
9093 @ When a picture contains text, this is represented by a fourteen-word node
9094 where the color information and |type| and |link| fields are augmented by
9095 additional fields that describe the text and how it is transformed.
9096 The |path_p| and |pen_p| pointers are replaced by a number that identifies
9097 the font and a string number that gives the text to be displayed.
9098 The |width|, |height|, and |depth| fields
9099 give the dimensions of the text at its design size, and the remaining six
9100 words give a transformation to be applied to the text. The |new_text_node|
9101 function initializes everything to default values so that the text comes out
9102 black with its reference point at the origin.
9104 @d text_p(A) link((A)+1) /* a string pointer for the text to display */
9105 @d font_n(A) info((A)+1) /* the font number */
9106 @d width_val(A) mp->mem[(A)+7].sc /* unscaled width of the text */
9107 @d height_val(A) mp->mem[(A)+9].sc /* unscaled height of the text */
9108 @d depth_val(A) mp->mem[(A)+10].sc /* unscaled depth of the text */
9109 @d text_tx_loc(A) ((A)+11)
9110 /* the first of six locations for transformation parameters */
9111 @d tx_val(A) mp->mem[(A)+11].sc /* $x$ shift amount */
9112 @d ty_val(A) mp->mem[(A)+12].sc /* $y$ shift amount */
9113 @d txx_val(A) mp->mem[(A)+13].sc /* |txx| transformation parameter */
9114 @d txy_val(A) mp->mem[(A)+14].sc /* |txy| transformation parameter */
9115 @d tyx_val(A) mp->mem[(A)+15].sc /* |tyx| transformation parameter */
9116 @d tyy_val(A) mp->mem[(A)+16].sc /* |tyy| transformation parameter */
9117 @d text_trans_part(A) mp->mem[(A)+11-x_part].sc
9118 /* interpret a text node ponter that has been offset by |x_part..yy_part| */
9119 @d text_node_size 17
9122 @c @<Declare text measuring subroutines@>;
9123 pointer mp_new_text_node (MP mp,char *f,str_number s) {
9124 /* make a text node for font |f| and text string |s| */
9125 pointer t; /* the new node */
9126 t=mp_get_node(mp, text_node_size);
9129 font_n(t)=mp_find_font(mp, f); /* this identifies the font */
9134 color_model(t)=uninitialized_model;
9136 post_script(t)=null;
9137 tx_val(t)=0; ty_val(t)=0;
9138 txx_val(t)=unity; txy_val(t)=0;
9139 tyx_val(t)=0; tyy_val(t)=unity;
9140 mp_set_text_box(mp, t); /* this finds the bounding box */
9144 @ The last two types of graphical objects that can occur in an edge structure
9145 are clipping paths and \&{setbounds} paths. These are slightly more difficult
9146 @:set_bounds_}{\&{setbounds} primitive@>
9147 to implement because we must keep track of exactly what is being clipped or
9148 bounded when pictures get merged together. For this reason, each clipping or
9149 \&{setbounds} operation is represented by a pair of nodes: first comes a
9150 two-word node whose |path_p| gives the relevant path, then there is the list
9151 of objects to clip or bound followed by a two-word node whose second word is
9154 Using at least two words for each graphical object node allows them all to be
9155 allocated and deallocated similarly with a global array |gr_object_size| to
9156 give the size in words for each object type.
9158 @d start_clip_size 2
9159 @d start_bounds_size 2
9160 @d stop_clip_size 2 /* the second word is not used here */
9161 @d stop_bounds_size 2 /* the second word is not used here */
9163 @d stop_type(A) ((A)+2)
9164 /* matching |type| for |start_clip_code| or |start_bounds_code| */
9165 @d has_color(A) (type((A))<mp_start_clip_code)
9166 /* does a graphical object have color fields? */
9167 @d has_pen(A) (type((A))<text_code)
9168 /* does a graphical object have a |pen_p| field? */
9169 @d is_start_or_stop(A) (type((A))>=mp_start_clip_code)
9170 @d is_stop(A) (type((A))>=mp_stop_clip_code)
9174 mp_start_clip_code=4, /* |type| of a node that starts clipping */
9175 mp_start_bounds_code, /* |type| of a node that gives a \&{setbounds} path */
9176 mp_stop_clip_code, /* |type| of a node that stops clipping */
9177 mp_stop_bounds_code /* |type| of a node that stops \&{setbounds} */
9181 pointer mp_new_bounds_node (MP mp,pointer p, small_number c) {
9182 /* make a node of type |c| where |p| is the clipping or \&{setbounds} path */
9183 pointer t; /* the new node */
9184 t=mp_get_node(mp, mp->gr_object_size[c]);
9190 @ We need an array to keep track of the sizes of graphical objects.
9193 small_number gr_object_size[mp_stop_bounds_code+1];
9196 mp->gr_object_size[fill_code]=fill_node_size;
9197 mp->gr_object_size[stroked_code]=stroked_node_size;
9198 mp->gr_object_size[text_code]=text_node_size;
9199 mp->gr_object_size[mp_start_clip_code]=start_clip_size;
9200 mp->gr_object_size[mp_stop_clip_code]=stop_clip_size;
9201 mp->gr_object_size[mp_start_bounds_code]=start_bounds_size;
9202 mp->gr_object_size[mp_stop_bounds_code]=stop_bounds_size;
9204 @ All the essential information in an edge structure is encoded as a linked list
9205 of graphical objects as we have just seen, but it is helpful to add some
9206 redundant information. A single edge structure might be used as a dash pattern
9207 many times, and it would be nice to avoid scanning the same structure
9208 repeatedly. Thus, an edge structure known to be a suitable dash pattern
9209 has a header that gives a list of dashes in a sorted order designed for rapid
9210 translation into \ps.
9212 Each dash is represented by a three-word node containing the initial and final
9213 $x$~coordinates as well as the usual |link| field. The |link| fields points to
9214 the dash node with the next higher $x$-coordinates and the final link points
9215 to a special location called |null_dash|. (There should be no overlap between
9216 dashes). Since the $y$~coordinate of the dash pattern is needed to determine
9217 the period of repetition, this needs to be stored in the edge header along
9218 with a pointer to the list of dash nodes.
9220 @d start_x(A) mp->mem[(A)+1].sc /* the starting $x$~coordinate in a dash node */
9221 @d stop_x(A) mp->mem[(A)+2].sc /* the ending $x$~coordinate in a dash node */
9224 /* in an edge header this points to the first dash node */
9225 @d dash_y(A) mp->mem[(A)+1].sc /* $y$ value for the dash list in an edge header */
9227 @ It is also convenient for an edge header to contain the bounding
9228 box information needed by the \&{llcorner} and \&{urcorner} operators
9229 so that this does not have to be recomputed unnecessarily. This is done by
9230 adding fields for the $x$~and $y$ extremes as well as a pointer that indicates
9231 how far the bounding box computation has gotten. Thus if the user asks for
9232 the bounding box and then adds some more text to the picture before asking
9233 for more bounding box information, the second computation need only look at
9234 the additional text.
9236 When the bounding box has not been computed, the |bblast| pointer points
9237 to a dummy link at the head of the graphical object list while the |minx_val|
9238 and |miny_val| fields contain |el_gordo| and the |maxx_val| and |maxy_val|
9239 fields contain |-el_gordo|.
9241 Since the bounding box of pictures containing objects of type
9242 |mp_start_bounds_code| depends on the value of \&{truecorners}, the bounding box
9243 @:true_corners_}{\&{truecorners} primitive@>
9244 data might not be valid for all values of this parameter. Hence, the |bbtype|
9245 field is needed to keep track of this.
9247 @d minx_val(A) mp->mem[(A)+2].sc
9248 @d miny_val(A) mp->mem[(A)+3].sc
9249 @d maxx_val(A) mp->mem[(A)+4].sc
9250 @d maxy_val(A) mp->mem[(A)+5].sc
9251 @d bblast(A) link((A)+6) /* last item considered in bounding box computation */
9252 @d bbtype(A) info((A)+6) /* tells how bounding box data depends on \&{truecorners} */
9253 @d dummy_loc(A) ((A)+7) /* where the object list begins in an edge header */
9255 /* |bbtype| value when bounding box data is valid for all \&{truecorners} values */
9257 /* |bbtype| value when bounding box data is for \&{truecorners}${}\le 0$ */
9259 /* |bbtype| value when bounding box data is for \&{truecorners}${}>0$ */
9262 void mp_init_bbox (MP mp,pointer h) {
9263 /* Initialize the bounding box information in edge structure |h| */
9264 bblast(h)=dummy_loc(h);
9265 bbtype(h)=no_bounds;
9266 minx_val(h)=el_gordo;
9267 miny_val(h)=el_gordo;
9268 maxx_val(h)=-el_gordo;
9269 maxy_val(h)=-el_gordo;
9272 @ The only other entries in an edge header are a reference count in the first
9273 word and a pointer to the tail of the object list in the last word.
9275 @d obj_tail(A) info((A)+7) /* points to the last entry in the object list */
9276 @d edge_header_size 8
9279 void mp_init_edges (MP mp,pointer h) {
9280 /* initialize an edge header to null values */
9281 dash_list(h)=null_dash;
9282 obj_tail(h)=dummy_loc(h);
9283 link(dummy_loc(h))=null;
9285 mp_init_bbox(mp, h);
9288 @ Here is how edge structures are deleted. The process can be recursive because
9289 of the need to dereference edge structures that are used as dash patterns.
9292 @d add_edge_ref(A) incr(ref_count((A)))
9293 @d delete_edge_ref(A) { if ( ref_count((A))==null ) mp_toss_edges(mp, (A));
9294 else decr(ref_count((A))); }
9296 @<Declare the recycling subroutines@>=
9297 void mp_flush_dash_list (MP mp,pointer h);
9298 pointer mp_toss_gr_object (MP mp,pointer p) ;
9299 void mp_toss_edges (MP mp,pointer h) ;
9301 @ @c void mp_toss_edges (MP mp,pointer h) {
9302 pointer p,q; /* pointers that scan the list being recycled */
9303 pointer r; /* an edge structure that object |p| refers to */
9304 mp_flush_dash_list(mp, h);
9305 q=link(dummy_loc(h));
9306 while ( (q!=null) ) {
9308 r=mp_toss_gr_object(mp, p);
9309 if ( r!=null ) delete_edge_ref(r);
9311 mp_free_node(mp, h,edge_header_size);
9313 void mp_flush_dash_list (MP mp,pointer h) {
9314 pointer p,q; /* pointers that scan the list being recycled */
9316 while ( q!=null_dash ) {
9318 mp_free_node(mp, p,dash_node_size);
9320 dash_list(h)=null_dash;
9322 pointer mp_toss_gr_object (MP mp,pointer p) {
9323 /* returns an edge structure that needs to be dereferenced */
9324 pointer e; /* the edge structure to return */
9326 @<Prepare to recycle graphical object |p|@>;
9327 mp_free_node(mp, p,mp->gr_object_size[type(p)]);
9331 @ @<Prepare to recycle graphical object |p|@>=
9334 mp_toss_knot_list(mp, path_p(p));
9335 if ( pen_p(p)!=null ) mp_toss_knot_list(mp, pen_p(p));
9336 if ( pre_script(p)!=null ) delete_str_ref(pre_script(p));
9337 if ( post_script(p)!=null ) delete_str_ref(post_script(p));
9340 mp_toss_knot_list(mp, path_p(p));
9341 if ( pen_p(p)!=null ) mp_toss_knot_list(mp, pen_p(p));
9342 if ( pre_script(p)!=null ) delete_str_ref(pre_script(p));
9343 if ( post_script(p)!=null ) delete_str_ref(post_script(p));
9347 delete_str_ref(text_p(p));
9348 if ( pre_script(p)!=null ) delete_str_ref(pre_script(p));
9349 if ( post_script(p)!=null ) delete_str_ref(post_script(p));
9351 case mp_start_clip_code:
9352 case mp_start_bounds_code:
9353 mp_toss_knot_list(mp, path_p(p));
9355 case mp_stop_clip_code:
9356 case mp_stop_bounds_code:
9358 } /* there are no other cases */
9360 @ If we use |add_edge_ref| to ``copy'' edge structures, the real copying needs
9361 to be done before making a significant change to an edge structure. Much of
9362 the work is done in a separate routine |copy_objects| that copies a list of
9363 graphical objects into a new edge header.
9365 @c @<Declare a function called |copy_objects|@>;
9366 pointer mp_private_edges (MP mp,pointer h) {
9367 /* make a private copy of the edge structure headed by |h| */
9368 pointer hh; /* the edge header for the new copy */
9369 pointer p,pp; /* pointers for copying the dash list */
9370 if ( ref_count(h)==null ) {
9374 hh=mp_copy_objects(mp, link(dummy_loc(h)),null);
9375 @<Copy the dash list from |h| to |hh|@>;
9376 @<Copy the bounding box information from |h| to |hh| and make |bblast(hh)|
9377 point into the new object list@>;
9382 @ Here we use the fact that |dash_list(hh)=link(hh)|.
9383 @^data structure assumptions@>
9385 @<Copy the dash list from |h| to |hh|@>=
9386 pp=hh; p=dash_list(h);
9387 while ( (p!=null_dash) ) {
9388 link(pp)=mp_get_node(mp, dash_node_size);
9390 start_x(pp)=start_x(p);
9391 stop_x(pp)=stop_x(p);
9395 dash_y(hh)=dash_y(h)
9397 @ @<Copy the bounding box information from |h| to |hh|...@>=
9398 minx_val(hh)=minx_val(h);
9399 miny_val(hh)=miny_val(h);
9400 maxx_val(hh)=maxx_val(h);
9401 maxy_val(hh)=maxy_val(h);
9402 bbtype(hh)=bbtype(h);
9403 p=dummy_loc(h); pp=dummy_loc(hh);
9404 while ((p!=bblast(h)) ) {
9405 if ( p==null ) mp_confusion(mp, "bblast");
9406 @:this can't happen bblast}{\quad bblast@>
9407 p=link(p); pp=link(pp);
9411 @ Here is the promised routine for copying graphical objects into a new edge
9412 structure. It starts copying at object~|p| and stops just before object~|q|.
9413 If |q| is null, it copies the entire sublist headed at |p|. The resulting edge
9414 structure requires further initialization by |init_bbox|.
9416 @<Declare a function called |copy_objects|@>=
9417 pointer mp_copy_objects (MP mp, pointer p, pointer q) {
9418 pointer hh; /* the new edge header */
9419 pointer pp; /* the last newly copied object */
9420 small_number k; /* temporary register */
9421 hh=mp_get_node(mp, edge_header_size);
9422 dash_list(hh)=null_dash;
9426 @<Make |link(pp)| point to a copy of object |p|, and update |p| and |pp|@>;
9433 @ @<Make |link(pp)| point to a copy of object |p|, and update |p| and |pp|@>=
9434 { k=mp->gr_object_size[type(p)];
9435 link(pp)=mp_get_node(mp, k);
9437 while ( (k>0) ) { decr(k); mp->mem[pp+k]=mp->mem[p+k]; };
9438 @<Fix anything in graphical object |pp| that should differ from the
9439 corresponding field in |p|@>;
9443 @ @<Fix anything in graphical object |pp| that should differ from the...@>=
9445 case mp_start_clip_code:
9446 case mp_start_bounds_code:
9447 path_p(pp)=mp_copy_path(mp, path_p(p));
9450 path_p(pp)=mp_copy_path(mp, path_p(p));
9451 if ( pen_p(p)!=null ) pen_p(pp)=copy_pen(pen_p(p));
9454 path_p(pp)=mp_copy_path(mp, path_p(p));
9455 pen_p(pp)=copy_pen(pen_p(p));
9456 if ( dash_p(p)!=null ) add_edge_ref(dash_p(pp));
9459 add_str_ref(text_p(pp));
9461 case mp_stop_clip_code:
9462 case mp_stop_bounds_code:
9464 } /* there are no other cases */
9466 @ Here is one way to find an acceptable value for the second argument to
9467 |copy_objects|. Given a non-null graphical object list, |skip_1component|
9468 skips past one picture component, where a ``picture component'' is a single
9469 graphical object, or a start bounds or start clip object and everything up
9470 through the matching stop bounds or stop clip object. The macro version avoids
9471 procedure call overhead and error handling: |skip_component(p)(e)| advances |p|
9472 unless |p| points to a stop bounds or stop clip node, in which case it executes
9475 @d skip_component(A)
9476 if ( ! is_start_or_stop((A)) ) (A)=link((A));
9477 else if ( ! is_stop((A)) ) (A)=mp_skip_1component(mp, (A));
9481 pointer mp_skip_1component (MP mp,pointer p) {
9482 integer lev; /* current nesting level */
9485 if ( is_start_or_stop(p) ) {
9486 if ( is_stop(p) ) decr(lev); else incr(lev);
9493 @ Here is a diagnostic routine for printing an edge structure in symbolic form.
9495 @<Declare subroutines for printing expressions@>=
9496 @<Declare subroutines needed by |print_edges|@>;
9497 void mp_print_edges (MP mp,pointer h, char *s, boolean nuline) {
9498 pointer p; /* a graphical object to be printed */
9499 pointer hh,pp; /* temporary pointers */
9500 scaled scf; /* a scale factor for the dash pattern */
9501 boolean ok_to_dash; /* |false| for polygonal pen strokes */
9502 mp_print_diagnostic(mp, "Edge structure",s,nuline);
9504 while ( link(p)!=null ) {
9508 @<Cases for printing graphical object node |p|@>;
9510 mp_print(mp, "[unknown object type!]");
9514 mp_print_nl(mp, "End edges");
9515 if ( p!=obj_tail(h) ) mp_print(mp, "?");
9517 mp_end_diagnostic(mp, true);
9520 @ @<Cases for printing graphical object node |p|@>=
9522 mp_print(mp, "Filled contour ");
9523 mp_print_obj_color(mp, p);
9524 mp_print_char(mp, ':'); mp_print_ln(mp);
9525 mp_pr_path(mp, path_p(p)); mp_print_ln(mp);
9526 if ( (pen_p(p)!=null) ) {
9527 @<Print join type for graphical object |p|@>;
9528 mp_print(mp, " with pen"); mp_print_ln(mp);
9529 mp_pr_pen(mp, pen_p(p));
9533 @ @<Print join type for graphical object |p|@>=
9534 switch (ljoin_val(p)) {
9536 mp_print(mp, "mitered joins limited ");
9537 mp_print_scaled(mp, miterlim_val(p));
9540 mp_print(mp, "round joins");
9543 mp_print(mp, "beveled joins");
9546 mp_print(mp, "?? joins");
9551 @ For stroked nodes, we need to print |lcap_val(p)| as well.
9553 @<Print join and cap types for stroked node |p|@>=
9554 switch (lcap_val(p)) {
9555 case 0:mp_print(mp, "butt"); break;
9556 case 1:mp_print(mp, "round"); break;
9557 case 2:mp_print(mp, "square"); break;
9558 default: mp_print(mp, "??"); break;
9561 mp_print(mp, " ends, ");
9562 @<Print join type for graphical object |p|@>
9564 @ Here is a routine that prints the color of a graphical object if it isn't
9565 black (the default color).
9567 @<Declare subroutines needed by |print_edges|@>=
9568 @<Declare a procedure called |print_compact_node|@>;
9569 void mp_print_obj_color (MP mp,pointer p) {
9570 if ( color_model(p)==grey_model ) {
9571 if ( grey_val(p)>0 ) {
9572 mp_print(mp, "greyed ");
9573 mp_print_compact_node(mp, obj_grey_loc(p),1);
9575 } else if ( color_model(p)==cmyk_model ) {
9576 if ( (cyan_val(p)>0) || (magenta_val(p)>0) ||
9577 (yellow_val(p)>0) || (black_val(p)>0) ) {
9578 mp_print(mp, "processcolored ");
9579 mp_print_compact_node(mp, obj_cyan_loc(p),4);
9581 } else if ( color_model(p)==rgb_model ) {
9582 if ( (red_val(p)>0) || (green_val(p)>0) || (blue_val(p)>0) ) {
9583 mp_print(mp, "colored ");
9584 mp_print_compact_node(mp, obj_red_loc(p),3);
9589 @ We also need a procedure for printing consecutive scaled values as if they
9590 were a known big node.
9592 @<Declare a procedure called |print_compact_node|@>=
9593 void mp_print_compact_node (MP mp,pointer p, small_number k) {
9594 pointer q; /* last location to print */
9596 mp_print_char(mp, '(');
9598 mp_print_scaled(mp, mp->mem[p].sc);
9599 if ( p<q ) mp_print_char(mp, ',');
9602 mp_print_char(mp, ')');
9605 @ @<Cases for printing graphical object node |p|@>=
9607 mp_print(mp, "Filled pen stroke ");
9608 mp_print_obj_color(mp, p);
9609 mp_print_char(mp, ':'); mp_print_ln(mp);
9610 mp_pr_path(mp, path_p(p));
9611 if ( dash_p(p)!=null ) {
9612 mp_print_nl(mp, "dashed (");
9613 @<Finish printing the dash pattern that |p| refers to@>;
9616 @<Print join and cap types for stroked node |p|@>;
9617 mp_print(mp, " with pen"); mp_print_ln(mp);
9618 if ( pen_p(p)==null ) mp_print(mp, "???"); /* shouldn't happen */
9620 else mp_pr_pen(mp, pen_p(p));
9623 @ Normally, the |dash_list| field in an edge header is set to |null_dash|
9624 when it is not known to define a suitable dash pattern. This is disallowed
9625 here because the |dash_p| field should never point to such an edge header.
9626 Note that memory is allocated for |start_x(null_dash)| and we are free to
9627 give it any convenient value.
9629 @<Finish printing the dash pattern that |p| refers to@>=
9630 ok_to_dash=pen_is_elliptical(pen_p(p));
9631 if ( ! ok_to_dash ) scf=unity; else scf=dash_scale(p);
9634 if ( (pp==null_dash) || (dash_y(hh)<0) ) {
9635 mp_print(mp, " ??");
9636 } else { start_x(null_dash)=start_x(pp)+dash_y(hh);
9637 while ( pp!=null_dash ) {
9638 mp_print(mp, "on ");
9639 mp_print_scaled(mp, mp_take_scaled(mp, stop_x(pp)-start_x(pp),scf));
9640 mp_print(mp, " off ");
9641 mp_print_scaled(mp, mp_take_scaled(mp, start_x(link(pp))-stop_x(pp),scf));
9643 if ( pp!=null_dash ) mp_print_char(mp, ' ');
9645 mp_print(mp, ") shifted ");
9646 mp_print_scaled(mp, -mp_take_scaled(mp, mp_dash_offset(mp, hh),scf));
9647 if ( ! ok_to_dash || (dash_y(hh)==0) ) mp_print(mp, " (this will be ignored)");
9650 @ @<Declare subroutines needed by |print_edges|@>=
9651 scaled mp_dash_offset (MP mp,pointer h) {
9652 scaled x; /* the answer */
9653 if ( (dash_list(h)==null_dash) || (dash_y(h)<0) ) mp_confusion(mp, "dash0");
9654 @:this can't happen dash0}{\quad dash0@>
9655 if ( dash_y(h)==0 ) {
9658 x=-(start_x(dash_list(h)) % dash_y(h));
9659 if ( x<0 ) x=x+dash_y(h);
9664 @ @<Cases for printing graphical object node |p|@>=
9666 mp_print_char(mp, '"'); mp_print_str(mp,text_p(p));
9667 mp_print(mp, "\" infont \""); mp_print(mp, mp->font_name[font_n(p)]);
9668 mp_print_char(mp, '"'); mp_print_ln(mp);
9669 mp_print_obj_color(mp, p);
9670 mp_print(mp, "transformed ");
9671 mp_print_compact_node(mp, text_tx_loc(p),6);
9674 @ @<Cases for printing graphical object node |p|@>=
9675 case mp_start_clip_code:
9676 mp_print(mp, "clipping path:");
9678 mp_pr_path(mp, path_p(p));
9680 case mp_stop_clip_code:
9681 mp_print(mp, "stop clipping");
9684 @ @<Cases for printing graphical object node |p|@>=
9685 case mp_start_bounds_code:
9686 mp_print(mp, "setbounds path:");
9688 mp_pr_path(mp, path_p(p));
9690 case mp_stop_bounds_code:
9691 mp_print(mp, "end of setbounds");
9694 @ To initialize the |dash_list| field in an edge header~|h|, we need a
9695 subroutine that scans an edge structure and tries to interpret it as a dash
9696 pattern. This can only be done when there are no filled regions or clipping
9697 paths and all the pen strokes have the same color. The first step is to let
9698 $y_0$ be the initial $y$~coordinate of the first pen stroke. Then we implicitly
9699 project all the pen stroke paths onto the line $y=y_0$ and require that there
9700 be no retracing. If the resulting paths cover a range of $x$~coordinates of
9701 length $\Delta x$, we set |dash_y(h)| to the length of the dash pattern by
9702 finding the maximum of $\Delta x$ and the absolute value of~$y_0$.
9704 @c @<Declare a procedure called |x_retrace_error|@>;
9705 pointer mp_make_dashes (MP mp,pointer h) { /* returns |h| or |null| */
9706 pointer p; /* this scans the stroked nodes in the object list */
9707 pointer p0; /* if not |null| this points to the first stroked node */
9708 pointer pp,qq,rr; /* pointers into |path_p(p)| */
9709 pointer d,dd; /* pointers used to create the dash list */
9710 @<Other local variables in |make_dashes|@>;
9711 scaled y0=0; /* the initial $y$ coordinate */
9712 if ( dash_list(h)!=null_dash )
9715 p=link(dummy_loc(h));
9717 if ( type(p)!=stroked_code ) {
9718 @<Compain that the edge structure contains a node of the wrong type
9719 and |goto not_found|@>;
9722 if ( p0==null ){ p0=p; y0=y_coord(pp); };
9723 @<Make |d| point to a new dash node created from stroke |p| and path |pp|
9724 or |goto not_found| if there is an error@>;
9725 @<Insert |d| into the dash list and |goto not_found| if there is an error@>;
9728 if ( dash_list(h)==null_dash )
9729 goto NOT_FOUND; /* No error message */
9730 @<Scan |dash_list(h)| and deal with any dashes that are themselves dashed@>;
9731 @<Set |dash_y(h)| and merge the first and last dashes if necessary@>;
9734 @<Flush the dash list, recycle |h| and return |null|@>;
9737 @ @<Compain that the edge structure contains a node of the wrong type...@>=
9739 print_err("Picture is too complicated to use as a dash pattern");
9740 help3("When you say `dashed p', picture p should not contain any")
9741 ("text, filled regions, or clipping paths. This time it did")
9742 ("so I'll just make it a solid line instead.");
9743 mp_put_get_error(mp);
9747 @ A similar error occurs when monotonicity fails.
9749 @<Declare a procedure called |x_retrace_error|@>=
9750 void mp_x_retrace_error (MP mp) {
9751 print_err("Picture is too complicated to use as a dash pattern");
9752 help3("When you say `dashed p', every path in p should be monotone")
9753 ("in x and there must be no overlapping. This failed")
9754 ("so I'll just make it a solid line instead.");
9755 mp_put_get_error(mp);
9758 @ We stash |p| in |info(d)| if |dash_p(p)<>0| so that subsequent processing can
9759 handle the case where the pen stroke |p| is itself dashed.
9761 @<Make |d| point to a new dash node created from stroke |p| and path...@>=
9762 @<Make sure |p| and |p0| are the same color and |goto not_found| if there is
9765 if ( link(pp)!=pp ) {
9768 @<Check for retracing between knots |qq| and |rr| and |goto not_found|
9769 if there is a problem@>;
9770 } while (right_type(rr)!=endpoint);
9772 d=mp_get_node(mp, dash_node_size);
9773 if ( dash_p(p)==0 ) info(d)=0; else info(d)=p;
9774 if ( x_coord(pp)<x_coord(rr) ) {
9775 start_x(d)=x_coord(pp);
9776 stop_x(d)=x_coord(rr);
9778 start_x(d)=x_coord(rr);
9779 stop_x(d)=x_coord(pp);
9782 @ We also need to check for the case where the segment from |qq| to |rr| is
9783 monotone in $x$ but is reversed relative to the path from |pp| to |qq|.
9785 @<Check for retracing between knots |qq| and |rr| and |goto not_found|...@>=
9790 if ( (x0>x1) || (x1>x2) || (x2>x3) ) {
9791 if ( (x0<x1) || (x1<x2) || (x2<x3) ) {
9792 if ( mp_ab_vs_cd(mp, x2-x1,x2-x1,x1-x0,x3-x2)>0 ) {
9793 mp_x_retrace_error(mp); goto NOT_FOUND;
9797 if ( (x_coord(pp)>x0) || (x0>x3) ) {
9798 if ( (x_coord(pp)<x0) || (x0<x3) ) {
9799 mp_x_retrace_error(mp); goto NOT_FOUND;
9803 @ @<Other local variables in |make_dashes|@>=
9804 scaled x0,x1,x2,x3; /* $x$ coordinates of the segment from |qq| to |rr| */
9806 @ @<Make sure |p| and |p0| are the same color and |goto not_found|...@>=
9807 if ( (red_val(p)!=red_val(p0)) || (black_val(p)!=black_val(p0)) ||
9808 (green_val(p)!=green_val(p0)) || (blue_val(p)!=blue_val(p0)) ) {
9809 print_err("Picture is too complicated to use as a dash pattern");
9810 help3("When you say `dashed p', everything in picture p should")
9811 ("be the same color. I can\'t handle your color changes")
9812 ("so I'll just make it a solid line instead.");
9813 mp_put_get_error(mp);
9817 @ @<Insert |d| into the dash list and |goto not_found| if there is an error@>=
9818 start_x(null_dash)=stop_x(d);
9819 dd=h; /* this makes |link(dd)=dash_list(h)| */
9820 while ( start_x(link(dd))<stop_x(d) )
9823 if ( (stop_x(dd)>start_x(d)) )
9824 { mp_x_retrace_error(mp); goto NOT_FOUND; };
9829 @ @<Set |dash_y(h)| and merge the first and last dashes if necessary@>=
9831 while ( (link(d)!=null_dash) )
9834 dash_y(h)=stop_x(d)-start_x(dd);
9835 if ( abs(y0)>dash_y(h) ) {
9837 } else if ( d!=dd ) {
9838 dash_list(h)=link(dd);
9839 stop_x(d)=stop_x(dd)+dash_y(h);
9840 mp_free_node(mp, dd,dash_node_size);
9843 @ We get here when the argument is a null picture or when there is an error.
9844 Recovering from an error involves making |dash_list(h)| empty to indicate
9845 that |h| is not known to be a valid dash pattern. We also dereference |h|
9846 since it is not being used for the return value.
9848 @<Flush the dash list, recycle |h| and return |null|@>=
9849 mp_flush_dash_list(mp, h);
9853 @ Having carefully saved the dashed stroked nodes in the
9854 corresponding dash nodes, we must be prepared to break up these dashes into
9857 @<Scan |dash_list(h)| and deal with any dashes that are themselves dashed@>=
9858 d=h; /* now |link(d)=dash_list(h)| */
9859 while ( link(d)!=null_dash ) {
9866 if ( (hh==null) ) mp_confusion(mp, "dash1");
9867 @:this can't happen dash0}{\quad dash1@>
9868 if ( dash_y(hh)==0 ) {
9871 if ( dash_list(hh)==null ) mp_confusion(mp, "dash1");
9872 @:this can't happen dash0}{\quad dash1@>
9873 @<Replace |link(d)| by a dashed version as determined by edge header
9874 |hh| and scale factor |ds|@>;
9879 @ @<Other local variables in |make_dashes|@>=
9880 pointer dln; /* |link(d)| */
9881 pointer hh; /* an edge header that tells how to break up |dln| */
9882 scaled hsf; /* the dash pattern from |hh| gets scaled by this */
9883 pointer ds; /* the stroked node from which |hh| and |hsf| are derived */
9884 scaled xoff; /* added to $x$ values in |dash_list(hh)| to match |dln| */
9886 @ @<Replace |link(d)| by a dashed version as determined by edge header...@>=
9889 xoff=start_x(dln)-mp_take_scaled(mp, hsf,start_x(dd))-
9890 mp_take_scaled(mp, hsf,mp_dash_offset(mp, hh));
9891 start_x(null_dash)=mp_take_scaled(mp, hsf,start_x(dd))
9892 +mp_take_scaled(mp, hsf,dash_y(hh));
9893 stop_x(null_dash)=start_x(null_dash);
9894 @<Advance |dd| until finding the first dash that overlaps |dln| when
9896 while ( start_x(dln)<=stop_x(dln) ) {
9897 @<If |dd| has `fallen off the end', back up to the beginning and fix |xoff|@>;
9898 @<Insert a dash between |d| and |dln| for the overlap with the offset version
9901 start_x(dln)=xoff+mp_take_scaled(mp, hsf,start_x(dd));
9904 mp_free_node(mp, dln,dash_node_size)
9906 @ The name of this module is a bit of a lie because we actually just find the
9907 first |dd| where |take_scaled (hsf, stop_x(dd))| is large enough to make an
9908 overlap possible. It could be that the unoffset version of dash |dln| falls
9909 in the gap between |dd| and its predecessor.
9911 @<Advance |dd| until finding the first dash that overlaps |dln| when...@>=
9912 while ( xoff+mp_take_scaled(mp, hsf,stop_x(dd))<start_x(dln) ) {
9916 @ @<If |dd| has `fallen off the end', back up to the beginning and fix...@>=
9917 if ( dd==null_dash ) {
9919 xoff=xoff+mp_take_scaled(mp, hsf,dash_y(hh));
9922 @ At this point we already know that
9923 |start_x(dln)<=xoff+take_scaled(hsf,stop_x(dd))|.
9925 @<Insert a dash between |d| and |dln| for the overlap with the offset...@>=
9926 if ( xoff+mp_take_scaled(mp, hsf,start_x(dd))<=stop_x(dln) ) {
9927 link(d)=mp_get_node(mp, dash_node_size);
9930 if ( start_x(dln)>xoff+mp_take_scaled(mp, hsf,start_x(dd)))
9931 start_x(d)=start_x(dln);
9933 start_x(d)=xoff+mp_take_scaled(mp, hsf,start_x(dd));
9934 if ( stop_x(dln)<xoff+mp_take_scaled(mp, hsf,stop_x(dd)) )
9935 stop_x(d)=stop_x(dln);
9937 stop_x(d)=xoff+mp_take_scaled(mp, hsf,stop_x(dd));
9940 @ The next major task is to update the bounding box information in an edge
9941 header~|h|. This is done via a procedure |adjust_bbox| that enlarges an edge
9942 header's bounding box to accommodate the box computed by |path_bbox| or
9943 |pen_bbox|. (This is stored in global variables |minx|, |miny|, |maxx|, and
9946 @c void mp_adjust_bbox (MP mp,pointer h) {
9947 if ( minx<minx_val(h) ) minx_val(h)=minx;
9948 if ( miny<miny_val(h) ) miny_val(h)=miny;
9949 if ( maxx>maxx_val(h) ) maxx_val(h)=maxx;
9950 if ( maxy>maxy_val(h) ) maxy_val(h)=maxy;
9953 @ Here is a special routine for updating the bounding box information in
9954 edge header~|h| to account for the squared-off ends of a non-cyclic path~|p|
9955 that is to be stroked with the pen~|pp|.
9957 @c void mp_box_ends (MP mp, pointer p, pointer pp, pointer h) {
9958 pointer q; /* a knot node adjacent to knot |p| */
9959 fraction dx,dy; /* a unit vector in the direction out of the path at~|p| */
9960 scaled d; /* a factor for adjusting the length of |(dx,dy)| */
9961 scaled z; /* a coordinate being tested against the bounding box */
9962 scaled xx,yy; /* the extreme pen vertex in the |(dx,dy)| direction */
9963 integer i; /* a loop counter */
9964 if ( right_type(p)!=endpoint ) {
9967 @<Make |(dx,dy)| the final direction for the path segment from
9968 |q| to~|p|; set~|d|@>;
9969 d=mp_pyth_add(mp, dx,dy);
9971 @<Normalize the direction |(dx,dy)| and find the pen offset |(xx,yy)|@>;
9972 for (i=1;i<= 2;i++) {
9973 @<Use |(dx,dy)| to generate a vertex of the square end cap and
9974 update the bounding box to accommodate it@>;
9978 if ( right_type(p)==endpoint ) {
9981 @<Advance |p| to the end of the path and make |q| the previous knot@>;
9987 @ @<Make |(dx,dy)| the final direction for the path segment from...@>=
9989 dx=x_coord(p)-right_x(p);
9990 dy=y_coord(p)-right_y(p);
9991 if ( (dx==0)&&(dy==0) ) {
9992 dx=x_coord(p)-left_x(q);
9993 dy=y_coord(p)-left_y(q);
9996 dx=x_coord(p)-left_x(p);
9997 dy=y_coord(p)-left_y(p);
9998 if ( (dx==0)&&(dy==0) ) {
9999 dx=x_coord(p)-right_x(q);
10000 dy=y_coord(p)-right_y(q);
10003 dx=x_coord(p)-x_coord(q);
10004 dy=y_coord(p)-y_coord(q)
10006 @ @<Normalize the direction |(dx,dy)| and find the pen offset |(xx,yy)|@>=
10007 dx=mp_make_fraction(mp, dx,d);
10008 dy=mp_make_fraction(mp, dy,d);
10009 mp_find_offset(mp, -dy,dx,pp);
10010 xx=mp->cur_x; yy=mp->cur_y
10012 @ @<Use |(dx,dy)| to generate a vertex of the square end cap and...@>=
10013 mp_find_offset(mp, dx,dy,pp);
10014 d=mp_take_fraction(mp, xx-mp->cur_x,dx)+mp_take_fraction(mp, yy-mp->cur_y,dy);
10015 if ( ((d<0)&&(i==1)) || ((d>0)&&(i==2)))
10016 mp_confusion(mp, "box_ends");
10017 @:this can't happen box ends}{\quad\\{box\_ends}@>
10018 z=x_coord(p)+mp->cur_x+mp_take_fraction(mp, d,dx);
10019 if ( z<minx_val(h) ) minx_val(h)=z;
10020 if ( z>maxx_val(h) ) maxx_val(h)=z;
10021 z=y_coord(p)+mp->cur_y+mp_take_fraction(mp, d,dy);
10022 if ( z<miny_val(h) ) miny_val(h)=z;
10023 if ( z>maxy_val(h) ) maxy_val(h)=z
10025 @ @<Advance |p| to the end of the path and make |q| the previous knot@>=
10029 } while (right_type(p)!=endpoint)
10031 @ The major difficulty in finding the bounding box of an edge structure is the
10032 effect of clipping paths. We treat them conservatively by only clipping to the
10033 clipping path's bounding box, but this still
10034 requires recursive calls to |set_bbox| in order to find the bounding box of
10036 the objects to be clipped. Such calls are distinguished by the fact that the
10037 boolean parameter |top_level| is false.
10039 @c void mp_set_bbox (MP mp,pointer h, boolean top_level) {
10040 pointer p; /* a graphical object being considered */
10041 scaled sminx,sminy,smaxx,smaxy;
10042 /* for saving the bounding box during recursive calls */
10043 scaled x0,x1,y0,y1; /* temporary registers */
10044 integer lev; /* nesting level for |mp_start_bounds_code| nodes */
10045 @<Wipe out any existing bounding box information if |bbtype(h)| is
10046 incompatible with |internal[true_corners]|@>;
10047 while ( link(bblast(h))!=null ) {
10051 case mp_stop_clip_code:
10052 if ( top_level ) mp_confusion(mp, "bbox"); else return;
10053 @:this can't happen bbox}{\quad bbox@>
10055 @<Other cases for updating the bounding box based on the type of object |p|@>;
10056 } /* all cases are enumerated above */
10058 if ( ! top_level ) mp_confusion(mp, "bbox");
10061 @ @<Wipe out any existing bounding box information if |bbtype(h)| is...@>=
10062 switch (bbtype(h)) {
10066 if ( mp->internal[true_corners]>0 ) mp_init_bbox(mp, h);
10069 if ( mp->internal[true_corners]<=0 ) mp_init_bbox(mp, h);
10071 } /* there are no other cases */
10073 @ @<Other cases for updating the bounding box...@>=
10075 mp_path_bbox(mp, path_p(p));
10076 if ( pen_p(p)!=null ) {
10079 mp_pen_bbox(mp, pen_p(p));
10085 mp_adjust_bbox(mp, h);
10088 @ @<Other cases for updating the bounding box...@>=
10089 case mp_start_bounds_code:
10090 if ( mp->internal[true_corners]>0 ) {
10091 bbtype(h)=bounds_unset;
10093 bbtype(h)=bounds_set;
10094 mp_path_bbox(mp, path_p(p));
10095 mp_adjust_bbox(mp, h);
10096 @<Scan to the matching |mp_stop_bounds_code| node and update |p| and
10100 case mp_stop_bounds_code:
10101 if ( mp->internal[true_corners]<=0 ) mp_confusion(mp, "bbox2");
10102 @:this can't happen bbox2}{\quad bbox2@>
10105 @ @<Scan to the matching |mp_stop_bounds_code| node and update |p| and...@>=
10108 if ( link(p)==null ) mp_confusion(mp, "bbox2");
10109 @:this can't happen bbox2}{\quad bbox2@>
10111 if ( type(p)==mp_start_bounds_code ) incr(lev);
10112 else if ( type(p)==mp_stop_bounds_code ) decr(lev);
10116 @ It saves a lot of grief here to be slightly conservative and not account for
10117 omitted parts of dashed lines. We also don't worry about the material omitted
10118 when using butt end caps. The basic computation is for round end caps and
10119 |box_ends| augments it for square end caps.
10121 @<Other cases for updating the bounding box...@>=
10123 mp_path_bbox(mp, path_p(p));
10126 mp_pen_bbox(mp, pen_p(p));
10131 mp_adjust_bbox(mp, h);
10132 if ( (left_type(path_p(p))==endpoint)&&(lcap_val(p)==2) )
10133 mp_box_ends(mp, path_p(p), pen_p(p), h);
10136 @ The height width and depth information stored in a text node determines a
10137 rectangle that needs to be transformed according to the transformation
10138 parameters stored in the text node.
10140 @<Other cases for updating the bounding box...@>=
10142 x1=mp_take_scaled(mp, txx_val(p),width_val(p));
10143 y0=mp_take_scaled(mp, txy_val(p),-depth_val(p));
10144 y1=mp_take_scaled(mp, txy_val(p),height_val(p));
10147 if ( y0<y1 ) { minx=minx+y0; maxx=maxx+y1; }
10148 else { minx=minx+y1; maxx=maxx+y0; }
10149 if ( x1<0 ) minx=minx+x1; else maxx=maxx+x1;
10150 x1=mp_take_scaled(mp, tyx_val(p),width_val(p));
10151 y0=mp_take_scaled(mp, tyy_val(p),-depth_val(p));
10152 y1=mp_take_scaled(mp, tyy_val(p),height_val(p));
10155 if ( y0<y1 ) { miny=miny+y0; maxy=maxy+y1; }
10156 else { miny=miny+y1; maxy=maxy+y0; }
10157 if ( x1<0 ) miny=miny+x1; else maxy=maxy+x1;
10158 mp_adjust_bbox(mp, h);
10161 @ This case involves a recursive call that advances |bblast(h)| to the node of
10162 type |mp_stop_clip_code| that matches |p|.
10164 @<Other cases for updating the bounding box...@>=
10165 case mp_start_clip_code:
10166 mp_path_bbox(mp, path_p(p));
10169 sminx=minx_val(h); sminy=miny_val(h);
10170 smaxx=maxx_val(h); smaxy=maxy_val(h);
10171 @<Reinitialize the bounding box in header |h| and call |set_bbox| recursively
10172 starting at |link(p)|@>;
10173 @<Clip the bounding box in |h| to the rectangle given by |x0|, |x1|,
10175 minx=sminx; miny=sminy;
10176 maxx=smaxx; maxy=smaxy;
10177 mp_adjust_bbox(mp, h);
10180 @ @<Reinitialize the bounding box in header |h| and call |set_bbox|...@>=
10181 minx_val(h)=el_gordo;
10182 miny_val(h)=el_gordo;
10183 maxx_val(h)=-el_gordo;
10184 maxy_val(h)=-el_gordo;
10185 mp_set_bbox(mp, h,false)
10187 @ @<Clip the bounding box in |h| to the rectangle given by |x0|, |x1|,...@>=
10188 if ( minx_val(h)<x0 ) minx_val(h)=x0;
10189 if ( miny_val(h)<y0 ) miny_val(h)=y0;
10190 if ( maxx_val(h)>x1 ) maxx_val(h)=x1;
10191 if ( maxy_val(h)>y1 ) maxy_val(h)=y1
10193 @* \[22] Finding an envelope.
10194 When \MP\ has a path and a polygonal pen, it needs to express the desired
10195 shape in terms of things \ps\ can understand. The present task is to compute
10196 a new path that describes the region to be filled. It is convenient to
10197 define this as a two step process where the first step is determining what
10198 offset to use for each segment of the path.
10200 @ Given a pointer |c| to a cyclic path,
10201 and a pointer~|h| to the first knot of a pen polygon,
10202 the |offset_prep| routine changes the path into cubics that are
10203 associated with particular pen offsets. Thus if the cubic between |p|
10204 and~|q| is associated with the |k|th offset and the cubic between |q| and~|r|
10205 has offset |l| then |info(q)=zero_off+l-k|. (The constant |zero_off| is added
10206 to because |l-k| could be negative.)
10208 After overwriting the type information with offset differences, we no longer
10209 have a true path so we refer to the knot list returned by |offset_prep| as an
10212 Since an envelope spec only determines relative changes in pen offsets,
10213 |offset_prep| sets a global variable |spec_offset| to the relative change from
10214 |h| to the first offset.
10216 @d zero_off 16384 /* added to offset changes to make them positive */
10219 integer spec_offset; /* number of pen edges between |h| and the initial offset */
10221 @ @c @<Declare subroutines needed by |offset_prep|@>;
10222 pointer mp_offset_prep (MP mp,pointer c, pointer h) {
10223 halfword n; /* the number of vertices in the pen polygon */
10224 pointer p,q,r,w, ww; /* for list manipulation */
10225 integer k_needed; /* amount to be added to |info(p)| when it is computed */
10226 pointer w0; /* a pointer to pen offset to use just before |p| */
10227 scaled dxin,dyin; /* the direction into knot |p| */
10228 integer turn_amt; /* change in pen offsets for the current cubic */
10229 @<Other local variables for |offset_prep|@>;
10231 @<Initialize the pen size~|n|@>;
10232 @<Initialize the incoming direction and pen offset at |c|@>;
10236 @<Split the cubic between |p| and |q|, if necessary, into cubics
10237 associated with single offsets, after which |q| should
10238 point to the end of the final such cubic@>;
10239 @<Advance |p| to node |q|, removing any ``dead'' cubics that
10240 might have been introduced by the splitting process@>;
10242 @<Fix the offset change in |info(c)| and set the return value of
10246 @ We shall want to keep track of where certain knots on the cyclic path
10247 wind up in the envelope spec. It doesn't suffice just to keep pointers to
10248 knot nodes because some nodes are deleted while removing dead cubics. Thus
10249 |offset_prep| updates the following pointers
10253 pointer spec_p2; /* pointers to distinguished knots */
10256 mp->spec_p1=null; mp->spec_p2=null;
10258 @ @<Initialize the pen size~|n|@>=
10265 @ Since the true incoming direction isn't known yet, we just pick a direction
10266 consistent with the pen offset~|h|. If this is wrong, it can be corrected
10269 @<Initialize the incoming direction and pen offset at |c|@>=
10270 dxin=x_coord(link(h))-x_coord(knil(h));
10271 dyin=y_coord(link(h))-y_coord(knil(h));
10272 if ( (dxin==0)&&(dyin==0) ) {
10273 dxin=y_coord(knil(h))-y_coord(h);
10274 dyin=x_coord(h)-x_coord(knil(h));
10278 @ We must be careful not to remove the only cubic in a cycle.
10280 But we must also be careful for another reason. If the user-supplied
10281 path starts with a set of degenerate cubics, these should not be removed
10282 because at this point we cannot do so cleanly. The relevant bug is
10283 tracker id 267, bugs 52c, reported by Boguslav.
10285 @<Advance |p| to node |q|, removing any ``dead'' cubics...@>=
10287 if ( x_coord(p)==right_x(p) ) if ( y_coord(p)==right_y(p) )
10288 if ( x_coord(p)==left_x(r) ) if ( y_coord(p)==left_y(r) )
10289 if ( x_coord(p)==x_coord(r) ) if ( y_coord(p)==y_coord(r) )
10290 if ( r!=p ) if ( ((r!=q) || (originator(r)!=metapost_user)) ) {
10291 @<Remove the cubic following |p| and update the data structures
10292 to merge |r| into |p|@>;
10297 @ @<Remove the cubic following |p| and update the data structures...@>=
10298 { k_needed=info(p)-zero_off;
10302 info(p)=k_needed+info(r);
10305 if ( r==c ) { info(p)=info(c); c=p; };
10306 if ( r==mp->spec_p1 ) mp->spec_p1=p;
10307 if ( r==mp->spec_p2 ) mp->spec_p2=p;
10308 r=p; mp_remove_cubic(mp, p);
10311 @ Not setting the |info| field of the newly created knot allows the splitting
10312 routine to work for paths.
10314 @<Declare subroutines needed by |offset_prep|@>=
10315 void mp_split_cubic (MP mp,pointer p, fraction t) { /* splits the cubic after |p| */
10316 scaled v; /* an intermediate value */
10317 pointer q,r; /* for list manipulation */
10318 q=link(p); r=mp_get_node(mp, knot_node_size); link(p)=r; link(r)=q;
10319 originator(r)=program_code;
10320 left_type(r)=explicit; right_type(r)=explicit;
10321 v=t_of_the_way(right_x(p),left_x(q));
10322 right_x(p)=t_of_the_way(x_coord(p),right_x(p));
10323 left_x(q)=t_of_the_way(left_x(q),x_coord(q));
10324 left_x(r)=t_of_the_way(right_x(p),v);
10325 right_x(r)=t_of_the_way(v,left_x(q));
10326 x_coord(r)=t_of_the_way(left_x(r),right_x(r));
10327 v=t_of_the_way(right_y(p),left_y(q));
10328 right_y(p)=t_of_the_way(y_coord(p),right_y(p));
10329 left_y(q)=t_of_the_way(left_y(q),y_coord(q));
10330 left_y(r)=t_of_the_way(right_y(p),v);
10331 right_y(r)=t_of_the_way(v,left_y(q));
10332 y_coord(r)=t_of_the_way(left_y(r),right_y(r));
10335 @ This does not set |info(p)| or |right_type(p)|.
10337 @<Declare subroutines needed by |offset_prep|@>=
10338 void mp_remove_cubic (MP mp,pointer p) { /* removes the dead cubic following~|p| */
10339 pointer q; /* the node that disappears */
10340 q=link(p); link(p)=link(q);
10341 right_x(p)=right_x(q); right_y(p)=right_y(q);
10342 mp_free_node(mp, q,knot_node_size);
10345 @ Let $d\prec d'$ mean that the counter-clockwise angle from $d$ to~$d'$ is
10346 strictly between zero and $180^\circ$. Then we can define $d\preceq d'$ to
10347 mean that the angle could be zero or $180^\circ$. If $w_k=(u_k,v_k)$ is the
10348 $k$th pen offset, the $k$th pen edge direction is defined by the formula
10349 $$d_k=(u\k-u_k,\,v\k-v_k).$$
10350 When listed by increasing $k$, these directions occur in counter-clockwise
10351 order so that $d_k\preceq d\k$ for all~$k$.
10352 The goal of |offset_prep| is to find an offset index~|k| to associate with
10353 each cubic, such that the direction $d(t)$ of the cubic satisfies
10354 $$d_{k-1}\preceq d(t)\preceq d_k\qquad\hbox{for $0\le t\le 1$.}\eqno(*)$$
10355 We may have to split a cubic into many pieces before each
10356 piece corresponds to a unique offset.
10358 @<Split the cubic between |p| and |q|, if necessary, into cubics...@>=
10359 info(p)=zero_off+k_needed;
10361 @<Prepare for derivative computations;
10362 |goto not_found| if the current cubic is dead@>;
10363 @<Find the initial direction |(dx,dy)|@>;
10364 @<Update |info(p)| and find the offset $w_k$ such that
10365 $d_{k-1}\preceq(\\{dx},\\{dy})\prec d_k$; also advance |w0| for
10366 the direction change at |p|@>;
10367 @<Find the final direction |(dxin,dyin)|@>;
10368 @<Decide on the net change in pen offsets and set |turn_amt|@>;
10369 @<Complete the offset splitting process@>;
10370 w0=mp_pen_walk(mp, w0,turn_amt);
10371 NOT_FOUND: do_nothing
10373 @ @<Declare subroutines needed by |offset_prep|@>=
10374 pointer mp_pen_walk (MP mp,pointer w, integer k) {
10375 /* walk |k| steps around a pen from |w| */
10376 while ( k>0 ) { w=link(w); decr(k); };
10377 while ( k<0 ) { w=knil(w); incr(k); };
10381 @ The direction of a cubic $B(z_0,z_1,z_2,z_3;t)=\bigl(x(t),y(t)\bigr)$ can be
10382 calculated from the quadratic polynomials
10383 ${1\over3}x'(t)=B(x_1-x_0,x_2-x_1,x_3-x_2;t)$ and
10384 ${1\over3}y'(t)=B(y_1-y_0,y_2-y_1,y_3-y_2;t)$.
10385 Since we may be calculating directions from several cubics
10386 split from the current one, it is desirable to do these calculations
10387 without losing too much precision. ``Scaled up'' values of the
10388 derivatives, which will be less tainted by accumulated errors than
10389 derivatives found from the cubics themselves, are maintained in
10390 local variables |x0|, |x1|, and |x2|, representing $X_0=2^l(x_1-x_0)$,
10391 $X_1=2^l(x_2-x_1)$, and $X_2=2^l(x_3-x_2)$; similarly |y0|, |y1|, and~|y2|
10392 represent $Y_0=2^l(y_1-y_0)$, $Y_1=2^l(y_2-y_1)$, and $Y_2=2^l(y_3-y_2)$.
10394 @<Other local variables for |offset_prep|@>=
10395 integer x0,x1,x2,y0,y1,y2; /* representatives of derivatives */
10396 integer t0,t1,t2; /* coefficients of polynomial for slope testing */
10397 integer du,dv,dx,dy; /* for directions of the pen and the curve */
10398 integer dx0,dy0; /* initial direction for the first cubic in the curve */
10399 integer mp_max_coef; /* used while scaling */
10400 integer x0a,x1a,x2a,y0a,y1a,y2a; /* intermediate values */
10401 fraction t; /* where the derivative passes through zero */
10402 fraction s; /* a temporary value */
10404 @ @<Prepare for derivative computations...@>=
10405 x0=right_x(p)-x_coord(p);
10406 x2=x_coord(q)-left_x(q);
10407 x1=left_x(q)-right_x(p);
10408 y0=right_y(p)-y_coord(p); y2=y_coord(q)-left_y(q);
10409 y1=left_y(q)-right_y(p);
10410 mp_max_coef=abs(x0);
10411 if ( abs(x1)>mp_max_coef ) mp_max_coef=abs(x1);
10412 if ( abs(x2)>mp_max_coef ) mp_max_coef=abs(x2);
10413 if ( abs(y0)>mp_max_coef ) mp_max_coef=abs(y0);
10414 if ( abs(y1)>mp_max_coef ) mp_max_coef=abs(y1);
10415 if ( abs(y2)>mp_max_coef ) mp_max_coef=abs(y2);
10416 if ( mp_max_coef==0 ) goto NOT_FOUND;
10417 while ( mp_max_coef<fraction_half ) {
10418 mp_max_coef+=mp_max_coef;
10419 x0+=x0; x1+=x1; x2+=x2;
10420 y0+=y0; y1+=y1; y2+=y2;
10423 @ Let us first solve a special case of the problem: Suppose we
10424 know an index~$k$ such that either (i)~$d(t)\succeq d_{k-1}$ for all~$t$
10425 and $d(0)\prec d_k$, or (ii)~$d(t)\preceq d_k$ for all~$t$ and
10426 $d(0)\succ d_{k-1}$.
10427 Then, in a sense, we're halfway done, since one of the two relations
10428 in $(*)$ is satisfied, and the other couldn't be satisfied for
10429 any other value of~|k|.
10431 Actually, the conditions can be relaxed somewhat since a relation such as
10432 $d(t)\succeq d_{k-1}$ restricts $d(t)$ to a half plane when all that really
10433 matters is whether $d(t)$ crosses the ray in the $d_{k-1}$ direction from
10434 the origin. The condition for case~(i) becomes $d_{k-1}\preceq d(0)\prec d_k$
10435 and $d(t)$ never crosses the $d_{k-1}$ ray in the clockwise direction.
10436 Case~(ii) is similar except $d(t)$ cannot cross the $d_k$ ray in the
10437 counterclockwise direction.
10439 The |fin_offset_prep| subroutine solves the stated subproblem.
10440 It has a parameter called |rise| that is |1| in
10441 case~(i), |-1| in case~(ii). Parameters |x0| through |y2| represent
10442 the derivative of the cubic following |p|.
10443 The |w| parameter should point to offset~$w_k$ and |info(p)| should already
10444 be set properly. The |turn_amt| parameter gives the absolute value of the
10445 overall net change in pen offsets.
10447 @<Declare subroutines needed by |offset_prep|@>=
10448 void mp_fin_offset_prep (MP mp,pointer p, pointer w, integer
10449 x0,integer x1, integer x2, integer y0, integer y1, integer y2,
10450 integer rise, integer turn_amt) {
10451 pointer ww; /* for list manipulation */
10452 scaled du,dv; /* for slope calculation */
10453 integer t0,t1,t2; /* test coefficients */
10454 fraction t; /* place where the derivative passes a critical slope */
10455 fraction s; /* slope or reciprocal slope */
10456 integer v; /* intermediate value for updating |x0..y2| */
10457 pointer q; /* original |link(p)| */
10460 if ( rise>0 ) ww=link(w); /* a pointer to $w\k$ */
10461 else ww=knil(w); /* a pointer to $w_{k-1}$ */
10462 @<Compute test coefficients |(t0,t1,t2)|
10463 for $d(t)$ versus $d_k$ or $d_{k-1}$@>;
10464 t=mp_crossing_point(mp, t0,t1,t2);
10465 if ( t>=fraction_one ) {
10466 if ( turn_amt>0 ) t=fraction_one; else return;
10468 @<Split the cubic at $t$,
10469 and split off another cubic if the derivative crosses back@>;
10474 @ We want $B(\\{t0},\\{t1},\\{t2};t)$ to be the dot product of $d(t)$ with a
10475 $-90^\circ$ rotation of the vector from |w| to |ww|. This makes the resulting
10476 function cross from positive to negative when $d_{k-1}\preceq d(t)\preceq d_k$
10479 @<Compute test coefficients |(t0,t1,t2)| for $d(t)$ versus...@>=
10480 du=x_coord(ww)-x_coord(w); dv=y_coord(ww)-y_coord(w);
10481 if ( abs(du)>=abs(dv) ) {
10482 s=mp_make_fraction(mp, dv,du);
10483 t0=mp_take_fraction(mp, x0,s)-y0;
10484 t1=mp_take_fraction(mp, x1,s)-y1;
10485 t2=mp_take_fraction(mp, x2,s)-y2;
10486 if ( du<0 ) { negate(t0); negate(t1); negate(t2); }
10488 s=mp_make_fraction(mp, du,dv);
10489 t0=x0-mp_take_fraction(mp, y0,s);
10490 t1=x1-mp_take_fraction(mp, y1,s);
10491 t2=x2-mp_take_fraction(mp, y2,s);
10492 if ( dv<0 ) { negate(t0); negate(t1); negate(t2); }
10494 if ( t0<0 ) t0=0 /* should be positive without rounding error */
10496 @ The curve has crossed $d_k$ or $d_{k-1}$; its initial segment satisfies
10497 $(*)$, and it might cross again, yielding another solution of $(*)$.
10499 @<Split the cubic at $t$, and split off another...@>=
10501 mp_split_cubic(mp, p,t); p=link(p); info(p)=zero_off+rise;
10503 v=t_of_the_way(x0,x1); x1=t_of_the_way(x1,x2);
10504 x0=t_of_the_way(v,x1);
10505 v=t_of_the_way(y0,y1); y1=t_of_the_way(y1,y2);
10506 y0=t_of_the_way(v,y1);
10507 if ( turn_amt<0 ) {
10508 t1=t_of_the_way(t1,t2);
10509 if ( t1>0 ) t1=0; /* without rounding error, |t1| would be |<=0| */
10510 t=mp_crossing_point(mp, 0,-t1,-t2);
10511 if ( t>fraction_one ) t=fraction_one;
10513 if ( (t==fraction_one)&&(link(p)!=q) ) {
10514 info(link(p))=info(link(p))-rise;
10516 mp_split_cubic(mp, p,t); info(link(p))=zero_off-rise;
10517 v=t_of_the_way(x1,x2); x1=t_of_the_way(x0,x1);
10518 x2=t_of_the_way(x1,v);
10519 v=t_of_the_way(y1,y2); y1=t_of_the_way(y0,y1);
10520 y2=t_of_the_way(y1,v);
10525 @ Now we must consider the general problem of |offset_prep|, when
10526 nothing is known about a given cubic. We start by finding its
10527 direction in the vicinity of |t=0|.
10529 If $z'(t)=0$, the given cubic is numerically unstable but |offset_prep|
10530 has not yet introduced any more numerical errors. Thus we can compute
10531 the true initial direction for the given cubic, even if it is almost
10534 @<Find the initial direction |(dx,dy)|@>=
10536 if ( dx==0 ) if ( dy==0 ) {
10538 if ( dx==0 ) if ( dy==0 ) {
10542 if ( p==c ) { dx0=dx; dy0=dy; }
10544 @ @<Find the final direction |(dxin,dyin)|@>=
10546 if ( dxin==0 ) if ( dyin==0 ) {
10548 if ( dxin==0 ) if ( dyin==0 ) {
10553 @ The next step is to bracket the initial direction between consecutive
10554 edges of the pen polygon. We must be careful to turn clockwise only if
10555 this makes the turn less than $180^\circ$. (A $180^\circ$ turn must be
10556 counter-clockwise in order to make \&{doublepath} envelopes come out
10557 @:double_path_}{\&{doublepath} primitive@>
10558 right.) This code depends on |w0| being the offset for |(dxin,dyin)|.
10560 @<Update |info(p)| and find the offset $w_k$ such that...@>=
10561 turn_amt=mp_get_turn_amt(mp, w0, dx, dy, mp_ab_vs_cd(mp, dy,dxin,dx,dyin)>=0);
10562 w=mp_pen_walk(mp, w0, turn_amt);
10564 info(p)=info(p)+turn_amt
10566 @ Decide how many pen offsets to go away from |w| in order to find the offset
10567 for |(dx,dy)|, going counterclockwise if |ccw| is |true|. This assumes that
10568 |w| is the offset for some direction $(x',y')$ from which the angle to |(dx,dy)|
10569 in the sense determined by |ccw| is less than or equal to $180^\circ$.
10571 If the pen polygon has only two edges, they could both be parallel
10572 to |(dx,dy)|. In this case, we must be careful to stop after crossing the first
10573 such edge in order to avoid an infinite loop.
10575 @<Declare subroutines needed by |offset_prep|@>=
10576 integer mp_get_turn_amt (MP mp,pointer w, scaled dx,
10577 scaled dy, boolean ccw) {
10578 pointer ww; /* a neighbor of knot~|w| */
10579 integer s; /* turn amount so far */
10580 integer t; /* |ab_vs_cd| result */
10585 t=mp_ab_vs_cd(mp, dy,x_coord(ww)-x_coord(w),
10586 dx,y_coord(ww)-y_coord(w));
10593 while ( mp_ab_vs_cd(mp, dy,x_coord(w)-x_coord(ww),
10594 dx,y_coord(w)-y_coord(ww))<0 ) {
10602 @ When we're all done, the final offset is |w0| and the final curve direction
10603 is |(dxin,dyin)|. With this knowledge of the incoming direction at |c|, we
10604 can correct |info(c)| which was erroneously based on an incoming offset
10607 @d fix_by(A) info(c)=info(c)+(A)
10609 @<Fix the offset change in |info(c)| and set the return value of...@>=
10610 mp->spec_offset=info(c)-zero_off;
10611 if ( link(c)==c ) {
10612 info(c)=zero_off+n;
10615 while ( w0!=h ) { fix_by(1); w0=link(w0); };
10616 while ( info(c)<=zero_off-n ) fix_by(n);
10617 while ( info(c)>zero_off ) fix_by(-n);
10618 if ( (info(c)!=zero_off)&&(mp_ab_vs_cd(mp, dy0,dxin,dx0,dyin)>=0) ) fix_by(n);
10622 @ Finally we want to reduce the general problem to situations that
10623 |fin_offset_prep| can handle. We split the cubic into at most three parts
10624 with respect to $d_{k-1}$, and apply |fin_offset_prep| to each part.
10626 @<Complete the offset splitting process@>=
10628 @<Compute test coeff...@>;
10629 @<Find the first |t| where $d(t)$ crosses $d_{k-1}$ or set
10630 |t:=fraction_one+1|@>;
10631 if ( t>fraction_one ) {
10632 mp_fin_offset_prep(mp, p,w,x0,x1,x2,y0,y1,y2,1,turn_amt);
10634 mp_split_cubic(mp, p,t); r=link(p);
10635 x1a=t_of_the_way(x0,x1); x1=t_of_the_way(x1,x2);
10636 x2a=t_of_the_way(x1a,x1);
10637 y1a=t_of_the_way(y0,y1); y1=t_of_the_way(y1,y2);
10638 y2a=t_of_the_way(y1a,y1);
10639 mp_fin_offset_prep(mp, p,w,x0,x1a,x2a,y0,y1a,y2a,1,0); x0=x2a; y0=y2a;
10640 info(r)=zero_off-1;
10641 if ( turn_amt>=0 ) {
10642 t1=t_of_the_way(t1,t2);
10644 t=mp_crossing_point(mp, 0,-t1,-t2);
10645 if ( t>fraction_one ) t=fraction_one;
10646 @<Split off another rising cubic for |fin_offset_prep|@>;
10647 mp_fin_offset_prep(mp, r,ww,x0,x1,x2,y0,y1,y2,-1,0);
10649 mp_fin_offset_prep(mp, r,ww,x0,x1,x2,y0,y1,y2,-1,-1-turn_amt);
10653 @ @<Split off another rising cubic for |fin_offset_prep|@>=
10654 mp_split_cubic(mp, r,t); info(link(r))=zero_off+1;
10655 x1a=t_of_the_way(x1,x2); x1=t_of_the_way(x0,x1);
10656 x0a=t_of_the_way(x1,x1a);
10657 y1a=t_of_the_way(y1,y2); y1=t_of_the_way(y0,y1);
10658 y0a=t_of_the_way(y1,y1a);
10659 mp_fin_offset_prep(mp, link(r),w,x0a,x1a,x2,y0a,y1a,y2,1,turn_amt);
10662 @ At this point, the direction of the incoming pen edge is |(-du,-dv)|.
10663 When the component of $d(t)$ perpendicular to |(-du,-dv)| crosses zero, we
10664 need to decide whether the directions are parallel or antiparallel. We
10665 can test this by finding the dot product of $d(t)$ and |(-du,-dv)|, but this
10666 should be avoided when the value of |turn_amt| already determines the
10667 answer. If |t2<0|, there is one crossing and it is antiparallel only if
10668 |turn_amt>=0|. If |turn_amt<0|, there should always be at least one
10669 crossing and the first crossing cannot be antiparallel.
10671 @<Find the first |t| where $d(t)$ crosses $d_{k-1}$ or set...@>=
10672 t=mp_crossing_point(mp, t0,t1,t2);
10673 if ( turn_amt>=0 ) {
10677 u0=t_of_the_way(x0,x1);
10678 u1=t_of_the_way(x1,x2);
10679 ss=mp_take_fraction(mp, -du,t_of_the_way(u0,u1));
10680 v0=t_of_the_way(y0,y1);
10681 v1=t_of_the_way(y1,y2);
10682 ss=ss+mp_take_fraction(mp, -dv,t_of_the_way(v0,v1));
10683 if ( ss<0 ) t=fraction_one+1;
10685 } else if ( t>fraction_one ) {
10689 @ @<Other local variables for |offset_prep|@>=
10690 integer u0,u1,v0,v1; /* intermediate values for $d(t)$ calculation */
10691 integer ss = 0; /* the part of the dot product computed so far */
10692 int d_sign; /* sign of overall change in direction for this cubic */
10694 @ If the cubic almost has a cusp, it is a numerically ill-conditioned
10695 problem to decide which way it loops around but that's OK as long we're
10696 consistent. To make \&{doublepath} envelopes work properly, reversing
10697 the path should always change the sign of |turn_amt|.
10699 @<Decide on the net change in pen offsets and set |turn_amt|@>=
10700 d_sign=mp_ab_vs_cd(mp, dx,dyin, dxin,dy);
10703 if ( dy>0 ) d_sign=1; else d_sign=-1;
10704 } else if ( dx>0 ) {
10710 @<Make |ss| negative if and only if the total change in direction is
10711 more than $180^\circ$@>;
10712 turn_amt=mp_get_turn_amt(mp, w, dxin, dyin, d_sign>0);
10713 if ( ss<0 ) turn_amt=turn_amt-d_sign*n
10715 @ In order to be invariant under path reversal, the result of this computation
10716 should not change when |x0|, |y0|, $\ldots$ are all negated and |(x0,y0)| is
10717 then swapped with |(x2,y2)|. We make use of the identities
10718 |take_fraction(-a,-b)=take_fraction(a,b)| and
10719 |t_of_the_way(-a,-b)=-(t_of_the_way(a,b))|.
10721 @<Make |ss| negative if and only if the total change in direction is...@>=
10722 t0=half(mp_take_fraction(mp, x0,y2))-half(mp_take_fraction(mp, x2,y0));
10723 t1=half(mp_take_fraction(mp, x1,y0+y2))-half(mp_take_fraction(mp, y1,x0+x2));
10724 if ( t0==0 ) t0=d_sign; /* path reversal always negates |d_sign| */
10726 t=mp_crossing_point(mp, t0,t1,-t0);
10727 u0=t_of_the_way(x0,x1);
10728 u1=t_of_the_way(x1,x2);
10729 v0=t_of_the_way(y0,y1);
10730 v1=t_of_the_way(y1,y2);
10732 t=mp_crossing_point(mp, -t0,t1,t0);
10733 u0=t_of_the_way(x2,x1);
10734 u1=t_of_the_way(x1,x0);
10735 v0=t_of_the_way(y2,y1);
10736 v1=t_of_the_way(y1,y0);
10738 s=mp_take_fraction(mp, x0+x2,t_of_the_way(u0,u1))+
10739 mp_take_fraction(mp, y0+y2,t_of_the_way(v0,v1))
10741 @ Here's a routine that prints an envelope spec in symbolic form. It assumes
10742 that the |cur_pen| has not been walked around to the first offset.
10745 void mp_print_spec (MP mp,pointer cur_spec, pointer cur_pen, char *s) {
10746 pointer p,q; /* list traversal */
10747 pointer w; /* the current pen offset */
10748 mp_print_diagnostic(mp, "Envelope spec",s,true);
10749 p=cur_spec; w=mp_pen_walk(mp, cur_pen,mp->spec_offset);
10751 mp_print_two(mp, x_coord(cur_spec),y_coord(cur_spec));
10752 mp_print(mp, " % beginning with offset ");
10753 mp_print_two(mp, x_coord(w),y_coord(w));
10757 @<Print the cubic between |p| and |q|@>;
10759 } while (! ((p==cur_spec) || (info(p)!=zero_off)));
10760 if ( info(p)!=zero_off ) {
10761 @<Update |w| as indicated by |info(p)| and print an explanation@>;
10763 } while (p!=cur_spec);
10764 mp_print_nl(mp, " & cycle");
10765 mp_end_diagnostic(mp, true);
10768 @ @<Update |w| as indicated by |info(p)| and print an explanation@>=
10770 w=mp_pen_walk(mp, w,info(p)-zero_off);
10771 mp_print(mp, " % ");
10772 if ( info(p)>zero_off ) mp_print(mp, "counter");
10773 mp_print(mp, "clockwise to offset ");
10774 mp_print_two(mp, x_coord(w),y_coord(w));
10777 @ @<Print the cubic between |p| and |q|@>=
10779 mp_print_nl(mp, " ..controls ");
10780 mp_print_two(mp, right_x(p),right_y(p));
10781 mp_print(mp, " and ");
10782 mp_print_two(mp, left_x(q),left_y(q));
10783 mp_print_nl(mp, " ..");
10784 mp_print_two(mp, x_coord(q),y_coord(q));
10787 @ Once we have an envelope spec, the remaining task to construct the actual
10788 envelope by offsetting each cubic as determined by the |info| fields in
10789 the knots. First we use |offset_prep| to convert the |c| into an envelope
10790 spec. Then we add the offsets so that |c| becomes a cyclic path that represents
10793 The |ljoin| and |miterlim| parameters control the treatment of points where the
10794 pen offset changes, and |lcap| controls the endpoints of a \&{doublepath}.
10795 The endpoints are easily located because |c| is given in undoubled form
10796 and then doubled in this procedure. We use |spec_p1| and |spec_p2| to keep
10797 track of the endpoints and treat them like very sharp corners.
10798 Butt end caps are treated like beveled joins; round end caps are treated like
10799 round joins; and square end caps are achieved by setting |join_type:=3|.
10801 None of these parameters apply to inside joins where the convolution tracing
10802 has retrograde lines. In such cases we use a simple connect-the-endpoints
10803 approach that is achieved by setting |join_type:=2|.
10805 @c @<Declare a function called |insert_knot|@>;
10806 pointer mp_make_envelope (MP mp,pointer c, pointer h, small_number ljoin,
10807 small_number lcap, scaled miterlim) {
10808 pointer p,q,r,q0; /* for manipulating the path */
10809 int join_type=0; /* codes |0..3| for mitered, round, beveled, or square */
10810 pointer w,w0; /* the pen knot for the current offset */
10811 scaled qx,qy; /* unshifted coordinates of |q| */
10812 halfword k,k0; /* controls pen edge insertion */
10813 @<Other local variables for |make_envelope|@>;
10814 dxin=0; dyin=0; dxout=0; dyout=0;
10815 mp->spec_p1=null; mp->spec_p2=null;
10816 @<If endpoint, double the path |c|, and set |spec_p1| and |spec_p2|@>;
10817 @<Use |offset_prep| to compute the envelope spec then walk |h| around to
10818 the initial offset@>;
10823 qx=x_coord(q); qy=y_coord(q);
10826 if ( k!=zero_off ) {
10827 @<Set |join_type| to indicate how to handle offset changes at~|q|@>;
10829 @<Add offset |w| to the cubic from |p| to |q|@>;
10830 while ( k!=zero_off ) {
10831 @<Step |w| and move |k| one step closer to |zero_off|@>;
10832 if ( (join_type==1)||(k==zero_off) )
10833 q=mp_insert_knot(mp, q,qx+x_coord(w),qy+y_coord(w));
10835 if ( q!=link(p) ) {
10836 @<Set |p=link(p)| and add knots between |p| and |q| as
10837 required by |join_type|@>;
10844 @ @<Use |offset_prep| to compute the envelope spec then walk |h| around to...@>=
10845 c=mp_offset_prep(mp, c,h);
10846 if ( mp->internal[tracing_specs]>0 )
10847 mp_print_spec(mp, c,h,"");
10848 h=mp_pen_walk(mp, h,mp->spec_offset)
10850 @ Mitered and squared-off joins depend on path directions that are difficult to
10851 compute for degenerate cubics. The envelope spec computed by |offset_prep| can
10852 have degenerate cubics only if the entire cycle collapses to a single
10853 degenerate cubic. Setting |join_type:=2| in this case makes the computed
10854 envelope degenerate as well.
10856 @<Set |join_type| to indicate how to handle offset changes at~|q|@>=
10857 if ( k<zero_off ) {
10860 if ( (q!=mp->spec_p1)&&(q!=mp->spec_p2) ) join_type=ljoin;
10861 else if ( lcap==2 ) join_type=3;
10862 else join_type=2-lcap;
10863 if ( (join_type==0)||(join_type==3) ) {
10864 @<Set the incoming and outgoing directions at |q|; in case of
10865 degeneracy set |join_type:=2|@>;
10866 if ( join_type==0 ) {
10867 @<If |miterlim| is less than the secant of half the angle at |q|
10868 then set |join_type:=2|@>;
10873 @ @<If |miterlim| is less than the secant of half the angle at |q|...@>=
10875 tmp=mp_take_fraction(mp, miterlim,fraction_half+
10876 half(mp_take_fraction(mp, dxin,dxout)+mp_take_fraction(mp, dyin,dyout)));
10878 if ( mp_take_scaled(mp, miterlim,tmp)<unity ) join_type=2;
10881 @ @<Other local variables for |make_envelope|@>=
10882 fraction dxin,dyin,dxout,dyout; /* directions at |q| when square or mitered */
10883 scaled tmp; /* a temporary value */
10885 @ The coordinates of |p| have already been shifted unless |p| is the first
10886 knot in which case they get shifted at the very end.
10888 @<Add offset |w| to the cubic from |p| to |q|@>=
10889 right_x(p)=right_x(p)+x_coord(w);
10890 right_y(p)=right_y(p)+y_coord(w);
10891 left_x(q)=left_x(q)+x_coord(w);
10892 left_y(q)=left_y(q)+y_coord(w);
10893 x_coord(q)=x_coord(q)+x_coord(w);
10894 y_coord(q)=y_coord(q)+y_coord(w);
10895 left_type(q)=explicit;
10896 right_type(q)=explicit
10898 @ @<Step |w| and move |k| one step closer to |zero_off|@>=
10899 if ( k>zero_off ){ w=link(w); decr(k); }
10900 else { w=knil(w); incr(k); }
10902 @ The cubic from |q| to the new knot at |(x,y)| becomes a line segment and
10903 the |right_x| and |right_y| fields of |r| are set from |q|. This is done in
10904 case the cubic containing these control points is ``yet to be examined.''
10906 @<Declare a function called |insert_knot|@>=
10907 pointer mp_insert_knot (MP mp,pointer q, scaled x, scaled y) {
10908 /* returns the inserted knot */
10909 pointer r; /* the new knot */
10910 r=mp_get_node(mp, knot_node_size);
10911 link(r)=link(q); link(q)=r;
10912 right_x(r)=right_x(q);
10913 right_y(r)=right_y(q);
10916 right_x(q)=x_coord(q);
10917 right_y(q)=y_coord(q);
10918 left_x(r)=x_coord(r);
10919 left_y(r)=y_coord(r);
10920 left_type(r)=explicit;
10921 right_type(r)=explicit;
10922 originator(r)=program_code;
10926 @ After setting |p:=link(p)|, either |join_type=1| or |q=link(p)|.
10928 @<Set |p=link(p)| and add knots between |p| and |q| as...@>=
10931 if ( (join_type==0)||(join_type==3) ) {
10932 if ( join_type==0 ) {
10933 @<Insert a new knot |r| between |p| and |q| as required for a mitered join@>
10935 @<Make |r| the last of two knots inserted between |p| and |q| to form a
10939 right_x(r)=x_coord(r);
10940 right_y(r)=y_coord(r);
10945 @ For very small angles, adding a knot is unnecessary and would cause numerical
10946 problems, so we just set |r:=null| in that case.
10948 @<Insert a new knot |r| between |p| and |q| as required for a mitered join@>=
10950 det=mp_take_fraction(mp, dyout,dxin)-mp_take_fraction(mp, dxout,dyin);
10951 if ( abs(det)<26844 ) {
10952 r=null; /* sine $<10^{-4}$ */
10954 tmp=mp_take_fraction(mp, x_coord(q)-x_coord(p),dyout)-
10955 mp_take_fraction(mp, y_coord(q)-y_coord(p),dxout);
10956 tmp=mp_make_fraction(mp, tmp,det);
10957 r=mp_insert_knot(mp, p,x_coord(p)+mp_take_fraction(mp, tmp,dxin),
10958 y_coord(p)+mp_take_fraction(mp, tmp,dyin));
10962 @ @<Other local variables for |make_envelope|@>=
10963 fraction det; /* a determinant used for mitered join calculations */
10965 @ @<Make |r| the last of two knots inserted between |p| and |q| to form a...@>=
10967 ht_x=y_coord(w)-y_coord(w0);
10968 ht_y=x_coord(w0)-x_coord(w);
10969 while ( (abs(ht_x)<fraction_half)&&(abs(ht_y)<fraction_half) ) {
10970 ht_x+=ht_x; ht_y+=ht_y;
10972 @<Scan the pen polygon between |w0| and |w| and make |max_ht| the range dot
10973 product with |(ht_x,ht_y)|@>;
10974 tmp=mp_make_fraction(mp, max_ht,mp_take_fraction(mp, dxin,ht_x)+
10975 mp_take_fraction(mp, dyin,ht_y));
10976 r=mp_insert_knot(mp, p,x_coord(p)+mp_take_fraction(mp, tmp,dxin),
10977 y_coord(p)+mp_take_fraction(mp, tmp,dyin));
10978 tmp=mp_make_fraction(mp, max_ht,mp_take_fraction(mp, dxout,ht_x)+
10979 mp_take_fraction(mp, dyout,ht_y));
10980 r=mp_insert_knot(mp, r,x_coord(q)+mp_take_fraction(mp, tmp,dxout),
10981 y_coord(q)+mp_take_fraction(mp, tmp,dyout));
10984 @ @<Other local variables for |make_envelope|@>=
10985 fraction ht_x,ht_y; /* perpendicular to the segment from |p| to |q| */
10986 scaled max_ht; /* maximum height of the pen polygon above the |w0|-|w| line */
10987 halfword kk; /* keeps track of the pen vertices being scanned */
10988 pointer ww; /* the pen vertex being tested */
10990 @ The dot product of the vector from |w0| to |ww| with |(ht_x,ht_y)| ranges
10991 from zero to |max_ht|.
10993 @<Scan the pen polygon between |w0| and |w| and make |max_ht| the range...@>=
10998 @<Step |ww| and move |kk| one step closer to |k0|@>;
10999 if ( kk==k0 ) break;
11000 tmp=mp_take_fraction(mp, x_coord(ww)-x_coord(w0),ht_x)+
11001 mp_take_fraction(mp, y_coord(ww)-y_coord(w0),ht_y);
11002 if ( tmp>max_ht ) max_ht=tmp;
11006 @ @<Step |ww| and move |kk| one step closer to |k0|@>=
11007 if ( kk>k0 ) { ww=link(ww); decr(kk); }
11008 else { ww=knil(ww); incr(kk); }
11010 @ @<If endpoint, double the path |c|, and set |spec_p1| and |spec_p2|@>=
11011 if ( left_type(c)==endpoint ) {
11012 mp->spec_p1=mp_htap_ypoc(mp, c);
11013 mp->spec_p2=mp->path_tail;
11014 originator(mp->spec_p1)=program_code;
11015 link(mp->spec_p2)=link(mp->spec_p1);
11016 link(mp->spec_p1)=c;
11017 mp_remove_cubic(mp, mp->spec_p1);
11019 if ( c!=link(c) ) {
11020 originator(mp->spec_p2)=program_code;
11021 mp_remove_cubic(mp, mp->spec_p2);
11023 @<Make |c| look like a cycle of length one@>;
11027 @ @<Make |c| look like a cycle of length one@>=
11029 left_type(c)=explicit; right_type(c)=explicit;
11030 left_x(c)=x_coord(c); left_y(c)=y_coord(c);
11031 right_x(c)=x_coord(c); right_y(c)=y_coord(c);
11034 @ In degenerate situations we might have to look at the knot preceding~|q|.
11035 That knot is |p| but if |p<>c|, its coordinates have already been offset by |w|.
11037 @<Set the incoming and outgoing directions at |q|; in case of...@>=
11038 dxin=x_coord(q)-left_x(q);
11039 dyin=y_coord(q)-left_y(q);
11040 if ( (dxin==0)&&(dyin==0) ) {
11041 dxin=x_coord(q)-right_x(p);
11042 dyin=y_coord(q)-right_y(p);
11043 if ( (dxin==0)&&(dyin==0) ) {
11044 dxin=x_coord(q)-x_coord(p);
11045 dyin=y_coord(q)-y_coord(p);
11046 if ( p!=c ) { /* the coordinates of |p| have been offset by |w| */
11047 dxin=dxin+x_coord(w);
11048 dyin=dyin+y_coord(w);
11052 tmp=mp_pyth_add(mp, dxin,dyin);
11056 dxin=mp_make_fraction(mp, dxin,tmp);
11057 dyin=mp_make_fraction(mp, dyin,tmp);
11058 @<Set the outgoing direction at |q|@>;
11061 @ If |q=c| then the coordinates of |r| and the control points between |q|
11062 and~|r| have already been offset by |h|.
11064 @<Set the outgoing direction at |q|@>=
11065 dxout=right_x(q)-x_coord(q);
11066 dyout=right_y(q)-y_coord(q);
11067 if ( (dxout==0)&&(dyout==0) ) {
11069 dxout=left_x(r)-x_coord(q);
11070 dyout=left_y(r)-y_coord(q);
11071 if ( (dxout==0)&&(dyout==0) ) {
11072 dxout=x_coord(r)-x_coord(q);
11073 dyout=y_coord(r)-y_coord(q);
11077 dxout=dxout-x_coord(h);
11078 dyout=dyout-y_coord(h);
11080 tmp=mp_pyth_add(mp, dxout,dyout);
11081 if ( tmp==0 ) mp_confusion(mp, "degenerate spec");
11082 @:this can't happen degerate spec}{\quad degenerate spec@>
11083 dxout=mp_make_fraction(mp, dxout,tmp);
11084 dyout=mp_make_fraction(mp, dyout,tmp)
11086 @* \[23] Direction and intersection times.
11087 A path of length $n$ is defined parametrically by functions $x(t)$ and
11088 $y(t)$, for |0<=t<=n|; we can regard $t$ as the ``time'' at which the path
11089 reaches the point $\bigl(x(t),y(t)\bigr)$. In this section of the program
11090 we shall consider operations that determine special times associated with
11091 given paths: the first time that a path travels in a given direction, and
11092 a pair of times at which two paths cross each other.
11094 @ Let's start with the easier task. The function |find_direction_time| is
11095 given a direction |(x,y)| and a path starting at~|h|. If the path never
11096 travels in direction |(x,y)|, the direction time will be~|-1|; otherwise
11097 it will be nonnegative.
11099 Certain anomalous cases can arise: If |(x,y)=(0,0)|, so that the given
11100 direction is undefined, the direction time will be~0. If $\bigl(x'(t),
11101 y'(t)\bigr)=(0,0)$, so that the path direction is undefined, it will be
11102 assumed to match any given direction at time~|t|.
11104 The routine solves this problem in nondegenerate cases by rotating the path
11105 and the given direction so that |(x,y)=(1,0)|; i.e., the main task will be
11106 to find when a given path first travels ``due east.''
11109 scaled mp_find_direction_time (MP mp,scaled x, scaled y, pointer h) {
11110 scaled max; /* $\max\bigl(\vert x\vert,\vert y\vert\bigr)$ */
11111 pointer p,q; /* for list traversal */
11112 scaled n; /* the direction time at knot |p| */
11113 scaled tt; /* the direction time within a cubic */
11114 @<Other local variables for |find_direction_time|@>;
11115 @<Normalize the given direction for better accuracy;
11116 but |return| with zero result if it's zero@>;
11119 if ( right_type(p)==endpoint ) break;
11121 @<Rotate the cubic between |p| and |q|; then
11122 |goto found| if the rotated cubic travels due east at some time |tt|;
11123 but |break| if an entire cyclic path has been traversed@>;
11131 @ @<Normalize the given direction for better accuracy...@>=
11132 if ( abs(x)<abs(y) ) {
11133 x=mp_make_fraction(mp, x,abs(y));
11134 if ( y>0 ) y=fraction_one; else y=-fraction_one;
11135 } else if ( x==0 ) {
11138 y=mp_make_fraction(mp, y,abs(x));
11139 if ( x>0 ) x=fraction_one; else x=-fraction_one;
11142 @ Since we're interested in the tangent directions, we work with the
11143 derivative $${\textstyle1\over3}B'(x_0,x_1,x_2,x_3;t)=
11144 B(x_1-x_0,x_2-x_1,x_3-x_2;t)$$ instead of
11145 $B(x_0,x_1,x_2,x_3;t)$ itself. The derived coefficients are also scaled up
11146 in order to achieve better accuracy.
11148 The given path may turn abruptly at a knot, and it might pass the critical
11149 tangent direction at such a time. Therefore we remember the direction |phi|
11150 in which the previous rotated cubic was traveling. (The value of |phi| will be
11151 undefined on the first cubic, i.e., when |n=0|.)
11153 @<Rotate the cubic between |p| and |q|; then...@>=
11155 @<Set local variables |x1,x2,x3| and |y1,y2,y3| to multiples of the control
11156 points of the rotated derivatives@>;
11157 if ( y1==0 ) if ( x1>=0 ) goto FOUND;
11159 @<Exit to |found| if an eastward direction occurs at knot |p|@>;
11162 if ( (x3!=0)||(y3!=0) ) phi=mp_n_arg(mp, x3,y3);
11163 @<Exit to |found| if the curve whose derivatives are specified by
11164 |x1,x2,x3,y1,y2,y3| travels eastward at some time~|tt|@>
11166 @ @<Other local variables for |find_direction_time|@>=
11167 scaled x1,x2,x3,y1,y2,y3; /* multiples of rotated derivatives */
11168 angle theta,phi; /* angles of exit and entry at a knot */
11169 fraction t; /* temp storage */
11171 @ @<Set local variables |x1,x2,x3| and |y1,y2,y3| to multiples...@>=
11172 x1=right_x(p)-x_coord(p); x2=left_x(q)-right_x(p);
11173 x3=x_coord(q)-left_x(q);
11174 y1=right_y(p)-y_coord(p); y2=left_y(q)-right_y(p);
11175 y3=y_coord(q)-left_y(q);
11177 if ( abs(x2)>max ) max=abs(x2);
11178 if ( abs(x3)>max ) max=abs(x3);
11179 if ( abs(y1)>max ) max=abs(y1);
11180 if ( abs(y2)>max ) max=abs(y2);
11181 if ( abs(y3)>max ) max=abs(y3);
11182 if ( max==0 ) goto FOUND;
11183 while ( max<fraction_half ){
11184 max+=max; x1+=x1; x2+=x2; x3+=x3;
11185 y1+=y1; y2+=y2; y3+=y3;
11187 t=x1; x1=mp_take_fraction(mp, x1,x)+mp_take_fraction(mp, y1,y);
11188 y1=mp_take_fraction(mp, y1,x)-mp_take_fraction(mp, t,y);
11189 t=x2; x2=mp_take_fraction(mp, x2,x)+mp_take_fraction(mp, y2,y);
11190 y2=mp_take_fraction(mp, y2,x)-mp_take_fraction(mp, t,y);
11191 t=x3; x3=mp_take_fraction(mp, x3,x)+mp_take_fraction(mp, y3,y);
11192 y3=mp_take_fraction(mp, y3,x)-mp_take_fraction(mp, t,y)
11194 @ @<Exit to |found| if an eastward direction occurs at knot |p|@>=
11195 theta=mp_n_arg(mp, x1,y1);
11196 if ( theta>=0 ) if ( phi<=0 ) if ( phi>=theta-one_eighty_deg ) goto FOUND;
11197 if ( theta<=0 ) if ( phi>=0 ) if ( phi<=theta+one_eighty_deg ) goto FOUND
11199 @ In this step we want to use the |crossing_point| routine to find the
11200 roots of the quadratic equation $B(y_1,y_2,y_3;t)=0$.
11201 Several complications arise: If the quadratic equation has a double root,
11202 the curve never crosses zero, and |crossing_point| will find nothing;
11203 this case occurs iff $y_1y_3=y_2^2$ and $y_1y_2<0$. If the quadratic
11204 equation has simple roots, or only one root, we may have to negate it
11205 so that $B(y_1,y_2,y_3;t)$ crosses from positive to negative at its first root.
11206 And finally, we need to do special things if $B(y_1,y_2,y_3;t)$ is
11209 @ @<Exit to |found| if the curve whose derivatives are specified by...@>=
11210 if ( x1<0 ) if ( x2<0 ) if ( x3<0 ) goto DONE;
11211 if ( mp_ab_vs_cd(mp, y1,y3,y2,y2)==0 ) {
11212 @<Handle the test for eastward directions when $y_1y_3=y_2^2$;
11213 either |goto found| or |goto done|@>;
11216 if ( y1<0 ) { y1=-y1; y2=-y2; y3=-y3; }
11217 else if ( y2>0 ){ y2=-y2; y3=-y3; };
11219 @<Check the places where $B(y_1,y_2,y_3;t)=0$ to see if
11220 $B(x_1,x_2,x_3;t)\ge0$@>;
11223 @ The quadratic polynomial $B(y_1,y_2,y_3;t)$ begins |>=0| and has at most
11224 two roots, because we know that it isn't identically zero.
11226 It must be admitted that the |crossing_point| routine is not perfectly accurate;
11227 rounding errors might cause it to find a root when $y_1y_3>y_2^2$, or to
11228 miss the roots when $y_1y_3<y_2^2$. The rotation process is itself
11229 subject to rounding errors. Yet this code optimistically tries to
11230 do the right thing.
11232 @d we_found_it { tt=(t+04000) / 010000; goto FOUND; }
11234 @<Check the places where $B(y_1,y_2,y_3;t)=0$...@>=
11235 t=mp_crossing_point(mp, y1,y2,y3);
11236 if ( t>fraction_one ) goto DONE;
11237 y2=t_of_the_way(y2,y3);
11238 x1=t_of_the_way(x1,x2);
11239 x2=t_of_the_way(x2,x3);
11240 x1=t_of_the_way(x1,x2);
11241 if ( x1>=0 ) we_found_it;
11243 tt=t; t=mp_crossing_point(mp, 0,-y2,-y3);
11244 if ( t>fraction_one ) goto DONE;
11245 x1=t_of_the_way(x1,x2);
11246 x2=t_of_the_way(x2,x3);
11247 if ( t_of_the_way(x1,x2)>=0 ) {
11248 t=t_of_the_way(tt,fraction_one); we_found_it;
11251 @ @<Handle the test for eastward directions when $y_1y_3=y_2^2$;
11252 either |goto found| or |goto done|@>=
11254 if ( mp_ab_vs_cd(mp, y1,y2,0,0)<0 ) {
11255 t=mp_make_fraction(mp, y1,y1-y2);
11256 x1=t_of_the_way(x1,x2);
11257 x2=t_of_the_way(x2,x3);
11258 if ( t_of_the_way(x1,x2)>=0 ) we_found_it;
11259 } else if ( y3==0 ) {
11261 @<Exit to |found| if the derivative $B(x_1,x_2,x_3;t)$ becomes |>=0|@>;
11262 } else if ( x3>=0 ) {
11263 tt=unity; goto FOUND;
11269 @ At this point we know that the derivative of |y(t)| is identically zero,
11270 and that |x1<0|; but either |x2>=0| or |x3>=0|, so there's some hope of
11273 @<Exit to |found| if the derivative $B(x_1,x_2,x_3;t)$ becomes |>=0|...@>=
11275 t=mp_crossing_point(mp, -x1,-x2,-x3);
11276 if ( t<=fraction_one ) we_found_it;
11277 if ( mp_ab_vs_cd(mp, x1,x3,x2,x2)<=0 ) {
11278 t=mp_make_fraction(mp, x1,x1-x2); we_found_it;
11282 @ The intersection of two cubics can be found by an interesting variant
11283 of the general bisection scheme described in the introduction to
11285 Given $w(t)=B(w_0,w_1,w_2,w_3;t)$ and $z(t)=B(z_0,z_1,z_2,z_3;t)$,
11286 we wish to find a pair of times $(t_1,t_2)$ such that $w(t_1)=z(t_2)$,
11287 if an intersection exists. First we find the smallest rectangle that
11288 encloses the points $\{w_0,w_1,w_2,w_3\}$ and check that it overlaps
11289 the smallest rectangle that encloses
11290 $\{z_0,z_1,z_2,z_3\}$; if not, the cubics certainly don't intersect.
11291 But if the rectangles do overlap, we bisect the intervals, getting
11292 new cubics $w'$ and~$w''$, $z'$~and~$z''$; the intersection routine first
11293 tries for an intersection between $w'$ and~$z'$, then (if unsuccessful)
11294 between $w'$ and~$z''$, then (if still unsuccessful) between $w''$ and~$z'$,
11295 finally (if thrice unsuccessful) between $w''$ and~$z''$. After $l$~successful
11296 levels of bisection we will have determined the intersection times $t_1$
11297 and~$t_2$ to $l$~bits of accuracy.
11299 \def\submin{_{\rm min}} \def\submax{_{\rm max}}
11300 As before, it is better to work with the numbers $W_k=2^l(w_k-w_{k-1})$
11301 and $Z_k=2^l(z_k-z_{k-1})$ rather than the coefficients $w_k$ and $z_k$
11302 themselves. We also need one other quantity, $\Delta=2^l(w_0-z_0)$,
11303 to determine when the enclosing rectangles overlap. Here's why:
11304 The $x$~coordinates of~$w(t)$ are between $u\submin$ and $u\submax$,
11305 and the $x$~coordinates of~$z(t)$ are between $x\submin$ and $x\submax$,
11306 if we write $w_k=(u_k,v_k)$ and $z_k=(x_k,y_k)$ and $u\submin=
11307 \min(u_0,u_1,u_2,u_3)$, etc. These intervals of $x$~coordinates
11308 overlap if and only if $u\submin\L x\submax$ and
11309 $x\submin\L u\submax$. Letting
11310 $$U\submin=\min(0,U_1,U_1+U_2,U_1+U_2+U_3),\;
11311 U\submax=\max(0,U_1,U_1+U_2,U_1+U_2+U_3),$$
11312 we have $u\submin=2^lu_0+U\submin$, etc.; the condition for overlap
11314 $$X\submin-U\submax\L 2^l(u_0-x_0)\L X\submax-U\submin.$$
11315 Thus we want to maintain the quantity $2^l(u_0-x_0)$; similarly,
11316 the quantity $2^l(v_0-y_0)$ accounts for the $y$~coordinates. The
11317 coordinates of $\Delta=2^l(w_0-z_0)$ must stay bounded as $l$ increases,
11318 because of the overlap condition; i.e., we know that $X\submin$,
11319 $X\submax$, and their relatives are bounded, hence $X\submax-
11320 U\submin$ and $X\submin-U\submax$ are bounded.
11322 @ Incidentally, if the given cubics intersect more than once, the process
11323 just sketched will not necessarily find the lexicographically smallest pair
11324 $(t_1,t_2)$. The solution actually obtained will be smallest in ``shuffled
11325 order''; i.e., if $t_1=(.a_1a_2\ldots a_{16})_2$ and
11326 $t_2=(.b_1b_2\ldots b_{16})_2$, then we will minimize
11327 $a_1b_1a_2b_2\ldots a_{16}b_{16}$, not
11328 $a_1a_2\ldots a_{16}b_1b_2\ldots b_{16}$.
11329 Shuffled order agrees with lexicographic order if all pairs of solutions
11330 $(t_1,t_2)$ and $(t_1',t_2')$ have the property that $t_1<t_1'$ iff
11331 $t_2<t_2'$; but in general, lexicographic order can be quite different,
11332 and the bisection algorithm would be substantially less efficient if it were
11333 constrained by lexicographic order.
11335 For example, suppose that an overlap has been found for $l=3$ and
11336 $(t_1,t_2)= (.101,.011)$ in binary, but that no overlap is produced by
11337 either of the alternatives $(.1010,.0110)$, $(.1010,.0111)$ at level~4.
11338 Then there is probably an intersection in one of the subintervals
11339 $(.1011,.011x)$; but lexicographic order would require us to explore
11340 $(.1010,.1xxx)$ and $(.1011,.00xx)$ and $(.1011,.010x)$ first. We wouldn't
11341 want to store all of the subdivision data for the second path, so the
11342 subdivisions would have to be regenerated many times. Such inefficiencies
11343 would be associated with every `1' in the binary representation of~$t_1$.
11345 @ The subdivision process introduces rounding errors, hence we need to
11346 make a more liberal test for overlap. It is not hard to show that the
11347 computed values of $U_i$ differ from the truth by at most~$l$, on
11348 level~$l$, hence $U\submin$ and $U\submax$ will be at most $3l$ in error.
11349 If $\beta$ is an upper bound on the absolute error in the computed
11350 components of $\Delta=(|delx|,|dely|)$ on level~$l$, we will replace
11351 the test `$X\submin-U\submax\L|delx|$' by the more liberal test
11352 `$X\submin-U\submax\L|delx|+|tol|$', where $|tol|=6l+\beta$.
11354 More accuracy is obtained if we try the algorithm first with |tol=0|;
11355 the more liberal tolerance is used only if an exact approach fails.
11356 It is convenient to do this double-take by letting `3' in the preceding
11357 paragraph be a parameter, which is first 0, then 3.
11360 unsigned int tol_step; /* either 0 or 3, usually */
11362 @ We shall use an explicit stack to implement the recursive bisection
11363 method described above. The |bisect_stack| array will contain numerous 5-word
11364 packets like $(U_1,U_2,U_3,U\submin,U\submax)$, as well as 20-word packets
11365 comprising the 5-word packets for $U$, $V$, $X$, and~$Y$.
11367 The following macros define the allocation of stack positions to
11368 the quantities needed for bisection-intersection.
11370 @d stack_1(A) mp->bisect_stack[(A)] /* $U_1$, $V_1$, $X_1$, or $Y_1$ */
11371 @d stack_2(A) mp->bisect_stack[(A)+1] /* $U_2$, $V_2$, $X_2$, or $Y_2$ */
11372 @d stack_3(A) mp->bisect_stack[(A)+2] /* $U_3$, $V_3$, $X_3$, or $Y_3$ */
11373 @d stack_min(A) mp->bisect_stack[(A)+3]
11374 /* $U\submin$, $V\submin$, $X\submin$, or $Y\submin$ */
11375 @d stack_max(A) mp->bisect_stack[(A)+4]
11376 /* $U\submax$, $V\submax$, $X\submax$, or $Y\submax$ */
11377 @d int_packets 20 /* number of words to represent $U_k$, $V_k$, $X_k$, and $Y_k$ */
11379 @d u_packet(A) ((A)-5)
11380 @d v_packet(A) ((A)-10)
11381 @d x_packet(A) ((A)-15)
11382 @d y_packet(A) ((A)-20)
11383 @d l_packets (mp->bisect_ptr-int_packets)
11384 @d r_packets mp->bisect_ptr
11385 @d ul_packet u_packet(l_packets) /* base of $U'_k$ variables */
11386 @d vl_packet v_packet(l_packets) /* base of $V'_k$ variables */
11387 @d xl_packet x_packet(l_packets) /* base of $X'_k$ variables */
11388 @d yl_packet y_packet(l_packets) /* base of $Y'_k$ variables */
11389 @d ur_packet u_packet(r_packets) /* base of $U''_k$ variables */
11390 @d vr_packet v_packet(r_packets) /* base of $V''_k$ variables */
11391 @d xr_packet x_packet(r_packets) /* base of $X''_k$ variables */
11392 @d yr_packet y_packet(r_packets) /* base of $Y''_k$ variables */
11394 @d u1l stack_1(ul_packet) /* $U'_1$ */
11395 @d u2l stack_2(ul_packet) /* $U'_2$ */
11396 @d u3l stack_3(ul_packet) /* $U'_3$ */
11397 @d v1l stack_1(vl_packet) /* $V'_1$ */
11398 @d v2l stack_2(vl_packet) /* $V'_2$ */
11399 @d v3l stack_3(vl_packet) /* $V'_3$ */
11400 @d x1l stack_1(xl_packet) /* $X'_1$ */
11401 @d x2l stack_2(xl_packet) /* $X'_2$ */
11402 @d x3l stack_3(xl_packet) /* $X'_3$ */
11403 @d y1l stack_1(yl_packet) /* $Y'_1$ */
11404 @d y2l stack_2(yl_packet) /* $Y'_2$ */
11405 @d y3l stack_3(yl_packet) /* $Y'_3$ */
11406 @d u1r stack_1(ur_packet) /* $U''_1$ */
11407 @d u2r stack_2(ur_packet) /* $U''_2$ */
11408 @d u3r stack_3(ur_packet) /* $U''_3$ */
11409 @d v1r stack_1(vr_packet) /* $V''_1$ */
11410 @d v2r stack_2(vr_packet) /* $V''_2$ */
11411 @d v3r stack_3(vr_packet) /* $V''_3$ */
11412 @d x1r stack_1(xr_packet) /* $X''_1$ */
11413 @d x2r stack_2(xr_packet) /* $X''_2$ */
11414 @d x3r stack_3(xr_packet) /* $X''_3$ */
11415 @d y1r stack_1(yr_packet) /* $Y''_1$ */
11416 @d y2r stack_2(yr_packet) /* $Y''_2$ */
11417 @d y3r stack_3(yr_packet) /* $Y''_3$ */
11419 @d stack_dx mp->bisect_stack[mp->bisect_ptr] /* stacked value of |delx| */
11420 @d stack_dy mp->bisect_stack[mp->bisect_ptr+1] /* stacked value of |dely| */
11421 @d stack_tol mp->bisect_stack[mp->bisect_ptr+2] /* stacked value of |tol| */
11422 @d stack_uv mp->bisect_stack[mp->bisect_ptr+3] /* stacked value of |uv| */
11423 @d stack_xy mp->bisect_stack[mp->bisect_ptr+4] /* stacked value of |xy| */
11424 @d int_increment (int_packets+int_packets+5) /* number of stack words per level */
11427 integer *bisect_stack;
11428 unsigned int bisect_ptr;
11430 @ @<Allocate or initialize ...@>=
11431 mp->bisect_stack = xmalloc((bistack_size+1),sizeof(integer));
11433 @ @<Dealloc variables@>=
11434 xfree(mp->bisect_stack);
11436 @ @<Check the ``constant''...@>=
11437 if ( int_packets+17*int_increment>bistack_size ) mp->bad=19;
11439 @ Computation of the min and max is a tedious but fairly fast sequence of
11440 instructions; exactly four comparisons are made in each branch.
11443 if ( stack_1((A))<0 ) {
11444 if ( stack_3((A))>=0 ) {
11445 if ( stack_2((A))<0 ) stack_min((A))=stack_1((A))+stack_2((A));
11446 else stack_min((A))=stack_1((A));
11447 stack_max((A))=stack_1((A))+stack_2((A))+stack_3((A));
11448 if ( stack_max((A))<0 ) stack_max((A))=0;
11450 stack_min((A))=stack_1((A))+stack_2((A))+stack_3((A));
11451 if ( stack_min((A))>stack_1((A)) ) stack_min((A))=stack_1((A));
11452 stack_max((A))=stack_1((A))+stack_2((A));
11453 if ( stack_max((A))<0 ) stack_max((A))=0;
11455 } else if ( stack_3((A))<=0 ) {
11456 if ( stack_2((A))>0 ) stack_max((A))=stack_1((A))+stack_2((A));
11457 else stack_max((A))=stack_1((A));
11458 stack_min((A))=stack_1((A))+stack_2((A))+stack_3((A));
11459 if ( stack_min((A))>0 ) stack_min((A))=0;
11461 stack_max((A))=stack_1((A))+stack_2((A))+stack_3((A));
11462 if ( stack_max((A))<stack_1((A)) ) stack_max((A))=stack_1((A));
11463 stack_min((A))=stack_1((A))+stack_2((A));
11464 if ( stack_min((A))>0 ) stack_min((A))=0;
11467 @ It's convenient to keep the current values of $l$, $t_1$, and $t_2$ in
11468 the integer form $2^l+2^lt_1$ and $2^l+2^lt_2$. The |cubic_intersection|
11469 routine uses global variables |cur_t| and |cur_tt| for this purpose;
11470 after successful completion, |cur_t| and |cur_tt| will contain |unity|
11471 plus the |scaled| values of $t_1$ and~$t_2$.
11473 The values of |cur_t| and |cur_tt| will be set to zero if |cubic_intersection|
11474 finds no intersection. The routine gives up and gives an approximate answer
11475 if it has backtracked
11476 more than 5000 times (otherwise there are cases where several minutes
11477 of fruitless computation would be possible).
11479 @d max_patience 5000
11482 integer cur_t;integer cur_tt; /* controls and results of |cubic_intersection| */
11483 integer time_to_go; /* this many backtracks before giving up */
11484 integer max_t; /* maximum of $2^{l+1}$ so far achieved */
11486 @ The given cubics $B(w_0,w_1,w_2,w_3;t)$ and
11487 $B(z_0,z_1,z_2,z_3;t)$ are specified in adjacent knot nodes |(p,link(p))|
11488 and |(pp,link(pp))|, respectively.
11490 @c void mp_cubic_intersection (MP mp,pointer p, pointer pp) {
11491 pointer q,qq; /* |link(p)|, |link(pp)| */
11492 mp->time_to_go=max_patience; mp->max_t=2;
11493 @<Initialize for intersections at level zero@>;
11496 if ( mp->delx-mp->tol<=stack_max(x_packet(mp->xy))-stack_min(u_packet(mp->uv)))
11497 if ( mp->delx+mp->tol>=stack_min(x_packet(mp->xy))-stack_max(u_packet(mp->uv)))
11498 if ( mp->dely-mp->tol<=stack_max(y_packet(mp->xy))-stack_min(v_packet(mp->uv)))
11499 if ( mp->dely+mp->tol>=stack_min(y_packet(mp->xy))-stack_max(v_packet(mp->uv)))
11501 if ( mp->cur_t>=mp->max_t ){
11502 if ( mp->max_t==two ) { /* we've done 17 bisections */
11503 mp->cur_t=halfp(mp->cur_t+1); mp->cur_tt=halfp(mp->cur_tt+1); return;
11505 mp->max_t+=mp->max_t; mp->appr_t=mp->cur_t; mp->appr_tt=mp->cur_tt;
11507 @<Subdivide for a new level of intersection@>;
11510 if ( mp->time_to_go>0 ) {
11511 decr(mp->time_to_go);
11513 while ( mp->appr_t<unity ) {
11514 mp->appr_t+=mp->appr_t; mp->appr_tt+=mp->appr_tt;
11516 mp->cur_t=mp->appr_t; mp->cur_tt=mp->appr_tt; return;
11518 @<Advance to the next pair |(cur_t,cur_tt)|@>;
11522 @ The following variables are global, although they are used only by
11523 |cubic_intersection|, because it is necessary on some machines to
11524 split |cubic_intersection| up into two procedures.
11527 integer delx;integer dely; /* the components of $\Delta=2^l(w_0-z_0)$ */
11528 integer tol; /* bound on the uncertainly in the overlap test */
11530 unsigned int xy; /* pointers to the current packets of interest */
11531 integer three_l; /* |tol_step| times the bisection level */
11532 integer appr_t;integer appr_tt; /* best approximations known to the answers */
11534 @ We shall assume that the coordinates are sufficiently non-extreme that
11535 integer overflow will not occur.
11537 @<Initialize for intersections at level zero@>=
11538 q=link(p); qq=link(pp); mp->bisect_ptr=int_packets;
11539 u1r=right_x(p)-x_coord(p); u2r=left_x(q)-right_x(p);
11540 u3r=x_coord(q)-left_x(q); set_min_max(ur_packet);
11541 v1r=right_y(p)-y_coord(p); v2r=left_y(q)-right_y(p);
11542 v3r=y_coord(q)-left_y(q); set_min_max(vr_packet);
11543 x1r=right_x(pp)-x_coord(pp); x2r=left_x(qq)-right_x(pp);
11544 x3r=x_coord(qq)-left_x(qq); set_min_max(xr_packet);
11545 y1r=right_y(pp)-y_coord(pp); y2r=left_y(qq)-right_y(pp);
11546 y3r=y_coord(qq)-left_y(qq); set_min_max(yr_packet);
11547 mp->delx=x_coord(p)-x_coord(pp); mp->dely=y_coord(p)-y_coord(pp);
11548 mp->tol=0; mp->uv=r_packets; mp->xy=r_packets;
11549 mp->three_l=0; mp->cur_t=1; mp->cur_tt=1
11551 @ @<Subdivide for a new level of intersection@>=
11552 stack_dx=mp->delx; stack_dy=mp->dely; stack_tol=mp->tol;
11553 stack_uv=mp->uv; stack_xy=mp->xy;
11554 mp->bisect_ptr=mp->bisect_ptr+int_increment;
11555 mp->cur_t+=mp->cur_t; mp->cur_tt+=mp->cur_tt;
11556 u1l=stack_1(u_packet(mp->uv)); u3r=stack_3(u_packet(mp->uv));
11557 u2l=half(u1l+stack_2(u_packet(mp->uv)));
11558 u2r=half(u3r+stack_2(u_packet(mp->uv)));
11559 u3l=half(u2l+u2r); u1r=u3l;
11560 set_min_max(ul_packet); set_min_max(ur_packet);
11561 v1l=stack_1(v_packet(mp->uv)); v3r=stack_3(v_packet(mp->uv));
11562 v2l=half(v1l+stack_2(v_packet(mp->uv)));
11563 v2r=half(v3r+stack_2(v_packet(mp->uv)));
11564 v3l=half(v2l+v2r); v1r=v3l;
11565 set_min_max(vl_packet); set_min_max(vr_packet);
11566 x1l=stack_1(x_packet(mp->xy)); x3r=stack_3(x_packet(mp->xy));
11567 x2l=half(x1l+stack_2(x_packet(mp->xy)));
11568 x2r=half(x3r+stack_2(x_packet(mp->xy)));
11569 x3l=half(x2l+x2r); x1r=x3l;
11570 set_min_max(xl_packet); set_min_max(xr_packet);
11571 y1l=stack_1(y_packet(mp->xy)); y3r=stack_3(y_packet(mp->xy));
11572 y2l=half(y1l+stack_2(y_packet(mp->xy)));
11573 y2r=half(y3r+stack_2(y_packet(mp->xy)));
11574 y3l=half(y2l+y2r); y1r=y3l;
11575 set_min_max(yl_packet); set_min_max(yr_packet);
11576 mp->uv=l_packets; mp->xy=l_packets;
11577 mp->delx+=mp->delx; mp->dely+=mp->dely;
11578 mp->tol=mp->tol-mp->three_l+mp->tol_step;
11579 mp->tol+=mp->tol; mp->three_l=mp->three_l+mp->tol_step
11581 @ @<Advance to the next pair |(cur_t,cur_tt)|@>=
11583 if ( odd(mp->cur_tt) ) {
11584 if ( odd(mp->cur_t) ) {
11585 @<Descend to the previous level and |goto not_found|@>;
11588 mp->delx=mp->delx+stack_1(u_packet(mp->uv))+stack_2(u_packet(mp->uv))
11589 +stack_3(u_packet(mp->uv));
11590 mp->dely=mp->dely+stack_1(v_packet(mp->uv))+stack_2(v_packet(mp->uv))
11591 +stack_3(v_packet(mp->uv));
11592 mp->uv=mp->uv+int_packets; /* switch from |l_packet| to |r_packet| */
11593 decr(mp->cur_tt); mp->xy=mp->xy-int_packets;
11594 /* switch from |r_packet| to |l_packet| */
11595 mp->delx=mp->delx+stack_1(x_packet(mp->xy))+stack_2(x_packet(mp->xy))
11596 +stack_3(x_packet(mp->xy));
11597 mp->dely=mp->dely+stack_1(y_packet(mp->xy))+stack_2(y_packet(mp->xy))
11598 +stack_3(y_packet(mp->xy));
11601 incr(mp->cur_tt); mp->tol=mp->tol+mp->three_l;
11602 mp->delx=mp->delx-stack_1(x_packet(mp->xy))-stack_2(x_packet(mp->xy))
11603 -stack_3(x_packet(mp->xy));
11604 mp->dely=mp->dely-stack_1(y_packet(mp->xy))-stack_2(y_packet(mp->xy))
11605 -stack_3(y_packet(mp->xy));
11606 mp->xy=mp->xy+int_packets; /* switch from |l_packet| to |r_packet| */
11609 @ @<Descend to the previous level...@>=
11611 mp->cur_t=halfp(mp->cur_t); mp->cur_tt=halfp(mp->cur_tt);
11612 if ( mp->cur_t==0 ) return;
11613 mp->bisect_ptr=mp->bisect_ptr-int_increment;
11614 mp->three_l=mp->three_l-mp->tol_step;
11615 mp->delx=stack_dx; mp->dely=stack_dy; mp->tol=stack_tol;
11616 mp->uv=stack_uv; mp->xy=stack_xy;
11620 @ The |path_intersection| procedure is much simpler.
11621 It invokes |cubic_intersection| in lexicographic order until finding a
11622 pair of cubics that intersect. The final intersection times are placed in
11623 |cur_t| and~|cur_tt|.
11625 @c void mp_path_intersection (MP mp,pointer h, pointer hh) {
11626 pointer p,pp; /* link registers that traverse the given paths */
11627 integer n,nn; /* integer parts of intersection times, minus |unity| */
11628 @<Change one-point paths into dead cycles@>;
11633 if ( right_type(p)!=endpoint ) {
11636 if ( right_type(pp)!=endpoint ) {
11637 mp_cubic_intersection(mp, p,pp);
11638 if ( mp->cur_t>0 ) {
11639 mp->cur_t=mp->cur_t+n; mp->cur_tt=mp->cur_tt+nn;
11643 nn=nn+unity; pp=link(pp);
11646 n=n+unity; p=link(p);
11648 mp->tol_step=mp->tol_step+3;
11649 } while (mp->tol_step<=3);
11650 mp->cur_t=-unity; mp->cur_tt=-unity;
11653 @ @<Change one-point paths...@>=
11654 if ( right_type(h)==endpoint ) {
11655 right_x(h)=x_coord(h); left_x(h)=x_coord(h);
11656 right_y(h)=y_coord(h); left_y(h)=y_coord(h); right_type(h)=explicit;
11658 if ( right_type(hh)==endpoint ) {
11659 right_x(hh)=x_coord(hh); left_x(hh)=x_coord(hh);
11660 right_y(hh)=y_coord(hh); left_y(hh)=y_coord(hh); right_type(hh)=explicit;
11663 @* \[24] Dynamic linear equations.
11664 \MP\ users define variables implicitly by stating equations that should be
11665 satisfied; the computer is supposed to be smart enough to solve those equations.
11666 And indeed, the computer tries valiantly to do so, by distinguishing five
11667 different types of numeric values:
11670 |type(p)=mp_known| is the nice case, when |value(p)| is the |scaled| value
11671 of the variable whose address is~|p|.
11674 |type(p)=mp_dependent| means that |value(p)| is not present, but |dep_list(p)|
11675 points to a {\sl dependency list\/} that expresses the value of variable~|p|
11676 as a |scaled| number plus a sum of independent variables with |fraction|
11680 |type(p)=mp_independent| means that |value(p)=64s+m|, where |s>0| is a ``serial
11681 number'' reflecting the time this variable was first used in an equation;
11682 also |0<=m<64|, and each dependent variable
11683 that refers to this one is actually referring to the future value of
11684 this variable times~$2^m$. (Usually |m=0|, but higher degrees of
11685 scaling are sometimes needed to keep the coefficients in dependency lists
11686 from getting too large. The value of~|m| will always be even.)
11689 |type(p)=mp_numeric_type| means that variable |p| hasn't appeared in an
11690 equation before, but it has been explicitly declared to be numeric.
11693 |type(p)=undefined| means that variable |p| hasn't appeared before.
11695 \smallskip\noindent
11696 We have actually discussed these five types in the reverse order of their
11697 history during a computation: Once |known|, a variable never again
11698 becomes |dependent|; once |dependent|, it almost never again becomes
11699 |mp_independent|; once |mp_independent|, it never again becomes |mp_numeric_type|;
11700 and once |mp_numeric_type|, it never again becomes |undefined| (except
11701 of course when the user specifically decides to scrap the old value
11702 and start again). A backward step may, however, take place: Sometimes
11703 a |dependent| variable becomes |mp_independent| again, when one of the
11704 independent variables it depends on is reverting to |undefined|.
11707 The next patch detects overflow of independent-variable serial
11708 numbers. Diagnosed and patched by Thorsten Dahlheimer.
11710 @d s_scale 64 /* the serial numbers are multiplied by this factor */
11711 @d max_indep_vars 0177777777 /* $2^{25}-1$ */
11712 @d max_serial_no 017777777700 /* |max_indep_vars*s_scale| */
11713 @d new_indep(A) /* create a new independent variable */
11714 { if ( mp->serial_no==max_serial_no )
11715 mp_fatal_error(mp, "variable instance identifiers exhausted");
11716 type((A))=mp_independent; mp->serial_no=mp->serial_no+s_scale;
11717 value((A))=mp->serial_no;
11721 integer serial_no; /* the most recent serial number, times |s_scale| */
11723 @ @<Make variable |q+s| newly independent@>=new_indep(q+s)
11725 @ But how are dependency lists represented? It's simple: The linear combination
11726 $\alpha_1v_1+\cdots+\alpha_kv_k+\beta$ appears in |k+1| value nodes. If
11727 |q=dep_list(p)| points to this list, and if |k>0|, then |value(q)=
11728 @t$\alpha_1$@>| (which is a |fraction|); |info(q)| points to the location
11729 of $\alpha_1$; and |link(p)| points to the dependency list
11730 $\alpha_2v_2+\cdots+\alpha_kv_k+\beta$. On the other hand if |k=0|,
11731 then |value(q)=@t$\beta$@>| (which is |scaled|) and |info(q)=null|.
11732 The independent variables $v_1$, \dots,~$v_k$ have been sorted so that
11733 they appear in decreasing order of their |value| fields (i.e., of
11734 their serial numbers). \ (It is convenient to use decreasing order,
11735 since |value(null)=0|. If the independent variables were not sorted by
11736 serial number but by some other criterion, such as their location in |mem|,
11737 the equation-solving mechanism would be too system-dependent, because
11738 the ordering can affect the computed results.)
11740 The |link| field in the node that contains the constant term $\beta$ is
11741 called the {\sl final link\/} of the dependency list. \MP\ maintains
11742 a doubly-linked master list of all dependency lists, in terms of a permanently
11744 in |mem| called |dep_head|. If there are no dependencies, we have
11745 |link(dep_head)=dep_head| and |prev_dep(dep_head)=dep_head|;
11746 otherwise |link(dep_head)| points to the first dependent variable, say~|p|,
11747 and |prev_dep(p)=dep_head|. We have |type(p)=mp_dependent|, and |dep_list(p)|
11748 points to its dependency list. If the final link of that dependency list
11749 occurs in location~|q|, then |link(q)| points to the next dependent
11750 variable (say~|r|); and we have |prev_dep(r)=q|, etc.
11752 @d dep_list(A) link(value_loc((A)))
11753 /* half of the |value| field in a |dependent| variable */
11754 @d prev_dep(A) info(value_loc((A)))
11755 /* the other half; makes a doubly linked list */
11756 @d dep_node_size 2 /* the number of words per dependency node */
11758 @<Initialize table entries...@>= mp->serial_no=0;
11759 link(dep_head)=dep_head; prev_dep(dep_head)=dep_head;
11760 info(dep_head)=null; dep_list(dep_head)=null;
11762 @ Actually the description above contains a little white lie. There's
11763 another kind of variable called |mp_proto_dependent|, which is
11764 just like a |dependent| one except that the $\alpha$ coefficients
11765 in its dependency list are |scaled| instead of being fractions.
11766 Proto-dependency lists are mixed with dependency lists in the
11767 nodes reachable from |dep_head|.
11769 @ Here is a procedure that prints a dependency list in symbolic form.
11770 The second parameter should be either |dependent| or |mp_proto_dependent|,
11771 to indicate the scaling of the coefficients.
11773 @<Declare subroutines for printing expressions@>=
11774 void mp_print_dependency (MP mp,pointer p, small_number t) {
11775 integer v; /* a coefficient */
11776 pointer pp,q; /* for list manipulation */
11779 v=abs(value(p)); q=info(p);
11780 if ( q==null ) { /* the constant term */
11781 if ( (v!=0)||(p==pp) ) {
11782 if ( value(p)>0 ) if ( p!=pp ) mp_print_char(mp, '+');
11783 mp_print_scaled(mp, value(p));
11787 @<Print the coefficient, unless it's $\pm1.0$@>;
11788 if ( type(q)!=mp_independent ) mp_confusion(mp, "dep");
11789 @:this can't happen dep}{\quad dep@>
11790 mp_print_variable_name(mp, q); v=value(q) % s_scale;
11791 while ( v>0 ) { mp_print(mp, "*4"); v=v-2; }
11796 @ @<Print the coefficient, unless it's $\pm1.0$@>=
11797 if ( value(p)<0 ) mp_print_char(mp, '-');
11798 else if ( p!=pp ) mp_print_char(mp, '+');
11799 if ( t==mp_dependent ) v=mp_round_fraction(mp, v);
11800 if ( v!=unity ) mp_print_scaled(mp, v)
11802 @ The maximum absolute value of a coefficient in a given dependency list
11803 is returned by the following simple function.
11805 @c fraction mp_max_coef (MP mp,pointer p) {
11806 fraction x; /* the maximum so far */
11808 while ( info(p)!=null ) {
11809 if ( abs(value(p))>x ) x=abs(value(p));
11815 @ One of the main operations needed on dependency lists is to add a multiple
11816 of one list to the other; we call this |p_plus_fq|, where |p| and~|q| point
11817 to dependency lists and |f| is a fraction.
11819 If the coefficient of any independent variable becomes |coef_bound| or
11820 more, in absolute value, this procedure changes the type of that variable
11821 to `|independent_needing_fix|', and sets the global variable |fix_needed|
11822 to~|true|. The value of $|coef_bound|=\mu$ is chosen so that
11823 $\mu^2+\mu<8$; this means that the numbers we deal with won't
11824 get too large. (Instead of the ``optimum'' $\mu=(\sqrt{33}-1)/2\approx
11825 2.3723$, the safer value 7/3 is taken as the threshold.)
11827 The changes mentioned in the preceding paragraph are actually done only if
11828 the global variable |watch_coefs| is |true|. But it usually is; in fact,
11829 it is |false| only when \MP\ is making a dependency list that will soon
11830 be equated to zero.
11832 Several procedures that act on dependency lists, including |p_plus_fq|,
11833 set the global variable |dep_final| to the final (constant term) node of
11834 the dependency list that they produce.
11836 @d coef_bound 04525252525 /* |fraction| approximation to 7/3 */
11837 @d independent_needing_fix 0
11840 boolean fix_needed; /* does at least one |independent| variable need scaling? */
11841 boolean watch_coefs; /* should we scale coefficients that exceed |coef_bound|? */
11842 pointer dep_final; /* location of the constant term and final link */
11845 mp->fix_needed=false; mp->watch_coefs=true;
11847 @ The |p_plus_fq| procedure has a fourth parameter, |t|, that should be
11848 set to |mp_proto_dependent| if |p| is a proto-dependency list. In this
11849 case |f| will be |scaled|, not a |fraction|. Similarly, the fifth parameter~|tt|
11850 should be |mp_proto_dependent| if |q| is a proto-dependency list.
11852 List |q| is unchanged by the operation; but list |p| is totally destroyed.
11854 The final link of the dependency list or proto-dependency list returned
11855 by |p_plus_fq| is the same as the original final link of~|p|. Indeed, the
11856 constant term of the result will be located in the same |mem| location
11857 as the original constant term of~|p|.
11859 Coefficients of the result are assumed to be zero if they are less than
11860 a certain threshold. This compensates for inevitable rounding errors,
11861 and tends to make more variables `|known|'. The threshold is approximately
11862 $10^{-5}$ in the case of normal dependency lists, $10^{-4}$ for
11863 proto-dependencies.
11865 @d fraction_threshold 2685 /* a |fraction| coefficient less than this is zeroed */
11866 @d half_fraction_threshold 1342 /* half of |fraction_threshold| */
11867 @d scaled_threshold 8 /* a |scaled| coefficient less than this is zeroed */
11868 @d half_scaled_threshold 4 /* half of |scaled_threshold| */
11870 @<Declare basic dependency-list subroutines@>=
11871 pointer mp_p_plus_fq ( MP mp, pointer p, integer f,
11872 pointer q, small_number t, small_number tt) ;
11875 pointer mp_p_plus_fq ( MP mp, pointer p, integer f,
11876 pointer q, small_number t, small_number tt) {
11877 pointer pp,qq; /* |info(p)| and |info(q)|, respectively */
11878 pointer r,s; /* for list manipulation */
11879 integer mp_threshold; /* defines a neighborhood of zero */
11880 integer v; /* temporary register */
11881 if ( t==mp_dependent ) mp_threshold=fraction_threshold;
11882 else mp_threshold=scaled_threshold;
11883 r=temp_head; pp=info(p); qq=info(q);
11889 @<Contribute a term from |p|, plus |f| times the
11890 corresponding term from |q|@>
11892 } else if ( value(pp)<value(qq) ) {
11893 @<Contribute a term from |q|, multiplied by~|f|@>
11895 link(r)=p; r=p; p=link(p); pp=info(p);
11898 if ( t==mp_dependent )
11899 value(p)=mp_slow_add(mp, value(p),mp_take_fraction(mp, value(q),f));
11901 value(p)=mp_slow_add(mp, value(p),mp_take_scaled(mp, value(q),f));
11902 link(r)=p; mp->dep_final=p;
11903 return link(temp_head);
11906 @ @<Contribute a term from |p|, plus |f|...@>=
11908 if ( tt==mp_dependent ) v=value(p)+mp_take_fraction(mp, f,value(q));
11909 else v=value(p)+mp_take_scaled(mp, f,value(q));
11910 value(p)=v; s=p; p=link(p);
11911 if ( abs(v)<mp_threshold ) {
11912 mp_free_node(mp, s,dep_node_size);
11914 if ( (abs(v)>=coef_bound) && mp->watch_coefs ) {
11915 type(qq)=independent_needing_fix; mp->fix_needed=true;
11919 pp=info(p); q=link(q); qq=info(q);
11922 @ @<Contribute a term from |q|, multiplied by~|f|@>=
11924 if ( tt==mp_dependent ) v=mp_take_fraction(mp, f,value(q));
11925 else v=mp_take_scaled(mp, f,value(q));
11926 if ( abs(v)>halfp(mp_threshold) ) {
11927 s=mp_get_node(mp, dep_node_size); info(s)=qq; value(s)=v;
11928 if ( (abs(v)>=coef_bound) && mp->watch_coefs ) {
11929 type(qq)=independent_needing_fix; mp->fix_needed=true;
11933 q=link(q); qq=info(q);
11936 @ It is convenient to have another subroutine for the special case
11937 of |p_plus_fq| when |f=1.0|. In this routine lists |p| and |q| are
11938 both of the same type~|t| (either |dependent| or |mp_proto_dependent|).
11940 @c pointer mp_p_plus_q (MP mp,pointer p, pointer q, small_number t) {
11941 pointer pp,qq; /* |info(p)| and |info(q)|, respectively */
11942 pointer r,s; /* for list manipulation */
11943 integer mp_threshold; /* defines a neighborhood of zero */
11944 integer v; /* temporary register */
11945 if ( t==mp_dependent ) mp_threshold=fraction_threshold;
11946 else mp_threshold=scaled_threshold;
11947 r=temp_head; pp=info(p); qq=info(q);
11953 @<Contribute a term from |p|, plus the
11954 corresponding term from |q|@>
11956 } else if ( value(pp)<value(qq) ) {
11957 s=mp_get_node(mp, dep_node_size); info(s)=qq; value(s)=value(q);
11958 q=link(q); qq=info(q); link(r)=s; r=s;
11960 link(r)=p; r=p; p=link(p); pp=info(p);
11963 value(p)=mp_slow_add(mp, value(p),value(q));
11964 link(r)=p; mp->dep_final=p;
11965 return link(temp_head);
11968 @ @<Contribute a term from |p|, plus the...@>=
11970 v=value(p)+value(q);
11971 value(p)=v; s=p; p=link(p); pp=info(p);
11972 if ( abs(v)<mp_threshold ) {
11973 mp_free_node(mp, s,dep_node_size);
11975 if ( (abs(v)>=coef_bound ) && mp->watch_coefs ) {
11976 type(qq)=independent_needing_fix; mp->fix_needed=true;
11980 q=link(q); qq=info(q);
11983 @ A somewhat simpler routine will multiply a dependency list
11984 by a given constant~|v|. The constant is either a |fraction| less than
11985 |fraction_one|, or it is |scaled|. In the latter case we might be forced to
11986 convert a dependency list to a proto-dependency list.
11987 Parameters |t0| and |t1| are the list types before and after;
11988 they should agree unless |t0=mp_dependent| and |t1=mp_proto_dependent|
11989 and |v_is_scaled=true|.
11991 @c pointer mp_p_times_v (MP mp,pointer p, integer v, small_number t0,
11992 small_number t1, boolean v_is_scaled) {
11993 pointer r,s; /* for list manipulation */
11994 integer w; /* tentative coefficient */
11995 integer mp_threshold;
11996 boolean scaling_down;
11997 if ( t0!=t1 ) scaling_down=true; else scaling_down=! v_is_scaled;
11998 if ( t1==mp_dependent ) mp_threshold=half_fraction_threshold;
11999 else mp_threshold=half_scaled_threshold;
12001 while ( info(p)!=null ) {
12002 if ( scaling_down ) w=mp_take_fraction(mp, v,value(p));
12003 else w=mp_take_scaled(mp, v,value(p));
12004 if ( abs(w)<=mp_threshold ) {
12005 s=link(p); mp_free_node(mp, p,dep_node_size); p=s;
12007 if ( abs(w)>=coef_bound ) {
12008 mp->fix_needed=true; type(info(p))=independent_needing_fix;
12010 link(r)=p; r=p; value(p)=w; p=link(p);
12014 if ( v_is_scaled ) value(p)=mp_take_scaled(mp, value(p),v);
12015 else value(p)=mp_take_fraction(mp, value(p),v);
12016 return link(temp_head);
12019 @ Similarly, we sometimes need to divide a dependency list
12020 by a given |scaled| constant.
12022 @<Declare basic dependency-list subroutines@>=
12023 pointer mp_p_over_v (MP mp,pointer p, scaled v, small_number
12024 t0, small_number t1) ;
12027 pointer mp_p_over_v (MP mp,pointer p, scaled v, small_number
12028 t0, small_number t1) {
12029 pointer r,s; /* for list manipulation */
12030 integer w; /* tentative coefficient */
12031 integer mp_threshold;
12032 boolean scaling_down;
12033 if ( t0!=t1 ) scaling_down=true; else scaling_down=false;
12034 if ( t1==mp_dependent ) mp_threshold=half_fraction_threshold;
12035 else mp_threshold=half_scaled_threshold;
12037 while ( info( p)!=null ) {
12038 if ( scaling_down ) {
12039 if ( abs(v)<02000000 ) w=mp_make_scaled(mp, value(p),v*010000);
12040 else w=mp_make_scaled(mp, mp_round_fraction(mp, value(p)),v);
12042 w=mp_make_scaled(mp, value(p),v);
12044 if ( abs(w)<=mp_threshold ) {
12045 s=link(p); mp_free_node(mp, p,dep_node_size); p=s;
12047 if ( abs(w)>=coef_bound ) {
12048 mp->fix_needed=true; type(info(p))=independent_needing_fix;
12050 link(r)=p; r=p; value(p)=w; p=link(p);
12053 link(r)=p; value(p)=mp_make_scaled(mp, value(p),v);
12054 return link(temp_head);
12057 @ Here's another utility routine for dependency lists. When an independent
12058 variable becomes dependent, we want to remove it from all existing
12059 dependencies. The |p_with_x_becoming_q| function computes the
12060 dependency list of~|p| after variable~|x| has been replaced by~|q|.
12062 This procedure has basically the same calling conventions as |p_plus_fq|:
12063 List~|q| is unchanged; list~|p| is destroyed; the constant node and the
12064 final link are inherited from~|p|; and the fourth parameter tells whether
12065 or not |p| is |mp_proto_dependent|. However, the global variable |dep_final|
12066 is not altered if |x| does not occur in list~|p|.
12068 @c pointer mp_p_with_x_becoming_q (MP mp,pointer p,
12069 pointer x, pointer q, small_number t) {
12070 pointer r,s; /* for list manipulation */
12071 integer v; /* coefficient of |x| */
12072 integer sx; /* serial number of |x| */
12073 s=p; r=temp_head; sx=value(x);
12074 while ( value(info(s))>sx ) { r=s; s=link(s); };
12075 if ( info(s)!=x ) {
12078 link(temp_head)=p; link(r)=link(s); v=value(s);
12079 mp_free_node(mp, s,dep_node_size);
12080 return mp_p_plus_fq(mp, link(temp_head),v,q,t,mp_dependent);
12084 @ Here's a simple procedure that reports an error when a variable
12085 has just received a known value that's out of the required range.
12087 @<Declare basic dependency-list subroutines@>=
12088 void mp_val_too_big (MP mp,scaled x) ;
12090 @ @c void mp_val_too_big (MP mp,scaled x) {
12091 if ( mp->internal[warning_check]>0 ) {
12092 print_err("Value is too large ("); mp_print_scaled(mp, x); mp_print_char(mp, ')');
12093 @.Value is too large@>
12094 help4("The equation I just processed has given some variable")
12095 ("a value of 4096 or more. Continue and I'll try to cope")
12096 ("with that big value; but it might be dangerous.")
12097 ("(Set warningcheck:=0 to suppress this message.)");
12102 @ When a dependent variable becomes known, the following routine
12103 removes its dependency list. Here |p| points to the variable, and
12104 |q| points to the dependency list (which is one node long).
12106 @<Declare basic dependency-list subroutines@>=
12107 void mp_make_known (MP mp,pointer p, pointer q) ;
12109 @ @c void mp_make_known (MP mp,pointer p, pointer q) {
12110 int t; /* the previous type */
12111 prev_dep(link(q))=prev_dep(p);
12112 link(prev_dep(p))=link(q); t=type(p);
12113 type(p)=mp_known; value(p)=value(q); mp_free_node(mp, q,dep_node_size);
12114 if ( abs(value(p))>=fraction_one ) mp_val_too_big(mp, value(p));
12115 if (( mp->internal[tracing_equations]>0) && mp_interesting(mp, p) ) {
12116 mp_begin_diagnostic(mp); mp_print_nl(mp, "#### ");
12117 @:]]]\#\#\#\#_}{\.{\#\#\#\#}@>
12118 mp_print_variable_name(mp, p);
12119 mp_print_char(mp, '='); mp_print_scaled(mp, value(p));
12120 mp_end_diagnostic(mp, false);
12122 if (( mp->cur_exp==p ) && mp->cur_type==t ) {
12123 mp->cur_type=mp_known; mp->cur_exp=value(p);
12124 mp_free_node(mp, p,value_node_size);
12128 @ The |fix_dependencies| routine is called into action when |fix_needed|
12129 has been triggered. The program keeps a list~|s| of independent variables
12130 whose coefficients must be divided by~4.
12132 In unusual cases, this fixup process might reduce one or more coefficients
12133 to zero, so that a variable will become known more or less by default.
12135 @<Declare basic dependency-list subroutines@>=
12136 void mp_fix_dependencies (MP mp);
12138 @ @c void mp_fix_dependencies (MP mp) {
12139 pointer p,q,r,s,t; /* list manipulation registers */
12140 pointer x; /* an independent variable */
12141 r=link(dep_head); s=null;
12142 while ( r!=dep_head ){
12144 @<Run through the dependency list for variable |t|, fixing
12145 all nodes, and ending with final link~|q|@>;
12147 if ( q==dep_list(t) ) mp_make_known(mp, t,q);
12149 while ( s!=null ) {
12150 p=link(s); x=info(s); free_avail(s); s=p;
12151 type(x)=mp_independent; value(x)=value(x)+2;
12153 mp->fix_needed=false;
12156 @ @d independent_being_fixed 1 /* this variable already appears in |s| */
12158 @<Run through the dependency list for variable |t|...@>=
12159 r=value_loc(t); /* |link(r)=dep_list(t)| */
12161 q=link(r); x=info(q);
12162 if ( x==null ) break;
12163 if ( type(x)<=independent_being_fixed ) {
12164 if ( type(x)<independent_being_fixed ) {
12165 p=mp_get_avail(mp); link(p)=s; s=p;
12166 info(s)=x; type(x)=independent_being_fixed;
12168 value(q)=value(q) / 4;
12169 if ( value(q)==0 ) {
12170 link(r)=link(q); mp_free_node(mp, q,dep_node_size); q=r;
12177 @ The |new_dep| routine installs a dependency list~|p| into the value node~|q|,
12178 linking it into the list of all known dependencies. We assume that
12179 |dep_final| points to the final node of list~|p|.
12181 @c void mp_new_dep (MP mp,pointer q, pointer p) {
12182 pointer r; /* what used to be the first dependency */
12183 dep_list(q)=p; prev_dep(q)=dep_head;
12184 r=link(dep_head); link(mp->dep_final)=r; prev_dep(r)=mp->dep_final;
12188 @ Here is one of the ways a dependency list gets started.
12189 The |const_dependency| routine produces a list that has nothing but
12192 @c pointer mp_const_dependency (MP mp, scaled v) {
12193 mp->dep_final=mp_get_node(mp, dep_node_size);
12194 value(mp->dep_final)=v; info(mp->dep_final)=null;
12195 return mp->dep_final;
12198 @ And here's a more interesting way to start a dependency list from scratch:
12199 The parameter to |single_dependency| is the location of an
12200 independent variable~|x|, and the result is the simple dependency list
12203 In the unlikely event that the given independent variable has been doubled so
12204 often that we can't refer to it with a nonzero coefficient,
12205 |single_dependency| returns the simple list `0'. This case can be
12206 recognized by testing that the returned list pointer is equal to
12209 @c pointer mp_single_dependency (MP mp,pointer p) {
12210 pointer q; /* the new dependency list */
12211 integer m; /* the number of doublings */
12212 m=value(p) % s_scale;
12214 return mp_const_dependency(mp, 0);
12216 q=mp_get_node(mp, dep_node_size);
12217 value(q)=two_to_the(28-m); info(q)=p;
12218 link(q)=mp_const_dependency(mp, 0);
12223 @ We sometimes need to make an exact copy of a dependency list.
12225 @c pointer mp_copy_dep_list (MP mp,pointer p) {
12226 pointer q; /* the new dependency list */
12227 q=mp_get_node(mp, dep_node_size); mp->dep_final=q;
12229 info(mp->dep_final)=info(p); value(mp->dep_final)=value(p);
12230 if ( info(mp->dep_final)==null ) break;
12231 link(mp->dep_final)=mp_get_node(mp, dep_node_size);
12232 mp->dep_final=link(mp->dep_final); p=link(p);
12237 @ But how do variables normally become known? Ah, now we get to the heart of the
12238 equation-solving mechanism. The |linear_eq| procedure is given a |dependent|
12239 or |mp_proto_dependent| list,~|p|, in which at least one independent variable
12240 appears. It equates this list to zero, by choosing an independent variable
12241 with the largest coefficient and making it dependent on the others. The
12242 newly dependent variable is eliminated from all current dependencies,
12243 thereby possibly making other dependent variables known.
12245 The given list |p| is, of course, totally destroyed by all this processing.
12247 @c void mp_linear_eq (MP mp, pointer p, small_number t) {
12248 pointer q,r,s; /* for link manipulation */
12249 pointer x; /* the variable that loses its independence */
12250 integer n; /* the number of times |x| had been halved */
12251 integer v; /* the coefficient of |x| in list |p| */
12252 pointer prev_r; /* lags one step behind |r| */
12253 pointer final_node; /* the constant term of the new dependency list */
12254 integer w; /* a tentative coefficient */
12255 @<Find a node |q| in list |p| whose coefficient |v| is largest@>;
12256 x=info(q); n=value(x) % s_scale;
12257 @<Divide list |p| by |-v|, removing node |q|@>;
12258 if ( mp->internal[tracing_equations]>0 ) {
12259 @<Display the new dependency@>;
12261 @<Simplify all existing dependencies by substituting for |x|@>;
12262 @<Change variable |x| from |independent| to |dependent| or |known|@>;
12263 if ( mp->fix_needed ) mp_fix_dependencies(mp);
12266 @ @<Find a node |q| in list |p| whose coefficient |v| is largest@>=
12267 q=p; r=link(p); v=value(q);
12268 while ( info(r)!=null ) {
12269 if ( abs(value(r))>abs(v) ) { q=r; v=value(r); };
12273 @ Here we want to change the coefficients from |scaled| to |fraction|,
12274 except in the constant term. In the common case of a trivial equation
12275 like `\.{x=3.14}', we will have |v=-fraction_one|, |q=p|, and |t=mp_dependent|.
12277 @<Divide list |p| by |-v|, removing node |q|@>=
12278 s=temp_head; link(s)=p; r=p;
12281 link(s)=link(r); mp_free_node(mp, r,dep_node_size);
12283 w=mp_make_fraction(mp, value(r),v);
12284 if ( abs(w)<=half_fraction_threshold ) {
12285 link(s)=link(r); mp_free_node(mp, r,dep_node_size);
12291 } while (info(r)!=null);
12292 if ( t==mp_proto_dependent ) {
12293 value(r)=-mp_make_scaled(mp, value(r),v);
12294 } else if ( v!=-fraction_one ) {
12295 value(r)=-mp_make_fraction(mp, value(r),v);
12297 final_node=r; p=link(temp_head)
12299 @ @<Display the new dependency@>=
12300 if ( mp_interesting(mp, x) ) {
12301 mp_begin_diagnostic(mp); mp_print_nl(mp, "## ");
12302 mp_print_variable_name(mp, x);
12303 @:]]]\#\#_}{\.{\#\#}@>
12305 while ( w>0 ) { mp_print(mp, "*4"); w=w-2; };
12306 mp_print_char(mp, '='); mp_print_dependency(mp, p,mp_dependent);
12307 mp_end_diagnostic(mp, false);
12310 @ @<Simplify all existing dependencies by substituting for |x|@>=
12311 prev_r=dep_head; r=link(dep_head);
12312 while ( r!=dep_head ) {
12313 s=dep_list(r); q=mp_p_with_x_becoming_q(mp, s,x,p,type(r));
12314 if ( info(q)==null ) {
12315 mp_make_known(mp, r,q);
12318 do { q=link(q); } while (info(q)!=null);
12324 @ @<Change variable |x| from |independent| to |dependent| or |known|@>=
12325 if ( n>0 ) @<Divide list |p| by $2^n$@>;
12326 if ( info(p)==null ) {
12329 if ( abs(value(x))>=fraction_one ) mp_val_too_big(mp, value(x));
12330 mp_free_node(mp, p,dep_node_size);
12331 if ( mp->cur_exp==x ) if ( mp->cur_type==mp_independent ) {
12332 mp->cur_exp=value(x); mp->cur_type=mp_known;
12333 mp_free_node(mp, x,value_node_size);
12336 type(x)=mp_dependent; mp->dep_final=final_node; mp_new_dep(mp, x,p);
12337 if ( mp->cur_exp==x ) if ( mp->cur_type==mp_independent ) mp->cur_type=mp_dependent;
12340 @ @<Divide list |p| by $2^n$@>=
12342 s=temp_head; link(temp_head)=p; r=p;
12345 else w=value(r) / two_to_the(n);
12346 if ( (abs(w)<=half_fraction_threshold)&&(info(r)!=null) ) {
12348 mp_free_node(mp, r,dep_node_size);
12353 } while (info(s)!=null);
12357 @ The |check_mem| procedure, which is used only when \MP\ is being
12358 debugged, makes sure that the current dependency lists are well formed.
12360 @<Check the list of linear dependencies@>=
12361 q=dep_head; p=link(q);
12362 while ( p!=dep_head ) {
12363 if ( prev_dep(p)!=q ) {
12364 mp_print_nl(mp, "Bad PREVDEP at "); mp_print_int(mp, p);
12369 r=info(p); q=p; p=link(q);
12370 if ( r==null ) break;
12371 if ( value(info(p))>=value(r) ) {
12372 mp_print_nl(mp, "Out of order at "); mp_print_int(mp, p);
12373 @.Out of order...@>
12378 @* \[25] Dynamic nonlinear equations.
12379 Variables of numeric type are maintained by the general scheme of
12380 independent, dependent, and known values that we have just studied;
12381 and the components of pair and transform variables are handled in the
12382 same way. But \MP\ also has five other types of values: \&{boolean},
12383 \&{string}, \&{pen}, \&{path}, and \&{picture}; what about them?
12385 Equations are allowed between nonlinear quantities, but only in a
12386 simple form. Two variables that haven't yet been assigned values are
12387 either equal to each other, or they're not.
12389 Before a boolean variable has received a value, its type is |mp_unknown_boolean|;
12390 similarly, there are variables whose type is |mp_unknown_string|, |mp_unknown_pen|,
12391 |mp_unknown_path|, and |mp_unknown_picture|. In such cases the value is either
12392 |null| (which means that no other variables are equivalent to this one), or
12393 it points to another variable of the same undefined type. The pointers in the
12394 latter case form a cycle of nodes, which we shall call a ``ring.''
12395 Rings of undefined variables may include capsules, which arise as
12396 intermediate results within expressions or as \&{expr} parameters to macros.
12398 When one member of a ring receives a value, the same value is given to
12399 all the other members. In the case of paths and pictures, this implies
12400 making separate copies of a potentially large data structure; users should
12401 restrain their enthusiasm for such generality, unless they have lots and
12402 lots of memory space.
12404 @ The following procedure is called when a capsule node is being
12405 added to a ring (e.g., when an unknown variable is mentioned in an expression).
12407 @c pointer mp_new_ring_entry (MP mp,pointer p) {
12408 pointer q; /* the new capsule node */
12409 q=mp_get_node(mp, value_node_size); name_type(q)=mp_capsule;
12411 if ( value(p)==null ) value(q)=p; else value(q)=value(p);
12416 @ Conversely, we might delete a capsule or a variable before it becomes known.
12417 The following procedure simply detaches a quantity from its ring,
12418 without recycling the storage.
12420 @<Declare the recycling subroutines@>=
12421 void mp_ring_delete (MP mp,pointer p) {
12424 if ( q!=null ) if ( q!=p ){
12425 while ( value(q)!=p ) q=value(q);
12430 @ Eventually there might be an equation that assigns values to all of the
12431 variables in a ring. The |nonlinear_eq| subroutine does the necessary
12432 propagation of values.
12434 If the parameter |flush_p| is |true|, node |p| itself needn't receive a
12435 value, it will soon be recycled.
12437 @c void mp_nonlinear_eq (MP mp,integer v, pointer p, boolean flush_p) {
12438 small_number t; /* the type of ring |p| */
12439 pointer q,r; /* link manipulation registers */
12440 t=type(p)-unknown_tag; q=value(p);
12441 if ( flush_p ) type(p)=mp_vacuous; else p=q;
12443 r=value(q); type(q)=t;
12445 case mp_boolean_type: value(q)=v; break;
12446 case mp_string_type: value(q)=v; add_str_ref(v); break;
12447 case mp_pen_type: value(q)=copy_pen(v); break;
12448 case mp_path_type: value(q)=mp_copy_path(mp, v); break;
12449 case mp_picture_type: value(q)=v; add_edge_ref(v); break;
12450 } /* there ain't no more cases */
12455 @ If two members of rings are equated, and if they have the same type,
12456 the |ring_merge| procedure is called on to make them equivalent.
12458 @c void mp_ring_merge (MP mp,pointer p, pointer q) {
12459 pointer r; /* traverses one list */
12463 @<Exclaim about a redundant equation@>;
12468 r=value(p); value(p)=value(q); value(q)=r;
12471 @ @<Exclaim about a redundant equation@>=
12473 print_err("Redundant equation");
12474 @.Redundant equation@>
12475 help2("I already knew that this equation was true.")
12476 ("But perhaps no harm has been done; let's continue.");
12477 mp_put_get_error(mp);
12480 @* \[26] Introduction to the syntactic routines.
12481 Let's pause a moment now and try to look at the Big Picture.
12482 The \MP\ program consists of three main parts: syntactic routines,
12483 semantic routines, and output routines. The chief purpose of the
12484 syntactic routines is to deliver the user's input to the semantic routines,
12485 while parsing expressions and locating operators and operands. The
12486 semantic routines act as an interpreter responding to these operators,
12487 which may be regarded as commands. And the output routines are
12488 periodically called on to produce compact font descriptions that can be
12489 used for typesetting or for making interim proof drawings. We have
12490 discussed the basic data structures and many of the details of semantic
12491 operations, so we are good and ready to plunge into the part of \MP\ that
12492 actually controls the activities.
12494 Our current goal is to come to grips with the |get_next| procedure,
12495 which is the keystone of \MP's input mechanism. Each call of |get_next|
12496 sets the value of three variables |cur_cmd|, |cur_mod|, and |cur_sym|,
12497 representing the next input token.
12498 $$\vbox{\halign{#\hfil\cr
12499 \hbox{|cur_cmd| denotes a command code from the long list of codes
12501 \hbox{|cur_mod| denotes a modifier of the command code;}\cr
12502 \hbox{|cur_sym| is the hash address of the symbolic token that was
12504 \hbox{\qquad or zero in the case of a numeric or string
12505 or capsule token.}\cr}}$$
12506 Underlying this external behavior of |get_next| is all the machinery
12507 necessary to convert from character files to tokens. At a given time we
12508 may be only partially finished with the reading of several files (for
12509 which \&{input} was specified), and partially finished with the expansion
12510 of some user-defined macros and/or some macro parameters, and partially
12511 finished reading some text that the user has inserted online,
12512 and so on. When reading a character file, the characters must be
12513 converted to tokens; comments and blank spaces must
12514 be removed, numeric and string tokens must be evaluated.
12516 To handle these situations, which might all be present simultaneously,
12517 \MP\ uses various stacks that hold information about the incomplete
12518 activities, and there is a finite state control for each level of the
12519 input mechanism. These stacks record the current state of an implicitly
12520 recursive process, but the |get_next| procedure is not recursive.
12523 eight_bits cur_cmd; /* current command set by |get_next| */
12524 integer cur_mod; /* operand of current command */
12525 halfword cur_sym; /* hash address of current symbol */
12527 @ The |print_cmd_mod| routine prints a symbolic interpretation of a
12528 command code and its modifier.
12529 It consists of a rather tedious sequence of print
12530 commands, and most of it is essentially an inverse to the |primitive|
12531 routine that enters a \MP\ primitive into |hash| and |eqtb|. Therefore almost
12532 all of this procedure appears elsewhere in the program, together with the
12533 corresponding |primitive| calls.
12535 @<Declare the procedure called |print_cmd_mod|@>=
12536 void mp_print_cmd_mod (MP mp,integer c, integer m) {
12538 @<Cases of |print_cmd_mod| for symbolic printing of primitives@>
12539 default: mp_print(mp, "[unknown command code!]"); break;
12543 @ Here is a procedure that displays a given command in braces, in the
12544 user's transcript file.
12546 @d show_cur_cmd_mod mp_show_cmd_mod(mp, mp->cur_cmd,mp->cur_mod)
12549 void mp_show_cmd_mod (MP mp,integer c, integer m) {
12550 mp_begin_diagnostic(mp); mp_print_nl(mp, "{");
12551 mp_print_cmd_mod(mp, c,m); mp_print_char(mp, '}');
12552 mp_end_diagnostic(mp, false);
12555 @* \[27] Input stacks and states.
12556 The state of \MP's input mechanism appears in the input stack, whose
12557 entries are records with five fields, called |index|, |start|, |loc|,
12558 |limit|, and |name|. The top element of this stack is maintained in a
12559 global variable for which no subscripting needs to be done; the other
12560 elements of the stack appear in an array. Hence the stack is declared thus:
12564 quarterword index_field;
12565 halfword start_field, loc_field, limit_field, name_field;
12569 in_state_record *input_stack;
12570 integer input_ptr; /* first unused location of |input_stack| */
12571 integer max_in_stack; /* largest value of |input_ptr| when pushing */
12572 in_state_record cur_input; /* the ``top'' input state */
12573 int stack_size; /* maximum number of simultaneous input sources */
12575 @ @<Allocate or initialize ...@>=
12576 mp->stack_size = 300;
12577 mp->input_stack = xmalloc((mp->stack_size+1),sizeof(in_state_record));
12579 @ @<Dealloc variables@>=
12580 xfree(mp->input_stack);
12582 @ We've already defined the special variable |loc==cur_input.loc_field|
12583 in our discussion of basic input-output routines. The other components of
12584 |cur_input| are defined in the same way:
12586 @d index mp->cur_input.index_field /* reference for buffer information */
12587 @d start mp->cur_input.start_field /* starting position in |buffer| */
12588 @d limit mp->cur_input.limit_field /* end of current line in |buffer| */
12589 @d name mp->cur_input.name_field /* name of the current file */
12591 @ Let's look more closely now at the five control variables
12592 (|index|,~|start|,~|loc|,~|limit|,~|name|),
12593 assuming that \MP\ is reading a line of characters that have been input
12594 from some file or from the user's terminal. There is an array called
12595 |buffer| that acts as a stack of all lines of characters that are
12596 currently being read from files, including all lines on subsidiary
12597 levels of the input stack that are not yet completed. \MP\ will return to
12598 the other lines when it is finished with the present input file.
12600 (Incidentally, on a machine with byte-oriented addressing, it would be
12601 appropriate to combine |buffer| with the |str_pool| array,
12602 letting the buffer entries grow downward from the top of the string pool
12603 and checking that these two tables don't bump into each other.)
12605 The line we are currently working on begins in position |start| of the
12606 buffer; the next character we are about to read is |buffer[loc]|; and
12607 |limit| is the location of the last character present. We always have
12608 |loc<=limit|. For convenience, |buffer[limit]| has been set to |"%"|, so
12609 that the end of a line is easily sensed.
12611 The |name| variable is a string number that designates the name of
12612 the current file, if we are reading an ordinary text file. Special codes
12613 |is_term..max_spec_src| indicate other sources of input text.
12615 @d is_term 0 /* |name| value when reading from the terminal for normal input */
12616 @d is_read 1 /* |name| value when executing a \&{readstring} or \&{readfrom} */
12617 @d is_scantok 2 /* |name| value when reading text generated by \&{scantokens} */
12618 @d max_spec_src is_scantok
12620 @ Additional information about the current line is available via the
12621 |index| variable, which counts how many lines of characters are present
12622 in the buffer below the current level. We have |index=0| when reading
12623 from the terminal and prompting the user for each line; then if the user types,
12624 e.g., `\.{input figs}', we will have |index=1| while reading
12625 the file \.{figs.mp}. However, it does not follow that |index| is the
12626 same as the input stack pointer, since many of the levels on the input
12627 stack may come from token lists and some |index| values may correspond
12628 to \.{MPX} files that are not currently on the stack.
12630 The global variable |in_open| is equal to the highest |index| value counting
12631 \.{MPX} files but excluding token-list input levels. Thus, the number of
12632 partially read lines in the buffer is |in_open+1| and we have |in_open>=index|
12633 when we are not reading a token list.
12635 If we are not currently reading from the terminal,
12636 we are reading from the file variable |input_file[index]|. We use
12637 the notation |terminal_input| as a convenient abbreviation for |name=is_term|,
12638 and |cur_file| as an abbreviation for |input_file[index]|.
12640 When \MP\ is not reading from the terminal, the global variable |line| contains
12641 the line number in the current file, for use in error messages. More precisely,
12642 |line| is a macro for |line_stack[index]| and the |line_stack| array gives
12643 the line number for each file in the |input_file| array.
12645 When an \.{MPX} file is opened the file name is stored in the |mpx_name|
12646 array so that the name doesn't get lost when the file is temporarily removed
12647 from the input stack.
12648 Thus when |input_file[k]| is an \.{MPX} file, its name is |mpx_name[k]|
12649 and it contains translated \TeX\ pictures for |input_file[k-1]|.
12650 Since this is not an \.{MPX} file, we have
12651 $$ \hbox{|mpx_name[k-1]<=absent|}. $$
12652 This |name| field is set to |finished| when |input_file[k]| is completely
12655 If more information about the input state is needed, it can be
12656 included in small arrays like those shown here. For example,
12657 the current page or segment number in the input file might be put
12658 into a variable |page|, that is really a macro for the current entry
12659 in `\ignorespaces|page_stack:array[0..max_in_open] of integer|\unskip'
12660 by analogy with |line_stack|.
12661 @^system dependencies@>
12663 @d terminal_input (name==is_term) /* are we reading from the terminal? */
12664 @d cur_file mp->input_file[index] /* the current |FILE *| variable */
12665 @d line mp->line_stack[index] /* current line number in the current source file */
12666 @d in_name mp->iname_stack[index] /* a string used to construct \.{MPX} file names */
12667 @d in_area mp->iarea_stack[index] /* another string for naming \.{MPX} files */
12668 @d absent 1 /* |name_field| value for unused |mpx_in_stack| entries */
12669 @d mpx_reading (mp->mpx_name[index]>absent)
12670 /* when reading a file, is it an \.{MPX} file? */
12672 /* |name_field| value when the corresponding \.{MPX} file is finished */
12675 integer in_open; /* the number of lines in the buffer, less one */
12676 unsigned int open_parens; /* the number of open text files */
12677 FILE * *input_file ;
12678 integer *line_stack ; /* the line number for each file */
12679 char * *iname_stack; /* used for naming \.{MPX} files */
12680 char * *iarea_stack; /* used for naming \.{MPX} files */
12681 halfword*mpx_name ;
12683 @ @<Allocate or ...@>=
12684 mp->input_file = xmalloc((mp->max_in_open+1),sizeof(FILE *));
12685 mp->line_stack = xmalloc((mp->max_in_open+1),sizeof(integer));
12686 mp->iname_stack = xmalloc((mp->max_in_open+1),sizeof(char *));
12687 mp->iarea_stack = xmalloc((mp->max_in_open+1),sizeof(char *));
12688 mp->mpx_name = xmalloc((mp->max_in_open+1),sizeof(halfword));
12691 for (k=0;k<=mp->max_in_open;k++) {
12692 mp->iname_stack[k] =NULL;
12693 mp->iarea_stack[k] =NULL;
12697 @ @<Dealloc variables@>=
12700 for (l=0;l<=mp->max_in_open;l++) {
12701 xfree(mp->iname_stack[l]);
12702 xfree(mp->iarea_stack[l]);
12705 xfree(mp->input_file);
12706 xfree(mp->line_stack);
12707 xfree(mp->iname_stack);
12708 xfree(mp->iarea_stack);
12709 xfree(mp->mpx_name);
12712 @ However, all this discussion about input state really applies only to the
12713 case that we are inputting from a file. There is another important case,
12714 namely when we are currently getting input from a token list. In this case
12715 |index>max_in_open|, and the conventions about the other state variables
12718 \yskip\hang|loc| is a pointer to the current node in the token list, i.e.,
12719 the node that will be read next. If |loc=null|, the token list has been
12722 \yskip\hang|start| points to the first node of the token list; this node
12723 may or may not contain a reference count, depending on the type of token
12726 \yskip\hang|token_type|, which takes the place of |index| in the
12727 discussion above, is a code number that explains what kind of token list
12730 \yskip\hang|name| points to the |eqtb| address of the control sequence
12731 being expanded, if the current token list is a macro not defined by
12732 \&{vardef}. Macros defined by \&{vardef} have |name=null|; their name
12733 can be deduced by looking at their first two parameters.
12735 \yskip\hang|param_start|, which takes the place of |limit|, tells where
12736 the parameters of the current macro or loop text begin in the |param_stack|.
12738 \yskip\noindent The |token_type| can take several values, depending on
12739 where the current token list came from:
12742 \indent|forever_text|, if the token list being scanned is the body of
12743 a \&{forever} loop;
12745 \indent|loop_text|, if the token list being scanned is the body of
12746 a \&{for} or \&{forsuffixes} loop;
12748 \indent|parameter|, if a \&{text} or \&{suffix} parameter is being scanned;
12750 \indent|backed_up|, if the token list being scanned has been inserted as
12751 `to be read again'.
12753 \indent|inserted|, if the token list being scanned has been inserted as
12754 part of error recovery;
12756 \indent|macro|, if the expansion of a user-defined symbolic token is being
12760 The token list begins with a reference count if and only if |token_type=
12762 @^reference counts@>
12764 @d token_type index /* type of current token list */
12765 @d token_state (index>(int)mp->max_in_open) /* are we scanning a token list? */
12766 @d file_state (index<=(int)mp->max_in_open) /* are we scanning a file line? */
12767 @d param_start limit /* base of macro parameters in |param_stack| */
12768 @d forever_text (mp->max_in_open+1) /* |token_type| code for loop texts */
12769 @d loop_text (mp->max_in_open+2) /* |token_type| code for loop texts */
12770 @d parameter (mp->max_in_open+3) /* |token_type| code for parameter texts */
12771 @d backed_up (mp->max_in_open+4) /* |token_type| code for texts to be reread */
12772 @d inserted (mp->max_in_open+5) /* |token_type| code for inserted texts */
12773 @d macro (mp->max_in_open+6) /* |token_type| code for macro replacement texts */
12775 @ The |param_stack| is an auxiliary array used to hold pointers to the token
12776 lists for parameters at the current level and subsidiary levels of input.
12777 This stack grows at a different rate from the others.
12780 pointer *param_stack; /* token list pointers for parameters */
12781 integer param_ptr; /* first unused entry in |param_stack| */
12782 integer max_param_stack; /* largest value of |param_ptr| */
12784 @ @<Allocate or initialize ...@>=
12785 mp->param_stack = xmalloc((mp->param_size+1),sizeof(pointer));
12787 @ @<Dealloc variables@>=
12788 xfree(mp->param_stack);
12790 @ Notice that the |line| isn't valid when |token_state| is true because it
12791 depends on |index|. If we really need to know the line number for the
12792 topmost file in the index stack we use the following function. If a page
12793 number or other information is needed, this routine should be modified to
12794 compute it as well.
12795 @^system dependencies@>
12797 @<Declare a function called |true_line|@>=
12798 integer mp_true_line (MP mp) {
12799 int k; /* an index into the input stack */
12800 if ( file_state && (name>max_spec_src) ) {
12805 ((mp->input_stack[(k-1)].index_field>mp->max_in_open)||
12806 (mp->input_stack[(k-1)].name_field<=max_spec_src))) {
12809 return mp->line_stack[(k-1)];
12814 @ Thus, the ``current input state'' can be very complicated indeed; there
12815 can be many levels and each level can arise in a variety of ways. The
12816 |show_context| procedure, which is used by \MP's error-reporting routine to
12817 print out the current input state on all levels down to the most recent
12818 line of characters from an input file, illustrates most of these conventions.
12819 The global variable |file_ptr| contains the lowest level that was
12820 displayed by this procedure.
12823 integer file_ptr; /* shallowest level shown by |show_context| */
12825 @ The status at each level is indicated by printing two lines, where the first
12826 line indicates what was read so far and the second line shows what remains
12827 to be read. The context is cropped, if necessary, so that the first line
12828 contains at most |half_error_line| characters, and the second contains
12829 at most |error_line|. Non-current input levels whose |token_type| is
12830 `|backed_up|' are shown only if they have not been fully read.
12832 @c void mp_show_context (MP mp) { /* prints where the scanner is */
12833 int old_setting; /* saved |selector| setting */
12834 @<Local variables for formatting calculations@>
12835 mp->file_ptr=mp->input_ptr; mp->input_stack[mp->file_ptr]=mp->cur_input;
12836 /* store current state */
12838 mp->cur_input=mp->input_stack[mp->file_ptr]; /* enter into the context */
12839 @<Display the current context@>;
12841 if ( (name>max_spec_src) || (mp->file_ptr==0) ) break;
12842 decr(mp->file_ptr);
12844 mp->cur_input=mp->input_stack[mp->input_ptr]; /* restore original state */
12847 @ @<Display the current context@>=
12848 if ( (mp->file_ptr==mp->input_ptr) || file_state ||
12849 (token_type!=backed_up) || (loc!=null) ) {
12850 /* we omit backed-up token lists that have already been read */
12851 mp->tally=0; /* get ready to count characters */
12852 old_setting=mp->selector;
12853 if ( file_state ) {
12854 @<Print location of current line@>;
12855 @<Pseudoprint the line@>;
12857 @<Print type of token list@>;
12858 @<Pseudoprint the token list@>;
12860 mp->selector=old_setting; /* stop pseudoprinting */
12861 @<Print two lines using the tricky pseudoprinted information@>;
12864 @ This routine should be changed, if necessary, to give the best possible
12865 indication of where the current line resides in the input file.
12866 For example, on some systems it is best to print both a page and line number.
12867 @^system dependencies@>
12869 @<Print location of current line@>=
12870 if ( name>max_spec_src ) {
12871 mp_print_nl(mp, "l."); mp_print_int(mp, mp_true_line(mp));
12872 } else if ( terminal_input ) {
12873 if ( mp->file_ptr==0 ) mp_print_nl(mp, "<*>");
12874 else mp_print_nl(mp, "<insert>");
12875 } else if ( name==is_scantok ) {
12876 mp_print_nl(mp, "<scantokens>");
12878 mp_print_nl(mp, "<read>");
12880 mp_print_char(mp, ' ')
12882 @ Can't use case statement here because the |token_type| is not
12883 a constant expression.
12885 @<Print type of token list@>=
12887 if(token_type==forever_text) {
12888 mp_print_nl(mp, "<forever> ");
12889 } else if (token_type==loop_text) {
12890 @<Print the current loop value@>;
12891 } else if (token_type==parameter) {
12892 mp_print_nl(mp, "<argument> ");
12893 } else if (token_type==backed_up) {
12894 if ( loc==null ) mp_print_nl(mp, "<recently read> ");
12895 else mp_print_nl(mp, "<to be read again> ");
12896 } else if (token_type==inserted) {
12897 mp_print_nl(mp, "<inserted text> ");
12898 } else if (token_type==macro) {
12900 if ( name!=null ) mp_print_text(name);
12901 else @<Print the name of a \&{vardef}'d macro@>;
12902 mp_print(mp, "->");
12904 mp_print_nl(mp, "?");/* this should never happen */
12909 @ The parameter that corresponds to a loop text is either a token list
12910 (in the case of \&{forsuffixes}) or a ``capsule'' (in the case of \&{for}).
12911 We'll discuss capsules later; for now, all we need to know is that
12912 the |link| field in a capsule parameter is |void| and that
12913 |print_exp(p,0)| displays the value of capsule~|p| in abbreviated form.
12915 @d diov (null+1) /* a null pointer different from |null| */
12917 @<Print the current loop value@>=
12918 { mp_print_nl(mp, "<for("); p=mp->param_stack[param_start];
12920 if ( link(p)==diov ) mp_print_exp(mp, p,0); /* we're in a \&{for} loop */
12921 else mp_show_token_list(mp, p,null,20,mp->tally);
12923 mp_print(mp, ")> ");
12926 @ The first two parameters of a macro defined by \&{vardef} will be token
12927 lists representing the macro's prefix and ``at point.'' By putting these
12928 together, we get the macro's full name.
12930 @<Print the name of a \&{vardef}'d macro@>=
12931 { p=mp->param_stack[param_start];
12933 mp_show_token_list(mp, mp->param_stack[param_start+1],null,20,mp->tally);
12936 while ( link(q)!=null ) q=link(q);
12937 link(q)=mp->param_stack[param_start+1];
12938 mp_show_token_list(mp, p,null,20,mp->tally);
12943 @ Now it is necessary to explain a little trick. We don't want to store a long
12944 string that corresponds to a token list, because that string might take up
12945 lots of memory; and we are printing during a time when an error message is
12946 being given, so we dare not do anything that might overflow one of \MP's
12947 tables. So `pseudoprinting' is the answer: We enter a mode of printing
12948 that stores characters into a buffer of length |error_line|, where character
12949 $k+1$ is placed into \hbox{|trick_buf[k mod error_line]|} if
12950 |k<trick_count|, otherwise character |k| is dropped. Initially we set
12951 |tally:=0| and |trick_count:=1000000|; then when we reach the
12952 point where transition from line 1 to line 2 should occur, we
12953 set |first_count:=tally| and |trick_count:=@tmax@>(error_line,
12954 tally+1+error_line-half_error_line)|. At the end of the
12955 pseudoprinting, the values of |first_count|, |tally|, and
12956 |trick_count| give us all the information we need to print the two lines,
12957 and all of the necessary text is in |trick_buf|.
12959 Namely, let |l| be the length of the descriptive information that appears
12960 on the first line. The length of the context information gathered for that
12961 line is |k=first_count|, and the length of the context information
12962 gathered for line~2 is $m=\min(|tally|, |trick_count|)-k$. If |l+k<=h|,
12963 where |h=half_error_line|, we print |trick_buf[0..k-1]| after the
12964 descriptive information on line~1, and set |n:=l+k|; here |n| is the
12965 length of line~1. If $l+k>h$, some cropping is necessary, so we set |n:=h|
12966 and print `\.{...}' followed by
12967 $$\hbox{|trick_buf[(l+k-h+3)..k-1]|,}$$
12968 where subscripts of |trick_buf| are circular modulo |error_line|. The
12969 second line consists of |n|~spaces followed by |trick_buf[k..(k+m-1)]|,
12970 unless |n+m>error_line|; in the latter case, further cropping is done.
12971 This is easier to program than to explain.
12973 @<Local variables for formatting...@>=
12974 int i; /* index into |buffer| */
12975 integer l; /* length of descriptive information on line 1 */
12976 integer m; /* context information gathered for line 2 */
12977 int n; /* length of line 1 */
12978 integer p; /* starting or ending place in |trick_buf| */
12979 integer q; /* temporary index */
12981 @ The following code tells the print routines to gather
12982 the desired information.
12984 @d begin_pseudoprint {
12985 l=mp->tally; mp->tally=0; mp->selector=pseudo;
12986 mp->trick_count=1000000;
12988 @d set_trick_count {
12989 mp->first_count=mp->tally;
12990 mp->trick_count=mp->tally+1+mp->error_line-mp->half_error_line;
12991 if ( mp->trick_count<mp->error_line ) mp->trick_count=mp->error_line;
12994 @ And the following code uses the information after it has been gathered.
12996 @<Print two lines using the tricky pseudoprinted information@>=
12997 if ( mp->trick_count==1000000 ) set_trick_count;
12998 /* |set_trick_count| must be performed */
12999 if ( mp->tally<mp->trick_count ) m=mp->tally-mp->first_count;
13000 else m=mp->trick_count-mp->first_count; /* context on line 2 */
13001 if ( l+mp->first_count<=mp->half_error_line ) {
13002 p=0; n=l+mp->first_count;
13004 mp_print(mp, "..."); p=l+mp->first_count-mp->half_error_line+3;
13005 n=mp->half_error_line;
13007 for (q=p;q<=mp->first_count-1;q++) {
13008 mp_print_char(mp, mp->trick_buf[q % mp->error_line]);
13011 for (q=1;q<=n;q++) {
13012 mp_print_char(mp, ' '); /* print |n| spaces to begin line~2 */
13014 if ( m+n<=mp->error_line ) p=mp->first_count+m;
13015 else p=mp->first_count+(mp->error_line-n-3);
13016 for (q=mp->first_count;q<=p-1;q++) {
13017 mp_print_char(mp, mp->trick_buf[q % mp->error_line]);
13019 if ( m+n>mp->error_line ) mp_print(mp, "...")
13021 @ But the trick is distracting us from our current goal, which is to
13022 understand the input state. So let's concentrate on the data structures that
13023 are being pseudoprinted as we finish up the |show_context| procedure.
13025 @<Pseudoprint the line@>=
13028 for (i=start;i<=limit-1;i++) {
13029 if ( i==loc ) set_trick_count;
13030 mp_print_str(mp, mp->buffer[i]);
13034 @ @<Pseudoprint the token list@>=
13036 if ( token_type!=macro ) mp_show_token_list(mp, start,loc,100000,0);
13037 else mp_show_macro(mp, start,loc,100000)
13039 @ Here is the missing piece of |show_token_list| that is activated when the
13040 token beginning line~2 is about to be shown:
13042 @<Do magic computation@>=set_trick_count
13044 @* \[28] Maintaining the input stacks.
13045 The following subroutines change the input status in commonly needed ways.
13047 First comes |push_input|, which stores the current state and creates a
13048 new level (having, initially, the same properties as the old).
13050 @d push_input { /* enter a new input level, save the old */
13051 if ( mp->input_ptr>mp->max_in_stack ) {
13052 mp->max_in_stack=mp->input_ptr;
13053 if ( mp->input_ptr==mp->stack_size ) {
13054 int l = (mp->stack_size+(mp->stack_size>>2));
13055 XREALLOC(mp->input_stack, l, in_state_record);
13056 mp->stack_size = l;
13059 mp->input_stack[mp->input_ptr]=mp->cur_input; /* stack the record */
13060 incr(mp->input_ptr);
13063 @ And of course what goes up must come down.
13065 @d pop_input { /* leave an input level, re-enter the old */
13066 decr(mp->input_ptr); mp->cur_input=mp->input_stack[mp->input_ptr];
13069 @ Here is a procedure that starts a new level of token-list input, given
13070 a token list |p| and its type |t|. If |t=macro|, the calling routine should
13071 set |name|, reset~|loc|, and increase the macro's reference count.
13073 @d back_list(A) mp_begin_token_list(mp, (A),backed_up) /* backs up a simple token list */
13075 @c void mp_begin_token_list (MP mp,pointer p, quarterword t) {
13076 push_input; start=p; token_type=t;
13077 param_start=mp->param_ptr; loc=p;
13080 @ When a token list has been fully scanned, the following computations
13081 should be done as we leave that level of input.
13084 @c void mp_end_token_list (MP mp) { /* leave a token-list input level */
13085 pointer p; /* temporary register */
13086 if ( token_type>=backed_up ) { /* token list to be deleted */
13087 if ( token_type<=inserted ) {
13088 mp_flush_token_list(mp, start); goto DONE;
13090 mp_delete_mac_ref(mp, start); /* update reference count */
13093 while ( mp->param_ptr>param_start ) { /* parameters must be flushed */
13094 decr(mp->param_ptr);
13095 p=mp->param_stack[mp->param_ptr];
13097 if ( link(p)==diov ) { /* it's an \&{expr} parameter */
13098 mp_recycle_value(mp, p); mp_free_node(mp, p,value_node_size);
13100 mp_flush_token_list(mp, p); /* it's a \&{suffix} or \&{text} parameter */
13105 pop_input; check_interrupt;
13108 @ The contents of |cur_cmd,cur_mod,cur_sym| are placed into an equivalent
13109 token by the |cur_tok| routine.
13112 @c @<Declare the procedure called |make_exp_copy|@>;
13113 pointer mp_cur_tok (MP mp) {
13114 pointer p; /* a new token node */
13115 small_number save_type; /* |cur_type| to be restored */
13116 integer save_exp; /* |cur_exp| to be restored */
13117 if ( mp->cur_sym==0 ) {
13118 if ( mp->cur_cmd==capsule_token ) {
13119 save_type=mp->cur_type; save_exp=mp->cur_exp;
13120 mp_make_exp_copy(mp, mp->cur_mod); p=mp_stash_cur_exp(mp); link(p)=null;
13121 mp->cur_type=save_type; mp->cur_exp=save_exp;
13123 p=mp_get_node(mp, token_node_size);
13124 value(p)=mp->cur_mod; name_type(p)=mp_token;
13125 if ( mp->cur_cmd==numeric_token ) type(p)=mp_known;
13126 else type(p)=mp_string_type;
13129 fast_get_avail(p); info(p)=mp->cur_sym;
13134 @ Sometimes \MP\ has read too far and wants to ``unscan'' what it has
13135 seen. The |back_input| procedure takes care of this by putting the token
13136 just scanned back into the input stream, ready to be read again.
13137 If |cur_sym<>0|, the values of |cur_cmd| and |cur_mod| are irrelevant.
13140 void mp_back_input (MP mp);
13142 @ @c void mp_back_input (MP mp) {/* undoes one token of input */
13143 pointer p; /* a token list of length one */
13145 while ( token_state &&(loc==null) )
13146 mp_end_token_list(mp); /* conserve stack space */
13150 @ The |back_error| routine is used when we want to restore or replace an
13151 offending token just before issuing an error message. We disable interrupts
13152 during the call of |back_input| so that the help message won't be lost.
13155 void mp_error (MP mp);
13156 void mp_back_error (MP mp);
13158 @ @c void mp_back_error (MP mp) { /* back up one token and call |error| */
13159 mp->OK_to_interrupt=false;
13161 mp->OK_to_interrupt=true; mp_error(mp);
13163 void mp_ins_error (MP mp) { /* back up one inserted token and call |error| */
13164 mp->OK_to_interrupt=false;
13165 mp_back_input(mp); token_type=inserted;
13166 mp->OK_to_interrupt=true; mp_error(mp);
13169 @ The |begin_file_reading| procedure starts a new level of input for lines
13170 of characters to be read from a file, or as an insertion from the
13171 terminal. It does not take care of opening the file, nor does it set |loc|
13172 or |limit| or |line|.
13173 @^system dependencies@>
13175 @c void mp_begin_file_reading (MP mp) {
13176 if ( mp->in_open==mp->max_in_open )
13177 mp_overflow(mp, "text input levels",mp->max_in_open);
13178 @:MetaPost capacity exceeded text input levels}{\quad text input levels@>
13179 if ( mp->first==mp->buf_size )
13180 mp_reallocate_buffer(mp,(mp->buf_size+(mp->buf_size>>2)));
13181 incr(mp->in_open); push_input; index=mp->in_open;
13182 mp->mpx_name[index]=absent;
13184 name=is_term; /* |terminal_input| is now |true| */
13187 @ Conversely, the variables must be downdated when such a level of input
13188 is finished. Any associated \.{MPX} file must also be closed and popped
13189 off the file stack.
13191 @c void mp_end_file_reading (MP mp) {
13192 if ( mp->in_open>index ) {
13193 if ( (mp->mpx_name[mp->in_open]==absent)||(name<=max_spec_src) ) {
13194 mp_confusion(mp, "endinput");
13195 @:this can't happen endinput}{\quad endinput@>
13197 fclose(mp->input_file[mp->in_open]); /* close an \.{MPX} file */
13198 delete_str_ref(mp->mpx_name[mp->in_open]);
13203 if ( index!=mp->in_open ) mp_confusion(mp, "endinput");
13204 if ( name>max_spec_src ) {
13206 delete_str_ref(name);
13207 xfree(in_name); in_name=NULL;
13208 xfree(in_area); in_area=NULL;
13210 pop_input; decr(mp->in_open);
13213 @ Here is a function that tries to resume input from an \.{MPX} file already
13214 associated with the current input file. It returns |false| if this doesn't
13217 @c boolean mp_begin_mpx_reading (MP mp) {
13218 if ( mp->in_open!=index+1 ) {
13221 if ( mp->mpx_name[mp->in_open]<=absent ) mp_confusion(mp, "mpx");
13222 @:this can't happen mpx}{\quad mpx@>
13223 if ( mp->first==mp->buf_size )
13224 mp_reallocate_buffer(mp,(mp->buf_size+(mp->buf_size>>2)));
13225 push_input; index=mp->in_open;
13227 name=mp->mpx_name[mp->in_open]; add_str_ref(name);
13228 @<Put an empty line in the input buffer@>;
13233 @ This procedure temporarily stops reading an \.{MPX} file.
13235 @c void mp_end_mpx_reading (MP mp) {
13236 if ( mp->in_open!=index ) mp_confusion(mp, "mpx");
13237 @:this can't happen mpx}{\quad mpx@>
13239 @<Complain that we are not at the end of a line in the \.{MPX} file@>;
13245 @ Here we enforce a restriction that simplifies the input stacks considerably.
13246 This should not inconvenience the user because \.{MPX} files are generated
13247 by an auxiliary program called \.{DVItoMP}.
13249 @ @<Complain that we are not at the end of a line in the \.{MPX} file@>=
13251 print_err("`mpxbreak' must be at the end of a line");
13252 help4("This file contains picture expressions for btex...etex")
13253 ("blocks. Such files are normally generated automatically")
13254 ("but this one seems to be messed up. I'm going to ignore")
13255 ("the rest of this line.");
13259 @ In order to keep the stack from overflowing during a long sequence of
13260 inserted `\.{show}' commands, the following routine removes completed
13261 error-inserted lines from memory.
13263 @c void mp_clear_for_error_prompt (MP mp) {
13264 while ( file_state && terminal_input &&
13265 (mp->input_ptr>0)&&(loc==limit) ) mp_end_file_reading(mp);
13266 mp_print_ln(mp); clear_terminal;
13269 @ To get \MP's whole input mechanism going, we perform the following
13272 @<Initialize the input routines@>=
13273 { mp->input_ptr=0; mp->max_in_stack=0;
13274 mp->in_open=0; mp->open_parens=0; mp->max_buf_stack=0;
13275 mp->param_ptr=0; mp->max_param_stack=0;
13277 start=1; index=0; line=0; name=is_term;
13278 mp->mpx_name[0]=absent;
13279 mp->force_eof=false;
13280 if ( ! mp_init_terminal(mp) ) exit(EXIT_FAILURE);
13281 limit=mp->last; mp->first=mp->last+1;
13282 /* |init_terminal| has set |loc| and |last| */
13285 @* \[29] Getting the next token.
13286 The heart of \MP's input mechanism is the |get_next| procedure, which
13287 we shall develop in the next few sections of the program. Perhaps we
13288 shouldn't actually call it the ``heart,'' however; it really acts as \MP's
13289 eyes and mouth, reading the source files and gobbling them up. And it also
13290 helps \MP\ to regurgitate stored token lists that are to be processed again.
13292 The main duty of |get_next| is to input one token and to set |cur_cmd|
13293 and |cur_mod| to that token's command code and modifier. Furthermore, if
13294 the input token is a symbolic token, that token's |hash| address
13295 is stored in |cur_sym|; otherwise |cur_sym| is set to zero.
13297 Underlying this simple description is a certain amount of complexity
13298 because of all the cases that need to be handled.
13299 However, the inner loop of |get_next| is reasonably short and fast.
13301 @ Before getting into |get_next|, we need to consider a mechanism by which
13302 \MP\ helps keep errors from propagating too far. Whenever the program goes
13303 into a mode where it keeps calling |get_next| repeatedly until a certain
13304 condition is met, it sets |scanner_status| to some value other than |normal|.
13305 Then if an input file ends, or if an `\&{outer}' symbol appears,
13306 an appropriate error recovery will be possible.
13308 The global variable |warning_info| helps in this error recovery by providing
13309 additional information. For example, |warning_info| might indicate the
13310 name of a macro whose replacement text is being scanned.
13312 @d normal 0 /* |scanner_status| at ``quiet times'' */
13313 @d skipping 1 /* |scanner_status| when false conditional text is being skipped */
13314 @d flushing 2 /* |scanner_status| when junk after a statement is being ignored */
13315 @d absorbing 3 /* |scanner_status| when a \&{text} parameter is being scanned */
13316 @d var_defining 4 /* |scanner_status| when a \&{vardef} is being scanned */
13317 @d op_defining 5 /* |scanner_status| when a macro \&{def} is being scanned */
13318 @d loop_defining 6 /* |scanner_status| when a \&{for} loop is being scanned */
13319 @d tex_flushing 7 /* |scanner_status| when skipping \TeX\ material */
13322 integer scanner_status; /* are we scanning at high speed? */
13323 integer warning_info; /* if so, what else do we need to know,
13324 in case an error occurs? */
13326 @ @<Initialize the input routines@>=
13327 mp->scanner_status=normal;
13329 @ The following subroutine
13330 is called when an `\&{outer}' symbolic token has been scanned or
13331 when the end of a file has been reached. These two cases are distinguished
13332 by |cur_sym|, which is zero at the end of a file.
13334 @c boolean mp_check_outer_validity (MP mp) {
13335 pointer p; /* points to inserted token list */
13336 if ( mp->scanner_status==normal ) {
13338 } else if ( mp->scanner_status==tex_flushing ) {
13339 @<Check if the file has ended while flushing \TeX\ material and set the
13340 result value for |check_outer_validity|@>;
13342 mp->deletions_allowed=false;
13343 @<Back up an outer symbolic token so that it can be reread@>;
13344 if ( mp->scanner_status>skipping ) {
13345 @<Tell the user what has run away and try to recover@>;
13347 print_err("Incomplete if; all text was ignored after line ");
13348 @.Incomplete if...@>
13349 mp_print_int(mp, mp->warning_info);
13350 help3("A forbidden `outer' token occurred in skipped text.")
13351 ("This kind of error happens when you say `if...' and forget")
13352 ("the matching `fi'. I've inserted a `fi'; this might work.");
13353 if ( mp->cur_sym==0 )
13354 mp->help_line[2]="The file ended while I was skipping conditional text.";
13355 mp->cur_sym=frozen_fi; mp_ins_error(mp);
13357 mp->deletions_allowed=true;
13362 @ @<Check if the file has ended while flushing \TeX\ material and set...@>=
13363 if ( mp->cur_sym!=0 ) {
13366 mp->deletions_allowed=false;
13367 print_err("TeX mode didn't end; all text was ignored after line ");
13368 mp_print_int(mp, mp->warning_info);
13369 help2("The file ended while I was looking for the `etex' to")
13370 ("finish this TeX material. I've inserted `etex' now.");
13371 mp->cur_sym = frozen_etex;
13373 mp->deletions_allowed=true;
13377 @ @<Back up an outer symbolic token so that it can be reread@>=
13378 if ( mp->cur_sym!=0 ) {
13379 p=mp_get_avail(mp); info(p)=mp->cur_sym;
13380 back_list(p); /* prepare to read the symbolic token again */
13383 @ @<Tell the user what has run away...@>=
13385 mp_runaway(mp); /* print the definition-so-far */
13386 if ( mp->cur_sym==0 ) {
13387 print_err("File ended");
13388 @.File ended while scanning...@>
13390 print_err("Forbidden token found");
13391 @.Forbidden token found...@>
13393 mp_print(mp, " while scanning ");
13394 help4("I suspect you have forgotten an `enddef',")
13395 ("causing me to read past where you wanted me to stop.")
13396 ("I'll try to recover; but if the error is serious,")
13397 ("you'd better type `E' or `X' now and fix your file.");
13398 switch (mp->scanner_status) {
13399 @<Complete the error message,
13400 and set |cur_sym| to a token that might help recover from the error@>
13401 } /* there are no other cases */
13405 @ As we consider various kinds of errors, it is also appropriate to
13406 change the first line of the help message just given; |help_line[3]|
13407 points to the string that might be changed.
13409 @<Complete the error message,...@>=
13411 mp_print(mp, "to the end of the statement");
13412 mp->help_line[3]="A previous error seems to have propagated,";
13413 mp->cur_sym=frozen_semicolon;
13416 mp_print(mp, "a text argument");
13417 mp->help_line[3]="It seems that a right delimiter was left out,";
13418 if ( mp->warning_info==0 ) {
13419 mp->cur_sym=frozen_end_group;
13421 mp->cur_sym=frozen_right_delimiter;
13422 equiv(frozen_right_delimiter)=mp->warning_info;
13427 mp_print(mp, "the definition of ");
13428 if ( mp->scanner_status==op_defining )
13429 mp_print_text(mp->warning_info);
13431 mp_print_variable_name(mp, mp->warning_info);
13432 mp->cur_sym=frozen_end_def;
13434 case loop_defining:
13435 mp_print(mp, "the text of a ");
13436 mp_print_text(mp->warning_info);
13437 mp_print(mp, " loop");
13438 mp->help_line[3]="I suspect you have forgotten an `endfor',";
13439 mp->cur_sym=frozen_end_for;
13442 @ The |runaway| procedure displays the first part of the text that occurred
13443 when \MP\ began its special |scanner_status|, if that text has been saved.
13445 @<Declare the procedure called |runaway|@>=
13446 void mp_runaway (MP mp) {
13447 if ( mp->scanner_status>flushing ) {
13448 mp_print_nl(mp, "Runaway ");
13449 switch (mp->scanner_status) {
13450 case absorbing: mp_print(mp, "text?"); break;
13452 case op_defining: mp_print(mp,"definition?"); break;
13453 case loop_defining: mp_print(mp, "loop?"); break;
13454 } /* there are no other cases */
13456 mp_show_token_list(mp, link(hold_head),null,mp->error_line-10,0);
13460 @ We need to mention a procedure that may be called by |get_next|.
13463 void mp_firm_up_the_line (MP mp);
13465 @ And now we're ready to take the plunge into |get_next| itself.
13466 Note that the behavior depends on the |scanner_status| because percent signs
13467 and double quotes need to be passed over when skipping TeX material.
13470 void mp_get_next (MP mp) {
13471 /* sets |cur_cmd|, |cur_mod|, |cur_sym| to next token */
13473 /*restart*/ /* go here to get the next input token */
13474 /*exit*/ /* go here when the next input token has been got */
13475 /*|common_ending|*/ /* go here to finish getting a symbolic token */
13476 /*found*/ /* go here when the end of a symbolic token has been found */
13477 /*switch*/ /* go here to branch on the class of an input character */
13478 /*|start_numeric_token|,|start_decimal_token|,|fin_numeric_token|,|done|*/
13479 /* go here at crucial stages when scanning a number */
13480 int k; /* an index into |buffer| */
13481 ASCII_code c; /* the current character in the buffer */
13482 ASCII_code class; /* its class number */
13483 integer n,f; /* registers for decimal-to-binary conversion */
13486 if ( file_state ) {
13487 @<Input from external file; |goto restart| if no input found,
13488 or |return| if a non-symbolic token is found@>;
13490 @<Input from token list; |goto restart| if end of list or
13491 if a parameter needs to be expanded,
13492 or |return| if a non-symbolic token is found@>;
13495 @<Finish getting the symbolic token in |cur_sym|;
13496 |goto restart| if it is illegal@>;
13499 @ When a symbolic token is declared to be `\&{outer}', its command code
13500 is increased by |outer_tag|.
13503 @<Finish getting the symbolic token in |cur_sym|...@>=
13504 mp->cur_cmd=eq_type(mp->cur_sym); mp->cur_mod=equiv(mp->cur_sym);
13505 if ( mp->cur_cmd>=outer_tag ) {
13506 if ( mp_check_outer_validity(mp) )
13507 mp->cur_cmd=mp->cur_cmd-outer_tag;
13512 @ A percent sign appears in |buffer[limit]|; this makes it unnecessary
13513 to have a special test for end-of-line.
13516 @<Input from external file;...@>=
13519 c=mp->buffer[loc]; incr(loc); class=mp->char_class[c];
13521 case digit_class: goto START_NUMERIC_TOKEN; break;
13523 class=mp->char_class[mp->buffer[loc]];
13524 if ( class>period_class ) {
13526 } else if ( class<period_class ) { /* |class=digit_class| */
13527 n=0; goto START_DECIMAL_TOKEN;
13531 case space_class: goto SWITCH; break;
13532 case percent_class:
13533 if ( mp->scanner_status==tex_flushing ) {
13534 if ( loc<limit ) goto SWITCH;
13536 @<Move to next line of file, or |goto restart| if there is no next line@>;
13541 if ( mp->scanner_status==tex_flushing ) goto SWITCH;
13542 else @<Get a string token and |return|@>;
13544 case isolated_classes:
13545 k=loc-1; goto FOUND; break;
13546 case invalid_class:
13547 if ( mp->scanner_status==tex_flushing ) goto SWITCH;
13548 else @<Decry the invalid character and |goto restart|@>;
13550 default: break; /* letters, etc. */
13553 while ( mp->char_class[mp->buffer[loc]]==class ) incr(loc);
13555 START_NUMERIC_TOKEN:
13556 @<Get the integer part |n| of a numeric token;
13557 set |f:=0| and |goto fin_numeric_token| if there is no decimal point@>;
13558 START_DECIMAL_TOKEN:
13559 @<Get the fraction part |f| of a numeric token@>;
13561 @<Pack the numeric and fraction parts of a numeric token
13564 mp->cur_sym=mp_id_lookup(mp, k,loc-k);
13567 @ We go to |restart| instead of to |SWITCH|, because |state| might equal
13568 |token_list| after the error has been dealt with
13569 (cf.\ |clear_for_error_prompt|).
13571 @<Decry the invalid...@>=
13573 print_err("Text line contains an invalid character");
13574 @.Text line contains...@>
13575 help2("A funny symbol that I can\'t read has just been input.")
13576 ("Continue, and I'll forget that it ever happened.");
13577 mp->deletions_allowed=false; mp_error(mp); mp->deletions_allowed=true;
13581 @ @<Get a string token and |return|@>=
13583 if ( mp->buffer[loc]=='"' ) {
13584 mp->cur_mod=rts("");
13586 k=loc; mp->buffer[limit+1]='"';
13589 } while (mp->buffer[loc]!='"');
13591 @<Decry the missing string delimiter and |goto restart|@>;
13594 mp->cur_mod=mp->buffer[k];
13598 append_char(mp->buffer[k]); incr(k);
13600 mp->cur_mod=mp_make_string(mp);
13603 incr(loc); mp->cur_cmd=string_token;
13607 @ We go to |restart| after this error message, not to |SWITCH|,
13608 because the |clear_for_error_prompt| routine might have reinstated
13609 |token_state| after |error| has finished.
13611 @<Decry the missing string delimiter and |goto restart|@>=
13613 loc=limit; /* the next character to be read on this line will be |"%"| */
13614 print_err("Incomplete string token has been flushed");
13615 @.Incomplete string token...@>
13616 help3("Strings should finish on the same line as they began.")
13617 ("I've deleted the partial string; you might want to")
13618 ("insert another by typing, e.g., `I\"new string\"'.");
13619 mp->deletions_allowed=false; mp_error(mp);
13620 mp->deletions_allowed=true;
13624 @ @<Get the integer part |n| of a numeric token...@>=
13626 while ( mp->char_class[mp->buffer[loc]]==digit_class ) {
13627 if ( n<32768 ) n=10*n+mp->buffer[loc]-'0';
13630 if ( mp->buffer[loc]=='.' )
13631 if ( mp->char_class[mp->buffer[loc+1]]==digit_class )
13634 goto FIN_NUMERIC_TOKEN;
13637 @ @<Get the fraction part |f| of a numeric token@>=
13640 if ( k<17 ) { /* digits for |k>=17| cannot affect the result */
13641 mp->dig[k]=mp->buffer[loc]-'0'; incr(k);
13644 } while (mp->char_class[mp->buffer[loc]]==digit_class);
13645 f=mp_round_decimals(mp, k);
13650 @ @<Pack the numeric and fraction parts of a numeric token and |return|@>=
13652 @<Set |cur_mod:=n*unity+f| and check if it is uncomfortably large@>;
13653 } else if ( mp->scanner_status!=tex_flushing ) {
13654 print_err("Enormous number has been reduced");
13655 @.Enormous number...@>
13656 help2("I can\'t handle numbers bigger than 32767.99998;")
13657 ("so I've changed your constant to that maximum amount.");
13658 mp->deletions_allowed=false; mp_error(mp); mp->deletions_allowed=true;
13659 mp->cur_mod=el_gordo;
13661 mp->cur_cmd=numeric_token; return
13663 @ @<Set |cur_mod:=n*unity+f| and check if it is uncomfortably large@>=
13665 mp->cur_mod=n*unity+f;
13666 if ( mp->cur_mod>=fraction_one ) {
13667 if ( (mp->internal[warning_check]>0) &&
13668 (mp->scanner_status!=tex_flushing) ) {
13669 print_err("Number is too large (");
13670 mp_print_scaled(mp, mp->cur_mod);
13671 mp_print_char(mp, ')');
13672 help3("It is at least 4096. Continue and I'll try to cope")
13673 ("with that big value; but it might be dangerous.")
13674 ("(Set warningcheck:=0 to suppress this message.)");
13680 @ Let's consider now what happens when |get_next| is looking at a token list.
13683 @<Input from token list;...@>=
13684 if ( loc>=mp->hi_mem_min ) { /* one-word token */
13685 mp->cur_sym=info(loc); loc=link(loc); /* move to next */
13686 if ( mp->cur_sym>=expr_base ) {
13687 if ( mp->cur_sym>=suffix_base ) {
13688 @<Insert a suffix or text parameter and |goto restart|@>;
13690 mp->cur_cmd=capsule_token;
13691 mp->cur_mod=mp->param_stack[param_start+mp->cur_sym-(expr_base)];
13692 mp->cur_sym=0; return;
13695 } else if ( loc>null ) {
13696 @<Get a stored numeric or string or capsule token and |return|@>
13697 } else { /* we are done with this token list */
13698 mp_end_token_list(mp); goto RESTART; /* resume previous level */
13701 @ @<Insert a suffix or text parameter...@>=
13703 if ( mp->cur_sym>=text_base ) mp->cur_sym=mp->cur_sym-mp->param_size;
13704 /* |param_size=text_base-suffix_base| */
13705 mp_begin_token_list(mp,
13706 mp->param_stack[param_start+mp->cur_sym-(suffix_base)],
13711 @ @<Get a stored numeric or string or capsule token...@>=
13713 if ( name_type(loc)==mp_token ) {
13714 mp->cur_mod=value(loc);
13715 if ( type(loc)==mp_known ) {
13716 mp->cur_cmd=numeric_token;
13718 mp->cur_cmd=string_token; add_str_ref(mp->cur_mod);
13721 mp->cur_mod=loc; mp->cur_cmd=capsule_token;
13723 loc=link(loc); return;
13726 @ All of the easy branches of |get_next| have now been taken care of.
13727 There is one more branch.
13729 @<Move to next line of file, or |goto restart|...@>=
13730 if ( name>max_spec_src ) {
13731 @<Read next line of file into |buffer|, or
13732 |goto restart| if the file has ended@>;
13734 if ( mp->input_ptr>0 ) {
13735 /* text was inserted during error recovery or by \&{scantokens} */
13736 mp_end_file_reading(mp); goto RESTART; /* resume previous level */
13738 if ( mp->selector<log_only || mp->selector>=write_file) mp_open_log_file(mp);
13739 if ( mp->interaction>mp_nonstop_mode ) {
13740 if ( limit==start ) /* previous line was empty */
13741 mp_print_nl(mp, "(Please type a command or say `end')");
13743 mp_print_ln(mp); mp->first=start;
13744 prompt_input("*"); /* input on-line into |buffer| */
13746 limit=mp->last; mp->buffer[limit]='%';
13747 mp->first=limit+1; loc=start;
13749 mp_fatal_error(mp, "*** (job aborted, no legal end found)");
13751 /* nonstop mode, which is intended for overnight batch processing,
13752 never waits for on-line input */
13756 @ The global variable |force_eof| is normally |false|; it is set |true|
13757 by an \&{endinput} command.
13760 boolean force_eof; /* should the next \&{input} be aborted early? */
13762 @ We must decrement |loc| in order to leave the buffer in a valid state
13763 when an error condition causes us to |goto restart| without calling
13764 |end_file_reading|.
13766 @<Read next line of file into |buffer|, or
13767 |goto restart| if the file has ended@>=
13769 incr(line); mp->first=start;
13770 if ( ! mp->force_eof ) {
13771 if ( mp_input_ln(mp, cur_file,true) ) /* not end of file */
13772 mp_firm_up_the_line(mp); /* this sets |limit| */
13774 mp->force_eof=true;
13776 if ( mp->force_eof ) {
13777 mp->force_eof=false;
13779 if ( mpx_reading ) {
13780 @<Complain that the \.{MPX} file ended unexpectly; then set
13781 |cur_sym:=frozen_mpx_break| and |goto comon_ending|@>;
13783 mp_print_char(mp, ')'); decr(mp->open_parens);
13784 update_terminal; /* show user that file has been read */
13785 mp_end_file_reading(mp); /* resume previous level */
13786 if ( mp_check_outer_validity(mp) ) goto RESTART;
13790 mp->buffer[limit]='%'; mp->first=limit+1; loc=start; /* ready to read */
13793 @ We should never actually come to the end of an \.{MPX} file because such
13794 files should have an \&{mpxbreak} after the translation of the last
13795 \&{btex}$\,\ldots\,$\&{etex} block.
13797 @<Complain that the \.{MPX} file ended unexpectly; then set...@>=
13799 mp->mpx_name[index]=finished;
13800 print_err("mpx file ended unexpectedly");
13801 help4("The file had too few picture expressions for btex...etex")
13802 ("blocks. Such files are normally generated automatically")
13803 ("but this one got messed up. You might want to insert a")
13804 ("picture expression now.");
13805 mp->deletions_allowed=false; mp_error(mp); mp->deletions_allowed=true;
13806 mp->cur_sym=frozen_mpx_break; goto COMMON_ENDING;
13809 @ Sometimes we want to make it look as though we have just read a blank line
13810 without really doing so.
13812 @<Put an empty line in the input buffer@>=
13813 mp->last=mp->first; limit=mp->last; /* simulate |input_ln| and |firm_up_the_line| */
13814 mp->buffer[limit]='%'; mp->first=limit+1; loc=start
13816 @ If the user has set the |pausing| parameter to some positive value,
13817 and if nonstop mode has not been selected, each line of input is displayed
13818 on the terminal and the transcript file, followed by `\.{=>}'.
13819 \MP\ waits for a response. If the response is null (i.e., if nothing is
13820 typed except perhaps a few blank spaces), the original
13821 line is accepted as it stands; otherwise the line typed is
13822 used instead of the line in the file.
13824 @c void mp_firm_up_the_line (MP mp) {
13825 size_t k; /* an index into |buffer| */
13827 if ( mp->internal[pausing]>0 ) if ( mp->interaction>mp_nonstop_mode ) {
13828 wake_up_terminal; mp_print_ln(mp);
13829 if ( start<limit ) {
13830 for (k=(size_t)start;k<=(size_t)(limit-1);k++) {
13831 mp_print_str(mp, mp->buffer[k]);
13834 mp->first=limit; prompt_input("=>"); /* wait for user response */
13836 if ( mp->last>mp->first ) {
13837 for (k=mp->first;k<=mp->last-1;k++) { /* move line down in buffer */
13838 mp->buffer[k+start-mp->first]=mp->buffer[k];
13840 limit=start+mp->last-mp->first;
13845 @* \[30] Dealing with \TeX\ material.
13846 The \&{btex}$\,\ldots\,$\&{etex} and \&{verbatimtex}$\,\ldots\,$\&{etex}
13847 features need to be implemented at a low level in the scanning process
13848 so that \MP\ can stay in synch with the a preprocessor that treats
13849 blocks of \TeX\ material as they occur in the input file without trying
13850 to expand \MP\ macros. Thus we need a special version of |get_next|
13851 that does not expand macros and such but does handle \&{btex},
13852 \&{verbatimtex}, etc.
13854 The special version of |get_next| is called |get_t_next|. It works by flushing
13855 \&{btex}$\,\ldots\,$\&{etex} and \&{verbatimtex}\allowbreak
13856 $\,\ldots\,$\&{etex} blocks, switching to the \.{MPX} file when it sees
13857 \&{btex}, and switching back when it sees \&{mpxbreak}.
13863 mp_primitive(mp, "btex",start_tex,btex_code);
13864 @:btex_}{\&{btex} primitive@>
13865 mp_primitive(mp, "verbatimtex",start_tex,verbatim_code);
13866 @:verbatimtex_}{\&{verbatimtex} primitive@>
13867 mp_primitive(mp, "etex",etex_marker,0); mp->eqtb[frozen_etex]=mp->eqtb[mp->cur_sym];
13868 @:etex_}{\&{etex} primitive@>
13869 mp_primitive(mp, "mpxbreak",mpx_break,0); mp->eqtb[frozen_mpx_break]=mp->eqtb[mp->cur_sym];
13870 @:mpx_break_}{\&{mpxbreak} primitive@>
13872 @ @<Cases of |print_cmd...@>=
13873 case start_tex: if ( m==btex_code ) mp_print(mp, "btex");
13874 else mp_print(mp, "verbatimtex"); break;
13875 case etex_marker: mp_print(mp, "etex"); break;
13876 case mpx_break: mp_print(mp, "mpxbreak"); break;
13878 @ Actually, |get_t_next| is a macro that avoids procedure overhead except
13879 in the unusual case where \&{btex}, \&{verbatimtex}, \&{etex}, or \&{mpxbreak}
13882 @d get_t_next {mp_get_next(mp); if ( mp->cur_cmd<=max_pre_command ) mp_t_next(mp); }
13885 void mp_start_mpx_input (MP mp);
13888 void mp_t_next (MP mp) {
13889 int old_status; /* saves the |scanner_status| */
13890 integer old_info; /* saves the |warning_info| */
13891 while ( mp->cur_cmd<=max_pre_command ) {
13892 if ( mp->cur_cmd==mpx_break ) {
13893 if ( ! file_state || (mp->mpx_name[index]==absent) ) {
13894 @<Complain about a misplaced \&{mpxbreak}@>;
13896 mp_end_mpx_reading(mp);
13899 } else if ( mp->cur_cmd==start_tex ) {
13900 if ( token_state || (name<=max_spec_src) ) {
13901 @<Complain that we are not reading a file@>;
13902 } else if ( mpx_reading ) {
13903 @<Complain that \.{MPX} files cannot contain \TeX\ material@>;
13904 } else if ( (mp->cur_mod!=verbatim_code)&&
13905 (mp->mpx_name[index]!=finished) ) {
13906 if ( ! mp_begin_mpx_reading(mp) ) mp_start_mpx_input(mp);
13911 @<Complain about a misplaced \&{etex}@>;
13913 goto COMMON_ENDING;
13915 @<Flush the \TeX\ material@>;
13921 @ We could be in the middle of an operation such as skipping false conditional
13922 text when \TeX\ material is encountered, so we must be careful to save the
13925 @<Flush the \TeX\ material@>=
13926 old_status=mp->scanner_status;
13927 old_info=mp->warning_info;
13928 mp->scanner_status=tex_flushing;
13929 mp->warning_info=line;
13930 do { mp_get_next(mp); } while (mp->cur_cmd!=etex_marker);
13931 mp->scanner_status=old_status;
13932 mp->warning_info=old_info
13934 @ @<Complain that \.{MPX} files cannot contain \TeX\ material@>=
13935 { print_err("An mpx file cannot contain btex or verbatimtex blocks");
13936 help4("This file contains picture expressions for btex...etex")
13937 ("blocks. Such files are normally generated automatically")
13938 ("but this one seems to be messed up. I'll just keep going")
13939 ("and hope for the best.");
13943 @ @<Complain that we are not reading a file@>=
13944 { print_err("You can only use `btex' or `verbatimtex' in a file");
13945 help3("I'll have to ignore this preprocessor command because it")
13946 ("only works when there is a file to preprocess. You might")
13947 ("want to delete everything up to the next `etex`.");
13951 @ @<Complain about a misplaced \&{mpxbreak}@>=
13952 { print_err("Misplaced mpxbreak");
13953 help2("I'll ignore this preprocessor command because it")
13954 ("doesn't belong here");
13958 @ @<Complain about a misplaced \&{etex}@>=
13959 { print_err("Extra etex will be ignored");
13960 help1("There is no btex or verbatimtex for this to match");
13964 @* \[31] Scanning macro definitions.
13965 \MP\ has a variety of ways to tuck tokens away into token lists for later
13966 use: Macros can be defined with \&{def}, \&{vardef}, \&{primarydef}, etc.;
13967 repeatable code can be defined with \&{for}, \&{forever}, \&{forsuffixes}.
13968 All such operations are handled by the routines in this part of the program.
13970 The modifier part of each command code is zero for the ``ending delimiters''
13971 like \&{enddef} and \&{endfor}.
13973 @d start_def 1 /* command modifier for \&{def} */
13974 @d var_def 2 /* command modifier for \&{vardef} */
13975 @d end_def 0 /* command modifier for \&{enddef} */
13976 @d start_forever 1 /* command modifier for \&{forever} */
13977 @d end_for 0 /* command modifier for \&{endfor} */
13980 mp_primitive(mp, "def",macro_def,start_def);
13981 @:def_}{\&{def} primitive@>
13982 mp_primitive(mp, "vardef",macro_def,var_def);
13983 @:var_def_}{\&{vardef} primitive@>
13984 mp_primitive(mp, "primarydef",macro_def,secondary_primary_macro);
13985 @:primary_def_}{\&{primarydef} primitive@>
13986 mp_primitive(mp, "secondarydef",macro_def,tertiary_secondary_macro);
13987 @:secondary_def_}{\&{secondarydef} primitive@>
13988 mp_primitive(mp, "tertiarydef",macro_def,expression_tertiary_macro);
13989 @:tertiary_def_}{\&{tertiarydef} primitive@>
13990 mp_primitive(mp, "enddef",macro_def,end_def); mp->eqtb[frozen_end_def]=mp->eqtb[mp->cur_sym];
13991 @:end_def_}{\&{enddef} primitive@>
13993 mp_primitive(mp, "for",iteration,expr_base);
13994 @:for_}{\&{for} primitive@>
13995 mp_primitive(mp, "forsuffixes",iteration,suffix_base);
13996 @:for_suffixes_}{\&{forsuffixes} primitive@>
13997 mp_primitive(mp, "forever",iteration,start_forever);
13998 @:forever_}{\&{forever} primitive@>
13999 mp_primitive(mp, "endfor",iteration,end_for); mp->eqtb[frozen_end_for]=mp->eqtb[mp->cur_sym];
14000 @:end_for_}{\&{endfor} primitive@>
14002 @ @<Cases of |print_cmd...@>=
14004 if ( m<=var_def ) {
14005 if ( m==start_def ) mp_print(mp, "def");
14006 else if ( m<start_def ) mp_print(mp, "enddef");
14007 else mp_print(mp, "vardef");
14008 } else if ( m==secondary_primary_macro ) {
14009 mp_print(mp, "primarydef");
14010 } else if ( m==tertiary_secondary_macro ) {
14011 mp_print(mp, "secondarydef");
14013 mp_print(mp, "tertiarydef");
14017 if ( m<=start_forever ) {
14018 if ( m==start_forever ) mp_print(mp, "forever");
14019 else mp_print(mp, "endfor");
14020 } else if ( m==expr_base ) {
14021 mp_print(mp, "for");
14023 mp_print(mp, "forsuffixes");
14027 @ Different macro-absorbing operations have different syntaxes, but they
14028 also have a lot in common. There is a list of special symbols that are to
14029 be replaced by parameter tokens; there is a special command code that
14030 ends the definition; the quotation conventions are identical. Therefore
14031 it makes sense to have most of the work done by a single subroutine. That
14032 subroutine is called |scan_toks|.
14034 The first parameter to |scan_toks| is the command code that will
14035 terminate scanning (either |macro_def|, |loop_repeat|, or |iteration|).
14037 The second parameter, |subst_list|, points to a (possibly empty) list
14038 of two-word nodes whose |info| and |value| fields specify symbol tokens
14039 before and after replacement. The list will be returned to free storage
14042 The third parameter is simply appended to the token list that is built.
14043 And the final parameter tells how many of the special operations
14044 \.{\#\AT!}, \.{\AT!}, and \.{\AT!\#} are to be replaced by suffix parameters.
14045 When such parameters are present, they are called \.{(SUFFIX0)},
14046 \.{(SUFFIX1)}, and \.{(SUFFIX2)}.
14048 @c pointer mp_scan_toks (MP mp,command_code terminator, pointer
14049 subst_list, pointer tail_end, small_number suffix_count) {
14050 pointer p; /* tail of the token list being built */
14051 pointer q; /* temporary for link management */
14052 integer balance; /* left delimiters minus right delimiters */
14053 p=hold_head; balance=1; link(hold_head)=null;
14056 if ( mp->cur_sym>0 ) {
14057 @<Substitute for |cur_sym|, if it's on the |subst_list|@>;
14058 if ( mp->cur_cmd==terminator ) {
14059 @<Adjust the balance; |break| if it's zero@>;
14060 } else if ( mp->cur_cmd==macro_special ) {
14061 @<Handle quoted symbols, \.{\#\AT!}, \.{\AT!}, or \.{\AT!\#}@>;
14064 link(p)=mp_cur_tok(mp); p=link(p);
14066 link(p)=tail_end; mp_flush_node_list(mp, subst_list);
14067 return link(hold_head);
14070 @ @<Substitute for |cur_sym|...@>=
14073 while ( q!=null ) {
14074 if ( info(q)==mp->cur_sym ) {
14075 mp->cur_sym=value(q); mp->cur_cmd=relax; break;
14081 @ @<Adjust the balance; |break| if it's zero@>=
14082 if ( mp->cur_mod>0 ) {
14090 @ Four commands are intended to be used only within macro texts: \&{quote},
14091 \.{\#\AT!}, \.{\AT!}, and \.{\AT!\#}. They are variants of a single command
14092 code called |macro_special|.
14094 @d quote 0 /* |macro_special| modifier for \&{quote} */
14095 @d macro_prefix 1 /* |macro_special| modifier for \.{\#\AT!} */
14096 @d macro_at 2 /* |macro_special| modifier for \.{\AT!} */
14097 @d macro_suffix 3 /* |macro_special| modifier for \.{\AT!\#} */
14100 mp_primitive(mp, "quote",macro_special,quote);
14101 @:quote_}{\&{quote} primitive@>
14102 mp_primitive(mp, "#@@",macro_special,macro_prefix);
14103 @:]]]\#\AT!_}{\.{\#\AT!} primitive@>
14104 mp_primitive(mp, "@@",macro_special,macro_at);
14105 @:]]]\AT!_}{\.{\AT!} primitive@>
14106 mp_primitive(mp, "@@#",macro_special,macro_suffix);
14107 @:]]]\AT!\#_}{\.{\AT!\#} primitive@>
14109 @ @<Cases of |print_cmd...@>=
14110 case macro_special:
14112 case macro_prefix: mp_print(mp, "#@@"); break;
14113 case macro_at: mp_print_char(mp, '@@'); break;
14114 case macro_suffix: mp_print(mp, "@@#"); break;
14115 default: mp_print(mp, "quote"); break;
14119 @ @<Handle quoted...@>=
14121 if ( mp->cur_mod==quote ) { get_t_next; }
14122 else if ( mp->cur_mod<=suffix_count )
14123 mp->cur_sym=suffix_base-1+mp->cur_mod;
14126 @ Here is a routine that's used whenever a token will be redefined. If
14127 the user's token is unredefinable, the `|frozen_inaccessible|' token is
14128 substituted; the latter is redefinable but essentially impossible to use,
14129 hence \MP's tables won't get fouled up.
14131 @c void mp_get_symbol (MP mp) { /* sets |cur_sym| to a safe symbol */
14134 if ( (mp->cur_sym==0)||(mp->cur_sym>frozen_inaccessible) ) {
14135 print_err("Missing symbolic token inserted");
14136 @.Missing symbolic token...@>
14137 help3("Sorry: You can\'t redefine a number, string, or expr.")
14138 ("I've inserted an inaccessible symbol so that your")
14139 ("definition will be completed without mixing me up too badly.");
14140 if ( mp->cur_sym>0 )
14141 mp->help_line[2]="Sorry: You can\'t redefine my error-recovery tokens.";
14142 else if ( mp->cur_cmd==string_token )
14143 delete_str_ref(mp->cur_mod);
14144 mp->cur_sym=frozen_inaccessible; mp_ins_error(mp); goto RESTART;
14148 @ Before we actually redefine a symbolic token, we need to clear away its
14149 former value, if it was a variable. The following stronger version of
14150 |get_symbol| does that.
14152 @c void mp_get_clear_symbol (MP mp) {
14153 mp_get_symbol(mp); mp_clear_symbol(mp, mp->cur_sym,false);
14156 @ Here's another little subroutine; it checks that an equals sign
14157 or assignment sign comes along at the proper place in a macro definition.
14159 @c void mp_check_equals (MP mp) {
14160 if ( mp->cur_cmd!=equals ) if ( mp->cur_cmd!=assignment ) {
14161 mp_missing_err(mp, "=");
14163 help5("The next thing in this `def' should have been `=',")
14164 ("because I've already looked at the definition heading.")
14165 ("But don't worry; I'll pretend that an equals sign")
14166 ("was present. Everything from here to `enddef'")
14167 ("will be the replacement text of this macro.");
14172 @ A \&{primarydef}, \&{secondarydef}, or \&{tertiarydef} is rather easily
14173 handled now that we have |scan_toks|. In this case there are
14174 two parameters, which will be \.{EXPR0} and \.{EXPR1} (i.e.,
14175 |expr_base| and |expr_base+1|).
14177 @c void mp_make_op_def (MP mp) {
14178 command_code m; /* the type of definition */
14179 pointer p,q,r; /* for list manipulation */
14181 mp_get_symbol(mp); q=mp_get_node(mp, token_node_size);
14182 info(q)=mp->cur_sym; value(q)=expr_base;
14183 mp_get_clear_symbol(mp); mp->warning_info=mp->cur_sym;
14184 mp_get_symbol(mp); p=mp_get_node(mp, token_node_size);
14185 info(p)=mp->cur_sym; value(p)=expr_base+1; link(p)=q;
14186 get_t_next; mp_check_equals(mp);
14187 mp->scanner_status=op_defining; q=mp_get_avail(mp); ref_count(q)=null;
14188 r=mp_get_avail(mp); link(q)=r; info(r)=general_macro;
14189 link(r)=mp_scan_toks(mp, macro_def,p,null,0);
14190 mp->scanner_status=normal; eq_type(mp->warning_info)=m;
14191 equiv(mp->warning_info)=q; mp_get_x_next(mp);
14194 @ Parameters to macros are introduced by the keywords \&{expr},
14195 \&{suffix}, \&{text}, \&{primary}, \&{secondary}, and \&{tertiary}.
14198 mp_primitive(mp, "expr",param_type,expr_base);
14199 @:expr_}{\&{expr} primitive@>
14200 mp_primitive(mp, "suffix",param_type,suffix_base);
14201 @:suffix_}{\&{suffix} primitive@>
14202 mp_primitive(mp, "text",param_type,text_base);
14203 @:text_}{\&{text} primitive@>
14204 mp_primitive(mp, "primary",param_type,primary_macro);
14205 @:primary_}{\&{primary} primitive@>
14206 mp_primitive(mp, "secondary",param_type,secondary_macro);
14207 @:secondary_}{\&{secondary} primitive@>
14208 mp_primitive(mp, "tertiary",param_type,tertiary_macro);
14209 @:tertiary_}{\&{tertiary} primitive@>
14211 @ @<Cases of |print_cmd...@>=
14213 if ( m>=expr_base ) {
14214 if ( m==expr_base ) mp_print(mp, "expr");
14215 else if ( m==suffix_base ) mp_print(mp, "suffix");
14216 else mp_print(mp, "text");
14217 } else if ( m<secondary_macro ) {
14218 mp_print(mp, "primary");
14219 } else if ( m==secondary_macro ) {
14220 mp_print(mp, "secondary");
14222 mp_print(mp, "tertiary");
14226 @ Let's turn next to the more complex processing associated with \&{def}
14227 and \&{vardef}. When the following procedure is called, |cur_mod|
14228 should be either |start_def| or |var_def|.
14230 @c @<Declare the procedure called |check_delimiter|@>;
14231 @<Declare the function called |scan_declared_variable|@>;
14232 void mp_scan_def (MP mp) {
14233 int m; /* the type of definition */
14234 int n; /* the number of special suffix parameters */
14235 int k; /* the total number of parameters */
14236 int c; /* the kind of macro we're defining */
14237 pointer r; /* parameter-substitution list */
14238 pointer q; /* tail of the macro token list */
14239 pointer p; /* temporary storage */
14240 halfword base; /* |expr_base|, |suffix_base|, or |text_base| */
14241 pointer l_delim,r_delim; /* matching delimiters */
14242 m=mp->cur_mod; c=general_macro; link(hold_head)=null;
14243 q=mp_get_avail(mp); ref_count(q)=null; r=null;
14244 @<Scan the token or variable to be defined;
14245 set |n|, |scanner_status|, and |warning_info|@>;
14247 if ( mp->cur_cmd==left_delimiter ) {
14248 @<Absorb delimited parameters, putting them into lists |q| and |r|@>;
14250 if ( mp->cur_cmd==param_type ) {
14251 @<Absorb undelimited parameters, putting them into list |r|@>;
14253 mp_check_equals(mp);
14254 p=mp_get_avail(mp); info(p)=c; link(q)=p;
14255 @<Attach the replacement text to the tail of node |p|@>;
14256 mp->scanner_status=normal; mp_get_x_next(mp);
14259 @ We don't put `|frozen_end_group|' into the replacement text of
14260 a \&{vardef}, because the user may want to redefine `\.{endgroup}'.
14262 @<Attach the replacement text to the tail of node |p|@>=
14263 if ( m==start_def ) {
14264 link(p)=mp_scan_toks(mp, macro_def,r,null,n);
14266 q=mp_get_avail(mp); info(q)=mp->bg_loc; link(p)=q;
14267 p=mp_get_avail(mp); info(p)=mp->eg_loc;
14268 link(q)=mp_scan_toks(mp, macro_def,r,p,n);
14270 if ( mp->warning_info==bad_vardef )
14271 mp_flush_token_list(mp, value(bad_vardef))
14275 int eg_loc; /* hash addresses of `\.{begingroup}' and `\.{endgroup}' */
14277 @ @<Scan the token or variable to be defined;...@>=
14278 if ( m==start_def ) {
14279 mp_get_clear_symbol(mp); mp->warning_info=mp->cur_sym; get_t_next;
14280 mp->scanner_status=op_defining; n=0;
14281 eq_type(mp->warning_info)=defined_macro; equiv(mp->warning_info)=q;
14283 p=mp_scan_declared_variable(mp);
14284 mp_flush_variable(mp, equiv(info(p)),link(p),true);
14285 mp->warning_info=mp_find_variable(mp, p); mp_flush_list(mp, p);
14286 if ( mp->warning_info==null ) @<Change to `\.{a bad variable}'@>;
14287 mp->scanner_status=var_defining; n=2;
14288 if ( mp->cur_cmd==macro_special ) if ( mp->cur_mod==macro_suffix ) {/* \.{\AT!\#} */
14291 type(mp->warning_info)=mp_unsuffixed_macro-2+n; value(mp->warning_info)=q;
14292 } /* |mp_suffixed_macro=mp_unsuffixed_macro+1| */
14294 @ @<Change to `\.{a bad variable}'@>=
14296 print_err("This variable already starts with a macro");
14297 @.This variable already...@>
14298 help2("After `vardef a' you can\'t say `vardef a.b'.")
14299 ("So I'll have to discard this definition.");
14300 mp_error(mp); mp->warning_info=bad_vardef;
14303 @ @<Initialize table entries...@>=
14304 name_type(bad_vardef)=mp_root; link(bad_vardef)=frozen_bad_vardef;
14305 equiv(frozen_bad_vardef)=bad_vardef; eq_type(frozen_bad_vardef)=tag_token;
14307 @ @<Absorb delimited parameters, putting them into lists |q| and |r|@>=
14309 l_delim=mp->cur_sym; r_delim=mp->cur_mod; get_t_next;
14310 if ( (mp->cur_cmd==param_type)&&(mp->cur_mod>=expr_base) ) {
14313 print_err("Missing parameter type; `expr' will be assumed");
14314 @.Missing parameter type@>
14315 help1("You should've had `expr' or `suffix' or `text' here.");
14316 mp_back_error(mp); base=expr_base;
14318 @<Absorb parameter tokens for type |base|@>;
14319 mp_check_delimiter(mp, l_delim,r_delim);
14321 } while (mp->cur_cmd==left_delimiter)
14323 @ @<Absorb parameter tokens for type |base|@>=
14325 link(q)=mp_get_avail(mp); q=link(q); info(q)=base+k;
14326 mp_get_symbol(mp); p=mp_get_node(mp, token_node_size);
14327 value(p)=base+k; info(p)=mp->cur_sym;
14328 if ( k==mp->param_size ) mp_overflow(mp, "parameter stack size",mp->param_size);
14329 @:MetaPost capacity exceeded parameter stack size}{\quad parameter stack size@>
14330 incr(k); link(p)=r; r=p; get_t_next;
14331 } while (mp->cur_cmd==comma)
14333 @ @<Absorb undelimited parameters, putting them into list |r|@>=
14335 p=mp_get_node(mp, token_node_size);
14336 if ( mp->cur_mod<expr_base ) {
14337 c=mp->cur_mod; value(p)=expr_base+k;
14339 value(p)=mp->cur_mod+k;
14340 if ( mp->cur_mod==expr_base ) c=expr_macro;
14341 else if ( mp->cur_mod==suffix_base ) c=suffix_macro;
14344 if ( k==mp->param_size ) mp_overflow(mp, "parameter stack size",mp->param_size);
14345 incr(k); mp_get_symbol(mp); info(p)=mp->cur_sym; link(p)=r; r=p; get_t_next;
14346 if ( c==expr_macro ) if ( mp->cur_cmd==of_token ) {
14347 c=of_macro; p=mp_get_node(mp, token_node_size);
14348 if ( k==mp->param_size ) mp_overflow(mp, "parameter stack size",mp->param_size);
14349 value(p)=expr_base+k; mp_get_symbol(mp); info(p)=mp->cur_sym;
14350 link(p)=r; r=p; get_t_next;
14354 @* \[32] Expanding the next token.
14355 Only a few command codes |<min_command| can possibly be returned by
14356 |get_t_next|; in increasing order, they are
14357 |if_test|, |fi_or_else|, |input|, |iteration|, |repeat_loop|,
14358 |exit_test|, |relax|, |scan_tokens|, |expand_after|, and |defined_macro|.
14360 \MP\ usually gets the next token of input by saying |get_x_next|. This is
14361 like |get_t_next| except that it keeps getting more tokens until
14362 finding |cur_cmd>=min_command|. In other words, |get_x_next| expands
14363 macros and removes conditionals or iterations or input instructions that
14366 It follows that |get_x_next| might invoke itself recursively. In fact,
14367 there is massive recursion, since macro expansion can involve the
14368 scanning of arbitrarily complex expressions, which in turn involve
14369 macro expansion and conditionals, etc.
14372 Therefore it's necessary to declare a whole bunch of |forward|
14373 procedures at this point, and to insert some other procedures
14374 that will be invoked by |get_x_next|.
14377 void mp_scan_primary (MP mp);
14378 void mp_scan_secondary (MP mp);
14379 void mp_scan_tertiary (MP mp);
14380 void mp_scan_expression (MP mp);
14381 void mp_scan_suffix (MP mp);
14382 @<Declare the procedure called |macro_call|@>;
14383 void mp_get_boolean (MP mp);
14384 void mp_pass_text (MP mp);
14385 void mp_conditional (MP mp);
14386 void mp_start_input (MP mp);
14387 void mp_begin_iteration (MP mp);
14388 void mp_resume_iteration (MP mp);
14389 void mp_stop_iteration (MP mp);
14391 @ An auxiliary subroutine called |expand| is used by |get_x_next|
14392 when it has to do exotic expansion commands.
14394 @c void mp_expand (MP mp) {
14395 pointer p; /* for list manipulation */
14396 size_t k; /* something that we hope is |<=buf_size| */
14397 pool_pointer j; /* index into |str_pool| */
14398 if ( mp->internal[tracing_commands]>unity )
14399 if ( mp->cur_cmd!=defined_macro )
14401 switch (mp->cur_cmd) {
14403 mp_conditional(mp); /* this procedure is discussed in Part 36 below */
14406 @<Terminate the current conditional and skip to \&{fi}@>;
14409 @<Initiate or terminate input from a file@>;
14412 if ( mp->cur_mod==end_for ) {
14413 @<Scold the user for having an extra \&{endfor}@>;
14415 mp_begin_iteration(mp); /* this procedure is discussed in Part 37 below */
14422 @<Exit a loop if the proper time has come@>;
14427 @<Expand the token after the next token@>;
14430 @<Put a string into the input buffer@>;
14432 case defined_macro:
14433 mp_macro_call(mp, mp->cur_mod,null,mp->cur_sym);
14435 }; /* there are no other cases */
14438 @ @<Scold the user...@>=
14440 print_err("Extra `endfor'");
14442 help2("I'm not currently working on a for loop,")
14443 ("so I had better not try to end anything.");
14447 @ The processing of \&{input} involves the |start_input| subroutine,
14448 which will be declared later; the processing of \&{endinput} is trivial.
14451 mp_primitive(mp, "input",input,0);
14452 @:input_}{\&{input} primitive@>
14453 mp_primitive(mp, "endinput",input,1);
14454 @:end_input_}{\&{endinput} primitive@>
14456 @ @<Cases of |print_cmd_mod|...@>=
14458 if ( m==0 ) mp_print(mp, "input");
14459 else mp_print(mp, "endinput");
14462 @ @<Initiate or terminate input...@>=
14463 if ( mp->cur_mod>0 ) mp->force_eof=true;
14464 else mp_start_input(mp)
14466 @ We'll discuss the complicated parts of loop operations later. For now
14467 it suffices to know that there's a global variable called |loop_ptr|
14468 that will be |null| if no loop is in progress.
14471 { while ( token_state &&(loc==null) )
14472 mp_end_token_list(mp); /* conserve stack space */
14473 if ( mp->loop_ptr==null ) {
14474 print_err("Lost loop");
14476 help2("I'm confused; after exiting from a loop, I still seem")
14477 ("to want to repeat it. I'll try to forget the problem.");
14480 mp_resume_iteration(mp); /* this procedure is in Part 37 below */
14484 @ @<Exit a loop if the proper time has come@>=
14485 { mp_get_boolean(mp);
14486 if ( mp->internal[tracing_commands]>unity )
14487 mp_show_cmd_mod(mp, nullary,mp->cur_exp);
14488 if ( mp->cur_exp==true_code ) {
14489 if ( mp->loop_ptr==null ) {
14490 print_err("No loop is in progress");
14491 @.No loop is in progress@>
14492 help1("Why say `exitif' when there's nothing to exit from?");
14493 if ( mp->cur_cmd==semicolon ) mp_error(mp); else mp_back_error(mp);
14495 @<Exit prematurely from an iteration@>;
14497 } else if ( mp->cur_cmd!=semicolon ) {
14498 mp_missing_err(mp, ";");
14500 help2("After `exitif <boolean exp>' I expect to see a semicolon.")
14501 ("I shall pretend that one was there."); mp_back_error(mp);
14505 @ Here we use the fact that |forever_text| is the only |token_type| that
14506 is less than |loop_text|.
14508 @<Exit prematurely...@>=
14511 if ( file_state ) {
14512 mp_end_file_reading(mp);
14514 if ( token_type<=loop_text ) p=start;
14515 mp_end_token_list(mp);
14518 if ( p!=info(mp->loop_ptr) ) mp_fatal_error(mp, "*** (loop confusion)");
14520 mp_stop_iteration(mp); /* this procedure is in Part 34 below */
14523 @ @<Expand the token after the next token@>=
14525 p=mp_cur_tok(mp); get_t_next;
14526 if ( mp->cur_cmd<min_command ) mp_expand(mp);
14527 else mp_back_input(mp);
14531 @ @<Put a string into the input buffer@>=
14532 { mp_get_x_next(mp); mp_scan_primary(mp);
14533 if ( mp->cur_type!=mp_string_type ) {
14534 mp_disp_err(mp, null,"Not a string");
14536 help2("I'm going to flush this expression, since")
14537 ("scantokens should be followed by a known string.");
14538 mp_put_get_flush_error(mp, 0);
14541 if ( length(mp->cur_exp)>0 )
14542 @<Pretend we're reading a new one-line file@>;
14546 @ @<Pretend we're reading a new one-line file@>=
14547 { mp_begin_file_reading(mp); name=is_scantok;
14548 k=mp->first+length(mp->cur_exp);
14549 if ( k>=mp->max_buf_stack ) {
14550 while ( k>=mp->buf_size ) {
14551 mp_reallocate_buffer(mp,(mp->buf_size+(mp->buf_size>>2)));
14553 mp->max_buf_stack=k+1;
14555 j=mp->str_start[mp->cur_exp]; limit=k;
14556 while ( mp->first<(size_t)limit ) {
14557 mp->buffer[mp->first]=mp->str_pool[j]; incr(j); incr(mp->first);
14559 mp->buffer[limit]='%'; mp->first=limit+1; loc=start;
14560 mp_flush_cur_exp(mp, 0);
14563 @ Here finally is |get_x_next|.
14565 The expression scanning routines to be considered later
14566 communicate via the global quantities |cur_type| and |cur_exp|;
14567 we must be very careful to save and restore these quantities while
14568 macros are being expanded.
14572 void mp_get_x_next (MP mp);
14574 @ @c void mp_get_x_next (MP mp) {
14575 pointer save_exp; /* a capsule to save |cur_type| and |cur_exp| */
14577 if ( mp->cur_cmd<min_command ) {
14578 save_exp=mp_stash_cur_exp(mp);
14580 if ( mp->cur_cmd==defined_macro )
14581 mp_macro_call(mp, mp->cur_mod,null,mp->cur_sym);
14585 } while (mp->cur_cmd<min_command);
14586 mp_unstash_cur_exp(mp, save_exp); /* that restores |cur_type| and |cur_exp| */
14590 @ Now let's consider the |macro_call| procedure, which is used to start up
14591 all user-defined macros. Since the arguments to a macro might be expressions,
14592 |macro_call| is recursive.
14595 The first parameter to |macro_call| points to the reference count of the
14596 token list that defines the macro. The second parameter contains any
14597 arguments that have already been parsed (see below). The third parameter
14598 points to the symbolic token that names the macro. If the third parameter
14599 is |null|, the macro was defined by \&{vardef}, so its name can be
14600 reconstructed from the prefix and ``at'' arguments found within the
14603 What is this second parameter? It's simply a linked list of one-word items,
14604 whose |info| fields point to the arguments. In other words, if |arg_list=null|,
14605 no arguments have been scanned yet; otherwise |info(arg_list)| points to
14606 the first scanned argument, and |link(arg_list)| points to the list of
14607 further arguments (if any).
14609 Arguments of type \&{expr} are so-called capsules, which we will
14610 discuss later when we concentrate on expressions; they can be
14611 recognized easily because their |link| field is |void|. Arguments of type
14612 \&{suffix} and \&{text} are token lists without reference counts.
14614 @ After argument scanning is complete, the arguments are moved to the
14615 |param_stack|. (They can't be put on that stack any sooner, because
14616 the stack is growing and shrinking in unpredictable ways as more arguments
14617 are being acquired.) Then the macro body is fed to the scanner; i.e.,
14618 the replacement text of the macro is placed at the top of the \MP's
14619 input stack, so that |get_t_next| will proceed to read it next.
14621 @<Declare the procedure called |macro_call|@>=
14622 @<Declare the procedure called |print_macro_name|@>;
14623 @<Declare the procedure called |print_arg|@>;
14624 @<Declare the procedure called |scan_text_arg|@>;
14625 void mp_macro_call (MP mp,pointer def_ref, pointer arg_list,
14626 pointer macro_name) ;
14629 void mp_macro_call (MP mp,pointer def_ref, pointer arg_list,
14630 pointer macro_name) {
14631 /* invokes a user-defined control sequence */
14632 pointer r; /* current node in the macro's token list */
14633 pointer p,q; /* for list manipulation */
14634 integer n; /* the number of arguments */
14635 pointer tail = 0; /* tail of the argument list */
14636 pointer l_delim=0,r_delim=0; /* a delimiter pair */
14637 r=link(def_ref); add_mac_ref(def_ref);
14638 if ( arg_list==null ) {
14641 @<Determine the number |n| of arguments already supplied,
14642 and set |tail| to the tail of |arg_list|@>;
14644 if ( mp->internal[tracing_macros]>0 ) {
14645 @<Show the text of the macro being expanded, and the existing arguments@>;
14647 @<Scan the remaining arguments, if any; set |r| to the first token
14648 of the replacement text@>;
14649 @<Feed the arguments and replacement text to the scanner@>;
14652 @ @<Show the text of the macro...@>=
14653 mp_begin_diagnostic(mp); mp_print_ln(mp);
14654 mp_print_macro_name(mp, arg_list,macro_name);
14655 if ( n==3 ) mp_print(mp, "@@#"); /* indicate a suffixed macro */
14656 mp_show_macro(mp, def_ref,null,100000);
14657 if ( arg_list!=null ) {
14661 mp_print_arg(mp, q,n,0);
14662 incr(n); p=link(p);
14665 mp_end_diagnostic(mp, false)
14668 @ @<Declare the procedure called |print_macro_name|@>=
14669 void mp_print_macro_name (MP mp,pointer a, pointer n);
14672 void mp_print_macro_name (MP mp,pointer a, pointer n) {
14673 pointer p,q; /* they traverse the first part of |a| */
14679 mp_print_text(info(info(link(a))));
14682 while ( link(q)!=null ) q=link(q);
14683 link(q)=info(link(a));
14684 mp_show_token_list(mp, p,null,1000,0);
14690 @ @<Declare the procedure called |print_arg|@>=
14691 void mp_print_arg (MP mp,pointer q, integer n, pointer b) ;
14694 void mp_print_arg (MP mp,pointer q, integer n, pointer b) {
14695 if ( link(q)==diov ) mp_print_nl(mp, "(EXPR");
14696 else if ( (b<text_base)&&(b!=text_macro) ) mp_print_nl(mp, "(SUFFIX");
14697 else mp_print_nl(mp, "(TEXT");
14698 mp_print_int(mp, n); mp_print(mp, ")<-");
14699 if ( link(q)==diov ) mp_print_exp(mp, q,1);
14700 else mp_show_token_list(mp, q,null,1000,0);
14703 @ @<Determine the number |n| of arguments already supplied...@>=
14705 n=1; tail=arg_list;
14706 while ( link(tail)!=null ) {
14707 incr(n); tail=link(tail);
14711 @ @<Scan the remaining arguments, if any; set |r|...@>=
14712 mp->cur_cmd=comma+1; /* anything |<>comma| will do */
14713 while ( info(r)>=expr_base ) {
14714 @<Scan the delimited argument represented by |info(r)|@>;
14717 if ( mp->cur_cmd==comma ) {
14718 print_err("Too many arguments to ");
14719 @.Too many arguments...@>
14720 mp_print_macro_name(mp, arg_list,macro_name); mp_print_char(mp, ';');
14721 mp_print_nl(mp, " Missing `"); mp_print_text(r_delim);
14723 mp_print(mp, "' has been inserted");
14724 help3("I'm going to assume that the comma I just read was a")
14725 ("right delimiter, and then I'll begin expanding the macro.")
14726 ("You might want to delete some tokens before continuing.");
14729 if ( info(r)!=general_macro ) {
14730 @<Scan undelimited argument(s)@>;
14734 @ At this point, the reader will find it advisable to review the explanation
14735 of token list format that was presented earlier, paying special attention to
14736 the conventions that apply only at the beginning of a macro's token list.
14738 On the other hand, the reader will have to take the expression-parsing
14739 aspects of the following program on faith; we will explain |cur_type|
14740 and |cur_exp| later. (Several things in this program depend on each other,
14741 and it's necessary to jump into the circle somewhere.)
14743 @<Scan the delimited argument represented by |info(r)|@>=
14744 if ( mp->cur_cmd!=comma ) {
14746 if ( mp->cur_cmd!=left_delimiter ) {
14747 print_err("Missing argument to ");
14748 @.Missing argument...@>
14749 mp_print_macro_name(mp, arg_list,macro_name);
14750 help3("That macro has more parameters than you thought.")
14751 ("I'll continue by pretending that each missing argument")
14752 ("is either zero or null.");
14753 if ( info(r)>=suffix_base ) {
14754 mp->cur_exp=null; mp->cur_type=mp_token_list;
14756 mp->cur_exp=0; mp->cur_type=mp_known;
14758 mp_back_error(mp); mp->cur_cmd=right_delimiter;
14761 l_delim=mp->cur_sym; r_delim=mp->cur_mod;
14763 @<Scan the argument represented by |info(r)|@>;
14764 if ( mp->cur_cmd!=comma )
14765 @<Check that the proper right delimiter was present@>;
14767 @<Append the current expression to |arg_list|@>
14769 @ @<Check that the proper right delim...@>=
14770 if ( (mp->cur_cmd!=right_delimiter)||(mp->cur_mod!=l_delim) ) {
14771 if ( info(link(r))>=expr_base ) {
14772 mp_missing_err(mp, ",");
14774 help3("I've finished reading a macro argument and am about to")
14775 ("read another; the arguments weren't delimited correctly.")
14776 ("You might want to delete some tokens before continuing.");
14777 mp_back_error(mp); mp->cur_cmd=comma;
14779 mp_missing_err(mp, str(text(r_delim)));
14781 help2("I've gotten to the end of the macro parameter list.")
14782 ("You might want to delete some tokens before continuing.");
14787 @ A \&{suffix} or \&{text} parameter will be have been scanned as
14788 a token list pointed to by |cur_exp|, in which case we will have
14789 |cur_type=token_list|.
14791 @<Append the current expression to |arg_list|@>=
14793 p=mp_get_avail(mp);
14794 if ( mp->cur_type==mp_token_list ) info(p)=mp->cur_exp;
14795 else info(p)=mp_stash_cur_exp(mp);
14796 if ( mp->internal[tracing_macros]>0 ) {
14797 mp_begin_diagnostic(mp); mp_print_arg(mp, info(p),n,info(r));
14798 mp_end_diagnostic(mp, false);
14800 if ( arg_list==null ) arg_list=p;
14805 @ @<Scan the argument represented by |info(r)|@>=
14806 if ( info(r)>=text_base ) {
14807 mp_scan_text_arg(mp, l_delim,r_delim);
14810 if ( info(r)>=suffix_base ) mp_scan_suffix(mp);
14811 else mp_scan_expression(mp);
14814 @ The parameters to |scan_text_arg| are either a pair of delimiters
14815 or zero; the latter case is for undelimited text arguments, which
14816 end with the first semicolon or \&{endgroup} or \&{end} that is not
14817 contained in a group.
14819 @<Declare the procedure called |scan_text_arg|@>=
14820 void mp_scan_text_arg (MP mp,pointer l_delim, pointer r_delim) ;
14823 void mp_scan_text_arg (MP mp,pointer l_delim, pointer r_delim) {
14824 integer balance; /* excess of |l_delim| over |r_delim| */
14825 pointer p; /* list tail */
14826 mp->warning_info=l_delim; mp->scanner_status=absorbing;
14827 p=hold_head; balance=1; link(hold_head)=null;
14830 if ( l_delim==0 ) {
14831 @<Adjust the balance for an undelimited argument; |break| if done@>;
14833 @<Adjust the balance for a delimited argument; |break| if done@>;
14835 link(p)=mp_cur_tok(mp); p=link(p);
14837 mp->cur_exp=link(hold_head); mp->cur_type=mp_token_list;
14838 mp->scanner_status=normal;
14841 @ @<Adjust the balance for a delimited argument...@>=
14842 if ( mp->cur_cmd==right_delimiter ) {
14843 if ( mp->cur_mod==l_delim ) {
14845 if ( balance==0 ) break;
14847 } else if ( mp->cur_cmd==left_delimiter ) {
14848 if ( mp->cur_mod==r_delim ) incr(balance);
14851 @ @<Adjust the balance for an undelimited...@>=
14852 if ( end_of_statement ) { /* |cur_cmd=semicolon|, |end_group|, or |stop| */
14853 if ( balance==1 ) { break; }
14854 else { if ( mp->cur_cmd==end_group ) decr(balance); }
14855 } else if ( mp->cur_cmd==begin_group ) {
14859 @ @<Scan undelimited argument(s)@>=
14861 if ( info(r)<text_macro ) {
14863 if ( info(r)!=suffix_macro ) {
14864 if ( (mp->cur_cmd==equals)||(mp->cur_cmd==assignment) ) mp_get_x_next(mp);
14868 case primary_macro:mp_scan_primary(mp); break;
14869 case secondary_macro:mp_scan_secondary(mp); break;
14870 case tertiary_macro:mp_scan_tertiary(mp); break;
14871 case expr_macro:mp_scan_expression(mp); break;
14873 @<Scan an expression followed by `\&{of} $\langle$primary$\rangle$'@>;
14876 @<Scan a suffix with optional delimiters@>;
14878 case text_macro:mp_scan_text_arg(mp, 0,0); break;
14879 } /* there are no other cases */
14881 @<Append the current expression to |arg_list|@>;
14884 @ @<Scan an expression followed by `\&{of} $\langle$primary$\rangle$'@>=
14886 mp_scan_expression(mp); p=mp_get_avail(mp); info(p)=mp_stash_cur_exp(mp);
14887 if ( mp->internal[tracing_macros]>0 ) {
14888 mp_begin_diagnostic(mp); mp_print_arg(mp, info(p),n,0);
14889 mp_end_diagnostic(mp, false);
14891 if ( arg_list==null ) arg_list=p; else link(tail)=p;
14893 if ( mp->cur_cmd!=of_token ) {
14894 mp_missing_err(mp, "of"); mp_print(mp, " for ");
14896 mp_print_macro_name(mp, arg_list,macro_name);
14897 help1("I've got the first argument; will look now for the other.");
14900 mp_get_x_next(mp); mp_scan_primary(mp);
14903 @ @<Scan a suffix with optional delimiters@>=
14905 if ( mp->cur_cmd!=left_delimiter ) {
14908 l_delim=mp->cur_sym; r_delim=mp->cur_mod; mp_get_x_next(mp);
14910 mp_scan_suffix(mp);
14911 if ( l_delim!=null ) {
14912 if ((mp->cur_cmd!=right_delimiter)||(mp->cur_mod!=l_delim) ) {
14913 mp_missing_err(mp, str(text(r_delim)));
14915 help2("I've gotten to the end of the macro parameter list.")
14916 ("You might want to delete some tokens before continuing.");
14923 @ Before we put a new token list on the input stack, it is wise to clean off
14924 all token lists that have recently been depleted. Then a user macro that ends
14925 with a call to itself will not require unbounded stack space.
14927 @<Feed the arguments and replacement text to the scanner@>=
14928 while ( token_state &&(loc==null) ) mp_end_token_list(mp); /* conserve stack space */
14929 if ( mp->param_ptr+n>mp->max_param_stack ) {
14930 mp->max_param_stack=mp->param_ptr+n;
14931 if ( mp->max_param_stack>mp->param_size )
14932 mp_overflow(mp, "parameter stack size",mp->param_size);
14933 @:MetaPost capacity exceeded parameter stack size}{\quad parameter stack size@>
14935 mp_begin_token_list(mp, def_ref,macro); name=macro_name; loc=r;
14939 mp->param_stack[mp->param_ptr]=info(p); incr(mp->param_ptr); p=link(p);
14941 mp_flush_list(mp, arg_list);
14944 @ It's sometimes necessary to put a single argument onto |param_stack|.
14945 The |stack_argument| subroutine does this.
14947 @c void mp_stack_argument (MP mp,pointer p) {
14948 if ( mp->param_ptr==mp->max_param_stack ) {
14949 incr(mp->max_param_stack);
14950 if ( mp->max_param_stack>mp->param_size )
14951 mp_overflow(mp, "parameter stack size",mp->param_size);
14952 @:MetaPost capacity exceeded parameter stack size}{\quad parameter stack size@>
14954 mp->param_stack[mp->param_ptr]=p; incr(mp->param_ptr);
14957 @* \[33] Conditional processing.
14958 Let's consider now the way \&{if} commands are handled.
14960 Conditions can be inside conditions, and this nesting has a stack
14961 that is independent of other stacks.
14962 Four global variables represent the top of the condition stack:
14963 |cond_ptr| points to pushed-down entries, if~any; |cur_if| tells whether
14964 we are processing \&{if} or \&{elseif}; |if_limit| specifies
14965 the largest code of a |fi_or_else| command that is syntactically legal;
14966 and |if_line| is the line number at which the current conditional began.
14968 If no conditions are currently in progress, the condition stack has the
14969 special state |cond_ptr=null|, |if_limit=normal|, |cur_if=0|, |if_line=0|.
14970 Otherwise |cond_ptr| points to a two-word node; the |type|, |name_type|, and
14971 |link| fields of the first word contain |if_limit|, |cur_if|, and
14972 |cond_ptr| at the next level, and the second word contains the
14973 corresponding |if_line|.
14975 @d if_node_size 2 /* number of words in stack entry for conditionals */
14976 @d if_line_field(A) mp->mem[(A)+1].cint
14977 @d if_code 1 /* code for \&{if} being evaluated */
14978 @d fi_code 2 /* code for \&{fi} */
14979 @d else_code 3 /* code for \&{else} */
14980 @d else_if_code 4 /* code for \&{elseif} */
14983 pointer cond_ptr; /* top of the condition stack */
14984 integer if_limit; /* upper bound on |fi_or_else| codes */
14985 small_number cur_if; /* type of conditional being worked on */
14986 integer if_line; /* line where that conditional began */
14989 mp->cond_ptr=null; mp->if_limit=normal; mp->cur_if=0; mp->if_line=0;
14992 mp_primitive(mp, "if",if_test,if_code);
14993 @:if_}{\&{if} primitive@>
14994 mp_primitive(mp, "fi",fi_or_else,fi_code); mp->eqtb[frozen_fi]=mp->eqtb[mp->cur_sym];
14995 @:fi_}{\&{fi} primitive@>
14996 mp_primitive(mp, "else",fi_or_else,else_code);
14997 @:else_}{\&{else} primitive@>
14998 mp_primitive(mp, "elseif",fi_or_else,else_if_code);
14999 @:else_if_}{\&{elseif} primitive@>
15001 @ @<Cases of |print_cmd_mod|...@>=
15005 case if_code:mp_print(mp, "if"); break;
15006 case fi_code:mp_print(mp, "fi"); break;
15007 case else_code:mp_print(mp, "else"); break;
15008 default: mp_print(mp, "elseif"); break;
15012 @ Here is a procedure that ignores text until coming to an \&{elseif},
15013 \&{else}, or \&{fi} at level zero of $\&{if}\ldots\&{fi}$
15014 nesting. After it has acted, |cur_mod| will indicate the token that
15017 \MP's smallest two command codes are |if_test| and |fi_or_else|; this
15018 makes the skipping process a bit simpler.
15021 void mp_pass_text (MP mp) {
15023 mp->scanner_status=skipping;
15024 mp->warning_info=mp_true_line(mp);
15027 if ( mp->cur_cmd<=fi_or_else ) {
15028 if ( mp->cur_cmd<fi_or_else ) {
15032 if ( mp->cur_mod==fi_code ) decr(l);
15035 @<Decrease the string reference count,
15036 if the current token is a string@>;
15039 mp->scanner_status=normal;
15042 @ @<Decrease the string reference count...@>=
15043 if ( mp->cur_cmd==string_token ) { delete_str_ref(mp->cur_mod); }
15045 @ When we begin to process a new \&{if}, we set |if_limit:=if_code|; then
15046 if \&{elseif} or \&{else} or \&{fi} occurs before the current \&{if}
15047 condition has been evaluated, a colon will be inserted.
15048 A construction like `\.{if fi}' would otherwise get \MP\ confused.
15050 @<Push the condition stack@>=
15051 { p=mp_get_node(mp, if_node_size); link(p)=mp->cond_ptr; type(p)=mp->if_limit;
15052 name_type(p)=mp->cur_if; if_line_field(p)=mp->if_line;
15053 mp->cond_ptr=p; mp->if_limit=if_code; mp->if_line=mp_true_line(mp);
15054 mp->cur_if=if_code;
15057 @ @<Pop the condition stack@>=
15058 { p=mp->cond_ptr; mp->if_line=if_line_field(p);
15059 mp->cur_if=name_type(p); mp->if_limit=type(p); mp->cond_ptr=link(p);
15060 mp_free_node(mp, p,if_node_size);
15063 @ Here's a procedure that changes the |if_limit| code corresponding to
15064 a given value of |cond_ptr|.
15066 @c void mp_change_if_limit (MP mp,small_number l, pointer p) {
15068 if ( p==mp->cond_ptr ) {
15069 mp->if_limit=l; /* that's the easy case */
15073 if ( q==null ) mp_confusion(mp, "if");
15074 @:this can't happen if}{\quad if@>
15075 if ( link(q)==p ) {
15083 @ The user is supposed to put colons into the proper parts of conditional
15084 statements. Therefore, \MP\ has to check for their presence.
15087 void mp_check_colon (MP mp) {
15088 if ( mp->cur_cmd!=colon ) {
15089 mp_missing_err(mp, ":");
15091 help2("There should've been a colon after the condition.")
15092 ("I shall pretend that one was there.");;
15097 @ A condition is started when the |get_x_next| procedure encounters
15098 an |if_test| command; in that case |get_x_next| calls |conditional|,
15099 which is a recursive procedure.
15102 @c void mp_conditional (MP mp) {
15103 pointer save_cond_ptr; /* |cond_ptr| corresponding to this conditional */
15104 int new_if_limit; /* future value of |if_limit| */
15105 pointer p; /* temporary register */
15106 @<Push the condition stack@>;
15107 save_cond_ptr=mp->cond_ptr;
15109 mp_get_boolean(mp); new_if_limit=else_if_code;
15110 if ( mp->internal[tracing_commands]>unity ) {
15111 @<Display the boolean value of |cur_exp|@>;
15114 mp_check_colon(mp);
15115 if ( mp->cur_exp==true_code ) {
15116 mp_change_if_limit(mp, new_if_limit,save_cond_ptr);
15117 return; /* wait for \&{elseif}, \&{else}, or \&{fi} */
15119 @<Skip to \&{elseif} or \&{else} or \&{fi}, then |goto done|@>;
15121 mp->cur_if=mp->cur_mod; mp->if_line=mp_true_line(mp);
15122 if ( mp->cur_mod==fi_code ) {
15123 @<Pop the condition stack@>
15124 } else if ( mp->cur_mod==else_if_code ) {
15127 mp->cur_exp=true_code; new_if_limit=fi_code; mp_get_x_next(mp);
15132 @ In a construction like `\&{if} \&{if} \&{true}: $0=1$: \\{foo}
15133 \&{else}: \\{bar} \&{fi}', the first \&{else}
15134 that we come to after learning that the \&{if} is false is not the
15135 \&{else} we're looking for. Hence the following curious logic is needed.
15137 @<Skip to \&{elseif}...@>=
15140 if ( mp->cond_ptr==save_cond_ptr ) goto DONE;
15141 else if ( mp->cur_mod==fi_code ) @<Pop the condition stack@>;
15145 @ @<Display the boolean value...@>=
15146 { mp_begin_diagnostic(mp);
15147 if ( mp->cur_exp==true_code ) mp_print(mp, "{true}");
15148 else mp_print(mp, "{false}");
15149 mp_end_diagnostic(mp, false);
15152 @ The processing of conditionals is complete except for the following
15153 code, which is actually part of |get_x_next|. It comes into play when
15154 \&{elseif}, \&{else}, or \&{fi} is scanned.
15156 @<Terminate the current conditional and skip to \&{fi}@>=
15157 if ( mp->cur_mod>mp->if_limit ) {
15158 if ( mp->if_limit==if_code ) { /* condition not yet evaluated */
15159 mp_missing_err(mp, ":");
15161 mp_back_input(mp); mp->cur_sym=frozen_colon; mp_ins_error(mp);
15163 print_err("Extra "); mp_print_cmd_mod(mp, fi_or_else,mp->cur_mod);
15167 help1("I'm ignoring this; it doesn't match any if.");
15171 while ( mp->cur_mod!=fi_code ) mp_pass_text(mp); /* skip to \&{fi} */
15172 @<Pop the condition stack@>;
15175 @* \[34] Iterations.
15176 To bring our treatment of |get_x_next| to a close, we need to consider what
15177 \MP\ does when it sees \&{for}, \&{forsuffixes}, and \&{forever}.
15179 There's a global variable |loop_ptr| that keeps track of the \&{for} loops
15180 that are currently active. If |loop_ptr=null|, no loops are in progress;
15181 otherwise |info(loop_ptr)| points to the iterative text of the current
15182 (innermost) loop, and |link(loop_ptr)| points to the data for any other
15183 loops that enclose the current one.
15185 A loop-control node also has two other fields, called |loop_type| and
15186 |loop_list|, whose contents depend on the type of loop:
15188 \yskip\indent|loop_type(loop_ptr)=null| means that |loop_list(loop_ptr)|
15189 points to a list of one-word nodes whose |info| fields point to the
15190 remaining argument values of a suffix list and expression list.
15192 \yskip\indent|loop_type(loop_ptr)=diov| means that the current loop is
15195 \yskip\indent|loop_type(loop_ptr)=progression_flag| means that
15196 |p=loop_list(loop_ptr)| points to a ``progression node'' and |value(p)|,
15197 |step_size(p)|, and |final_value(p)| contain the data for an arithmetic
15200 \yskip\indent|loop_type(loop_ptr)=p>diov| means that |p| points to an edge
15201 header and |loop_list(loop_ptr)| points into the graphical object list for
15204 \yskip\noindent In the case of a progression node, the first word is not used
15205 because the link field of words in the dynamic memory area cannot be arbitrary.
15207 @d loop_list_loc(A) ((A)+1) /* where the |loop_list| field resides */
15208 @d loop_type(A) info(loop_list_loc((A))) /* the type of \&{for} loop */
15209 @d loop_list(A) link(loop_list_loc((A))) /* the remaining list elements */
15210 @d loop_node_size 2 /* the number of words in a loop control node */
15211 @d progression_node_size 4 /* the number of words in a progression node */
15212 @d step_size(A) mp->mem[(A)+2].sc /* the step size in an arithmetic progression */
15213 @d final_value(A) mp->mem[(A)+3].sc /* the final value in an arithmetic progression */
15214 @d progression_flag (null+2)
15215 /* |loop_type| value when |loop_list| points to a progression node */
15218 pointer loop_ptr; /* top of the loop-control-node stack */
15223 @ If the expressions that define an arithmetic progression in
15224 a \&{for} loop don't have known numeric values, the |bad_for|
15225 subroutine screams at the user.
15227 @c void mp_bad_for (MP mp, char * s) {
15228 mp_disp_err(mp, null,"Improper "); /* show the bad expression above the message */
15229 @.Improper...replaced by 0@>
15230 mp_print(mp, s); mp_print(mp, " has been replaced by 0");
15231 help4("When you say `for x=a step b until c',")
15232 ("the initial value `a' and the step size `b'")
15233 ("and the final value `c' must have known numeric values.")
15234 ("I'm zeroing this one. Proceed, with fingers crossed.");
15235 mp_put_get_flush_error(mp, 0);
15238 @ Here's what \MP\ does when \&{for}, \&{forsuffixes}, or \&{forever}
15239 has just been scanned. (This code requires slight familiarity with
15240 expression-parsing routines that we have not yet discussed; but it seems
15241 to belong in the present part of the program, even though the original author
15242 didn't write it until later. The reader may wish to come back to it.)
15244 @c void mp_begin_iteration (MP mp) {
15245 halfword m; /* |expr_base| (\&{for}) or |suffix_base| (\&{forsuffixes}) */
15246 halfword n; /* hash address of the current symbol */
15247 pointer s; /* the new loop-control node */
15248 pointer p; /* substitution list for |scan_toks| */
15249 pointer q; /* link manipulation register */
15250 pointer pp; /* a new progression node */
15251 m=mp->cur_mod; n=mp->cur_sym; s=mp_get_node(mp, loop_node_size);
15252 if ( m==start_forever ){
15253 loop_type(s)=diov; p=null; mp_get_x_next(mp);
15255 mp_get_symbol(mp); p=mp_get_node(mp, token_node_size);
15256 info(p)=mp->cur_sym; value(p)=m;
15258 if ( mp->cur_cmd==within_token ) {
15259 @<Set up a picture iteration@>;
15261 @<Check for the |"="| or |":="| in a loop header@>;
15262 @<Scan the values to be used in the loop@>;
15265 @<Check for the presence of a colon@>;
15266 @<Scan the loop text and put it on the loop control stack@>;
15267 mp_resume_iteration(mp);
15270 @ @<Check for the |"="| or |":="| in a loop header@>=
15271 if ( (mp->cur_cmd!=equals)&&(mp->cur_cmd!=assignment) ) {
15272 mp_missing_err(mp, "=");
15274 help3("The next thing in this loop should have been `=' or `:='.")
15275 ("But don't worry; I'll pretend that an equals sign")
15276 ("was present, and I'll look for the values next.");
15280 @ @<Check for the presence of a colon@>=
15281 if ( mp->cur_cmd!=colon ) {
15282 mp_missing_err(mp, ":");
15284 help3("The next thing in this loop should have been a `:'.")
15285 ("So I'll pretend that a colon was present;")
15286 ("everything from here to `endfor' will be iterated.");
15290 @ We append a special |frozen_repeat_loop| token in place of the
15291 `\&{endfor}' at the end of the loop. This will come through \MP's scanner
15292 at the proper time to cause the loop to be repeated.
15294 (If the user tries some shenanigan like `\&{for} $\ldots$ \&{let} \&{endfor}',
15295 he will be foiled by the |get_symbol| routine, which keeps frozen
15296 tokens unchanged. Furthermore the |frozen_repeat_loop| is an \&{outer}
15297 token, so it won't be lost accidentally.)
15299 @ @<Scan the loop text...@>=
15300 q=mp_get_avail(mp); info(q)=frozen_repeat_loop;
15301 mp->scanner_status=loop_defining; mp->warning_info=n;
15302 info(s)=mp_scan_toks(mp, iteration,p,q,0); mp->scanner_status=normal;
15303 link(s)=mp->loop_ptr; mp->loop_ptr=s
15305 @ @<Initialize table...@>=
15306 eq_type(frozen_repeat_loop)=repeat_loop+outer_tag;
15307 text(frozen_repeat_loop)=intern(" ENDFOR");
15309 @ The loop text is inserted into \MP's scanning apparatus by the
15310 |resume_iteration| routine.
15312 @c void mp_resume_iteration (MP mp) {
15313 pointer p,q; /* link registers */
15314 p=loop_type(mp->loop_ptr);
15315 if ( p==progression_flag ) {
15316 p=loop_list(mp->loop_ptr); /* now |p| points to a progression node */
15317 mp->cur_exp=value(p);
15318 if ( @<The arithmetic progression has ended@> ) {
15319 mp_stop_iteration(mp);
15322 mp->cur_type=mp_known; q=mp_stash_cur_exp(mp); /* make |q| an \&{expr} argument */
15323 value(p)=mp->cur_exp+step_size(p); /* set |value(p)| for the next iteration */
15324 } else if ( p==null ) {
15325 p=loop_list(mp->loop_ptr);
15327 mp_stop_iteration(mp);
15330 loop_list(mp->loop_ptr)=link(p); q=info(p); free_avail(p);
15331 } else if ( p==diov ) {
15332 mp_begin_token_list(mp, info(mp->loop_ptr),forever_text); return;
15334 @<Make |q| a capsule containing the next picture component from
15335 |loop_list(loop_ptr)| or |goto not_found|@>;
15337 mp_begin_token_list(mp, info(mp->loop_ptr),loop_text);
15338 mp_stack_argument(mp, q);
15339 if ( mp->internal[tracing_commands]>unity ) {
15340 @<Trace the start of a loop@>;
15344 mp_stop_iteration(mp);
15347 @ @<The arithmetic progression has ended@>=
15348 ((step_size(p)>0)&&(mp->cur_exp>final_value(p)))||
15349 ((step_size(p)<0)&&(mp->cur_exp<final_value(p)))
15351 @ @<Trace the start of a loop@>=
15353 mp_begin_diagnostic(mp); mp_print_nl(mp, "{loop value=");
15355 if ( (q!=null)&&(link(q)==diov) ) mp_print_exp(mp, q,1);
15356 else mp_show_token_list(mp, q,null,50,0);
15357 mp_print_char(mp, '}'); mp_end_diagnostic(mp, false);
15360 @ @<Make |q| a capsule containing the next picture component from...@>=
15361 { q=loop_list(mp->loop_ptr);
15362 if ( q==null ) goto NOT_FOUND;
15363 skip_component(q) goto NOT_FOUND;
15364 mp->cur_exp=mp_copy_objects(mp, loop_list(mp->loop_ptr),q);
15365 mp_init_bbox(mp, mp->cur_exp);
15366 mp->cur_type=mp_picture_type;
15367 loop_list(mp->loop_ptr)=q;
15368 q=mp_stash_cur_exp(mp);
15371 @ A level of loop control disappears when |resume_iteration| has decided
15372 not to resume, or when an \&{exitif} construction has removed the loop text
15373 from the input stack.
15375 @c void mp_stop_iteration (MP mp) {
15376 pointer p,q; /* the usual */
15377 p=loop_type(mp->loop_ptr);
15378 if ( p==progression_flag ) {
15379 mp_free_node(mp, loop_list(mp->loop_ptr),progression_node_size);
15380 } else if ( p==null ){
15381 q=loop_list(mp->loop_ptr);
15382 while ( q!=null ) {
15385 if ( link(p)==diov ) { /* it's an \&{expr} parameter */
15386 mp_recycle_value(mp, p); mp_free_node(mp, p,value_node_size);
15388 mp_flush_token_list(mp, p); /* it's a \&{suffix} or \&{text} parameter */
15391 p=q; q=link(q); free_avail(p);
15393 } else if ( p>progression_flag ) {
15394 delete_edge_ref(p);
15396 p=mp->loop_ptr; mp->loop_ptr=link(p); mp_flush_token_list(mp, info(p));
15397 mp_free_node(mp, p,loop_node_size);
15400 @ Now that we know all about loop control, we can finish up
15401 the missing portion of |begin_iteration| and we'll be done.
15403 The following code is performed after the `\.=' has been scanned in
15404 a \&{for} construction (if |m=expr_base|) or a \&{forsuffixes} construction
15405 (if |m=suffix_base|).
15407 @<Scan the values to be used in the loop@>=
15408 loop_type(s)=null; q=loop_list_loc(s); link(q)=null; /* |link(q)=loop_list(s)| */
15411 if ( m!=expr_base ) {
15412 mp_scan_suffix(mp);
15414 if ( mp->cur_cmd>=colon ) if ( mp->cur_cmd<=comma )
15416 mp_scan_expression(mp);
15417 if ( mp->cur_cmd==step_token ) if ( q==loop_list_loc(s) ) {
15418 @<Prepare for step-until construction and |break|@>;
15420 mp->cur_exp=mp_stash_cur_exp(mp);
15422 link(q)=mp_get_avail(mp); q=link(q);
15423 info(q)=mp->cur_exp; mp->cur_type=mp_vacuous;
15426 } while (mp->cur_cmd==comma)
15428 @ @<Prepare for step-until construction and |break|@>=
15430 if ( mp->cur_type!=mp_known ) mp_bad_for(mp, "initial value");
15431 pp=mp_get_node(mp, progression_node_size); value(pp)=mp->cur_exp;
15432 mp_get_x_next(mp); mp_scan_expression(mp);
15433 if ( mp->cur_type!=mp_known ) mp_bad_for(mp, "step size");
15434 step_size(pp)=mp->cur_exp;
15435 if ( mp->cur_cmd!=until_token ) {
15436 mp_missing_err(mp, "until");
15437 @.Missing `until'@>
15438 help2("I assume you meant to say `until' after `step'.")
15439 ("So I'll look for the final value and colon next.");
15442 mp_get_x_next(mp); mp_scan_expression(mp);
15443 if ( mp->cur_type!=mp_known ) mp_bad_for(mp, "final value");
15444 final_value(pp)=mp->cur_exp; loop_list(s)=pp;
15445 loop_type(s)=progression_flag;
15449 @ The last case is when we have just seen ``\&{within}'', and we need to
15450 parse a picture expression and prepare to iterate over it.
15452 @<Set up a picture iteration@>=
15453 { mp_get_x_next(mp);
15454 mp_scan_expression(mp);
15455 @<Make sure the current expression is a known picture@>;
15456 loop_type(s)=mp->cur_exp; mp->cur_type=mp_vacuous;
15457 q=link(dummy_loc(mp->cur_exp));
15459 if ( is_start_or_stop(q) )
15460 if ( mp_skip_1component(mp, q)==null ) q=link(q);
15464 @ @<Make sure the current expression is a known picture@>=
15465 if ( mp->cur_type!=mp_picture_type ) {
15466 mp_disp_err(mp, null,"Improper iteration spec has been replaced by nullpicture");
15467 help1("When you say `for x in p', p must be a known picture.");
15468 mp_put_get_flush_error(mp, mp_get_node(mp, edge_header_size));
15469 mp_init_edges(mp, mp->cur_exp); mp->cur_type=mp_picture_type;
15472 @* \[35] File names.
15473 It's time now to fret about file names. Besides the fact that different
15474 operating systems treat files in different ways, we must cope with the
15475 fact that completely different naming conventions are used by different
15476 groups of people. The following programs show what is required for one
15477 particular operating system; similar routines for other systems are not
15478 difficult to devise.
15479 @^system dependencies@>
15481 \MP\ assumes that a file name has three parts: the name proper; its
15482 ``extension''; and a ``file area'' where it is found in an external file
15483 system. The extension of an input file is assumed to be
15484 `\.{.mp}' unless otherwise specified; it is `\.{.log}' on the
15485 transcript file that records each run of \MP; it is `\.{.tfm}' on the font
15486 metric files that describe characters in any fonts created by \MP; it is
15487 `\.{.ps}' or `.{\it nnn}' for some number {\it nnn} on the \ps\ output files;
15488 and it is `\.{.mem}' on the mem files written by \.{INIMP} to initialize \MP.
15489 The file area can be arbitrary on input files, but files are usually
15490 output to the user's current area. If an input file cannot be
15491 found on the specified area, \MP\ will look for it on a special system
15492 area; this special area is intended for commonly used input files.
15494 Simple uses of \MP\ refer only to file names that have no explicit
15495 extension or area. For example, a person usually says `\.{input} \.{cmr10}'
15496 instead of `\.{input} \.{cmr10.new}'. Simple file
15497 names are best, because they make the \MP\ source files portable;
15498 whenever a file name consists entirely of letters and digits, it should be
15499 treated in the same way by all implementations of \MP. However, users
15500 need the ability to refer to other files in their environment, especially
15501 when responding to error messages concerning unopenable files; therefore
15502 we want to let them use the syntax that appears in their favorite
15505 @ \MP\ uses the same conventions that have proved to be satisfactory for
15506 \TeX\ and \MF. In order to isolate the system-dependent aspects of file names,
15507 @^system dependencies@>
15508 the system-independent parts of \MP\ are expressed in terms
15509 of three system-dependent
15510 procedures called |begin_name|, |more_name|, and |end_name|. In
15511 essence, if the user-specified characters of the file name are $c_1\ldots c_n$,
15512 the system-independent driver program does the operations
15513 $$|begin_name|;\,|more_name|(c_1);\,\ldots\,;|more_name|(c_n);
15515 These three procedures communicate with each other via global variables.
15516 Afterwards the file name will appear in the string pool as three strings
15517 called |cur_name|\penalty10000\hskip-.05em,
15518 |cur_area|, and |cur_ext|; the latter two are null (i.e.,
15519 |""|), unless they were explicitly specified by the user.
15521 Actually the situation is slightly more complicated, because \MP\ needs
15522 to know when the file name ends. The |more_name| routine is a function
15523 (with side effects) that returns |true| on the calls |more_name|$(c_1)$,
15524 \dots, |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$
15525 returns |false|; or, it returns |true| and $c_n$ is the last character
15526 on the current input line. In other words,
15527 |more_name| is supposed to return |true| unless it is sure that the
15528 file name has been completely scanned; and |end_name| is supposed to be able
15529 to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of
15530 whether $|more_name|(c_n)$ returned |true| or |false|.
15533 char * cur_name; /* name of file just scanned */
15534 char * cur_area; /* file area just scanned, or \.{""} */
15535 char * cur_ext; /* file extension just scanned, or \.{""} */
15537 @ It is easier to maintain reference counts if we assign initial values.
15540 mp->cur_name=xstrdup("");
15541 mp->cur_area=xstrdup("");
15542 mp->cur_ext=xstrdup("");
15544 @ @<Dealloc variables@>=
15545 xfree(mp->cur_area);
15546 xfree(mp->cur_name);
15547 xfree(mp->cur_ext);
15549 @ The file names we shall deal with for illustrative purposes have the
15550 following structure: If the name contains `\.>' or `\.:', the file area
15551 consists of all characters up to and including the final such character;
15552 otherwise the file area is null. If the remaining file name contains
15553 `\..', the file extension consists of all such characters from the first
15554 remaining `\..' to the end, otherwise the file extension is null.
15555 @^system dependencies@>
15557 We can scan such file names easily by using two global variables that keep track
15558 of the occurrences of area and extension delimiters. Note that these variables
15559 cannot be of type |pool_pointer| because a string pool compaction could occur
15560 while scanning a file name.
15563 integer area_delimiter;
15564 /* most recent `\.>' or `\.:' relative to |str_start[str_ptr]| */
15565 integer ext_delimiter; /* the relevant `\..', if any */
15567 @ Input files that can't be found in the user's area may appear in standard
15568 system areas called |MP_area| and |MF_area|. (The latter is used when the file
15569 extension is |".mf"|.) The standard system area for font metric files
15570 to be read is |MP_font_area|.
15571 This system area name will, of course, vary from place to place.
15572 @^system dependencies@>
15574 @d MP_area "MPinputs:"
15576 @d MF_area "MFinputs:"
15581 @ Here now is the first of the system-dependent routines for file name scanning.
15582 @^system dependencies@>
15584 @<Declare subroutines for parsing file names@>=
15585 void mp_begin_name (MP mp) {
15586 xfree(mp->cur_name);
15587 xfree(mp->cur_area);
15588 xfree(mp->cur_ext);
15589 mp->area_delimiter=-1;
15590 mp->ext_delimiter=-1;
15593 @ And here's the second.
15594 @^system dependencies@>
15596 @<Declare subroutines for parsing file names@>=
15597 boolean mp_more_name (MP mp, ASCII_code c) {
15601 if ( (c=='>')||(c==':') ) {
15602 mp->area_delimiter=mp->pool_ptr;
15603 mp->ext_delimiter=-1;
15604 } else if ( (c=='.')&&(mp->ext_delimiter<0) ) {
15605 mp->ext_delimiter=mp->pool_ptr;
15607 str_room(1); append_char(c); /* contribute |c| to the current string */
15613 @^system dependencies@>
15615 @d copy_pool_segment(A,B,C) {
15616 A = xmalloc(C+1,sizeof(char));
15617 strncpy(A,(char *)(mp->str_pool+B),C);
15620 @<Declare subroutines for parsing file names@>=
15621 void mp_end_name (MP mp) {
15622 pool_pointer s; /* length of area, name, and extension */
15625 s = mp->str_start[mp->str_ptr];
15626 if ( mp->area_delimiter<0 ) {
15627 mp->cur_area=xstrdup("");
15629 len = mp->area_delimiter-s;
15630 copy_pool_segment(mp->cur_area,s,len);
15633 if ( mp->ext_delimiter<0 ) {
15634 mp->cur_ext=xstrdup("");
15635 len = mp->pool_ptr-s;
15637 copy_pool_segment(mp->cur_ext,mp->ext_delimiter,(mp->pool_ptr-mp->ext_delimiter));
15638 len = mp->ext_delimiter-s;
15640 copy_pool_segment(mp->cur_name,s,len);
15641 mp->pool_ptr=s; /* don't need this partial string */
15644 @ Conversely, here is a routine that takes three strings and prints a file
15645 name that might have produced them. (The routine is system dependent, because
15646 some operating systems put the file area last instead of first.)
15647 @^system dependencies@>
15649 @<Basic printing...@>=
15650 void mp_print_file_name (MP mp, char * n, char * a, char * e) {
15651 mp_print(mp, a); mp_print(mp, n); mp_print(mp, e);
15654 @ Another system-dependent routine is needed to convert three internal
15656 to the |name_of_file| value that is used to open files. The present code
15657 allows both lowercase and uppercase letters in the file name.
15658 @^system dependencies@>
15660 @d append_to_name(A) { c=(A);
15661 if ( k<file_name_size ) {
15662 mp->name_of_file[k]=xchr(c);
15667 @<Declare subroutines for parsing file names@>=
15668 void mp_pack_file_name (MP mp, char *n, char *a, char *e) {
15669 integer k; /* number of positions filled in |name_of_file| */
15670 ASCII_code c; /* character being packed */
15671 char *j; /* a character index */
15675 for (j=a;*j;j++) { append_to_name(*j); }
15677 for (j=n;*j;j++) { append_to_name(*j); }
15679 for (j=e;*j;j++) { append_to_name(*j); }
15681 mp->name_of_file[k]=0;
15686 void mp_pack_file_name (MP mp, char *n, char *a, char *e) ;
15688 @ A messier routine is also needed, since mem file names must be scanned
15689 before \MP's string mechanism has been initialized. We shall use the
15690 global variable |MP_mem_default| to supply the text for default system areas
15691 and extensions related to mem files.
15692 @^system dependencies@>
15694 @d mem_default_length 9 /* length of the |MP_mem_default| string */
15695 @d mem_ext_length 4 /* length of its `\.{.mem}' part */
15696 @d mem_extension ".mem" /* the extension, as a \.{WEB} constant */
15699 char *MP_mem_default;
15700 char *mem_name; /* for commandline */
15702 @ @<Option variables@>=
15703 char *mem_name; /* for commandline */
15705 @ @<Allocate or initialize ...@>=
15706 mp->MP_mem_default = xstrdup("plain.mem");
15707 mp->mem_name = mp_xstrdup(opt->mem_name);
15709 @^system dependencies@>
15711 @ @<Dealloc variables@>=
15712 xfree(mp->MP_mem_default);
15713 xfree(mp->mem_name);
15715 @ @<Check the ``constant'' values for consistency@>=
15716 if ( mem_default_length>file_name_size ) mp->bad=20;
15718 @ Here is the messy routine that was just mentioned. It sets |name_of_file|
15719 from the first |n| characters of |MP_mem_default|, followed by
15720 |buffer[a..b]|, followed by the last |mem_ext_length| characters of
15723 We dare not give error messages here, since \MP\ calls this routine before
15724 the |error| routine is ready to roll. Instead, we simply drop excess characters,
15725 since the error will be detected in another way when a strange file name
15727 @^system dependencies@>
15729 @c void mp_pack_buffered_name (MP mp,small_number n, integer a,
15731 integer k; /* number of positions filled in |name_of_file| */
15732 ASCII_code c; /* character being packed */
15733 integer j; /* index into |buffer| or |MP_mem_default| */
15734 if ( n+b-a+1+mem_ext_length>file_name_size )
15735 b=a+file_name_size-n-1-mem_ext_length;
15737 for (j=0;j<n;j++) {
15738 append_to_name(xord((int)mp->MP_mem_default[j]));
15740 for (j=a;j<=b;j++) {
15741 append_to_name(mp->buffer[j]);
15743 for (j=mem_default_length-mem_ext_length;
15744 j<mem_default_length;j++) {
15745 append_to_name(xord((int)mp->MP_mem_default[j]));
15747 mp->name_of_file[k]=0;
15751 @ Here is the only place we use |pack_buffered_name|. This part of the program
15752 becomes active when a ``virgin'' \MP\ is trying to get going, just after
15753 the preliminary initialization, or when the user is substituting another
15754 mem file by typing `\.\&' after the initial `\.{**}' prompt. The buffer
15755 contains the first line of input in |buffer[loc..(last-1)]|, where
15756 |loc<last| and |buffer[loc]<>" "|.
15759 boolean mp_open_mem_file (MP mp) ;
15762 boolean mp_open_mem_file (MP mp) {
15763 int j; /* the first space after the file name */
15764 if (mp->mem_name!=NULL) {
15765 mp->mem_file = mp_open_file(mp, mp->mem_name, "rb", mp_filetype_memfile);
15766 if ( mp->mem_file ) return true;
15769 if ( mp->buffer[loc]=='&' ) {
15770 incr(loc); j=loc; mp->buffer[mp->last]=' ';
15771 while ( mp->buffer[j]!=' ' ) incr(j);
15772 mp_pack_buffered_name(mp, 0,loc,j-1); /* try first without the system file area */
15773 if ( mp_w_open_in(mp, &mp->mem_file) ) goto FOUND;
15775 wterm_ln("Sorry, I can\'t find that mem file; will try PLAIN.");
15776 @.Sorry, I can't find...@>
15779 /* now pull out all the stops: try for the system \.{plain} file */
15780 mp_pack_buffered_name(mp, mem_default_length-mem_ext_length,0,0);
15781 if ( ! mp_w_open_in(mp, &mp->mem_file) ) {
15783 wterm_ln("I can\'t find the PLAIN mem file!\n");
15784 @.I can't find PLAIN...@>
15789 loc=j; return true;
15792 @ Operating systems often make it possible to determine the exact name (and
15793 possible version number) of a file that has been opened. The following routine,
15794 which simply makes a \MP\ string from the value of |name_of_file|, should
15795 ideally be changed to deduce the full name of file~|f|, which is the file
15796 most recently opened, if it is possible to do this in a \PASCAL\ program.
15797 @^system dependencies@>
15800 #define mp_a_make_name_string(A,B) mp_make_name_string(A)
15801 #define mp_b_make_name_string(A,B) mp_make_name_string(A)
15802 #define mp_w_make_name_string(A,B) mp_make_name_string(A)
15805 str_number mp_make_name_string (MP mp) {
15806 int k; /* index into |name_of_file| */
15807 str_room(mp->name_length);
15808 for (k=0;k<mp->name_length;k++) {
15809 append_char(xord((int)mp->name_of_file[k]));
15811 return mp_make_string(mp);
15814 @ Now let's consider the ``driver''
15815 routines by which \MP\ deals with file names
15816 in a system-independent manner. First comes a procedure that looks for a
15817 file name in the input by taking the information from the input buffer.
15818 (We can't use |get_next|, because the conversion to tokens would
15819 destroy necessary information.)
15821 This procedure doesn't allow semicolons or percent signs to be part of
15822 file names, because of other conventions of \MP.
15823 {\sl The {\logos METAFONT\/}book} doesn't
15824 use semicolons or percents immediately after file names, but some users
15825 no doubt will find it natural to do so; therefore system-dependent
15826 changes to allow such characters in file names should probably
15827 be made with reluctance, and only when an entire file name that
15828 includes special characters is ``quoted'' somehow.
15829 @^system dependencies@>
15831 @c void mp_scan_file_name (MP mp) {
15833 while ( mp->buffer[loc]==' ' ) incr(loc);
15835 if ( (mp->buffer[loc]==';')||(mp->buffer[loc]=='%') ) break;
15836 if ( ! mp_more_name(mp, mp->buffer[loc]) ) break;
15842 @ Here is another version that takes its input from a string.
15844 @<Declare subroutines for parsing file names@>=
15845 void mp_str_scan_file (MP mp, str_number s) {
15846 pool_pointer p,q; /* current position and stopping point */
15848 p=mp->str_start[s]; q=str_stop(s);
15850 if ( ! mp_more_name(mp, mp->str_pool[p]) ) break;
15856 @ And one that reads from a |char*|.
15858 @<Declare subroutines for parsing file names@>=
15859 void mp_ptr_scan_file (MP mp, char *s) {
15860 char *p, *q; /* current position and stopping point */
15862 p=s; q=p+strlen(s);
15864 if ( ! mp_more_name(mp, *p)) break;
15871 @ The global variable |job_name| contains the file name that was first
15872 \&{input} by the user. This name is extended by `\.{.log}' and `\.{ps}' and
15873 `\.{.mem}' and `\.{.tfm}' in order to make the names of \MP's output files.
15876 char *job_name; /* principal file name */
15877 boolean log_opened; /* has the transcript file been opened? */
15878 char *log_name; /* full name of the log file */
15880 @ @<Option variables@>=
15881 char *job_name; /* principal file name */
15883 @ Initially |job_name=NULL|; it becomes nonzero as soon as the true name is known.
15884 We have |job_name=NULL| if and only if the `\.{log}' file has not been opened,
15885 except of course for a short time just after |job_name| has become nonzero.
15887 @<Allocate or ...@>=
15888 mp->job_name=opt->job_name;
15889 mp->log_opened=false;
15891 @ @<Dealloc variables@>=
15892 xfree(mp->job_name);
15894 @ Here is a routine that manufactures the output file names, assuming that
15895 |job_name<>0|. It ignores and changes the current settings of |cur_area|
15898 @d pack_cur_name mp_pack_file_name(mp, mp->cur_name,mp->cur_area,mp->cur_ext)
15901 void mp_pack_job_name (MP mp, char *s) ;
15903 @ @c void mp_pack_job_name (MP mp, char *s) { /* |s = ".log"|, |".mem"|, |".ps"|, or .\\{nnn} */
15904 xfree(mp->cur_name); mp->cur_name=xstrdup(mp->job_name);
15905 xfree(mp->cur_area); mp->cur_area=xstrdup("");
15906 xfree(mp->cur_ext); mp->cur_ext=xstrdup(s);
15910 @ If some trouble arises when \MP\ tries to open a file, the following
15911 routine calls upon the user to supply another file name. Parameter~|s|
15912 is used in the error message to identify the type of file; parameter~|e|
15913 is the default extension if none is given. Upon exit from the routine,
15914 variables |cur_name|, |cur_area|, |cur_ext|, and |name_of_file| are
15915 ready for another attempt at file opening.
15918 void mp_prompt_file_name (MP mp,char * s, char * e) ;
15920 @ @c void mp_prompt_file_name (MP mp,char * s, char * e) {
15921 size_t k; /* index into |buffer| */
15922 char * saved_cur_name;
15923 if ( mp->interaction==mp_scroll_mode )
15925 if (strcmp(s,"input file name")==0) {
15926 print_err("I can\'t find file `");
15927 @.I can't find file x@>
15929 print_err("I can\'t write on file `");
15931 @.I can't write on file x@>
15932 mp_print_file_name(mp, mp->cur_name,mp->cur_area,mp->cur_ext);
15933 mp_print(mp, "'.");
15934 if (strcmp(e,"")==0)
15935 mp_show_context(mp);
15936 mp_print_nl(mp, "Please type another "); mp_print(mp, s);
15938 if ( mp->interaction<mp_scroll_mode )
15939 mp_fatal_error(mp, "*** (job aborted, file error in nonstop mode)");
15940 @.job aborted, file error...@>
15941 saved_cur_name = xstrdup(mp->cur_name);
15942 clear_terminal; prompt_input(": "); @<Scan file name in the buffer@>;
15943 if (strcmp(mp->cur_ext,"")==0)
15945 if (strlen(mp->cur_name)==0) {
15946 mp->cur_name=saved_cur_name;
15948 xfree(saved_cur_name);
15953 @ @<Scan file name in the buffer@>=
15955 mp_begin_name(mp); k=mp->first;
15956 while ( (mp->buffer[k]==' ')&&(k<mp->last) ) incr(k);
15958 if ( k==mp->last ) break;
15959 if ( ! mp_more_name(mp, mp->buffer[k]) ) break;
15965 @ The |open_log_file| routine is used to open the transcript file and to help
15966 it catch up to what has previously been printed on the terminal.
15968 @c void mp_open_log_file (MP mp) {
15969 int old_setting; /* previous |selector| setting */
15970 int k; /* index into |months| and |buffer| */
15971 int l; /* end of first input line */
15972 integer m; /* the current month */
15973 char *months="JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
15974 /* abbreviations of month names */
15975 old_setting=mp->selector;
15976 if ( mp->job_name==NULL ) {
15977 mp->job_name=xstrdup("mpout");
15979 mp_pack_job_name(mp,".log");
15980 while ( ! mp_a_open_out(mp, &mp->log_file, mp_filetype_log) ) {
15981 @<Try to get a different log file name@>;
15983 mp->log_name=xstrdup(mp->name_of_file);
15984 mp->selector=log_only; mp->log_opened=true;
15985 @<Print the banner line, including the date and time@>;
15986 mp->input_stack[mp->input_ptr]=mp->cur_input;
15987 /* make sure bottom level is in memory */
15988 mp_print_nl(mp, "**");
15990 l=mp->input_stack[0].limit_field-1; /* last position of first line */
15991 for (k=0;k<=l;k++) mp_print_str(mp, mp->buffer[k]);
15992 mp_print_ln(mp); /* now the transcript file contains the first line of input */
15993 mp->selector=old_setting+2; /* |log_only| or |term_and_log| */
15996 @ @<Dealloc variables@>=
15997 xfree(mp->log_name);
15999 @ Sometimes |open_log_file| is called at awkward moments when \MP\ is
16000 unable to print error messages or even to |show_context|.
16001 The |prompt_file_name| routine can result in a |fatal_error|, but the |error|
16002 routine will not be invoked because |log_opened| will be false.
16004 The normal idea of |mp_batch_mode| is that nothing at all should be written
16005 on the terminal. However, in the unusual case that
16006 no log file could be opened, we make an exception and allow
16007 an explanatory message to be seen.
16009 Incidentally, the program always refers to the log file as a `\.{transcript
16010 file}', because some systems cannot use the extension `\.{.log}' for
16013 @<Try to get a different log file name@>=
16015 mp->selector=term_only;
16016 mp_prompt_file_name(mp, "transcript file name",".log");
16019 @ @<Print the banner...@>=
16022 mp_print(mp, mp->mem_ident); mp_print(mp, " ");
16023 mp_print_int(mp, mp_round_unscaled(mp, mp->internal[day]));
16024 mp_print_char(mp, ' ');
16025 m=mp_round_unscaled(mp, mp->internal[month]);
16026 for (k=3*m-3;k<3*m;k++) { wlog_chr(months[k]); }
16027 mp_print_char(mp, ' ');
16028 mp_print_int(mp, mp_round_unscaled(mp, mp->internal[year]));
16029 mp_print_char(mp, ' ');
16030 m=mp_round_unscaled(mp, mp->internal[mp_time]);
16031 mp_print_dd(mp, m / 60); mp_print_char(mp, ':'); mp_print_dd(mp, m % 60);
16034 @ The |try_extension| function tries to open an input file determined by
16035 |cur_name|, |cur_area|, and the argument |ext|. It returns |false| if it
16036 can't find the file in |cur_area| or the appropriate system area.
16038 @c boolean mp_try_extension (MP mp,char *ext) {
16039 mp_pack_file_name(mp, mp->cur_name,mp->cur_area, ext);
16040 in_name=xstrdup(mp->cur_name);
16041 in_area=xstrdup(mp->cur_area);
16042 if ( mp_a_open_in(mp, &cur_file, mp_filetype_program) ) {
16045 if (strcmp(ext,".mf")==0 ) in_area=xstrdup(MF_area);
16046 else in_area=xstrdup(MP_area);
16047 mp_pack_file_name(mp, mp->cur_name,in_area,ext);
16048 return mp_a_open_in(mp, &cur_file, mp_filetype_program);
16053 @ Let's turn now to the procedure that is used to initiate file reading
16054 when an `\.{input}' command is being processed.
16056 @c void mp_start_input (MP mp) { /* \MP\ will \.{input} something */
16057 char *fname = NULL;
16058 @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>;
16060 mp_begin_file_reading(mp); /* set up |cur_file| and new level of input */
16061 if ( strlen(mp->cur_ext)==0 ) {
16062 if ( mp_try_extension(mp, ".mp") ) break;
16063 else if ( mp_try_extension(mp, "") ) break;
16064 else if ( mp_try_extension(mp, ".mf") ) break;
16065 /* |else do_nothing; | */
16066 } else if ( mp_try_extension(mp, mp->cur_ext) ) {
16069 mp_end_file_reading(mp); /* remove the level that didn't work */
16070 mp_prompt_file_name(mp, "input file name","");
16072 name=mp_a_make_name_string(mp, cur_file);
16073 fname = xstrdup(mp->name_of_file);
16074 if ( mp->job_name==NULL ) {
16075 mp->job_name=xstrdup(mp->cur_name);
16076 mp_open_log_file(mp);
16077 } /* |open_log_file| doesn't |show_context|, so |limit|
16078 and |loc| needn't be set to meaningful values yet */
16079 if ( ((int)mp->term_offset+(int)strlen(fname)) > (mp->max_print_line-2)) mp_print_ln(mp);
16080 else if ( (mp->term_offset>0)||(mp->file_offset>0) ) mp_print_char(mp, ' ');
16081 mp_print_char(mp, '('); incr(mp->open_parens); mp_print(mp, fname);
16084 @<Flush |name| and replace it with |cur_name| if it won't be needed@>;
16085 @<Read the first line of the new file@>;
16088 @ This code should be omitted if |a_make_name_string| returns something other
16089 than just a copy of its argument and the full file name is needed for opening
16090 \.{MPX} files or implementing the switch-to-editor option.
16091 @^system dependencies@>
16093 @<Flush |name| and replace it with |cur_name| if it won't be needed@>=
16094 mp_flush_string(mp, name); name=rts(mp->cur_name); xfree(mp->cur_name)
16096 @ Here we have to remember to tell the |input_ln| routine not to
16097 start with a |get|. If the file is empty, it is considered to
16098 contain a single blank line.
16099 @^system dependencies@>
16101 @<Read the first line...@>=
16104 (void)mp_input_ln(mp, cur_file,false);
16105 mp_firm_up_the_line(mp);
16106 mp->buffer[limit]='%'; mp->first=limit+1; loc=start;
16109 @ @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>=
16110 while ( token_state &&(loc==null) ) mp_end_token_list(mp);
16111 if ( token_state ) {
16112 print_err("File names can't appear within macros");
16113 @.File names can't...@>
16114 help3("Sorry...I've converted what follows to tokens,")
16115 ("possibly garbaging the name you gave.")
16116 ("Please delete the tokens and insert the name again.");
16119 if ( file_state ) {
16120 mp_scan_file_name(mp);
16122 xfree(mp->cur_name); mp->cur_name=xstrdup("");
16123 xfree(mp->cur_ext); mp->cur_ext =xstrdup("");
16124 xfree(mp->cur_area); mp->cur_area=xstrdup("");
16127 @ Sometimes we need to deal with two file names at once. This procedure
16128 copies the given string into a special array for an old file name.
16130 @c void mp_copy_old_name (MP mp,str_number s) {
16131 integer k; /* number of positions filled in |old_file_name| */
16132 pool_pointer j; /* index into |str_pool| */
16134 for (j=mp->str_start[s];j<=str_stop(s)-1;j++) {
16136 if ( k<=file_name_size )
16137 mp->old_file_name[k]=xchr(mp->str_pool[j]);
16139 mp->old_file_name[++k] = 0;
16143 char old_file_name[file_name_size+1]; /* analogous to |name_of_file| */
16145 @ The following simple routine starts reading the \.{MPX} file associated
16146 with the current input file.
16148 @c void mp_start_mpx_input (MP mp) {
16149 mp_pack_file_name(mp, in_name, in_area, ".mpx");
16150 @<Try to make sure |name_of_file| refers to a valid \.{MPX} file and
16151 |goto not_found| if there is a problem@>;
16152 mp_begin_file_reading(mp);
16153 if ( ! mp_a_open_in(mp, &cur_file, mp_filetype_program) ) {
16154 mp_end_file_reading(mp);
16157 name=mp_a_make_name_string(mp, cur_file);
16158 mp->mpx_name[index]=name; add_str_ref(name);
16159 @<Read the first line of the new file@>;
16162 @<Explain that the \.{MPX} file can't be read and |succumb|@>;
16165 @ This should ideally be changed to do whatever is necessary to create the
16166 \.{MPX} file given by |name_of_file| if it does not exist or if it is out
16167 of date. This requires invoking \.{MPtoTeX} on the |old_file_name| and passing
16168 the results through \TeX\ and \.{DVItoMP}. (It is possible to use a
16169 completely different typesetting program if suitable postprocessor is
16170 available to perform the function of \.{DVItoMP}.)
16171 @^system dependencies@>
16174 typedef boolean (*run_make_mpx_command)(MP mp, char *origname, char *mtxname);
16177 run_make_mpx_command run_make_mpx;
16179 @ @<Option variables@>=
16180 run_make_mpx_command run_make_mpx;
16182 @ @<Allocate or initialize ...@>=
16183 set_callback_option(run_make_mpx);
16185 @ @<Exported function headers@>=
16186 boolean mp_run_make_mpx (MP mp, char *origname, char *mtxname);
16188 @ The default does nothing.
16190 boolean mp_run_make_mpx (MP mp, char *origname, char *mtxname) {
16191 if (mp && origname && mtxname) /* for -W */
16198 @ @<Try to make sure |name_of_file| refers to a valid \.{MPX} file and
16199 |goto not_found| if there is a problem@>=
16200 mp_copy_old_name(mp, name);
16201 if (!(mp->run_make_mpx)(mp, mp->old_file_name, mp->name_of_file))
16204 @ @<Explain that the \.{MPX} file can't be read and |succumb|@>=
16205 if ( mp->interaction==mp_error_stop_mode ) wake_up_terminal;
16206 mp_print_nl(mp, ">> ");
16207 mp_print(mp, mp->old_file_name);
16208 mp_print_nl(mp, ">> ");
16209 mp_print(mp, mp->name_of_file);
16210 mp_print_nl(mp, "! Unable to make mpx file");
16211 help4("The two files given above are one of your source files")
16212 ("and an auxiliary file I need to read to find out what your")
16213 ("btex..etex blocks mean. If you don't know why I had trouble,")
16214 ("try running it manually through MPtoTeX, TeX, and DVItoMP");
16217 @ The last file-opening commands are for files accessed via the \&{readfrom}
16218 @:read_from_}{\&{readfrom} primitive@>
16219 operator and the \&{write} command. Such files are stored in separate arrays.
16220 @:write_}{\&{write} primitive@>
16222 @<Types in the outer block@>=
16223 typedef unsigned int readf_index; /* |0..max_read_files| */
16224 typedef unsigned int write_index; /* |0..max_write_files| */
16227 readf_index max_read_files; /* maximum number of simultaneously open \&{readfrom} files */
16228 FILE ** rd_file; /* \&{readfrom} files */
16229 char ** rd_fname; /* corresponding file name or 0 if file not open */
16230 readf_index read_files; /* number of valid entries in the above arrays */
16231 write_index max_write_files; /* maximum number of simultaneously open \&{write} */
16232 FILE ** wr_file; /* \&{write} files */
16233 char ** wr_fname; /* corresponding file name or 0 if file not open */
16234 write_index write_files; /* number of valid entries in the above arrays */
16236 @ @<Allocate or initialize ...@>=
16237 mp->max_read_files=8;
16238 mp->rd_file = xmalloc((mp->max_read_files+1),sizeof(FILE *));
16239 mp->rd_fname = xmalloc((mp->max_read_files+1),sizeof(char *));
16240 memset(mp->rd_fname, 0, sizeof(char *)*(mp->max_read_files+1));
16242 mp->max_write_files=8;
16243 mp->wr_file = xmalloc((mp->max_write_files+1),sizeof(FILE *));
16244 mp->wr_fname = xmalloc((mp->max_write_files+1),sizeof(char *));
16245 memset(mp->wr_fname, 0, sizeof(char *)*(mp->max_write_files+1));
16249 @ This routine starts reading the file named by string~|s| without setting
16250 |loc|, |limit|, or |name|. It returns |false| if the file is empty or cannot
16251 be opened. Otherwise it updates |rd_file[n]| and |rd_fname[n]|.
16253 @c boolean mp_start_read_input (MP mp,char *s, readf_index n) {
16254 mp_ptr_scan_file(mp, s);
16256 mp_begin_file_reading(mp);
16257 if ( ! mp_a_open_in(mp, &mp->rd_file[n], mp_filetype_text) )
16259 if ( ! mp_input_ln(mp, mp->rd_file[n], false) ) {
16260 fclose(mp->rd_file[n]);
16263 mp->rd_fname[n]=xstrdup(mp->name_of_file);
16266 mp_end_file_reading(mp);
16270 @ Open |wr_file[n]| using file name~|s| and update |wr_fname[n]|.
16273 void mp_open_write_file (MP mp, char *s, readf_index n) ;
16275 @ @c void mp_open_write_file (MP mp,char *s, readf_index n) {
16276 mp_ptr_scan_file(mp, s);
16278 while ( ! mp_a_open_out(mp, &mp->wr_file[n], mp_filetype_text) )
16279 mp_prompt_file_name(mp, "file name for write output","");
16280 mp->wr_fname[n]=xstrdup(mp->name_of_file);
16284 @* \[36] Introduction to the parsing routines.
16285 We come now to the central nervous system that sparks many of \MP's activities.
16286 By evaluating expressions, from their primary constituents to ever larger
16287 subexpressions, \MP\ builds the structures that ultimately define complete
16288 pictures or fonts of type.
16290 Four mutually recursive subroutines are involved in this process: We call them
16291 $$\hbox{|scan_primary|, |scan_secondary|, |scan_tertiary|,
16292 and |scan_expression|.}$$
16294 Each of them is parameterless and begins with the first token to be scanned
16295 already represented in |cur_cmd|, |cur_mod|, and |cur_sym|. After execution,
16296 the value of the primary or secondary or tertiary or expression that was
16297 found will appear in the global variables |cur_type| and |cur_exp|. The
16298 token following the expression will be represented in |cur_cmd|, |cur_mod|,
16301 Technically speaking, the parsing algorithms are ``LL(1),'' more or less;
16302 backup mechanisms have been added in order to provide reasonable error
16306 small_number cur_type; /* the type of the expression just found */
16307 integer cur_exp; /* the value of the expression just found */
16312 @ Many different kinds of expressions are possible, so it is wise to have
16313 precise descriptions of what |cur_type| and |cur_exp| mean in all cases:
16316 |cur_type=mp_vacuous| means that this expression didn't turn out to have a
16317 value at all, because it arose from a \&{begingroup}$\,\ldots\,$\&{endgroup}
16318 construction in which there was no expression before the \&{endgroup}.
16319 In this case |cur_exp| has some irrelevant value.
16322 |cur_type=mp_boolean_type| means that |cur_exp| is either |true_code|
16326 |cur_type=mp_unknown_boolean| means that |cur_exp| points to a capsule
16327 node that is in the ring of variables equivalent
16328 to at least one undefined boolean variable.
16331 |cur_type=mp_string_type| means that |cur_exp| is a string number (i.e., an
16332 integer in the range |0<=cur_exp<str_ptr|). That string's reference count
16333 includes this particular reference.
16336 |cur_type=mp_unknown_string| means that |cur_exp| points to a capsule
16337 node that is in the ring of variables equivalent
16338 to at least one undefined string variable.
16341 |cur_type=mp_pen_type| means that |cur_exp| points to a node in a pen. Nobody
16342 else points to any of the nodes in this pen. The pen may be polygonal or
16346 |cur_type=mp_unknown_pen| means that |cur_exp| points to a capsule
16347 node that is in the ring of variables equivalent
16348 to at least one undefined pen variable.
16351 |cur_type=mp_path_type| means that |cur_exp| points to a the first node of
16352 a path; nobody else points to this particular path. The control points of
16353 the path will have been chosen.
16356 |cur_type=mp_unknown_path| means that |cur_exp| points to a capsule
16357 node that is in the ring of variables equivalent
16358 to at least one undefined path variable.
16361 |cur_type=mp_picture_type| means that |cur_exp| points to an edge header node.
16362 There may be other pointers to this particular set of edges. The header node
16363 contains a reference count that includes this particular reference.
16366 |cur_type=mp_unknown_picture| means that |cur_exp| points to a capsule
16367 node that is in the ring of variables equivalent
16368 to at least one undefined picture variable.
16371 |cur_type=mp_transform_type| means that |cur_exp| points to a |mp_transform_type|
16372 capsule node. The |value| part of this capsule
16373 points to a transform node that contains six numeric values,
16374 each of which is |independent|, |dependent|, |mp_proto_dependent|, or |known|.
16377 |cur_type=mp_color_type| means that |cur_exp| points to a |color_type|
16378 capsule node. The |value| part of this capsule
16379 points to a color node that contains three numeric values,
16380 each of which is |independent|, |dependent|, |mp_proto_dependent|, or |known|.
16383 |cur_type=mp_cmykcolor_type| means that |cur_exp| points to a |mp_cmykcolor_type|
16384 capsule node. The |value| part of this capsule
16385 points to a color node that contains four numeric values,
16386 each of which is |independent|, |dependent|, |mp_proto_dependent|, or |known|.
16389 |cur_type=mp_pair_type| means that |cur_exp| points to a capsule
16390 node whose type is |mp_pair_type|. The |value| part of this capsule
16391 points to a pair node that contains two numeric values,
16392 each of which is |independent|, |dependent|, |mp_proto_dependent|, or |known|.
16395 |cur_type=mp_known| means that |cur_exp| is a |scaled| value.
16398 |cur_type=mp_dependent| means that |cur_exp| points to a capsule node whose type
16399 is |dependent|. The |dep_list| field in this capsule points to the associated
16403 |cur_type=mp_proto_dependent| means that |cur_exp| points to a |mp_proto_dependent|
16404 capsule node. The |dep_list| field in this capsule
16405 points to the associated dependency list.
16408 |cur_type=independent| means that |cur_exp| points to a capsule node
16409 whose type is |independent|. This somewhat unusual case can arise, for
16410 example, in the expression
16411 `$x+\&{begingroup}\penalty0\,\&{string}\,x; 0\,\&{endgroup}$'.
16414 |cur_type=mp_token_list| means that |cur_exp| points to a linked list of
16415 tokens. This case arises only on the left-hand side of an assignment
16416 (`\.{:=}') operation, under very special circumstances.
16418 \smallskip\noindent
16419 The possible settings of |cur_type| have been listed here in increasing
16420 numerical order. Notice that |cur_type| will never be |mp_numeric_type| or
16421 |suffixed_macro| or |mp_unsuffixed_macro|, although variables of those types
16422 are allowed. Conversely, \MP\ has no variables of type |mp_vacuous| or
16425 @ Capsules are two-word nodes that have a similar meaning
16426 to |cur_type| and |cur_exp|. Such nodes have |name_type=capsule|
16427 and |link<=diov|; and their |type| field is one of the possibilities for
16428 |cur_type| listed above.
16430 The |value| field of a capsule is, in most cases, the value that
16431 corresponds to its |type|, as |cur_exp| corresponds to |cur_type|.
16432 However, when |cur_exp| would point to a capsule,
16433 no extra layer of indirection is present; the |value|
16434 field is what would have been called |value(cur_exp)| if it had not been
16435 encapsulated. Furthermore, if the type is |dependent| or
16436 |mp_proto_dependent|, the |value| field of a capsule is replaced by
16437 |dep_list| and |prev_dep| fields, since dependency lists in capsules are
16438 always part of the general |dep_list| structure.
16440 The |get_x_next| routine is careful not to change the values of |cur_type|
16441 and |cur_exp| when it gets an expanded token. However, |get_x_next| might
16442 call a macro, which might parse an expression, which might execute lots of
16443 commands in a group; hence it's possible that |cur_type| might change
16444 from, say, |mp_unknown_boolean| to |mp_boolean_type|, or from |dependent| to
16445 |known| or |independent|, during the time |get_x_next| is called. The
16446 programs below are careful to stash sensitive intermediate results in
16447 capsules, so that \MP's generality doesn't cause trouble.
16449 Here's a procedure that illustrates these conventions. It takes
16450 the contents of $(|cur_type|\kern-.3pt,|cur_exp|\kern-.3pt)$
16451 and stashes them away in a
16452 capsule. It is not used when |cur_type=mp_token_list|.
16453 After the operation, |cur_type=mp_vacuous|; hence there is no need to
16454 copy path lists or to update reference counts, etc.
16456 The special link |diov| is put on the capsule returned by
16457 |stash_cur_exp|, because this procedure is used to store macro parameters
16458 that must be easily distinguishable from token lists.
16460 @<Declare the stashing/unstashing routines@>=
16461 pointer mp_stash_cur_exp (MP mp) {
16462 pointer p; /* the capsule that will be returned */
16463 switch (mp->cur_type) {
16464 case unknown_types:
16465 case mp_transform_type:
16466 case mp_color_type:
16469 case mp_proto_dependent:
16470 case mp_independent:
16471 case mp_cmykcolor_type:
16475 p=mp_get_node(mp, value_node_size); name_type(p)=mp_capsule;
16476 type(p)=mp->cur_type; value(p)=mp->cur_exp;
16479 mp->cur_type=mp_vacuous; link(p)=diov;
16483 @ The inverse of |stash_cur_exp| is the following procedure, which
16484 deletes an unnecessary capsule and puts its contents into |cur_type|
16487 The program steps of \MP\ can be divided into two categories: those in
16488 which |cur_type| and |cur_exp| are ``alive'' and those in which they are
16489 ``dead,'' in the sense that |cur_type| and |cur_exp| contain relevant
16490 information or not. It's important not to ignore them when they're alive,
16491 and it's important not to pay attention to them when they're dead.
16493 There's also an intermediate category: If |cur_type=mp_vacuous|, then
16494 |cur_exp| is irrelevant, hence we can proceed without caring if |cur_type|
16495 and |cur_exp| are alive or dead. In such cases we say that |cur_type|
16496 and |cur_exp| are {\sl dormant}. It is permissible to call |get_x_next|
16497 only when they are alive or dormant.
16499 The \\{stash} procedure above assumes that |cur_type| and |cur_exp|
16500 are alive or dormant. The \\{unstash} procedure assumes that they are
16501 dead or dormant; it resuscitates them.
16503 @<Declare the stashing/unstashing...@>=
16504 void mp_unstash_cur_exp (MP mp,pointer p) ;
16507 void mp_unstash_cur_exp (MP mp,pointer p) {
16508 mp->cur_type=type(p);
16509 switch (mp->cur_type) {
16510 case unknown_types:
16511 case mp_transform_type:
16512 case mp_color_type:
16515 case mp_proto_dependent:
16516 case mp_independent:
16517 case mp_cmykcolor_type:
16521 mp->cur_exp=value(p);
16522 mp_free_node(mp, p,value_node_size);
16527 @ The following procedure prints the values of expressions in an
16528 abbreviated format. If its first parameter |p| is null, the value of
16529 |(cur_type,cur_exp)| is displayed; otherwise |p| should be a capsule
16530 containing the desired value. The second parameter controls the amount of
16531 output. If it is~0, dependency lists will be abbreviated to
16532 `\.{linearform}' unless they consist of a single term. If it is greater
16533 than~1, complicated structures (pens, pictures, and paths) will be displayed
16536 @<Declare subroutines for printing expressions@>=
16537 @<Declare the procedure called |print_dp|@>;
16538 @<Declare the stashing/unstashing routines@>;
16539 void mp_print_exp (MP mp,pointer p, small_number verbosity) {
16540 boolean restore_cur_exp; /* should |cur_exp| be restored? */
16541 small_number t; /* the type of the expression */
16542 pointer q; /* a big node being displayed */
16543 integer v=0; /* the value of the expression */
16545 restore_cur_exp=false;
16547 p=mp_stash_cur_exp(mp); restore_cur_exp=true;
16550 if ( t<mp_dependent ) v=value(p); else if ( t<mp_independent ) v=dep_list(p);
16551 @<Print an abbreviated value of |v| with format depending on |t|@>;
16552 if ( restore_cur_exp ) mp_unstash_cur_exp(mp, p);
16555 @ @<Print an abbreviated value of |v| with format depending on |t|@>=
16557 case mp_vacuous:mp_print(mp, "mp_vacuous"); break;
16558 case mp_boolean_type:
16559 if ( v==true_code ) mp_print(mp, "true"); else mp_print(mp, "false");
16561 case unknown_types: case mp_numeric_type:
16562 @<Display a variable that's been declared but not defined@>;
16564 case mp_string_type:
16565 mp_print_char(mp, '"'); mp_print_str(mp, v); mp_print_char(mp, '"');
16567 case mp_pen_type: case mp_path_type: case mp_picture_type:
16568 @<Display a complex type@>;
16570 case mp_transform_type: case mp_color_type: case mp_pair_type: case mp_cmykcolor_type:
16571 if ( v==null ) mp_print_type(mp, t);
16572 else @<Display a big node@>;
16574 case mp_known:mp_print_scaled(mp, v); break;
16575 case mp_dependent: case mp_proto_dependent:
16576 mp_print_dp(mp, t,v,verbosity);
16578 case mp_independent:mp_print_variable_name(mp, p); break;
16579 default: mp_confusion(mp, "exp"); break;
16580 @:this can't happen exp}{\quad exp@>
16583 @ @<Display a big node@>=
16585 mp_print_char(mp, '('); q=v+mp->big_node_size[t];
16587 if ( type(v)==mp_known ) mp_print_scaled(mp, value(v));
16588 else if ( type(v)==mp_independent ) mp_print_variable_name(mp, v);
16589 else mp_print_dp(mp, type(v),dep_list(v),verbosity);
16591 if ( v!=q ) mp_print_char(mp, ',');
16593 mp_print_char(mp, ')');
16596 @ Values of type \&{picture}, \&{path}, and \&{pen} are displayed verbosely
16597 in the log file only, unless the user has given a positive value to
16600 @<Display a complex type@>=
16601 if ( verbosity<=1 ) {
16602 mp_print_type(mp, t);
16604 if ( mp->selector==term_and_log )
16605 if ( mp->internal[tracing_online]<=0 ) {
16606 mp->selector=term_only;
16607 mp_print_type(mp, t); mp_print(mp, " (see the transcript file)");
16608 mp->selector=term_and_log;
16611 case mp_pen_type:mp_print_pen(mp, v,"",false); break;
16612 case mp_path_type:mp_print_path(mp, v,"",false); break;
16613 case mp_picture_type:mp_print_edges(mp, v,"",false); break;
16614 } /* there are no other cases */
16617 @ @<Declare the procedure called |print_dp|@>=
16618 void mp_print_dp (MP mp,small_number t, pointer p,
16619 small_number verbosity) {
16620 pointer q; /* the node following |p| */
16622 if ( (info(q)==null) || (verbosity>0) ) mp_print_dependency(mp, p,t);
16623 else mp_print(mp, "linearform");
16626 @ The displayed name of a variable in a ring will not be a capsule unless
16627 the ring consists entirely of capsules.
16629 @<Display a variable that's been declared but not defined@>=
16630 { mp_print_type(mp, t);
16632 { mp_print_char(mp, ' ');
16633 while ( (name_type(v)==mp_capsule) && (v!=p) ) v=value(v);
16634 mp_print_variable_name(mp, v);
16638 @ When errors are detected during parsing, it is often helpful to
16639 display an expression just above the error message, using |exp_err|
16640 or |disp_err| instead of |print_err|.
16642 @d exp_err(A) mp_disp_err(mp, null,(A)) /* displays the current expression */
16644 @<Declare subroutines for printing expressions@>=
16645 void mp_disp_err (MP mp,pointer p, char *s) {
16646 if ( mp->interaction==mp_error_stop_mode ) wake_up_terminal;
16647 mp_print_nl(mp, ">> ");
16649 mp_print_exp(mp, p,1); /* ``medium verbose'' printing of the expression */
16651 mp_print_nl(mp, "! "); mp_print(mp, s);
16656 @ If |cur_type| and |cur_exp| contain relevant information that should
16657 be recycled, we will use the following procedure, which changes |cur_type|
16658 to |known| and stores a given value in |cur_exp|. We can think of |cur_type|
16659 and |cur_exp| as either alive or dormant after this has been done,
16660 because |cur_exp| will not contain a pointer value.
16662 @ @c void mp_flush_cur_exp (MP mp,scaled v) {
16663 switch (mp->cur_type) {
16664 case unknown_types: case mp_transform_type: case mp_color_type: case mp_pair_type:
16665 case mp_dependent: case mp_proto_dependent: case mp_independent: case mp_cmykcolor_type:
16666 mp_recycle_value(mp, mp->cur_exp);
16667 mp_free_node(mp, mp->cur_exp,value_node_size);
16669 case mp_string_type:
16670 delete_str_ref(mp->cur_exp); break;
16671 case mp_pen_type: case mp_path_type:
16672 mp_toss_knot_list(mp, mp->cur_exp); break;
16673 case mp_picture_type:
16674 delete_edge_ref(mp->cur_exp); break;
16678 mp->cur_type=mp_known; mp->cur_exp=v;
16681 @ There's a much more general procedure that is capable of releasing
16682 the storage associated with any two-word value packet.
16684 @<Declare the recycling subroutines@>=
16685 void mp_recycle_value (MP mp,pointer p) ;
16687 @ @c void mp_recycle_value (MP mp,pointer p) {
16688 small_number t; /* a type code */
16689 integer vv; /* another value */
16690 pointer q,r,s,pp; /* link manipulation registers */
16691 integer v=0; /* a value */
16693 if ( t<mp_dependent ) v=value(p);
16695 case undefined: case mp_vacuous: case mp_boolean_type: case mp_known:
16696 case mp_numeric_type:
16698 case unknown_types:
16699 mp_ring_delete(mp, p); break;
16700 case mp_string_type:
16701 delete_str_ref(v); break;
16702 case mp_path_type: case mp_pen_type:
16703 mp_toss_knot_list(mp, v); break;
16704 case mp_picture_type:
16705 delete_edge_ref(v); break;
16706 case mp_cmykcolor_type: case mp_pair_type: case mp_color_type:
16707 case mp_transform_type:
16708 @<Recycle a big node@>; break;
16709 case mp_dependent: case mp_proto_dependent:
16710 @<Recycle a dependency list@>; break;
16711 case mp_independent:
16712 @<Recycle an independent variable@>; break;
16713 case mp_token_list: case mp_structured:
16714 mp_confusion(mp, "recycle"); break;
16715 @:this can't happen recycle}{\quad recycle@>
16716 case mp_unsuffixed_macro: case mp_suffixed_macro:
16717 mp_delete_mac_ref(mp, value(p)); break;
16718 } /* there are no other cases */
16722 @ @<Recycle a big node@>=
16724 q=v+mp->big_node_size[t];
16726 q=q-2; mp_recycle_value(mp, q);
16728 mp_free_node(mp, v,mp->big_node_size[t]);
16731 @ @<Recycle a dependency list@>=
16734 while ( info(q)!=null ) q=link(q);
16735 link(prev_dep(p))=link(q);
16736 prev_dep(link(q))=prev_dep(p);
16737 link(q)=null; mp_flush_node_list(mp, dep_list(p));
16740 @ When an independent variable disappears, it simply fades away, unless
16741 something depends on it. In the latter case, a dependent variable whose
16742 coefficient of dependence is maximal will take its place.
16743 The relevant algorithm is due to Ignacio~A. Zabala, who implemented it
16744 as part of his Ph.D. thesis (Stanford University, December 1982).
16745 @^Zabala Salelles, Ignacio Andres@>
16747 For example, suppose that variable $x$ is being recycled, and that the
16748 only variables depending on~$x$ are $y=2x+a$ and $z=x+b$. In this case
16749 we want to make $y$ independent and $z=.5y-.5a+b$; no other variables
16750 will depend on~$y$. If $\\{tracingequations}>0$ in this situation,
16751 we will print `\.{\#\#\# -2x=-y+a}'.
16753 There's a slight complication, however: An independent variable $x$
16754 can occur both in dependency lists and in proto-dependency lists.
16755 This makes it necessary to be careful when deciding which coefficient
16758 Furthermore, this complication is not so slight when
16759 a proto-dependent variable is chosen to become independent. For example,
16760 suppose that $y=2x+100a$ is proto-dependent while $z=x+b$ is dependent;
16761 then we must change $z=.5y-50a+b$ to a proto-dependency, because of the
16762 large coefficient `50'.
16764 In order to deal with these complications without wasting too much time,
16765 we shall link together the occurrences of~$x$ among all the linear
16766 dependencies, maintaining separate lists for the dependent and
16767 proto-dependent cases.
16769 @<Recycle an independent variable@>=
16771 mp->max_c[mp_dependent]=0; mp->max_c[mp_proto_dependent]=0;
16772 mp->max_link[mp_dependent]=null; mp->max_link[mp_proto_dependent]=null;
16774 while ( q!=dep_head ) {
16775 s=value_loc(q); /* now |link(s)=dep_list(q)| */
16778 if ( info(r)==null ) break;;
16779 if ( info(r)!=p ) {
16782 t=type(q); link(s)=link(r); info(r)=q;
16783 if ( abs(value(r))>mp->max_c[t] ) {
16784 @<Record a new maximum coefficient of type |t|@>;
16786 link(r)=mp->max_link[t]; mp->max_link[t]=r;
16792 if ( (mp->max_c[mp_dependent]>0)||(mp->max_c[mp_proto_dependent]>0) ) {
16793 @<Choose a dependent variable to take the place of the disappearing
16794 independent variable, and change all remaining dependencies
16799 @ The code for independency removal makes use of three two-word arrays.
16802 integer max_c[mp_proto_dependent+1]; /* max coefficient magnitude */
16803 pointer max_ptr[mp_proto_dependent+1]; /* where |p| occurs with |max_c| */
16804 pointer max_link[mp_proto_dependent+1]; /* other occurrences of |p| */
16806 @ @<Record a new maximum coefficient...@>=
16808 if ( mp->max_c[t]>0 ) {
16809 link(mp->max_ptr[t])=mp->max_link[t]; mp->max_link[t]=mp->max_ptr[t];
16811 mp->max_c[t]=abs(value(r)); mp->max_ptr[t]=r;
16814 @ @<Choose a dependent...@>=
16816 if ( (mp->max_c[mp_dependent] / 010000 >= mp->max_c[mp_proto_dependent]) )
16819 t=mp_proto_dependent;
16820 @<Determine the dependency list |s| to substitute for the independent
16822 t=mp_dependent+mp_proto_dependent-t; /* complement |t| */
16823 if ( mp->max_c[t]>0 ) { /* we need to pick up an unchosen dependency */
16824 link(mp->max_ptr[t])=mp->max_link[t]; mp->max_link[t]=mp->max_ptr[t];
16826 if ( t!=mp_dependent ) { @<Substitute new dependencies in place of |p|@>; }
16827 else { @<Substitute new proto-dependencies in place of |p|@>;}
16828 mp_flush_node_list(mp, s);
16829 if ( mp->fix_needed ) mp_fix_dependencies(mp);
16833 @ Let |s=max_ptr[t]|. At this point we have $|value|(s)=\pm|max_c|[t]$,
16834 and |info(s)| points to the dependent variable~|pp| of type~|t| from
16835 whose dependency list we have removed node~|s|. We must reinsert
16836 node~|s| into the dependency list, with coefficient $-1.0$, and with
16837 |pp| as the new independent variable. Since |pp| will have a larger serial
16838 number than any other variable, we can put node |s| at the head of the
16841 @<Determine the dep...@>=
16842 s=mp->max_ptr[t]; pp=info(s); v=value(s);
16843 if ( t==mp_dependent ) value(s)=-fraction_one; else value(s)=-unity;
16844 r=dep_list(pp); link(s)=r;
16845 while ( info(r)!=null ) r=link(r);
16846 q=link(r); link(r)=null;
16847 prev_dep(q)=prev_dep(pp); link(prev_dep(pp))=q;
16849 if ( mp->cur_exp==pp ) if ( mp->cur_type==t ) mp->cur_type=mp_independent;
16850 if ( mp->internal[tracing_equations]>0 ) {
16851 @<Show the transformed dependency@>;
16854 @ Now $(-v)$ times the formerly independent variable~|p| is being replaced
16855 by the dependency list~|s|.
16857 @<Show the transformed...@>=
16858 if ( mp_interesting(mp, p) ) {
16859 mp_begin_diagnostic(mp); mp_print_nl(mp, "### ");
16860 @:]]]\#\#\#_}{\.{\#\#\#}@>
16861 if ( v>0 ) mp_print_char(mp, '-');
16862 if ( t==mp_dependent ) vv=mp_round_fraction(mp, mp->max_c[mp_dependent]);
16863 else vv=mp->max_c[mp_proto_dependent];
16864 if ( vv!=unity ) mp_print_scaled(mp, vv);
16865 mp_print_variable_name(mp, p);
16866 while ( value(p) % s_scale>0 ) {
16867 mp_print(mp, "*4"); value(p)=value(p)-2;
16869 if ( t==mp_dependent ) mp_print_char(mp, '='); else mp_print(mp, " = ");
16870 mp_print_dependency(mp, s,t);
16871 mp_end_diagnostic(mp, false);
16874 @ Finally, there are dependent and proto-dependent variables whose
16875 dependency lists must be brought up to date.
16877 @<Substitute new dependencies...@>=
16878 for (t=mp_dependent;t<=mp_proto_dependent;t++){
16880 while ( r!=null ) {
16882 dep_list(q)=mp_p_plus_fq(mp, dep_list(q),
16883 mp_make_fraction(mp, value(r),-v),s,t,mp_dependent);
16884 if ( dep_list(q)==mp->dep_final ) mp_make_known(mp, q,mp->dep_final);
16885 q=r; r=link(r); mp_free_node(mp, q,dep_node_size);
16889 @ @<Substitute new proto...@>=
16890 for (t=mp_dependent;t<=mp_proto_dependent;t++) {
16892 while ( r!=null ) {
16894 if ( t==mp_dependent ) { /* for safety's sake, we change |q| to |mp_proto_dependent| */
16895 if ( mp->cur_exp==q ) if ( mp->cur_type==mp_dependent )
16896 mp->cur_type=mp_proto_dependent;
16897 dep_list(q)=mp_p_over_v(mp, dep_list(q),unity,mp_dependent,mp_proto_dependent);
16898 type(q)=mp_proto_dependent; value(r)=mp_round_fraction(mp, value(r));
16900 dep_list(q)=mp_p_plus_fq(mp, dep_list(q),
16901 mp_make_scaled(mp, value(r),-v),s,mp_proto_dependent,mp_proto_dependent);
16902 if ( dep_list(q)==mp->dep_final ) mp_make_known(mp, q,mp->dep_final);
16903 q=r; r=link(r); mp_free_node(mp, q,dep_node_size);
16907 @ Here are some routines that provide handy combinations of actions
16908 that are often needed during error recovery. For example,
16909 `|flush_error|' flushes the current expression, replaces it by
16910 a given value, and calls |error|.
16912 Errors often are detected after an extra token has already been scanned.
16913 The `\\{put\_get}' routines put that token back before calling |error|;
16914 then they get it back again. (Or perhaps they get another token, if
16915 the user has changed things.)
16918 void mp_flush_error (MP mp,scaled v);
16919 void mp_put_get_error (MP mp);
16920 void mp_put_get_flush_error (MP mp,scaled v) ;
16923 void mp_flush_error (MP mp,scaled v) {
16924 mp_error(mp); mp_flush_cur_exp(mp, v);
16926 void mp_put_get_error (MP mp) {
16927 mp_back_error(mp); mp_get_x_next(mp);
16929 void mp_put_get_flush_error (MP mp,scaled v) {
16930 mp_put_get_error(mp);
16931 mp_flush_cur_exp(mp, v);
16934 @ A global variable |var_flag| is set to a special command code
16935 just before \MP\ calls |scan_expression|, if the expression should be
16936 treated as a variable when this command code immediately follows. For
16937 example, |var_flag| is set to |assignment| at the beginning of a
16938 statement, because we want to know the {\sl location\/} of a variable at
16939 the left of `\.{:=}', not the {\sl value\/} of that variable.
16941 The |scan_expression| subroutine calls |scan_tertiary|,
16942 which calls |scan_secondary|, which calls |scan_primary|, which sets
16943 |var_flag:=0|. In this way each of the scanning routines ``knows''
16944 when it has been called with a special |var_flag|, but |var_flag| is
16947 A variable preceding a command that equals |var_flag| is converted to a
16948 token list rather than a value. Furthermore, an `\.{=}' sign following an
16949 expression with |var_flag=assignment| is not considered to be a relation
16950 that produces boolean expressions.
16954 int var_flag; /* command that wants a variable */
16959 @* \[37] Parsing primary expressions.
16960 The first parsing routine, |scan_primary|, is also the most complicated one,
16961 since it involves so many different cases. But each case---with one
16962 exception---is fairly simple by itself.
16964 When |scan_primary| begins, the first token of the primary to be scanned
16965 should already appear in |cur_cmd|, |cur_mod|, and |cur_sym|. The values
16966 of |cur_type| and |cur_exp| should be either dead or dormant, as explained
16967 earlier. If |cur_cmd| is not between |min_primary_command| and
16968 |max_primary_command|, inclusive, a syntax error will be signaled.
16970 @<Declare the basic parsing subroutines@>=
16971 void mp_scan_primary (MP mp) {
16972 pointer p,q,r; /* for list manipulation */
16973 quarterword c; /* a primitive operation code */
16974 int my_var_flag; /* initial value of |my_var_flag| */
16975 pointer l_delim,r_delim; /* hash addresses of a delimiter pair */
16976 @<Other local variables for |scan_primary|@>;
16977 my_var_flag=mp->var_flag; mp->var_flag=0;
16980 @<Supply diagnostic information, if requested@>;
16981 switch (mp->cur_cmd) {
16982 case left_delimiter:
16983 @<Scan a delimited primary@>; break;
16985 @<Scan a grouped primary@>; break;
16987 @<Scan a string constant@>; break;
16988 case numeric_token:
16989 @<Scan a primary that starts with a numeric token@>; break;
16991 @<Scan a nullary operation@>; break;
16992 case unary: case type_name: case cycle: case plus_or_minus:
16993 @<Scan a unary operation@>; break;
16994 case primary_binary:
16995 @<Scan a binary operation with `\&{of}' between its operands@>; break;
16997 @<Convert a suffix to a string@>; break;
16998 case internal_quantity:
16999 @<Scan an internal numeric quantity@>; break;
17000 case capsule_token:
17001 mp_make_exp_copy(mp, mp->cur_mod); break;
17003 @<Scan a variable primary; |goto restart| if it turns out to be a macro@>; break;
17005 mp_bad_exp(mp, "A primary"); goto RESTART; break;
17006 @.A primary expression...@>
17008 mp_get_x_next(mp); /* the routines |goto done| if they don't want this */
17010 if ( mp->cur_cmd==left_bracket ) {
17011 if ( mp->cur_type>=mp_known ) {
17012 @<Scan a mediation construction@>;
17019 @ Errors at the beginning of expressions are flagged by |bad_exp|.
17021 @c void mp_bad_exp (MP mp,char * s) {
17023 print_err(s); mp_print(mp, " expression can't begin with `");
17024 mp_print_cmd_mod(mp, mp->cur_cmd,mp->cur_mod);
17025 mp_print_char(mp, '\'');
17026 help4("I'm afraid I need some sort of value in order to continue,")
17027 ("so I've tentatively inserted `0'. You may want to")
17028 ("delete this zero and insert something else;")
17029 ("see Chapter 27 of The METAFONTbook for an example.");
17030 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
17031 mp_back_input(mp); mp->cur_sym=0; mp->cur_cmd=numeric_token;
17032 mp->cur_mod=0; mp_ins_error(mp);
17033 save_flag=mp->var_flag; mp->var_flag=0; mp_get_x_next(mp);
17034 mp->var_flag=save_flag;
17037 @ @<Supply diagnostic information, if requested@>=
17039 if ( mp->panicking ) mp_check_mem(mp, false);
17041 if ( mp->interrupt!=0 ) if ( mp->OK_to_interrupt ) {
17042 mp_back_input(mp); check_interrupt; mp_get_x_next(mp);
17045 @ @<Scan a delimited primary@>=
17047 l_delim=mp->cur_sym; r_delim=mp->cur_mod;
17048 mp_get_x_next(mp); mp_scan_expression(mp);
17049 if ( (mp->cur_cmd==comma) && (mp->cur_type>=mp_known) ) {
17050 @<Scan the rest of a delimited set of numerics@>;
17052 mp_check_delimiter(mp, l_delim,r_delim);
17056 @ The |stash_in| subroutine puts the current (numeric) expression into a field
17057 within a ``big node.''
17059 @c void mp_stash_in (MP mp,pointer p) {
17060 pointer q; /* temporary register */
17061 type(p)=mp->cur_type;
17062 if ( mp->cur_type==mp_known ) {
17063 value(p)=mp->cur_exp;
17065 if ( mp->cur_type==mp_independent ) {
17066 @<Stash an independent |cur_exp| into a big node@>;
17068 mp->mem[value_loc(p)]=mp->mem[value_loc(mp->cur_exp)];
17069 /* |dep_list(p):=dep_list(cur_exp)| and |prev_dep(p):=prev_dep(cur_exp)| */
17070 link(prev_dep(p))=p;
17072 mp_free_node(mp, mp->cur_exp,value_node_size);
17074 mp->cur_type=mp_vacuous;
17077 @ In rare cases the current expression can become |independent|. There
17078 may be many dependency lists pointing to such an independent capsule,
17079 so we can't simply move it into place within a big node. Instead,
17080 we copy it, then recycle it.
17082 @ @<Stash an independent |cur_exp|...@>=
17084 q=mp_single_dependency(mp, mp->cur_exp);
17085 if ( q==mp->dep_final ){
17086 type(p)=mp_known; value(p)=0; mp_free_node(mp, q,dep_node_size);
17088 type(p)=mp_dependent; mp_new_dep(mp, p,q);
17090 mp_recycle_value(mp, mp->cur_exp);
17093 @ This code uses the fact that |red_part_loc| and |green_part_loc|
17094 are synonymous with |x_part_loc| and |y_part_loc|.
17096 @<Scan the rest of a delimited set of numerics@>=
17098 p=mp_stash_cur_exp(mp);
17099 mp_get_x_next(mp); mp_scan_expression(mp);
17100 @<Make sure the second part of a pair or color has a numeric type@>;
17101 q=mp_get_node(mp, value_node_size); name_type(q)=mp_capsule;
17102 if ( mp->cur_cmd==comma ) type(q)=mp_color_type;
17103 else type(q)=mp_pair_type;
17104 mp_init_big_node(mp, q); r=value(q);
17105 mp_stash_in(mp, y_part_loc(r));
17106 mp_unstash_cur_exp(mp, p);
17107 mp_stash_in(mp, x_part_loc(r));
17108 if ( mp->cur_cmd==comma ) {
17109 @<Scan the last of a triplet of numerics@>;
17111 if ( mp->cur_cmd==comma ) {
17112 type(q)=mp_cmykcolor_type;
17113 mp_init_big_node(mp, q); t=value(q);
17114 mp->mem[cyan_part_loc(t)]=mp->mem[red_part_loc(r)];
17115 value(cyan_part_loc(t))=value(red_part_loc(r));
17116 mp->mem[magenta_part_loc(t)]=mp->mem[green_part_loc(r)];
17117 value(magenta_part_loc(t))=value(green_part_loc(r));
17118 mp->mem[yellow_part_loc(t)]=mp->mem[blue_part_loc(r)];
17119 value(yellow_part_loc(t))=value(blue_part_loc(r));
17120 mp_recycle_value(mp, r);
17122 @<Scan the last of a quartet of numerics@>;
17124 mp_check_delimiter(mp, l_delim,r_delim);
17125 mp->cur_type=type(q);
17129 @ @<Make sure the second part of a pair or color has a numeric type@>=
17130 if ( mp->cur_type<mp_known ) {
17131 exp_err("Nonnumeric ypart has been replaced by 0");
17132 @.Nonnumeric...replaced by 0@>
17133 help4("I've started to scan a pair `(a,b)' or a color `(a,b,c)';")
17134 ("but after finding a nice `a' I found a `b' that isn't")
17135 ("of numeric type. So I've changed that part to zero.")
17136 ("(The b that I didn't like appears above the error message.)");
17137 mp_put_get_flush_error(mp, 0);
17140 @ @<Scan the last of a triplet of numerics@>=
17142 mp_get_x_next(mp); mp_scan_expression(mp);
17143 if ( mp->cur_type<mp_known ) {
17144 exp_err("Nonnumeric third part has been replaced by 0");
17145 @.Nonnumeric...replaced by 0@>
17146 help3("I've just scanned a color `(a,b,c)' or cmykcolor(a,b,c,d); but the `c'")
17147 ("isn't of numeric type. So I've changed that part to zero.")
17148 ("(The c that I didn't like appears above the error message.)");
17149 mp_put_get_flush_error(mp, 0);
17151 mp_stash_in(mp, blue_part_loc(r));
17154 @ @<Scan the last of a quartet of numerics@>=
17156 mp_get_x_next(mp); mp_scan_expression(mp);
17157 if ( mp->cur_type<mp_known ) {
17158 exp_err("Nonnumeric blackpart has been replaced by 0");
17159 @.Nonnumeric...replaced by 0@>
17160 help3("I've just scanned a cmykcolor `(c,m,y,k)'; but the `k' isn't")
17161 ("of numeric type. So I've changed that part to zero.")
17162 ("(The k that I didn't like appears above the error message.)");
17163 mp_put_get_flush_error(mp, 0);
17165 mp_stash_in(mp, black_part_loc(r));
17168 @ The local variable |group_line| keeps track of the line
17169 where a \&{begingroup} command occurred; this will be useful
17170 in an error message if the group doesn't actually end.
17172 @<Other local variables for |scan_primary|@>=
17173 integer group_line; /* where a group began */
17175 @ @<Scan a grouped primary@>=
17177 group_line=mp_true_line(mp);
17178 if ( mp->internal[tracing_commands]>0 ) show_cur_cmd_mod;
17179 save_boundary_item(p);
17181 mp_do_statement(mp); /* ends with |cur_cmd>=semicolon| */
17182 } while (! (mp->cur_cmd!=semicolon));
17183 if ( mp->cur_cmd!=end_group ) {
17184 print_err("A group begun on line ");
17185 @.A group...never ended@>
17186 mp_print_int(mp, group_line);
17187 mp_print(mp, " never ended");
17188 help2("I saw a `begingroup' back there that hasn't been matched")
17189 ("by `endgroup'. So I've inserted `endgroup' now.");
17190 mp_back_error(mp); mp->cur_cmd=end_group;
17193 /* this might change |cur_type|, if independent variables are recycled */
17194 if ( mp->internal[tracing_commands]>0 ) show_cur_cmd_mod;
17197 @ @<Scan a string constant@>=
17199 mp->cur_type=mp_string_type; mp->cur_exp=mp->cur_mod;
17202 @ Later we'll come to procedures that perform actual operations like
17203 addition, square root, and so on; our purpose now is to do the parsing.
17204 But we might as well mention those future procedures now, so that the
17205 suspense won't be too bad:
17208 |do_nullary(c)| does primitive operations that have no operands (e.g.,
17209 `\&{true}' or `\&{pencircle}');
17212 |do_unary(c)| applies a primitive operation to the current expression;
17215 |do_binary(p,c)| applies a primitive operation to the capsule~|p|
17216 and the current expression.
17218 @<Scan a nullary operation@>=mp_do_nullary(mp, mp->cur_mod)
17220 @ @<Scan a unary operation@>=
17222 c=mp->cur_mod; mp_get_x_next(mp); mp_scan_primary(mp);
17223 mp_do_unary(mp, c); goto DONE;
17226 @ A numeric token might be a primary by itself, or it might be the
17227 numerator of a fraction composed solely of numeric tokens, or it might
17228 multiply the primary that follows (provided that the primary doesn't begin
17229 with a plus sign or a minus sign). The code here uses the facts that
17230 |max_primary_command=plus_or_minus| and
17231 |max_primary_command-1=numeric_token|. If a fraction is found that is less
17232 than unity, we try to retain higher precision when we use it in scalar
17235 @<Other local variables for |scan_primary|@>=
17236 scaled num,denom; /* for primaries that are fractions, like `1/2' */
17238 @ @<Scan a primary that starts with a numeric token@>=
17240 mp->cur_exp=mp->cur_mod; mp->cur_type=mp_known; mp_get_x_next(mp);
17241 if ( mp->cur_cmd!=slash ) {
17245 if ( mp->cur_cmd!=numeric_token ) {
17247 mp->cur_cmd=slash; mp->cur_mod=over; mp->cur_sym=frozen_slash;
17250 num=mp->cur_exp; denom=mp->cur_mod;
17251 if ( denom==0 ) { @<Protest division by zero@>; }
17252 else { mp->cur_exp=mp_make_scaled(mp, num,denom); }
17253 check_arith; mp_get_x_next(mp);
17255 if ( mp->cur_cmd>=min_primary_command ) {
17256 if ( mp->cur_cmd<numeric_token ) { /* in particular, |cur_cmd<>plus_or_minus| */
17257 p=mp_stash_cur_exp(mp); mp_scan_primary(mp);
17258 if ( (abs(num)>=abs(denom))||(mp->cur_type<mp_color_type) ) {
17259 mp_do_binary(mp, p,times);
17261 mp_frac_mult(mp, num,denom);
17262 mp_free_node(mp, p,value_node_size);
17269 @ @<Protest division...@>=
17271 print_err("Division by zero");
17272 @.Division by zero@>
17273 help1("I'll pretend that you meant to divide by 1."); mp_error(mp);
17276 @ @<Scan a binary operation with `\&{of}' between its operands@>=
17278 c=mp->cur_mod; mp_get_x_next(mp); mp_scan_expression(mp);
17279 if ( mp->cur_cmd!=of_token ) {
17280 mp_missing_err(mp, "of"); mp_print(mp, " for ");
17281 mp_print_cmd_mod(mp, primary_binary,c);
17283 help1("I've got the first argument; will look now for the other.");
17286 p=mp_stash_cur_exp(mp); mp_get_x_next(mp); mp_scan_primary(mp);
17287 mp_do_binary(mp, p,c); goto DONE;
17290 @ @<Convert a suffix to a string@>=
17292 mp_get_x_next(mp); mp_scan_suffix(mp);
17293 mp->old_setting=mp->selector; mp->selector=new_string;
17294 mp_show_token_list(mp, mp->cur_exp,null,100000,0);
17295 mp_flush_token_list(mp, mp->cur_exp);
17296 mp->cur_exp=mp_make_string(mp); mp->selector=mp->old_setting;
17297 mp->cur_type=mp_string_type;
17301 @ If an internal quantity appears all by itself on the left of an
17302 assignment, we return a token list of length one, containing the address
17303 of the internal quantity plus |hash_end|. (This accords with the conventions
17304 of the save stack, as described earlier.)
17306 @<Scan an internal...@>=
17309 if ( my_var_flag==assignment ) {
17311 if ( mp->cur_cmd==assignment ) {
17312 mp->cur_exp=mp_get_avail(mp);
17313 info(mp->cur_exp)=q+hash_end; mp->cur_type=mp_token_list;
17318 mp->cur_type=mp_known; mp->cur_exp=mp->internal[q];
17321 @ The most difficult part of |scan_primary| has been saved for last, since
17322 it was necessary to build up some confidence first. We can now face the task
17323 of scanning a variable.
17325 As we scan a variable, we build a token list containing the relevant
17326 names and subscript values, simultaneously following along in the
17327 ``collective'' structure to see if we are actually dealing with a macro
17328 instead of a value.
17330 The local variables |pre_head| and |post_head| will point to the beginning
17331 of the prefix and suffix lists; |tail| will point to the end of the list
17332 that is currently growing.
17334 Another local variable, |tt|, contains partial information about the
17335 declared type of the variable-so-far. If |tt>=mp_unsuffixed_macro|, the
17336 relation |tt=type(q)| will always hold. If |tt=undefined|, the routine
17337 doesn't bother to update its information about type. And if
17338 |undefined<tt<mp_unsuffixed_macro|, the precise value of |tt| isn't critical.
17340 @ @<Other local variables for |scan_primary|@>=
17341 pointer pre_head,post_head,tail;
17342 /* prefix and suffix list variables */
17343 small_number tt; /* approximation to the type of the variable-so-far */
17344 pointer t; /* a token */
17345 pointer macro_ref = 0; /* reference count for a suffixed macro */
17347 @ @<Scan a variable primary...@>=
17349 fast_get_avail(pre_head); tail=pre_head; post_head=null; tt=mp_vacuous;
17351 t=mp_cur_tok(mp); link(tail)=t;
17352 if ( tt!=undefined ) {
17353 @<Find the approximate type |tt| and corresponding~|q|@>;
17354 if ( tt>=mp_unsuffixed_macro ) {
17355 @<Either begin an unsuffixed macro call or
17356 prepare for a suffixed one@>;
17359 mp_get_x_next(mp); tail=t;
17360 if ( mp->cur_cmd==left_bracket ) {
17361 @<Scan for a subscript; replace |cur_cmd| by |numeric_token| if found@>;
17363 if ( mp->cur_cmd>max_suffix_token ) break;
17364 if ( mp->cur_cmd<min_suffix_token ) break;
17365 } /* now |cur_cmd| is |internal_quantity|, |tag_token|, or |numeric_token| */
17366 @<Handle unusual cases that masquerade as variables, and |goto restart|
17367 or |goto done| if appropriate;
17368 otherwise make a copy of the variable and |goto done|@>;
17371 @ @<Either begin an unsuffixed macro call or...@>=
17374 if ( tt>mp_unsuffixed_macro ) { /* |tt=mp_suffixed_macro| */
17375 post_head=mp_get_avail(mp); tail=post_head; link(tail)=t;
17376 tt=undefined; macro_ref=value(q); add_mac_ref(macro_ref);
17378 @<Set up unsuffixed macro call and |goto restart|@>;
17382 @ @<Scan for a subscript; replace |cur_cmd| by |numeric_token| if found@>=
17384 mp_get_x_next(mp); mp_scan_expression(mp);
17385 if ( mp->cur_cmd!=right_bracket ) {
17386 @<Put the left bracket and the expression back to be rescanned@>;
17388 if ( mp->cur_type!=mp_known ) mp_bad_subscript(mp);
17389 mp->cur_cmd=numeric_token; mp->cur_mod=mp->cur_exp; mp->cur_sym=0;
17393 @ The left bracket that we thought was introducing a subscript might have
17394 actually been the left bracket in a mediation construction like `\.{x[a,b]}'.
17395 So we don't issue an error message at this point; but we do want to back up
17396 so as to avoid any embarrassment about our incorrect assumption.
17398 @<Put the left bracket and the expression back to be rescanned@>=
17400 mp_back_input(mp); /* that was the token following the current expression */
17401 mp_back_expr(mp); mp->cur_cmd=left_bracket;
17402 mp->cur_mod=0; mp->cur_sym=frozen_left_bracket;
17405 @ Here's a routine that puts the current expression back to be read again.
17407 @c void mp_back_expr (MP mp) {
17408 pointer p; /* capsule token */
17409 p=mp_stash_cur_exp(mp); link(p)=null; back_list(p);
17412 @ Unknown subscripts lead to the following error message.
17414 @c void mp_bad_subscript (MP mp) {
17415 exp_err("Improper subscript has been replaced by zero");
17416 @.Improper subscript...@>
17417 help3("A bracketed subscript must have a known numeric value;")
17418 ("unfortunately, what I found was the value that appears just")
17419 ("above this error message. So I'll try a zero subscript.");
17420 mp_flush_error(mp, 0);
17423 @ Every time we call |get_x_next|, there's a chance that the variable we've
17424 been looking at will disappear. Thus, we cannot safely keep |q| pointing
17425 into the variable structure; we need to start searching from the root each time.
17427 @<Find the approximate type |tt| and corresponding~|q|@>=
17430 p=link(pre_head); q=info(p); tt=undefined;
17431 if ( eq_type(q) % outer_tag==tag_token ) {
17433 if ( q==null ) goto DONE2;
17437 tt=type(q); goto DONE2;
17439 if ( type(q)!=mp_structured ) goto DONE2;
17440 q=link(attr_head(q)); /* the |collective_subscript| attribute */
17441 if ( p>=mp->hi_mem_min ) { /* it's not a subscript */
17442 do { q=link(q); } while (! (attr_loc(q)>=info(p)));
17443 if ( attr_loc(q)>info(p) ) goto DONE2;
17451 @ How do things stand now? Well, we have scanned an entire variable name,
17452 including possible subscripts and/or attributes; |cur_cmd|, |cur_mod|, and
17453 |cur_sym| represent the token that follows. If |post_head=null|, a
17454 token list for this variable name starts at |link(pre_head)|, with all
17455 subscripts evaluated. But if |post_head<>null|, the variable turned out
17456 to be a suffixed macro; |pre_head| is the head of the prefix list, while
17457 |post_head| is the head of a token list containing both `\.{\AT!}' and
17460 Our immediate problem is to see if this variable still exists. (Variable
17461 structures can change drastically whenever we call |get_x_next|; users
17462 aren't supposed to do this, but the fact that it is possible means that
17463 we must be cautious.)
17465 The following procedure prints an error message when a variable
17466 unexpectedly disappears. Its help message isn't quite right for
17467 our present purposes, but we'll be able to fix that up.
17470 void mp_obliterated (MP mp,pointer q) {
17471 print_err("Variable "); mp_show_token_list(mp, q,null,1000,0);
17472 mp_print(mp, " has been obliterated");
17473 @.Variable...obliterated@>
17474 help5("It seems you did a nasty thing---probably by accident,")
17475 ("but nevertheless you nearly hornswoggled me...")
17476 ("While I was evaluating the right-hand side of this")
17477 ("command, something happened, and the left-hand side")
17478 ("is no longer a variable! So I won't change anything.");
17481 @ If the variable does exist, we also need to check
17482 for a few other special cases before deciding that a plain old ordinary
17483 variable has, indeed, been scanned.
17485 @<Handle unusual cases that masquerade as variables...@>=
17486 if ( post_head!=null ) {
17487 @<Set up suffixed macro call and |goto restart|@>;
17489 q=link(pre_head); free_avail(pre_head);
17490 if ( mp->cur_cmd==my_var_flag ) {
17491 mp->cur_type=mp_token_list; mp->cur_exp=q; goto DONE;
17493 p=mp_find_variable(mp, q);
17495 mp_make_exp_copy(mp, p);
17497 mp_obliterated(mp, q);
17498 mp->help_line[2]="While I was evaluating the suffix of this variable,";
17499 mp->help_line[1]="something was redefined, and it's no longer a variable!";
17500 mp->help_line[0]="In order to get back on my feet, I've inserted `0' instead.";
17501 mp_put_get_flush_error(mp, 0);
17503 mp_flush_node_list(mp, q);
17506 @ The only complication associated with macro calling is that the prefix
17507 and ``at'' parameters must be packaged in an appropriate list of lists.
17509 @<Set up unsuffixed macro call and |goto restart|@>=
17511 p=mp_get_avail(mp); info(pre_head)=link(pre_head); link(pre_head)=p;
17512 info(p)=t; mp_macro_call(mp, value(q),pre_head,null);
17517 @ If the ``variable'' that turned out to be a suffixed macro no longer exists,
17518 we don't care, because we have reserved a pointer (|macro_ref|) to its
17521 @<Set up suffixed macro call and |goto restart|@>=
17523 mp_back_input(mp); p=mp_get_avail(mp); q=link(post_head);
17524 info(pre_head)=link(pre_head); link(pre_head)=post_head;
17525 info(post_head)=q; link(post_head)=p; info(p)=link(q); link(q)=null;
17526 mp_macro_call(mp, macro_ref,pre_head,null); decr(ref_count(macro_ref));
17527 mp_get_x_next(mp); goto RESTART;
17530 @ Our remaining job is simply to make a copy of the value that has been
17531 found. Some cases are harder than others, but complexity arises solely
17532 because of the multiplicity of possible cases.
17534 @<Declare the procedure called |make_exp_copy|@>=
17535 @<Declare subroutines needed by |make_exp_copy|@>;
17536 void mp_make_exp_copy (MP mp,pointer p) {
17537 pointer q,r,t; /* registers for list manipulation */
17539 mp->cur_type=type(p);
17540 switch (mp->cur_type) {
17541 case mp_vacuous: case mp_boolean_type: case mp_known:
17542 mp->cur_exp=value(p); break;
17543 case unknown_types:
17544 mp->cur_exp=mp_new_ring_entry(mp, p);
17546 case mp_string_type:
17547 mp->cur_exp=value(p); add_str_ref(mp->cur_exp);
17549 case mp_picture_type:
17550 mp->cur_exp=value(p);add_edge_ref(mp->cur_exp);
17553 mp->cur_exp=copy_pen(value(p));
17556 mp->cur_exp=mp_copy_path(mp, value(p));
17558 case mp_transform_type: case mp_color_type:
17559 case mp_cmykcolor_type: case mp_pair_type:
17560 @<Copy the big node |p|@>;
17562 case mp_dependent: case mp_proto_dependent:
17563 mp_encapsulate(mp, mp_copy_dep_list(mp, dep_list(p)));
17565 case mp_numeric_type:
17566 new_indep(p); goto RESTART;
17568 case mp_independent:
17569 q=mp_single_dependency(mp, p);
17570 if ( q==mp->dep_final ){
17571 mp->cur_type=mp_known; mp->cur_exp=0; mp_free_node(mp, q,value_node_size);
17573 mp->cur_type=mp_dependent; mp_encapsulate(mp, q);
17577 mp_confusion(mp, "copy");
17578 @:this can't happen copy}{\quad copy@>
17583 @ The |encapsulate| subroutine assumes that |dep_final| is the
17584 tail of dependency list~|p|.
17586 @<Declare subroutines needed by |make_exp_copy|@>=
17587 void mp_encapsulate (MP mp,pointer p) {
17588 mp->cur_exp=mp_get_node(mp, value_node_size); type(mp->cur_exp)=mp->cur_type;
17589 name_type(mp->cur_exp)=mp_capsule; mp_new_dep(mp, mp->cur_exp,p);
17592 @ The most tedious case arises when the user refers to a
17593 \&{pair}, \&{color}, or \&{transform} variable; we must copy several fields,
17594 each of which can be |independent|, |dependent|, |mp_proto_dependent|,
17597 @<Copy the big node |p|@>=
17599 if ( value(p)==null )
17600 mp_init_big_node(mp, p);
17601 t=mp_get_node(mp, value_node_size); name_type(t)=mp_capsule; type(t)=mp->cur_type;
17602 mp_init_big_node(mp, t);
17603 q=value(p)+mp->big_node_size[mp->cur_type];
17604 r=value(t)+mp->big_node_size[mp->cur_type];
17606 q=q-2; r=r-2; mp_install(mp, r,q);
17607 } while (q!=value(p));
17611 @ The |install| procedure copies a numeric field~|q| into field~|r| of
17612 a big node that will be part of a capsule.
17614 @<Declare subroutines needed by |make_exp_copy|@>=
17615 void mp_install (MP mp,pointer r, pointer q) {
17616 pointer p; /* temporary register */
17617 if ( type(q)==mp_known ){
17618 value(r)=value(q); type(r)=mp_known;
17619 } else if ( type(q)==mp_independent ) {
17620 p=mp_single_dependency(mp, q);
17621 if ( p==mp->dep_final ) {
17622 type(r)=mp_known; value(r)=0; mp_free_node(mp, p,value_node_size);
17624 type(r)=mp_dependent; mp_new_dep(mp, r,p);
17627 type(r)=type(q); mp_new_dep(mp, r,mp_copy_dep_list(mp, dep_list(q)));
17631 @ Expressions of the form `\.{a[b,c]}' are converted into
17632 `\.{b+a*(c-b)}', without checking the types of \.b~or~\.c,
17633 provided that \.a is numeric.
17635 @<Scan a mediation...@>=
17637 p=mp_stash_cur_exp(mp); mp_get_x_next(mp); mp_scan_expression(mp);
17638 if ( mp->cur_cmd!=comma ) {
17639 @<Put the left bracket and the expression back...@>;
17640 mp_unstash_cur_exp(mp, p);
17642 q=mp_stash_cur_exp(mp); mp_get_x_next(mp); mp_scan_expression(mp);
17643 if ( mp->cur_cmd!=right_bracket ) {
17644 mp_missing_err(mp, "]");
17646 help3("I've scanned an expression of the form `a[b,c',")
17647 ("so a right bracket should have come next.")
17648 ("I shall pretend that one was there.");
17651 r=mp_stash_cur_exp(mp); mp_make_exp_copy(mp, q);
17652 mp_do_binary(mp, r,minus); mp_do_binary(mp, p,times);
17653 mp_do_binary(mp, q,plus); mp_get_x_next(mp);
17657 @ Here is a comparatively simple routine that is used to scan the
17658 \&{suffix} parameters of a macro.
17660 @<Declare the basic parsing subroutines@>=
17661 void mp_scan_suffix (MP mp) {
17662 pointer h,t; /* head and tail of the list being built */
17663 pointer p; /* temporary register */
17664 h=mp_get_avail(mp); t=h;
17666 if ( mp->cur_cmd==left_bracket ) {
17667 @<Scan a bracketed subscript and set |cur_cmd:=numeric_token|@>;
17669 if ( mp->cur_cmd==numeric_token ) {
17670 p=mp_new_num_tok(mp, mp->cur_mod);
17671 } else if ((mp->cur_cmd==tag_token)||(mp->cur_cmd==internal_quantity) ) {
17672 p=mp_get_avail(mp); info(p)=mp->cur_sym;
17676 link(t)=p; t=p; mp_get_x_next(mp);
17678 mp->cur_exp=link(h); free_avail(h); mp->cur_type=mp_token_list;
17681 @ @<Scan a bracketed subscript and set |cur_cmd:=numeric_token|@>=
17683 mp_get_x_next(mp); mp_scan_expression(mp);
17684 if ( mp->cur_type!=mp_known ) mp_bad_subscript(mp);
17685 if ( mp->cur_cmd!=right_bracket ) {
17686 mp_missing_err(mp, "]");
17688 help3("I've seen a `[' and a subscript value, in a suffix,")
17689 ("so a right bracket should have come next.")
17690 ("I shall pretend that one was there.");
17693 mp->cur_cmd=numeric_token; mp->cur_mod=mp->cur_exp;
17696 @* \[38] Parsing secondary and higher expressions.
17697 After the intricacies of |scan_primary|\kern-1pt,
17698 the |scan_secondary| routine is
17699 refreshingly simple. It's not trivial, but the operations are relatively
17700 straightforward; the main difficulty is, again, that expressions and data
17701 structures might change drastically every time we call |get_x_next|, so a
17702 cautious approach is mandatory. For example, a macro defined by
17703 \&{primarydef} might have disappeared by the time its second argument has
17704 been scanned; we solve this by increasing the reference count of its token
17705 list, so that the macro can be called even after it has been clobbered.
17707 @<Declare the basic parsing subroutines@>=
17708 void mp_scan_secondary (MP mp) {
17709 pointer p; /* for list manipulation */
17710 halfword c,d; /* operation codes or modifiers */
17711 pointer mac_name; /* token defined with \&{primarydef} */
17713 if ((mp->cur_cmd<min_primary_command)||
17714 (mp->cur_cmd>max_primary_command) )
17715 mp_bad_exp(mp, "A secondary");
17716 @.A secondary expression...@>
17717 mp_scan_primary(mp);
17719 if ( mp->cur_cmd<=max_secondary_command )
17720 if ( mp->cur_cmd>=min_secondary_command ) {
17721 p=mp_stash_cur_exp(mp); c=mp->cur_mod; d=mp->cur_cmd;
17722 if ( d==secondary_primary_macro ) {
17723 mac_name=mp->cur_sym; add_mac_ref(c);
17725 mp_get_x_next(mp); mp_scan_primary(mp);
17726 if ( d!=secondary_primary_macro ) {
17727 mp_do_binary(mp, p,c);
17729 mp_back_input(mp); mp_binary_mac(mp, p,c,mac_name);
17730 decr(ref_count(c)); mp_get_x_next(mp);
17737 @ The following procedure calls a macro that has two parameters,
17740 @c void mp_binary_mac (MP mp,pointer p, pointer c, pointer n) {
17741 pointer q,r; /* nodes in the parameter list */
17742 q=mp_get_avail(mp); r=mp_get_avail(mp); link(q)=r;
17743 info(q)=p; info(r)=mp_stash_cur_exp(mp);
17744 mp_macro_call(mp, c,q,n);
17747 @ The next procedure, |scan_tertiary|, is pretty much the same deal.
17749 @<Declare the basic parsing subroutines@>=
17750 void mp_scan_tertiary (MP mp) {
17751 pointer p; /* for list manipulation */
17752 halfword c,d; /* operation codes or modifiers */
17753 pointer mac_name; /* token defined with \&{secondarydef} */
17755 if ((mp->cur_cmd<min_primary_command)||
17756 (mp->cur_cmd>max_primary_command) )
17757 mp_bad_exp(mp, "A tertiary");
17758 @.A tertiary expression...@>
17759 mp_scan_secondary(mp);
17761 if ( mp->cur_cmd<=max_tertiary_command ) {
17762 if ( mp->cur_cmd>=min_tertiary_command ) {
17763 p=mp_stash_cur_exp(mp); c=mp->cur_mod; d=mp->cur_cmd;
17764 if ( d==tertiary_secondary_macro ) {
17765 mac_name=mp->cur_sym; add_mac_ref(c);
17767 mp_get_x_next(mp); mp_scan_secondary(mp);
17768 if ( d!=tertiary_secondary_macro ) {
17769 mp_do_binary(mp, p,c);
17771 mp_back_input(mp); mp_binary_mac(mp, p,c,mac_name);
17772 decr(ref_count(c)); mp_get_x_next(mp);
17780 @ Finally we reach the deepest level in our quartet of parsing routines.
17781 This one is much like the others; but it has an extra complication from
17782 paths, which materialize here.
17784 @d continue_path 25 /* a label inside of |scan_expression| */
17785 @d finish_path 26 /* another */
17787 @<Declare the basic parsing subroutines@>=
17788 void mp_scan_expression (MP mp) {
17789 pointer p,q,r,pp,qq; /* for list manipulation */
17790 halfword c,d; /* operation codes or modifiers */
17791 int my_var_flag; /* initial value of |var_flag| */
17792 pointer mac_name; /* token defined with \&{tertiarydef} */
17793 boolean cycle_hit; /* did a path expression just end with `\&{cycle}'? */
17794 scaled x,y; /* explicit coordinates or tension at a path join */
17795 int t; /* knot type following a path join */
17797 my_var_flag=mp->var_flag; mac_name=null;
17799 if ((mp->cur_cmd<min_primary_command)||
17800 (mp->cur_cmd>max_primary_command) )
17801 mp_bad_exp(mp, "An");
17802 @.An expression...@>
17803 mp_scan_tertiary(mp);
17805 if ( mp->cur_cmd<=max_expression_command )
17806 if ( mp->cur_cmd>=min_expression_command ) {
17807 if ( (mp->cur_cmd!=equals)||(my_var_flag!=assignment) ) {
17808 p=mp_stash_cur_exp(mp); c=mp->cur_mod; d=mp->cur_cmd;
17809 if ( d==expression_tertiary_macro ) {
17810 mac_name=mp->cur_sym; add_mac_ref(c);
17812 if ( (d<ampersand)||((d==ampersand)&&
17813 ((type(p)==mp_pair_type)||(type(p)==mp_path_type))) ) {
17814 @<Scan a path construction operation;
17815 but |return| if |p| has the wrong type@>;
17817 mp_get_x_next(mp); mp_scan_tertiary(mp);
17818 if ( d!=expression_tertiary_macro ) {
17819 mp_do_binary(mp, p,c);
17821 mp_back_input(mp); mp_binary_mac(mp, p,c,mac_name);
17822 decr(ref_count(c)); mp_get_x_next(mp);
17831 @ The reader should review the data structure conventions for paths before
17832 hoping to understand the next part of this code.
17834 @<Scan a path construction operation...@>=
17837 @<Convert the left operand, |p|, into a partial path ending at~|q|;
17838 but |return| if |p| doesn't have a suitable type@>;
17840 @<Determine the path join parameters;
17841 but |goto finish_path| if there's only a direction specifier@>;
17842 if ( mp->cur_cmd==cycle ) {
17843 @<Get ready to close a cycle@>;
17845 mp_scan_tertiary(mp);
17846 @<Convert the right operand, |cur_exp|,
17847 into a partial path from |pp| to~|qq|@>;
17849 @<Join the partial paths and reset |p| and |q| to the head and tail
17851 if ( mp->cur_cmd>=min_expression_command )
17852 if ( mp->cur_cmd<=ampersand ) if ( ! cycle_hit ) goto CONTINUE_PATH;
17854 @<Choose control points for the path and put the result into |cur_exp|@>;
17857 @ @<Convert the left operand, |p|, into a partial path ending at~|q|...@>=
17859 mp_unstash_cur_exp(mp, p);
17860 if ( mp->cur_type==mp_pair_type ) p=mp_new_knot(mp);
17861 else if ( mp->cur_type==mp_path_type ) p=mp->cur_exp;
17864 while ( link(q)!=p ) q=link(q);
17865 if ( left_type(p)!=endpoint ) { /* open up a cycle */
17866 r=mp_copy_knot(mp, p); link(q)=r; q=r;
17868 left_type(p)=open; right_type(q)=open;
17871 @ A pair of numeric values is changed into a knot node for a one-point path
17872 when \MP\ discovers that the pair is part of a path.
17874 @c@<Declare the procedure called |known_pair|@>;
17875 pointer mp_new_knot (MP mp) { /* convert a pair to a knot with two endpoints */
17876 pointer q; /* the new node */
17877 q=mp_get_node(mp, knot_node_size); left_type(q)=endpoint;
17878 right_type(q)=endpoint; originator(q)=metapost_user; link(q)=q;
17879 mp_known_pair(mp); x_coord(q)=mp->cur_x; y_coord(q)=mp->cur_y;
17883 @ The |known_pair| subroutine sets |cur_x| and |cur_y| to the components
17884 of the current expression, assuming that the current expression is a
17885 pair of known numerics. Unknown components are zeroed, and the
17886 current expression is flushed.
17888 @<Declare the procedure called |known_pair|@>=
17889 void mp_known_pair (MP mp) {
17890 pointer p; /* the pair node */
17891 if ( mp->cur_type!=mp_pair_type ) {
17892 exp_err("Undefined coordinates have been replaced by (0,0)");
17893 @.Undefined coordinates...@>
17894 help5("I need x and y numbers for this part of the path.")
17895 ("The value I found (see above) was no good;")
17896 ("so I'll try to keep going by using zero instead.")
17897 ("(Chapter 27 of The METAFONTbook explains that")
17898 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
17899 ("you might want to type `I ??" "?' now.)");
17900 mp_put_get_flush_error(mp, 0); mp->cur_x=0; mp->cur_y=0;
17902 p=value(mp->cur_exp);
17903 @<Make sure that both |x| and |y| parts of |p| are known;
17904 copy them into |cur_x| and |cur_y|@>;
17905 mp_flush_cur_exp(mp, 0);
17909 @ @<Make sure that both |x| and |y| parts of |p| are known...@>=
17910 if ( type(x_part_loc(p))==mp_known ) {
17911 mp->cur_x=value(x_part_loc(p));
17913 mp_disp_err(mp, x_part_loc(p),
17914 "Undefined x coordinate has been replaced by 0");
17915 @.Undefined coordinates...@>
17916 help5("I need a `known' x value for this part of the path.")
17917 ("The value I found (see above) was no good;")
17918 ("so I'll try to keep going by using zero instead.")
17919 ("(Chapter 27 of The METAFONTbook explains that")
17920 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
17921 ("you might want to type `I ??" "?' now.)");
17922 mp_put_get_error(mp); mp_recycle_value(mp, x_part_loc(p)); mp->cur_x=0;
17924 if ( type(y_part_loc(p))==mp_known ) {
17925 mp->cur_y=value(y_part_loc(p));
17927 mp_disp_err(mp, y_part_loc(p),
17928 "Undefined y coordinate has been replaced by 0");
17929 help5("I need a `known' y value for this part of the path.")
17930 ("The value I found (see above) was no good;")
17931 ("so I'll try to keep going by using zero instead.")
17932 ("(Chapter 27 of The METAFONTbook explains that")
17933 ("you might want to type `I ??" "?' now.)");
17934 mp_put_get_error(mp); mp_recycle_value(mp, y_part_loc(p)); mp->cur_y=0;
17937 @ At this point |cur_cmd| is either |ampersand|, |left_brace|, or |path_join|.
17939 @<Determine the path join parameters...@>=
17940 if ( mp->cur_cmd==left_brace ) {
17941 @<Put the pre-join direction information into node |q|@>;
17944 if ( d==path_join ) {
17945 @<Determine the tension and/or control points@>;
17946 } else if ( d!=ampersand ) {
17950 if ( mp->cur_cmd==left_brace ) {
17951 @<Put the post-join direction information into |x| and |t|@>;
17952 } else if ( right_type(q)!=explicit ) {
17956 @ The |scan_direction| subroutine looks at the directional information
17957 that is enclosed in braces, and also scans ahead to the following character.
17958 A type code is returned, either |open| (if the direction was $(0,0)$),
17959 or |curl| (if the direction was a curl of known value |cur_exp|), or
17960 |given| (if the direction is given by the |angle| value that now
17961 appears in |cur_exp|).
17963 There's nothing difficult about this subroutine, but the program is rather
17964 lengthy because a variety of potential errors need to be nipped in the bud.
17966 @c small_number mp_scan_direction (MP mp) {
17967 int t; /* the type of information found */
17968 scaled x; /* an |x| coordinate */
17970 if ( mp->cur_cmd==curl_command ) {
17971 @<Scan a curl specification@>;
17973 @<Scan a given direction@>;
17975 if ( mp->cur_cmd!=right_brace ) {
17976 mp_missing_err(mp, "}");
17977 @.Missing `\char`\}'@>
17978 help3("I've scanned a direction spec for part of a path,")
17979 ("so a right brace should have come next.")
17980 ("I shall pretend that one was there.");
17987 @ @<Scan a curl specification@>=
17988 { mp_get_x_next(mp); mp_scan_expression(mp);
17989 if ( (mp->cur_type!=mp_known)||(mp->cur_exp<0) ){
17990 exp_err("Improper curl has been replaced by 1");
17992 help1("A curl must be a known, nonnegative number.");
17993 mp_put_get_flush_error(mp, unity);
17998 @ @<Scan a given direction@>=
17999 { mp_scan_expression(mp);
18000 if ( mp->cur_type>mp_pair_type ) {
18001 @<Get given directions separated by commas@>;
18005 if ( (mp->cur_x==0)&&(mp->cur_y==0) ) t=open;
18006 else { t=given; mp->cur_exp=mp_n_arg(mp, mp->cur_x,mp->cur_y);}
18009 @ @<Get given directions separated by commas@>=
18011 if ( mp->cur_type!=mp_known ) {
18012 exp_err("Undefined x coordinate has been replaced by 0");
18013 @.Undefined coordinates...@>
18014 help5("I need a `known' x value for this part of the path.")
18015 ("The value I found (see above) was no good;")
18016 ("so I'll try to keep going by using zero instead.")
18017 ("(Chapter 27 of The METAFONTbook explains that")
18018 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
18019 ("you might want to type `I ??" "?' now.)");
18020 mp_put_get_flush_error(mp, 0);
18023 if ( mp->cur_cmd!=comma ) {
18024 mp_missing_err(mp, ",");
18026 help2("I've got the x coordinate of a path direction;")
18027 ("will look for the y coordinate next.");
18030 mp_get_x_next(mp); mp_scan_expression(mp);
18031 if ( mp->cur_type!=mp_known ) {
18032 exp_err("Undefined y coordinate has been replaced by 0");
18033 help5("I need a `known' y value for this part of the path.")
18034 ("The value I found (see above) was no good;")
18035 ("so I'll try to keep going by using zero instead.")
18036 ("(Chapter 27 of The METAFONTbook explains that")
18037 ("you might want to type `I ??" "?' now.)");
18038 mp_put_get_flush_error(mp, 0);
18040 mp->cur_y=mp->cur_exp; mp->cur_x=x;
18043 @ At this point |right_type(q)| is usually |open|, but it may have been
18044 set to some other value by a previous splicing operation. We must maintain
18045 the value of |right_type(q)| in unusual cases such as
18046 `\.{..z1\{z2\}\&\{z3\}z1\{0,0\}..}'.
18048 @<Put the pre-join...@>=
18050 t=mp_scan_direction(mp);
18052 right_type(q)=t; right_given(q)=mp->cur_exp;
18053 if ( left_type(q)==open ) {
18054 left_type(q)=t; left_given(q)=mp->cur_exp;
18055 } /* note that |left_given(q)=left_curl(q)| */
18059 @ Since |left_tension| and |left_y| share the same position in knot nodes,
18060 and since |left_given| is similarly equivalent to |left_x|, we use
18061 |x| and |y| to hold the given direction and tension information when
18062 there are no explicit control points.
18064 @<Put the post-join...@>=
18066 t=mp_scan_direction(mp);
18067 if ( right_type(q)!=explicit ) x=mp->cur_exp;
18068 else t=explicit; /* the direction information is superfluous */
18071 @ @<Determine the tension and/or...@>=
18074 if ( mp->cur_cmd==tension ) {
18075 @<Set explicit tensions@>;
18076 } else if ( mp->cur_cmd==controls ) {
18077 @<Set explicit control points@>;
18079 right_tension(q)=unity; y=unity; mp_back_input(mp); /* default tension */
18082 if ( mp->cur_cmd!=path_join ) {
18083 mp_missing_err(mp, "..");
18085 help1("A path join command should end with two dots.");
18092 @ @<Set explicit tensions@>=
18094 mp_get_x_next(mp); y=mp->cur_cmd;
18095 if ( mp->cur_cmd==at_least ) mp_get_x_next(mp);
18096 mp_scan_primary(mp);
18097 @<Make sure that the current expression is a valid tension setting@>;
18098 if ( y==at_least ) negate(mp->cur_exp);
18099 right_tension(q)=mp->cur_exp;
18100 if ( mp->cur_cmd==and_command ) {
18101 mp_get_x_next(mp); y=mp->cur_cmd;
18102 if ( mp->cur_cmd==at_least ) mp_get_x_next(mp);
18103 mp_scan_primary(mp);
18104 @<Make sure that the current expression is a valid tension setting@>;
18105 if ( y==at_least ) negate(mp->cur_exp);
18110 @ @d min_tension three_quarter_unit
18112 @<Make sure that the current expression is a valid tension setting@>=
18113 if ( (mp->cur_type!=mp_known)||(mp->cur_exp<min_tension) ) {
18114 exp_err("Improper tension has been set to 1");
18115 @.Improper tension@>
18116 help1("The expression above should have been a number >=3/4.");
18117 mp_put_get_flush_error(mp, unity);
18120 @ @<Set explicit control points@>=
18122 right_type(q)=explicit; t=explicit; mp_get_x_next(mp); mp_scan_primary(mp);
18123 mp_known_pair(mp); right_x(q)=mp->cur_x; right_y(q)=mp->cur_y;
18124 if ( mp->cur_cmd!=and_command ) {
18125 x=right_x(q); y=right_y(q);
18127 mp_get_x_next(mp); mp_scan_primary(mp);
18128 mp_known_pair(mp); x=mp->cur_x; y=mp->cur_y;
18132 @ @<Convert the right operand, |cur_exp|, into a partial path...@>=
18134 if ( mp->cur_type!=mp_path_type ) pp=mp_new_knot(mp);
18135 else pp=mp->cur_exp;
18137 while ( link(qq)!=pp ) qq=link(qq);
18138 if ( left_type(pp)!=endpoint ) { /* open up a cycle */
18139 r=mp_copy_knot(mp, pp); link(qq)=r; qq=r;
18141 left_type(pp)=open; right_type(qq)=open;
18144 @ If a person tries to define an entire path by saying `\.{(x,y)\&cycle}',
18145 we silently change the specification to `\.{(x,y)..cycle}', since a cycle
18146 shouldn't have length zero.
18148 @<Get ready to close a cycle@>=
18150 cycle_hit=true; mp_get_x_next(mp); pp=p; qq=p;
18151 if ( d==ampersand ) if ( p==q ) {
18152 d=path_join; right_tension(q)=unity; y=unity;
18156 @ @<Join the partial paths and reset |p| and |q|...@>=
18158 if ( d==ampersand ) {
18159 if ( (x_coord(q)!=x_coord(pp))||(y_coord(q)!=y_coord(pp)) ) {
18160 print_err("Paths don't touch; `&' will be changed to `..'");
18161 @.Paths don't touch@>
18162 help3("When you join paths `p&q', the ending point of p")
18163 ("must be exactly equal to the starting point of q.")
18164 ("So I'm going to pretend that you said `p..q' instead.");
18165 mp_put_get_error(mp); d=path_join; right_tension(q)=unity; y=unity;
18168 @<Plug an opening in |right_type(pp)|, if possible@>;
18169 if ( d==ampersand ) {
18170 @<Splice independent paths together@>;
18172 @<Plug an opening in |right_type(q)|, if possible@>;
18173 link(q)=pp; left_y(pp)=y;
18174 if ( t!=open ) { left_x(pp)=x; left_type(pp)=t; };
18179 @ @<Plug an opening in |right_type(q)|...@>=
18180 if ( right_type(q)==open ) {
18181 if ( (left_type(q)==curl)||(left_type(q)==given) ) {
18182 right_type(q)=left_type(q); right_given(q)=left_given(q);
18186 @ @<Plug an opening in |right_type(pp)|...@>=
18187 if ( right_type(pp)==open ) {
18188 if ( (t==curl)||(t==given) ) {
18189 right_type(pp)=t; right_given(pp)=x;
18193 @ @<Splice independent paths together@>=
18195 if ( left_type(q)==open ) if ( right_type(q)==open ) {
18196 left_type(q)=curl; left_curl(q)=unity;
18198 if ( right_type(pp)==open ) if ( t==open ) {
18199 right_type(pp)=curl; right_curl(pp)=unity;
18201 right_type(q)=right_type(pp); link(q)=link(pp);
18202 right_x(q)=right_x(pp); right_y(q)=right_y(pp);
18203 mp_free_node(mp, pp,knot_node_size);
18204 if ( qq==pp ) qq=q;
18207 @ @<Choose control points for the path...@>=
18209 if ( d==ampersand ) p=q;
18211 left_type(p)=endpoint;
18212 if ( right_type(p)==open ) {
18213 right_type(p)=curl; right_curl(p)=unity;
18215 right_type(q)=endpoint;
18216 if ( left_type(q)==open ) {
18217 left_type(q)=curl; left_curl(q)=unity;
18221 mp_make_choices(mp, p);
18222 mp->cur_type=mp_path_type; mp->cur_exp=p
18224 @ Finally, we sometimes need to scan an expression whose value is
18225 supposed to be either |true_code| or |false_code|.
18227 @<Declare the basic parsing subroutines@>=
18228 void mp_get_boolean (MP mp) {
18229 mp_get_x_next(mp); mp_scan_expression(mp);
18230 if ( mp->cur_type!=mp_boolean_type ) {
18231 exp_err("Undefined condition will be treated as `false'");
18232 @.Undefined condition...@>
18233 help2("The expression shown above should have had a definite")
18234 ("true-or-false value. I'm changing it to `false'.");
18235 mp_put_get_flush_error(mp, false_code); mp->cur_type=mp_boolean_type;
18239 @* \[39] Doing the operations.
18240 The purpose of parsing is primarily to permit people to avoid piles of
18241 parentheses. But the real work is done after the structure of an expression
18242 has been recognized; that's when new expressions are generated. We
18243 turn now to the guts of \MP, which handles individual operators that
18244 have come through the parsing mechanism.
18246 We'll start with the easy ones that take no operands, then work our way
18247 up to operators with one and ultimately two arguments. In other words,
18248 we will write the three procedures |do_nullary|, |do_unary|, and |do_binary|
18249 that are invoked periodically by the expression scanners.
18251 First let's make sure that all of the primitive operators are in the
18252 hash table. Although |scan_primary| and its relatives made use of the
18253 \\{cmd} code for these operators, the \\{do} routines base everything
18254 on the \\{mod} code. For example, |do_binary| doesn't care whether the
18255 operation it performs is a |primary_binary| or |secondary_binary|, etc.
18258 mp_primitive(mp, "true",nullary,true_code);
18259 @:true_}{\&{true} primitive@>
18260 mp_primitive(mp, "false",nullary,false_code);
18261 @:false_}{\&{false} primitive@>
18262 mp_primitive(mp, "nullpicture",nullary,null_picture_code);
18263 @:null_picture_}{\&{nullpicture} primitive@>
18264 mp_primitive(mp, "nullpen",nullary,null_pen_code);
18265 @:null_pen_}{\&{nullpen} primitive@>
18266 mp_primitive(mp, "jobname",nullary,job_name_op);
18267 @:job_name_}{\&{jobname} primitive@>
18268 mp_primitive(mp, "readstring",nullary,read_string_op);
18269 @:read_string_}{\&{readstring} primitive@>
18270 mp_primitive(mp, "pencircle",nullary,pen_circle);
18271 @:pen_circle_}{\&{pencircle} primitive@>
18272 mp_primitive(mp, "normaldeviate",nullary,normal_deviate);
18273 @:normal_deviate_}{\&{normaldeviate} primitive@>
18274 mp_primitive(mp, "readfrom",unary,read_from_op);
18275 @:read_from_}{\&{readfrom} primitive@>
18276 mp_primitive(mp, "closefrom",unary,close_from_op);
18277 @:close_from_}{\&{closefrom} primitive@>
18278 mp_primitive(mp, "odd",unary,odd_op);
18279 @:odd_}{\&{odd} primitive@>
18280 mp_primitive(mp, "known",unary,known_op);
18281 @:known_}{\&{known} primitive@>
18282 mp_primitive(mp, "unknown",unary,unknown_op);
18283 @:unknown_}{\&{unknown} primitive@>
18284 mp_primitive(mp, "not",unary,not_op);
18285 @:not_}{\&{not} primitive@>
18286 mp_primitive(mp, "decimal",unary,decimal);
18287 @:decimal_}{\&{decimal} primitive@>
18288 mp_primitive(mp, "reverse",unary,reverse);
18289 @:reverse_}{\&{reverse} primitive@>
18290 mp_primitive(mp, "makepath",unary,make_path_op);
18291 @:make_path_}{\&{makepath} primitive@>
18292 mp_primitive(mp, "makepen",unary,make_pen_op);
18293 @:make_pen_}{\&{makepen} primitive@>
18294 mp_primitive(mp, "oct",unary,oct_op);
18295 @:oct_}{\&{oct} primitive@>
18296 mp_primitive(mp, "hex",unary,hex_op);
18297 @:hex_}{\&{hex} primitive@>
18298 mp_primitive(mp, "ASCII",unary,ASCII_op);
18299 @:ASCII_}{\&{ASCII} primitive@>
18300 mp_primitive(mp, "char",unary,char_op);
18301 @:char_}{\&{char} primitive@>
18302 mp_primitive(mp, "length",unary,length_op);
18303 @:length_}{\&{length} primitive@>
18304 mp_primitive(mp, "turningnumber",unary,turning_op);
18305 @:turning_number_}{\&{turningnumber} primitive@>
18306 mp_primitive(mp, "xpart",unary,x_part);
18307 @:x_part_}{\&{xpart} primitive@>
18308 mp_primitive(mp, "ypart",unary,y_part);
18309 @:y_part_}{\&{ypart} primitive@>
18310 mp_primitive(mp, "xxpart",unary,xx_part);
18311 @:xx_part_}{\&{xxpart} primitive@>
18312 mp_primitive(mp, "xypart",unary,xy_part);
18313 @:xy_part_}{\&{xypart} primitive@>
18314 mp_primitive(mp, "yxpart",unary,yx_part);
18315 @:yx_part_}{\&{yxpart} primitive@>
18316 mp_primitive(mp, "yypart",unary,yy_part);
18317 @:yy_part_}{\&{yypart} primitive@>
18318 mp_primitive(mp, "redpart",unary,red_part);
18319 @:red_part_}{\&{redpart} primitive@>
18320 mp_primitive(mp, "greenpart",unary,green_part);
18321 @:green_part_}{\&{greenpart} primitive@>
18322 mp_primitive(mp, "bluepart",unary,blue_part);
18323 @:blue_part_}{\&{bluepart} primitive@>
18324 mp_primitive(mp, "cyanpart",unary,cyan_part);
18325 @:cyan_part_}{\&{cyanpart} primitive@>
18326 mp_primitive(mp, "magentapart",unary,magenta_part);
18327 @:magenta_part_}{\&{magentapart} primitive@>
18328 mp_primitive(mp, "yellowpart",unary,yellow_part);
18329 @:yellow_part_}{\&{yellowpart} primitive@>
18330 mp_primitive(mp, "blackpart",unary,black_part);
18331 @:black_part_}{\&{blackpart} primitive@>
18332 mp_primitive(mp, "greypart",unary,grey_part);
18333 @:grey_part_}{\&{greypart} primitive@>
18334 mp_primitive(mp, "colormodel",unary,color_model_part);
18335 @:color_model_part_}{\&{colormodel} primitive@>
18336 mp_primitive(mp, "fontpart",unary,font_part);
18337 @:font_part_}{\&{fontpart} primitive@>
18338 mp_primitive(mp, "textpart",unary,text_part);
18339 @:text_part_}{\&{textpart} primitive@>
18340 mp_primitive(mp, "pathpart",unary,path_part);
18341 @:path_part_}{\&{pathpart} primitive@>
18342 mp_primitive(mp, "penpart",unary,pen_part);
18343 @:pen_part_}{\&{penpart} primitive@>
18344 mp_primitive(mp, "dashpart",unary,dash_part);
18345 @:dash_part_}{\&{dashpart} primitive@>
18346 mp_primitive(mp, "sqrt",unary,sqrt_op);
18347 @:sqrt_}{\&{sqrt} primitive@>
18348 mp_primitive(mp, "mexp",unary,m_exp_op);
18349 @:m_exp_}{\&{mexp} primitive@>
18350 mp_primitive(mp, "mlog",unary,m_log_op);
18351 @:m_log_}{\&{mlog} primitive@>
18352 mp_primitive(mp, "sind",unary,sin_d_op);
18353 @:sin_d_}{\&{sind} primitive@>
18354 mp_primitive(mp, "cosd",unary,cos_d_op);
18355 @:cos_d_}{\&{cosd} primitive@>
18356 mp_primitive(mp, "floor",unary,floor_op);
18357 @:floor_}{\&{floor} primitive@>
18358 mp_primitive(mp, "uniformdeviate",unary,uniform_deviate);
18359 @:uniform_deviate_}{\&{uniformdeviate} primitive@>
18360 mp_primitive(mp, "charexists",unary,char_exists_op);
18361 @:char_exists_}{\&{charexists} primitive@>
18362 mp_primitive(mp, "fontsize",unary,font_size);
18363 @:font_size_}{\&{fontsize} primitive@>
18364 mp_primitive(mp, "llcorner",unary,ll_corner_op);
18365 @:ll_corner_}{\&{llcorner} primitive@>
18366 mp_primitive(mp, "lrcorner",unary,lr_corner_op);
18367 @:lr_corner_}{\&{lrcorner} primitive@>
18368 mp_primitive(mp, "ulcorner",unary,ul_corner_op);
18369 @:ul_corner_}{\&{ulcorner} primitive@>
18370 mp_primitive(mp, "urcorner",unary,ur_corner_op);
18371 @:ur_corner_}{\&{urcorner} primitive@>
18372 mp_primitive(mp, "arclength",unary,arc_length);
18373 @:arc_length_}{\&{arclength} primitive@>
18374 mp_primitive(mp, "angle",unary,angle_op);
18375 @:angle_}{\&{angle} primitive@>
18376 mp_primitive(mp, "cycle",cycle,cycle_op);
18377 @:cycle_}{\&{cycle} primitive@>
18378 mp_primitive(mp, "stroked",unary,stroked_op);
18379 @:stroked_}{\&{stroked} primitive@>
18380 mp_primitive(mp, "filled",unary,filled_op);
18381 @:filled_}{\&{filled} primitive@>
18382 mp_primitive(mp, "textual",unary,textual_op);
18383 @:textual_}{\&{textual} primitive@>
18384 mp_primitive(mp, "clipped",unary,clipped_op);
18385 @:clipped_}{\&{clipped} primitive@>
18386 mp_primitive(mp, "bounded",unary,bounded_op);
18387 @:bounded_}{\&{bounded} primitive@>
18388 mp_primitive(mp, "+",plus_or_minus,plus);
18389 @:+ }{\.{+} primitive@>
18390 mp_primitive(mp, "-",plus_or_minus,minus);
18391 @:- }{\.{-} primitive@>
18392 mp_primitive(mp, "*",secondary_binary,times);
18393 @:* }{\.{*} primitive@>
18394 mp_primitive(mp, "/",slash,over); mp->eqtb[frozen_slash]=mp->eqtb[mp->cur_sym];
18395 @:/ }{\.{/} primitive@>
18396 mp_primitive(mp, "++",tertiary_binary,pythag_add);
18397 @:++_}{\.{++} primitive@>
18398 mp_primitive(mp, "+-+",tertiary_binary,pythag_sub);
18399 @:+-+_}{\.{+-+} primitive@>
18400 mp_primitive(mp, "or",tertiary_binary,or_op);
18401 @:or_}{\&{or} primitive@>
18402 mp_primitive(mp, "and",and_command,and_op);
18403 @:and_}{\&{and} primitive@>
18404 mp_primitive(mp, "<",expression_binary,less_than);
18405 @:< }{\.{<} primitive@>
18406 mp_primitive(mp, "<=",expression_binary,less_or_equal);
18407 @:<=_}{\.{<=} primitive@>
18408 mp_primitive(mp, ">",expression_binary,greater_than);
18409 @:> }{\.{>} primitive@>
18410 mp_primitive(mp, ">=",expression_binary,greater_or_equal);
18411 @:>=_}{\.{>=} primitive@>
18412 mp_primitive(mp, "=",equals,equal_to);
18413 @:= }{\.{=} primitive@>
18414 mp_primitive(mp, "<>",expression_binary,unequal_to);
18415 @:<>_}{\.{<>} primitive@>
18416 mp_primitive(mp, "substring",primary_binary,substring_of);
18417 @:substring_}{\&{substring} primitive@>
18418 mp_primitive(mp, "subpath",primary_binary,subpath_of);
18419 @:subpath_}{\&{subpath} primitive@>
18420 mp_primitive(mp, "directiontime",primary_binary,direction_time_of);
18421 @:direction_time_}{\&{directiontime} primitive@>
18422 mp_primitive(mp, "point",primary_binary,point_of);
18423 @:point_}{\&{point} primitive@>
18424 mp_primitive(mp, "precontrol",primary_binary,precontrol_of);
18425 @:precontrol_}{\&{precontrol} primitive@>
18426 mp_primitive(mp, "postcontrol",primary_binary,postcontrol_of);
18427 @:postcontrol_}{\&{postcontrol} primitive@>
18428 mp_primitive(mp, "penoffset",primary_binary,pen_offset_of);
18429 @:pen_offset_}{\&{penoffset} primitive@>
18430 mp_primitive(mp, "arctime",primary_binary,arc_time_of);
18431 @:arc_time_of_}{\&{arctime} primitive@>
18432 mp_primitive(mp, "mpversion",nullary,mp_version);
18433 @:mp_verison_}{\&{mpversion} primitive@>
18434 mp_primitive(mp, "&",ampersand,concatenate);
18435 @:!!!}{\.{\&} primitive@>
18436 mp_primitive(mp, "rotated",secondary_binary,rotated_by);
18437 @:rotated_}{\&{rotated} primitive@>
18438 mp_primitive(mp, "slanted",secondary_binary,slanted_by);
18439 @:slanted_}{\&{slanted} primitive@>
18440 mp_primitive(mp, "scaled",secondary_binary,scaled_by);
18441 @:scaled_}{\&{scaled} primitive@>
18442 mp_primitive(mp, "shifted",secondary_binary,shifted_by);
18443 @:shifted_}{\&{shifted} primitive@>
18444 mp_primitive(mp, "transformed",secondary_binary,transformed_by);
18445 @:transformed_}{\&{transformed} primitive@>
18446 mp_primitive(mp, "xscaled",secondary_binary,x_scaled);
18447 @:x_scaled_}{\&{xscaled} primitive@>
18448 mp_primitive(mp, "yscaled",secondary_binary,y_scaled);
18449 @:y_scaled_}{\&{yscaled} primitive@>
18450 mp_primitive(mp, "zscaled",secondary_binary,z_scaled);
18451 @:z_scaled_}{\&{zscaled} primitive@>
18452 mp_primitive(mp, "infont",secondary_binary,in_font);
18453 @:in_font_}{\&{infont} primitive@>
18454 mp_primitive(mp, "intersectiontimes",tertiary_binary,intersect);
18455 @:intersection_times_}{\&{intersectiontimes} primitive@>
18457 @ @<Cases of |print_cmd...@>=
18460 case primary_binary:
18461 case secondary_binary:
18462 case tertiary_binary:
18463 case expression_binary:
18465 case plus_or_minus:
18470 mp_print_op(mp, m);
18473 @ OK, let's look at the simplest \\{do} procedure first.
18475 @c @<Declare nullary action procedure@>;
18476 void mp_do_nullary (MP mp,quarterword c) {
18478 if ( mp->internal[tracing_commands]>two )
18479 mp_show_cmd_mod(mp, nullary,c);
18481 case true_code: case false_code:
18482 mp->cur_type=mp_boolean_type; mp->cur_exp=c;
18484 case null_picture_code:
18485 mp->cur_type=mp_picture_type;
18486 mp->cur_exp=mp_get_node(mp, edge_header_size);
18487 mp_init_edges(mp, mp->cur_exp);
18489 case null_pen_code:
18490 mp->cur_type=mp_pen_type; mp->cur_exp=mp_get_pen_circle(mp, 0);
18492 case normal_deviate:
18493 mp->cur_type=mp_known; mp->cur_exp=mp_norm_rand(mp);
18496 mp->cur_type=mp_pen_type; mp->cur_exp=mp_get_pen_circle(mp, unity);
18499 if ( mp->job_name==NULL ) mp_open_log_file(mp);
18500 mp->cur_type=mp_string_type; mp->cur_exp=rts(mp->job_name);
18503 mp->cur_type=mp_string_type;
18504 mp->cur_exp=intern(metapost_version) ;
18506 case read_string_op:
18507 @<Read a string from the terminal@>;
18509 } /* there are no other cases */
18513 @ @<Read a string...@>=
18515 if ( mp->interaction<=mp_nonstop_mode )
18516 mp_fatal_error(mp, "*** (cannot readstring in nonstop modes)");
18517 mp_begin_file_reading(mp); name=is_read;
18518 limit=start; prompt_input("");
18519 mp_finish_read(mp);
18522 @ @<Declare nullary action procedure@>=
18523 void mp_finish_read (MP mp) { /* copy |buffer| line to |cur_exp| */
18525 str_room((int)mp->last-start);
18526 for (k=start;k<=mp->last-1;k++) {
18527 append_char(mp->buffer[k]);
18529 mp_end_file_reading(mp); mp->cur_type=mp_string_type;
18530 mp->cur_exp=mp_make_string(mp);
18533 @ Things get a bit more interesting when there's an operand. The
18534 operand to |do_unary| appears in |cur_type| and |cur_exp|.
18536 @c @<Declare unary action procedures@>;
18537 void mp_do_unary (MP mp,quarterword c) {
18538 pointer p,q,r; /* for list manipulation */
18539 integer x; /* a temporary register */
18541 if ( mp->internal[tracing_commands]>two )
18542 @<Trace the current unary operation@>;
18545 if ( mp->cur_type<mp_color_type ) mp_bad_unary(mp, plus);
18548 @<Negate the current expression@>;
18550 @<Additional cases of unary operators@>;
18551 } /* there are no other cases */
18555 @ The |nice_pair| function returns |true| if both components of a pair
18558 @<Declare unary action procedures@>=
18559 boolean mp_nice_pair (MP mp,integer p, quarterword t) {
18560 if ( t==mp_pair_type ) {
18562 if ( type(x_part_loc(p))==mp_known )
18563 if ( type(y_part_loc(p))==mp_known )
18569 @ The |nice_color_or_pair| function is analogous except that it also accepts
18570 fully known colors.
18572 @<Declare unary action procedures@>=
18573 boolean mp_nice_color_or_pair (MP mp,integer p, quarterword t) {
18574 pointer q,r; /* for scanning the big node */
18575 if ( (t!=mp_pair_type)&&(t!=mp_color_type)&&(t!=mp_cmykcolor_type) ) {
18579 r=q+mp->big_node_size[type(p)];
18582 if ( type(r)!=mp_known )
18589 @ @<Declare unary action...@>=
18590 void mp_print_known_or_unknown_type (MP mp,small_number t, integer v) {
18591 mp_print_char(mp, '(');
18592 if ( t>mp_known ) mp_print(mp, "unknown numeric");
18593 else { if ( (t==mp_pair_type)||(t==mp_color_type)||(t==mp_cmykcolor_type) )
18594 if ( ! mp_nice_color_or_pair(mp, v,t) ) mp_print(mp, "unknown ");
18595 mp_print_type(mp, t);
18597 mp_print_char(mp, ')');
18600 @ @<Declare unary action...@>=
18601 void mp_bad_unary (MP mp,quarterword c) {
18602 exp_err("Not implemented: "); mp_print_op(mp, c);
18603 @.Not implemented...@>
18604 mp_print_known_or_unknown_type(mp, mp->cur_type,mp->cur_exp);
18605 help3("I'm afraid I don't know how to apply that operation to that")
18606 ("particular type. Continue, and I'll simply return the")
18607 ("argument (shown above) as the result of the operation.");
18608 mp_put_get_error(mp);
18611 @ @<Trace the current unary operation@>=
18613 mp_begin_diagnostic(mp); mp_print_nl(mp, "{");
18614 mp_print_op(mp, c); mp_print_char(mp, '(');
18615 mp_print_exp(mp, null,0); /* show the operand, but not verbosely */
18616 mp_print(mp, ")}"); mp_end_diagnostic(mp, false);
18619 @ Negation is easy except when the current expression
18620 is of type |independent|, or when it is a pair with one or more
18621 |independent| components.
18623 It is tempting to argue that the negative of an independent variable
18624 is an independent variable, hence we don't have to do anything when
18625 negating it. The fallacy is that other dependent variables pointing
18626 to the current expression must change the sign of their
18627 coefficients if we make no change to the current expression.
18629 Instead, we work around the problem by copying the current expression
18630 and recycling it afterwards (cf.~the |stash_in| routine).
18632 @<Negate the current expression@>=
18633 switch (mp->cur_type) {
18634 case mp_color_type:
18635 case mp_cmykcolor_type:
18637 case mp_independent:
18638 q=mp->cur_exp; mp_make_exp_copy(mp, q);
18639 if ( mp->cur_type==mp_dependent ) {
18640 mp_negate_dep_list(mp, dep_list(mp->cur_exp));
18641 } else if ( mp->cur_type<=mp_pair_type ) { /* |mp_color_type| or |mp_pair_type| */
18642 p=value(mp->cur_exp);
18643 r=p+mp->big_node_size[mp->cur_type];
18646 if ( type(r)==mp_known ) negate(value(r));
18647 else mp_negate_dep_list(mp, dep_list(r));
18649 } /* if |cur_type=mp_known| then |cur_exp=0| */
18650 mp_recycle_value(mp, q); mp_free_node(mp, q,value_node_size);
18653 case mp_proto_dependent:
18654 mp_negate_dep_list(mp, dep_list(mp->cur_exp));
18657 negate(mp->cur_exp);
18660 mp_bad_unary(mp, minus);
18664 @ @<Declare unary action...@>=
18665 void mp_negate_dep_list (MP mp,pointer p) {
18668 if ( info(p)==null ) return;
18673 @ @<Additional cases of unary operators@>=
18675 if ( mp->cur_type!=mp_boolean_type ) mp_bad_unary(mp, not_op);
18676 else mp->cur_exp=true_code+false_code-mp->cur_exp;
18679 @ @d three_sixty_units 23592960 /* that's |360*unity| */
18680 @d boolean_reset(A) if ( (A) ) mp->cur_exp=true_code; else mp->cur_exp=false_code
18682 @<Additional cases of unary operators@>=
18689 case uniform_deviate:
18691 case char_exists_op:
18692 if ( mp->cur_type!=mp_known ) {
18693 mp_bad_unary(mp, c);
18696 case sqrt_op:mp->cur_exp=mp_square_rt(mp, mp->cur_exp);break;
18697 case m_exp_op:mp->cur_exp=mp_m_exp(mp, mp->cur_exp);break;
18698 case m_log_op:mp->cur_exp=mp_m_log(mp, mp->cur_exp);break;
18701 mp_n_sin_cos(mp, (mp->cur_exp % three_sixty_units)*16);
18702 if ( c==sin_d_op ) mp->cur_exp=mp_round_fraction(mp, mp->n_sin);
18703 else mp->cur_exp=mp_round_fraction(mp, mp->n_cos);
18705 case floor_op:mp->cur_exp=mp_floor_scaled(mp, mp->cur_exp);break;
18706 case uniform_deviate:mp->cur_exp=mp_unif_rand(mp, mp->cur_exp);break;
18708 boolean_reset(odd(mp_round_unscaled(mp, mp->cur_exp)));
18709 mp->cur_type=mp_boolean_type;
18711 case char_exists_op:
18712 @<Determine if a character has been shipped out@>;
18714 } /* there are no other cases */
18718 @ @<Additional cases of unary operators@>=
18720 if ( mp_nice_pair(mp, mp->cur_exp,mp->cur_type) ) {
18721 p=value(mp->cur_exp);
18722 x=mp_n_arg(mp, value(x_part_loc(p)),value(y_part_loc(p)));
18723 if ( x>=0 ) mp_flush_cur_exp(mp, (x+8)/ 16);
18724 else mp_flush_cur_exp(mp, -((-x+8)/ 16));
18726 mp_bad_unary(mp, angle_op);
18730 @ If the current expression is a pair, but the context wants it to
18731 be a path, we call |pair_to_path|.
18733 @<Declare unary action...@>=
18734 void mp_pair_to_path (MP mp) {
18735 mp->cur_exp=mp_new_knot(mp);
18736 mp->cur_type=mp_path_type;
18739 @ @<Additional cases of unary operators@>=
18742 if ( (mp->cur_type==mp_pair_type)||(mp->cur_type==mp_transform_type) )
18743 mp_take_part(mp, c);
18744 else if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c);
18745 else mp_bad_unary(mp, c);
18751 if ( mp->cur_type==mp_transform_type ) mp_take_part(mp, c);
18752 else if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c);
18753 else mp_bad_unary(mp, c);
18758 if ( mp->cur_type==mp_color_type ) mp_take_part(mp, c);
18759 else if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c);
18760 else mp_bad_unary(mp, c);
18766 if ( mp->cur_type==mp_cmykcolor_type) mp_take_part(mp, c);
18767 else if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c);
18768 else mp_bad_unary(mp, c);
18771 if ( mp->cur_type==mp_known ) mp->cur_exp=value(c);
18772 else if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c);
18773 else mp_bad_unary(mp, c);
18775 case color_model_part:
18776 if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c);
18777 else mp_bad_unary(mp, c);
18780 @ In the following procedure, |cur_exp| points to a capsule, which points to
18781 a big node. We want to delete all but one part of the big node.
18783 @<Declare unary action...@>=
18784 void mp_take_part (MP mp,quarterword c) {
18785 pointer p; /* the big node */
18786 p=value(mp->cur_exp); value(temp_val)=p; type(temp_val)=mp->cur_type;
18787 link(p)=temp_val; mp_free_node(mp, mp->cur_exp,value_node_size);
18788 mp_make_exp_copy(mp, p+mp->sector_offset[c+mp_x_part_sector-x_part]);
18789 mp_recycle_value(mp, temp_val);
18792 @ @<Initialize table entries...@>=
18793 name_type(temp_val)=mp_capsule;
18795 @ @<Additional cases of unary operators@>=
18801 if ( mp->cur_type==mp_picture_type ) mp_take_pict_part(mp, c);
18802 else mp_bad_unary(mp, c);
18805 @ @<Declarations@>=
18806 void mp_scale_edges (MP mp);
18808 @ @<Declare unary action...@>=
18809 void mp_take_pict_part (MP mp,quarterword c) {
18810 pointer p; /* first graphical object in |cur_exp| */
18811 p=link(dummy_loc(mp->cur_exp));
18814 case x_part: case y_part: case xx_part:
18815 case xy_part: case yx_part: case yy_part:
18816 if ( type(p)==text_code ) mp_flush_cur_exp(mp, text_trans_part(p+c));
18817 else goto NOT_FOUND;
18819 case red_part: case green_part: case blue_part:
18820 if ( has_color(p) ) mp_flush_cur_exp(mp, obj_color_part(p+c));
18821 else goto NOT_FOUND;
18823 case cyan_part: case magenta_part: case yellow_part:
18825 if ( has_color(p) ) {
18826 if ( color_model(p)==uninitialized_model )
18827 mp_flush_cur_exp(mp, unity);
18829 mp_flush_cur_exp(mp, obj_color_part(p+c+(red_part-cyan_part)));
18830 } else goto NOT_FOUND;
18833 if ( has_color(p) )
18834 mp_flush_cur_exp(mp, obj_color_part(p+c+(red_part-grey_part)));
18835 else goto NOT_FOUND;
18837 case color_model_part:
18838 if ( has_color(p) ) {
18839 if ( color_model(p)==uninitialized_model )
18840 mp_flush_cur_exp(mp, mp->internal[default_color_model]);
18842 mp_flush_cur_exp(mp, color_model(p)*unity);
18843 } else goto NOT_FOUND;
18845 @<Handle other cases in |take_pict_part| or |goto not_found|@>;
18846 } /* all cases have been enumerated */
18850 @<Convert the current expression to a null value appropriate
18854 @ @<Handle other cases in |take_pict_part| or |goto not_found|@>=
18856 if ( type(p)!=text_code ) goto NOT_FOUND;
18858 mp_flush_cur_exp(mp, text_p(p));
18859 add_str_ref(mp->cur_exp);
18860 mp->cur_type=mp_string_type;
18864 if ( type(p)!=text_code ) goto NOT_FOUND;
18866 mp_flush_cur_exp(mp, rts(mp->font_name[font_n(p)]));
18867 add_str_ref(mp->cur_exp);
18868 mp->cur_type=mp_string_type;
18872 if ( type(p)==text_code ) goto NOT_FOUND;
18873 else if ( is_stop(p) ) mp_confusion(mp, "pict");
18874 @:this can't happen pict}{\quad pict@>
18876 mp_flush_cur_exp(mp, mp_copy_path(mp, path_p(p)));
18877 mp->cur_type=mp_path_type;
18881 if ( ! has_pen(p) ) goto NOT_FOUND;
18883 if ( pen_p(p)==null ) goto NOT_FOUND;
18884 else { mp_flush_cur_exp(mp, copy_pen(pen_p(p)));
18885 mp->cur_type=mp_pen_type;
18890 if ( type(p)!=stroked_code ) goto NOT_FOUND;
18891 else { if ( dash_p(p)==null ) goto NOT_FOUND;
18892 else { add_edge_ref(dash_p(p));
18893 mp->se_sf=dash_scale(p);
18894 mp->se_pic=dash_p(p);
18895 mp_scale_edges(mp);
18896 mp_flush_cur_exp(mp, mp->se_pic);
18897 mp->cur_type=mp_picture_type;
18902 @ Since |scale_edges| had to be declared |forward|, it had to be declared as a
18903 parameterless procedure even though it really takes two arguments and updates
18904 one of them. Hence the following globals are needed.
18907 pointer se_pic; /* edge header used and updated by |scale_edges| */
18908 scaled se_sf; /* the scale factor argument to |scale_edges| */
18910 @ @<Convert the current expression to a null value appropriate...@>=
18912 case text_part: case font_part:
18913 mp_flush_cur_exp(mp, rts(""));
18914 mp->cur_type=mp_string_type;
18917 mp_flush_cur_exp(mp, mp_get_node(mp, knot_node_size));
18918 left_type(mp->cur_exp)=endpoint;
18919 right_type(mp->cur_exp)=endpoint;
18920 link(mp->cur_exp)=mp->cur_exp;
18921 x_coord(mp->cur_exp)=0;
18922 y_coord(mp->cur_exp)=0;
18923 originator(mp->cur_exp)=metapost_user;
18924 mp->cur_type=mp_path_type;
18927 mp_flush_cur_exp(mp, mp_get_pen_circle(mp, 0));
18928 mp->cur_type=mp_pen_type;
18931 mp_flush_cur_exp(mp, mp_get_node(mp, edge_header_size));
18932 mp_init_edges(mp, mp->cur_exp);
18933 mp->cur_type=mp_picture_type;
18936 mp_flush_cur_exp(mp, 0);
18940 @ @<Additional cases of unary...@>=
18942 if ( mp->cur_type!=mp_known ) {
18943 mp_bad_unary(mp, char_op);
18945 mp->cur_exp=mp_round_unscaled(mp, mp->cur_exp) % 256;
18946 mp->cur_type=mp_string_type;
18947 if ( mp->cur_exp<0 ) mp->cur_exp=mp->cur_exp+256;
18951 if ( mp->cur_type!=mp_known ) {
18952 mp_bad_unary(mp, decimal);
18954 mp->old_setting=mp->selector; mp->selector=new_string;
18955 mp_print_scaled(mp, mp->cur_exp); mp->cur_exp=mp_make_string(mp);
18956 mp->selector=mp->old_setting; mp->cur_type=mp_string_type;
18962 if ( mp->cur_type!=mp_string_type ) mp_bad_unary(mp, c);
18963 else mp_str_to_num(mp, c);
18966 if ( mp->cur_type!=mp_string_type ) mp_bad_unary(mp, font_size);
18967 else @<Find the design size of the font whose name is |cur_exp|@>;
18970 @ @<Declare unary action...@>=
18971 void mp_str_to_num (MP mp,quarterword c) { /* converts a string to a number */
18972 integer n; /* accumulator */
18973 ASCII_code m; /* current character */
18974 pool_pointer k; /* index into |str_pool| */
18975 int b; /* radix of conversion */
18976 boolean bad_char; /* did the string contain an invalid digit? */
18977 if ( c==ASCII_op ) {
18978 if ( length(mp->cur_exp)==0 ) n=-1;
18979 else n=mp->str_pool[mp->str_start[mp->cur_exp]];
18981 if ( c==oct_op ) b=8; else b=16;
18982 n=0; bad_char=false;
18983 for (k=mp->str_start[mp->cur_exp];k<=str_stop(mp->cur_exp)-1;k++) {
18985 if ( (m>='0')&&(m<='9') ) m=m-'0';
18986 else if ( (m>='A')&&(m<='F') ) m=m-'A'+10;
18987 else if ( (m>='a')&&(m<='f') ) m=m-'a'+10;
18988 else { bad_char=true; m=0; };
18989 if ( m>=b ) { bad_char=true; m=0; };
18990 if ( n<32768 / b ) n=n*b+m; else n=32767;
18992 @<Give error messages if |bad_char| or |n>=4096|@>;
18994 mp_flush_cur_exp(mp, n*unity);
18997 @ @<Give error messages if |bad_char|...@>=
18999 exp_err("String contains illegal digits");
19000 @.String contains illegal digits@>
19002 help1("I zeroed out characters that weren't in the range 0..7.");
19004 help1("I zeroed out characters that weren't hex digits.");
19006 mp_put_get_error(mp);
19009 if ( mp->internal[warning_check]>0 ) {
19010 print_err("Number too large (");
19011 mp_print_int(mp, n); mp_print_char(mp, ')');
19012 @.Number too large@>
19013 help2("I have trouble with numbers greater than 4095; watch out.")
19014 ("(Set warningcheck:=0 to suppress this message.)");
19015 mp_put_get_error(mp);
19019 @ The length operation is somewhat unusual in that it applies to a variety
19020 of different types of operands.
19022 @<Additional cases of unary...@>=
19024 switch (mp->cur_type) {
19025 case mp_string_type: mp_flush_cur_exp(mp, length(mp->cur_exp)*unity); break;
19026 case mp_path_type: mp_flush_cur_exp(mp, mp_path_length(mp)); break;
19027 case mp_known: mp->cur_exp=abs(mp->cur_exp); break;
19028 case mp_picture_type: mp_flush_cur_exp(mp, mp_pict_length(mp)); break;
19030 if ( mp_nice_pair(mp, mp->cur_exp,mp->cur_type) )
19031 mp_flush_cur_exp(mp, mp_pyth_add(mp,
19032 value(x_part_loc(value(mp->cur_exp))),
19033 value(y_part_loc(value(mp->cur_exp)))));
19034 else mp_bad_unary(mp, c);
19039 @ @<Declare unary action...@>=
19040 scaled mp_path_length (MP mp) { /* computes the length of the current path */
19041 scaled n; /* the path length so far */
19042 pointer p; /* traverser */
19044 if ( left_type(p)==endpoint ) n=-unity; else n=0;
19045 do { p=link(p); n=n+unity; } while (p!=mp->cur_exp);
19049 @ @<Declare unary action...@>=
19050 scaled mp_pict_length (MP mp) {
19051 /* counts interior components in picture |cur_exp| */
19052 scaled n; /* the count so far */
19053 pointer p; /* traverser */
19055 p=link(dummy_loc(mp->cur_exp));
19057 if ( is_start_or_stop(p) )
19058 if ( mp_skip_1component(mp, p)==null ) p=link(p);
19059 while ( p!=null ) {
19060 skip_component(p) return n;
19067 @ Implement |turningnumber|
19069 @<Additional cases of unary...@>=
19071 if ( mp->cur_type==mp_pair_type ) mp_flush_cur_exp(mp, 0);
19072 else if ( mp->cur_type!=mp_path_type ) mp_bad_unary(mp, turning_op);
19073 else if ( left_type(mp->cur_exp)==endpoint )
19074 mp_flush_cur_exp(mp, 0); /* not a cyclic path */
19076 mp_flush_cur_exp(mp, mp_turn_cycles_wrapper(mp, mp->cur_exp));
19079 @ The function |an_angle| returns the value of the |angle| primitive, or $0$ if the
19080 argument is |origin|.
19082 @<Declare unary action...@>=
19083 angle mp_an_angle (MP mp,scaled xpar, scaled ypar) {
19084 if ( (! ((xpar==0) && (ypar==0))) )
19085 return mp_n_arg(mp, xpar,ypar);
19090 @ The actual turning number is (for the moment) computed in a C function
19091 that receives eight integers corresponding to the four controlling points,
19092 and returns a single angle. Besides those, we have to account for discrete
19093 moves at the actual points.
19095 @d floor(a) (a>=0 ? a : -(int)(-a))
19096 @d bezier_error (720<<20)+1
19097 @d sign(v) ((v)>0 ? 1 : ((v)<0 ? -1 : 0 ))
19098 @d print_roots(a) { if (debuglevel>(65536*2))
19099 fprintf(stdout,"bezier_slope(): %s, i=%f, o=%f, angle=%f\n", (a),in,out,res); }
19100 @d out ((double)(xo>>20))
19101 @d mid ((double)(xm>>20))
19102 @d in ((double)(xi>>20))
19103 @d divisor (256*256)
19104 @d double2angle(a) (int)floor(a*256.0*256.0*16.0)
19106 @<Declare unary action...@>=
19107 angle mp_bezier_slope(MP mp, integer AX,integer AY,integer BX,integer BY,
19108 integer CX,integer CY,integer DX,integer DY, int debuglevel);
19111 angle mp_bezier_slope(MP mp, integer AX,integer AY,integer BX,integer BY,
19112 integer CX,integer CY,integer DX,integer DY, int debuglevel) {
19114 integer deltax,deltay;
19115 double ax,ay,bx,by,cx,cy,dx,dy;
19116 angle xi = 0, xo = 0, xm = 0;
19118 ax=AX/divisor; ay=AY/divisor;
19119 bx=BX/divisor; by=BY/divisor;
19120 cx=CX/divisor; cy=CY/divisor;
19121 dx=DX/divisor; dy=DY/divisor;
19123 deltax = (BX-AX); deltay = (BY-AY);
19124 if (deltax==0 && deltay == 0) { deltax=(CX-AX); deltay=(CY-AY); }
19125 if (deltax==0 && deltay == 0) { deltax=(DX-AX); deltay=(DY-AY); }
19126 xi = mp_an_angle(mp,deltax,deltay);
19128 deltax = (CX-BX); deltay = (CY-BY);
19129 xm = mp_an_angle(mp,deltax,deltay);
19131 deltax = (DX-CX); deltay = (DY-CY);
19132 if (deltax==0 && deltay == 0) { deltax=(DX-BX); deltay=(DY-BY); }
19133 if (deltax==0 && deltay == 0) { deltax=(DX-AX); deltay=(DY-AY); }
19134 xo = mp_an_angle(mp,deltax,deltay);
19136 a = (bx-ax)*(cy-by) - (cx-bx)*(by-ay); /* a = (bp-ap)x(cp-bp); */
19137 b = (bx-ax)*(dy-cy) - (by-ay)*(dx-cx);; /* b = (bp-ap)x(dp-cp);*/
19138 c = (cx-bx)*(dy-cy) - (dx-cx)*(cy-by); /* c = (cp-bp)x(dp-cp);*/
19140 if (debuglevel>(65536*2)) {
19142 "bezier_slope(): (%.2f,%.2f),(%.2f,%.2f),(%.2f,%.2f),(%.2f,%.2f)\n",
19143 ax,ay,bx,by,cx,cy,dx,dy);
19145 "bezier_slope(): a,b,c,b^2,4ac: (%.2f,%.2f,%.2f,%.2f,%.2f)\n",a,b,c,b*b,4*a*c);
19148 if ((a==0)&&(c==0)) {
19149 res = (b==0 ? 0 : (out-in));
19150 print_roots("no roots (a)");
19151 } else if ((a==0)||(c==0)) {
19152 if ((sign(b) == sign(a)) || (sign(b) == sign(c))) {
19153 res = out-in; /* ? */
19156 else if (res>180.0)
19158 print_roots("no roots (b)");
19160 res = out-in; /* ? */
19161 print_roots("one root (a)");
19163 } else if ((sign(a)*sign(c))<0) {
19164 res = out-in; /* ? */
19167 else if (res>180.0)
19169 print_roots("one root (b)");
19171 if (sign(a) == sign(b)) {
19172 res = out-in; /* ? */
19175 else if (res>180.0)
19177 print_roots("no roots (d)");
19179 if ((b*b) == (4*a*c)) {
19180 res = bezier_error;
19181 print_roots("double root"); /* cusp */
19182 } else if ((b*b) < (4*a*c)) {
19183 res = out-in; /* ? */
19184 if (res<=0.0 &&res>-180.0)
19186 else if (res>=0.0 && res<180.0)
19188 print_roots("no roots (e)");
19193 else if (res>180.0)
19195 print_roots("two roots"); /* two inflections */
19199 return double2angle(res);
19203 @d p_nextnext link(link(p))
19205 @d seven_twenty_deg 05500000000 /* $720\cdot2^{20}$, represents $720^\circ$ */
19207 @<Declare unary action...@>=
19208 scaled mp_new_turn_cycles (MP mp,pointer c) {
19209 angle res,ang; /* the angles of intermediate results */
19210 scaled turns; /* the turn counter */
19211 pointer p; /* for running around the path */
19212 integer xp,yp; /* coordinates of next point */
19213 integer x,y; /* helper coordinates */
19214 angle in_angle,out_angle; /* helper angles */
19215 int old_setting; /* saved |selector| setting */
19219 old_setting = mp->selector; mp->selector=term_only;
19220 if ( mp->internal[tracing_commands]>unity ) {
19221 mp_begin_diagnostic(mp);
19222 mp_print_nl(mp, "");
19223 mp_end_diagnostic(mp, false);
19226 xp = x_coord(p_next); yp = y_coord(p_next);
19227 ang = mp_bezier_slope(mp,x_coord(p), y_coord(p), right_x(p), right_y(p),
19228 left_x(p_next), left_y(p_next), xp, yp,
19229 mp->internal[tracing_commands]);
19230 if ( ang>seven_twenty_deg ) {
19231 print_err("Strange path");
19233 mp->selector=old_setting;
19237 if ( res > one_eighty_deg ) {
19238 res = res - three_sixty_deg;
19239 turns = turns + unity;
19241 if ( res <= -one_eighty_deg ) {
19242 res = res + three_sixty_deg;
19243 turns = turns - unity;
19245 /* incoming angle at next point */
19246 x = left_x(p_next); y = left_y(p_next);
19247 if ( (xp==x)&&(yp==y) ) { x = right_x(p); y = right_y(p); };
19248 if ( (xp==x)&&(yp==y) ) { x = x_coord(p); y = y_coord(p); };
19249 in_angle = mp_an_angle(mp, xp - x, yp - y);
19250 /* outgoing angle at next point */
19251 x = right_x(p_next); y = right_y(p_next);
19252 if ( (xp==x)&&(yp==y) ) { x = left_x(p_nextnext); y = left_y(p_nextnext); };
19253 if ( (xp==x)&&(yp==y) ) { x = x_coord(p_nextnext); y = y_coord(p_nextnext); };
19254 out_angle = mp_an_angle(mp, x - xp, y- yp);
19255 ang = (out_angle - in_angle);
19259 if ( res >= one_eighty_deg ) {
19260 res = res - three_sixty_deg;
19261 turns = turns + unity;
19263 if ( res <= -one_eighty_deg ) {
19264 res = res + three_sixty_deg;
19265 turns = turns - unity;
19270 mp->selector=old_setting;
19275 @ This code is based on Bogus\l{}av Jackowski's
19276 |emergency_turningnumber| macro, with some minor changes by Taco
19277 Hoekwater. The macro code looked more like this:
19279 vardef turning\_number primary p =
19280 ~~save res, ang, turns;
19282 ~~if length p <= 2:
19283 ~~~~if Angle ((point 0 of p) - (postcontrol 0 of p)) >= 0: 1 else: -1 fi
19285 ~~~~for t = 0 upto length p-1 :
19286 ~~~~~~angc := Angle ((point t+1 of p) - (point t of p))
19287 ~~~~~~~~- Angle ((point t of p) - (point t-1 of p));
19288 ~~~~~~if angc > 180: angc := angc - 360; fi;
19289 ~~~~~~if angc < -180: angc := angc + 360; fi;
19290 ~~~~~~res := res + angc;
19295 The general idea is to calculate only the sum of the angles of
19296 straight lines between the points, of a path, not worrying about cusps
19297 or self-intersections in the segments at all. If the segment is not
19298 well-behaved, the result is not necesarily correct. But the old code
19299 was not always correct either, and worse, it sometimes failed for
19300 well-behaved paths as well. All known bugs that were triggered by the
19301 original code no longer occur with this code, and it runs roughly 3
19302 times as fast because the algorithm is much simpler.
19304 @ It is possible to overflow the return value of the |turn_cycles|
19305 function when the path is sufficiently long and winding, but I am not
19306 going to bother testing for that. In any case, it would only return
19307 the looped result value, which is not a big problem.
19309 The macro code for the repeat loop was a bit nicer to look
19310 at than the pascal code, because it could use |point -1 of p|. In
19311 pascal, the fastest way to loop around the path is not to look
19312 backward once, but forward twice. These defines help hide the trick.
19314 @d p_to link(link(p))
19318 @<Declare unary action...@>=
19319 scaled mp_turn_cycles (MP mp,pointer c) {
19320 angle res,ang; /* the angles of intermediate results */
19321 scaled turns; /* the turn counter */
19322 pointer p; /* for running around the path */
19323 res=0; turns= 0; p=c;
19325 ang = mp_an_angle (mp, x_coord(p_to) - x_coord(p_here),
19326 y_coord(p_to) - y_coord(p_here))
19327 - mp_an_angle (mp, x_coord(p_here) - x_coord(p_from),
19328 y_coord(p_here) - y_coord(p_from));
19331 if ( res >= three_sixty_deg ) {
19332 res = res - three_sixty_deg;
19333 turns = turns + unity;
19335 if ( res <= -three_sixty_deg ) {
19336 res = res + three_sixty_deg;
19337 turns = turns - unity;
19344 @ @<Declare unary action...@>=
19345 scaled mp_turn_cycles_wrapper (MP mp,pointer c) {
19347 scaled saved_t_o; /* tracing\_online saved */
19348 if ( (link(c)==c)||(link(link(c))==c) ) {
19349 if ( mp_an_angle (mp, x_coord(c) - right_x(c), y_coord(c) - right_y(c)) > 0 )
19354 nval = mp_new_turn_cycles(mp, c);
19355 oval = mp_turn_cycles(mp, c);
19356 if ( nval!=oval ) {
19357 saved_t_o=mp->internal[tracing_online];
19358 mp->internal[tracing_online]=unity;
19359 mp_begin_diagnostic(mp);
19360 mp_print_nl (mp, "Warning: the turningnumber algorithms do not agree."
19361 " The current computed value is ");
19362 mp_print_scaled(mp, nval);
19363 mp_print(mp, ", but the 'connect-the-dots' algorithm returned ");
19364 mp_print_scaled(mp, oval);
19365 mp_end_diagnostic(mp, false);
19366 mp->internal[tracing_online]=saved_t_o;
19372 @ @<Declare unary action...@>=
19373 scaled mp_count_turns (MP mp,pointer c) {
19374 pointer p; /* a knot in envelope spec |c| */
19375 integer t; /* total pen offset changes counted */
19378 t=t+info(p)-zero_off;
19381 return ((t / 3)*unity);
19384 @ @d type_range(A,B) {
19385 if ( (mp->cur_type>=(A)) && (mp->cur_type<=(B)) )
19386 mp_flush_cur_exp(mp, true_code);
19387 else mp_flush_cur_exp(mp, false_code);
19388 mp->cur_type=mp_boolean_type;
19391 if ( mp->cur_type==(A) ) mp_flush_cur_exp(mp, true_code);
19392 else mp_flush_cur_exp(mp, false_code);
19393 mp->cur_type=mp_boolean_type;
19396 @<Additional cases of unary operators@>=
19397 case mp_boolean_type:
19398 type_range(mp_boolean_type,mp_unknown_boolean); break;
19399 case mp_string_type:
19400 type_range(mp_string_type,mp_unknown_string); break;
19402 type_range(mp_pen_type,mp_unknown_pen); break;
19404 type_range(mp_path_type,mp_unknown_path); break;
19405 case mp_picture_type:
19406 type_range(mp_picture_type,mp_unknown_picture); break;
19407 case mp_transform_type: case mp_color_type: case mp_cmykcolor_type:
19409 type_test(c); break;
19410 case mp_numeric_type:
19411 type_range(mp_known,mp_independent); break;
19412 case known_op: case unknown_op:
19413 mp_test_known(mp, c); break;
19415 @ @<Declare unary action procedures@>=
19416 void mp_test_known (MP mp,quarterword c) {
19417 int b; /* is the current expression known? */
19418 pointer p,q; /* locations in a big node */
19420 switch (mp->cur_type) {
19421 case mp_vacuous: case mp_boolean_type: case mp_string_type:
19422 case mp_pen_type: case mp_path_type: case mp_picture_type:
19426 case mp_transform_type:
19427 case mp_color_type: case mp_cmykcolor_type: case mp_pair_type:
19428 p=value(mp->cur_exp);
19429 q=p+mp->big_node_size[mp->cur_type];
19432 if ( type(q)!=mp_known )
19441 if ( c==known_op ) mp_flush_cur_exp(mp, b);
19442 else mp_flush_cur_exp(mp, true_code+false_code-b);
19443 mp->cur_type=mp_boolean_type;
19446 @ @<Additional cases of unary operators@>=
19448 if ( mp->cur_type!=mp_path_type ) mp_flush_cur_exp(mp, false_code);
19449 else if ( left_type(mp->cur_exp)!=endpoint ) mp_flush_cur_exp(mp, true_code);
19450 else mp_flush_cur_exp(mp, false_code);
19451 mp->cur_type=mp_boolean_type;
19454 @ @<Additional cases of unary operators@>=
19456 if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp);
19457 if ( mp->cur_type!=mp_path_type ) mp_bad_unary(mp, arc_length);
19458 else mp_flush_cur_exp(mp, mp_get_arc_length(mp, mp->cur_exp));
19461 @ Here we use the fact that |c-filled_op+fill_code| is the desired graphical
19463 @^data structure assumptions@>
19465 @<Additional cases of unary operators@>=
19471 if ( mp->cur_type!=mp_picture_type ) mp_flush_cur_exp(mp, false_code);
19472 else if ( link(dummy_loc(mp->cur_exp))==null ) mp_flush_cur_exp(mp, false_code);
19473 else if ( type(link(dummy_loc(mp->cur_exp)))==c+fill_code-filled_op )
19474 mp_flush_cur_exp(mp, true_code);
19475 else mp_flush_cur_exp(mp, false_code);
19476 mp->cur_type=mp_boolean_type;
19479 @ @<Additional cases of unary operators@>=
19481 if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp);
19482 if ( mp->cur_type!=mp_path_type ) mp_bad_unary(mp, make_pen_op);
19484 mp->cur_type=mp_pen_type;
19485 mp->cur_exp=mp_make_pen(mp, mp->cur_exp,true);
19489 if ( mp->cur_type!=mp_pen_type ) mp_bad_unary(mp, make_path_op);
19491 mp->cur_type=mp_path_type;
19492 mp_make_path(mp, mp->cur_exp);
19496 if ( mp->cur_type==mp_path_type ) {
19497 p=mp_htap_ypoc(mp, mp->cur_exp);
19498 if ( right_type(p)==endpoint ) p=link(p);
19499 mp_toss_knot_list(mp, mp->cur_exp); mp->cur_exp=p;
19500 } else if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp);
19501 else mp_bad_unary(mp, reverse);
19504 @ The |pair_value| routine changes the current expression to a
19505 given ordered pair of values.
19507 @<Declare unary action procedures@>=
19508 void mp_pair_value (MP mp,scaled x, scaled y) {
19509 pointer p; /* a pair node */
19510 p=mp_get_node(mp, value_node_size);
19511 mp_flush_cur_exp(mp, p); mp->cur_type=mp_pair_type;
19512 type(p)=mp_pair_type; name_type(p)=mp_capsule; mp_init_big_node(mp, p);
19514 type(x_part_loc(p))=mp_known; value(x_part_loc(p))=x;
19515 type(y_part_loc(p))=mp_known; value(y_part_loc(p))=y;
19518 @ @<Additional cases of unary operators@>=
19520 if ( ! mp_get_cur_bbox(mp) ) mp_bad_unary(mp, ll_corner_op);
19521 else mp_pair_value(mp, minx,miny);
19524 if ( ! mp_get_cur_bbox(mp) ) mp_bad_unary(mp, lr_corner_op);
19525 else mp_pair_value(mp, maxx,miny);
19528 if ( ! mp_get_cur_bbox(mp) ) mp_bad_unary(mp, ul_corner_op);
19529 else mp_pair_value(mp, minx,maxy);
19532 if ( ! mp_get_cur_bbox(mp) ) mp_bad_unary(mp, ur_corner_op);
19533 else mp_pair_value(mp, maxx,maxy);
19536 @ Here is a function that sets |minx|, |maxx|, |miny|, |maxy| to the bounding
19537 box of the current expression. The boolean result is |false| if the expression
19538 has the wrong type.
19540 @<Declare unary action procedures@>=
19541 boolean mp_get_cur_bbox (MP mp) {
19542 switch (mp->cur_type) {
19543 case mp_picture_type:
19544 mp_set_bbox(mp, mp->cur_exp,true);
19545 if ( minx_val(mp->cur_exp)>maxx_val(mp->cur_exp) ) {
19546 minx=0; maxx=0; miny=0; maxy=0;
19548 minx=minx_val(mp->cur_exp);
19549 maxx=maxx_val(mp->cur_exp);
19550 miny=miny_val(mp->cur_exp);
19551 maxy=maxy_val(mp->cur_exp);
19555 mp_path_bbox(mp, mp->cur_exp);
19558 mp_pen_bbox(mp, mp->cur_exp);
19566 @ @<Additional cases of unary operators@>=
19568 case close_from_op:
19569 if ( mp->cur_type!=mp_string_type ) mp_bad_unary(mp, c);
19570 else mp_do_read_or_close(mp,c);
19573 @ Here is a routine that interprets |cur_exp| as a file name and tries to read
19574 a line from the file or to close the file.
19576 @d close_file 46 /* go here when closing the file */
19578 @<Declare unary action procedures@>=
19579 void mp_do_read_or_close (MP mp,quarterword c) {
19580 readf_index n,n0; /* indices for searching |rd_fname| */
19581 @<Find the |n| where |rd_fname[n]=cur_exp|; if |cur_exp| must be inserted,
19582 call |start_read_input| and |goto found| or |not_found|@>;
19583 mp_begin_file_reading(mp);
19585 if ( mp_input_ln(mp, mp->rd_file[n],true) )
19587 mp_end_file_reading(mp);
19589 @<Record the end of file and set |cur_exp| to a dummy value@>;
19592 mp_flush_cur_exp(mp, 0); mp->cur_type=mp_vacuous;
19595 mp_flush_cur_exp(mp, 0);
19596 mp_finish_read(mp);
19599 @ Free slots in the |rd_file| and |rd_fname| arrays are marked with NULL's in
19602 @<Find the |n| where |rd_fname[n]=cur_exp|...@>=
19607 fn = str(mp->cur_exp);
19608 while (mp_xstrcmp(fn,mp->rd_fname[n])!=0) {
19611 } else if ( c==close_from_op ) {
19614 if ( n0==mp->read_files ) {
19615 if ( mp->read_files<mp->max_read_files ) {
19616 incr(mp->read_files);
19621 l = mp->max_read_files + (mp->max_read_files>>2);
19622 rd_file = xmalloc((l+1), sizeof(FILE *));
19623 rd_fname = xmalloc((l+1), sizeof(char *));
19624 for (k=0;k<=l;k++) {
19625 if (k<=mp->max_read_files) {
19626 rd_file[k]=mp->rd_file[k];
19627 rd_fname[k]=mp->rd_fname[k];
19633 xfree(mp->rd_file); xfree(mp->rd_fname);
19634 mp->max_read_files = l;
19635 mp->rd_file = rd_file;
19636 mp->rd_fname = rd_fname;
19640 if ( mp_start_read_input(mp,fn,n) )
19645 if ( mp->rd_fname[n]==NULL ) { n0=n; }
19647 if ( c==close_from_op ) {
19648 fclose(mp->rd_file[n]);
19653 @ @<Record the end of file and set |cur_exp| to a dummy value@>=
19654 xfree(mp->rd_fname[n]);
19655 mp->rd_fname[n]=NULL;
19656 if ( n==mp->read_files-1 ) mp->read_files=n;
19657 if ( c==close_from_op )
19659 mp_flush_cur_exp(mp, mp->eof_line);
19660 mp->cur_type=mp_string_type
19662 @ The string denoting end-of-file is a one-byte string at position zero, by definition
19665 str_number eof_line;
19670 @ Finally, we have the operations that combine a capsule~|p|
19671 with the current expression.
19673 @c @<Declare binary action procedures@>;
19674 void mp_do_binary (MP mp,pointer p, quarterword c) {
19675 pointer q,r,rr; /* for list manipulation */
19676 pointer old_p,old_exp; /* capsules to recycle */
19677 integer v; /* for numeric manipulation */
19679 if ( mp->internal[tracing_commands]>two ) {
19680 @<Trace the current binary operation@>;
19682 @<Sidestep |independent| cases in capsule |p|@>;
19683 @<Sidestep |independent| cases in the current expression@>;
19685 case plus: case minus:
19686 @<Add or subtract the current expression from |p|@>;
19688 @<Additional cases of binary operators@>;
19689 }; /* there are no other cases */
19690 mp_recycle_value(mp, p);
19691 mp_free_node(mp, p,value_node_size); /* |return| to avoid this */
19693 @<Recycle any sidestepped |independent| capsules@>;
19696 @ @<Declare binary action...@>=
19697 void mp_bad_binary (MP mp,pointer p, quarterword c) {
19698 mp_disp_err(mp, p,"");
19699 exp_err("Not implemented: ");
19700 @.Not implemented...@>
19701 if ( c>=min_of ) mp_print_op(mp, c);
19702 mp_print_known_or_unknown_type(mp, type(p),p);
19703 if ( c>=min_of ) mp_print(mp, "of"); else mp_print_op(mp, c);
19704 mp_print_known_or_unknown_type(mp, mp->cur_type,mp->cur_exp);
19705 help3("I'm afraid I don't know how to apply that operation to that")
19706 ("combination of types. Continue, and I'll return the second")
19707 ("argument (see above) as the result of the operation.");
19708 mp_put_get_error(mp);
19711 @ @<Trace the current binary operation@>=
19713 mp_begin_diagnostic(mp); mp_print_nl(mp, "{(");
19714 mp_print_exp(mp,p,0); /* show the operand, but not verbosely */
19715 mp_print_char(mp,')'); mp_print_op(mp,c); mp_print_char(mp,'(');
19716 mp_print_exp(mp,null,0); mp_print(mp,")}");
19717 mp_end_diagnostic(mp, false);
19720 @ Several of the binary operations are potentially complicated by the
19721 fact that |independent| values can sneak into capsules. For example,
19722 we've seen an instance of this difficulty in the unary operation
19723 of negation. In order to reduce the number of cases that need to be
19724 handled, we first change the two operands (if necessary)
19725 to rid them of |independent| components. The original operands are
19726 put into capsules called |old_p| and |old_exp|, which will be
19727 recycled after the binary operation has been safely carried out.
19729 @<Recycle any sidestepped |independent| capsules@>=
19730 if ( old_p!=null ) {
19731 mp_recycle_value(mp, old_p); mp_free_node(mp, old_p,value_node_size);
19733 if ( old_exp!=null ) {
19734 mp_recycle_value(mp, old_exp); mp_free_node(mp, old_exp,value_node_size);
19737 @ A big node is considered to be ``tarnished'' if it contains at least one
19738 independent component. We will define a simple function called `|tarnished|'
19739 that returns |null| if and only if its argument is not tarnished.
19741 @<Sidestep |independent| cases in capsule |p|@>=
19743 case mp_transform_type:
19744 case mp_color_type:
19745 case mp_cmykcolor_type:
19747 old_p=mp_tarnished(mp, p);
19749 case mp_independent: old_p=diov; break;
19750 default: old_p=null; break;
19752 if ( old_p!=null ) {
19753 q=mp_stash_cur_exp(mp); old_p=p; mp_make_exp_copy(mp, old_p);
19754 p=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, q);
19757 @ @<Sidestep |independent| cases in the current expression@>=
19758 switch (mp->cur_type) {
19759 case mp_transform_type:
19760 case mp_color_type:
19761 case mp_cmykcolor_type:
19763 old_exp=mp_tarnished(mp, mp->cur_exp);
19765 case mp_independent:old_exp=diov; break;
19766 default: old_exp=null; break;
19768 if ( old_exp!=null ) {
19769 old_exp=mp->cur_exp; mp_make_exp_copy(mp, old_exp);
19772 @ @<Declare binary action...@>=
19773 pointer mp_tarnished (MP mp,pointer p) {
19774 pointer q; /* beginning of the big node */
19775 pointer r; /* current position in the big node */
19776 q=value(p); r=q+mp->big_node_size[type(p)];
19779 if ( type(r)==mp_independent ) return diov;
19784 @ @<Add or subtract the current expression from |p|@>=
19785 if ( (mp->cur_type<mp_color_type)||(type(p)<mp_color_type) ) {
19786 mp_bad_binary(mp, p,c);
19788 if ((mp->cur_type>mp_pair_type)&&(type(p)>mp_pair_type) ) {
19789 mp_add_or_subtract(mp, p,null,c);
19791 if ( mp->cur_type!=type(p) ) {
19792 mp_bad_binary(mp, p,c);
19794 q=value(p); r=value(mp->cur_exp);
19795 rr=r+mp->big_node_size[mp->cur_type];
19797 mp_add_or_subtract(mp, q,r,c);
19804 @ The first argument to |add_or_subtract| is the location of a value node
19805 in a capsule or pair node that will soon be recycled. The second argument
19806 is either a location within a pair or transform node of |cur_exp|,
19807 or it is null (which means that |cur_exp| itself should be the second
19808 argument). The third argument is either |plus| or |minus|.
19810 The sum or difference of the numeric quantities will replace the second
19811 operand. Arithmetic overflow may go undetected; users aren't supposed to
19812 be monkeying around with really big values.
19814 @<Declare binary action...@>=
19815 @<Declare the procedure called |dep_finish|@>;
19816 void mp_add_or_subtract (MP mp,pointer p, pointer q, quarterword c) {
19817 small_number s,t; /* operand types */
19818 pointer r; /* list traverser */
19819 integer v; /* second operand value */
19822 if ( t<mp_dependent ) v=mp->cur_exp; else v=dep_list(mp->cur_exp);
19825 if ( t<mp_dependent ) v=value(q); else v=dep_list(q);
19827 if ( t==mp_known ) {
19828 if ( c==minus ) negate(v);
19829 if ( type(p)==mp_known ) {
19830 v=mp_slow_add(mp, value(p),v);
19831 if ( q==null ) mp->cur_exp=v; else value(q)=v;
19834 @<Add a known value to the constant term of |dep_list(p)|@>;
19836 if ( c==minus ) mp_negate_dep_list(mp, v);
19837 @<Add operand |p| to the dependency list |v|@>;
19841 @ @<Add a known value to the constant term of |dep_list(p)|@>=
19843 while ( info(r)!=null ) r=link(r);
19844 value(r)=mp_slow_add(mp, value(r),v);
19846 q=mp_get_node(mp, value_node_size); mp->cur_exp=q; mp->cur_type=type(p);
19847 name_type(q)=mp_capsule;
19849 dep_list(q)=dep_list(p); type(q)=type(p);
19850 prev_dep(q)=prev_dep(p); link(prev_dep(p))=q;
19851 type(p)=mp_known; /* this will keep the recycler from collecting non-garbage */
19853 @ We prefer |dependent| lists to |mp_proto_dependent| ones, because it is
19854 nice to retain the extra accuracy of |fraction| coefficients.
19855 But we have to handle both kinds, and mixtures too.
19857 @<Add operand |p| to the dependency list |v|@>=
19858 if ( type(p)==mp_known ) {
19859 @<Add the known |value(p)| to the constant term of |v|@>;
19861 s=type(p); r=dep_list(p);
19862 if ( t==mp_dependent ) {
19863 if ( s==mp_dependent ) {
19864 if ( mp_max_coef(mp, r)+mp_max_coef(mp, v)<coef_bound )
19865 v=mp_p_plus_q(mp, v,r,mp_dependent); goto DONE;
19866 } /* |fix_needed| will necessarily be false */
19867 t=mp_proto_dependent;
19868 v=mp_p_over_v(mp, v,unity,mp_dependent,mp_proto_dependent);
19870 if ( s==mp_proto_dependent ) v=mp_p_plus_q(mp, v,r,mp_proto_dependent);
19871 else v=mp_p_plus_fq(mp, v,unity,r,mp_proto_dependent,mp_dependent);
19873 @<Output the answer, |v| (which might have become |known|)@>;
19876 @ @<Add the known |value(p)| to the constant term of |v|@>=
19878 while ( info(v)!=null ) v=link(v);
19879 value(v)=mp_slow_add(mp, value(p),value(v));
19882 @ @<Output the answer, |v| (which might have become |known|)@>=
19883 if ( q!=null ) mp_dep_finish(mp, v,q,t);
19884 else { mp->cur_type=t; mp_dep_finish(mp, v,null,t); }
19886 @ Here's the current situation: The dependency list |v| of type |t|
19887 should either be put into the current expression (if |q=null|) or
19888 into location |q| within a pair node (otherwise). The destination (|cur_exp|
19889 or |q|) formerly held a dependency list with the same
19890 final pointer as the list |v|.
19892 @<Declare the procedure called |dep_finish|@>=
19893 void mp_dep_finish (MP mp, pointer v, pointer q, small_number t) {
19894 pointer p; /* the destination */
19895 scaled vv; /* the value, if it is |known| */
19896 if ( q==null ) p=mp->cur_exp; else p=q;
19897 dep_list(p)=v; type(p)=t;
19898 if ( info(v)==null ) {
19901 mp_flush_cur_exp(mp, vv);
19903 mp_recycle_value(mp, p); type(q)=mp_known; value(q)=vv;
19905 } else if ( q==null ) {
19908 if ( mp->fix_needed ) mp_fix_dependencies(mp);
19911 @ Let's turn now to the six basic relations of comparison.
19913 @<Additional cases of binary operators@>=
19914 case less_than: case less_or_equal: case greater_than:
19915 case greater_or_equal: case equal_to: case unequal_to:
19916 check_arith; /* at this point |arith_error| should be |false|? */
19917 if ( (mp->cur_type>mp_pair_type)&&(type(p)>mp_pair_type) ) {
19918 mp_add_or_subtract(mp, p,null,minus); /* |cur_exp:=(p)-cur_exp| */
19919 } else if ( mp->cur_type!=type(p) ) {
19920 mp_bad_binary(mp, p,c); goto DONE;
19921 } else if ( mp->cur_type==mp_string_type ) {
19922 mp_flush_cur_exp(mp, mp_str_vs_str(mp, value(p),mp->cur_exp));
19923 } else if ((mp->cur_type==mp_unknown_string)||
19924 (mp->cur_type==mp_unknown_boolean) ) {
19925 @<Check if unknowns have been equated@>;
19926 } else if ( (mp->cur_type<=mp_pair_type)&&(mp->cur_type>=mp_transform_type)) {
19927 @<Reduce comparison of big nodes to comparison of scalars@>;
19928 } else if ( mp->cur_type==mp_boolean_type ) {
19929 mp_flush_cur_exp(mp, mp->cur_exp-value(p));
19931 mp_bad_binary(mp, p,c); goto DONE;
19933 @<Compare the current expression with zero@>;
19935 mp->arith_error=false; /* ignore overflow in comparisons */
19938 @ @<Compare the current expression with zero@>=
19939 if ( mp->cur_type!=mp_known ) {
19940 if ( mp->cur_type<mp_known ) {
19941 mp_disp_err(mp, p,"");
19942 help1("The quantities shown above have not been equated.")
19944 help2("Oh dear. I can\'t decide if the expression above is positive,")
19945 ("negative, or zero. So this comparison test won't be `true'.");
19947 exp_err("Unknown relation will be considered false");
19948 @.Unknown relation...@>
19949 mp_put_get_flush_error(mp, false_code);
19952 case less_than: boolean_reset(mp->cur_exp<0); break;
19953 case less_or_equal: boolean_reset(mp->cur_exp<=0); break;
19954 case greater_than: boolean_reset(mp->cur_exp>0); break;
19955 case greater_or_equal: boolean_reset(mp->cur_exp>=0); break;
19956 case equal_to: boolean_reset(mp->cur_exp==0); break;
19957 case unequal_to: boolean_reset(mp->cur_exp!=0); break;
19958 }; /* there are no other cases */
19960 mp->cur_type=mp_boolean_type
19962 @ When two unknown strings are in the same ring, we know that they are
19963 equal. Otherwise, we don't know whether they are equal or not, so we
19966 @<Check if unknowns have been equated@>=
19968 q=value(mp->cur_exp);
19969 while ( (q!=mp->cur_exp)&&(q!=p) ) q=value(q);
19970 if ( q==p ) mp_flush_cur_exp(mp, 0);
19973 @ @<Reduce comparison of big nodes to comparison of scalars@>=
19975 q=value(p); r=value(mp->cur_exp);
19976 rr=r+mp->big_node_size[mp->cur_type]-2;
19977 while (1) { mp_add_or_subtract(mp, q,r,minus);
19978 if ( type(r)!=mp_known ) break;
19979 if ( value(r)!=0 ) break;
19980 if ( r==rr ) break;
19983 mp_take_part(mp, name_type(r)+x_part-mp_x_part_sector);
19986 @ Here we use the sneaky fact that |and_op-false_code=or_op-true_code|.
19988 @<Additional cases of binary operators@>=
19991 if ( (type(p)!=mp_boolean_type)||(mp->cur_type!=mp_boolean_type) )
19992 mp_bad_binary(mp, p,c);
19993 else if ( value(p)==c+false_code-and_op ) mp->cur_exp=value(p);
19996 @ @<Additional cases of binary operators@>=
19998 if ( (mp->cur_type<mp_color_type)||(type(p)<mp_color_type) ) {
19999 mp_bad_binary(mp, p,times);
20000 } else if ( (mp->cur_type==mp_known)||(type(p)==mp_known) ) {
20001 @<Multiply when at least one operand is known@>;
20002 } else if ( (mp_nice_color_or_pair(mp, p,type(p))&&(mp->cur_type>mp_pair_type))
20003 ||(mp_nice_color_or_pair(mp, mp->cur_exp,mp->cur_type)&&
20004 (type(p)>mp_pair_type)) ) {
20005 mp_hard_times(mp, p); return;
20007 mp_bad_binary(mp, p,times);
20011 @ @<Multiply when at least one operand is known@>=
20013 if ( type(p)==mp_known ) {
20014 v=value(p); mp_free_node(mp, p,value_node_size);
20016 v=mp->cur_exp; mp_unstash_cur_exp(mp, p);
20018 if ( mp->cur_type==mp_known ) {
20019 mp->cur_exp=mp_take_scaled(mp, mp->cur_exp,v);
20020 } else if ( (mp->cur_type==mp_pair_type)||(mp->cur_type==mp_color_type)||
20021 (mp->cur_type==mp_cmykcolor_type) ) {
20022 p=value(mp->cur_exp)+mp->big_node_size[mp->cur_type];
20024 p=p-2; mp_dep_mult(mp, p,v,true);
20025 } while (p!=value(mp->cur_exp));
20027 mp_dep_mult(mp, null,v,true);
20032 @ @<Declare binary action...@>=
20033 void mp_dep_mult (MP mp,pointer p, integer v, boolean v_is_scaled) {
20034 pointer q; /* the dependency list being multiplied by |v| */
20035 small_number s,t; /* its type, before and after */
20038 } else if ( type(p)!=mp_known ) {
20041 if ( v_is_scaled ) value(p)=mp_take_scaled(mp, value(p),v);
20042 else value(p)=mp_take_fraction(mp, value(p),v);
20045 t=type(q); q=dep_list(q); s=t;
20046 if ( t==mp_dependent ) if ( v_is_scaled )
20047 if (mp_ab_vs_cd(mp, mp_max_coef(mp,q),abs(v),coef_bound-1,unity)>=0 )
20048 t=mp_proto_dependent;
20049 q=mp_p_times_v(mp, q,v,s,t,v_is_scaled);
20050 mp_dep_finish(mp, q,p,t);
20053 @ Here is a routine that is similar to |times|; but it is invoked only
20054 internally, when |v| is a |fraction| whose magnitude is at most~1,
20055 and when |cur_type>=mp_color_type|.
20057 @c void mp_frac_mult (MP mp,scaled n, scaled d) {
20058 /* multiplies |cur_exp| by |n/d| */
20059 pointer p; /* a pair node */
20060 pointer old_exp; /* a capsule to recycle */
20061 fraction v; /* |n/d| */
20062 if ( mp->internal[tracing_commands]>two ) {
20063 @<Trace the fraction multiplication@>;
20065 switch (mp->cur_type) {
20066 case mp_transform_type:
20067 case mp_color_type:
20068 case mp_cmykcolor_type:
20070 old_exp=mp_tarnished(mp, mp->cur_exp);
20072 case mp_independent: old_exp=diov; break;
20073 default: old_exp=null; break;
20075 if ( old_exp!=null ) {
20076 old_exp=mp->cur_exp; mp_make_exp_copy(mp, old_exp);
20078 v=mp_make_fraction(mp, n,d);
20079 if ( mp->cur_type==mp_known ) {
20080 mp->cur_exp=mp_take_fraction(mp, mp->cur_exp,v);
20081 } else if ( mp->cur_type<=mp_pair_type ) {
20082 p=value(mp->cur_exp)+mp->big_node_size[mp->cur_type];
20085 mp_dep_mult(mp, p,v,false);
20086 } while (p!=value(mp->cur_exp));
20088 mp_dep_mult(mp, null,v,false);
20090 if ( old_exp!=null ) {
20091 mp_recycle_value(mp, old_exp);
20092 mp_free_node(mp, old_exp,value_node_size);
20096 @ @<Trace the fraction multiplication@>=
20098 mp_begin_diagnostic(mp);
20099 mp_print_nl(mp, "{("); mp_print_scaled(mp,n); mp_print_char(mp,'/');
20100 mp_print_scaled(mp,d); mp_print(mp,")*("); mp_print_exp(mp,null,0);
20102 mp_end_diagnostic(mp, false);
20105 @ The |hard_times| routine multiplies a nice color or pair by a dependency list.
20107 @<Declare binary action procedures@>=
20108 void mp_hard_times (MP mp,pointer p) {
20109 pointer q; /* a copy of the dependent variable |p| */
20110 pointer r; /* a component of the big node for the nice color or pair */
20111 scaled v; /* the known value for |r| */
20112 if ( type(p)<=mp_pair_type ) {
20113 q=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, p); p=q;
20114 }; /* now |cur_type=mp_pair_type| or |cur_type=mp_color_type| */
20115 r=value(mp->cur_exp)+mp->big_node_size[mp->cur_type];
20120 if ( r==value(mp->cur_exp) )
20122 mp_new_dep(mp, r,mp_copy_dep_list(mp, dep_list(p)));
20123 mp_dep_mult(mp, r,v,true);
20125 mp->mem[value_loc(r)]=mp->mem[value_loc(p)];
20126 link(prev_dep(p))=r;
20127 mp_free_node(mp, p,value_node_size);
20128 mp_dep_mult(mp, r,v,true);
20131 @ @<Additional cases of binary operators@>=
20133 if ( (mp->cur_type!=mp_known)||(type(p)<mp_color_type) ) {
20134 mp_bad_binary(mp, p,over);
20136 v=mp->cur_exp; mp_unstash_cur_exp(mp, p);
20138 @<Squeal about division by zero@>;
20140 if ( mp->cur_type==mp_known ) {
20141 mp->cur_exp=mp_make_scaled(mp, mp->cur_exp,v);
20142 } else if ( mp->cur_type<=mp_pair_type ) {
20143 p=value(mp->cur_exp)+mp->big_node_size[mp->cur_type];
20145 p=p-2; mp_dep_div(mp, p,v);
20146 } while (p!=value(mp->cur_exp));
20148 mp_dep_div(mp, null,v);
20155 @ @<Declare binary action...@>=
20156 void mp_dep_div (MP mp,pointer p, scaled v) {
20157 pointer q; /* the dependency list being divided by |v| */
20158 small_number s,t; /* its type, before and after */
20159 if ( p==null ) q=mp->cur_exp;
20160 else if ( type(p)!=mp_known ) q=p;
20161 else { value(p)=mp_make_scaled(mp, value(p),v); return; };
20162 t=type(q); q=dep_list(q); s=t;
20163 if ( t==mp_dependent )
20164 if ( mp_ab_vs_cd(mp, mp_max_coef(mp,q),unity,coef_bound-1,abs(v))>=0 )
20165 t=mp_proto_dependent;
20166 q=mp_p_over_v(mp, q,v,s,t);
20167 mp_dep_finish(mp, q,p,t);
20170 @ @<Squeal about division by zero@>=
20172 exp_err("Division by zero");
20173 @.Division by zero@>
20174 help2("You're trying to divide the quantity shown above the error")
20175 ("message by zero. I'm going to divide it by one instead.");
20176 mp_put_get_error(mp);
20179 @ @<Additional cases of binary operators@>=
20182 if ( (mp->cur_type==mp_known)&&(type(p)==mp_known) ) {
20183 if ( c==pythag_add ) mp->cur_exp=mp_pyth_add(mp, value(p),mp->cur_exp);
20184 else mp->cur_exp=mp_pyth_sub(mp, value(p),mp->cur_exp);
20185 } else mp_bad_binary(mp, p,c);
20188 @ The next few sections of the program deal with affine transformations
20189 of coordinate data.
20191 @<Additional cases of binary operators@>=
20192 case rotated_by: case slanted_by:
20193 case scaled_by: case shifted_by: case transformed_by:
20194 case x_scaled: case y_scaled: case z_scaled:
20195 if ( type(p)==mp_path_type ) {
20196 path_trans(c,p); return;
20197 } else if ( type(p)==mp_pen_type ) {
20199 mp->cur_exp=mp_convex_hull(mp, mp->cur_exp);
20200 /* rounding error could destroy convexity */
20202 } else if ( (type(p)==mp_pair_type)||(type(p)==mp_transform_type) ) {
20203 mp_big_trans(mp, p,c);
20204 } else if ( type(p)==mp_picture_type ) {
20205 mp_do_edges_trans(mp, p,c); return;
20207 mp_bad_binary(mp, p,c);
20211 @ Let |c| be one of the eight transform operators. The procedure call
20212 |set_up_trans(c)| first changes |cur_exp| to a transform that corresponds to
20213 |c| and the original value of |cur_exp|. (In particular, |cur_exp| doesn't
20214 change at all if |c=transformed_by|.)
20216 Then, if all components of the resulting transform are |known|, they are
20217 moved to the global variables |txx|, |txy|, |tyx|, |tyy|, |tx|, |ty|;
20218 and |cur_exp| is changed to the known value zero.
20220 @<Declare binary action...@>=
20221 void mp_set_up_trans (MP mp,quarterword c) {
20222 pointer p,q,r; /* list manipulation registers */
20223 if ( (c!=transformed_by)||(mp->cur_type!=mp_transform_type) ) {
20224 @<Put the current transform into |cur_exp|@>;
20226 @<If the current transform is entirely known, stash it in global variables;
20227 otherwise |return|@>;
20236 scaled ty; /* current transform coefficients */
20238 @ @<Put the current transform...@>=
20240 p=mp_stash_cur_exp(mp);
20241 mp->cur_exp=mp_id_transform(mp);
20242 mp->cur_type=mp_transform_type;
20243 q=value(mp->cur_exp);
20245 @<For each of the eight cases, change the relevant fields of |cur_exp|
20247 but do nothing if capsule |p| doesn't have the appropriate type@>;
20248 }; /* there are no other cases */
20249 mp_disp_err(mp, p,"Improper transformation argument");
20250 @.Improper transformation argument@>
20251 help3("The expression shown above has the wrong type,")
20252 ("so I can\'t transform anything using it.")
20253 ("Proceed, and I'll omit the transformation.");
20254 mp_put_get_error(mp);
20256 mp_recycle_value(mp, p);
20257 mp_free_node(mp, p,value_node_size);
20260 @ @<If the current transform is entirely known, ...@>=
20261 q=value(mp->cur_exp); r=q+transform_node_size;
20264 if ( type(r)!=mp_known ) return;
20266 mp->txx=value(xx_part_loc(q));
20267 mp->txy=value(xy_part_loc(q));
20268 mp->tyx=value(yx_part_loc(q));
20269 mp->tyy=value(yy_part_loc(q));
20270 mp->tx=value(x_part_loc(q));
20271 mp->ty=value(y_part_loc(q));
20272 mp_flush_cur_exp(mp, 0)
20274 @ @<For each of the eight cases...@>=
20276 if ( type(p)==mp_known )
20277 @<Install sines and cosines, then |goto done|@>;
20280 if ( type(p)>mp_pair_type ) {
20281 mp_install(mp, xy_part_loc(q),p); goto DONE;
20285 if ( type(p)>mp_pair_type ) {
20286 mp_install(mp, xx_part_loc(q),p); mp_install(mp, yy_part_loc(q),p);
20291 if ( type(p)==mp_pair_type ) {
20292 r=value(p); mp_install(mp, x_part_loc(q),x_part_loc(r));
20293 mp_install(mp, y_part_loc(q),y_part_loc(r)); goto DONE;
20297 if ( type(p)>mp_pair_type ) {
20298 mp_install(mp, xx_part_loc(q),p); goto DONE;
20302 if ( type(p)>mp_pair_type ) {
20303 mp_install(mp, yy_part_loc(q),p); goto DONE;
20307 if ( type(p)==mp_pair_type )
20308 @<Install a complex multiplier, then |goto done|@>;
20310 case transformed_by:
20314 @ @<Install sines and cosines, then |goto done|@>=
20315 { mp_n_sin_cos(mp, (value(p) % three_sixty_units)*16);
20316 value(xx_part_loc(q))=mp_round_fraction(mp, mp->n_cos);
20317 value(yx_part_loc(q))=mp_round_fraction(mp, mp->n_sin);
20318 value(xy_part_loc(q))=-value(yx_part_loc(q));
20319 value(yy_part_loc(q))=value(xx_part_loc(q));
20323 @ @<Install a complex multiplier, then |goto done|@>=
20326 mp_install(mp, xx_part_loc(q),x_part_loc(r));
20327 mp_install(mp, yy_part_loc(q),x_part_loc(r));
20328 mp_install(mp, yx_part_loc(q),y_part_loc(r));
20329 if ( type(y_part_loc(r))==mp_known ) negate(value(y_part_loc(r)));
20330 else mp_negate_dep_list(mp, dep_list(y_part_loc(r)));
20331 mp_install(mp, xy_part_loc(q),y_part_loc(r));
20335 @ Procedure |set_up_known_trans| is like |set_up_trans|, but it
20336 insists that the transformation be entirely known.
20338 @<Declare binary action...@>=
20339 void mp_set_up_known_trans (MP mp,quarterword c) {
20340 mp_set_up_trans(mp, c);
20341 if ( mp->cur_type!=mp_known ) {
20342 exp_err("Transform components aren't all known");
20343 @.Transform components...@>
20344 help3("I'm unable to apply a partially specified transformation")
20345 ("except to a fully known pair or transform.")
20346 ("Proceed, and I'll omit the transformation.");
20347 mp_put_get_flush_error(mp, 0);
20348 mp->txx=unity; mp->txy=0; mp->tyx=0; mp->tyy=unity;
20349 mp->tx=0; mp->ty=0;
20353 @ Here's a procedure that applies the transform |txx..ty| to a pair of
20354 coordinates in locations |p| and~|q|.
20356 @<Declare binary action...@>=
20357 void mp_trans (MP mp,pointer p, pointer q) {
20358 scaled v; /* the new |x| value */
20359 v=mp_take_scaled(mp, mp->mem[p].sc,mp->txx)+
20360 mp_take_scaled(mp, mp->mem[q].sc,mp->txy)+mp->tx;
20361 mp->mem[q].sc=mp_take_scaled(mp, mp->mem[p].sc,mp->tyx)+
20362 mp_take_scaled(mp, mp->mem[q].sc,mp->tyy)+mp->ty;
20366 @ The simplest transformation procedure applies a transform to all
20367 coordinates of a path. The |path_trans(c)(p)| macro applies
20368 a transformation defined by |cur_exp| and the transform operator |c|
20371 @d path_trans(A,B) { mp_set_up_known_trans(mp, (A));
20372 mp_unstash_cur_exp(mp, (B));
20373 mp_do_path_trans(mp, mp->cur_exp); }
20375 @<Declare binary action...@>=
20376 void mp_do_path_trans (MP mp,pointer p) {
20377 pointer q; /* list traverser */
20380 if ( left_type(q)!=endpoint )
20381 mp_trans(mp, q+3,q+4); /* that's |left_x| and |left_y| */
20382 mp_trans(mp, q+1,q+2); /* that's |x_coord| and |y_coord| */
20383 if ( right_type(q)!=endpoint )
20384 mp_trans(mp, q+5,q+6); /* that's |right_x| and |right_y| */
20385 @^data structure assumptions@>
20390 @ Transforming a pen is very similar, except that there are no |left_type|
20391 and |right_type| fields.
20393 @d pen_trans(A,B) { mp_set_up_known_trans(mp, (A));
20394 mp_unstash_cur_exp(mp, (B));
20395 mp_do_pen_trans(mp, mp->cur_exp); }
20397 @<Declare binary action...@>=
20398 void mp_do_pen_trans (MP mp,pointer p) {
20399 pointer q; /* list traverser */
20400 if ( pen_is_elliptical(p) ) {
20401 mp_trans(mp, p+3,p+4); /* that's |left_x| and |left_y| */
20402 mp_trans(mp, p+5,p+6); /* that's |right_x| and |right_y| */
20406 mp_trans(mp, q+1,q+2); /* that's |x_coord| and |y_coord| */
20407 @^data structure assumptions@>
20412 @ The next transformation procedure applies to edge structures. It will do
20413 any transformation, but the results may be substandard if the picture contains
20414 text that uses downloaded bitmap fonts. The binary action procedure is
20415 |do_edges_trans|, but we also need a function that just scales a picture.
20416 That routine is |scale_edges|. Both it and the underlying routine |edges_trans|
20417 should be thought of as procedures that update an edge structure |h|, except
20418 that they have to return a (possibly new) structure because of the need to call
20421 @<Declare binary action...@>=
20422 pointer mp_edges_trans (MP mp, pointer h) {
20423 pointer q; /* the object being transformed */
20424 pointer r,s; /* for list manipulation */
20425 scaled sx,sy; /* saved transformation parameters */
20426 scaled sqdet; /* square root of determinant for |dash_scale| */
20427 integer sgndet; /* sign of the determinant */
20428 scaled v; /* a temporary value */
20429 h=mp_private_edges(mp, h);
20430 sqdet=mp_sqrt_det(mp, mp->txx,mp->txy,mp->tyx,mp->tyy);
20431 sgndet=mp_ab_vs_cd(mp, mp->txx,mp->tyy,mp->txy,mp->tyx);
20432 if ( dash_list(h)!=null_dash ) {
20433 @<Try to transform the dash list of |h|@>;
20435 @<Make the bounding box of |h| unknown if it can't be updated properly
20436 without scanning the whole structure@>;
20437 q=link(dummy_loc(h));
20438 while ( q!=null ) {
20439 @<Transform graphical object |q|@>;
20444 void mp_do_edges_trans (MP mp,pointer p, quarterword c) {
20445 mp_set_up_known_trans(mp, c);
20446 value(p)=mp_edges_trans(mp, value(p));
20447 mp_unstash_cur_exp(mp, p);
20449 void mp_scale_edges (MP mp) {
20450 mp->txx=mp->se_sf; mp->tyy=mp->se_sf;
20451 mp->txy=0; mp->tyx=0; mp->tx=0; mp->ty=0;
20452 mp->se_pic=mp_edges_trans(mp, mp->se_pic);
20455 @ @<Try to transform the dash list of |h|@>=
20456 if ( (mp->txy!=0)||(mp->tyx!=0)||
20457 (mp->ty!=0)||(abs(mp->txx)!=abs(mp->tyy))) {
20458 mp_flush_dash_list(mp, h);
20460 if ( mp->txx<0 ) { @<Reverse the dash list of |h|@>; }
20461 @<Scale the dash list by |txx| and shift it by |tx|@>;
20462 dash_y(h)=mp_take_scaled(mp, dash_y(h),abs(mp->tyy));
20465 @ @<Reverse the dash list of |h|@>=
20468 dash_list(h)=null_dash;
20469 while ( r!=null_dash ) {
20471 v=start_x(s); start_x(s)=stop_x(s); stop_x(s)=v;
20472 link(s)=dash_list(h);
20477 @ @<Scale the dash list by |txx| and shift it by |tx|@>=
20479 while ( r!=null_dash ) {
20480 start_x(r)=mp_take_scaled(mp, start_x(r),mp->txx)+mp->tx;
20481 stop_x(r)=mp_take_scaled(mp, stop_x(r),mp->txx)+mp->tx;
20485 @ @<Make the bounding box of |h| unknown if it can't be updated properly...@>=
20486 if ( (mp->txx==0)&&(mp->tyy==0) ) {
20487 @<Swap the $x$ and $y$ parameters in the bounding box of |h|@>;
20488 } else if ( (mp->txy!=0)||(mp->tyx!=0) ) {
20489 mp_init_bbox(mp, h);
20492 if ( minx_val(h)<=maxx_val(h) ) {
20493 @<Scale the bounding box by |txx+txy| and |tyx+tyy|; then shift by
20500 @ @<Swap the $x$ and $y$ parameters in the bounding box of |h|@>=
20502 v=minx_val(h); minx_val(h)=miny_val(h); miny_val(h)=v;
20503 v=maxx_val(h); maxx_val(h)=maxy_val(h); maxy_val(h)=v;
20506 @ The sum ``|txx+txy|'' is whichever of |txx| or |txy| is nonzero. The other
20509 @<Scale the bounding box by |txx+txy| and |tyx+tyy|; then shift...@>=
20511 minx_val(h)=mp_take_scaled(mp, minx_val(h),mp->txx+mp->txy)+mp->tx;
20512 maxx_val(h)=mp_take_scaled(mp, maxx_val(h),mp->txx+mp->txy)+mp->tx;
20513 miny_val(h)=mp_take_scaled(mp, miny_val(h),mp->tyx+mp->tyy)+mp->ty;
20514 maxy_val(h)=mp_take_scaled(mp, maxy_val(h),mp->tyx+mp->tyy)+mp->ty;
20515 if ( mp->txx+mp->txy<0 ) {
20516 v=minx_val(h); minx_val(h)=maxx_val(h); maxx_val(h)=v;
20518 if ( mp->tyx+mp->tyy<0 ) {
20519 v=miny_val(h); miny_val(h)=maxy_val(h); maxy_val(h)=v;
20523 @ Now we ready for the main task of transforming the graphical objects in edge
20526 @<Transform graphical object |q|@>=
20528 case fill_code: case stroked_code:
20529 mp_do_path_trans(mp, path_p(q));
20530 @<Transform |pen_p(q)|, making sure polygonal pens stay counter-clockwise@>;
20532 case mp_start_clip_code: case mp_start_bounds_code:
20533 mp_do_path_trans(mp, path_p(q));
20537 @<Transform the compact transformation starting at |r|@>;
20539 case mp_stop_clip_code: case mp_stop_bounds_code:
20541 } /* there are no other cases */
20543 @ Note that the shift parameters |(tx,ty)| apply only to the path being stroked.
20544 The |dash_scale| has to be adjusted to scale the dash lengths in |dash_p(q)|
20545 since the \ps\ output procedures will try to compensate for the transformation
20546 we are applying to |pen_p(q)|. Since this compensation is based on the square
20547 root of the determinant, |sqdet| is the appropriate factor.
20549 @<Transform |pen_p(q)|, making sure...@>=
20550 if ( pen_p(q)!=null ) {
20551 sx=mp->tx; sy=mp->ty;
20552 mp->tx=0; mp->ty=0;
20553 mp_do_pen_trans(mp, pen_p(q));
20554 if ( ((type(q)==stroked_code)&&(dash_p(q)!=null)) )
20555 dash_scale(q)=mp_take_scaled(mp, dash_scale(q),sqdet);
20556 if ( ! pen_is_elliptical(pen_p(q)) )
20558 pen_p(q)=mp_make_pen(mp, mp_copy_path(mp, pen_p(q)),true);
20559 /* this unreverses the pen */
20560 mp->tx=sx; mp->ty=sy;
20563 @ This uses the fact that transformations are stored in the order
20564 |(tx,ty,txx,txy,tyx,tyy)|.
20565 @^data structure assumptions@>
20567 @<Transform the compact transformation starting at |r|@>=
20568 mp_trans(mp, r,r+1);
20569 sx=mp->tx; sy=mp->ty;
20570 mp->tx=0; mp->ty=0;
20571 mp_trans(mp, r+2,r+4);
20572 mp_trans(mp, r+3,r+5);
20573 mp->tx=sx; mp->ty=sy
20575 @ The hard cases of transformation occur when big nodes are involved,
20576 and when some of their components are unknown.
20578 @<Declare binary action...@>=
20579 @<Declare subroutines needed by |big_trans|@>;
20580 void mp_big_trans (MP mp,pointer p, quarterword c) {
20581 pointer q,r,pp,qq; /* list manipulation registers */
20582 small_number s; /* size of a big node */
20583 s=mp->big_node_size[type(p)]; q=value(p); r=q+s;
20586 if ( type(r)!=mp_known ) {
20587 @<Transform an unknown big node and |return|@>;
20590 @<Transform a known big node@>;
20591 }; /* node |p| will now be recycled by |do_binary| */
20593 @ @<Transform an unknown big node and |return|@>=
20595 mp_set_up_known_trans(mp, c); mp_make_exp_copy(mp, p);
20596 r=value(mp->cur_exp);
20597 if ( mp->cur_type==mp_transform_type ) {
20598 mp_bilin1(mp, yy_part_loc(r),mp->tyy,xy_part_loc(q),mp->tyx,0);
20599 mp_bilin1(mp, yx_part_loc(r),mp->tyy,xx_part_loc(q),mp->tyx,0);
20600 mp_bilin1(mp, xy_part_loc(r),mp->txx,yy_part_loc(q),mp->txy,0);
20601 mp_bilin1(mp, xx_part_loc(r),mp->txx,yx_part_loc(q),mp->txy,0);
20603 mp_bilin1(mp, y_part_loc(r),mp->tyy,x_part_loc(q),mp->tyx,mp->ty);
20604 mp_bilin1(mp, x_part_loc(r),mp->txx,y_part_loc(q),mp->txy,mp->tx);
20608 @ Let |p| point to a two-word value field inside a big node of |cur_exp|,
20609 and let |q| point to a another value field. The |bilin1| procedure
20610 replaces |p| by $p\cdot t+q\cdot u+\delta$.
20612 @<Declare subroutines needed by |big_trans|@>=
20613 void mp_bilin1 (MP mp, pointer p, scaled t, pointer q,
20614 scaled u, scaled delta) {
20615 pointer r; /* list traverser */
20616 if ( t!=unity ) mp_dep_mult(mp, p,t,true);
20618 if ( type(q)==mp_known ) {
20619 delta+=mp_take_scaled(mp, value(q),u);
20621 @<Ensure that |type(p)=mp_proto_dependent|@>;
20622 dep_list(p)=mp_p_plus_fq(mp, dep_list(p),u,dep_list(q),
20623 mp_proto_dependent,type(q));
20626 if ( type(p)==mp_known ) {
20630 while ( info(r)!=null ) r=link(r);
20632 if ( r!=dep_list(p) ) value(r)=delta;
20633 else { mp_recycle_value(mp, p); type(p)=mp_known; value(p)=delta; };
20635 if ( mp->fix_needed ) mp_fix_dependencies(mp);
20638 @ @<Ensure that |type(p)=mp_proto_dependent|@>=
20639 if ( type(p)!=mp_proto_dependent ) {
20640 if ( type(p)==mp_known )
20641 mp_new_dep(mp, p,mp_const_dependency(mp, value(p)));
20643 dep_list(p)=mp_p_times_v(mp, dep_list(p),unity,mp_dependent,
20644 mp_proto_dependent,true);
20645 type(p)=mp_proto_dependent;
20648 @ @<Transform a known big node@>=
20649 mp_set_up_trans(mp, c);
20650 if ( mp->cur_type==mp_known ) {
20651 @<Transform known by known@>;
20653 pp=mp_stash_cur_exp(mp); qq=value(pp);
20654 mp_make_exp_copy(mp, p); r=value(mp->cur_exp);
20655 if ( mp->cur_type==mp_transform_type ) {
20656 mp_bilin2(mp, yy_part_loc(r),yy_part_loc(qq),
20657 value(xy_part_loc(q)),yx_part_loc(qq),null);
20658 mp_bilin2(mp, yx_part_loc(r),yy_part_loc(qq),
20659 value(xx_part_loc(q)),yx_part_loc(qq),null);
20660 mp_bilin2(mp, xy_part_loc(r),xx_part_loc(qq),
20661 value(yy_part_loc(q)),xy_part_loc(qq),null);
20662 mp_bilin2(mp, xx_part_loc(r),xx_part_loc(qq),
20663 value(yx_part_loc(q)),xy_part_loc(qq),null);
20665 mp_bilin2(mp, y_part_loc(r),yy_part_loc(qq),
20666 value(x_part_loc(q)),yx_part_loc(qq),y_part_loc(qq));
20667 mp_bilin2(mp, x_part_loc(r),xx_part_loc(qq),
20668 value(y_part_loc(q)),xy_part_loc(qq),x_part_loc(qq));
20669 mp_recycle_value(mp, pp); mp_free_node(mp, pp,value_node_size);
20672 @ Let |p| be a |mp_proto_dependent| value whose dependency list ends
20673 at |dep_final|. The following procedure adds |v| times another
20674 numeric quantity to~|p|.
20676 @<Declare subroutines needed by |big_trans|@>=
20677 void mp_add_mult_dep (MP mp,pointer p, scaled v, pointer r) {
20678 if ( type(r)==mp_known ) {
20679 value(mp->dep_final)+=mp_take_scaled(mp, value(r),v);
20681 dep_list(p)=mp_p_plus_fq(mp, dep_list(p),v,dep_list(r),
20682 mp_proto_dependent,type(r));
20683 if ( mp->fix_needed ) mp_fix_dependencies(mp);
20687 @ The |bilin2| procedure is something like |bilin1|, but with known
20688 and unknown quantities reversed. Parameter |p| points to a value field
20689 within the big node for |cur_exp|; and |type(p)=mp_known|. Parameters
20690 |t| and~|u| point to value fields elsewhere; so does parameter~|q|,
20691 unless it is |null| (which stands for zero). Location~|p| will be
20692 replaced by $p\cdot t+v\cdot u+q$.
20694 @<Declare subroutines needed by |big_trans|@>=
20695 void mp_bilin2 (MP mp,pointer p, pointer t, scaled v,
20696 pointer u, pointer q) {
20697 scaled vv; /* temporary storage for |value(p)| */
20698 vv=value(p); type(p)=mp_proto_dependent;
20699 mp_new_dep(mp, p,mp_const_dependency(mp, 0)); /* this sets |dep_final| */
20701 mp_add_mult_dep(mp, p,vv,t); /* |dep_final| doesn't change */
20702 if ( v!=0 ) mp_add_mult_dep(mp, p,v,u);
20703 if ( q!=null ) mp_add_mult_dep(mp, p,unity,q);
20704 if ( dep_list(p)==mp->dep_final ) {
20705 vv=value(mp->dep_final); mp_recycle_value(mp, p);
20706 type(p)=mp_known; value(p)=vv;
20710 @ @<Transform known by known@>=
20712 mp_make_exp_copy(mp, p); r=value(mp->cur_exp);
20713 if ( mp->cur_type==mp_transform_type ) {
20714 mp_bilin3(mp, yy_part_loc(r),mp->tyy,value(xy_part_loc(q)),mp->tyx,0);
20715 mp_bilin3(mp, yx_part_loc(r),mp->tyy,value(xx_part_loc(q)),mp->tyx,0);
20716 mp_bilin3(mp, xy_part_loc(r),mp->txx,value(yy_part_loc(q)),mp->txy,0);
20717 mp_bilin3(mp, xx_part_loc(r),mp->txx,value(yx_part_loc(q)),mp->txy,0);
20719 mp_bilin3(mp, y_part_loc(r),mp->tyy,value(x_part_loc(q)),mp->tyx,mp->ty);
20720 mp_bilin3(mp, x_part_loc(r),mp->txx,value(y_part_loc(q)),mp->txy,mp->tx);
20723 @ Finally, in |bilin3| everything is |known|.
20725 @<Declare subroutines needed by |big_trans|@>=
20726 void mp_bilin3 (MP mp,pointer p, scaled t,
20727 scaled v, scaled u, scaled delta) {
20729 delta+=mp_take_scaled(mp, value(p),t);
20732 if ( u!=0 ) value(p)=delta+mp_take_scaled(mp, v,u);
20733 else value(p)=delta;
20736 @ @<Additional cases of binary operators@>=
20738 if ( (mp->cur_type==mp_string_type)&&(type(p)==mp_string_type) ) mp_cat(mp, p);
20739 else mp_bad_binary(mp, p,concatenate);
20742 if ( mp_nice_pair(mp, p,type(p))&&(mp->cur_type==mp_string_type) )
20743 mp_chop_string(mp, value(p));
20744 else mp_bad_binary(mp, p,substring_of);
20747 if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp);
20748 if ( mp_nice_pair(mp, p,type(p))&&(mp->cur_type==mp_path_type) )
20749 mp_chop_path(mp, value(p));
20750 else mp_bad_binary(mp, p,subpath_of);
20753 @ @<Declare binary action...@>=
20754 void mp_cat (MP mp,pointer p) {
20755 str_number a,b; /* the strings being concatenated */
20756 pool_pointer k; /* index into |str_pool| */
20757 a=value(p); b=mp->cur_exp; str_room(length(a)+length(b));
20758 for (k=mp->str_start[a];k<=str_stop(a)-1;k++) {
20759 append_char(mp->str_pool[k]);
20761 for (k=mp->str_start[b];k<=str_stop(b)-1;k++) {
20762 append_char(mp->str_pool[k]);
20764 mp->cur_exp=mp_make_string(mp); delete_str_ref(b);
20767 @ @<Declare binary action...@>=
20768 void mp_chop_string (MP mp,pointer p) {
20769 integer a, b; /* start and stop points */
20770 integer l; /* length of the original string */
20771 integer k; /* runs from |a| to |b| */
20772 str_number s; /* the original string */
20773 boolean reversed; /* was |a>b|? */
20774 a=mp_round_unscaled(mp, value(x_part_loc(p)));
20775 b=mp_round_unscaled(mp, value(y_part_loc(p)));
20776 if ( a<=b ) reversed=false;
20777 else { reversed=true; k=a; a=b; b=k; };
20778 s=mp->cur_exp; l=length(s);
20789 for (k=mp->str_start[s]+b-1;k>=mp->str_start[s]+a;k--) {
20790 append_char(mp->str_pool[k]);
20793 for (k=mp->str_start[s]+a;k<=mp->str_start[s]+b-1;k++) {
20794 append_char(mp->str_pool[k]);
20797 mp->cur_exp=mp_make_string(mp); delete_str_ref(s);
20800 @ @<Declare binary action...@>=
20801 void mp_chop_path (MP mp,pointer p) {
20802 pointer q; /* a knot in the original path */
20803 pointer pp,qq,rr,ss; /* link variables for copies of path nodes */
20804 scaled a,b,k,l; /* indices for chopping */
20805 boolean reversed; /* was |a>b|? */
20806 l=mp_path_length(mp); a=value(x_part_loc(p)); b=value(y_part_loc(p));
20807 if ( a<=b ) reversed=false;
20808 else { reversed=true; k=a; a=b; b=k; };
20809 @<Dispense with the cases |a<0| and/or |b>l|@>;
20811 while ( a>=unity ) {
20812 q=link(q); a=a-unity; b=b-unity;
20815 @<Construct a path from |pp| to |qq| of length zero@>;
20817 @<Construct a path from |pp| to |qq| of length $\lceil b\rceil$@>;
20819 left_type(pp)=endpoint; right_type(qq)=endpoint; link(qq)=pp;
20820 mp_toss_knot_list(mp, mp->cur_exp);
20822 mp->cur_exp=link(mp_htap_ypoc(mp, pp)); mp_toss_knot_list(mp, pp);
20828 @ @<Dispense with the cases |a<0| and/or |b>l|@>=
20830 if ( left_type(mp->cur_exp)==endpoint ) {
20831 a=0; if ( b<0 ) b=0;
20833 do { a=a+l; b=b+l; } while (a<0); /* a cycle always has length |l>0| */
20837 if ( left_type(mp->cur_exp)==endpoint ) {
20838 b=l; if ( a>l ) a=l;
20846 @ @<Construct a path from |pp| to |qq| of length $\lceil b\rceil$@>=
20848 pp=mp_copy_knot(mp, q); qq=pp;
20850 q=link(q); rr=qq; qq=mp_copy_knot(mp, q); link(rr)=qq; b=b-unity;
20853 ss=pp; pp=link(pp);
20854 mp_split_cubic(mp, ss,a*010000); pp=link(ss);
20855 mp_free_node(mp, ss,knot_node_size);
20857 b=mp_make_scaled(mp, b,unity-a); rr=pp;
20861 mp_split_cubic(mp, rr,(b+unity)*010000);
20862 mp_free_node(mp, qq,knot_node_size);
20867 @ @<Construct a path from |pp| to |qq| of length zero@>=
20869 if ( a>0 ) { mp_split_cubic(mp, q,a*010000); q=link(q); };
20870 pp=mp_copy_knot(mp, q); qq=pp;
20873 @ @<Additional cases of binary operators@>=
20874 case point_of: case precontrol_of: case postcontrol_of:
20875 if ( mp->cur_type==mp_pair_type )
20876 mp_pair_to_path(mp);
20877 if ( (mp->cur_type==mp_path_type)&&(type(p)==mp_known) )
20878 mp_find_point(mp, value(p),c);
20880 mp_bad_binary(mp, p,c);
20882 case pen_offset_of:
20883 if ( (mp->cur_type==mp_pen_type)&& mp_nice_pair(mp, p,type(p)) )
20884 mp_set_up_offset(mp, value(p));
20886 mp_bad_binary(mp, p,pen_offset_of);
20888 case direction_time_of:
20889 if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp);
20890 if ( (mp->cur_type==mp_path_type)&& mp_nice_pair(mp, p,type(p)) )
20891 mp_set_up_direction_time(mp, value(p));
20893 mp_bad_binary(mp, p,direction_time_of);
20896 @ @<Declare binary action...@>=
20897 void mp_set_up_offset (MP mp,pointer p) {
20898 mp_find_offset(mp, value(x_part_loc(p)),value(y_part_loc(p)),mp->cur_exp);
20899 mp_pair_value(mp, mp->cur_x,mp->cur_y);
20901 void mp_set_up_direction_time (MP mp,pointer p) {
20902 mp_flush_cur_exp(mp, mp_find_direction_time(mp, value(x_part_loc(p)),
20903 value(y_part_loc(p)),mp->cur_exp));
20906 @ @<Declare binary action...@>=
20907 void mp_find_point (MP mp,scaled v, quarterword c) {
20908 pointer p; /* the path */
20909 scaled n; /* its length */
20911 if ( left_type(p)==endpoint ) n=-unity; else n=0;
20912 do { p=link(p); n=n+unity; } while (p!=mp->cur_exp);
20915 } else if ( v<0 ) {
20916 if ( left_type(p)==endpoint ) v=0;
20917 else v=n-1-((-v-1) % n);
20918 } else if ( v>n ) {
20919 if ( left_type(p)==endpoint ) v=n;
20923 while ( v>=unity ) { p=link(p); v=v-unity; };
20925 @<Insert a fractional node by splitting the cubic@>;
20927 @<Set the current expression to the desired path coordinates@>;
20930 @ @<Insert a fractional node...@>=
20931 { mp_split_cubic(mp, p,v*010000); p=link(p); }
20933 @ @<Set the current expression to the desired path coordinates...@>=
20936 mp_pair_value(mp, x_coord(p),y_coord(p));
20938 case precontrol_of:
20939 if ( left_type(p)==endpoint ) mp_pair_value(mp, x_coord(p),y_coord(p));
20940 else mp_pair_value(mp, left_x(p),left_y(p));
20942 case postcontrol_of:
20943 if ( right_type(p)==endpoint ) mp_pair_value(mp, x_coord(p),y_coord(p));
20944 else mp_pair_value(mp, right_x(p),right_y(p));
20946 } /* there are no other cases */
20948 @ @<Additional cases of binary operators@>=
20950 if ( mp->cur_type==mp_pair_type )
20951 mp_pair_to_path(mp);
20952 if ( (mp->cur_type==mp_path_type)&&(type(p)==mp_known) )
20953 mp_flush_cur_exp(mp, mp_get_arc_time(mp, mp->cur_exp,value(p)));
20955 mp_bad_binary(mp, p,c);
20958 @ @<Additional cases of bin...@>=
20960 if ( type(p)==mp_pair_type ) {
20961 q=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, p);
20962 mp_pair_to_path(mp); p=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, q);
20964 if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp);
20965 if ( (mp->cur_type==mp_path_type)&&(type(p)==mp_path_type) ) {
20966 mp_path_intersection(mp, value(p),mp->cur_exp);
20967 mp_pair_value(mp, mp->cur_t,mp->cur_tt);
20969 mp_bad_binary(mp, p,intersect);
20973 @ @<Additional cases of bin...@>=
20975 if ( (mp->cur_type!=mp_string_type)||(type(p)!=mp_string_type))
20976 mp_bad_binary(mp, p,in_font);
20977 else { mp_do_infont(mp, p); return; }
20980 @ Function |new_text_node| owns the reference count for its second argument
20981 (the text string) but not its first (the font name).
20983 @<Declare binary action...@>=
20984 void mp_do_infont (MP mp,pointer p) {
20986 q=mp_get_node(mp, edge_header_size);
20987 mp_init_edges(mp, q);
20988 link(obj_tail(q))=mp_new_text_node(mp, str(mp->cur_exp),value(p));
20989 obj_tail(q)=link(obj_tail(q));
20990 mp_free_node(mp, p,value_node_size);
20991 mp_flush_cur_exp(mp, q);
20992 mp->cur_type=mp_picture_type;
20995 @* \[40] Statements and commands.
20996 The chief executive of \MP\ is the |do_statement| routine, which
20997 contains the master switch that causes all the various pieces of \MP\
20998 to do their things, in the right order.
21000 In a sense, this is the grand climax of the program: It applies all the
21001 tools that we have worked so hard to construct. In another sense, this is
21002 the messiest part of the program: It necessarily refers to other pieces
21003 of code all over the place, so that a person can't fully understand what is
21004 going on without paging back and forth to be reminded of conventions that
21005 are defined elsewhere. We are now at the hub of the web.
21007 The structure of |do_statement| itself is quite simple. The first token
21008 of the statement is fetched using |get_x_next|. If it can be the first
21009 token of an expression, we look for an equation, an assignment, or a
21010 title. Otherwise we use a \&{case} construction to branch at high speed to
21011 the appropriate routine for various and sundry other types of commands,
21012 each of which has an ``action procedure'' that does the necessary work.
21014 The program uses the fact that
21015 $$\hbox{|min_primary_command=max_statement_command=type_name|}$$
21016 to interpret a statement that starts with, e.g., `\&{string}',
21017 as a type declaration rather than a boolean expression.
21019 @c void mp_do_statement (MP mp) { /* governs \MP's activities */
21020 mp->cur_type=mp_vacuous; mp_get_x_next(mp);
21021 if ( mp->cur_cmd>max_primary_command ) {
21022 @<Worry about bad statement@>;
21023 } else if ( mp->cur_cmd>max_statement_command ) {
21024 @<Do an equation, assignment, title, or
21025 `$\langle\,$expression$\,\rangle\,$\&{endgroup}'@>;
21027 @<Do a statement that doesn't begin with an expression@>;
21029 if ( mp->cur_cmd<semicolon )
21030 @<Flush unparsable junk that was found after the statement@>;
21034 @ @<Declarations@>=
21035 @<Declare action procedures for use by |do_statement|@>;
21037 @ The only command codes |>max_primary_command| that can be present
21038 at the beginning of a statement are |semicolon| and higher; these
21039 occur when the statement is null.
21041 @<Worry about bad statement@>=
21043 if ( mp->cur_cmd<semicolon ) {
21044 print_err("A statement can't begin with `");
21045 @.A statement can't begin with x@>
21046 mp_print_cmd_mod(mp, mp->cur_cmd,mp->cur_mod); mp_print_char(mp, '\'');
21047 help5("I was looking for the beginning of a new statement.")
21048 ("If you just proceed without changing anything, I'll ignore")
21049 ("everything up to the next `;'. Please insert a semicolon")
21050 ("now in front of anything that you don't want me to delete.")
21051 ("(See Chapter 27 of The METAFONTbook for an example.)");
21052 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
21053 mp_back_error(mp); mp_get_x_next(mp);
21057 @ The help message printed here says that everything is flushed up to
21058 a semicolon, but actually the commands |end_group| and |stop| will
21059 also terminate a statement.
21061 @<Flush unparsable junk that was found after the statement@>=
21063 print_err("Extra tokens will be flushed");
21064 @.Extra tokens will be flushed@>
21065 help6("I've just read as much of that statement as I could fathom,")
21066 ("so a semicolon should have been next. It's very puzzling...")
21067 ("but I'll try to get myself back together, by ignoring")
21068 ("everything up to the next `;'. Please insert a semicolon")
21069 ("now in front of anything that you don't want me to delete.")
21070 ("(See Chapter 27 of The METAFONTbook for an example.)");
21071 @:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
21072 mp_back_error(mp); mp->scanner_status=flushing;
21075 @<Decrease the string reference count...@>;
21076 } while (! end_of_statement); /* |cur_cmd=semicolon|, |end_group|, or |stop| */
21077 mp->scanner_status=normal;
21080 @ If |do_statement| ends with |cur_cmd=end_group|, we should have
21081 |cur_type=mp_vacuous| unless the statement was simply an expression;
21082 in the latter case, |cur_type| and |cur_exp| should represent that
21085 @<Do a statement that doesn't...@>=
21087 if ( mp->internal[tracing_commands]>0 )
21089 switch (mp->cur_cmd ) {
21090 case type_name:mp_do_type_declaration(mp); break;
21092 if ( mp->cur_mod>var_def ) mp_make_op_def(mp);
21093 else if ( mp->cur_mod>end_def ) mp_scan_def(mp);
21095 @<Cases of |do_statement| that invoke particular commands@>;
21096 } /* there are no other cases */
21097 mp->cur_type=mp_vacuous;
21100 @ The most important statements begin with expressions.
21102 @<Do an equation, assignment, title, or...@>=
21104 mp->var_flag=assignment; mp_scan_expression(mp);
21105 if ( mp->cur_cmd<end_group ) {
21106 if ( mp->cur_cmd==equals ) mp_do_equation(mp);
21107 else if ( mp->cur_cmd==assignment ) mp_do_assignment(mp);
21108 else if ( mp->cur_type==mp_string_type ) {@<Do a title@> ; }
21109 else if ( mp->cur_type!=mp_vacuous ){
21110 exp_err("Isolated expression");
21111 @.Isolated expression@>
21112 help3("I couldn't find an `=' or `:=' after the")
21113 ("expression that is shown above this error message,")
21114 ("so I guess I'll just ignore it and carry on.");
21115 mp_put_get_error(mp);
21117 mp_flush_cur_exp(mp, 0); mp->cur_type=mp_vacuous;
21123 if ( mp->internal[tracing_titles]>0 ) {
21124 mp_print_nl(mp, ""); mp_print_str(mp, mp->cur_exp); update_terminal;
21128 @ Equations and assignments are performed by the pair of mutually recursive
21130 routines |do_equation| and |do_assignment|. These routines are called when
21131 |cur_cmd=equals| and when |cur_cmd=assignment|, respectively; the left-hand
21132 side is in |cur_type| and |cur_exp|, while the right-hand side is yet
21133 to be scanned. After the routines are finished, |cur_type| and |cur_exp|
21134 will be equal to the right-hand side (which will normally be equal
21135 to the left-hand side).
21137 @<Declare action procedures for use by |do_statement|@>=
21138 @<Declare the procedure called |try_eq|@>;
21139 @<Declare the procedure called |make_eq|@>;
21140 void mp_do_equation (MP mp) ;
21143 void mp_do_equation (MP mp) {
21144 pointer lhs; /* capsule for the left-hand side */
21145 pointer p; /* temporary register */
21146 lhs=mp_stash_cur_exp(mp); mp_get_x_next(mp);
21147 mp->var_flag=assignment; mp_scan_expression(mp);
21148 if ( mp->cur_cmd==equals ) mp_do_equation(mp);
21149 else if ( mp->cur_cmd==assignment ) mp_do_assignment(mp);
21150 if ( mp->internal[tracing_commands]>two )
21151 @<Trace the current equation@>;
21152 if ( mp->cur_type==mp_unknown_path ) if ( type(lhs)==mp_pair_type ) {
21153 p=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, lhs); lhs=p;
21154 }; /* in this case |make_eq| will change the pair to a path */
21155 mp_make_eq(mp, lhs); /* equate |lhs| to |(cur_type,cur_exp)| */
21158 @ And |do_assignment| is similar to |do_expression|:
21161 void mp_do_assignment (MP mp);
21163 @ @<Declare action procedures for use by |do_statement|@>=
21164 void mp_do_assignment (MP mp) ;
21167 void mp_do_assignment (MP mp) {
21168 pointer lhs; /* token list for the left-hand side */
21169 pointer p; /* where the left-hand value is stored */
21170 pointer q; /* temporary capsule for the right-hand value */
21171 if ( mp->cur_type!=mp_token_list ) {
21172 exp_err("Improper `:=' will be changed to `='");
21174 help2("I didn't find a variable name at the left of the `:=',")
21175 ("so I'm going to pretend that you said `=' instead.");
21176 mp_error(mp); mp_do_equation(mp);
21178 lhs=mp->cur_exp; mp->cur_type=mp_vacuous;
21179 mp_get_x_next(mp); mp->var_flag=assignment; mp_scan_expression(mp);
21180 if ( mp->cur_cmd==equals ) mp_do_equation(mp);
21181 else if ( mp->cur_cmd==assignment ) mp_do_assignment(mp);
21182 if ( mp->internal[tracing_commands]>two )
21183 @<Trace the current assignment@>;
21184 if ( info(lhs)>hash_end ) {
21185 @<Assign the current expression to an internal variable@>;
21187 @<Assign the current expression to the variable |lhs|@>;
21189 mp_flush_node_list(mp, lhs);
21193 @ @<Trace the current equation@>=
21195 mp_begin_diagnostic(mp); mp_print_nl(mp, "{("); mp_print_exp(mp,lhs,0);
21196 mp_print(mp,")=("); mp_print_exp(mp,null,0);
21197 mp_print(mp,")}"); mp_end_diagnostic(mp, false);
21200 @ @<Trace the current assignment@>=
21202 mp_begin_diagnostic(mp); mp_print_nl(mp, "{");
21203 if ( info(lhs)>hash_end )
21204 mp_print(mp, mp->int_name[info(lhs)-(hash_end)]);
21206 mp_show_token_list(mp, lhs,null,1000,0);
21207 mp_print(mp, ":="); mp_print_exp(mp, null,0);
21208 mp_print_char(mp, '}'); mp_end_diagnostic(mp, false);
21211 @ @<Assign the current expression to an internal variable@>=
21212 if ( mp->cur_type==mp_known ) {
21213 mp->internal[info(lhs)-(hash_end)]=mp->cur_exp;
21215 exp_err("Internal quantity `");
21216 @.Internal quantity...@>
21217 mp_print(mp, mp->int_name[info(lhs)-(hash_end)]);
21218 mp_print(mp, "' must receive a known value");
21219 help2("I can\'t set an internal quantity to anything but a known")
21220 ("numeric value, so I'll have to ignore this assignment.");
21221 mp_put_get_error(mp);
21224 @ @<Assign the current expression to the variable |lhs|@>=
21226 p=mp_find_variable(mp, lhs);
21228 q=mp_stash_cur_exp(mp); mp->cur_type=mp_und_type(mp, p);
21229 mp_recycle_value(mp, p);
21230 type(p)=mp->cur_type; value(p)=null; mp_make_exp_copy(mp, p);
21231 p=mp_stash_cur_exp(mp); mp_unstash_cur_exp(mp, q); mp_make_eq(mp, p);
21233 mp_obliterated(mp, lhs); mp_put_get_error(mp);
21238 @ And now we get to the nitty-gritty. The |make_eq| procedure is given
21239 a pointer to a capsule that is to be equated to the current expression.
21241 @<Declare the procedure called |make_eq|@>=
21242 void mp_make_eq (MP mp,pointer lhs) ;
21246 @c void mp_make_eq (MP mp,pointer lhs) {
21247 small_number t; /* type of the left-hand side */
21248 pointer p,q; /* pointers inside of big nodes */
21249 integer v=0; /* value of the left-hand side */
21252 if ( t<=mp_pair_type ) v=value(lhs);
21254 @<For each type |t|, make an equation and |goto done| unless |cur_type|
21255 is incompatible with~|t|@>;
21256 } /* all cases have been listed */
21257 @<Announce that the equation cannot be performed@>;
21259 check_arith; mp_recycle_value(mp, lhs);
21260 mp_free_node(mp, lhs,value_node_size);
21263 @ @<Announce that the equation cannot be performed@>=
21264 mp_disp_err(mp, lhs,"");
21265 exp_err("Equation cannot be performed (");
21266 @.Equation cannot be performed@>
21267 if ( type(lhs)<=mp_pair_type ) mp_print_type(mp, type(lhs));
21268 else mp_print(mp, "numeric");
21269 mp_print_char(mp, '=');
21270 if ( mp->cur_type<=mp_pair_type ) mp_print_type(mp, mp->cur_type);
21271 else mp_print(mp, "numeric");
21272 mp_print_char(mp, ')');
21273 help2("I'm sorry, but I don't know how to make such things equal.")
21274 ("(See the two expressions just above the error message.)");
21275 mp_put_get_error(mp)
21277 @ @<For each type |t|, make an equation and |goto done| unless...@>=
21278 case mp_boolean_type: case mp_string_type: case mp_pen_type:
21279 case mp_path_type: case mp_picture_type:
21280 if ( mp->cur_type==t+unknown_tag ) {
21281 mp_nonlinear_eq(mp, v,mp->cur_exp,false); goto DONE;
21282 } else if ( mp->cur_type==t ) {
21283 @<Report redundant or inconsistent equation and |goto done|@>;
21286 case unknown_types:
21287 if ( mp->cur_type==t-unknown_tag ) {
21288 mp_nonlinear_eq(mp, mp->cur_exp,lhs,true); goto DONE;
21289 } else if ( mp->cur_type==t ) {
21290 mp_ring_merge(mp, lhs,mp->cur_exp); goto DONE;
21291 } else if ( mp->cur_type==mp_pair_type ) {
21292 if ( t==mp_unknown_path ) {
21293 mp_pair_to_path(mp); goto RESTART;
21297 case mp_transform_type: case mp_color_type:
21298 case mp_cmykcolor_type: case mp_pair_type:
21299 if ( mp->cur_type==t ) {
21300 @<Do multiple equations and |goto done|@>;
21303 case mp_known: case mp_dependent:
21304 case mp_proto_dependent: case mp_independent:
21305 if ( mp->cur_type>=mp_known ) {
21306 mp_try_eq(mp, lhs,null); goto DONE;
21312 @ @<Report redundant or inconsistent equation and |goto done|@>=
21314 if ( mp->cur_type<=mp_string_type ) {
21315 if ( mp->cur_type==mp_string_type ) {
21316 if ( mp_str_vs_str(mp, v,mp->cur_exp)!=0 ) {
21319 } else if ( v!=mp->cur_exp ) {
21322 @<Exclaim about a redundant equation@>; goto DONE;
21324 print_err("Redundant or inconsistent equation");
21325 @.Redundant or inconsistent equation@>
21326 help2("An equation between already-known quantities can't help.")
21327 ("But don't worry; continue and I'll just ignore it.");
21328 mp_put_get_error(mp); goto DONE;
21330 print_err("Inconsistent equation");
21331 @.Inconsistent equation@>
21332 help2("The equation I just read contradicts what was said before.")
21333 ("But don't worry; continue and I'll just ignore it.");
21334 mp_put_get_error(mp); goto DONE;
21337 @ @<Do multiple equations and |goto done|@>=
21339 p=v+mp->big_node_size[t];
21340 q=value(mp->cur_exp)+mp->big_node_size[t];
21342 p=p-2; q=q-2; mp_try_eq(mp, p,q);
21347 @ The first argument to |try_eq| is the location of a value node
21348 in a capsule that will soon be recycled. The second argument is
21349 either a location within a pair or transform node pointed to by
21350 |cur_exp|, or it is |null| (which means that |cur_exp| itself
21351 serves as the second argument). The idea is to leave |cur_exp| unchanged,
21352 but to equate the two operands.
21354 @<Declare the procedure called |try_eq|@>=
21355 void mp_try_eq (MP mp,pointer l, pointer r) ;
21358 @c void mp_try_eq (MP mp,pointer l, pointer r) {
21359 pointer p; /* dependency list for right operand minus left operand */
21360 int t; /* the type of list |p| */
21361 pointer q; /* the constant term of |p| is here */
21362 pointer pp; /* dependency list for right operand */
21363 int tt; /* the type of list |pp| */
21364 boolean copied; /* have we copied a list that ought to be recycled? */
21365 @<Remove the left operand from its container, negate it, and
21366 put it into dependency list~|p| with constant term~|q|@>;
21367 @<Add the right operand to list |p|@>;
21368 if ( info(p)==null ) {
21369 @<Deal with redundant or inconsistent equation@>;
21371 mp_linear_eq(mp, p,t);
21372 if ( r==null ) if ( mp->cur_type!=mp_known ) {
21373 if ( type(mp->cur_exp)==mp_known ) {
21374 pp=mp->cur_exp; mp->cur_exp=value(mp->cur_exp); mp->cur_type=mp_known;
21375 mp_free_node(mp, pp,value_node_size);
21381 @ @<Remove the left operand from its container, negate it, and...@>=
21383 if ( t==mp_known ) {
21384 t=mp_dependent; p=mp_const_dependency(mp, -value(l)); q=p;
21385 } else if ( t==mp_independent ) {
21386 t=mp_dependent; p=mp_single_dependency(mp, l); negate(value(p));
21389 p=dep_list(l); q=p;
21392 if ( info(q)==null ) break;
21395 link(prev_dep(l))=link(q); prev_dep(link(q))=prev_dep(l);
21399 @ @<Deal with redundant or inconsistent equation@>=
21401 if ( abs(value(p))>64 ) { /* off by .001 or more */
21402 print_err("Inconsistent equation");
21403 @.Inconsistent equation@>
21404 mp_print(mp, " (off by "); mp_print_scaled(mp, value(p));
21405 mp_print_char(mp, ')');
21406 help2("The equation I just read contradicts what was said before.")
21407 ("But don't worry; continue and I'll just ignore it.");
21408 mp_put_get_error(mp);
21409 } else if ( r==null ) {
21410 @<Exclaim about a redundant equation@>;
21412 mp_free_node(mp, p,dep_node_size);
21415 @ @<Add the right operand to list |p|@>=
21417 if ( mp->cur_type==mp_known ) {
21418 value(q)=value(q)+mp->cur_exp; goto DONE1;
21421 if ( tt==mp_independent ) pp=mp_single_dependency(mp, mp->cur_exp);
21422 else pp=dep_list(mp->cur_exp);
21425 if ( type(r)==mp_known ) {
21426 value(q)=value(q)+value(r); goto DONE1;
21429 if ( tt==mp_independent ) pp=mp_single_dependency(mp, r);
21430 else pp=dep_list(r);
21433 if ( tt!=mp_independent ) copied=false;
21434 else { copied=true; tt=mp_dependent; };
21435 @<Add dependency list |pp| of type |tt| to dependency list~|p| of type~|t|@>;
21436 if ( copied ) mp_flush_node_list(mp, pp);
21439 @ @<Add dependency list |pp| of type |tt| to dependency list~|p| of type~|t|@>=
21440 mp->watch_coefs=false;
21442 p=mp_p_plus_q(mp, p,pp,t);
21443 } else if ( t==mp_proto_dependent ) {
21444 p=mp_p_plus_fq(mp, p,unity,pp,mp_proto_dependent,mp_dependent);
21447 while ( info(q)!=null ) {
21448 value(q)=mp_round_fraction(mp, value(q)); q=link(q);
21450 t=mp_proto_dependent; p=mp_p_plus_q(mp, p,pp,t);
21452 mp->watch_coefs=true;
21454 @ Our next goal is to process type declarations. For this purpose it's
21455 convenient to have a procedure that scans a $\langle\,$declared
21456 variable$\,\rangle$ and returns the corresponding token list. After the
21457 following procedure has acted, the token after the declared variable
21458 will have been scanned, so it will appear in |cur_cmd|, |cur_mod|,
21461 @<Declare the function called |scan_declared_variable|@>=
21462 pointer mp_scan_declared_variable (MP mp) {
21463 pointer x; /* hash address of the variable's root */
21464 pointer h,t; /* head and tail of the token list to be returned */
21465 pointer l; /* hash address of left bracket */
21466 mp_get_symbol(mp); x=mp->cur_sym;
21467 if ( mp->cur_cmd!=tag_token ) mp_clear_symbol(mp, x,false);
21468 h=mp_get_avail(mp); info(h)=x; t=h;
21471 if ( mp->cur_sym==0 ) break;
21472 if ( mp->cur_cmd!=tag_token ) if ( mp->cur_cmd!=internal_quantity) {
21473 if ( mp->cur_cmd==left_bracket ) {
21474 @<Descend past a collective subscript@>;
21479 link(t)=mp_get_avail(mp); t=link(t); info(t)=mp->cur_sym;
21481 if ( eq_type(x)!=tag_token ) mp_clear_symbol(mp, x,false);
21482 if ( equiv(x)==null ) mp_new_root(mp, x);
21486 @ If the subscript isn't collective, we don't accept it as part of the
21489 @<Descend past a collective subscript@>=
21491 l=mp->cur_sym; mp_get_x_next(mp);
21492 if ( mp->cur_cmd!=right_bracket ) {
21493 mp_back_input(mp); mp->cur_sym=l; mp->cur_cmd=left_bracket; break;
21495 mp->cur_sym=collective_subscript;
21499 @ Type declarations are introduced by the following primitive operations.
21502 mp_primitive(mp, "numeric",type_name,mp_numeric_type);
21503 @:numeric_}{\&{numeric} primitive@>
21504 mp_primitive(mp, "string",type_name,mp_string_type);
21505 @:string_}{\&{string} primitive@>
21506 mp_primitive(mp, "boolean",type_name,mp_boolean_type);
21507 @:boolean_}{\&{boolean} primitive@>
21508 mp_primitive(mp, "path",type_name,mp_path_type);
21509 @:path_}{\&{path} primitive@>
21510 mp_primitive(mp, "pen",type_name,mp_pen_type);
21511 @:pen_}{\&{pen} primitive@>
21512 mp_primitive(mp, "picture",type_name,mp_picture_type);
21513 @:picture_}{\&{picture} primitive@>
21514 mp_primitive(mp, "transform",type_name,mp_transform_type);
21515 @:transform_}{\&{transform} primitive@>
21516 mp_primitive(mp, "color",type_name,mp_color_type);
21517 @:color_}{\&{color} primitive@>
21518 mp_primitive(mp, "rgbcolor",type_name,mp_color_type);
21519 @:color_}{\&{rgbcolor} primitive@>
21520 mp_primitive(mp, "cmykcolor",type_name,mp_cmykcolor_type);
21521 @:color_}{\&{cmykcolor} primitive@>
21522 mp_primitive(mp, "pair",type_name,mp_pair_type);
21523 @:pair_}{\&{pair} primitive@>
21525 @ @<Cases of |print_cmd...@>=
21526 case type_name: mp_print_type(mp, m); break;
21528 @ Now we are ready to handle type declarations, assuming that a
21529 |type_name| has just been scanned.
21531 @<Declare action procedures for use by |do_statement|@>=
21532 void mp_do_type_declaration (MP mp) ;
21535 void mp_do_type_declaration (MP mp) {
21536 small_number t; /* the type being declared */
21537 pointer p; /* token list for a declared variable */
21538 pointer q; /* value node for the variable */
21539 if ( mp->cur_mod>=mp_transform_type )
21542 t=mp->cur_mod+unknown_tag;
21544 p=mp_scan_declared_variable(mp);
21545 mp_flush_variable(mp, equiv(info(p)),link(p),false);
21546 q=mp_find_variable(mp, p);
21548 type(q)=t; value(q)=null;
21550 print_err("Declared variable conflicts with previous vardef");
21551 @.Declared variable conflicts...@>
21552 help2("You can't use, e.g., `numeric foo[]' after `vardef foo'.")
21553 ("Proceed, and I'll ignore the illegal redeclaration.");
21554 mp_put_get_error(mp);
21556 mp_flush_list(mp, p);
21557 if ( mp->cur_cmd<comma ) {
21558 @<Flush spurious symbols after the declared variable@>;
21560 } while (! end_of_statement);
21563 @ @<Flush spurious symbols after the declared variable@>=
21565 print_err("Illegal suffix of declared variable will be flushed");
21566 @.Illegal suffix...flushed@>
21567 help5("Variables in declarations must consist entirely of")
21568 ("names and collective subscripts, e.g., `x[]a'.")
21569 ("Are you trying to use a reserved word in a variable name?")
21570 ("I'm going to discard the junk I found here,")
21571 ("up to the next comma or the end of the declaration.");
21572 if ( mp->cur_cmd==numeric_token )
21573 mp->help_line[2]="Explicit subscripts like `x15a' aren't permitted.";
21574 mp_put_get_error(mp); mp->scanner_status=flushing;
21577 @<Decrease the string reference count...@>;
21578 } while (mp->cur_cmd<comma); /* either |end_of_statement| or |cur_cmd=comma| */
21579 mp->scanner_status=normal;
21582 @ \MP's |main_control| procedure just calls |do_statement| repeatedly
21583 until coming to the end of the user's program.
21584 Each execution of |do_statement| concludes with
21585 |cur_cmd=semicolon|, |end_group|, or |stop|.
21587 @c void mp_main_control (MP mp) {
21589 mp_do_statement(mp);
21590 if ( mp->cur_cmd==end_group ) {
21591 print_err("Extra `endgroup'");
21592 @.Extra `endgroup'@>
21593 help2("I'm not currently working on a `begingroup',")
21594 ("so I had better not try to end anything.");
21595 mp_flush_error(mp, 0);
21597 } while (mp->cur_cmd!=stop);
21599 int mp_run (MP mp) {
21600 mp_main_control(mp); /* come to life */
21601 mp_final_cleanup(mp); /* prepare for death */
21602 mp_close_files_and_terminate(mp);
21603 return mp->history;
21605 char * mp_mplib_version (MP mp) {
21607 return mplib_version;
21609 char * mp_metapost_version (MP mp) {
21611 return metapost_version;
21614 @ @<Exported function headers@>=
21615 int mp_run (MP mp);
21616 char * mp_mplib_version (MP mp);
21617 char * mp_metapost_version (MP mp);
21620 mp_primitive(mp, "end",stop,0);
21621 @:end_}{\&{end} primitive@>
21622 mp_primitive(mp, "dump",stop,1);
21623 @:dump_}{\&{dump} primitive@>
21625 @ @<Cases of |print_cmd...@>=
21627 if ( m==0 ) mp_print(mp, "end");
21628 else mp_print(mp, "dump");
21632 Let's turn now to statements that are classified as ``commands'' because
21633 of their imperative nature. We'll begin with simple ones, so that it
21634 will be clear how to hook command processing into the |do_statement| routine;
21635 then we'll tackle the tougher commands.
21637 Here's one of the simplest:
21639 @<Cases of |do_statement|...@>=
21640 case random_seed: mp_do_random_seed(mp); break;
21642 @ @<Declare action procedures for use by |do_statement|@>=
21643 void mp_do_random_seed (MP mp) ;
21645 @ @c void mp_do_random_seed (MP mp) {
21647 if ( mp->cur_cmd!=assignment ) {
21648 mp_missing_err(mp, ":=");
21650 help1("Always say `randomseed:=<numeric expression>'.");
21653 mp_get_x_next(mp); mp_scan_expression(mp);
21654 if ( mp->cur_type!=mp_known ) {
21655 exp_err("Unknown value will be ignored");
21656 @.Unknown value...ignored@>
21657 help2("Your expression was too random for me to handle,")
21658 ("so I won't change the random seed just now.");
21659 mp_put_get_flush_error(mp, 0);
21661 @<Initialize the random seed to |cur_exp|@>;
21665 @ @<Initialize the random seed to |cur_exp|@>=
21667 mp_init_randoms(mp, mp->cur_exp);
21668 if ( mp->selector>=log_only && mp->selector<write_file) {
21669 mp->old_setting=mp->selector; mp->selector=log_only;
21670 mp_print_nl(mp, "{randomseed:=");
21671 mp_print_scaled(mp, mp->cur_exp);
21672 mp_print_char(mp, '}');
21673 mp_print_nl(mp, ""); mp->selector=mp->old_setting;
21677 @ And here's another simple one (somewhat different in flavor):
21679 @<Cases of |do_statement|...@>=
21681 mp_print_ln(mp); mp->interaction=mp->cur_mod;
21682 @<Initialize the print |selector| based on |interaction|@>;
21683 if ( mp->log_opened ) mp->selector=mp->selector+2;
21688 mp_primitive(mp, "batchmode",mode_command,mp_batch_mode);
21689 @:mp_batch_mode_}{\&{batchmode} primitive@>
21690 mp_primitive(mp, "nonstopmode",mode_command,mp_nonstop_mode);
21691 @:mp_nonstop_mode_}{\&{nonstopmode} primitive@>
21692 mp_primitive(mp, "scrollmode",mode_command,mp_scroll_mode);
21693 @:mp_scroll_mode_}{\&{scrollmode} primitive@>
21694 mp_primitive(mp, "errorstopmode",mode_command,mp_error_stop_mode);
21695 @:mp_error_stop_mode_}{\&{errorstopmode} primitive@>
21697 @ @<Cases of |print_cmd_mod|...@>=
21700 case mp_batch_mode: mp_print(mp, "batchmode"); break;
21701 case mp_nonstop_mode: mp_print(mp, "nonstopmode"); break;
21702 case mp_scroll_mode: mp_print(mp, "scrollmode"); break;
21703 default: mp_print(mp, "errorstopmode"); break;
21707 @ The `\&{inner}' and `\&{outer}' commands are only slightly harder.
21709 @<Cases of |do_statement|...@>=
21710 case protection_command: mp_do_protection(mp); break;
21713 mp_primitive(mp, "inner",protection_command,0);
21714 @:inner_}{\&{inner} primitive@>
21715 mp_primitive(mp, "outer",protection_command,1);
21716 @:outer_}{\&{outer} primitive@>
21718 @ @<Cases of |print_cmd...@>=
21719 case protection_command:
21720 if ( m==0 ) mp_print(mp, "inner");
21721 else mp_print(mp, "outer");
21724 @ @<Declare action procedures for use by |do_statement|@>=
21725 void mp_do_protection (MP mp) ;
21727 @ @c void mp_do_protection (MP mp) {
21728 int m; /* 0 to unprotect, 1 to protect */
21729 halfword t; /* the |eq_type| before we change it */
21732 mp_get_symbol(mp); t=eq_type(mp->cur_sym);
21734 if ( t>=outer_tag )
21735 eq_type(mp->cur_sym)=t-outer_tag;
21736 } else if ( t<outer_tag ) {
21737 eq_type(mp->cur_sym)=t+outer_tag;
21740 } while (mp->cur_cmd==comma);
21743 @ \MP\ never defines the tokens `\.(' and `\.)' to be primitives, but
21744 plain \MP\ begins with the declaration `\&{delimiters} \.{()}'. Such a
21745 declaration assigns the command code |left_delimiter| to `\.{(}' and
21746 |right_delimiter| to `\.{)}'; the |equiv| of each delimiter is the
21747 hash address of its mate.
21749 @<Cases of |do_statement|...@>=
21750 case delimiters: mp_def_delims(mp); break;
21752 @ @<Declare action procedures for use by |do_statement|@>=
21753 void mp_def_delims (MP mp) ;
21755 @ @c void mp_def_delims (MP mp) {
21756 pointer l_delim,r_delim; /* the new delimiter pair */
21757 mp_get_clear_symbol(mp); l_delim=mp->cur_sym;
21758 mp_get_clear_symbol(mp); r_delim=mp->cur_sym;
21759 eq_type(l_delim)=left_delimiter; equiv(l_delim)=r_delim;
21760 eq_type(r_delim)=right_delimiter; equiv(r_delim)=l_delim;
21764 @ Here is a procedure that is called when \MP\ has reached a point
21765 where some right delimiter is mandatory.
21767 @<Declare the procedure called |check_delimiter|@>=
21768 void mp_check_delimiter (MP mp,pointer l_delim, pointer r_delim) {
21769 if ( mp->cur_cmd==right_delimiter )
21770 if ( mp->cur_mod==l_delim )
21772 if ( mp->cur_sym!=r_delim ) {
21773 mp_missing_err(mp, str(text(r_delim)));
21775 help2("I found no right delimiter to match a left one. So I've")
21776 ("put one in, behind the scenes; this may fix the problem.");
21779 print_err("The token `"); mp_print_text(r_delim);
21780 @.The token...delimiter@>
21781 mp_print(mp, "' is no longer a right delimiter");
21782 help3("Strange: This token has lost its former meaning!")
21783 ("I'll read it as a right delimiter this time;")
21784 ("but watch out, I'll probably miss it later.");
21789 @ The next four commands save or change the values associated with tokens.
21791 @<Cases of |do_statement|...@>=
21794 mp_get_symbol(mp); mp_save_variable(mp, mp->cur_sym); mp_get_x_next(mp);
21795 } while (mp->cur_cmd==comma);
21797 case interim_command: mp_do_interim(mp); break;
21798 case let_command: mp_do_let(mp); break;
21799 case new_internal: mp_do_new_internal(mp); break;
21801 @ @<Declare action procedures for use by |do_statement|@>=
21802 void mp_do_statement (MP mp);
21803 void mp_do_interim (MP mp);
21805 @ @c void mp_do_interim (MP mp) {
21807 if ( mp->cur_cmd!=internal_quantity ) {
21808 print_err("The token `");
21809 @.The token...quantity@>
21810 if ( mp->cur_sym==0 ) mp_print(mp, "(%CAPSULE)");
21811 else mp_print_text(mp->cur_sym);
21812 mp_print(mp, "' isn't an internal quantity");
21813 help1("Something like `tracingonline' should follow `interim'.");
21816 mp_save_internal(mp, mp->cur_mod); mp_back_input(mp);
21818 mp_do_statement(mp);
21821 @ The following procedure is careful not to undefine the left-hand symbol
21822 too soon, lest commands like `{\tt let x=x}' have a surprising effect.
21824 @<Declare action procedures for use by |do_statement|@>=
21825 void mp_do_let (MP mp) ;
21827 @ @c void mp_do_let (MP mp) {
21828 pointer l; /* hash location of the left-hand symbol */
21829 mp_get_symbol(mp); l=mp->cur_sym; mp_get_x_next(mp);
21830 if ( mp->cur_cmd!=equals ) if ( mp->cur_cmd!=assignment ) {
21831 mp_missing_err(mp, "=");
21833 help3("You should have said `let symbol = something'.")
21834 ("But don't worry; I'll pretend that an equals sign")
21835 ("was present. The next token I read will be `something'.");
21839 switch (mp->cur_cmd) {
21840 case defined_macro: case secondary_primary_macro:
21841 case tertiary_secondary_macro: case expression_tertiary_macro:
21842 add_mac_ref(mp->cur_mod);
21847 mp_clear_symbol(mp, l,false); eq_type(l)=mp->cur_cmd;
21848 if ( mp->cur_cmd==tag_token ) equiv(l)=null;
21849 else equiv(l)=mp->cur_mod;
21853 @ @<Declarations@>=
21854 void mp_grow_internals (MP mp, int l);
21855 void mp_do_new_internal (MP mp) ;
21858 void mp_grow_internals (MP mp, int l) {
21862 if ( hash_end+l>max_halfword ) {
21863 mp_confusion(mp, "out of memory space"); /* can't be reached */
21865 int_name = xmalloc ((l+1),sizeof(char *));
21866 internal = xmalloc ((l+1),sizeof(scaled));
21867 for (k=0;k<=l; k++ ) {
21868 if (k<=mp->max_internal) {
21869 internal[k]=mp->internal[k];
21870 int_name[k]=mp->int_name[k];
21876 xfree(mp->internal); xfree(mp->int_name);
21877 mp->int_name = int_name;
21878 mp->internal = internal;
21879 mp->max_internal = l;
21883 void mp_do_new_internal (MP mp) {
21885 if ( mp->int_ptr==mp->max_internal ) {
21886 mp_grow_internals(mp, (mp->max_internal + (mp->max_internal>>2)));
21888 mp_get_clear_symbol(mp); incr(mp->int_ptr);
21889 eq_type(mp->cur_sym)=internal_quantity;
21890 equiv(mp->cur_sym)=mp->int_ptr;
21891 if(mp->int_name[mp->int_ptr]!=NULL)
21892 xfree(mp->int_name[mp->int_ptr]);
21893 mp->int_name[mp->int_ptr]=str(text(mp->cur_sym));
21894 mp->internal[mp->int_ptr]=0;
21896 } while (mp->cur_cmd==comma);
21899 @ @<Dealloc variables@>=
21900 for (k=0;k<=mp->max_internal;k++) {
21901 xfree(mp->int_name[k]);
21903 xfree(mp->internal);
21904 xfree(mp->int_name);
21907 @ The various `\&{show}' commands are distinguished by modifier fields
21910 @d show_token_code 0 /* show the meaning of a single token */
21911 @d show_stats_code 1 /* show current memory and string usage */
21912 @d show_code 2 /* show a list of expressions */
21913 @d show_var_code 3 /* show a variable and its descendents */
21914 @d show_dependencies_code 4 /* show dependent variables in terms of independents */
21917 mp_primitive(mp, "showtoken",show_command,show_token_code);
21918 @:show_token_}{\&{showtoken} primitive@>
21919 mp_primitive(mp, "showstats",show_command,show_stats_code);
21920 @:show_stats_}{\&{showstats} primitive@>
21921 mp_primitive(mp, "show",show_command,show_code);
21922 @:show_}{\&{show} primitive@>
21923 mp_primitive(mp, "showvariable",show_command,show_var_code);
21924 @:show_var_}{\&{showvariable} primitive@>
21925 mp_primitive(mp, "showdependencies",show_command,show_dependencies_code);
21926 @:show_dependencies_}{\&{showdependencies} primitive@>
21928 @ @<Cases of |print_cmd...@>=
21931 case show_token_code:mp_print(mp, "showtoken"); break;
21932 case show_stats_code:mp_print(mp, "showstats"); break;
21933 case show_code:mp_print(mp, "show"); break;
21934 case show_var_code:mp_print(mp, "showvariable"); break;
21935 default: mp_print(mp, "showdependencies"); break;
21939 @ @<Cases of |do_statement|...@>=
21940 case show_command:mp_do_show_whatever(mp); break;
21942 @ The value of |cur_mod| controls the |verbosity| in the |print_exp| routine:
21943 if it's |show_code|, complicated structures are abbreviated, otherwise
21946 @<Declare action procedures for use by |do_statement|@>=
21947 void mp_do_show (MP mp) ;
21949 @ @c void mp_do_show (MP mp) {
21951 mp_get_x_next(mp); mp_scan_expression(mp);
21952 mp_print_nl(mp, ">> ");
21954 mp_print_exp(mp, null,2); mp_flush_cur_exp(mp, 0);
21955 } while (mp->cur_cmd==comma);
21958 @ @<Declare action procedures for use by |do_statement|@>=
21959 void mp_disp_token (MP mp) ;
21961 @ @c void mp_disp_token (MP mp) {
21962 mp_print_nl(mp, "> ");
21964 if ( mp->cur_sym==0 ) {
21965 @<Show a numeric or string or capsule token@>;
21967 mp_print_text(mp->cur_sym); mp_print_char(mp, '=');
21968 if ( eq_type(mp->cur_sym)>=outer_tag ) mp_print(mp, "(outer) ");
21969 mp_print_cmd_mod(mp, mp->cur_cmd,mp->cur_mod);
21970 if ( mp->cur_cmd==defined_macro ) {
21971 mp_print_ln(mp); mp_show_macro(mp, mp->cur_mod,null,100000);
21972 } /* this avoids recursion between |show_macro| and |print_cmd_mod| */
21977 @ @<Show a numeric or string or capsule token@>=
21979 if ( mp->cur_cmd==numeric_token ) {
21980 mp_print_scaled(mp, mp->cur_mod);
21981 } else if ( mp->cur_cmd==capsule_token ) {
21982 mp->g_pointer=mp->cur_mod; mp_print_capsule(mp);
21984 mp_print_char(mp, '"');
21985 mp_print_str(mp, mp->cur_mod); mp_print_char(mp, '"');
21986 delete_str_ref(mp->cur_mod);
21990 @ The following cases of |print_cmd_mod| might arise in connection
21991 with |disp_token|, although they don't correspond to any
21994 @<Cases of |print_cmd_...@>=
21995 case left_delimiter:
21996 case right_delimiter:
21997 if ( c==left_delimiter ) mp_print(mp, "left");
21998 else mp_print(mp, "right");
21999 mp_print(mp, " delimiter that matches ");
22003 if ( m==null ) mp_print(mp, "tag");
22004 else mp_print(mp, "variable");
22006 case defined_macro:
22007 mp_print(mp, "macro:");
22009 case secondary_primary_macro:
22010 case tertiary_secondary_macro:
22011 case expression_tertiary_macro:
22012 mp_print_cmd_mod(mp, macro_def,c);
22013 mp_print(mp, "'d macro:");
22014 mp_print_ln(mp); mp_show_token_list(mp, link(link(m)),null,1000,0);
22017 mp_print(mp, "[repeat the loop]");
22019 case internal_quantity:
22020 mp_print(mp, mp->int_name[m]);
22023 @ @<Declare action procedures for use by |do_statement|@>=
22024 void mp_do_show_token (MP mp) ;
22026 @ @c void mp_do_show_token (MP mp) {
22028 get_t_next; mp_disp_token(mp);
22030 } while (mp->cur_cmd==comma);
22033 @ @<Declare action procedures for use by |do_statement|@>=
22034 void mp_do_show_stats (MP mp) ;
22036 @ @c void mp_do_show_stats (MP mp) {
22037 mp_print_nl(mp, "Memory usage ");
22038 @.Memory usage...@>
22039 mp_print_int(mp, mp->var_used); mp_print_char(mp, '&'); mp_print_int(mp, mp->dyn_used);
22041 mp_print(mp, "unknown");
22042 mp_print(mp, " ("); mp_print_int(mp, mp->hi_mem_min-mp->lo_mem_max-1);
22043 mp_print(mp, " still untouched)"); mp_print_ln(mp);
22044 mp_print_nl(mp, "String usage ");
22045 mp_print_int(mp, mp->strs_in_use-mp->init_str_use);
22046 mp_print_char(mp, '&'); mp_print_int(mp, mp->pool_in_use-mp->init_pool_ptr);
22048 mp_print(mp, "unknown");
22049 mp_print(mp, " (");
22050 mp_print_int(mp, mp->max_strings-1-mp->strs_used_up); mp_print_char(mp, '&');
22051 mp_print_int(mp, mp->pool_size-mp->pool_ptr);
22052 mp_print(mp, " now untouched)"); mp_print_ln(mp);
22056 @ Here's a recursive procedure that gives an abbreviated account
22057 of a variable, for use by |do_show_var|.
22059 @<Declare action procedures for use by |do_statement|@>=
22060 void mp_disp_var (MP mp,pointer p) ;
22062 @ @c void mp_disp_var (MP mp,pointer p) {
22063 pointer q; /* traverses attributes and subscripts */
22064 int n; /* amount of macro text to show */
22065 if ( type(p)==mp_structured ) {
22066 @<Descend the structure@>;
22067 } else if ( type(p)>=mp_unsuffixed_macro ) {
22068 @<Display a variable macro@>;
22069 } else if ( type(p)!=undefined ){
22070 mp_print_nl(mp, ""); mp_print_variable_name(mp, p);
22071 mp_print_char(mp, '=');
22072 mp_print_exp(mp, p,0);
22076 @ @<Descend the structure@>=
22079 do { mp_disp_var(mp, q); q=link(q); } while (q!=end_attr);
22081 while ( name_type(q)==mp_subscr ) {
22082 mp_disp_var(mp, q); q=link(q);
22086 @ @<Display a variable macro@>=
22088 mp_print_nl(mp, ""); mp_print_variable_name(mp, p);
22089 if ( type(p)>mp_unsuffixed_macro )
22090 mp_print(mp, "@@#"); /* |suffixed_macro| */
22091 mp_print(mp, "=macro:");
22092 if ( (int)mp->file_offset>=mp->max_print_line-20 ) n=5;
22093 else n=mp->max_print_line-mp->file_offset-15;
22094 mp_show_macro(mp, value(p),null,n);
22097 @ @<Declare action procedures for use by |do_statement|@>=
22098 void mp_do_show_var (MP mp) ;
22100 @ @c void mp_do_show_var (MP mp) {
22103 if ( mp->cur_sym>0 ) if ( mp->cur_sym<=hash_end )
22104 if ( mp->cur_cmd==tag_token ) if ( mp->cur_mod!=null ) {
22105 mp_disp_var(mp, mp->cur_mod); goto DONE;
22110 } while (mp->cur_cmd==comma);
22113 @ @<Declare action procedures for use by |do_statement|@>=
22114 void mp_do_show_dependencies (MP mp) ;
22116 @ @c void mp_do_show_dependencies (MP mp) {
22117 pointer p; /* link that runs through all dependencies */
22119 while ( p!=dep_head ) {
22120 if ( mp_interesting(mp, p) ) {
22121 mp_print_nl(mp, ""); mp_print_variable_name(mp, p);
22122 if ( type(p)==mp_dependent ) mp_print_char(mp, '=');
22123 else mp_print(mp, " = "); /* extra spaces imply proto-dependency */
22124 mp_print_dependency(mp, dep_list(p),type(p));
22127 while ( info(p)!=null ) p=link(p);
22133 @ Finally we are ready for the procedure that governs all of the
22136 @<Declare action procedures for use by |do_statement|@>=
22137 void mp_do_show_whatever (MP mp) ;
22139 @ @c void mp_do_show_whatever (MP mp) {
22140 if ( mp->interaction==mp_error_stop_mode ) wake_up_terminal;
22141 switch (mp->cur_mod) {
22142 case show_token_code:mp_do_show_token(mp); break;
22143 case show_stats_code:mp_do_show_stats(mp); break;
22144 case show_code:mp_do_show(mp); break;
22145 case show_var_code:mp_do_show_var(mp); break;
22146 case show_dependencies_code:mp_do_show_dependencies(mp); break;
22147 } /* there are no other cases */
22148 if ( mp->internal[showstopping]>0 ){
22151 if ( mp->interaction<mp_error_stop_mode ) {
22152 help0; decr(mp->error_count);
22154 help1("This isn't an error message; I'm just showing something.");
22156 if ( mp->cur_cmd==semicolon ) mp_error(mp);
22157 else mp_put_get_error(mp);
22161 @ The `\&{addto}' command needs the following additional primitives:
22163 @d double_path_code 0 /* command modifier for `\&{doublepath}' */
22164 @d contour_code 1 /* command modifier for `\&{contour}' */
22165 @d also_code 2 /* command modifier for `\&{also}' */
22167 @ Pre and postscripts need two new identifiers:
22169 @d with_pre_script 11
22170 @d with_post_script 13
22173 mp_primitive(mp, "doublepath",thing_to_add,double_path_code);
22174 @:double_path_}{\&{doublepath} primitive@>
22175 mp_primitive(mp, "contour",thing_to_add,contour_code);
22176 @:contour_}{\&{contour} primitive@>
22177 mp_primitive(mp, "also",thing_to_add,also_code);
22178 @:also_}{\&{also} primitive@>
22179 mp_primitive(mp, "withpen",with_option,mp_pen_type);
22180 @:with_pen_}{\&{withpen} primitive@>
22181 mp_primitive(mp, "dashed",with_option,mp_picture_type);
22182 @:dashed_}{\&{dashed} primitive@>
22183 mp_primitive(mp, "withprescript",with_option,with_pre_script);
22184 @:with_pre_script_}{\&{withprescript} primitive@>
22185 mp_primitive(mp, "withpostscript",with_option,with_post_script);
22186 @:with_post_script_}{\&{withpostscript} primitive@>
22187 mp_primitive(mp, "withoutcolor",with_option,no_model);
22188 @:with_color_}{\&{withoutcolor} primitive@>
22189 mp_primitive(mp, "withgreyscale",with_option,grey_model);
22190 @:with_color_}{\&{withgreyscale} primitive@>
22191 mp_primitive(mp, "withcolor",with_option,uninitialized_model);
22192 @:with_color_}{\&{withcolor} primitive@>
22193 /* \&{withrgbcolor} is an alias for \&{withcolor} */
22194 mp_primitive(mp, "withrgbcolor",with_option,rgb_model);
22195 @:with_color_}{\&{withrgbcolor} primitive@>
22196 mp_primitive(mp, "withcmykcolor",with_option,cmyk_model);
22197 @:with_color_}{\&{withcmykcolor} primitive@>
22199 @ @<Cases of |print_cmd...@>=
22201 if ( m==contour_code ) mp_print(mp, "contour");
22202 else if ( m==double_path_code ) mp_print(mp, "doublepath");
22203 else mp_print(mp, "also");
22206 if ( m==mp_pen_type ) mp_print(mp, "withpen");
22207 else if ( m==with_pre_script ) mp_print(mp, "withprescript");
22208 else if ( m==with_post_script ) mp_print(mp, "withpostscript");
22209 else if ( m==no_model ) mp_print(mp, "withoutcolor");
22210 else if ( m==rgb_model ) mp_print(mp, "withrgbcolor");
22211 else if ( m==uninitialized_model ) mp_print(mp, "withcolor");
22212 else if ( m==cmyk_model ) mp_print(mp, "withcmykcolor");
22213 else if ( m==grey_model ) mp_print(mp, "withgreyscale");
22214 else mp_print(mp, "dashed");
22217 @ The |scan_with_list| procedure parses a $\langle$with list$\rangle$ and
22218 updates the list of graphical objects starting at |p|. Each $\langle$with
22219 clause$\rangle$ updates all graphical objects whose |type| is compatible.
22220 Other objects are ignored.
22222 @<Declare action procedures for use by |do_statement|@>=
22223 void mp_scan_with_list (MP mp,pointer p) ;
22225 @ @c void mp_scan_with_list (MP mp,pointer p) {
22226 small_number t; /* |cur_mod| of the |with_option| (should match |cur_type|) */
22227 pointer q; /* for list manipulation */
22228 int old_setting; /* saved |selector| setting */
22229 pointer k; /* for finding the near-last item in a list */
22230 str_number s; /* for string cleanup after combining */
22231 pointer cp,pp,dp,ap,bp;
22232 /* objects being updated; |void| initially; |null| to suppress update */
22233 cp=diov; pp=diov; dp=diov; ap=diov; bp=diov;
22235 while ( mp->cur_cmd==with_option ){
22238 if ( t!=no_model ) mp_scan_expression(mp);
22239 if (((t==with_pre_script)&&(mp->cur_type!=mp_string_type))||
22240 ((t==with_post_script)&&(mp->cur_type!=mp_string_type))||
22241 ((t==uninitialized_model)&&
22242 ((mp->cur_type!=mp_cmykcolor_type)&&(mp->cur_type!=mp_color_type)
22243 &&(mp->cur_type!=mp_known)&&(mp->cur_type!=mp_boolean_type)))||
22244 ((t==cmyk_model)&&(mp->cur_type!=mp_cmykcolor_type))||
22245 ((t==rgb_model)&&(mp->cur_type!=mp_color_type))||
22246 ((t==grey_model)&&(mp->cur_type!=mp_known))||
22247 ((t==mp_pen_type)&&(mp->cur_type!=t))||
22248 ((t==mp_picture_type)&&(mp->cur_type!=t)) ) {
22249 @<Complain about improper type@>;
22250 } else if ( t==uninitialized_model ) {
22251 if ( cp==diov ) @<Make |cp| a colored object in object list~|p|@>;
22253 @<Transfer a color from the current expression to object~|cp|@>;
22254 mp_flush_cur_exp(mp, 0);
22255 } else if ( t==rgb_model ) {
22256 if ( cp==diov ) @<Make |cp| a colored object in object list~|p|@>;
22258 @<Transfer a rgbcolor from the current expression to object~|cp|@>;
22259 mp_flush_cur_exp(mp, 0);
22260 } else if ( t==cmyk_model ) {
22261 if ( cp==diov ) @<Make |cp| a colored object in object list~|p|@>;
22263 @<Transfer a cmykcolor from the current expression to object~|cp|@>;
22264 mp_flush_cur_exp(mp, 0);
22265 } else if ( t==grey_model ) {
22266 if ( cp==diov ) @<Make |cp| a colored object in object list~|p|@>;
22268 @<Transfer a greyscale from the current expression to object~|cp|@>;
22269 mp_flush_cur_exp(mp, 0);
22270 } else if ( t==no_model ) {
22271 if ( cp==diov ) @<Make |cp| a colored object in object list~|p|@>;
22273 @<Transfer a noncolor from the current expression to object~|cp|@>;
22274 } else if ( t==mp_pen_type ) {
22275 if ( pp==diov ) @<Make |pp| an object in list~|p| that needs a pen@>;
22277 if ( pen_p(pp)!=null ) mp_toss_knot_list(mp, pen_p(pp));
22278 pen_p(pp)=mp->cur_exp; mp->cur_type=mp_vacuous;
22280 } else if ( t==with_pre_script ) {
22283 while ( (ap!=null)&&(! has_color(ap)) )
22286 if ( pre_script(ap)!=null ) { /* build a new,combined string */
22288 old_setting=mp->selector;
22289 mp->selector=new_string;
22290 str_room(length(pre_script(ap))+length(mp->cur_exp)+2);
22291 mp_print_str(mp, mp->cur_exp);
22292 append_char(13); /* a forced \ps\ newline */
22293 mp_print_str(mp, pre_script(ap));
22294 pre_script(ap)=mp_make_string(mp);
22296 mp->selector=old_setting;
22298 pre_script(ap)=mp->cur_exp;
22300 mp->cur_type=mp_vacuous;
22302 } else if ( t==with_post_script ) {
22306 while ( link(k)!=null ) {
22308 if ( has_color(k) ) bp=k;
22311 if ( post_script(bp)!=null ) {
22313 old_setting=mp->selector;
22314 mp->selector=new_string;
22315 str_room(length(post_script(bp))+length(mp->cur_exp)+2);
22316 mp_print_str(mp, post_script(bp));
22317 append_char(13); /* a forced \ps\ newline */
22318 mp_print_str(mp, mp->cur_exp);
22319 post_script(bp)=mp_make_string(mp);
22321 mp->selector=old_setting;
22323 post_script(bp)=mp->cur_exp;
22325 mp->cur_type=mp_vacuous;
22329 @<Make |dp| a stroked node in list~|p|@>;
22331 if ( dash_p(dp)!=null ) delete_edge_ref(dash_p(dp));
22332 dash_p(dp)=mp_make_dashes(mp, mp->cur_exp);
22333 dash_scale(dp)=unity;
22334 mp->cur_type=mp_vacuous;
22338 @<Copy the information from objects |cp|, |pp|, and |dp| into the rest
22342 @ @<Complain about improper type@>=
22343 { exp_err("Improper type");
22345 help2("Next time say `withpen <known pen expression>';")
22346 ("I'll ignore the bad `with' clause and look for another.");
22347 if ( t==with_pre_script )
22348 mp->help_line[1]="Next time say `withprescript <known string expression>';";
22349 else if ( t==with_post_script )
22350 mp->help_line[1]="Next time say `withpostscript <known string expression>';";
22351 else if ( t==mp_picture_type )
22352 mp->help_line[1]="Next time say `dashed <known picture expression>';";
22353 else if ( t==uninitialized_model )
22354 mp->help_line[1]="Next time say `withcolor <known color expression>';";
22355 else if ( t==rgb_model )
22356 mp->help_line[1]="Next time say `withrgbcolor <known color expression>';";
22357 else if ( t==cmyk_model )
22358 mp->help_line[1]="Next time say `withcmykcolor <known cmykcolor expression>';";
22359 else if ( t==grey_model )
22360 mp->help_line[1]="Next time say `withgreyscale <known numeric expression>';";;
22361 mp_put_get_flush_error(mp, 0);
22364 @ Forcing the color to be between |0| and |unity| here guarantees that no
22365 picture will ever contain a color outside the legal range for \ps\ graphics.
22367 @<Transfer a color from the current expression to object~|cp|@>=
22368 { if ( mp->cur_type==mp_color_type )
22369 @<Transfer a rgbcolor from the current expression to object~|cp|@>
22370 else if ( mp->cur_type==mp_cmykcolor_type )
22371 @<Transfer a cmykcolor from the current expression to object~|cp|@>
22372 else if ( mp->cur_type==mp_known )
22373 @<Transfer a greyscale from the current expression to object~|cp|@>
22374 else if ( mp->cur_exp==false_code )
22375 @<Transfer a noncolor from the current expression to object~|cp|@>;
22378 @ @<Transfer a rgbcolor from the current expression to object~|cp|@>=
22379 { q=value(mp->cur_exp);
22384 red_val(cp)=value(red_part_loc(q));
22385 green_val(cp)=value(green_part_loc(q));
22386 blue_val(cp)=value(blue_part_loc(q));
22387 color_model(cp)=rgb_model;
22388 if ( red_val(cp)<0 ) red_val(cp)=0;
22389 if ( green_val(cp)<0 ) green_val(cp)=0;
22390 if ( blue_val(cp)<0 ) blue_val(cp)=0;
22391 if ( red_val(cp)>unity ) red_val(cp)=unity;
22392 if ( green_val(cp)>unity ) green_val(cp)=unity;
22393 if ( blue_val(cp)>unity ) blue_val(cp)=unity;
22396 @ @<Transfer a cmykcolor from the current expression to object~|cp|@>=
22397 { q=value(mp->cur_exp);
22398 cyan_val(cp)=value(cyan_part_loc(q));
22399 magenta_val(cp)=value(magenta_part_loc(q));
22400 yellow_val(cp)=value(yellow_part_loc(q));
22401 black_val(cp)=value(black_part_loc(q));
22402 color_model(cp)=cmyk_model;
22403 if ( cyan_val(cp)<0 ) cyan_val(cp)=0;
22404 if ( magenta_val(cp)<0 ) magenta_val(cp)=0;
22405 if ( yellow_val(cp)<0 ) yellow_val(cp)=0;
22406 if ( black_val(cp)<0 ) black_val(cp)=0;
22407 if ( cyan_val(cp)>unity ) cyan_val(cp)=unity;
22408 if ( magenta_val(cp)>unity ) magenta_val(cp)=unity;
22409 if ( yellow_val(cp)>unity ) yellow_val(cp)=unity;
22410 if ( black_val(cp)>unity ) black_val(cp)=unity;
22413 @ @<Transfer a greyscale from the current expression to object~|cp|@>=
22420 color_model(cp)=grey_model;
22421 if ( grey_val(cp)<0 ) grey_val(cp)=0;
22422 if ( grey_val(cp)>unity ) grey_val(cp)=unity;
22425 @ @<Transfer a noncolor from the current expression to object~|cp|@>=
22432 color_model(cp)=no_model;
22435 @ @<Make |cp| a colored object in object list~|p|@>=
22437 while ( cp!=null ){
22438 if ( has_color(cp) ) break;
22443 @ @<Make |pp| an object in list~|p| that needs a pen@>=
22445 while ( pp!=null ) {
22446 if ( has_pen(pp) ) break;
22451 @ @<Make |dp| a stroked node in list~|p|@>=
22453 while ( dp!=null ) {
22454 if ( type(dp)==stroked_code ) break;
22459 @ @<Copy the information from objects |cp|, |pp|, and |dp| into...@>=
22460 @<Copy |cp|'s color into the colored objects linked to~|cp|@>;
22462 @<Copy |pen_p(pp)| into stroked and filled nodes linked to |pp|@>;
22463 if ( dp>diov ) @<Make stroked nodes linked to |dp| refer to |dash_p(dp)|@>
22465 @ @<Copy |cp|'s color into the colored objects linked to~|cp|@>=
22467 while ( q!=null ) {
22468 if ( has_color(q) ) {
22469 red_val(q)=red_val(cp);
22470 green_val(q)=green_val(cp);
22471 blue_val(q)=blue_val(cp);
22472 black_val(q)=black_val(cp);
22473 color_model(q)=color_model(cp);
22479 @ @<Copy |pen_p(pp)| into stroked and filled nodes linked to |pp|@>=
22481 while ( q!=null ) {
22482 if ( has_pen(q) ) {
22483 if ( pen_p(q)!=null ) mp_toss_knot_list(mp, pen_p(q));
22484 pen_p(q)=copy_pen(pen_p(pp));
22490 @ @<Make stroked nodes linked to |dp| refer to |dash_p(dp)|@>=
22492 while ( q!=null ) {
22493 if ( type(q)==stroked_code ) {
22494 if ( dash_p(q)!=null ) delete_edge_ref(dash_p(q));
22495 dash_p(q)=dash_p(dp);
22496 dash_scale(q)=unity;
22497 if ( dash_p(q)!=null ) add_edge_ref(dash_p(q));
22503 @ One of the things we need to do when we've parsed an \&{addto} or
22504 similar command is find the header of a supposed \&{picture} variable, given
22505 a token list for that variable. Since the edge structure is about to be
22506 updated, we use |private_edges| to make sure that this is possible.
22508 @<Declare action procedures for use by |do_statement|@>=
22509 pointer mp_find_edges_var (MP mp, pointer t) ;
22511 @ @c pointer mp_find_edges_var (MP mp, pointer t) {
22513 pointer cur_edges; /* the return value */
22514 p=mp_find_variable(mp, t); cur_edges=null;
22516 mp_obliterated(mp, t); mp_put_get_error(mp);
22517 } else if ( type(p)!=mp_picture_type ) {
22518 print_err("Variable "); mp_show_token_list(mp, t,null,1000,0);
22519 @.Variable x is the wrong type@>
22520 mp_print(mp, " is the wrong type (");
22521 mp_print_type(mp, type(p)); mp_print_char(mp, ')');
22522 help2("I was looking for a \"known\" picture variable.")
22523 ("So I'll not change anything just now.");
22524 mp_put_get_error(mp);
22526 value(p)=mp_private_edges(mp, value(p));
22527 cur_edges=value(p);
22529 mp_flush_node_list(mp, t);
22533 @ @<Cases of |do_statement|...@>=
22534 case add_to_command: mp_do_add_to(mp); break;
22535 case bounds_command:mp_do_bounds(mp); break;
22538 mp_primitive(mp, "clip",bounds_command,mp_start_clip_code);
22539 @:clip_}{\&{clip} primitive@>
22540 mp_primitive(mp, "setbounds",bounds_command,mp_start_bounds_code);
22541 @:set_bounds_}{\&{setbounds} primitive@>
22543 @ @<Cases of |print_cmd...@>=
22544 case bounds_command:
22545 if ( m==mp_start_clip_code ) mp_print(mp, "clip");
22546 else mp_print(mp, "setbounds");
22549 @ The following function parses the beginning of an \&{addto} or \&{clip}
22550 command: it expects a variable name followed by a token with |cur_cmd=sep|
22551 and then an expression. The function returns the token list for the variable
22552 and stores the command modifier for the separator token in the global variable
22553 |last_add_type|. We must be careful because this variable might get overwritten
22554 any time we call |get_x_next|.
22557 quarterword last_add_type;
22558 /* command modifier that identifies the last \&{addto} command */
22560 @ @<Declare action procedures for use by |do_statement|@>=
22561 pointer mp_start_draw_cmd (MP mp,quarterword sep) ;
22563 @ @c pointer mp_start_draw_cmd (MP mp,quarterword sep) {
22564 pointer lhv; /* variable to add to left */
22565 quarterword add_type=0; /* value to be returned in |last_add_type| */
22567 mp_get_x_next(mp); mp->var_flag=sep; mp_scan_primary(mp);
22568 if ( mp->cur_type!=mp_token_list ) {
22569 @<Abandon edges command because there's no variable@>;
22571 lhv=mp->cur_exp; add_type=mp->cur_mod;
22572 mp->cur_type=mp_vacuous; mp_get_x_next(mp); mp_scan_expression(mp);
22574 mp->last_add_type=add_type;
22578 @ @<Abandon edges command because there's no variable@>=
22579 { exp_err("Not a suitable variable");
22580 @.Not a suitable variable@>
22581 help4("At this point I needed to see the name of a picture variable.")
22582 ("(Or perhaps you have indeed presented me with one; I might")
22583 ("have missed it, if it wasn't followed by the proper token.)")
22584 ("So I'll not change anything just now.");
22585 mp_put_get_flush_error(mp, 0);
22588 @ Here is an example of how to use |start_draw_cmd|.
22590 @<Declare action procedures for use by |do_statement|@>=
22591 void mp_do_bounds (MP mp) ;
22593 @ @c void mp_do_bounds (MP mp) {
22594 pointer lhv,lhe; /* variable on left, the corresponding edge structure */
22595 pointer p; /* for list manipulation */
22596 integer m; /* initial value of |cur_mod| */
22598 lhv=mp_start_draw_cmd(mp, to_token);
22600 lhe=mp_find_edges_var(mp, lhv);
22602 mp_flush_cur_exp(mp, 0);
22603 } else if ( mp->cur_type!=mp_path_type ) {
22604 exp_err("Improper `clip'");
22605 @.Improper `addto'@>
22606 help2("This expression should have specified a known path.")
22607 ("So I'll not change anything just now.");
22608 mp_put_get_flush_error(mp, 0);
22609 } else if ( left_type(mp->cur_exp)==endpoint ) {
22610 @<Complain about a non-cycle@>;
22612 @<Make |cur_exp| into a \&{setbounds} or clipping path and add it to |lhe|@>;
22617 @ @<Complain about a non-cycle@>=
22618 { print_err("Not a cycle");
22620 help2("That contour should have ended with `..cycle' or `&cycle'.")
22621 ("So I'll not change anything just now."); mp_put_get_error(mp);
22624 @ @<Make |cur_exp| into a \&{setbounds} or clipping path and add...@>=
22625 { p=mp_new_bounds_node(mp, mp->cur_exp,m);
22626 link(p)=link(dummy_loc(lhe));
22627 link(dummy_loc(lhe))=p;
22628 if ( obj_tail(lhe)==dummy_loc(lhe) ) obj_tail(lhe)=p;
22629 p=mp_get_node(mp, mp->gr_object_size[stop_type(m)]);
22630 type(p)=stop_type(m);
22631 link(obj_tail(lhe))=p;
22633 mp_init_bbox(mp, lhe);
22636 @ The |do_add_to| procedure is a little like |do_clip| but there are a lot more
22637 cases to deal with.
22639 @<Declare action procedures for use by |do_statement|@>=
22640 void mp_do_add_to (MP mp) ;
22642 @ @c void mp_do_add_to (MP mp) {
22643 pointer lhv,lhe; /* variable on left, the corresponding edge structure */
22644 pointer p; /* the graphical object or list for |scan_with_list| to update */
22645 pointer e; /* an edge structure to be merged */
22646 quarterword add_type; /* |also_code|, |contour_code|, or |double_path_code| */
22647 lhv=mp_start_draw_cmd(mp, thing_to_add); add_type=mp->last_add_type;
22649 if ( add_type==also_code ) {
22650 @<Make sure the current expression is a suitable picture and set |e| and |p|
22653 @<Create a graphical object |p| based on |add_type| and the current
22656 mp_scan_with_list(mp, p);
22657 @<Use |p|, |e|, and |add_type| to augment |lhv| as requested@>;
22661 @ Setting |p:=null| causes the $\langle$with list$\rangle$ to be ignored;
22662 setting |e:=null| prevents anything from being added to |lhe|.
22664 @ @<Make sure the current expression is a suitable picture and set |e|...@>=
22667 if ( mp->cur_type!=mp_picture_type ) {
22668 exp_err("Improper `addto'");
22669 @.Improper `addto'@>
22670 help2("This expression should have specified a known picture.")
22671 ("So I'll not change anything just now."); mp_put_get_flush_error(mp, 0);
22673 e=mp_private_edges(mp, mp->cur_exp); mp->cur_type=mp_vacuous;
22674 p=link(dummy_loc(e));
22678 @ In this case |add_type<>also_code| so setting |p:=null| suppresses future
22679 attempts to add to the edge structure.
22681 @<Create a graphical object |p| based on |add_type| and the current...@>=
22683 if ( mp->cur_type==mp_pair_type ) mp_pair_to_path(mp);
22684 if ( mp->cur_type!=mp_path_type ) {
22685 exp_err("Improper `addto'");
22686 @.Improper `addto'@>
22687 help2("This expression should have specified a known path.")
22688 ("So I'll not change anything just now.");
22689 mp_put_get_flush_error(mp, 0);
22690 } else if ( add_type==contour_code ) {
22691 if ( left_type(mp->cur_exp)==endpoint ) {
22692 @<Complain about a non-cycle@>;
22694 p=mp_new_fill_node(mp, mp->cur_exp);
22695 mp->cur_type=mp_vacuous;
22698 p=mp_new_stroked_node(mp, mp->cur_exp);
22699 mp->cur_type=mp_vacuous;
22703 @ @<Use |p|, |e|, and |add_type| to augment |lhv| as requested@>=
22704 lhe=mp_find_edges_var(mp, lhv);
22706 if ( (e==null)&&(p!=null) ) e=mp_toss_gr_object(mp, p);
22707 if ( e!=null ) delete_edge_ref(e);
22708 } else if ( add_type==also_code ) {
22710 @<Merge |e| into |lhe| and delete |e|@>;
22714 } else if ( p!=null ) {
22715 link(obj_tail(lhe))=p;
22717 if ( add_type==double_path_code )
22718 if ( pen_p(p)==null )
22719 pen_p(p)=mp_get_pen_circle(mp, 0);
22722 @ @<Merge |e| into |lhe| and delete |e|@>=
22723 { if ( link(dummy_loc(e))!=null ) {
22724 link(obj_tail(lhe))=link(dummy_loc(e));
22725 obj_tail(lhe)=obj_tail(e);
22726 obj_tail(e)=dummy_loc(e);
22727 link(dummy_loc(e))=null;
22728 mp_flush_dash_list(mp, lhe);
22730 mp_toss_edges(mp, e);
22733 @ @<Cases of |do_statement|...@>=
22734 case ship_out_command: mp_do_ship_out(mp); break;
22736 @ @<Declare action procedures for use by |do_statement|@>=
22737 @<Declare the function called |tfm_check|@>;
22738 @<Declare the \ps\ output procedures@>;
22739 void mp_do_ship_out (MP mp) ;
22741 @ @c void mp_do_ship_out (MP mp) {
22742 integer c; /* the character code */
22743 mp_get_x_next(mp); mp_scan_expression(mp);
22744 if ( mp->cur_type!=mp_picture_type ) {
22745 @<Complain that it's not a known picture@>;
22747 c=mp_round_unscaled(mp, mp->internal[char_code]) % 256;
22748 if ( c<0 ) c=c+256;
22749 @<Store the width information for character code~|c|@>;
22750 mp_ship_out(mp, mp->cur_exp);
22751 mp_flush_cur_exp(mp, 0);
22755 @ @<Complain that it's not a known picture@>=
22757 exp_err("Not a known picture");
22758 help1("I can only output known pictures.");
22759 mp_put_get_flush_error(mp, 0);
22762 @ The \&{everyjob} command simply assigns a nonzero value to the global variable
22765 @<Cases of |do_statement|...@>=
22766 case every_job_command:
22767 mp_get_symbol(mp); mp->start_sym=mp->cur_sym; mp_get_x_next(mp);
22771 halfword start_sym; /* a symbolic token to insert at beginning of job */
22776 @ Finally, we have only the ``message'' commands remaining.
22779 @d err_message_code 1
22781 @d filename_template_code 3
22782 @d print_with_leading_zeroes(A) g = mp->pool_ptr;
22783 mp_print_int(mp, (A)); g = mp->pool_ptr-g;
22785 mp->pool_ptr = mp->pool_ptr - g;
22787 mp_print_char(mp, '0');
22790 mp_print_int(mp, (A));
22795 mp_primitive(mp, "message",message_command,message_code);
22796 @:message_}{\&{message} primitive@>
22797 mp_primitive(mp, "errmessage",message_command,err_message_code);
22798 @:err_message_}{\&{errmessage} primitive@>
22799 mp_primitive(mp, "errhelp",message_command,err_help_code);
22800 @:err_help_}{\&{errhelp} primitive@>
22801 mp_primitive(mp, "filenametemplate",message_command,filename_template_code);
22802 @:filename_template_}{\&{filenametemplate} primitive@>
22804 @ @<Cases of |print_cmd...@>=
22805 case message_command:
22806 if ( m<err_message_code ) mp_print(mp, "message");
22807 else if ( m==err_message_code ) mp_print(mp, "errmessage");
22808 else if ( m==filename_template_code ) mp_print(mp, "filenametemplate");
22809 else mp_print(mp, "errhelp");
22812 @ @<Cases of |do_statement|...@>=
22813 case message_command: mp_do_message(mp); break;
22815 @ @<Declare action procedures for use by |do_statement|@>=
22816 @<Declare a procedure called |no_string_err|@>;
22817 void mp_do_message (MP mp) ;
22820 @c void mp_do_message (MP mp) {
22821 int m; /* the type of message */
22822 m=mp->cur_mod; mp_get_x_next(mp); mp_scan_expression(mp);
22823 if ( mp->cur_type!=mp_string_type )
22824 mp_no_string_err(mp, "A message should be a known string expression.");
22828 mp_print_nl(mp, ""); mp_print_str(mp, mp->cur_exp);
22830 case err_message_code:
22831 @<Print string |cur_exp| as an error message@>;
22833 case err_help_code:
22834 @<Save string |cur_exp| as the |err_help|@>;
22836 case filename_template_code:
22837 @<Save the filename template@>;
22839 } /* there are no other cases */
22841 mp_flush_cur_exp(mp, 0);
22844 @ @<Declare a procedure called |no_string_err|@>=
22845 void mp_no_string_err (MP mp,char *s) {
22846 exp_err("Not a string");
22849 mp_put_get_error(mp);
22852 @ The global variable |err_help| is zero when the user has most recently
22853 given an empty help string, or if none has ever been given.
22855 @<Save string |cur_exp| as the |err_help|@>=
22857 if ( mp->err_help!=0 ) delete_str_ref(mp->err_help);
22858 if ( length(mp->cur_exp)==0 ) mp->err_help=0;
22859 else { mp->err_help=mp->cur_exp; add_str_ref(mp->err_help); }
22862 @ If \&{errmessage} occurs often in |mp_scroll_mode|, without user-defined
22863 \&{errhelp}, we don't want to give a long help message each time. So we
22864 give a verbose explanation only once.
22867 boolean long_help_seen; /* has the long \.{\\errmessage} help been used? */
22869 @ @<Set init...@>=mp->long_help_seen=false;
22871 @ @<Print string |cur_exp| as an error message@>=
22873 print_err(""); mp_print_str(mp, mp->cur_exp);
22874 if ( mp->err_help!=0 ) {
22875 mp->use_err_help=true;
22876 } else if ( mp->long_help_seen ) {
22877 help1("(That was another `errmessage'.)") ;
22879 if ( mp->interaction<mp_error_stop_mode ) mp->long_help_seen=true;
22880 help4("This error message was generated by an `errmessage'")
22881 ("command, so I can\'t give any explicit help.")
22882 ("Pretend that you're Miss Marple: Examine all clues,")
22884 ("and deduce the truth by inspired guesses.");
22886 mp_put_get_error(mp); mp->use_err_help=false;
22889 @ @<Cases of |do_statement|...@>=
22890 case write_command: mp_do_write(mp); break;
22892 @ @<Declare action procedures for use by |do_statement|@>=
22893 void mp_do_write (MP mp) ;
22895 @ @c void mp_do_write (MP mp) {
22896 str_number t; /* the line of text to be written */
22897 write_index n,n0; /* for searching |wr_fname| and |wr_file| arrays */
22898 int old_setting; /* for saving |selector| during output */
22900 mp_scan_expression(mp);
22901 if ( mp->cur_type!=mp_string_type ) {
22902 mp_no_string_err(mp, "The text to be written should be a known string expression");
22903 } else if ( mp->cur_cmd!=to_token ) {
22904 print_err("Missing `to' clause");
22905 help1("A write command should end with `to <filename>'");
22906 mp_put_get_error(mp);
22908 t=mp->cur_exp; mp->cur_type=mp_vacuous;
22910 mp_scan_expression(mp);
22911 if ( mp->cur_type!=mp_string_type )
22912 mp_no_string_err(mp, "I can\'t write to that file name. It isn't a known string");
22914 @<Write |t| to the file named by |cur_exp|@>;
22918 mp_flush_cur_exp(mp, 0);
22921 @ @<Write |t| to the file named by |cur_exp|@>=
22923 @<Find |n| where |wr_fname[n]=cur_exp| and call |open_write_file| if
22924 |cur_exp| must be inserted@>;
22925 if ( mp_str_vs_str(mp, t,mp->eof_line)==0 ) {
22926 @<Record the end of file on |wr_file[n]|@>;
22928 old_setting=mp->selector;
22929 mp->selector=n+write_file;
22930 mp_print_str(mp, t); mp_print_ln(mp);
22931 mp->selector = old_setting;
22935 @ @<Find |n| where |wr_fname[n]=cur_exp| and call |open_write_file| if...@>=
22937 char *fn = str(mp->cur_exp);
22939 n0=mp->write_files;
22940 while (mp_xstrcmp(fn,mp->wr_fname[n])!=0) {
22941 if ( n==0 ) { /* bottom reached */
22942 if ( n0==mp->write_files ) {
22943 if ( mp->write_files<mp->max_write_files ) {
22944 incr(mp->write_files);
22949 l = mp->max_write_files + (mp->max_write_files>>2);
22950 wr_file = xmalloc((l+1),sizeof(FILE *));
22951 wr_fname = xmalloc((l+1),sizeof(char *));
22952 for (k=0;k<=l;k++) {
22953 if (k<=mp->max_write_files) {
22954 wr_file[k]=mp->wr_file[k];
22955 wr_fname[k]=mp->wr_fname[k];
22961 xfree(mp->wr_file); xfree(mp->wr_fname);
22962 mp->max_write_files = l;
22963 mp->wr_file = wr_file;
22964 mp->wr_fname = wr_fname;
22968 mp_open_write_file(mp, fn ,n);
22971 if ( mp->wr_fname[n]==NULL ) n0=n;
22976 @ @<Record the end of file on |wr_file[n]|@>=
22977 { fclose(mp->wr_file[n]);
22978 xfree(mp->wr_fname[n]);
22979 mp->wr_fname[n]=NULL;
22980 if ( n==mp->write_files-1 ) mp->write_files=n;
22984 @* \[42] Writing font metric data.
22985 \TeX\ gets its knowledge about fonts from font metric files, also called
22986 \.{TFM} files; the `\.T' in `\.{TFM}' stands for \TeX,
22987 but other programs know about them too. One of \MP's duties is to
22988 write \.{TFM} files so that the user's fonts can readily be
22989 applied to typesetting.
22990 @:TFM files}{\.{TFM} files@>
22991 @^font metric files@>
22993 The information in a \.{TFM} file appears in a sequence of 8-bit bytes.
22994 Since the number of bytes is always a multiple of~4, we could
22995 also regard the file as a sequence of 32-bit words, but \MP\ uses the
22996 byte interpretation. The format of \.{TFM} files was designed by
22997 Lyle Ramshaw in 1980. The intent is to convey a lot of different kinds
22998 @^Ramshaw, Lyle Harold@>
22999 of information in a compact but useful form.
23002 FILE * tfm_file; /* the font metric output goes here */
23003 char * metric_file_name; /* full name of the font metric file */
23005 @ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit
23006 integers that give the lengths of the various subsequent portions
23007 of the file. These twelve integers are, in order:
23008 $$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr
23009 |lf|&length of the entire file, in words;\cr
23010 |lh|&length of the header data, in words;\cr
23011 |bc|&smallest character code in the font;\cr
23012 |ec|&largest character code in the font;\cr
23013 |nw|&number of words in the width table;\cr
23014 |nh|&number of words in the height table;\cr
23015 |nd|&number of words in the depth table;\cr
23016 |ni|&number of words in the italic correction table;\cr
23017 |nl|&number of words in the lig/kern table;\cr
23018 |nk|&number of words in the kern table;\cr
23019 |ne|&number of words in the extensible character table;\cr
23020 |np|&number of font parameter words.\cr}}$$
23021 They are all nonnegative and less than $2^{15}$. We must have |bc-1<=ec<=255|,
23023 $$\hbox{|lf=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$
23024 Note that a font may contain as many as 256 characters (if |bc=0| and |ec=255|),
23025 and as few as 0 characters (if |bc=ec+1|).
23027 Incidentally, when two or more 8-bit bytes are combined to form an integer of
23028 16 or more bits, the most significant bytes appear first in the file.
23029 This is called BigEndian order.
23030 @^BigEndian order@>
23032 @ The rest of the \.{TFM} file may be regarded as a sequence of ten data
23035 The most important data type used here is a |fix_word|, which is
23036 a 32-bit representation of a binary fraction. A |fix_word| is a signed
23037 quantity, with the two's complement of the entire word used to represent
23038 negation. Of the 32 bits in a |fix_word|, exactly 12 are to the left of the
23039 binary point; thus, the largest |fix_word| value is $2048-2^{-20}$, and
23040 the smallest is $-2048$. We will see below, however, that all but two of
23041 the |fix_word| values must lie between $-16$ and $+16$.
23043 @ The first data array is a block of header information, which contains
23044 general facts about the font. The header must contain at least two words,
23045 |header[0]| and |header[1]|, whose meaning is explained below. Additional
23046 header information of use to other software routines might also be
23047 included, and \MP\ will generate it if the \.{headerbyte} command occurs.
23048 For example, 16 more words of header information are in use at the Xerox
23049 Palo Alto Research Center; the first ten specify the character coding
23050 scheme used (e.g., `\.{XEROX TEXT}' or `\.{TEX MATHSY}'), the next five
23051 give the font family name (e.g., `\.{HELVETICA}' or `\.{CMSY}'), and the
23052 last gives the ``face byte.''
23054 \yskip\hang|header[0]| is a 32-bit check sum that \MP\ will copy into
23055 the \.{GF} output file. This helps ensure consistency between files,
23056 since \TeX\ records the check sums from the \.{TFM}'s it reads, and these
23057 should match the check sums on actual fonts that are used. The actual
23058 relation between this check sum and the rest of the \.{TFM} file is not
23059 important; the check sum is simply an identification number with the
23060 property that incompatible fonts almost always have distinct check sums.
23063 \yskip\hang|header[1]| is a |fix_word| containing the design size of the
23064 font, in units of \TeX\ points. This number must be at least 1.0; it is
23065 fairly arbitrary, but usually the design size is 10.0 for a ``10 point''
23066 font, i.e., a font that was designed to look best at a 10-point size,
23067 whatever that really means. When a \TeX\ user asks for a font `\.{at}
23068 $\delta$ \.{pt}', the effect is to override the design size and replace it
23069 by $\delta$, and to multiply the $x$ and~$y$ coordinates of the points in
23070 the font image by a factor of $\delta$ divided by the design size. {\sl
23071 All other dimensions in the\/ \.{TFM} file are |fix_word|\kern-1pt\
23072 numbers in design-size units.} Thus, for example, the value of |param[6]|,
23073 which defines the \.{em} unit, is often the |fix_word| value $2^{20}=1.0$,
23074 since many fonts have a design size equal to one em. The other dimensions
23075 must be less than 16 design-size units in absolute value; thus,
23076 |header[1]| and |param[1]| are the only |fix_word| entries in the whole
23077 \.{TFM} file whose first byte might be something besides 0 or 255.
23079 @ Next comes the |char_info| array, which contains one |char_info_word|
23080 per character. Each word in this part of the file contains six fields
23081 packed into four bytes as follows.
23083 \yskip\hang first byte: |width_index| (8 bits)\par
23084 \hang second byte: |height_index| (4 bits) times 16, plus |depth_index|
23086 \hang third byte: |italic_index| (6 bits) times 4, plus |tag|
23088 \hang fourth byte: |remainder| (8 bits)\par
23090 The actual width of a character is \\{width}|[width_index]|, in design-size
23091 units; this is a device for compressing information, since many characters
23092 have the same width. Since it is quite common for many characters
23093 to have the same height, depth, or italic correction, the \.{TFM} format
23094 imposes a limit of 16 different heights, 16 different depths, and
23095 64 different italic corrections.
23097 Incidentally, the relation $\\{width}[0]=\\{height}[0]=\\{depth}[0]=
23098 \\{italic}[0]=0$ should always hold, so that an index of zero implies a
23099 value of zero. The |width_index| should never be zero unless the
23100 character does not exist in the font, since a character is valid if and
23101 only if it lies between |bc| and |ec| and has a nonzero |width_index|.
23103 @ The |tag| field in a |char_info_word| has four values that explain how to
23104 interpret the |remainder| field.
23106 \yskip\hang|tag=0| (|no_tag|) means that |remainder| is unused.\par
23107 \hang|tag=1| (|lig_tag|) means that this character has a ligature/kerning
23108 program starting at location |remainder| in the |lig_kern| array.\par
23109 \hang|tag=2| (|list_tag|) means that this character is part of a chain of
23110 characters of ascending sizes, and not the largest in the chain. The
23111 |remainder| field gives the character code of the next larger character.\par
23112 \hang|tag=3| (|ext_tag|) means that this character code represents an
23113 extensible character, i.e., a character that is built up of smaller pieces
23114 so that it can be made arbitrarily large. The pieces are specified in
23115 |exten[remainder]|.\par
23117 Characters with |tag=2| and |tag=3| are treated as characters with |tag=0|
23118 unless they are used in special circumstances in math formulas. For example,
23119 \TeX's \.{\\sum} operation looks for a |list_tag|, and the \.{\\left}
23120 operation looks for both |list_tag| and |ext_tag|.
23122 @d no_tag 0 /* vanilla character */
23123 @d lig_tag 1 /* character has a ligature/kerning program */
23124 @d list_tag 2 /* character has a successor in a charlist */
23125 @d ext_tag 3 /* character is extensible */
23127 @ The |lig_kern| array contains instructions in a simple programming language
23128 that explains what to do for special letter pairs. Each word in this array is a
23129 |lig_kern_command| of four bytes.
23131 \yskip\hang first byte: |skip_byte|, indicates that this is the final program
23132 step if the byte is 128 or more, otherwise the next step is obtained by
23133 skipping this number of intervening steps.\par
23134 \hang second byte: |next_char|, ``if |next_char| follows the current character,
23135 then perform the operation and stop, otherwise continue.''\par
23136 \hang third byte: |op_byte|, indicates a ligature step if less than~128,
23137 a kern step otherwise.\par
23138 \hang fourth byte: |remainder|.\par
23141 additional space equal to |kern[256*(op_byte-128)+remainder]| is inserted
23142 between the current character and |next_char|. This amount is
23143 often negative, so that the characters are brought closer together
23144 by kerning; but it might be positive.
23146 There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$ where
23147 $0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is
23148 |remainder| is inserted between the current character and |next_char|;
23149 then the current character is deleted if $b=0$, and |next_char| is
23150 deleted if $c=0$; then we pass over $a$~characters to reach the next
23151 current character (which may have a ligature/kerning program of its own).
23153 If the very first instruction of the |lig_kern| array has |skip_byte=255|,
23154 the |next_char| byte is the so-called right boundary character of this font;
23155 the value of |next_char| need not lie between |bc| and~|ec|.
23156 If the very last instruction of the |lig_kern| array has |skip_byte=255|,
23157 there is a special ligature/kerning program for a left boundary character,
23158 beginning at location |256*op_byte+remainder|.
23159 The interpretation is that \TeX\ puts implicit boundary characters
23160 before and after each consecutive string of characters from the same font.
23161 These implicit characters do not appear in the output, but they can affect
23162 ligatures and kerning.
23164 If the very first instruction of a character's |lig_kern| program has
23165 |skip_byte>128|, the program actually begins in location
23166 |256*op_byte+remainder|. This feature allows access to large |lig_kern|
23167 arrays, because the first instruction must otherwise
23168 appear in a location |<=255|.
23170 Any instruction with |skip_byte>128| in the |lig_kern| array must satisfy
23172 $$\hbox{|256*op_byte+remainder<nl|.}$$
23173 If such an instruction is encountered during
23174 normal program execution, it denotes an unconditional halt; no ligature
23175 command is performed.
23178 /* value indicating `\.{STOP}' in a lig/kern program */
23179 @d kern_flag (128) /* op code for a kern step */
23180 @d skip_byte(A) mp->lig_kern[(A)].b0
23181 @d next_char(A) mp->lig_kern[(A)].b1
23182 @d op_byte(A) mp->lig_kern[(A)].b2
23183 @d rem_byte(A) mp->lig_kern[(A)].b3
23185 @ Extensible characters are specified by an |extensible_recipe|, which
23186 consists of four bytes called |top|, |mid|, |bot|, and |rep| (in this
23187 order). These bytes are the character codes of individual pieces used to
23188 build up a large symbol. If |top|, |mid|, or |bot| are zero, they are not
23189 present in the built-up result. For example, an extensible vertical line is
23190 like an extensible bracket, except that the top and bottom pieces are missing.
23192 Let $T$, $M$, $B$, and $R$ denote the respective pieces, or an empty box
23193 if the piece isn't present. Then the extensible characters have the form
23194 $TR^kMR^kB$ from top to bottom, for some |k>=0|, unless $M$ is absent;
23195 in the latter case we can have $TR^kB$ for both even and odd values of~|k|.
23196 The width of the extensible character is the width of $R$; and the
23197 height-plus-depth is the sum of the individual height-plus-depths of the
23198 components used, since the pieces are butted together in a vertical list.
23200 @d ext_top(A) mp->exten[(A)].b0 /* |top| piece in a recipe */
23201 @d ext_mid(A) mp->exten[(A)].b1 /* |mid| piece in a recipe */
23202 @d ext_bot(A) mp->exten[(A)].b2 /* |bot| piece in a recipe */
23203 @d ext_rep(A) mp->exten[(A)].b3 /* |rep| piece in a recipe */
23205 @ The final portion of a \.{TFM} file is the |param| array, which is another
23206 sequence of |fix_word| values.
23208 \yskip\hang|param[1]=slant| is the amount of italic slant, which is used
23209 to help position accents. For example, |slant=.25| means that when you go
23210 up one unit, you also go .25 units to the right. The |slant| is a pure
23211 number; it is the only |fix_word| other than the design size itself that is
23212 not scaled by the design size.
23214 \hang|param[2]=space| is the normal spacing between words in text.
23215 Note that character 040 in the font need not have anything to do with
23218 \hang|param[3]=space_stretch| is the amount of glue stretching between words.
23220 \hang|param[4]=space_shrink| is the amount of glue shrinking between words.
23222 \hang|param[5]=x_height| is the size of one ex in the font; it is also
23223 the height of letters for which accents don't have to be raised or lowered.
23225 \hang|param[6]=quad| is the size of one em in the font.
23227 \hang|param[7]=extra_space| is the amount added to |param[2]| at the
23231 If fewer than seven parameters are present, \TeX\ sets the missing parameters
23236 @d space_stretch_code 3
23237 @d space_shrink_code 4
23240 @d extra_space_code 7
23242 @ So that is what \.{TFM} files hold. One of \MP's duties is to output such
23243 information, and it does this all at once at the end of a job.
23244 In order to prepare for such frenetic activity, it squirrels away the
23245 necessary facts in various arrays as information becomes available.
23247 Character dimensions (\&{charwd}, \&{charht}, \&{chardp}, and \&{charic})
23248 are stored respectively in |tfm_width|, |tfm_height|, |tfm_depth|, and
23249 |tfm_ital_corr|. Other information about a character (e.g., about
23250 its ligatures or successors) is accessible via the |char_tag| and
23251 |char_remainder| arrays. Other information about the font as a whole
23252 is kept in additional arrays called |header_byte|, |lig_kern|,
23253 |kern|, |exten|, and |param|.
23255 @d max_tfm_int 32510
23256 @d undefined_label max_tfm_int /* an undefined local label */
23259 #define TFM_ITEMS 257
23261 eight_bits ec; /* smallest and largest character codes shipped out */
23262 scaled tfm_width[TFM_ITEMS]; /* \&{charwd} values */
23263 scaled tfm_height[TFM_ITEMS]; /* \&{charht} values */
23264 scaled tfm_depth[TFM_ITEMS]; /* \&{chardp} values */
23265 scaled tfm_ital_corr[TFM_ITEMS]; /* \&{charic} values */
23266 boolean char_exists[TFM_ITEMS]; /* has this code been shipped out? */
23267 int char_tag[TFM_ITEMS]; /* |remainder| category */
23268 int char_remainder[TFM_ITEMS]; /* the |remainder| byte */
23269 char *header_byte; /* bytes of the \.{TFM} header */
23270 int header_last; /* last initialized \.{TFM} header byte */
23271 int header_size; /* size of the \.{TFM} header */
23272 four_quarters *lig_kern; /* the ligature/kern table */
23273 short nl; /* the number of ligature/kern steps so far */
23274 scaled *kern; /* distinct kerning amounts */
23275 short nk; /* the number of distinct kerns so far */
23276 four_quarters exten[TFM_ITEMS]; /* extensible character recipes */
23277 short ne; /* the number of extensible characters so far */
23278 scaled *param; /* \&{fontinfo} parameters */
23279 short np; /* the largest \&{fontinfo} parameter specified so far */
23280 short nw;short nh;short nd;short ni; /* sizes of \.{TFM} subtables */
23281 short skip_table[TFM_ITEMS]; /* local label status */
23282 boolean lk_started; /* has there been a lig/kern step in this command yet? */
23283 integer bchar; /* right boundary character */
23284 short bch_label; /* left boundary starting location */
23285 short ll;short lll; /* registers used for lig/kern processing */
23286 short label_loc[257]; /* lig/kern starting addresses */
23287 eight_bits label_char[257]; /* characters for |label_loc| */
23288 short label_ptr; /* highest position occupied in |label_loc| */
23290 @ @<Allocate or initialize ...@>=
23291 mp->header_last = 0; mp->header_size = 128; /* just for init */
23292 mp->header_byte = xmalloc(mp->header_size, sizeof(char));
23293 mp->lig_kern = NULL; /* allocated when needed */
23294 mp->kern = NULL; /* allocated when needed */
23295 mp->param = NULL; /* allocated when needed */
23297 @ @<Dealloc variables@>=
23298 xfree(mp->header_byte);
23299 xfree(mp->lig_kern);
23304 for (k=0;k<= 255;k++ ) {
23305 mp->tfm_width[k]=0; mp->tfm_height[k]=0; mp->tfm_depth[k]=0; mp->tfm_ital_corr[k]=0;
23306 mp->char_exists[k]=false; mp->char_tag[k]=no_tag; mp->char_remainder[k]=0;
23307 mp->skip_table[k]=undefined_label;
23309 memset(mp->header_byte,0,mp->header_size);
23310 mp->bc=255; mp->ec=0; mp->nl=0; mp->nk=0; mp->ne=0; mp->np=0;
23311 mp->internal[boundary_char]=-unity;
23312 mp->bch_label=undefined_label;
23313 mp->label_loc[0]=-1; mp->label_ptr=0;
23315 @ @<Declarations@>=
23316 scaled mp_tfm_check (MP mp,small_number m) ;
23318 @ @<Declare the function called |tfm_check|@>=
23319 scaled mp_tfm_check (MP mp,small_number m) {
23320 if ( abs(mp->internal[m])>=fraction_half ) {
23321 print_err("Enormous "); mp_print(mp, mp->int_name[m]);
23322 @.Enormous charwd...@>
23323 @.Enormous chardp...@>
23324 @.Enormous charht...@>
23325 @.Enormous charic...@>
23326 @.Enormous designsize...@>
23327 mp_print(mp, " has been reduced");
23328 help1("Font metric dimensions must be less than 2048pt.");
23329 mp_put_get_error(mp);
23330 if ( mp->internal[m]>0 ) return (fraction_half-1);
23331 else return (1-fraction_half);
23333 return mp->internal[m];
23337 @ @<Store the width information for character code~|c|@>=
23338 if ( c<mp->bc ) mp->bc=c;
23339 if ( c>mp->ec ) mp->ec=c;
23340 mp->char_exists[c]=true;
23341 mp->tfm_width[c]=mp_tfm_check(mp, char_wd);
23342 mp->tfm_height[c]=mp_tfm_check(mp, char_ht);
23343 mp->tfm_depth[c]=mp_tfm_check(mp, char_dp);
23344 mp->tfm_ital_corr[c]=mp_tfm_check(mp, char_ic)
23346 @ Now let's consider \MP's special \.{TFM}-oriented commands.
23348 @<Cases of |do_statement|...@>=
23349 case tfm_command: mp_do_tfm_command(mp); break;
23351 @ @d char_list_code 0
23352 @d lig_table_code 1
23353 @d extensible_code 2
23354 @d header_byte_code 3
23355 @d font_dimen_code 4
23358 mp_primitive(mp, "charlist",tfm_command,char_list_code);
23359 @:char_list_}{\&{charlist} primitive@>
23360 mp_primitive(mp, "ligtable",tfm_command,lig_table_code);
23361 @:lig_table_}{\&{ligtable} primitive@>
23362 mp_primitive(mp, "extensible",tfm_command,extensible_code);
23363 @:extensible_}{\&{extensible} primitive@>
23364 mp_primitive(mp, "headerbyte",tfm_command,header_byte_code);
23365 @:header_byte_}{\&{headerbyte} primitive@>
23366 mp_primitive(mp, "fontdimen",tfm_command,font_dimen_code);
23367 @:font_dimen_}{\&{fontdimen} primitive@>
23369 @ @<Cases of |print_cmd...@>=
23372 case char_list_code:mp_print(mp, "charlist"); break;
23373 case lig_table_code:mp_print(mp, "ligtable"); break;
23374 case extensible_code:mp_print(mp, "extensible"); break;
23375 case header_byte_code:mp_print(mp, "headerbyte"); break;
23376 default: mp_print(mp, "fontdimen"); break;
23380 @ @<Declare action procedures for use by |do_statement|@>=
23381 eight_bits mp_get_code (MP mp) ;
23383 @ @c eight_bits mp_get_code (MP mp) { /* scans a character code value */
23384 integer c; /* the code value found */
23385 mp_get_x_next(mp); mp_scan_expression(mp);
23386 if ( mp->cur_type==mp_known ) {
23387 c=mp_round_unscaled(mp, mp->cur_exp);
23388 if ( c>=0 ) if ( c<256 ) return c;
23389 } else if ( mp->cur_type==mp_string_type ) {
23390 if ( length(mp->cur_exp)==1 ) {
23391 c=mp->str_pool[mp->str_start[mp->cur_exp]];
23395 exp_err("Invalid code has been replaced by 0");
23396 @.Invalid code...@>
23397 help2("I was looking for a number between 0 and 255, or for a")
23398 ("string of length 1. Didn't find it; will use 0 instead.");
23399 mp_put_get_flush_error(mp, 0); c=0;
23403 @ @<Declare action procedures for use by |do_statement|@>=
23404 void mp_set_tag (MP mp,halfword c, small_number t, halfword r) ;
23406 @ @c void mp_set_tag (MP mp,halfword c, small_number t, halfword r) {
23407 if ( mp->char_tag[c]==no_tag ) {
23408 mp->char_tag[c]=t; mp->char_remainder[c]=r;
23410 incr(mp->label_ptr); mp->label_loc[mp->label_ptr]=r;
23411 mp->label_char[mp->label_ptr]=c;
23414 @<Complain about a character tag conflict@>;
23418 @ @<Complain about a character tag conflict@>=
23420 print_err("Character ");
23421 if ( (c>' ')&&(c<127) ) mp_print_char(mp,c);
23422 else if ( c==256 ) mp_print(mp, "||");
23423 else { mp_print(mp, "code "); mp_print_int(mp, c); };
23424 mp_print(mp, " is already ");
23425 @.Character c is already...@>
23426 switch (mp->char_tag[c]) {
23427 case lig_tag: mp_print(mp, "in a ligtable"); break;
23428 case list_tag: mp_print(mp, "in a charlist"); break;
23429 case ext_tag: mp_print(mp, "extensible"); break;
23430 } /* there are no other cases */
23431 help2("It's not legal to label a character more than once.")
23432 ("So I'll not change anything just now.");
23433 mp_put_get_error(mp);
23436 @ @<Declare action procedures for use by |do_statement|@>=
23437 void mp_do_tfm_command (MP mp) ;
23439 @ @c void mp_do_tfm_command (MP mp) {
23440 int c,cc; /* character codes */
23441 int k; /* index into the |kern| array */
23442 int j; /* index into |header_byte| or |param| */
23443 switch (mp->cur_mod) {
23444 case char_list_code:
23446 /* we will store a list of character successors */
23447 while ( mp->cur_cmd==colon ) {
23448 cc=mp_get_code(mp); mp_set_tag(mp, c,list_tag,cc); c=cc;
23451 case lig_table_code:
23452 if (mp->lig_kern==NULL)
23453 mp->lig_kern = xmalloc((max_tfm_int+1),sizeof(four_quarters));
23454 if (mp->kern==NULL)
23455 mp->kern = xmalloc((max_tfm_int+1),sizeof(scaled));
23456 @<Store a list of ligature/kern steps@>;
23458 case extensible_code:
23459 @<Define an extensible recipe@>;
23461 case header_byte_code:
23462 case font_dimen_code:
23463 c=mp->cur_mod; mp_get_x_next(mp);
23464 mp_scan_expression(mp);
23465 if ( (mp->cur_type!=mp_known)||(mp->cur_exp<half_unit) ) {
23466 exp_err("Improper location");
23467 @.Improper location@>
23468 help2("I was looking for a known, positive number.")
23469 ("For safety's sake I'll ignore the present command.");
23470 mp_put_get_error(mp);
23472 j=mp_round_unscaled(mp, mp->cur_exp);
23473 if ( mp->cur_cmd!=colon ) {
23474 mp_missing_err(mp, ":");
23476 help1("A colon should follow a headerbyte or fontinfo location.");
23479 if ( c==header_byte_code ) {
23480 @<Store a list of header bytes@>;
23482 if (mp->param==NULL)
23483 mp->param = xmalloc((max_tfm_int+1),sizeof(scaled));
23484 @<Store a list of font dimensions@>;
23488 } /* there are no other cases */
23491 @ @<Store a list of ligature/kern steps@>=
23493 mp->lk_started=false;
23496 if ((mp->cur_cmd==skip_to)&& mp->lk_started )
23497 @<Process a |skip_to| command and |goto done|@>;
23498 if ( mp->cur_cmd==bchar_label ) { c=256; mp->cur_cmd=colon; }
23499 else { mp_back_input(mp); c=mp_get_code(mp); };
23500 if ((mp->cur_cmd==colon)||(mp->cur_cmd==double_colon)) {
23501 @<Record a label in a lig/kern subprogram and |goto continue|@>;
23503 if ( mp->cur_cmd==lig_kern_token ) {
23504 @<Compile a ligature/kern command@>;
23506 print_err("Illegal ligtable step");
23507 @.Illegal ligtable step@>
23508 help1("I was looking for `=:' or `kern' here.");
23509 mp_back_error(mp); next_char(mp->nl)=qi(0);
23510 op_byte(mp->nl)=qi(0); rem_byte(mp->nl)=qi(0);
23511 skip_byte(mp->nl)=stop_flag+1; /* this specifies an unconditional stop */
23513 if ( mp->nl==max_tfm_int) mp_fatal_error(mp, "ligtable too large");
23515 if ( mp->cur_cmd==comma ) goto CONTINUE;
23516 if ( skip_byte(mp->nl-1)<stop_flag ) skip_byte(mp->nl-1)=stop_flag;
23521 mp_primitive(mp, "=:",lig_kern_token,0);
23522 @:=:_}{\.{=:} primitive@>
23523 mp_primitive(mp, "=:|",lig_kern_token,1);
23524 @:=:/_}{\.{=:\char'174} primitive@>
23525 mp_primitive(mp, "=:|>",lig_kern_token,5);
23526 @:=:/>_}{\.{=:\char'174>} primitive@>
23527 mp_primitive(mp, "|=:",lig_kern_token,2);
23528 @:=:/_}{\.{\char'174=:} primitive@>
23529 mp_primitive(mp, "|=:>",lig_kern_token,6);
23530 @:=:/>_}{\.{\char'174=:>} primitive@>
23531 mp_primitive(mp, "|=:|",lig_kern_token,3);
23532 @:=:/_}{\.{\char'174=:\char'174} primitive@>
23533 mp_primitive(mp, "|=:|>",lig_kern_token,7);
23534 @:=:/>_}{\.{\char'174=:\char'174>} primitive@>
23535 mp_primitive(mp, "|=:|>>",lig_kern_token,11);
23536 @:=:/>_}{\.{\char'174=:\char'174>>} primitive@>
23537 mp_primitive(mp, "kern",lig_kern_token,128);
23538 @:kern_}{\&{kern} primitive@>
23540 @ @<Cases of |print_cmd...@>=
23541 case lig_kern_token:
23543 case 0:mp_print(mp, "=:"); break;
23544 case 1:mp_print(mp, "=:|"); break;
23545 case 2:mp_print(mp, "|=:"); break;
23546 case 3:mp_print(mp, "|=:|"); break;
23547 case 5:mp_print(mp, "=:|>"); break;
23548 case 6:mp_print(mp, "|=:>"); break;
23549 case 7:mp_print(mp, "|=:|>"); break;
23550 case 11:mp_print(mp, "|=:|>>"); break;
23551 default: mp_print(mp, "kern"); break;
23555 @ Local labels are implemented by maintaining the |skip_table| array,
23556 where |skip_table[c]| is either |undefined_label| or the address of the
23557 most recent lig/kern instruction that skips to local label~|c|. In the
23558 latter case, the |skip_byte| in that instruction will (temporarily)
23559 be zero if there were no prior skips to this label, or it will be the
23560 distance to the prior skip.
23562 We may need to cancel skips that span more than 127 lig/kern steps.
23564 @d cancel_skips(A) mp->ll=(A);
23566 mp->lll=qo(skip_byte(mp->ll));
23567 skip_byte(mp->ll)=stop_flag; mp->ll=mp->ll-mp->lll;
23568 } while (mp->lll!=0)
23569 @d skip_error(A) { print_err("Too far to skip");
23570 @.Too far to skip@>
23571 help1("At most 127 lig/kern steps can separate skipto1 from 1::.");
23572 mp_error(mp); cancel_skips((A));
23575 @<Process a |skip_to| command and |goto done|@>=
23578 if ( mp->nl-mp->skip_table[c]>128 ) { /* |skip_table[c]<<nl<=undefined_label| */
23579 skip_error(mp->skip_table[c]); mp->skip_table[c]=undefined_label;
23581 if ( mp->skip_table[c]==undefined_label ) skip_byte(mp->nl-1)=qi(0);
23582 else skip_byte(mp->nl-1)=qi(mp->nl-mp->skip_table[c]-1);
23583 mp->skip_table[c]=mp->nl-1; goto DONE;
23586 @ @<Record a label in a lig/kern subprogram and |goto continue|@>=
23588 if ( mp->cur_cmd==colon ) {
23589 if ( c==256 ) mp->bch_label=mp->nl;
23590 else mp_set_tag(mp, c,lig_tag,mp->nl);
23591 } else if ( mp->skip_table[c]<undefined_label ) {
23592 mp->ll=mp->skip_table[c]; mp->skip_table[c]=undefined_label;
23594 mp->lll=qo(skip_byte(mp->ll));
23595 if ( mp->nl-mp->ll>128 ) {
23596 skip_error(mp->ll); goto CONTINUE;
23598 skip_byte(mp->ll)=qi(mp->nl-mp->ll-1); mp->ll=mp->ll-mp->lll;
23599 } while (mp->lll!=0);
23604 @ @<Compile a ligature/kern...@>=
23606 next_char(mp->nl)=qi(c); skip_byte(mp->nl)=qi(0);
23607 if ( mp->cur_mod<128 ) { /* ligature op */
23608 op_byte(mp->nl)=qi(mp->cur_mod); rem_byte(mp->nl)=qi(mp_get_code(mp));
23610 mp_get_x_next(mp); mp_scan_expression(mp);
23611 if ( mp->cur_type!=mp_known ) {
23612 exp_err("Improper kern");
23614 help2("The amount of kern should be a known numeric value.")
23615 ("I'm zeroing this one. Proceed, with fingers crossed.");
23616 mp_put_get_flush_error(mp, 0);
23618 mp->kern[mp->nk]=mp->cur_exp;
23620 while ( mp->kern[k]!=mp->cur_exp ) incr(k);
23622 if ( mp->nk==max_tfm_int ) mp_fatal_error(mp, "too many TFM kerns");
23625 op_byte(mp->nl)=kern_flag+(k / 256);
23626 rem_byte(mp->nl)=qi((k % 256));
23628 mp->lk_started=true;
23631 @ @d missing_extensible_punctuation(A)
23632 { mp_missing_err(mp, (A));
23633 @.Missing `\char`\#'@>
23634 help1("I'm processing `extensible c: t,m,b,r'."); mp_back_error(mp);
23637 @<Define an extensible recipe@>=
23639 if ( mp->ne==256 ) mp_fatal_error(mp, "too many extensible recipies");
23640 c=mp_get_code(mp); mp_set_tag(mp, c,ext_tag,mp->ne);
23641 if ( mp->cur_cmd!=colon ) missing_extensible_punctuation(":");
23642 ext_top(mp->ne)=qi(mp_get_code(mp));
23643 if ( mp->cur_cmd!=comma ) missing_extensible_punctuation(",");
23644 ext_mid(mp->ne)=qi(mp_get_code(mp));
23645 if ( mp->cur_cmd!=comma ) missing_extensible_punctuation(",");
23646 ext_bot(mp->ne)=qi(mp_get_code(mp));
23647 if ( mp->cur_cmd!=comma ) missing_extensible_punctuation(",");
23648 ext_rep(mp->ne)=qi(mp_get_code(mp));
23652 @ The header could contain ASCII zeroes, so can't use |strdup|.
23654 @<Store a list of header bytes@>=
23656 if ( j>=mp->header_size ) {
23657 int l = mp->header_size + (mp->header_size >> 2);
23658 char *t = xmalloc(l,sizeof(char));
23660 memcpy(t,mp->header_byte,mp->header_size);
23661 xfree (mp->header_byte);
23662 mp->header_byte = t;
23663 mp->header_size = l;
23665 mp->header_byte[j]=mp_get_code(mp);
23666 incr(j); incr(mp->header_last);
23667 } while (mp->cur_cmd==comma)
23669 @ @<Store a list of font dimensions@>=
23671 if ( j>max_tfm_int ) mp_fatal_error(mp, "too many fontdimens");
23672 while ( j>mp->np ) { incr(mp->np); mp->param[mp->np]=0; };
23673 mp_get_x_next(mp); mp_scan_expression(mp);
23674 if ( mp->cur_type!=mp_known ){
23675 exp_err("Improper font parameter");
23676 @.Improper font parameter@>
23677 help1("I'm zeroing this one. Proceed, with fingers crossed.");
23678 mp_put_get_flush_error(mp, 0);
23680 mp->param[j]=mp->cur_exp; incr(j);
23681 } while (mp->cur_cmd==comma)
23683 @ OK: We've stored all the data that is needed for the \.{TFM} file.
23684 All that remains is to output it in the correct format.
23686 An interesting problem needs to be solved in this connection, because
23687 the \.{TFM} format allows at most 256~widths, 16~heights, 16~depths,
23688 and 64~italic corrections. If the data has more distinct values than
23689 this, we want to meet the necessary restrictions by perturbing the
23690 given values as little as possible.
23692 \MP\ solves this problem in two steps. First the values of a given
23693 kind (widths, heights, depths, or italic corrections) are sorted;
23694 then the list of sorted values is perturbed, if necessary.
23696 The sorting operation is facilitated by having a special node of
23697 essentially infinite |value| at the end of the current list.
23699 @<Initialize table entries...@>=
23700 value(inf_val)=fraction_four;
23702 @ Straight linear insertion is good enough for sorting, since the lists
23703 are usually not terribly long. As we work on the data, the current list
23704 will start at |link(temp_head)| and end at |inf_val|; the nodes in this
23705 list will be in increasing order of their |value| fields.
23707 Given such a list, the |sort_in| function takes a value and returns a pointer
23708 to where that value can be found in the list. The value is inserted in
23709 the proper place, if necessary.
23711 At the time we need to do these operations, most of \MP's work has been
23712 completed, so we will have plenty of memory to play with. The value nodes
23713 that are allocated for sorting will never be returned to free storage.
23715 @d clear_the_list link(temp_head)=inf_val
23717 @c pointer mp_sort_in (MP mp,scaled v) {
23718 pointer p,q,r; /* list manipulation registers */
23722 if ( v<=value(q) ) break;
23725 if ( v<value(q) ) {
23726 r=mp_get_node(mp, value_node_size); value(r)=v; link(r)=q; link(p)=r;
23731 @ Now we come to the interesting part, where we reduce the list if necessary
23732 until it has the required size. The |min_cover| routine is basic to this
23733 process; it computes the minimum number~|m| such that the values of the
23734 current sorted list can be covered by |m|~intervals of width~|d|. It
23735 also sets the global value |perturbation| to the smallest value $d'>d$
23736 such that the covering found by this algorithm would be different.
23738 In particular, |min_cover(0)| returns the number of distinct values in the
23739 current list and sets |perturbation| to the minimum distance between
23742 @c integer mp_min_cover (MP mp,scaled d) {
23743 pointer p; /* runs through the current list */
23744 scaled l; /* the least element covered by the current interval */
23745 integer m; /* lower bound on the size of the minimum cover */
23746 m=0; p=link(temp_head); mp->perturbation=el_gordo;
23747 while ( p!=inf_val ){
23748 incr(m); l=value(p);
23749 do { p=link(p); } while (value(p)<=l+d);
23750 if ( value(p)-l<mp->perturbation )
23751 mp->perturbation=value(p)-l;
23757 scaled perturbation; /* quantity related to \.{TFM} rounding */
23758 integer excess; /* the list is this much too long */
23760 @ The smallest |d| such that a given list can be covered with |m| intervals
23761 is determined by the |threshold| routine, which is sort of an inverse
23762 to |min_cover|. The idea is to increase the interval size rapidly until
23763 finding the range, then to go sequentially until the exact borderline has
23766 @c scaled mp_threshold (MP mp,integer m) {
23767 scaled d; /* lower bound on the smallest interval size */
23768 mp->excess=mp_min_cover(mp, 0)-m;
23769 if ( mp->excess<=0 ) {
23773 d=mp->perturbation;
23774 } while (mp_min_cover(mp, d+d)>m);
23775 while ( mp_min_cover(mp, d)>m )
23776 d=mp->perturbation;
23781 @ The |skimp| procedure reduces the current list to at most |m| entries,
23782 by changing values if necessary. It also sets |info(p):=k| if |value(p)|
23783 is the |k|th distinct value on the resulting list, and it sets
23784 |perturbation| to the maximum amount by which a |value| field has
23785 been changed. The size of the resulting list is returned as the
23788 @c integer mp_skimp (MP mp,integer m) {
23789 scaled d; /* the size of intervals being coalesced */
23790 pointer p,q,r; /* list manipulation registers */
23791 scaled l; /* the least value in the current interval */
23792 scaled v; /* a compromise value */
23793 d=mp_threshold(mp, m); mp->perturbation=0;
23794 q=temp_head; m=0; p=link(temp_head);
23795 while ( p!=inf_val ) {
23796 incr(m); l=value(p); info(p)=m;
23797 if ( value(link(p))<=l+d ) {
23798 @<Replace an interval of values by its midpoint@>;
23805 @ @<Replace an interval...@>=
23808 p=link(p); info(p)=m;
23809 decr(mp->excess); if ( mp->excess==0 ) d=0;
23810 } while (value(link(p))<=l+d);
23811 v=l+halfp(value(p)-l);
23812 if ( value(p)-v>mp->perturbation )
23813 mp->perturbation=value(p)-v;
23816 r=link(r); value(r)=v;
23818 link(q)=p; /* remove duplicate values from the current list */
23821 @ A warning message is issued whenever something is perturbed by
23822 more than 1/16\thinspace pt.
23824 @c void mp_tfm_warning (MP mp,small_number m) {
23825 mp_print_nl(mp, "(some ");
23826 mp_print(mp, mp->int_name[m]);
23827 @.some charwds...@>
23828 @.some chardps...@>
23829 @.some charhts...@>
23830 @.some charics...@>
23831 mp_print(mp, " values had to be adjusted by as much as ");
23832 mp_print_scaled(mp, mp->perturbation); mp_print(mp, "pt)");
23835 @ Here's an example of how we use these routines.
23836 The width data needs to be perturbed only if there are 256 distinct
23837 widths, but \MP\ must check for this case even though it is
23840 An integer variable |k| will be defined when we use this code.
23841 The |dimen_head| array will contain pointers to the sorted
23842 lists of dimensions.
23844 @<Massage the \.{TFM} widths@>=
23846 for (k=mp->bc;k<=mp->ec;k++) {
23847 if ( mp->char_exists[k] )
23848 mp->tfm_width[k]=mp_sort_in(mp, mp->tfm_width[k]);
23850 mp->nw=mp_skimp(mp, 255)+1; mp->dimen_head[1]=link(temp_head);
23851 if ( mp->perturbation>=010000 ) mp_tfm_warning(mp, char_wd)
23854 pointer dimen_head[5]; /* lists of \.{TFM} dimensions */
23856 @ Heights, depths, and italic corrections are different from widths
23857 not only because their list length is more severely restricted, but
23858 also because zero values do not need to be put into the lists.
23860 @<Massage the \.{TFM} heights, depths, and italic corrections@>=
23862 for (k=mp->bc;k<=mp->ec;k++) {
23863 if ( mp->char_exists[k] ) {
23864 if ( mp->tfm_height[k]==0 ) mp->tfm_height[k]=zero_val;
23865 else mp->tfm_height[k]=mp_sort_in(mp, mp->tfm_height[k]);
23868 mp->nh=mp_skimp(mp, 15)+1; mp->dimen_head[2]=link(temp_head);
23869 if ( mp->perturbation>=010000 ) mp_tfm_warning(mp, char_ht);
23871 for (k=mp->bc;k<=mp->ec;k++) {
23872 if ( mp->char_exists[k] ) {
23873 if ( mp->tfm_depth[k]==0 ) mp->tfm_depth[k]=zero_val;
23874 else mp->tfm_depth[k]=mp_sort_in(mp, mp->tfm_depth[k]);
23877 mp->nd=mp_skimp(mp, 15)+1; mp->dimen_head[3]=link(temp_head);
23878 if ( mp->perturbation>=010000 ) mp_tfm_warning(mp, char_dp);
23880 for (k=mp->bc;k<=mp->ec;k++) {
23881 if ( mp->char_exists[k] ) {
23882 if ( mp->tfm_ital_corr[k]==0 ) mp->tfm_ital_corr[k]=zero_val;
23883 else mp->tfm_ital_corr[k]=mp_sort_in(mp, mp->tfm_ital_corr[k]);
23886 mp->ni=mp_skimp(mp, 63)+1; mp->dimen_head[4]=link(temp_head);
23887 if ( mp->perturbation>=010000 ) mp_tfm_warning(mp, char_ic)
23889 @ @<Initialize table entries...@>=
23890 value(zero_val)=0; info(zero_val)=0;
23892 @ Bytes 5--8 of the header are set to the design size, unless the user has
23893 some crazy reason for specifying them differently.
23895 Error messages are not allowed at the time this procedure is called,
23896 so a warning is printed instead.
23898 The value of |max_tfm_dimen| is calculated so that
23899 $$\hbox{|make_scaled(16*max_tfm_dimen,internal[design_size])|}
23900 < \\{three\_bytes}.$$
23902 @d three_bytes 0100000000 /* $2^{24}$ */
23905 void mp_fix_design_size (MP mp) {
23906 scaled d; /* the design size */
23907 d=mp->internal[design_size];
23908 if ( (d<unity)||(d>=fraction_half) ) {
23910 mp_print_nl(mp, "(illegal design size has been changed to 128pt)");
23911 @.illegal design size...@>
23912 d=040000000; mp->internal[design_size]=d;
23914 if ( mp->header_byte[4]<0 ) if ( mp->header_byte[5]<0 )
23915 if ( mp->header_byte[6]<0 ) if ( mp->header_byte[7]<0 ) {
23916 mp->header_byte[4]=d / 04000000;
23917 mp->header_byte[5]=(d / 4096) % 256;
23918 mp->header_byte[6]=(d / 16) % 256;
23919 mp->header_byte[7]=(d % 16)*16;
23921 mp->max_tfm_dimen=16*mp->internal[design_size]-mp->internal[design_size] / 010000000;
23922 if ( mp->max_tfm_dimen>=fraction_half ) mp->max_tfm_dimen=fraction_half-1;
23925 @ The |dimen_out| procedure computes a |fix_word| relative to the
23926 design size. If the data was out of range, it is corrected and the
23927 global variable |tfm_changed| is increased by~one.
23929 @c integer mp_dimen_out (MP mp,scaled x) {
23930 if ( abs(x)>mp->max_tfm_dimen ) {
23931 incr(mp->tfm_changed);
23932 if ( x>0 ) x=three_bytes-1; else x=1-three_bytes;
23934 x=mp_make_scaled(mp, x*16,mp->internal[design_size]);
23940 scaled max_tfm_dimen; /* bound on widths, heights, kerns, etc. */
23941 integer tfm_changed; /* the number of data entries that were out of bounds */
23943 @ If the user has not specified any of the first four header bytes,
23944 the |fix_check_sum| procedure replaces them by a ``check sum'' computed
23945 from the |tfm_width| data relative to the design size.
23948 @c void mp_fix_check_sum (MP mp) {
23949 eight_bits k; /* runs through character codes */
23950 eight_bits B1,B2,B3,B4; /* bytes of the check sum */
23951 integer x; /* hash value used in check sum computation */
23952 if ( mp->header_byte[0]==0 && mp->header_byte[1]==0 &&
23953 mp->header_byte[2]==0 && mp->header_byte[3]==0 ) {
23954 @<Compute a check sum in |(b1,b2,b3,b4)|@>;
23955 mp->header_byte[0]=B1; mp->header_byte[1]=B2;
23956 mp->header_byte[2]=B3; mp->header_byte[3]=B4;
23961 @ @<Compute a check sum in |(b1,b2,b3,b4)|@>=
23962 B1=mp->bc; B2=mp->ec; B3=mp->bc; B4=mp->ec; mp->tfm_changed=0;
23963 for (k=mp->bc;k<=mp->ec;k++) {
23964 if ( mp->char_exists[k] ) {
23965 x=mp_dimen_out(mp, value(mp->tfm_width[k]))+(k+4)*020000000; /* this is positive */
23966 B1=(B1+B1+x) % 255;
23967 B2=(B2+B2+x) % 253;
23968 B3=(B3+B3+x) % 251;
23969 B4=(B4+B4+x) % 247;
23973 @ Finally we're ready to actually write the \.{TFM} information.
23974 Here are some utility routines for this purpose.
23976 @d tfm_out(A) fputc((A),mp->tfm_file) /* output one byte to |tfm_file| */
23978 @c void mp_tfm_two (MP mp,integer x) { /* output two bytes to |tfm_file| */
23979 tfm_out(x / 256); tfm_out(x % 256);
23981 void mp_tfm_four (MP mp,integer x) { /* output four bytes to |tfm_file| */
23982 if ( x>=0 ) tfm_out(x / three_bytes);
23984 x=x+010000000000; /* use two's complement for negative values */
23986 tfm_out((x / three_bytes) + 128);
23988 x=x % three_bytes; tfm_out(x / unity);
23989 x=x % unity; tfm_out(x / 0400);
23992 void mp_tfm_qqqq (MP mp,four_quarters x) { /* output four quarterwords to |tfm_file| */
23993 tfm_out(qo(x.b0)); tfm_out(qo(x.b1));
23994 tfm_out(qo(x.b2)); tfm_out(qo(x.b3));
23997 @ @<Finish the \.{TFM} file@>=
23998 if ( mp->job_name==NULL ) mp_open_log_file(mp);
23999 mp_pack_job_name(mp, ".tfm");
24000 while ( ! mp_b_open_out(mp, &mp->tfm_file, mp_filetype_metrics) )
24001 mp_prompt_file_name(mp, "file name for font metrics",".tfm");
24002 mp->metric_file_name=xstrdup(mp->name_of_file);
24003 @<Output the subfile sizes and header bytes@>;
24004 @<Output the character information bytes, then
24005 output the dimensions themselves@>;
24006 @<Output the ligature/kern program@>;
24007 @<Output the extensible character recipes and the font metric parameters@>;
24008 if ( mp->internal[tracing_stats]>0 )
24009 @<Log the subfile sizes of the \.{TFM} file@>;
24010 mp_print_nl(mp, "Font metrics written on ");
24011 mp_print(mp, mp->metric_file_name); mp_print_char(mp, '.');
24012 @.Font metrics written...@>
24013 fclose(mp->tfm_file)
24015 @ Integer variables |lh|, |k|, and |lk_offset| will be defined when we use
24018 @<Output the subfile sizes and header bytes@>=
24020 LH=(k+3) / 4; /* this is the number of header words */
24021 if ( mp->bc>mp->ec ) mp->bc=1; /* if there are no characters, |ec=0| and |bc=1| */
24022 @<Compute the ligature/kern program offset and implant the
24023 left boundary label@>;
24024 mp_tfm_two(mp,6+LH+(mp->ec-mp->bc+1)+mp->nw+mp->nh+mp->nd+mp->ni+mp->nl
24025 +lk_offset+mp->nk+mp->ne+mp->np);
24026 /* this is the total number of file words that will be output */
24027 mp_tfm_two(mp, LH); mp_tfm_two(mp, mp->bc); mp_tfm_two(mp, mp->ec);
24028 mp_tfm_two(mp, mp->nw); mp_tfm_two(mp, mp->nh);
24029 mp_tfm_two(mp, mp->nd); mp_tfm_two(mp, mp->ni); mp_tfm_two(mp, mp->nl+lk_offset);
24030 mp_tfm_two(mp, mp->nk); mp_tfm_two(mp, mp->ne);
24031 mp_tfm_two(mp, mp->np);
24032 for (k=0;k< 4*LH;k++) {
24033 tfm_out(mp->header_byte[k]);
24036 @ @<Output the character information bytes...@>=
24037 for (k=mp->bc;k<=mp->ec;k++) {
24038 if ( ! mp->char_exists[k] ) {
24039 mp_tfm_four(mp, 0);
24041 tfm_out(info(mp->tfm_width[k])); /* the width index */
24042 tfm_out((info(mp->tfm_height[k]))*16+info(mp->tfm_depth[k]));
24043 tfm_out((info(mp->tfm_ital_corr[k]))*4+mp->char_tag[k]);
24044 tfm_out(mp->char_remainder[k]);
24048 for (k=1;k<=4;k++) {
24049 mp_tfm_four(mp, 0); p=mp->dimen_head[k];
24050 while ( p!=inf_val ) {
24051 mp_tfm_four(mp, mp_dimen_out(mp, value(p))); p=link(p);
24056 @ We need to output special instructions at the beginning of the
24057 |lig_kern| array in order to specify the right boundary character
24058 and/or to handle starting addresses that exceed 255. The |label_loc|
24059 and |label_char| arrays have been set up to record all the
24060 starting addresses; we have $-1=|label_loc|[0]<|label_loc|[1]\le\cdots
24061 \le|label_loc|[|label_ptr]|$.
24063 @<Compute the ligature/kern program offset...@>=
24064 mp->bchar=mp_round_unscaled(mp, mp->internal[boundary_char]);
24065 if ((mp->bchar<0)||(mp->bchar>255))
24066 { mp->bchar=-1; mp->lk_started=false; lk_offset=0; }
24067 else { mp->lk_started=true; lk_offset=1; };
24068 @<Find the minimum |lk_offset| and adjust all remainders@>;
24069 if ( mp->bch_label<undefined_label )
24070 { skip_byte(mp->nl)=qi(255); next_char(mp->nl)=qi(0);
24071 op_byte(mp->nl)=qi(((mp->bch_label+lk_offset)/ 256));
24072 rem_byte(mp->nl)=qi(((mp->bch_label+lk_offset)% 256));
24073 incr(mp->nl); /* possibly |nl=lig_table_size+1| */
24076 @ @<Find the minimum |lk_offset|...@>=
24077 k=mp->label_ptr; /* pointer to the largest unallocated label */
24078 if ( mp->label_loc[k]+lk_offset>255 ) {
24079 lk_offset=0; mp->lk_started=false; /* location 0 can do double duty */
24081 mp->char_remainder[mp->label_char[k]]=lk_offset;
24082 while ( mp->label_loc[k-1]==mp->label_loc[k] ) {
24083 decr(k); mp->char_remainder[mp->label_char[k]]=lk_offset;
24085 incr(lk_offset); decr(k);
24086 } while (! (lk_offset+mp->label_loc[k]<256));
24087 /* N.B.: |lk_offset=256| satisfies this when |k=0| */
24089 if ( lk_offset>0 ) {
24091 mp->char_remainder[mp->label_char[k]]
24092 =mp->char_remainder[mp->label_char[k]]+lk_offset;
24097 @ @<Output the ligature/kern program@>=
24098 for (k=0;k<= 255;k++ ) {
24099 if ( mp->skip_table[k]<undefined_label ) {
24100 mp_print_nl(mp, "(local label "); mp_print_int(mp, k); mp_print(mp, ":: was missing)");
24101 @.local label l:: was missing@>
24102 cancel_skips(mp->skip_table[k]);
24105 if ( mp->lk_started ) { /* |lk_offset=1| for the special |bchar| */
24106 tfm_out(255); tfm_out(mp->bchar); mp_tfm_two(mp, 0);
24108 for (k=1;k<=lk_offset;k++) {/* output the redirection specs */
24109 mp->ll=mp->label_loc[mp->label_ptr];
24110 if ( mp->bchar<0 ) { tfm_out(254); tfm_out(0); }
24111 else { tfm_out(255); tfm_out(mp->bchar); };
24112 mp_tfm_two(mp, mp->ll+lk_offset);
24114 decr(mp->label_ptr);
24115 } while (! (mp->label_loc[mp->label_ptr]<mp->ll));
24118 for (k=0;k<=mp->nl-1;k++) mp_tfm_qqqq(mp, mp->lig_kern[k]);
24119 for (k=0;k<=mp->nk-1;k++) mp_tfm_four(mp, mp_dimen_out(mp, mp->kern[k]))
24121 @ @<Output the extensible character recipes...@>=
24122 for (k=0;k<=mp->ne-1;k++)
24123 mp_tfm_qqqq(mp, mp->exten[k]);
24124 for (k=1;k<=mp->np;k++) {
24126 if ( abs(mp->param[1])<fraction_half ) {
24127 mp_tfm_four(mp, mp->param[1]*16);
24129 incr(mp->tfm_changed);
24130 if ( mp->param[1]>0 ) mp_tfm_four(mp, el_gordo);
24131 else mp_tfm_four(mp, -el_gordo);
24134 mp_tfm_four(mp, mp_dimen_out(mp, mp->param[k]));
24137 if ( mp->tfm_changed>0 ) {
24138 if ( mp->tfm_changed==1 ) mp_print_nl(mp, "(a font metric dimension");
24139 @.a font metric dimension...@>
24141 mp_print_nl(mp, "("); mp_print_int(mp, mp->tfm_changed);
24142 @.font metric dimensions...@>
24143 mp_print(mp, " font metric dimensions");
24145 mp_print(mp, " had to be decreased)");
24148 @ @<Log the subfile sizes of the \.{TFM} file@>=
24152 if ( mp->bch_label<undefined_label ) decr(mp->nl);
24153 snprintf(s,128,"(You used %iw,%ih,%id,%ii,%il,%ik,%ie,%ip metric file positions)",
24154 mp->nw, mp->nh, mp->nd, mp->ni, mp->nl, mp->nk, mp->ne,mp->np);
24158 @* \[43] Reading font metric data.
24160 \MP\ isn't a typesetting program but it does need to find the bounding box
24161 of a sequence of typeset characters. Thus it needs to read \.{TFM} files as
24162 well as write them.
24167 @ All the width, height, and depth information is stored in an array called
24168 |font_info|. This array is allocated sequentially and each font is stored
24169 as a series of |char_info| words followed by the width, height, and depth
24170 tables. Since |font_name| entries are permanent, their |str_ref| values are
24171 set to |max_str_ref|.
24174 typedef unsigned int font_number; /* |0..font_max| */
24176 @ The |font_info| array is indexed via a group directory arrays.
24177 For example, the |char_info| data for character~|c| in font~|f| will be
24178 in |font_info[char_base[f]+c].qqqq|.
24181 font_number font_max; /* maximum font number for included text fonts */
24182 size_t font_mem_size; /* number of words for \.{TFM} information for text fonts */
24183 memory_word *font_info; /* height, width, and depth data */
24184 char **font_enc_name; /* encoding names, if any */
24185 boolean *font_ps_name_fixed; /* are the postscript names fixed already? */
24186 int next_fmem; /* next unused entry in |font_info| */
24187 font_number last_fnum; /* last font number used so far */
24188 scaled *font_dsize; /* 16 times the ``design'' size in \ps\ points */
24189 char **font_name; /* name as specified in the \&{infont} command */
24190 char **font_ps_name; /* PostScript name for use when |internal[prologues]>0| */
24191 font_number last_ps_fnum; /* last valid |font_ps_name| index */
24192 eight_bits *font_bc;
24193 eight_bits *font_ec; /* first and last character code */
24194 int *char_base; /* base address for |char_info| */
24195 int *width_base; /* index for zeroth character width */
24196 int *height_base; /* index for zeroth character height */
24197 int *depth_base; /* index for zeroth character depth */
24198 pointer *font_sizes;
24200 @ @<Allocate or initialize ...@>=
24201 mp->font_mem_size = 10000;
24202 mp->font_info = xmalloc ((mp->font_mem_size+1),sizeof(memory_word));
24203 memset (mp->font_info,0,sizeof(memory_word)*(mp->font_mem_size+1));
24204 mp->font_enc_name = NULL;
24205 mp->font_ps_name_fixed = NULL;
24206 mp->font_dsize = NULL;
24207 mp->font_name = NULL;
24208 mp->font_ps_name = NULL;
24209 mp->font_bc = NULL;
24210 mp->font_ec = NULL;
24211 mp->last_fnum = null_font;
24212 mp->char_base = NULL;
24213 mp->width_base = NULL;
24214 mp->height_base = NULL;
24215 mp->depth_base = NULL;
24216 mp->font_sizes = null;
24218 @ @<Dealloc variables@>=
24219 xfree(mp->font_info);
24220 xfree(mp->font_enc_name);
24221 xfree(mp->font_ps_name_fixed);
24222 xfree(mp->font_dsize);
24223 xfree(mp->font_name);
24224 xfree(mp->font_ps_name);
24225 xfree(mp->font_bc);
24226 xfree(mp->font_ec);
24227 xfree(mp->char_base);
24228 xfree(mp->width_base);
24229 xfree(mp->height_base);
24230 xfree(mp->depth_base);
24231 xfree(mp->font_sizes);
24235 void mp_reallocate_fonts (MP mp, font_number l) {
24237 XREALLOC(mp->font_enc_name, l, char *);
24238 XREALLOC(mp->font_ps_name_fixed, l, boolean);
24239 XREALLOC(mp->font_dsize, l, scaled);
24240 XREALLOC(mp->font_name, l, char *);
24241 XREALLOC(mp->font_ps_name, l, char *);
24242 XREALLOC(mp->font_bc, l, eight_bits);
24243 XREALLOC(mp->font_ec, l, eight_bits);
24244 XREALLOC(mp->char_base, l, int);
24245 XREALLOC(mp->width_base, l, int);
24246 XREALLOC(mp->height_base, l, int);
24247 XREALLOC(mp->depth_base, l, int);
24248 XREALLOC(mp->font_sizes, l, pointer);
24249 for (f=(mp->last_fnum+1);f<=l;f++) {
24250 mp->font_enc_name[f]=NULL;
24251 mp->font_ps_name_fixed[f] = false;
24252 mp->font_name[f]=NULL;
24253 mp->font_ps_name[f]=NULL;
24254 mp->font_sizes[f]=null;
24259 @ @<Declare |mp_reallocate| functions@>=
24260 void mp_reallocate_fonts (MP mp, font_number l);
24263 @ A |null_font| containing no characters is useful for error recovery. Its
24264 |font_name| entry starts out empty but is reset each time an erroneous font is
24265 found. This helps to cut down on the number of duplicate error messages without
24266 wasting a lot of space.
24268 @d null_font 0 /* the |font_number| for an empty font */
24270 @<Set initial...@>=
24271 mp->font_dsize[null_font]=0;
24272 mp->font_bc[null_font]=1;
24273 mp->font_ec[null_font]=0;
24274 mp->char_base[null_font]=0;
24275 mp->width_base[null_font]=0;
24276 mp->height_base[null_font]=0;
24277 mp->depth_base[null_font]=0;
24279 mp->last_fnum=null_font;
24280 mp->last_ps_fnum=null_font;
24281 mp->font_name[null_font]="nullfont";
24282 mp->font_ps_name[null_font]="";
24284 @ Each |char_info| word is of type |four_quarters|. The |b0| field contains
24285 the |width index|; the |b1| field contains the height
24286 index; the |b2| fields contains the depth index, and the |b3| field used only
24287 for temporary storage. (It is used to keep track of which characters occur in
24288 an edge structure that is being shipped out.)
24289 The corresponding words in the width, height, and depth tables are stored as
24290 |scaled| values in units of \ps\ points.
24292 With the macros below, the |char_info| word for character~|c| in font~|f| is
24293 |char_info(f)(c)| and the width is
24294 $$\hbox{|char_width(f)(char_info(f)(c)).sc|.}$$
24296 @d char_info_end(A) (A)].qqqq
24297 @d char_info(A) mp->font_info[mp->char_base[(A)]+char_info_end
24298 @d char_width_end(A) (A).b0].sc
24299 @d char_width(A) mp->font_info[mp->width_base[(A)]+char_width_end
24300 @d char_height_end(A) (A).b1].sc
24301 @d char_height(A) mp->font_info[mp->height_base[(A)]+char_height_end
24302 @d char_depth_end(A) (A).b2].sc
24303 @d char_depth(A) mp->font_info[mp->depth_base[(A)]+char_depth_end
24304 @d ichar_exists(A) ((A).b0>0)
24306 @ The |font_ps_name| for a built-in font should be what PostScript expects.
24307 A preliminary name is obtained here from the \.{TFM} name as given in the
24308 |fname| argument. This gets updated later from an external table if necessary.
24310 @<Declare text measuring subroutines@>=
24311 @<Declare subroutines for parsing file names@>;
24312 font_number mp_read_font_info (MP mp, char*fname) {
24313 boolean file_opened; /* has |tfm_infile| been opened? */
24314 font_number n; /* the number to return */
24315 halfword lf,tfm_lh,bc,ec,nw,nh,nd; /* subfile size parameters */
24316 size_t whd_size; /* words needed for heights, widths, and depths */
24317 int i,ii; /* |font_info| indices */
24318 int jj; /* counts bytes to be ignored */
24319 scaled z; /* used to compute the design size */
24321 /* height, width, or depth as a fraction of design size times $2^{-8}$ */
24322 eight_bits h_and_d; /* height and depth indices being unpacked */
24323 int tfbyte; /* a byte read from the file */
24325 @<Open |tfm_infile| for input@>;
24326 @<Read data from |tfm_infile|; if there is no room, say so and |goto done|;
24327 otherwise |goto bad_tfm| or |goto done| as appropriate@>;
24329 @<Complain that the \.{TFM} file is bad@>;
24331 if ( file_opened ) fclose(mp->tfm_infile);
24332 if ( n!=null_font ) {
24333 mp->font_ps_name[n]=fname;
24334 mp->font_name[n]=fname;
24339 @ \MP\ doesn't bother to check the entire \.{TFM} file for errors or explain
24340 precisely what is wrong if it does find a problem. Programs called \.{TFtoPL}
24341 @.TFtoPL@> @.PLtoTF@>
24342 and \.{PLtoTF} can be used to debug \.{TFM} files.
24344 @<Complain that the \.{TFM} file is bad@>=
24345 print_err("Font ");
24346 mp_print(mp, fname);
24347 if ( file_opened ) mp_print(mp, " not usable: TFM file is bad");
24348 else mp_print(mp, " not usable: TFM file not found");
24349 help3("I wasn't able to read the size data for this font so this")
24350 ("`infont' operation won't produce anything. If the font name")
24351 ("is right, you might ask an expert to make a TFM file");
24353 mp->help_line[0]="is right, try asking an expert to fix the TFM file";
24356 @ @<Read data from |tfm_infile|; if there is no room, say so...@>=
24357 @<Read the \.{TFM} size fields@>;
24358 @<Use the size fields to allocate space in |font_info|@>;
24359 @<Read the \.{TFM} header@>;
24360 @<Read the character data and the width, height, and depth tables and
24363 @ A bad \.{TFM} file can be shorter than it claims to be. The code given here
24364 might try to read past the end of the file if this happens. Changes will be
24365 needed if it causes a system error to refer to |tfm_infile^| or call
24366 |get_tfm_infile| when |eof(tfm_infile)| is true. For example, the definition
24367 @^system dependencies@>
24368 of |tfget| could be changed to
24369 ``|begin get(tfm_infile); if eof(tfm_infile) then goto bad_tfm; end|.''
24371 @d tfget {tfbyte = fgetc(mp->tfm_infile); }
24372 @d read_two(A) { (A)=tfbyte;
24373 if ( (A)>127 ) goto BAD_TFM;
24374 tfget; (A)=(A)*0400+tfbyte;
24376 @d tf_ignore(A) { for (jj=(A);jj>=1;jj--) tfget; }
24378 @<Read the \.{TFM} size fields@>=
24379 tfget; read_two(lf);
24380 tfget; read_two(tfm_lh);
24381 tfget; read_two(bc);
24382 tfget; read_two(ec);
24383 if ( (bc>1+ec)||(ec>255) ) goto BAD_TFM;
24384 tfget; read_two(nw);
24385 tfget; read_two(nh);
24386 tfget; read_two(nd);
24387 whd_size=(ec+1-bc)+nw+nh+nd;
24388 if ( lf<(int)(6+tfm_lh+whd_size) ) goto BAD_TFM;
24391 @ Offsets are added to |char_base[n]| and |width_base[n]| so that is not
24392 necessary to apply the |so| and |qo| macros when looking up the width of a
24393 character in the string pool. In order to ensure nonnegative |char_base|
24394 values when |bc>0|, it may be necessary to reserve a few unused |font_info|
24397 @<Use the size fields to allocate space in |font_info|@>=
24398 if ( mp->next_fmem<bc) mp->next_fmem=bc; /* ensure nonnegative |char_base| */
24399 if (mp->last_fnum==mp->font_max)
24400 mp_reallocate_fonts(mp,(mp->font_max+(mp->font_max>>2)));
24401 while (mp->next_fmem+whd_size>=mp->font_mem_size) {
24402 size_t l = mp->font_mem_size+(mp->font_mem_size>>2);
24403 memory_word *font_info;
24404 font_info = xmalloc ((l+1),sizeof(memory_word));
24405 memset (font_info,0,sizeof(memory_word)*(l+1));
24406 memcpy (font_info,mp->font_info,sizeof(memory_word)*(mp->font_mem_size+1));
24407 xfree(mp->font_info);
24408 mp->font_info = font_info;
24409 mp->font_mem_size = l;
24411 incr(mp->last_fnum);
24415 mp->char_base[n]=mp->next_fmem-bc;
24416 mp->width_base[n]=mp->next_fmem+ec-bc+1;
24417 mp->height_base[n]=mp->width_base[n]+nw;
24418 mp->depth_base[n]=mp->height_base[n]+nh;
24419 mp->next_fmem=mp->next_fmem+whd_size;
24422 @ @<Read the \.{TFM} header@>=
24423 if ( tfm_lh<2 ) goto BAD_TFM;
24425 tfget; read_two(z);
24426 tfget; z=z*0400+tfbyte;
24427 tfget; z=z*0400+tfbyte; /* now |z| is 16 times the design size */
24428 mp->font_dsize[n]=mp_take_fraction(mp, z,267432584);
24429 /* times ${72\over72.27}2^{28}$ to convert from \TeX\ points */
24430 tf_ignore(4*(tfm_lh-2))
24432 @ @<Read the character data and the width, height, and depth tables...@>=
24433 ii=mp->width_base[n];
24434 i=mp->char_base[n]+bc;
24436 tfget; mp->font_info[i].qqqq.b0=qi(tfbyte);
24437 tfget; h_and_d=tfbyte;
24438 mp->font_info[i].qqqq.b1=h_and_d / 16;
24439 mp->font_info[i].qqqq.b2=h_and_d % 16;
24443 while ( i<mp->next_fmem ) {
24444 @<Read a four byte dimension, scale it by the design size, store it in
24445 |font_info[i]|, and increment |i|@>;
24447 if (feof(mp->tfm_infile) ) goto BAD_TFM;
24450 @ The raw dimension read into |d| should have magnitude at most $2^{24}$ when
24451 interpreted as an integer, and this includes a scale factor of $2^{20}$. Thus
24452 we can multiply it by sixteen and think of it as a |fraction| that has been
24453 divided by sixteen. This cancels the extra scale factor contained in
24456 @<Read a four byte dimension, scale it by the design size, store it in...@>=
24459 if ( d>=0200 ) d=d-0400;
24460 tfget; d=d*0400+tfbyte;
24461 tfget; d=d*0400+tfbyte;
24462 tfget; d=d*0400+tfbyte;
24463 mp->font_info[i].sc=mp_take_fraction(mp, d*16,mp->font_dsize[n]);
24467 @ This function does no longer use the file name parser, because |fname| is
24468 a C string already.
24469 @<Open |tfm_infile| for input@>=
24471 mp_ptr_scan_file(mp, fname);
24472 if ( strlen(mp->cur_area)==0 ) { xfree(mp->cur_area); mp->cur_area=xstrdup(MP_font_area);}
24473 if ( strlen(mp->cur_ext)==0 ) { xfree(mp->cur_ext); mp->cur_ext=xstrdup(".tfm"); }
24475 mp->tfm_infile = mp_open_file(mp, mp->name_of_file, "rb",mp_filetype_metrics);
24476 if ( !mp->tfm_infile ) goto BAD_TFM;
24479 @ When we have a font name and we don't know whether it has been loaded yet,
24480 we scan the |font_name| array before calling |read_font_info|.
24482 @<Declare text measuring subroutines@>=
24483 font_number mp_find_font (MP mp, char *f) {
24485 for (n=0;n<=mp->last_fnum;n++) {
24486 if (mp_xstrcmp(f,mp->font_name[n])==0 )
24489 return mp_read_font_info(mp, f);
24492 @ One simple application of |find_font| is the implementation of the |font_size|
24493 operator that gets the design size for a given font name.
24495 @<Find the design size of the font whose name is |cur_exp|@>=
24496 mp_flush_cur_exp(mp, (mp->font_dsize[mp_find_font(mp, str(mp->cur_exp))]+8) / 16)
24498 @ If we discover that the font doesn't have a requested character, we omit it
24499 from the bounding box computation and expect the \ps\ interpreter to drop it.
24500 This routine issues a warning message if the user has asked for it.
24502 @<Declare text measuring subroutines@>=
24503 void mp_lost_warning (MP mp,font_number f, pool_pointer k) {
24504 if ( mp->internal[tracing_lost_chars]>0 ) {
24505 mp_begin_diagnostic(mp);
24506 if ( mp->selector==log_only ) incr(mp->selector);
24507 mp_print_nl(mp, "Missing character: There is no ");
24508 @.Missing character@>
24509 mp_print_str(mp, mp->str_pool[k]);
24510 mp_print(mp, " in font ");
24511 mp_print(mp, mp->font_name[f]); mp_print_char(mp, '!');
24512 mp_end_diagnostic(mp, false);
24516 @ The whole purpose of saving the height, width, and depth information is to be
24517 able to find the bounding box of an item of text in an edge structure. The
24518 |set_text_box| procedure takes a text node and adds this information.
24520 @<Declare text measuring subroutines@>=
24521 void mp_set_text_box (MP mp,pointer p) {
24522 font_number f; /* |font_n(p)| */
24523 ASCII_code bc,ec; /* range of valid characters for font |f| */
24524 pool_pointer k,kk; /* current character and character to stop at */
24525 four_quarters cc; /* the |char_info| for the current character */
24526 scaled h,d; /* dimensions of the current character */
24528 height_val(p)=-el_gordo;
24529 depth_val(p)=-el_gordo;
24533 kk=str_stop(text_p(p));
24534 k=mp->str_start[text_p(p)];
24536 @<Adjust |p|'s bounding box to contain |str_pool[k]|; advance |k|@>;
24538 @<Set the height and depth to zero if the bounding box is empty@>;
24541 @ @<Adjust |p|'s bounding box to contain |str_pool[k]|; advance |k|@>=
24543 if ( (mp->str_pool[k]<bc)||(mp->str_pool[k]>ec) ) {
24544 mp_lost_warning(mp, f,k);
24546 cc=char_info(f)(mp->str_pool[k]);
24547 if ( ! ichar_exists(cc) ) {
24548 mp_lost_warning(mp, f,k);
24550 width_val(p)=width_val(p)+char_width(f)(cc);
24551 h=char_height(f)(cc);
24552 d=char_depth(f)(cc);
24553 if ( h>height_val(p) ) height_val(p)=h;
24554 if ( d>depth_val(p) ) depth_val(p)=d;
24560 @ Let's hope modern compilers do comparisons correctly when the difference would
24563 @<Set the height and depth to zero if the bounding box is empty@>=
24564 if ( height_val(p)<-depth_val(p) ) {
24569 @ The new primitives fontmapfile and fontmapline.
24571 @<Declare action procedures for use by |do_statement|@>=
24572 void mp_do_mapfile (MP mp) ;
24573 void mp_do_mapline (MP mp) ;
24575 @ @c void mp_do_mapfile (MP mp) {
24576 mp_get_x_next(mp); mp_scan_expression(mp);
24577 if ( mp->cur_type!=mp_string_type ) {
24578 @<Complain about improper map operation@>;
24580 mp_map_file(mp,mp->cur_exp);
24583 void mp_do_mapline (MP mp) {
24584 mp_get_x_next(mp); mp_scan_expression(mp);
24585 if ( mp->cur_type!=mp_string_type ) {
24586 @<Complain about improper map operation@>;
24588 mp_map_line(mp,mp->cur_exp);
24592 @ @<Complain about improper map operation@>=
24594 exp_err("Unsuitable expression");
24595 help1("Only known strings can be map files or map lines.");
24596 mp_put_get_error(mp);
24599 @ This is temporary.
24601 @d ps_room(A) mp_ps_room(mp,A)
24603 @<Declare the \ps\ output procedures@>=
24604 void mp_ps_print_cmd (MP mp, char *l, char *s) {
24605 if ( mp->internal[mpprocset]>0 ) { ps_room(strlen(s)); mp_print(mp,s); }
24606 else { ps_room(strlen(l)); mp_print(mp, l); };
24608 void mp_print_cmd (MP mp,char *l, char *s) {
24609 if ( mp->internal[mpprocset]>0 ) mp_print(mp, s);
24610 else mp_print(mp, l);
24613 @ To print |scaled| value to PDF output we need some subroutines to ensure
24616 @d max_integer 0x7FFFFFFF /* $2^{31}-1$ */
24619 scaled one_bp; /* scaled value corresponds to 1bp */
24620 scaled one_hundred_bp; /* scaled value corresponds to 100bp */
24621 scaled one_hundred_inch; /* scaled value corresponds to 100in */
24622 integer ten_pow[10]; /* $10^0..10^9$ */
24623 integer scaled_out; /* amount of |scaled| that was taken out in |divide_scaled| */
24626 mp->one_bp = 65782; /* 65781.76 */
24627 mp->one_hundred_bp = 6578176;
24628 mp->one_hundred_inch = 473628672;
24629 mp->ten_pow[0] = 1;
24630 for (i = 1;i<= 9; i++ ) {
24631 mp->ten_pow[i] = 10*mp->ten_pow[i - 1];
24634 @ The following function divides |s| by |m|. |dd| is number of decimal digits.
24636 @c scaled mp_divide_scaled (MP mp,scaled s, scaled m, integer dd) {
24640 if ( s < 0 ) { sign = -sign; s = -s; }
24641 if ( m < 0 ) { sign = -sign; m = -m; }
24643 mp_confusion(mp, "arithmetic: divided by zero");
24644 else if ( m >= (max_integer / 10) )
24645 mp_confusion(mp, "arithmetic: number too big");
24648 for (i = 1;i<=dd;i++) {
24649 q = 10*q + (10*r) / m;
24652 if ( 2*r >= m ) { incr(q); r = r - m; }
24653 mp->scaled_out = sign*(s - (r / mp->ten_pow[dd]));
24657 @* \[44] Shipping pictures out.
24658 The |ship_out| procedure, to be described below, is given a pointer to
24659 an edge structure. Its mission is to output a file containing the \ps\
24660 description of an edge structure.
24662 @ Each time an edge structure is shipped out we write a new \ps\ output
24663 file named according to the current \&{charcode}.
24664 @:char_code_}{\&{charcode} primitive@>
24666 @<Declare the \ps\ output procedures@>=
24667 void mp_open_output_file (MP mp) ;
24669 @ @c void mp_open_output_file (MP mp) {
24670 integer c; /* \&{charcode} rounded to the nearest integer */
24671 int old_setting; /* previous |selector| setting */
24672 pool_pointer i; /* indexes into |filename_template| */
24673 integer cc; /* a temporary integer for template building */
24674 integer f,g=0; /* field widths */
24675 if ( mp->job_name==NULL ) mp_open_log_file(mp);
24676 c=mp_round_unscaled(mp, mp->internal[char_code]);
24677 if ( mp->filename_template==0 ) {
24678 char *s; /* a file extension derived from |c| */
24682 @<Use |c| to compute the file extension |s|@>;
24683 mp_pack_job_name(mp, s);
24685 while ( ! mp_a_open_out(mp, &mp->ps_file, mp_filetype_postscript) )
24686 mp_prompt_file_name(mp, "file name for output",s);
24687 } else { /* initializations */
24688 str_number s, n; /* a file extension derived from |c| */
24689 old_setting=mp->selector;
24690 mp->selector=new_string;
24692 i = mp->str_start[mp->filename_template];
24693 n = rts(""); /* initialize */
24694 while ( i<str_stop(mp->filename_template) ) {
24695 if ( mp->str_pool[i]=='%' ) {
24698 if ( i<str_stop(mp->filename_template) ) {
24699 if ( mp->str_pool[i]=='j' ) {
24700 mp_print(mp, mp->job_name);
24701 } else if ( mp->str_pool[i]=='d' ) {
24702 cc= mp_round_unscaled(mp, mp->internal[day]);
24703 print_with_leading_zeroes(cc);
24704 } else if ( mp->str_pool[i]=='m' ) {
24705 cc= mp_round_unscaled(mp, mp->internal[month]);
24706 print_with_leading_zeroes(cc);
24707 } else if ( mp->str_pool[i]=='y' ) {
24708 cc= mp_round_unscaled(mp, mp->internal[year]);
24709 print_with_leading_zeroes(cc);
24710 } else if ( mp->str_pool[i]=='H' ) {
24711 cc= mp_round_unscaled(mp, mp->internal[mp_time]) / 60;
24712 print_with_leading_zeroes(cc);
24713 } else if ( mp->str_pool[i]=='M' ) {
24714 cc= mp_round_unscaled(mp, mp->internal[mp_time]) % 60;
24715 print_with_leading_zeroes(cc);
24716 } else if ( mp->str_pool[i]=='c' ) {
24717 if ( c<0 ) mp_print(mp, "ps");
24718 else print_with_leading_zeroes(c);
24719 } else if ( (mp->str_pool[i]>='0') &&
24720 (mp->str_pool[i]<='9') ) {
24722 f = (f*10) + mp->str_pool[i]-'0';
24725 mp_print_str(mp, mp->str_pool[i]);
24729 if ( mp->str_pool[i]=='.' )
24731 n = mp_make_string(mp);
24732 mp_print_str(mp, mp->str_pool[i]);
24736 s = mp_make_string(mp);
24737 mp->selector= old_setting;
24738 if (length(n)==0) {
24742 mp_pack_file_name(mp, str(n),"",str(s));
24743 while ( ! mp_a_open_out(mp, &mp->ps_file, mp_filetype_postscript) )
24744 mp_prompt_file_name(mp, "file name for output",str(s));
24748 @<Store the true output file name if appropriate@>;
24749 @<Begin the progress report for the output of picture~|c|@>;
24752 @ The file extension created here could be up to five characters long in
24753 extreme cases so it may have to be shortened on some systems.
24754 @^system dependencies@>
24756 @<Use |c| to compute the file extension |s|@>=
24759 snprintf(s,7,".%i",(int)c);
24762 @ The user won't want to see all the output file names so we only save the
24763 first and last ones and a count of how many there were. For this purpose
24764 files are ordered primarily by \&{charcode} and secondarily by order of
24766 @:char_code_}{\&{charcode} primitive@>
24768 @<Store the true output file name if appropriate@>=
24769 if ((c<mp->first_output_code)&&(mp->first_output_code>=0)) {
24770 mp->first_output_code=c;
24771 xfree(mp->first_file_name);
24772 mp->first_file_name=xstrdup(mp->name_of_file);
24774 if ( c>=mp->last_output_code ) {
24775 mp->last_output_code=c;
24776 xfree(mp->last_file_name);
24777 mp->last_file_name=xstrdup(mp->name_of_file);
24781 char * first_file_name;
24782 char * last_file_name; /* full file names */
24783 integer first_output_code;integer last_output_code; /* rounded \&{charcode} values */
24784 @:char_code_}{\&{charcode} primitive@>
24785 integer total_shipped; /* total number of |ship_out| operations completed */
24788 mp->first_file_name=xstrdup("");
24789 mp->last_file_name=xstrdup("");
24790 mp->first_output_code=32768;
24791 mp->last_output_code=-32768;
24792 mp->total_shipped=0;
24794 @ @<Dealloc variables@>=
24795 xfree(mp->first_file_name);
24796 xfree(mp->last_file_name);
24798 @ @<Begin the progress report for the output of picture~|c|@>=
24799 if ( (int)mp->term_offset>mp->max_print_line-6 ) mp_print_ln(mp);
24800 else if ( (mp->term_offset>0)||(mp->file_offset>0) ) mp_print_char(mp, ' ');
24801 mp_print_char(mp, '[');
24802 if ( c>=0 ) mp_print_int(mp, c)
24804 @ @<End progress report@>=
24805 mp_print_char(mp, ']');
24807 incr(mp->total_shipped)
24809 @ @<Explain what output files were written@>=
24810 if ( mp->total_shipped>0 ) {
24811 mp_print_nl(mp, "");
24812 mp_print_int(mp, mp->total_shipped);
24813 mp_print(mp, " output file");
24814 if ( mp->total_shipped>1 ) mp_print_char(mp, 's');
24815 mp_print(mp, " written: ");
24816 mp_print(mp, mp->first_file_name);
24817 if ( mp->total_shipped>1 ) {
24818 if ( 31+strlen(mp->first_file_name)+
24819 strlen(mp->last_file_name)> (unsigned)mp->max_print_line)
24821 mp_print(mp, " .. ");
24822 mp_print(mp, mp->last_file_name);
24827 @ The most important output procedure is the one that gives the \ps\ version of
24830 @<Declare the \ps\ output procedures@>=
24831 void mp_ps_path_out (MP mp,pointer h) {
24832 pointer p,q; /* for scanning the path */
24833 scaled d; /* a temporary value */
24834 boolean curved; /* |true| unless the cubic is almost straight */
24836 if ( mp->need_newpath )
24837 mp_print_cmd(mp, "newpath ","n ");
24838 mp->need_newpath=true;
24839 mp_ps_pair_out(mp, x_coord(h),y_coord(h));
24840 mp_print_cmd(mp, "moveto","m");
24843 if ( right_type(p)==endpoint ) {
24844 if ( p==h ) mp_ps_print_cmd(mp, " 0 0 rlineto"," 0 0 r");
24848 @<Start a new line and print the \ps\ commands for the curve from
24852 mp_ps_print_cmd(mp, " closepath"," p");
24856 boolean need_newpath;
24857 /* will |ps_path_out| need to issue a \&{newpath} command next time */
24858 @:newpath_}{\&{newpath} command@>
24860 @ @<Start a new line and print the \ps\ commands for the curve from...@>=
24862 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>;
24865 mp_ps_pair_out(mp, right_x(p),right_y(p));
24866 mp_ps_pair_out(mp, left_x(q),left_y(q));
24867 mp_ps_pair_out(mp, x_coord(q),y_coord(q));
24868 mp_ps_print_cmd(mp, "curveto","c");
24869 } else if ( q!=h ){
24870 mp_ps_pair_out(mp, x_coord(q),y_coord(q));
24871 mp_ps_print_cmd(mp, "lineto","l");
24874 @ Two types of straight lines come up often in \MP\ paths:
24875 cubics with zero initial and final velocity as created by |make_path| or
24876 |make_envelope|, and cubics with control points uniformly spaced on a line
24877 as created by |make_choices|.
24879 @d bend_tolerance 131 /* allow rounding error of $2\cdot10^{-3}$ */
24881 @<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>=
24882 if ( right_x(p)==x_coord(p) )
24883 if ( right_y(p)==y_coord(p) )
24884 if ( left_x(q)==x_coord(q) )
24885 if ( left_y(q)==y_coord(q) ) curved=false;
24886 d=left_x(q)-right_x(p);
24887 if ( abs(right_x(p)-x_coord(p)-d)<=bend_tolerance )
24888 if ( abs(x_coord(q)-left_x(q)-d)<=bend_tolerance )
24889 { d=left_y(q)-right_y(p);
24890 if ( abs(right_y(p)-y_coord(p)-d)<=bend_tolerance )
24891 if ( abs(y_coord(q)-left_y(q)-d)<=bend_tolerance ) curved=false;
24894 @ We need to keep track of several parameters from the \ps\ graphics state.
24896 This allows us to be sure that \ps\ has the correct values when they are
24897 needed without wasting time and space setting them unnecessarily.
24900 @d gs_red mp->mem[mp->gs_state+1].sc
24901 @d gs_green mp->mem[mp->gs_state+2].sc
24902 @d gs_blue mp->mem[mp->gs_state+3].sc
24903 @d gs_black mp->mem[mp->gs_state+4].sc
24904 /* color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command */
24905 @d gs_colormodel mp->mem[mp->gs_state+5].qqqq.b0
24906 /* the current colormodel */
24907 @d gs_ljoin mp->mem[mp->gs_state+5].qqqq.b1
24908 @d gs_lcap mp->mem[mp->gs_state+5].qqqq.b2
24909 /* values from the last \&{setlinejoin} and \&{setlinecap} commands */
24910 @d gs_adj_wx mp->mem[mp->gs_state+5].qqqq.b3
24911 /* what resolution-dependent adjustment applies to the width */
24912 @d gs_miterlim mp->mem[mp->gs_state+6].sc
24913 /* the value from the last \&{setmiterlimit} command */
24914 @d gs_dash_p mp->mem[mp->gs_state+7].hh.lh
24915 /* edge structure for last \&{setdash} command */
24916 @d gs_previous mp->mem[mp->gs_state+7].hh.rh
24917 /* backlink to the previous |gs_state| structure */
24918 @d gs_dash_sc mp->mem[mp->gs_state+8].sc
24919 /* scale factor used with |gs_dash_p| */
24920 @d gs_width mp->mem[mp->gs_state+9].sc
24921 /* width setting or $-1$ if no \&{setlinewidth} command so far */
24929 @ To avoid making undue assumptions about the initial graphics state, these
24930 parameters are given special values that are guaranteed not to match anything
24931 in the edge structure being shipped out. On the other hand, the initial color
24932 should be black so that the translation of an all-black picture will have no
24933 \&{setcolor} commands. (These would be undesirable in a font application.)
24934 Hence we use |c=0| when initializing the graphics state and we use |c<0|
24935 to recover from a situation where we have lost track of the graphics state.
24937 @<Declare the \ps\ output procedures@>=
24938 void mp_unknown_graphics_state (MP mp,scaled c) ;
24940 @ @c void mp_unknown_graphics_state (MP mp,scaled c) {
24941 pointer p; /* to shift graphic states around */
24942 quarterword k; /* a loop index for copying the |gs_state| */
24943 if ( (c==0)||(c==-1) ) {
24944 if ( mp->gs_state==null ) {
24945 mp->gs_state = mp_get_node(mp, gs_node_size);
24948 while ( gs_previous!=null ) {
24950 mp_free_node(mp, mp->gs_state,gs_node_size);
24954 gs_red=c; gs_green=c; gs_blue=c; gs_black=c;
24955 gs_colormodel=uninitialized_model;
24962 } else if ( c==1 ) {
24964 mp->gs_state = mp_get_node(mp, gs_node_size);
24965 for (k=1;k<=gs_node_size-1;k++)
24966 mp->mem[mp->gs_state+k]=mp->mem[p+k];
24968 } else if ( c==2 ) {
24970 mp_free_node(mp, mp->gs_state,gs_node_size);
24975 @ When it is time to output a graphical object, |fix_graphics_state| ensures
24976 that \ps's idea of the graphics state agrees with what is stored in the object.
24978 @<Declare the \ps\ output procedures@>=
24979 @<Declare subroutines needed by |fix_graphics_state|@>;
24980 void mp_fix_graphics_state (MP mp, pointer p) ;
24983 void mp_fix_graphics_state (MP mp, pointer p) {
24984 /* get ready to output graphical object |p| */
24985 pointer hh,pp; /* for list manipulation */
24986 scaled wx,wy,ww; /* dimensions of pen bounding box */
24987 boolean adj_wx; /* whether pixel rounding should be based on |wx| or |wy| */
24988 integer tx,ty; /* temporaries for computing |adj_wx| */
24989 scaled scf; /* a scale factor for the dash pattern */
24990 if ( has_color(p) )
24991 @<Make sure \ps\ will use the right color for object~|p|@>;
24992 if ( (type(p)==fill_code)||(type(p)==stroked_code) )
24993 if ( pen_p(p)!=null )
24994 if ( pen_is_elliptical(pen_p(p)) ) {
24995 @<Generate \ps\ code that sets the stroke width to the
24996 appropriate rounded value@>;
24997 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>;
24998 @<Decide whether the line cap parameter matters and set it if necessary@>;
24999 @<Set the other numeric parameters as needed for object~|p|@>;
25001 if ( mp->ps_offset>0 ) mp_print_ln(mp);
25004 @ @<Decide whether the line cap parameter matters and set it if necessary@>=
25005 if ( type(p)==stroked_code )
25006 if ( (left_type(path_p(p))==endpoint)||(dash_p(p)!=null) )
25007 if ( gs_lcap!=lcap_val(p) ) {
25009 mp_print_char(mp, ' ');
25010 mp_print_char(mp, '0'+lcap_val(p));
25011 mp_print_cmd(mp, " setlinecap"," lc");
25012 gs_lcap=lcap_val(p);
25015 @ @<Set the other numeric parameters as needed for object~|p|@>=
25016 if ( gs_ljoin!=ljoin_val(p) ) {
25018 mp_print_char(mp, ' ');
25019 mp_print_char(mp, '0'+ljoin_val(p)); mp_print_cmd(mp, " setlinejoin"," lj");
25020 gs_ljoin=ljoin_val(p);
25022 if ( gs_miterlim!=miterlim_val(p) ) {
25024 mp_print_char(mp, ' ');
25025 mp_print_scaled(mp, miterlim_val(p)); mp_print_cmd(mp, " setmiterlimit"," ml");
25026 gs_miterlim=miterlim_val(p);
25029 @ @<Make sure \ps\ will use the right color for object~|p|@>=
25031 if ( (color_model(p)==rgb_model)||
25032 ((color_model(p)==uninitialized_model)&&
25033 ((mp->internal[default_color_model] / unity)==rgb_model)) ) {
25034 if ( (gs_colormodel!=rgb_model)||(gs_red!=red_val(p))||
25035 (gs_green!=green_val(p))||(gs_blue!=blue_val(p)) ) {
25037 gs_green=green_val(p);
25038 gs_blue=blue_val(p);
25040 gs_colormodel=rgb_model;
25042 mp_print_char(mp, ' ');
25043 mp_print_scaled(mp, gs_red); mp_print_char(mp, ' ');
25044 mp_print_scaled(mp, gs_green); mp_print_char(mp, ' ');
25045 mp_print_scaled(mp, gs_blue);
25046 mp_print_cmd(mp, " setrgbcolor", " R");
25049 } else if ( (color_model(p)==cmyk_model)||
25050 ((color_model(p)==uninitialized_model)&&
25051 ((mp->internal[default_color_model] / unity)==cmyk_model)) ) {
25052 if ( (gs_red!=cyan_val(p))||(gs_green!=magenta_val(p))||
25053 (gs_blue!=yellow_val(p))||(gs_black!=black_val(p))||
25054 (gs_colormodel!=cmyk_model) ) {
25055 if ( color_model(p)==uninitialized_model ) {
25061 gs_red=cyan_val(p);
25062 gs_green=magenta_val(p);
25063 gs_blue=yellow_val(p);
25064 gs_black=black_val(p);
25066 gs_colormodel=cmyk_model;
25068 mp_print_char(mp, ' ');
25069 mp_print_scaled(mp, gs_red); mp_print_char(mp, ' ');
25070 mp_print_scaled(mp, gs_green); mp_print_char(mp, ' ');
25071 mp_print_scaled(mp, gs_blue); mp_print_char(mp, ' ');
25072 mp_print_scaled(mp, gs_black);
25073 mp_print_cmd(mp, " setcmykcolor"," C");
25076 } else if ( (color_model(p)==grey_model)||
25077 ((color_model(p)==uninitialized_model)&&
25078 ((mp->internal[default_color_model] / unity)==grey_model)) ) {
25079 if ( (gs_red!=grey_val(p))||(gs_colormodel!=grey_model) ) {
25080 gs_red = grey_val(p);
25084 gs_colormodel=grey_model;
25086 mp_print_char(mp, ' ');
25087 mp_print_scaled(mp, gs_red);
25088 mp_print_cmd(mp, " setgray"," G");
25092 if ( color_model(p)==no_model )
25093 gs_colormodel=no_model;
25096 @ In order to get consistent widths for horizontal and vertical pen strokes, we
25097 want \ps\ to use an integer number of pixels for the \&{setwidth} parameter.
25098 @:setwidth}{\&{setwidth}command@>
25099 We set |gs_width| to the ideal horizontal or vertical stroke width and then
25100 generate \ps\ code that computes the rounded value. For non-circular pens, the
25101 pen shape will be rescaled so that horizontal or vertical parts of the stroke
25102 have the computed width.
25104 Rounding the width to whole pixels is not likely to improve the appearance of
25105 diagonal or curved strokes, but we do it anyway for consistency. The
25106 \&{truncate} command generated here tends to make all the strokes a little
25107 @:truncate}{\&{truncate} command@>
25108 thinner, but this is appropriate for \ps's scan-conversion rules. Even with
25109 truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$.
25110 It would be better to have $\lceil w\rceil$ but that is ridiculously expensive
25113 @<Generate \ps\ code that sets the stroke width...@>=
25114 @<Set |wx| and |wy| to the width and height of the bounding box for
25116 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more
25117 important and set |adj_wx| and |ww| accordingly@>;
25118 if ( (ww!=gs_width) || (adj_wx!=gs_adj_wx) ) {
25121 mp_print_char(mp, ' '); mp_print_scaled(mp, ww);
25122 mp_ps_print_cmd(mp,
25123 " 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw");
25125 if ( mp->internal[mpprocset]>0 ) {
25127 mp_print_char(mp, ' ');
25128 mp_print_scaled(mp, ww);
25129 mp_ps_print(mp, " vlw");
25132 mp_print(mp, " 0 "); mp_print_scaled(mp, ww);
25133 mp_ps_print(mp, " dtransform truncate idtransform setlinewidth pop");
25137 gs_adj_wx = adj_wx;
25140 @ @<Set |wx| and |wy| to the width and height of the bounding box for...@>=
25142 if ( (right_x(pp)==x_coord(pp)) && (left_y(pp)==y_coord(pp)) ) {
25143 wx = abs(left_x(pp) - x_coord(pp));
25144 wy = abs(right_y(pp) - y_coord(pp));
25146 wx = mp_pyth_add(mp, left_x(pp)-x_coord(pp), right_x(pp)-x_coord(pp));
25147 wy = mp_pyth_add(mp, left_y(pp)-y_coord(pp), right_y(pp)-y_coord(pp));
25150 @ The path is considered ``essentially horizontal'' if its range of
25151 $y$~coordinates is less than the $y$~range |wy| for the pen. ``Essentially
25152 vertical'' paths are detected similarly. This code ensures that no component
25153 of the pen transformation is more that |aspect_bound*(ww+1)|.
25155 @d aspect_bound 10 /* ``less important'' of |wx|, |wy| cannot exceed the other by
25156 more than this factor */
25158 @<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>=
25160 if ( mp_coord_rangeOK(mp, path_p(p), y_loc(0), wy) ) tx=aspect_bound;
25161 else if ( mp_coord_rangeOK(mp, path_p(p), x_loc(0), wx) ) ty=aspect_bound;
25162 if ( wy / ty>=wx / tx ) { ww=wy; adj_wx=false; }
25163 else { ww=wx; adj_wx=true; }
25165 @ This routine quickly tests if path |h| is ``essentially horizontal'' or
25166 ``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is
25167 allowable range for $x$ or~$y$. We do not need and cannot afford a full
25168 bounding-box computation.
25170 @<Declare subroutines needed by |fix_graphics_state|@>=
25171 boolean mp_coord_rangeOK (MP mp,pointer h,
25172 small_number zoff, scaled dz) {
25173 pointer p; /* for scanning the path form |h| */
25174 scaled zlo,zhi; /* coordinate range so far */
25175 scaled z; /* coordinate currently being tested */
25176 zlo=knot_coord(h+zoff);
25179 while ( right_type(p)!=endpoint ) {
25180 z=right_coord(p+zoff);
25181 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
25183 z=left_coord(p+zoff);
25184 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
25185 z=knot_coord(p+zoff);
25186 @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>;
25192 @ @<Make |zlo..zhi| include |z| and |return false| if |zhi-zlo>dz|@>=
25193 if ( z<zlo ) zlo=z;
25194 else if ( z>zhi ) zhi=z;
25195 if ( zhi-zlo>dz ) return false
25197 @ Filling with an elliptical pen is implemented via a combination of \&{stroke}
25198 and \&{fill} commands and a nontrivial dash pattern would interfere with this.
25199 @:stroke}{\&{stroke} command@>
25200 @:fill}{\&{fill} command@>
25201 Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as
25204 @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>=
25205 if ( type(p)==fill_code ) {
25209 scf=mp_get_pen_scale(mp, pen_p(p));
25211 if ( gs_width==0 ) scf=dash_scale(p); else hh=null;
25213 scf=mp_make_scaled(mp, gs_width,scf);
25214 scf=mp_take_scaled(mp, scf,dash_scale(p));
25218 if ( gs_dash_p!=null ) {
25219 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
25222 } else if ( (gs_dash_sc!=scf) || ! mp_same_dashes(mp, gs_dash_p,hh) ) {
25223 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>;
25226 @ Translating a dash list into \ps\ is very similar to printing it symbolically
25227 in |print_edges|. A dash pattern with |dash_y(hh)=0| has length zero and is
25228 ignored. The same fate applies in the bizarre case of a dash pattern that
25229 cannot be printed without overflow.
25231 @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>=
25234 if ( (dash_y(hh)==0) || (abs(dash_y(hh)) / unity >= el_gordo / scf)){
25235 mp_ps_print_cmd(mp, " [] 0 setdash"," rd");
25238 start_x(null_dash)=start_x(pp)+dash_y(hh);
25240 mp_print(mp, " [");
25241 while ( pp!=null_dash ) {
25242 mp_ps_pair_out(mp, mp_take_scaled(mp, stop_x(pp)-start_x(pp),scf),
25243 mp_take_scaled(mp, start_x(link(pp))-stop_x(pp),scf));
25247 mp_print(mp, "] ");
25248 mp_print_scaled(mp, mp_take_scaled(mp, mp_dash_offset(mp, hh),scf));
25249 mp_print_cmd(mp, " setdash"," sd");
25253 @ @<Declare subroutines needed by |fix_graphics_state|@>=
25254 boolean mp_same_dashes (MP mp,pointer h, pointer hh) ;
25257 boolean mp_same_dashes (MP mp,pointer h, pointer hh) {
25258 /* do |h| and |hh| represent the same dash pattern? */
25259 pointer p,pp; /* dash nodes being compared */
25260 if ( h==hh ) return true;
25261 else if ( (h<=diov)||(hh<=diov) ) return false;
25262 else if ( dash_y(h)!=dash_y(hh) ) return false;
25263 else { @<Compare |dash_list(h)| and |dash_list(hh)|@>; }
25264 return false; /* can't happen */
25267 @ @<Compare |dash_list(h)| and |dash_list(hh)|@>=
25270 while ( (p!=null_dash)&&(pp!=null_dash) ) {
25271 if ( (start_x(p)!=start_x(pp))||(stop_x(p)!=stop_x(pp)) ) {
25281 @ When stroking a path with an elliptical pen, it is necessary to transform
25282 the coordinate system so that a unit circular pen will have the desired shape.
25283 To keep this transformation local, we enclose it in a
25284 $$\&{gsave}\ldots\&{grestore}$$
25285 block. Any translation component must be applied to the path being stroked
25286 while the rest of the transformation must apply only to the pen.
25287 If |fill_also=true|, the path is to be filled as well as stroked so we must
25288 insert commands to do this after giving the path.
25290 @<Declare the \ps\ output procedures@>=
25291 void mp_stroke_ellipse (MP mp,pointer h, boolean fill_also) ;
25294 @c void mp_stroke_ellipse (MP mp,pointer h, boolean fill_also) {
25295 /* generate an elliptical pen stroke from object |h| */
25296 scaled txx,txy,tyx,tyy; /* transformation parameters */
25297 pointer p; /* the pen to stroke with */
25298 scaled d1,det; /* for tweaking transformation parameters */
25299 integer s; /* also for tweaking transformation paramters */
25300 boolean transformed; /* keeps track of whether gsave/grestore are needed */
25302 @<Use |pen_p(h)| to set the transformation parameters and give the initial
25304 @<Tweak the transformation parameters so the transformation is nonsingular@>;
25305 mp_ps_path_out(mp, path_p(h));
25306 if ( mp->internal[mpprocset]==0 ) {
25307 if ( fill_also ) mp_print_nl(mp, "gsave fill grestore");
25308 @<Issue \ps\ commands to transform the coordinate system@>;
25309 mp_ps_print(mp, " stroke");
25310 if ( transformed ) mp_ps_print(mp, " grestore");
25312 if ( fill_also ) mp_print_nl(mp, "B"); else mp_print_ln(mp);
25313 if ( (txy!=0)||(tyx!=0) ) {
25314 mp_print(mp, " [");
25315 mp_ps_pair_out(mp, txx,tyx);
25316 mp_ps_pair_out(mp, txy,tyy);
25317 mp_ps_print(mp, "0 0] t");
25318 } else if ((txx!=unity)||(tyy!=unity) ) {
25319 mp_ps_pair_out(mp,txx,tyy);
25320 mp_print(mp, " s");
25322 mp_ps_print(mp, " S");
25323 if ( transformed ) mp_ps_print(mp, " Q");
25328 @ @<Use |pen_p(h)| to set the transformation parameters and give the...@>=
25334 if ( (x_coord(p)!=0)||(y_coord(p)!=0) ) {
25335 mp_print_nl(mp, ""); mp_print_cmd(mp, "gsave ","q ");
25336 mp_ps_pair_out(mp, x_coord(p),y_coord(p));
25337 mp_ps_print(mp, "translate ");
25344 mp_print_nl(mp, "");
25346 @<Adjust the transformation to account for |gs_width| and output the
25347 initial \&{gsave} if |transformed| should be |true|@>
25349 @ @<Adjust the transformation to account for |gs_width| and output the...@>=
25350 if ( gs_width!=unity ) {
25351 if ( gs_width==0 ) {
25352 txx=unity; tyy=unity;
25354 txx=mp_make_scaled(mp, txx,gs_width);
25355 txy=mp_make_scaled(mp, txy,gs_width);
25356 tyx=mp_make_scaled(mp, tyx,gs_width);
25357 tyy=mp_make_scaled(mp, tyy,gs_width);
25360 if ( (txy!=0)||(tyx!=0)||(txx!=unity)||(tyy!=unity) ) {
25361 if ( (! transformed) ){
25362 mp_ps_print_cmd(mp, "gsave ","q ");
25367 @ @<Issue \ps\ commands to transform the coordinate system@>=
25368 if ( (txy!=0)||(tyx!=0) ){
25370 mp_print_char(mp, '[');
25371 mp_ps_pair_out(mp, txx,tyx);
25372 mp_ps_pair_out(mp, txy,tyy);
25373 mp_ps_print(mp, "0 0] concat");
25374 } else if ( (txx!=unity)||(tyy!=unity) ){
25376 mp_ps_pair_out(mp, txx,tyy);
25377 mp_print(mp, "scale");
25380 @ The \ps\ interpreter will probably abort if it encounters a singular
25381 transformation matrix. The determinant must be large enough to ensure that
25382 the printed representation will be nonsingular. Since the printed
25383 representation is always within $2^{-17}$ of the internal |scaled| value, the
25384 total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on
25385 the magnitudes of |txx/65536|, |txy/65536|, etc.
25387 The |aspect_bound*(gs_width+1)| bound on the components of the pen
25388 transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|.
25390 @<Tweak the transformation parameters so the transformation is nonsingular@>=
25391 det=mp_take_scaled(mp, txx,tyy) - mp_take_scaled(mp, txy,tyx);
25392 d1=4*aspect_bound+1;
25393 if ( abs(det)<d1 ) {
25394 if ( det>=0 ) { d1=d1-det; s=1; }
25395 else { d1=-d1-det; s=-1; };
25397 if ( abs(txx)+abs(tyy)>=abs(txy)+abs(tyy) ) {
25398 if ( abs(txx)>abs(tyy) ) tyy=tyy+(d1+s*abs(txx)) / txx;
25399 else txx=txx+(d1+s*abs(tyy)) / tyy;
25401 if ( abs(txy)>abs(tyx) ) tyx=tyx+(d1+s*abs(txy)) / txy;
25402 else txy=txy+(d1+s*abs(tyx)) / tyx;
25406 @ Here is a simple routine that just fills a cycle.
25408 @<Declare the \ps\ output procedures@>=
25409 void mp_ps_fill_out (MP mp,pointer p) ;
25412 void mp_ps_fill_out (MP mp,pointer p) { /* fill cyclic path~|p| */
25413 mp_ps_path_out(mp, p);
25414 mp_ps_print_cmd(mp, " fill"," F");
25418 @ Given a cyclic path~|p| and a graphical object~|h|, the |do_outer_envelope|
25419 procedure fills the cycle generated by |make_envelope|. It need not do
25420 anything unless some region has positive winding number with respect to~|p|,
25421 but it does not seem worthwhile to for test this.
25423 @<Declare the \ps\ output procedures@>=
25424 void mp_do_outer_envelope (MP mp,pointer p, pointer h) ;
25427 void mp_do_outer_envelope (MP mp,pointer p, pointer h) {
25428 p=mp_make_envelope(mp, p, pen_p(h), ljoin_val(h), 0, miterlim_val(h));
25429 mp_ps_fill_out(mp, p);
25430 mp_toss_knot_list(mp, p);
25433 @ A text node may specify an arbitrary transformation but the usual case
25434 involves only shifting, scaling, and occasionally rotation. The purpose
25435 of |choose_scale| is to select a scale factor so that the remaining
25436 transformation is as ``nice'' as possible. The definition of ``nice''
25437 is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
25438 nice because they work out well for bitmap fonts. The code here selects
25439 a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
25440 non-shifting part of the transformation matrix. It is careful to avoid
25441 additions that might cause undetected overflow.
25443 @<Declare the \ps\ output procedures@>=
25444 scaled mp_choose_scale (MP mp,pointer p) ;
25446 @ @c scaled mp_choose_scale (MP mp,pointer p) {
25447 /* |p| should point to a text node */
25448 scaled a,b,c,d,ad,bc; /* temporary values */
25453 if ( (a<0) ) negate(a);
25454 if ( (b<0) ) negate(b);
25455 if ( (c<0) ) negate(c);
25456 if ( (d<0) ) negate(d);
25459 return mp_pyth_add(mp, mp_pyth_add(mp, d+ad,ad), mp_pyth_add(mp, c+bc,bc));
25462 @ @<Declare the \ps\ output procedures@>=
25463 void mp_mark_string_chars (MP mp,font_number f, str_number s) ;
25466 void mp_mark_string_chars (MP mp,font_number f, str_number s) {
25467 integer b; /* |char_base[f]| */
25468 ASCII_code bc,ec; /* only characters between these bounds are marked */
25469 pool_pointer k; /* an index into string |s| */
25470 b=mp->char_base[f];
25474 while ( k>mp->str_start[s] ){
25476 if ( (mp->str_pool[k]>=bc)&&(mp->str_pool[k]<=ec) )
25477 mp->font_info[b+mp->str_pool[k]].qqqq.b3=used;
25481 @ There may be many sizes of one font and we need to keep track of the
25482 characters used for each size. This is done by keeping a linked list of
25483 sizes for each font with a counter in each text node giving the appropriate
25484 position in the size list for its font.
25486 @d sc_factor(A) mp->mem[(A)+1].sc /* the scale factor stored in a font size node */
25487 @d font_size_size 2 /* size of a font size node */
25490 boolean mp_has_font_size(MP mp, font_number f );
25493 boolean mp_has_font_size(MP mp, font_number f ) {
25494 return (mp->font_sizes[f]!=null);
25498 @ The overflow here is caused by the fact the returned value
25499 has to fit in a |name_type|, which is a quarterword.
25501 @d fscale_tolerance 65 /* that's $.001\times2^{16}$ */
25503 @<Declare the \ps\ output procedures@>=
25504 quarterword mp_size_index (MP mp, font_number f, scaled s) {
25505 pointer p,q; /* the previous and current font size nodes */
25506 quarterword i; /* the size index for |q| */
25507 q=mp->font_sizes[f];
25509 while ( q!=null ) {
25510 if ( abs(s-sc_factor(q))<=fscale_tolerance )
25513 { p=q; q=link(q); incr(i); };
25514 if ( i==max_quarterword )
25515 mp_overflow(mp, "sizes per font",max_quarterword);
25516 @:MetaPost capacity exceeded sizes per font}{\quad sizes per font@>
25518 q=mp_get_node(mp, font_size_size);
25520 if ( i==0 ) mp->font_sizes[f]=q; else link(p)=q;
25524 @ @<Declare the \ps\ output procedures@>=
25525 scaled mp_indexed_size (MP mp,font_number f, quarterword j) {
25526 pointer p; /* a font size node */
25527 quarterword i; /* the size index for |p| */
25528 p=mp->font_sizes[f];
25530 if ( p==null ) mp_confusion(mp, "size");
25532 incr(i); p=link(p);
25533 if ( p==null ) mp_confusion(mp, "size");
25535 return sc_factor(p);
25538 @ @<Declare the \ps\ output procedures@>=
25539 void mp_clear_sizes (MP mp) ;
25541 @ @c void mp_clear_sizes (MP mp) {
25542 font_number f; /* the font whose size list is being cleared */
25543 pointer p; /* current font size nodes */
25544 for (f=null_font+1;f<=mp->last_fnum;f++) {
25545 while ( mp->font_sizes[f]!=null ) {
25546 p=mp->font_sizes[f];
25547 mp->font_sizes[f]=link(p);
25548 mp_free_node(mp, p,font_size_size);
25553 @ The \&{special} command saves up lines of text to be printed during the next
25554 |ship_out| operation. The saved items are stored as a list of capsule tokens.
25557 pointer last_pending; /* the last token in a list of pending specials */
25560 mp->last_pending=spec_head;
25562 @ @<Cases of |do_statement|...@>=
25563 case special_command:
25564 if ( mp->cur_mod==0 ) mp_do_special(mp); else
25565 if ( mp->cur_mod==1 ) mp_do_mapfile(mp); else
25569 @ @<Declare action procedures for use by |do_statement|@>=
25570 void mp_do_special (MP mp) ;
25572 @ @c void mp_do_special (MP mp) {
25573 mp_get_x_next(mp); mp_scan_expression(mp);
25574 if ( mp->cur_type!=mp_string_type ) {
25575 @<Complain about improper special operation@>;
25577 link(mp->last_pending)=mp_stash_cur_exp(mp);
25578 mp->last_pending=link(mp->last_pending);
25579 link(mp->last_pending)=null;
25583 @ @<Complain about improper special operation@>=
25585 exp_err("Unsuitable expression");
25586 help1("Only known strings are allowed for output as specials.");
25587 mp_put_get_error(mp);
25590 @ @<Print any pending specials@>=
25592 while ( t!=null ) {
25593 mp_print_str(mp, value(t));
25597 mp_flush_token_list(mp, link(spec_head));
25598 link(spec_head)=null;
25599 mp->last_pending=spec_head
25601 @ We are now ready for the main output procedure. Note that the |selector|
25602 setting is saved in a global variable so that |begin_diagnostic| can access it.
25604 @<Declare the \ps\ output procedures@>=
25605 void mp_ship_out (MP mp, pointer h) ;
25608 void mp_ship_out (MP mp, pointer h) { /* output edge structure |h| */
25609 pointer p; /* the current graphical object */
25610 pointer q; /* something that |p| points to */
25611 integer t; /* a temporary value */
25612 font_number f; /* fonts used in a text node or as loop counters */
25614 scaled ds,scf; /* design size and scale factor for a text node */
25615 boolean transformed; /* is the coordinate system being transformed? */
25616 mp_open_output_file(mp);
25617 mp->non_ps_setting=mp->selector; mp->selector=ps_file_only;
25618 if ( (mp->internal[prologues]==two)||(mp->internal[prologues]==three) ) {
25619 @<Print improved initial comment and bounding box for edge structure~|h|@>;
25620 @<Scan all the text nodes and mark the used characters@>;
25621 mp_load_encodings(mp,mp->last_fnum);
25622 @<Update encoding names@>;
25623 @<Print the improved prologue and setup@>;
25625 @<Print the initial comment and give the bounding box for edge structure~|h|@>;
25626 if ( (mp->internal[prologues]>0) && (mp->last_ps_fnum<mp->last_fnum) )
25627 mp_read_psname_table(mp);
25628 mp_print_prologue(mp, (mp->internal[prologues]>>16), (mp->internal[mpprocset]>>16), ldf);
25630 @<Print any pending specials@>;
25631 mp_unknown_graphics_state(mp, 0);
25632 mp->need_newpath=true;
25633 p=link(dummy_loc(h));
25634 while ( p!=null ) {
25635 if ( has_color(p) ) {
25636 if ( (pre_script(p))!=null ) {
25637 mp_print_nl (mp, str(pre_script(p))); mp_print_ln(mp);
25640 mp_fix_graphics_state(mp, p);
25642 @<Cases for translating graphical object~|p| into \ps@>;
25643 case mp_start_bounds_code:
25644 case mp_stop_bounds_code:
25646 } /* all cases are enumerated */
25649 mp_print_cmd(mp, "showpage","P"); mp_print_ln(mp);
25650 mp_print(mp, "%%EOF"); mp_print_ln(mp);
25651 fclose(mp->ps_file);
25652 mp->selector=mp->non_ps_setting;
25653 if ( mp->internal[prologues]<=0 ) mp_clear_sizes(mp);
25654 @<End progress report@>;
25655 if ( mp->internal[tracing_output]>0 )
25656 mp_print_edges(mp, h," (just shipped out)",true);
25660 void mp_apply_mark_string_chars(MP mp, pointer h, int next_size);
25663 void mp_apply_mark_string_chars(MP mp, pointer h, int next_size) {
25665 p=link(dummy_loc(h));
25666 while ( p!=null ) {
25667 if ( type(p)==text_code )
25668 if ( font_n(p)!=null_font )
25669 if ( name_type(p)==next_size )
25670 mp_mark_string_chars(mp, font_n(p),text_p(p));
25676 @<Print the improved prologue and setup@>=
25678 mp_print_improved_prologue(mp, (mp->internal[prologues]>>16),(mp->internal[mpprocset]>>16),
25679 (mp->internal[gtroffmode]>>16), null, h);
25683 @<Print improved initial comment and bounding box for edge...@>=
25684 mp_print(mp, "%!PS-Adobe-3.0 EPSF-3.0");
25685 mp_print_nl(mp, "%%BoundingBox: ");
25686 mp_set_bbox(mp, h,true);
25687 if ( minx_val(h)>maxx_val(h) ) {
25688 mp_print(mp, "0 0 0 0");
25690 mp_ps_pair_out(mp, mp_floor_scaled(mp, minx_val(h)),mp_floor_scaled(mp, miny_val(h)));
25691 mp_ps_pair_out(mp, -mp_floor_scaled(mp, -maxx_val(h)),-mp_floor_scaled(mp, -maxy_val(h)));
25693 mp_print_nl(mp, "%%HiResBoundingBox: ");
25694 if ( minx_val(h)>maxx_val(h) ) {
25695 mp_print(mp, "0 0 0 0");
25697 mp_ps_pair_out(mp, minx_val(h),miny_val(h));
25698 mp_ps_pair_out(mp, maxx_val(h),maxy_val(h));
25700 mp_print_nl(mp, "%%Creator: MetaPost ");
25701 mp_print(mp, metapost_version);
25702 mp_print_nl(mp, "%%CreationDate: ");
25703 mp_print_int(mp, mp_round_unscaled(mp, mp->internal[year])); mp_print_char(mp, '.');
25704 mp_print_dd(mp, mp_round_unscaled(mp, mp->internal[month])); mp_print_char(mp, '.');
25705 mp_print_dd(mp, mp_round_unscaled(mp, mp->internal[day])); mp_print_char(mp, ':');
25706 t=mp_round_unscaled(mp, mp->internal[mp_time]);
25707 mp_print_dd(mp, t / 60); mp_print_dd(mp, t % 60);
25708 mp_print_nl(mp, "%%Pages: 1");
25712 @ @<Scan all the text nodes and mark the used ...@>=
25713 for (f=null_font+1;f<=mp->last_fnum;f++) {
25714 if ( mp->font_sizes[f]!=null ) {
25715 mp_unmark_font(mp, f);
25716 mp->font_sizes[f]=null;
25718 if ( mp->font_enc_name[f]!=NULL )
25719 xfree(mp->font_enc_name[f]);
25720 mp->font_enc_name[f] = NULL;
25722 for (f=null_font+1;f<=mp->last_fnum;f++) {
25723 p=link(dummy_loc(h));
25724 while ( p!=null ) {
25725 if ( type(p)==text_code ) {
25726 if ( font_n(p)!=null_font ) {
25727 mp->font_sizes[font_n(p)] = diov;
25728 mp_mark_string_chars(mp, font_n(p),text_p(p));
25729 if ( mp_has_fm_entry(mp,font_n(p),NULL) )
25730 mp->font_ps_name[font_n(p)] = mp_fm_font_name(mp,font_n(p));
25737 @ @<Update encoding names@>=
25738 for (f=null_font+1;f<=mp->last_fnum;f++) {
25739 p=link(dummy_loc(h));
25740 while ( p!=null ) {
25741 if ( type(p)==text_code )
25742 if ( font_n(p)!=null_font )
25743 if ( mp_has_fm_entry(mp,font_n(p),NULL) )
25744 if ( mp->font_enc_name[font_n(p)]==NULL )
25745 mp->font_enc_name[font_n(p)] = mp_fm_encoding_name(mp,font_n(p));
25750 @ These special comments described in the {\sl PostScript Language Reference
25751 Manual}, 2nd.~edition are understood by some \ps-reading programs.
25752 We can't normally output ``conforming'' \ps\ because
25753 the structuring conventions don't allow us to say ``Please make sure the
25754 following characters are downloaded and define the \.{fshow} macro to access
25757 The exact bounding box is written out if |prologues<0|, although this
25758 is not standard \ps, since it allows \TeX\ to calculate the box dimensions
25759 accurately. (Overfull boxes are avoided if an illustration is made to
25760 match a given \.{\char`\\hsize}.)
25762 @<Print the initial comment and give the bounding box for edge...@>=
25763 mp_print(mp, "%!PS");
25764 if ( mp->internal[prologues]>0 ) mp_print(mp, "-Adobe-3.0 EPSF-3.0");
25765 mp_print_nl(mp, "%%BoundingBox: ");
25766 mp_set_bbox(mp, h,true);
25767 if ( minx_val(h)>maxx_val(h) ) mp_print(mp, "0 0 0 0");
25768 else if ( mp->internal[prologues]<0 ) {
25769 mp_ps_pair_out(mp, minx_val(h),miny_val(h));
25770 mp_ps_pair_out(mp, maxx_val(h),maxy_val(h));
25772 mp_ps_pair_out(mp, mp_floor_scaled(mp, minx_val(h)),mp_floor_scaled(mp, miny_val(h)));
25773 mp_ps_pair_out(mp, -mp_floor_scaled(mp, -maxx_val(h)),-mp_floor_scaled(mp, -maxy_val(h)));
25775 mp_print_nl(mp, "%%HiResBoundingBox: ");
25776 if ( minx_val(h)>maxx_val(h) ) mp_print(mp, "0 0 0 0");
25778 mp_ps_pair_out(mp, minx_val(h),miny_val(h));
25779 mp_ps_pair_out(mp, maxx_val(h),maxy_val(h));
25781 mp_print_nl(mp, "%%Creator: MetaPost ");
25782 mp_print(mp, metapost_version);
25783 mp_print_nl(mp, "%%CreationDate: ");
25784 mp_print_int(mp, mp_round_unscaled(mp, mp->internal[year])); mp_print_char(mp, '.');
25785 mp_print_dd(mp, mp_round_unscaled(mp, mp->internal[month])); mp_print_char(mp, '.');
25786 mp_print_dd(mp, mp_round_unscaled(mp, mp->internal[day])); mp_print_char(mp, ':');
25787 t=mp_round_unscaled(mp, mp->internal[mp_time]);
25788 mp_print_dd(mp, t / 60); mp_print_dd(mp, t % 60);
25789 mp_print_nl(mp, "%%Pages: 1");
25790 @<List all the fonts and magnifications for edge structure~|h|@>;
25793 @ @<List all the fonts and magnifications for edge structure~|h|@>=
25794 @<Scan all the text nodes and set the |font_sizes| lists;
25795 if |internal[prologues]<=0| list the sizes selected by |choose_scale|,
25796 apply |unmark_font| to each font encountered, and call |mark_string|
25797 whenever the size index is zero@>;
25798 ldf = mp_print_font_comments (mp, (mp->internal[prologues]>>16), null, h)
25800 @ @<Scan all the text nodes and set the |font_sizes| lists;...@>=
25801 for (f=null_font+1;f<=mp->last_fnum;f++)
25802 mp->font_sizes[f]=null;
25803 p=link(dummy_loc(h));
25804 while ( p!=null ) {
25805 if ( type(p)==text_code ) {
25806 if ( font_n(p)!=null_font ) {
25808 if ( mp->internal[prologues]>0 ) {
25809 mp->font_sizes[f]=diov;
25811 if ( mp->font_sizes[f]==null ) mp_unmark_font(mp, f);
25812 name_type(p)=mp_size_index(mp, f,mp_choose_scale(mp, p));
25813 if ( name_type(p)==0 )
25814 mp_mark_string_chars(mp, f,text_p(p));
25821 @ @<Cases for translating graphical object~|p| into \ps@>=
25822 case mp_start_clip_code:
25823 mp_print_nl(mp, ""); mp_print_cmd(mp, "gsave ","q ");
25824 mp_ps_path_out(mp, path_p(p));
25825 mp_ps_print_cmd(mp, " clip"," W");
25827 if ( mp->internal[restore_clip_color]>0 )
25828 mp_unknown_graphics_state(mp, 1);
25830 case mp_stop_clip_code:
25831 mp_print_nl(mp, ""); mp_print_cmd(mp, "grestore","Q");
25833 if ( mp->internal[restore_clip_color]>0 )
25834 mp_unknown_graphics_state(mp, 2);
25836 mp_unknown_graphics_state(mp, -1);
25839 @ @<Cases for translating graphical object~|p| into \ps@>=
25841 if ( pen_p(p)==null ) mp_ps_fill_out(mp, path_p(p));
25842 else if ( pen_is_elliptical(pen_p(p)) ) mp_stroke_ellipse(mp, p,true);
25844 mp_do_outer_envelope(mp, mp_copy_path(mp, path_p(p)), p);
25845 mp_do_outer_envelope(mp, mp_htap_ypoc(mp, path_p(p)), p);
25847 if ( (post_script(p))!=null ) {
25848 mp_print_nl (mp, str(post_script(p))); mp_print_ln(mp);
25852 if ( pen_is_elliptical(pen_p(p)) ) mp_stroke_ellipse(mp, p,false);
25854 q=mp_copy_path(mp, path_p(p));
25856 @<Break the cycle and set |t:=1| if path |q| is cyclic@>;
25857 q=mp_make_envelope(mp, q,pen_p(p),ljoin_val(p),t,miterlim_val(p));
25858 mp_ps_fill_out(mp, q);
25859 mp_toss_knot_list(mp, q);
25861 if ( (post_script(p))!=null ) {
25862 mp_print_nl (mp, str(post_script(p))); mp_print_ln(mp);
25866 @ The envelope of a cyclic path~|q| could be computed by calling
25867 |make_envelope| once for |q| and once for its reversal. We don't do this
25868 because it would fail color regions that are covered by the pen regardless
25869 of where it is placed on~|q|.
25871 @<Break the cycle and set |t:=1| if path |q| is cyclic@>=
25872 if ( left_type(q)!=endpoint ) {
25873 left_type(mp_insert_knot(mp, q,x_coord(q),y_coord(q)))=endpoint;
25874 right_type(q)=endpoint;
25879 @ @<Cases for translating graphical object~|p| into \ps@>=
25881 if ( (font_n(p)!=null_font) && (length(text_p(p))>0) ) {
25882 if ( mp->internal[prologues]>0 )
25883 scf=mp_choose_scale(mp, p);
25885 scf=mp_indexed_size(mp, font_n(p), name_type(p));
25886 @<Shift or transform as necessary before outputting text node~|p| at scale
25887 factor~|scf|; set |transformed:=true| if the original transformation must
25889 mp_ps_string_out(mp, str(text_p(p)));
25890 mp_ps_name_out(mp, mp->font_name[font_n(p)],false);
25891 @<Print the size information and \ps\ commands for text node~|p|@>;
25894 if ( (post_script(p))!=null ) {
25895 mp_print_nl (mp, str(post_script(p))); mp_print_ln(mp);
25899 @ @<Print the size information and \ps\ commands for text node~|p|@>=
25901 mp_print_char(mp, ' ');
25902 ds=(mp->font_dsize[font_n(p)]+8) / 16;
25903 mp_print_scaled(mp, mp_take_scaled(mp, ds,scf));
25904 mp_print(mp, " fshow");
25906 mp_ps_print_cmd(mp, " grestore"," Q")
25908 @ @<Shift or transform as necessary before outputting text node~|p| at...@>=
25909 transformed=(txx_val(p)!=scf)||(tyy_val(p)!=scf)||
25910 (txy_val(p)!=0)||(tyx_val(p)!=0);
25911 if ( transformed ) {
25912 mp_print_cmd(mp, "gsave [", "q [");
25913 mp_ps_pair_out(mp, mp_make_scaled(mp, txx_val(p),scf),
25914 mp_make_scaled(mp, tyx_val(p),scf));
25915 mp_ps_pair_out(mp, mp_make_scaled(mp, txy_val(p),scf),
25916 mp_make_scaled(mp, tyy_val(p),scf));
25917 mp_ps_pair_out(mp, tx_val(p),ty_val(p));
25918 mp_ps_print_cmd(mp, "] concat 0 0 moveto","] t 0 0 m");
25920 mp_ps_pair_out(mp, tx_val(p),ty_val(p));
25921 mp_ps_print_cmd(mp, "moveto","m");
25925 @ Now that we've finished |ship_out|, let's look at the other commands
25926 by which a user can send things to the \.{GF} file.
25928 @ @<Determine if a character has been shipped out@>=
25930 mp->cur_exp=mp_round_unscaled(mp, mp->cur_exp) % 256;
25931 if ( mp->cur_exp<0 ) mp->cur_exp=mp->cur_exp+256;
25932 boolean_reset(mp->char_exists[mp->cur_exp]);
25933 mp->cur_type=mp_boolean_type;
25939 @ @<Allocate or initialize ...@>=
25940 mp_backend_initialize(mp);
25943 mp_backend_free(mp);
25946 @* \[45] Dumping and undumping the tables.
25947 After \.{INIMP} has seen a collection of macros, it
25948 can write all the necessary information on an auxiliary file so
25949 that production versions of \MP\ are able to initialize their
25950 memory at high speed. The present section of the program takes
25951 care of such output and input. We shall consider simultaneously
25952 the processes of storing and restoring,
25953 so that the inverse relation between them is clear.
25956 The global variable |mem_ident| is a string that is printed right
25957 after the |banner| line when \MP\ is ready to start. For \.{INIMP} this
25958 string says simply `\.{(INIMP)}'; for other versions of \MP\ it says,
25959 for example, `\.{(mem=plain 90.4.14)}', showing the year,
25960 month, and day that the mem file was created. We have |mem_ident=0|
25961 before \MP's tables are loaded.
25967 mp->mem_ident=NULL;
25969 @ @<Initialize table entries...@>=
25970 if (mp->ini_version)
25971 mp->mem_ident=xstrdup(" (INIMP)");
25973 @ @<Declare act...@>=
25974 void mp_store_mem_file (MP mp) ;
25976 @ @c void mp_store_mem_file (MP mp) {
25977 integer k; /* all-purpose index */
25978 pointer p,q; /* all-purpose pointers */
25979 integer x; /* something to dump */
25980 four_quarters w; /* four ASCII codes */
25982 @<Create the |mem_ident|, open the mem file,
25983 and inform the user that dumping has begun@>;
25984 @<Dump constants for consistency check@>;
25985 @<Dump the string pool@>;
25986 @<Dump the dynamic memory@>;
25987 @<Dump the table of equivalents and the hash table@>;
25988 @<Dump a few more things and the closing check word@>;
25989 @<Close the mem file@>;
25992 @ Corresponding to the procedure that dumps a mem file, we also have a function
25993 that reads~one~in. The function returns |false| if the dumped mem is
25994 incompatible with the present \MP\ table sizes, etc.
25996 @d off_base 6666 /* go here if the mem file is unacceptable */
25997 @d too_small(A) { wake_up_terminal;
25998 wterm_ln("---! Must increase the "); wterm((A));
25999 @.Must increase the x@>
26004 boolean mp_load_mem_file (MP mp) {
26005 integer k; /* all-purpose index */
26006 pointer p,q; /* all-purpose pointers */
26007 integer x; /* something undumped */
26008 str_number s; /* some temporary string */
26009 four_quarters w; /* four ASCII codes */
26011 @<Undump constants for consistency check@>;
26012 @<Undump the string pool@>;
26013 @<Undump the dynamic memory@>;
26014 @<Undump the table of equivalents and the hash table@>;
26015 @<Undump a few more things and the closing check word@>;
26016 return true; /* it worked! */
26019 wterm_ln("(Fatal mem file error; I'm stymied)\n");
26020 @.Fatal mem file error@>
26024 @ @<Declarations@>=
26025 boolean mp_load_mem_file (MP mp) ;
26027 @ Mem files consist of |memory_word| items, and we use the following
26028 macros to dump words of different types:
26030 @d dump_wd(A) { WW=(A); fwrite(&WW,sizeof(WW),1,mp->mem_file); }
26031 @d dump_int(A) { int cint=(A); fwrite(&cint,sizeof(cint),1,mp->mem_file); }
26032 @d dump_hh(A) { WW.hh=(A); fwrite(&WW,sizeof(WW),1,mp->mem_file); }
26033 @d dump_qqqq(A) { WW.qqqq=(A); fwrite(&WW,sizeof(WW),1,mp->mem_file); }
26034 @d dump_string(A) { dump_int(strlen(A)+1);
26035 fwrite(A,strlen(A)+1,1,mp->mem_file); }
26038 FILE * mem_file; /* for input or output of mem information */
26040 @ The inverse macros are slightly more complicated, since we need to check
26041 the range of the values we are reading in. We say `|undump(a)(b)(x)|' to
26042 read an integer value |x| that is supposed to be in the range |a<=x<=b|.
26044 @d undump_wd(A) { fread(&WW,sizeof(WW),1,mp->mem_file); (A)=WW; }
26045 @d undump_int(A) { int cint; fread(&cint,sizeof(cint),1,mp->mem_file); (A)=cint; }
26046 @d undump_hh(A) { fread(&WW,sizeof(WW),1,mp->mem_file); (A)=WW.hh; }
26047 @d undump_qqqq(A) { fread(&WW,sizeof(WW),1,mp->mem_file); (A)=WW.qqqq; }
26048 @d undump_strings(A,B,C) {
26049 undump_int(x); if ( (x<(A)) || (x>(B)) ) goto OFF_BASE; else (C)=str(x); }
26050 @d undump(A,B,C) { undump_int(x); if ( (x<(A)) || (x>(int)(B)) ) goto OFF_BASE; else (C)=x; }
26051 @d undump_size(A,B,C,D) { undump_int(x);
26052 if (x<(A)) goto OFF_BASE;
26053 if (x>(B)) { too_small((C)); } else {(D)=x;} }
26054 @d undump_string(A) { integer XX=0; undump_int(XX);
26055 A = xmalloc(XX,sizeof(char));
26056 fread(A,XX,1,mp->mem_file); }
26058 @ The next few sections of the program should make it clear how we use the
26059 dump/undump macros.
26061 @<Dump constants for consistency check@>=
26062 dump_int(mp->mem_top);
26063 dump_int(mp->hash_size);
26064 dump_int(mp->hash_prime)
26065 dump_int(mp->param_size);
26066 dump_int(mp->max_in_open);
26068 @ Sections of a \.{WEB} program that are ``commented out'' still contribute
26069 strings to the string pool; therefore \.{INIMP} and \MP\ will have
26070 the same strings. (And it is, of course, a good thing that they do.)
26074 @<Undump constants for consistency check@>=
26075 undump_int(x); mp->mem_top = x;
26076 undump_int(x); if (mp->hash_size != x) goto OFF_BASE;
26077 undump_int(x); if (mp->hash_prime != x) goto OFF_BASE;
26078 undump_int(x); if (mp->param_size != x) goto OFF_BASE;
26079 undump_int(x); if (mp->max_in_open != x) goto OFF_BASE
26081 @ We do string pool compaction to avoid dumping unused strings.
26084 w.b0=qi(mp->str_pool[k]); w.b1=qi(mp->str_pool[k+1]);
26085 w.b2=qi(mp->str_pool[k+2]); w.b3=qi(mp->str_pool[k+3]);
26088 @<Dump the string pool@>=
26089 mp_do_compaction(mp, mp->pool_size);
26090 dump_int(mp->pool_ptr);
26091 dump_int(mp->max_str_ptr);
26092 dump_int(mp->str_ptr);
26094 while ( (mp->next_str[k]==k+1) && (k<=mp->max_str_ptr) )
26097 while ( k<=mp->max_str_ptr ) {
26098 dump_int(mp->next_str[k]); incr(k);
26102 dump_int(mp->str_start[k]); /* TODO: valgrind warning here */
26103 if ( k==mp->str_ptr ) {
26110 while (k+4<mp->pool_ptr ) {
26111 dump_four_ASCII; k=k+4;
26113 k=mp->pool_ptr-4; dump_four_ASCII;
26114 mp_print_ln(mp); mp_print(mp, "at most "); mp_print_int(mp, mp->max_str_ptr);
26115 mp_print(mp, " strings of total length ");
26116 mp_print_int(mp, mp->pool_ptr)
26118 @ @d undump_four_ASCII
26120 mp->str_pool[k]=qo(w.b0); mp->str_pool[k+1]=qo(w.b1);
26121 mp->str_pool[k+2]=qo(w.b2); mp->str_pool[k+3]=qo(w.b3)
26123 @<Undump the string pool@>=
26124 undump_int(mp->pool_ptr);
26125 mp_reallocate_pool(mp, mp->pool_ptr) ;
26126 undump_int(mp->max_str_ptr);
26127 mp_reallocate_strings (mp,mp->max_str_ptr) ;
26128 undump(0,mp->max_str_ptr,mp->str_ptr);
26129 undump(0,mp->max_str_ptr+1,s);
26130 for (k=0;k<=s-1;k++)
26131 mp->next_str[k]=k+1;
26132 for (k=s;k<=mp->max_str_ptr;k++)
26133 undump(s+1,mp->max_str_ptr+1,mp->next_str[k]);
26134 mp->fixed_str_use=0;
26137 undump(0,mp->pool_ptr,mp->str_start[k]);
26138 if ( k==mp->str_ptr ) break;
26139 mp->str_ref[k]=max_str_ref;
26140 incr(mp->fixed_str_use);
26141 mp->last_fixed_str=k; k=mp->next_str[k];
26144 while ( k+4<mp->pool_ptr ) {
26145 undump_four_ASCII; k=k+4;
26147 k=mp->pool_ptr-4; undump_four_ASCII;
26148 mp->init_str_use=mp->fixed_str_use; mp->init_pool_ptr=mp->pool_ptr;
26149 mp->max_pool_ptr=mp->pool_ptr;
26150 mp->strs_used_up=mp->fixed_str_use;
26151 mp->pool_in_use=mp->str_start[mp->str_ptr]; mp->strs_in_use=mp->fixed_str_use;
26152 mp->max_pl_used=mp->pool_in_use; mp->max_strs_used=mp->strs_in_use;
26153 mp->pact_count=0; mp->pact_chars=0; mp->pact_strs=0;
26155 @ By sorting the list of available spaces in the variable-size portion of
26156 |mem|, we are usually able to get by without having to dump very much
26157 of the dynamic memory.
26159 We recompute |var_used| and |dyn_used|, so that \.{INIMP} dumps valid
26160 information even when it has not been gathering statistics.
26162 @<Dump the dynamic memory@>=
26163 mp_sort_avail(mp); mp->var_used=0;
26164 dump_int(mp->lo_mem_max); dump_int(mp->rover);
26165 p=0; q=mp->rover; x=0;
26167 for (k=p;k<= q+1;k++)
26168 dump_wd(mp->mem[k]);
26169 x=x+q+2-p; mp->var_used=mp->var_used+q-p;
26170 p=q+node_size(q); q=rlink(q);
26171 } while (q!=mp->rover);
26172 mp->var_used=mp->var_used+mp->lo_mem_max-p;
26173 mp->dyn_used=mp->mem_end+1-mp->hi_mem_min;
26174 for (k=p;k<= mp->lo_mem_max;k++ )
26175 dump_wd(mp->mem[k]);
26176 x=x+mp->lo_mem_max+1-p;
26177 dump_int(mp->hi_mem_min); dump_int(mp->avail);
26178 for (k=mp->hi_mem_min;k<=mp->mem_end;k++ )
26179 dump_wd(mp->mem[k]);
26180 x=x+mp->mem_end+1-mp->hi_mem_min;
26182 while ( p!=null ) {
26183 decr(mp->dyn_used); p=link(p);
26185 dump_int(mp->var_used); dump_int(mp->dyn_used);
26186 mp_print_ln(mp); mp_print_int(mp, x);
26187 mp_print(mp, " memory locations dumped; current usage is ");
26188 mp_print_int(mp, mp->var_used); mp_print_char(mp, '&'); mp_print_int(mp, mp->dyn_used)
26190 @ @<Undump the dynamic memory@>=
26191 undump(lo_mem_stat_max+1000,hi_mem_stat_min-1,mp->lo_mem_max);
26192 undump(lo_mem_stat_max+1,mp->lo_mem_max,mp->rover);
26195 for (k=p;k<= q+1; k++)
26196 undump_wd(mp->mem[k]);
26198 if ( (p>mp->lo_mem_max)||((q>=rlink(q))&&(rlink(q)!=mp->rover)) )
26201 } while (q!=mp->rover);
26202 for (k=p;k<=mp->lo_mem_max;k++ )
26203 undump_wd(mp->mem[k]);
26204 undump(mp->lo_mem_max+1,hi_mem_stat_min,mp->hi_mem_min);
26205 undump(null,mp->mem_top,mp->avail); mp->mem_end=mp->mem_top;
26206 for (k=mp->hi_mem_min;k<= mp->mem_end;k++)
26207 undump_wd(mp->mem[k]);
26208 undump_int(mp->var_used); undump_int(mp->dyn_used)
26210 @ A different scheme is used to compress the hash table, since its lower region
26211 is usually sparse. When |text(p)<>0| for |p<=hash_used|, we output three
26212 words: |p|, |hash[p]|, and |eqtb[p]|. The hash table is, of course, densely
26213 packed for |p>=hash_used|, so the remaining entries are output in~a~block.
26215 @<Dump the table of equivalents and the hash table@>=
26216 dump_int(mp->hash_used);
26217 mp->st_count=frozen_inaccessible-1-mp->hash_used;
26218 for (p=1;p<=mp->hash_used;p++) {
26219 if ( text(p)!=0 ) {
26220 dump_int(p); dump_hh(mp->hash[p]); dump_hh(mp->eqtb[p]); incr(mp->st_count);
26223 for (p=mp->hash_used+1;p<=(int)hash_end;p++) {
26224 dump_hh(mp->hash[p]); dump_hh(mp->eqtb[p]);
26226 dump_int(mp->st_count);
26227 mp_print_ln(mp); mp_print_int(mp, mp->st_count); mp_print(mp, " symbolic tokens")
26229 @ @<Undump the table of equivalents and the hash table@>=
26230 undump(1,frozen_inaccessible,mp->hash_used);
26233 undump(p+1,mp->hash_used,p);
26234 undump_hh(mp->hash[p]); undump_hh(mp->eqtb[p]);
26235 } while (p!=mp->hash_used);
26236 for (p=mp->hash_used+1;p<=(int)hash_end;p++ ) {
26237 undump_hh(mp->hash[p]); undump_hh(mp->eqtb[p]);
26239 undump_int(mp->st_count)
26241 @ We have already printed a lot of statistics, so we set |tracing_stats:=0|
26242 to prevent them appearing again.
26244 @<Dump a few more things and the closing check word@>=
26245 dump_int(mp->max_internal);
26246 dump_int(mp->int_ptr);
26247 for (k=1;k<= mp->int_ptr;k++ ) {
26248 dump_int(mp->internal[k]);
26249 dump_string(mp->int_name[k]);
26251 dump_int(mp->start_sym);
26252 dump_int(mp->interaction);
26253 dump_string(mp->mem_ident);
26254 dump_int(mp->bg_loc); dump_int(mp->eg_loc); dump_int(mp->serial_no); dump_int(69073);
26255 mp->internal[tracing_stats]=0
26257 @ @<Undump a few more things and the closing check word@>=
26259 if (x>mp->max_internal) mp_grow_internals(mp,x);
26260 undump_int(mp->int_ptr);
26261 for (k=1;k<= mp->int_ptr;k++) {
26262 undump_int(mp->internal[k]);
26263 undump_string(mp->int_name[k]);
26265 undump(0,frozen_inaccessible,mp->start_sym);
26266 if (mp->interaction==mp_unspecified_mode) {
26267 undump(mp_unspecified_mode,mp_error_stop_mode,mp->interaction);
26269 undump(mp_unspecified_mode,mp_error_stop_mode,x);
26271 undump_string(mp->mem_ident);
26272 undump(1,hash_end,mp->bg_loc);
26273 undump(1,hash_end,mp->eg_loc);
26274 undump_int(mp->serial_no);
26276 if ( (x!=69073)|| feof(mp->mem_file) ) goto OFF_BASE
26278 @ @<Create the |mem_ident|...@>=
26280 xfree(mp->mem_ident);
26281 mp->mem_ident = xmalloc(256,1);
26282 snprintf(mp->mem_ident,256," (mem=%s %i.%i.%i)",
26284 (int)(mp_round_unscaled(mp, mp->internal[year]) % 100),
26285 (int)mp_round_unscaled(mp, mp->internal[month]),
26286 (int)mp_round_unscaled(mp, mp->internal[day]));
26287 mp_pack_job_name(mp, mem_extension);
26288 while (! mp_w_open_out(mp, &mp->mem_file) )
26289 mp_prompt_file_name(mp, "mem file name", mem_extension);
26290 mp_print_nl(mp, "Beginning to dump on file ");
26291 @.Beginning to dump...@>
26292 mp_print(mp, mp->name_of_file);
26293 mp_print_nl(mp, mp->mem_ident);
26296 @ @<Dealloc variables@>=
26297 xfree(mp->mem_ident);
26299 @ @<Close the mem file@>=
26300 fclose(mp->mem_file)
26302 @* \[46] The main program.
26303 This is it: the part of \MP\ that executes all those procedures we have
26306 Well---almost. We haven't put the parsing subroutines into the
26307 program yet; and we'd better leave space for a few more routines that may
26308 have been forgotten.
26310 @c @<Declare the basic parsing subroutines@>;
26311 @<Declare miscellaneous procedures that were declared |forward|@>;
26312 @<Last-minute procedures@>
26314 @ We've noted that there are two versions of \MP. One, called \.{INIMP},
26316 has to be run first; it initializes everything from scratch, without
26317 reading a mem file, and it has the capability of dumping a mem file.
26318 The other one is called `\.{VIRMP}'; it is a ``virgin'' program that needs
26320 to input a mem file in order to get started. \.{VIRMP} typically has
26321 a bit more memory capacity than \.{INIMP}, because it does not need the
26322 space consumed by the dumping/undumping routines and the numerous calls on
26325 The \.{VIRMP} program cannot read a mem file instantaneously, of course;
26326 the best implementations therefore allow for production versions of \MP\ that
26327 not only avoid the loading routine for \PASCAL\ object code, they also have
26328 a mem file pre-loaded.
26331 boolean ini_version; /* are we iniMP? */
26333 @ @<Option variables@>=
26334 boolean ini_version; /* are we iniMP? */
26336 @ @<Set |ini_version|@>=
26337 mp->ini_version = (opt->ini_version ? true : false);
26339 @ Here we do whatever is needed to complete \MP's job gracefully on the
26340 local operating system. The code here might come into play after a fatal
26341 error; it must therefore consist entirely of ``safe'' operations that
26342 cannot produce error messages. For example, it would be a mistake to call
26343 |str_room| or |make_string| at this time, because a call on |overflow|
26344 might lead to an infinite loop.
26345 @^system dependencies@>
26347 This program doesn't bother to close the input files that may still be open.
26349 @<Last-minute...@>=
26350 void mp_close_files_and_terminate (MP mp) {
26351 integer k; /* all-purpose index */
26352 integer LH; /* the length of the \.{TFM} header, in words */
26353 int lk_offset; /* extra words inserted at beginning of |lig_kern| array */
26354 pointer p; /* runs through a list of \.{TFM} dimensions */
26355 @<Close all open files in the |rd_file| and |wr_file| arrays@>;
26356 if ( mp->internal[tracing_stats]>0 )
26357 @<Output statistics about this job@>;
26359 @<Do all the finishing work on the \.{TFM} file@>;
26360 @<Explain what output files were written@>;
26361 if ( mp->log_opened ){
26363 fclose(mp->log_file); mp->selector=mp->selector-2;
26364 if ( mp->selector==term_only ) {
26365 mp_print_nl(mp, "Transcript written on ");
26366 @.Transcript written...@>
26367 mp_print(mp, mp->log_name); mp_print_char(mp, '.');
26373 @ @<Declarations@>=
26374 void mp_close_files_and_terminate (MP mp) ;
26376 @ @<Close all open files in the |rd_file| and |wr_file| arrays@>=
26377 for (k=0;k<=(int)mp->read_files-1;k++ ) {
26378 if ( mp->rd_fname[k]!=NULL ) {
26379 fclose(mp->rd_file[k]);
26382 for (k=0;k<=(int)mp->write_files-1;k++) {
26383 if ( mp->wr_fname[k]!=NULL ) {
26384 fclose(mp->wr_file[k]);
26389 for (k=0;k<(int)mp->max_read_files;k++ ) {
26390 if ( mp->rd_fname[k]!=NULL ) {
26391 fclose(mp->rd_file[k]);
26392 mp_xfree(mp->rd_fname[k]);
26395 mp_xfree(mp->rd_file);
26396 mp_xfree(mp->rd_fname);
26397 for (k=0;k<(int)mp->max_write_files;k++) {
26398 if ( mp->wr_fname[k]!=NULL ) {
26399 fclose(mp->wr_file[k]);
26400 mp_xfree(mp->wr_fname[k]);
26403 mp_xfree(mp->wr_file);
26404 mp_xfree(mp->wr_fname);
26407 @ We want to produce a \.{TFM} file if and only if |fontmaking| is positive.
26409 We reclaim all of the variable-size memory at this point, so that
26410 there is no chance of another memory overflow after the memory capacity
26411 has already been exceeded.
26413 @<Do all the finishing work on the \.{TFM} file@>=
26414 if ( mp->internal[fontmaking]>0 ) {
26415 @<Make the dynamic memory into one big available node@>;
26416 @<Massage the \.{TFM} widths@>;
26417 mp_fix_design_size(mp); mp_fix_check_sum(mp);
26418 @<Massage the \.{TFM} heights, depths, and italic corrections@>;
26419 mp->internal[fontmaking]=0; /* avoid loop in case of fatal error */
26420 @<Finish the \.{TFM} file@>;
26423 @ @<Make the dynamic memory into one big available node@>=
26424 mp->rover=lo_mem_stat_max+1; link(mp->rover)=empty_flag; mp->lo_mem_max=mp->hi_mem_min-1;
26425 if ( mp->lo_mem_max-mp->rover>max_halfword ) mp->lo_mem_max=max_halfword+mp->rover;
26426 node_size(mp->rover)=mp->lo_mem_max-mp->rover;
26427 llink(mp->rover)=mp->rover; rlink(mp->rover)=mp->rover;
26428 link(mp->lo_mem_max)=null; info(mp->lo_mem_max)=null
26430 @ The present section goes directly to the log file instead of using
26431 |print| commands, because there's no need for these strings to take
26432 up |str_pool| memory when a non-{\bf stat} version of \MP\ is being used.
26434 @<Output statistics...@>=
26435 if ( mp->log_opened ) {
26438 wlog_ln("Here is how much of MetaPost's memory you used:");
26439 @.Here is how much...@>
26440 snprintf(s,128," %i string%s out of %i",(int)mp->max_strs_used-mp->init_str_use,
26441 (mp->max_strs_used!=mp->init_str_use+1 ? "s" : ""),
26442 (int)(mp->max_strings-1-mp->init_str_use));
26444 snprintf(s,128," %i string characters out of %i",
26445 (int)mp->max_pl_used-mp->init_pool_ptr,
26446 (int)mp->pool_size-mp->init_pool_ptr);
26448 snprintf(s,128," %i words of memory out of %i",
26449 (int)mp->lo_mem_max+mp->mem_end-mp->hi_mem_min+2,
26450 (int)mp->mem_end+1);
26452 snprintf(s,128," %i symbolic tokens out of %i", (int)mp->st_count, (int)mp->hash_size);
26454 snprintf(s,128," %ii, %in, %ip, %ib stack positions out of %ii, %in, %ip, %ib",
26455 (int)mp->max_in_stack,(int)mp->int_ptr,
26456 (int)mp->max_param_stack,(int)mp->max_buf_stack+1,
26457 (int)mp->stack_size,(int)mp->max_internal,(int)mp->param_size,(int)mp->buf_size);
26459 snprintf(s,128," %i string compactions (moved %i characters, %i strings)",
26460 (int)mp->pact_count,(int)mp->pact_chars,(int)mp->pact_strs);
26464 @ We get to the |final_cleanup| routine when \&{end} or \&{dump} has
26467 @<Last-minute...@>=
26468 void mp_final_cleanup (MP mp) {
26469 small_number c; /* 0 for \&{end}, 1 for \&{dump} */
26471 if ( mp->job_name==NULL ) mp_open_log_file(mp);
26472 while ( mp->input_ptr>0 ) {
26473 if ( token_state ) mp_end_token_list(mp);
26474 else mp_end_file_reading(mp);
26476 while ( mp->loop_ptr!=null ) mp_stop_iteration(mp);
26477 while ( mp->open_parens>0 ) {
26478 mp_print(mp, " )"); decr(mp->open_parens);
26480 while ( mp->cond_ptr!=null ) {
26481 mp_print_nl(mp, "(end occurred when ");
26482 @.end occurred...@>
26483 mp_print_cmd_mod(mp, fi_or_else,mp->cur_if);
26484 /* `\.{if}' or `\.{elseif}' or `\.{else}' */
26485 if ( mp->if_line!=0 ) {
26486 mp_print(mp, " on line "); mp_print_int(mp, mp->if_line);
26488 mp_print(mp, " was incomplete)");
26489 mp->if_line=if_line_field(mp->cond_ptr);
26490 mp->cur_if=name_type(mp->cond_ptr); mp->cond_ptr=link(mp->cond_ptr);
26492 if ( mp->history!=spotless )
26493 if ( ((mp->history==warning_issued)||(mp->interaction<mp_error_stop_mode)) )
26494 if ( mp->selector==term_and_log ) {
26495 mp->selector=term_only;
26496 mp_print_nl(mp, "(see the transcript file for additional information)");
26497 @.see the transcript file...@>
26498 mp->selector=term_and_log;
26501 if (mp->ini_version) {
26502 mp_store_mem_file(mp); return;
26504 mp_print_nl(mp, "(dump is performed only by INIMP)"); return;
26505 @.dump...only by INIMP@>
26509 @ @<Declarations@>=
26510 void mp_final_cleanup (MP mp) ;
26511 void mp_init_prim (MP mp) ;
26512 void mp_init_tab (MP mp) ;
26514 @ @<Last-minute...@>=
26515 void mp_init_prim (MP mp) { /* initialize all the primitives */
26519 void mp_init_tab (MP mp) { /* initialize other tables */
26520 integer k; /* all-purpose index */
26521 @<Initialize table entries (done by \.{INIMP} only)@>;
26525 @ When we begin the following code, \MP's tables may still contain garbage;
26526 the strings might not even be present. Thus we must proceed cautiously to get
26529 But when we finish this part of the program, \MP\ is ready to call on the
26530 |main_control| routine to do its work.
26532 @<Get the first line...@>=
26534 @<Initialize the input routines@>;
26535 if ( (mp->mem_ident==NULL)||(mp->buffer[loc]=='&') ) {
26536 if ( mp->mem_ident!=NULL ) mp_initialize(mp); /* erase preloaded mem */
26537 if ( ! mp_open_mem_file(mp) ) return false;
26538 if ( ! mp_load_mem_file(mp) ) {
26539 fclose( mp->mem_file); return false;
26541 fclose( mp->mem_file);
26542 while ( (loc<limit)&&(mp->buffer[loc]==' ') ) incr(loc);
26544 mp->buffer[limit]='%';
26545 mp_fix_date_and_time(mp);
26546 mp->sys_random_seed = (mp->get_random_seed)(mp);
26547 mp_init_randoms(mp, mp->sys_random_seed);
26548 @<Initialize the print |selector|...@>;
26549 if ( loc<limit ) if ( mp->buffer[loc]!='\\' )
26550 mp_start_input(mp); /* \&{input} assumed */
26553 @ @<Run inimpost commands@>=
26555 mp_get_strings_started(mp);
26556 mp_init_tab(mp); /* initialize the tables */
26557 mp_init_prim(mp); /* call |primitive| for each primitive */
26558 mp->init_str_use=mp->str_ptr; mp->init_pool_ptr=mp->pool_ptr;
26559 mp->max_str_ptr=mp->str_ptr; mp->max_pool_ptr=mp->pool_ptr;
26560 mp_fix_date_and_time(mp);
26564 @* \[47] Debugging.
26565 Once \MP\ is working, you should be able to diagnose most errors with
26566 the \.{show} commands and other diagnostic features. But for the initial
26567 stages of debugging, and for the revelation of really deep mysteries, you
26568 can compile \MP\ with a few more aids, including the \PASCAL\ runtime
26569 checks and its debugger. An additional routine called |debug_help|
26570 will also come into play when you type `\.D' after an error message;
26571 |debug_help| also occurs just before a fatal error causes \MP\ to succumb.
26573 @^system dependencies@>
26575 The interface to |debug_help| is primitive, but it is good enough when used
26576 with a \PASCAL\ debugger that allows you to set breakpoints and to read
26577 variables and change their values. After getting the prompt `\.{debug \#}', you
26578 type either a negative number (this exits |debug_help|), or zero (this
26579 goes to a location where you can set a breakpoint, thereby entering into
26580 dialog with the \PASCAL\ debugger), or a positive number |m| followed by
26581 an argument |n|. The meaning of |m| and |n| will be clear from the
26582 program below. (If |m=13|, there is an additional argument, |l|.)
26585 @<Last-minute...@>=
26586 void mp_debug_help (MP mp) { /* routine to display various things */
26591 mp_print_nl(mp, "debug # (-1 to exit):"); update_terminal;
26594 fscanf(mp->term_in,"%i",&m);
26598 fscanf(mp->term_in,"%i",&n);
26600 @<Numbered cases for |debug_help|@>;
26601 default: mp_print(mp, "?"); break;
26606 @ @<Numbered cases...@>=
26607 case 1: mp_print_word(mp, mp->mem[n]); /* display |mem[n]| in all forms */
26609 case 2: mp_print_int(mp, info(n));
26611 case 3: mp_print_int(mp, link(n));
26613 case 4: mp_print_int(mp, eq_type(n)); mp_print_char(mp, ':'); mp_print_int(mp, equiv(n));
26615 case 5: mp_print_variable_name(mp, n);
26617 case 6: mp_print_int(mp, mp->internal[n]);
26619 case 7: mp_do_show_dependencies(mp);
26621 case 9: mp_show_token_list(mp, n,null,100000,0);
26623 case 10: mp_print_str(mp, n);
26625 case 11: mp_check_mem(mp, n>0); /* check wellformedness; print new busy locations if |n>0| */
26627 case 12: mp_search_mem(mp, n); /* look for pointers to |n| */
26629 case 13: l = 0; fscanf(mp->term_in,"%i",&l); mp_print_cmd_mod(mp, n,l);
26631 case 14: for (k=0;k<=n;k++) mp_print_str(mp, mp->buffer[k]);
26633 case 15: mp->panicking=! mp->panicking;
26637 @ Saving the filename template
26639 @<Save the filename template@>=
26641 if ( mp->filename_template!=0 ) delete_str_ref(mp->filename_template);
26642 if ( length(mp->cur_exp)==0 ) mp->filename_template=0;
26644 mp->filename_template=mp->cur_exp; add_str_ref(mp->filename_template);
26648 @* \[48] System-dependent changes.
26649 This section should be replaced, if necessary, by any special
26650 modification of the program
26651 that are necessary to make \MP\ work at a particular installation.
26652 It is usually best to design your change file so that all changes to
26653 previous sections preserve the section numbering; then everybody's version
26654 will be consistent with the published program. More extensive changes,
26655 which introduce new sections, can be inserted here; then only the index
26656 itself will get a new section number.
26657 @^system dependencies@>
26660 Here is where you can find all uses of each identifier in the program,
26661 with underlined entries pointing to where the identifier was defined.
26662 If the identifier is only one letter long, however, you get to see only
26663 the underlined entries. {\sl All references are to section numbers instead of
26666 This index also lists error messages and other aspects of the program
26667 that you might want to look up some day. For example, the entry
26668 for ``system dependencies'' lists all sections that should receive
26669 special attention from people who are installing \MP\ in a new
26670 operating environment. A list of various things that can't happen appears
26671 under ``this can't happen''.
26672 Approximately 25 sections are listed under ``inner loop''; these account
26673 for more than 60\pct! of \MP's running time, exclusive of input and output.